From 078eca583eec21d317e931c84db8f084bef4305d Mon Sep 17 00:00:00 2001 From: Elizabeth Alexander Hunt Date: Wed, 22 Apr 2026 15:58:41 -0700 Subject: Snapshot --- entity.lua | 208 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 208 insertions(+) create mode 100644 entity.lua (limited to 'entity.lua') 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 -- cgit v1.3