Merge pull request 'Next bunch of mob fixes' (#2637) from mob_fixes into master

Reviewed-on: https://git.minetest.land/MineClone2/MineClone2/pulls/2637
This commit is contained in:
cora 2022-09-13 15:55:10 +00:00
commit 630c2fbcef
7 changed files with 101 additions and 55 deletions

View File

@ -1,4 +1,3 @@
-- API for Mobs Redo: MineClone 2 Edition (MRM) -- API for Mobs Redo: MineClone 2 Edition (MRM)
mcl_mobs = {} mcl_mobs = {}
@ -14,12 +13,6 @@ local FLOP_HOR_SPEED = 1.5
local ENTITY_CRAMMING_MAX = 24 local ENTITY_CRAMMING_MAX = 24
local CRAMMING_DAMAGE = 3 local CRAMMING_DAMAGE = 3
local MOB_CAP = {}
MOB_CAP.hostile = 70
MOB_CAP.passive = 10
MOB_CAP.ambient = 15
MOB_CAP.water = 15
-- Localize -- Localize
local S = minetest.get_translator("mcl_mobs") local S = minetest.get_translator("mcl_mobs")
@ -36,6 +29,7 @@ local max = math.max
local atann = math.atan local atann = math.atan
local random = math.random local random = math.random
local floor = math.floor local floor = math.floor
local ceil = math.ceil
local atan = function(x) local atan = function(x)
if not x or x ~= x then if not x or x ~= x then
@ -45,7 +39,6 @@ local atan = function(x)
end end
end end
-- Load settings -- Load settings
local damage_enabled = minetest.settings:get_bool("enable_damage") local damage_enabled = minetest.settings:get_bool("enable_damage")
local disable_blood = minetest.settings:get_bool("mobs_disable_blood") local disable_blood = minetest.settings:get_bool("mobs_disable_blood")
@ -55,11 +48,10 @@ local spawn_protected = minetest.settings:get_bool("mobs_spawn_protected") ~= fa
local remove_far = true local remove_far = true
local difficulty = tonumber(minetest.settings:get("mob_difficulty")) or 1.0 local difficulty = tonumber(minetest.settings:get("mob_difficulty")) or 1.0
local show_health = false local show_health = false
local max_per_block = tonumber(minetest.settings:get("max_objects_per_block") or 64)
local mobs_spawn_chance = tonumber(minetest.settings:get("mobs_spawn_chance") or 2.5)
-- Shows helpful debug info above each mob -- Shows helpful debug info above each mob
local mobs_debug = minetest.settings:get_bool("mobs_debug", false) local mobs_debug = minetest.settings:get_bool("mobs_debug", false)
local spawn_logging = minetest.settings:get_bool("mcl_logging_mobs_spawn",true)
-- Peaceful mode message so players will know there are no monsters -- Peaceful mode message so players will know there are no monsters
if minetest.settings:get_bool("only_peaceful_mobs", false) then if minetest.settings:get_bool("only_peaceful_mobs", false) then
@ -155,7 +147,7 @@ local mob_sound = function(self, soundname, is_opinion, fixed_pitch)
pitch = base_pitch pitch = base_pitch
end end
-- randomize the pitch a bit -- randomize the pitch a bit
pitch = pitch + math.random(-10, 10) * 0.005 pitch = pitch + random(-10, 10) * 0.005
end end
minetest.sound_play(sound, { minetest.sound_play(sound, {
object = self.object, object = self.object,
@ -288,7 +280,7 @@ local function update_roll(self)
local cbox = table.copy(self.collisionbox) local cbox = table.copy(self.collisionbox)
local acbox = self.object:get_properties().collisionbox local acbox = self.object:get_properties().collisionbox
if math.abs(cbox[2] - acbox[2]) > 0.1 then if abs(cbox[2] - acbox[2]) > 0.1 then
was_Fleckenstein = true was_Fleckenstein = true
end end
@ -311,7 +303,10 @@ end
-- set and return valid yaw -- set and return valid yaw
local set_yaw = function(self, yaw, delay, dtime) local set_yaw = function(self, yaw, delay, dtime)
if true then
self.object:set_yaw(yaw)
return yaw
end
if not yaw or yaw ~= yaw then if not yaw or yaw ~= yaw then
yaw = 0 yaw = 0
end end
@ -320,7 +315,7 @@ local set_yaw = function(self, yaw, delay, dtime)
if delay == 0 then if delay == 0 then
if self.shaking and dtime then if self.shaking and dtime then
yaw = yaw + (math.random() * 2 - 1) * 5 * dtime yaw = yaw + (random() * 2 - 1) * 5 * dtime
end end
self.object:set_yaw(yaw) self.object:set_yaw(yaw)
update_roll(self) update_roll(self)
@ -589,7 +584,7 @@ local damage_effect = function(self, damage)
-- damage particles -- damage particles
if (not disable_blood) and damage > 0 then if (not disable_blood) and damage > 0 then
local amount_large = math.floor(damage / 2) local amount_large = floor(damage / 2)
local amount_small = damage % 2 local amount_small = damage % 2
local pos = self.object:get_pos() local pos = self.object:get_pos()
@ -715,7 +710,7 @@ local item_drop = function(self, cooked, looting_level)
end end
if do_common_looting then if do_common_looting then
num = num + math.floor(math.random(0, looting_level) + 0.5) num = num + floor(random(0, looting_level) + 0.5)
end end
if num > 0 then if num > 0 then
@ -824,7 +819,7 @@ local check_for_death = function(self, cause, cmi_cause)
item_drop(self, cooked, looting) item_drop(self, cooked, looting)
if ((not self.child) or self.type ~= "animal") and (minetest.get_us_time() - self.xp_timestamp <= 5000000) then if ((not self.child) or self.type ~= "animal") and (minetest.get_us_time() - self.xp_timestamp <= 5000000) then
mcl_experience.throw_xp(self.object:get_pos(), math.random(self.xp_min, self.xp_max)) mcl_experience.throw_xp(self.object:get_pos(), random(self.xp_min, self.xp_max))
end end
end end
end end
@ -1205,7 +1200,7 @@ local do_env_damage = function(self)
end end
if drowning then if drowning then
self.breath = math.max(0, self.breath - 1) self.breath = max(0, self.breath - 1)
effect(pos, 2, "bubble.png", nil, nil, 1, nil) effect(pos, 2, "bubble.png", nil, nil, 1, nil)
if self.breath <= 0 then if self.breath <= 0 then
@ -1223,7 +1218,7 @@ local do_env_damage = function(self)
return true return true
end end
else else
self.breath = math.min(self.breath_max, self.breath + 1) self.breath = min(self.breath_max, self.breath + 1)
end end
end end
@ -1549,7 +1544,7 @@ local breed = function(self)
return return
end end
mcl_experience.throw_xp(pos, math.random(1, 7)) mcl_experience.throw_xp(pos, random(1, 7))
-- custom breed function -- custom breed function
if parent1.on_breed then if parent1.on_breed then
@ -1565,7 +1560,7 @@ local breed = function(self)
-- Use texture of one of the parents -- Use texture of one of the parents
local p = math.random(1, 2) local p = random(1, 2)
if p == 1 then if p == 1 then
ent_c.base_texture = parent1.base_texture ent_c.base_texture = parent1.base_texture
else else
@ -1727,7 +1722,7 @@ local smart_mobs = function(self, s, p, dist, dtime)
end, self) end, self)
end end
if math.abs(vector.subtract(s,target_pos).y) > self.stepheight then if abs(vector.subtract(s,target_pos).y) > self.stepheight then
if height_switcher then if height_switcher then
use_pathfind = true use_pathfind = true
@ -1768,7 +1763,7 @@ local smart_mobs = function(self, s, p, dist, dtime)
if self.fear_height ~= 0 then dropheight = self.fear_height end if self.fear_height ~= 0 then dropheight = self.fear_height end
local jumpheight = 0 local jumpheight = 0
if self.jump and self.jump_height >= 4 then if self.jump and self.jump_height >= 4 then
jumpheight = math.min(math.ceil(self.jump_height / 4), 4) jumpheight = min(ceil(self.jump_height / 4), 4)
elseif self.stepheight > 0.5 then elseif self.stepheight > 0.5 then
jumpheight = 1 jumpheight = 1
end end
@ -1799,7 +1794,7 @@ local smart_mobs = function(self, s, p, dist, dtime)
end end
end end
local sheight = math.ceil(self.collisionbox[5]) + 1 local sheight = ceil(self.collisionbox[5]) + 1
-- assume mob is 2 blocks high so it digs above its head -- assume mob is 2 blocks high so it digs above its head
s.y = s.y + sheight s.y = s.y + sheight
@ -1946,7 +1941,7 @@ local monster_attack = function(self)
-- find specific mob to attack, failing that attack player/npc/animal -- find specific mob to attack, failing that attack player/npc/animal
if specific_attack(self.specific_attack, name) if specific_attack(self.specific_attack, name)
and (type == "player" or type == "npc" and (type == "player" or ( type == "npc" and self.attack_npcs )
or (type == "animal" and self.attack_animals == true)) then or (type == "animal" and self.attack_animals == true)) then
p = player:get_pos() p = player:get_pos()
@ -2237,9 +2232,9 @@ local follow_flop = function(self)
if sdef and sdef.walkable then if sdef and sdef.walkable then
mob_sound(self, "flop") mob_sound(self, "flop")
self.object:set_velocity({ self.object:set_velocity({
x = math.random(-FLOP_HOR_SPEED, FLOP_HOR_SPEED), x = random(-FLOP_HOR_SPEED, FLOP_HOR_SPEED),
y = FLOP_HEIGHT, y = FLOP_HEIGHT,
z = math.random(-FLOP_HOR_SPEED, FLOP_HOR_SPEED), z = random(-FLOP_HOR_SPEED, FLOP_HOR_SPEED),
}) })
end end
@ -2294,8 +2289,8 @@ local function go_to_pos(entity,b)
return true return true
end end
local v = { x = b.x - s.x, z = b.z - s.z } local v = { x = b.x - s.x, z = b.z - s.z }
local yaw = (math.atan(v.z / v.x) + math.pi / 2) - entity.rotate local yaw = (atann(v.z / v.x) + pi / 2) - entity.rotate
if b.x > s.x then yaw = yaw + math.pi end if b.x > s.x then yaw = yaw + pi end
entity.object:set_yaw(yaw) entity.object:set_yaw(yaw)
set_velocity(entity,entity.follow_velocity) set_velocity(entity,entity.follow_velocity)
mcl_mobs:set_animation(entity, "walk") mcl_mobs:set_animation(entity, "walk")
@ -3403,21 +3398,22 @@ end
-- get entity staticdata -- get entity staticdata
local mob_staticdata = function(self) local mob_staticdata = function(self)
--[[
-- remove mob when out of range unless tamed -- remove mob when out of range unless tamed
if remove_far if remove_far
and self.can_despawn and self.can_despawn
and self.remove_ok and self.remove_ok
and ((not self.nametag) or (self.nametag == "")) and ((not self.nametag) or (self.nametag == ""))
and self.lifetimer <= 20 then and self.lifetimer <= 20 then
if spawn_logging then
minetest.log("action", "Mob "..name.." despawns in mob_staticdata at "..minetest.pos_to_string(self.object.get_pos(), 1)) minetest.log("action", "[mcl_mobs] Mob "..tostring(self.name).." despawns in mob_staticdata at "..minetest.pos_to_string(self.object:get_pos()))
end
mcl_burning.extinguish(self.object) mcl_burning.extinguish(self.object)
self.object:remove() self.object:remove()
return ""-- nil return "remove"-- nil
end end
--]]
self.remove_ok = true self.remove_ok = true
self.attack = nil self.attack = nil
self.following = nil self.following = nil
@ -3453,6 +3449,11 @@ local mob_activate = function(self, staticdata, def, dtime)
return return
end end
if staticdata == "remove" then
mcl_burning.extinguish(self.object)
self.object:remove()
return
end
-- load entity variables -- load entity variables
local tmp = minetest.deserialize(staticdata) local tmp = minetest.deserialize(staticdata)
@ -3473,7 +3474,7 @@ local mob_activate = function(self, staticdata, def, dtime)
local c = 1 local c = 1
if #def.textures > c then c = #def.textures end if #def.textures > c then c = #def.textures end
self.base_texture = def.textures[math.random(c)] self.base_texture = def.textures[random(c)]
self.base_mesh = def.mesh self.base_mesh = def.mesh
self.base_size = self.visual_size self.base_size = self.visual_size
self.base_colbox = self.collisionbox self.base_colbox = self.collisionbox
@ -3620,6 +3621,7 @@ end
-- main mob function -- main mob function
local mob_step = function(self, dtime) local mob_step = function(self, dtime)
self.lifetimer = self.lifetimer - dtime
check_item_pickup(self) check_item_pickup(self)
check_aggro(self,dtime) check_aggro(self,dtime)
if not self.fire_resistant then if not self.fire_resistant then
@ -3686,7 +3688,7 @@ local mob_step = function(self, dtime)
self.delay = self.delay - 1 self.delay = self.delay - 1
if self.shaking then if self.shaking then
yaw = yaw + (math.random() * 2 - 1) * 5 * dtime yaw = yaw + (random() * 2 - 1) * 5 * dtime
end end
self.object:set_yaw(yaw) self.object:set_yaw(yaw)
update_roll(self) update_roll(self)
@ -3833,14 +3835,15 @@ local mob_step = function(self, dtime)
and ((not self.nametag) or (self.nametag == "")) and ((not self.nametag) or (self.nametag == ""))
and self.state ~= "attack" and self.state ~= "attack"
and self.following == nil then and self.following == nil then
self.lifetimer = self.lifetimer - dtime
if self.despawn_immediately or self.lifetimer <= 0 then if self.despawn_immediately or self.lifetimer <= 0 then
minetest.log("action", "Mob "..self.name.." despawns in mob_step at "..minetest.pos_to_string(pos, 1)) if spawn_logging then
minetest.log("action", "[mcl_mobs] Mob "..self.name.." despawns in mob_step at "..minetest.pos_to_string(pos, 1))
end
mcl_burning.extinguish(self.object) mcl_burning.extinguish(self.object)
self.object:remove() self.object:remove()
return
elseif self.lifetimer <= 10 then elseif self.lifetimer <= 10 then
if math.random(10) < 4 then if random(10) < 4 then
self.despawn_immediately = true self.despawn_immediately = true
else else
self.lifetimer = 20 self.lifetimer = 20
@ -4030,6 +4033,7 @@ minetest.register_entity(name, {
dogshoot_count_max = def.dogshoot_count_max or 5, dogshoot_count_max = def.dogshoot_count_max or 5,
dogshoot_count2_max = def.dogshoot_count2_max or (def.dogshoot_count_max or 5), dogshoot_count2_max = def.dogshoot_count2_max or (def.dogshoot_count_max or 5),
attack_animals = def.attack_animals or false, attack_animals = def.attack_animals or false,
attack_npcs = def.attack_npcs or false,
specific_attack = def.specific_attack, specific_attack = def.specific_attack,
runaway_from = def.runaway_from, runaway_from = def.runaway_from,
owner_loyal = def.owner_loyal, owner_loyal = def.owner_loyal,
@ -4512,7 +4516,7 @@ minetest.register_globalstep(function(dtime)
for _, obj in pairs(minetest.get_objects_inside_radius(pos, 47)) do for _, obj in pairs(minetest.get_objects_inside_radius(pos, 47)) do
local lua = obj:get_luaentity() local lua = obj:get_luaentity()
if lua and lua.is_mob then if lua and lua.is_mob then
lua.lifetimer = math.max(20, lua.lifetimer) lua.lifetimer = max(20, lua.lifetimer)
lua.despawn_immediately = false lua.despawn_immediately = false
end end
end end

View File

@ -29,16 +29,17 @@ local dbg_spawn_succ = 0
local aoc_range = 136 local aoc_range = 136
local mob_cap = { local mob_cap = {
monster = 70, monster = minetest.settings:get_bool("mcl_mob_cap_monster") or 70,
animal =10, animal = minetest.settings:get_bool("mcl_mob_cap_animal") or 10,
ambient =15, ambient = minetest.settings:get_bool("mcl_mob_cap_ambient") or 15,
water = 5, --currently unused water = minetest.settings:get_bool("mcl_mob_cap_water") or 5, --currently unused
water_ambient = 20, --currently unused water_ambient = minetest.settings:get_bool("mcl_mob_cap_water_ambient") or 20, --currently unused
} }
--do mobs spawn? --do mobs spawn?
local mobs_spawn = minetest.settings:get_bool("mobs_spawn", true) ~= false local mobs_spawn = minetest.settings:get_bool("mobs_spawn", true) ~= false
local spawn_protected = minetest.settings:get_bool("mobs_spawn_protected") ~= false
local logging = minetest.settings:get_bool("mcl_logging_mobs_spawn",true)
local noise_params = { local noise_params = {
offset = 0, offset = 0,
@ -456,6 +457,7 @@ local function spawn_check(pos,spawn_def)
and (spawn_def.check_position and spawn_def.check_position(pos) or true) and (spawn_def.check_position and spawn_def.check_position(pos) or true)
and (not is_farm_animal(spawn_def.name) or is_grass) and (not is_farm_animal(spawn_def.name) or is_grass)
and (spawn_def.type_of_spawning ~= "water" or is_water) and (spawn_def.type_of_spawning ~= "water" or is_water)
and ( not spawn_protected or not minetest.is_protected(s, "") )
and not is_bedrock then and not is_bedrock then
--only need to poll for node light if everything else worked --only need to poll for node light if everything else worked
local gotten_light = get_node_light(pos) local gotten_light = get_node_light(pos)
@ -466,6 +468,15 @@ local function spawn_check(pos,spawn_def)
return false return false
end end
function mcl_mobs.spawn(pos,id)
local def = minetest.registered_entities[id] or minetest.registered_entities["mobs_mc:"..id] or minetest.registered_entities["extra_mobs:"..id]
if not def or (def.can_spawn and not def.can_spawn(pos)) or not def.is_mob then
return false
end
return minetest.add_entity(pos, def.name)
end
local function spawn_group(p,mob,spawn_on,group_max,group_min) local function spawn_group(p,mob,spawn_on,group_max,group_min)
if not group_min then group_min = 1 end if not group_min then group_min = 1 end
local nn= minetest.find_nodes_in_area_under_air(vector.offset(p,-5,-3,-5),vector.offset(p,5,3,5),spawn_on) local nn= minetest.find_nodes_in_area_under_air(vector.offset(p,-5,-3,-5),vector.offset(p,5,3,5),spawn_on)
@ -481,13 +492,27 @@ local function spawn_group(p,mob,spawn_on,group_max,group_min)
if mob.type_of_spawning == "water" then if mob.type_of_spawning == "water" then
sp = get_water_spawn(sp) sp = get_water_spawn(sp)
end end
o = minetest.add_entity(sp,mob.name) o = mcl_mobs.spawn(sp,mob.name)
if o then dbg_spawn_succ = dbg_spawn_succ + 1 end if o then dbg_spawn_succ = dbg_spawn_succ + 1 end
end end
end end
return o return o
end end
mcl_mobs.spawn_group = spawn_group
minetest.register_chatcommand("spawn_mob",{
privs = { debug = true },
func = function(n,param)
local pos = minetest.get_player_by_name(n):get_pos()
if mcl_mobs.spawn(pos,param) then
return true, param.." spawned at "..minetest.pos_to_string(pos),
minetest.log("action", n.." spawned "..param.." at "..minetest.pos_to_string(pos))
end
return false, "Couldn't spawn "..param
end
})
if mobs_spawn then if mobs_spawn then
local perlin_noise local perlin_noise
@ -538,11 +563,16 @@ if mobs_spawn then
--everything is correct, spawn mob --everything is correct, spawn mob
local object local object
if spawn_in_group and ( mob_type ~= "monster" or math.random(5) == 1 ) then if spawn_in_group and ( mob_type ~= "monster" or math.random(5) == 1 ) then
if logging then
minetest.log("action", "[mcl_mobs] A group of mob " .. mob_def.name .. " spawns at " .. minetest.pos_to_string(spawning_position, 1))
end
object = spawn_group(spawning_position,mob_def,{minetest.get_node(vector.offset(spawning_position,0,-1,0)).name},spawn_in_group,spawn_in_group_min) object = spawn_group(spawning_position,mob_def,{minetest.get_node(vector.offset(spawning_position,0,-1,0)).name},spawn_in_group,spawn_in_group_min)
minetest.log("action", "A group of mob " .. mob_def.name .. " spawns at " .. minetest.pos_to_string(spawning_position, 1))
else else
object = minetest.add_entity(spawning_position, mob_def.name) if logging then
minetest.log("action", "Mob " .. mob_def.name .. " spawns at " .. minetest.pos_to_string(spawning_position, 1)) minetest.log("action", "[mcl_mobs] Mob " .. mob_def.name .. " spawns at " .. minetest.pos_to_string(spawning_position, 1))
end
object = mcl_mobs.spawn(spawning_position, mob_def.name)
end end

View File

@ -41,6 +41,7 @@ pillager = {
fear_height = 4, fear_height = 4,
arrow = "mcl_bows:arrow_entity", arrow = "mcl_bows:arrow_entity",
attack_type = "dogshoot", -- Alternate punching/shooting attack_type = "dogshoot", -- Alternate punching/shooting
attack_npcs = true,
reach = 0, -- Punching max distance reach = 0, -- Punching max distance
damage = 0, -- Punching damage damage = 0, -- Punching damage
dogshoot_switch = 1, -- Start of shooting dogshoot_switch = 1, -- Start of shooting

View File

@ -38,6 +38,7 @@ mcl_mobs:register_mob("mobs_mc:vindicator", {
walk_velocity = 1.2, walk_velocity = 1.2,
run_velocity = 2.4, run_velocity = 2.4,
attack_type = "dogfight", attack_type = "dogfight",
attack_npcs = true,
drops = { drops = {
{name = "mcl_core:emerald", {name = "mcl_core:emerald",
chance = 1, chance = 1,

View File

@ -133,6 +133,7 @@ mcl_mobs:register_mob("mobs_mc:villager_zombie", {
view_range = 16, view_range = 16,
fear_height = 4, fear_height = 4,
harmed_by_heal = true, harmed_by_heal = true,
attack_npcs = true,
}) })
mcl_mobs:spawn_specific( mcl_mobs:spawn_specific(

View File

@ -101,6 +101,7 @@ local zombie = {
self.object:set_properties({visual_size = self.visual_size}) self.object:set_properties({visual_size = self.visual_size})
self.base_size = self.visual_size self.base_size = self.visual_size
end, end,
attack_npcs = true,
} }
mcl_mobs:register_mob("mobs_mc:zombie", zombie) mcl_mobs:register_mob("mobs_mc:zombie", zombie)

View File

@ -76,10 +76,6 @@ mcl_enable_skin_customization (Enable player skin customization) bool true
# This setting is only read at startup. # This setting is only read at startup.
mobs_spawn (Spawn mobs naturally) bool true mobs_spawn (Spawn mobs naturally) bool true
# Controls the overall amount of mobs that spawn. The higher the number,
# the less often mobs will spawn. This does not affect mob spawners.
mobs_spawn_chance (Mob spawn chance) float 2.5 0.0
# If enabled, only peaceful mobs will appear naturally. This does not # If enabled, only peaceful mobs will appear naturally. This does not
# affect mob spawners. # affect mob spawners.
# This setting is only read at startup. # This setting is only read at startup.
@ -103,6 +99,15 @@ mobs_griefing (Mobs change blocks) bool true
# If enabled, mobs won't damage particles when they got hurt. # If enabled, mobs won't damage particles when they got hurt.
mobs_disable_blood (Disable mob damage particles) bool false mobs_disable_blood (Disable mob damage particles) bool false
#Maximum amount of monsters that will spawn near a player (default:70)
mcl_mob_cap_monster (Mob cap monsters) int 70 0 2048
#Maximum amount of animals that will spawn near a player (default:10)
mcl_mob_cap_animal (Mob cap animals) int 10 0 1024
#Maximum amount of ambient mobs that will spawn near a player (default:15)
mcl_mob_cap_ambient (Mob cap ambient mobs) int 15 0 1024
[Audio] [Audio]
# Enable flame sound. # Enable flame sound.
flame_sound (Flame sound) bool true flame_sound (Flame sound) bool true
@ -186,3 +191,6 @@ mcl_enable_fapples (Enable swiftness on enchanted golden apples) bool true
[Debugging] [Debugging]
# If enabled, this will show the itemstring of an item in the description. # If enabled, this will show the itemstring of an item in the description.
mcl_item_id_debug (Item ID Debug) bool false mcl_item_id_debug (Item ID Debug) bool false
#Log mob spawning and despawning events
mcl_logging_mobs_spawn (Log Mob Spawning) bool true