2015-07-29 20:42:45 +01:00

280 lines
7.3 KiB
Lua

local MAX_OBJECT_COUNT = 10
local WALKING_SPEED = 2.5
local RUNNING_SPEED = 4
local ENABLE_TNT = minetest.get_modpath("tnt") ~= nil
local ATTACK_RADIUS = NPCF_RELOAD_DISTANCE + 10
local TARGET_RADIUS = 20
local TARGET_CHANCE = 1000
local TARGET_TIME_MIN = 60
local TARGET_TIME_MAX = 300
local SPAWN_CHANCE = 3
local SPAWN_UPDATE_TIME = 4
local SPAWN_NODES = {
"default:dirt_with_grass",
"default:cobble",
"default:sand",
"default:desert_sand",
"default:desert_stone",
}
local AVOIDED_NODES = {
"ignore",
"default:water_source",
"default:water_flowing",
"default:lava_source",
"default:lava_flowing",
}
local spawn_timer = 0
local target_players = {}
local function get_target_player(pos)
local target_player = nil
local min_dist = ATTACK_RADIUS
for _,player in ipairs(minetest.get_connected_players()) do
if player then
local player_pos = player:getpos()
local hp = player:get_hp() or 0
if player_pos and hp > 0 then
local dist = vector.distance(pos, player_pos)
if dist < min_dist then
target_player = player
min_dist = dist
end
end
end
end
return target_player
end
local function spawn_mob(pos)
if minetest.get_node_light(pos) < 10 then
return
end
for i = 1, MAX_OBJECT_COUNT do
local id = ":npcf_mob_"..i
if not npcf.npcs[id] then
local yaw = math.rad(math.random(360))
local ref = {
id = id,
pos = pos,
yaw = yaw,
name = "npcf_mob:npc",
}
local npc = npcf:add_npc(ref)
if npc then
npc:update()
end
break
end
end
end
npcf:register_npc("npcf_mob:npc", {
description = "Mob NPC",
textures = {"npcf_mob_skin.png"},
collisionbox = {-0.35,-1.0,-0.35, 0.35,0.5,0.35},
animation_speed = 25,
metadata = {
anim_stop = "Stand",
},
var = {
speed = WALKING_SPEED,
avoid_dir = 1,
last_pos = {x=0,y=0,z=0},
target = nil,
},
stepheight = 1.1,
register_spawner = false,
armor_groups = {fleshy=100},
on_update = function(npc)
if math.random(5) == 1 then
if not get_target_player(npc.pos) then
if npc.object then
npc.object:remove()
end
npcf.npcs[npc.id] = nil
end
end
end,
on_activate = function(self)
self.object:setvelocity({x=0, y=0, z=0})
self.object:setacceleration({x=0, y=-10, z=0})
end,
on_destruct = function(self, hitter)
local id = self.npc_id
if id then
npcf.npcs[id] = nil
end
local pos = self.object:getpos()
if pos then
minetest.add_particlespawner(
50, 1, pos, pos,
{x=-3, y=3, z=-3}, {x=3, y=3, z=3},
{x=-2, y=-2, z=-2}, {x=2, y=-2, z=2},
0.1, 0.75, 2, 8, false, "npcf_mob_particle.png"
)
if ENABLE_TNT == true then
pos.y = pos.y - 1
minetest.add_node(pos, {name="tnt:tnt_burning"})
minetest.get_node_timer(pos):start(1)
else
local player_pos = hitter:getpos()
if player_pos then
local dist = vector.distance(pos, player_pos)
local damage = (50 * 0.5 ^ dist) * 2
hitter:punch(self.object, 1.0, {
full_punch_interval = 1.0,
damage_groups = {fleshy=damage},
})
end
end
end
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()
speed = self.var.speed
if math.random(5) == 1 then
if speed == 0 or speed == RUNNING_SPEED then
speed = WALKING_SPEED
elseif math.random(3) == 1 then
speed = RUNNING_SPEED
elseif math.random(10) == 1 then
speed = 0
end
elseif math.random(30) == 1 then
self.var.avoid_dir = self.var.avoid_dir * -1
end
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) < 2 then
speed = 0
self.object:punch(self.var.target, 1.0, {
full_punch_interval = 1.0,
damage_groups = {fleshy=20},
})
end
end
end
if math.random(10) == 1 or valid_target == false then
self.var.target = get_target_player(pos)
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, 1)
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 self.var.target == nil then
speed = 0
end
self.var.speed = speed
self.object:setyaw(yaw)
end
self.var.last_pos = pos
if speed > 0 then
npcf:set_animation(self, NPCF_ANIM_WALK)
else
npcf:set_animation(self, NPCF_ANIM_STAND)
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,
})
minetest.register_globalstep(function(dtime)
spawn_timer = spawn_timer + dtime
if spawn_timer > SPAWN_UPDATE_TIME then
for _,player in ipairs(minetest.get_connected_players()) do
if player then
local name = player:get_player_name()
local hp = player:get_hp() or 0
if name and hp > 0 then
if not target_players[name] then
if math.random(TARGET_CHANCE) == 1 then
local time = TARGET_TIME_MAX - TARGET_TIME_MIN
target_players[name] = math.random(time) + TARGET_TIME_MIN
end
end
end
if target_players[name] and math.random(SPAWN_CHANCE) == 1 then
local pos = player:getpos()
if pos then
local angle = math.rad(math.random(360))
local x = pos.x + math.cos(angle) * TARGET_RADIUS
local z = pos.z + math.sin(angle) * TARGET_RADIUS
local p1 = {x=x, y=pos.y + TARGET_RADIUS, z=z}
local p2 = {x=x, y=pos.y - TARGET_RADIUS, z=z}
local res, spawn_pos = minetest.line_of_sight(p1, p2, 1)
if spawn_pos then
local node = minetest.get_node(spawn_pos)
for _, v in ipairs(SPAWN_NODES) do
if node.name == v then
spawn_pos.y = spawn_pos.y + 1.5
spawn_mob(spawn_pos)
break
end
end
end
end
target_players[name] = target_players[name] - dtime
if target_players[name] <= 0 then
target_players[name] = nil
end
end
end
end
spawn_timer = 0
end
end)
minetest.register_on_dieplayer(function(player)
for _, npc in pairs(npcf.npcs) do
if npc.object and npc.name == "npcf_mob:npc" then
npc.var.target = get_target_player(npc.pos)
end
end
end)