-
Notifications
You must be signed in to change notification settings - Fork 0
/
human.lua
executable file
·379 lines (314 loc) · 10.2 KB
/
human.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
local class = require "middleclass.middleclass"
local Mobile = require "mobile"
local Human = class("Human", Mobile)
local global = require "global"
local Gun = require "gun"
local Zombie = require "zombie"
local Trap = require "trap"
local Bullet = require "bullet"
local Gate = require "gate"
local Barricade = require "barricade"
local drawing = require "drawing"
local size = 10
local range = 100
local heal_rate = 10
local cooldown_rate = 1
local image = love.graphics.newImage('human.png')
local image_selected = love.graphics.newImage('human_selected.png')
local forenames = {"Clarence",
"Galahad",
"Joseph",
"Edward",
"Sebastian",
"Colin",
"Rupert",
"Montague",
"Hugo",
"Gerald",
"Cyril",
"Ann",
"Constance",
"Millicent",
"Daphne",
"Joan",
"Aileen",
"Linda",
"Susan",
"Lavender",
"Alexandra",
"Eve"}
local surnames = {"Threepwood",
"Warblington",
"Keeble",
"Jackson",
"Cumberbatch",
"Runciman",
"Fish",
"Moresby",
"Garland",
"Wedge",
"Allsop"}
local talents = {"Velociraptor Whisperer"}
local dreams = {"Get a summer house in Spain",
"Buy a farm",
"Start a business",
"Get married",
"See son through university",
"Become a teacher",
"Learn to dance"}
function Human:initialize(x, y, ammo, cooldown, reload)
Mobile.initialize(self, x, y, 100)
self.hitbox = global.addHitbox(self, x-1, y-1,
image:getWidth()+2, image:getHeight()+2)
self.selected = false
self.targetx = nil
self.targety = nil
self.infected = false
self:setMaxSpeed(50)
self.mode = "normal"
self.max_cooldown = 10
self.cooldown = self.max_cooldown
self.tiredCooldownMax = love.math.random(120, 240)
self.tiredCooldown = 0
self.accuracy = 10
self.gun = Gun:new(15, 10, ammo, cooldown, reload, 10)
self.baseBulletChance = 0.05 -- Chance of being damaged by a bullet
self.bulletChance = self.baseBulletChance
self:setName(forenames[love.math.random(1, #forenames)],
surnames[love.math.random(1, #surnames)])
if love.math.random() <= 0.25 then
self.talent = talents[love.math.random(1, #talents)]
end
self.dream = dreams[love.math.random(1, #dreams)]
self.repath_backoff = nil
self.repath_attempts = 0
end
function Human:stopsBullets()
return false
end
function Human:setName(forename, surname)
self.forename = forename
self.surname = surname
self.name = forename .. " " .. surname
end
function Human:say(msg)
self.lastsaid = msg
global.log("(" .. self.name .. ") " .. msg)
end
function Human:draw()
love.graphics.setColor(255, 255, 255)
local im = image
if self.selected then
im = image_selected
end
love.graphics.draw(im,
self.x+im:getWidth()/2, self.y+im:getWidth()/2,
self.rotation,
1, 1,
im:getWidth()/2, im:getHeight()/2)
drawing.bar(self.x, self.y - 5,
im:getWidth(), 2,
self.hp / self.maxhp,
{0, 255, 0},
{255, 0, 0})
drawing.bar(self.x, self.y - 3,
im:getWidth(), 2,
self.cooldown / self.max_cooldown,
{0, 255, 255},
{0, 0, 255})
end
function Human:update(dt)
self.gun:update(dt)
self.cooldown = self.cooldown + cooldown_rate * dt
if self.cooldown >= self.max_cooldown then
self.cooldown = self.max_cooldown
end
local zeds = {}
local zcount = 0
for _, e in pairs(global.zombies) do
-- cycle breaking :(
if self:getAbsDistance(e) < range and self:canSee(e) then
zcount = zcount + 1
zeds[zcount] = e
end
end
if self.mode == "normal" then
if zcount > 0 then
-- Pick a random zombie
local zid = love.math.random(zcount)
local zx, zy = zeds[zid].x, zeds[zid].y
self.rotation = math.atan2(zx - self.x, self.y - zy)
self.gun:fire(self.x + size / 2, self.y + size / 2, zeds[zid])
end
elseif self.mode == "heal" then
self:heal(heal_rate * dt)
elseif self.mode == "trap" then
if self.cooldown == self.max_cooldown then
self.cooldown = 0
global.addDrawable(Trap(self.x, self.y))
end
elseif self.mode == "barricade" then
if self.cooldown == self.max_cooldown then
global.addDrawable(Barricade(self.x, self.y))
self:setMode("normal")
end
end
-- Heal passively (but slowly)
if self.mode ~= "heal" then
self:heal(dt)
end
-- Become tired
local scale = 3 * self.tiredCooldown / self.tiredCooldownMax
self.gun.accuracy = self.accuracy * scale
self.bulletChance = self.baseBulletChance * scale
if self.tiredCooldown < self.tiredCooldownMax then
self.tiredCooldown = self.tiredCooldown + dt
end
closeZ = self:getClosest("Zombie", 20)
if self.targetx == nil and closeZ then
self:setTarget(self.x - (closeZ.x - self.x), self.y - (closeZ.y - self.y))
end
if self.infected then
self:hurt(5 * dt)
end
if love.math.random() <= 0.05 * dt then
local phrases1 = {"Ho hum",
"Red hair, sir, in my opinion, is dangerous.",
"Do trousers matter?",
"I am far from being gruntled.",
"What ho!",
"What ho?",
"What ho! What ho!",
"I always advise people never to give advice.",
"Unseen in the background, Fate was quietly slipping lead into the boxing-glove.",
"I am not always good and noble.",
"If he had a mind, there was something on it.",
"You would not enjoy Nietzsche, sir. He is fundamentally unsound."}
local p1count = 12
local phrases2 = {}
if self.gun.ammo <= 0 then
table.insert(phrases2, "I'm out of ammo!")
end
if zcount == 0 then
table.insert(phrases2, "Can't see anything...")
elseif zcount <= 3 then
table.insert(phrases2, "Got the blighter in my sights!")
else
table.insert(phrases2, "There are so many!")
end
if self.infected then
table.insert(phrases2, "I'm feeling a little woozy...")
end
if self.hp < 10 then
table.insert(phrases2, "It's all going dark!")
elseif self.hp < 50 then
table.insert(phrases2, "That hurt!")
end
local p2prob = 0.5
if #phrases2 > 2 then
p2prob = 0.75
end
if love.math.random() <= p2prob then
self:say(phrases2[love.math.random(1, #phrases2)])
else
self:say(phrases1[love.math.random(1, #phrases1)])
end
end
-- backoff on repathing if we get stuck
if self.repath_backoff then
self.repath_backoff = self.repath_backoff - dt
if self.repath_backoff <= 0 then
self.repath_backoff = nil
end
end
-- naive pathfinding
if self.destx and self.desty then
if (not (self.targetx and self.targety) or
self:getAbsDistance({x=self.targetx, y=self.targety}) <= 1) and
not self.repath_backoff then
local cx, cy = self.hitbox:center()
local tx, ty = global.grid:pathNext({cx, cy},
{self.destx, self.desty})
tx = tx - cx + self.x
ty = ty - cy + self.y
self:setTarget(tx, ty)
end
if self:getAbsDistance({x=self.destx, y=self.desty}) <= 1 then
self:stop()
end
end
Mobile.update(self, dt)
end
function Human:onCollision(other, dx, dy)
if other:isInstanceOf(Gate) then
other:passingHuman()
return
end
if other.stopsHumans then
Mobile.onCollision(self, other, dx, dy)
end
end
function Human:toggleSelected()
self.selected = not self.selected
end
function Human:setSelected()
self.selected = true
end
function Human:setUnselected()
self.selected = false
end
function Human:zomb()
self.infected = true
self:say("It got me!")
end
function Human:setMode(mode)
if mode == "normal" or mode == "heal" then
self.cooldown = self.max_cooldown
else
self.cooldown = 0
end
self.mode = mode
end
function Human:heal(amount)
self.hp = self.hp + amount
if self.hp >= self.maxhp then
self.hp = self.maxhp
self.infected = false
end
end
function Human:hurt(damage)
self.hp = self.hp - damage
if self.hp <= 0 then
self:say("Argh!")
if self.infected then
self:say("*hiss*")
global.addDrawable(Zombie:new(self.x, self.y))
end
self:destroy()
end
end
function Human:awaken()
self.tiredCooldown = 0
end
function Human:setDest(x, y)
self.destx = x
self.desty = y
self.targetx = nil
self.targety = nil
self.buffer = {}
end
function Human:stop()
Mobile.stop(self)
if self.destx and self.desty and self:getAbsDistance({x=self.destx, y=self.desty}) <= 1 or self.repath_attempts > 3 then
self.destx = nil
self.desty = nil
self.repath_attempts = 0
elseif self.destx and self.desty then
self.repath_backoff = math.random(5 * self.repath_attempts, 10 * self.repath_attempts) / 10
self.repath_attempts = self.repath_attempts + 1
end
end
function Human:brains()
return -10
end
return Human