diff --git a/mods/ITEMS/mcl_end/building.lua b/mods/ITEMS/mcl_end/building.lua new file mode 100644 index 0000000000..2e7c425237 --- /dev/null +++ b/mods/ITEMS/mcl_end/building.lua @@ -0,0 +1,192 @@ +-- Building blocks and decorative nodes + +minetest.register_node("mcl_end:end_stone", { + description = "End Stone", + _doc_items_longdesc = doc.sub.items.temp.build, + tiles = {"mcl_end_end_stone.png"}, + stack_max = 64, + groups = {pickaxey=1, building_block=1, material_stone=1}, + sounds = mcl_sounds.node_sound_stone_defaults(), + _mcl_blast_resistance = 45, + _mcl_hardness = 3, +}) + +minetest.register_node("mcl_end:end_bricks", { + description = "End Stone Bricks", + _doc_items_longdesc = doc.sub.items.temp.build, + tiles = {"mcl_end_end_bricks.png"}, + is_ground_content = false, + stack_max = 64, + groups = {pickaxey=1, building_block=1, material_stone=1}, + sounds = mcl_sounds.node_sound_stone_defaults(), + _mcl_blast_resistance = 4, + _mcl_hardness = 0.8, +}) + +minetest.register_node("mcl_end:purpur_block", { + description = "Purpur Block", + _doc_items_longdesc = doc.sub.items.temp.build, + tiles = {"mcl_end_purpur_block.png"}, + is_ground_content = false, + stack_max = 64, + groups = {pickaxey=1, building_block=1, material_stone=1}, + sounds = mcl_sounds.node_sound_stone_defaults(), + _mcl_blast_resistance = 30, + _mcl_hardness = 1.5, +}) + +minetest.register_node("mcl_end:purpur_pillar", { + description = "Purpur Pillar", + _doc_items_longdesc = doc.sub.items.temp.build, + stack_max = 64, + paramtype2 = "facedir", + is_ground_content = false, + on_place = mcl_util.rotate_axis, + tiles = {"mcl_end_purpur_pillar_top.png", "mcl_end_purpur_pillar_top.png", "mcl_end_purpur_pillar.png"}, + groups = {pickaxey=1, building_block=1, material_stone=1}, + sounds = mcl_sounds.node_sound_stone_defaults(), + _mcl_blast_resistance = 30, + _mcl_hardness = 1.5, +}) + +minetest.register_node("mcl_end:end_rod", { + description = "End Rod", + _doc_items_longdesc = "End rods are decorational light sources.", + tiles = { + "mcl_end_end_rod_top.png", + "mcl_end_end_rod_bottom.png", + "mcl_end_end_rod_side.png", + "mcl_end_end_rod_side.png", + "mcl_end_end_rod_side.png", + "mcl_end_end_rod_side.png", + }, + drawtype = "nodebox", + is_ground_content = false, + paramtype = "light", + paramtype2 = "facedir", + light_source = 14, + sunlight_propagates = true, + groups = { dig_immediate=3, deco_block=1, destroy_by_lava_flow=1, }, + node_box = { + type = "fixed", + fixed = { + {-0.125, -0.5, -0.125, 0.125, -0.4375, 0.125}, -- Base + {-0.0625, -0.4375, -0.0625, 0.0625, 0.5, 0.0625}, -- Rod + }, + }, + selection_box = { + type = "fixed", + fixed = { + {-0.125, -0.5, -0.125, 0.125, 0.5, 0.125}, -- Base + }, + }, + collision_box = { + type = "fixed", + fixed = { + {-0.125, -0.5, -0.125, 0.125, 0.5, 0.125}, -- Base + }, + }, + on_place = function(itemstack, placer, pointed_thing) + if pointed_thing.type ~= "node" then + return itemstack + end + + local p0 = pointed_thing.under + local p1 = pointed_thing.above + local param2 = 0 + + local placer_pos = placer:getpos() + if placer_pos then + local dir = { + x = p1.x - placer_pos.x, + y = p1.y - placer_pos.y, + z = p1.z - placer_pos.z + } + param2 = minetest.dir_to_facedir(dir) + end + + if p0.y - 1 == p1.y then + param2 = 20 + elseif p0.x - 1 == p1.x then + param2 = 16 + elseif p0.x + 1 == p1.x then + param2 = 12 + elseif p0.z - 1 == p1.z then + param2 = 8 + elseif p0.z + 1 == p1.z then + param2 = 4 + end + + return minetest.item_place(itemstack, placer, pointed_thing, param2) + end, + + sounds = mcl_sounds.node_sound_glass_defaults(), + _mcl_blast_resistance = 0, +}) + +minetest.register_node("mcl_end:dragon_egg", { + description = "Dragon Egg", + _doc_items_longdesc = "A dragon egg is a decorational item which can be placed.", + tiles = { + "mcl_end_dragon_egg.png", + "mcl_end_dragon_egg.png", + "mcl_end_dragon_egg.png", + "mcl_end_dragon_egg.png", + "mcl_end_dragon_egg.png", + "mcl_end_dragon_egg.png", + }, + drawtype = "nodebox", + is_ground_content = false, + paramtype = "light", + light_source = 1, + node_box = { + type = "fixed", + fixed = { + {-0.375, -0.5, -0.375, 0.375, -0.4375, 0.375}, + {-0.5, -0.4375, -0.5, 0.5, -0.1875, 0.5}, + {-0.4375, -0.1875, -0.4375, 0.4375, 0, 0.4375}, + {-0.375, 0, -0.375, 0.375, 0.125, 0.375}, + {-0.3125, 0.125, -0.3125, 0.3125, 0.25, 0.3125}, + {-0.25, 0.25, -0.25, 0.25, 0.3125, 0.25}, + {-0.1875, 0.3125, -0.1875, 0.1875, 0.375, 0.1875}, + {-0.125, 0.375, -0.125, 0.125, 0.4375, 0.125}, + {-0.0625, 0.4375, -0.0625, 0.0625, 0.5, 0.0625}, + } + }, + selection_box = { + type = "regular", + }, + groups = {handy=1, falling_node = 1, deco_block = 1, not_in_creative_inventory = 1, dig_by_piston = 1 }, + sounds = mcl_sounds.node_sound_stone_defaults(), + _mcl_blast_resistance = 45, + _mcl_hardness = 3, + -- TODO: Make dragon egg teleport on punching +}) + + + +-- Crafting recipes +minetest.register_craft({ + output = "mcl_end:end_bricks 4", + recipe = { + {"mcl_end:end_stone", "mcl_end:end_stone"}, + {"mcl_end:end_stone", "mcl_end:end_stone"}, + } +}) + +minetest.register_craft({ + output = "mcl_end:purpur_block 4", + recipe = { + {"mcl_end:chorus_fruit_popped", "mcl_end:chorus_fruit_popped",}, + {"mcl_end:chorus_fruit_popped", "mcl_end:chorus_fruit_popped",}, + } +}) + +minetest.register_craft({ + output = "mcl_end:end_rod 4", + recipe = { + {"mcl_mobitems:blaze_rod"}, + {"mcl_end:chorus_fruit_popped"}, + }, +}) + diff --git a/mods/ITEMS/mcl_end/chorus_plant.lua b/mods/ITEMS/mcl_end/chorus_plant.lua new file mode 100644 index 0000000000..38022570fd --- /dev/null +++ b/mods/ITEMS/mcl_end/chorus_plant.lua @@ -0,0 +1,288 @@ +-- Chorus plants. +-- This includes chorus flowers, chorus plant stem nodes and chorus fruit + +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}, + } +} + +minetest.register_node("mcl_end:chorus_flower", { + description = "Chorus Flower", + 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 + return minetest.item_place(itemstack, placer, pointed_thing) + else + return itemstack + end + end, + _mcl_blast_resistance = 2, + _mcl_hardness = 0.4, +}) + +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 + 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 + + local grown = false + 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 >= 5 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 + if not grown then + minetest.set_node(pos, {name = "mcl_end:chorus_flower_dead"}) + end + end + end, +}) + +minetest.register_node("mcl_end:chorus_flower_dead", { + description = "Dead Chorus Flower", + 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", + 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, + -- TODO: Maybe improve nodebox a bit to look more “natural” + 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, +}) + +-- Craftitems +minetest.register_craftitem("mcl_end:chorus_fruit", { + description = "Chorus Fruit", + _doc_items_longdesc = "Chorus fruits are the fruits of the chorus plant which is home to the End. They can be eaten to restore a few hunger points.", + wield_image = "mcl_end_chorus_fruit.png", + inventory_image = "mcl_end_chorus_fruit.png", + -- TODO: Teleport player + on_place = minetest.item_eat(4), + on_secondary_use = minetest.item_eat(4), + groups = { food = 2, 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, +}) + +minetest.register_craft({ + type = "cooking", + output = "mcl_end:chorus_fruit_popped", + recipe = "mcl_end:chorus_fruit", + cooktime = 10, +}) + diff --git a/mods/ITEMS/mcl_end/eye_of_ender.lua b/mods/ITEMS/mcl_end/eye_of_ender.lua new file mode 100644 index 0000000000..131db7bbc6 --- /dev/null +++ b/mods/ITEMS/mcl_end/eye_of_ender.lua @@ -0,0 +1,153 @@ +-- Eye of Ender + +minetest.register_entity("mcl_end:ender_eye", { + physical = false, + textures = {"mcl_end_ender_eye.png"}, + visual_size = {x=1.5, y=1.5}, + collisionbox = {0,0,0,0,0,0}, + + -- Save and restore age + get_staticdata = function(self) + return tostring(self._age) + end, + on_activate = function(self, staticdata, dtime_s) + local age = tonumber(staticdata) + if type(age) == "number" then + self._age = age + if self._age >= 2 then + self._phase = 1 + else + self._phase = 0 + end + end + end, + + on_step = function(self, dtime) + self._age = self._age + dtime + if self._age >= 3 then + -- End of life + local r = math.random(1,5) + if r == 1 or minetest.settings:get_bool("creative_mode") then + -- 20% chance to get destroyed completely. + -- 100% if in Creative Mode + self.object:remove() + return + else + -- 80% to drop as an item + local pos = self.object:get_pos() + local v = self.object:getvelocity() + self.object:remove() + local item = minetest.add_item(pos, "mcl_end:ender_eye") + item:setvelocity(v) + return + end + elseif self._age >= 2 then + if self._phase == 0 then + self._phase = 1 + -- Stop the eye and wait for another second. + -- The vertical speed changes are just eye candy. + self.object:setacceleration({x=0, y=-3, z=0}) + self.object:setvelocity({x=0, y=self.object:getvelocity().y*0.2, z=0}) + end + else + -- Fly normally and generate particles + local pos = self.object:get_pos() + pos.x = pos.x + math.random(-1, 1)*0.5 + pos.y = pos.y + math.random(-1, 0)*0.5 + pos.z = pos.z + math.random(-1, 1)*0.5 + minetest.add_particle({ + pos = pos, + texture = "mcl_particles_teleport.png", + expirationtime = 1, + velocity = {x=math.random(-1, 1)*0.1, y=math.random(-30, 0)*0.1, z=math.random(-1, 1)*0.1}, + acceleration = {x=0, y=0, z=0}, + size = 2.5, + }) + end + end, + + _age = 0, -- age in seconds + _phase = 0, -- phase 0: flying. phase 1: idling in mid air, about to drop or shatter +}) + +minetest.register_craftitem("mcl_end:ender_eye", { + description = "Eye of Ender", + _doc_items_longdesc = "This item is used to locate End portal shrines in the Overworld and to activate End portals." .. "\n" .. "NOTE: The End dimension is currently incomplete and boring.", + _doc_items_usagehelp = "Use the attack key to release the eye of ender. It will rise and fly in the horizontal direction of the closest end portal shrine. If you're very close, the eye of ender will take the direct path to the End portal shrine instead. After a few seconds, it stops. It may drop as an item, but there's a 20% chance it shatters." .. "\n" .. "To activate an End portal, eyes of ender need to be placed into each block of an intact End portal frame.", + wield_image = "mcl_end_ender_eye.png", + inventory_image = "mcl_end_ender_eye.png", + stack_max = 64, + -- Throw eye of ender to make it fly to the closest stronghold + on_use = function(itemstack, user, pointed_thing) + if user == nil then + return + end + local origin = user:get_pos() + origin.y = origin.y + 1.5 + local strongholds = mcl_structures.get_registered_structures("stronghold") + local dim = mcl_worlds.pos_to_dimension(origin) + local is_creative = minetest.settings:get_bool("creative_mode") + + -- Just drop the eye of ender if there are no strongholds + if #strongholds <= 0 or dim ~= "overworld" then + if not is_creative then + minetest.item_drop(ItemStack("mcl_end:ender_eye"), user, user:get_pos()) + itemstack:take_item() + end + return itemstack + end + + -- Find closest stronghold. + -- Note: Only the horizontal axes are taken into account. + local closest_stronghold + local lowest_dist + for s=1, #strongholds do + local h_pos = table.copy(strongholds[s].pos) + local h_origin = table.copy(origin) + h_pos.y = 0 + h_origin.y = 0 + local dist = vector.distance(h_origin, h_pos) + if not closest_stronghold then + closest_stronghold = strongholds[s] + lowest_dist = dist + else + if dist < lowest_dist then + closest_stronghold = strongholds[s] + lowest_dist = dist + end + end + end + + -- Throw it! + local obj = minetest.add_entity(origin, "mcl_end:ender_eye") + local dir + + if lowest_dist <= 25 then + local velocity = 4 + -- Stronghold is close: Fly directly to stronghold and take Y into account. + dir = vector.normalize(vector.direction(origin, closest_stronghold.pos)) + obj:setvelocity({x=dir.x*velocity, y=dir.y*velocity, z=dir.z*velocity}) + else + local velocity = 12 + -- Don't care about Y if stronghold is still far away. + -- Fly to direction of X/Z, and always upwards so it can be seen easily. + local o = {x=origin.x, y=0, z=origin.z} + local s = {x=closest_stronghold.pos.x, y=0, z=closest_stronghold.pos.z} + dir = vector.normalize(vector.direction(o, s)) + obj:setacceleration({x=dir.x*-3, y=4, z=dir.z*-3}) + obj:setvelocity({x=dir.x*velocity, y=3, z=dir.z*velocity}) + end + + + if not is_creative then + itemstack:take_item() + end + return itemstack + end, +}) + +minetest.register_craft({ + type = "shapeless", + output = "mcl_end:ender_eye", + recipe = {"mcl_mobitems:blaze_powder", "mcl_throwing:ender_pearl"}, +}) \ No newline at end of file diff --git a/mods/ITEMS/mcl_end/init.lua b/mods/ITEMS/mcl_end/init.lua index 7d8d8d0eda..6b76dfcde6 100644 --- a/mods/ITEMS/mcl_end/init.lua +++ b/mods/ITEMS/mcl_end/init.lua @@ -1,627 +1,4 @@ --- Nodes -minetest.register_node("mcl_end:end_stone", { - description = "End Stone", - _doc_items_longdesc = doc.sub.items.temp.build, - tiles = {"mcl_end_end_stone.png"}, - stack_max = 64, - groups = {pickaxey=1, building_block=1, material_stone=1}, - sounds = mcl_sounds.node_sound_stone_defaults(), - _mcl_blast_resistance = 45, - _mcl_hardness = 3, -}) - -minetest.register_node("mcl_end:end_bricks", { - description = "End Stone Bricks", - _doc_items_longdesc = doc.sub.items.temp.build, - tiles = {"mcl_end_end_bricks.png"}, - is_ground_content = false, - stack_max = 64, - groups = {pickaxey=1, building_block=1, material_stone=1}, - sounds = mcl_sounds.node_sound_stone_defaults(), - _mcl_blast_resistance = 4, - _mcl_hardness = 0.8, -}) - -minetest.register_node("mcl_end:purpur_block", { - description = "Purpur Block", - _doc_items_longdesc = doc.sub.items.temp.build, - tiles = {"mcl_end_purpur_block.png"}, - is_ground_content = false, - stack_max = 64, - groups = {pickaxey=1, building_block=1, material_stone=1}, - sounds = mcl_sounds.node_sound_stone_defaults(), - _mcl_blast_resistance = 30, - _mcl_hardness = 1.5, -}) - -minetest.register_node("mcl_end:purpur_pillar", { - description = "Purpur Pillar", - _doc_items_longdesc = doc.sub.items.temp.build, - stack_max = 64, - paramtype2 = "facedir", - is_ground_content = false, - on_place = mcl_util.rotate_axis, - tiles = {"mcl_end_purpur_pillar_top.png", "mcl_end_purpur_pillar_top.png", "mcl_end_purpur_pillar.png"}, - groups = {pickaxey=1, building_block=1, material_stone=1}, - sounds = mcl_sounds.node_sound_stone_defaults(), - _mcl_blast_resistance = 30, - _mcl_hardness = 1.5, -}) - -minetest.register_node("mcl_end:end_rod", { - description = "End Rod", - _doc_items_longdesc = "End rods are decorational light sources.", - tiles = { - "mcl_end_end_rod_top.png", - "mcl_end_end_rod_bottom.png", - "mcl_end_end_rod_side.png", - "mcl_end_end_rod_side.png", - "mcl_end_end_rod_side.png", - "mcl_end_end_rod_side.png", - }, - drawtype = "nodebox", - is_ground_content = false, - paramtype = "light", - paramtype2 = "facedir", - light_source = 14, - sunlight_propagates = true, - groups = { dig_immediate=3, deco_block=1, destroy_by_lava_flow=1, }, - node_box = { - type = "fixed", - fixed = { - {-0.125, -0.5, -0.125, 0.125, -0.4375, 0.125}, -- Base - {-0.0625, -0.4375, -0.0625, 0.0625, 0.5, 0.0625}, -- Rod - }, - }, - selection_box = { - type = "fixed", - fixed = { - {-0.125, -0.5, -0.125, 0.125, 0.5, 0.125}, -- Base - }, - }, - collision_box = { - type = "fixed", - fixed = { - {-0.125, -0.5, -0.125, 0.125, 0.5, 0.125}, -- Base - }, - }, - on_place = function(itemstack, placer, pointed_thing) - if pointed_thing.type ~= "node" then - return itemstack - end - - local p0 = pointed_thing.under - local p1 = pointed_thing.above - local param2 = 0 - - local placer_pos = placer:getpos() - if placer_pos then - local dir = { - x = p1.x - placer_pos.x, - y = p1.y - placer_pos.y, - z = p1.z - placer_pos.z - } - param2 = minetest.dir_to_facedir(dir) - end - - if p0.y - 1 == p1.y then - param2 = 20 - elseif p0.x - 1 == p1.x then - param2 = 16 - elseif p0.x + 1 == p1.x then - param2 = 12 - elseif p0.z - 1 == p1.z then - param2 = 8 - elseif p0.z + 1 == p1.z then - param2 = 4 - end - - return minetest.item_place(itemstack, placer, pointed_thing, param2) - end, - - sounds = mcl_sounds.node_sound_glass_defaults(), - _mcl_blast_resistance = 0, -}) - -minetest.register_node("mcl_end:dragon_egg", { - description = "Dragon Egg", - _doc_items_longdesc = "A dragon egg is a decorational item which can be placed.", - tiles = { - "mcl_end_dragon_egg.png", - "mcl_end_dragon_egg.png", - "mcl_end_dragon_egg.png", - "mcl_end_dragon_egg.png", - "mcl_end_dragon_egg.png", - "mcl_end_dragon_egg.png", - }, - drawtype = "nodebox", - is_ground_content = false, - paramtype = "light", - light_source = 1, - node_box = { - type = "fixed", - fixed = { - {-0.375, -0.5, -0.375, 0.375, -0.4375, 0.375}, - {-0.5, -0.4375, -0.5, 0.5, -0.1875, 0.5}, - {-0.4375, -0.1875, -0.4375, 0.4375, 0, 0.4375}, - {-0.375, 0, -0.375, 0.375, 0.125, 0.375}, - {-0.3125, 0.125, -0.3125, 0.3125, 0.25, 0.3125}, - {-0.25, 0.25, -0.25, 0.25, 0.3125, 0.25}, - {-0.1875, 0.3125, -0.1875, 0.1875, 0.375, 0.1875}, - {-0.125, 0.375, -0.125, 0.125, 0.4375, 0.125}, - {-0.0625, 0.4375, -0.0625, 0.0625, 0.5, 0.0625}, - } - }, - selection_box = { - type = "regular", - }, - groups = {handy=1, falling_node = 1, deco_block = 1, not_in_creative_inventory = 1, dig_by_piston = 1 }, - sounds = mcl_sounds.node_sound_stone_defaults(), - _mcl_blast_resistance = 45, - _mcl_hardness = 3, - -- TODO: Make dragon egg teleport on punching -}) - --- Eye of ender -minetest.register_entity("mcl_end:ender_eye", { - physical = false, - textures = {"mcl_end_ender_eye.png"}, - visual_size = {x=1.5, y=1.5}, - collisionbox = {0,0,0,0,0,0}, - - -- Save and restore age - get_staticdata = function(self) - return tostring(self._age) - end, - on_activate = function(self, staticdata, dtime_s) - local age = tonumber(staticdata) - if type(age) == "number" then - self._age = age - if self._age >= 2 then - self._phase = 1 - else - self._phase = 0 - end - end - end, - - on_step = function(self, dtime) - self._age = self._age + dtime - if self._age >= 3 then - -- End of life - local r = math.random(1,5) - if r == 1 or minetest.settings:get_bool("creative_mode") then - -- 20% chance to get destroyed completely. - -- 100% if in Creative Mode - self.object:remove() - return - else - -- 80% to drop as an item - local pos = self.object:get_pos() - local v = self.object:getvelocity() - self.object:remove() - local item = minetest.add_item(pos, "mcl_end:ender_eye") - item:setvelocity(v) - return - end - elseif self._age >= 2 then - if self._phase == 0 then - self._phase = 1 - -- Stop the eye and wait for another second. - -- The vertical speed changes are just eye candy. - self.object:setacceleration({x=0, y=-3, z=0}) - self.object:setvelocity({x=0, y=self.object:getvelocity().y*0.2, z=0}) - end - else - -- Fly normally and generate particles - local pos = self.object:get_pos() - pos.x = pos.x + math.random(-1, 1)*0.5 - pos.y = pos.y + math.random(-1, 0)*0.5 - pos.z = pos.z + math.random(-1, 1)*0.5 - minetest.add_particle({ - pos = pos, - texture = "mcl_particles_teleport.png", - expirationtime = 1, - velocity = {x=math.random(-1, 1)*0.1, y=math.random(-30, 0)*0.1, z=math.random(-1, 1)*0.1}, - acceleration = {x=0, y=0, z=0}, - size = 2.5, - }) - end - end, - - _age = 0, -- age in seconds - _phase = 0, -- phase 0: flying. phase 1: idling in mid air, about to drop or shatter -}) - -minetest.register_craftitem("mcl_end:ender_eye", { - description = "Eye of Ender", - _doc_items_longdesc = "This item is used to locate End portal shrines in the Overworld and to activate End portals." .. "\n" .. "NOTE: The End dimension is currently incomplete and boring.", - _doc_items_usagehelp = "Use the attack key to release the eye of ender. It will rise and fly in the horizontal direction of the closest end portal shrine. If you're very close, the eye of ender will take the direct path to the End portal shrine instead. After a few seconds, it stops. It may drop as an item, but there's a 20% chance it shatters." .. "\n" .. "To activate an End portal, eyes of ender need to be placed into each block of an intact End portal frame.", - wield_image = "mcl_end_ender_eye.png", - inventory_image = "mcl_end_ender_eye.png", - stack_max = 64, - -- Throw eye of ender to make it fly to the closest stronghold - on_use = function(itemstack, user, pointed_thing) - if user == nil then - return - end - local origin = user:get_pos() - origin.y = origin.y + 1.5 - local strongholds = mcl_structures.get_registered_structures("stronghold") - local dim = mcl_worlds.pos_to_dimension(origin) - local is_creative = minetest.settings:get_bool("creative_mode") - - -- Just drop the eye of ender if there are no strongholds - if #strongholds <= 0 or dim ~= "overworld" then - if not is_creative then - minetest.item_drop(ItemStack("mcl_end:ender_eye"), user, user:get_pos()) - itemstack:take_item() - end - return itemstack - end - - -- Find closest stronghold. - -- Note: Only the horizontal axes are taken into account. - local closest_stronghold - local lowest_dist - for s=1, #strongholds do - local h_pos = table.copy(strongholds[s].pos) - local h_origin = table.copy(origin) - h_pos.y = 0 - h_origin.y = 0 - local dist = vector.distance(h_origin, h_pos) - if not closest_stronghold then - closest_stronghold = strongholds[s] - lowest_dist = dist - else - if dist < lowest_dist then - closest_stronghold = strongholds[s] - lowest_dist = dist - end - end - end - - -- Throw it! - local obj = minetest.add_entity(origin, "mcl_end:ender_eye") - local dir - - if lowest_dist <= 25 then - local velocity = 4 - -- Stronghold is close: Fly directly to stronghold and take Y into account. - dir = vector.normalize(vector.direction(origin, closest_stronghold.pos)) - obj:setvelocity({x=dir.x*velocity, y=dir.y*velocity, z=dir.z*velocity}) - else - local velocity = 12 - -- Don't care about Y if stronghold is still far away. - -- Fly to direction of X/Z, and always upwards so it can be seen easily. - local o = {x=origin.x, y=0, z=origin.z} - local s = {x=closest_stronghold.pos.x, y=0, z=closest_stronghold.pos.z} - dir = vector.normalize(vector.direction(o, s)) - obj:setacceleration({x=dir.x*-3, y=4, z=dir.z*-3}) - obj:setvelocity({x=dir.x*velocity, y=3, z=dir.z*velocity}) - end - - - if not is_creative then - itemstack:take_item() - end - return itemstack - end, -}) - -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}, - } -} - -minetest.register_node("mcl_end:chorus_flower", { - description = "Chorus Flower", - 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 - return minetest.item_place(itemstack, placer, pointed_thing) - else - return itemstack - end - end, - _mcl_blast_resistance = 2, - _mcl_hardness = 0.4, -}) - -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 - 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 - - local grown = false - 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 >= 5 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 - if not grown then - minetest.set_node(pos, {name = "mcl_end:chorus_flower_dead"}) - end - end - end, -}) - -minetest.register_node("mcl_end:chorus_flower_dead", { - description = "Dead Chorus Flower", - 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", - 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, - -- TODO: Maybe improve nodebox a bit to look more “natural” - 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, -}) - --- Craftitems -minetest.register_craftitem("mcl_end:chorus_fruit", { - description = "Chorus Fruit", - _doc_items_longdesc = "Chorus fruits are the fruits of the chorus plant which is home to the End. They can be eaten to restore a few hunger points.", - wield_image = "mcl_end_chorus_fruit.png", - inventory_image = "mcl_end_chorus_fruit.png", - -- TODO: Teleport player - on_place = minetest.item_eat(4), - on_secondary_use = minetest.item_eat(4), - groups = { food = 2, 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 recipes -minetest.register_craft({ - output = "mcl_end:end_bricks 4", - recipe = { - {"mcl_end:end_stone", "mcl_end:end_stone"}, - {"mcl_end:end_stone", "mcl_end:end_stone"}, - } -}) - -minetest.register_craft({ - output = "mcl_end:purpur_block 4", - recipe = { - {"mcl_end:chorus_fruit_popped", "mcl_end:chorus_fruit_popped",}, - {"mcl_end:chorus_fruit_popped", "mcl_end:chorus_fruit_popped",}, - } -}) - -minetest.register_craft({ - output = "mcl_end:end_rod 4", - recipe = { - {"mcl_mobitems:blaze_rod"}, - {"mcl_end:chorus_fruit_popped"}, - }, -}) - -minetest.register_craft({ - type = "shapeless", - output = "mcl_end:ender_eye", - recipe = {"mcl_mobitems:blaze_powder", "mcl_throwing:ender_pearl"}, -}) - -minetest.register_craft({ - type = "cooking", - output = "mcl_end:chorus_fruit_popped", - recipe = "mcl_end:chorus_fruit", - cooktime = 10, -}) - +local basepath = minetest.get_modpath(minetest.get_current_modname()) +dofile(basepath.."/building.lua") +dofile(basepath.."/chorus_plant.lua") +dofile(basepath.."/eye_of_ender.lua")