Added 'dogshoot' attack type, self.reach and api tweaks

master
TenPlus1 2015-09-24 18:36:23 +01:00
parent ec890d4a56
commit 784c00f6d1
13 changed files with 1121 additions and 1134 deletions

View File

@ -28,6 +28,7 @@ This mod contains the following additions:
Changelog:
1.17- Added 'dogshoot' attack type, shoots when out of reach, melee attack when in reach, also api tweaks and self.reach added
1.16- Mobs follow multiple items now, Npc's can breed
1.15- Added Feeding/Taming/Breeding function, right-click to pick up any sheep with X mark on them and replace with new one to fix compatibility.
1.14- All .self variables saved in staticdata, Fixed self.health bug

164
api.lua
View File

@ -1,4 +1,4 @@
-- Mobs Api (23rd September 2015)
-- Mobs Api (24th September 2015)
mobs = {}
mobs.mod = "redo"
@ -10,6 +10,7 @@ mobs.protected = tonumber(minetest.setting_get("mobs_spawn_protected")) or 1
mobs.remove = minetest.setting_getbool("remove_far_mobs")
function mobs:register_mob(name, def)
minetest.register_entity(name, {
stepheight = def.stepheight or 0.6,
name = name,
@ -34,7 +35,7 @@ function mobs:register_mob(name, def)
view_range = def.view_range or 5,
walk_velocity = def.walk_velocity or 1,
run_velocity = def.run_velocity or 2,
damage = def.damage,
damage = def.damage or 0,
light_damage = def.light_damage or 0,
water_damage = def.water_damage or 0,
lava_damage = def.lava_damage or 0,
@ -49,7 +50,7 @@ function mobs:register_mob(name, def)
shoot_interval = def.shoot_interval,
sounds = def.sounds or {},
animation = def.animation,
follow = def.follow, -- or "",
follow = def.follow,
jump = def.jump or true,
walk_chance = def.walk_chance or 50,
attacks_monsters = def.attacks_monsters or false,
@ -67,7 +68,7 @@ function mobs:register_mob(name, def)
replace_with = def.replace_with,
replace_offset = def.replace_offset or 0,
timer = 0,
env_damage_timer = 0, -- only if state = "attack"
env_damage_timer = 0, -- only used when state = "attack"
attack = {player = nil, dist = nil},
state = "stand",
tamed = false,
@ -77,8 +78,9 @@ function mobs:register_mob(name, def)
child = false,
gotten = false,
health = 0,
reach = def.reach or 3,
do_attack = function(self, player, dist)
do_attack = function(self, player)
if self.state ~= "attack" then
if math.random(0,100) < 90
and self.sounds.war_cry then
@ -89,7 +91,6 @@ function mobs:register_mob(name, def)
end
self.state = "attack"
self.attack.player = player
self.attack.dist = dist
end
end,
@ -120,7 +121,6 @@ function mobs:register_mob(name, def)
local d = {x = vx / ds, z = vz / ds}
local p = {x = pos.x / ps, z = pos.z / ps}
local an = (d.x * p.x) + (d.z * p.z)
if math.deg(math.acos(an)) > (self.fov / 2) then
return false
end
@ -183,13 +183,14 @@ function mobs:register_mob(name, def)
on_step = function(self, dtime)
-- remove monsters if playing on peaceful
if (self.type == "monster" and peaceful_only)
or not within_limits(self.object:getpos(), 0) then
self.object:remove()
return
end
-- if lifetimer run out and not npc; tamed or attacking then remove mob
-- when lifetimer expires remove mob (except npc and tamed)
if self.type ~= "npc"
and not self.tamed then
self.lifetimer = self.lifetimer - dtime
@ -203,7 +204,7 @@ function mobs:register_mob(name, def)
end
end
-- check for mob drop/replace (used for chicken egg and sheep eating grass/wheat)
-- node replace check (chicken lays egg, cows eating grass etc)
if self.replace_rate
and self.child == false
and math.random(1, self.replace_rate) == 1 then
@ -213,7 +214,6 @@ function mobs:register_mob(name, def)
if self.replace_what
and self.object:getvelocity().y == 0
and #minetest.find_nodes_in_area(pos, pos, self.replace_what) > 0 then
--and self.state == "stand" then
minetest.set_node(pos, {name = self.replace_with})
end
end
@ -306,6 +306,7 @@ function mobs:register_mob(name, def)
if check_for_death(self) then return end
end
if self.water_damage ~= 0 or self.lava_damage ~= 0 then
pos.y = pos.y + self.collisionbox[2] -- foot level
local nod = minetest.get_node_or_nil(pos)
if not nod then return end ; -- print ("standing in "..nod.name)
@ -329,7 +330,7 @@ function mobs:register_mob(name, def)
effect(pos, 5, "fire_basic_flame.png")
if check_for_death(self) then return end
end
end
end
local do_jump = function(self)
@ -337,8 +338,6 @@ function mobs:register_mob(name, def)
return
end
self.jumptimer = (self.jumptimer or 0) + 1
if self.jumptimer < 3 then
local pos = self.object:getpos()
pos.y = (pos.y + self.collisionbox[2]) - 0.2
local nod = minetest.get_node_or_nil(pos)
@ -378,9 +377,6 @@ function mobs:register_mob(name, def)
end
end
end
else
self.jumptimer = 0
end
end
-- environmental damage timer (every 1 second)
@ -448,7 +444,7 @@ function mobs:register_mob(name, def)
end
-- attack player
if min_player then
self.do_attack(self, min_player, min_dist)
self.do_attack(self, min_player)
end
end
@ -475,7 +471,7 @@ function mobs:register_mob(name, def)
end
end
if min_player then
self.do_attack(self, min_player, min_dist)
self.do_attack(self, min_player)
end
end
@ -502,7 +498,7 @@ function mobs:register_mob(name, def)
visual_size = self.base_size,
collisionbox = self.base_colbox,
})
-- jump when grown to now fall into ground
-- jump when grown so not to fall into ground
local v = self.object:getvelocity()
v.y = self.jump_height
v.x = 0 ; v.z = 0
@ -611,7 +607,6 @@ function mobs:register_mob(name, def)
-- stop following player if not holding specific item
if self.following
and self.following.is_player
--and self.following:get_wielded_item():get_name() ~= self.follow then
and follow_holding(self, self.following) == false then
self.following = nil
end
@ -631,6 +626,8 @@ function mobs:register_mob(name, def)
if p then
local dist = ((p.x - s.x) ^ 2 + (p.y - s.y) ^ 2 + (p.z - s.z) ^ 2) ^ 0.5
-- dont follow if out of range
if dist > self.view_range then
self.following = nil
else
@ -642,7 +639,7 @@ function mobs:register_mob(name, def)
self.object:setyaw(yaw)
-- anyone but standing npc's can move along
if dist > 3 -- was 2
if dist > 3
and self.order ~= "stand" then
if (self.jump
and self.get_velocity(self) <= 0.5
@ -651,7 +648,7 @@ function mobs:register_mob(name, def)
and self.jump_chance > 0) then
self.direction = {
x = math.sin(yaw) * -1,
y = -20,
y = 0,
z = math.cos(yaw)
}
do_jump(self)
@ -670,15 +667,13 @@ function mobs:register_mob(name, def)
end
if self.state == "stand" then
-- randomly turn
if math.random(1, 4) == 1 then
-- if there is a player nearby look at them
local lp = nil
local s = self.object:getpos()
if self.type == "npc" then
local o = minetest.get_objects_inside_radius(self.object:getpos(), 3)
local yaw = 0
for _,o in ipairs(o) do
if o:is_player() then
@ -688,6 +683,7 @@ function mobs:register_mob(name, def)
end
end
-- look at any players nearby, otherwise turn randomly
if lp ~= nil then
local vec = {x = lp.x - s.x, y = lp.y - s.y, z = lp.z - s.z}
yaw = (math.atan(vec.z / vec.x) + math.pi / 2) - self.rotate
@ -716,13 +712,6 @@ function mobs:register_mob(name, def)
self.state = "walk"
self.set_animation(self, "walk")
end
-- jumping mobs only
-- if self.jump and math.random(1, 100) <= self.jump_chance then
-- self.direction = {x = 0, y = 0, z = 0}
-- do_jump(self)
-- self.set_velocity(self, self.walk_velocity)
-- end
end
elseif self.state == "walk" then
@ -735,10 +724,12 @@ and self.fly_in == "default:water_source"
and not lp then
print ("out of water")
self.set_velocity(self, 0)
self.state = "flop" -- change to undefined state so nothing more happens
-- change to undefined state so nothing more happens
self.state = "flop"
self:set_animation("stand")
return
end
-- if water nearby then turn away
if lp then
local vec = {x = lp.x - s.x, y = lp.y - s.y, z = lp.z - s.z}
@ -747,16 +738,17 @@ end
yaw = yaw + math.pi
end
self.object:setyaw(yaw)
-- otherwise randomly turn
elseif math.random(1, 100) <= 30 then
self.object:setyaw(self.object:getyaw() + ((math.random(0, 360) - 180) / 180 * math.pi))
end
-- jump when walking comes to a halt
if self.jump and self.get_velocity(self) <= 0.5
and self.object:getvelocity().y == 0 then
self.direction = {
x = math.sin(yaw) * -1,
y = -20,
y = 0,
z = math.cos(yaw)
}
do_jump(self)
@ -770,40 +762,41 @@ end
self:set_animation("stand")
end
-- exploding mobs
elseif self.state == "attack" and self.attack_type == "explode" then
if not self.attack.player
or not self.attack.player:is_player() then
self.state = "stand"
self:set_animation("stand")
self.timer = 0
self.blinktimer = 0
return
end
-- attack routines (explode, dogfight, shoot, dogshoot)
elseif self.state == "attack" then
-- calculate distance from mob and enemy
local s = self.object:getpos()
local p = self.attack.player:getpos()
if not p then p = s end
local dist = ((p.x - s.x) ^ 2 + (p.y - s.y) ^ 2 + (p.z - s.z) ^ 2) ^ 0.5
if dist > self.view_range or self.attack.player:get_hp() <= 0 then
-- stop attacking if no player or out of range
if dist > self.view_range
or not self.attack.player
or not self.attack.player:getpos()
or self.attack.player:get_hp() <= 0 then
--print(" ** stop attacking **", dist, self.view_range)
self.state = "stand"
self.v_start = false
self.set_velocity(self, 0)
self:set_animation("stand")
self.attack = {player = nil, dist = nil}
self.v_start = false
self.timer = 0
self.blinktimer = 0
self.attack = {player = nil, dist = nil}
self:set_animation("stand")
return
else
self:set_animation("walk")
self.attack.dist = dist
end
if self.attack_type == "explode" then
local vec = {x = p.x - s.x, y = p.y - s.y, z = p.z - s.z}
yaw = math.atan(vec.z / vec.x) + math.pi / 2 - self.rotate
if p.x > s.x then
yaw = yaw + math.pi
end
self.object:setyaw(yaw)
if self.attack.dist > 3 then
if dist > self.reach then
if not self.v_start then
self.v_start = true
self.set_velocity(self, self.run_velocity)
@ -836,7 +829,8 @@ end
end
if self.timer > 3 then
local pos = vector.round(self.object:getpos())
entity_physics(pos, 3) -- hurt player/mobs caught in blast area
-- hurt player/mobs caught in blast area
entity_physics(pos, 3)
if minetest.find_node_near(pos, 1, {"group:water"})
or minetest.is_protected(pos, "") then
self.object:remove()
@ -855,24 +849,13 @@ end
mobs:explosion(pos, 2, 0, 1, self.sounds.explode)
end
end
-- end of exploding mobs
elseif self.state == "attack"
and self.attack_type == "dogfight" then
if not self.attack.player
or not self.attack.player:getpos() then
print("stop attacking")
self.state = "stand"
self:set_animation("stand")
return
end
local s = self.object:getpos()
local p = self.attack.player:getpos()
local dist = ((p.x - s.x) ^ 2 + (p.y - s.y) ^ 2 + (p.z - s.z) ^ 2) ^ 0.5
elseif self.attack_type == "dogfight"
or (self.attack_type == "dogshoot" and dist <= self.reach) then
-- fly bit modified from BlockMens creatures mod
if self.fly
and dist > 2 then
and dist > self.reach then
local nod = minetest.get_node_or_nil(s)
local p1 = s
@ -914,6 +897,7 @@ end
end
-- end fly bit
-- ignore enemy if out of range
if dist > self.view_range
or self.attack.player:get_hp() <= 0 then
self.state = "stand"
@ -921,8 +905,6 @@ end
self.attack = {player = nil, dist = nil}
self:set_animation("stand")
return
else
self.attack.dist = dist
end
local vec = {x = p.x - s.x, y = p.y - s.y, z = p.z - s.z}
@ -931,8 +913,10 @@ end
yaw = yaw + math.pi
end
self.object:setyaw(yaw)
-- attack distance is 2 + half of mob width so the bigger mobs can attack (like slimes)
if self.attack.dist > ((-self.collisionbox[1] + self.collisionbox[4]) / 2) + 2 then
-- move towards enemy if beyond mob reach
-- set reach for each mob (default is 3)
if dist > self.reach then
-- jump attack
if (self.jump
and self.get_velocity(self) <= 0.5
@ -941,7 +925,7 @@ end
and self.jump_chance > 0) then
self.direction = {
x = math.sin(yaw) * -1,
y = -20,
y = 0,
z = math.cos(yaw)
}
do_jump(self)
@ -976,8 +960,8 @@ end
end
end
elseif self.state == "attack"
and self.attack_type == "shoot" then
elseif self.attack_type == "shoot"
or (self.attack_type == "dogshoot" and dist > self.reach) then
local s = self.object:getpos()
local p = self.attack.player:getpos()
@ -988,15 +972,6 @@ end
p.y = p.y - .5
s.y = s.y + .5
local dist = ((p.x - s.x) ^ 2 + (p.y - s.y) ^ 2 + (p.z - s.z) ^ 2) ^ 0.5
if dist > self.view_range
or self.attack.player:get_hp() <= 0 then
self.state = "stand"
self.set_velocity(self, 0)
self:set_animation("stand")
return
else
self.attack.dist = dist
end
local vec = {x = p.x - s.x, y = p.y - s.y, z = p.z - s.z}
yaw = (math.atan(vec.z / vec.x) + math.pi / 2) - self.rotate
@ -1010,7 +985,6 @@ end
and self.timer > self.shoot_interval
and math.random(1, 100) <= 60 then
self.timer = 0
self:set_animation("punch")
if self.sounds.attack then
@ -1025,13 +999,16 @@ end
local obj = minetest.add_entity(p, self.arrow)
local amount = (vec.x ^ 2 + vec.y ^ 2 + vec.z ^ 2) ^ 0.5
local v = obj:get_luaentity().velocity
vec.y = vec.y + self.shoot_offset -- this makes shoot aim accurate
-- offset makes shoot aim accurate
vec.y = vec.y + self.shoot_offset
vec.x = vec.x *v / amount
vec.y = vec.y *v / amount
vec.z = vec.z *v / amount
obj:setvelocity(vec)
end
end
end -- END if self.state == "attack"
end,
on_activate = function(self, staticdata, dtime_s)
@ -1077,7 +1054,7 @@ end
mesh = def.gotten_mesh
end
-- if object is child then set half size
-- set child objects to half size
if self.child == true then
vis_size = {
x = self.base_size.x / 2,
@ -1103,7 +1080,6 @@ end
self.object:set_hp(self.health)
self.object:set_armor_groups({fleshy = self.armor})
self.state = "stand"
--self.order = "stand"
self.following = nil
self.old_y = self.object:getpos().y
self.object:setyaw(math.random(1, 360) / 180 * math.pi)
@ -1114,7 +1090,6 @@ end
self.visual_size = vis_size
-- set anything changed above
self.object:set_properties(self)
end,
get_staticdata = function(self)
@ -1125,7 +1100,7 @@ end
self.object:remove()
end
self.remove_ok = true
self.attack = nil
self.attack = {player = nil, dist = nil}
self.following = nil
local tmp = {}
@ -1197,7 +1172,7 @@ end
if self.passive == false
and not self.tamed then
if self.state ~= "attack" then
self.do_attack(self, hitter, 1)
self.do_attack(self, hitter)
end
-- alert others to the attack
local obj = nil
@ -1206,14 +1181,15 @@ end
if obj then
if obj.group_attack == true
and obj.state ~= "attack" then
obj.do_attack(obj, hitter, 1)
obj.do_attack(obj, hitter)
end
end
end
end
end,
})
end
end -- END mobs:register_mob function
mobs.spawning_mobs = {}
@ -1539,7 +1515,7 @@ function mobs:register_egg(mob, desc, background, addegg)
local mob = minetest.add_entity(pos, mob)
local ent = mob:get_luaentity()
if ent.type ~= "monster" then
-- set owner
-- set owner and tame
ent.owner = placer:get_player_name()
ent.tamed = true
end

View File

@ -4,6 +4,7 @@
mobs:register_mob("mobs:bunny", {
type = "animal",
passive = true,
reach = 1,
hp_min = 1,
hp_max = 4,
armor = 200,
@ -55,7 +56,6 @@ mobs:register_mob("mobs:bunny", {
textures = {"mobs_bunny_evil.png"},
})
self.type = "monster"
self.state = "attack"
self.object:set_hp(20)
return
end

View File

@ -5,6 +5,7 @@ mobs:register_mob("mobs:cow", {
type = "animal",
passive = false,
attack_type = "dogfight",
reach = 2,
damage = 4,
hp_min = 5,
hp_max = 20,

View File

@ -5,6 +5,7 @@ mobs:register_mob("mobs:dirt_monster", {
type = "monster",
passive = false,
attack_type = "dogfight",
reach = 2,
damage = 2,
hp_min = 3,
hp_max = 27,

View File

@ -5,7 +5,8 @@ mobs:register_mob("mobs:dungeon_master", {
type = "monster",
passive = false,
damage = 4,
attack_type = "shoot",
attack_type = "dogshoot",
reach = 3,
shoot_interval = 2.5,
arrow = "mobs:fireball",
shoot_offset = 1,

View File

@ -5,6 +5,7 @@ mobs:register_mob("mobs:lava_flan", {
type = "monster",
passive = false,
attack_type = "dogfight",
reach = 2,
damage = 3,
hp_min = 10,
hp_max = 35,

View File

@ -5,6 +5,7 @@ mobs:register_mob("mobs:oerkki", {
type = "monster",
passive = false,
attack_type = "dogfight",
reach = 2,
damage = 4,
hp_min = 8,
hp_max = 34,

View File

@ -5,6 +5,7 @@ mobs:register_mob("mobs:sand_monster", {
type = "monster",
passive = false,
attack_type = "dogfight",
reach = 2,
damage = 1,
hp_min = 4,
hp_max = 20,

View File

@ -5,6 +5,7 @@ mobs:register_mob("mobs:spider", {
type = "monster",
passive = false,
attack_type = "dogfight",
reach = 2,
damage = 3,
hp_min = 20,
hp_max = 40,

View File

@ -5,6 +5,7 @@ mobs:register_mob("mobs:stone_monster", {
type = "monster",
passive = false,
attack_type = "dogfight",
reach = 2,
damage = 3,
hp_min = 12,
hp_max = 35,

View File

@ -5,6 +5,7 @@ mobs:register_mob("mobs:tree_monster", {
type = "monster",
passive = false,
attack_type = "dogfight",
reach = 2,
damage = 2,
hp_min = 7,
hp_max = 33,

View File

@ -5,6 +5,7 @@ mobs:register_mob("mobs:pumba", {
type = "animal",
passive = false,
attack_type = "dogfight",
reach = 2,
damage = 2,
hp_min = 5,
hp_max = 15,