From 5d2c820d889949ec2d8b73119fcb2506d30fadad Mon Sep 17 00:00:00 2001 From: zorman2000 Date: Thu, 19 Jan 2017 20:42:46 -0500 Subject: [PATCH] Actions: No longer have 'self' as part of the arguments object for each action. This caused an error with Lua serialization that crashed Minetest while the NPC was executing actions and it got unloaded (like exit game or mapblock unloaded). --- README.md | 2 +- actions/actions.lua | 80 +++++++++++++++++++-------------------------- npc.lua | 33 ++++++++++--------- 3 files changed, 52 insertions(+), 63 deletions(-) diff --git a/README.md b/README.md index b1f73ef..16d81bc 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,7 @@ __Phase 3__: Trading: In progress - [ ] Dedicated traders are traders that, when talked to, always make buy and sell offers. They have a greater variety too. - [ ] NPCs will also be able to offer "services", for example, repairing tools, by receiving an item and a payment, and then returning a specific item. -__Phase 4__: Actions: Main functionality complete +__Phase 4__: Actions: Complete - [x] NPCs should be able to use chests, furnaces, doors, beds and sit on "sittable" nodes (in progress) - [x] NPCs should be able to walk to specific places. Should also be able to open doors, fence gates and any other type of openable node while going to a place. - [x] NPCs should have the ability to identify nodes that belong to him/her, and recall them/ diff --git a/actions/actions.lua b/actions/actions.lua index 318b63c..5a5890b 100644 --- a/actions/actions.lua +++ b/actions/actions.lua @@ -45,8 +45,8 @@ npc.actions.two_nps_speed = 1.90 -- The following action alters the timer interval for executing actions, therefore -- making waits and pauses possible, or increase timing when some actions want to -- be performed faster, like walking. -function npc.actions.set_interval(args) - local self = args.self +function npc.actions.set_interval(self, args) + local self_actions = args.self_actions local new_interval = args.interval local freeze_mobs_api = args.freeze @@ -57,7 +57,7 @@ end -- The following action is for allowing the rest of mobs redo API to be executed -- after this action ends. This is useful for times when no action is needed -- and the NPC is allowed to roam freely. -function npc.actions.freeze(args) +function npc.actions.freeze(self, args) local freeze_mobs_api = args.freeze minetest.log("Received: "..dump(freeze_mobs_api)) minetest.log("Returning: "..dump(not(freeze_mobs_api))) @@ -66,8 +66,7 @@ end -- This action is to rotate to mob to a specifc direction. Currently, the code -- contains also for diagonals, but remaining in the orthogonal domain is preferrable. -function npc.actions.rotate(args) - local self = args.self +function npc.actions.rotate(self, args) local dir = args.dir local yaw = 0 self.rotate = 0 @@ -94,8 +93,7 @@ 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 +function npc.actions.walk_step(self, args) local dir = args.dir local speed = args.speed local vel = {} @@ -113,7 +111,7 @@ function npc.actions.walk_step(args) vel = {x=-speed, y=0, z=0} end -- Rotate NPC - npc.actions.rotate({self=self, dir=dir}) + npc.actions.rotate(self, {dir=dir}) -- Set velocity so that NPC walks self.object:setvelocity(vel) -- Set walk animation @@ -124,8 +122,7 @@ function npc.actions.walk_step(args) end -- This action makes the NPC stand and remain like that -function npc.actions.stand(args) - local self = args.self +function npc.actions.stand(self, args) local pos = args.pos local dir = args.dir -- Stop NPC @@ -136,7 +133,7 @@ function npc.actions.stand(args) end -- If dir given, set to that dir if dir ~= nil then - npc.actions.rotate({self=self, dir=dir}) + npc.actions.rotate(self, {dir=dir}) end -- Set stand animation self.object:set_animation({ @@ -146,8 +143,7 @@ function npc.actions.stand(args) end -- This action makes the NPC sit on the node where it is -function npc.actions.sit(args) - local self = args.self +function npc.actions.sit(self, args) local pos = args.pos local dir = args.dir -- Stop NPC @@ -158,7 +154,7 @@ function npc.actions.sit(args) end -- If dir given, set to that dir if dir ~= nil then - npc.actions.rotate({self=self, dir=dir}) + npc.actions.rotate(self, {dir=dir}) end -- Set sit animation self.object:set_animation({ @@ -168,8 +164,7 @@ function npc.actions.sit(args) end -- This action makes the NPC lay on the node where it is -function npc.actions.lay(args) - local self = args.self +function npc.actions.lay(self, args) local pos = args.pos -- Stop NPC self.object:setvelocity({x=0, y=0, z=0}) @@ -188,8 +183,7 @@ end -- This function is a convenience function to make it easy to put -- and get items from another inventory (be it a player inv or -- a node inv) -function npc.actions.put_item_on_external_inventory(args) - local self = args.self +function npc.actions.put_item_on_external_inventory(self, args) local player = args.player local pos = args.pos local inv_list = args.inv_list @@ -226,8 +220,7 @@ function npc.actions.put_item_on_external_inventory(args) return false end -function npc.actions.take_item_from_external_inventory(args) - local self = args.self +function npc.actions.take_item_from_external_inventory(self, args) local player = args.player local pos = args.pos local inv_list = args.inv_list @@ -253,8 +246,7 @@ function npc.actions.take_item_from_external_inventory(args) return false end -function npc.actions.check_external_inventory_contains_item(args) - local self = args.self +function npc.actions.check_external_inventory_contains_item(self, args) local player = args.player local pos = args.pos local inv_list = args.inv_list @@ -297,8 +289,7 @@ end -- This function is used to open or close openable nodes. -- Currently supported openable nodes are: any doors using the -- default doors API, and the cottages mod gates and doors. -function npc.actions.use_door(args) - local self = args.self +function npc.actions.use_door(self, args) local pos = args.pos local action = args.action local dir = args.dir @@ -386,7 +377,6 @@ function npc.actions.use_furnace(self, pos, item, freeze) -- Put this item on the fuel inventory list of the furnace local args = { - self = self, player = nil, pos = pos, inv_list = "fuel", @@ -396,7 +386,6 @@ function npc.actions.use_furnace(self, pos, item, freeze) npc.add_action(self, npc.actions.put_item_on_external_inventory, args) -- Put the item that we want to cook on the furnace args = { - self = self, player = nil, pos = pos, inv_list = "src", @@ -408,21 +397,21 @@ function npc.actions.use_furnace(self, pos, item, freeze) -- Now, set NPC to wait until furnace is done. minetest.log("Setting wait action for "..dump(total_cook_time)) - npc.add_action(self, npc.actions.set_interval, {self=self, interval=total_cook_time, freeze=freeze}) + npc.add_action(self, npc.actions.set_interval, {interval=total_cook_time, freeze=freeze}) -- Reset timer - npc.add_action(self, npc.actions.set_interval, {self=self, interval=1, freeze=true}) + npc.add_action(self, npc.actions.set_interval, {interval=1, freeze=true}) -- If freeze is false, then we will have to find the way back to the furnace -- once cooking is done. if freeze == false then minetest.log("Adding walk to position to wandering: "..dump(pos)) - npc.add_task(self, npc.actions.walk_to_pos, {self=self, end_pos=pos, walkable={}}) + npc.add_task(self, npc.actions.walk_to_pos, {end_pos=pos, walkable={}}) end -- Take cooked items back args = { - self = self, + player = nil, pos = pos, inv_list = "dst", @@ -453,15 +442,15 @@ function npc.actions.use_bed(self, pos, action) -- Get position local bed_pos = npc.actions.nodes.beds[node.name].get_lay_pos(pos, dir) -- Sit down on bed, rotate to correct direction - npc.add_action(self, npc.actions.sit, {self=self, pos=bed_pos, dir=(node.param2 + 2) % 4}) + npc.add_action(self, npc.actions.sit, {pos=bed_pos, dir=(node.param2 + 2) % 4}) -- Lay down - npc.add_action(self, npc.actions.lay, {self=self}) + npc.add_action(self, npc.actions.lay, {}) else -- Calculate position to get up local bed_pos_y = npc.actions.nodes.beds[node.name].get_lay_pos(pos, dir).y local bed_pos = {x = pos.x, y = bed_pos_y, z = pos.z} -- Sit up - npc.add_action(self, npc.actions.sit, {self=self, pos=bed_pos}) + npc.add_action(self, npc.actions.sit, {pos=bed_pos}) -- Initialize direction: Default is front of bottom of bed local dir = (node.param2 + 2) % 4 -- Find empty node around node @@ -492,7 +481,7 @@ function npc.actions.use_bed(self, pos, action) end end -- Stand out of bed - npc.add_action(self, npc.actions.stand, {self=self, pos=pos_out_of_bed, dir=dir}) + npc.add_action(self, npc.actions.stand, {pos=pos_out_of_bed, dir=dir}) end end @@ -506,7 +495,7 @@ function npc.actions.use_sittable(self, pos, action) minetest.log("Got sit position: "..dump(sit_pos)) local sit_pos = npc.actions.nodes.sittable[node.name].get_sit_pos(pos, node.param2) -- Sit down on bench/chair/stairs - npc.add_action(self, npc.actions.sit, {self=self, pos=sit_pos, dir=(node.param2 + 2) % 4}) + npc.add_action(self, npc.actions.sit, {pos=sit_pos, dir=(node.param2 + 2) % 4}) else -- Find empty areas around chair local dir = node.param2 + 2 % 4 @@ -519,7 +508,7 @@ function npc.actions.use_sittable(self, pos, action) local pos_out_of_sittable = {x=empty_nodes[1].pos.x, y=empty_nodes[1].pos.y + 1, z=empty_nodes[1].pos.z} -- Stand - npc.add_action(self, npc.actions.stand, {self=self, pos=pos_out_of_sittable, dir=dir}) + npc.add_action(self, npc.actions.stand, {pos=pos_out_of_sittable, dir=dir}) end end @@ -547,9 +536,8 @@ end -- is included, which is a table of node names, these nodes are -- going to be considered walkable for the algorithm to find a -- path. -function npc.actions.walk_to_pos(args) +function npc.actions.walk_to_pos(self, args) -- Get arguments for this task - local self = args.self local end_pos = args.end_pos local walkable_nodes = args.walkable @@ -576,7 +564,7 @@ function npc.actions.walk_to_pos(args) -- Set the action timer interval to half second. This is to account for -- the increased speed when walking. - npc.add_action(self, npc.actions.set_interval, {self=self, interval=0.5, freeze=true}) + npc.add_action(self, npc.actions.set_interval, {interval=0.5, freeze=true}) -- Add steps to path for i = 1, #path do @@ -585,7 +573,7 @@ function npc.actions.walk_to_pos(args) -- Add direction to last node local dir = npc.actions.get_direction(path[i].pos, end_pos) -- Add stand animation at end - npc.add_action(self, npc.actions.stand, {self = self, dir = dir}) + npc.add_action(self, npc.actions.stand, { dir = dir}) break end -- Get direction to move from path[i] to path[i+1] @@ -597,15 +585,15 @@ function npc.actions.walk_to_pos(args) if npc.actions.get_openable_node_state(node, dir) == npc.actions.const.doors.state.CLOSED then minetest.log("Opening action to open door") -- Stop to open door, this avoids misplaced movements later on - npc.add_action(self, npc.actions.stand, {self=self, dir=dir}) + npc.add_action(self, npc.actions.stand, {dir=dir}) -- Open door - npc.add_action(self, npc.actions.use_door, {self=self, pos=path[i+1].pos, dir=dir, action=npc.actions.const.doors.action.OPEN}) + npc.add_action(self, npc.actions.use_door, {pos=path[i+1].pos, dir=dir, action=npc.actions.const.doors.action.OPEN}) door_opened = true end end -- Add walk action to action queue - npc.add_action(self, npc.actions.walk_step, {self = self, dir = dir, speed = speed}) + npc.add_action(self, npc.actions.walk_step, {dir = dir, speed = speed}) if door_opened then -- Stop to close door, this avoids misplaced movements later on @@ -620,9 +608,9 @@ function npc.actions.walk_to_pos(args) x_adj = -0.1 end local pos_on_close = {x=path[i+1].pos.x + x_adj, y=path[i+1].pos.y + 1, z=path[i+1].pos.z + z_adj} - npc.add_action(self, npc.actions.stand, {self=self, dir=(dir + 2)% 4, pos=pos_on_close}) + npc.add_action(self, npc.actions.stand, {dir=(dir + 2)% 4, pos=pos_on_close}) -- Close door - npc.add_action(self, npc.actions.use_door, {self=self, pos=path[i+1].pos, action=npc.actions.const.doors.action.CLOSE}) + npc.add_action(self, npc.actions.use_door, {pos=path[i+1].pos, action=npc.actions.const.doors.action.CLOSE}) door_opened = false end @@ -631,7 +619,7 @@ function npc.actions.walk_to_pos(args) -- Return the action interval to default interval of 1 second -- By default, always freeze. - npc.add_action(self, npc.actions.set_interval, {self=self, interval=1, freeze=true}) + npc.add_action(self, npc.actions.set_interval, {interval=1, freeze=true}) else minetest.log("Unable to find path.") diff --git a/npc.lua b/npc.lua index 6da4d56..681eb39 100755 --- a/npc.lua +++ b/npc.lua @@ -238,7 +238,7 @@ function npc.execute_action(self) -- Clear queue self.actions.queue = {} -- Now, execute the task with its arguments - action_obj.action(action_obj.args) + action_obj.action(self, action_obj.args) -- After all new actions has been added by task, add the previously -- queued actions back for i = 1, #backup_queue do @@ -251,7 +251,7 @@ function npc.execute_action(self) -- Store current position self.actions.state_before_lock.pos = self.object:getpos() -- Execute action as normal - result = action_obj.action(action_obj.args) + result = action_obj.action(self, action_obj.args) -- Remove task table.remove(self.actions.queue, 1) -- Set state @@ -281,7 +281,7 @@ function npc.lock_actions(self) pos.y = self.object:getpos().y end -- Stop NPC - npc.actions.stand({self=self, pos=pos}) + npc.actions.stand({pos=pos}) -- Avoid all timer execution self.actions.action_timer_lock = true -- Reset timer so that it has some time after interaction is done @@ -384,6 +384,9 @@ local function npc_spawn(self, pos) -- Get Lua Entity local ent = self:get_luaentity() + -- Avoid NPC to be removed by mobs_redo API + ent.remove_ok = false + -- Set name ent.nametag = "Kio" @@ -486,7 +489,7 @@ local function npc_spawn(self, pos) --npc.add_action(ent, npc.actions.stand, {self = ent}) --npc.add_action(ent, npc.actions.stand, {self = ent}) if nodes[1] ~= nil then - npc.add_task(ent, npc.actions.walk_to_pos, {self=ent, end_pos=nodes[1], walkable={}}) + npc.add_task(ent, npc.actions.walk_to_pos, {end_pos=nodes[1], walkable={}}) npc.actions.use_furnace(ent, nodes[1], "default:cobble 5", false) --npc.add_action(ent, npc.actions.sit, {self = ent}) -- npc.add_action(ent, npc.actions.lay, {self = ent}) @@ -625,19 +628,17 @@ mobs:register_mob("advanced_npc:npc", { end, do_custom = function(self, dtime) - -- Timer function for casual traders to reset their trade offers - if self.trader_data ~= nil then - self.trader_data.change_offers_timer = self.trader_data.change_offers_timer + dtime - -- Check if time has come to change offers - if self.trader_data.trader_status == npc.trade.CASUAL and - self.trader_data.change_offers_timer >= self.trader_data.change_offers_timer_interval then - -- Reset timer - self.trader_data.change_offers_timer = 0 - -- Re-select casual trade offers - select_casual_trade_offers(self) - end + -- 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 + if self.trader_data.trader_status == npc.trade.CASUAL and + self.trader_data.change_offers_timer >= self.trader_data.change_offers_timer_interval then + -- Reset timer + self.trader_data.change_offers_timer = 0 + -- Re-select casual trade offers + select_casual_trade_offers(self) end - + -- Timer function for gifts for i = 1, #self.relationships do local relationship = self.relationships[i]