From 69aa04534a6229d77e8cb2d3de459770a1e1ae9c Mon Sep 17 00:00:00 2001 From: Sokomine Date: Sun, 10 Sep 2023 01:32:35 +0200 Subject: [PATCH] added an example npc that has no dependencies --- init.lua | 8 +- talking_npc.lua | 230 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 235 insertions(+), 3 deletions(-) create mode 100644 talking_npc.lua diff --git a/init.lua b/init.lua index 4b95231..558b673 100644 --- a/init.lua +++ b/init.lua @@ -4,16 +4,18 @@ local modpath = minetest.get_modpath("npc_talk")..DIR_DELIM npc_talk = {} --- register an example mob and a spawn egg -dofile(modpath .. "example_npc.lua") +-- a very basic NPC that doesn't require any other mods (apart from default) +dofile(modpath .. "talking_npc.lua") +-- register an example mob and a spawn egg (requires mobs_redo) +dofile(modpath .. "example_npc.lua") -- add textures from textures/npc_talk_main_TEXTURE_NAME.png for the example npc dofile(modpath .. "add_skins_and_capes.lua") -- demonstration that a node can be used for starting talking as well dofile(modpath .. "example_talk_sign.lua") --- make mobs_npc from mobs_redo ready to be talked to +-- make mobs_npc from mobs_redo ready to be talked to (requires mobs_npc) dofile(modpath .. "talk_to_mobs_npc.lua") -- mostly a demonstration for mobs_animal (allows to talk to white and red sheep and a cow) diff --git a/talking_npc.lua b/talking_npc.lua new file mode 100644 index 0000000..62fdf38 --- /dev/null +++ b/talking_npc.lua @@ -0,0 +1,230 @@ +-- 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, t = {} + for _, v in pairs(self) do + 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 + local t + for _,stat in pairs(tmp) do + 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 = itemstack:get_metadata() + 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 + + +-- 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", + } +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()")