init.lua 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618
  1. advtrains_livery_database = {}
  2. local callback_functions = {}
  3. local livery_templates = {}
  4. local registered_wagons = {}
  5. local predefined_liveries = {}
  6. local overlays_per_template_limit = 6
  7. -------------------------------------------------------------------------------
  8. -- External Utilities:
  9. -- This utility function compares a target version, expressed as a table of
  10. -- key-value pairs with keys of "major", "minor" and "patch", against a current
  11. -- version expressed as a table with similar form. It returns true if the
  12. -- current version's "major" value is equal to or greater than the target
  13. -- version's "major value. The target version can have nil values for "patch"
  14. -- or both "patch" and "minor" numbers in which case those values will be
  15. -- ignored.
  16. function advtrains_livery_database.compare_version_info(target_version, current_version)
  17. local major = tonumber(target_version.major)
  18. if not major or major > current_version.major then
  19. return false
  20. end
  21. if major < current_version.major then
  22. return true
  23. end
  24. local minor = tonumber(target_version.minor)
  25. local patch = tonumber(target_version.patch)
  26. if not minor then
  27. return not patch
  28. end
  29. if minor > current_version.minor then
  30. return false
  31. end
  32. if minor < current_version.minor then
  33. return true
  34. end
  35. if patch and patch > current_version.patch then
  36. return false
  37. end
  38. return true
  39. end
  40. function advtrains_livery_database.is_valid_livery_design(livery_design)
  41. return livery_design and
  42. livery_design.wagon_type and
  43. livery_design.livery_template_name
  44. end
  45. function advtrains_livery_database.are_livery_designs_equivalent(livery_design_1, livery_design_2)
  46. if not livery_design_1 or
  47. not livery_design_2 or
  48. livery_design_1.wagon_type ~= livery_design_2.wagon_type or
  49. livery_design_1.livery_template_name ~= livery_design_2.livery_template_name or
  50. livery_design_1.overlays and not livery_design_2.overlays or
  51. not livery_design_1.overlays and livery_design_2.overlays then
  52. return false
  53. end
  54. -- Every overlay in livery design 1 should be in livery design 2 and...
  55. for seq_number, overlay in pairs(livery_design_1.overlays) do
  56. if not livery_design_2.overlays[seq_number] or
  57. overlay.id ~= livery_design_2.overlays[seq_number].id or
  58. overlay.color ~= livery_design_2.overlays[seq_number].color then
  59. return false
  60. end
  61. end
  62. -- ...every overlay in livery design 2 should also be in livery design 1
  63. for seq_number, overlay in pairs(livery_design_2.overlays) do
  64. if not livery_design_1.overlays[seq_number] or
  65. overlay.id ~= livery_design_1.overlays[seq_number].id or
  66. overlay.color ~= livery_design_1.overlays[seq_number].color then
  67. return false
  68. end
  69. end
  70. return true
  71. end
  72. function advtrains_livery_database.clone_livery_design(src_livery_design)
  73. if not src_livery_design then
  74. return nil
  75. end
  76. local livery_design = {
  77. wagon_type = src_livery_design.wagon_type,
  78. livery_template_name = src_livery_design.livery_template_name,
  79. }
  80. if src_livery_design.overlays then
  81. livery_design.overlays = {}
  82. for seq_num, overlay in pairs(src_livery_design.overlays) do
  83. livery_design.overlays[seq_num] = {
  84. id = overlay.id,
  85. color = overlay.color,
  86. }
  87. end
  88. end
  89. return livery_design
  90. end
  91. function advtrains_livery_database.clone_textures(src)
  92. if not src then
  93. return nil
  94. end
  95. local textures = {}
  96. for k, texture in pairs(src) do
  97. textures[k] = texture
  98. end
  99. return textures
  100. end
  101. function advtrains_livery_database.get_overlays_per_template_limit()
  102. return overlays_per_template_limit
  103. end
  104. -------------------------------------------------------------------------------
  105. -- Internal Utilities:
  106. local function get_wagon_type_prefix(wagon_type)
  107. local delimiter_pos, _ = string.find(wagon_type, ":")
  108. if not delimiter_pos or delimiter_pos < 2 then
  109. return
  110. end
  111. return wagon_type:sub(1, delimiter_pos - 1)
  112. end
  113. local function get_template(wagon_type, base_texture)
  114. for name, template in pairs(livery_templates[wagon_type]) do
  115. if template.base_textures[1] == base_texture then
  116. return name, template
  117. end
  118. end
  119. end
  120. -------------------------------------------------------------------------------
  121. -- Mod Version Utilities:
  122. function advtrains_livery_database.get_mod_version()
  123. return {major = 0, minor = 9, patch = 0}
  124. end
  125. -- This utility function is intended to allow dependent mods to check if the
  126. -- needed version of advtrains_livery_database is in use. It returns true if
  127. -- the current version of advtrains_livery_database is equal to or greater
  128. -- than the target version info.
  129. function advtrains_livery_database.is_compatible_mod_version(target_version)
  130. local current_version = advtrains_livery_database.get_mod_version()
  131. return advtrains_livery_database.compare_version_info(target_version, current_version)
  132. end
  133. -------------------------------------------------------------------------------
  134. -- Primary API:
  135. -- Every mod that needs to register one or more livery templates should call
  136. -- this function exactly once.
  137. function advtrains_livery_database.register_mod(mod_name, optional_callback_functions)
  138. assert(mod_name, "Missing required mod name")
  139. if not callback_functions[mod_name] then
  140. if optional_callback_functions then
  141. callback_functions[mod_name] = {
  142. on_custom_get_livery_design_from_textures = optional_callback_functions.on_custom_get_livery_design_from_textures, -- Function parameters: (wagon_type, livery_textures, wagon_id)
  143. on_pre_get_livery_design_from_textures = optional_callback_functions.on_pre_get_livery_design_from_textures, -- Function parameters: (wagon_type, livery_textures, wagon_id, livery_design)
  144. on_custom_get_livery_textures_from_design = optional_callback_functions.on_custom_get_livery_textures_from_design, -- Function parameters: (livery_design, wagon_id)
  145. on_post_get_livery_textures_from_design = optional_callback_functions.on_post_get_livery_textures_from_design, -- Function parameters: (livery_design, wagon_id, livery_textures)
  146. }
  147. else
  148. callback_functions[mod_name] = {}
  149. end
  150. else
  151. minetest.debug("WARNING: An attempt to register mod '"..mod_name.."' multiple times with the train livery database is not supported and has been blocked.")
  152. end
  153. end
  154. -- This function should be called exactly once per wagon type being registered
  155. -- for a mod, regardless of how many templates are being registered for the
  156. -- wagon type.
  157. --
  158. -- Note: The mod_name parameter is optional if both wagon_type prefix is not
  159. -- null and not equal to "advtrains".
  160. function advtrains_livery_database.register_wagon(wagon_type, mod_name)
  161. assert(wagon_type, "Invalid wagon type")
  162. -- Determine the effective wagon_mod. This is the mod whose callback
  163. -- functions will be used for the given wagon_type and livery_template_name.
  164. local actual_wagon_mod = nil
  165. local wagon_type_prefix = get_wagon_type_prefix(wagon_type)
  166. if wagon_type_prefix and wagon_type_prefix ~= "advtrains" then
  167. actual_wagon_mod = wagon_type_prefix
  168. elseif mod_name and mod_name ~="advtrains" then
  169. -- The mod_name parameter is only used if the wagon_type_prefix is nil
  170. -- or not "advtrains". If used, it must be neither nil nor "advtrains".
  171. -- Its purpose is to allow mods that define their wagons in the
  172. -- "advtrains" namespace to register livery templates that use callback
  173. -- functions that are specific to the owning mod.
  174. actual_wagon_mod = mod_name
  175. end
  176. assert(actual_wagon_mod, "Invalid wagon_mod parameter") -- The wagon_mod parameter must be neither nil nor equal to "advtrains" if wagon_type_prefix is nil or equal to "advtrains"
  177. assert(callback_functions[actual_wagon_mod], "Attempt to register a wagon_type for an unregistered mod ('"..actual_wagon_mod.."').")
  178. if not registered_wagons[wagon_type] then
  179. registered_wagons[wagon_type] = {
  180. mod_name = actual_wagon_mod,
  181. }
  182. end
  183. end
  184. -- Use this funtion to determine the mod for which a given wagon type was
  185. -- registered. This is need because some mods define their wagon_type using
  186. -- the prefix "advtrains" rather than their mod name.
  187. function advtrains_livery_database.get_wagon_mod_name(wagon_type)
  188. if wagon_type and registered_wagons[wagon_type] then
  189. return registered_wagons[wagon_type].mod_name
  190. end
  191. return
  192. end
  193. -- This function should be called once for each livery template that is
  194. -- registered for a given wagon type.
  195. function advtrains_livery_database.add_livery_template(wagon_type, livery_template_name, base_textures, livery_mod, overlay_count, designer, texture_license, texture_creator, notes)
  196. assert(wagon_type, "Invalid wagon type")
  197. assert(registered_wagons[wagon_type], "Attempt to register a livery template for an unregistered wagon type ('"..wagon_type.."').")
  198. assert(livery_template_name, "Invalid livery template name")
  199. assert(base_textures, "Invalid base textures")
  200. assert(base_textures[1], "Missing base texture")
  201. assert(livery_mod, "Missing livery mod name")
  202. assert(overlay_count, "Missing overlay count")
  203. assert(callback_functions[livery_mod], "Attempt to add a livery template for an unregistered mod ('"..livery_mod.."').")
  204. if not livery_templates[wagon_type] then
  205. livery_templates[wagon_type] = {}
  206. end
  207. if not livery_templates[wagon_type][livery_template_name] then
  208. -- Only add the livery template if another livery template for the
  209. -- given wagon type that has the same base texture has not been
  210. -- previously registered. In other words, for a given wagon_type, each
  211. -- livery template should have a unique base_textures[1]. This
  212. -- restriction is used so that base_texture[1] of given wagon can be
  213. -- used to identify its livery template. A workaround for this
  214. -- restriction for the template creator is to duplicate the texture
  215. -- file and give it a unique name.
  216. local existing_livery_template_name, _ = get_template(wagon_type, base_textures[1])
  217. assert(not existing_livery_template_name,
  218. "Attempt to register a livery template ('"..livery_template_name..
  219. "') that does not have a uniquely named base texture for wagon type ('"..wagon_type..
  220. "'). A previously registered livery template ('"..(existing_livery_template_name or "<nil>")..
  221. "') already uses the same texture file ('"..base_textures[1].."').")
  222. livery_templates[wagon_type][livery_template_name] = {
  223. base_textures = advtrains_livery_database.clone_textures(base_textures),
  224. designer = designer,
  225. texture_license = texture_license,
  226. texture_creator = texture_creator,
  227. notes = notes,
  228. livery_mod = livery_mod,
  229. expected_overlay_count = overlay_count,
  230. overlays = {},
  231. }
  232. end
  233. end
  234. -- This function is called to register an overlay for a given livery template.
  235. -- It should be called once for each overlay in the template.
  236. function advtrains_livery_database.add_livery_template_overlay(wagon_type, livery_template_name, overlay_id, overlay_name, overlay_slot_idx, overlay_texture, overlay_alpha)
  237. assert(wagon_type, "Invalid wagon type")
  238. assert(livery_template_name, "Invalid livery template name")
  239. assert(overlay_id, "Invalid overlay id")
  240. assert(overlay_name, "Invalid overlay name")
  241. assert(overlay_slot_idx and tonumber(overlay_slot_idx) > 0, "Invalid overlay slot index")
  242. assert(overlay_texture, "Invalid overlay texture")
  243. assert(livery_templates[wagon_type], "wagon type, '"..wagon_type.."' not registered")
  244. assert(livery_templates[wagon_type][livery_template_name], "livery template name, '"..livery_template_name.."' not registered for wagon type, '"..wagon_type.."'")
  245. local overlay_count = #livery_templates[wagon_type][livery_template_name].overlays
  246. if overlay_count >= overlays_per_template_limit then
  247. minetest.debug(
  248. "Failed to add overlay ('"..overlay_name..
  249. "') for livery template ('"..livery_template_name..
  250. "') of wagon type ('"..wagon_type..
  251. "') due to exceeding overlays per template limit.")
  252. return
  253. end
  254. if livery_templates[wagon_type][livery_template_name].overlays[overlay_id] then
  255. minetest.debug(
  256. "Failed to add overlay ('"..overlay_name..
  257. "') for livery template ('"..livery_template_name..
  258. "') of wagon type ('"..wagon_type..
  259. "') due to overlay id "..overlay_id.." having already been added.")
  260. return
  261. end
  262. if overlay_count >= livery_templates[wagon_type][livery_template_name].expected_overlay_count then
  263. -- This could be due to a mod not correctly specifying the number of
  264. -- overlays that that will be in the template when it called
  265. -- add_livery_template() or it could be an attempt by one mod to update
  266. -- the overlays of a template created by another mod.
  267. minetest.debug(
  268. "Failed to add overlay ('"..overlay_name..
  269. "') for livery template ('"..livery_template_name..
  270. "') of wagon type ('"..wagon_type..
  271. "') due to exceeding the expected number of overlays.")
  272. return
  273. end
  274. livery_templates[wagon_type][livery_template_name].overlays[overlay_id] = {
  275. name =overlay_name,
  276. slot_idx = tonumber(overlay_slot_idx),
  277. texture = overlay_texture,
  278. alpha = overlay_alpha and tonumber(overlay_alpha) or 255,
  279. }
  280. end
  281. -- This function registers a predefined livery for a wagon type. Predefined
  282. -- liveries are optional.
  283. -- Note that predefined livery names must be unique per wagon type.
  284. --
  285. -- Ideally, predefined liveries should be used to showcase examples of a few
  286. -- designs that are possibile for a given templete. Registering too many
  287. -- predefined liveries for a given template could be counter produtive since
  288. -- a player could become overwhelmed with the number of options. That concern
  289. -- could be addressed if the tool that displays predefined liveries provides a
  290. -- filtering and/or search mechanism.
  291. function advtrains_livery_database.add_predefined_livery(design_name, livery_design, mod_name, notes)
  292. assert(design_name, "Invalid design name")
  293. assert(advtrains_livery_database.is_valid_livery_design(livery_design), "Invalid livery design")
  294. assert(mod_name, "Invalid mod name")
  295. assert(callback_functions[mod_name], "Attempt to add predefined livery for an unregistered mod ('"..mod_name.."').")
  296. -- Don't add predefined liveries that reference a livery template that is
  297. -- not already registered.
  298. if not livery_templates[livery_design.wagon_type] or not livery_templates[livery_design.wagon_type][livery_design.livery_template_name] then
  299. minetest.debug("Failed to add predefined livery design ('"..design_name.."') for wagon type ('"..livery_design.wagon_type.."') due to unknown livery template ('"..livery_design.livery_template_name.."')")
  300. return false
  301. end
  302. if not predefined_liveries[livery_design.wagon_type] then
  303. predefined_liveries[livery_design.wagon_type] = {}
  304. end
  305. if not predefined_liveries[livery_design.wagon_type][design_name] then
  306. predefined_liveries[livery_design.wagon_type][design_name] = {
  307. mod_name = mod_name,
  308. notes = notes,
  309. livery_design = advtrains_livery_database.clone_livery_design(livery_design),
  310. }
  311. return true
  312. else
  313. minetest.debug("Failed to add predefined livery design due to duplicate name ('"..design_name.."') for wagon type ('"..livery_design.wagon_type.."')")
  314. end
  315. return false
  316. end
  317. -- Get the list of names of predefined liveries for a given wagon type.
  318. function advtrains_livery_database.get_predefined_livery_names(wagon_type)
  319. local names = {}
  320. if wagon_type and predefined_liveries[wagon_type] then
  321. for name, predefined_livery in pairs(predefined_liveries[wagon_type]) do
  322. table.insert(names, {livery_name = name, livery_template_name = predefined_livery.livery_design.livery_template_name})
  323. end
  324. end
  325. return names
  326. end
  327. -- Get the predefined livery for a given wagon type and predefined livery name.
  328. -- Note that predefined livery names are unique per wagon type.
  329. function advtrains_livery_database.get_predefined_livery(wagon_type, design_name)
  330. if wagon_type and design_name and predefined_liveries[wagon_type] and predefined_liveries[wagon_type][design_name] then
  331. return advtrains_livery_database.clone_livery_design(predefined_liveries[wagon_type][design_name].livery_design)
  332. end
  333. return
  334. end
  335. function advtrains_livery_database.get_wagon_livery_overlay_name(wagon_type, livery_template_name, overlay_seq_number)
  336. assert(wagon_type, "Invalid wagon type")
  337. assert(livery_template_name, "Invalid livery template name")
  338. assert(overlay_seq_number, "Invalid overlay sequence number")
  339. if livery_templates[wagon_type] and
  340. livery_templates[wagon_type][livery_template_name] and
  341. livery_templates[wagon_type][livery_template_name].overlays and
  342. livery_templates[wagon_type][livery_template_name].overlays[overlay_seq_number] then
  343. return livery_templates[wagon_type][livery_template_name].overlays[overlay_seq_number].name
  344. end
  345. end
  346. function advtrains_livery_database.has_wagon_livery_template(wagon_type, livery_template_name)
  347. if wagon_type and livery_templates[wagon_type] and livery_template_name and livery_templates[wagon_type][livery_template_name] then
  348. return true
  349. end
  350. return false
  351. end
  352. function advtrains_livery_database.get_wagon_livery_template(wagon_type, livery_template_name)
  353. assert(wagon_type, "Invalid wagon type")
  354. assert(livery_template_name, "Invalid livery template name")
  355. if not advtrains_livery_database.has_wagon_livery_template(wagon_type, livery_template_name) then
  356. return nil
  357. end
  358. -- Create a copy of the template
  359. local wagon_livery_template = {
  360. base_textures = advtrains_livery_database.clone_textures(livery_templates[wagon_type][livery_template_name].base_textures),
  361. designer = livery_templates[wagon_type][livery_template_name].designer,
  362. texture_license = livery_templates[wagon_type][livery_template_name].texture_license,
  363. texture_creator = livery_templates[wagon_type][livery_template_name].texture_creator,
  364. notes = livery_templates[wagon_type][livery_template_name].notes,
  365. livery_mod = livery_templates[wagon_type][livery_template_name].livery_mod,
  366. overlays = {},
  367. }
  368. for id, overlay in ipairs(livery_templates[wagon_type][livery_template_name].overlays) do
  369. wagon_livery_template.overlays[id] = {
  370. name = overlay.name,
  371. slot_idx = overlay.slot_idx,
  372. texture = overlay.texture,
  373. alpha = overlay.alpha,
  374. }
  375. end
  376. return wagon_livery_template
  377. end
  378. function advtrains_livery_database.get_livery_template_names_for_wagon(wagon_type)
  379. local livery_template_names = {}
  380. if wagon_type and livery_templates[wagon_type] then
  381. for name, _ in pairs(livery_templates[wagon_type]) do
  382. table.insert(livery_template_names, name)
  383. end
  384. end
  385. return livery_template_names
  386. end
  387. -- Attempt to determine the livery_design for a given wagon_type from its
  388. -- livery textures. Note that if livery textures were modified by the owning
  389. -- mod due to weathering, loads, etc. then this function could fail. The mod
  390. -- should provide an implementation of on_custom_get_livery_design_from_textures()
  391. -- or on_pre_get_livery_design_from_textures() to handle such cases.
  392. function advtrains_livery_database.get_livery_design_from_textures(wagon_type, textures, wagon_id)
  393. if not wagon_type or not textures or not textures[1] then
  394. return nil
  395. end
  396. local wagon_mod_name = advtrains_livery_database.get_wagon_mod_name(wagon_type)
  397. if not wagon_mod_name then
  398. return
  399. end
  400. local livery_textures = advtrains_livery_database.clone_textures(textures)
  401. -- Allow the mod to provide its own implementation for this function.
  402. if callback_functions[wagon_mod_name] and callback_functions[wagon_mod_name].on_custom_get_livery_design_from_textures then
  403. return callback_functions[wagon_mod_name].on_custom_get_livery_design_from_textures(wagon_type, livery_textures, wagon_id)
  404. end
  405. if not livery_templates[wagon_type] then
  406. return nil
  407. end
  408. if callback_functions[wagon_mod_name] and callback_functions[wagon_mod_name].on_pre_get_livery_design_from_textures then
  409. -- Allow mods to adjust the textures in case they have been previously
  410. -- modified for loads, weathering, etc.
  411. livery_textures = callback_functions[wagon_mod_name].on_pre_get_livery_design_from_textures(wagon_type, advtrains_livery_database.clone_textures(livery_textures), wagon_id)
  412. end
  413. -- Extract the base texture from the first texture in livery_textures.
  414. -- This will be used to identify the livery_template_name.
  415. local base_texture = livery_textures[1]
  416. local i, _ = string.find(livery_textures[1], "^", 1, true)
  417. if i and i > 1 then
  418. -- Trim any modifiers that were applied to the texture
  419. base_texture = string.sub(livery_textures[1], 1, i - 1)
  420. end
  421. -- Find the livery template for the given wagon_type and base_texture.
  422. local livery_template_name, livery_template = get_template(wagon_type, base_texture)
  423. -- Abort if no matching template was found
  424. if not livery_template then
  425. return nil
  426. end
  427. -- Create a candidate livery_design
  428. local livery_design = {
  429. wagon_type = wagon_type,
  430. livery_template_name = livery_template_name,
  431. overlays = {},
  432. }
  433. -- For each overlay clause in livery_textures, extract its texture and
  434. -- attempt to identify an overlay in the livery_template that has the same
  435. -- texture. Having a fewer number of overlays in livery_textures than the
  436. -- livery_template is OK. Having more is not. Thus, if livery_textures
  437. -- contains an overlay texture than is not present in the livery_template
  438. -- then the livery_template is not a match for livery_textures.
  439. for slot_idx, livery_texture in ipairs(livery_textures) do
  440. local pos = 1
  441. while pos do
  442. local _, j = string.find(livery_texture, "^(", pos, true)
  443. local n = j
  444. if j then
  445. local m
  446. m, n = string.find(livery_texture, "^[colorize:#", pos, true)
  447. if n then
  448. local overlay_texture = string.sub(livery_texture, j + 1, m - 1)
  449. local overlay_color = string.sub(livery_texture, n, n + 6)
  450. -- Seek a matching overlay in livery_template based on the
  451. -- found texture.
  452. local found = false
  453. for id, overlay in ipairs(livery_template.overlays) do
  454. if overlay.slot_idx == slot_idx and overlay.texture:upper() == overlay_texture:upper() then
  455. livery_design.overlays[id] = {id = id, color=overlay_color}
  456. found = true
  457. end
  458. end
  459. -- Abort if livery_texture contains an overlay that is not
  460. -- present in livery_template
  461. if not found then
  462. return nil
  463. end
  464. end
  465. end
  466. pos = n
  467. end
  468. end
  469. return livery_design
  470. end
  471. -- Given an instance of the livery_design table and an optional wagon id, this
  472. -- function should return livery strings suitable for updating an advtrains
  473. -- wagon. If specified, the wagon_id parameter is used in case the base texture
  474. -- varies by instance of the wagon due to weathering, load, etc.
  475. function advtrains_livery_database.get_livery_textures_from_design(livery_design, wagon_id)
  476. assert(livery_design, "Invalid livery design")
  477. assert(livery_design.wagon_type, "Missing wagon type in livery_design")
  478. assert(livery_design.livery_template_name, "Missing livery template name in livery_design")
  479. local wagon_mod_name = advtrains_livery_database.get_wagon_mod_name(livery_design.wagon_type)
  480. if not wagon_mod_name then
  481. return nil
  482. end
  483. -- Allow the mod to provide its own implementation for this function.
  484. if callback_functions[wagon_mod_name] and callback_functions[wagon_mod_name].on_custom_get_livery_textures_from_design then
  485. return callback_functions[wagon_mod_name].on_custom_get_livery_textures_from_design(livery_design, wagon_id)
  486. end
  487. if not livery_templates[livery_design.wagon_type] or
  488. not livery_templates[livery_design.wagon_type][livery_design.livery_template_name] or
  489. not livery_templates[livery_design.wagon_type][livery_design.livery_template_name].base_textures or
  490. not livery_templates[livery_design.wagon_type][livery_design.livery_template_name].base_textures[1] then
  491. return nil
  492. end
  493. local livery_template = livery_templates[livery_design.wagon_type][livery_design.livery_template_name]
  494. local livery_textures = advtrains_livery_database.clone_textures(livery_template.base_textures)
  495. -- Append a "colorize" clause for each valid overlay in livery_design
  496. if livery_template.overlays and livery_design.overlays then
  497. local ordered_overlays = {}
  498. for seq_num, overlay in pairs(livery_design.overlays) do
  499. table.insert(ordered_overlays, {seq_num = seq_num, id = overlay.id, color = overlay.color})
  500. end
  501. table.sort(ordered_overlays, function(a, b) return a.seq_num < b.seq_num end)
  502. for _, overlay in ipairs(ordered_overlays) do
  503. if livery_template.overlays and #livery_template.overlays > 0 and tonumber(overlay.id) <= #livery_template.overlays then
  504. local slot_idx = livery_template.overlays[overlay.id].slot_idx
  505. if overlay.id and overlay.color and livery_template.overlays[overlay.id] and slot_idx and livery_template.overlays[overlay.id].texture then
  506. local alpha = livery_template.overlays[overlay.id].alpha or 255
  507. if alpha < 0 then alpha = 0 end
  508. if alpha > 255 then alpha = 255 end
  509. local overlay_texture = "^("..livery_template.overlays[overlay.id].texture.."^[colorize:"..overlay.color..":"..alpha..")"
  510. livery_textures[slot_idx] = livery_textures[slot_idx]..overlay_texture
  511. end
  512. end
  513. end
  514. end
  515. if callback_functions[wagon_mod_name] and callback_functions[wagon_mod_name].on_post_get_livery_textures_from_design then
  516. -- Allow mods to adjust the textures in case they need to be modified
  517. -- for loads, weathering, etc.
  518. livery_textures = callback_functions[wagon_mod_name].on_post_get_livery_textures_from_design(livery_design, wagon_id, livery_textures)
  519. end
  520. return livery_textures
  521. end