local S = minetest.get_translator("advtrains_doc_integration") local fsescape = minetest.formspec_escape local worldpath = minetest.get_worldpath() .. DIR_DELIM advtrains_doc_integration = {} 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("%s", htescape(str)) end local function htheader(str) return string.format("%s", htescape(str)) end local function htaction(action, str, noesc) if not noesc then str = htescape(str) end return string.format("", action, str) 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 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 ht_coupler_name(n) local s = advtrains.coupler_types[n] if s then return htescape(s) else return htmono(n) end 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 advtrains_livery_tools_information do -- helper for Marnack's Advtrains livery tools local atliv = {} function atliv:get_textures_from_design(design) local tp = type(design) if tp == "string" then return self:get_textures_from_design(self.liveries[design]) elseif tp ~= "table" then return end local template = self.templates[design.livery_template_name] if not template then 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 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)) end end return map(textures, function(st) return table.concat(st, "^") end) end local mt = { __index = atliv, } advtrains_livery_tools_information = function(wname) if not advtrains_livery_database then return nil end local tnames = advtrains_livery_database.get_livery_template_names_for_wagon(wname) if next(tnames) == nil then return nil end local templates = {} for _, tname in pairs(tnames) do templates[tname] = advtrains_livery_database.get_wagon_livery_template(wname, tname) end local lnames = advtrains_livery_database.get_predefined_livery_names(wname) local lnames_t = {} local liveries = {} for _, lid in pairs(lnames) do local lname = lid.livery_name liveries[lname] = advtrains_livery_database.get_predefined_livery(wname, lname) table.insert(lnames_t, lname) end table.sort(tnames) table.sort(lnames_t) local obj = { templates = templates, template_names = tnames, liveries = liveries, livery_names = lnames_t, } return setmetatable(obj, mt) 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_wagon_prototype(itemname, prototype) local p = prototype_cache[itemname] if p then return p end 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 local bikelivdef = p.livery_definition local dlxlivdef = dlxtrains_livery_information(p) local atlivdef = advtrains_livery_tools_information(itemname) p.dlxtrains_livery = dlxlivdef p.advtrains_livery_tools = atlivdef local txbase = p.textures if dlxlivdef then txbase = {string.format("%s_%s.png", dlxlivdef.filename_prefix, dlxlivdef[0].code)} end p.livery_textures = {[0] = txbase} if bikelivdef then local slot = p.livery_texture_slot local components = bikelivdef.components local basefile = bikelivdef.base_texture_file for _, pdef in ipairs(bikelivdef.presets) do 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))) end tx[slot] = table.concat(st, "^") table.insert(p.livery_textures, tx) end end if atlivdef then for _, dname in ipairs(atlivdef.livery_names) do table.insert(p.livery_textures, atlivdef:get_textures_from_design(dname) or txbase) end end prototype_cache[itemname] = p 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 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 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 htaction("playhorn", htmono(hornsound.name)) or S("Undefined"))) 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 livids = 0 local function livprev(desc) livids = livids+1 return htaction(string.format("preview_%d", livids), desc) end local livrst = (get_livery_preview_selection(pname, prototype.name) ~= 0) and " " .. htaction("preview_0", S("[Reset Preview]")) or "" 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) livids = livids+1 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 dlxlivdef = prototype.dlxtrains_livery table.insert(desctext, S2("DlxTrains livery system: @1", dlxlivdef and "Supported" or "Unsupported")) 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 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 if ndef.at_conns then return S("This track has the following conns table by default: @1", describe_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 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} \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(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 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, }) 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 if not act then return elseif act == "action:playhorn" then local sound = (advtrains.wagon_prototypes[ent] or {}).horn_sound if type(sound) == "table" then sound = table.copy(sound) else sound = {name = sound} end if type(sound.name) ~= "string" or sound.name == "" then return end minetest.sound_play(sound, {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)