Actions: Added some fundamental movement actions.

Places: Added functions for adding and getting places, and for finding places of specific types. Added beds, chairs and chest as places.
NPC: Added places map and action queue.
This commit is contained in:
zorman2000 2016-12-18 12:32:39 -05:00
parent a2e38ab85f
commit 646b422082
6 changed files with 275 additions and 7 deletions

85
actions/actions.lua Normal file
View File

@ -0,0 +1,85 @@
-- Actions code for Advanced NPC by Zorman2000
---------------------------------------------------------------------------------------
-- Action functionality
---------------------------------------------------------------------------------------
-- The NPCs will be able to perform five fundamental actions that will allow
-- for them to perform any other kind of interaction in the world. These
-- fundamental actions are: place a node, dig a node, put items on an inventory,
-- take items from an inventory, find a node closeby (radius 3) and
-- walk a step on specific direction. These actions will be set on an action queue.
-- The queue will have the specific steps, in order, for the NPC to be able to do
-- something (example, go to a specific place and put a chest there). The
-- fundamental actions are added to the action queue to make a complete task for the NPC.
npc.actions = {}
function npc.actions.rotate(args)
local self = args.self
local dir = args.dir
local yaw = 0
self.rotate = 0
if dir == npc.direction.north then
yaw = 315
elseif dir == npc.direction.east then
yaw = 225
elseif dir == npc.direction.south then
yaw = 135
elseif dir == npc.direction.west then
yaw = 45
end
self.object:setyaw(yaw)
end
-- This function will make the NPC walk one step on a
-- specifc direction. One step means one node. It returns
-- true if it can move on that direction, and false if there is an obstacle
function npc.actions.walk_step(args)
local self = args.self
local dir = args.dir
local vel = {}
if dir == npc.direction.north then
vel = {x=0, y=0, z=1}
elseif dir == npc.direction.east then
vel = {x=1, y=0, z=0}
elseif dir == npc.direction.south then
vel = {x=0, y=0, z=-1}
elseif dir == npc.direction.west then
vel = {x=-1, y=0, z=0}
end
set_animation(self, "walk")
npc.rotate({self=self, dir=dir})
self.object:setvelocity(vel)
end
-- This action makes the NPC stand and remain like that
function npc.actions.stand(args)
local self = args.self
-- Stop NPC
self.object:setvelocity({x=0, y=0, z=0})
-- Set stand animation
set_animation(self, "stand")
end
-- This action makes the NPC sit on the node where it is
function npc.actions.sit(args)
local self = args.self
-- Stop NPC
self.object:setvelocity({x=0, y=0, z=0})
-- Set sit animation
self.object:set_animation({
x = npc.ANIMATION_SIT_START,
y = npc.ANIMATION_SIT_END},
self.animation.speed_normal, 0)
end
-- This action makes the NPC lay on the node where it is
function npc.actions.lay(args)
local self = args.self
-- Stop NPC
self.object:setvelocity({x=0, y=0, z=0})
-- Set sit animation
self.object:set_animation({
x = npc.ANIMATION_LAY_START,
y = npc.ANIMATION_LAY_END},
self.animation.speed_normal, 0)
end

82
actions/places.lua Normal file
View File

@ -0,0 +1,82 @@
-- Places code for Advanced NPC by Zorman2000
---------------------------------------------------------------------------------------
-- Places functionality
---------------------------------------------------------------------------------------
-- In addition, the NPCs need to know where some places are, and know
-- where there are nodes they can use. For example, they need to know where the
-- chest they use is located, both to walk to it and to use it. They also need
-- to know where the farm they work is located, or where the bed they sleep is.
-- Other mods have to be supported for this to work correctly, as there are
-- many sitting nodes, many beds, many tables, chests, etc. For now, by default,
-- support for default and cottages is going to be provided.
npc.places = {}
npc.places.nodes = {
BEDS = {
"beds:bed_bottom",
"beds:fancy_bed_bottom"
},
CHAIRS = {
"cottages:bench"
},
CHESTS = {
"default:chest",
"default:chest_locked"
}
}
npc.places.PLACE_TYPE = {
"OWN_BED",
"OWN_CHEST",
"HOUSE_CHAIR",
"HOUSE_TABLE",
"HOUSE_FURNACE",
"HOUSE_ENTRANCE"
}
function npc.places.add(self, place_name, place_type, pos)
self.places_map[place_name] = {type=place_type, pos=pos}
end
-- Adds a specific node to the NPC places, and modifies the
-- node metadata to identify the NPC as the owner. This allows
-- other NPCs to avoid to take this as their own.
function npc.places.add_owned(self, place_name, place_type, pos)
-- Get node metadata
local meta = minetest.get_meta(pos)
-- Check if it is owned by an NPC?
if meta:get_string("npc_owner") == "" then
-- Set owned by NPC
meta:set_string("npc_owner", self.npc_id)
-- Add place to list
npc.places.add(self, place_name, place_type, pos)
return true
end
return false
end
function npc.places.get_by_type(self, place_type)
local result = {}
for place_name, place_entry in pairs(self.places_map) do
if place_entry.type == place_type then
table.insert(result, place_name)
end
end
return result
end
-- This function searches on a squared are of the given radius
-- for nodes of the given type. The type should be npc.places.nodes
function npc.places.find_new_nearby(self, type, radius)
-- Get current pos
local current_pos = self.object:getpos()
-- Determine area points
local start_pos = {x=current_pos.x - radius, y=current_pos.y - 1, z=current_pos.z - radius}
local end_pos = {x=current_pos.x + radius, y=current_pos.y + 1, z=current_pos.z + radius}
-- Get nodes
local nodes = minetest.find_nodes_in_area(start_pos, end_pos, type)
return nodes
end

View File

@ -1,4 +1,4 @@
-- Advanced NPC mod by Zorman2000
local path = minetest.get_modpath("advanced_npc")
-- Intllib
@ -30,5 +30,7 @@ dofile(path .. "/dialogue.lua")
dofile(path .. "/random_data.lua")
dofile(path .. "/trade/trade.lua")
dofile(path .. "/trade/prices.lua")
dofile(path .. "/actions/actions.lua")
dofile(path .. "/actions/places.lua")
print (S("[MOD] Advanced NPC loaded"))
print (S("[Mod] Advanced NPC loaded"))

104
npc.lua
View File

@ -11,6 +11,18 @@ npc.MALE = "male"
npc.INVENTORY_ITEM_MAX_STACK = 99
npc.ANIMATION_SIT_START = 81
npc.ANIMATION_SIT_END = 160
npc.ANIMATION_LAY_START = 162
npc.ANIMATION_LAY_END = 166
npc.direction = {
north = 1,
east = 2,
south = 3,
west = 4
}
---------------------------------------------------------------------------------------
-- General functions
---------------------------------------------------------------------------------------
@ -212,6 +224,32 @@ function npc.start_dialogue(self, clicker, show_married_dialogue)
end
---------------------------------------------------------------------------------------
-- Action functionality
---------------------------------------------------------------------------------------
-- This function adds a function to the action queue.
-- Actions should be added in strict order for tasks to work as expected.
function npc.add_action(self, action, arguments)
self.freeze = true
minetest.log("Current Pos: "..dump(self.object:getpos()))
local action_entry = {action=action, args=arguments}
minetest.log(dump(action_entry))
table.insert(self.actions.queue, action_entry)
end
-- This function removes the first action in the action queue
-- and then exexcutes it
function npc.execute_action(self)
if table.getn(self.actions.queue) == 0 then
return false
end
minetest.log("Executing action")
local action_obj = self.actions.queue[1]
action_obj.action(action_obj.args)
table.remove(self.actions.queue, 1)
return true
end
---------------------------------------------------------------------------------------
-- Spawning functions
@ -273,8 +311,15 @@ end
-- NPC will have and choose random, starting values
local function npc_spawn(self, pos)
minetest.log("Spawning new NPC:")
-- Get Lua Entity
local ent = self:get_luaentity()
-- Set name
ent.nametag = "Kio"
-- Set ID
ent.npc_id = tostring(math.random(1000, 9999))..":"..ent.nametag
-- Determine sex based on textures
if (is_female_texture(ent.base_texture)) then
@ -330,6 +375,44 @@ local function npc_spawn(self, pos)
select_casual_trade_offers(ent)
end
-- Action queue
ent.actions = {
-- The queue is a queue of actions to be performed on each interval
queue = {},
-- Current value of the action timer
action_timer = 0,
-- Determines the interval for each action in the action queue
action_interval = 1
}
-- This flag is checked on every step. If it is true, the rest of
-- Mobs Redo API is not executed
ent.freeze = false
-- This map will hold all the places for the NPC
-- Map entries should be like: "bed" = {x=1, y=1, z=1}
ent.places_map = {}
-- Temporary initialization of actions for testing
-- npc.add_action(ent, npc.action.stand, {self = ent})
-- npc.add_action(ent, npc.action.stand, {self = ent})
-- npc.add_action(ent, npc.action.walk_step, {self = ent, dir = npc.direction.east})
-- npc.add_action(ent, npc.action.walk_step, {self = ent, dir = npc.direction.east})
-- npc.add_action(ent, npc.action.walk_step, {self = ent, dir = npc.direction.east})
-- npc.add_action(ent, npc.action.walk_step, {self = ent, dir = npc.direction.east})
-- npc.add_action(ent, npc.action.walk_step, {self = ent, dir = npc.direction.east})
-- npc.add_action(ent, npc.action.walk_step, {self = ent, dir = npc.direction.east})
-- npc.add_action(ent, npc.action.sit, {self = ent})
-- npc.add_action(ent, npc.action.rotate, {self = ent, dir = npc.direction.south})
-- npc.add_action(ent, npc.action.lay, {self = ent})
-- Temporary initialization of places
local bed_nodes = npc.places.find_new_nearby(ent, npc.places.nodes.BEDS, 8)
minetest.log("Number of bed nodes: "..dump(#bed_nodes))
if #bed_nodes > 0 then
npc.places.add_owned(ent, "bed1", npc.places.PLACE_TYPE.OWN_BED, bed_nodes[1])
end
minetest.log(dump(ent))
-- Refreshes entity
@ -432,6 +515,7 @@ mobs:register_mob("advanced_npc:npc", {
end,
do_custom = function(self, dtime)
-- Timer function for casual traders to reset their trade offers
self.trader_data.change_offers_timer = self.trader_data.change_offers_timer + dtime
-- Check if time has come to change offers
@ -474,7 +558,23 @@ mobs:register_mob("advanced_npc:npc", {
minetest.log(dump(self))
end
end
end
end
-- Action queue timer
self.actions.action_timer = self.actions.action_timer + dtime
if self.actions.action_timer >= self.actions.action_interval then
-- Reset action timer
self.actions.action_timer = 0
-- Execute action
npc.execute_action(self)
-- Check if there are more actions to execute
if table.getn(self.actions.queue) == 0 then
-- Unfreeze NPC so the rest of Mobs API work
--self.freeze = false
end
end
return not self.freeze
end
})
@ -494,7 +594,7 @@ mobs:spawn({
-- Item definitions
-------------------------------------------------------------------------
mobs:register_egg("advanced_npc:npc", S("Npc"), "default_brick.png", 1)
mobs:register_egg("advanced_npc:npc", S("NPC"), "default_brick.png", 1)
-- compatibility
mobs:alias_mob("mobs:npc", "advanced_npc:npc")

View File

@ -1,3 +1,4 @@
-- Relationships code for Advanced NPC by Zorman2000
---------------------------------------------------------------------------------------
-- Gift and relationship system
---------------------------------------------------------------------------------------

View File

@ -1,4 +1,4 @@
-- NPC trading abilities by Zorman2000
-- Trading code for Advanced NPC by Zorman2000
npc.trade = {}
@ -125,8 +125,6 @@ end
-- This function will return an offer object, based
-- on the items the NPC has.
-- Criteria: The NPC will offer to sell its items
-- if it doesn't has any currency.
function npc.trade.get_casual_trade_offer(self, offer_type)
local result = {}
-- Check offer type