init.lua 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433
  1. -- Note: this code only makes sense if the server is restarted on
  2. -- a regular basis, such as 12:00 AM every night. Use a cronjob!
  3. -- This code modifies the `default:snow` definition to create seasons.
  4. snow = {}
  5. -- Localize for performance.
  6. local math_floor = math.floor
  7. local math_random = math.random
  8. function snow.on_player_walk_over(pos, player)
  9. minetest.swap_node(pos, {name="snow:footprints", param2=math_random(0, 3)})
  10. end
  11. function snow.on_dig(pos, node, digger)
  12. if digger and digger:is_player() then
  13. minetest.remove_node(pos)
  14. core.check_for_falling(pos)
  15. local inv = digger:get_inventory()
  16. if inv then
  17. inv:add_item("main", "default:snow")
  18. end
  19. end
  20. end
  21. -- Enable default:snow to bypass protection (MustTest).
  22. -- However, I take pains to prevent griefing by e.g. replacing other nodes than air.
  23. -- This is called whenever snow is placed, even in protected areas.
  24. function snow.on_place(itemstack, placer, pt)
  25. if pt.above and pt.under then
  26. local node = minetest.get_node(pt.under)
  27. local udef = minetest.reg_ns_nodes[node.name]
  28. if udef and udef.on_rightclick and not (placer and placer:get_player_control().sneak) then
  29. return udef.on_rightclick(pt.under, node, placer, itemstack, pt) or itemstack
  30. end
  31. if minetest.get_node(pt.above).name == "air" then
  32. local nunder = minetest.get_node(pt.under).name
  33. -- Don't place snow if already placed.
  34. if nunder == itemstack:get_name() then
  35. return itemstack
  36. end
  37. -- Don't allow placement on non-walkable nodes. Prevents griefing of non-walkable things like grass, flowers.
  38. local def = minetest.reg_ns_nodes[nunder]
  39. if not def or not def.walkable then
  40. return itemstack
  41. end
  42. -- Don't allow placement on slabs or stairs. That just looks ugly.
  43. if minetest.get_item_group(nunder, "stair") > 0 or
  44. minetest.get_item_group(nunder, "slab") > 0 or
  45. minetest.get_item_group(nunder, "wall") > 0 or
  46. minetest.get_item_group(nunder, "rail") > 0 or
  47. minetest.get_item_group(nunder, "fence") > 0 then
  48. return itemstack
  49. end
  50. minetest.set_node(pt.above, {name=itemstack:get_name()})
  51. dirtspread.on_environment(pt.above) -- Explicit call.
  52. droplift.notify(pt.above)
  53. -- We cannot call minetest.place_node() because that will create an infinite loop.
  54. minetest.check_for_falling(pt.above)
  55. itemstack:take_item()
  56. return itemstack
  57. end
  58. end
  59. end
  60. -- Original snowdef.
  61. local origsnowdef = {
  62. description = "Snow\n\nThis will melt away in warm weather.\nIt comes back in cold weather.\nCan bypass protection.",
  63. tiles = {"default_snow.png"},
  64. inventory_image = "default_snowball.png",
  65. wield_image = "default_snowball.png",
  66. buildable_to = true,
  67. use_texture_alpha = true,
  68. crushing_damage = 0,
  69. -- These 2 cannot be changed dynamically without creating lighting issues.
  70. paramtype = "light",
  71. sunlight_propagates = true,
  72. floodable = true,
  73. walkable = true,
  74. drawtype = "nodebox",
  75. movement_speed_multiplier = default.SLOW_SPEED,
  76. _melts_to = "air",
  77. -- All snow types should become `default:snow`.
  78. -- These shouldn't ever be gotten directly by players.
  79. -- The exception is `snow:snowtest_4`, which spawns on trees.
  80. drop = "default:snow",
  81. -- Nodebox is set up manually.
  82. node_box = {
  83. type = "fixed",
  84. fixed = {
  85. {},
  86. },
  87. },
  88. groups = utility.dig_groups("snow", {
  89. falling_node = 1,
  90. puts_out_fire = 1,
  91. snow = 1,
  92. snowy = 1,
  93. cold = 1,
  94. melts = 1,
  95. -- Currently just used to notify thin_ice and torches.
  96. notify_construct = 1,
  97. }),
  98. -- Sound info.
  99. sounds = default.node_sound_snow_defaults(),
  100. on_construct = function(pos)
  101. end,
  102. on_timer = function(pos, elapsed)
  103. if rc.ice_melts_at_pos(pos) then
  104. minetest.remove_node(pos)
  105. end
  106. end,
  107. on_dig = function(...)
  108. return snow.on_dig(...)
  109. end,
  110. on_place = function(...)
  111. return snow.on_place(...)
  112. end,
  113. on_player_walk_over = function(...)
  114. return snow.on_player_walk_over(...)
  115. end,
  116. }
  117. -- Every snow height must be different (except for the 0's and 16's).
  118. -- This is because footprints-in-snow rely on this table, too.
  119. local snowheight = {
  120. 0,
  121. 0,
  122. 0,
  123. 0,
  124. 2,
  125. 3,
  126. 5,
  127. 7,
  128. 8,
  129. 9,
  130. 10,
  131. 11,
  132. 12,
  133. 13,
  134. 14,
  135. 16, -- Footprints won't make indentation. We can assume snow has hard crust.
  136. 16,
  137. }
  138. -- Create 17 snowdef tables.
  139. -- Snowdef #1 is 'invisible', & snowdef #17 is full-block size.
  140. snow.snowdef = {}
  141. -- Must execute exactly 17 times, the code relies on this!
  142. for i = 1, 17, 1 do
  143. snow.snowdef[i] = table.copy(origsnowdef)
  144. local fixed = {
  145. {0, 0, 0, 16, snowheight[i], 16},
  146. }
  147. -- Flatten the first few. This is because they are transparent,
  148. -- and their edges would otherwise create GFX artifacts.
  149. if i <= 4 then
  150. fixed[1][5] = 0.1
  151. -- Hide side tiles.
  152. for j = 2, 6, 1 do
  153. snow.snowdef[i].tiles[j] = "invisible.png"
  154. end
  155. snow.snowdef[i].drop = ""
  156. end
  157. utility.transform_nodebox(fixed)
  158. snow.snowdef[i].node_box.fixed = fixed
  159. local str = snow.snowdef[i].tiles[1]
  160. if i == 3 then
  161. snow.snowdef[i].tiles[1] = str .. "^[opacity:30"
  162. elseif i == 4 then
  163. snow.snowdef[i].tiles[1] = str .. "^[opacity:130"
  164. else
  165. snow.snowdef[i].use_texture_alpha = nil
  166. end
  167. if i <= 2 then
  168. -- If `i<=2` then node is invisible and should be as inert as possible.
  169. snow.snowdef[i].drawtype = "airlike"
  170. snow.snowdef[i].pointable = false
  171. snow.snowdef[i].node_box = nil
  172. snow.snowdef[i].groups.puts_out_fire = nil
  173. snow.snowdef[i].groups.snow = nil
  174. snow.snowdef[i].groups.snowy = nil
  175. snow.snowdef[i].groups.melts = nil
  176. snow.snowdef[i].groups.dig_immediate = 2
  177. snow.snowdef[i].no_sound_on_fall = true
  178. snow.snowdef[i].sounds = nil
  179. end
  180. if i == 3 then
  181. snow.snowdef[i].pointable = false
  182. snow.snowdef[i].no_sound_on_fall = true
  183. snow.snowdef[i].groups.dig_immediate = 2
  184. end
  185. if i == 4 then
  186. snow.snowdef[i].groups.dig_immediate = 2
  187. end
  188. if i <= 5 then
  189. snow.snowdef[i].movement_speed_multiplier = default.NORM_SPEED
  190. end
  191. if i >= 6 and i <= 10 then
  192. snow.snowdef[i].movement_speed_multiplier = default.SLOW_SPEED_NETHER
  193. end
  194. -- Deeper snow will default to `default.SLOW_SPEED`.
  195. if i <= 4 then
  196. snow.snowdef[i].walkable = false
  197. end
  198. -- The last 3 nodes are full-blocks.
  199. if i >= 15 then
  200. snow.snowdef[i].drawtype = "normal"
  201. snow.snowdef[i].node_box = nil
  202. end
  203. -- Everything else except `i==1` is nodebox.
  204. -- `i==1` should be invisible and mostly inert.
  205. -- These shouldn't ever be gotten directly by players.
  206. minetest.register_node("snow:snowtest_" .. i, snow.snowdef[i])
  207. end
  208. -- Create reverse sequence from the above 17 nodedefs.
  209. do
  210. local which = 17
  211. for i = 18, 17*2, 1 do
  212. assert(which >= 1)
  213. assert(which <= 17)
  214. snow.snowdef[i] = snow.snowdef[which]
  215. which = which - 1
  216. end
  217. end
  218. -- Choose snow level!
  219. snow.snowlevel = 1
  220. function snow.choose_level()
  221. local cnt = 17*2 -- 34-day cycle.
  222. local off = 1 -- Need offset otherwise counting starts from 0.
  223. local epoch = os.time({year=2016, month=10, day=1})
  224. local secs = os.time()-epoch
  225. local day = (((secs/60)/60)/24)
  226. local which = math_floor(day%cnt)+off
  227. assert(which >= 1)
  228. assert(which <= 17*2)
  229. return which
  230. --return 5
  231. end
  232. snow.snowlevel = snow.choose_level()
  233. function snow.get_day()
  234. local day = snow.choose_level()
  235. local season = "Season of White"
  236. if day <= 15 or day >= 20 then
  237. season = "Season of Drifts"
  238. end
  239. if day <= 7 or day >= 28 then
  240. season = "Season of Slush"
  241. end
  242. if day <= 3 or day >= 32 then
  243. season = "Season of Stone"
  244. end
  245. return day .. "/34 (" .. season .. ")"
  246. end
  247. function snow.get_snowdef()
  248. if snow.snowdef[snow.snowlevel] then
  249. return snow.snowdef[snow.snowlevel]
  250. end
  251. return origsnowdef
  252. end
  253. function snow.get_snowfootdef()
  254. -- The footprints snowdef is a copy of whatever the current snowdef
  255. -- is, with some minor modifications.
  256. local def = table.copy(origsnowdef)
  257. -- Reduce snow-height for footprints.
  258. local thislevel = snow.snowlevel
  259. if thislevel >= 3 and thislevel <= 17 then
  260. thislevel = thislevel - 1
  261. elseif thislevel <= 32 and thislevel >= 18 then
  262. thislevel = thislevel + 1
  263. end
  264. if snow.snowdef[thislevel] then
  265. def = table.copy(snow.snowdef[thislevel])
  266. end
  267. def.paramtype2 = "facedir"
  268. def.on_rotate = false -- It would be silly if screwdriver could rotate this.
  269. def.dumpnodes_tile = {"default_snow.png"}
  270. def.tiles = {
  271. "(default_snow.png^snow_footstep.png)",
  272. "default_snow.png",
  273. }
  274. do
  275. local i = thislevel
  276. local str1 = def.tiles[1]
  277. local str2 = def.tiles[2]
  278. if i == 2 or i == 33 then
  279. def.tiles[1] = str1 .. "^[opacity:30"
  280. def.tiles[2] = str1 .. "^[opacity:30"
  281. elseif i == 3 or i == 32 then
  282. def.tiles[1] = str1 .. "^[opacity:130"
  283. def.tiles[2] = str1 .. "^[opacity:130"
  284. else
  285. def.use_texture_alpha = nil
  286. end
  287. end
  288. -- The regular snow already did this.
  289. def.groups.notify_construct = nil
  290. -- We do need the 'on_dig' function.
  291. -- Cannot nil this out.
  292. --def.on_dig = nil
  293. -- It should never be possible to place 'snow with footprints'
  294. -- so this is unnecessary.
  295. def.on_place = nil
  296. def.on_construct = function(pos)
  297. if rc.ice_melts_at_pos(pos) then
  298. minetest.remove_node(pos)
  299. return
  300. end
  301. local time = 60*60*24*7
  302. local rand = math_random(60*60*1, 60*60*24)
  303. minetest.get_node_timer(pos):start(time+rand)
  304. end
  305. def.on_timer = function(pos, elapsed)
  306. minetest.set_node(pos, {name="default:snow"})
  307. end
  308. def.on_player_walk_over = function(pos, player)
  309. local time = 60*60*24*7
  310. local rand = math_random(60*60*1, 60*60*24)
  311. minetest.get_node_timer(pos):start(time+rand)
  312. end
  313. -- Snow with footprints turns back to snow if it falls.
  314. def.on_finish_collapse = function(pos, node)
  315. minetest.swap_node(pos, {name="default:snow"})
  316. end
  317. def.on_collapse_to_entity = function(pos, node)
  318. core.add_item(pos, {name="default:snow"})
  319. end
  320. return def
  321. end
  322. function snow.should_spawn_icemen()
  323. if snow.snowlevel >= 7 and snow.snowlevel <= 28 then
  324. return true
  325. end
  326. return false
  327. end
  328. -- API function to determine whether snow is at all visible.
  329. -- Shall return false when snow is completely transparent (implying that it should be pretending like it is not there).
  330. function snow.is_visible()
  331. if snow.snowlevel >= 3 and snow.snowlevel <= 32 then
  332. return true
  333. end
  334. return false
  335. end
  336. function snow.get_treedef()
  337. local def = table.copy(origsnowdef)
  338. local thislevel = 5
  339. if snow.snowdef[thislevel] then
  340. def = table.copy(snow.snowdef[thislevel])
  341. end
  342. def.description = "Tree Snow"
  343. def.groups.dig_immediate = 2
  344. def.drop = ""
  345. def.on_dig = nil
  346. def.on_place = nil
  347. def.on_player_walk_over = nil
  348. -- Player should not be able to obtain node.
  349. def.on_finish_collapse = function(pos, node)
  350. if math_random(1, 3) == 1 then
  351. minetest.remove_node(pos)
  352. end
  353. end
  354. def.on_collapse_to_entity = function(pos, node)
  355. -- Do nothing.
  356. end
  357. return def
  358. end
  359. -- The snow definition changes dynamically based on date.
  360. minetest.register_node(":default:snow", snow.get_snowdef())
  361. minetest.register_node("snow:footprints", snow.get_snowfootdef())
  362. minetest.register_node("snow:tree", snow.get_treedef())
  363. -- Should return `true' if name is snow or any variant thereof,
  364. -- (footsteps, treesnow) NOT including default:snowblock.
  365. function snow.is_snow(name)
  366. if name == "default:snow" or
  367. name == "snow:tree" or
  368. name == "snow:footprints" then
  369. return true
  370. end
  371. end
  372. -- Should match the names in the above function.
  373. function snow.get_names()
  374. return {"default:snow", "snow:tree", "snow:footprints"}
  375. end