init.lua 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370
  1. -- global callbacks
  2. trail = {}
  3. -- Parameters
  4. local FOO = true -- Enable footprints.
  5. local FUNCYC = 0.2 -- Function cycle in seconds.
  6. local TRACHA = minetest.settings:get("trail_hardpack_chance") or 0.1 -- Chance walked dirt/grass is worn and compacted to trail:trail.
  7. local ICECHA = minetest.settings:get("trail_ice_chance") or 0.05 -- Chance walked snowblock is compacted to ice.
  8. local EROSION = minetest.settings:get_bool("trail_erosion", true) -- Enable footprint erosion.
  9. local TRAIL_EROSION = minetest.settings:get_bool("trail_trail_erosion", true) -- Allow hard-packed soil to erode back to dirt
  10. local EROINT = minetest.settings:get("trail_erosion_interval") or 16 -- Erosion interval.
  11. local EROCHA = minetest.settings:get("trail_erosion_chance") or 128 -- Erosion 1/x chance.
  12. -- Utility
  13. local function deep_copy(input)
  14. if type(input) ~= "table" then
  15. return input
  16. end
  17. local output = {}
  18. for index, value in pairs(input) do
  19. output[index] = deep_copy(value)
  20. end
  21. return output
  22. end
  23. -- Player positions
  24. local player_pos_previous = {}
  25. minetest.register_on_joinplayer(function(player)
  26. player_pos_previous[player:get_player_name()] = {x = 0, y = 0, z = 0}
  27. end)
  28. minetest.register_on_leaveplayer(function(player)
  29. player_pos_previous[player:get_player_name()] = nil
  30. end)
  31. local trails = {}
  32. local erosion = {}
  33. trail.register_trample_node = function(trampleable_node_name, trample_def)
  34. if trails[trampleable_node_name] then
  35. minetest.log("error", "[trail] Attempted to call trail.register_trample_node to register trampleable node "
  36. .. trampleable_node_name ..", which has already been registered as trampleable.")
  37. return
  38. end
  39. local trampleable_node_def = minetest.registered_nodes[trampleable_node_name]
  40. if trampleable_node_def == nil then
  41. minetest.log("error", "[trail] Attempted to call trail.register_trample_node with the trampleable node "
  42. .. trampleable_node_name ..", which has not yet been registered as a node.")
  43. return
  44. end
  45. local trampled_node_name = trample_def.trampled_node_name or trampleable_node_name.."_trampled"
  46. if not minetest.registered_nodes[trampled_node_name] then
  47. local trampled_node_def = deep_copy(trampleable_node_def) -- start with a deep copy of the source node's definition
  48. if trample_def.trampled_node_def_override then -- override any values that need to be overridden explicitly
  49. for key, value in pairs(trample_def.trampled_node_def_override) do
  50. trampled_node_def[key] = value
  51. end
  52. end
  53. -- Set up the erosion ABM group
  54. if EROSION and trample_def.erodes ~= false then
  55. local groups = trampled_node_def.groups or {}
  56. groups.trail_erodes = 1
  57. trampled_node_def.groups = groups
  58. erosion[trampled_node_name] = trampleable_node_name
  59. end
  60. -- If the source node doesn't have a special drop, then set drop to drop a source node rather than dropping a node with a footstep.
  61. if trampled_node_def.drop == nil then
  62. trampled_node_def.drop = trampleable_node_name
  63. end
  64. -- Modify the +Y tile with a footprint overlay
  65. if trample_def.add_footprint_overlay ~= false then
  66. local tiles = trampled_node_def.tiles
  67. local first_tile = tiles[1]
  68. local second_tile = tiles[2]
  69. if second_tile == nil then
  70. -- The provided node def only has one tile for all sides. We need to only modify the +Y tile,
  71. -- so we need to copy the original first tile into the second tile slot
  72. tiles[2] = deep_copy(first_tile)
  73. end
  74. if type(first_tile) == "table" then
  75. first_tile.name = first_tile.name .. "^default_footprint.png"
  76. elseif type(first_tile) == "string" then
  77. first_tile = first_tile .. "^default_footprint.png"
  78. end
  79. trampled_node_def.tiles[1] = first_tile
  80. end
  81. minetest.register_node(":"..trampled_node_name, trampled_node_def)
  82. -- If hard pack has been defined for this trail type, add it
  83. local hard_pack_node_name = trample_def.hard_pack_node_name
  84. if hard_pack_node_name then
  85. local hard_pack_probability = trample_def.hard_pack_probability or 0.1
  86. trails[trampled_node_name] = {name=hard_pack_node_name, probability=hard_pack_probability}
  87. end
  88. end
  89. local probability = trample_def.probability or 1
  90. trails[trampleable_node_name] = {name=trampled_node_name, probability=probability, randomize_trampled_param2 = trample_def.randomize_trampled_param2,}
  91. end
  92. trail.register_erosion = function(source_node_name, destination_node_name)
  93. if not EROSION then
  94. return
  95. end
  96. if minetest.registered_nodes[source_node_name] == nil then
  97. minetest.log("error", "[trail] attempted to call trail.register_erosion with unregistered source node "
  98. .. source_node_name)
  99. return
  100. end
  101. if minetest.registered_nodes[destination_node_name] == nil then
  102. minetest.log("error", "[trail] attempted to call trail.register_erosion with unregistered destination node "
  103. .. destination_node_name)
  104. return
  105. end
  106. if minetest.get_item_group(source_node_name, "trail_erodes") == 0 then
  107. minetest.log("error", "[trail] attempted to call trail.register_erosion with source node "
  108. .. destination_node_name .. " that wasn't in group trail_erodes.")
  109. return
  110. end
  111. erosion[source_node_name] = destination_node_name
  112. end
  113. -- Nodes
  114. -- hard-packed soil
  115. local trail_trail_def = {
  116. tiles = {"trail_trailtop.png", "default_dirt.png",
  117. "default_dirt.png^trail_trailside.png"},
  118. groups = {crumbly = 2},
  119. drop = "default:dirt",
  120. sounds = default.node_sound_dirt_defaults(),
  121. }
  122. if TRAIL_EROSION then
  123. trail_trail_def.groups.trail_erodes = 1
  124. end
  125. minetest.register_node("trail:trail", trail_trail_def)
  126. if TRAIL_EROSION then
  127. trail.register_erosion("trail:trail", "default:dirt")
  128. end
  129. -- Default dirt
  130. trail.register_trample_node("default:dirt", {
  131. trampled_node_name = "trail:dirt",
  132. trampled_node_def_override = {description = "Dirt with Footprint",},
  133. hard_pack_node_name = "trail:trail",
  134. hard_pack_probability = TRACHA,
  135. })
  136. trail.register_trample_node("default:dirt_with_grass", {
  137. trampled_node_name = "trail:dirt_with_grass",
  138. trampled_node_def_override = {description = "Dirt with Grass and Footprint",},
  139. hard_pack_node_name = "trail:trail",
  140. hard_pack_probability = TRACHA,
  141. })
  142. trail.register_trample_node("default:dirt_with_dry_grass", {
  143. trampled_node_name = "trail:dirt_with_dry_grass",
  144. trampled_node_def_override = {description = "Dirt with Dry Grass and Footprint",},
  145. hard_pack_node_name = "trail:trail",
  146. hard_pack_probability = TRACHA,
  147. })
  148. trail.register_trample_node("default:dirt_with_snow", {
  149. trampled_node_name = "trail:dirt_with_snow",
  150. trampled_node_def_override = {description = "Dirt with Snow and Footprint",},
  151. hard_pack_node_name = "trail:trail",
  152. hard_pack_probability = TRACHA,
  153. })
  154. trail.register_trample_node("default:dirt_with_rainforest_litter", {
  155. trampled_node_name = "trail:dirt_with_rainforest_litter",
  156. trampled_node_def_override = {description = "Dirt with Rainforest Litter and Footprint",},
  157. hard_pack_node_name = "trail:trail",
  158. hard_pack_probability = TRACHA,
  159. })
  160. trail.register_trample_node("default:dirt_with_coniferous_litter", {
  161. trampled_node_name = "trail:dirt_with_coniferous_litter",
  162. trampled_node_def_override = {description = "Dirt with Coniferous Litter and Footprint",},
  163. hard_pack_node_name = "trail:trail",
  164. hard_pack_probability = TRACHA,
  165. })
  166. -- Default sand
  167. trail.register_trample_node("default:sand", {
  168. trampled_node_name = "trail:sand",
  169. trampled_node_def_override = {description = "Sand with Footprint",},
  170. })
  171. trail.register_trample_node("default:desert_sand", {
  172. trampled_node_name = "trail:desert_sand",
  173. trampled_node_def_override = {description = "Desert Sand with Footprint",},
  174. })
  175. trail.register_trample_node("default:silver_sand", {
  176. trampled_node_name = "trail:silver_sand",
  177. trampled_node_def_override = {description = "Silver Sand with Footprint",},
  178. })
  179. trail.register_trample_node("default:gravel", {
  180. trampled_node_name = "trail:gravel",
  181. trampled_node_def_override = {description = "Gravel with Footprint",},
  182. })
  183. -- Default snow
  184. trail.register_trample_node("default:snowblock", {
  185. trampled_node_name = "trail:snowblock",
  186. trampled_node_def_override = {description = "Snow Block with Footprint",},
  187. hard_pack_node_name = "default:ice",
  188. hard_pack_probability = ICECHA,
  189. })
  190. trail.register_trample_node("default:snow", {
  191. trampled_node_name = "trail:snow",
  192. trampled_node_def_override = {description = "Snow with Footprint",},
  193. })
  194. --[[if minetest.get_modpath("farming") then
  195. -- Flattened wheat
  196. minetest.register_node("trail:wheat", {
  197. description = "Flattened Wheat",
  198. tiles = {"trail_flat_wheat.png"},
  199. inventory_image = "trail_flat_wheat.png",
  200. drawtype = "nodebox",
  201. paramtype = "light",
  202. paramtype2 = "facedir",
  203. buildable_to = true,
  204. node_box = {
  205. type = "fixed",
  206. fixed = {
  207. {-0.5, -0.5, -0.5, 0.5, -3 / 8, 0.5}
  208. },
  209. },
  210. groups = {snappy = 3, flammable = 2, attached_node = 1},
  211. drop = "",
  212. sounds = default.node_sound_leaves_defaults(),
  213. })
  214. trail.register_trample_node("farming:wheat_5", {
  215. trampled_node_name = "trail:wheat",
  216. randomize_trampled_param2 = true,
  217. })
  218. trail.register_trample_node("farming:wheat_6", {
  219. trampled_node_name = "trail:wheat",
  220. randomize_trampled_param2 = true,
  221. })
  222. trail.register_trample_node("farming:wheat_7", {
  223. trampled_node_name = "trail:wheat",
  224. randomize_trampled_param2 = true,
  225. })
  226. trail.register_trample_node("farming:wheat_8", {
  227. trampled_node_name = "trail:wheat",
  228. randomize_trampled_param2 = true,
  229. })
  230. end
  231. --]]
  232. -- Globalstep function
  233. if FOO then
  234. local timer = 0
  235. local get_param2 = function(trail_def)
  236. if trail_def.randomize_trampled_param2 then
  237. return math.random(0,3)
  238. end
  239. return 0
  240. end
  241. minetest.register_globalstep(function(dtime)
  242. timer = timer + dtime
  243. if timer > FUNCYC then
  244. timer = 0
  245. for _, player in ipairs(minetest.get_connected_players()) do
  246. local pos = player:get_pos()
  247. local player_name = player:get_player_name()
  248. local current_player_pos = {
  249. x = math.floor(pos.x + 0.5),
  250. y = math.floor(pos.y + 0.2),
  251. z = math.floor(pos.z + 0.5)
  252. }
  253. --if player_pos_previous[player_name] == nil then
  254. --break
  255. --end
  256. if current_player_pos.x ~= player_pos_previous[player_name].x or
  257. current_player_pos.y < player_pos_previous[player_name].y or
  258. current_player_pos.z ~= player_pos_previous[player_name].z then
  259. local p_snow = {
  260. x = math.floor(pos.x + 0.5),
  261. y = math.floor(pos.y + 1.2),
  262. z = math.floor(pos.z + 0.5)
  263. }
  264. local n_snow = minetest.get_node(p_snow).name
  265. -- test ground cover first (snow, wheat)
  266. local trail_def = trails[n_snow]
  267. if trail_def then
  268. if math.random() <= trail_def.probability then
  269. local p_snowpl = {
  270. x = math.floor(pos.x + 0.5),
  271. y = math.floor(pos.y + 0.5),
  272. z = math.floor(pos.z + 0.5)
  273. }
  274. minetest.set_node(p_snowpl, {name = trail_def.name, param2 = get_param2(trail_def)})
  275. end
  276. else
  277. local p_ground = {
  278. x = math.floor(pos.x + 0.5),
  279. y = math.floor(pos.y + 0.4),
  280. z = math.floor(pos.z + 0.5)
  281. }
  282. local n_ground = minetest.get_node(p_ground).name
  283. trail_def = trails[n_ground]
  284. if trail_def and math.random() <= trail_def.probability then
  285. local p_groundpl = {
  286. x = math.floor(pos.x + 0.5),
  287. y = math.floor(pos.y - 0.25),
  288. z = math.floor(pos.z + 0.5)
  289. }
  290. minetest.set_node(p_groundpl, {name = trail_def.name, param2 = get_param2(trail_def)})
  291. end
  292. end
  293. end
  294. player_pos_previous[player_name] = current_player_pos
  295. end
  296. end
  297. end)
  298. end
  299. -- ABM
  300. if EROSION then
  301. minetest.register_abm({
  302. nodenames = {"group:trail_erodes"},
  303. interval = EROINT,
  304. chance = EROCHA,
  305. catch_up = true,
  306. action = function(pos, node, _, _)
  307. local nodename = node.name
  308. local erodes_to = erosion[nodename]
  309. if erodes_to then
  310. minetest.set_node(pos, {name = erodes_to})
  311. else
  312. minetest.log("error", "[trail] The node " .. nodename .. " is in group trail_erodes but "
  313. .. " didn't have an erosion target node defined.")
  314. end
  315. end
  316. })
  317. end