init.lua 13 KB

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