fluids.lua 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664
  1. local function combine_table(a, b)
  2. a = a or {}
  3. for k,v in pairs(a) do
  4. b[k] = v
  5. end
  6. return b
  7. end
  8. local function register_fluid(modname, name, info)
  9. local fname = modname..":"..name
  10. local full_fname = modname..":"..name.."_full"
  11. local gname = modname.."_"..name.."_fluid"
  12. local groups = { liquid = 3, [gname]=1}
  13. for n,l in pairs(info.groups) do
  14. groups[n] = l
  15. end
  16. local full_groups = {not_in_creative_inventory = 1}
  17. for n,l in pairs(groups) do
  18. full_groups[n] = l
  19. end
  20. minetest.register_node(fname, combine_table(info.def, {
  21. description = info.desc,
  22. drawtype = "nodebox",
  23. paramtype = "light",
  24. paramtype2 = "leveled",
  25. tiles = {
  26. {
  27. name = "default_river_water_source_animated.png"..info.colorize,
  28. animation = {
  29. type = "vertical_frames",
  30. aspect_w = 16,
  31. aspect_h = 16,
  32. length = 2.0,
  33. },
  34. },
  35. },
  36. special_tiles = {
  37. {
  38. name = "default_river_water_source_animated.png"..info.colorize,
  39. animation = {
  40. type = "vertical_frames",
  41. aspect_w = 16,
  42. aspect_h = 16,
  43. length = 2.0,
  44. },
  45. backface_culling = false,
  46. },
  47. },
  48. leveled = 64,
  49. alpha = 160,
  50. drop = "",
  51. drowning = 1,
  52. walkable = false, -- because viscosity doesn't work for regular nodes, and the liquid hack can't be leveled
  53. climbable = true, -- because viscosity doesn't work for regular nodes, and the liquid hack can't be leveled
  54. pointable = false,
  55. diggable = false,
  56. buildable_to = true,
  57. post_effect_color = info.post_effect_color,
  58. groups = groups,
  59. nonfull_name = fname,
  60. sounds = default.node_sound_water_defaults(),
  61. node_box = {
  62. type = "leveled",
  63. fixed = {
  64. {-0.5, -0.5, -0.5, 0.5, 0.5, 0.5}, -- NodeBox1
  65. }
  66. }
  67. }))
  68. -- this is a special node used to optimize large pools of water
  69. -- its flowing abm runs much less frequently
  70. minetest.register_node(full_fname, combine_table(info.def, {
  71. description = info.desc,
  72. drawtype = "nodebox",
  73. paramtype = "light",
  74. paramtype2 = "leveled",
  75. tiles = {
  76. {
  77. name = "default_river_water_source_animated.png"..info.colorize,
  78. animation = {
  79. type = "vertical_frames",
  80. aspect_w = 16,
  81. aspect_h = 16,
  82. length = 2.0,
  83. },
  84. },
  85. },
  86. special_tiles = {
  87. {
  88. name = "default_river_water_source_animated.png"..info.colorize,
  89. animation = {
  90. type = "vertical_frames",
  91. aspect_w = 16,
  92. aspect_h = 16,
  93. length = 2.0,
  94. },
  95. backface_culling = false,
  96. },
  97. },
  98. leveled = 64,
  99. alpha = 160,
  100. drop = "",
  101. drowning = 1,
  102. walkable = false, -- because viscosity doesn't work for regular nodes, and the liquid hack can't be leveled
  103. climbable = true, -- because viscosity doesn't work for regular nodes, and the liquid hack can't be leveled
  104. pointable = false,
  105. diggable = false,
  106. buildable_to = true,
  107. post_effect_color = info.post_effect_color,
  108. groups = full_groups,
  109. nonfull_name = fname,
  110. sounds = default.node_sound_water_defaults(),
  111. node_box = {
  112. type = "leveled",
  113. fixed = {
  114. {-0.5, -0.5, -0.5, 0.5, 0.5, 0.5}, -- NodeBox1
  115. }
  116. }
  117. }))
  118. local default_soak = {
  119. ["default:cobble"] = 10,
  120. ["default:desert_cobble"] = 10,
  121. ["default:mossycobble"] = 9,
  122. ["default:dirt"] = 2,
  123. ["default:dirt_with_grass"] = 2,
  124. ["default:dirt_with_grass_footsteps"] = 2,
  125. ["default:dirt_with_dry_grass"] = 2,
  126. ["default:dirt_with_coniferous_litter"] = 2,
  127. ["default:dirt_with_rainforest_litter"] = 1,
  128. ["default:gravel"] = 8,
  129. ["default:coral_orange"] = 6,
  130. ["default:coral_brown"] = 6,
  131. ["default:coral_skeleton"] = 6,
  132. ["default:sand"] = 6,
  133. ["default:sand_with_kelp"] = 6,
  134. ["default:desert_sand"] = 7,
  135. ["default:silver_sand"] = 7,
  136. ["default:snow"] = 4,
  137. ["default:snowblock"] = 4,
  138. ["default:leaves"] = 60,
  139. ["default:bush_leaves"] = 60,
  140. ["default:jungleleaves"] = 60,
  141. ["default:pine_needles"] = 60,
  142. ["default:acacia_leaves"] = 60,
  143. ["default:acacia_bush_leaves"] = 60,
  144. ["default:aspen_leaves"] = 60,
  145. -- dilution
  146. ["default:water_source"] = 65,
  147. ["default:water_flowing"] = 65,
  148. ["default:river_water_source"] = 65,
  149. ["default:river_water_flowing"] = 65,
  150. -- boiling -- TODO: steam effect
  151. ["default:lava_source"] = 65,
  152. ["default:lava_flowing"] = 65,
  153. -- no ladder hacks
  154. ["default:ladder_wood"] = 65,
  155. -- ["default:ladder_steel"] = 65, -- need to figure out a way for water to flow through ladders
  156. ["default:sign_wall_wood"] = 65,
  157. ["default:sign_wall_steel"] = 65,
  158. ["default:fence_wood"] = 65,
  159. ["default:fence_acacia_wood"] = 65,
  160. ["default:fence_junglewood"] = 65,
  161. ["default:fence_pine_wood"] = 65,
  162. ["default:fence_aspen_wood"] = 65,
  163. ["default:torch"] = 65,
  164. ["carts:rail"] = 65,
  165. ["carts:brakerail"] = 65,
  166. ["carts:powerrail"] = 65,
  167. }
  168. local soak_names = {}
  169. local soak = {}
  170. if info.no_default_soak ~= true then
  171. soak = default_soak
  172. for n,_ in pairs(soak) do
  173. table.insert(soak_names, n)
  174. end
  175. end
  176. -- todo: superflammability
  177. -- boil-off for water near fire
  178. -- vapors or explosions
  179. if info.evap_chance > 0 then
  180. if info.vapor == true then
  181. -- evaporation to flammable vapors
  182. minetest.register_abm({
  183. nodenames = {"group:"..gname},
  184. neighbors = {"air"},
  185. interval = info.evap_interval,
  186. chance = info.evap_chance,
  187. action = function(pos)
  188. local mylevel = minetest.get_node_level(pos)
  189. if math.random(16 - minetest.get_node_light(pos)) == 1 then
  190. if mylevel > info.evap_rate then
  191. local vn = minetest.find_node_near(pos, 1, {"air"})
  192. if vn then
  193. -- only evaporate if there is somewhere to evaporate to
  194. minetest.set_node_level(pos, mylevel - info.evap_rate)
  195. minetest.set_node(vn, {name = "bitumen:vapor_2"})
  196. end
  197. else
  198. minetest.set_node(pos, {name = "bitumen:vapor_2"})
  199. end
  200. end
  201. end
  202. })
  203. else
  204. -- normal evaporation
  205. minetest.register_abm({
  206. nodenames = {"group:"..gname},
  207. neighbors = {"air"},
  208. interval = info.evap_interval,
  209. chance = info.evap_chance,
  210. action = function(pos)
  211. local mylevel = minetest.get_node_level(pos)
  212. if math.random(16 - minetest.get_node_light(pos)) == 1 then
  213. if mylevel > info.evap_rate then
  214. minetest.set_node_level(pos, mylevel - info.evap_rate)
  215. else
  216. minetest.set_node(pos, {name = "air"})
  217. end
  218. end
  219. end
  220. })
  221. end
  222. end
  223. -- de-stagnation (faster flowing)
  224. minetest.register_abm({
  225. nodenames = {full_fname},
  226. neighbors = {"air"},
  227. interval = info.reflow_interval or 5,
  228. chance = info.flow_chance or 1,
  229. action = function(pos)
  230. -- if it's near air it might flow
  231. minetest.set_node(pos, {name = fname})
  232. end
  233. })
  234. -- flowing
  235. minetest.register_abm({
  236. nodenames = {fname},
  237. neighbors = {"group:"..gname, "air", "group:bitumen_vapor"},
  238. interval = info.flow_interval or 1,
  239. chance = info.flow_chance or 1,
  240. action = function(pos)
  241. local mylevel = minetest.get_node_level(pos)
  242. -- print("\n mylevel ".. mylevel)
  243. -- falling
  244. local below = {x=pos.x, y=pos.y - 1, z=pos.z}
  245. local nbelow = minetest.get_node(below).name
  246. if nbelow == "air" then
  247. minetest.set_node(below, {name=fname})
  248. minetest.set_node_level(below, mylevel)
  249. minetest.set_node(pos, {name="air"})
  250. return
  251. elseif (minetest.registered_nodes[nbelow].groups.bitumen_vapor or 0) > 0 then
  252. -- fall through vapors
  253. minetest.set_node(below, {name=fname})
  254. minetest.set_node_level(below, mylevel)
  255. minetest.set_node(pos, {name=nbelow})
  256. elseif nbelow == fname then
  257. local blevel = minetest.get_node_level(below)
  258. if blevel < 64 then
  259. local sum = mylevel + blevel
  260. minetest.set_node_level(below, math.min(64, sum))
  261. if sum > 64 then
  262. mylevel = sum - 64
  263. minetest.set_node_level(pos, mylevel)
  264. -- keep flowing the liquid. this speeds up cascades
  265. else
  266. minetest.set_node(pos, {name="air"})
  267. return
  268. end
  269. end
  270. else -- check soaking
  271. local rate = soak[nbelow]
  272. if rate ~= nil then
  273. local remains = mylevel - rate
  274. if remains > 0 then
  275. minetest.set_node_level(pos, remains)
  276. else
  277. minetest.set_node(pos, {name="air"})
  278. end
  279. if 1 == math.random(120 / mylevel) then
  280. minetest.set_node(below, {name = "air"})
  281. end
  282. mylevel = remains
  283. --return -- keep the fluid mechanics
  284. end
  285. end
  286. local air_nodes = minetest.find_nodes_in_area(
  287. {x=pos.x - 1, y=pos.y - 1, z=pos.z - 1},
  288. {x=pos.x + 1, y=pos.y, z=pos.z + 1},
  289. {"air", "group:bitumen_vapor"}
  290. )
  291. -- print("x: "..pos.x.." y: "..pos.y.." z: "..pos.z)
  292. -- print("air list len ".. #air_nodes)
  293. local off = math.random(#air_nodes)
  294. for i = 1,#air_nodes do
  295. --local theirlevel = minetest.get_node_level(fp)
  296. local fp = air_nodes[((i + off) % #air_nodes) + 1]
  297. if mylevel >= 2 then
  298. local half = math.ceil(mylevel / 2)
  299. minetest.set_node_level(pos, mylevel - half)
  300. minetest.set_node(fp, {name= fname})
  301. minetest.set_node_level(fp, half)
  302. -- minetest.check_for_falling(fp)
  303. return
  304. end
  305. end
  306. local flow_nodes = minetest.find_nodes_in_area(
  307. {x=pos.x - 1, y=pos.y , z=pos.z - 1},
  308. {x=pos.x + 1, y=pos.y, z=pos.z + 1},
  309. "group:"..gname
  310. )
  311. -- print("x: "..pos.x.." y: "..pos.y.." z: "..pos.z)
  312. -- print("list len ".. #flow_nodes)
  313. local off = math.random(#flow_nodes)
  314. for i = 1,#flow_nodes do
  315. local fp = flow_nodes[((i + off) % #flow_nodes) + 1]
  316. local theirlevel = minetest.get_node_level(fp)
  317. -- print("theirlevel "..theirlevel)
  318. if mylevel - theirlevel >= 2 then
  319. local diff = (mylevel - theirlevel)
  320. local half = math.ceil(diff / 2)
  321. minetest.set_node_level(pos, mylevel - half)
  322. minetest.set_node_level(fp, theirlevel + (diff - half))
  323. return
  324. end
  325. end
  326. -- local n = minetest.get_node(fp);
  327. -- -- check above to make sure it can get here
  328. -- local na = minetest.get_node({x=fp.x, y=fp.y+1, z=fp.z})
  329. --
  330. -- -- print("name: " .. na.name .. " l: " ..g)
  331. -- if na.name == "default:river_water_flowing" or na.name == "default:river_water_flowing" then
  332. -- minetest.set_node(fp, {name=node.name})
  333. -- minetest.set_node(pos, {name=n.name})
  334. -- return
  335. -- end
  336. -- end
  337. -- stagnation: this may not work
  338. if mylevel == 64 then
  339. --print("stagnating ".. pos.x .. ","..pos.y..","..pos.z)
  340. minetest.set_node(pos, {name = full_fname})
  341. end
  342. end
  343. })
  344. end
  345. bitumen.register_fluid = register_fluid
  346. -- distillation products
  347. register_fluid("bitumen", "mineral_spirits", {
  348. desc = "Mineral Spirits",
  349. groups = {flammable=1, petroleum=1, flash_ignite=3},
  350. colorize = "^[colorize:white:160",
  351. post_effect_color = {a = 103, r = 30, g = 76, b = 90},
  352. vapor = true,
  353. evap_interval = 10,
  354. evap_chance = 10,
  355. evap_rate = 5,
  356. })
  357. register_fluid("bitumen", "gasoline", {
  358. desc = "Gasoline",
  359. groups = {flammable=1, petroleum=1, flash_ignite=2},
  360. colorize = "^[colorize:yellow:160",
  361. post_effect_color = {a = 103, r = 30, g = 76, b = 90},
  362. vapor = true,
  363. evap_interval = 20,
  364. evap_chance = 10,
  365. evap_rate = 5,
  366. })
  367. register_fluid("bitumen", "diesel", {
  368. desc = "Diesel",
  369. groups = {flammable=1, petroleum=1},
  370. colorize = "^[colorize:red:160",
  371. post_effect_color = {a = 103, r = 230, g = 76, b = 90},
  372. evap_interval = 20,
  373. evap_chance = 20,
  374. evap_rate = 2,
  375. })
  376. register_fluid("bitumen", "kerosene", {
  377. desc = "Kerosene",
  378. groups = {flammable=1, petroleum=1},
  379. colorize = "^[colorize:white:100",
  380. post_effect_color = {a = 103, r = 80, g = 76, b = 190},
  381. vapor = true,
  382. evap_interval = 20,
  383. evap_chance = 20,
  384. evap_rate = 8,
  385. })
  386. register_fluid("bitumen", "light_oil", {
  387. desc = "Light Oil",
  388. groups = {flammable=1, petroleum=1},
  389. colorize = "^[colorize:brown:220",
  390. post_effect_color = {a = 103, r = 80, g = 76, b = 90},
  391. evap_chance = 0,
  392. })
  393. register_fluid("bitumen", "heavy_oil", {
  394. desc = "Heavy Oil",
  395. groups = {flammable=1, petroleum=1},
  396. colorize = "^[colorize:brown:240",
  397. post_effect_color = {a = 103, r = 80, g = 76, b = 90},
  398. evap_chance = 0,
  399. })
  400. register_fluid("bitumen", "tar", {
  401. desc = "Tar",
  402. groups = {flammable=1, petroleum=1},
  403. colorize = "^[colorize:black:210",
  404. post_effect_color = {a = 103, r = 80, g = 76, b = 90},
  405. evap_chance = 0,
  406. })
  407. -- oil itself
  408. register_fluid("bitumen", "crude_oil", {
  409. desc = "Crude Oil",
  410. groups = {flammable=1, petroleum=1},
  411. reflow_interval = 5,
  412. reflow_chance = 2,
  413. flow_interval = 3,
  414. flow_chance = 2,
  415. -- oil has its own special soaking code
  416. no_default_soak = true,
  417. colorize = "^[colorize:black:240",
  418. post_effect_color = {a = 103, r = 80, g = 76, b = 90},
  419. evap_chance = 0,
  420. })
  421. -- other
  422. bitumen.register_fluid("bitumen", "drill_mud", {
  423. desc = "Drilling Mud",
  424. groups = {petroleum=1},
  425. reflow_interval = 5,
  426. reflow_chance = 1,
  427. flow_interval = 1,
  428. flow_chance = 1,
  429. colorize = "^[colorize:brown:40",
  430. post_effect_color = {a = 103, r = 80, g = 76, b = 90},
  431. evap_chance = 0,
  432. })
  433. bitumen.register_fluid("bitumen", "drill_mud_dirty", {
  434. desc = "Dirty Drilling Mud",
  435. groups = {petroleum=1},
  436. reflow_interval = 5,
  437. reflow_chance = 1,
  438. flow_interval = 1,
  439. flow_chance = 1,
  440. colorize = "^[colorize:brown:140",
  441. post_effect_color = {a = 103, r = 80, g = 76, b = 90},
  442. evap_chance = 0,
  443. })
  444. -- the default fire spreads too slow for gasoline...
  445. minetest.register_abm({
  446. nodenames = {"group:flash_ignite"},
  447. neighbors = {
  448. "fire:basic_flame",
  449. "fire:permanent_flame",
  450. "default:torch",
  451. "default:lava_source",
  452. "default:lava_flowing",
  453. "tnt:gunpowder_burning",
  454. },
  455. interval = 5,
  456. chance = 1,
  457. action = function(pos, node, active_object_count, active_object_count_wider)
  458. local airs = minetest.find_nodes_in_area(
  459. {x=pos.x-2, y=pos.y-2, z=pos.z-2},
  460. {x=pos.x+2, y=pos.y+2, z=pos.z+2},
  461. {"air"}
  462. )
  463. if not airs then
  464. return
  465. end
  466. local count = math.min(#airs, math.random(3,6))
  467. for i = 1, count do
  468. local ap = airs[math.random(#airs)]
  469. minetest.set_node(ap, {name="fire:basic_flame"})
  470. end
  471. end
  472. })
  473. minetest.register_node("bitumen:mapgen_crude_oil", {
  474. description = "mapgen crude oil placeholder. you should not see this. if you do, wait 60 seconds.",
  475. tiles = { "default_copper_block.png" },
  476. groups = { cracky = 3 },
  477. })
  478. -- build a shell around crude oil blobs
  479. -- this keeps the oild from gushing into caves, both escaping and killing performance
  480. minetest.register_lbm({
  481. name = "bitumen:crude_shell_gen",
  482. nodenames = {"bitumen:mapgen_crude_oil"},
  483. run_at_every_load = true,
  484. action = function(pos, node)
  485. local airs = minetest.find_nodes_in_area(
  486. vector.add(pos, {x=-1, y=-1, z=-1}),
  487. vector.add(pos, {x=1, y=1, z=1}),
  488. {"air"}
  489. )
  490. for _,p in ipairs(airs) do
  491. minetest.set_node(p, {name="default:stone"})
  492. end
  493. minetest.set_node(pos, {name = "bitumen:crude_oil_full"})
  494. minetest.set_node_level(pos, 64)
  495. end
  496. })
  497. minetest.register_abm({
  498. nodenames = {"bitumen:mapgen_crude_oil"},
  499. interval = 60,
  500. chance = 1,
  501. action = function(pos, node)
  502. local airs = minetest.find_nodes_in_area(
  503. vector.add(pos, {x=-1, y=-1, z=-1}),
  504. vector.add(pos, {x=1, y=1, z=1}),
  505. {"air"}
  506. )
  507. for _,p in ipairs(airs) do
  508. minetest.set_node(p, {name="default:stone"})
  509. end
  510. minetest.set_node(pos, {name = "bitumen:crude_oil_full"})
  511. minetest.set_node_level(pos, 64)
  512. end
  513. })
  514. --temp hack for dev
  515. minetest.register_node("bitumen:crudesource", {
  516. description = "thing",
  517. tiles = { "default_copper_block.png" },
  518. groups = { cracky = 3 },
  519. })
  520. minetest.register_abm({
  521. nodenames = {"bitumen:crudesource"},
  522. interval = 1,
  523. chance = 1,
  524. action = function(pos, node, active_object_count, active_object_count_wider)
  525. pos.y = pos.y - 1
  526. minetest.set_node(pos, {name = "bitumen:crude_oil"})
  527. minetest.set_node_level(pos, 64)
  528. end
  529. })