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
|
require "data.directions"
local Loot = require "objects.loot"
local physics = require "logic.physics"
local enemyData = require "data.enemyData"
local FrameTimer = require "utils.frameTimer"
local audio = require "utils.audio"
local Enemy = {}
Enemy.__index = Enemy
function Enemy:new(name, location)
local data = enemyData[name]
local obj = {
objectType = "enemy",
type = name,
presetLocation = (type(location) == "string" and location) or nil,
x = (type(location) == "table" and location.x) or nil,
y = (type(location) == "table" and location.y) or nil,
r = data.size or nil,
zoom = data.zoom or 1,
speed = data.speed or nil,
health = data.health or nil,
maxHealth = data.health or nil,
stunned = false,
frameTimer = FrameTimer:new(config.frameRate,
(data.sprite and data.sprite.totalFrames) or 1),
movePattern = data.movePattern or nil,
}
setmetatable(obj, Enemy)
return obj
end
-- enemy behaviors
function Enemy:move(dt)
-- deal with stunned status
if self.stunned and self.stunned > 0 then
self.stunned = self.stunned - dt
self.physicsBody:setLinearVelocity(0, 0)
return false
elseif self.stunned and self.stunned <= 0 then
self.stunned = nil
end
-- deal with sticky status
if self.sticky and self.sticky > 0 then
self.sticky = self.sticky - dt
if not self.originalSpeed then
self.originalSpeed = self.speed
end
self.speed = self.originalSpeed / (self.sticky + 1)
elseif self.sticky and self.sticky <= 0 then
self.sticky = nil
self.speed = self.originalSpeed
end
-- deal with punched status
if self.punched and self.punched > G.time then
return
end
if self.movePattern == "vertical" then
self:moveVertical(G.player.x, dt)
else self:moveTo(G.player.x, G.player.y, dt)
end
end
function Enemy:moveTo(x, y, dt)
local dv = self.speed
local dx, dy = x - self.physicsBody:getX(), y - self.physicsBody:getY()
local dist = math.sqrt(dx * dx + dy * dy)
if dist > 0 then
dx, dy = dx / dist, dy / dist
end
local vx, vy = dx * dv, dy * dv
self.physicsBody:setLinearVelocity(vx, vy)
end
function Enemy:moveVertical(toX, dt)
self:moveTo(toX, self.y, dt)
end
function Enemy:facing()
local dx = self.x - G.player.x
local dy = self.y - G.player.y
return utils.angleToDirection(utils.atan2(dy, dx))
end
-- enemy life cycle
function Enemy:takeDamage(damage)
self.health = self.health - damage
if self:isDead() then
self:die()
end
end
function Enemy:isDead()
return self.health <= 0
end
function Enemy:die()
audio.play("hit")
-- drop loots
for i, item in ipairs(enemyData[self.type].drops) do
local temp = math.random(10) / 10
if temp <= item.chances then
local offset = (i - 1) * 5
local lootItem = Loot:new(item.type, self.x + offset, self.y + offset)
table.insert(G.currentRoom.objects.loots, lootItem)
end
end
local enemies = G.currentRoom.objects.enemies
G.BodyLifeCycleManager:destroy(self.physicsBody)
table.remove(enemies, utils.indexof(enemies, self))
end
function Enemy:onHit(shot)
if enemyData[self.type] and enemyData[self.type].onHit then
enemyData[self.type].onHit(shot)
end
end
-- enemy status
function Enemy:stun(duration)
self.stunned = duration
end
function Enemy:getSticky(duration)
self.sticky = duration
end
-- initializers
function Enemy:placeInRoom(room)
local offset = self.r*2 + G.player.r*2 + config.summonMargin
if not self.x or not self.y then
self.x, self.y = room:getLocation(self.presetLocation or "random", offset)
end
G.BodyLifeCycleManager:create(self)
end
-- entry function
function Enemy:update(dt)
self:move(dt)
self.frameTimer:update(dt)
local data = physics.getObjectPosition(self)
self.x, self.y = data.x, data.y
end
return Enemy
|