Taking flight¶
Unfortunately for kobolds, they can’t fly. In this section we’re going to create a
System that plunges actors into the abyss if they’re unable to fly.
Kicking kobolds into the abyss
Creating the void component¶
First we’ll create a component to indicate cells where you can fall.
Navigate to
modules/game/componentsCreate a new file called
void.lua
Put the following into void.lua:
--- @class Void : Component
local Void = prism.Component:extend "Void"
return Void
Adding void to our pit¶
Navigate to
modules/game/cells/pit.lua
Add the following line to its components:
prism.components.Void()
Creating the fall action¶
Next we’re going to create an lua:class:Action to represent an actor falling.
Navigate to the
modules/game/actionsdirectory.Create a new file called
fall.lua.Define the
Fallaction:
--- @class Fall : Action
--- @overload fun(owner: Actor): Fall
local Fall = prism.Action:extend "Fall"
return Fall
To perform the fall itself, all we’re going to do is remove the actor:
function Fall:perform(level)
level:removeActor(self.owner) -- into the depths with you!
end
Determining whether we should fall is a bit more complex. We need the following to be true:
The cell we’re standing on has the void component, which we can check simply:
function Fall:canPerform(level)
local x, y = self.owner:getPosition():decompose()
local cell = level:getCell(x, y)
-- We can only fall on cells that are voids.
if not cell:has(prism.components.Void) then return false end
And that we can’t move through the cell. We can get the cell’s collision mask and compare it with
our own with Collision.checkBitmaskOverlap() to accomplish that check. If the actor
doesn’t have a Mover component we’ll default to falling.
local cellMask = cell:getCollisionMask()
local mover = self.owner:get(prism.components.Mover)
if mover then
-- We have a Void component on the cell. If the actor CAN'T move here
-- then they fall.
return not prism.Collision.checkBitmaskOverlap(cellMask, mover.mask)
end
return true
end
return Fall
Complete fall.lua
--- @class Fall : Action
--- @overload fun(owner: Actor): Fall
local Fall = prism.Action:extend "Fall"
function Fall:canPerform(level)
local x, y = self.owner:getPosition():decompose()
local cell = level:getCell(x, y)
-- We can only fall on cells that are voids.
if not cell:has(prism.components.Void) then return false end
local cellMask = cell:getCollisionMask()
local mover = self.owner:get(prism.components.Mover)
if mover then
-- We have a Void component on the cell. If the actor CAN'T move here
-- then they fall.
return not prism.Collision.checkBitmaskOverlap(cellMask, mover.mask)
end
return true
end
function Fall:perform(level)
level:removeActor(self.owner)
end
return Fall
Triggering fall with a system¶
We’ve defined a fall action, but kobolds aren’t exactly volunteering to fall into the void. Let’s
create a System to make sure things fall when they ought to. Create a new directory
modules/game/systems and a new file fallsystem.lua.
We want the actor to fall immediately when they land on a valid tile, so we’ll use the
System:onMove() callback to apply the fall action whenever valid.
Level:tryPerform() will perform the action if it’s valid, but won’t error if it’s not.
--- @class FallSystem : System
local FallSystem = prism.System:extend "FallSystem"
function FallSystem:onMove(level, actor)
level:tryPerform(prism.actions.Fall(actor))
end
return FallSystem
Note
See System for a listing of events you can hook into!
Registering the Fall system¶
Navigate back to gamelevelstate.lua and on line 29 you’ll see where we register systems. Go
ahead and add prism.systems.Fall() to the bottom of the list like so.
builder:addSystems(
prism.systems.SensesSystem(),
prism.systems.SightSystem(),
prism.systems.FallSystem()
)
Wrapping up¶
With our FallSystem in place, kobolds and other unfortunate creatures will now tumble into the
void if they end their turn standing on a pit they can’t fly over. We’ve used a component to tag
dangerous tiles, an action to represent involuntary movement, and a system to enforce game logic
based on actor movement.
In the next section of the tutorial, we’ll dive into something a little more active: combat. We’ll set up a health component, and teach actors how to attack.