T4 Modules Howto Guide/Inventory Dialogs
Contents
Inventory and Equipment Dialogs
This section assumes that objects and inventory has already been enabled by following the instructions in the Objects guide.
Picking up and dropping items
To start, lets add the ability to actually pickup and drop items.
First, in your Game.lua file, find the setupCommands() method. Inside this method add the following entry to the self.key:addBinds command:
PICKUP_FLOOR = function() if self.player.no_inventory_access then return end self.player:playerPickup() end, DROP_FLOOR = function() if self.player.no_inventory_access then return end self.player:playerDrop() end,
This allows the pickup and drop commands to be mapped, which causes the player:playerPickup() or player:playerDrop() methods to execute. Now lets add these methods to the Player.lua file:
function _M:playerPickup() -- If 2 or more objects, display a pickup dialog, otherwise just picks up if game.level.map:getObject(self.x, self.y, 2) then local d d = self:showPickupFloor("Pickup", nil, function(o, item) self:pickupFloor(item, true) self.changed = true d:used() end) else self:pickupFloor(1, true) self:sortInven() self:useEnergy() self.changed = true end end function _M:playerDrop() local inven = self:getInven(self.INVEN_INVEN) local d d = self:showInventory("Drop object", inven, nil, function(o, item) self:dropFloor(inven, item, true, true) self:sortInven(inven) self:useEnergy() self.changed = true return true end) end
Now you can play and pick up and drop your items!
Inventory and equipment Screen
Now that you can pick up and drop items, you need to be able to obtain an inventory list and equip items.
First, as above, add the following to the key mappings in Game.lua:
SHOW_INVENTORY = function() if self.player.no_inventory_access then return end local d d = self.player:showEquipInven("Inventory", nil, function(o, inven, item, button, event) if not o then return end local ud = require("mod.dialogs.UseItemDialog").new(event == "button", self.player, o, item, inven, function(_, _, _, stop) d:generate() d:generateList() if stop then self:unregisterDialog(d) end end) self:registerDialog(ud) end) end,
Once done, you can try run the game and enjoy your new inventory listing. You may however notice that the game will crash if you try to actually use or equip anything in your inventory. This is because we have not yet defined the UseItemDialog.
In your module's Dialogs directory, create a new file file UseItemDialog.lua and paste the following:
require "engine.class" require "engine.ui.Dialog" local List = require "engine.ui.List" local Savefile = require "engine.Savefile" local Map = require "engine.Map" module(..., package.seeall, class.inherit(engine.ui.Dialog)) function _M:init(center_mouse, actor, object, item, inven, onuse) self.actor = actor self.object = object self.inven = inven self.item = item self.onuse = onuse self:generateList() local name = object:getName() local w = self.font_bold:size(name) engine.ui.Dialog.init(self, name, 1, 1) local list = List.new{width=math.max(w, self.max) + 10, nb_items=#self.list, list=self.list, fct=function(item) self:use(item) end} self:loadUI{ {left=0, top=0, ui=list}, } self:setupUI(true, true, function(w, h) if center_mouse then local mx, my = core.mouse.get() self.force_x = mx - w / 2 self.force_y = my - (self.h - self.ih + list.fh / 3) end end) self.key:addBinds{ EXIT = function() game:unregisterDialog(self) end, } end function _M:use(item) if not item then return end game:unregisterDialog(self) local act = item.action --if act == "use" then --self.actor:playerUseItem(self.object, self.item, self.inven, self.onuse) --self.onuse(self.inven, self.item, self.object, true) --else if act == "drop" then self.actor:doDrop(self.inven, self.item, function() self.onuse(self.inven, self.item, self.object, false) end) elseif act == "wear" then self.actor:doWear(self.inven, self.item, self.object) self.onuse(self.inven, self.item, self.object, false) elseif act == "takeoff" then self.actor:doTakeoff(self.inven, self.item, self.object) self.onuse(self.inven, self.item, self.object, false) end end function _M:generateList() local list = {} --if self.object:canUseObject() then list[#list+1] = {name="Use", action="use"} end if self.inven == self.actor.INVEN_INVEN and self.object:wornInven() and self.actor:getInven(self.object:wornInven()) then list[#list+1] = {name="Wield/Wear", action="wear"} end if self.inven ~= self.actor.INVEN_INVEN and self.object:wornInven() then list[#list+1] = {name="Take off", action="takeoff"} end if self.inven == self.actor.INVEN_INVEN then list[#list+1] = {name="Drop", action="drop"} end self.max = 0 self.maxh = 0 for i, v in ipairs(list) do local w, h = self.font:size(v.name) self.max = math.max(self.max, w) self.maxh = self.maxh + self.font_h end self.list = list end
In your Player.lua class file paste the following two functions:
function _M:doDrop(inven, item, on_done, nb) if self.no_inventory_access then return end if nb == nil or nb >= self:getInven(inven)[item]:getNumber() then self:dropFloor(inven, item, true, true) else for i = 1, nb do self:dropFloor(inven, item, true) end end self:sortInven(inven) self:useEnergy() self.changed = true if on_done then on_done() end end function _M:doWear(inven, item, o) self:removeObject(inven, item, true) local ro = self:wearObject(o, true, true) if ro then if type(ro) == "table" then self:addObject(inven, ro) end elseif not ro then self:addObject(inven, o) end self:sortInven() self:useEnergy() self.changed = true end function _M:doTakeoff(inven, item, o) if self:takeoffObject(inven, item) then self:addObject(self.INVEN_INVEN, o) end self:sortInven() self:useEnergy() self.changed = true end
You should now be able to drop items from within the inventory screen as well as equip and takeoff items.
Consumable and activatable items
Right now you can only "wear", "takeoff", "pickup" and "drop" items. This section deals with allowing objects to be "used".
First, return to the UseItemDialog.lua file in the dialogs directory and uncomment the commented lines in the use and generateList methods.
The onuse flag refers to whether the inventory screen should be closed or not upon using an item.
Next you need to add a new method to the object class to allow the code to check whether an object can be used. If you have not already, this will involve actually creating an objects class. Paste the following into the Object.lua class file:
require "engine.class" require "engine.Object" require "engine.interface.ObjectActivable" local Stats = require("engine.interface.ActorStats") local Talents = require("engine.interface.ActorTalents") local DamageType = require("engine.DamageType") module(..., package.seeall, class.inherit( engine.Object, engine.interface.ObjectActivable, engine.interface.ActorTalents )) function _M:init(t, no_default) t.encumber = t.encumber or 0 engine.Object.init(self, t, no_default) engine.interface.ObjectActivable.init(self, t) engine.interface.ActorTalents.init(self, t) end function _M:canAct() if self.power_regen or self.use_talent then return true end return false end function _M:act() self:regenPower() self:cooldownTalents() self:useEnergy() end function _M:use(who, typ, inven, item) inven = who:getInven(inven) if self:wornInven() and not self.wielded and not self.use_no_wear then game.logPlayer(who, "You must wear this object to use it!") return end local types = {} if self:canUseObject() then types[#types+1] = "use" end if not typ and #types == 1 then typ = types[1] end if typ == "use" then local ret = {self:useObject(who, inven, item)} if ret[1] then if self.use_sound then game:playSoundNear(who, self.use_sound) end who:useEnergy(game.energy_to_act * (inven.use_speed or 1)) end return unpack(ret) end end
You will also need to modify you Game.lua class: In the loaded function modify the Zone:setup call to also include your new Object class:
Zone:setup{npc_class="mod.class.NPC", grid_class="mod.class.Grid", object_class="mod.class.Object"}
Also add the following function to the Player.lua class:
function _M:playerUseItem(object, item, inven) local use_fct = function(o, inven, item) if not o then return end local co = coroutine.create(function() self.changed = true local ret = o:use(self, nil, inven, item) or {} if not ret.used then return end if ret.destroy then if o.multicharge and o.multicharge > 1 then o.multicharge = o.multicharge - 1 else local _, del = self:removeObject(self:getInven(inven), item) if del then game.log("You have no more %s.", o:getName{no_count=true, do_color=true}) else game.log("You have %s.", o:getName{do_color=true}) end self:sortInven(self:getInven(inven)) end end end) local ok, ret = coroutine.resume(co) if not ok and ret then print(debug.traceback(co)) error(ret) end return true end if object and item then return use_fct(object, inven, item) end local titleupdator = self:getEncumberTitleUpdator("Use object") self:showEquipInven(titleupdator(), function(o) return o:canUseObject() end, use_fct ) end
Congratulations, you now have usable items.
Now all you need to do is define your items usable.
For consumable objects like potions:
use_simple = { name = "power name", use = function(self,who) <Code for power use here> return {used = true, destroy = true} end },
For an object like a wand with limited charges:
multicharge = <charges>, use_simple = { name = "power name", use = function(self,who) <Code for power use here> return {used = true, destroy = true} end },
For a magical object with regenerating power:
max_power = <max power>, power_regen = <power regained per turn>, use_power = {name = "power name", power = <power use per charge>, use = function(self, who) <Code for power use here> return true end },
For a magical object that activates a talent on use:
max_power = <max power>, power_regen = <power regained per turn>, use_talent = {id = Talents.T_SOMETALENT, level = <level of talent>, power = <power use per charge> },
Go back to T4 Modules Howto Guide