init.lua 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630
  1. seasons = {}
  2. function deepclone(t)
  3. if type(t) ~= "table" then
  4. return t
  5. end
  6. -- local meta = getmetatable(t)
  7. local target = {}
  8. for k, v in pairs(t) do
  9. if type(v) == "table" then
  10. target[k] = deepclone(v)
  11. else
  12. target[k] = v
  13. end
  14. end
  15. --setmetatable(target, meta)
  16. return target
  17. end
  18. local SEASONS_YEARLEN = 60 * 60 * 2
  19. local between = function(v, a, b)
  20. if v >= a and v < b then
  21. return true
  22. end
  23. return false
  24. end
  25. local abm_list = {}
  26. local core_lookup = {}
  27. local changes_lookup = {
  28. spring = {},
  29. summer = {},
  30. fall = {},
  31. winter = {},
  32. }
  33. local function splitname(name)
  34. local c = string.find(name, ":", 1)
  35. return string.sub(name, 1, c - 1), string.sub(name, c + 1, string.len(name))
  36. end
  37. function reg_changes(ssn, oldmod, oldname)
  38. local old = oldmod..":"..oldname
  39. local new = "seasons:"..ssn.."_"..oldmod.."_"..oldname
  40. core_lookup[old] = old
  41. if ssn == "summer" then -- minetest is in "summer" by default
  42. changes_lookup[ssn][old] = old
  43. table.insert(abm_list, old)
  44. else
  45. core_lookup[new] = old
  46. changes_lookup[ssn][old] = new
  47. table.insert(abm_list, new)
  48. end
  49. end
  50. function reg_custom(ssn, old, new)
  51. core_lookup[old] = old
  52. if ssn == "summer" then -- minetest is in "summer" by default
  53. changes_lookup[ssn][old] = old
  54. table.insert(abm_list, old)
  55. else
  56. print(dump(core_lookup))
  57. core_lookup[new] = old
  58. changes_lookup[ssn][old] = new
  59. table.insert(abm_list, new)
  60. end
  61. end
  62. seasons.reg_custom = reg_custom
  63. function reg_generic(oldmod, oldname, tiles, drops, default_season)
  64. local old = oldmod..":"..oldname
  65. local ds = default_season or "summer"
  66. local function reg(ssn)
  67. local new
  68. if ssn == ds then -- minetest is in "summer" by default
  69. new = old
  70. else
  71. new = "seasons:"..ssn.."_"..oldmod.."_"..oldname
  72. end
  73. if ssn ~= ds then
  74. local def = deepclone(minetest.registered_nodes[old])
  75. def.groups.not_in_creative_inventory = 1
  76. if tiles and tiles[ssn] then
  77. def.tiles = tiles[ssn]
  78. else
  79. def.tiles = {"seasons_"..ssn.."_"..oldmod.."_"..oldname..".png"}
  80. end
  81. if drops and drops[ssn] then
  82. def.drop = drops[ssn]
  83. end
  84. if oldname == "dirt_with_grass" then
  85. print(dump(def))
  86. end
  87. minetest.register_node(new, def)
  88. end
  89. -- print("new: "..new.." old: "..old)
  90. core_lookup[new] = old
  91. changes_lookup[ssn][old] = new
  92. table.insert(abm_list, new)
  93. end
  94. reg("spring")
  95. reg("summer")
  96. reg("fall")
  97. reg("winter")
  98. end
  99. reg_generic("default", "dirt_with_grass", {
  100. spring = {"seasons_spring_default_grass.png", "default_dirt.png", {name = "default_dirt.png^seasons_spring_default_grass_side.png", tileable_vertical = false}},
  101. fall = {"seasons_fall_default_grass.png", "default_dirt.png", {name = "default_dirt.png^seasons_fall_default_grass_side.png", tileable_vertical = false}},
  102. winter = {"seasons_winter_default_grass.png", "default_dirt.png", {name = "default_dirt.png^seasons_winter_default_grass_side.png", tileable_vertical = false}},
  103. },
  104. nil)
  105. for i = 1, 5 do
  106. reg_generic("default", "grass_"..i, nil, nil)
  107. end
  108. reg_generic("default", "bush_leaves", {
  109. spring = {"seasons_spring_default_leaves_simple.png"},
  110. fall = {"seasons_fall_default_leaves_simple.png"},
  111. winter = {"seasons_winter_default_leaves_simple.png"},
  112. },
  113. nil)
  114. reg_generic("default", "jungleleaves", nil, nil)
  115. default.register_leafdecay({
  116. trunks = {"default:jungletree"},
  117. leaves = {
  118. "default:jungleleaves",
  119. "seasons:winter_default_jungleleaves",
  120. "seasons:fall_default_jungleleaves",
  121. "seasons:spring_default_jungleleaves",
  122. },
  123. radius = 3,
  124. })
  125. reg_generic("default", "acacia_leaves", nil, nil)
  126. default.register_leafdecay({
  127. trunks = {"default:acacia_tree"},
  128. leaves = {
  129. "default:acacia_leaves",
  130. "seasons:winter_default_acacia_leaves",
  131. "seasons:fall_default_acacia_leaves",
  132. "seasons:spring_default_acacia_leaves",
  133. },
  134. radius = 2,
  135. })
  136. reg_generic("default", "aspen_leaves", nil, nil)
  137. --[[
  138. saplings
  139. dirt_with_footsteps ?
  140. jungle trees
  141. acacia trees
  142. apple
  143. bushes
  144. flowers
  145. ferns
  146. cattails
  147. pine leaves turning red in summer/fall
  148. falling leaf particles
  149. ]]
  150. --reg_generic("default", "dirt_with_grass", {}, nil)
  151. function reg_leaves(ssn)
  152. reg_changes(ssn, "default", "leaves")
  153. minetest.register_node("seasons:"..ssn.."_default_leaves", {
  154. description = "Apple Tree Leaves",
  155. drawtype = "allfaces_optional",
  156. waving = 1,
  157. tiles = {"seasons_"..ssn.."_leaves.png"},
  158. special_tiles = {"default_leaves_simple.png"},
  159. paramtype = "light",
  160. stack_max = 30,
  161. is_ground_content = false,
  162. groups = {snappy = 3, leafdecay = 3, flammable = 2, leaves = 1},
  163. drop = {
  164. max_items = 1,
  165. items = {
  166. {
  167. -- player will get sapling with 1/20 chance
  168. items = {'default:sapling'},
  169. rarity = 20,
  170. },
  171. {
  172. -- player will get leaves only if he get no saplings,
  173. -- this is because max_items is 1
  174. items = {'seasons:'..ssn..'_default_leaves'},
  175. }
  176. }
  177. },
  178. sounds = default.node_sound_leaves_defaults(),
  179. after_place_node = default.after_place_leaves,
  180. })
  181. -- default.register_leafdecay({
  182. -- trunks = {"default:tree"},
  183. -- leaves = {"seasons:"..ssn.."_default_leaves"},
  184. -- radius = 2,
  185. -- })
  186. end
  187. reg_leaves("spring")
  188. reg_leaves("fall")
  189. reg_leaves("winter")
  190. reg_changes("summer", "default", "leaves")
  191. function reg_aspen_leaves(ssn)
  192. reg_changes(ssn, "default", "aspen_leaves")
  193. minetest.register_node("seasons:"..ssn.."_default_aspen_leaves", {
  194. description = "Aspen Tree Leaves",
  195. drawtype = "allfaces_optional",
  196. tiles = {"seasons_"..ssn.."_aspen_leaves.png"},
  197. waving = 1,
  198. paramtype = "light",
  199. stack_max = 30,
  200. is_ground_content = false,
  201. groups = {snappy = 3, leafdecay = 3, flammable = 2, leaves = 1},
  202. drop = {
  203. max_items = 1,
  204. items = {
  205. {items = {"default:aspen_sapling"}, rarity = 20},
  206. {items = {"seasons:"..ssn.."_default_aspen_leaves"}}
  207. }
  208. },
  209. sounds = default.node_sound_leaves_defaults(),
  210. after_place_node = default.after_place_leaves,
  211. })
  212. -- default.register_leafdecay({
  213. -- trunks = {"default:aspen_tree"},
  214. -- leaves = {"seasons:"..ssn.."_default_aspen_leaves"},
  215. -- radius = 3,
  216. -- })
  217. end
  218. reg_aspen_leaves("spring")
  219. reg_aspen_leaves("fall")
  220. reg_aspen_leaves("winter")
  221. reg_changes("summer", "default", "aspen_leaves")
  222. -- flowers bloom in spring, not summer
  223. reg_generic("flowers", "rose", nil,
  224. { -- drops
  225. spring = {"flowers:rose"},
  226. summer = {}, -- nothin
  227. fall = {}, -- TODO: rosehip
  228. winter = {}, -- TODO: rose bud
  229. },
  230. "spring")
  231. reg_generic("flowers", "tulip",
  232. {
  233. summer = {"seasons_summer_flowers_tulip.png"},
  234. fall = {"seasons_fall_flowers_tulip.png"},
  235. winter = {"seasons_winter_flowers_tulip.png"}
  236. },
  237. { -- drops
  238. --spring = {"flowers:tulip"},
  239. summer = {}, -- nothin
  240. fall = {}, -- nothin
  241. winter = {}, -- TODO: bulb
  242. },
  243. "spring")
  244. --[[
  245. reg_generic("flowers", "tulip_black",
  246. {
  247. summer = {"seasons_summer_flowers_tulip.png"},
  248. fall = {"seasons_fall_flowers_tulip.png"},
  249. winter = {"seasons_winter_flowers_tulip.png"}
  250. },
  251. { -- drops
  252. --spring = {"flowers:tulip_black"},
  253. summer = {}, -- nothin
  254. fall = {}, -- nothin
  255. winter = {}, -- TODO: bulb
  256. },
  257. "spring")
  258. ]]
  259. local def
  260. -- dandelions are done manually because the default ones represent two seasons
  261. -- fall
  262. def = deepclone(minetest.registered_nodes["flowers:dandelion_yellow"])
  263. def.groups.not_in_creative_inventory = 1
  264. def.tiles = {"seasons_fall_flowers_dandelion.png"}
  265. def.drops = {}
  266. minetest.register_node("seasons:fall_flowers_dandelion", def)
  267. -- winter
  268. def = deepclone(minetest.registered_nodes["flowers:dandelion_yellow"])
  269. def.groups.not_in_creative_inventory = 1
  270. def.tiles = {"seasons_winter_flowers_dandelion.png"}
  271. def.drops = {}
  272. minetest.register_node("seasons:winter_flowers_dandelion", def)
  273. -- lookups
  274. core_lookup["seasons:winter_flowers_dandelion"] = "flowers:dandelion_yellow"
  275. core_lookup["seasons:fall_flowers_dandelion"] = "flowers:dandelion_yellow"
  276. core_lookup["flowers:dandelion_yellow"] = "flowers:dandelion_yellow"
  277. core_lookup["flowers:dandelion_white"] = "flowers:dandelion_yellow" -- this is correct
  278. changes_lookup["fall"]["flowers:dandelion_yellow"] = "seasons:fall_flowers_dandelion"
  279. changes_lookup["winter"]["flowers:dandelion_yellow"] = "seasons:winter_flowers_dandelion"
  280. changes_lookup["spring"]["flowers:dandelion_yellow"] = "flowers:dandelion_yellow"
  281. changes_lookup["summer"]["flowers:dandelion_yellow"] = "flowers:dandelion_white"
  282. table.insert(abm_list, "seasons:fall_flowers_dandelion")
  283. table.insert(abm_list, "seasons:winter_flowers_dandelion")
  284. table.insert(abm_list, "flowers:dandelion_yellow")
  285. table.insert(abm_list, "flowers:dandelion_white")
  286. local get_season_data = function()
  287. local t = minetest.get_gametime()
  288. local s = ((t * math.pi * 2) / (SEASONS_YEARLEN)) % (math.pi * 2)
  289. local snorm = (math.sin(s) + 1) * 0.5
  290. local cnorm = (math.cos(s) + 1) * 0.5
  291. local sign = 1
  292. if cnorm < .5 then
  293. sign = -1
  294. end
  295. local season
  296. if between(s, 0, .2) then
  297. season = "winter"
  298. elseif between(s, .8, 1.0) then
  299. season = "summer"
  300. else
  301. if sign > 0 then
  302. season = "spring"
  303. else
  304. season = "fall"
  305. end
  306. end
  307. return season, snorm, sign
  308. end
  309. local get_season = function()
  310. local season, time
  311. local t = minetest.get_gametime()
  312. local s = (t % SEASONS_YEARLEN) / SEASONS_YEARLEN
  313. if between(s, 0, .2) then
  314. season = "spring"
  315. time = (s - .0) / .2
  316. elseif between(s, .2, .5) then
  317. season = "summer"
  318. time = (s - .2) / .3
  319. elseif between(s, .5, .7) then
  320. season = "fall"
  321. time = (s - .5) / .2
  322. elseif between(s, .7, 1.0) then
  323. season = "winter"
  324. time = (s - .7) / .3
  325. end
  326. return season, time, s
  327. end
  328. seasons.get_season = get_season
  329. minetest.register_abm({
  330. label = "Leaf Change",
  331. nodenames = abm_list,
  332. interval = 5,
  333. chance = 180,
  334. catch_up = true,
  335. action = function(pos, node)
  336. local s, progress = get_season()
  337. --local name = changes[s][node.name]
  338. local core = core_lookup[node.name]
  339. local name = changes_lookup[s][core]
  340. if name == nil or name == node.name then return end
  341. minetest.set_node(pos, {name = name})
  342. end,
  343. })
  344. -- water freezing
  345. def = deepclone(minetest.registered_nodes["default:ice"])
  346. def.groups.not_in_creative_inventory = 1
  347. def.drops = {"default:ice"}
  348. minetest.register_node("seasons:ice_water_source", def)
  349. def = deepclone(minetest.registered_nodes["default:ice"])
  350. def.groups.not_in_creative_inventory = 1
  351. def.drops = {}
  352. minetest.register_node("seasons:ice_water_flowing", def)
  353. def = deepclone(minetest.registered_nodes["default:ice"])
  354. def.groups.not_in_creative_inventory = 1
  355. def.drops = {"default:ice"} -- TODO: riverwater ice
  356. minetest.register_node("seasons:ice_river_water_source", def)
  357. def = deepclone(minetest.registered_nodes["default:ice"])
  358. def.groups.not_in_creative_inventory = 1
  359. def.drops = {}
  360. minetest.register_node("seasons:ice_river_water_flowing", def)
  361. local ice_lookup = {
  362. ["default:water_source"] = "seasons:ice_water_source",
  363. ["default:water_flowing"] = "seasons:ice_water_flowing",
  364. ["default:river_water_source"] = "seasons:ice_river_water_source",
  365. ["default:river_water_flowing"] = "seasons:ice_river_water_flowing",
  366. }
  367. local water_lookup = {
  368. ["seasons:ice_water_source"] = "default:water_source",
  369. ["seasons:ice_water_flowing"] = "default:water_flowing",
  370. ["seasons:ice_river_water_source"] = "default:river_water_source",
  371. ["seasons:ice_river_water_flowing"] = "default:river_water_flowing",
  372. }
  373. minetest.register_abm({
  374. label = "Water Freeze",
  375. nodenames = {
  376. "default:water_source",
  377. "default:water_flowing",
  378. "default:river_water_source",
  379. "default:river_water_flowing",
  380. },
  381. neighbors = "air",
  382. interval = 5,
  383. chance = 180,
  384. catch_up = true,
  385. action = function(pos, node)
  386. local s, progress = get_season()
  387. local name
  388. if s ~= "winter" then
  389. return
  390. end
  391. minetest.set_node(pos, {name = ice_lookup[node.name]})
  392. end,
  393. })
  394. minetest.register_abm({
  395. label = "Water Thaw",
  396. nodenames = {
  397. "seasons:ice_water_source",
  398. "seasons:ice_water_flowing",
  399. "seasons:ice_river_water_source",
  400. "seasons:ice_river_water_flowing",
  401. },
  402. interval = 5,
  403. chance = 180,
  404. catch_up = true,
  405. action = function(pos, node)
  406. local s, progress = get_season()
  407. local name
  408. if s == "winter" then
  409. return
  410. end
  411. minetest.set_node(pos, {name = water_lookup[node.name]})
  412. end,
  413. })
  414. local last_season = {
  415. spring = "winter",
  416. summer = "spring",
  417. fall = "summer",
  418. winter = "fall",
  419. }
  420. minetest.register_lbm({
  421. name = "seasons:catchup",
  422. nodenames = abm_list,
  423. run_at_every_load = true,
  424. action = function(pos, node)
  425. local s, progress = get_season()
  426. if math.random() > (progress * 1.2) then
  427. -- use last season's node
  428. s = last_season[s]
  429. end
  430. --local name = changes[s][node.name]
  431. local core = core_lookup[node.name]
  432. local name = changes_lookup[s][core]
  433. if name == nil or name == node.name then return end
  434. minetest.set_node(pos, {name = name})
  435. end,
  436. })
  437. if minetest.global_exists("storms") then
  438. storms.register_heat_bias(function(pos, orig)
  439. local season, stime, spin = get_season()
  440. spin = math.sin(spin * 2 * math.pi) * 20
  441. -- print("heat bias: ".. spin)
  442. return spin
  443. end)
  444. --[[
  445. storms.register_freq_bias(function(pos, orig)
  446. local season, stime, spin = get_season()
  447. spin = math.sin(spin * 2 * math.pi)
  448. print("freq bias: ".. spin)
  449. return spin
  450. end)
  451. ]]
  452. end
  453. default.register_leafdecay({
  454. trunks = {"default:tree"},
  455. leaves = {
  456. "default:apple",
  457. "default:leaves",
  458. "seasons:winter_default_leaves",
  459. "seasons:fall_default_leaves",
  460. "seasons:spring_default_leaves",
  461. },
  462. radius = 2,
  463. })
  464. default.register_leafdecay({
  465. trunks = {"default:aspen_tree"},
  466. leaves = {
  467. "default:aspen_leaves",
  468. "seasons:winter_default_acacia_leaves",
  469. "seasons:fall_default_acacia_leaves",
  470. "seasons:spring_default_acacia_leaves",
  471. },
  472. radius = 3,
  473. })
  474. default.register_leafdecay({
  475. trunks = {"default:bush_stem"},
  476. leaves = {
  477. "default:bush_leaves",
  478. "seasons:winter_default_bush_leaves",
  479. "seasons:fall_default_bush_leaves",
  480. "seasons:spring_default_bush_leaves",
  481. },
  482. radius = 1,
  483. })