init.lua 11 KB

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