diff options
| -rw-r--r-- | ai.lua | 64 | ||||
| -rw-r--r-- | collisions.lua | 34 | ||||
| -rw-r--r-- | dyl.lua | 11 | ||||
| -rw-r--r-- | dyl.p8 | 9 | ||||
| -rw-r--r-- | entity.lua | 46 | ||||
| -rw-r--r-- | map.lua | 9 | ||||
| -rw-r--r-- | math.lua | 9 | ||||
| -rw-r--r-- | util.lua | 9 |
8 files changed, 155 insertions, 36 deletions
@@ -0,0 +1,64 @@ +AiController = {} +AiController.__index = AiController + +function AiController:new(controlling) + return setmetatable({ + controlling = controlling, + time = 0 + }, AiController) +end + +function AiController:follow(target) + self.target = target +end + +function AiController:update(dt) + if self.target ~= nil and not self.target.knockback then + diff = self.target.position - self.controlling.position + self.controlling.velocity = diff:normal() * 20 + self.controlling:transition_to(States.Walk) + end + self.time += dt + if self.time >= 5 then + self:shoot() + self.time = 0 + end +end + +function gliding_gun() + _glider_damage = { amount = 2, knockback = { magnitude = 300, time = 0.080 } } + GlidingGunBuilder = EntityBuilder:new(World) + function GlidingGunBuilder:transition_state() + end + return GlidingGunBuilder:b_type(Entities.Bullet) + :b_line_of_sight(vec2(1, 0)) + :b_position(vec2(100, 100)) + :b_render_order(0) + :b_collidable() + :b_live_for(25) + :b_velocity(vec2(-40, 0)) + :b_collision_bounds(vec2(3, 4), vec2(5, 6)) + :b_add_state( + States.Active, { + animation = { + pos_y = { sequence = { 75, 76, 77, 78 }, dt = 0.10 }, + neg_y = { sequence = { 75, 76, 77, 78 }, dt = 0.10, reflect = vec2(true, true) }, + pos_x = { sequence = { 75, 76, 77, 78 }, dt = 0.10 }, + neg_x = { sequence = { 75, 76, 77, 78 }, dt = 0.10, reflect = vec2(true, true) } + } + } + ) + :b_state(States.Active) + :build() +end + +function AiController:shoot() + diff = (self.target.position - self.controlling.position):normal() * 50 + a, b, c = gliding_gun(), gliding_gun(), gliding_gun() + a.position = vec2(self.controlling.position) + a.velocity = diff:clockwise_rotate(-0.05) + b.position = vec2(self.controlling.position) + b.velocity = diff:clockwise_rotate(0.05) + c.position = vec2(self.controlling.position) + c.velocity = diff +end diff --git a/collisions.lua b/collisions.lua index f57554e..3d3ba46 100644 --- a/collisions.lua +++ b/collisions.lua @@ -1,10 +1,22 @@ function is_colliding(a, b) - ax1, bx1 = a.position.x, b.position.x - ax2, bx2 = ax1 + SPRITE_DIMS.x, bx1 + SPRITE_DIMS.y - ay1, by1 = a.position.y, b.position.y - ay2, by2 = ay1 + SPRITE_DIMS.y, by1 + SPRITE_DIMS.x - return (ax1 < bx2 and ax2 > bx1 - and ay1 < by2 and ay2 > by1) + return ( + a.top_left.x < b.bottom_right.x and a.bottom_right.x > b.top_left.x + and a.top_left.y < b.bottom_right.y and a.bottom_right.y > b.top_left.y + ) +end + +_hurts = { + vec2(Entities.Bullet, Entities.Player), + vec2(Entities.Sword, Entities.Enemy), +} +function hurts(a, b) + if b:is_in_iframe() then + return false + end + if not (a.damage and b.health) then + return false + end + return some(_hurts, function (v) return a.entity_type == v.x and b.entity_type == v.y end) end function handle_collision(a, b) @@ -12,10 +24,7 @@ function handle_collision(a, b) b:equip(a) return end - if a.damage and b.health then - if b:is_in_iframe() then - return - end + if hurts(a, b) then b:take_damage(a.line_of_sight, a.damage) end end @@ -27,7 +36,10 @@ function run_collisions() if a.id == b.id then goto continue end - if is_colliding(a, b) then + if is_colliding( + {top_left = (a.collision_bounds.top_left + a.position), bottom_right = (a.collision_bounds.bottom_right + a.position) }, + {top_left = (b.collision_bounds.top_left + b.position), bottom_right = (b.collision_bounds.bottom_right + b.position) } + ) then handle_collision(a, b) end ::continue:: @@ -152,7 +152,7 @@ end function sword() _particle_distance = 4 - _attack_burst_sec = 0.200 + _attack_burst_sec = 0.300 _sword_damage = { amount = 2, knockback = { magnitude = 300, time = 0.080 } } SwordBuilder = EntityBuilder:new(World) function SwordBuilder:transition_state() @@ -167,7 +167,7 @@ function sword() end end end - SwordBuilder:b_type(Entities.Sword) + return SwordBuilder:b_type(Entities.Sword) :b_line_of_sight(vec2(1, 0)) :b_position(vec2(-50, 20)) :b_render_order(0) @@ -200,7 +200,7 @@ function sword() } ) :b_state(States.Idle) - return SwordBuilder:build() + :build() end function block(pos, sprite) @@ -291,12 +291,13 @@ end building(vec2(10, -90)) _enemy = enemy() +_enemy.controller = AiController:new(_enemy) _sword = sword() _wife = wife() -- _bow = bow() _fire = fire() _player = player() --- _player:equip(_sword) +_enemy.controller:follow(_player) _walk_speed = 35 -- powerup increase? function handle_input() @@ -323,7 +324,7 @@ function resume() _continue = true _step_t = time() end -function _update60() +function _update() -- clear here in case we want to print to screen outside of _draw cls(0) @@ -7,6 +7,7 @@ __lua__ #include world.lua #include collisions.lua #include camera.lua +#include ai.lua #include dyl.lua ------------------------ -- don't you leave -- @@ -48,10 +49,10 @@ __gfx__ 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000030005c00050000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -04444000000990000000300005cc0c50000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -04994000009a7900000880000511c150000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -04444000009aa9000087880005111150000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00440000000990000088880005111150000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +04444000000990000000300005cc0c50000000000000000000000000000000000000000000000000000000000000800000800000000800000000000000000000 +04994000009a7900000880000511c150000000000000000000000000000000000000000000000000000000000080800000088000000080000080800000000000 +04444000009aa9000087880005111150000000000000000000000000000000000000000000000000000000000008900000890000008890000008900000000000 +00440000000990000088880005111150000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000 00000000000000000008800000555500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 @@ -7,9 +7,11 @@ Entities = { Wife = 5, Fire = 6, Door = 7, - Block = 8 + Block = 8, + Bullet = 9, } - -- TODO: Why are these all in one object. We should have like "SpriteStates", "FireStates", etc. + +-- TODO: Why are these all in one object. We should have like "SpriteStates", "FireStates", etc. States = { Walk = "walk", Idle = "idle", @@ -45,6 +47,10 @@ function Entity:kill() end function Entity:update(dt) + if self.controller ~= nil then + self.controller:update(dt) + end + self:integrate(dt) self:update_line_of_sight() @@ -69,16 +75,20 @@ function Entity:update(dt) 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) + los = self.velocity:normal():apply(abs) + n_los = self.velocity:apply(normalize_scalar) + if n_los == vec2(0, 0) then + return + end + if los.y >= los.x then + self.line_of_sight = vec2(0, n_los.y) + else + self.line_of_sight = vec2(n_los.x, 0) end end -- prevent cobblestoning during non-manhattan movement by "lagging" the sprite --- behind the actual physical position +-- behind the actual physical position. -- this part was painful. function Entity:update_sprite_position() if self.sprite_position == nil then return end @@ -119,14 +129,9 @@ function Entity:update_sprite_position() if dy < 0 then func = ceil end self.sprite_position.y += func(dy) end - - return self end function Entity:take_damage(direction, damage_spec) - if self.health == nil or self:is_in_iframe() then - return - end self.health -= damage_spec.amount if damage_spec.knockback ~= nil then self.knockback = { @@ -268,8 +273,23 @@ function EntityBuilder:b_line_of_sight(vec) self.line_of_sight = vec2(vec) return self end +function EntityBuilder:b_velocity(vec) + self.velocity = vec2(vec) + return self +end +function EntityBuilder:b_collision_bounds(top_left, bottom_right) + self.collision_bounds = { + top_left = vec2(top_left), + bottom_right = vec2(bottom_right) + } + return self +end function EntityBuilder:build() self.equipped = {} + self.collision_bounds = { + top_left = vec2(0, 0), + bottom_right = vec2(SPRITE_DIMS) + } return self.build_context.world.add(setmetatable(self, Entity)) end
\ No newline at end of file @@ -3,7 +3,7 @@ _t = { C = { name = "current", color = 7, blink = true }, K = { name = "visited", color = 6 }, M = { name = "mystery", color = 2 }, - R = { name = "restock", color = 9 }, + S = { name = "supply", color = 9 }, E = { name = "empty", color = 0 } } @@ -21,4 +21,9 @@ DisplayMap = { { _t.E, _t.E, _t.H, _t.E, _t.E }, { _t.E, _t.E, _t.E, _t.E, _t.E }, { _t.E, _t.E, _t.E, _t.E, _t.E } -}
\ No newline at end of file +} + +-- todo +-- function Map.blit() +-- for i=1,DisplayMap. +-- end
\ No newline at end of file @@ -25,7 +25,14 @@ end function Vec2:normal() local m = self:magnitude() if m == 0 then return vec2(self.x, self.y) end - return self / m + return vec2(self / m) +end + +-- 1 picorad == 2pi +function Vec2:clockwise_rotate(picorads) + local m = self:magnitude() + local a = atan2(self.x, self.y) + picorads + return vec2(m * cos(a), m * sin(a)) end function Vec2:apply(f) @@ -47,6 +47,15 @@ function qsort(a, c, l, r) return a end +function some(a, pred) + for k, v in pairs(a) do + if pred(v) then + return true + end + end + return false +end + function filter(a, pred) filtered = {} for k, v in pairs(a) do |
