tvine.lua 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644
  1. -- Twisted Vine Mod
  2. -- Idea and textures by alauer/Dresdan
  3. -- Code by MustTest
  4. tvine = tvine or {}
  5. tvine.modpath = minetest.get_modpath("default")
  6. tvine.steptime = {min=60*5, max=60*20}
  7. --tvine.steptime = {min=1, max=1}
  8. tvine.maxheight = 16
  9. tvine.minlight = 10
  10. tvine.maxlight = 15
  11. tvine.light_source = 9
  12. -- Localize for performance.
  13. local math_floor = math.floor
  14. local math_random = math.random
  15. function tvine.is_plant_name(name)
  16. if name == "default:tvine" then
  17. return true
  18. elseif name == "default:tvine_alt" then
  19. return true
  20. elseif name == "default:tvine_top" then
  21. return true
  22. elseif name == "default:tvine_top_alt" then
  23. return true
  24. end
  25. return false
  26. end
  27. function tvine.is_stalk_name(name)
  28. if name == "default:tvine" then
  29. return true
  30. elseif name == "default:tvine_alt" then
  31. return true
  32. end
  33. return false
  34. end
  35. function tvine.is_top_name(name)
  36. if name == "default:tvine_top" then
  37. return true
  38. elseif name == "default:tvine_top_alt" then
  39. return true
  40. end
  41. return false
  42. end
  43. -- Should return a random height for an individual plant to grow.
  44. function tvine.random_height()
  45. local m = tvine.maxheight
  46. local h = math_floor(m / 2)
  47. local x = 2
  48. if h < x then h = x end
  49. if m < h then m = h end
  50. return math_floor(math_random(math_random(x, h), math_random(h, m)))
  51. end
  52. function tvine.is_dirt_name(name)
  53. if name == "default:dirt_with_grass" or
  54. name == "default:dirt" or
  55. name == "moregrass:darkgrass" or
  56. name == "talinite:ore" or
  57. name == "talinite:desert_ore" or
  58. minetest.get_item_group(name, "soil") > 1 then
  59. return true
  60. end
  61. end
  62. function tvine.has_dirt(pos)
  63. local p = vector.add(pos, {x=0, y=-1, z=0})
  64. local name = minetest.get_node(p).name
  65. -- Must be on dirt or grass.
  66. if tvine.is_dirt_name(name) then
  67. return true
  68. end
  69. end
  70. function tvine.can_grow(pos)
  71. -- Must have dirt nearby.
  72. if not tvine.has_dirt(pos) then
  73. return
  74. end
  75. -- Must have water nearby.
  76. local p = vector.add(pos, {x=0, y=-1, z=0})
  77. if not minetest.find_node_near(p, 3, {"group:water"}) then
  78. return
  79. end
  80. -- Also needs minerals.
  81. if not minetest.find_node_near(p, 3, {"glowstone:minerals"}) then
  82. return
  83. end
  84. return true
  85. end
  86. -- Obtain growth height from soil, initializing it if not done yet.
  87. function tvine.get_grow_height(pos)
  88. local meta = minetest.get_meta({x=pos.x, y=pos.y-1, z=pos.z})
  89. local maxh = meta:get_int("tvine_height")
  90. if maxh == 0 then
  91. maxh = tvine.random_height()
  92. meta:set_int("tvine_height", maxh)
  93. meta:mark_as_private("tvine_height")
  94. end
  95. return maxh
  96. end
  97. -- Should be called when plant is dug.
  98. function tvine.reset_grow_height_and_timer(pos)
  99. -- Find soil node below plant.
  100. local p = vector.new(pos)
  101. local name = minetest.get_node(p).name
  102. local d = 0
  103. while not tvine.is_dirt_name(name) and d < tvine.maxheight do
  104. -- All except bottom-most node must be plant.
  105. if not tvine.is_plant_name(name) then
  106. return
  107. end
  108. p.y = p.y - 1
  109. d = d + 1
  110. name = minetest.get_node(p).name
  111. end
  112. -- Must be on dirt or grass.
  113. if tvine.is_dirt_name(name) then
  114. local meta = minetest.get_meta(p)
  115. local maxh = tvine.random_height()
  116. meta:set_int("tvine_height", maxh)
  117. meta:mark_as_private("tvine_height")
  118. else
  119. return
  120. end
  121. -- Restart timer for plant directly above soil.
  122. p.y = p.y + 1
  123. if not tvine.is_plant_name(minetest.get_node(p).name) then
  124. return
  125. end
  126. local min = tvine.steptime.min
  127. local max = tvine.steptime.max
  128. minetest.get_node_timer(p):start(math_random(min, max))
  129. end
  130. -- Attempt to grow tvine.
  131. -- Return 0 means nothing to report.
  132. -- 10 means plant has reached max height.
  133. function tvine.grow(pos, node)
  134. -- Check if we can grow.
  135. if not tvine.can_grow(pos) then
  136. return 0
  137. end
  138. if minetest.find_node_near(pos, 2, "group:cold") then
  139. return 12
  140. end
  141. -- Get how high we can grow.
  142. local maxh = tvine.get_grow_height(pos)
  143. -- Find current height of plant.
  144. local height = 0
  145. while tvine.is_plant_name(node.name) and height < maxh do
  146. height = height + 1
  147. pos.y = pos.y + 1
  148. node = minetest.get_node(pos)
  149. end
  150. if height >= maxh then
  151. -- Plant has reached max height.
  152. return 10
  153. end
  154. -- Check if we have room to grow some more.
  155. if not (node.name == "air") then
  156. return 0
  157. end
  158. -- Check if we have enough light.
  159. if minetest.get_node_light(pos) < tvine.minlight then
  160. return 0
  161. end
  162. -- Grow!
  163. local p2 = {x=pos.x, y=pos.y-1, z=pos.z}
  164. local n2 = minetest.get_node(p2)
  165. if tvine.is_plant_name(n2.name) then
  166. minetest.swap_node(p2, {name = tvine.chose_bot_name(p2), param2=n2.param2})
  167. end
  168. minetest.swap_node(pos, {name = tvine.chose_top_name(pos), param2=n2.param2})
  169. return 0
  170. end
  171. function tvine.chose_name(pos)
  172. local p = {x=pos.x, y=pos.y-1, z=pos.z}
  173. local n = minetest.get_node(p).name
  174. if n == "default:tvine" then
  175. return "default:tvine_alt"
  176. elseif n == "default:tvine_alt" then
  177. return "default:tvine"
  178. end
  179. return "default:tvine"
  180. end
  181. function tvine.chose_top_name(pos)
  182. local p = {x=pos.x, y=pos.y-1, z=pos.z}
  183. local n = minetest.get_node(p).name
  184. if n == "default:tvine" then
  185. return "default:tvine_top_alt"
  186. elseif n == "default:tvine_alt" then
  187. return "default:tvine_top"
  188. end
  189. return "default:tvine"
  190. end
  191. function tvine.chose_bot_name(pos)
  192. local p = {x=pos.x, y=pos.y-1, z=pos.z}
  193. local n = minetest.get_node(p).name
  194. if n == "default:tvine" then
  195. return "default:tvine_alt"
  196. elseif n == "default:tvine_alt" then
  197. return "default:tvine"
  198. elseif n == "default:tvine_top" then
  199. return "default:tvine_alt"
  200. elseif n == "default:tvine_top_alt" then
  201. return "default:tvine"
  202. end
  203. if math_random(1, 2) == 1 then
  204. return "default:tvine"
  205. end
  206. return "default:tvine_alt"
  207. end
  208. function tvine.on_construct(pos)
  209. local p = {x=pos.x, y=pos.y-1, z=pos.z}
  210. local n = minetest.get_node(p)
  211. if not tvine.is_plant_name(n.name) then
  212. if math_random(1, 2) == 1 then
  213. minetest.swap_node(pos, {name="default:tvine_top", param2=math_random(0, 239)})
  214. else
  215. minetest.swap_node(pos, {name="default:tvine_top_alt", param2=math_random(0, 239)})
  216. end
  217. else
  218. local n2 = minetest.get_node(pos)
  219. n2.param2 = n.param2
  220. minetest.swap_node(pos, n2)
  221. end
  222. end
  223. function tvine.on_seed_timer(pos, elapsed)
  224. if math_random(1, 2) == 1 then
  225. minetest.set_node(pos, {name = "default:tvine_top"})
  226. else
  227. minetest.set_node(pos, {name = "default:tvine_top_alt"})
  228. end
  229. -- Only the ground-level plant piece should have nodetimer.
  230. -- If plant is not placed on soil, it will never have nodetimer.
  231. if tvine.has_dirt(pos) then
  232. local min = tvine.steptime.min
  233. local max = tvine.steptime.max
  234. minetest.get_node_timer(pos):start(math_random(min, max))
  235. end
  236. end
  237. function tvine.on_destruct(pos)
  238. tvine.reset_grow_height_and_timer(pos)
  239. end
  240. function tvine.on_timer(pos, elapsed)
  241. --minetest.chat_send_all("# Server: Plant timer @ " .. minetest.pos_to_string(pos) .. "!")
  242. local p = vector.new(pos)
  243. local node = minetest.get_node(pos)
  244. local result = tvine.grow(pos, node)
  245. -- Plant has reached max height.
  246. if result == 10 then return end
  247. -- Plant cannot grow because of ice.
  248. if result == 12 then return end
  249. local min = tvine.steptime.min
  250. local max = tvine.steptime.max
  251. minetest.get_node_timer(p):start(math_random(min, max))
  252. --return true
  253. end
  254. function tvine.after_dig_node(pos, node, metadata, digger)
  255. tvine.dig_up(pos, node, digger)
  256. -- No return value.
  257. end
  258. function tvine.dig_up(pos, node, digger)
  259. if digger == nil then return end
  260. local np = {x = pos.x, y = pos.y + 1, z = pos.z}
  261. local nn = minetest.get_node(np)
  262. if tvine.is_plant_name(nn.name) then
  263. minetest.node_dig(np, nn, digger)
  264. end
  265. end
  266. function tvine.on_display_construct(pos)
  267. local n = minetest.get_node(pos)
  268. n.param2 = math_random(0, 239)
  269. minetest.swap_node(pos, n)
  270. end
  271. if not tvine.run_once then
  272. -- This version is for display only - does not grow or provide seeds.
  273. minetest.register_node("default:tvine_display", {
  274. description = "Twisted Vine",
  275. drawtype = "plantlike",
  276. tiles = {"default_tvine_display.png"},
  277. inventory_image = "default_tvine_display.png",
  278. wield_image = "default_tvine_display.png",
  279. paramtype = "light",
  280. paramtype2 = "degrotate",
  281. sunlight_propagates = true,
  282. light_source = tvine.light_source,
  283. walkable = false,
  284. waving = 1,
  285. -- Manually placed vines are not climbable.
  286. selection_box = {
  287. type = "fixed",
  288. fixed = {-0.3, -0.5, -0.3, 0.3, 0.5, 0.3}
  289. },
  290. groups = utility.dig_groups("plant", {flammable = 2}),
  291. sounds = default.node_sound_leaves_defaults(),
  292. movement_speed_multiplier = default.SLOW_SPEED_PLANTS,
  293. on_construct = function(...) return tvine.on_display_construct(...) end,
  294. })
  295. minetest.register_node("default:tvine_seed", {
  296. description = "Twisted Vine Seed",
  297. tiles = {"tvine_seed.png"},
  298. wield_image = "tvine_seed.png",
  299. inventory_image = "tvine_seed.png",
  300. drawtype = "signlike",
  301. paramtype = "light",
  302. paramtype2 = "wallmounted",
  303. walkable = false,
  304. sunlight_propagates = true,
  305. selection_box = {
  306. type = "fixed",
  307. fixed = {-0.5, -0.5, -0.5, 0.5, -5/16, 0.5}
  308. },
  309. groups = utility.dig_groups("seeds", {seed = 1, attached_node = 1, flammable = 2, notify_destruct = 1}),
  310. on_place = function(itemstack, placer, pointed_thing)
  311. return farming.place_seed(itemstack, placer, pointed_thing, "default:tvine_seed")
  312. end,
  313. soil_nodes = {
  314. "default:dirt_with_grass",
  315. "default:dirt",
  316. "moregrass:darkgrass",
  317. "talinite:ore",
  318. "talinite:desert_ore",
  319. },
  320. on_timer = function(...) return farming.grow_plant(...) end,
  321. minlight = tvine.minlight,
  322. maxlight = tvine.maxlight,
  323. next_plant = {"default:tvine_stunted"},
  324. sounds = default.node_sound_dirt_defaults({
  325. dug = {name = "default_grass_footstep", gain = 0.2},
  326. place = {name = "default_place_node", gain = 0.25},
  327. }),
  328. farming_minerals_unused = true,
  329. })
  330. -- This version provides seeds!
  331. minetest.register_node("default:tvine_stunted", {
  332. description = "Twisted Vine (Hacker!)",
  333. drawtype = "plantlike",
  334. tiles = {"default_tvine_display.png"},
  335. inventory_image = "default_tvine.png",
  336. wield_image = "default_tvine.png",
  337. paramtype = "light",
  338. paramtype2 = "degrotate",
  339. sunlight_propagates = true,
  340. light_source = tvine.light_source,
  341. walkable = false,
  342. waving = 1,
  343. -- Manually placed vines are not climbable.
  344. selection_box = {
  345. type = "fixed",
  346. fixed = {-0.3, -0.5, -0.3, 0.3, 0.5, 0.3}
  347. },
  348. drop = "default:tvine_seed",
  349. groups = utility.dig_groups("plant", {flammable = 2}),
  350. sounds = default.node_sound_leaves_defaults(),
  351. movement_speed_multiplier = default.SLOW_SPEED_PLANTS,
  352. next_plant = {"default:tvine_top", "default:tvine_top_alt"},
  353. on_timer = function(...) return farming.grow_plant(...) end,
  354. minlight = tvine.minlight,
  355. maxlight = tvine.maxlight,
  356. soil_nodes = {
  357. "default:dirt_with_grass",
  358. "default:dirt",
  359. "moregrass:darkgrass",
  360. "talinite:ore",
  361. "talinite:desert_ore",
  362. },
  363. farming_minerals_unused = true,
  364. farming_growing_time_min = 60*10,
  365. farming_growing_time_max = 60*40,
  366. on_collapse_to_entity = function(pos, node)
  367. minetest.add_item(pos, {name="default:tvine_display"})
  368. end,
  369. })
  370. stalk_drops = {
  371. max_items = 1,
  372. items = {
  373. {items = {'talinite:dust'}, rarity = 2},
  374. },
  375. }
  376. minetest.register_node("default:tvine", {
  377. description = "Twisted Vine (Hacker!)",
  378. drawtype = "plantlike",
  379. tiles = {"default_tvine.png"},
  380. inventory_image = "default_tvine.png",
  381. wield_image = "default_tvine.png",
  382. paramtype = "light",
  383. paramtype2 = "degrotate",
  384. sunlight_propagates = true,
  385. light_source = tvine.light_source,
  386. walkable = false,
  387. climbable = true,
  388. drop = stalk_drops,
  389. selection_box = {
  390. type = "fixed",
  391. fixed = {-0.3, -0.5, -0.3, 0.3, 0.5, 0.3}
  392. },
  393. groups = utility.dig_groups("plant", {flammable = 2}),
  394. sounds = default.node_sound_leaves_defaults(),
  395. movement_speed_multiplier = default.SLOW_SPEED_PLANTS,
  396. on_construct = function(...)
  397. return tvine.on_construct(...)
  398. end,
  399. on_destruct = function(...)
  400. return tvine.on_destruct(...)
  401. end,
  402. on_timer = function(...)
  403. return tvine.on_timer(...)
  404. end,
  405. after_dig_node = function(...)
  406. return tvine.after_dig_node(...)
  407. end,
  408. on_finish_collapse = function(pos, node)
  409. minetest.swap_node(pos, {name="default:tvine_display", param2=math_random(0, 239)})
  410. end,
  411. on_collapse_to_entity = function(pos, node)
  412. minetest.add_item(pos, {name="default:tvine_display"})
  413. end,
  414. })
  415. minetest.register_node("default:tvine_alt", {
  416. description = "Twisted Vine (Hacker!)",
  417. drawtype = "plantlike",
  418. tiles = {"default_tvine_alt.png"},
  419. inventory_image = "default_tvine.png",
  420. wield_image = "default_tvine.png",
  421. paramtype = "light",
  422. paramtype2 = "degrotate",
  423. sunlight_propagates = true,
  424. light_source = tvine.light_source,
  425. walkable = false,
  426. climbable = true,
  427. drop = stalk_drops,
  428. selection_box = {
  429. type = "fixed",
  430. fixed = {-0.3, -0.5, -0.3, 0.3, 0.5, 0.3}
  431. },
  432. groups = utility.dig_groups("plant", {flammable = 2}),
  433. sounds = default.node_sound_leaves_defaults(),
  434. movement_speed_multiplier = default.SLOW_SPEED_PLANTS,
  435. on_construct = function(...)
  436. return tvine.on_construct(...)
  437. end,
  438. on_destruct = function(...)
  439. return tvine.on_destruct(...)
  440. end,
  441. on_timer = function(...)
  442. return tvine.on_timer(...)
  443. end,
  444. after_dig_node = function(...)
  445. return tvine.after_dig_node(...)
  446. end,
  447. on_finish_collapse = function(pos, node)
  448. minetest.swap_node(pos, {name="default:tvine_display", param2=math_random(0, 239)})
  449. end,
  450. on_collapse_to_entity = function(pos, node)
  451. minetest.add_item(pos, {name="default:tvine_display"})
  452. end,
  453. })
  454. top_drops = {
  455. max_items = 2,
  456. items = {
  457. {items = {'default:tvine_display'}, rarity = 5},
  458. {items = {'default:tvine_seed'}},
  459. {items = {'default:tvine_seed'}, rarity = 10},
  460. },
  461. }
  462. minetest.register_node("default:tvine_top", {
  463. description = "Twisted Vine (Hacker!)",
  464. drawtype = "plantlike",
  465. tiles = {"default_tvine_top.png"},
  466. inventory_image = "default_tvine.png",
  467. wield_image = "default_tvine.png",
  468. paramtype = "light",
  469. paramtype2 = "degrotate",
  470. sunlight_propagates = true,
  471. light_source = tvine.light_source,
  472. walkable = false,
  473. waving = 1,
  474. drop = top_drops,
  475. selection_box = {
  476. type = "fixed",
  477. fixed = {-0.3, -0.5, -0.3, 0.3, 0.5, 0.3}
  478. },
  479. groups = utility.dig_groups("plant", {flammable = 2}),
  480. sounds = default.node_sound_leaves_defaults(),
  481. movement_speed_multiplier = default.SLOW_SPEED_PLANTS,
  482. on_construct = function(...)
  483. return tvine.on_construct(...)
  484. end,
  485. on_destruct = function(...)
  486. return tvine.on_destruct(...)
  487. end,
  488. on_timer = function(...)
  489. return tvine.on_timer(...)
  490. end,
  491. after_dig_node = function(...)
  492. return tvine.after_dig_node(...)
  493. end,
  494. on_finish_collapse = function(pos, node)
  495. minetest.swap_node(pos, {name="default:tvine_display", param2=math_random(0, 239)})
  496. end,
  497. on_collapse_to_entity = function(pos, node)
  498. minetest.add_item(pos, {name="default:tvine_display"})
  499. end,
  500. -- Instruct farming mod to restart the timer.
  501. -- Otherwise, after growing the last plant, the timer would halt.
  502. farming_restart_timer = true,
  503. farming_growing_time_min = tvine.steptime.min,
  504. farming_growing_time_max = tvine.steptime.max,
  505. })
  506. minetest.register_node("default:tvine_top_alt", {
  507. description = "Twisted Vine (Hacker!)",
  508. drawtype = "plantlike",
  509. tiles = {"default_tvine_top_alt.png"},
  510. inventory_image = "default_tvine.png",
  511. wield_image = "default_tvine.png",
  512. paramtype = "light",
  513. paramtype2 = "degrotate",
  514. sunlight_propagates = true,
  515. light_source = tvine.light_source,
  516. walkable = false,
  517. waving = 1,
  518. drop = top_drops,
  519. selection_box = {
  520. type = "fixed",
  521. fixed = {-0.3, -0.5, -0.3, 0.3, 0.5, 0.3}
  522. },
  523. groups = utility.dig_groups("plant", {flammable = 2}),
  524. sounds = default.node_sound_leaves_defaults(),
  525. movement_speed_multiplier = default.SLOW_SPEED_PLANTS,
  526. on_construct = function(...)
  527. return tvine.on_construct(...)
  528. end,
  529. on_destruct = function(...)
  530. return tvine.on_destruct(...)
  531. end,
  532. on_timer = function(...)
  533. return tvine.on_timer(...)
  534. end,
  535. after_dig_node = function(...)
  536. return tvine.after_dig_node(...)
  537. end,
  538. on_finish_collapse = function(pos, node)
  539. minetest.swap_node(pos, {name="default:tvine_display", param2=math_random(0, 239)})
  540. end,
  541. on_collapse_to_entity = function(pos, node)
  542. minetest.add_item(pos, {name="default:tvine_display"})
  543. end,
  544. -- Instruct farming mod to restart the timer.
  545. -- Otherwise, after growing the last plant, the timer would halt.
  546. farming_restart_timer = true,
  547. farming_growing_time_min = tvine.steptime.min,
  548. farming_growing_time_max = tvine.steptime.max,
  549. })
  550. minetest.register_craft({
  551. type = "extracting",
  552. output = 'talinite:dust 4',
  553. recipe = 'default:tvine_display',
  554. time = 5,
  555. })
  556. local c = "tvine:core"
  557. local f = tvine.modpath .. "/tvine.lua"
  558. reload.register_file(c, f, false)
  559. tvine.run_once = true
  560. end