Great batch of improvements

This commit is contained in:
the-real-herowl 2023-08-20 08:56:30 +02:00 committed by the-real-herowl
parent 729d8ec9e0
commit df17688b7d
4 changed files with 232 additions and 36 deletions

View file

@ -1216,6 +1216,9 @@ function mob_class:do_states_attack (dtime)
end end
end end
end end
elseif self.attack_type == "custom" and self.attack_state then
self.attack_state(self, dtime)
else else
end end

View file

@ -312,6 +312,7 @@ function mcl_mobs.register_mob(name, def)
return self:mob_activate(staticdata, def, dtime) return self:mob_activate(staticdata, def, dtime)
end, end,
attack_state = def.attack_state,
harmed_by_heal = def.harmed_by_heal, harmed_by_heal = def.harmed_by_heal,
is_boss = def.is_boss, is_boss = def.is_boss,
dealt_effect = def.dealt_effect, dealt_effect = def.dealt_effect,
@ -348,6 +349,7 @@ function mcl_mobs.register_arrow(name, def)
collisionbox = {0, 0, 0, 0, 0, 0}, -- remove box around arrows collisionbox = {0, 0, 0, 0, 0, 0}, -- remove box around arrows
timer = 0, timer = 0,
switch = 0, switch = 0,
_lifetime = def._lifetime or 150,
owner_id = def.owner_id, owner_id = def.owner_id,
rotate = def.rotate, rotate = def.rotate,
on_punch = function(self) on_punch = function(self)
@ -367,7 +369,7 @@ function mcl_mobs.register_arrow(name, def)
local pos = self.object:get_pos() local pos = self.object:get_pos()
if self.switch == 0 if self.switch == 0
or self.timer > 150 or self.timer > self._lifetime
or not within_limits(pos, 0) then or not within_limits(pos, 0) then
mcl_burning.extinguish(self.object) mcl_burning.extinguish(self.object)
self.object:remove(); self.object:remove();

View file

@ -59,11 +59,8 @@ local function wither_spawn(pos, player)
if check_schem(p, schem) and check_limit(pos) then if check_schem(p, schem) and check_limit(pos) then
remove_schem(p, schem) remove_schem(p, schem)
local wither = minetest.add_entity(vector.add(p, {x = 0, y = 1, z = 0, [d] = 1}), "mobs_mc:wither") local wither = minetest.add_entity(vector.add(p, {x = 0, y = 1, z = 0, [d] = 1}), "mobs_mc:wither")
local witherer = wither:get_luaentity() local wither_ent = wither:get_luaentity()
witherer._spawner = player:get_player_name() wither_ent._spawner = player:get_player_name()
witherer._custom_timer = 0.0
witherer._death_timer = 0.0
witherer._health_old = witherer.hp_max
local dim = mcl_worlds.pos_to_dimension(pos) local dim = mcl_worlds.pos_to_dimension(pos)
if dim == "overworld" then if dim == "overworld" then
wboss_overworld = wboss_overworld + 1 wboss_overworld = wboss_overworld + 1

View file

@ -4,6 +4,15 @@
--License for code WTFPL and otherwise stated in readmes --License for code WTFPL and otherwise stated in readmes
local S = minetest.get_translator("mobs_mc") local S = minetest.get_translator("mobs_mc")
local mobs_griefing = minetest.settings:get_bool("mobs_griefing") ~= false
local function atan(x)
if not x or x ~= x then
return 0
else
return math.atan(x)
end
end
--################### --###################
--################### WITHER --################### WITHER
@ -31,10 +40,11 @@ mcl_mobs.register_mob("mobs_mc:wither", {
}, },
visual_size = {x=4, y=4}, visual_size = {x=4, y=4},
makes_footstep_sound = true, makes_footstep_sound = true,
view_range = 16, view_range = 50,
fear_height = 4, fear_height = 4,
walk_velocity = 2, walk_velocity = 2,
run_velocity = 4, run_velocity = 4,
strafes = true,
sounds = { sounds = {
shoot_attack = "mobs_mc_ender_dragon_shoot", shoot_attack = "mobs_mc_ender_dragon_shoot",
attack = "mobs_mc_ender_dragon_attack", attack = "mobs_mc_ender_dragon_attack",
@ -57,7 +67,7 @@ mcl_mobs.register_mob("mobs_mc:wither", {
}, },
lava_damage = 0, lava_damage = 0,
fire_damage = 0, fire_damage = 0,
attack_type = "shoot", attack_type = "custom",
explosion_strength = 8, explosion_strength = 8,
dogshoot_stop = true, dogshoot_stop = true,
arrow = "mobs_mc:wither_skull", arrow = "mobs_mc:wither_skull",
@ -73,19 +83,56 @@ mcl_mobs.register_mob("mobs_mc:wither", {
harmed_by_heal = true, harmed_by_heal = true,
is_boss = true, is_boss = true,
do_custom = function(self, dtime) do_custom = function(self, dtime)
if self._spawning then
if not self._spw_max then self._spw_max = self._spawning end
self._spawning = self._spawning - dtime
local bardef = {
color = "dark_purple",
text = "Wither spawning",
percentage = math.floor((self._spw_max - self._spawning) / self._spw_max * 100),
}
local pos = self.object:get_pos()
for _, player in pairs(minetest.get_connected_players()) do
local d = vector.distance(pos, player:get_pos())
if d <= 80 then
mcl_bossbars.add_bar(player, bardef, true, d)
end
end
self.object:set_yaw(self._spawning*10)
local factor = math.floor((math.sin(self._spawning*10)+1.5) * 85)
local str = minetest.colorspec_to_colorstring({r=factor, g=factor, b=factor})
self.object:set_texture_mod("^[brighten^[multiply:"..str)
if self._spawning <= 0 then
if mobs_griefing and not minetest.is_protected(pos, "") then
mcl_explosions.explode(pos, 10, { drop_chance = 1.0 }, self.object)
else
mcl_mobs.mob_class.safe_boom(self, pos, 10)
end
self.object:set_texture_mod("")
self._spawning = nil
self._spw_max = nil
else
return false
end
end
self._custom_timer = self._custom_timer + dtime self._custom_timer = self._custom_timer + dtime
if self._custom_timer > 1 then if self._custom_timer > 1 then
self.health = math.min(self.health + 1, self.hp_max) self.health = math.min(self.health + 1, self.hp_max)
self._custom_timer = self._custom_timer - 1 self._custom_timer = self._custom_timer - 1
end end
if self._spawner then
local spawner = minetest.get_player_by_name(self._spawner) local spawner = minetest.get_player_by_name(self._spawner)
if spawner then if spawner then
self._death_timer = 0 self._death_timer = 0
local pos = self.object:get_pos() local pos = self.object:get_pos()
local spw = spawner:get_pos() local spw = spawner:get_pos()
local dist = vector.distance(pos, spw) local dist = vector.distance(pos, spw)
if dist > 60 then -- teleport to the player who spawned the wither if dist > 60 then -- teleport to the player who spawned the wither TODO add a setting to disable this
local R = 10 local R = 10
pos.x = spw.x + math.random(-R, R) pos.x = spw.x + math.random(-R, R)
pos.y = spw.y + math.random(-R, R) pos.y = spw.y + math.random(-R, R)
@ -101,6 +148,7 @@ mcl_mobs.register_mob("mobs_mc:wither", {
end end
self._health_old = self.health self._health_old = self.health
end end
end
local dim = mcl_worlds.pos_to_dimension(self.object:get_pos()) local dim = mcl_worlds.pos_to_dimension(self.object:get_pos())
if dim == "overworld" then mobs_mc.wither_count_overworld = mobs_mc.wither_count_overworld + 1 if dim == "overworld" then mobs_mc.wither_count_overworld = mobs_mc.wither_count_overworld + 1
@ -127,22 +175,162 @@ mcl_mobs.register_mob("mobs_mc:wither", {
self.arrow = "mobs_mc:wither_skull" self.arrow = "mobs_mc:wither_skull"
end end
end, end,
attack_state = function(self, dtime)
local s = self.object:get_pos()
local p = self.attack:get_pos() or s
p.y = p.y - .5
s.y = s.y + .5
local dist = vector.distance(p, s)
local vec = {
x = p.x - s.x,
y = p.y - s.y,
z = p.z - s.z
}
local yaw = (atan(vec.z / vec.x) +math.pi/ 2) - self.rotate
if p.x > s.x then yaw = yaw +math.pi end
yaw = self:set_yaw( yaw, 0, dtime)
local stay_away_from_player = vector.zero()
--strafe back and fourth
--stay away from player so as to shoot them
if dist < self.avoid_distance and self.shooter_avoid_enemy then
self:set_animation( "shoot")
stay_away_from_player=vector.multiply(vector.direction(p, s), 0.33)
end
if self.fly then
local vel = self.object:get_velocity()
local diff = s.y - p.y
local FLY_FACTOR = self.walk_velocity
if diff < 10 then
self.object:set_velocity({x=vel.x, y= FLY_FACTOR, z=vel.z})
elseif diff > 15 then
self.object:set_velocity({x=vel.x, y=-FLY_FACTOR, z=vel.z})
end
for i=1, 15 do
if minetest.get_node(vector.offset(s, 0, -i, 0)).name ~= "air" then
self.object:set_velocity({x=vel.x, y= FLY_FACTOR, z=vel.z})
break
elseif minetest.get_node(vector.offset(s, 0, i, 0)).name ~= "air" then
self.object:set_velocity({x=vel.x, y=-FLY_FACTOR/i, z=vel.z})
break
end
end
end
if self.strafes then
if not self.strafe_direction then
self.strafe_direction = 1.57
end
if math.random(40) == 1 then
self.strafe_direction = self.strafe_direction*-1
end
local dir = vector.rotate_around_axis(vector.direction(s, p), vector.new(0,1,0), self.strafe_direction)
local dir2 = vector.multiply(dir, 0.3 * self.walk_velocity)
if dir2 and stay_away_from_player then
self.acc = vector.add(dir2, stay_away_from_player)
end
else
self:set_velocity( 0)
end
if dist > 30 then self.acc = vector.add(self.acc, vector.direction(s, p)*0.01) end
local side_cor = vector.new(0.7*math.cos(yaw), 0, 0.7*math.sin(yaw))
local m = self.object:get_pos() -- position of the middle head
local sr = self.object:get_pos() + side_cor -- position of side right head
local sl = self.object:get_pos() - side_cor -- position of side left head
-- height corrections
m.y = m.y + self.collisionbox[5]
sr.y = sr.y + self.collisionbox[5] - 0.3
sl.y = sl.y + self.collisionbox[5] - 0.3
local rand_pos = math.random(1,3)
if rand_pos == 1 then m = sr
elseif rand_pos == 2 then m = sl end
-- TODO multiple targets at once?
-- TODO targeting most mobs when no players can be seen/in addition to players
if self.shoot_interval
and self.timer > self.shoot_interval
and not minetest.raycast(vector.add(m, vector.new(0,self.shoot_offset,0)), vector.add(self.attack:get_pos(), vector.new(0,1.5,0)), false, false):next()
and math.random(1, 100) <= 60 then
self.timer = 0
self:set_animation( "shoot")
-- play shoot attack sound
self:mob_sound("shoot_attack")
-- Shoot arrow
if minetest.registered_entities[self.arrow] then
local arrow, ent
local v = 1
if not self.shoot_arrow then
self.firing = true
minetest.after(1, function()
self.firing = false
end)
arrow = minetest.add_entity(m, self.arrow)
ent = arrow:get_luaentity()
if ent.velocity then
v = ent.velocity
end
ent.switch = 1
ent.owner_id = tostring(self.object) -- add unique owner id to arrow
-- important for mcl_shields
ent._shooter = self.object
ent._saved_shooter_pos = self.object:get_pos()
end
local amount = (vec.x * vec.x + vec.y * vec.y + vec.z * vec.z) ^ 0.5
-- 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)
if self.shoot_arrow then
vec = vector.normalize(vec)
self:shoot_arrow(m, vec)
else
arrow:set_velocity(vec)
end
end
end
end,
do_punch = function(self, hitter, tflp, tool_capabilities, dir) do_punch = function(self, hitter, tflp, tool_capabilities, dir)
if self._spawning then return false end
local ent = hitter:get_luaentity() local ent = hitter:get_luaentity()
if ent and self._arrow_resistant and (string.find(ent.name, "arrow") or string.find(ent.name, "rocket")) then return false end if ent and self._arrow_resistant and (string.find(ent.name, "arrow") or string.find(ent.name, "rocket")) then return false end
return true return true
end, end,
deal_damage = function(self, damage, mcl_reason) deal_damage = function(self, damage, mcl_reason)
if self._spawning then return end
if self._arrow_resistant and mcl_reason.type == "magic" then return end if self._arrow_resistant and mcl_reason.type == "magic" then return end
self.health = self.health - damage self.health = self.health - damage
end, end,
on_spawn = function(self) on_spawn = function(self)
minetest.sound_play("mobs_mc_wither_spawn", {object=self.object, gain=1.0, max_hear_distance=64}) minetest.sound_play("mobs_mc_wither_spawn", {object=self.object, gain=1.0, max_hear_distance=64})
self._custom_timer = 0.0
self._death_timer = 0.0
self._health_old = self.hp_max
self._spawning = 10
return true
end, end,
}) })
local mobs_griefing = minetest.settings:get_bool("mobs_griefing") ~= false
local wither_rose_soil = { "group:grass_block", "mcl_core:dirt", "mcl_core:coarse_dirt", "mcl_nether:netherrack", "group:soul_block", "mcl_mud:mud", "mcl_moss:moss" } local wither_rose_soil = { "group:grass_block", "mcl_core:dirt", "mcl_core:coarse_dirt", "mcl_nether:netherrack", "group:soul_block", "mcl_mud:mud", "mcl_moss:moss" }
mcl_mobs.register_arrow("mobs_mc:wither_skull", { mcl_mobs.register_arrow("mobs_mc:wither_skull", {
@ -156,8 +344,9 @@ mcl_mobs.register_arrow("mobs_mc:wither_skull", {
"mobs_mc_wither_projectile.png^[verticalframe:6:4", -- back "mobs_mc_wither_projectile.png^[verticalframe:6:4", -- back
"mobs_mc_wither_projectile.png^[verticalframe:6:5", -- front "mobs_mc_wither_projectile.png^[verticalframe:6:5", -- front
}, },
velocity = 6, velocity = 7,
rotate = 90, rotate = 90,
_lifetime = 350,
-- direct hit -- direct hit
hit_player = function(self, player) hit_player = function(self, player)
@ -167,8 +356,9 @@ mcl_mobs.register_arrow("mobs_mc:wither_skull", {
}, nil) }, nil)
mcl_mobs.effect_functions["withering"](player, 0.5, 10) mcl_mobs.effect_functions["withering"](player, 0.5, 10)
mcl_mobs.mob_class.boom(self,self.object:get_pos(), 1) mcl_mobs.mob_class.boom(self,self.object:get_pos(), 1)
if player:get_hp() <= 0 then local shooter = self._shooter:get_luaentity()
self._shooter:get_luaentity().health = self._shooter:get_luaentity().health + 5 if player:get_hp() <= 0 and shooter then
shooter.health = shooter.health + 5
end end
end, end,
@ -181,7 +371,8 @@ mcl_mobs.register_arrow("mobs_mc:wither_skull", {
mcl_mobs.mob_class.boom(self,self.object:get_pos(), 1) mcl_mobs.mob_class.boom(self,self.object:get_pos(), 1)
local l = mob:get_luaentity() local l = mob:get_luaentity()
if l and l.health - 8 <= 0 then if l and l.health - 8 <= 0 then
self._shooter:get_luaentity().health = self._shooter:get_luaentity().health + 5 local shooter = self._shooter:get_luaentity()
if shooter then shooter.health = shooter.health + 5 end
local n = minetest.find_node_near(mob:get_pos(),2,wither_rose_soil) local n = minetest.find_node_near(mob:get_pos(),2,wither_rose_soil)
if n then if n then
local p = vector.offset(n,0,1,0) local p = vector.offset(n,0,1,0)
@ -212,6 +403,7 @@ mcl_mobs.register_arrow("mobs_mc:wither_skull_strong", {
}, },
velocity = 4, velocity = 4,
rotate = 90, rotate = 90,
_lifetime = 500,
-- direct hit -- direct hit
hit_player = function(self, player) hit_player = function(self, player)
@ -226,8 +418,9 @@ mcl_mobs.register_arrow("mobs_mc:wither_skull_strong", {
else else
mcl_mobs.mob_class.safe_boom(self, pos, 1) --need to call it this way bc self is the "arrow" object here mcl_mobs.mob_class.safe_boom(self, pos, 1) --need to call it this way bc self is the "arrow" object here
end end
if player:get_hp() <= 0 then local shooter = self._shooter:get_luaentity()
self._shooter:get_luaentity().health = self._shooter:get_luaentity().health + 5 if player:get_hp() <= 0 and shooter then
shooter.health = shooter.health + 5
end end
end, end,
@ -245,7 +438,8 @@ mcl_mobs.register_arrow("mobs_mc:wither_skull_strong", {
end end
local l = mob:get_luaentity() local l = mob:get_luaentity()
if l and l.health - 8 <= 0 then if l and l.health - 8 <= 0 then
self._shooter:get_luaentity().health = self._shooter:get_luaentity().health + 5 local shooter = self._shooter:get_luaentity()
if shooter then shooter.health = shooter.health + 5 end
local n = minetest.find_node_near(mob:get_pos(),2,wither_rose_soil) local n = minetest.find_node_near(mob:get_pos(),2,wither_rose_soil)
if n then if n then
local p = vector.offset(n,0,1,0) local p = vector.offset(n,0,1,0)