init.lua 12 KB

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