diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c9b1058 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +ldoc_output diff --git a/.woodpecker/deploy.yml b/.woodpecker/deploy.yml new file mode 100644 index 0000000..4fe75fd --- /dev/null +++ b/.woodpecker/deploy.yml @@ -0,0 +1,23 @@ +steps: + ldoc-generate: + image: alpine + commands: + - apk update + - apk add git ldoc + - ldoc . + - git init ldoc_output + ldoc-push: + depends_on: ldoc-generate + image: appleboy/drone-git-push + settings: + ssh_key: + from_secret: ssh_key + remote: git@codeberg.org:y5nw/advtrains-doc-integration + branch: pages + path: ldoc_output + force: true + commit: true + commit_message: "[CI SKIP] LDoc for commit ${CI_COMMIT_SHA}" +when: + - event: push + branch: master diff --git a/.woodpecker.yml b/.woodpecker/test.yml similarity index 100% rename from .woodpecker.yml rename to .woodpecker/test.yml diff --git a/README.md b/README.md index 2b2d81a..59f3c8a 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,7 @@ -This mod integrates Advtrains with the [doc][minetest-doc] mod. +# Documentation System Integration for Advtrains + +This mod integrates Advtrains with the [doc][minetest-doc] mod, among +other things. [minetest-doc]: https://content.minetest.net/packages/Wuzzy/doc/ diff --git a/config.ld b/config.ld new file mode 100644 index 0000000..7e7ac05 --- /dev/null +++ b/config.ld @@ -0,0 +1,8 @@ +dir = "ldoc_output" +file = {".", exclude = {"spec"}} +format = "markdown" +project = "Documentation System Integration for Advtrains" +title = "Manual for advtrains_doc_integration" +package = "advtrains_doc_integration" +readme = "README.md" +-- vim: syntax=lua diff --git a/describe.lua b/describe.lua new file mode 100644 index 0000000..ec8be95 --- /dev/null +++ b/describe.lua @@ -0,0 +1,83 @@ +--- Describing things. +-- This module includes functions used for describing things. +-- @module advtrains_doc_integration.describe +-- @alias D +local M = advtrains_doc_integration.mathutils +local utils = advtrains_doc_integration.utils +local D = {} + +--- Describe a conns object +-- @tparam integer|table conns The conns object to describe. +-- @treturn string|nil The description of the conns object. +function D.conns(conns) + local connsdesc = {[0] = "N", "NNE", "NE", "ENE", "E", "ESE", "SE", "SSE", "S", "SSW", "SW", "WSW", "W", "WNW", "NW", "NNW"} + if type(conns) == "table" then + if conns.c then + if conns.y and conns.y ~= 0 then + return ("%s%+d%%"):format(D.conns(conns.c), conns.y*100) + else + return D.conns(conns.c) + end + else + local cst = utils.map(conns, D.conns) + local cstl = #cst + if cstl == 2 then + return ("%s - %s"):format(unpack(cst)) + elseif cstl == 3 then + return ("[%s <-] %s -> %s"):format(cst[3], cst[1], cst[2]) + elseif cstl == 4 then + return ("%s - %s; %s - %s"):format(unpack(cst)) + elseif cstl == 5 then + return ("[%s,%s <-] %s -> %s"):format(cst[3], cst[4], cst[1], cst[2]) + end + end + else + return connsdesc[tonumber(conns)] + end +end + +--- Describe a (mixed) fraction +-- @tparam integer int The integer part of the number. +-- @tparam integer num The numerator. +-- @tparam integer denom The denominator. +-- @treturn string The description of the mixed fraction. +function D.mixed_fraction(int, num, denom) + local str = tostring(int) + if num > 0 then + local fs = ("%d/%d"):format(num, denom) + if int ~= 0 then + str = ("%d %s"):format(int, fs) + else + str = fs + end + end + return str +end + +--- Describe a short length (using mm and in). +-- @tparam number x The length in meters. +-- @treturn string The description of the length. +function D.length(x) + local inch, inchfrac = math.modf(x/0.0254) + local ft = math.floor(inch/12) + inch, inchfrac = inch%12, math.floor(inchfrac*32) + local imst = {} + if ft > 0 then + table.insert(imst, ft .. "'") + end + if inch > 0 or inchfrac > 0 or ft == 0 then + table.insert(imst, D.mixed_fraction(inch, M.reduce_fraction(inchfrac, 32)) .. '"') + end + return ("%d mm (%s)"):format(1000*x, table.concat(imst, " ")) +end + +--- Describe a speed value (using m/s, km/h and mph). +-- @tparam number x The speed in m/s. +-- @treturn string The description of the speed. +function D.speed(x) + local kmph = x*3.6 + local mph = kmph/1.609344 + return string.format("%.1f m/s (%.1f km/h; %.1f mph)", x, kmph, mph) +end + +return D diff --git a/init.lua b/init.lua index b1ba21d..7acd4d4 100644 --- a/init.lua +++ b/init.lua @@ -1,8 +1,16 @@ +--- Entry point. +-- @module advtrains_doc_integration +local worldpath = minetest.get_worldpath() .. "/" +local modpath = minetest.get_modpath("advtrains_doc_integration") local S = minetest.get_translator("advtrains_doc_integration") local fsescape = minetest.formspec_escape -local worldpath = minetest.get_worldpath() .. DIR_DELIM advtrains_doc_integration = {} +for _, m in ipairs{"mathutils", "describe", "utils"} do + advtrains_doc_integration[m] = dofile(("%s/%s.lua"):format(modpath, m)) +end +local D = advtrains_doc_integration.describe + local function S2(a, b) return S(a, S(b)) end @@ -102,68 +110,6 @@ local function htsound(action, soundspec) return sounddesc end -local function describe_conns(conns) - local connsdesc = {[0] = "N", "NNE", "NE", "ENE", "E", "ESE", "SE", "SSE", "S", "SSW", "SW", "WSW", "W", "WNW", "NW", "NNW"} - if type(conns) == "table" then - if conns.c then - if conns.y and conns.y ~= 0 then - return ("%s%+d%%"):format(describe_conns(conns.c), conns.y*100) - else - return describe_conns(conns.c) - end - else - local cst = map(conns, describe_conns) - local cstl = #cst - if cstl == 2 then - return ("%s - %s"):format(unpack(cst)) - elseif cstl == 3 then - return ("[%s <-] %s -> %s"):format(cst[3], cst[1], cst[2]) - elseif cstl == 4 then - return ("%s - %s; %s - %s"):format(unpack(cst)) - elseif cstl == 5 then - return ("[%s,%s <-] %s -> %s"):format(cst[3], cst[4], cst[1], cst[2]) - end - end - else - return connsdesc[tonumber(conns)] - end -end -advtrains_doc_integration.describe_conns = describe_conns - -local function describe_length(x) - local inch = x/0.0254 - local infdenom = 32 - local infnum = math.floor(inch*infdenom)%infdenom - local ft = math.floor(inch/12) - inch = math.floor(inch)%12 - local st = {} - if ft > 0 then - table.insert(st, ft .. "'") - end - local infstr = "" - if infnum > 0 then - while infnum%2 == 0 do - infnum, infdenom = infnum/2, infdenom/2 - end - infstr = string.format(" %d/%d", infnum, infdenom) - end - if inch > 0 then - table.insert(st, string.format(' %s%s"', inch, infstr)) - elseif infnum > 0 then - table.insert(st, infstr .. '"') - end - if not next(st) then - st = '0"' - end - return string.format("%d mm (%s)", 1000*x, table.concat(st)) -end - -local function describe_speed(x) - local kmph = x*3.6 - local mph = kmph/1.609344 - return string.format("%.1f m/s (%.1f km/h; %.1f mph)", x, kmph, mph) -end - local function addlist(lst, tbl, title, fallback1, fallback2, mapf) if not tbl then if fallback2 then @@ -437,8 +383,8 @@ local function doc_render_wagon_information(prototype, pname) addlist(desctext, prototype.drives_on, S("Drives on:"), nil, nil, htmono) addlist(desctext, prototype.coupler_types_front, S("Compatible front couplers:"), S2("Front coupler: @1", "Absent"), S2("Front coupler: @1", "Universal"), ht_coupler_name) addlist(desctext, prototype.coupler_types_back, S("Compatible rear couplers:"), S2("Rear coupler: @1", "Absent"), S2("Rear coupler: @1", "Universal"), ht_coupler_name) - table.insert(desctext, S("Wagon span: @1", prototype.wagon_span and describe_length(2*prototype.wagon_span) or S("Undefined"))) - table.insert(desctext, S("Maximum speed: @1", prototype.max_speed and describe_speed(prototype.max_speed) or S("Undefined"))) + table.insert(desctext, S("Wagon span: @1", prototype.wagon_span and D.length(2*prototype.wagon_span) or S("Undefined"))) + table.insert(desctext, S("Maximum speed: @1", prototype.max_speed and D.speed(prototype.max_speed) or S("Undefined"))) table.insert(desctext, S2("Motive power: @1", prototype.is_locomotive and "Present" or "Absent")) table.insert(desctext, S("Horn sound: @1", htsound("playhorn", prototype.horn_sound))) if prototype.doors.open.sound or prototype.doors.close.sound then @@ -636,7 +582,7 @@ if doc then end end if ndef.at_conns then - return S("This track has the following conns table by default: @1", describe_conns(ndef.at_conns) or "?") + return S("This track has the following conns table by default: @1", D.conns(ndef.at_conns) or "?") end return "" end) diff --git a/mathutils.lua b/mathutils.lua new file mode 100644 index 0000000..c7377ff --- /dev/null +++ b/mathutils.lua @@ -0,0 +1,43 @@ +--- Math utilities. +-- @module advtrains_doc_integration.mathutils +-- @alias M +local M = {} + +local function gcdmain(x, y, ...) + if y == nil then + return x or 1 + end + y = y%x + if y == 0 then + return gcdmain(x, ...) + end + return gcdmain(y, x, ...) +end + +--- Compute the greatest common divider. +-- @tparam integer ... The numbers used for the computation. +-- @treturn integer The GCD of the given numbers. +function M.gcd(...) + local args = {...} + for k, v in pairs(args) do + if v == 0 then + return 0 + elseif v ~= v then + return v + elseif v < 0 then + args[k] = -v + end + end + return gcdmain(unpack(args)) +end + +--- Reduce a fraction. +-- @tparam integer num The numerator. +-- @tparam integer denom The denominator. +-- @treturn int,int The reduced fraction. +function M.reduce_fraction(num, denom) + local gcd = M.gcd(num, denom) + return num/gcd, denom/gcd +end + +return M diff --git a/spec/mathutils_spec.lua b/spec/mathutils_spec.lua new file mode 100644 index 0000000..8c2befc --- /dev/null +++ b/spec/mathutils_spec.lua @@ -0,0 +1,7 @@ +local M = require "mathutils" +describe("GCD", function() + it("should calculate values correctly", function() + assert.equals(1, M.gcd(5, 7, 9)) + assert.equals(4, M.gcd(8, 24, 20)) + end) +end) diff --git a/utils.lua b/utils.lua new file mode 100644 index 0000000..077148d --- /dev/null +++ b/utils.lua @@ -0,0 +1,18 @@ +--- Utility functions. +-- @module advtrains_doc_integration.utils +-- @alias M +local M = {} + +--- Create a table by applying a function to each element. +-- @tparam table tbl The table to map from. +-- @tparam function func The function to apply. +-- @treturn table The resulting table. +function M.map(tbl, func) + local t = {} + for k, v in pairs(tbl or {}) do + t[k] = func(v) + end + return t +end + +return M