From 5d6c8831ca900ff4f20ba0a281c29dc28fd2467e Mon Sep 17 00:00:00 2001 From: David Foerster Date: Thu, 15 Feb 2024 18:53:43 +0100 Subject: [PATCH 1/8] Add assert() around loadfile() invocations --- src/data.lua | 2 +- src/tools.lua | 4 ++-- src/values.lua | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/data.lua b/src/data.lua index 66faf38..78699d4 100644 --- a/src/data.lua +++ b/src/data.lua @@ -12,4 +12,4 @@ if i < #arg then tex.error("zu viele Argumente. Erstes überflüssiges Argument: '" .. tostring(arg[i+1]) .. "'") end -return loadfile("values.lua", "t")(arg[i]) \ No newline at end of file +return assert(loadfile("values.lua", "t"))(arg[i]) diff --git a/src/tools.lua b/src/tools.lua index 5a50f0a..a3c7d98 100644 --- a/src/tools.lua +++ b/src/tools.lua @@ -33,7 +33,7 @@ elseif arg[curarg] ~= nil then end local d = require("schemadef") -local schema = loadfile("schema.lua", "t")(true) +local schema = assert(loadfile("schema.lua", "t"))(true) if gendoc then if standalone then @@ -94,7 +94,7 @@ end if validate then local res = 0 local prev_pcount = 0 - local values = loadfile("values.lua") + local values = assert(loadfile("values.lua", "t")) for i = curarg + 1,#arg do values(arg[i]) end diff --git a/src/values.lua b/src/values.lua index ea75c35..6d14f00 100644 --- a/src/values.lua +++ b/src/values.lua @@ -1,5 +1,5 @@ local d = require("schemadef") -local schema = loadfile("schema.lua", "t")(false) +local schema = assert(loadfile("schema.lua", "t"))(false) local skt = require("skt") local input = ... From 3ce87afb47119a1ce2a9de1eec5a38b144728e9c Mon Sep 17 00:00:00 2001 From: David Foerster Date: Tue, 12 Mar 2024 14:53:36 +0100 Subject: [PATCH 2/8] Implement math.round(x): nearest integer, halfway away from zero ...in accordance with Basisregelwerk page 13 ("Rundungen"). The previous implementation relies on `string.format("%.0f", x)` whose behaviour is inconsistent accross Lua versions. --- src/stdext.lua | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 src/stdext.lua diff --git a/src/stdext.lua b/src/stdext.lua new file mode 100644 index 0000000..417be60 --- /dev/null +++ b/src/stdext.lua @@ -0,0 +1,10 @@ +-- Rounds to the nearest integer. Halfway cases are arounded _away_ from zero. +function math.round(x) + local integral, fractional = math.modf(x) + if fractional >= 0.5 then + return integral + 1 + elseif fractional <= -0.5 then + return integral - 1 + end + return integral +end From b17c0485f7a29e0acc38853eaf10b9d58ee797ce Mon Sep 17 00:00:00 2001 From: David Foerster Date: Tue, 12 Mar 2024 15:04:15 +0100 Subject: [PATCH 3/8] Use math.round() over common.round() in trivial cases --- src/common.lua | 3 ++- src/frontseite.lua | 5 +++-- src/kampfbogen.lua | 11 ++++++----- src/talentbogen.lua | 5 +++-- src/values.lua | 5 +++-- 5 files changed, 17 insertions(+), 12 deletions(-) diff --git a/src/common.lua b/src/common.lua index cf09898..6f6cbd4 100644 --- a/src/common.lua +++ b/src/common.lua @@ -1,3 +1,4 @@ +require("stdext") local data = require("data") local schema = require("schema") @@ -501,7 +502,7 @@ function common.schaden.render(tp) elseif tp.die ~= nil then tex.sprint(-2, "+") end - tex.sprint(-2, common.round(math.abs(tp.num))) + tex.sprint(-2, math.round(math.abs(tp.num))) end end diff --git a/src/frontseite.lua b/src/frontseite.lua index 0ce016f..b74a14e 100644 --- a/src/frontseite.lua +++ b/src/frontseite.lua @@ -1,6 +1,7 @@ local data = require("data") local common = require("common") local schema = require("schema") +require("stdext") local frontseite = {} @@ -213,7 +214,7 @@ function eigenschaften.rechts(self) if data.Nachteile.Glasknochen then base = base - 2 end - tex.sprint(-2, common.round(base)) + tex.sprint(-2, math.round(base)) end end tex.sprint([[\\\hline\end{tabular}}\egroup\par]]) @@ -261,4 +262,4 @@ end frontseite.eigenschaften = eigenschaften -return frontseite \ No newline at end of file +return frontseite diff --git a/src/kampfbogen.lua b/src/kampfbogen.lua index e76dceb..0fe5ce3 100644 --- a/src/kampfbogen.lua +++ b/src/kampfbogen.lua @@ -1,3 +1,4 @@ +require("stdext") local data = require("data") local common = require("common") local schemadef = require("schemadef") @@ -14,7 +15,7 @@ local function calc_be(input) if string.find(input, "^BEx") then return be * input:sub(4) elseif string.find(input, "^BE%-") then - return common.round(math.max(0, be - tonumber(input:sub(4)))) + return math.round(math.max(0, be - tonumber(input:sub(4)))) elseif string.find(input, "^BE") then return be else @@ -158,14 +159,14 @@ nahkampf_render[11]= {false, function(v, talent, ebe) if talent == nil or #talent < 4 or atb == "" or #v < 8 then return end - tex.sprint(-2, atpa_mod(atb - common.round(ebe/2, true), talent.AT, v["TP/KK Schwelle"], v["TP/KK Schritt"], v["WM AT"], art(v), talent.Spezialisierungen)) + tex.sprint(-2, atpa_mod(atb - math.round(ebe/2, true), talent.AT, v["TP/KK Schwelle"], v["TP/KK Schritt"], v["WM AT"], art(v), talent.Spezialisierungen)) end} nahkampf_render[12]= {false, function(v, talent, ebe) local pab = data:cur("PA") if talent == nil or #talent < 5 or pab == "" or #v < 9 then return end - tex.sprint(-2, atpa_mod(pab - common.round(ebe/2), data.PA(talent), v["TP/KK Schwelle"], v["TP/KK Schritt"], v["WM PA"], art(v), talent.Spezialisierungen)) + tex.sprint(-2, atpa_mod(pab - math.round(ebe/2), data.PA(talent), v["TP/KK Schwelle"], v["TP/KK Schritt"], v["WM PA"], art(v), talent.Spezialisierungen)) end} nahkampf_render[13]= {false, function(v, talent, ebe) if #v < 6 then @@ -252,7 +253,7 @@ local waffenlos_render = { added = added + 1 end end - tex.sprint(-2, atpa_mod(atb - common.round(ebe/2, true), talent.AT, v["TP/KK Schwelle"], v["TP/KK Schritt"], 0)) + tex.sprint(-2, atpa_mod(atb - math.round(ebe/2, true), talent.AT, v["TP/KK Schwelle"], v["TP/KK Schritt"], 0)) end}, [6]= {false, function(v, talent, ebe) local pab = data:cur("PA") @@ -266,7 +267,7 @@ local waffenlos_render = { added = added + 1 end end - tex.sprint(-2, atpa_mod(pab - common.round(ebe/2), data.PA(talent), v["TP/KK Schwelle"], v["TP/KK Schritt"], 0)) + tex.sprint(-2, atpa_mod(pab - math.round(ebe/2), data.PA(talent), v["TP/KK Schwelle"], v["TP/KK Schritt"], 0)) end}, [7]= {false, function(v, talent, ebe) tp = common.schaden.mod({dice=1, die=6, num=0}, v["TP/KK Schwelle"], v["TP/KK Schritt"]) diff --git a/src/talentbogen.lua b/src/talentbogen.lua index 67cef53..7661db3 100644 --- a/src/talentbogen.lua +++ b/src/talentbogen.lua @@ -1,6 +1,7 @@ local schema = require("schema") local data = require("data") local common = require("common") +require("stdext") local talent = {} @@ -187,7 +188,7 @@ function talent.meta(v) ::found:: end if sum ~= nil and #v[5] > 0 then - tex.sprint(-2, common.round(sum / #v[5])) + tex.sprint(-2, math.round(sum / #v[5])) end end @@ -379,4 +380,4 @@ function talentbogen.gruppen() end -return talentbogen \ No newline at end of file +return talentbogen diff --git a/src/values.lua b/src/values.lua index 6d14f00..56478d9 100644 --- a/src/values.lua +++ b/src/values.lua @@ -1,3 +1,4 @@ +require("stdext") local d = require("schemadef") local schema = assert(loadfile("schema.lua", "t"))(false) local skt = require("skt") @@ -134,7 +135,7 @@ function getter_map.sparse(val, div) if val == 0 then return "" end - return tonumber(string.format("%.0f", val/div + 0.0001)) -- round up at 0.5 + return math.round(val / div) end values.sparse = getter_map.sparse @@ -1213,4 +1214,4 @@ for _, e in ipairs(schema.Ereignisse:instance()) do table.insert(values.Ereignisse, event) end -return values \ No newline at end of file +return values From 51f2c5dba1db5fd05ec640bfb0be4d89df4ce7d1 Mon Sep 17 00:00:00 2001 From: David Foerster Date: Tue, 12 Mar 2024 15:10:21 +0100 Subject: [PATCH 4/8] common.render_delta(): use math.round(), fix other issue - common.render_delta() used to generate the wrong sign for non-zero values in [-0.5, 0.5]. - Use local variable for sign character to better describe what's happening. --- src/common.lua | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/common.lua b/src/common.lua index 6f6cbd4..8421551 100644 --- a/src/common.lua +++ b/src/common.lua @@ -356,15 +356,17 @@ end function common.render_delta(input) if type(input) == "number" then - if input < 0 then - tex.sprint(-2, "−") - elseif input > 0 then - tex.sprint(-2, "+") + local sign + if input >= 0.5 then + sign = "+" + elseif input <= -0.5 then + sign = "−" + else + sign = "" end - tex.sprint(common.round(math.abs(input))) - else - tex.sprint(-2, input) + input = string.format("%s%d", sign, math.round(math.abs(input))) end + tex.sprint(-2, input) end function common.merkmalliste(input, zauber) From 5338e60f40c1464f9bbd45b3eee3d6db725e2904 Mon Sep 17 00:00:00 2001 From: David Foerster Date: Tue, 12 Mar 2024 15:14:18 +0100 Subject: [PATCH 5/8] kampfbogen.energieleiste(): use math.round(), fix other issue - Use a *local* variable to store intermediary results. - Emit "&" and the integral value with a single tex.sprint() since the latter can't result in special characters. --- src/kampfbogen.lua | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/kampfbogen.lua b/src/kampfbogen.lua index 0fe5ce3..05c8146 100644 --- a/src/kampfbogen.lua +++ b/src/kampfbogen.lua @@ -405,13 +405,12 @@ end function kampfbogen.energieleiste(label, val) tex.sprint(-2, label) - num = tonumber(val) - if num == nil then + val = tonumber(val) + if val == nil then tex.sprint("&&&&") else for i=1,4 do - tex.sprint("&") - tex.sprint(-2, common.round(num/i)) + tex.sprint("&", math.round(val / i)) end end tex.sprint("&") From db0b334aecf2cf66c3a7b90be17c674814b1147c Mon Sep 17 00:00:00 2001 From: David Foerster Date: Tue, 12 Mar 2024 15:15:14 +0100 Subject: [PATCH 6/8] Remove common.round() --- src/common.lua | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/common.lua b/src/common.lua index 8421551..ffc4075 100644 --- a/src/common.lua +++ b/src/common.lua @@ -265,11 +265,6 @@ function common.checkboxlist(items) end end -function common.round(v, down) - local delta = down and -0.0001 or 0.0001 -- round up at 0.5 unless down given - return tonumber(string.format("%.0f", v + delta)) -end - function common.ritualkenntnis(items, count) tex.sprint([[{\normalfont\normalsize\setlength{\arrayrulewidth}{1pt} \begin{NiceTabular}{p{3.7cm}@{(}x{3.2cm}@{):\hspace{2pt}}x{0.7cm}x{0.5cm}}]]) From a298815c32e096de47ab24164a105d77d1d0b9df Mon Sep 17 00:00:00 2001 From: David Foerster Date: Thu, 14 Mar 2024 00:33:51 +0100 Subject: [PATCH 7/8] Convert other instances to math.round() that previously involved `math.floor(x + 0.5)` or `string.format("%.0f", x)`. --- src/skt.lua | 18 +++++++++--------- src/values.lua | 21 +++++++++++---------- src/zauberdokument.lua | 5 +++-- 3 files changed, 23 insertions(+), 21 deletions(-) diff --git a/src/skt.lua b/src/skt.lua index 717e14c..23c23af 100644 --- a/src/skt.lua +++ b/src/skt.lua @@ -1,3 +1,5 @@ +require("stdext") + local skt = { spalte = { {n="A*", f=1}, {n="A", f=1, [31] = 50}, {n="B", f=2}, {n="C", f=3}, {n="D", f=4, [28] = 170, [31] = 200}, {n="E", f=5, [8] = 48}, {n="F", f=7.5, [30] = 350, [31] = 375}, {n="G", f=10, [30] = 480, [31] = 500}, {n="H", f=20, [2] = 35, [6] = 140, [24] = 720, [27] = 830, [31] = 1000} @@ -52,7 +54,7 @@ function skt.spalte:effektiv(basis, zielwert, methode) end function skt.faktor:apply(value) - return math.floor(value * self[1] + 0.5) + return math.round(value * self[1]) end for _, f in ipairs(skt.faktor) do @@ -61,11 +63,9 @@ end function skt:kosten(spalte, zielwert) local index = self.spalte:num(spalte) - if zielwert > 31 then - zielwert = 31 - end + zielwert = math.min(zielwert, 31) if zielwert <= 0 then - return 5 * math.floor(self.spalte[index].f + 0.5) + return 5 * math.round(self.spalte[index].f) end if index == 1 then return math.max(self:kosten("A", zielwert) - 2, 1) @@ -74,14 +74,14 @@ function skt:kosten(spalte, zielwert) if explicit ~= nil then return explicit end - local val = tonumber(string.format("%.0f", 0.8 * self.spalte[index].f * zielwert^1.2)) + local val = math.round(0.8 * self.spalte[index].f * zielwert^1.2) if val > 200 then - val = math.floor((val + 5) / 10) * 10 + val = math.round(val / 10) * 10 elseif val > 50 then - val = math.floor((val + 2) / 5) * 5 + val = math.round(val / 5) * 5 end return val end -return skt \ No newline at end of file +return skt diff --git a/src/values.lua b/src/values.lua index 56478d9..76dd108 100644 --- a/src/values.lua +++ b/src/values.lua @@ -516,13 +516,12 @@ function values:gesamtRuestung(teile, decimals) end if gRS == nil then return nil, nil end gRS = gRS/20 - gBE = gBE/20 - sterne - if gBE < 0 then gBE = 0 end - if decimals then - return gRS, gBE - else - return tonumber(string.format("%.0f", gRS + 0.0001)), tonumber(string.format("%.0f", gBE + 0.0001)) + gBE = math.max(gBE/20 - sterne, 0) + if not decimals then + gRS = math.round(gRS) + gBE = math.round(gBE) end + return gRS, gBE end function values:ap_mod(kosten) @@ -757,9 +756,11 @@ function values:spezialisierung(e) end event[1] = event[1] .. e.Fertigkeit .. ", " .. e.Methode .. "): " .. e.Name ziel.Spezialisierungen:append(e.Name) - local ap = #ziel.Spezialisierungen * 20 * math.floor(skt.spalte[skt.spalte:num(spalte)].f + 0.5) + local ap = ( + #ziel.Spezialisierungen * 20 + * math.round(skt.spalte[skt.spalte:num(spalte)].f)) if e.Methode == "SE" then - ap = math.floor(ap / 2 + 0.5) + ap = math.round(ap / 2) end event[2] = -1 * ap event[3] = faktor @@ -925,7 +926,7 @@ function values:aktiviere(e) elseif smt.name == "Ritual" then ap = e.Subjekt.Lernkosten if e.Methode == "SE" then - ap = math.floor((ap / 2) + 0.51) + ap = math.round(ap / 2) end faktor = self:tgruppe_faktor("Zauber") self.Magie.Rituale:append(e.Subjekt, e.Sortierung) @@ -1063,7 +1064,7 @@ function values:senkung(e) if found == nil then tex.error("\n[Senkung] unbekannte Schlechte Eigenschaft: '" .. e.Name .. "'") end - local ap = math.floor((found.Wert - e.Zielwert) * 50 * found.GP + 0.5) + local ap = math.round((found.Wert - e.Zielwert) * 50 * found.GP) if e.Zielwert == 0 then event[1] = event[1] .. ": Schlechte Eigenschaft mit Wert " .. found.Wert .. " entfernt" table.remove(self.Nachteile.Eigenschaften.value, found_index) diff --git a/src/zauberdokument.lua b/src/zauberdokument.lua index 6b50ae1..9bd03dd 100644 --- a/src/zauberdokument.lua +++ b/src/zauberdokument.lua @@ -1,3 +1,4 @@ +require("stdext") local data = require("data") local common = require("common") @@ -16,7 +17,7 @@ function zauberdokument.asp_regeneration() end local mr = data.SF.Magisch.MeisterlicheRegeneration if mr ~= nil then - val.num = val.num + 3 + math.floor((data:cur(mr) + 1)/3) + val.num = val.num + 3 + math.round(data:cur(mr) / 3) tex.sprint(-2, val.num) else local reg = data.SF.Magisch:getlist("Regeneration") @@ -30,4 +31,4 @@ function zauberdokument.asp_regeneration() end end -return zauberdokument \ No newline at end of file +return zauberdokument From c6ea0c9e8d00de4baa42d4be3606ac0d1803776f Mon Sep 17 00:00:00 2001 From: David Foerster Date: Thu, 21 Mar 2024 01:40:42 +0100 Subject: [PATCH 8/8] Kampfbogen: Fix "rounding" of effective AT/PA encumberance Strictly speaking, the rules of distribution of effective encumberance onto AT/PA values use integer floor/ceil division by two rather than rounding (Basisregelwerk, p. 137). So that's what we'll use in the implementation. --- src/kampfbogen.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/kampfbogen.lua b/src/kampfbogen.lua index 05c8146..891670b 100644 --- a/src/kampfbogen.lua +++ b/src/kampfbogen.lua @@ -159,14 +159,14 @@ nahkampf_render[11]= {false, function(v, talent, ebe) if talent == nil or #talent < 4 or atb == "" or #v < 8 then return end - tex.sprint(-2, atpa_mod(atb - math.round(ebe/2, true), talent.AT, v["TP/KK Schwelle"], v["TP/KK Schritt"], v["WM AT"], art(v), talent.Spezialisierungen)) + tex.sprint(-2, atpa_mod(atb - ebe // 2, talent.AT, v["TP/KK Schwelle"], v["TP/KK Schritt"], v["WM AT"], art(v), talent.Spezialisierungen)) end} nahkampf_render[12]= {false, function(v, talent, ebe) local pab = data:cur("PA") if talent == nil or #talent < 5 or pab == "" or #v < 9 then return end - tex.sprint(-2, atpa_mod(pab - math.round(ebe/2), data.PA(talent), v["TP/KK Schwelle"], v["TP/KK Schritt"], v["WM PA"], art(v), talent.Spezialisierungen)) + tex.sprint(-2, atpa_mod(pab - (ebe + 1) // 2, data.PA(talent), v["TP/KK Schwelle"], v["TP/KK Schritt"], v["WM PA"], art(v), talent.Spezialisierungen)) end} nahkampf_render[13]= {false, function(v, talent, ebe) if #v < 6 then @@ -253,7 +253,7 @@ local waffenlos_render = { added = added + 1 end end - tex.sprint(-2, atpa_mod(atb - math.round(ebe/2, true), talent.AT, v["TP/KK Schwelle"], v["TP/KK Schritt"], 0)) + tex.sprint(-2, atpa_mod(atb - ebe // 2, talent.AT, v["TP/KK Schwelle"], v["TP/KK Schritt"], 0)) end}, [6]= {false, function(v, talent, ebe) local pab = data:cur("PA") @@ -267,7 +267,7 @@ local waffenlos_render = { added = added + 1 end end - tex.sprint(-2, atpa_mod(pab - math.round(ebe/2), data.PA(talent), v["TP/KK Schwelle"], v["TP/KK Schritt"], 0)) + tex.sprint(-2, atpa_mod(pab - (ebe + 1) // 2, data.PA(talent), v["TP/KK Schwelle"], v["TP/KK Schritt"], 0)) end}, [7]= {false, function(v, talent, ebe) tp = common.schaden.mod({dice=1, die=6, num=0}, v["TP/KK Schwelle"], v["TP/KK Schritt"])