summaryrefslogtreecommitdiff
path: root/objects/Enemy.lua
blob: 763708299809912ed3474b6e47de34c6d91e3279 (plain)
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