very VIP version; already working to some degree
This commit is contained in:
parent
5425c0269b
commit
7f8e486d48
|
@ -0,0 +1,88 @@
|
|||
-- which NPC object did the player talk to?
|
||||
guards.player_talked_to = {}
|
||||
|
||||
|
||||
guards.on_receive_fields = function(player, formname, fields)
|
||||
local name = player:get_player_name()
|
||||
local self = guards.player_talked_to[name]
|
||||
if(not(self)) then
|
||||
return
|
||||
end
|
||||
if self.owner ~= name then
|
||||
return
|
||||
end
|
||||
if fields.origin then
|
||||
self.origin.pos = self.object:get_pos()
|
||||
self.origin.yaw = self.object:get_yaw()
|
||||
end
|
||||
if fields.follow_owner and fields.follow_owner == "true" then
|
||||
self.metadata.patrol = "false"
|
||||
self.order = "follow"
|
||||
self.following = player
|
||||
minetest.chat_send_player(name,
|
||||
"New Order: "..tostring(self.order)..
|
||||
" State: "..tostring(self.state)..
|
||||
" Owner: "..tostring(self.owner))
|
||||
elseif fields.walk_speed and fields.walk_speed ~= "" and fields.walk_speed ~= tostring(self.walk_velocity) then
|
||||
self.walk_velocity = math.max(1, tonumber(fields.walk_speed)) * 1.0
|
||||
elseif fields.fear_height and fields.fear_height ~= "" and fields.fear_height ~= tostring(self.fear_height) then
|
||||
self.fear_height = math.max(1, tonumber(fields.fear_height))
|
||||
elseif fields.follow_owner and fields.follow_owner == "false" then
|
||||
self.order = "stand"
|
||||
self.following = nil
|
||||
elseif fields.patrol then
|
||||
if fields.patrol == "false" then
|
||||
self.metadata.patrol_index = 0
|
||||
end
|
||||
self.order = "patrol"
|
||||
self.following = nil
|
||||
elseif fields.add_patrol then
|
||||
local pos = self.object:get_pos()
|
||||
if pos then
|
||||
table.insert(self.metadata.patrol_points, pos)
|
||||
end
|
||||
elseif fields.clear_patrol then
|
||||
self.metadata.patrol_points = {}
|
||||
else
|
||||
return
|
||||
end
|
||||
minetest.show_formspec(name, "guards:main", guards.get_formspec(self, player))
|
||||
end
|
||||
|
||||
|
||||
guards.get_formspec = function(self, clicker)
|
||||
return "size[8,8.5]"
|
||||
.."checkbox[0.5,4.5;follow_owner;Follow;"..tostring(self.order == "follow").."]"
|
||||
.."checkbox[3.5,4.5;patrol;Patrol;"..self.metadata.patrol.."]"
|
||||
.."field[6.0,2.5;2.0.0,0.5;walk_speed;Walking speed;"..tostring(self.walk_velocity).."]"
|
||||
.."field[6.0,4.0;2.0.0,0.5;fear_height;Fear height;"..tostring(self.fear_height).."]"
|
||||
.."field[6.0,5.5;2.0.0,0.5;rest_time;Rest (sec);"..self.metadata.patrol_rest.."]"
|
||||
.."label[0.5,6.5;Patrol Points: "..#self.metadata.patrol_points.."]"
|
||||
.."button[5.5,6.5;2.5,0.5;add_patrol;Add Point]"
|
||||
.."button[3.5,6.5;2.0,0.5;clear_patrol;Clear]"
|
||||
.."button[0.0,8.0;2.0,0.5;origin;Set Origin]"
|
||||
.."button_exit[7.0,8.0;1.0,0.5;;Ok]"
|
||||
end
|
||||
|
||||
|
||||
guards.on_rightclick = function(self, clicker)
|
||||
if(not(clicker)) then
|
||||
return
|
||||
end
|
||||
local name = clicker:get_player_name()
|
||||
if name and name == self.owner then
|
||||
-- catch the guard with a lasso
|
||||
if mobs:capture_mob(self, clicker, nil, 100, 100, true, nil) then
|
||||
return
|
||||
end
|
||||
guards.player_talked_to[name] = self
|
||||
minetest.show_formspec(name, "guards:main", guards.get_formspec(self, clicker))
|
||||
end
|
||||
end
|
||||
|
||||
minetest.register_on_player_receive_fields( function(player, formname, fields)
|
||||
if(formname == "guards:main") then
|
||||
guards.on_receive_fields(player, formname, fields)
|
||||
return true
|
||||
end
|
||||
end)
|
|
@ -0,0 +1,87 @@
|
|||
|
||||
guards = {}
|
||||
|
||||
local abs = math.abs
|
||||
|
||||
dofile(minetest.get_modpath("guards").."/formspec.lua");
|
||||
dofile(minetest.get_modpath("guards").."/patrol.lua");
|
||||
|
||||
guards.on_spawn = function(self)
|
||||
-- npcf-guard-specific data:
|
||||
self.metadata = {
|
||||
attack_players = "false",
|
||||
patrol = "false",
|
||||
patrol_points = {},
|
||||
patrol_index = 0,
|
||||
patrol_rest = 2,
|
||||
show_armor = "true",
|
||||
}
|
||||
self.var = {
|
||||
rest_timer = 0,
|
||||
}
|
||||
self.origin = {
|
||||
pos = self.object:get_pos(),
|
||||
yaw = self.object:get_yaw(),
|
||||
}
|
||||
return true
|
||||
end
|
||||
|
||||
guards.guard_data = {
|
||||
-- type needs to be "npc" - else it doesn't know how to follow
|
||||
type = "npc", -- "guard",
|
||||
passive = true,
|
||||
damage = 9,
|
||||
attack_type = "dogfight",
|
||||
attacks_monsters = true,
|
||||
attack_npcs = false,
|
||||
owner_loyal = false,
|
||||
pathfinding = false,
|
||||
hp_min = 60,
|
||||
hp_max = 100,
|
||||
armor = 0,
|
||||
collisionbox = {-0.3, 0.0, -0.3, 0.3, 1.7, 0.3},
|
||||
visual = "mesh",
|
||||
visual_size = {x = 1, y = 1},
|
||||
mesh = "skinsdb_3d_armor_character_5.b3d",
|
||||
drawtype = "front",
|
||||
textures = {{
|
||||
"blank.png", -- cape?
|
||||
-- "yl_speak_up_main_default.png", -- 64x64 skin
|
||||
"2019_05_19_survivalist-dave-slim-13008091.png",
|
||||
"3d_armor_trans.png", -- shield?!
|
||||
"3d_armor_trans.png", -- item right hand
|
||||
}},
|
||||
makes_footstep_sound = true,
|
||||
sounds = {},
|
||||
walk_velocity = 2,
|
||||
run_velocity = 3,
|
||||
jump = false,
|
||||
water_damage = 0,
|
||||
lava_damage = 0,
|
||||
light_damage = 0,
|
||||
view_range = 4,
|
||||
owner = "Guard Guild",
|
||||
order = "stand",
|
||||
fear_height = 3,
|
||||
animation = {
|
||||
speed_normal = 30,
|
||||
speed_run = 30,
|
||||
stand_start = 0,
|
||||
stand_end = 79,
|
||||
walk_start = 168,
|
||||
walk_end = 187,
|
||||
run_start = 168,
|
||||
run_end = 187,
|
||||
punch_start = 200,
|
||||
punch_end = 219,
|
||||
},
|
||||
|
||||
-- show the formspec
|
||||
on_rightclick = guards.on_rightclick,
|
||||
-- set up metadata
|
||||
on_spawn = guards.on_spawn,
|
||||
do_custom = guards.on_step,
|
||||
}
|
||||
|
||||
mobs:register_mob("guards:guard", guards.guard_data)
|
||||
mobs:register_egg("guards:guard", "Guard", "wool_grey.png", 1)
|
|
@ -0,0 +1,6 @@
|
|||
author = Sokomine
|
||||
description = Guards that can patrol (based on npcf)
|
||||
release = 20220226
|
||||
title = Guards
|
||||
name = guards
|
||||
depends = mobs, mob_world_interaction
|
|
@ -0,0 +1,236 @@
|
|||
local abs = math.abs
|
||||
|
||||
|
||||
guards.get_walkable_pos = function(pos, dist)
|
||||
local destpos
|
||||
local rpos = vector.round(pos)
|
||||
for y = rpos.y+dist-1, rpos.y-dist-1, -1 do
|
||||
for x = rpos.x-dist, rpos.x+dist do
|
||||
for z = rpos.z-dist, rpos.z+dist do
|
||||
local p = {x=x, y=y, z=z}
|
||||
local node = minetest.get_node(p)
|
||||
local nodedef = minetest.registered_nodes[node.name]
|
||||
if not (node.name == "air" or nodedef and (nodedef.walkable == false or nodedef.drawtype == "airlike")) then
|
||||
p.y = p.y +1
|
||||
local node = minetest.get_node(p)
|
||||
local nodedef = minetest.registered_nodes[node.name]
|
||||
if node.name == "air" or nodedef and (nodedef.walkable == false or nodedef.drawtype == "airlike") then
|
||||
if destpos == nil or vector.distance(p, pos) < vector.distance(destpos, pos) then
|
||||
destpos = p
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return destpos
|
||||
end
|
||||
|
||||
guards.get_path = function(self, pos)
|
||||
-- minetest.chat_send_all("GUARDS get_path from "..
|
||||
-- minetest.serialize(spos).." to "..
|
||||
-- minetest.serialize(pos))
|
||||
local spos = self.object:get_pos()
|
||||
local startpos = vector.round(spos)
|
||||
startpos.y = startpos.y - 1 -- NPC is to high
|
||||
local refpos
|
||||
if vector.distance(spos, pos) > 20 then
|
||||
refpos = vector.add(spos, vector.multiply(vector.direction(spos, pos), 20))
|
||||
else
|
||||
refpos = pos
|
||||
end
|
||||
|
||||
local destpos = guards.get_walkable_pos(refpos, 2)
|
||||
if not destpos then
|
||||
destpos = spos
|
||||
end
|
||||
startpos.y = startpos.y + 1
|
||||
local max_drop = (self.fear_height or 6) - 1
|
||||
local path = minetest.find_path(startpos, destpos, 30, 1, max_drop, "A*_noprefetch")
|
||||
-- local path = mob_world_interaction.find_path(startpos, destpos, { collisionbox = {1,0,3,4,2}});
|
||||
-- minetest.chat_send_player("singleplayer", "Path found: "..minetest.serialize(path).." from: "..minetest.pos_to_string(startpos).." to: "..minetest.pos_to_string(destpos))
|
||||
|
||||
if not path then
|
||||
path = { destpos, pos }
|
||||
--print("fallback path to "..minetest.pos_to_string(pos))
|
||||
elseif path then
|
||||
--print("calculated path to "..minetest.pos_to_string(destpos).."for destination"..minetest.pos_to_string(pos))
|
||||
table.insert(path, pos)
|
||||
end
|
||||
|
||||
--[[
|
||||
-- from below the NPC to below the target point
|
||||
destpos.y = destpos.y - 1
|
||||
-- without objects
|
||||
local ray = Raycast(startpos, destpos, false, true)
|
||||
for pointed_thing in ray do
|
||||
minetest.chat_send_player("singleplayer", "Ray: "..minetest.serialize(pointed_thing))
|
||||
end
|
||||
--]]
|
||||
return path
|
||||
end
|
||||
|
||||
|
||||
|
||||
-- the mob first tires to walk in a straight line;
|
||||
-- if that fails (cliff, collusion, stuck) it will switch to a pahtfinder
|
||||
guards.switch_to_pathfinding = function(self)
|
||||
-- the old path no longer works; search a new one
|
||||
self.follow_path = guards.get_path(self, self.follow_path[#self.follow_path])
|
||||
self.path_stuck_timer = 0
|
||||
-- at_cliff isn't updated that frequently; wait a bit
|
||||
self.wait_a_bit = 2.0
|
||||
end
|
||||
|
||||
|
||||
-- taken from npcf
|
||||
guards.get_face_direction = function(self, p1, p2)
|
||||
if p1 and p2 and p1.x and p2.x and p1.z and p2.z then
|
||||
local px = p1.x - p2.x
|
||||
local pz = p2.z - p1.z
|
||||
return math.atan2(px, pz)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
guards.on_step = function(self, dtime, moveresult)
|
||||
if(self.order ~= "patrol") then
|
||||
return true
|
||||
end
|
||||
|
||||
self.ptimer = (self.ptimer or 0) + dtime
|
||||
-- if self.ptimer < 0.2 then
|
||||
-- return true
|
||||
-- end
|
||||
local pos = self.object:get_pos()
|
||||
|
||||
if(not(self.path_stuck_timer)) then
|
||||
self.path_stuck_timer = 0
|
||||
end
|
||||
-- the NPC has a path which it shall follow
|
||||
if(self.follow_path and #self.follow_path > 0) then
|
||||
local p_next = self.follow_path[1]
|
||||
local dist = abs(pos.x - p_next.x) + abs(pos.z - p_next.z) + abs(pos.y - p_next.y)
|
||||
local last_pos = self.path_lastpos
|
||||
|
||||
if(dist < 0.8) then
|
||||
-- minetest.set_node({x=p_next.x, y=p_next.y-1, z=p_next.z}, {name="wool:orange"})
|
||||
table.remove(self.follow_path , 1)
|
||||
-- not stuck - temporary target reached
|
||||
self.path_stuck_timer = 0
|
||||
self.wait_a_bit = 0
|
||||
elseif(self.wait_a_bit and self.wait_a_bit > 0) then
|
||||
self.wait_a_bit = self.wait_a_bit - dtime
|
||||
elseif(self.at_cliff) then
|
||||
minetest.chat_send_player("singleplayer", "There's a cliff! Searching new path.")
|
||||
guards.switch_to_pathfinding(self)
|
||||
elseif(last_pos and (abs(last_pos.x - pos.x) + abs(last_pos.z - pos.z) + abs(last_pos.y - pos.y )< 0.3)) then
|
||||
self.path_stuck_timer = self.path_stuck_timer + dtime
|
||||
if self.path_stuck_timer > 5.0 then
|
||||
local coll_node = 0
|
||||
local coll_obj = 0
|
||||
if(moveresult and moveresult.collisions and #moveresult.collisions > 1) then
|
||||
for i, v in ipairs(moveresult.collisions) do
|
||||
if(v.type and v.type == "object") then
|
||||
coll_obj = coll_obj + 1
|
||||
elseif(v.type and v.type == "node") then
|
||||
coll_node = coll_node + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
minetest.chat_send_player("singleplayer", "I'm stuck! Searching new path."..
|
||||
" Objects: "..tostring(coll_obj).." Nodes: "..tostring(coll_node))
|
||||
-- if(coll_node > 1) then
|
||||
guards.switch_to_pathfinding(self)
|
||||
-- else
|
||||
---- minetest.chat_send_player("singleplayer", "I'm stuck! There's something in the way.")
|
||||
-- self.wait_a_bit = 5.0
|
||||
-- end
|
||||
end
|
||||
else
|
||||
-- not stuck - just walking to temporary target
|
||||
self.path_stuck_timer = 0
|
||||
end
|
||||
-- store last postion to detect if we get stuck
|
||||
self.path_lastpos = {x = pos.x, y = pos.y, z = pos.z}
|
||||
|
||||
-- either an error occoured..
|
||||
if(not(self.follow_path)
|
||||
-- ..or we reached the end of the path
|
||||
or #self.follow_path < 1) then
|
||||
if(self.follow_path and #self.follow_path < 1) then
|
||||
self.state = "position_reached"
|
||||
elseif(self.at_cliff) then
|
||||
self.state = "failed_goto_because_cliff"
|
||||
elseif(self.path_stuck_timer > 0) then
|
||||
self.state = "failed_goto_because_stuck"
|
||||
end
|
||||
-- minetest.chat_send_player("singleplayer","Guard: "..tostring(self.state))
|
||||
self.path_stuck_timer = 0
|
||||
self.follow_path = nil
|
||||
self:set_velocity(0)
|
||||
self:set_animation("stand")
|
||||
|
||||
-- normal walking along the path
|
||||
-- TODO: do this only when starting to walk?
|
||||
else
|
||||
-- go to the next position that belongs to the path
|
||||
p_next = self.follow_path[1]
|
||||
|
||||
local yaw = guards.get_face_direction(self, pos, p_next)
|
||||
self.object:set_yaw(yaw)
|
||||
if(self.wait_a_bit and self.wait_a_bit > 0) then
|
||||
self:set_velocity(0)
|
||||
self:set_animation("stand")
|
||||
else
|
||||
self.wait_a_bit = nil
|
||||
self:set_velocity(self.walk_velocity)
|
||||
self:set_animation("walk")
|
||||
self.state = "follow_path"
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
elseif(self.order == "patrol") then
|
||||
self.var.rest_timer = self.var.rest_timer + self.ptimer
|
||||
if(self.state == "position_reached") then
|
||||
local index = self.metadata.patrol_index
|
||||
local patrol_pos = self.metadata.patrol_points[index]
|
||||
self.object:set_pos(patrol_pos)
|
||||
self.path_stuck_timer = 0
|
||||
self.state = "patrol_waiting"
|
||||
self:set_velocity(0)
|
||||
self:set_animation("stand")
|
||||
elseif self.var.rest_timer > self.metadata.patrol_rest then
|
||||
local index = self.metadata.patrol_index + 1
|
||||
if index > #self.metadata.patrol_points then
|
||||
index = 1
|
||||
end
|
||||
local patrol_pos = self.metadata.patrol_points[index]
|
||||
if patrol_pos then
|
||||
self.metadata.patrol_index = index
|
||||
self.follow_path = {patrol_pos}
|
||||
self.state = "goto_position"
|
||||
self.var.rest_timer = 0
|
||||
|
||||
--[[
|
||||
local distance = vector.distance(pos, patrol_pos)
|
||||
minetest.chat_send_player("singleplayer","Guard patrol: "..minetest.pos_to_string(patrol_pos).." d: "..tostring(distance))
|
||||
if distance > 1.0 then
|
||||
-- start by trying to walk directly
|
||||
self.follow_path = {patrol_pos}
|
||||
else
|
||||
self.object:set_pos(patrol_pos)
|
||||
self.metadata.patrol_index = index
|
||||
self.var.rest_timer = 0
|
||||
self:set_velocity(0)
|
||||
self:set_animation("stand")
|
||||
end
|
||||
--]]
|
||||
end
|
||||
end
|
||||
end
|
||||
self.ptimer = 0
|
||||
-- return false
|
||||
return true
|
||||
end
|
Loading…
Reference in New Issue