From 5ab1e5c0bbed7d0958b4ba211e3085e97bb91ffd Mon Sep 17 00:00:00 2001 From: Federico Igne Date: Sun, 5 Mar 2023 21:25:43 +0000 Subject: refactor(entities): shorten component getters/checkers for entities --- raccoon.lua | 189 +++++++++++++++++++++++++++++++----------------------------- 1 file changed, 98 insertions(+), 91 deletions(-) diff --git a/raccoon.lua b/raccoon.lua index 0ada342..c0fec2f 100644 --- a/raccoon.lua +++ b/raccoon.lua @@ -104,41 +104,51 @@ end -- Entities keep track of components using a table indexed by component -- class. -- @usage --- Given an entity `e`, a component `c` of class `C` +-- Given an entity `e`, a component `c1` of class `C` -- ``` --- e:with_component(c) --- e.has_component[C] -- true --- e.get_component[C] -- c +-- e:with(c1) +-- e:has_c() == e:components[c1.__index] == true +-- e:get_c() == e:components[c1.__index] == c1 -- ``` +-- +-- @return this entity. function Entity:init() self.id = Entity.get_id() - self.has_component = {} - self.get_component = {} + self.components = {} return self end --- Add component to this entity. --- @param c the component to be added to this entity. +-- @param t the component(s) to be added to this entity. -- @return this entity. -- -- @note the modified entity is returned to allow chains of multiple calls to this method. +-- -- @usage -- ``` --- entity --- :with_component(c1) --- :with_component(c2) --- :with_component(c3) +-- entity:with(c1):with(c2):with(c3) +-- entity:with({c1,c2,c3}) -- ``` -function Entity:with_component(c) - self.has_component[c.__index] = true - self.get_component[c.__index] = c +function Entity:with(t) + if t.meta then + self.components[t.__index] = t + else + for _,c in ipairs(t) do + self.components[c.__index] = c + end + end return self end --- Remove component from this entity. --- @param c the component to be removed from this entity. +-- @param t the component(s) to be removed from this entity. -- @return this entity. -function Entity:remove_component(c) - self.has_component[c.__index] = false - self.get_component[c.__index] = nil -- possibly triggers GC +function Entity:without(t) + if t.meta then + self.components[t.__index] = nil -- possibly triggers GC + else + for _,c in ipairs(t) do + self.components[c.__index] = nil + end + end return self end @@ -147,12 +157,20 @@ end -- component. Components are what make entities interesting and -- different from each other. -- --- @note at the moment this is simply a wrapper to create a new class. --- Component-specific functions can be added in the wrapper, if --- necessary. +-- The new component keeps track of its name in a `meta` table inside of it. +-- @note for `t` table, you can use `t.meta` as a boolean check for whether `t` +-- is a component. +-- +-- Given a component "Comp", functions `has_comp` and `get_comp` are added to `Entity`. local Component = util.new_class() -function Component:init() - return util.new_class() +function Component:init(name) + local component = util.new_class() + component.meta = { name = name or "" } -- NOTE use meta as "component check" + if name then + Entity["has_"..name:lower()] = function(self) return self.components[_G[name].__index] end + Entity["get_"..name:lower()] = function(self) return self.components[_G[name].__index] end + end + return component end --- Systems. @@ -188,7 +206,7 @@ function System:init(...) -- @todo is there a better way? local selected = true for _, component in ipairs(self.components) do - if not entity.has_component[component] then + if not Entity["has_"..component.meta.name:lower()](entity) then selected = false break end @@ -199,7 +217,7 @@ function System:init(...) if cmps then -- Remove components from entity for _,c in ipairs(cmps) do - entity:remove_component(c) + entity:without(c) end else -- Remove entity @@ -212,7 +230,7 @@ function System:init(...) return cls end -local Pos = Component() +Pos = Component("Pos") function Pos:init(x,y) self.x = x or 0 self.y = y or 0 @@ -235,7 +253,7 @@ function Pos:to_screen(lpos) return self.x - lpos.x, self.y - lpos.y end -local Movement = Component() +Movement = Component("Movement") function Movement:init(accel, max) -- direction facing self.dir = true -- true: right, false: left @@ -250,19 +268,19 @@ function Movement:is_moving() return self.cur.x ~= 0 or self.cur.y ~= 0 end -local Pixel = Component() +Pixel = Component("Pixel") function Pixel:init(c) self.color = c return self end -local Sprite = Component() +Sprite = Component("Sprite") function Sprite:init(i) self.i = i return self end -local Jump = Component() +Jump = Component("Jump") function Jump:init(pwr) self.jumping = false self.cur = 0 @@ -270,7 +288,7 @@ function Jump:init(pwr) return self end -local Input = Component() +Input = Component("Input") function Input:init(up, down, left, right, a, b) self.btns = { up or 0, down or 1, left or 2, right or 3, a or 4, b or 5 } self.raw = { false, false, false, false, false, false } @@ -278,14 +296,14 @@ function Input:init(up, down, left, right, a, b) return self end -local Level = Component() +Level = Component("Level") function Level:init(id, screens) self.id = id self.screens = screens return self end -local Entities = Component() +Entities = Component("Entities") function Entities:init(list) for i,v in pairs(list) do self[i] = v @@ -300,7 +318,7 @@ function Action:init(a,b) return self end -local CircleEffect = Component() +CircleEffect = Component("CircleEffect") function CircleEffect:init(r2, r1, time, color) self.r2 = r2 self.r1 = r1 or 1 @@ -312,7 +330,7 @@ function CircleEffect:draw(x,y) circb(x,y,self.r1,self.color) end -local PosAnim = Component() +PosAnim = Component("PosAnim") function PosAnim:init(f,d) self.t = 0 self.d = d @@ -320,7 +338,7 @@ function PosAnim:init(f,d) return self end -local Particles = Component() +Particles = Component("Particles") function Particles:init(d) self.t = 0 self.d = d @@ -332,21 +350,21 @@ function Particles:spawn(pos, entities) local offset = math.random(7) -- TODO change to pos.width once implemented local speed = 0.2 + math.random() table.insert(entities, - Entity():with_component(Pixel(12)) - :with_component(Pos(pos.x + offset,pos.y)) - :with_component(PosAnim(function(t,p) p.y = p.y - speed end, 10))) + Entity():with(Pixel(12)) + :with(Pos(pos.x + offset,pos.y)) + :with(PosAnim(function(t,p) p.y = p.y - speed end, 10))) end end -local ForceField = Component() function ForceField:init(force,range,instant) +ForceField = Component("ForceField") self.force = force or 1 self.range = range or 1 self.instant = instant return self end -local Metadata = Component() +Metadata = Component("Metadata") function Metadata:init(name) self.name = name or "Unnamed" return self @@ -354,12 +372,11 @@ end local ForceFieldSystem = System(Pos,ForceField) function ForceFieldSystem:exec(entity,enemies) - local pos = entity.get_component[Pos] - local ff = entity.get_component[ForceField] + local pos = entity:get_pos() + local ff = entity:get_forcefield() for _,e in pairs(enemies) do - if e.has_component[Pos] and e.has_component[Movement] then - local pos1 = e.get_component[Pos] - local mov = e.get_component[Movement] + if e:has_pos() and ff.is_target(e) then + local pos1 = e:get_pos() local dist,vec = util.distance(pos,pos1) if dist < ff.range then if e.has_component[Metadata] then @@ -380,7 +397,7 @@ end local InputSystem = System(Input) function InputSystem:exec(entity) - local input = entity.get_component[Input] + local input = entity:get_input() for i = 1,6 do input.raw[i] = btn(input.btns[i]) input.rawp[i] = btnp(input.btns[i]) @@ -520,25 +537,25 @@ function DrawingSystem:exec(entity,level) end function DrawingSystem:draw(game) local level = game.levels[game.level] - local info = level.get_component[Level] - local pos = level.get_component[Pos] + local info = level:get_level() + local pos = level:get_pos() map(pos.x//8,pos.y//8,31,18,-(pos.x%8),-(pos.y%8),-1) DrawingSystem:run(game.entities,pos) end local LevelSystem = System(Level) function LevelSystem:exec(level, game) - local info = level.get_component[Level] - local lpos = level.get_component[Pos] - local entities = level.get_component[Entities] - local ppos = game.player.get_component[Pos] + local info = level:get_level() + local lpos = level:get_pos() + local entities = level:get_entities() + local ppos = game.player:get_pos() lpos.x = math.min( math.max(0, ppos.x - util.screen.width // 2), (info.screens - 1) * 240 ) for i,e in pairs(entities) do - if e.has_component[Pos] then - local pos = e.get_component[Pos] + if e:has_pos() then + local pos = e:get_pos() if pos.x >= lpos.x and pos.x < lpos.x + 240 and pos.y >= lpos.y and pos.y < lpos.y + 134 then table.insert(game.entities, e) @@ -550,9 +567,9 @@ end local ActionSystem = System(Pos,Input,Action) function ActionSystem:exec(entity,game) - local pos = entity.get_component[Pos] - local input = entity.get_component[Input] - local action = entity.get_component[Action] + local pos = entity:get_pos() + local input = entity:get_input() + local action = entity:get_action() if input.rawp[5] and action.a then action.a(entity,game) end @@ -563,8 +580,8 @@ end local EffectSystem = System() function EffectSystem:exec(entity) - if entity.has_component[CircleEffect] then - local effect = entity.get_component[CircleEffect] + if entity:has_circleeffect() then + local effect = entity:get_circleeffect() effect.r1 = effect.r1 + effect.step if effect.r1 > effect.r2 then return true @@ -574,8 +591,8 @@ end local PosAnimSystem = System(Pos, PosAnim) function PosAnimSystem:exec(entity) - local pos = entity.get_component[Pos] - local anim = entity.get_component[PosAnim] + local pos = entity:get_pos() + local anim = entity:get_posanim() anim.f(anim.t, pos) anim.t = anim.t + 1 if anim.d and anim.t > anim.d then @@ -585,8 +602,8 @@ end local ParticlesSystem = System(Pos, Particles) function ParticlesSystem:exec(entity, game) - local pos = entity.get_component[Pos] - local parts = entity.get_component[Particles] + local pos = entity:get_pos() + local parts = entity:get_particles() parts:spawn(pos,game.entities) parts.t = parts.t + 1 if parts.d and parts.t > parts.d then @@ -594,34 +611,24 @@ function ParticlesSystem:exec(entity, game) end end -local player = Entity() - :with_component(Metadata("Giulia")) - :with_component(Pos(100,10)) - :with_component(Movement()) - :with_component(Jump(10)) - :with_component(Sprite(256)) - :with_component(Input(0,1,2,3,4,5)) - :with_component(Action( - function(entity,game) - --local lpos = game.levels[game.level].get_component[Pos] - local pos = entity.get_component[Pos]--:to_screen(lpos) - table.insert( - game.entities, - Entity():with_component(Pos(pos.x,pos.y)):with_component(ForceField(5,10,true)):with_component(CircleEffect(20))) - end, - nil)) +local player = Entity():with({ + Metadata("Giulia"), Sprite(256), + Pos(100,10), Health(100), Status(), + Movement(), Jump(10), + Input(0,1,2,3,4,5), Action(Action.forcefield,nil) +}) local level1_entities = { -- Turists - Entity():with_component(Metadata("Tourist1")):with_component(Pos(16*8,11*8)):with_component(Sprite(272)):with_component(Movement()), - Entity():with_component(Metadata("Tourist2")):with_component(Pos(47*8, 8*8)):with_component(Sprite(272)):with_component(Movement()), - Entity():with_component(Metadata("Tourist3")):with_component(Pos(85*8, 7*8)):with_component(Sprite(272)):with_component(Movement()), + Entity():with({Metadata("Tourist1"), Sprite(272), Pos(16*8,11*8), Health(5), Status(), Movement(), MovementAI(), Target(player:get_pos())}), + Entity():with({Metadata("Tourist2"), Sprite(272), Pos(47*8, 8*8), Health(5), Status(), Movement(), MovementAI()}), + Entity():with({Metadata("Tourist3"), Sprite(272), Pos(85*8, 7*8), Health(5), Status(), Movement(), MovementAI()}), -- Waypoints - Entity():with_component(Pos(15*8,11*8)):with_component(Sprite(288)) - :with_component(PosAnim(function(t,p) p.y = p.y - math.sin(t/6)/3 end)) - :with_component(Particles()), - Entity():with_component(Pos(48*8, 8*8)):with_component(Sprite(288)) - :with_component(PosAnim(function(t,p) p.y = p.y - math.sin(t/6)/3 end)), + Entity():with(Pos(15*8,11*8)):with(Sprite(288)) + :with(PosAnim(function(t,p) p.y = p.y - math.sin(t/6)/3 end)) + :with(Particles()), + Entity():with(Pos(48*8, 8*8)):with(Sprite(288)) + :with(PosAnim(function(t,p) p.y = p.y - math.sin(t/6)/3 end)), } -- TODO turn this into something more structured, containing everything related @@ -632,9 +639,9 @@ local game = { level = 1, levels = { Entity() - :with_component(Level(0,3)) - :with_component(Pos(0,0)) - :with_component(Entities(level1_entities)) + :with(Level(0,3)) + :with(Pos(0,0)) + :with(Entities(level1_entities)) } } -- cgit v1.2.3