Actions: Add mine animation to dig/place actions.
Fix bug enabling wandering after dig action. Improved "random" dir functionality for walk_step to avoid getting NPC stuck. General formatting fixes.
This commit is contained in:
parent
ce47958144
commit
70c7a3c96f
@ -15,6 +15,49 @@ npc.actions = {}
|
||||
|
||||
npc.actions.default_interval = 1
|
||||
|
||||
npc.actions.dir_data = {
|
||||
-- North
|
||||
[0] = {
|
||||
yaw = 0,
|
||||
vel = {x=0, y=0, z=1}
|
||||
},
|
||||
-- East
|
||||
[1] = {
|
||||
yaw = (3 * math.pi) / 2,
|
||||
vel = {x=1, y=0, z=0}
|
||||
},
|
||||
-- South
|
||||
[2] = {
|
||||
yaw = math.pi,
|
||||
vel = {x=0, y=0, z=-1}
|
||||
},
|
||||
-- West
|
||||
[3] = {
|
||||
yaw = math.pi / 2,
|
||||
vel = {x=-1, y=0, z=0}
|
||||
},
|
||||
-- North east
|
||||
[4] = {
|
||||
yaw = (7 * math.pi) / 4,
|
||||
vel = {x=1, y=0, z=1}
|
||||
},
|
||||
-- North west
|
||||
[5] = {
|
||||
yaw = math.pi / 4,
|
||||
vel = {x=-1, y=0, z=1}
|
||||
},
|
||||
-- South east
|
||||
[6] = {
|
||||
yaw = (5 * math.pi) / 4,
|
||||
vel = {x=1, y=0, z=-1}
|
||||
},
|
||||
-- South west
|
||||
[7] = {
|
||||
yaw = (3 * math.pi) / 4,
|
||||
vel = {x=-1, y=0, z=-1}
|
||||
}
|
||||
}
|
||||
|
||||
-- Describes actions with doors or openable nodes
|
||||
npc.actions.const = {
|
||||
doors = {
|
||||
@ -164,12 +207,18 @@ end
|
||||
-- If 'add_to_inventory' is true, it will put the digged node in the NPC
|
||||
-- inventory.
|
||||
-- Returns true if dig is successful, otherwise false
|
||||
function npc.actions.dig(self, args)
|
||||
function npc.actions.dig(self, args)
|
||||
local pos = args.pos
|
||||
local add_to_inventory = args.add_to_inventory
|
||||
local bypass_protection = args.bypass_protection
|
||||
local node = minetest.get_node_or_nil(pos)
|
||||
if node then
|
||||
-- Set mine animation
|
||||
self.object:set_animation({
|
||||
x = npc.ANIMATION_MINE_START,
|
||||
y = npc.ANIMATION_MINE_END},
|
||||
self.animation.speed_normal, 0)
|
||||
|
||||
-- Check if protection not enforced
|
||||
if not force_dig then
|
||||
-- Try to dig node
|
||||
@ -178,9 +227,9 @@ function npc.actions.dig(self, args)
|
||||
if add_to_inventory then
|
||||
-- Get node drop
|
||||
local drop = minetest.registered_nodes[node.name].drop
|
||||
local drop_itemname = node.name
|
||||
if drop and drop.items then
|
||||
local random_item = drop.items[math.random(1, #drop)]
|
||||
local drop_itemname = node.name
|
||||
if drop and drop.items then
|
||||
local random_item = drop.items[math.random(1, #drop.items)]
|
||||
if random_item then
|
||||
drop_itemname = random_item.items[1]
|
||||
end
|
||||
@ -188,7 +237,8 @@ function npc.actions.dig(self, args)
|
||||
-- Add to NPC inventory
|
||||
npc.add_item_to_inventory(self, drop_itemname, 1)
|
||||
end
|
||||
return true
|
||||
--return true
|
||||
return
|
||||
end
|
||||
else
|
||||
-- Add to inventory
|
||||
@ -202,7 +252,7 @@ function npc.actions.dig(self, args)
|
||||
minetest.set_node(pos, {name="air"})
|
||||
end
|
||||
end
|
||||
return false
|
||||
--return false
|
||||
end
|
||||
|
||||
|
||||
@ -221,11 +271,11 @@ function npc.actions.place(self, args)
|
||||
local bypass_protection = args.bypass_protection
|
||||
local node_at_pos = minetest.get_node_or_nil(pos)
|
||||
-- Check if position is empty or has a node that can be built to
|
||||
if node_at_pos and
|
||||
(node_at_pos.name == "air" or minetest.registered_nodes(node_at_pos.name).buildable_to == true) then
|
||||
if node_at_pos and
|
||||
(node_at_pos.name == "air" or minetest.registered_nodes(node_at_pos.name).buildable_to == true) then
|
||||
-- Check protection
|
||||
if (not bypass_protection and not minetest.is_protected(pos, self.npc_name))
|
||||
or bypass_protection == true then
|
||||
or bypass_protection == true then
|
||||
-- Take from inventory if necessary
|
||||
local place_item = false
|
||||
if source == npc.actions.take_from_inventory then
|
||||
@ -240,16 +290,55 @@ function npc.actions.place(self, args)
|
||||
end
|
||||
-- Place node
|
||||
if place_item == true then
|
||||
-- Set mine animation
|
||||
self.object:set_animation({
|
||||
x = npc.ANIMATION_MINE_START,
|
||||
y = npc.ANIMATION_MINE_END},
|
||||
self.animation.speed_normal, 0)
|
||||
-- Place node
|
||||
minetest.set_node(pos, {name=node})
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- This function allows to move into directions that are walkable. It
|
||||
-- avoids fences and allows to move on plants.
|
||||
-- This will make for nice wanderings, making the NPC move smartly instead
|
||||
-- of just getting stuck at places
|
||||
local function random_dir_helper(start_pos, speed, dir_start, dir_end)
|
||||
-- Limit the number of tries - otherwise it could become an infinite loop
|
||||
for i = 1, 8 do
|
||||
local dir = math.random(dir_start, dir_end)
|
||||
local vel = vector.multiply(npc.actions.dir_data[dir].vel, speed)
|
||||
local pos = vector.add(start_pos, vel)
|
||||
local node = minetest.get_node(pos)
|
||||
if node then
|
||||
if node.name == "air"
|
||||
-- Any walkable node except fences
|
||||
or (minetest.registered_nodes[node.name].walkable == true
|
||||
and minetest.registered_nodes[node.name].groups.fence ~= 1)
|
||||
-- Farming plants
|
||||
or minetest.registered_nodes[node.name].groups.plant == 1 then
|
||||
return dir
|
||||
end
|
||||
end
|
||||
end
|
||||
-- Return -1 signaling that no good direction could be found
|
||||
return -1
|
||||
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(self, args)
|
||||
local dir = args.dir
|
||||
local start_pos = args.start_pos
|
||||
local end_pos = args.end_pos
|
||||
-- Calculate dir if positions are given
|
||||
if start_pos and end_pos and not dir then
|
||||
dir = npc.actions.get_direction(start_pos, end_pos)
|
||||
end
|
||||
|
||||
local yaw = 0
|
||||
self.rotate = 0
|
||||
if dir == npc.direction.north then
|
||||
@ -277,6 +366,7 @@ end
|
||||
-- true if it can move on that direction, and false if there is an obstacle
|
||||
function npc.actions.walk_step(self, args)
|
||||
local dir = args.dir
|
||||
local step_into_air_only = args.step_into_air_only
|
||||
local speed = args.speed
|
||||
local target_pos = args.target_pos
|
||||
local start_pos = args.start_pos
|
||||
@ -287,12 +377,12 @@ function npc.actions.walk_step(self, args)
|
||||
speed = npc.actions.one_nps_speed
|
||||
end
|
||||
|
||||
-- Set is_walking = true
|
||||
self.actions.walking.is_walking = true
|
||||
|
||||
-- Check if dir should be random
|
||||
if dir == "random" then
|
||||
dir = math.random(0, 7)
|
||||
if dir == "random_all" or dir == "random" then
|
||||
dir = random_dir_helper(start_pos, speed, 0, 7)
|
||||
end
|
||||
if dir == "random_orthogonal" then
|
||||
dir = random_dir_helper(start_pos, speed, 0, 3)
|
||||
end
|
||||
|
||||
if dir == npc.direction.north then
|
||||
@ -311,17 +401,16 @@ function npc.actions.walk_step(self, args)
|
||||
vel = {x=-speed, y=0, z=0}
|
||||
elseif dir == npc.direction.north_west then
|
||||
vel = {x=-speed, y=0, z=speed }
|
||||
else
|
||||
-- No direction provided or NPC is trapped, don't move NPC
|
||||
vel = {x=0, y=0, z=0}
|
||||
end
|
||||
|
||||
-- Automatically calculate target pos for dir = "random"
|
||||
if args.dir == "random" and not target_pos and start_pos then
|
||||
--minetest.log("Vel: "..dump(vel.x/speed)..", "..dump(vel.y/speed)..", "..dump(vel.z/speed))
|
||||
target_pos = {x=start_pos.x + (vel.x/speed), y=start_pos.y + (vel.y/speed), z=start_pos.z + (vel.z/speed)}
|
||||
end
|
||||
|
||||
-- If there is a target position to reach, set it
|
||||
-- If there is a target position to reach, set it and set walking to true
|
||||
if target_pos ~= nil then
|
||||
self.actions.walking.target_pos = target_pos
|
||||
-- Set is_walking = true
|
||||
self.actions.walking.is_walking = true
|
||||
end
|
||||
|
||||
-- Rotate NPC
|
||||
@ -330,37 +419,37 @@ function npc.actions.walk_step(self, args)
|
||||
self.object:setvelocity(vel)
|
||||
-- Set walk animation
|
||||
self.object:set_animation({
|
||||
x = npc.ANIMATION_WALK_START,
|
||||
y = npc.ANIMATION_WALK_END},
|
||||
self.animation.speed_normal, 0)
|
||||
x = npc.ANIMATION_WALK_START,
|
||||
y = npc.ANIMATION_WALK_END},
|
||||
self.animation.speed_normal, 0)
|
||||
end
|
||||
|
||||
-- This action makes the NPC stand and remain like that
|
||||
function npc.actions.stand(self, args)
|
||||
local pos = args.pos
|
||||
local dir = args.dir
|
||||
-- Set is_walking = true
|
||||
self.actions.walking.is_walking = false
|
||||
-- Set is_walking = false
|
||||
self.actions.walking.is_walking = false
|
||||
-- Stop NPC
|
||||
self.object:setvelocity({x=0, y=0, z=0})
|
||||
-- If position given, set to that position
|
||||
if pos ~= nil then
|
||||
self.object:moveto(pos)
|
||||
end
|
||||
-- If dir given, set to that dir
|
||||
-- If dir given, set to that dir
|
||||
if dir ~= nil then
|
||||
npc.actions.rotate(self, {dir=dir})
|
||||
end
|
||||
-- Set stand animation
|
||||
self.object:set_animation({
|
||||
x = npc.ANIMATION_STAND_START,
|
||||
y = npc.ANIMATION_STAND_END},
|
||||
self.animation.speed_normal, 0)
|
||||
x = npc.ANIMATION_STAND_START,
|
||||
y = npc.ANIMATION_STAND_END},
|
||||
self.animation.speed_normal, 0)
|
||||
end
|
||||
|
||||
-- This action makes the NPC sit on the node where it is
|
||||
function npc.actions.sit(self, args)
|
||||
local pos = args.pos
|
||||
local pos = args.pos
|
||||
local dir = args.dir
|
||||
-- Stop NPC
|
||||
self.object:setvelocity({x=0, y=0, z=0})
|
||||
@ -374,9 +463,9 @@ function npc.actions.sit(self, args)
|
||||
end
|
||||
-- Set sit animation
|
||||
self.object:set_animation({
|
||||
x = npc.ANIMATION_SIT_START,
|
||||
y = npc.ANIMATION_SIT_END},
|
||||
self.animation.speed_normal, 0)
|
||||
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
|
||||
@ -390,9 +479,9 @@ function npc.actions.lay(self, args)
|
||||
end
|
||||
-- Set sit animation
|
||||
self.object:set_animation({
|
||||
x = npc.ANIMATION_LAY_START,
|
||||
y = npc.ANIMATION_LAY_END},
|
||||
self.animation.speed_normal, 0)
|
||||
x = npc.ANIMATION_LAY_START,
|
||||
y = npc.ANIMATION_LAY_END},
|
||||
self.animation.speed_normal, 0)
|
||||
end
|
||||
|
||||
-- Inventory functions for players and for nodes
|
||||
@ -424,7 +513,7 @@ function npc.actions.put_item_on_external_inventory(self, args)
|
||||
end
|
||||
-- Add items to external inventory
|
||||
inv:add_item(inv_list, item)
|
||||
|
||||
|
||||
-- If this is a furnace, start furnace timer
|
||||
if is_furnace == true then
|
||||
minetest.get_node_timer(pos):start(1.0)
|
||||
@ -600,15 +689,15 @@ function npc.actions.use_furnace(self, args)
|
||||
-- Define which items are usable as fuels. The NPC
|
||||
-- will mainly use this as fuels to avoid getting useful
|
||||
-- items (such as coal lumps) for burning
|
||||
local fuels = {"default:leaves",
|
||||
"default:pine_needles",
|
||||
"default:tree",
|
||||
"default:acacia_tree",
|
||||
"default:aspen_tree",
|
||||
"default:jungletree",
|
||||
"default:pine_tree",
|
||||
"default:coalblock",
|
||||
"farming:straw"}
|
||||
local fuels = {"default:leaves",
|
||||
"default:pine_needles",
|
||||
"default:tree",
|
||||
"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))
|
||||
@ -619,23 +708,23 @@ function npc.actions.use_furnace(self, args)
|
||||
|
||||
-- Check if NPC has a fuel item
|
||||
for i = 1,9 do
|
||||
local fuel_item = npc.inventory_contains(self, fuels[i])
|
||||
|
||||
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 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)
|
||||
npc.log("DEBUG", "Fuel time: "..dump(fuel_time))
|
||||
|
||||
-- Get item to cook's cooking time
|
||||
local cook_result =
|
||||
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)
|
||||
npc.log("DEBUG", "Cook: "..dump(cook_result))
|
||||
|
||||
npc.log("DEBUG", "Total cook time: "..total_cook_time
|
||||
..", total fuel burn time: "..dump(total_fuel_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
|
||||
@ -658,21 +747,21 @@ function npc.actions.use_furnace(self, args)
|
||||
|
||||
-- Put this item on the fuel inventory list of the furnace
|
||||
local args = {
|
||||
player = nil,
|
||||
pos = pos,
|
||||
inv_list = "fuel",
|
||||
item_name = npc.get_item_name(fuel_item.item_string),
|
||||
count = fuel_amount
|
||||
player = nil,
|
||||
pos = pos,
|
||||
inv_list = "fuel",
|
||||
item_name = npc.get_item_name(fuel_item.item_string),
|
||||
count = fuel_amount
|
||||
}
|
||||
npc.add_action(self, npc.actions.cmd.PUT_ITEM, args)
|
||||
-- Put the item that we want to cook on the furnace
|
||||
args = {
|
||||
player = nil,
|
||||
pos = pos,
|
||||
inv_list = "src",
|
||||
item_name = npc.get_item_name(src_item.item_string),
|
||||
count = npc.get_item_count(item),
|
||||
is_furnace = true
|
||||
player = nil,
|
||||
pos = pos,
|
||||
inv_list = "src",
|
||||
item_name = npc.get_item_name(src_item.item_string),
|
||||
count = npc.get_item_count(item),
|
||||
is_furnace = true
|
||||
}
|
||||
npc.add_action(self, npc.actions.cmd.PUT_ITEM, args)
|
||||
|
||||
@ -692,13 +781,13 @@ function npc.actions.use_furnace(self, args)
|
||||
|
||||
-- Take cooked items back
|
||||
args = {
|
||||
|
||||
player = nil,
|
||||
pos = pos,
|
||||
inv_list = "dst",
|
||||
item_name = cook_result.item:get_name(),
|
||||
count = npc.get_item_count(item),
|
||||
is_furnace = false
|
||||
|
||||
player = nil,
|
||||
pos = pos,
|
||||
inv_list = "dst",
|
||||
item_name = cook_result.item:get_name(),
|
||||
count = npc.get_item_count(item),
|
||||
is_furnace = false
|
||||
}
|
||||
npc.log("DEBUG", "Taking item back: "..minetest.pos_to_string(pos))
|
||||
npc.add_action(self, npc.actions.cmd.TAKE_ITEM, args)
|
||||
@ -743,7 +832,7 @@ function npc.actions.use_bed(self, args)
|
||||
return
|
||||
end
|
||||
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}
|
||||
local bed_pos = {x = pos.x, y = bed_pos_y, z = pos.z}
|
||||
-- Sit up
|
||||
npc.add_action(self, npc.actions.cmd.SIT, {pos=bed_pos})
|
||||
-- Initialize direction: Default is front of bottom of bed
|
||||
@ -761,7 +850,7 @@ function npc.actions.use_bed(self, args)
|
||||
end
|
||||
-- Calculate position to get out of bed
|
||||
local pos_out_of_bed =
|
||||
{x=empty_nodes[1].pos.x, y=empty_nodes[1].pos.y + 1, z=empty_nodes[1].pos.z}
|
||||
{x=empty_nodes[1].pos.x, y=empty_nodes[1].pos.y + 1, z=empty_nodes[1].pos.z}
|
||||
-- Account for benches if they are present to avoid standing over them
|
||||
if empty_nodes[1].name == "cottages:bench" then
|
||||
pos_out_of_bed = {x=empty_nodes[1].pos.x, y=empty_nodes[1].pos.y + 1, z=empty_nodes[1].pos.z}
|
||||
@ -860,7 +949,7 @@ end
|
||||
-- path.
|
||||
function npc.actions.walk_to_pos(self, args)
|
||||
-- Get arguments for this task
|
||||
local end_pos = get_pos_argument(self, args.end_pos, args.use_access_node)
|
||||
local end_pos = get_pos_argument(self, args.end_pos, args.use_access_node or true)
|
||||
if end_pos == nil then
|
||||
npc.log("WARNING", "Got nil position in 'walk_to_pos' using args.pos: "..dump(args.end_pos))
|
||||
return
|
||||
@ -968,6 +1057,6 @@ function npc.actions.walk_to_pos(self, args)
|
||||
if enforce_move then
|
||||
-- Move to end pos
|
||||
self.object:moveto({x=end_pos.x, y=end_pos.y+1, z=end_pos.z})
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue
Block a user