require "gurps_tables" function thrust_or_swing(typ, st) if st < 1 then return "0" end if _GTHRUSTSWINGTABLE[typ][st] then return _GTHRUSTSWINGTABLE[typ][st] else return thrust_or_swing(typ, st - 1) end end function thrust(st) return thrust_or_swing("thrust", st) end function swing(st) return thrust_or_swing("swing", st) end function print_dice(dice_no, modifier) tex.sprint([[\mbox{]]) tex.sprint(dice_no .. "d") -- If `modifier` is a valid number local modifier_number = tonumber(modifier) if modifier_number then if modifier_number > 0 then tex.sprint("+" .. modifier_number) elseif modifier_number < 0 then tex.sprint("-" .. modifier_number) -- else, don't print anything end -- elseif `modifier` is non-nil elseif modifier then tex.sprint("+" .. modifier) end tex.sprint("}") end function valued_trait(value, points) return { value=value, points=points or "?" } end function value(value) return { value=value } end function trait(points) return { points=points or "?" } end function base_stat(stat, multiplier, default) default = default or 10 stat = stat or default multiplier = multiplier or 10 return valued_trait(stat, (stat - default)*multiplier) end -- Creates an attack table. function attack(stat, damage, based_on) rettable = { stat=stat or "?", damage=damage or "?", based_on=based_on or "?" } -- i.e. if it's based on a real stat and that stat is a skill if based_on ~= "?" and character.skills[based_on] then rettable.adjustment = character.skills[based_on] - stat elseif based_on ~= "?" and character.spells[based_on] then rettable.adjustment = character.spells[based_on] - stat end return rettable end function create_character(args) -- Creates a character local args = args or {} local c = {} c.pointless_stats = {} c.pointless_stats.DR = value(args.DR or 0) c.pointless_stats.SM = value(args.SM or 0) c.base_stats = { ST=base_stat(args.ST, 10 - c.pointless_stats.SM.value), DX=base_stat(args.DX, 20), IQ=base_stat(args.IQ, 20), HT=base_stat(args.HT), } -- Gets the value for a base stat function gv(c, key) return c.base_stats[key].value end c.pointless_stats.thr = value(thrust(gv(c, "ST"))) c.pointless_stats.sw = value(swing(gv(c, "ST"))) c.base_stats.HP = base_stat(args.HP or gv(c, "ST"), 5, gv(c, "ST")) c.base_stats.Per = base_stat(args.Per or gv(c, "IQ"), 5, gv(c, "IQ")) c.base_stats.Will = base_stat(args.Will or gv(c, "IQ"), 5, gv(c, "IQ")) c.base_stats.FP = base_stat(args.FP or gv(c, "HT"), 5, gv(c, "HT")) -- c.advantages = {} -- c.disadvantages = {} c.advantages = args.advantages c.disadvantages = args.disadvantages -- TODO abstract the logic for spells into general skills c.skills = {} for name,obj in pairs(args.skills) do if obj.difficulty then x = split(obj.difficulty) -- TODO fix this to be spell points vv points = calculate_skill_points(c, x[1], x[2], obj.value) c.skills[name] = valued_trait(obj.value, points) else c.skills[name] = valued_trait(obj.value) end end c.spells = {} for name,obj in pairs(args.spells) do if obj.difficulty then x = split(obj.difficulty) -- TODO fix this to be spell points vv points = calculate_skill_points(c, x[1], x[2], obj.value) c.spells[name] = valued_trait(obj.value, points) else c.spells[name] = valued_trait(obj.value) end end c.attacks = {} return c end -- character = create_character() function count_points() running_total = 0 for i,traits in pairs({"base_stats", "advantages", "disadvantages", "skills", "spells"}) do if character[traits] then for j,v in pairs(character[traits]) do if v.points ~= "?" then running_total = running_total + v.points end end end end return running_total end function print_little_section(title, tbl) tex.print([[\paragraph{]] .. title .. [[}]]) local x = {} for k,v in pairs(tbl) do if v.value == nil then table.insert(x, k .. "[" .. v.points .. "]") elseif v.points == nil then table.insert(x, k .. "~" .. v.value) else table.insert(x, k .. "~" .. v.value .. "[" .. v.points .. "]") end end if next(tbl) == nil then table.insert(x, [[\ldots{}]]) end tex.print(table.concat(x, ", ") .. ".") end base_stats = { "ST", "DX", "IQ", "HT", "HP", "Per", "Will", "FP" } function print_character() tex.print([[\subsubsection{Stats (]] .. count_points() .. [[~pt)}]]) tex.print([[\paragraph{Base stats}]]) local x = {} for i, base_stat in ipairs(base_stats) do local obj = character.base_stats[base_stat] table.insert( x, base_stat .. [[~]] .. obj.value .. "[" .. obj.points .. "]") end tex.print(table.concat(x, ", ") .. ".") print_little_section("Other", character.pointless_stats) print_little_section("Advantages", character.advantages) print_little_section("Disadvantages", character.disadvantages) print_little_section("Skills", character.skills) if next(character.spells) then print_little_section("Spells", character.spells) end end function print_character_as_lens() tex.print([[\subsubsection{Lens (+]] .. count_points() .. [[~pt)}]]) tex.print([[\paragraph{Base stats}]]) local x = {} for i, base_stat in ipairs(base_stats) do local obj = character.base_stats[base_stat] if obj.value ~= 10 then table.insert( x, base_stat .. [[~+]] .. obj.value - 10 .. "[" .. obj.points .. "]") end end tex.print(table.concat(x, ", ") .. ".") if next(character.advantages) ~= nil then print_little_section("Advantages", character.advantages) end if next(character.disadvantages) ~= nil then print_little_section("Disadvantages", character.disadvantages) end if next(character.skills) ~= nil then print_little_section("Skills", character.skills) end if next(character.spells) ~= nil then print_little_section("Spells", character.spells) end end -- Creates a new LaTeX command -- -- TODO move this to the dtx file because that's where documentation for LaTeX -- files lives function print_set_cmd(name, extra_args) extra_args = extra_args or {"nil"} tex.print( string.format( [[\NewDocumentCommand\set%s{m}]] .. [[{\luadirect{character.%s = base_stat(#1, %s)}}]], name, name, table.concat(extra_args, ", ") ) ) end print_set_cmd("ST") print_set_cmd("DX", {"20"}) print_set_cmd("IQ", {"20"}) print_set_cmd("HT") print_set_cmd("HP", {"5", "character.base_stats.ST.value"}) print_set_cmd("Per", {"5", "character.base_stats.IQ.value"}) print_set_cmd("Will", {"5", "character.base_stats.IQ.value"}) print_set_cmd("FP", {"5", "character.base_stats.HT.value"}) --- Splits string (on / by default) function split(s, split_on) local split_on = split_on or [[/]] local rettable = {} for i in string.gmatch(s, "[^" .. split_on .. "]+") do table.insert(rettable, i) end return rettable end function calculate_skill_points(character, based_on, difficulty, skill_level) -- TODO rename this variable if difficulty == "Easy" then difficulty_modifier = 0 elseif difficulty == "Average" then difficulty_modifier = 1 elseif difficulty == "Hard" then difficulty_modifier = 2 elseif difficulty == "Very Hard" then difficulty_modifier = 3 elseif difficulty == "Wildcard" then return 3*calculate_skill_points(based_on, "Very Hard", skill_level) end -- TODO make support for non-base stat based_on values local relative_level = skill_level - character.base_stats[based_on].value if relative_level == (0 - difficulty_modifier) then return 1 elseif relative_level == (1 - difficulty_modifier) then return 2 elseif relative_level == (2 - difficulty_modifier) then return 4 elseif relative_level > (2 - difficulty_modifier) then return (relative_level - 1 + difficulty_modifier) * 4 end end