1 # This file defines a World struct that keeps track of everything
11 struct Goal(pos:Vector2)
12 SIZE := Vector2(32,32)
14 texture := Texture.load((./assets/Hurricane.png))
15 texture.draw(g.pos, Goal.SIZE)
17 # Return a displacement relative to `a` that will push it out of `b`
18 func solve_overlap(a_pos:Vector2, a_size:Vector2, b_pos:Vector2, b_size:Vector2 -> Vector2)
19 a_left := a_pos.x - a_size.x/2
20 a_right := a_pos.x + a_size.x/2
21 a_top := a_pos.y - a_size.x/2
22 a_bottom := a_pos.y + a_size.y/2
24 b_left := b_pos.x - b_size.x/2
25 b_right := b_pos.x + b_size.x/2
26 b_top := b_pos.y - b_size.y/2
27 b_bottom := b_pos.y + b_size.y/2
29 # Calculate the overlap in each dimension
30 overlap_x := (a_right _min_ b_right) - (a_left _max_ b_left)
31 overlap_y := (a_bottom _min_ b_bottom) - (a_top _max_ b_top)
33 # If either axis is not overlapping, then there is no collision:
34 if overlap_x <= 0 or overlap_y <= 0
37 if overlap_x < overlap_y
38 if a_right > b_left and a_right < b_right
39 return Vector2(-(overlap_x), 0)
40 else if a_left < b_right and a_left > b_left
41 return Vector2(overlap_x, 0)
43 if a_top < b_bottom and a_top > b_top
44 return Vector2(0, overlap_y)
45 else if a_bottom > b_top and a_bottom < b_bottom
46 return Vector2(0, -overlap_y)
50 func overlaps(a_pos:Vector2, a_size:Vector2, b_pos:Vector2, b_size:Vector2 -> Bool)
51 return solve_overlap(a_pos, a_size, b_pos, b_size) != Vector2(0, 0)
53 struct Particle(pos,vel:Vector2,radius:Num32,color:Color)
54 func update(p:&Particle, dt:Num32)
57 p.radius -= dt*Num32(20.0)
61 DrawCircleV(p.pos, p.radius, p.color)
63 func draw_centered_text(x,y:Int32, text:Text, color:Color, font_size=Int32(48), shadow=yes)
65 draw_centered_text(x+2,y,text, Color(0,0,0), font_size, shadow=no)
66 draw_centered_text(x-2,y,text, Color(0,0,0), font_size, shadow=no)
67 draw_centered_text(x,y+2,text, Color(0,0,0), font_size, shadow=no)
68 draw_centered_text(x,y-2,text, Color(0,0,0), font_size, shadow=no)
70 string := CString(text)
71 width := MeasureText(string, 48)
72 DrawText(string, x - width/2, y - font_size/2, font_size, color)
75 player=@Player(Vector2(0,0), Vector2(0,0)),
76 camera=@Camera(Vector2(0,0)),
78 satellites:@[@Satellite]=@[],
79 particles:@[@Particle]=@[],
81 letters:@[Letter]=@[],
85 DT := Num32(1.)/Num32(60.)
86 STIFFNESS := Num32(0.3)
88 func from_map(path:Path -> @World)
90 world.load_map(path.read() or exit("Could not find the game map: $path"))
91 world.camera.zoom = C_code : Num32 (
92 (float)GetScreenWidth()/1000.
96 func update(w:@World, dt:Num32)
100 w.dt_accum -= World.DT
101 w.camera.follow(w.player.pos)
104 func update_once(w:@World)
105 if w.goal and w.player.pos.dist(w.goal!.pos) < 30
106 w.player.target_vel = Vector2(0,0)
107 w.player.pos = w.player.pos.mix(w.goal!.pos, .03)
108 else if w.player.has_signal and not w.player.dead
109 target_x := C_code:Num32 (
110 (Num32_t)((IsKeyDown(KEY_A) ? -1 : 0) + (IsKeyDown(KEY_D) ? 1 : 0))
112 target_y := C_code:Num32 (
113 (Num32_t)((IsKeyDown(KEY_W) ? -1 : 0) + (IsKeyDown(KEY_S) ? 1 : 0))
115 w.player.target_vel = Vector2(target_x, target_y).norm() * Player.WALK_SPEED
119 w.player.facing = w.player.facing.norm().rotated(Num32.TAU/60)
121 w.player.has_signal = no
122 for s in w.satellites
123 s.update(w.boxes, w.player)
128 w.particles[] = [p for p in w.particles if p.radius > 0]
130 GR := Num32(.5) + Num32.sqrt(5)!/Num32(2)
131 if not w.won_time and w.goal and overlaps(w.player.pos, Player.SIZE, w.goal!.pos, Goal.SIZE)
132 w.won_time = GetTime()
135 Color(0xFF, 0x00, 0x66),
136 Color(0x00, 0xCC, 0xFF),
137 Color(0xFF, 0xFF, 0x33),
138 Color(0x66, 0xFF, 0x66),
139 Color(0xFF, 0x66, 0x00),
140 Color(0x99, 0x33, 0xFF),
141 Color(0xFF, 0x33, 0x99),
142 Color(0x00, 0xFF, 0xCC),
145 angle := Num32.TAU * ((Num32(i) * GR) mod Num32(1))
149 vel=Vector2(random.num32(100,500),0).rotated(angle),
150 radius=random.num32(7,10),
151 color=colors[i mod1 colors.length],
156 # Resolve player overlapping with any boxes:
159 correction := solve_overlap(w.player.pos, Player.SIZE, b.pos, b.size)
160 if b.fatal and correction != Vector2(0,0) and not w.player.dead
161 # Player hit a killer wall
163 w.camera.add_shake(100)
165 Color(0xFF, 0xA5, 0x00, 0xc0),
166 Color(0xD2, 0x69, 0x1E, 0xc0),
167 Color(0x8B, 0x45, 0x13, 0xc0),
168 Color(0x70, 0x42, 0x22, 0xc0),
169 Color(0x55, 0x33, 0x22, 0xc0),
170 Color(0x88, 0x88, 0x88, 0xc0),
171 Color(0x44, 0x22, 0x11, 0xc0),
172 Color(0xCC, 0x44, 0x00, 0xc0),
175 angle := Num32.TAU * ((Num32(i) * GR) mod Num32(1))
179 vel=Vector2(random.num32(100,600),0).rotated(angle),
180 radius=random.num32(10,30),
181 color=colors[i mod1 colors.length],
185 w.player.pos += World.STIFFNESS * correction
187 w.camera.update(World.DT)
190 ClearBackground(Color(0,0,0))
191 bg := Texture.load((./assets/background.png), yes)
194 Vector2(Num32(GetScreenWidth()), Num32(GetScreenHeight())),
195 texture_offset=w.camera.pos*Num32(0.5),
196 tint=Color(0xff,0xff,0xc0,0xFF),
200 Vector2(Num32(GetScreenWidth()), Num32(GetScreenHeight())),
201 texture_offset=w.camera.pos*Num32(0.25) + Vector2(500,300),
202 tint=Color(0xff,0xff,0xc0,0x80),
206 w.camera.begin_drawing()
207 defer w.camera.end_drawing()
212 for s in w.satellites
218 for s in w.satellites
231 draw_centered_text(GetScreenWidth()/2, GetScreenHeight()/2 + 70, "You Win!", color=Color(0x80,0xff,0x80))
232 else if w.player.dead or (not w.player.has_signal and w.player.pos.dist(w.player.prev_pos) < Num32(1.0))
233 draw_centered_text(GetScreenWidth()/2, GetScreenHeight()/2 + 70, "Press 'R' to Restart", color=Color(0xff,0x80,0x80))
235 func load_map(w:@World, map:Text)
237 box_size := Vector2(25., 50.)
238 star_textures := [Texture.load(t) for t in (./assets/WhiteStar*).glob()]
239 for y,line in map.lines()
240 for x,cell in line.split()
241 pos := Vector2((Num32(x)-1) * box_size.x, (Num32(y)-1) * box_size.y)
242 if cell == "[" or cell == "]"
243 box := @Box(pos, size=box_size)
246 box := @Box(pos, size=box_size, color=Color(0xe0,0x10,0x10), fatal=yes)
249 box := @Box(pos, size=box_size, color=Color(0xc0,0xc0,0xff,0x40))
254 pos += box_size/2 - Player.SIZE/2
255 w.player = @Player(pos,pos)
259 w.satellites.insert(@Satellite(pos))
261 w.letters.insert(Letter(CString(cell), pos))