summaryrefslogtreecommitdiff
path: root/entity.lua
diff options
context:
space:
mode:
Diffstat (limited to 'entity.lua')
-rw-r--r--entity.lua208
1 files changed, 208 insertions, 0 deletions
diff --git a/entity.lua b/entity.lua
new file mode 100644
index 0000000..dfad84f
--- /dev/null
+++ b/entity.lua
@@ -0,0 +1,208 @@
+Entities = {
+ Player = 0,
+ Sword = 1,
+ Enemy = 2
+}
+States = {
+ Walk = "walk",
+ Idle = "idle"
+}
+
+
+_id = 0
+function _next_id()
+ i = _id
+ _id += 1
+ return i
+end
+
+Entity = {}
+Entity.__index = Entity
+function Entity:b_add_state(name, state)
+ if self.states == nil then self.states = {} end
+ self.states[name] = state
+ if self.state == nil then self.state = name end
+ return self
+end
+function Entity:b_state(name)
+ assert(self.states[name] != nil)
+ self.state = name
+ return self
+end
+function Entity:b_render_order(ord)
+ self.render_order = ord
+ return self
+end
+function Entity:b_position(vec)
+ self.position = vec2(vec)
+ return self
+end
+function Entity:b_sprite_position(vec)
+ self.sprite_position = vec2(vec)
+ return self
+end
+function Entity:b_type(entity_type)
+ self.entity_type = type
+ return self
+end
+function Entity:b_equipped(equipped)
+ self.equipped = equipped
+ return self
+end
+function Entity:b_collidable()
+ self.collision = true
+ return self
+end
+
+function Entity:transition_to(state_name)
+ if self:transition(self.state, state_name) then
+ self.state = state_name
+ self.state_stopwatch = 0
+ end
+ return self
+end
+
+function Entity:transition(from, to)
+ if from == States.Walk and to == States.Idle or to == States.Idle and from == States.Walk then
+ return true
+ end
+ return false
+end
+
+function Entity:update(dt)
+ if self.state_stopwatch == nil then
+ self.state_stopwatch = 0
+ end
+ self.state_stopwatch += dt
+ self:integrate(dt)
+
+ self:update_sprite_position()
+ self:update_line_of_sight()
+
+ if (self.velocity.x == 0 and self.velocity.x == self.velocity.y) then
+ self:transition_to(States.Idle)
+ else
+ self:transition_to(States.Walk)
+ end
+
+ if (self.equipped != nil) then
+ parent = self
+ foreach(self.equipped, function (e) e:equipped_from(parent) end)
+ end
+end
+
+function Entity:update_line_of_sight()
+ nv = self.velocity:apply(normalize_scalar)
+ if nv.y != 0 then self.line_of_sight = vec2(0, nv.y)
+ elseif nv.x != 0 then self.line_of_sight = vec2(nv.x, 0)
+ else self.line_of_sight = vec2(0, 0) end
+end
+
+-- prevent cobblestoning during non-manhattan movement by "lagging" the sprite
+-- behind the actual physical position
+function Entity:update_sprite_position()
+ if self.sprite_position == nil then return end
+
+ -- step in only x or y. trivial
+ if self.velocity == nil or self.velocity.y == 0 or self.velocity.x == 0 then
+ self.sprite_position.x = self.position.x
+ self.sprite_position.y = self.position.y
+ return
+ end
+
+ dpos = self.position - self.sprite_position
+ dx, dy = dpos.x, dpos.y
+ adx, ady = abs(dx), abs(dy)
+
+ yslow = abs(self.velocity.y) < abs(self.velocity.x)
+ xslow = abs(self.velocity.x) <= abs(self.velocity.y)
+
+ pushx, pushy = false, false
+ jerkdelta = 1
+ if adx >= jerkdelta and xslow and ady >= 1 then
+ pushx, pushy = true, true
+ elseif ady >= jerkdelta and yslow and adx >= 1 then
+ pushx, pushy = true, true
+ elseif xslow and ady >= 1 then
+ pushy = true
+ elseif yslow and adx >= 1 then
+ pushx = true
+ end
+
+ if pushx then
+ func = flr
+ if dx < 0 then func = ceil end
+ self.sprite_position.x += func(dx)
+ end
+ if pushy then
+ func = flr
+ if dy < 0 then func = ceil end
+ self.sprite_position.y += func(dy)
+ end
+
+ return self
+end
+
+function Entity:integrate(dt)
+ if (self.velocity != nil) then
+ self.position = self.position + (self.velocity * dt)
+ end
+end
+
+_equip_distance = 6
+function Entity:equipped_from(parent)
+ self.line_of_sight = vec2(parent.line_of_sight)
+
+ offset = (parent.line_of_sight * _equip_distance)
+
+ self.position = parent.position + offset
+ self.sprite_position = parent.sprite_position + offset
+end
+
+function _get_frame(anim, t)
+ len = #anim.seq
+ frames_passed = (t / anim.dt)
+ idx = flr(1 + (frames_passed % len))
+ return anim.seq[idx]
+end
+
+_animation_keys = {"neg", "", "pos"}
+function _get_animation_key(line_of_sight)
+ n_line_of_sight = line_of_sight:apply(normalize_scalar)
+ if n_line_of_sight.y ~= 0 then
+ return _animation_keys[n_line_of_sight.y + 2] .. "_y"
+ end
+ return _animation_keys[n_line_of_sight.x + 2] .. "_x"
+end
+
+function Entity:render()
+ pos = self.sprite_position or self.position
+
+ animation = self.states[self.state].animation
+ if (animation == nil) then return end
+ key = _get_animation_key(self.line_of_sight)
+ animation = animation[key] or animation
+ assert(animation[key])
+
+ frame = _get_frame(animation.sequence, self.state_t)
+ reflection = animation.reflection or vec2(false, false)
+ spr(
+ frame,
+ entity.sprite_position.x, entity.sprite_position.y,
+ 1, 1,
+ reflection.x, reflection.y
+ )
+end
+
+function entity()
+ id = _next_id()
+ World.add(setmetatable(
+ {
+ id = id,
+ position = vec2(0,0),
+ velocity = vec2(0,0),
+ collision = false
+ }, Entity
+ ))
+ return World[id]
+end