forked from SimonLarsen/mrrescue
-
Notifications
You must be signed in to change notification settings - Fork 0
/
ingame.lua
524 lines (454 loc) · 14.2 KB
/
ingame.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
ingame = {}
local lg = love.graphics
INGAME_ACTIVE, INGAME_FADE_IN, INGAME_NEXTLEVEL_OUT, INGAME_FALL_OUT, INGAME_PRESCREEN,
INGAME_GAMEOVER_OUT, INGAME_GAMEOVER, INGAME_WON, INGAME_COUNTDOWN, INGAME_COUNTDOWN_IN = 0,1,2,3,4,5,6,7,8,9
COMBO_TIME = 4
function ingame.enter()
state = STATE_INGAME
translate_x, translate_y = 0,0
stopMusic()
ingame.newGame()
end
function ingame.newGame()
ingame_state = INGAME_COUNTDOWN_IN
max_casualties = 6-level
ingame.shake = 0
casualties = 0
score = 0
saved = 0
section = 1
last_missed = 0
last_rescue = 0
combo = 0
max_combo = 0
time = 0
transition_time = 0
warning_frame = 0
map = Map.create(section, level)
local startx, starty = map:getStart()
player = Player.create(startx,starty,level)
end
function ingame.nextLevel()
last_missed = #map.humans
casualties = casualties + last_missed
stats[6] = stats[6] + 3
if casualties >= max_casualties then
ingame_state = INGAME_GAMEOVER
else
ingame_state = INGAME_PRESCREEN
score = score + 1000
section = section + 1
map = Map.create(section, level)
player:warp(map:getStart())
end
end
function ingame.update(dt)
if love.keyboard.isDown("e") then
dt = dt/4
end
updateKeys()
-- INGAME STATE
if ingame_state == INGAME_ACTIVE then
time = time + dt
-- Check combo counter
last_rescue = last_rescue + dt
if last_rescue > COMBO_TIME then
max_combo = math.max(max_combo, combo)
combo = 0
end
-- Update map entities
map:update(dt)
-- Update entities
player:update(dt)
-- Calculate translation offest
translate_x = cap(player.x-WIDTH/2, 0, MAPW-WIDTH)
translate_y = cap(player.y-11-HEIGHT/2, 0, MAPH-HEIGHT+30)
if ingame.shake > 0 then
ingame.shake = ingame.shake - dt
end
map:setDrawRange(translate_x, translate_y, WIDTH, HEIGHT)
-- Set next level transition state if player has climbed out of the screen
if player.y < 0 then
ingame_state = INGAME_NEXTLEVEL_OUT
transition_time = 0
-- Set out of screen transition if player has fallen out of screen
elseif player.y > MAPH+32 then
ingame_state = INGAME_FALL_OUT
transition_time = 0
end
-- Update warning icon frame
warning_frame = (warning_frame + dt*2) % 2
-- Kill player if too many casualties
if casualties >= max_casualties then
player.temperature = player.max_temperature
end
-- Transition TO or FROM next level
elseif ingame_state == INGAME_NEXTLEVEL_OUT or ingame_state == INGAME_FALL_OUT
or ingame_state == INGAME_FADE_IN or ingame_state == INGAME_GAMEOVER_OUT
or ingame_state == INGAME_COUNTDOWN_IN then
transition_time = transition_time + dt*15
-- Calculate translation offest
translate_x = cap(player.x-WIDTH/2, 0, MAPW-WIDTH)
translate_y = cap(player.y-11-HEIGHT/2, 0, MAPH-HEIGHT+30)
map:setDrawRange(translate_x, translate_y, WIDTH, HEIGHT)
map:update(dt)
if transition_time > 20 then
if ingame_state == INGAME_NEXTLEVEL_OUT then
ingame.nextLevel()
setPrescreenMessage()
elseif ingame_state == INGAME_FALL_OUT then
player:warp(map:getStart())
ingame_state = INGAME_FADE_IN
elseif ingame_state == INGAME_FADE_IN then
ingame_state = INGAME_ACTIVE
elseif ingame_state == INGAME_COUNTDOWN_IN then
ingame_state = INGAME_COUNTDOWN
playSound("countdown")
elseif ingame_state == INGAME_GAMEOVER_OUT then
setPrescreenMessage()
ingame_state = INGAME_GAMEOVER
end
transition_time = 0
end
elseif ingame_state == INGAME_PRESCREEN or ingame_state == INGAME_GAMEOVER then
transition_time = transition_time + dt*3
elseif ingame_state == INGAME_WON then
if translate_y > -HEIGHT then
translate_y = translate_y - dt*20
end
map:setDrawRange(translate_x, translate_y, WIDTH, HEIGHT)
map:update(dt)
elseif ingame_state == INGAME_COUNTDOWN then
-- Calculate translation offest
translate_x = cap(player.x-WIDTH/2, 0, MAPW-WIDTH)
translate_y = cap(player.y-11-HEIGHT/2, 0, MAPH-HEIGHT+30)
map:setDrawRange(translate_x, translate_y, WIDTH, HEIGHT)
map:update(dt)
transition_time = transition_time + dt
if transition_time >= 3.5 then
ingame_state = INGAME_ACTIVE
playMusic(table.random({"rockerronni","bundesliga","scooterfest"}))
end
end
end
function ingame.draw()
-- Scale screen
lg.push()
lg.scale(config.scale)
if ingame_state == INGAME_ACTIVE or ingame_state == INGAME_FADE_IN
or ingame_state == INGAME_NEXTLEVEL_OUT or ingame_state == INGAME_FALL_OUT
or ingame_state == INGAME_GAMEOVER_OUT or ingame_state == INGAME_WON
or ingame_state == INGAME_COUNTDOWN_IN or ingame_state == INGAME_COUNTDOWN then
lg.push()
-- Translate to center player
if ingame.shake > 0 then
lg.translate(-math.floor(translate_x+math.random()*4-2), -math.floor(translate_y+math.random()*2.5))
else
lg.translate(-math.floor(translate_x), -math.floor(translate_y))
end
-- Draw back
map:drawBack()
-- Draw player
player:draw()
-- Draw front
map:drawFront()
if player.state == PS_DEAD then
player:draw()
end
lg.pop()
if map.type == MT_NORMAL then
lg.setBlendMode("multiplicative")
lg.draw(canvas, 0,0)
lg.setBlendMode("alpha")
end
-- Draw red screen if hit
if player.heat > 0 then
lg.setColor(255,255,255,cap(player.heat*255, 16, 255))
lg.drawq(img.red_screen, quad.red_screen, 0,0)
lg.setColor(255,255,255,255)
end
-- Draw hud
drawHUD()
-- Draw transition if eligible
if ingame_state == INGAME_NEXTLEVEL_OUT or ingame_state == INGAME_FALL_OUT or ingame_state == INGAME_GAMEOVER_OUT then
local frame = math.floor(transition_time)
lg.pop()
--lg.pop()
lg.push()
lg.scale(config.scale)
for ix = 0,7 do
for iy = 0,6 do
lg.drawq(img.circles, quad.circles[math.max(0,math.min(frame-13+ix+iy,6))], ix*32, iy*32)
end
end
end
if ingame_state == INGAME_FADE_IN or ingame_state == INGAME_COUNTDOWN_IN then
local frame = math.floor(transition_time)
for ix = 0,7 do
for iy = 0,6 do
lg.drawq(img.circles, quad.circles[6-math.max(0,math.min(frame-13+ix+iy,6))], ix*32, iy*32)
end
end
end
if ingame_state == INGAME_WON then
if translate_y < 0 then
drawWonMessage()
end
end
if ingame_state == INGAME_COUNTDOWN then
local frame = math.floor(transition_time)
lg.drawq(img.countdown, quad.countdown[frame], 96, 87)
end
elseif ingame_state == INGAME_PRESCREEN then
drawPrescreen()
elseif ingame_state == INGAME_GAMEOVER then
drawGameover()
end
lg.pop()
end
function drawWonMessage()
local alpha = cap((-translate_y)/100, 0, 1)
lg.setColor(0,0,0,alpha*255)
lg.rectangle("fill", 0, 40, WIDTH, #WON_MESSAGES[level]*10+12)
lg.setColor(255,255,255,alpha*255)
for i,v in ipairs(WON_MESSAGES[level]) do
lg.printf(v, 0, 48+(i-1)*10, WIDTH, "center")
end
lg.setColor(255,255,255,255)
end
function drawHUD()
lg.draw(img.hud, 0, HEIGHT-32)
-- Draw water tank bar
local water_ratio = player.water / player.water_capacity
quad.water_bar:setViewport(0, 0, math.floor(water_ratio*55+0.5), 11)
if player.overloaded == false then
if player.hasReserve == true then
lg.drawq(img.reserve_bar, quad.water_bar, 10, HEIGHT-22)
else
lg.drawq(img.water_bar, quad.water_bar, 10, HEIGHT-22)
end
else
lg.drawq(img.overloaded_bar, quad.water_bar, 10, HEIGHT-22)
end
-- Draw temperature bar
local temp_length = math.floor((player.temperature/player.max_temperature)*82+0.5)
quad.temperature_bar:setViewport(0,0, temp_length, 6)
lg.drawq(img.temperature_bar, quad.temperature_bar, 75, HEIGHT-25)
lg.drawq(img.temperature_bar, quad.temperature_bar_end, 75+temp_length, HEIGHT-25)
-- Draw casualty count
for i=1,max_casualties do
if i<= casualties then
lg.drawq(img.hud_people, quad.hud_people_red, 168+(i-1)*5, HEIGHT-25)
else
lg.drawq(img.hud_people, quad.hud_people_green, 168+(i-1)*5, HEIGHT-25)
end
end
-- Draw second HUD layer
lg.draw(img.hud2, 0, HEIGHT-32)
-- Blink temperature bar if needed
if player:isDying() then
local color = 0
if warning_frame <= 1 then
color = 30 + warning_frame * 225
else
color = 255 - (warning_frame % 1)*225
end
lg.setColor(255,color,color)
lg.draw(img.temperature_bar_blink, 74, HEIGHT-26)
lg.setColor(255,255,255)
end
-- Draw item slots
for i=1,3 do
if i <= player.num_regens then
lg.drawq(img.item_slots, quad.item_slot_regen, 78+(i-1)*6, HEIGHT-14)
end
if i <= player.num_tanks then
lg.drawq(img.item_slots, quad.item_slot_tank, 100+(i-1)*6, HEIGHT-14)
end
if i <= player.num_suits then
lg.drawq(img.item_slots, quad.item_slot_suit, 122+(i-1)*6, HEIGHT-14)
end
end
-- Draw score
lg.setFont(font.bold)
lg.setColor(16,12,9)
lg.print("SCORE: "..score,150,187)
lg.setColor(246,247,221)
lg.print("SCORE: "..score,150,186)
lg.setColor(255,255,255)
-- Draw boss health bar
if map.type == MT_BOSS and ingame_state ~= INGAME_WON then
lg.drawq(img.boss_health, quad.boss_health, 0, 11)
local boss_length = math.floor((map.boss.health/map.boss.MAX_HEALTH)*178+0.5)
lg.drawq(img.boss_health, quad.boss_bar, 64,22, 0, boss_length, 1)
lg.drawq(img.boss_health, quad.boss_bar_end, 64+boss_length,22, 0)
local bossframe = 0
if map.boss.angry == true then bossframe = bossframe + 2 end
if map.boss.hit == true or map.boss.state == BS_DEAD then bossframe = bossframe + 1 end
lg.drawq(map.boss:getPortraitImage(), quad.boss_portrait[bossframe], 15,15)
end
-- Draw panic/burning human icons
drawIcons()
end
function drawPrescreen()
local floor = section*3-2
lg.setFont(font.bold)
if map.type == MT_NORMAL then
lg.printf("FLOOR ".. floor .. "-" .. floor+2, 0, 40, WIDTH, "center")
lg.drawq(img.captain_dialog, quad.prescreen_music, 7, 183)
else
lg.printf("ROOF", 0, 40, WIDTH, "center")
end
local fr = math.floor(transition_time) % 2
lg.drawq(img.captain_dialog, quad.captain_dialog[fr], 28, 72)
lg.printf(prescreen_message, 74, 80, 150, "left")
lg.printf("PRESS RETURN TO CONTINUE", 0, 150, WIDTH, "center")
end
function drawGameover()
local fr = math.floor(transition_time) % 2
lg.drawq(img.captain_dialog_sad, quad.captain_dialog[fr], 28, 72)
if casualties >= max_casualties then
lg.printf(prescreen_message, 74, 80, 140, "left")
elseif player.state == PS_DEAD then
lg.printf(prescreen_message, 74, 80, 140, "left")
end
end
function setPrescreenMessage()
if casualties >= max_casualties then
prescreen_message = "TOO MANY CIVILIANS HAVE DIED!\n\nYOU ARE FIRED!"
elseif player.state == PS_DEAD then
prescreen_message = "YOUR SUIT OVERHEATED!\n\nGAME OVER"
else
if section == 1 then
prescreen_message = table.random(GOODLUCK_MESSAGES)
elseif map.type == MT_BOSS then
prescreen_message = BOSS_MESSAGE[level]
playMusic("roof")
elseif last_missed > 0 then
if last_missed == 1 then
prescreen_message = "HEY THERE, BUDDY!\nYOU MISSED 1 PERSON.\nTRY A LITTLE HARDER."
else
prescreen_message = "HEY THERE, BUDDY!\nYOU LET "..last_missed.." PEOPLE BURN TO DEATH.\nTRY A LITTLE HARDER."
end
else
prescreen_message = table.random(NO_CASUALTIES_MESSAGES)
end
end
end
--- Draws warning icons for panicing/burning/dead enemies
function drawIcons()
-- Draw for panicing/burning humans
for i,v in ipairs(map.humans) do
if (v.state == HS_BURN or v.state == HS_PANIC) and
(v.x < translate_x or v.x > translate_x+WIDTH or v.y < translate_y or v.y > translate_y+174) then
if v.state == HS_BURN then
drawIcon(v.x, v.y-12, 0)
else
drawIcon(v.x, v.y-12, 2)
end
end
end
-- Draw for ashes particles (dead humans)
for i,v in ipairs(map.particles) do
if v.isAshes == true and
(v.x < translate_x or v.x > translate_x+WIDTH or v.y < translate_y or v.y > translate_y+174) then
drawIcon(v.x, v.y-12, 4)
end
end
end
--- Draws a warning icon for an entity.
-- Does not check if entity is actually outside screen.
-- @param x X position of entity
-- @param y Y position of entity
-- @param frame_offset Offset into warning icon quad array
function drawIcon(x,y,frame_offset)
local frame = cap(frame_offset + math.floor(warning_frame), 0, 4)
local deltax = x - (translate_x+WIDTH/2)
local deltay = y - (translate_y+84)
local xt,yt
if deltax > 0 then xt = 114/deltax
else xt = -114/deltax end
if deltay > 0 then yt = 72/deltay
else yt = -72/deltay end
local t
if xt > yt then t = yt
else t = xt end
lg.drawq(img.warning_icons, quad.warning_icons[frame], WIDTH/2+t*deltax, 84+t*deltay, 0,1,1,11,10)
end
--- Updates the light map canvas
-- Assumes the view matrix is translated but not scaled
function updateLightmap()
if ingame_state == INGAME_ACTIVE or ingame_state == INGAME_FADE_IN
or ingame_state == INGAME_NEXTLEVEL_OUT or ingame_state == INGAME_FALL_OUT
or ingame_state == INGAME_GAMEOVER_OUT or ingame_state == INGAME_WON
or ingame_state == INGAME_COUNTDOWN_IN or ingame_state == INGAME_COUNTDOWN then
lg.translate(-math.floor(translate_x), -math.floor(translate_y))
canvas:clear(0,0,0,255)
lg.setCanvas(canvas)
lg.setBlendMode("additive")
lg.draw(img.light_player, player.flx-128, player.fly-138)
map:drawFireLight()
for i,v in ipairs(map.enemies) do
v:drawLight()
end
-- Light up outside building
lg.rectangle("fill", 0,0, 32, MAPH)
lg.rectangle("fill", MAPW-32, 0, 32, MAPH)
lg.setBlendMode("alpha")
lg.setCanvas()
end
end
function ingame.keypressed(k,uni)
if ingame_state == INGAME_ACTIVE then
if k == "escape" then
ingame_menu.enter()
elseif k == "p" then
map:addParticle(PopupText.create("megacombo"))
else
for a, key in pairs(config.keys) do
if k == key then
player:action(a)
end
end
end
elseif ingame_state == INGAME_PRESCREEN then
if k == "return" or k == " " then
ingame_state = INGAME_FADE_IN
transition_time = 0
elseif k == "left" then
prevSong()
elseif k == "right" then
nextSong()
end
elseif ingame_state == INGAME_GAMEOVER then
if k == "return" or k == " " then
summary.enter()
end
elseif ingame_state == INGAME_WON and translate_y < 0 then
if k == "return" or k == " " then
summary.enter()
end
end
end
function ingame.action(k)
if ingame_state == INGAME_ACTIVE then
if k == "pause" then
ingame_menu.enter()
else
player:action(k)
end
elseif ingame_state == INGAME_PRESCREEN then
if k == "left" then
prevSong()
elseif k == "right" then
nextSong()
else
ingame_state = INGAME_FADE_IN
transition_time = 0
end
elseif ingame_state == INGAME_GAMEOVER then
summary.enter()
elseif ingame_state == INGAME_WON and translate_y < 0 then
summary.enter()
end
end