VoxeLibre/mods/ITEMS/mcl_end/chorus_plant.lua

387 lines
13 KiB
Lua
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

-- Chorus plants
-- This includes chorus flowers, chorus plant stem nodes and chorus fruit
--- Plant parts ---
local MAX_FLOWER_AGE = 5 -- Maximum age of chorus flower before it dies
local chorus_flower_box = {
type = "fixed",
fixed = {
{-0.5, -0.375, -0.375, 0.5, 0.375, 0.375},
{-0.375, -0.375, 0.375, 0.375, 0.375, 0.5},
{-0.375, -0.375, -0.5, 0.375, 0.375, -0.375},
{-0.375, 0.375, -0.375, 0.375, 0.5, 0.375},
{-0.375, -0.5, -0.375, 0.375, -0.375, 0.375},
}
}
-- Helper function
local function round(num, idp)
local mult = 10^(idp or 0)
return math.floor(num * mult + 0.5) / mult
end
minetest.register_node("mcl_end:chorus_flower", {
description = "Chorus Flower",
_doc_items_longdesc = "A chorus flower is the living part of a chorus plant. It can grow into a tall chorus plant, step by step. When it grows, it may die on old age eventually. It also dies when it is unable to grow.",
_doc_items_usagehelp = "Place it and wait for it to grow. It can only be placed on top of end stone, on top of a chorus plant stem, or at the side of exactly a chorus plant stem.",
tiles = {
"mcl_end_chorus_flower.png",
"mcl_end_chorus_flower.png",
"mcl_end_chorus_flower.png",
"mcl_end_chorus_flower.png",
"mcl_end_chorus_flower.png",
"mcl_end_chorus_flower.png",
},
drawtype = "nodebox",
paramtype = "light",
sunlight_propagates = true,
node_box = chorus_flower_box,
selection_box = { type = "regular" },
sounds = mcl_sounds.node_sound_wood_defaults(),
groups = {handy=1,axey=1, deco_block = 1, dig_by_piston = 1, destroy_by_lava_flow = 1,},
node_placement_prediction = "",
on_place = function(itemstack, placer, pointed_thing)
local node_under = minetest.get_node(pointed_thing.under)
local node_above = minetest.get_node(pointed_thing.above)
if placer and not placer:get_player_control().sneak then
-- Use pointed node's on_rightclick function first, if present
if minetest.registered_nodes[node_under.name] and minetest.registered_nodes[node_under.name].on_rightclick then
return minetest.registered_nodes[node_under.name].on_rightclick(pointed_thing.under, node_under, placer, itemstack) or itemstack
end
end
--[[ Part 1: Check placement rules. Placement is legal is one of the following
conditions is met:
1) On top of end stone or chorus plant
2) On top of air and horizontally adjacent to exactly 1 chorus plant ]]
local pos
if minetest.registered_nodes[node_under.name].buildable_to then
pos = pointed_thing.under
else
pos = pointed_thing.above
end
local below = {x=pos.x, y=pos.y-1, z=pos.z}
local node_below = minetest.get_node(below)
local plant_ok = false
-- Condition 1
if node_below.name == "mcl_end:chorus_plant" or node_below.name == "mcl_end:end_stone" then
plant_ok = true
-- Condition 2
elseif node_below.name == "air" then
local around = {
{ x= 1, y=0, z= 0 },
{ x=-1, y=0, z= 0 },
{ x= 0, y=0, z= 1 },
{ x= 0, y=0, z=-1 },
}
local around_count = 0
for a=1, #around do
local pos_side = vector.add(pos, around[a])
local node_side = minetest.get_node(pos_side)
if node_side.name == "mcl_end:chorus_plant" then
around_count = around_count + 1
if around_count > 1 then
break
end
end
end
if around_count == 1 then
plant_ok = true
end
end
if plant_ok then
-- Placement OK! Proceed normally
minetest.sound_play(mcl_sounds.node_sound_wood_defaults().place, {pos = pos})
return minetest.item_place_node(itemstack, placer, pointed_thing)
else
return itemstack
end
end,
_mcl_blast_resistance = 2,
_mcl_hardness = 0.4,
})
minetest.register_node("mcl_end:chorus_flower_dead", {
description = "Dead Chorus Flower",
_doc_items_longdesc = "This is a part of a chorus plant. It doesn't grow. Chorus flowers die of old age or when they are unable to grow. A dead chorus flower can be harvested to obtain a fresh chorus flower which is able to grow again.",
tiles = {
"mcl_end_chorus_flower_dead.png",
"mcl_end_chorus_flower_dead.png",
"mcl_end_chorus_flower_dead.png",
"mcl_end_chorus_flower_dead.png",
"mcl_end_chorus_flower_dead.png",
"mcl_end_chorus_flower_dead.png",
},
drawtype = "nodebox",
paramtype = "light",
sunlight_propagates = true,
node_box = chorus_flower_box,
selection_box = { type = "regular" },
sounds = mcl_sounds.node_sound_wood_defaults(),
drop = "mcl_end:chorus_flower",
groups = {handy=1,axey=1, deco_block = 1, dig_by_piston = 1, destroy_by_lava_flow = 1,},
_mcl_blast_resistance = 2,
_mcl_hardness = 0.4,
})
minetest.register_node("mcl_end:chorus_plant", {
description = "Chorus Plant Stem",
_doc_items_longdesc = "A chorus plant stem is the part of a chorus plant which holds the whole plant together. It needs end stone as its soil. Stems are grown from chorus flowers.",
tiles = {
"mcl_end_chorus_plant.png",
"mcl_end_chorus_plant.png",
"mcl_end_chorus_plant.png",
"mcl_end_chorus_plant.png",
"mcl_end_chorus_plant.png",
"mcl_end_chorus_plant.png",
},
drawtype = "nodebox",
paramtype = "light",
sunlight_propagates = true,
node_box = {
type = "connected",
fixed = { -0.25, -0.25, -0.25, 0.25, 0.25, 0.25 }, -- Core
connect_top = { -0.1875, 0.25, -0.1875, 0.1875, 0.5, 0.1875 },
connect_left = { -0.5, -0.1875, -0.1875, -0.25, 0.1875, 0.1875 },
connect_right = { 0.25, -0.1875, -0.1875, 0.5, 0.1875, 0.1875 },
connect_bottom = { -0.1875, -0.5, -0.25, 0.1875, -0.25, 0.25 },
connect_front = { -0.1875, -0.1875, -0.5, 0.1875, 0.1875, -0.25 },
connect_back = { -0.1875, -0.1875, 0.25, 0.1875, 0.1875, 0.5 },
},
connect_sides = { "top", "bottom", "front", "back", "left", "right" },
connects_to = {"mcl_end:chorus_plant", "mcl_end:chorus_flower", "mcl_end:chorus_flower_dead", "mcl_end:end_stone"},
sounds = mcl_sounds.node_sound_wood_defaults(),
drop = {
items = {
{ items = { "mcl_end:chorus_fruit"}, rarity = 2 },
}
},
groups = {handy=1,axey=1, not_in_creative_inventory = 1, dig_by_piston = 1, destroy_by_lava_flow = 1 },
_mcl_blast_resistance = 2,
_mcl_hardness = 0.4,
})
--- ABM ---
minetest.register_abm({
label = "Chorus plant growth",
nodenames = { "mcl_end:chorus_flower" },
interval = 35.0,
chance = 4.0,
action = function(pos, node, active_object_count, active_object_count_wider)
local above = { x = pos.x, y = pos.y + 1, z = pos.z }
local node_above = minetest.get_node(above)
local around = {
{ x=-1, y=0, z= 0 },
{ x= 1, y=0, z= 0 },
{ x= 0, y=0, z=-1 },
{ x= 0, y=0, z= 1 },
}
local air_around = true
for a=1, #around do
if minetest.get_node(vector.add(above, around[a])).name ~= "air" then
air_around = false
break
end
end
local grown = false
if node_above.name == "air" and air_around then
local branching = false
local h = 0
for y=1, 4 do
local checkpos = {x=pos.x, y=pos.y-y, z=pos.z}
local node = minetest.get_node(checkpos)
if node.name == "mcl_end:chorus_plant" then
h = y
if not branching then
for a=1, #around do
local node_side = minetest.get_node(vector.add(checkpos, around[a]))
if node_side.name == "mcl_end:chorus_plant" then
branching = true
end
end
end
else
break
end
end
local grow_chance
if h <= 1 then
grow_chance = 100
elseif h == 2 and branching == false then
grow_chance = 60
elseif h == 2 and branching == true then
grow_chance = 50
elseif h == 3 and branching == false then
grow_chance = 40
elseif h == 3 and branching == true then
grow_chance = 25
elseif h == 4 and branching == false then
grow_chance = 20
end
if grow_chance then
local new_flowers = {}
local r = math.random(1, 100)
local age = node.param2
if r <= grow_chance then
table.insert(new_flowers, above)
else
age = age + 1
local branches
if branching == false then
branches = math.random(1, 4)
elseif branching == true then
branches = math.random(0, 3)
end
local branch_grown = false
for b=1, branches do
local next_branch = math.random(1, #around)
local branch = vector.add(pos, around[next_branch])
local below_branch = vector.add(branch, {x=0,y=-1,z=0})
if minetest.get_node(below_branch).name == "air" then
table.insert(new_flowers, branch)
end
end
end
for _, f in ipairs(new_flowers) do
if age >= MAX_FLOWER_AGE then
minetest.set_node(f, {name="mcl_end:chorus_flower_dead"})
grown = true
else
minetest.set_node(f, {name="mcl_end:chorus_flower", param2 = age})
grown = true
end
end
if #new_flowers >= 1 then
minetest.set_node(pos, {name="mcl_end:chorus_plant"})
grown = true
end
end
end
if not grown then
minetest.set_node(pos, {name = "mcl_end:chorus_flower_dead"})
end
end,
})
--- Chorus fruit ---
-- Attempt to randomly teleport the player within a 8×8×8 box around. Rules:
-- * Not in solid blocks.
-- * Not in liquids.
-- * Always on top of a solid block
-- * Maximum attempts: 16
--
-- Returns true on success.
local random_teleport = function(player)
local pos = player:get_pos()
-- 16 attempts to find a suitable position
for a=1, 16 do
-- Teleportation box
local x,y,z
x = math.random(round(pos.x)-8, round(pos.x)+8)
y = math.random(math.ceil(pos.y)-8, math.ceil(pos.y)+8)
z = math.random(round(pos.z)-8, round(pos.z)+8)
local node_cache = {}
local ground_level = false
-- Scan nodes from selected position until we hit ground
for t=0, 16 do
local tpos = {x=x, y=y-t, z=z}
local tnode = minetest.get_node(tpos)
if tnode.name == "mcl_core:void" or tnode.name == "ignore" then
break
end
local tdef = minetest.registered_nodes[tnode.name]
table.insert(node_cache, {pos=tpos, node=tnode})
if tdef.walkable then
ground_level = true
break
end
end
-- Ground found? Then let's check if the player has enough room
if ground_level and #node_cache >= 1 then
local streak = 0
local last_was_walkable = true
for c=#node_cache, 1, -1 do
local tpos = node_cache[c].pos
local tnode = node_cache[c].node
local tdef = minetest.registered_nodes[tnode.name]
-- Player needs a space of 2 safe non-liquid nodes on top of a walkable node
if not tdef.walkable and tdef.liquidtype == "none" and tdef.damage_per_second <= 0 then
if (streak == 0 and last_was_walkable) or (streak > 0) then
streak = streak + 1
end
else
streak = 0
end
last_was_walkable = tdef.walkable
if streak >= 2 then
-- JACKPOT! Now we can teleport.
local goal = {x=tpos.x, y=tpos.y-1.5, z=tpos.z}
player:set_pos(goal)
minetest.sound_play({name="mcl_end_teleport", gain=0.8}, {pos=goal, max_hear_distance=16})
return true
end
end
end
end
return false
end
-- Randomly teleport player and update hunger
local eat_chorus_fruit = function(itemstack, player, pointed_thing)
if player and pointed_thing and pointed_thing.type == "node" and not player:get_player_control().sneak then
local node_under = minetest.get_node(pointed_thing.under)
-- Use pointed node's on_rightclick function first, if present
if minetest.registered_nodes[node_under.name] and minetest.registered_nodes[node_under.name].on_rightclick then
return minetest.registered_nodes[node_under.name].on_rightclick(pointed_thing.under, node_under, player, itemstack) or itemstack
end
end
local count = itemstack:get_count()
local new_itemstack = minetest.do_item_eat(0, nil, itemstack, player, pointed_thing)
local new_count = new_itemstack:get_count()
if count ~= new_count or new_itemstack:get_name() ~= "mcl_end:chorus_fruit" or (minetest.settings:get_bool("creative_mode") == true) then
random_teleport(player)
end
return new_itemstack
end
minetest.register_craftitem("mcl_end:chorus_fruit", {
description = "Chorus Fruit",
_doc_items_longdesc = "A chorus fruit is an edible fruit from the chorus plant which is home to the End. Eating it teleports you to the top of a random solid block nearby, provided you won't end up inside a liquid, solid or harmful blocks. Teleportation might fail if there are very few or no places to teleport to.",
wield_image = "mcl_end_chorus_fruit.png",
inventory_image = "mcl_end_chorus_fruit.png",
on_place = eat_chorus_fruit,
on_secondary_use = eat_chorus_fruit,
groups = { food = 2, transport = 1, eatable = 4, can_eat_when_full = 1 },
_mcl_saturation = 2.4,
stack_max = 64,
})
minetest.register_craftitem("mcl_end:chorus_fruit_popped", {
description = "Popped Chorus Fruit",
_doc_items_longdesc = doc.sub.items.temp.craftitem,
wield_image = "mcl_end_chorus_fruit_popped.png",
inventory_image = "mcl_end_chorus_fruit_popped.png",
groups = { craftitem = 1 },
stack_max = 64,
})
--- Crafting ---
minetest.register_craft({
type = "cooking",
output = "mcl_end:chorus_fruit_popped",
recipe = "mcl_end:chorus_fruit",
cooktime = 10,
})