init.lua 11 KB

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