560 lines
17 KiB
Lua
560 lines
17 KiB
Lua
local S = minetest.get_translator("advtrains_doc_integration")
|
|
local fsescape = minetest.formspec_escape
|
|
local worldpath = minetest.get_worldpath() .. DIR_DELIM
|
|
|
|
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 latex_escape(str)
|
|
return string.gsub(str, ".", {
|
|
["&"] = [[\&]],
|
|
["%"] = [[\%]],
|
|
["$"] = [[\$]],
|
|
["#"] = [[\#]],
|
|
["_"] = [[\_]],
|
|
["{"] = [[\{]],
|
|
["}"] = [[\}]],
|
|
["~"] = [[\textasciitilde]],
|
|
["^"] = [[\textasciicircum]],
|
|
["\\"] = [[\textbackslash]],
|
|
})
|
|
end
|
|
|
|
local function SL(x)
|
|
return latex_escape(Ss(x))
|
|
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 quotestring(str)
|
|
return string.format("%q", str)
|
|
end
|
|
|
|
if doc then
|
|
if doc.sub.items then
|
|
local register_factoid = doc.sub.items.register_factoid
|
|
local function group_factoid(cat, gr, f)
|
|
register_factoid(cat, "groups", function(_, def)
|
|
return f(def.groups[gr] or 0) or ""
|
|
end)
|
|
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
|
|
register_factoid("nodes", "groups", function(_, ndef)
|
|
if ndef.advtrains then
|
|
if ndef.advtrains.set_aspect then
|
|
return S("This is a signal with a variable aspect.")
|
|
elseif ndef.advtrains.get_aspect then
|
|
return S("This is a signal with a static aspect.")
|
|
end
|
|
end
|
|
return ""
|
|
end)
|
|
end
|
|
|
|
doc.add_category("advtrains_wagons", {
|
|
name = S("Wagons"),
|
|
build_formspec = doc.entry_builders.formspec,
|
|
})
|
|
end
|
|
|
|
local function describe_length(x)
|
|
local inch = math.floor(x/0.0254)
|
|
local ft = math.floor(inch/12)
|
|
inch = inch%12
|
|
local st = {}
|
|
if ft > 0 then
|
|
table.insert(st, ft .. "'")
|
|
end
|
|
if inch > 0 then
|
|
table.insert(st, inch .. '"')
|
|
end
|
|
if not next(st) then
|
|
st = '0"'
|
|
end
|
|
return string.format("%.2f m (%s)", 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
|
|
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, "* " .. 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 dlxtrains_livery_information(prototype)
|
|
if not dlxtrains then
|
|
return nil
|
|
end
|
|
local old_update_livery = dlxtrains.update_livery
|
|
dlxtrains.update_livery = function(_, _, x)
|
|
return coroutine.yield(x)
|
|
end
|
|
local env = {
|
|
coroutine = coroutine,
|
|
dlxtrains = table.copy(dlxtrains),
|
|
}
|
|
local function main(G, f)
|
|
setfenv(0, G)
|
|
f()
|
|
return error()
|
|
end
|
|
local t = {coroutine.resume(coroutine.create(main), env, prototype.custom_may_destroy or function() end)}
|
|
dlxtrains.update_livery = old_update_livery
|
|
if not t[1] then
|
|
return nil
|
|
end
|
|
return unpack(t, 2)
|
|
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 function adjust_wagon_prototype(prototype)
|
|
local p = table.copy(prototype)
|
|
if p._doc_wagon_longdesc then
|
|
p.longdesc = p._long_wagon_longdesc
|
|
end
|
|
if type(p.horn_sound) == "string" then
|
|
p.horn_sound = {name = prototype.horn_sound}
|
|
end
|
|
if p.horn_sound and p.horn_sound.name == "" then
|
|
p.horn_sound = nil
|
|
end
|
|
local pax, driver = 0, 0
|
|
if p.seats and p.seat_groups then
|
|
for _, v in pairs(p.seats) do
|
|
if p.seat_groups[v.group].driving_ctrl_access then
|
|
driver = driver + 1
|
|
else
|
|
pax = pax + 1
|
|
end
|
|
p.seat_groups[v.group].count = (p.seat_groups[v.group].count or 0) + 1
|
|
end
|
|
end
|
|
p.max_passengers = pax
|
|
p.max_drivers = driver
|
|
p.max_seats = pax+driver
|
|
return p
|
|
end
|
|
|
|
local function doc_register_wagon(itemname)
|
|
local prototype = adjust_wagon_prototype(advtrains.wagon_prototypes[itemname])
|
|
local desctext = {}
|
|
if prototype._doc_wagon_longdesc then
|
|
table.insert(desctext, tostring(prototype._doc_wagon_longdesc))
|
|
blankline(desctext)
|
|
end
|
|
table.insert(desctext, S("Itemstring: @1", quotestring(itemname)))
|
|
addlist(desctext, prototype.drops, S("Drops:"), S("Drops nothing"), false, function(_, v) return list_itemstring(v) end)
|
|
|
|
blankline(desctext)
|
|
addlist(desctext, prototype.drives_on, S("Drives on:"))
|
|
addlist(desctext, prototype.coupler_types_front, S("Compatible front couplers:"), S2("Front coupler: @1", "Absent"), S2("Front coupler: @1", "Universal"), get_coupler_name)
|
|
addlist(desctext, prototype.coupler_types_back, S("Compatible rear couplers:"), S2("Rear coupler: @1", "Absent"), S2("Rear coupler: @1", "Universal"), get_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, S2("Motive power: @1", prototype.is_locomotive and "Present" or "Absent"))
|
|
local hornsound = prototype.horn_sound
|
|
table.insert(desctext, S("Horn sound: @1", hornsound and quotestring(hornsound.name) or S("Undefined")))
|
|
|
|
blankline(desctext)
|
|
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: %s", k, v)
|
|
end)
|
|
else
|
|
table.insert(desctext, S2("Cargo inventory: @1", "Absent"))
|
|
end
|
|
|
|
blankline(desctext)
|
|
table.insert(desctext, S("Mesh: @1", quotestring(prototype.mesh)))
|
|
addlist(desctext, prototype.textures, S("Textures:"), S("No textures"), false, function(_, v) return quotestring(v) 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 tostring(v.description) end)
|
|
addlist(bikelivdesc, prototype.livery_definition.presets, S("Livery presets:"), nil, nil, function(_, v) return tostring(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 dlxlivdef = dlxtrains_livery_information(prototype)
|
|
table.insert(desctext, S2("DlxTrains livery system: @1", dlxlivdef and "Supported" or "Unsupported"))
|
|
|
|
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
|
|
|
|
blankline(desctext)
|
|
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 = table.concat(map(prototype.textures, fsescape), ",")
|
|
if dlxlivdef then
|
|
textures = fsescape(string.format("%s_%s.png", dlxlivdef.filename_prefix, dlxlivdef[0].code))
|
|
end
|
|
local fstext = {
|
|
string.format("textarea[%f,%f;%f,%f;;;%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(itemname)),
|
|
string.format("model[%f,%f;%f,%f;%s;%s;%s;%f,%f]",
|
|
x1-mside, y1-mside, mside, mside, "wagon_model", mesh, textures, -30, 135),
|
|
}
|
|
minetest.override_item(itemname, {_doc_items_create_entry = false})
|
|
doc.add_entry("advtrains_wagons", itemname, {
|
|
name = ItemStack(itemname):get_short_description(),
|
|
data = table.concat(fstext),
|
|
})
|
|
end
|
|
|
|
if doc then
|
|
for k in pairs(advtrains.wagon_prototypes) do
|
|
doc_register_wagon(k)
|
|
end
|
|
end
|
|
|
|
local _register_wagon = advtrains.register_wagon
|
|
function advtrains.register_wagon(name, ...)
|
|
if not string.find(name, ":") then
|
|
name = "advtrains:" .. name
|
|
end
|
|
_register_wagon(name, ...)
|
|
if doc then
|
|
doc_register_wagon(name)
|
|
end
|
|
end
|
|
|
|
advtrains_doc_integration = {}
|
|
|
|
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(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}
|
|
\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(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 lcouplers[1] then
|
|
lcouplers = {[[Default]]}
|
|
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 i == 1 then
|
|
fd = fcouplers and "" or "Universal"
|
|
fd = string.format([[\multirow[t]{%d}{*}{%s}]], #lcouplers, fd)
|
|
end
|
|
if rlim then
|
|
rd = rcouplers[cid] and [[$\bullet$]] or ""
|
|
elseif i == 1 then
|
|
rd = rcouplers and "" or "Universal"
|
|
rd = string.format([[\multirow[t]{%d}{*}{%s}]], #lcouplers, rd)
|
|
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
|
|
if hasinv or hasseats 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
|
|
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)
|
|
local cstr = c.color
|
|
local color = string.match(c.color,"^#(%x%x%x%x%x%x)$")
|
|
cstr = SL(cstr)
|
|
if color then
|
|
color = SL(string.upper(color))
|
|
table.insert(st, string.format([[%s & \tikz \definecolor{c}{HTML}{%s} \draw[fill=c] (0,0) rectangle (1em,1em); \texttt{%s}\\]], cdesc, color, cstr))
|
|
else
|
|
table.insert(st, string.format([[%s & \texttt{%s}\\]], cdesc, cstr))
|
|
end
|
|
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
|
|
|
|
table.insert(st, [[
|
|
\end{document}
|
|
]])
|
|
st = table.concat(st, "\n")
|
|
minetest.safe_file_write(filename, st)
|
|
end
|
|
|
|
function advtrains_doc_integration.write_all_wagons_as_latex()
|
|
for k in pairs(advtrains.wagon_prototypes) do
|
|
advtrains_doc_integration.write_wagon_info_as_latex(k)
|
|
end
|
|
end
|
|
|
|
minetest.register_chatcommand("atdoc_write", {
|
|
params = "",
|
|
description = S("Export Advtrains-related information"),
|
|
privs = {server = true},
|
|
func = function()
|
|
advtrains_doc_integration.write_all_wagons_as_latex()
|
|
end,
|
|
})
|