Gearing up¶
In this chapter we’ll start gearing up our hero using the equipment module!
Setting up¶
To begin, load the equipment module.
prism.loadModule("prism/extra/equipment")
Then we’ll give our player an Equipper component in player.lua.
prism.components.Equipper {
"head",
"armor",
"boots",
{ name = "ringl", category = "ring", label = "ring" },
{ name = "ringr", category = "ring", label = "ring" },
"amulet"
}
Forging the ring¶
We’ll start off with a ring of vitality that uses our HealthModifier from part 13. The
Equipment component accepts a list of required categories and a condition to apply
while equipped.
prism.registerActor("RingofVitality", function()
return prism.Actor.fromComponents {
prism.components.Name("Ring of Vitality"),
prism.components.Drawable {
index = "o",
color = prism.Color4.YELLOW,
},
prism.components.Item(),
prism.components.Equipment(
"ring",
prism.condition.Condition(prism.modifiers.HealthModifier(5))
),
prism.components.Position(),
}
end)
Note
Equipment accepts a list of categories to support cases like two-handed weapons, e.g. { "hand",
"hand" }.
We can get them to start dropping from chests by adding another entry in loot/chest.lua. We’ll
give it the same weight as wands.
{
entry = "RingofVitality",
weight = 50
}
Find (or cheat one in with Geometer, we won’t judge) a ring and pop open the inventory screen. If
you select the ring you should already see an option to equip it! You should see our health go up by
5 when we slip it on. The built in Equip action gets picked up automatically by our
InventoryActionState, and modifier was handled previously.
The armory¶
To view and unequip our items we’ll implement a new game state,
modules/game/gamestates/equipmentstate.lua. Let’s start off with our controls and defining our
familiar state fields, along with the Equipper component.
local controls = require "controls"
--- @class EquipmentState : GameState
--- @field previousState GameState
--- @overload fun(display: Display, decision: ActionDecision, level: Level, equipper: Equipper): self
local EquipmentState = spectrum.GameState:extend "EquipmentState"
In our constructor we’ll set our fields and then build up a data structure to display the slots. For
each slot, we’ll grab the label and the actor (which may be nil).
--- @param display Display
--- @param decision ActionDecision
--- @param level Level
--- @param equipper Equipper
function EquipmentState:__new(display, decision, level, equipper)
self.display = display
self.decision = decision
self.level = level
self.equipper = equipper
self.entries = {}
self.letters = {}
for i, slot in ipairs(equipper.slots or {}) do
self.entries[i] = {
slot = slot.label,
actor = equipper:get(slot.name)
}
self.letters[i] = string.char(96 + i) -- a, b, c, ...
end
end
Don’t forget to set our previousState when we load into this one!
function EquipmentState:load(previous)
self.previousState = previous
end
Next we have to draw our slots. Like usual, we start by drawing the previous state and clearing the
display. Then for each of our entries we build up a line to display, e.g. [a] head - (empty).
function EquipmentState:draw()
self.previousState:draw()
self.display:clear()
self.display:print(self.display.width - 28, 1, "Equipment", nil, nil, 2)
for i, entry in ipairs(self.entries) do
local letter = self.letters[i]
local slot = entry.slot
local name = entry.actor and prism.components.Name.get(entry.actor) or "(empty)"
local line = ("[%s] %s - %s"):format(letter, slot, name)
self.display:print(self.display.width - 28, 1 + i, line, nil, nil, 2)
if entry.actor then
self.display:putActor(self.display.width - 28 + #line, 1 + i, drawable)
end
end
self.display:draw()
end
Finally, we’ll call the built in Unequip action and pop the state when we press the
corresponding key. We’ll allow backing out of the state like our other ones.
function EquipmentState:update(dt)
controls:update()
for i, letter in ipairs(self.letters) do
if spectrum.Input.key[letter].pressed then
self.decision:setAction(
prism.actions.Unequip(self.decision.actor, self.entries[i].actor),
self.level
)
self.manager:pop()
end
end
-- No equipment interaction yet—just allow closing
if controls.equipment.pressed or controls.back.pressed then
self.manager:pop()
return
end
end
return EquipmentState
Hook it together¶
All we have left to do is hook our state up in gamelevelstate.lua.
if controls.equipment.pressed then
local equipper = owner:get(prism.components.Equipper)
if equipper then
local equipState =
spectrum.gamestates.EquipmentState(self.display, decision, self.level, equipper)
self.manager:push(equipState)
end
end
And add an entry to controls.lua.
equipment = "o",
Pop the ring on again and open the equipment state. The ring should show up in the screen, and pressing the corresponding key should remove it, lowering our max health by 5.
Next time¶
We’ll go over serialization in the next chapter, allowing us to save our game and resume it later!