Refactor code, part 2
This commit is contained in:
parent
b588e2b9fe
commit
2f95d90be3
35
README.md
35
README.md
@ -13,12 +13,15 @@ This mod adds the "Wagons" category that contains information related
|
||||
to the wagons. It also adds adds certain Advtrains-related properties
|
||||
to certain nodes.
|
||||
|
||||
This mod also provides the `atdoc_write` command, which can be used to
|
||||
export wagon datasheets.
|
||||
|
||||
**Please keep in mind that, while this mod attempts to provide accurate
|
||||
information, its accuracy, especially for information related to
|
||||
liveries, is limited by various factors. Refer the source code of the
|
||||
relevant mods for authoritative information.**
|
||||
|
||||
## API
|
||||
## Documentation strings
|
||||
|
||||
Wagon mods can add a description to the `_doc_wagon_longdesc` field of
|
||||
the wagon prototype; adding `advtrains_doc_integration` as an optional
|
||||
@ -26,30 +29,10 @@ dependency is _not_ needed.
|
||||
|
||||
## CI
|
||||
|
||||
If you use [Mineunit][mineunit], you can copy (or add a symlink to)
|
||||
this directory into the fixtures directory of your mod and then using
|
||||
(for exmaple) the code below to generate wagon datasheets.
|
||||
|
||||
```
|
||||
require "mineunit"
|
||||
mineunit "core"
|
||||
fixture "advtrains_doc_integration/ci/init"
|
||||
sourcefile "init"
|
||||
```
|
||||
This mod also be used with [Mineunit][mineunit] in addition to a
|
||||
regular Minetest environment, such as to generate wagon datasheets.
|
||||
However, please note that, due to various limitations, the datasheet
|
||||
generated in such an environment does not contain certain information
|
||||
that would otherwise be available from Minetest.
|
||||
|
||||
[mineunit]: https://github.com/S-S-X/mineunit
|
||||
|
||||
If you do not use Mineunit, the following functions are available in
|
||||
the `advtrains_doc_integration` table:
|
||||
|
||||
* `write_wagon_info_as_latex`, which exports the datasheet of a single
|
||||
type of wagon as LaTeX, and
|
||||
* `write_all_wagons_as_latex`, which exports all wagon datasheets as
|
||||
LaTeX.
|
||||
|
||||
It is also possible to export wagon datasheets using the `atdoc_write`
|
||||
command in Minetest.
|
||||
|
||||
The datasheets are exported to the world directory (or, for Mineunit,
|
||||
the fixtures directory). Note that the result of generating datasheets
|
||||
from Minetest and from CI may differ for various reasons.
|
||||
|
10
config.ld
10
config.ld
@ -5,4 +5,12 @@ project = "Documentation System Integration for Advtrains"
|
||||
title = "Manual for advtrains_doc_integration"
|
||||
package = "advtrains_doc_integration"
|
||||
readme = "README.md"
|
||||
-- vim: syntax=lua
|
||||
|
||||
custom_see_handler("^advtrains%.[%w%._]+$", function(name)
|
||||
return name, "https://git.bananach.space/advtrains.git/tree/advtrains/api_doc.txt"
|
||||
end)
|
||||
custom_see_handler("^SimpleSoundSpec$", function()
|
||||
return "SimpleSoundSpec", "https://api.minetest.net/sounds/#simplesoundspec"
|
||||
end)
|
||||
|
||||
-- vim: ft=lua
|
||||
|
@ -7,7 +7,7 @@ local utils = advtrains_doc_integration.utils
|
||||
local D = {}
|
||||
|
||||
--- Describe a conns object
|
||||
-- @tparam integer|table conns The conns object to describe.
|
||||
-- @tparam advtrains.conns|{advtrains.conns,number,...} 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"}
|
||||
|
324
doc_ui.lua
Normal file
324
doc_ui.lua
Normal file
@ -0,0 +1,324 @@
|
||||
local S = minetest.get_translator "advtrains_doc_integration"
|
||||
local D = advtrains_doc_integration.describe
|
||||
local H = advtrains_doc_integration.hypertext
|
||||
local utils = advtrains_doc_integration.utils
|
||||
local fsescape = minetest.formspec_escape
|
||||
|
||||
local function S2(a, b)
|
||||
return S(a, S(b))
|
||||
end
|
||||
|
||||
local function addlist(lst, tbl, title, fallback1, fallback2, mapf)
|
||||
if not tbl then
|
||||
if fallback2 then
|
||||
table.insert(lst, fallback2)
|
||||
elseif fallback2 == false and fallback1 then
|
||||
table.insert(lst, fallback1)
|
||||
end
|
||||
elseif next(tbl) ~= nil then
|
||||
table.insert(lst, title)
|
||||
for k, v in pairs(tbl) do
|
||||
if mapf then
|
||||
k = mapf(k, v)
|
||||
end
|
||||
table.insert(lst, H.listitem(k, true))
|
||||
end
|
||||
elseif fallback1 then
|
||||
table.insert(lst, fallback1)
|
||||
end
|
||||
end
|
||||
|
||||
local function list_itemstring(x)
|
||||
local item = ItemStack(x)
|
||||
if item:is_empty() then
|
||||
return S("Emptyness")
|
||||
end
|
||||
return string.format("%s: %d", item:get_short_description(), item:get_count())
|
||||
end
|
||||
|
||||
local function blankline(st)
|
||||
return table.insert(st, "")
|
||||
end
|
||||
|
||||
local wlivprev = {}
|
||||
|
||||
local function get_livery_preview_selection(pname, itemname)
|
||||
return (wlivprev[pname] or {})[itemname] or 0
|
||||
end
|
||||
|
||||
local function get_livery_preview(itemname, id)
|
||||
local tx = (advtrains_doc_integration.prototypes[itemname] or {}).livery_textures
|
||||
return tx[id] or tx[0]
|
||||
end
|
||||
|
||||
local function render_livery_textures(pname, itemname)
|
||||
local str = table.concat(utils.map(get_livery_preview(itemname, get_livery_preview_selection(pname, itemname)), fsescape), ",")
|
||||
return str
|
||||
end
|
||||
|
||||
local function set_livery_preview_selection(pname, itemname, id)
|
||||
local t = wlivprev[pname]
|
||||
if not t then
|
||||
t = {}
|
||||
wlivprev[pname] = t
|
||||
end
|
||||
t[itemname] = id
|
||||
end
|
||||
|
||||
local function doc_render_wagon_information(prototype, pname)
|
||||
local desctext = {}
|
||||
if prototype._doc_wagon_longdesc then
|
||||
table.insert(desctext, tostring(prototype._doc_wagon_longdesc))
|
||||
blankline(desctext)
|
||||
end
|
||||
table.insert(desctext, H.header(S("Basic Information")))
|
||||
table.insert(desctext, S("Itemstring: @1", H.mono(prototype.name)))
|
||||
addlist(desctext, prototype.drops, S("Drops:"), S("Drops nothing"), false, function(_, v) return list_itemstring(v) end)
|
||||
addlist(desctext, prototype.drives_on, S("Drives on:"), nil, nil, H.mono)
|
||||
addlist(desctext, prototype.coupler_types_front, S("Compatible front couplers:"), S2("Front coupler: @1", "Absent"), S2("Front coupler: @1", "Universal"), utils.get_coupler_name)
|
||||
addlist(desctext, prototype.coupler_types_back, S("Compatible rear couplers:"), S2("Rear coupler: @1", "Absent"), S2("Rear coupler: @1", "Universal"), utils.get_couple_name)
|
||||
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", H.describe_sound("playhorn", prototype.horn_sound)))
|
||||
if prototype.doors.open.sound or prototype.doors.close.sound then
|
||||
table.insert(desctext, S("Door sound: @1 (when opening), @2 (when closing)",
|
||||
H.describe_sound("playdooropen", prototype.doors.open.sound),
|
||||
H.describe_sound("playdoorclose", prototype.doors.close.sound)))
|
||||
else
|
||||
table.insert(desctext, S2("Door sound: @1", "Undefined"))
|
||||
end
|
||||
|
||||
blankline(desctext)
|
||||
table.insert(desctext, H.header(S("Wagon Capacity")))
|
||||
table.insert(desctext, S("Passenger seats: @1", prototype.max_passengers))
|
||||
table.insert(desctext, S("Driver seats: @1", prototype.max_drivers))
|
||||
if prototype.has_inventory then
|
||||
addlist(desctext, prototype.inventory_list_sizes, S("Cargo inventory size:"), S2("Cargo inventory: @1", "Present"), false, function(k, v)
|
||||
return string.format("%s: %d", H.mono(k), v)
|
||||
end)
|
||||
else
|
||||
table.insert(desctext, S2("Cargo inventory: @1", "Absent"))
|
||||
end
|
||||
if techage and prototype.techage_liquid_capacity then
|
||||
table.insert(desctext, S("Liquid Capacity (Techage): @1", string.format("%d", prototype.techage_liquid_capacity)))
|
||||
end
|
||||
|
||||
blankline(desctext)
|
||||
table.insert(desctext, H.header(S("Wagon Appearance")))
|
||||
table.insert(desctext, S("Mesh: @1", prototype.mesh and H.mono(prototype.mesh) or "None"))
|
||||
addlist(desctext, prototype.textures, S("Textures:"), S("No textures"), false, function(_, v) return H.mono(v) end)
|
||||
|
||||
local livsel = get_livery_preview_selection(pname, prototype.name)
|
||||
local livrst = (livsel ~= 0 and not prototype.dlxtrains_livery) and " " .. H.action("preview_0", S("[Reset Preview]")) or ""
|
||||
local livids = 0
|
||||
local function livprev(desc)
|
||||
livids = livids+1
|
||||
local label = H.plain(desc)
|
||||
if livids == livsel then
|
||||
label = H.bold(desc)
|
||||
end
|
||||
return H.action(string.format("preview_%d", livids), label, true)
|
||||
end
|
||||
|
||||
local dlxlivdef = prototype.dlxtrains_livery
|
||||
table.insert(desctext, S2("DlxTrains livery system: @1", dlxlivdef and "Supported" or "Unsupported"))
|
||||
if dlxlivdef then
|
||||
livids = -1
|
||||
table.insert(desctext, "Livery presets:")
|
||||
for k = 0, dlxlivdef.count-1 do
|
||||
table.insert(desctext, H.listitem(string.format("%s (%s, %s)", H.mono(dlxlivdef[k].code), livprev(S("default")), livprev(S("weathered"))), true))
|
||||
end
|
||||
end
|
||||
|
||||
local bikeliv = S("Unsupported")
|
||||
local bikelivdesc = nil
|
||||
if prototype.set_livery then
|
||||
if prototype.livery_definition then
|
||||
bikeliv = S("Supported by the multi_component_liveries mod")
|
||||
bikelivdesc = {}
|
||||
addlist(bikelivdesc, prototype.livery_definition.components, S("Livery components:"), nil, nil, function(_, v) return H.plain(v.description) end)
|
||||
addlist(bikelivdesc, prototype.livery_definition.presets, S("Livery presets:") .. livrst, nil, nil, function(_, v) return livprev(v.description) end)
|
||||
bikelivdesc = table.concat(bikelivdesc, "\n")
|
||||
else
|
||||
bikeliv = S("Supported")
|
||||
end
|
||||
end
|
||||
table.insert(desctext, S("Livery system with bike painter: @1", bikeliv))
|
||||
table.insert(desctext, bikelivdesc)
|
||||
|
||||
local atlivdef = prototype.advtrains_livery_tools
|
||||
table.insert(desctext, S2("Advtrains livery tools (Marnack): @1", atlivdef and "Supported" or "Unsupported"))
|
||||
if atlivdef then
|
||||
addlist(desctext, atlivdef.template_names, S("Livery templates:"), nil, nil, function(_, v) return H.plain(v) end)
|
||||
addlist(desctext, atlivdef.livery_names, S("Livery presets:") .. livrst, nil, nil, function(_, v) return livprev(v) end)
|
||||
end
|
||||
|
||||
blankline(desctext)
|
||||
table.insert(desctext, H.header(S("Implementation Details")))
|
||||
local attachment_offset_support = S("Unsupported")
|
||||
if advtrains_attachment_offset_patch then
|
||||
local t = advtrains_attachment_offset_patch
|
||||
if prototype.get_on == t.get_on_override and prototype.get_off == t.get_off_override then
|
||||
attachment_offset_support = S("Supported")
|
||||
end
|
||||
end
|
||||
|
||||
table.insert(desctext, S("Proper player attachment positioning: @1", attachment_offset_support))
|
||||
for k, v in pairs {
|
||||
custom_on_activate = "Custom instantiation callback",
|
||||
custom_on_step = "Custom step function",
|
||||
custom_on_velocity_change = "Custom velocity change callback",
|
||||
} do
|
||||
table.insert(desctext, S2(v .. ": @1", prototype[k] and "Defined" or "Undefined"))
|
||||
end
|
||||
|
||||
local x0, y0 = doc.FORMSPEC.ENTRY_START_X+0.25, doc.FORMSPEC.ENTRY_START_Y
|
||||
local x1, y1 = doc.FORMSPEC.ENTRY_END_X+0.75, doc.FORMSPEC.ENTRY_END_Y+0.625
|
||||
local width, height = x1-x0, y1-y0
|
||||
local mside = height/2
|
||||
|
||||
local mesh = fsescape(prototype.mesh or "")
|
||||
local textures = render_livery_textures(pname, prototype.name)
|
||||
local fstext = {
|
||||
string.format("hypertext[%f,%f;%f,%f;entry_body;%s]", x0, y0, width-mside, height+0.875, fsescape(table.concat(desctext, "\n"))),
|
||||
string.format("item_image[%f,%f;%f,%f;%s]", x1-mside, y0+0.0625, mside, mside, fsescape(prototype.name)),
|
||||
string.format("model[%f,%f;%f,%f;%s;%s;%s;%f,%f]",
|
||||
x1-mside, y1-mside, mside, mside, "wagon_model", mesh, textures, -30, 135),
|
||||
}
|
||||
return table.concat(fstext, "\n")
|
||||
end
|
||||
|
||||
if doc then
|
||||
advtrains_doc_integration.register_on_prototype_loaded(function(itemname, prototype)
|
||||
minetest.override_item(itemname, {_doc_items_create_entry = false})
|
||||
doc.add_entry("advtrains_wagons", itemname, {
|
||||
name = ItemStack(itemname):get_short_description(),
|
||||
data = prototype,
|
||||
})
|
||||
if doc.sub.identifier then
|
||||
doc.sub.identifier.register_object(itemname, "advtrains_wagons", itemname)
|
||||
end
|
||||
end)
|
||||
|
||||
if doc.sub.items then
|
||||
local register_factoid = doc.sub.items.register_factoid
|
||||
local function ndef_field_factoid(cat, ftype, f, ...)
|
||||
local ftp = type(f)
|
||||
if ftp == "string" then
|
||||
local desc = f
|
||||
f = function(x)
|
||||
if x ~= nil then
|
||||
return desc
|
||||
end
|
||||
end
|
||||
end
|
||||
local keys = {...}
|
||||
local idx = function(t)
|
||||
for _, k in ipairs(keys) do
|
||||
if type(t) ~= "table" then
|
||||
return nil
|
||||
end
|
||||
t = t[k]
|
||||
end
|
||||
return t
|
||||
end
|
||||
local function fgen(_, def)
|
||||
return f(idx(def)) or ""
|
||||
end
|
||||
return register_factoid(cat, ftype, fgen)
|
||||
end
|
||||
local function group_factoid(cat, gr, f)
|
||||
local function func(x)
|
||||
return f(x or 0)
|
||||
end
|
||||
return ndef_field_factoid(cat, "groups", func, "groups", gr)
|
||||
end
|
||||
for cat, cinfo in pairs{
|
||||
nodes = {
|
||||
not_blocking_trains = S("This block does not block trains."),
|
||||
save_in_at_nodedb = S("This block is saved in the Advtrains node database."),
|
||||
},
|
||||
} do
|
||||
for group, ginfo in pairs(cinfo) do
|
||||
local tp = type(ginfo)
|
||||
if tp == "string" then
|
||||
group_factoid(cat, group, function(x)
|
||||
if x > 0 then
|
||||
return ginfo
|
||||
end
|
||||
end)
|
||||
elseif tp == "function" then
|
||||
group_factoid(cat, group, ginfo)
|
||||
end
|
||||
end
|
||||
end
|
||||
for fname, t in pairs {
|
||||
advtrains = {
|
||||
on_train_enter = S("This track reacts to passing trains."),
|
||||
on_train_approach = S("This track reacts to approaching trains."),
|
||||
},
|
||||
luaautomation = {
|
||||
fire_event = S("This block handles LuaATC events."),
|
||||
},
|
||||
} do
|
||||
for subfname, val in pairs(t) do
|
||||
local function f(x)
|
||||
if x ~= nil then
|
||||
return val
|
||||
end
|
||||
end
|
||||
ndef_field_factoid("nodes", "groups", f, fname, subfname)
|
||||
end
|
||||
end
|
||||
register_factoid("nodes", "groups", function(_, ndef)
|
||||
if ndef.advtrains then
|
||||
local atdef = ndef.advtrains
|
||||
if atdef.set_aspect then
|
||||
return S("This is a signal with a variable aspect.")
|
||||
elseif atdef.get_aspect then
|
||||
return S("This is a signal with a static aspect.")
|
||||
end
|
||||
end
|
||||
if ndef.at_conns then
|
||||
return S("This track has the following conns table by default: @1", D.conns(ndef.at_conns) or "?")
|
||||
end
|
||||
return ""
|
||||
end)
|
||||
end
|
||||
|
||||
doc.add_category("advtrains_wagons", {
|
||||
name = S("Wagons"),
|
||||
build_formspec = doc_render_wagon_information,
|
||||
})
|
||||
end
|
||||
|
||||
minetest.register_on_player_receive_fields(function(player, formname, fields)
|
||||
if formname ~= "doc:entry" then
|
||||
return
|
||||
end
|
||||
local pname = player:get_player_name()
|
||||
local cat, ent = doc.get_selection(pname)
|
||||
if cat ~= "advtrains_wagons" or ent == nil then
|
||||
return
|
||||
end
|
||||
local act = fields.entry_body
|
||||
local prototype = advtrains_doc_integration.prototypes[ent]
|
||||
local sounds = {
|
||||
["action:playhorn"] = prototype.horn_sound,
|
||||
["action:playdooropen"] = prototype.doors.open.sound,
|
||||
["action:playdoorclose"] = prototype.doors.close.sound,
|
||||
}
|
||||
if not act then
|
||||
return
|
||||
elseif sounds[act] then
|
||||
minetest.sound_play(sounds[act], {to_player = pname}, true)
|
||||
else
|
||||
local txid = string.match(act, [[^action:preview_(%d+)$]])
|
||||
txid = tonumber(txid)
|
||||
if txid then
|
||||
set_livery_preview_selection(pname, ent, txid)
|
||||
doc.show_entry(pname, cat, ent)
|
||||
end
|
||||
end
|
||||
end)
|
92
hypertext.lua
Normal file
92
hypertext.lua
Normal file
@ -0,0 +1,92 @@
|
||||
--- Utilities for hypertext used in Minetest formspecs.
|
||||
-- @module advtrains_doc_integration.hypertext
|
||||
-- @alias H
|
||||
local H = {}
|
||||
local S = minetest.get_translator("advtrains_doc_integration")
|
||||
|
||||
--- Create a plain text hypertext element.
|
||||
-- @tparam string str The content of the element.
|
||||
-- @tparam[opt] boolean noescape Whether to avoid escaping the content.
|
||||
-- @treturn string The hypertext element containing the text.
|
||||
function H.plain(str, noescape)
|
||||
str = tostring(str)
|
||||
if noescape then
|
||||
return str
|
||||
end
|
||||
return (string.gsub(str, "([<>])", [[\%1]]))
|
||||
end
|
||||
|
||||
local function addtag(tag, attrs, str, noescape)
|
||||
local attrlist = {}
|
||||
for k, v in pairs(attrs) do
|
||||
table.insert(attrlist, (" %s=%s"):format(k, v))
|
||||
end
|
||||
return ("<%s%s>%s</%s>"):format(tag, table.concat(attrlist), H.plain(str, noescape), tag)
|
||||
end
|
||||
|
||||
--- Create a hypertext element with monospace text.
|
||||
-- @tparam string str The content of the element.
|
||||
-- @tparam[opt] boolean noescape Whether to avoid escaping the content.
|
||||
-- @treturn string The hypertext element containing the text.
|
||||
function H.mono(str, noescape)
|
||||
return addtag("mono", {}, str, noescape)
|
||||
end
|
||||
|
||||
--- Create a hypertext element with bold text.
|
||||
-- @tparam string str The content of the element.
|
||||
-- @tparam[opt] boolean noescape Whether to avoid escaping the content.
|
||||
-- @treturn string The hypertext element containing the text.
|
||||
function H.bold(str, noescape)
|
||||
return addtag("b", {}, str, noescape)
|
||||
end
|
||||
|
||||
--- Create a hypertext element with italic text.
|
||||
-- @tparam string str The content of the element.
|
||||
-- @tparam[opt] boolean noescape Whether to avoid escaping the content.
|
||||
-- @treturn string The hypertext element containing the text.
|
||||
function H.italic(str, noescape)
|
||||
return addtag("i", {}, str, noescape)
|
||||
end
|
||||
|
||||
--- Create a hypertext element with text formatted as header.
|
||||
-- @tparam string str The content of the element.
|
||||
-- @tparam[opt] boolean noescape Whether to avoid escaping the content.
|
||||
-- @treturn string The hypertext element containing the text.
|
||||
function H.header(str, noescape)
|
||||
return H.bold(str, noescape)
|
||||
end
|
||||
|
||||
--- Create a hypertext element with text formatted as an item in a list.
|
||||
-- @tparam string str The content of the element.
|
||||
-- @tparam[opt] boolean noescape Whether to avoid escaping the content.
|
||||
-- @treturn string The hypertext element containing the text.
|
||||
function H.listitem(str, noescape)
|
||||
return "• " .. H.plain(str, noescape)
|
||||
end
|
||||
|
||||
--- Create a hypertext element with an action.
|
||||
-- @tparam string action The name of the action.
|
||||
-- @tparam string str The content of the element.
|
||||
-- @tparam[opt] boolean noescape Whether to avoid escaping the content.
|
||||
-- @treturn string The hypertext element containing the text.
|
||||
function H.action(action, str, noescape)
|
||||
return addtag("action", {name = action},
|
||||
addtag("style", {color = "cyan"}, str, noescape), true)
|
||||
end
|
||||
|
||||
--- Describe a soundspec.
|
||||
-- @tparam[opt] string action The name of the action.
|
||||
-- @tparam SimpleSoundSpec soundspec The soundspec to describe.
|
||||
-- @treturn string The hypertext element describing the soundspec.
|
||||
function H.describe_sound(action, soundspec)
|
||||
if not soundspec then
|
||||
return S("Undefined")
|
||||
end
|
||||
local sounddesc = H.mono(soundspec.name)
|
||||
if action then
|
||||
sounddesc = H.action(action, sounddesc, true)
|
||||
end
|
||||
return sounddesc
|
||||
end
|
||||
|
||||
return H
|
776
init.lua
776
init.lua
@ -1,147 +1,16 @@
|
||||
--- Entry point.
|
||||
-- @module advtrains_doc_integration
|
||||
local worldpath = minetest.get_worldpath() .. "/"
|
||||
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
|
||||
advtrains_doc_integration = {}
|
||||
|
||||
for _, m in ipairs{"mathutils", "describe", "utils"} do
|
||||
for _, m in ipairs{
|
||||
"mathutils", "utils", "describe", "hypertext", "latex",
|
||||
} 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
|
||||
|
||||
local function Ss(x)
|
||||
if type(x) ~= "string" then
|
||||
return x
|
||||
end
|
||||
return minetest.get_translated_string("en", x)
|
||||
end
|
||||
|
||||
local function txescape(str)
|
||||
return (string.gsub(tostring(str), "[:^\\]", [[\%1]]))
|
||||
end
|
||||
|
||||
local function htescape(str)
|
||||
return (string.gsub(tostring(str), "([<>])", [[\%1]])) -- clip to one result
|
||||
end
|
||||
|
||||
local function latex_escape(str)
|
||||
return (string.gsub(str, ".", {
|
||||
["&"] = [[\&]],
|
||||
["%"] = [[\%]],
|
||||
["$"] = [[\$]],
|
||||
["#"] = [[\#]],
|
||||
["_"] = [[\_]],
|
||||
["{"] = [[\{]],
|
||||
["}"] = [[\}]],
|
||||
["~"] = [[\textasciitilde]],
|
||||
["^"] = [[\textasciicircum]],
|
||||
["\\"] = [[\textbackslash]],
|
||||
}))
|
||||
end
|
||||
|
||||
local function SL(x)
|
||||
return latex_escape(Ss(x))
|
||||
end
|
||||
|
||||
local function spairs(tbl, sort)
|
||||
local keys = {}
|
||||
local kn = {}
|
||||
for k in pairs(tbl or {}) do
|
||||
table.insert(keys, k)
|
||||
end
|
||||
table.sort(keys, sort)
|
||||
for i = 2, #keys do
|
||||
kn[keys[i-1]] = keys[i]
|
||||
end
|
||||
return function(t, n)
|
||||
local k = kn[n]
|
||||
if n == nil then
|
||||
k = keys[1]
|
||||
end
|
||||
return k, t[k]
|
||||
end, tbl, nil
|
||||
end
|
||||
|
||||
local function map(tbl, func)
|
||||
local t = {}
|
||||
for k, v in pairs(tbl or {}) do
|
||||
t[k] = func(v)
|
||||
end
|
||||
return t
|
||||
end
|
||||
|
||||
local function htmono(str)
|
||||
return string.format("<mono>%s</mono>", htescape(str))
|
||||
end
|
||||
|
||||
local function htbold(str)
|
||||
return string.format("<b>%s</b>", htescape(str))
|
||||
end
|
||||
|
||||
local function htheader(str)
|
||||
return htbold(str)
|
||||
end
|
||||
|
||||
local function htitem(str)
|
||||
return "• " .. str
|
||||
end
|
||||
|
||||
local function htaction(action, str, noesc)
|
||||
if not noesc then
|
||||
str = htescape(str)
|
||||
end
|
||||
return string.format("<action name=%s><style color=cyan>%s</style></action>", action, str)
|
||||
end
|
||||
|
||||
local function htsound(action, soundspec)
|
||||
if not soundspec then
|
||||
return S("Undefined")
|
||||
end
|
||||
local sounddesc = htmono(soundspec.name)
|
||||
if action then
|
||||
sounddesc = htaction(action, sounddesc, true)
|
||||
end
|
||||
return sounddesc
|
||||
end
|
||||
|
||||
local function addlist(lst, tbl, title, fallback1, fallback2, mapf)
|
||||
if not tbl then
|
||||
if fallback2 then
|
||||
table.insert(lst, fallback2)
|
||||
elseif fallback2 == false and fallback1 then
|
||||
table.insert(lst, fallback1)
|
||||
end
|
||||
elseif next(tbl) ~= nil then
|
||||
table.insert(lst, title)
|
||||
for k, v in pairs(tbl) do
|
||||
if mapf then
|
||||
k = mapf(k, v)
|
||||
end
|
||||
table.insert(lst, htitem(k))
|
||||
end
|
||||
elseif fallback1 then
|
||||
table.insert(lst, fallback1)
|
||||
end
|
||||
end
|
||||
|
||||
local function get_coupler_name(n)
|
||||
return advtrains.coupler_types[n] or n
|
||||
end
|
||||
|
||||
local function ht_coupler_name(n)
|
||||
local s = advtrains.coupler_types[n]
|
||||
if s then
|
||||
return htescape(s)
|
||||
else
|
||||
return htmono(n)
|
||||
end
|
||||
end
|
||||
local utils = advtrains_doc_integration.utils
|
||||
|
||||
local function dlxtrains_livery_information(prototype)
|
||||
if not dlxtrains then
|
||||
@ -183,16 +52,16 @@ do -- helper for Marnack's Advtrains livery tools
|
||||
return nil
|
||||
end
|
||||
local odef = template.overlays
|
||||
local textures = map(template.base_textures, function(str) return {str} end)
|
||||
for _, overlay in spairs(design.overlays) do
|
||||
local textures = utils.map(template.base_textures, function(str) return {str} end)
|
||||
for _, overlay in utils.spairs(design.overlays) do
|
||||
local o = odef[overlay.id]
|
||||
local t = textures[(o or {}).slot_idx]
|
||||
if t and o then
|
||||
local alpha = math.min(255, math.max(0, o.alpha or 255))
|
||||
table.insert(t, string.format("(%s^[colorize:%s:%d)", txescape(o.texture), txescape(overlay.color), alpha))
|
||||
table.insert(t, string.format("(%s^[colorize:%s:%d)", utils.texture_escape(o.texture), utils.texture_escape(overlay.color), alpha))
|
||||
end
|
||||
end
|
||||
return map(textures, function(st) return table.concat(st, "^") end)
|
||||
return utils.map(textures, function(st) return table.concat(st, "^") end)
|
||||
end
|
||||
local mt = {
|
||||
__index = atliv,
|
||||
@ -229,31 +98,9 @@ do -- helper for Marnack's Advtrains livery tools
|
||||
end
|
||||
end
|
||||
|
||||
local function list_itemstring(x)
|
||||
local item = ItemStack(x)
|
||||
if item:is_empty() then
|
||||
return S("Emptyness")
|
||||
end
|
||||
return string.format("%s: %d", item:get_short_description(), item:get_count())
|
||||
end
|
||||
|
||||
local function blankline(st)
|
||||
return table.insert(st, "")
|
||||
end
|
||||
|
||||
local prototype_cache = {}
|
||||
advtrains_doc_integration.prototypes = prototype_cache -- ONLY FOR DEBUGGING
|
||||
|
||||
local function adjust_soundspec(spec)
|
||||
if type(spec) == "string" then
|
||||
spec = {name = spec}
|
||||
end
|
||||
if type(spec) == "table" and spec.name and spec.name ~= "" then
|
||||
return spec
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
local function adjust_wagon_prototype(itemname, prototype)
|
||||
local p = prototype_cache[itemname]
|
||||
if p then
|
||||
@ -264,7 +111,7 @@ local function adjust_wagon_prototype(itemname, prototype)
|
||||
if p._doc_wagon_longdesc then
|
||||
p.longdesc = p._long_wagon_longdesc
|
||||
end
|
||||
p.horn_sound = adjust_soundspec(p.horn_sound)
|
||||
p.horn_sound = utils.adjust_soundspec(p.horn_sound)
|
||||
local pax, driver = 0, 0
|
||||
if p.seats and p.seat_groups then
|
||||
for _, v in pairs(p.seats) do
|
||||
@ -284,7 +131,7 @@ local function adjust_wagon_prototype(itemname, prototype)
|
||||
for _, state in pairs {"open", "close"} do
|
||||
local st = (prototype.doors or {})[state] or {}
|
||||
local state_data = {
|
||||
sound = adjust_soundspec(st.sound),
|
||||
sound = utils.adjust_soundspec(st.sound),
|
||||
}
|
||||
p.doors[state] = state_data
|
||||
end
|
||||
@ -316,7 +163,7 @@ local function adjust_wagon_prototype(itemname, prototype)
|
||||
local tx = table.copy(txbase)
|
||||
local st = {basefile}
|
||||
for _, l in ipairs(pdef.livery_stack.layers) do
|
||||
table.insert(st, string.format("(%s^[colorize:%s)", txescape(components[l.component].texture_file), txescape(l.color)))
|
||||
table.insert(st, string.format("(%s^[colorize:%s)", utils.texture_escape(components[l.component].texture_file), utils.texture_escape(l.color)))
|
||||
end
|
||||
tx[slot] = table.concat(st, "^")
|
||||
table.insert(p.livery_textures, tx)
|
||||
@ -333,545 +180,39 @@ local function adjust_wagon_prototype(itemname, prototype)
|
||||
return p
|
||||
end
|
||||
|
||||
local function doc_register_wagon(itemname)
|
||||
local prototype = adjust_wagon_prototype(itemname, advtrains.wagon_prototypes[itemname])
|
||||
prototype.name = itemname
|
||||
minetest.override_item(itemname, {_doc_items_create_entry = false})
|
||||
doc.add_entry("advtrains_wagons", itemname, {
|
||||
name = ItemStack(itemname):get_short_description(),
|
||||
data = prototype,
|
||||
})
|
||||
if doc.sub.identifier then
|
||||
doc.sub.identifier.register_object(itemname, "advtrains_wagons", itemname)
|
||||
end
|
||||
local registered_on_prototype_loaded = {}
|
||||
|
||||
--- Register a callback that will be run when a prototype becomes available.
|
||||
-- @tparam function callback The callback to run when the prototype
|
||||
-- becomes available. The callback function is passed the itemname and the
|
||||
-- prototype of the wagon.
|
||||
function advtrains_doc_integration.register_on_prototype_loaded(callback)
|
||||
table.insert(registered_on_prototype_loaded, callback)
|
||||
end
|
||||
|
||||
local wlivprev = {}
|
||||
|
||||
local function get_livery_preview_selection(pname, itemname)
|
||||
return (wlivprev[pname] or {})[itemname] or 0
|
||||
end
|
||||
|
||||
local function get_livery_preview(itemname, id)
|
||||
local tx = (prototype_cache[itemname] or {}).livery_textures
|
||||
return tx[id] or tx[0]
|
||||
end
|
||||
|
||||
local function render_livery_textures(pname, itemname)
|
||||
local str = table.concat(map(get_livery_preview(itemname, get_livery_preview_selection(pname, itemname)), fsescape), ",")
|
||||
return str
|
||||
end
|
||||
|
||||
local function set_livery_preview_selection(pname, itemname, id)
|
||||
local t = wlivprev[pname]
|
||||
if not t then
|
||||
t = {}
|
||||
wlivprev[pname] = t
|
||||
end
|
||||
t[itemname] = id
|
||||
end
|
||||
|
||||
local function doc_render_wagon_information(prototype, pname)
|
||||
local desctext = {}
|
||||
if prototype._doc_wagon_longdesc then
|
||||
table.insert(desctext, tostring(prototype._doc_wagon_longdesc))
|
||||
blankline(desctext)
|
||||
end
|
||||
table.insert(desctext, htheader(S("Basic Information")))
|
||||
table.insert(desctext, S("Itemstring: @1", htmono(prototype.name)))
|
||||
addlist(desctext, prototype.drops, S("Drops:"), S("Drops nothing"), false, function(_, v) return list_itemstring(v) end)
|
||||
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 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
|
||||
table.insert(desctext, S("Door sound: @1 (when opening), @2 (when closing)",
|
||||
htsound("playdooropen", prototype.doors.open.sound),
|
||||
htsound("playdoorclose", prototype.doors.close.sound)))
|
||||
else
|
||||
table.insert(desctext, S2("Door sound: @1", "Undefined"))
|
||||
end
|
||||
|
||||
blankline(desctext)
|
||||
table.insert(desctext, htheader(S("Wagon Capacity")))
|
||||
table.insert(desctext, S("Passenger seats: @1", prototype.max_passengers))
|
||||
table.insert(desctext, S("Driver seats: @1", prototype.max_drivers))
|
||||
if prototype.has_inventory then
|
||||
addlist(desctext, prototype.inventory_list_sizes, S("Cargo inventory size:"), S2("Cargo inventory: @1", "Present"), false, function(k, v)
|
||||
return string.format("%s: %d", htmono(k), v)
|
||||
end)
|
||||
else
|
||||
table.insert(desctext, S2("Cargo inventory: @1", "Absent"))
|
||||
end
|
||||
if techage and prototype.techage_liquid_capacity then
|
||||
table.insert(desctext, S("Liquid Capacity (Techage): @1", string.format("%d", prototype.techage_liquid_capacity)))
|
||||
end
|
||||
|
||||
blankline(desctext)
|
||||
table.insert(desctext, htheader(S("Wagon Appearance")))
|
||||
table.insert(desctext, S("Mesh: @1", prototype.mesh and htmono(prototype.mesh) or "None"))
|
||||
addlist(desctext, prototype.textures, S("Textures:"), S("No textures"), false, function(_, v) return htmono(v) end)
|
||||
|
||||
local livsel = get_livery_preview_selection(pname, prototype.name)
|
||||
local livrst = (livsel ~= 0 and not prototype.dlxtrains_livery) and " " .. htaction("preview_0", S("[Reset Preview]")) or ""
|
||||
local livids = 0
|
||||
local function livprev(desc)
|
||||
livids = livids+1
|
||||
local label = htescape(desc)
|
||||
if livids == livsel then
|
||||
label = htbold(desc)
|
||||
end
|
||||
return htaction(string.format("preview_%d", livids), label, true)
|
||||
end
|
||||
|
||||
local dlxlivdef = prototype.dlxtrains_livery
|
||||
table.insert(desctext, S2("DlxTrains livery system: @1", dlxlivdef and "Supported" or "Unsupported"))
|
||||
if dlxlivdef then
|
||||
livids = -1
|
||||
table.insert(desctext, "Livery presets:")
|
||||
for k = 0, dlxlivdef.count-1 do
|
||||
table.insert(desctext, htitem(string.format("%s (%s, %s)", htmono(dlxlivdef[k].code), livprev(S("default")), livprev(S("weathered")))))
|
||||
minetest.register_on_mods_loaded(function()
|
||||
for itemname, prototype in pairs(advtrains.wagon_prototypes) do
|
||||
prototype = adjust_wagon_prototype(itemname, prototype)
|
||||
prototype.name = itemname
|
||||
for _, cb in ipairs(registered_on_prototype_loaded) do
|
||||
cb(itemname, prototype)
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
local bikeliv = S("Unsupported")
|
||||
local bikelivdesc = nil
|
||||
if prototype.set_livery then
|
||||
if prototype.livery_definition then
|
||||
bikeliv = S("Supported by the multi_component_liveries mod")
|
||||
bikelivdesc = {}
|
||||
addlist(bikelivdesc, prototype.livery_definition.components, S("Livery components:"), nil, nil, function(_, v) return htescape(v.description) end)
|
||||
addlist(bikelivdesc, prototype.livery_definition.presets, S("Livery presets:") .. livrst, nil, nil, function(_, v) return livprev(v.description) end)
|
||||
bikelivdesc = table.concat(bikelivdesc, "\n")
|
||||
else
|
||||
bikeliv = S("Supported")
|
||||
end
|
||||
end
|
||||
table.insert(desctext, S("Livery system with bike painter: @1", bikeliv))
|
||||
table.insert(desctext, bikelivdesc)
|
||||
|
||||
local atlivdef = prototype.advtrains_livery_tools
|
||||
table.insert(desctext, S2("Advtrains livery tools (Marnack): @1", atlivdef and "Supported" or "Unsupported"))
|
||||
if atlivdef then
|
||||
addlist(desctext, atlivdef.template_names, S("Livery templates:"), nil, nil, function(_, v) return htescape(v) end)
|
||||
addlist(desctext, atlivdef.livery_names, S("Livery presets:") .. livrst, nil, nil, function(_, v) return livprev(v) end)
|
||||
end
|
||||
|
||||
blankline(desctext)
|
||||
table.insert(desctext, htheader(S("Implementation Details")))
|
||||
local attachment_offset_support = S("Unsupported")
|
||||
if advtrains_attachment_offset_patch then
|
||||
local t = advtrains_attachment_offset_patch
|
||||
if prototype.get_on == t.get_on_override and prototype.get_off == t.get_off_override then
|
||||
attachment_offset_support = S("Supported")
|
||||
end
|
||||
end
|
||||
|
||||
table.insert(desctext, S("Proper player attachment positioning: @1", attachment_offset_support))
|
||||
for k, v in pairs {
|
||||
custom_on_activate = "Custom instantiation callback",
|
||||
custom_on_step = "Custom step function",
|
||||
custom_on_velocity_change = "Custom velocity change callback",
|
||||
} do
|
||||
table.insert(desctext, S2(v .. ": @1", prototype[k] and "Defined" or "Undefined"))
|
||||
end
|
||||
|
||||
local x0, y0 = doc.FORMSPEC.ENTRY_START_X+0.25, doc.FORMSPEC.ENTRY_START_Y
|
||||
local x1, y1 = doc.FORMSPEC.ENTRY_END_X+0.75, doc.FORMSPEC.ENTRY_END_Y+0.625
|
||||
local width, height = x1-x0, y1-y0
|
||||
local mside = height/2
|
||||
|
||||
local mesh = fsescape(prototype.mesh or "")
|
||||
local textures = render_livery_textures(pname, prototype.name)
|
||||
local fstext = {
|
||||
string.format("hypertext[%f,%f;%f,%f;entry_body;%s]", x0, y0, width-mside, height+0.875, fsescape(table.concat(desctext, "\n"))),
|
||||
string.format("item_image[%f,%f;%f,%f;%s]", x1-mside, y0+0.0625, mside, mside, fsescape(prototype.name)),
|
||||
string.format("model[%f,%f;%f,%f;%s;%s;%s;%f,%f]",
|
||||
x1-mside, y1-mside, mside, mside, "wagon_model", mesh, textures, -30, 135),
|
||||
}
|
||||
return table.concat(fstext, "\n")
|
||||
end
|
||||
|
||||
if doc then
|
||||
minetest.register_on_mods_loaded(function()
|
||||
for k in pairs(advtrains.wagon_prototypes) do
|
||||
doc_register_wagon(k)
|
||||
end
|
||||
end)
|
||||
|
||||
if doc.sub.items then
|
||||
local register_factoid = doc.sub.items.register_factoid
|
||||
local function ndef_field_factoid(cat, ftype, f, ...)
|
||||
local ftp = type(f)
|
||||
if ftp == "string" then
|
||||
local desc = f
|
||||
f = function(x)
|
||||
if x ~= nil then
|
||||
return desc
|
||||
end
|
||||
end
|
||||
end
|
||||
local keys = {...}
|
||||
local idx = function(t)
|
||||
for _, k in ipairs(keys) do
|
||||
if type(t) ~= "table" then
|
||||
return nil
|
||||
end
|
||||
t = t[k]
|
||||
end
|
||||
return t
|
||||
end
|
||||
local function fgen(_, def)
|
||||
return f(idx(def)) or ""
|
||||
end
|
||||
return register_factoid(cat, ftype, fgen)
|
||||
end
|
||||
local function group_factoid(cat, gr, f)
|
||||
local function func(x)
|
||||
return f(x or 0)
|
||||
end
|
||||
return ndef_field_factoid(cat, "groups", func, "groups", gr)
|
||||
end
|
||||
for cat, cinfo in pairs{
|
||||
nodes = {
|
||||
not_blocking_trains = S("This block does not block trains."),
|
||||
save_in_at_nodedb = S("This block is saved in the Advtrains node database."),
|
||||
},
|
||||
} do
|
||||
for group, ginfo in pairs(cinfo) do
|
||||
local tp = type(ginfo)
|
||||
if tp == "string" then
|
||||
group_factoid(cat, group, function(x)
|
||||
if x > 0 then
|
||||
return ginfo
|
||||
end
|
||||
end)
|
||||
elseif tp == "function" then
|
||||
group_factoid(cat, group, ginfo)
|
||||
end
|
||||
end
|
||||
end
|
||||
for fname, t in pairs {
|
||||
advtrains = {
|
||||
on_train_enter = S("This track reacts to passing trains."),
|
||||
on_train_approach = S("This track reacts to approaching trains."),
|
||||
},
|
||||
luaautomation = {
|
||||
fire_event = S("This block handles LuaATC events."),
|
||||
},
|
||||
} do
|
||||
for subfname, val in pairs(t) do
|
||||
local function f(x)
|
||||
if x ~= nil then
|
||||
return val
|
||||
end
|
||||
end
|
||||
ndef_field_factoid("nodes", "groups", f, fname, subfname)
|
||||
end
|
||||
end
|
||||
register_factoid("nodes", "groups", function(_, ndef)
|
||||
if ndef.advtrains then
|
||||
local atdef = ndef.advtrains
|
||||
if atdef.set_aspect then
|
||||
return S("This is a signal with a variable aspect.")
|
||||
elseif atdef.get_aspect then
|
||||
return S("This is a signal with a static aspect.")
|
||||
end
|
||||
end
|
||||
if ndef.at_conns then
|
||||
return S("This track has the following conns table by default: @1", D.conns(ndef.at_conns) or "?")
|
||||
end
|
||||
return ""
|
||||
end)
|
||||
end
|
||||
|
||||
doc.add_category("advtrains_wagons", {
|
||||
name = S("Wagons"),
|
||||
build_formspec = doc_render_wagon_information,
|
||||
})
|
||||
end
|
||||
|
||||
local function latex_colordesc(cstr)
|
||||
local color = string.match(cstr,"^#(%x%x%x%x%x%x)$")
|
||||
cstr = SL(cstr)
|
||||
if color then
|
||||
color = SL(string.upper(color))
|
||||
return string.format([[\tikz \definecolor{c}{HTML}{%s} \draw[fill=c] (0,0) rectangle (1em,1em); \texttt{%s}]], color, cstr)
|
||||
else
|
||||
return string.format([[\texttt{%s}]], cstr)
|
||||
end
|
||||
end
|
||||
|
||||
--- Write a wagon datasheet to a LaTeX file in the world path.
|
||||
-- @tparam string itemname The item name of the wagon prototype.
|
||||
function advtrains_doc_integration.write_wagon_info_as_latex(itemname)
|
||||
local filename = string.format("%satdoc_wagon_%s.tex", worldpath, itemname:gsub(":", "_"))
|
||||
local prototype = adjust_wagon_prototype(itemname, advtrains.wagon_prototypes[itemname])
|
||||
local wname = ItemStack(itemname):get_short_description()
|
||||
local st = {string.format([[
|
||||
\documentclass{article}
|
||||
\usepackage[a4paper,margin=1in,bottom=1.5in]{geometry}
|
||||
\usepackage[T1]{fontenc}
|
||||
\usepackage{tikz}
|
||||
\usepackage{booktabs,multirow,tabularx}
|
||||
\renewcommand{\arraystretch}{1.5}
|
||||
\usepackage{hyperref}
|
||||
\hypersetup{pdftitle={Wagon Datasheet: %s}}
|
||||
\title{Wagon Datasheet}
|
||||
\author{%s}
|
||||
\setlength{\parindent}{0pt}
|
||||
\begin{document}
|
||||
\maketitle
|
||||
]], SL(wname), SL(wname))}
|
||||
|
||||
table.insert(st, [[\section{Basic Information}]])
|
||||
if prototype.longdesc then
|
||||
table.insert(st, SL(prototype.longdesc) .. "\n")
|
||||
local filename = string.format("%s/atdoc_wagon_%s.tex", worldpath, itemname:gsub(":", "_"))
|
||||
local description = advtrains_doc_integration.latex.describe_wagon_prototype(itemname)
|
||||
if description then
|
||||
minetest.safe_file_write(filename, description)
|
||||
end
|
||||
table.insert(st, [[\begin{tabularx}{\textwidth}{l X}]])
|
||||
table.insert(st, string.format([[Itemstring & \texttt{%s}\\]], SL(itemname)))
|
||||
if prototype.drives_on then
|
||||
local i0 = #st+1
|
||||
local count = 0
|
||||
for k in pairs(prototype.drives_on) do
|
||||
table.insert(st, string.format([[& \texttt{%s}\\]], SL(k)))
|
||||
count = count + 1
|
||||
end
|
||||
if count > 0 then
|
||||
st[i0] = string.format([[Drives on %s]], st[i0])
|
||||
end
|
||||
end
|
||||
if prototype.wagon_span then
|
||||
table.insert(st, string.format([[Wagon span & %d mm\\]], prototype.wagon_span*2000))
|
||||
end
|
||||
if prototype.max_speed then
|
||||
table.insert(st, string.format([[Maximum speed & %d m/s\\]], prototype.max_speed))
|
||||
end
|
||||
table.insert(st, string.format([[Motive power & %s\\]], prototype.is_locomotive and "Present" or "Absent"))
|
||||
if prototype.horn_sound then
|
||||
table.insert(st, string.format([[Horn sound & \texttt{%s}\\]], SL(prototype.horn_sound.name)))
|
||||
else
|
||||
table.insert(st, [[Horn sound & Undefined\\]])
|
||||
end
|
||||
if prototype.mesh then
|
||||
table.insert(st, string.format([[Mesh & \texttt{%s}\\]], SL(prototype.mesh)))
|
||||
end
|
||||
if prototype.textures then
|
||||
local i0 = #st+1
|
||||
local count = 0
|
||||
for _, i in pairs(prototype.textures) do
|
||||
table.insert(st, string.format([[& \texttt{%s}\\]], SL(i)))
|
||||
count = count + 1
|
||||
end
|
||||
if count > 0 then
|
||||
st[i0] = string.format([[Textures %s]], st[i0])
|
||||
end
|
||||
end
|
||||
do
|
||||
local i0 = #st+1
|
||||
local count = 0
|
||||
for _, i in ipairs(prototype.drops or {}) do
|
||||
local item = ItemStack(i)
|
||||
if not item:is_empty() then
|
||||
local desc = string.format([[\texttt{%s}]], SL(item:get_name()))
|
||||
if item:is_known() then
|
||||
desc = SL(item:get_short_description())
|
||||
end
|
||||
table.insert(st, string.format([[& %s: %d\\]], desc, item:get_count()))
|
||||
count = count + 1
|
||||
end
|
||||
end
|
||||
if count > 0 then
|
||||
st[i0] = [[Drops ]] .. st[i0]
|
||||
else
|
||||
table.insert(st, [[Drops & Nothing \\]])
|
||||
end
|
||||
end
|
||||
table.insert(st, [[\end{tabularx}]])
|
||||
|
||||
table.insert(st, [[\section{Coupler Compatibility}]])
|
||||
do
|
||||
local fcouplers = prototype.coupler_types_front
|
||||
local rcouplers = prototype.coupler_types_back
|
||||
local ccouplers = {}
|
||||
local lcouplers = {}
|
||||
local couplerid = {}
|
||||
local flim, rlim
|
||||
for k in pairs(fcouplers or {}) do
|
||||
flim = true
|
||||
ccouplers[k] = true
|
||||
end
|
||||
for k in pairs(rcouplers or {}) do
|
||||
rlim = true
|
||||
ccouplers[k] = true
|
||||
end
|
||||
for k in pairs(ccouplers) do
|
||||
local desc = SL(get_coupler_name(k))
|
||||
table.insert(lcouplers, desc)
|
||||
couplerid[desc] = k
|
||||
end
|
||||
table.sort(lcouplers)
|
||||
table.insert(st, [[
|
||||
\begin{tabularx}{\textwidth}{X c c}
|
||||
\toprule
|
||||
\multirow[t]{2}{*}{\bfseries Coupler Type} & \multicolumn{2}{c}{\bfseries Compatibility}\\
|
||||
\cmidrule(lr){2-3}
|
||||
& {\bfseries Front Coupler} & {\bfseries Rear Coupler}\\\midrule
|
||||
]])
|
||||
if not (fcouplers and rcouplers) then
|
||||
local fd = fcouplers and "" or [[$\bullet$]]
|
||||
local rd = rcouplers and "" or [[$\bullet$]]
|
||||
table.insert(st, string.format([[\textit{Universal}&%s&%s\\]], fd, rd))
|
||||
end
|
||||
for i = 1, #lcouplers do
|
||||
local cdesc = lcouplers[i]
|
||||
local cid = couplerid[cdesc]
|
||||
local fd, rd = "", ""
|
||||
if flim then
|
||||
fd = fcouplers[cid] and [[$\bullet$]] or ""
|
||||
elseif not fcouplers then
|
||||
fd = [[$\Uparrow$]]
|
||||
end
|
||||
if rlim then
|
||||
rd = rcouplers[cid] and [[$\bullet$]] or ""
|
||||
elseif not rcouplers then
|
||||
rd = [[$\Uparrow$]]
|
||||
end
|
||||
table.insert(st, string.format([[%s&%s&%s\\]], cdesc, fd, rd))
|
||||
end
|
||||
table.insert(st, [[\bottomrule]])
|
||||
table.insert(st, [[\end{tabularx}]])
|
||||
end
|
||||
|
||||
local hasinv = prototype.has_inventory
|
||||
local hasseats = prototype.max_seats>0
|
||||
local taliquid = prototype.techage_liquid_capacity or 0
|
||||
if hasinv or hasseats or taliquid>0 then
|
||||
table.insert(st, [[\section{Wagon Capacity}]])
|
||||
if hasseats then
|
||||
table.insert(st, [[
|
||||
\begin{tabularx}{\textwidth}{X c c}
|
||||
\toprule
|
||||
{\bfseries Seat Group} & {\bfseries Driver Stand} & {\bfseries Seat Count}\\\midrule
|
||||
]])
|
||||
for _, d in pairs(prototype.seat_groups) do
|
||||
table.insert(st, string.format([[%s & %s & %d\\]], SL(d.name), d.driving_ctrl_access and [[$\bullet$]] or "", d.count))
|
||||
end
|
||||
table.insert(st, [[\bottomrule]])
|
||||
table.insert(st, [[\end{tabularx}]])
|
||||
end
|
||||
if hasinv then
|
||||
if next(prototype.inventory_list_sizes or {}) ~= nil then
|
||||
table.insert(st, [[
|
||||
\begin{tabularx}{\textwidth}{X c}
|
||||
\toprule
|
||||
{\bfseries Inventory Name} & {\bfseries Capacity}\\\midrule
|
||||
]])
|
||||
for k, v in pairs(prototype.inventory_list_sizes) do
|
||||
table.insert(st, string.format([[\texttt{%s} & %d\\]], SL(k), v))
|
||||
end
|
||||
table.insert(st, [[\bottomrule]])
|
||||
table.insert(st, [[\end{tabularx}]])
|
||||
else
|
||||
table.insert(st, [[This wagon has an inventory of unknown size.]])
|
||||
end
|
||||
end
|
||||
if taliquid > 0 then
|
||||
table.insert(st, string.format([[
|
||||
\begin{tabularx}{\textwidth}{X l}
|
||||
{Liquid Capacity (Techage)} & %d
|
||||
\end{tabularx}
|
||||
]], taliquid))
|
||||
end
|
||||
end
|
||||
|
||||
if prototype.set_livery then
|
||||
if prototype.livery_definition then
|
||||
table.insert(st, [[\section{Multi-Component Liveries}]])
|
||||
local components = prototype.livery_definition.components
|
||||
local presets = prototype.livery_definition.presets
|
||||
table.insert(st, [[\subsection*{Components}]])
|
||||
table.insert(st, [[\begin{itemize}]])
|
||||
for _, c in ipairs(components) do
|
||||
table.insert(st, string.format([[\item %s]], SL(c.description)))
|
||||
end
|
||||
table.insert(st, [[\end{itemize}]])
|
||||
for _, p in ipairs(presets) do
|
||||
table.insert(st, string.format([[\subsection*{Preset: %s}]], SL(p.description)))
|
||||
table.insert(st, [[
|
||||
\begin{tabularx}{\textwidth}{X c}
|
||||
\toprule
|
||||
{\bfseries Component} & {\bfseries Color} \\\midrule
|
||||
]])
|
||||
for _, c in ipairs(p.livery_stack.layers) do
|
||||
local cdesc = SL(components[c.component].description)
|
||||
table.insert(st, string.format([[%s & %s\\]], cdesc, latex_colordesc(c.color)))
|
||||
end
|
||||
table.insert(st, [[
|
||||
\bottomrule
|
||||
\end{tabularx}
|
||||
]])
|
||||
end
|
||||
else
|
||||
table.insert(st, [[\section{Livery System (Bike Painter)}]])
|
||||
table.insert(st, [[This wagon can be painted by the bike painter.]])
|
||||
end
|
||||
end
|
||||
|
||||
local dlxlivdef = dlxtrains_livery_information(prototype)
|
||||
if dlxlivdef then
|
||||
table.insert(st, [[
|
||||
\section{DlxTrains Livery Sytem}
|
||||
This wagon can be customized with DlxTrains' livery system.
|
||||
]])
|
||||
end
|
||||
|
||||
local atlivdef = advtrains_livery_tools_information(itemname)
|
||||
if atlivdef then
|
||||
table.insert(st, [[\section{Advtrains Livery Tool (Marnack)}]])
|
||||
for _, tname in ipairs(atlivdef.template_names) do
|
||||
local tdef = atlivdef.templates[tname]
|
||||
table.insert(st, string.format([[\subsection*{Template: %s}]], SL(tname)))
|
||||
table.insert(st, SL(tdef.notes))
|
||||
table.insert(st, "")
|
||||
table.insert(st, "This template contains the following components:")
|
||||
table.insert(st, [[\begin{itemize}]])
|
||||
for _, overlay in ipairs(tdef.overlays) do
|
||||
table.insert(st, string.format([[\item %s]], SL(overlay.name)))
|
||||
end
|
||||
table.insert(st, [[\end{itemize}]])
|
||||
end
|
||||
for _, lname in ipairs(atlivdef.livery_names) do
|
||||
local ldef = atlivdef.liveries[lname]
|
||||
local tname = ldef.livery_template_name
|
||||
table.insert(st, string.format([[\subsection*{Preset: %s}]], SL(lname)))
|
||||
table.insert(st, string.format([[Template: %s]], SL(tname)))
|
||||
table.insert(st, "")
|
||||
table.insert(st, [[
|
||||
\begin{tabularx}{\textwidth}{X c}
|
||||
\toprule
|
||||
{\bfseries Component} & {\bfseries Color}\\\midrule]])
|
||||
for _, overlay in pairs(ldef.overlays) do
|
||||
local cname = atlivdef.templates[tname].overlays[overlay.id].name
|
||||
table.insert(st, string.format([[%s & %s\\]], SL(cname), latex_colordesc(overlay.color)))
|
||||
end
|
||||
table.insert(st, [[
|
||||
\bottomrule
|
||||
\end{tabularx}
|
||||
]])
|
||||
end
|
||||
end
|
||||
|
||||
table.insert(st, [[
|
||||
\end{document}
|
||||
]])
|
||||
st = table.concat(st, "\n")
|
||||
minetest.safe_file_write(filename, st)
|
||||
end
|
||||
|
||||
--- Write a wagon datasheet for each wagon using @{write_wagon_info_as_latex}.
|
||||
function advtrains_doc_integration.write_all_wagons_as_latex()
|
||||
for k in pairs(advtrains.wagon_prototypes) do
|
||||
for k in pairs(prototype_cache) do
|
||||
advtrains_doc_integration.write_wagon_info_as_latex(k)
|
||||
end
|
||||
end
|
||||
@ -885,50 +226,7 @@ minetest.register_chatcommand("atdoc_write", {
|
||||
end,
|
||||
})
|
||||
|
||||
minetest.register_on_player_receive_fields(function(player, formname, fields)
|
||||
if formname ~= "doc:entry" then
|
||||
return
|
||||
end
|
||||
local pname = player:get_player_name()
|
||||
local cat, ent = doc.get_selection(pname)
|
||||
if cat ~= "advtrains_wagons" or ent == nil then
|
||||
return
|
||||
end
|
||||
local act = fields.entry_body
|
||||
local prototype = prototype_cache[ent]
|
||||
local sounds = {
|
||||
["action:playhorn"] = prototype.horn_sound,
|
||||
["action:playdooropen"] = prototype.doors.open.sound,
|
||||
["action:playdoorclose"] = prototype.doors.close.sound,
|
||||
}
|
||||
if not act then
|
||||
return
|
||||
elseif sounds[act] then
|
||||
minetest.sound_play(sounds[act], {to_player = pname}, true)
|
||||
else
|
||||
local txid = string.match(act, [[^action:preview_(%d+)$]])
|
||||
txid = tonumber(txid)
|
||||
if txid then
|
||||
set_livery_preview_selection(pname, ent, txid)
|
||||
doc.show_entry(pname, cat, ent)
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
dofile(modpath .. "/doc_ui.lua")
|
||||
if minetest.get_modpath("mtt") then
|
||||
if doc then
|
||||
mtt.register("wagon information formspec", function(cb)
|
||||
local player = mtt.join_player("singleplayer")
|
||||
for k in pairs(prototype_cache) do
|
||||
print("Found wagon prototype: " .. k)
|
||||
doc.show_entry("singleplayer", "advtrains_wagons", k, true)
|
||||
end
|
||||
player:leave()
|
||||
cb()
|
||||
end)
|
||||
end
|
||||
mtt.register("wagon datasheet generation", function(cb)
|
||||
advtrains_doc_integration.write_all_wagons_as_latex()
|
||||
cb()
|
||||
end)
|
||||
dofile(modpath .. "/mtt.lua")
|
||||
end
|
||||
|
316
latex.lua
Normal file
316
latex.lua
Normal file
@ -0,0 +1,316 @@
|
||||
--- Code related to LaTeX generation.
|
||||
-- @module advtrains_doc_integration.latex
|
||||
local latex = {}
|
||||
local utils = advtrains_doc_integration.utils
|
||||
|
||||
--- Escape string in LaTeX.
|
||||
-- @tparam string str The string to escape.
|
||||
-- @treturn string The escaped string.
|
||||
function latex.escape(str)
|
||||
return (string.gsub(str, ".", {
|
||||
["&"] = [[\&]],
|
||||
["%"] = [[\%]],
|
||||
["$"] = [[\$]],
|
||||
["#"] = [[\#]],
|
||||
["_"] = [[\_]],
|
||||
["{"] = [[\{]],
|
||||
["}"] = [[\}]],
|
||||
["~"] = [[\textasciitilde]],
|
||||
["^"] = [[\textasciicircum]],
|
||||
["\\"] = [[\textbackslash]],
|
||||
}))
|
||||
end
|
||||
|
||||
--- Escape a translated string in LaTeX.
|
||||
-- @tparam string lang The language to use for server-side translation
|
||||
-- @tparam string str The string to escape.
|
||||
-- @treturn The escaped string.
|
||||
function latex.escape_translated(lang, str)
|
||||
return latex.escape(minetest.get_translated_string(lang, str))
|
||||
end
|
||||
|
||||
local function SL(str)
|
||||
return latex.escape_translated("en", str)
|
||||
end
|
||||
|
||||
--- Describe a color in LaTeX.
|
||||
-- @tparam string cstr The color string.
|
||||
-- @treturn string The string describing the color.
|
||||
function latex.describe_color(cstr)
|
||||
local color = string.match(cstr,"^#(%x%x%x%x%x%x)$")
|
||||
cstr = SL(cstr)
|
||||
if color then
|
||||
color = SL(string.upper(color))
|
||||
return string.format([[\tikz \definecolor{c}{HTML}{%s} \draw[fill=c] (0,0) rectangle (1em,1em); \texttt{%s}]], color, cstr)
|
||||
else
|
||||
return string.format([[\texttt{%s}]], cstr)
|
||||
end
|
||||
end
|
||||
|
||||
--- Describe a wagon prototype in LaTeX.
|
||||
-- @tparam string itemname The item name of the wagon prototype.
|
||||
-- @treturn string The description of the prototype.
|
||||
function latex.describe_wagon_prototype(itemname)
|
||||
local prototype = advtrains_doc_integration.prototypes[itemname]
|
||||
local wname = ItemStack(itemname):get_short_description()
|
||||
local st = {string.format([[
|
||||
\documentclass{article}
|
||||
\usepackage[a4paper,margin=1in,bottom=1.5in]{geometry}
|
||||
\usepackage[T1]{fontenc}
|
||||
\usepackage{tikz}
|
||||
\usepackage{booktabs,multirow,tabularx}
|
||||
\renewcommand{\arraystretch}{1.5}
|
||||
\usepackage{hyperref}
|
||||
\hypersetup{pdftitle={Wagon Datasheet: %s}}
|
||||
\title{Wagon Datasheet}
|
||||
\author{%s}
|
||||
\setlength{\parindent}{0pt}
|
||||
\begin{document}
|
||||
\maketitle
|
||||
]], SL(wname), SL(wname))}
|
||||
|
||||
table.insert(st, [[\section{Basic Information}]])
|
||||
if prototype.longdesc then
|
||||
table.insert(st, SL(prototype.longdesc) .. "\n")
|
||||
end
|
||||
table.insert(st, [[\begin{tabularx}{\textwidth}{l X}]])
|
||||
table.insert(st, string.format([[Itemstring & \texttt{%s}\\]], SL(itemname)))
|
||||
if prototype.drives_on then
|
||||
local i0 = #st+1
|
||||
local count = 0
|
||||
for k in pairs(prototype.drives_on) do
|
||||
table.insert(st, string.format([[& \texttt{%s}\\]], SL(k)))
|
||||
count = count + 1
|
||||
end
|
||||
if count > 0 then
|
||||
st[i0] = string.format([[Drives on %s]], st[i0])
|
||||
end
|
||||
end
|
||||
if prototype.wagon_span then
|
||||
table.insert(st, string.format([[Wagon span & %d mm\\]], prototype.wagon_span*2000))
|
||||
end
|
||||
if prototype.max_speed then
|
||||
table.insert(st, string.format([[Maximum speed & %d m/s\\]], prototype.max_speed))
|
||||
end
|
||||
table.insert(st, string.format([[Motive power & %s\\]], prototype.is_locomotive and "Present" or "Absent"))
|
||||
if prototype.horn_sound then
|
||||
table.insert(st, string.format([[Horn sound & \texttt{%s}\\]], SL(prototype.horn_sound.name)))
|
||||
else
|
||||
table.insert(st, [[Horn sound & Undefined\\]])
|
||||
end
|
||||
if prototype.mesh then
|
||||
table.insert(st, string.format([[Mesh & \texttt{%s}\\]], SL(prototype.mesh)))
|
||||
end
|
||||
if prototype.textures then
|
||||
local i0 = #st+1
|
||||
local count = 0
|
||||
for _, i in pairs(prototype.textures) do
|
||||
table.insert(st, string.format([[& \texttt{%s}\\]], SL(i)))
|
||||
count = count + 1
|
||||
end
|
||||
if count > 0 then
|
||||
st[i0] = string.format([[Textures %s]], st[i0])
|
||||
end
|
||||
end
|
||||
do
|
||||
local i0 = #st+1
|
||||
local count = 0
|
||||
for _, i in ipairs(prototype.drops or {}) do
|
||||
local item = ItemStack(i)
|
||||
if not item:is_empty() then
|
||||
local desc = string.format([[\texttt{%s}]], SL(item:get_name()))
|
||||
if item:is_known() then
|
||||
desc = SL(item:get_short_description())
|
||||
end
|
||||
table.insert(st, string.format([[& %s: %d\\]], desc, item:get_count()))
|
||||
count = count + 1
|
||||
end
|
||||
end
|
||||
if count > 0 then
|
||||
st[i0] = [[Drops ]] .. st[i0]
|
||||
else
|
||||
table.insert(st, [[Drops & Nothing \\]])
|
||||
end
|
||||
end
|
||||
table.insert(st, [[\end{tabularx}]])
|
||||
|
||||
table.insert(st, [[\section{Coupler Compatibility}]])
|
||||
do
|
||||
local fcouplers = prototype.coupler_types_front
|
||||
local rcouplers = prototype.coupler_types_back
|
||||
local ccouplers = {}
|
||||
local lcouplers = {}
|
||||
local couplerid = {}
|
||||
local flim, rlim
|
||||
for k in pairs(fcouplers or {}) do
|
||||
flim = true
|
||||
ccouplers[k] = true
|
||||
end
|
||||
for k in pairs(rcouplers or {}) do
|
||||
rlim = true
|
||||
ccouplers[k] = true
|
||||
end
|
||||
for k in pairs(ccouplers) do
|
||||
local desc = SL(utils.get_coupler_name(k))
|
||||
table.insert(lcouplers, desc)
|
||||
couplerid[desc] = k
|
||||
end
|
||||
table.sort(lcouplers)
|
||||
table.insert(st, [[
|
||||
\begin{tabularx}{\textwidth}{X c c}
|
||||
\toprule
|
||||
\multirow[t]{2}{*}{\bfseries Coupler Type} & \multicolumn{2}{c}{\bfseries Compatibility}\\
|
||||
\cmidrule(lr){2-3}
|
||||
& {\bfseries Front Coupler} & {\bfseries Rear Coupler}\\\midrule
|
||||
]])
|
||||
if not (fcouplers and rcouplers) then
|
||||
local fd = fcouplers and "" or [[$\bullet$]]
|
||||
local rd = rcouplers and "" or [[$\bullet$]]
|
||||
table.insert(st, string.format([[\textit{Universal}&%s&%s\\]], fd, rd))
|
||||
end
|
||||
for i = 1, #lcouplers do
|
||||
local cdesc = lcouplers[i]
|
||||
local cid = couplerid[cdesc]
|
||||
local fd, rd = "", ""
|
||||
if flim then
|
||||
fd = fcouplers[cid] and [[$\bullet$]] or ""
|
||||
elseif not fcouplers then
|
||||
fd = [[$\Uparrow$]]
|
||||
end
|
||||
if rlim then
|
||||
rd = rcouplers[cid] and [[$\bullet$]] or ""
|
||||
elseif not rcouplers then
|
||||
rd = [[$\Uparrow$]]
|
||||
end
|
||||
table.insert(st, string.format([[%s&%s&%s\\]], cdesc, fd, rd))
|
||||
end
|
||||
table.insert(st, [[\bottomrule]])
|
||||
table.insert(st, [[\end{tabularx}]])
|
||||
end
|
||||
|
||||
local hasinv = prototype.has_inventory
|
||||
local hasseats = prototype.max_seats>0
|
||||
local taliquid = prototype.techage_liquid_capacity or 0
|
||||
if hasinv or hasseats or taliquid>0 then
|
||||
table.insert(st, [[\section{Wagon Capacity}]])
|
||||
if hasseats then
|
||||
table.insert(st, [[
|
||||
\begin{tabularx}{\textwidth}{X c c}
|
||||
\toprule
|
||||
{\bfseries Seat Group} & {\bfseries Driver Stand} & {\bfseries Seat Count}\\\midrule
|
||||
]])
|
||||
for _, d in pairs(prototype.seat_groups) do
|
||||
table.insert(st, string.format([[%s & %s & %d\\]], SL(d.name), d.driving_ctrl_access and [[$\bullet$]] or "", d.count))
|
||||
end
|
||||
table.insert(st, [[\bottomrule]])
|
||||
table.insert(st, [[\end{tabularx}]])
|
||||
end
|
||||
if hasinv then
|
||||
if next(prototype.inventory_list_sizes or {}) ~= nil then
|
||||
table.insert(st, [[
|
||||
\begin{tabularx}{\textwidth}{X c}
|
||||
\toprule
|
||||
{\bfseries Inventory Name} & {\bfseries Capacity}\\\midrule
|
||||
]])
|
||||
for k, v in pairs(prototype.inventory_list_sizes) do
|
||||
table.insert(st, string.format([[\texttt{%s} & %d\\]], SL(k), v))
|
||||
end
|
||||
table.insert(st, [[\bottomrule]])
|
||||
table.insert(st, [[\end{tabularx}]])
|
||||
else
|
||||
table.insert(st, [[This wagon has an inventory of unknown size.]])
|
||||
end
|
||||
end
|
||||
if taliquid > 0 then
|
||||
table.insert(st, string.format([[
|
||||
\begin{tabularx}{\textwidth}{X l}
|
||||
{Liquid Capacity (Techage)} & %d
|
||||
\end{tabularx}
|
||||
]], taliquid))
|
||||
end
|
||||
end
|
||||
|
||||
if prototype.set_livery then
|
||||
if prototype.livery_definition then
|
||||
table.insert(st, [[\section{Multi-Component Liveries}]])
|
||||
local components = prototype.livery_definition.components
|
||||
local presets = prototype.livery_definition.presets
|
||||
table.insert(st, [[\subsection*{Components}]])
|
||||
table.insert(st, [[\begin{itemize}]])
|
||||
for _, c in ipairs(components) do
|
||||
table.insert(st, string.format([[\item %s]], SL(c.description)))
|
||||
end
|
||||
table.insert(st, [[\end{itemize}]])
|
||||
for _, p in ipairs(presets) do
|
||||
table.insert(st, string.format([[\subsection*{Preset: %s}]], SL(p.description)))
|
||||
table.insert(st, [[
|
||||
\begin{tabularx}{\textwidth}{X c}
|
||||
\toprule
|
||||
{\bfseries Component} & {\bfseries Color} \\\midrule
|
||||
]])
|
||||
for _, c in ipairs(p.livery_stack.layers) do
|
||||
local cdesc = SL(components[c.component].description)
|
||||
table.insert(st, string.format([[%s & %s\\]], cdesc, latex.describe_color(c.color)))
|
||||
end
|
||||
table.insert(st, [[
|
||||
\bottomrule
|
||||
\end{tabularx}
|
||||
]])
|
||||
end
|
||||
else
|
||||
table.insert(st, [[\section{Livery System (Bike Painter)}]])
|
||||
table.insert(st, [[This wagon can be painted by the bike painter.]])
|
||||
end
|
||||
end
|
||||
|
||||
local dlxlivdef = prototype.dlxtrains_livery
|
||||
if dlxlivdef then
|
||||
table.insert(st, [[
|
||||
\section{DlxTrains Livery Sytem}
|
||||
This wagon can be customized with DlxTrains' livery system.
|
||||
]])
|
||||
end
|
||||
|
||||
local atlivdef = prototype.advtrains_livery_tools
|
||||
if atlivdef then
|
||||
table.insert(st, [[\section{Advtrains Livery Tool (Marnack)}]])
|
||||
for _, tname in ipairs(atlivdef.template_names) do
|
||||
local tdef = atlivdef.templates[tname]
|
||||
table.insert(st, string.format([[\subsection*{Template: %s}]], SL(tname)))
|
||||
table.insert(st, SL(tdef.notes))
|
||||
table.insert(st, "")
|
||||
table.insert(st, "This template contains the following components:")
|
||||
table.insert(st, [[\begin{itemize}]])
|
||||
for _, overlay in ipairs(tdef.overlays) do
|
||||
table.insert(st, string.format([[\item %s]], SL(overlay.name)))
|
||||
end
|
||||
table.insert(st, [[\end{itemize}]])
|
||||
end
|
||||
for _, lname in ipairs(atlivdef.livery_names) do
|
||||
local ldef = atlivdef.liveries[lname]
|
||||
local tname = ldef.livery_template_name
|
||||
table.insert(st, string.format([[\subsection*{Preset: %s}]], SL(lname)))
|
||||
table.insert(st, string.format([[Template: %s]], SL(tname)))
|
||||
table.insert(st, "")
|
||||
table.insert(st, [[
|
||||
\begin{tabularx}{\textwidth}{X c}
|
||||
\toprule
|
||||
{\bfseries Component} & {\bfseries Color}\\\midrule]])
|
||||
for _, overlay in pairs(ldef.overlays) do
|
||||
local cname = atlivdef.templates[tname].overlays[overlay.id].name
|
||||
table.insert(st, string.format([[%s & %s\\]], SL(cname), latex.describe_color(overlay.color)))
|
||||
end
|
||||
table.insert(st, [[
|
||||
\bottomrule
|
||||
\end{tabularx}
|
||||
]])
|
||||
end
|
||||
end
|
||||
|
||||
table.insert(st, [[
|
||||
\end{document}
|
||||
]])
|
||||
return table.concat(st, "\n")
|
||||
end
|
||||
|
||||
return latex
|
@ -15,6 +15,8 @@ local function gcdmain(x, y, ...)
|
||||
end
|
||||
|
||||
--- Compute the greatest common divider.
|
||||
-- Note that the behavior of this function is undefined if the arguments
|
||||
-- include infinity or NaN.
|
||||
-- @tparam integer ... The numbers used for the computation.
|
||||
-- @treturn integer The GCD of the given numbers.
|
||||
function M.gcd(...)
|
||||
|
15
mtt.lua
Normal file
15
mtt.lua
Normal file
@ -0,0 +1,15 @@
|
||||
if doc then
|
||||
mtt.register("wagon information formspec", function(cb)
|
||||
local player = mtt.join_player("singleplayer")
|
||||
for k in pairs(advtrains_doc_integration.prototypes) do
|
||||
print("Found wagon prototype: " .. k)
|
||||
doc.show_entry("singleplayer", "advtrains_wagons", k, true)
|
||||
end
|
||||
player:leave()
|
||||
cb()
|
||||
end)
|
||||
end
|
||||
mtt.register("wagon datasheet generation", function(cb)
|
||||
advtrains_doc_integration.write_all_wagons_as_latex()
|
||||
cb()
|
||||
end)
|
62
utils.lua
62
utils.lua
@ -1,13 +1,13 @@
|
||||
--- Utility functions.
|
||||
-- @module advtrains_doc_integration.utils
|
||||
-- @alias M
|
||||
local M = {}
|
||||
-- @alias utils
|
||||
local utils = {}
|
||||
|
||||
--- 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)
|
||||
function utils.map(tbl, func)
|
||||
local t = {}
|
||||
for k, v in pairs(tbl or {}) do
|
||||
t[k] = func(v)
|
||||
@ -15,4 +15,58 @@ function M.map(tbl, func)
|
||||
return t
|
||||
end
|
||||
|
||||
return M
|
||||
--- Create an iterator that iterates through the table in the order of
|
||||
-- the keys sorted in a certain order.
|
||||
-- Note that the behavior is undefined if a key is added during the iteration.
|
||||
-- @tparam table tbl The table to iterate
|
||||
-- @tparam[opt] function sort The function passed to @{table.sort} for
|
||||
-- sorting the keys. The default sorting order is used if the function
|
||||
-- is not provided.
|
||||
-- @return An iterator suitable for use with Lua's `for` loop.
|
||||
function utils.spairs(tbl, sort)
|
||||
local keys = {}
|
||||
local kn = {}
|
||||
for k in pairs(tbl or {}) do
|
||||
table.insert(keys, k)
|
||||
end
|
||||
table.sort(keys, sort)
|
||||
for i = 2, #keys do
|
||||
kn[keys[i-1]] = keys[i]
|
||||
end
|
||||
return function(t, n)
|
||||
local k = kn[n]
|
||||
if n == nil then
|
||||
k = keys[1]
|
||||
end
|
||||
return k, t[k]
|
||||
end, tbl, nil
|
||||
end
|
||||
|
||||
--- Gets the name of the coupler
|
||||
-- @tparam string str The technical name of the coupler
|
||||
-- @treturn string The name of the coupler
|
||||
function utils.get_coupler_name(str)
|
||||
return advtrains.coupler_types[str] or str
|
||||
end
|
||||
|
||||
--- Adjust the soundspec to table form.
|
||||
-- @tparam SimpleSoundSpec spec The soundspec to adjust.
|
||||
-- @treturn SimpleSoundSpec The adjusted soundspec.
|
||||
function utils.adjust_soundspec(spec)
|
||||
if type(spec) == "string" then
|
||||
spec = {name = spec}
|
||||
end
|
||||
if type(spec) == "table" and spec.name and spec.name ~= "" then
|
||||
return spec
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
--- Escape the texture string.
|
||||
-- @tparam string str The texture string to escape.
|
||||
-- @treturn string The escaped texture string.
|
||||
function utils.texture_escape(str)
|
||||
return (string.gsub(tostring(str), "[:^\\]", [[\%1]]))
|
||||
end
|
||||
|
||||
return utils
|
||||
|
Loading…
x
Reference in New Issue
Block a user