spills.lua 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496
  1. local function splitname(name)
  2. local c = string.find(name, ":", 1)
  3. return string.sub(name, 1, c - 1), string.sub(name, c + 1, string.len(name))
  4. end
  5. function deepclone(t)
  6. if type(t) ~= "table" then
  7. return t
  8. end
  9. local meta = getmetatable(t)
  10. local target = {}
  11. for k, v in pairs(t) do
  12. if type(v) == "table" then
  13. target[k] = deepclone(v)
  14. else
  15. target[k] = v
  16. end
  17. end
  18. setmetatable(target, meta)
  19. return target
  20. end
  21. local spill_nodes = {
  22. "default:cobble",
  23. "default:mossycobble",
  24. "default:desert_cobble",
  25. "default:sand",
  26. "default:silver_sand",
  27. "default:desert_sand",
  28. "default:gravel",
  29. "default:snow",
  30. "default:snowblock",
  31. "default:dirt",
  32. "default:dirt_with_grass",
  33. "default:dirt_with_grass_footsteps",
  34. "default:dirt_with_dry_grass",
  35. "default:dirt_with_snow",
  36. "default:dirt_with_rainforest_litter",
  37. "default:dirt_with_coniferous_litter",
  38. -- "default:water_source",
  39. }
  40. local soil_more = {}
  41. local soil_less = {}
  42. local soil_abm_nodes = {}
  43. local soil_abm_neighbors = {}
  44. local burnable_soiled_nodes = {}
  45. local function spill_name(orig, lvl)
  46. local mod, node = splitname(orig)
  47. return "bitumen:oily_"..mod.."_"..node.."_"..lvl
  48. end
  49. local function reg_level(old, modname, nodename, level)
  50. local def = deepclone(minetest.registered_nodes[old])
  51. if not def then
  52. return nil
  53. end
  54. --def.groups.not_in_creative_inventory = 1
  55. def.groups.bitumen_oily = level
  56. def.description = "Oily " .. def.description
  57. local name1 = "bitumen:oily_"..modname.."_"..nodename.."_"..level
  58. -- table.insert(abm_list_1, old)
  59. -- downgrades[old] = name1
  60. for k, v in pairs(def.tiles) do
  61. local o = def.tiles[k]
  62. if type(o) == 'string' then
  63. def.tiles[k] = def.tiles[k].."^bitumen_oil_splat_"..level..".png"
  64. elseif type(o) == 'table' then
  65. o.name = o.name .. "^bitumen_oil_splat_"..level..".png"
  66. end
  67. end
  68. def.drops = name1
  69. minetest.register_node(name1, def)
  70. return name1
  71. end
  72. local register_spill_node = function(old)
  73. local modname, nodename = splitname(old)
  74. local n1 = reg_level(old, modname, nodename, 1)
  75. local n2 = reg_level(old, modname, nodename, 2)
  76. local n3 = reg_level(old, modname, nodename, 3)
  77. if n1 and n2 and n3 then
  78. soil_more[old] = n1
  79. soil_more[n1] = n2
  80. soil_more[n2] = n3
  81. soil_less[n3] = n2
  82. soil_less[n2] = n1
  83. soil_less[n1] = old
  84. table.insert(soil_abm_nodes, n3)
  85. table.insert(soil_abm_nodes, n2)
  86. table.insert(soil_abm_neighbors, n2)
  87. table.insert(soil_abm_neighbors, n1)
  88. table.insert(soil_abm_neighbors, old)
  89. table.insert(burnable_soiled_nodes, n3)
  90. table.insert(burnable_soiled_nodes, n2)
  91. end
  92. end
  93. -- TODO: wool, stairs, walls, seasons
  94. for _,n in ipairs(spill_nodes) do
  95. register_spill_node(n)
  96. end
  97. -- water is handled differently
  98. local function reg_oily_water(level)
  99. local color = 120 + (level * 3)
  100. local aff = (level + 2)
  101. minetest.register_node("bitumen:oily_default_water_source_"..level, {
  102. description = "Oily Water Source",
  103. drawtype = "liquid",
  104. tiles = {
  105. {
  106. name = "default_water_source_animated.png^[colorize:black:"..color,
  107. animation = {
  108. type = "vertical_frames",
  109. aspect_w = 16,
  110. aspect_h = 16,
  111. length = 2.0,
  112. },
  113. },
  114. },
  115. special_tiles = {
  116. -- New-style water source material (mostly unused)
  117. {
  118. name = "default_water_source_animated.png^[colorize:black:"..color,
  119. animation = {
  120. type = "vertical_frames",
  121. aspect_w = 16,
  122. aspect_h = 16,
  123. length = 2.0,
  124. },
  125. backface_culling = false,
  126. },
  127. },
  128. alpha = 160,
  129. paramtype = "light",
  130. walkable = false,
  131. pointable = false,
  132. diggable = false,
  133. buildable_to = true,
  134. is_ground_content = false,
  135. drop = "",
  136. drowning = 1,
  137. liquidtype = "source",
  138. liquid_alternative_flowing = "bitumen:oily_default_water_flowing_"..level,
  139. liquid_alternative_source = "bitumen:oily_default_water_source_"..level,
  140. liquid_viscosity = 1,
  141. post_effect_color = {a = 103 + (level * 50), r = 30 / aff, g = 60 / aff, b = 90 / aff},
  142. groups = {water = 3, liquid = 3, puts_out_fire = 1, cools_lava = 1, bitumen_oily = level},
  143. sounds = default.node_sound_water_defaults(),
  144. })
  145. minetest.register_node("bitumen:oily_default_water_flowing_"..level, {
  146. description = "Oily Flowing Water",
  147. drawtype = "flowingliquid",
  148. tiles = {"default_water.png^[colorize:black:"..color},
  149. special_tiles = {
  150. {
  151. name = "default_water_flowing_animated.png^[colorize:black:"..color,
  152. backface_culling = false,
  153. animation = {
  154. type = "vertical_frames",
  155. aspect_w = 16,
  156. aspect_h = 16,
  157. length = 0.8,
  158. },
  159. },
  160. {
  161. name = "default_water_flowing_animated.png^[colorize:black:"..color,
  162. backface_culling = true,
  163. animation = {
  164. type = "vertical_frames",
  165. aspect_w = 16,
  166. aspect_h = 16,
  167. length = 0.8,
  168. },
  169. },
  170. },
  171. alpha = 160,
  172. paramtype = "light",
  173. paramtype2 = "flowingliquid",
  174. walkable = false,
  175. pointable = false,
  176. diggable = false,
  177. buildable_to = true,
  178. is_ground_content = false,
  179. drop = "",
  180. drowning = 1,
  181. liquidtype = "flowing",
  182. liquid_alternative_flowing = "bitumen:oily_default_water_flowing_"..level,
  183. liquid_alternative_source = "bitumen:oily_default_water_source_"..level,
  184. liquid_viscosity = 1,
  185. post_effect_color = {a = 103 + (level * 50), r = 30 / aff, g = 60 / aff, b = 90 / aff},
  186. groups = {water = 3, liquid = 3, puts_out_fire = 1,
  187. not_in_creative_inventory = 1, cools_lava = 1, bitumen_oily = level},
  188. sounds = default.node_sound_water_defaults(),
  189. })
  190. minetest.register_node("bitumen:oily_default_river_water_source_"..level, {
  191. description = "Oily River Water Source",
  192. drawtype = "liquid",
  193. tiles = {
  194. {
  195. name = "default_river_water_source_animated.png^[colorize:black:"..color,
  196. animation = {
  197. type = "vertical_frames",
  198. aspect_w = 16,
  199. aspect_h = 16,
  200. length = 2.0,
  201. },
  202. },
  203. },
  204. special_tiles = {
  205. {
  206. name = "default_river_water_source_animated.png^[colorize:black:"..color,
  207. animation = {
  208. type = "vertical_frames",
  209. aspect_w = 16,
  210. aspect_h = 16,
  211. length = 2.0,
  212. },
  213. backface_culling = false,
  214. },
  215. },
  216. alpha = 160,
  217. paramtype = "light",
  218. walkable = false,
  219. pointable = false,
  220. diggable = false,
  221. buildable_to = true,
  222. is_ground_content = false,
  223. drop = "",
  224. drowning = 1,
  225. liquidtype = "source",
  226. liquid_alternative_flowing = "bitumen:oily_default_river_water_flowing_"..level,
  227. liquid_alternative_source = "bitumen:oily_default_river_water_source_"..level,
  228. liquid_viscosity = 1,
  229. -- Not renewable to avoid horizontal spread of water sources in sloping
  230. -- rivers that can cause water to overflow riverbanks and cause floods.
  231. -- River water source is instead made renewable by the 'force renew'
  232. -- option used in the 'bucket' mod by the river water bucket.
  233. liquid_renewable = false,
  234. liquid_range = 2,
  235. post_effect_color = {a = 103 + (level * 50), r = 30 / aff, g = 76 / aff, b = 90 / aff},
  236. groups = {water = 3, liquid = 3, puts_out_fire = 1, cools_lava = 1, bitumen_oily = level},
  237. sounds = default.node_sound_water_defaults(),
  238. })
  239. minetest.register_node("bitumen:oily_default_river_water_flowing_"..level, {
  240. description = "Oily Flowing River Water",
  241. drawtype = "flowingliquid",
  242. tiles = {"default_river_water.png^[colorize:black:"..color},
  243. special_tiles = {
  244. {
  245. name = "default_river_water_flowing_animated.png^[colorize:black:"..color,
  246. backface_culling = false,
  247. animation = {
  248. type = "vertical_frames",
  249. aspect_w = 16,
  250. aspect_h = 16,
  251. length = 0.8,
  252. },
  253. },
  254. {
  255. name = "default_river_water_flowing_animated.png^[colorize:black:"..color,
  256. backface_culling = true,
  257. animation = {
  258. type = "vertical_frames",
  259. aspect_w = 16,
  260. aspect_h = 16,
  261. length = 0.8,
  262. },
  263. },
  264. },
  265. alpha = 160,
  266. paramtype = "light",
  267. paramtype2 = "flowingliquid",
  268. walkable = false,
  269. pointable = false,
  270. diggable = false,
  271. buildable_to = true,
  272. is_ground_content = false,
  273. drop = "",
  274. drowning = 1,
  275. liquidtype = "flowing",
  276. liquid_alternative_flowing = "bitumen:oily_default_river_water_flowing_"..level,
  277. liquid_alternative_source = "bitumen:oily_default_river_water_source_"..level,
  278. liquid_viscosity = 1,
  279. liquid_renewable = false,
  280. liquid_range = 2,
  281. post_effect_color = {a = 103 + (level * 50), r = 30 / aff, g = 76 / aff, b = 90 / aff},
  282. groups = {water = 3, liquid = 3, puts_out_fire = 1,
  283. not_in_creative_inventory = 1, cools_lava = 1, bitumen_oily = level},
  284. sounds = default.node_sound_water_defaults(),
  285. })
  286. end
  287. local function insert_soiling(mod, node)
  288. local old = mod .. ":" .. node
  289. local n1 = "bitumen:oily_"..mod.."_"..node.."_1"
  290. local n2 = "bitumen:oily_"..mod.."_"..node.."_2"
  291. local n3 = "bitumen:oily_"..mod.."_"..node.."_3"
  292. soil_more[old] = n1
  293. soil_more[n1] = n2
  294. soil_more[n2] = n3
  295. soil_less[n3] = n2
  296. soil_less[n2] = n1
  297. soil_less[n1] = old
  298. table.insert(soil_abm_nodes, n3)
  299. table.insert(soil_abm_nodes, n2)
  300. table.insert(soil_abm_neighbors, n2)
  301. table.insert(soil_abm_neighbors, n1)
  302. table.insert(soil_abm_neighbors, old)
  303. end
  304. reg_oily_water(1)
  305. reg_oily_water(2)
  306. reg_oily_water(3)
  307. insert_soiling("default", "water_source")
  308. insert_soiling("default", "river_water_source")
  309. -- abms
  310. minetest.register_abm({
  311. nodenames = soil_abm_nodes,
  312. neighbors = soil_abm_neighbors,
  313. interval = 5,
  314. chance = 20,
  315. action = function(pos, node, active_object_count, active_object_count_wider)
  316. local alvl = minetest.registered_nodes[node.name].groups.bitumen_oily or 0
  317. local unsoiled = minetest.find_nodes_in_area(
  318. {x=pos.x-1, y=pos.y-1, z=pos.z-1},
  319. {x=pos.x+1, y=pos.y , z=pos.z+1},
  320. soil_abm_neighbors
  321. )
  322. if not unsoiled then
  323. return
  324. end
  325. local off = math.random(#unsoiled)
  326. for i = 1, #unsoiled do
  327. local bp = unsoiled[((i + off) % #unsoiled) + 1]
  328. local bnode = minetest.get_node(bp)
  329. local blvl = minetest.registered_nodes[bnode.name].groups.bitumen_oily or 0
  330. if alvl > blvl then
  331. minetest.set_node(pos, {name = soil_less[node.name]})
  332. minetest.set_node(bp, {name = soil_more[bnode.name]})
  333. return
  334. end
  335. end
  336. end
  337. })
  338. -- crude oil spreads into certain tiles
  339. minetest.register_abm({
  340. nodenames = {"bitumen:crude_oil", "bitumen:crude_oil_full"},
  341. neighbors = soil_abm_neighbors,
  342. interval = 3,
  343. chance = 10,
  344. action = function(pos, node, active_object_count, active_object_count_wider)
  345. local unsoiled = minetest.find_nodes_in_area(
  346. {x=pos.x-1, y=pos.y-1, z=pos.z-1},
  347. {x=pos.x+1, y=pos.y , z=pos.z+1},
  348. soil_abm_neighbors
  349. )
  350. if not unsoiled then
  351. return
  352. end
  353. local off = math.random(#unsoiled)
  354. for i = 1, #unsoiled do
  355. local bp = unsoiled[((i + off) % #unsoiled) + 1]
  356. local bnode = minetest.get_node(bp)
  357. local x = soil_more[bnode.name]
  358. if x == nil then
  359. -- print(dump2(x).. " -> " ..dump2(bnode.name))
  360. else
  361. local lvl = minetest.get_node_level(pos)
  362. local nl = math.max(0, lvl - 10)
  363. if nl == 0 then
  364. minetest.set_node(pos, {name="air"})
  365. else
  366. minetest.set_node_level(pos, nl)
  367. end
  368. minetest.set_node(bp, {name=x})
  369. return
  370. end
  371. end
  372. end
  373. })
  374. minetest.register_abm({
  375. label = "Spilled nodes burn slowly",
  376. nodenames = burnable_soiled_nodes,
  377. neighbors = {"fire:basic_flame"},
  378. interval = 4,
  379. chance = 20,
  380. catch_up = true,
  381. action = function(pos, node, active_object_count, active_object_count_wider)
  382. local n = minetest.get_node(pos)
  383. if n then
  384. local less = soil_less[n.name]
  385. if less then
  386. minetest.set_node(pos, {name = less})
  387. end
  388. end
  389. end,
  390. })
  391. minetest.register_abm({
  392. label = "Spilled nodes catch fire",
  393. nodenames = burnable_soiled_nodes,
  394. neighbors = {"fire:basic_flame"},
  395. interval = 3,
  396. chance = 5,
  397. catch_up = true,
  398. action = function(pos, node, active_object_count, active_object_count_wider)
  399. local n = minetest.find_node_near(pos, 1, {"air"})
  400. if n then
  401. minetest.set_node(n, {name = "fire:basic_flame"})
  402. end
  403. end,
  404. })