Actions: Add ability for NPC to fully use a furnace.
Add ability to add tasks to action queue.
This commit is contained in:
parent
137f02e863
commit
9556e76b79
@ -93,15 +93,20 @@ end
|
|||||||
function npc.actions.walk_step(args)
|
function npc.actions.walk_step(args)
|
||||||
local self = args.self
|
local self = args.self
|
||||||
local dir = args.dir
|
local dir = args.dir
|
||||||
|
local speed = args.speed
|
||||||
local vel = {}
|
local vel = {}
|
||||||
|
-- Set default node per seconds
|
||||||
|
if speed == nil then
|
||||||
|
speed = 0.98
|
||||||
|
end
|
||||||
if dir == npc.direction.north then
|
if dir == npc.direction.north then
|
||||||
vel = {x=0, y=0, z=0.98}
|
vel = {x=0, y=0, z=speed}
|
||||||
elseif dir == npc.direction.east then
|
elseif dir == npc.direction.east then
|
||||||
vel = {x=0.98, y=0, z=0}
|
vel = {x=speed, y=0, z=0}
|
||||||
elseif dir == npc.direction.south then
|
elseif dir == npc.direction.south then
|
||||||
vel = {x=0, y=0, z=-0.98}
|
vel = {x=0, y=0, z=-speed}
|
||||||
elseif dir == npc.direction.west then
|
elseif dir == npc.direction.west then
|
||||||
vel = {x=-0.98, y=0, z=0}
|
vel = {x=-speed, y=0, z=0}
|
||||||
end
|
end
|
||||||
-- Rotate NPC
|
-- Rotate NPC
|
||||||
npc.actions.rotate({self=self, dir=dir})
|
npc.actions.rotate({self=self, dir=dir})
|
||||||
@ -228,7 +233,7 @@ function npc.actions.take_item_from_external_inventory(args)
|
|||||||
if player ~= nil then
|
if player ~= nil then
|
||||||
inv = minetest.get_inventory({type="player", name=player})
|
inv = minetest.get_inventory({type="player", name=player})
|
||||||
else
|
else
|
||||||
inv = minetest.get_inventory({type="node", pos})
|
inv = minetest.get_inventory({type="node", pos=pos})
|
||||||
end
|
end
|
||||||
-- Create ItemSTack to take from external inventory
|
-- Create ItemSTack to take from external inventory
|
||||||
local item = ItemStack(item_name.." "..count)
|
local item = ItemStack(item_name.." "..count)
|
||||||
@ -255,7 +260,7 @@ function npc.actions.check_external_inventory_contains_item(args)
|
|||||||
if player ~= nil then
|
if player ~= nil then
|
||||||
inv = minetest.get_inventory({type="player", name=player})
|
inv = minetest.get_inventory({type="player", name=player})
|
||||||
else
|
else
|
||||||
inv = minetest.get_inventory({type="node", pos})
|
inv = minetest.get_inventory({type="node", pos=pos})
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Create ItemStack for checking the external inventory
|
-- Create ItemStack for checking the external inventory
|
||||||
@ -315,16 +320,66 @@ end
|
|||||||
-- with the fuel items the NPC will take whatever was cooked and whatever
|
-- with the fuel items the NPC will take whatever was cooked and whatever
|
||||||
-- remained to cook. The function received the position of the furnace
|
-- remained to cook. The function received the position of the furnace
|
||||||
-- to use, and the item to cook in furnace. Item is an itemstring
|
-- to use, and the item to cook in furnace. Item is an itemstring
|
||||||
function npc.actions.use_furnace(self, pos, item)
|
function npc.actions.use_furnace(self, pos, item, freeze)
|
||||||
-- Check if any item in the NPC inventory serve as fuel
|
-- Define which items are usable as fuels. The NPC
|
||||||
-- For now, just use some specific items as fuels
|
-- will mainly use this as fuels to avoid getting useful
|
||||||
local fuels = {"default:leaves", "default:tree", ""}
|
-- items (such as coal lumps) for burning
|
||||||
-- Check if NPC has a fuel item
|
local fuels = {"default:leaves",
|
||||||
for i = 1,2 do
|
"default:pine_needles",
|
||||||
local fuel_item = npc.inventory_contains(self, fuels[i])
|
"default:tree",
|
||||||
local src_item = npc.inventory_contains(self, item)
|
"default:acacia_tree",
|
||||||
|
"default:aspen_tree",
|
||||||
|
"default:jungletree",
|
||||||
|
"default:pine_tree",
|
||||||
|
"default:coalblock",
|
||||||
|
"farming:straw"}
|
||||||
|
|
||||||
|
-- Check if NPC has item to cook
|
||||||
|
local src_item = npc.inventory_contains(self, npc.get_item_name(item))
|
||||||
|
if src_item == nil then
|
||||||
|
-- Unable to cook item that is not in inventory
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Check if NPC has a fuel item
|
||||||
|
for i = 1,9 do
|
||||||
|
local fuel_item = npc.inventory_contains(self, fuels[i])
|
||||||
|
|
||||||
|
if fuel_item ~= nil then
|
||||||
|
-- Get fuel item's burn time
|
||||||
|
local fuel_time =
|
||||||
|
minetest.get_craft_result({method="fuel", width=1, items={ItemStack(fuel_item.item_string)}}).time
|
||||||
|
local total_fuel_time = fuel_time * npc.get_item_count(fuel_item.item_string)
|
||||||
|
minetest.log("Fuel time: "..dump(fuel_time))
|
||||||
|
|
||||||
|
-- Get item to cook's cooking time
|
||||||
|
local cook_result =
|
||||||
|
minetest.get_craft_result({method="cooking", width=1, items={ItemStack(src_item.item_string)}})
|
||||||
|
local total_cook_time = cook_result.time * npc.get_item_count(item)
|
||||||
|
minetest.log("Cook: "..dump(cook_result))
|
||||||
|
|
||||||
|
minetest.log("Total cook time: "..total_cook_time
|
||||||
|
..", total fuel burn time: "..dump(total_fuel_time))
|
||||||
|
|
||||||
|
-- Check if there is enough fuel to cook all items
|
||||||
|
if total_cook_time > total_fuel_time then
|
||||||
|
-- Don't have enough fuel to cook item. Return the difference
|
||||||
|
-- so it may help on trying to acquire the fuel later.
|
||||||
|
-- NOTE: Yes, returning here means that NPC could probably have other
|
||||||
|
-- items usable as fuels and ignore them. This should be ok for now,
|
||||||
|
-- considering that fuel items are ordered in a way where cheaper, less
|
||||||
|
-- useless items come first, saving possible valuable items.
|
||||||
|
return cook_result.time - fuel_time
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Calculate how much fuel is needed
|
||||||
|
local fuel_amount = total_cook_time / fuel_time
|
||||||
|
if fuel_amount < 1 then
|
||||||
|
fuel_amount = 1
|
||||||
|
end
|
||||||
|
|
||||||
|
minetest.log("Amount of fuel needed: "..fuel_amount)
|
||||||
|
|
||||||
if fuel_item ~= nil and src_item ~= nil then
|
|
||||||
-- Put this item on the fuel inventory list of the furnace
|
-- Put this item on the fuel inventory list of the furnace
|
||||||
local args = {
|
local args = {
|
||||||
self = self,
|
self = self,
|
||||||
@ -332,7 +387,7 @@ function npc.actions.use_furnace(self, pos, item)
|
|||||||
pos = pos,
|
pos = pos,
|
||||||
inv_list = "fuel",
|
inv_list = "fuel",
|
||||||
item_name = npc.get_item_name(fuel_item.item_string),
|
item_name = npc.get_item_name(fuel_item.item_string),
|
||||||
count = npc.get_item_count(fuel_item.item_string)
|
count = fuel_amount
|
||||||
}
|
}
|
||||||
npc.add_action(self, npc.actions.put_item_on_external_inventory, args)
|
npc.add_action(self, npc.actions.put_item_on_external_inventory, args)
|
||||||
-- Put the item that we want to cook on the furnace
|
-- Put the item that we want to cook on the furnace
|
||||||
@ -342,16 +397,39 @@ function npc.actions.use_furnace(self, pos, item)
|
|||||||
pos = pos,
|
pos = pos,
|
||||||
inv_list = "src",
|
inv_list = "src",
|
||||||
item_name = npc.get_item_name(src_item.item_string),
|
item_name = npc.get_item_name(src_item.item_string),
|
||||||
count = npc.get_item_count(src_item.item_string),
|
count = npc.get_item_count(item),
|
||||||
is_furnace = true
|
is_furnace = true
|
||||||
}
|
}
|
||||||
npc.add_action(self, npc.actions.put_item_on_external_inventory, args)
|
npc.add_action(self, npc.actions.put_item_on_external_inventory, args)
|
||||||
|
|
||||||
-- TODO: Need to add a way to calculate how many seconds will pass
|
-- Now, set NPC to wait until furnace is done.
|
||||||
-- until the furnace is done, or at least the items that we expect
|
minetest.log("Setting wait action for "..dump(total_cook_time))
|
||||||
-- to get (assume all items to be cooked are the ones ewe expect back)
|
npc.add_action(self, npc.actions.set_interval, {self=self, interval=total_cook_time, freeze=freeze})
|
||||||
-- Then, add that many stand actions, then an action to take the items.
|
|
||||||
|
|
||||||
|
-- Reset timer
|
||||||
|
npc.add_action(self, npc.actions.set_interval, {self=self, 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={}})
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Take cooked items back
|
||||||
|
args = {
|
||||||
|
self = self,
|
||||||
|
player = nil,
|
||||||
|
pos = pos,
|
||||||
|
inv_list = "dst",
|
||||||
|
item_name = cook_result.item:get_name(),
|
||||||
|
count = npc.get_item_count(item),
|
||||||
|
is_furnace = false
|
||||||
|
}
|
||||||
|
minetest.log("Taking item back: "..dump(pos))
|
||||||
|
npc.add_action(self, npc.actions.take_item_from_external_inventory, args)
|
||||||
|
|
||||||
|
minetest.log("Inventory: "..dump(self.inventory))
|
||||||
|
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
@ -465,9 +543,17 @@ end
|
|||||||
-- is included, which is a table of node names, these nodes are
|
-- is included, which is a table of node names, these nodes are
|
||||||
-- going to be considered walkable for the algorithm to find a
|
-- going to be considered walkable for the algorithm to find a
|
||||||
-- path.
|
-- path.
|
||||||
function npc.actions.walk_to_pos(self, end_pos, walkable_nodes)
|
function npc.actions.walk_to_pos(args)
|
||||||
|
|
||||||
local start_pos = self.object:getpos()
|
local self = args.self
|
||||||
|
local end_pos = args.end_pos
|
||||||
|
local walkable_nodes = args.walkable
|
||||||
|
|
||||||
|
-- Round start_pos to make sure it can find start and end
|
||||||
|
local start_pos = vector.round(self.object:getpos())
|
||||||
|
-- Use y of end_pos (this can only be done assuming flat terrain)
|
||||||
|
start_pos.y = self.object:getpos().y
|
||||||
|
minetest.log("Walk to pos: Using start position: "..dump(start_pos))
|
||||||
|
|
||||||
-- Set walkable nodes to empty if the parameter hasn't been used
|
-- Set walkable nodes to empty if the parameter hasn't been used
|
||||||
if walkable_nodes == nil then
|
if walkable_nodes == nil then
|
||||||
@ -513,7 +599,18 @@ function npc.actions.walk_to_pos(self, end_pos, walkable_nodes)
|
|||||||
|
|
||||||
if door_opened then
|
if door_opened then
|
||||||
-- Stop to close door, this avoids misplaced movements later on
|
-- Stop to close door, this avoids misplaced movements later on
|
||||||
npc.add_action(self, npc.actions.stand, {self=self, dir=(dir + 2)% 4})
|
local x_adj, z_adj = 0, 0
|
||||||
|
if dir == 0 then
|
||||||
|
z_adj = 0.1
|
||||||
|
elseif dir == 1 then
|
||||||
|
x_adj = 0.1
|
||||||
|
elseif dir == 2 then
|
||||||
|
z_adj = -0.1
|
||||||
|
elseif dir == 3 then
|
||||||
|
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})
|
||||||
-- Close door
|
-- 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, {self=self, pos=path[i+1].pos, action=npc.actions.const.doors.action.CLOSE})
|
||||||
|
|
||||||
|
@ -201,7 +201,7 @@ end
|
|||||||
-- for the pathfinding algorithm to use
|
-- for the pathfinding algorithm to use
|
||||||
function pathfinder.find_start_and_end_pos(map)
|
function pathfinder.find_start_and_end_pos(map)
|
||||||
-- This is for debug
|
-- This is for debug
|
||||||
--print_map(map)
|
print_map(map)
|
||||||
local result = {}
|
local result = {}
|
||||||
for z,row in pairs(map) do
|
for z,row in pairs(map) do
|
||||||
for x,node in pairs(row) do
|
for x,node in pairs(row) do
|
||||||
|
56
npc.lua
56
npc.lua
@ -188,22 +188,52 @@ end
|
|||||||
function npc.add_action(self, action, arguments)
|
function npc.add_action(self, action, arguments)
|
||||||
--self.freeze = true
|
--self.freeze = true
|
||||||
--minetest.log("Current Pos: "..dump(self.object:getpos()))
|
--minetest.log("Current Pos: "..dump(self.object:getpos()))
|
||||||
local action_entry = {action=action, args=arguments}
|
local action_entry = {action=action, args=arguments, is_task=false}
|
||||||
--minetest.log(dump(action_entry))
|
--minetest.log(dump(action_entry))
|
||||||
table.insert(self.actions.queue, action_entry)
|
table.insert(self.actions.queue, action_entry)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- This function adds task actions in-place, as opposed to
|
||||||
|
-- at the end of the queue. This allows for continued order
|
||||||
|
function npc.add_task(self, task, args)
|
||||||
|
local action_entry = {action=task, args=args, is_task=true}
|
||||||
|
table.insert(self.actions.queue, action_entry)
|
||||||
|
end
|
||||||
|
|
||||||
-- This function removes the first action in the action queue
|
-- This function removes the first action in the action queue
|
||||||
-- and then exexcutes it
|
-- and then executes it
|
||||||
function npc.execute_action(self)
|
function npc.execute_action(self)
|
||||||
|
local result = nil
|
||||||
if table.getn(self.actions.queue) == 0 then
|
if table.getn(self.actions.queue) == 0 then
|
||||||
-- Keep state the same if there are no more actions in actions queue
|
-- Keep state the same if there are no more actions in actions queue
|
||||||
return self.freeze
|
return self.freeze
|
||||||
end
|
end
|
||||||
minetest.log("Executing action")
|
|
||||||
local action_obj = self.actions.queue[1]
|
local action_obj = self.actions.queue[1]
|
||||||
local result = action_obj.action(action_obj.args)
|
-- If the entry is a task, then push all this new operations in
|
||||||
|
-- stack fashion
|
||||||
|
if action_obj.is_task == true then
|
||||||
|
minetest.log("Executing task")
|
||||||
|
-- Remove from queue
|
||||||
table.remove(self.actions.queue, 1)
|
table.remove(self.actions.queue, 1)
|
||||||
|
-- Backup current queue
|
||||||
|
local backup_queue = self.actions.queue
|
||||||
|
-- Clear queue
|
||||||
|
self.actions.queue = {}
|
||||||
|
-- Now, execute the task with its arguments
|
||||||
|
action_obj.action(action_obj.args)
|
||||||
|
-- After all new actions has been added by task, add the previously
|
||||||
|
-- queued actions back
|
||||||
|
for i = 1, #backup_queue do
|
||||||
|
table.insert(self.actions.queue, backup_queue[i])
|
||||||
|
end
|
||||||
|
minetest.log("New actions queue: "..dump(self))
|
||||||
|
else
|
||||||
|
minetest.log("Executing action")
|
||||||
|
-- Execute action as normal
|
||||||
|
result = action_obj.action(action_obj.args)
|
||||||
|
-- Remove executed action from queue
|
||||||
|
table.remove(self.actions.queue, 1)
|
||||||
|
end
|
||||||
return result
|
return result
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -250,6 +280,10 @@ local function choose_spawn_items(self)
|
|||||||
local currency_item_count = math.random(5, 10)
|
local currency_item_count = math.random(5, 10)
|
||||||
npc.add_item_to_inventory(self, npc.trade.prices.currency.tier3, currency_item_count)
|
npc.add_item_to_inventory(self, npc.trade.prices.currency.tier3, currency_item_count)
|
||||||
|
|
||||||
|
-- For test
|
||||||
|
npc.add_item_to_inventory(self, "default:tree", 10)
|
||||||
|
npc.add_item_to_inventory(self, "default:cobble", 10)
|
||||||
|
|
||||||
minetest.log("Initial inventory: "..dump(self.inventory))
|
minetest.log("Initial inventory: "..dump(self.inventory))
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -352,7 +386,7 @@ local function npc_spawn(self, pos)
|
|||||||
ent.places_map = {}
|
ent.places_map = {}
|
||||||
|
|
||||||
-- Temporary initialization of actions for testing
|
-- Temporary initialization of actions for testing
|
||||||
local nodes = npc.places.find_sittable_nodes_nearby(ent.object:getpos(), 20)
|
local nodes = npc.places.find_node_nearby(ent.object:getpos(), {"default:furnace"}, 20)
|
||||||
minetest.log("Found nodes: "..dump(nodes))
|
minetest.log("Found nodes: "..dump(nodes))
|
||||||
|
|
||||||
--local path = pathfinder.find_path(ent.object:getpos(), nodes[1], 20)
|
--local path = pathfinder.find_path(ent.object:getpos(), nodes[1], 20)
|
||||||
@ -361,14 +395,14 @@ local function npc_spawn(self, pos)
|
|||||||
--npc.add_action(ent, npc.actions.stand, {self = ent})
|
--npc.add_action(ent, npc.actions.stand, {self = ent})
|
||||||
--npc.add_action(ent, npc.actions.stand, {self = ent})
|
--npc.add_action(ent, npc.actions.stand, {self = ent})
|
||||||
if nodes[1] ~= nil then
|
if nodes[1] ~= nil then
|
||||||
npc.actions.walk_to_pos(ent, nodes[1], {})
|
npc.add_task(ent, npc.actions.walk_to_pos, {self=ent, end_pos=nodes[1], walkable={}})
|
||||||
npc.actions.use_sittable(ent, nodes[1], npc.actions.const.sittable.SIT)
|
npc.actions.use_furnace(ent, nodes[1], "default:cobble 10", false)
|
||||||
npc.add_action(ent, npc.actions.sit, {self = ent})
|
--npc.add_action(ent, npc.actions.sit, {self = ent})
|
||||||
-- npc.add_action(ent, npc.actions.lay, {self = ent})
|
-- npc.add_action(ent, npc.actions.lay, {self = ent})
|
||||||
-- npc.add_action(ent, npc.actions.lay, {self = ent})
|
-- npc.add_action(ent, npc.actions.lay, {self = ent})
|
||||||
-- npc.add_action(ent, npc.actions.lay, {self = ent})
|
-- npc.add_action(ent, npc.actions.lay, {self = ent})
|
||||||
npc.actions.use_sittable(ent, nodes[1], npc.actions.const.sittable.GET_UP)
|
--npc.actions.use_sittable(ent, nodes[1], npc.actions.const.sittable.GET_UP)
|
||||||
npc.add_action(ent, npc.actions.set_interval, {self=ent, interval=10, freeze=true})
|
--npc.add_action(ent, npc.actions.set_interval, {self=ent, interval=10, freeze=true})
|
||||||
npc.add_action(ent, npc.actions.freeze, {freeze = false})
|
npc.add_action(ent, npc.actions.freeze, {freeze = false})
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -496,6 +530,7 @@ mobs:register_mob("advanced_npc:npc", {
|
|||||||
do_custom = function(self, dtime)
|
do_custom = function(self, dtime)
|
||||||
|
|
||||||
-- Timer function for casual traders to reset their trade offers
|
-- 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
|
self.trader_data.change_offers_timer = self.trader_data.change_offers_timer + dtime
|
||||||
-- Check if time has come to change offers
|
-- Check if time has come to change offers
|
||||||
if self.trader_data.trader_status == npc.trade.CASUAL and
|
if self.trader_data.trader_status == npc.trade.CASUAL and
|
||||||
@ -504,6 +539,7 @@ mobs:register_mob("advanced_npc:npc", {
|
|||||||
self.trader_data.change_offers_timer = 0
|
self.trader_data.change_offers_timer = 0
|
||||||
-- Re-select casual trade offers
|
-- Re-select casual trade offers
|
||||||
select_casual_trade_offers(self)
|
select_casual_trade_offers(self)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Timer function for gifts
|
-- Timer function for gifts
|
||||||
|
Loading…
Reference in New Issue
Block a user