npc_talk/talking_npc.lua
2023-09-10 22:06:01 +02:00

268 lines
9.0 KiB
Lua

-- based on my mobf_trader mod - but does not require any other mods
-- find out the right mesh; if the wrong one is used, the traders become invisible
npc_talk.talking_npc_mesh = "character.b3d";
-- 3darmor/wieldview is great
if( minetest.get_modpath( '3d_armor' )) then
npc_talk.talking_npc_mesh = "3d_armor_character.b3d";
end
npc_talk.talking_npc_texture = "character.png"
npc_talk.talking_npc_inventory_image = "character.png"
-- look at the player
npc_talk.look_at_player = function(self, player)
if(not(player) or not(self)) then
return
end
-- look at the player
local v1 = self.object:get_pos()
local v2 = player:get_pos()
local dx = v1.x - v2.x
local dz = v2.z - v1.z
self.object:set_yaw(math.atan2(dx, dz))
end
npc_talk.talking_npc_entity_prototype = {
-- so far, this is more or less the redefinition of the standard player model
physical = true,
collisionbox = {-0.30, 0.0,-0.30, 0.30,1.8,0.30},
visual = "mesh";
visual_size = {x=1, y=1, z=1},
mesh = npc_talk.talking_npc_mesh,
textures = {npc_talk.talking_npc_texture},
inventory_image = npc_talk.talking_npc_inventory_image,
description = 'Talking NPC (right-click to talk)',
-- the NPC needs an owner
owner = "",
armor_groups = {immortal=1},
hp_max = 100, -- just to be sure
-- this mob only has to stand around and talk to players
animation = {
stand_START = 0,
stand_END = 79,
sit_START = 81,
sit_END = 160,
sleep_START = 162,
sleep_END = 166,
walk_START = 168,
walk_END = 187,
mine_START = 189,
mine_END = 198,
walkmine_START = 200,
walkmine_END = 219,
},
animation_speed = 30,
get_staticdata = function(self)
return npc_talk.talking_npc_get_staticdata(self)
end,
on_activate = function(self, staticdata, dtime)
return npc_talk.talking_npc_on_activate(self, staticdata, dtime)
end,
-- actually talk to the NPC
on_rightclick = function(self, clicker)
npc_talk.look_at_player(self, clicker)
return yl_speak_up.mobs_on_rightclick(self, clicker)
end,
}
npc_talk.talking_npc_get_staticdata = function(self)
-- taken basicly from mobs_redo
local data = {}
for _, v in pairs(self) do
local t = type(v)
if( t ~= "function" and t ~= "nil" and t ~= "userdata" and _ ~= "object" and _ ~= "_cmi_components") then
data[_] = self[_]
end
end
return minetest.serialize(data)
end
npc_talk.talking_npc_on_activate = function(self, staticdata, dtime)
-- this is taken from mobs_redo and copies all staticdata to self
local tmp = minetest.deserialize(staticdata)
if tmp then
for _,stat in pairs(tmp) do
local t = type(stat)
if t ~= "function" and t ~= "nil" and t ~= "userdata" then
self[_] = stat
end
end
end
if(self.yl_speak_up) then
-- set texture
if(self.yl_speak_up.skin) then
local tex = self.yl_speak_up.skin
self.textures = {tex[1]}
end
-- set animation
if(self.yl_speak_up.animation) then
self.object:set_animation(self.yl_speak_up.animation)
end
-- set infotext
if(yl_speak_up.infotext) then
local i_text = ""
if(self.yl_speak_up.npc_name) then
i_text = i_text .. self.yl_speak_up.npc_name .. "\n"
end
if(self.yl_speak_up.npc_description) then
i_text = i_text .. self.yl_speak_up.npc_description .. "\n"
end
i_text = i_text .. yl_speak_up.infotext
self.infotext = i_text
end
end
-- set anything changed above
self.object:set_properties(self)
-- the NPC is subject to gravity but does not move around
self.object:set_velocity( {x=0, y= 0, z=0})
self.object:set_acceleration({x=0, y=-10, z=0})
-- show the nametag (or not - depending on the mob's configuration)
if(self.yl_speak_up) then
yl_speak_up.update_nametag(self)
end
end
-- the mob as entity
minetest.register_entity( "npc_talk:talking_npc", npc_talk.talking_npc_entity_prototype)
-- the mob as an item - carried in the inventory
-- If you want to add mobs with diffrent names/descriptions/inventory images/entities, just add your
-- own register_craftitem and use this as an example.
minetest.register_craftitem("npc_talk:talking_npc_item", {
name = "Talking NPC",
description = "Talking NPC. Place somewhere and right-click in order to talk to him.",
groups = {},
stack_max = 1,
inventory_image = npc_talk.talking_npc_inventory_image,
wield_image = npc_talk.talking_npc_inventory_image,
drawtype = "mesh",
mesh = npc_talk.talking_npc_mesh,
textures = {npc_talk.talking_npc_texture},
on_place = function(itemstack, placer, pointed_thing)
return npc_talk.talking_npc_on_place(itemstack, placer, pointed_thing)
end,
})
-- place the craftitem from your inventory to get the NPC entity in the world
npc_talk.talking_npc_on_place = function(itemstack, placer, pointed_thing)
-- taken from mobs_redo
local pos = pointed_thing.above
-- does existing on_rightclick function exist?
local under = minetest.get_node(pointed_thing.under)
local def = minetest.registered_nodes[under.name]
if(def and def.on_rightclick)then
return def.on_rightclick(pointed_thing.under, under, placer, itemstack, pointed_thing)
end
if(pos and not minetest.is_protected(pos, placer:get_player_name())) then
local mob = "npc_talk:talking_npc"
pos.y = pos.y + 1
local data_str = itemstack:get_metadata()
local data = minetest.deserialize(data_str)
local smob = minetest.add_entity(pos, mob, data)
local ent = smob and smob:get_luaentity()
if(not(ent)) then
return
end
ent.owner = placer:get_player_name()
ent.tamed = true
itemstack:take_item()
end
return itemstack
end
npc_talk.talking_npc_pick_up = function(self, player)
-- taken from/inspired by mobs_redo as well
if(not(self) or not(player:is_player()) or not(player:get_inventory())) then
return false
end
local pname = player:get_player_name()
if(not(minetest.check_player_privs(pname, "protection_bypass"))
and self.owner ~= pname) then
minetest.chat_send_player(pname, "This NPC is owned by "..tostring(self.owner or "?")..".")
return false
end
local mobname = "npc_talk:talking_npc_item"
if(not(player:get_inventory():room_for_item("main", mobname))) then
minetest.chat_send_player(pname, "You have not enough room in your inventory.")
return false
end
local stack = ItemStack(mobname)
local data = minetest.serialize(npc_talk.talking_npc_get_staticdata(self))
stack:set_metadata(data)
local inv = player:get_inventory()
if(inv:room_for_item("main", stack)) then
inv:add_item("main", stack)
else
minetest.add_item(player:get_pos(), stack)
end
self.object:remove()
return stack
end
-- add the necessary data for skin configuration etc.
npc_talk.enable_talk_to_talking_npc = function()
-- the following information is needed in order to handle skin changes
-- this model is used by mobs_npc
yl_speak_up.mesh_data["character.b3d"] = {
-- the first texture is the skin
texture_index = 1,
-- there is no support for capes or wielded items
can_show_wielded_items = false,
-- textures are applied directly
textures_to_skin = false,
-- this information needed so that the animation of the mob can be set (i.e. them sitting)
animation = {
-- {x = start_frame, y = end_frame, collisionbox}
stand_still = {x = 0, y = 0, collisionbox = {-0.3, 0.0, -0.3, 0.3, 1.7, 0.3}},
stand = {x = 0, y = 79, collisionbox = {-0.3, 0.0, -0.3, 0.3, 1.7, 0.3}},
sit = {x = 81, y = 160, collisionbox = {-0.3, 0.0, -0.3, 0.3, 1.0, 0.3}},
lay = {x = 162, y = 166, collisionbox = {-0.6, 0.0, -0.6, 0.6, 0.3, 0.6}},
walk = {x = 168, y = 187, collisionbox = {-0.3, 0.0, -0.3, 0.3, 1.7, 0.3}},
mine = {x = 189, y = 198, collisionbox = {-0.3, 0.0, -0.3, 0.3, 1.7, 0.3}},
walk_mine = {x = 200, y = 219, collisionbox = {-0.3, 0.0, -0.3, 0.3, 1.7, 0.3}},
},
}
-- list all textures that shall be available as skins for this particular npc:
-- the normal NPC (can be fed and tamed with bread)
yl_speak_up.mob_skins["npc_talk:talking_npc"] = {
npc_talk.talking_npc_texture,
-- at least add some variety - even if we have no textures
npc_talk.talking_npc_texture.."^[colorizehsl:0",
npc_talk.talking_npc_texture.."^[colorizehsl:60",
npc_talk.talking_npc_texture.."^[colorizehsl:120",
npc_talk.talking_npc_texture.."^[colorizehsl:180",
npc_talk.talking_npc_texture.."^[colorizehsl:-60",
npc_talk.talking_npc_texture.."^[colorizehsl:-120",
npc_talk.talking_npc_texture.."^[colorizehsl:-180",
}
yl_speak_up.add_on_rightclick_entry["npc_talk:talking_npc"] = {
text_if_true = "[Pick this NPC up.]",
text_if_false = "[Something wrent wrong. This is for picking the mob up.]",
condition = true,
execute_function = npc_talk.talking_npc_pick_up,
}
end
-- make sure the NPC can be configured regarding its skin
yl_speak_up.register_on_reload(npc_talk.enable_talk_to_talking_npc, "npc_talk.enable_talk_to_talking_npc()")