208 lines
6.0 KiB
Lua
208 lines
6.0 KiB
Lua
local FREE_ROAMING = true
|
|
local WALKING_SPEED = 1
|
|
local RUNNING_SPEED = 2
|
|
local FOLLOW_RADIUS_MIN = 5
|
|
local FOLLOW_RADIUS_MAX = 30
|
|
local AVOIDED_NODES = {
|
|
"ignore",
|
|
"default:water_source",
|
|
"default:water_flowing",
|
|
"default:lava_source",
|
|
"default:lava_flowing",
|
|
}
|
|
local ANIMATION = {
|
|
["Stand"] = {id=1, state=NPCF_ANIM_STAND},
|
|
["Sit"] = {id=2, state=NPCF_ANIM_SIT},
|
|
["Lay"] = {id=3, state=NPCF_ANIM_LAY},
|
|
["Mine"] = {id=4, state=NPCF_ANIM_MINE},
|
|
}
|
|
|
|
local function get_target_player(self)
|
|
local target_player = nil
|
|
local min_dist = FOLLOW_RADIUS_MAX
|
|
for _,player in ipairs(minetest.get_connected_players()) do
|
|
if player then
|
|
local pos = player:getpos()
|
|
if pos then
|
|
local dist = vector.distance(pos, pos)
|
|
if dist < min_dist then
|
|
target_player = player
|
|
min_dist = dist
|
|
end
|
|
end
|
|
end
|
|
end
|
|
return target_player
|
|
end
|
|
|
|
npcf:register_npc("npcf_deco:npc" ,{
|
|
description = "Decorative NPC",
|
|
mesh = "npcf_deco.x",
|
|
textures = {"npcf_deco_skin.png"},
|
|
animation_speed = 12,
|
|
animation = {
|
|
stand_START = 0,
|
|
stand_END = 79,
|
|
sit_START = 81,
|
|
sit_END = 160,
|
|
lay_START = 162,
|
|
lay_END = 166,
|
|
walk_START = 168,
|
|
walk_END = 187,
|
|
mine_START = 189,
|
|
mine_END = 198,
|
|
run_START = 221,
|
|
run_END = 240,
|
|
},
|
|
metadata = {
|
|
free_roaming = "false",
|
|
follow_players = "false",
|
|
anim_stop = "Stand",
|
|
},
|
|
var = {
|
|
speed = 1,
|
|
avoid_dir = 1,
|
|
last_pos = {x=0,y=0,z=0},
|
|
target = nil,
|
|
},
|
|
stepheight = 1.1,
|
|
inventory_image = "npcf_deco_inv.png",
|
|
on_activate = function(self)
|
|
self.object:setvelocity({x=0, y=0, z=0})
|
|
self.object:setacceleration({x=0, y=-10, z=0})
|
|
npcf:set_animation(self, ANIMATION[self.metadata.anim_stop].state)
|
|
if self.metadata.follow_players == "true" then
|
|
self.var.target = get_target_player(self)
|
|
end
|
|
end,
|
|
on_rightclick = function(self, clicker)
|
|
local player_name = clicker:get_player_name()
|
|
local message = "Hello"
|
|
if self.metadata.message then
|
|
message = minetest.formspec_escape(self.metadata.message)
|
|
end
|
|
local formspec
|
|
if player_name == self.owner then
|
|
local selected_id = ANIMATION[self.metadata.anim_stop].id or ""
|
|
formspec = "size[8,4.0]"
|
|
.."field[0.5,1.0;7.5,0.5;message;Message;"..message.."]"
|
|
.."label[0.5,1.8;Stationary Animation\\:]"
|
|
.."dropdown[4.0,1.8;3.5;anim_stop;Stand,Sit,Lay,Mine;"..selected_id.."]"
|
|
.."checkbox[0.5,2.7;follow_players;Follow Players;"..self.metadata.follow_players.."]"
|
|
.."button_exit[7.0,3.5;1.0,0.5;;Ok]"
|
|
if FREE_ROAMING == true then
|
|
formspec = formspec.."checkbox[3.5,2.7;free_roaming;Wander Map;"..self.metadata.free_roaming.."]"
|
|
end
|
|
else
|
|
formspec = "size[8,4]".."label[0,0;"..message.."]"
|
|
end
|
|
self.var.speed = 0
|
|
npcf:show_formspec(player_name, self.npc_id, formspec)
|
|
end,
|
|
on_step = function(self, dtime)
|
|
if self.timer > 1 then
|
|
self.timer = 0
|
|
local speed = 0
|
|
local pos = self.object:getpos()
|
|
local yaw = self.object:getyaw()
|
|
local turn = pos.x == self.var.last_pos.x and pos.z == self.var.last_pos.z
|
|
local acceleration = {x=0, y=-10, z=0}
|
|
local velocity = self.object:getvelocity()
|
|
local roaming = FREE_ROAMING == true and self.metadata.free_roaming == "true"
|
|
if roaming == true or self.metadata.follow_players == "true" then
|
|
speed = self.var.speed
|
|
if math.random(10) == 1 then
|
|
if speed == 0 or speed == RUNNING_SPEED then
|
|
speed = WALKING_SPEED
|
|
elseif math.random(5) == 1 then
|
|
speed = RUNNING_SPEED
|
|
elseif math.random(5) == 1 then
|
|
speed = 0
|
|
end
|
|
elseif math.random(30) == 1 then
|
|
self.var.avoid_dir = self.var.avoid_dir * -1
|
|
end
|
|
if self.metadata.follow_players == "true" then
|
|
local valid_target = false
|
|
if self.var.target then
|
|
local target = self.var.target:getpos()
|
|
if target then
|
|
valid_target = true
|
|
yaw = npcf:get_face_direction(pos, target)
|
|
if vector.distance(pos, target) < FOLLOW_RADIUS_MIN then
|
|
speed = 0
|
|
end
|
|
end
|
|
end
|
|
if math.random(10) == 1 or valid_target == false then
|
|
self.var.target = get_target_player(self)
|
|
end
|
|
end
|
|
if speed ~= 0 then
|
|
local node_pos = vector.add(npcf:get_walk_velocity(5, 0, yaw), pos)
|
|
node_pos = vector.round(node_pos)
|
|
local air_content = 0
|
|
for i = 1, 5 do
|
|
local test_pos = {x=node_pos.x, y=node_pos.y-i, z=node_pos.z}
|
|
local node = minetest.get_node(test_pos)
|
|
if node.name == "air" then
|
|
air_content = air_content + 1
|
|
end
|
|
for _, v in ipairs(AVOIDED_NODES) do
|
|
if node.name == v then
|
|
turn = true
|
|
break
|
|
end
|
|
end
|
|
end
|
|
if turn == false then
|
|
local objects = minetest.get_objects_inside_radius(node_pos, 2)
|
|
if #objects > 0 then
|
|
turn = true
|
|
end
|
|
end
|
|
if turn == true or air_content == 5 then
|
|
yaw = yaw + math.pi * 0.5 * self.var.avoid_dir
|
|
speed = WALKING_SPEED
|
|
elseif pos.x == self.var.last_pos.x or pos.z == self.var.last_pos.z then
|
|
yaw = yaw + math.pi * 0.25 * self.var.avoid_dir
|
|
end
|
|
if roaming == true then
|
|
if math.random(4) == 1 then
|
|
yaw = yaw + (math.random(3) - 2) * 0.25
|
|
end
|
|
elseif self.var.target == nil then
|
|
speed = 0
|
|
end
|
|
end
|
|
self.var.speed = speed
|
|
self.object:setyaw(yaw)
|
|
end
|
|
self.var.last_pos = pos
|
|
if speed == 0 then
|
|
npcf:set_animation(self, ANIMATION[self.metadata.anim_stop].state)
|
|
elseif speed == RUNNING_SPEED then
|
|
self.object:set_animation({x=self.animation.run_START, y=self.animation.run_END}, 20)
|
|
self.state = 0
|
|
else
|
|
npcf:set_animation(self, NPCF_ANIM_WALK)
|
|
end
|
|
local node = minetest.get_node(pos)
|
|
if string.find(node.name, "^default:water") then
|
|
acceleration = {x=0, y=-4, z=0}
|
|
velocity = {x=0, y=3, z=0}
|
|
elseif minetest.find_node_near(pos, 2, {"group:water"}) then
|
|
acceleration = {x=0, y=-1, z=0}
|
|
end
|
|
self.object:setvelocity(npcf:get_walk_velocity(speed, velocity.y, yaw))
|
|
self.object:setacceleration(acceleration)
|
|
end
|
|
end,
|
|
on_receive_fields = function(self, fields, sender)
|
|
if fields.free_roaming or fields.follow_players then
|
|
self.var.speed = WALKING_SPEED
|
|
end
|
|
end,
|
|
})
|
|
|