local module = {} setmetatable(module, {__index=_G}) setfenv(1, module) local m = require 'marigold-svg.marigold' function capitalize(str) return string.gsub(str, "^.", string.upper) end --===== misc =====-- local function stack(...) local array = {...} local l l = function(acc_f, acc_h) if #array == 0 then return acc_f, acc_h end local f, h = array[1][1], array[1][2] table.remove(array, 1) local acc_f = function(y) local tbl = acc_f(y) table.insert(tbl, f(y+acc_h)) return tbl end local acc_h = acc_h + h return l(acc_f, acc_h) end local f, h = l(function(y) return {} end, 0) return function(y) return m.g(f(y)) end, h end local function pad(top, bottom, f, h) return function(y) return f(y+top) end, top + h + bottom end local function _divider() local size = 3 return function(y) return m.rect{ x=0, y=y, width=500, height=size, style="fill:darkred;", } end, size end local divider = function() return pad(0, 10, _divider()) end --===== basic stats =====-- local function name(stats) local size = 40 return function(y) return m.text(stats.name, { x=10, y=y+size, style="font-size:"..size.."px;font-variant-caps:small-caps;stroke:black;fill:darkred;font-family:serif", }) end, size end local function subheading(stats) local size = 14 return function(y) return m.text( string.format("%s %s, %s", capitalize(stats.size), stats.type, stats.alignment), { x=10, y=y+size, style="font-size:"..size.."px;font-style:italic;" } ) end, size end local function header(stats) local f, h = stack( {pad(10, 10, name(stats))}, {pad(0, 10, subheading(stats))} ) return f, h end local function _statline(name, value) local size = 14 return function(y) return m.text( ''..name..' '..value, { x=10, y=y+size, style="font-size:"..size.."px;fill:darkred;" } ) end, 14 end local statline = function(name, value) return pad(0, 2, _statline(name, value)) end local function armor_hp(stats) local f, h = pad(0, 10, stack( {statline('Armor Class', stats.ac)}, {statline('Hit Points', stats.hp)}, {statline('Speed', stats.speed)} )) return f, h end local function bonus(ability_score) return math.floor( (ability_score-10)/2 ) end local function scorebox(name, ability_score, x, y) local score = tonumber(ability_score) local value = string.format("%d (%s%d)", score, (score<0 and '-') or '+', bonus(score)) return m.g{ m.text(name, { x=x+10, y=y+15, style="font-weight:bold;font-size:15px;fill:darkred" }), m.text(value, {x=x, y=y+30, style="font-size:15px;fill:darkred;" }), } end local function ability_scores(stats) return function(y) return m.g{ scorebox('STR', stats.str, 10, y), scorebox('DEX', stats.dex, 90, y), scorebox('CON', stats.con, 170, y), scorebox('INT', stats.int, 250, y), scorebox('WIS', stats.wis, 330, y), scorebox('CHA', stats.cha, 410, y), } end, 40 end local function base(stats) local f, h = stack( {header(stats)}, {divider()}, {armor_hp(stats)}, {divider()}, {ability_scores(stats)}, {divider()} ) return f, h end --===== attributes =====-- local function attribs(stats) return function(y) return m.rect{ x=0, y=y, width=500, height=20, style="fill:blue", } end, 20 end --===== actions =====-- local function actions(stats) return function(y) return m.g{} end, 0 end --===== draw =====-- function draw(stats) local f, h = stack({base(stats)}, {attribs(stats)}, {actions(stats)}) return m.render(m.svg{ viewBox = string.format("0 0 500 %d", h), width = 500, height = height, style = "font-family:sans-serif;", f(0), }) end return module