T4 Modules Howto Guide/Character Sheet Dialogs
Contents
Character Sheet
Player Display
A Player Display is the sidebar usually on the left that gives you a quick summary of your stats and status.
In order to create your own Player Display, create and copy the following code into your module's /class/PlayerDisplay.lua file.
require "engine.class" module(..., package.seeall, class.make) function _M:init(x, y, w, h, bgcolor, font, size) self.display_x = x self.display_y = y self.w, self.h = w, h self.bgcolor = bgcolor self.font = core.display.newFont(font, size) self:resize(x, y, w, h) end --- Resize the display area function _M:resize(x, y, w, h) self.display_x, self.display_y = x, y self.w, self.h = w, h self.font_h = self.font:lineSkip() self.font_w = self.font:size(" ") self.bars_x = self.font_w * 9 self.bars_w = self.w - self.bars_x - 5 self.surface = core.display.newSurface(w, h) self.surface_line = core.display.newSurface(w, self.font_h) self.texture = self.surface:glTexture() self.items = {} end function _M:makeTexture(text, x, y, r, g, b, max_w) local s = self.surface_line s:erase(0, 0, 0, 0) s:drawColorStringBlended(self.font, text, 0, 0, r, g, b, true, max_w) local item = { s:glTexture() } item.x = x item.y = y item.w = self.w item.h = self.font_h self.items[#self.items+1] = item return item.w, item.h, item.x, item.y end function _M:makeTextureBar(text, nfmt, val, max, reg, x, y, r, g, b, bar_col, bar_bgcol) local s = self.surface_line s:erase(0, 0, 0, 0) s:erase(bar_bgcol.r, bar_bgcol.g, bar_bgcol.b, 255, self.bars_x, h, self.bars_w, self.font_h) s:erase(bar_col.r, bar_col.g, bar_col.b, 255, self.bars_x, h, self.bars_w * val / max, self.font_h) s:drawColorStringBlended(self.font, text, 0, 0, r, g, b, true) s:drawColorStringBlended(self.font, (nfmt or "%d/%d"):format(val, max), self.bars_x + 5, 0, r, g, b) if reg and reg ~= 0 then local reg_txt = (" (%s%.2f)"):format((reg > 0 and "+") or "",reg) local reg_txt_w = self.font:size(reg_txt) s:drawColorStringBlended(self.font, reg_txt, self.bars_x + self.bars_w - reg_txt_w - 3, 0, r, g, b) end local item = { s:glTexture() } item.x = x item.y = y item.w = self.w item.h = self.font_h self.items[#self.items+1] = item return item.w, item.h, item.x, item.y end -- Displays the stats function _M:display() local player = game.player if not player or not player.changed or not game.level then return end self.items = {} local h = 6 local x = 2 self.font:setStyle("bold") self:makeTexture(("%s#{normal}#"):format(player.name), 0, h, colors.GOLD.r, colors.GOLD.g, colors.GOLD.b, self.w) h = h + self.font_h self.font:setStyle("normal") self:makeTexture(("Str/Dex/Con: #00ff00#%3d/%3d/%3d"):format(player:getStr(), player:getDex(), player:getCon()), x, h, 255, 255, 255) h = h + self.font_h h = h + self.font_h self:makeTextureBar("#c00000#Life:", nil, player.life, player.max_life, player.life_regen * util.bound((player.healing_factor or 1), 0, 2.5), x, h, 255, 255, 255, colors.DARK_RED, colors.VERY_DARK_RED) h = h + self.font_h self:makeTextureBar("#ffcc80#Power:", nil, player:getPower(), player.max_power, player.power_regen, x, h, 255, 255, 255, colors.DARK_BLUE, {r=colors.DARK_BLUE.r/2, g=colors.DARK_BLUE.g/2, b=colors.DARK_BLUE.b/2}) h = h + self.font_h if savefile_pipe.saving then h = h + self.font_h self:makeTextureBar("Saving:", "%d%%", 100 * savefile_pipe.current_nb / savefile_pipe.total_nb, 100, nil, x, h, colors.YELLOW.r, colors.YELLOW.g, colors.YELLOW.b, {r=49, g=54,b=42},{r=17, g=19, b=0}) h = h + self.font_h end end function _M:toScreen(nb_keyframes) self:display() core.display.drawQuad(self.display_x, self.display_y, self.w, self.h, 100,100,100, 200) for i = 1, #self.items do local item = self.items[i] if type(item) == "table" then if item.glow then local glow = (1+math.sin(core.game.getTime() / 500)) / 2 * 100 + 120 item[1]:toScreenFull(self.display_x + item.x, self.display_y + item.y, item.w, item.h, item[2], item[3], 1, 1, 1, glow / 255) else item[1]:toScreenFull(self.display_x + item.x, self.display_y + item.y, item.w, item.h, item[2], item[3]) end else item(self.display_x, self.display_y) end end end
The class is now defined, but your game still has to be told to display it. Add the following near to the top of your Game.lua file:
local PlayerDisplay = require "mod.class.PlayerDisplay"
Then in the run function add
self.player_display = PlayerDisplay.new(0, 0, 200, self.h, {30,30,0}, "/data/font/VeraMono.ttf", 12)
Also modify the LogFlasher's x position so it does not render underneath the PlayerDisplay. Change it as following:
self.flash = LogFlasher.new(208, 0, self.w, 20, nil, nil, nil, {255,255,255}, {0,0,0})
And finally find the display method and insert the following line:
self.player_display:toScreen(nb_keyframe)
And you are done, you should now have a working PlayerDisplay that you can modify to suit your needs.
Character Sheet
In the example module, you may notice that pressing 'C' causes an exception to occur due to a missing /dialogs/CharacterSheet.lua
Following is two example CharacterSheet Dialogs you can use in your own module. Simply paste one of these into your /dialogs/CharacterSheet.lua, start the game and press 'C' to see how it looks.
Simple example
This example is a very basic bare-bones Character sheet that can be customized any way necessarily. Simply update the drawDialog method with the information you wish to display of your own module.
require "engine.class" local Dialog = require "engine.ui.Dialog" local Talents = require "engine.interface.ActorTalents" local SurfaceZone = require "engine.ui.SurfaceZone" local Stats = require "engine.interface.ActorStats" local Textzone = require "engine.ui.Textzone" module(..., package.seeall, class.inherit(Dialog)) function _M:init(actor) self.actor = actor self.font = core.display.newFont("/data/font/VeraMono.ttf", 12) Dialog.init(self, "Character Sheet: "..self.actor.name, math.max(game.w * 0.7, 950), 500, nil, nil, font) self.c_desc = SurfaceZone.new{width=self.iw, height=self.ih,alpha=0} self:loadUI{ {left=0, top=0, ui=self.c_desc}, } self:setupUI() self:drawDialog() self.key:addBind("EXIT", function() cs_player_dup = game.player:clone() game:unregisterDialog(self) end) end function _M:drawDialog() local player = self.actor local s = self.c_desc.s s:erase(0,0,0,0) local h = 0 local w = 0 h = 0 w = 0 s:drawStringBlended(self.font, "Name : "..(player.name or "Unnamed"), w, h, 255, 255, 255, true) h = h + self.font_h s:drawStringBlended(self.font, "Role : "..(player.descriptor.role or player.type:capitalize()), w, h, 255, 255, 255, true) h = h + self.font_h h = h + self.font_h -- Adds an empty row h = 0 w = self.w * 0.25 -- start on second column s:drawStringBlended(self.font, "STR : "..(player:getStr()), w, h, 0, 255, 255, true) h = h + self.font_h s:drawStringBlended(self.font, "DEX : "..(player:getDex()), w, h, 255, 0, 255, true) h = h + self.font_h s:drawStringBlended(self.font, "CON : "..(player:getCon()), w, h, 255, 255, 0, true) h = h + self.font_h self.c_desc:generate() self.changed = false end
Complex example
This example is a cut down version of the character sheet used in ToME. It includes tabs, tooltips and the ability to dump your character to a text file. Simply update the drawDialog method with the information you wish to display of your own module.
To see how the ToME character sheet performs some of its more advanced functions, refer to its source code.
require "engine.class" local Dialog = require "engine.ui.Dialog" local Talents = require "engine.interface.ActorTalents" local Tab = require "engine.ui.Tab" local SurfaceZone = require "engine.ui.SurfaceZone" local Separator = require "engine.ui.Separator" local Stats = require "engine.interface.ActorStats" local Textzone = require "engine.ui.Textzone" module(..., package.seeall, class.inherit(Dialog)) function _M:init(actor) self.actor = actor Dialog.init(self, "Character Sheet: "..self.actor.name, math.max(game.w * 0.7, 950), 500) self.font = core.display.newFont("/data/font/VeraMono.ttf", 12) self.font_h = self.font:lineSkip() self.c_general = Tab.new{title="General", default=true, fct=function() end, on_change=function(s) if s then self:switchTo("general") end end} self.c_attack = Tab.new{title="Attack", default=false, fct=function() end, on_change=function(s) if s then self:switchTo("attack") end end} self.c_defence = Tab.new{title="Defense", default=false, fct=function() end, on_change=function(s) if s then self:switchTo("defence") end end} local tw, th = self.font_bold:size(self.title) self.vs = Separator.new{dir="vertical", size=self.iw} self.c_tut = Textzone.new{width=self.iw * 0.6, auto_height=true, no_color_bleed=true, font = self.font, text=[[ Keyboard: #00FF00#'d'#LAST# to save character dump. #00FF00#TAB key#LAST# to switch between tabs. Mouse: Hover over stat for info ]]} self.c_desc = SurfaceZone.new{width=self.iw, height=self.ih - self.c_general.h - self.vs.h - self.c_tut.h,alpha=0} self.hoffset = 17 + self.c_tut.h + self.vs.h + self.c_general.h self:loadUI{ {left=0, top=0, ui=self.c_tut}, {left=15, top=self.c_tut.h, ui=self.c_general}, {left=15+self.c_general.w, top=self.c_tut.h, ui=self.c_attack}, {left=15+self.c_general.w+self.c_attack.w, top=self.c_tut.h, ui=self.c_defence}, {left=0, top=self.c_tut.h + self.c_general.h, ui=self.vs}, {left=0, top=self.c_tut.h + self.c_general.h + 5 + self.vs.h, ui=self.c_desc}, } self:setFocus(self.c_general) self:setupUI() self:switchTo("general") self:updateKeys() end function _M:switchTo(kind) self:drawDialog(kind, cs_player_dup) if kind == "general" then self.c_attack.selected = false self.c_defence.selected = false elseif kind == "attack" then self.c_general.selected = false self.c_defence.selected = false elseif kind == "defence" then self.c_attack.selected = false self.c_general.selected = false end self:updateKeys() end function _M:updateKeys() self.key:addCommands{ _TAB = function() self:tabTabs() end, __TEXTINPUT = function(c) if c == 'd' or c == 'D' then self:dump() end end, } self.key:addBinds{ EXIT = function() cs_player_dup = game.player:clone() game:unregisterDialog(self) end, } end function _M:tabTabs() if self.c_general.selected == true then self.c_attack:select() elseif self.c_attack.selected == true then self.c_defence:select() elseif self.c_defence.selected == true then self.c_general:select() end end function _M:mouseTooltip(text, _, _, _, w, h, x, y) self:mouseZones({ { x=x, y=y+self.hoffset, w=w, h=h, fct=function(button) game.tooltip_x, game.tooltip_y = 1, 1; game.tooltip:displayAtMap(nil, nil, game.w, game.h, text) end}, }, true) end function _M:mouseZones(t, no_new) -- Offset the x and y with the window position and window title if not t.norestrict then for i, z in ipairs(t) do if not z.norestrict then z.x = z.x + self.display_x + 5 z.y = z.y + self.display_y + 20 + 3 end end end if not no_new then self.mouse = engine.Mouse.new() end self.mouse:registerZones(t) end function _M:drawDialog(kind) self.mouse:reset() self:setupUI() local player = self.actor local s = self.c_desc.s s:erase(0,0,0,0) local h = 0 local w = 0 if kind == "general" then h = 0 w = 0 s:drawStringBlended(self.font, "Name : "..(player.name or "Unnamed"), w, h, 255, 255, 255, true) h = h + self.font_h s:drawStringBlended(self.font, "Role : "..(player.descriptor.role or player.type:capitalize()), w, h, 255, 255, 255, true) h = h + self.font_h h = h + self.font_h -- Adds an empty row -- Draw some text with an attatched tooltip self:mouseTooltip([[#GOLD#A Tooltip!!#LAST#]], s:drawColorStringBlended(self.font, ("#c00000#Life: #00ff00#%d/%d"):format(player.life, player.max_life), w, h, 255, 255, 255, true)) h = h + self.font_h h = 0 w = self.w * 0.25 -- start on second column s:drawStringBlended(self.font, "STR : "..(player:getStr()), w, h, 0, 255, 255, true) h = h + self.font_h s:drawStringBlended(self.font, "DEX : "..(player:getDex()), w, h, 255, 0, 255, true) h = h + self.font_h s:drawStringBlended(self.font, "CON : "..(player:getCon()), w, h, 255, 255, 0, true) h = h + self.font_h elseif kind=="attack" then h = 0 w = 0 -- draw the attack tab here elseif kind=="defence" then h = 0 w = 0 -- draw the defence tab here end self.c_desc:generate() self.changed = false end function _M:dump() local player = self.actor fs.mkdir("/character-dumps") local file = "/character-dumps/"..(player.name:gsub("[^a-zA-Z0-9_-.]", "_")).."-"..os.date("%Y%m%d-%H%M%S")..".txt" local fff = fs.open(file, "w") local labelwidth = 17 local w1 = function(s) s = s or "" fff:write(s:removeColorCodes()) fff:write("\n") end --prepare label and value local makelabel = function(s,r) while s:len() < labelwidth do s = s.." " end return ("%s: %s"):format(s, r) end w1(" [MyModule Character Dump]") w1() w1(("%-32s"):format(makelabel("Name", player.name))) w1(("%-32s"):format(makelabel("Role", player.descriptor.role or player.type:capitalize()))) w1(("STR: %d"):format(player:getStr())) w1(("DEX: %d"):format(player:getDex())) w1(("CON: %d"):format(player:getCon())) fff:close() Dialog:simplePopup("Character dump complete", "File: "..fs.getRealPath(file)) end
Go back to T4 Modules Howto Guide