Add stuff?

master
Lean Rada 2015-05-05 20:53:50 +08:00
parent b7556f6e28
commit 27f7336ad7
7 changed files with 375 additions and 71 deletions

View File

@ -28,6 +28,18 @@ director.spawn_list = {
spawn_time = 31.0,
spawn_location = "ground",
},
{
description = "Aranay group",
name = "defense:aranay",
intensity_min = 0.0,
intensity_max = 0.5,
group_min = 1,
group_max = 2,
probability = 0.3,
day_start = 0,
spawn_time = 18.0,
spawn_location = "ground",
},
{
description = "Paniki group",
name = "defense:paniki",
@ -274,9 +286,11 @@ function director:load()
if data then
self.intensity = data.intensity
self.cooldown_timer = data.cooldown_timer
spawn_timers = data.spawn_timers
last_average_health = data.last_average_health
last_mob_count = data.last_mob_count
for k,v in pairs(data.spawn_timers) do
spawn_timers[k] = v
end
end
assert(file:close())
end

View File

@ -37,6 +37,7 @@ dofile2("music.lua")
dofile2("mob.lua")
dofile2("mobs/unggoy.lua")
dofile2("mobs/aranay.lua")
dofile2("mobs/sarangay.lua")
dofile2("mobs/paniki.lua")
dofile2("mobs/botete.lua")

View File

@ -7,7 +7,7 @@ mobs.default_prototype = {
collide_with_objects = true,
makes_footstep_sound = true,
visual = "mesh",
automatic_face_movement_dir = false,
automatic_face_movement_dir = true,
stepheight = 0.6,
-- custom properties
id = 0,
@ -28,17 +28,23 @@ mobs.default_prototype = {
life_timer = 75,
pause_timer = 0,
timer = 0,
-- cache
cache_is_standing = nil,
cache_find_nearest_player = nil,
}
function mobs.default_prototype:on_activate(staticdata)
self.object:set_armor_groups({fleshy = 100 - self.armor})
if self.movement == "ground" then
if self.movement ~= "air" then
self.object:setacceleration({x=0, y=mobs.gravity, z=0})
end
self.id = math.random(0, 100000)
end
function mobs.default_prototype:on_step(dtime)
self.cache_is_standing = nil
self.cache_find_nearest_player = nil
if self.pause_timer <= 0 then
if self.destination then
@ -56,6 +62,13 @@ function mobs.default_prototype:on_step(dtime)
if self.movement ~= "air" and not self:is_standing() then
self:set_animation("fall", {"jump", "attack", "move_attack"})
end
if self.movement == "crawl" then
if self:is_standing() then
self.object:setacceleration({x=0, y=0, z=0})
else
self.object:setacceleration({x=0, y=mobs.gravity, z=0})
end
end
-- Die when morning comes
if not defense:is_dark() then
@ -146,7 +159,8 @@ function mobs.default_prototype:hunt()
end
if direction then
self.destination = vector.add(pos, vector.multiply(direction, 1.25))
minetest.chat_send_all("dir:" .. minetest.pos_to_string(direction))
self.destination = vector.add(pos, vector.multiply(direction, 1.2))
else
local r = math.max(0, self.attack_range - 2)
local dir = vector.direction(nearest.position, self.object:getpos())
@ -154,7 +168,6 @@ function mobs.default_prototype:hunt()
end
end
end
end
function mobs.default_prototype:do_attack(obj)
@ -167,7 +180,6 @@ function mobs.default_prototype:do_attack(obj)
else
self:set_animation("attack")
end
self.object:setyaw(math.atan2(dir.z, dir.x))
end
self.life_timer = math.min(300, self.life_timer + 60)
end
@ -175,6 +187,7 @@ end
function mobs.default_prototype:jump(direction)
if self:is_standing() then
if direction then
direction.y = 0
direction = vector.normalize(direction)
else
direction = {x=0,y=0,z=0}
@ -184,7 +197,6 @@ function mobs.default_prototype:jump(direction)
v.x = direction.x * self.jump_height
v.z = direction.z * self.jump_height
self.object:setvelocity(vector.add(self.object:getvelocity(), v))
self.object:setyaw(math.atan2(direction.z, direction.x))
self:set_animation("jump")
end
end
@ -195,9 +207,26 @@ function mobs.default_prototype:die()
end
function mobs.default_prototype:is_standing()
if self.cache_is_standing ~= nil then
return self.cache_is_standing
end
if self.movement == "air" then
self.cache_is_standing = false
return false
end
if self.movement == "crawl" then
local ret = self:calculate_wall_normal() ~= nil
self.cache_is_standing = ret
return ret
end
if self.object:getvelocity().y ~= 0 then
self.cache_is_standing = false
return false
end
local p = self.object:getpos()
p.y = p.y + self.collisionbox[2] - 0.5
local corners = {
@ -208,10 +237,13 @@ function mobs.default_prototype:is_standing()
}
for _,c in ipairs(corners) do
local node = minetest.get_node_or_nil(c)
if not node or minetest.registered_nodes[node.name].walkable and self.object:getvelocity().y == 0 then
if not node or minetest.registered_nodes[node.name].walkable then
self.cache_is_standing = true
return true
end
end
self.cache_is_standing = false
return false
end
@ -236,6 +268,10 @@ function mobs.default_prototype:set_animation(name, inhibit)
end
function mobs.default_prototype:find_nearest_player()
if self.cache_find_nearest_player ~= nil then
return self.cache_find_nearest_player
end
local p = self.object:getpos()
local nearest_player = nil
local nearest_pos = p
@ -257,7 +293,41 @@ function mobs.default_prototype:find_nearest_player()
end
end
end
return {player=nearest_player, position=nearest_pos, distance=nearest_dist}
local ret = {player=nearest_player, position=nearest_pos, distance=nearest_dist}
self.cache_find_nearest_player = ret
return ret
end
function mobs.default_prototype:calculate_wall_normal()
local p = self.object:getpos()
local normals = {1,0,-1}
local xs = {self.collisionbox[1]-0.5,0,self.collisionbox[4]+0.5}
local ys = {self.collisionbox[2]-0.5,0,self.collisionbox[5]+0.5}
local zs = {self.collisionbox[3]-0.5,0,self.collisionbox[6]+0.5}
local normal = vector.new()
local count = 0
for xi=1,3 do
for yi=1,3 do
for zi=1,3 do
if xi ~= 2 and yi ~= 2 and zi ~= 2 then
local sp = vector.add(p, {x=xs[xi], y=ys[yi], z=zs[zi]})
local node = minetest.get_node_or_nil(sp)
if node and minetest.registered_nodes[node.name].walkable then
normal = vector.add(normal, {x=normals[xi], y=normals[yi], z=normals[zi]})
count = count + 1
end
end
end
end
end
if count > 0 then
return vector.normalize(normal)
else
return nil
end
end
mobs.move_method = {}
@ -279,7 +349,7 @@ function mobs.move_method:air(dtime, destination)
if vector.length(v) < self.move_speed * 1.5 then
t = math.pow(0.1, dtime)
else
t = math.pow(0.8, dtime)
t = math.pow(0.4, dtime)
speed = speed * 0.9
end
self.object:setvelocity(vector.add(
@ -288,14 +358,6 @@ function mobs.move_method:air(dtime, destination)
))
if speed > self.move_speed * 0.04 then
local yaw = self.object:getyaw()
local yaw_delta = math.atan2(delta.z, delta.x) - yaw
if yaw_delta < -math.pi then
yaw_delta = yaw_delta + math.pi * 2
elseif yaw_delta > math.pi then
yaw_delta = yaw_delta - math.pi * 2
end
self.object:setyaw(yaw + yaw_delta * (1-t))
self:set_animation("move", {"attack", "move_attack"})
else
self:set_animation("idle", {"attack", "move_attack"})
@ -320,7 +382,7 @@ function mobs.move_method:ground(dtime, destination)
if self:is_standing() and vector.length(v) < self.move_speed * 4 then
t = math.pow(0.001, dtime)
else
t = math.pow(0.7, dtime)
t = math.pow(0.4, dtime)
speed = speed * 0.9
end
local dir = vector.normalize(delta)
@ -335,10 +397,10 @@ function mobs.move_method:ground(dtime, destination)
local jump = nil
if self.smart_path then
local p = self.object:getpos()
if destination.y > p.y + 1 then
if destination.y > p.y + 0.55 then
for y=p.y,p.y+self.jump_height do
jump = defense.pathfinder:get_direction(self.name, {x=p.x, y=y, z=p.z})
if jump and (jump.x ~= 0 or jump.z ~= 0) then
if not jump or (jump.x ~= 0 or jump.z ~= 0) then
break
end
end
@ -370,14 +432,40 @@ function mobs.move_method:ground(dtime, destination)
self:jump(jump)
elseif self:is_standing() then
if speed > self.move_speed * 0.06 then
local yaw = self.object:getyaw()
local yaw_delta = math.atan2(dir.z, dir.x) - yaw
if yaw_delta < -math.pi then
yaw_delta = yaw_delta + math.pi * 2
elseif yaw_delta > math.pi then
yaw_delta = yaw_delta - math.pi * 2
end
self.object:setyaw(yaw + yaw_delta * (1-t))
self:set_animation("move", {"move_attack"})
else
self:set_animation("idle", {"attack", "move_attack"})
end
end
end
function mobs.move_method:crawl(dtime, destination)
local delta = vector.subtract(destination, self.object:getpos())
local dist = vector.length(delta)
local speed = self.move_speed * math.max(0, math.min(1, 1.2 * dist))
local t
local v = self.object:getvelocity()
if self:is_standing() and vector.length(v) < self.move_speed * 4 then
t = math.pow(0.001, dtime)
else
t = math.pow(0.4, dtime)
speed = speed * 0.9
end
local wall = self:calculate_wall_normal()
if wall and dist > 0 then
local dot = math.abs(wall.x * delta.x + wall.y * delta.y + wall.z * delta.z)
delta = vector.add(delta, vector.multiply(wall, -dot))
end
local dir = vector.normalize(delta)
local v2 = vector.add(
vector.multiply(v, t),
vector.multiply(dir, speed * (1-t))
)
self.object:setvelocity(v2)
if self:is_standing() then
if speed > self.move_speed * 0.06 then
self:set_animation("move", {"move_attack"})
else
self:set_animation("idle", {"attack", "move_attack"})

View File

@ -0,0 +1,124 @@
minetest.register_entity("defense:aranay_proxy", {
physical = false,
collisionbox = {0,0,0,0,0,0},
visual = "mesh",
mesh = "defense_aranay.b3d",
textures = {"defense_aranay.png"},
parent = nil,
timer = 0.5,
on_step = function(self, dtime)
if self.timer > 0 then
self.timer = self.timer - dtime
else
local active_parent = false
-- The engine does not provide a simple way to check if an entity is still alive
for _,e in pairs(minetest.luaentities) do
if e == self.parent then
active_parent = true
break
end
end
if not active_parent or not self.parent then
self.object:remove()
end
end
end
})
local function dot(a, b)
return a.x*b.x + a.y*b.y + a.z*b.z
end
local function cross(a, b)
return {
x=a.y*b.z - a.z*b.y,
z=a.z*b.x - a.x*b.z,
y=a.x*b.y - a.y*b.x
}
end
local function calculate_rotation(dir, up)
-- http://stackoverflow.com/a/21627251
local angle_h = math.atan2(dir.z, dir.x)
local angle_p = math.asin(dir.y)
local w0 = vector.normalize({x=dir.z, y=0, z=-dir.x})
local u0 = cross(w0, dir)
local angle_b = math.atan2(dot(w0,up), dot(u0,up))
return {x=angle_p, y=angle_h, z=angle_b}
end
defense.mobs.register_mob("defense:aranay", {
hp_max = 6,
collisionbox = {-0.4,-0.01,-0.4, 0.4,0.8,0.4},
mesh = "defense_aranay_core.b3d",
makes_footstep_sound = true,
automatic_face_movement_dir = false,
animation = {
idle = {a=0, b=19, rate=20},
jump = {a=20, b=39, rate=5},
fall = {a=20, b=39, rate=5},
attack = {a=60, b=69, rate=20},
move = {a=20, b=39, rate=40},
move_attack = {a=40, b=59, rate=20},
},
mass = 3,
movement = "crawl",
move_speed = 3,
armor = 0,
attack_damage = 1,
attack_range = 1.5,
attack_interval = 0.8,
proxy = nil,
rotation = {x=0, y=0, z=0},
on_activate = function(self, staticdata)
defense.mobs.default_prototype.on_activate(self, staticdata)
self.proxy = minetest.add_entity(self.object:getpos(), "defense:aranay_proxy")
self.proxy:get_luaentity().parent = self
self.proxy:set_attach(self.object, "", {x=0, y=0, z=0}, {x=0, y=0, z=0})
end,
on_step = function(self, dtime)
defense.mobs.default_prototype.on_step(self, dtime)
self:hunt()
-- Rotation
if vector.length(self.object:getvelocity()) > 0.6 then
local wall = self:calculate_wall_normal()
if wall then
local dir = vector.normalize(self.object:getvelocity())
local abs_dot = math.abs(dot(dir, wall))
local up = vector.normalize(vector.add(wall, vector.multiply(dir, -abs_dot)))
local rot = calculate_rotation(dir, up)
self.rotation = vector.multiply(rot, -180/math.pi)
end
self.proxy:set_attach(self.object, "", {x=0, y=0, z=0}, self.rotation)
end
end,
set_animation = function(self, name, inhibit)
if self.current_animation == name then
return
end
if inhibit then
for _,p in ipairs(inhibit) do
if self.current_animation == p and self.timer < self.current_animation_end then
return
end
end
end
local anim_prop = self.animation[name]
if anim_prop then
self.current_animation = name
self.current_animation_end = self.timer + (anim_prop.b - anim_prop.a - 1) / anim_prop.rate
self.proxy:set_animation({x=anim_prop.a, y=anim_prop.b}, anim_prop.rate, 0)
end
end,
})

Binary file not shown.

View File

@ -29,17 +29,18 @@ function pathfinder:register_class(class, properties)
end
-- Returns a number
-- function pathfinder:get_distance(class, position)
-- local field = self:get_field(class, position)
-- if not field then
-- return nil
-- end
-- return field.distance
-- end
function pathfinder:get_distance(class, position)
local field = self:get_field(class, position)
if not field then
return nil
end
return field.distance
end
-- Returns a vector
function pathfinder:get_direction(class, position)
local directions = {
[0]={x=0, y=0, z=0},
{x=0, y=-1, z=0},
{x=0, y=1, z=0},
{x=0, y=0, z=-1},
@ -50,24 +51,25 @@ function pathfinder:get_direction(class, position)
local total = vector.new(0, 0, 0)
local count = 0
local time = minetest.get_gametime()
local ipos = {x=math.floor(position.x), y=math.floor(position.y), z=math.floor(position.z)}
local cells = {
position,
{x=position.x + 1, y=position.y, z=position.z},
{x=position.x - 1, y=position.y, z=position.z},
{x=position.x, y=position.y + 1, z=position.z},
{x=position.x, y=position.y - 1, z=position.z},
{x=position.x, y=position.y, z=position.z + 1},
{x=position.x, y=position.y, z=position.z - 1},
ipos,
{x=ipos.x + 1, y=ipos.y, z=ipos.z},
{x=ipos.x - 1, y=ipos.y, z=ipos.z},
{x=ipos.x, y=ipos.y + 1, z=ipos.z},
{x=ipos.x, y=ipos.y - 1, z=ipos.z},
{x=ipos.x, y=ipos.y, z=ipos.z + 1},
{x=ipos.x, y=ipos.y, z=ipos.z - 1},
}
for _,p in ipairs(cells) do
local field = self:get_field(class, p)
if field then
local last_time = player_last_update[field.player] or field.time
if last_time + field.distance * 4 > time then
if field.time + field.distance * 4 > last_time then
local direction = directions[field.direction]
total = vector.add(total, direction)
local weight = 1/(1 + field.distance)
total = vector.add(total, vector.multiply(direction, weight))
end
end
end
@ -80,11 +82,16 @@ function pathfinder:get_direction(class, position)
end
-- Returns a table {time, distance}
function pathfinder:get_field(class, position)
function pathfinder:get_field(class, position, no_position_adjust)
local collisionbox = self.classes[class].collisionbox
local x = math.floor(position.x + collisionbox[1] + 0.01)
local y = math.floor(position.y + collisionbox[2] + 0.01)
local z = math.floor(position.z + collisionbox[3] + 0.01)
if not no_position_adjust then
position.x = position.x + collisionbox[1] + 0.01
position.y = position.y + collisionbox[2] + 0.01
position.z = position.z + collisionbox[3] + 0.01
end
local x = math.floor(position.x)
local y = math.floor(position.y)
local z = math.floor(position.z)
local chunk_key = math.floor(x/chunk_size) ..
":" .. math.floor(y/chunk_size) ..
@ -101,11 +108,16 @@ function pathfinder:get_field(class, position)
return chunk[index]
end
function pathfinder:set_field(class, position, player, distance, direction, time)
function pathfinder:set_field(class, position, player, distance, direction, time, no_position_adjust)
local collisionbox = self.classes[class].collisionbox
local x = math.floor(position.x + collisionbox[1] + 0.01)
local y = math.floor(position.y + collisionbox[2] + 0.01)
local z = math.floor(position.z + collisionbox[3] + 0.01)
if not no_position_adjust then
position.x = position.x + collisionbox[1] + 0.01
position.y = position.y + collisionbox[2] + 0.01
position.z = position.z + collisionbox[3] + 0.01
end
local x = math.floor(position.x)
local y = math.floor(position.y)
local z = math.floor(position.z)
local chunk_key = math.floor(x/chunk_size) ..
":" .. math.floor(y/chunk_size) ..
@ -125,9 +137,9 @@ end
function pathfinder:delete_field(class, position)
local collisionbox = self.classes[class].collisionbox
local x = math.floor(position.x + collisionbox[1] + 0.01)
local y = math.floor(position.y + collisionbox[2] + 0.01)
local z = math.floor(position.z + collisionbox[3] + 0.01)
local x = math.floor(position.x)
local y = math.floor(position.y)
local z = math.floor(position.z)
local chunk_key = math.floor(x/chunk_size) ..
":" .. math.floor(y/chunk_size) ..
@ -178,19 +190,21 @@ function pathfinder:update(dtime)
local current = Queue.pop(vq)
for di,n in ipairs(neighborhood) do
local npos = vector.add(current.position, n)
npos.x = math.floor(npos.x)
npos.y = math.floor(npos.y)
npos.z = math.floor(npos.z)
npos.x = math.floor(npos.x + 0.5)
npos.y = math.floor(npos.y + 0.5)
npos.z = math.floor(npos.z + 0.5)
local cost = class.cost_method(class, npos, current.position)
if cost and cost < self.path_max_range_far then
local next_distance = current.distance + cost
local neighbor_field = self:get_field(c, npos)
if not neighbor_field
or neighbor_field.time < current.time
and neighbor_field.direction ~= di
or neighbor_field.time == current.time
and neighbor_field.distance > next_distance then
self:set_field(c, npos, current.player, next_distance, di, current.time)
local neighbor_field = self:get_field(c, npos, true)
if not neighbor_field or
neighbor_field.time < current.time and
neighbor_field.direction ~= di or
neighbor_field.time == current.time and
(neighbor_field.distance > next_distance or
neighbor_field.distance == next_distance and
math.random() < 0.5) then
self:set_field(c, npos, current.player, next_distance, di, current.time, true)
if next_distance < self.path_max_range or current.far and next_distance < self.path_max_range_far then
if size < 800 then
Queue.push(vq, {
@ -235,12 +249,12 @@ function pathfinder:update(dtime)
for _,p in ipairs(minetest.get_connected_players()) do
local pos = p:getpos()
for c,_ in pairs(self.classes) do
for y=pos.y-0.5,pos.y+0.5 do
for y=math.floor(pos.y),math.ceil(pos.y) do
local tp = {x=pos.x, y=y, z=pos.z}
local field = self:get_field(c, tp)
if not field or field.distance > 0 then
local name = p:get_player_name()
self:set_field(c, tp, name, 0, 4, time)
self:set_field(c, tp, name, 0, 0, time)
Queue.push(visit_queues[c], {position=tp, player=name, distance=0, direction=0, time=time})
player_last_update[name] = time
end
@ -251,7 +265,7 @@ end
pathfinder.cost_method = {}
function pathfinder.cost_method.air(class, pos, parent)
-- Check if solid
-- Check if in solid
for y=pos.y,pos.y+class.size.y-1 do
for z=pos.z,pos.z+class.size.z-1 do
for x=pos.x,pos.x+class.size.x-1 do
@ -309,7 +323,7 @@ function pathfinder.cost_method.ground(class, pos, parent)
local node = minetest.get_node_or_nil(l)
if not node then return nil end
if minetest.registered_nodes[node.name].walkable then
return ground_distance
return 1 + ground_distance
end
end
end
@ -324,6 +338,69 @@ function pathfinder.cost_method.ground(class, pos, parent)
return 1
end
function pathfinder.cost_method.crawl(class, pos, parent)
-- Check if in solid
for y=pos.y,pos.y+class.size.y-1 do
for z=pos.z,pos.z+class.size.z-1 do
for x=pos.x,pos.x+class.size.x-1 do
local node = minetest.get_node_or_nil({x=x, y=y, z=z})
if not node then return nil end
if minetest.registered_nodes[node.name].walkable then
return math.huge
end
end
end
end
-- Check if touching solid
-- xz-plane
for x=pos.x-1,pos.x+class.size.x do
for z=pos.z-1,pos.z+class.size.z do
local node_n = minetest.get_node_or_nil({x=x, y=pos.y-1, z=z})
if not node_n then return nil end
if minetest.registered_nodes[node_n.name].walkable then
return 1
end
local node_p = minetest.get_node_or_nil({x=x, y=pos.y+class.size.y, z=z})
if not node_p then return nil end
if minetest.registered_nodes[node_p.name].walkable then
return 1
end
end
end
-- xy-plane
for x=pos.x,pos.x+class.size.x-1 do
for y=pos.y,pos.y+class.size.y-1 do
local node_n = minetest.get_node_or_nil({x=x, y=y, z=pos.z-1})
if not node_n then return nil end
if minetest.registered_nodes[node_n.name].walkable then
return 1
end
local node_p = minetest.get_node_or_nil({x=x, y=y, z=pos.z+class.size.z})
if not node_p then return nil end
if minetest.registered_nodes[node_p.name].walkable then
return 1
end
end
end
-- yz-plane
for y=pos.y,pos.y+class.size.y-1 do
for z=pos.z,pos.z+class.size.z-1 do
local node_n = minetest.get_node_or_nil({x=pos.x-1, y=y, z=z})
if not node_n then return nil end
if minetest.registered_nodes[node_n.name].walkable then
return 1
end
local node_p = minetest.get_node_or_nil({x=pos.x+class.size.z, y=y, z=z})
if not node_p then return nil end
if minetest.registered_nodes[node_p.name].walkable then
return 1
end
end
end
return math.huge
end
minetest.register_globalstep(function(dtime)
pathfinder:update(dtime)