2024-12-24 14:35:19 +01:00

745 lines
24 KiB
Lua
Raw Permalink 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.

local S, PS = minetest.get_translator("lzr_parrot_npc")
local FS = function(...) return minetest.formspec_escape(S(...)) end
local NS = function(s) return s end
local F = minetest.formspec_escape
local mod_storage = minetest.get_mod_storage()
lzr_parrot_npc = {}
--~ Parrot name. The players main companion and hint-giver. Named after gold
local GOLDIE_NAME = NS("Goldie the Parrot")
--~ Parrot name. The players main companion and hint-giver. Named after gold
local GOLDIE_NAME_SHORT = NS("Goldie")
--~ @1 is a parrot name
local SAYS = NS("@1 says:")
local HIDDEN_PARROT_NOT_FOUND = 0
local HIDDEN_PARROT_FOUND = 1
local HIDDEN_PARROT_FLY_HEIGHT = 20
local HIDDEN_PARROT_FLY_SPEED = 3
local HIDDEN_PARROT_FLY_DESPAWN_DISTANCE = 200
local HIDDEN_PARROT_FLY_DELAY = 2.0
-- Mininum and maximum delay in second between idle parrot animations
local PARROT_ANIMATION_DELAY_MIN = 2.0
local PARROT_ANIMATION_DELAY_MAX = 8.0
-- Check if parrot is stuck in solid block every this many seconds
local PARROT_STUCK_CHECK_INTERVAL = 1.0
-- Vertical fly speed of parrot to try to get unstuck
local PARROT_UNSTUCK_FLY_SPEED = 2.0
-- Vertical fly speed of parrot if falling
local PARROT_FALL_FLY_SPEED = -1.0
-- Pitch of parrot call sound when scorched
local PARROT_SCORCHED_CALL_PITCH = 0.8
-- Holds name and object of hidden parrot speaker who last spawned a speak dialog
local last_hidden_speaker = nil
local last_hidden_speaker_object = nil
local PARROT_ANIMS = {
idle = { frame_range = { x=0, y=0 }, frame_speed = 2 },
flap_small = { frame_range = { x=0, y=2 }, frame_speed = 6 },
flap_big = { frame_range = { x=2, y=4 }, frame_speed = 20 },
head_bounce = { frame_range = { x=4, y=6 }, frame_speed = 10 },
head_roll_right = { frame_range = { x=6, y=8 }, frame_speed = 2 },
head_roll_left = { frame_range = { x=8, y=10 }, frame_speed = 2 },
head_roll_leftright = { frame_range = { x=6, y=10 }, frame_speed = 2 },
}
local function make_call(to_player, pitch)
minetest.sound_play({name="lzr_parrot_npc_curr"}, {to_player=to_player:get_player_name(), pitch=pitch})
end
local speaker_portraits = {
goldie = "lzr_parrot_npc_goldie_portrait.png",
ruby = "lzr_parrot_npc_ruby_portrait.png",
emmy = "lzr_parrot_npc_emmy_portrait.png",
saphie = "lzr_parrot_npc_saphie_portrait.png",
garnie = "lzr_parrot_npc_garnie_portrait.png",
tuckie = "lzr_parrot_npc_tuckie_portrait.png",
dimey = "lzr_parrot_npc_dimey_portrait.png",
}
-- List of hidden parrots. These parrots, together with Goldie,
-- replicate the 7 laser colors.
local hidden_parrots = {
"ruby",
"emmy",
"saphie",
"garnie",
"tuckie",
"dimey",
}
local hidden_parrot_names = {
--~ Parrot name; named after ruby, a red gemstone
ruby = NS("Ruby the Hidden Parrot"),
--~ Parrot name; named after emerald, a green gemstone
emmy = NS("Emmy the Hidden Parrot"),
--~ Parrot name; named after saphire, a blue gemstone
saphie = NS("Saphie the Hidden Parrot"),
--~ Parrot name; named after garnet, a magenta gemstone
garnie = NS("Garnie the Hidden Parrot"),
--~ Parrot name; named after turquoise, a gemstone
tuckie = NS("Tuckie the Hidden Parrot"),
--~ Parrot name; named after diamond
dimey = NS("Dimey the Hidden Parrot"),
}
local hidden_parrot_names_short = {
--~ Parrot name; named after ruby, a red gemstone
ruby = NS("Ruby"),
--~ Parrot name; named after emerald, a green gemstone
emmy = NS("Emmy"),
--~ Parrot name; named after saphire, a blue gemstone
saphie = NS("Saphie"),
--~ Parrot name; named after garnet, a magenta gemstone
garnie = NS("Garnie"),
--~ Parrot name; named after turquoise, a gemstone
tuckie = NS("Tuckie"),
--~ Parrot name; named after diamond
dimey = NS("Dimey"),
}
lzr_parrot_npc.get_hidden_parrot_name = function(num)
return hidden_parrots[num]
end
lzr_parrot_npc.was_hidden_parrot_found = function(parrot_name)
local found = mod_storage:get_int("lzr_parrot_npc:hidden_parrot_"..parrot_name)
if found == HIDDEN_PARROT_FOUND then
return true
else
return false
end
end
lzr_parrot_npc.were_all_hidden_parrots_found = function()
for p=1, #hidden_parrots do
if not lzr_parrot_npc.was_hidden_parrot_found(hidden_parrots[p]) then
return false
end
end
return true
end
lzr_parrot_npc.count_found_hidden_parrots = function()
local found = 0
for p=1, #hidden_parrots do
if lzr_parrot_npc.was_hidden_parrot_found(hidden_parrots[p]) then
found = found + 1
end
end
return found
end
lzr_parrot_npc.speak = function(player, message, speaker, is_scorched)
local portrait
if is_scorched then
portrait = "lzr_parrot_npc_scorched_portrait.png"
else
portrait = speaker_portraits[speaker]
end
local title
if speaker == "goldie" then
title = FS(SAYS, S(GOLDIE_NAME))
else
title = FS(SAYS, S(hidden_parrot_names[speaker]))
end
local form = "formspec_version[7]size[10,5.5]"..
"box[0,0;10,0.8;#0000004f]"..
"label[0.4,0.4;"..title.."]"..
"box[0.5,1;2.0,3;#0000002f]"..
"image[0.75,1;1.5,3;"..portrait.."]"..
"box[3,1;6.5,3;#ffffff1f]"..
"textarea[3,1;6.5,3;;;"..F(message).."]"..
"button_exit[3.5,4.4;3,0.8;ok;"..FS("OK").."]"
minetest.show_formspec(player:get_player_name(), "lzr_parrot_npc:speech", form)
local pitch
if is_scorched then
pitch = PARROT_SCORCHED_CALL_PITCH
end
make_call(player, pitch)
end
-- Make parrot face player
local face_player = function(parrot, player)
local papos = parrot.object:get_pos()
local plpos = player:get_pos()
papos.y = 0
plpos.y = 0
local dir = vector.direction(papos, plpos)
local yaw = minetest.dir_to_yaw(dir)
parrot.object:set_yaw(yaw)
end
local texture_repeat = function(texture)
local textures = {}
for t=1, 10 do
table.insert(textures, texture)
end
return textures
end
-- Make the given parrot object react to player.
-- Shows associated text of current level (if present),
-- show
local react = function(parrot, player)
if player and player:is_player() then
local state = lzr_gamestate.get_state()
local spoken = false
if parrot._scorched then
--~ Parrot speech indicating a parrot is coughing after an explosion
lzr_parrot_npc.speak(player, S("*cough* *cough*"), "goldie", true)
spoken = true
elseif state == lzr_gamestate.LEVEL or state == lzr_gamestate.LEVEL_COMPLETE then
local speeches = lzr_levels.get_npc_texts()
if speeches and speeches.goldie and speeches.goldie ~= "" then
lzr_parrot_npc.speak(player, speeches.goldie, "goldie")
spoken = true
end
elseif state == lzr_gamestate.MENU then
local treasures = lzr_levels.count_total_collected_treasures()
local text
if treasures == 0 then
text = S("Im sad because we dont have any treasures.")
else
text = PS("We have @1 gold block in our possession.", "We have @1 gold blocks in our possession.", treasures, treasures)
end
lzr_parrot_npc.speak(player, text, "goldie")
spoken = true
end
if not spoken then
make_call(player)
end
face_player(parrot, player)
-- Bounce head
if parrot._animation_timer > 0.5 and not parrot._falling and not parrot._unstucking then
local anim = PARROT_ANIMS.head_bounce
parrot.object:set_animation(anim.frame_range, anim.frame_speed, 0, false)
parrot._animation_timer = 0
parrot._next_animation_at = math.random(PARROT_ANIMATION_DELAY_MIN, PARROT_ANIMATION_DELAY_MAX)
end
end
end
local pos_needs_unstuck = function(pos)
local node = minetest.get_node(pos)
local def = minetest.registered_nodes[node.name]
if def and def.walkable and minetest.get_item_group(node.name, "takable") ~= 0 then
local apos = vector.offset(pos, 0, 1, 0)
local anode = minetest.get_node(apos)
local adef = minetest.registered_nodes[anode.name]
if adef and adef.walkable then
if minetest.get_item_group(anode.name, "takable") ~= 0 then
return true
else
return false
end
else
return true
end
else
return false
end
end
local is_on_solid = function(pos)
local bpos = vector.offset(pos, 0, -0.05, 0)
local node = minetest.get_node(bpos)
local def = minetest.registered_nodes[node.name]
if not def or def.walkable then
return true
else
return false
end
end
local parrot_check_stuck = function(self)
if not self._unstucking and not self._falling then
local pos = self.object:get_pos()
if pos_needs_unstuck(pos) then
self._unstucking = true
self._falling = false
self.object:set_velocity({x=0, y=PARROT_UNSTUCK_FLY_SPEED, z=0})
local anim = PARROT_ANIMS.flap_big
self.object:set_animation(anim.frame_range, anim.frame_speed, 0, true)
elseif not is_on_solid(pos) then
self._falling = true
self._unstucking = false
self.object:set_velocity({x=0, y=PARROT_FALL_FLY_SPEED, z=0})
local anim = PARROT_ANIMS.flap_big
self.object:set_animation(anim.frame_range, anim.frame_speed, 0, true)
end
self._stuck_check_timer = 0
end
end
local parrot_activate = function(self)
self.object:set_armor_groups({immortal=1})
self._next_animation_at = math.random(PARROT_ANIMATION_DELAY_MIN, PARROT_ANIMATION_DELAY_MAX)
end
local parrot_activate_goldie = function(self)
parrot_activate(self)
self.object:set_properties({
infotext = S(GOLDIE_NAME_SHORT)
})
end
local parrot_step_idle = function(self, dtime)
if self._scorched then
self._scorched_timer = self._scorched_timer + dtime
if self._scorched_timer >= self._unscorched_time then
self.object:set_properties({
textures = self._base_textures,
})
self._scorched = false
end
end
self._stuck_check_timer = self._stuck_check_timer + dtime
if self._stuck_check_timer >= PARROT_STUCK_CHECK_INTERVAL then
parrot_check_stuck(self)
end
if self._unstucking then
local pos = self.object:get_pos()
if not pos_needs_unstuck(pos) then
self.object:set_velocity({x=0, y=0, z=0})
self.object:set_animation(PARROT_ANIMS.idle.frame_range, PARROT_ANIMS.idle.frame_speed, 0, false)
self._falling = false
self._unstucking = false
end
return
elseif self._falling then
local pos = self.object:get_pos()
if is_on_solid(pos) then
self.object:set_velocity({x=0, y=0, z=0})
self.object:set_animation(PARROT_ANIMS.idle.frame_range, PARROT_ANIMS.idle.frame_speed, 0, false)
self._falling = false
self._unstucking = false
end
return
end
-- Play random animation in random intervals
self._animation_timer = self._animation_timer + dtime
if self._animation_timer >= self._next_animation_at then
local idle_anims = {
"flap_small",
"flap_small",
"flap_small",
"head_bounce",
"head_roll_right",
"head_roll_right",
"head_roll_right",
"head_roll_left",
"head_roll_left",
"head_roll_left",
}
local a = math.random(1, #idle_anims)
local anim = PARROT_ANIMS[idle_anims[a]]
self.object:set_animation(anim.frame_range, anim.frame_speed, 0, false)
self._animation_timer = 0
self._next_animation_at = math.random(PARROT_ANIMATION_DELAY_MIN, PARROT_ANIMATION_DELAY_MAX)
end
end
local parrot_scorch = function(self, time)
self.object:set_properties({
textures=texture_repeat("lzr_parrot_npc_scorched.png"),
})
self._scorched = true
self._scorched_timer = 0
self._unscorched_time = time
end
minetest.register_entity("lzr_parrot_npc:parrot", {
initial_properties = {
visual = "mesh",
mesh = "lzr_parrot_npc_parrot.gltf",
visual_size = { x=1, y=1, z=1 },
textures = texture_repeat("lzr_parrot_npc_goldie.png"),
backface_culling = false,
static_save = false,
physical = false,
collide_with_objects = false,
selectionbox = {
-0.15, 0, -0.15, 0.15, 0.65, 0.3, rotate = true,
},
},
_animation_timer = 0,
_next_animation_at = nil,
_stuck_check_timer = 0,
_unstucking = false,
_scorched = false,
_scorched_timer = nil,
_unchorched_time = nil,
_scorch = parrot_scorch,
_base_textures = texture_repeat("lzr_parrot_npc_goldie.png"),
on_activate = parrot_activate_goldie,
on_step = parrot_step_idle,
on_rightclick = function(self, clicker)
react(self, clicker)
end,
on_punch = function(self, puncher)
react(self, puncher)
end,
})
local mark_hidden_parrot_as_found = function(player, parrot_name)
local found = mod_storage:get_int("lzr_parrot_npc:hidden_parrot_"..parrot_name)
if found == HIDDEN_PARROT_FOUND then
if lzr_parrot_npc.were_all_hidden_parrots_found() then
lzr_menu.place_painting("parrot_finder")
end
return false
else
mod_storage:set_int("lzr_parrot_npc:hidden_parrot_"..parrot_name, HIDDEN_PARROT_FOUND)
lzr_menu.spawn_hidden_parrot(parrot_name)
minetest.log("action", "[lzr_parrot_npc] Player found hidden parrot '"..parrot_name.."'")
if lzr_parrot_npc.were_all_hidden_parrots_found() then
lzr_menu.place_painting("parrot_finder")
end
return true
end
end
local react_hidden = function(parrot, player)
if player and player:is_player() then
local hidden_parrot_identity = parrot._hidden_id
local call_pitch
if hidden_parrot_identity and mark_hidden_parrot_as_found(player, hidden_parrot_identity) then
local found = lzr_parrot_npc.count_found_hidden_parrots()
local text
-- First found parrot
if found == 1 then
local friends = {}
for h=1, #hidden_parrots do
if hidden_parrots[h] ~= hidden_parrot_identity then
table.insert(friends, S(hidden_parrot_names_short[hidden_parrots[h]]))
end
end
local f1, f2, f3, f4, f5 = friends[1], friends[2], friends[3], friends[4], friends[5]
text = S("Hey, you found my secret hideout! Now its useless, such a shame …").."\n"..
S("Ive got an idea. Let me come with you, okay?").."\n\n"..
S("I have five friends scattered around the world, I want to meet them again. Their names are @1, @2, @3, @4 and @5.", f1, f2, f3, f4, f5)
-- Last found parrot
elseif found == #hidden_parrots then
text = S("Incredible! Its you! Ive heard a lot from your journeys. Now the whole gang is re-united!").."\n"
if hidden_parrot_identity == "tuckie" then
text = text .. S("Ive got a beautiful portrait of myself. You can have it. How nice is that?")
else
text = text .. S("Ive found a beautiful portrait of our friend Tuckie. You can have it. How nice is that?")
end
-- Found parrot, neither first or last
else
local remaining = #hidden_parrots - found
text = S("You found my secret hiding spot!").."\n"..
PS("Only @1 parrot is still in hiding.", "There are still @1 hidden parrots left to go.", remaining, remaining)
end
last_hidden_speaker = hidden_parrot_identity
last_hidden_speaker_object = parrot
lzr_parrot_npc.speak(player, text, hidden_parrot_identity, parrot._scorched == true)
face_player(parrot, player)
return
else
make_call(player, call_pitch)
end
if parrot._flying_home_phase == 0 then
face_player(parrot, player)
-- Bounce head
if parrot._animation_timer > 0.5 then
local anim = PARROT_ANIMS.head_bounce
parrot.object:set_animation(anim.frame_range, anim.frame_speed, 0, false)
parrot._animation_timer = 0
parrot._next_animation_at = math.random(PARROT_ANIMATION_DELAY_MIN, PARROT_ANIMATION_DELAY_MAX)
end
end
end
end
local hidden_parrot_fly_home = function(parrot)
if parrot._flying_home_phase < 1 then
parrot._flying_home_phase = 1
parrot._fly_timer = 0
minetest.log("action", "[lzr_parrot_npc] Hidden parrot will fly home soon: "..tostring(last_hidden_speaker))
end
end
minetest.register_on_player_receive_fields(function(player, formname, fields)
if formname == "lzr_parrot_npc:speech" and fields.quit and last_hidden_speaker then
local parrot = last_hidden_speaker_object
hidden_parrot_fly_home(parrot)
end
end)
local parrot_step_hidden = function(self, dtime)
if self._flying_home_phase ~= 0 then
if self._scorched and self._base_textures then
self._scorched_timer = self._scorched_timer + dtime
if self._scorched_timer >= self._unscorched_time then
self.object:set_properties({
textures = self._base_textures,
})
self._scorched = false
end
end
-- Phase 1: Wait for a short time before lifting off
if self._flying_home_phase == 1 then
self._fly_timer = self._fly_timer + dtime
if self._fly_timer > HIDDEN_PARROT_FLY_DELAY then
self._flying_home_phase = 2
self._fly_start_pos = self.object:get_pos()
self.object:set_velocity({x=0,y=3,z=0})
local anim = PARROT_ANIMS.flap_big
self.object:set_animation(anim.frame_range, anim.frame_speed, 0, true)
minetest.log("action", "[lzr_parrot_npc] Hidden parrot lifts off: "..tostring(self._hidden_id))
end
-- Phase 2: Liftoff! Fly upwards until we reach a certain height
elseif self._flying_home_phase == 2 then
local pos = self.object:get_pos()
local fly_height = pos.y - self._fly_start_pos.y
if fly_height >= HIDDEN_PARROT_FLY_HEIGHT then
self.object:set_velocity(vector.zero())
self._flying_home_phase = 3
local hpos = table.copy(pos)
hpos.y = 0
local spos = table.copy(lzr_globals.MENU_SHIP_POS)
spos.y = 0
local dir = vector.direction(hpos, spos)
local vel = vector.multiply(dir, HIDDEN_PARROT_FLY_SPEED)
self.object:set_velocity(vel)
local yaw = minetest.dir_to_yaw(dir)
self.object:set_yaw(yaw)
minetest.log("action", "[lzr_parrot_npc] Hidden parrot flies home: "..tostring(self._hidden_id))
return
end
-- Phase 3: Fly horizontally towards the main ship (rough direction)
elseif self._flying_home_phase == 3 then
local pos = self.object:get_pos()
if vector.distance(pos, self._fly_start_pos) > HIDDEN_PARROT_FLY_DESPAWN_DISTANCE then
minetest.log("action", "[lzr_parrot_npc] Hidden parrot has flown enough and despawns: "..tostring(self._hidden_id))
self.object:remove()
return
end
end
else
parrot_step_idle(self, dtime)
end
end
-- Hidden Parrot NPC. One of the game's hidden parrots.
-- When interacted with, the parrot counts as found.
minetest.register_entity("lzr_parrot_npc:hidden_parrot", {
initial_properties = {
visual = "mesh",
mesh = "lzr_parrot_npc_parrot.gltf",
visual_size = { x=1, y=1, z=1 },
textures = {
texture_repeat("lzr_parrot_npc_ruby.png"),
},
static_save = false,
physical = false,
collide_with_objects = false,
selectionbox = {
-0.15, 0, -0.15, 0.15, 0.65, 0.3, rotate = true,
},
backface_culling = false,
},
_animation_timer = 0,
_next_animation_at = nil,
_stuck_check_timer = 0,
_unstucking = false,
_scorched = false,
_scorched_timer = nil,
_unchorched_time = nil,
_scorch = parrot_scorch,
_base_textures = texture_repeat("lzr_parrot_npc_ruby.png"),
_fly_start_pos = nil,
_fly_timer = 0,
_flying_home_phase = 0,
-- Hidden parrot ID
_hidden_id = nil,
-- Initialize hidden parrot with the given ID (parrot identifier).
-- MUST be called right after spawning entity.
_init = function(self, hidden_id)
self._hidden_id = hidden_id
local name = S(hidden_parrot_names_short[hidden_id])
local textures = texture_repeat("lzr_parrot_npc_"..hidden_id..".png")
self.object:set_properties({
textures = textures,
infotext = name,
})
self._base_textures = textures
end,
on_activate = parrot_activate,
on_step = parrot_step_hidden,
on_rightclick = function(self, clicker)
react_hidden(self, clicker)
end,
on_punch = function(self, puncher)
react_hidden(self, puncher)
end,
})
minetest.register_node("lzr_parrot_npc:stand", {
description = S("Parrot Stand"),
drawtype = "nodebox",
paramtype = "light",
paramtype2 = "4dir",
sunlight_propagates = true,
is_ground_content = false,
node_box = {
type = "fixed",
fixed = {
{ -0.5, -0.5, -0.5, 0.5, 1/16, 0.5 }, -- base (slab-like)
{ -1/16, 1/16, -1/16, 1/16, 7/16, 1/16 }, -- vertical stick
{ -6/16, 7/16, -1/16, 6/16, 0.5, 1/16 }, -- parrot stick
},
},
tiles = { { name = "default_wood.png", align_style = "world" } },
groups = { breakable = 1 },
sounds = lzr_sounds.node_sound_wood_defaults(),
})
minetest.register_node("lzr_parrot_npc:parrot_spawner", {
--~ A block that spawns a normal parrot
description = S("Parrot Spawner"),
_tt_help = S("Goldie the Parrot will spawn here").."\n"..
S("(only one per level allowed)"),
tiles = {
{ name = "lzr_parrot_npc_goldie_spawner_top.png", backface_culling = true },
{ name = "lzr_parrot_npc_goldie_spawner_bottom.png", backface_culling = true },
{ name = "lzr_parrot_npc_goldie_spawner_left.png", backface_culling = true },
{ name = "lzr_parrot_npc_goldie_spawner_right.png", backface_culling = true },
{ name = "lzr_parrot_npc_goldie_spawner_back.png", backface_culling = true },
{ name = "lzr_parrot_npc_goldie_spawner_front.png", backface_culling = true },
},
paramtype2 = "degrotate",
drawtype = "mesh",
mesh = "lzr_parrot_npc_cube.obj",
selection_box = {
type = "fixed",
fixed = { -0.4, -0.4, -0.4, 0.4, 0.4, 0.4 },
},
visual_scale = 0.8,
wield_scale = { x=0.8, y=0.8, z=0.8 },
paramtype = "light",
sunlight_propagates = true,
walkable = false,
groups = { breakable = 1, rotatable = 3, spawner = 1 },
use_texture_alpha = "clip",
sounds = {
_rotate = "",
},
})
minetest.register_node("lzr_parrot_npc:hidden_parrot_spawner", {
--~ A block that spawns a hidden parrot
description = S("Hidden Parrot Spawner"),
_tt_help = S("A hidden parrot will spawn here"),
tiles = {
{ name = "lzr_parrot_npc_hidden_parrot_spawner_top.png", backface_culling = true },
{ name = "lzr_parrot_npc_hidden_parrot_spawner_bottom.png", backface_culling = true },
{ name = "lzr_parrot_npc_hidden_parrot_spawner_left.png", backface_culling = true },
{ name = "lzr_parrot_npc_hidden_parrot_spawner_right.png", backface_culling = true },
{ name = "lzr_parrot_npc_hidden_parrot_spawner_back.png", backface_culling = true },
{ name = "lzr_parrot_npc_hidden_parrot_spawner_front.png", backface_culling = true },
},
paramtype2 = "color4dir",
drawtype = "mesh",
palette = "lzr_parrot_npc_hidden_parrot_spawner_palette.png",
mesh = "lzr_parrot_npc_cube.obj",
selection_box = {
type = "fixed",
fixed = { -0.4, -0.4, -0.4, 0.4, 0.4, 0.4 },
},
visual_scale = 0.8,
wield_scale = { x=0.8, y=0.8, z=0.8 },
paramtype = "light",
sunlight_propagates = true,
walkable = false,
-- hide the hidden parrot spawner ;)
groups = { breakable = 1, rotatable = 3, spawner = 1, not_in_creative_inventory = 1 },
use_texture_alpha = "clip",
sounds = {
_rotate = "",
},
drop = "lzr_parrot_npc:hidden_parrot_spawner",
})
function lzr_parrot_npc.clear_hidden_parrot_progress()
for h=1, #hidden_parrots do
mod_storage:set_int("lzr_parrot_npc:hidden_parrot_"..hidden_parrots[h], HIDDEN_PARROT_NOT_FOUND)
end
lzr_menu.remove_painting("parrot_finder")
minetest.log("action", "[lzr_parrot_npc] Hidden parrot progress was cleared")
end
local function spawn_menu_parrots()
for p=1, #hidden_parrots do
local parrot_name = hidden_parrots[p]
local found = mod_storage:get_int("lzr_parrot_npc:hidden_parrot_"..parrot_name)
if found == HIDDEN_PARROT_FOUND then
lzr_menu.spawn_hidden_parrot(parrot_name)
end
end
end
lzr_menu.register_on_ship_built(function()
spawn_menu_parrots()
end)
lzr_menu.register_on_ship_rebuilt(function()
spawn_menu_parrots()
if lzr_parrot_npc.were_all_hidden_parrots_found() then
lzr_menu.place_painting("parrot_finder")
end
end)
lzr_menu.register_on_player_ship_enter(function()
spawn_menu_parrots()
end)
-- Notify parrots to instantly trigger unstuck algorithm when a node was placed
minetest.register_on_placenode(function(pos, node)
local def = minetest.registered_nodes[node.name]
if def and def.walkable then
local objs = minetest.get_objects_inside_radius(pos, 1)
for o=1, #objs do
local obj = objs[o]
local ent = obj:get_luaentity()
if ent and (ent.name == "lzr_parrot_npc:parrot" or ent.name == "lzr_parrot_npc:hidden_parrot") then
parrot_check_stuck(ent)
end
end
end
end)
-- Notify parrots to instantly trigger unstuck algorithm when a node was dug
minetest.register_on_dignode(function(pos, node)
local apos = vector.offset(pos, 0, 1, 0)
local objs = minetest.get_objects_inside_radius(apos, 1)
for o=1, #objs do
local obj = objs[o]
local ent = obj:get_luaentity()
if ent and (ent.name == "lzr_parrot_npc:parrot" or ent.name == "lzr_parrot_npc:hidden_parrot") then
parrot_check_stuck(ent)
end
end
end)