diff --git a/mods/ENTITIES/extra_mobs/fox.lua b/mods/ENTITIES/extra_mobs/fox.lua index 7df04b554..10f9ca898 100644 --- a/mods/ENTITIES/extra_mobs/fox.lua +++ b/mods/ENTITIES/extra_mobs/fox.lua @@ -18,12 +18,7 @@ local S = minetest.get_translator("extra_mobs") --################### fox --################### -local followitem = "" -if minetest.get_modpath("mc_sweet_berry") then - followitem = "mc_sweet_berry:sweet_berry" -else - followitem = nil -end +local followitem = "mcl_farming:sweet_berry" local fox = { type = "monster", @@ -123,35 +118,30 @@ local fox = { mobs:register_mob("extra_mobs:fox", fox) -- spawning -mobs:spawn_specific( -"extra_mobs:fox", -"overworld", -"ground", -{ -"FlowerForest", -"Swampland", -"Taiga", -"ExtremeHills", -"BirchForest", -"MegaSpruceTaiga", -"MegaTaiga", -"ExtremeHills+", -"Forest", -"Plains", -"ColdTaiga", -"SunflowerPlains", -"RoofedForest", -"MesaPlateauFM_grasstop", -"ExtremeHillsM", -"BirchForestM", -}, -0, -minetest.LIGHT_MAX+1, -30, -6000, -3, -mobs_mc.spawn_height.water, -mobs_mc.spawn_height.overworld_max) +mobs:spawn_setup({ + name = "extra_mobs:fox", + biomes = { + "FlowerForest", + "Swampland", + "Taiga", + "ExtremeHills", + "BirchForest", + "MegaSpruceTaiga", + "MegaTaiga", + "ExtremeHills+", + "Forest", + "Plains", + "ColdTaiga", + "SunflowerPlains", + "RoofedForest", + "MesaPlateauFM_grasstop", + "ExtremeHillsM", + "BirchForestM", + }, + interval = 30, + chance = 6000, + min_height = mobs_mc.spawn_height.water, +}) --mobs:spawn_specific("extra_mobs:fox", "overworld", "ground", 0, minetest.LIGHT_MAX+1, 30, 6000, 3, 0, 500) --[[ diff --git a/mods/ENTITIES/extra_mobs/glow_squid_items.lua b/mods/ENTITIES/extra_mobs/glow_squid_items.lua index db4d88e89..c7f30662b 100644 --- a/mods/ENTITIES/extra_mobs/glow_squid_items.lua +++ b/mods/ENTITIES/extra_mobs/glow_squid_items.lua @@ -54,10 +54,11 @@ minetest.register_entity("extra_mobs:glow_item_frame_item",{ end end, get_staticdata = function(self) + if not self then return end if self._nodename ~= nil and self._texture ~= nil then local ret = self._nodename .. ';' .. self._texture if self._scale ~= nil then - ret = ret .. ';' .. self._scale + ret = ret .. ';' .. tostring(self._scale) end return ret end diff --git a/mods/ENTITIES/extra_mobs/strider.lua b/mods/ENTITIES/extra_mobs/strider.lua index d6c854207..bc86379cd 100644 --- a/mods/ENTITIES/extra_mobs/strider.lua +++ b/mods/ENTITIES/extra_mobs/strider.lua @@ -213,20 +213,36 @@ baby_strider.child = 1 mobs:register_mob("extra_mobs:baby_strider", baby_strider) -- Regular spawning in the Nether -mobs:spawn_specific( -"extra_mobs:strider", -"nether", -"lava", -{ -"Nether" -}, -0, -minetest.LIGHT_MAX+1, -30, -6000, -3, -mobs_mc.spawn_height.nether_min, -mobs_mc.spawn_height.nether_max) + +mobs:spawn_setup({ + name = "extra_mobs:strider", + type_of_spawning = "lava", + dimension = "nether", + biomes = { + "Nether" + }, + min_height = mcl_mapgen.nether.min, + max_height = mcl_mapgen.nether.max, + chance = 2000, + check_position = function(pos) + return minetest.get_node({x = pos.x, y = pos.y - 1, z = pos.z}).name:find("lava") + end +}) + +mobs:spawn_setup({ + name = "extra_mobs:baby_strider", + type_of_spawning = "lava", + dimension = "nether", + biomes = { + "Nether" + }, + min_height = mcl_mapgen.nether.min, + max_height = mcl_mapgen.nether.max, + chance = 100, + check_position = function(pos) + return minetest.get_node({x = pos.x, y = pos.y - 1, z = pos.z}).name:find("lava") + end +}) -- spawn eggs mobs:register_egg("extra_mobs:strider", S("Strider"), "extra_mobs_spawn_icon_strider.png", 0) diff --git a/mods/ENTITIES/mcl_boats/init.lua b/mods/ENTITIES/mcl_boats/init.lua index 9ec06d870..3a26c1b36 100644 --- a/mods/ENTITIES/mcl_boats/init.lua +++ b/mods/ENTITIES/mcl_boats/init.lua @@ -175,10 +175,13 @@ function boat.on_activate(self, staticdata, dtime_s) end function boat.get_staticdata(self) + if not self then return end + local object = self.object + local object_properties = object and object.get_properties and object:get_properties() return minetest.serialize({ v = self._v, itemstring = self._itemstring, - textures = self.object:get_properties().textures + textures = object_properties and object_properties.textures }) end diff --git a/mods/ENTITIES/mcl_falling_nodes/init.lua b/mods/ENTITIES/mcl_falling_nodes/init.lua index d527603de..769f1d4a9 100644 --- a/mods/ENTITIES/mcl_falling_nodes/init.lua +++ b/mods/ENTITIES/mcl_falling_nodes/init.lua @@ -89,6 +89,7 @@ minetest.register_entity(":__builtin:falling_node", { }) end, get_staticdata = function(self) + if not self then return end local meta = self.meta -- Workaround: Save inventory seperately from metadata. -- Because Minetest crashes when a node with inventory gets deactivated diff --git a/mods/ENTITIES/mcl_item_entity/init.lua b/mods/ENTITIES/mcl_item_entity/init.lua index b1202f4ad..e88f4dd80 100644 --- a/mods/ENTITIES/mcl_item_entity/init.lua +++ b/mods/ENTITIES/mcl_item_entity/init.lua @@ -486,6 +486,7 @@ minetest.register_entity(":__builtin:item", { end, get_staticdata = function(self) + if not self then return end local data = minetest.serialize({ itemstring = self.itemstring, always_collect = self.always_collect, diff --git a/mods/ENTITIES/mcl_minecarts/init.lua b/mods/ENTITIES/mcl_minecarts/init.lua index 119a13523..4294c4630 100644 --- a/mods/ENTITIES/mcl_minecarts/init.lua +++ b/mods/ENTITIES/mcl_minecarts/init.lua @@ -503,6 +503,7 @@ local function register_entity(entity_id, mesh, textures, drop, on_rightclick, o end function cart:get_staticdata() + if not self then return end return minetest.serialize({_railtype = self._railtype}) end diff --git a/mods/ENTITIES/mcl_mobs/api/api.lua b/mods/ENTITIES/mcl_mobs/api/api.lua index ea7589f47..c72dca0bd 100644 --- a/mods/ENTITIES/mcl_mobs/api/api.lua +++ b/mods/ENTITIES/mcl_mobs/api/api.lua @@ -425,7 +425,9 @@ function mobs:register_mob(name, def) end, get_staticdata = function(self) - return mobs.mob_staticdata(self) + if self and mobs then + return mobs.mob_staticdata(self) + end end, --harmed_by_heal = def.harmed_by_heal, diff --git a/mods/ENTITIES/mcl_mobs/api/mob_functions/ai.lua b/mods/ENTITIES/mcl_mobs/api/mob_functions/ai.lua index cbbda43d5..ab91a0542 100644 --- a/mods/ENTITIES/mcl_mobs/api/mob_functions/ai.lua +++ b/mods/ENTITIES/mcl_mobs/api/mob_functions/ai.lua @@ -902,10 +902,14 @@ function mobs.mob_step(self, dtime) object = self.object, max_hear_distance = 5 }, true) - self.object:punch(self.object, 1.0, { - full_punch_interval = 1.0, - damage_groups = {fleshy = self.lava_damage} - }, nil) +--[[ if not mcl_burning.is_burning(self.object) then + mcl_burning.set_on_fire(self.object, 1.1) + else +]] self.object:punch(self.object, 1.0, { + full_punch_interval = 1.0, + damage_groups = {fleshy = self.lava_damage} + }, nil) +-- end self.lava_counter = 0 self.health = self.health - lava_damage self:teleport() diff --git a/mods/ENTITIES/mcl_mobs/api/mob_functions/environment.lua b/mods/ENTITIES/mcl_mobs/api/mob_functions/environment.lua index c7fb073b0..22013e35e 100644 --- a/mods/ENTITIES/mcl_mobs/api/mob_functions/environment.lua +++ b/mods/ENTITIES/mcl_mobs/api/mob_functions/environment.lua @@ -211,26 +211,6 @@ mobs.teleport = function(self, target) end end ---a function used for despawning mobs -mobs.check_for_player_within_area = function(self, radius) - local pos1 = self.object:get_pos() - if not pos1 then return end - --get players in radius - for _,player in pairs(minetest_get_connected_players()) do - if player and player:get_hp() > 0 then - local pos2 = player:get_pos() - local distance = vector_distance(pos1,pos2) - if distance < radius then - --found a player - return true - end - end - end - --did not find a player - return false -end - - --a simple helper function for mobs following mobs.get_2d_distance = function(pos1,pos2) pos1.y = 0 diff --git a/mods/ENTITIES/mcl_mobs/api/mob_functions/set_up.lua b/mods/ENTITIES/mcl_mobs/api/mob_functions/set_up.lua index b9cf2f669..d9cc4237c 100644 --- a/mods/ENTITIES/mcl_mobs/api/mob_functions/set_up.lua +++ b/mods/ENTITIES/mcl_mobs/api/mob_functions/set_up.lua @@ -5,9 +5,27 @@ local minetest_settings = minetest.settings -- CMI support check local use_cmi = minetest.global_exists("cmi") +local vector_distance = vector.distance +local minetest_get_connected_players = minetest.get_connected_players +local math_random = math.random + mobs.can_despawn = function(self) - return (not self.tamed and not self.bred and not self.nametag and - not mobs.check_for_player_within_area(self, 64)); + if self.tamed or self.bred or self.nametag then return false end + local mob_pos = self.object:get_pos() + if not mob_pos then return true end + local distance = 999 + for _, player in pairs(minetest_get_connected_players()) do + if player and player:get_hp() > 0 then + local player_pos = player:get_pos() + local new_distance = vector_distance(player_pos, mob_pos) + if new_distance < distance then + distance = new_distance + if distance < 33 then return false end + if distance < 128 and math_random(1, 42) ~= 11 then return false end + end + end + end + return true end -- get entity staticdata @@ -24,7 +42,7 @@ mobs.mob_staticdata = function(self) self.following = nil if use_cmi then - self.serialized_cmi_components = cmi.serialize_components(self._cmi_components) + self.serialized_cmi_components = cmi and cmi.serialize_components(self._cmi_components) end local tmp = {} diff --git a/mods/ENTITIES/mcl_mobs/api/spawning.lua b/mods/ENTITIES/mcl_mobs/api/spawning.lua index bf07ca94d..424989426 100644 --- a/mods/ENTITIES/mcl_mobs/api/spawning.lua +++ b/mods/ENTITIES/mcl_mobs/api/spawning.lua @@ -6,11 +6,14 @@ local find_nodes_in_area_under_air = minetest.find_nodes_in_area_under_air local get_biome_name = minetest.get_biome_name local get_objects_inside_radius = minetest.get_objects_inside_radius local get_connected_players = minetest.get_connected_players - +local minetest_get_perlin = minetest.get_perlin local math_random = math.random local math_floor = math.floor ---local max = math.max +local math_ceil = math.ceil +local math_cos = math.cos +local math_sin = math.sin +local math_round = function(x) return (x > 0) and math_floor(x + 0.5) or math_ceil(x - 0.5) end --local vector_distance = vector.distance local vector_new = vector.new @@ -22,151 +25,171 @@ local table_remove = table.remove local pairs = pairs -- range for mob count -local aoc_range = 48 +local aoc_range = 32 --do mobs spawn? local mobs_spawn = minetest.settings:get_bool("mobs_spawn", true) ~= false ---[[ -THIS IS THE BIG LIST OF ALL BIOMES - used for programming/updating mobs +local noise_params = { + offset = 0, + scale = 3, + spread = { + x = 301, + y = 50, + z = 304, + }, + seed = 100, + octaves = 3, + persistence = 0.5, +} -underground: -"FlowerForest_underground", -"JungleEdge_underground",local spawning_position = spawning_position_list[math.random(1,#spawning_position_list)] -"ColdTaiga_underground", -"IcePlains_underground", -"IcePlainsSpikes_underground", -"MegaTaiga_underground", -"Taiga_underground", -"ExtremeHills+_underground", -"JungleM_underground", -"ExtremeHillsM_underground", -"JungleEdgeM_underground", +-- THIS IS THE BIG LIST OF ALL BIOMES - used for programming/updating mobs +-- Also used for missing parameter +-- Please update the list when adding new biomes! -ocean: -"RoofedForest_ocean", -"JungleEdgeM_ocean", -"BirchForestM_ocean", -"BirchForest_ocean", -"IcePlains_deep_ocean", -"Jungle_deep_ocean", -"Savanna_ocean", -"MesaPlateauF_ocean", -"ExtremeHillsM_deep_ocean", -"Savanna_deep_ocean", -"SunflowerPlains_ocean", -"Swampland_deep_ocean", -"Swampland_ocean", -"MegaSpruceTaiga_deep_ocean", -"ExtremeHillsM_ocean", -"JungleEdgeM_deep_ocean", -"SunflowerPlains_deep_ocean", -"BirchForest_deep_ocean", -"IcePlainsSpikes_ocean", -"Mesa_ocean", -"StoneBeach_ocean", -"Plains_deep_ocean", -"JungleEdge_deep_ocean", -"SavannaM_deep_ocean", -"Desert_deep_ocean", -"Mesa_deep_ocean", -"ColdTaiga_deep_ocean", -"Plains_ocean", -"MesaPlateauFM_ocean", -"Forest_deep_ocean", -"JungleM_deep_ocean", -"FlowerForest_deep_ocean", -"MushroomIsland_ocean", -"MegaTaiga_ocean", -"StoneBeach_deep_ocean", -"IcePlainsSpikes_deep_ocean", -"ColdTaiga_ocean", -"SavannaM_ocean", -"MesaPlateauF_deep_ocean", -"MesaBryce_deep_ocean", -"ExtremeHills+_deep_ocean", -"ExtremeHills_ocean", -"MushroomIsland_deep_ocean", -"Forest_ocean", -"MegaTaiga_deep_ocean", -"JungleEdge_ocean", -"MesaBryce_ocean", -"MegaSpruceTaiga_ocean", -"ExtremeHills+_ocean", -"Jungle_ocean", -"RoofedForest_deep_ocean", -"IcePlains_ocean", -"FlowerForest_ocean", -"ExtremeHills_deep_ocean", -"MesaPlateauFM_deep_ocean", -"Desert_ocean", -"Taiga_ocean", -"BirchForestM_deep_ocean", -"Taiga_deep_ocean", -"JungleM_ocean", +local list_of_all_biomes = { -water or beach? -"MesaPlateauFM_sandlevel", -"MesaPlateauF_sandlevel", -"MesaBryce_sandlevel", -"Mesa_sandlevel", + -- underground: -beach: -"FlowerForest_beach", -"Forest_beach", -"StoneBeach", -"ColdTaiga_beach_water", -"Taiga_beach", -"Savanna_beach", -"Plains_beach", -"ExtremeHills_beach", -"ColdTaiga_beach", -"Swampland_shore", -"MushroomIslandShore", -"JungleM_shore", -"Jungle_shore", + "FlowerForest_underground", + "JungleEdge_underground", + "ColdTaiga_underground", + "IcePlains_underground", + "IcePlainsSpikes_underground", + "MegaTaiga_underground", + "Taiga_underground", + "ExtremeHills+_underground", + "JungleM_underground", + "ExtremeHillsM_underground", + "JungleEdgeM_underground", -dimension biome: -"Nether", -"End", + -- ocean: -Overworld regular: -"Mesa", -"FlowerForest", -"Swampland", -"Taiga", -"ExtremeHills", -"Jungle", -"Savanna", -"BirchForest", -"MegaSpruceTaiga", -"MegaTaiga", -"ExtremeHills+", -"Forest", -"Plains", -"Desert", -"ColdTaiga", -"MushroomIsland", -"IcePlainsSpikes", -"SunflowerPlains", -"IcePlains", -"RoofedForest", -"ExtremeHills+_snowtop", -"MesaPlateauFM_grasstop", -"JungleEdgeM", -"ExtremeHillsM", -"JungleM", -"BirchForestM", -"MesaPlateauF", -"MesaPlateauFM", -"MesaPlateauF_grasstop", -"MesaBryce", -"JungleEdge", -"SavannaM", -]]-- + "RoofedForest_ocean", + "JungleEdgeM_ocean", + "BirchForestM_ocean", + "BirchForest_ocean", + "IcePlains_deep_ocean", + "Jungle_deep_ocean", + "Savanna_ocean", + "MesaPlateauF_ocean", + "ExtremeHillsM_deep_ocean", + "Savanna_deep_ocean", + "SunflowerPlains_ocean", + "Swampland_deep_ocean", + "Swampland_ocean", + "MegaSpruceTaiga_deep_ocean", + "ExtremeHillsM_ocean", + "JungleEdgeM_deep_ocean", + "SunflowerPlains_deep_ocean", + "BirchForest_deep_ocean", + "IcePlainsSpikes_ocean", + "Mesa_ocean", + "StoneBeach_ocean", + "Plains_deep_ocean", + "JungleEdge_deep_ocean", + "SavannaM_deep_ocean", + "Desert_deep_ocean", + "Mesa_deep_ocean", + "ColdTaiga_deep_ocean", + "Plains_ocean", + "MesaPlateauFM_ocean", + "Forest_deep_ocean", + "JungleM_deep_ocean", + "FlowerForest_deep_ocean", + "MushroomIsland_ocean", + "MegaTaiga_ocean", + "StoneBeach_deep_ocean", + "IcePlainsSpikes_deep_ocean", + "ColdTaiga_ocean", + "SavannaM_ocean", + "MesaPlateauF_deep_ocean", + "MesaBryce_deep_ocean", + "ExtremeHills+_deep_ocean", + "ExtremeHills_ocean", + "MushroomIsland_deep_ocean", + "Forest_ocean", + "MegaTaiga_deep_ocean", + "JungleEdge_ocean", + "MesaBryce_ocean", + "MegaSpruceTaiga_ocean", + "ExtremeHills+_ocean", + "Jungle_ocean", + "RoofedForest_deep_ocean", + "IcePlains_ocean", + "FlowerForest_ocean", + "ExtremeHills_deep_ocean", + "MesaPlateauFM_deep_ocean", + "Desert_ocean", + "Taiga_ocean", + "BirchForestM_deep_ocean", + "Taiga_deep_ocean", + "JungleM_ocean", + -- water or beach? + "MesaPlateauFM_sandlevel", + "MesaPlateauF_sandlevel", + "MesaBryce_sandlevel", + "Mesa_sandlevel", + + -- beach: + + "FlowerForest_beach", + "Forest_beach", + "StoneBeach", + "ColdTaiga_beach_water", + "Taiga_beach", + "Savanna_beach", + "Plains_beach", + "ExtremeHills_beach", + "ColdTaiga_beach", + "Swampland_shore", + "MushroomIslandShore", + "JungleM_shore", + "Jungle_shore", + + -- dimension biome: + + "Nether", + "End", + + -- Overworld regular: + + "Mesa", + "FlowerForest", + "Swampland", + "Taiga", + "ExtremeHills", + "Jungle", + "Savanna", + "BirchForest", + "MegaSpruceTaiga", + "MegaTaiga", + "ExtremeHills+", + "Forest", + "Plains", + "Desert", + "ColdTaiga", + "MushroomIsland", + "IcePlainsSpikes", + "SunflowerPlains", + "IcePlains", + "RoofedForest", + "ExtremeHills+_snowtop", + "MesaPlateauFM_grasstop", + "JungleEdgeM", + "ExtremeHillsM", + "JungleM", + "BirchForestM", + "MesaPlateauF", + "MesaPlateauFM", + "MesaPlateauF_grasstop", + "MesaBryce", + "JungleEdge", + "SavannaM", +} -- count how many mobs are in an area local function count_mobs(pos) @@ -216,11 +239,73 @@ WARNING: BIOME INTEGRATION NEEDED -> How to get biome through lua?? --this is where all of the spawning information is kept local spawn_dictionary = {} +local summary_chance = 0 + +function mobs:spawn_setup(def) + if not mobs_spawn then return end + + if not def then + minetest.log("warning", "Empty mob spawn setup definition") + return + end + + local name = def.name + if not name then + minetest.log("warning", "Missing mob name") + return + end + + local dimension = def.dimension or "overworld" + local type_of_spawning = def.type_of_spawning or "ground" + local biomes = def.biomes or list_of_all_biomes + local min_light = def.min_light or 0 + local max_light = def.max_light or (minetest.LIGHT_MAX + 1) + local chance = def.chance or 1000 + local aoc = def.aoc or aoc_range + local min_height = def.min_height or mcl_mapgen.overworld.min + local max_height = def.max_height or mcl_mapgen.overworld.max + local day_toggle = def.day_toggle + local on_spawn = def.on_spawn + local check_position = def.check_position + + -- chance/spawn number override in minetest.conf for registered mob + local numbers = minetest.settings:get(name) + if numbers then + numbers = numbers:split(",") + chance = tonumber(numbers[1]) or chance + aoc = tonumber(numbers[2]) or aoc + if chance == 0 then + minetest.log("warning", string.format("[mobs] %s has spawning disabled", name)) + return + end + minetest.log("action", string.format("[mobs] Chance setting for %s changed to %s (total: %s)", name, chance, aoc)) + end + + if chance < 1 then + chance = 1 + minetest.log("warning", "Chance shouldn't be less than 1 (mob name: " .. name ..")") + end + + spawn_dictionary[#spawn_dictionary + 1] = { + name = name, + dimension = dimension, + type_of_spawning = type_of_spawning, + biomes = biomes, + min_light = min_light, + max_light = max_light, + chance = chance, + aoc = aoc, + min_height = min_height, + max_height = max_height, + day_toggle = day_toggle, + check_position = check_position, + on_spawn = on_spawn, + } + summary_chance = summary_chance + chance +end function mobs:spawn_specific(name, dimension, type_of_spawning, biomes, min_light, max_light, interval, chance, aoc, min_height, max_height, day_toggle, on_spawn) - --print(dump(biomes)) - -- Do mobs spawn at all? if not mobs_spawn then return @@ -239,179 +324,7 @@ function mobs:spawn_specific(name, dimension, type_of_spawning, biomes, min_ligh return end - minetest.log("action", - string.format("[mobs] Chance setting for %s changed to %s (total: %s)", name, chance, aoc)) - end - - --[[ - local function spawn_action(pos, node, active_object_count, active_object_count_wider, name) - - local orig_pos = table.copy(pos) - -- is mob actually registered? - if not mobs.spawning_mobs[name] - or not minetest.registered_entities[name] then - minetest.log("warning", "Mob spawn of "..name.." failed, unknown entity or mob is not registered for spawning!") - return - end - - -- additional custom checks for spawning mob - if mobs:spawn_abm_check(pos, node, name) == true then - minetest.log("info", "Mob spawn of "..name.." at "..minetest.pos_to_string(pos).." failed, ABM check rejected!") - return - end - - -- count nearby mobs in same spawn class - local entdef = minetest.registered_entities[name] - local spawn_class = entdef and entdef.spawn_class - if not spawn_class then - if entdef.type == "monster" then - spawn_class = "hostile" - else - spawn_class = "passive" - end - end - local in_class_cap = count_mobs(pos, "!"..spawn_class) < MOB_CAP[spawn_class] - -- do not spawn if too many of same mob in area - if active_object_count_wider >= max_per_block -- large-range mob cap - or (not in_class_cap) -- spawn class mob cap - or count_mobs(pos, name) >= aoc then -- per-mob mob cap - -- too many entities - minetest.log("info", "Mob spawn of "..name.." at "..minetest.pos_to_string(pos).." failed, too crowded!") - return - end - - -- if toggle set to nil then ignore day/night check - if day_toggle then - - local tod = (minetest.get_timeofday() or 0) * 24000 - - if tod > 4500 and tod < 19500 then - -- daylight, but mob wants night - if day_toggle == false then - -- mob needs night - minetest.log("info", "Mob spawn of "..name.." at "..minetest.pos_to_string(pos).." failed, mob needs light!") - return - end - else - -- night time but mob wants day - if day_toggle == true then - -- mob needs day - minetest.log("info", "Mob spawn of "..name.." at "..minetest.pos_to_string(pos).." failed, mob needs daylight!") - return - end - end - end - - -- spawn above node - pos.y = pos.y + 1 - - -- only spawn away from player - local objs = minetest.get_objects_inside_radius(pos, 24) - - for n = 1, #objs do - - if objs[n]:is_player() then - -- player too close - minetest.log("info", "Mob spawn of "..name.." at "..minetest.pos_to_string(pos).." failed, player too close!") - return - end - end - - -- mobs cannot spawn in protected areas when enabled - if not spawn_protected - and minetest.is_protected(pos, "") then - minetest.log("info", "Mob spawn of "..name.." at "..minetest.pos_to_string(pos).." failed, position is protected!") - return - end - - -- are we spawning within height limits? - if pos.y > max_height - or pos.y < min_height then - minetest.log("info", "Mob spawn of "..name.." at "..minetest.pos_to_string(pos).." failed, out of height limit!") - return - end - - -- are light levels ok? - local light = minetest.get_node_light(pos) - if not light - or light > max_light - or light < min_light then - minetest.log("info", "Mob spawn of "..name.." at "..minetest.pos_to_string(pos).." failed, bad light!") - return - end - - -- do we have enough space to spawn mob? - local ent = minetest.registered_entities[name] - local width_x = max(1, math.ceil(ent.collisionbox[4] - ent.collisionbox[1])) - local min_x, max_x - if width_x % 2 == 0 then - max_x = math.floor(width_x/2) - min_x = -(max_x-1) - else - max_x = math.floor(width_x/2) - min_x = -max_x - end - - local width_z = max(1, math.ceil(ent.collisionbox[6] - ent.collisionbox[3])) - local min_z, max_z - if width_z % 2 == 0 then - max_z = math.floor(width_z/2) - min_z = -(max_z-1) - else - max_z = math.floor(width_z/2) - min_z = -max_z - end - - local max_y = max(0, math.ceil(ent.collisionbox[5] - ent.collisionbox[2]) - 1) - - for y = 0, max_y do - for x = min_x, max_x do - for z = min_z, max_z do - local pos2 = {x = pos.x+x, y = pos.y+y, z = pos.z+z} - if minetest.registered_nodes[node_ok(pos2).name].walkable == true then - -- inside block - minetest.log("info", "Mob spawn of "..name.." at "..minetest.pos_to_string(pos).." failed, too little space!") - if ent.spawn_small_alternative and (not minetest.registered_nodes[node_ok(pos).name].walkable) then - minetest.log("info", "Trying to spawn smaller alternative mob: "..ent.spawn_small_alternative) - spawn_action(orig_pos, node, active_object_count, active_object_count_wider, ent.spawn_small_alternative) - end - return - end - end - end - end - - -- tweak X/Y/Z spawn pos - if width_x % 2 == 0 then - pos.x = pos.x + 0.5 - end - if width_z % 2 == 0 then - pos.z = pos.z + 0.5 - end - pos.y = pos.y - 0.5 - - local mob = minetest.add_entity(pos, name) - minetest.log("action", "Mob spawned: "..name.." at "..minetest.pos_to_string(pos)) - - if on_spawn then - - local ent = mob:get_luaentity() - - on_spawn(ent, pos) - end - end - - local function spawn_abm_action(pos, node, active_object_count, active_object_count_wider) - spawn_action(pos, node, active_object_count, active_object_count_wider, name) - end - ]]-- - - local entdef = minetest.registered_entities[name] - local spawn_class - if entdef.type == "monster" then - spawn_class = "hostile" - else - spawn_class = "passive" + minetest.log("action", string.format("[mobs] Chance setting for %s changed to %s (total: %s)", name, chance, aoc)) end --load information into the spawn dictionary @@ -423,107 +336,34 @@ function mobs:spawn_specific(name, dimension, type_of_spawning, biomes, min_ligh spawn_dictionary[key]["biomes"] = biomes spawn_dictionary[key]["min_light"] = min_light spawn_dictionary[key]["max_light"] = max_light - spawn_dictionary[key]["interval"] = interval spawn_dictionary[key]["chance"] = chance spawn_dictionary[key]["aoc"] = aoc spawn_dictionary[key]["min_height"] = min_height spawn_dictionary[key]["max_height"] = max_height spawn_dictionary[key]["day_toggle"] = day_toggle - --spawn_dictionary[key]["on_spawn"] = spawn_abm_action - spawn_dictionary[key]["spawn_class"] = spawn_class - --[[ - minetest.register_abm({ - label = name .. " spawning", - nodenames = nodes, - neighbors = neighbors, - interval = interval, - chance = floor(max(1, chance * mobs_spawn_chance)), - catch_up = false, - action = spawn_abm_action, - }) - ]]-- + summary_chance = summary_chance + chance end --- compatibility with older mob registration --- we're going to forget about this for now -j4i ---[[ -function mobs:register_spawn(name, nodes, max_light, min_light, chance, active_object_count, max_height, day_toggle) - - mobs:spawn_specific(name, nodes, {"air"}, min_light, max_light, 30, - chance, active_object_count, -31000, max_height, day_toggle) +local two_pi = 2 * math.pi +local function get_next_mob_spawn_pos(pos) + local distance = math_random(25, 32) + local angle = math_random() * two_pi + return { + x = math_round(pos.x + distance * math_cos(angle)), + y = pos.y, + z = math_round(pos.z + distance * math_sin(angle)) + } end -]]-- - - ---Don't disable this yet-j4i --- MarkBu's spawn function - -function mobs:spawn(def) - --does nothing for now - --[[ - local name = def.name - local nodes = def.nodes or {"group:soil", "group:stone"} - local neighbors = def.neighbors or {"air"} - local min_light = def.min_light or 0 - local max_light = def.max_light or 15 - local interval = def.interval or 30 - local chance = def.chance or 5000 - local active_object_count = def.active_object_count or 1 - local min_height = def.min_height or -31000 - local max_height = def.max_height or 31000 - local day_toggle = def.day_toggle - local on_spawn = def.on_spawn - - mobs:spawn_specific(name, nodes, neighbors, min_light, max_light, interval, - chance, active_object_count, min_height, max_height, day_toggle, on_spawn) - ]]-- -end - - - -local axis ---inner and outer part of square donut radius -local inner = 15 -local outer = 64 -local int = {-1,1} - -local function position_calculation(pos) - - pos = vector_floor(pos) - - --this is used to determine the axis buffer from the player - axis = math_random(0,1) - - --cast towards the direction - if axis == 0 then --x - pos.x = pos.x + math_random(inner,outer)*int[math_random(1,2)] - pos.z = pos.z + math_random(-outer,outer) - else --z - pos.z = pos.z + math_random(inner,outer)*int[math_random(1,2)] - pos.x = pos.x + math_random(-outer,outer) - end - return pos -end - ---[[ -local decypher_limits_dictionary = { - ["overworld"] = {mcl_vars.mg_overworld_min,mcl_vars.mg_overworld_max}, - ["nether"] = {mcl_vars.mg_nether_min, mcl_vars.mg_nether_max}, - ["end"] = {mcl_vars.mg_end_min, mcl_vars.mg_end_max} -} -]]-- local function decypher_limits(posy) - --local min_max_table = decypher_limits_dictionary[dimension] - --return min_max_table[1],min_max_table[2] posy = math_floor(posy) return posy - 32, posy + 32 end --a simple helper function for mob_spawn local function biome_check(biome_list, biome_goal) - for _,data in ipairs(biome_list) do + for _, data in pairs(biome_list) do if data == biome_goal then return true end @@ -533,176 +373,102 @@ local function biome_check(biome_list, biome_goal) end ---todo mob limiting ---MAIN LOOP - if mobs_spawn then + + local perlin_noise + + local function spawn_a_mob(pos, dimension, y_min, y_max) + local dimension = dimension or mcl_worlds.pos_to_dimension(pos) + local goal_pos = get_next_mob_spawn_pos(pos) + local spawning_position_list = find_nodes_in_area_under_air( + {x = goal_pos.x, y = y_min, z = goal_pos.z}, + {x = goal_pos.x, y = y_max, z = goal_pos.z}, + {"group:solid", "group:water", "group:lava"} + ) + if #spawning_position_list <= 0 then return end + local spawning_position = spawning_position_list[math_random(1, #spawning_position_list)] + + --hard code mob limit in area to 5 for now + if count_mobs(spawning_position) >= 5 then return end + + local gotten_node = get_node(spawning_position).name + local gotten_biome = minetest.get_biome_data(spawning_position) + if not gotten_node or not gotten_biome then return end + gotten_biome = get_biome_name(gotten_biome.biome) --makes it easier to work with + + --add this so mobs don't spawn inside nodes + spawning_position.y = spawning_position.y + 1 + + --only need to poll for node light if everything else worked + local gotten_light = get_node_light(spawning_position) + + local is_water = get_item_group(gotten_node, "water") ~= 0 + local is_lava = get_item_group(gotten_node, "lava") ~= 0 + local is_ground = not (is_water or is_lava) + if not is_ground then + spawning_position.y = spawning_position.y - 1 + end + + local mob_def + + --create a disconnected clone of the spawn dictionary + --prevents memory leak + local mob_library_worker_table = table_copy(spawn_dictionary) + + --grab mob that fits into the spawning location + --randomly grab a mob, don't exclude any possibilities + perlin_noise = perlin_noise or minetest_get_perlin(noise_params) + local noise = perlin_noise:get_3d(spawning_position) + local current_summary_chance = summary_chance + while #mob_library_worker_table > 0 do + local mob_chance_offset = (math_round(noise * current_summary_chance + 12345) % current_summary_chance) + 1 + local mob_index = 1 + local mob_chance = mob_library_worker_table[mob_index].chance + local step_chance = mob_chance + while step_chance < mob_chance_offset do + mob_index = mob_index + 1 + mob_chance = mob_library_worker_table[mob_index].chance + step_chance = step_chance + mob_chance + end + local mob_def = mob_library_worker_table[mob_index] + if mob_def + and spawning_position.y >= mob_def.min_height + and spawning_position.y <= mob_def.max_height + and mob_def.dimension == dimension + and biome_check(mob_def.biomes, gotten_biome) + and gotten_light >= mob_def.min_light + and gotten_light <= mob_def.max_light + and (is_ground or mob_def.type_of_spawning ~= "ground") + and (mob_def.check_position and mob_def.check_position(spawning_position) or true) + then + --everything is correct, spawn mob + local object = minetest.add_entity(spawning_position, mob_def.name) + if object then + return mob_def.on_spawn and mob_def.on_spawn(object, pos) + end + end + current_summary_chance = current_summary_chance - mob_chance + table_remove(mob_library_worker_table, mob_index) + end + end + + + --MAIN LOOP + local timer = 0 minetest.register_globalstep(function(dtime) timer = timer + dtime - if timer >= 10 then - timer = 0 - for _,player in pairs(get_connected_players()) do - -- after this line each "break" means "continue" - local do_mob_spawning = true - repeat - --don't need to get these variables more than once - --they happen in a single server step - - local player_pos = player:get_pos() - local dimension = mcl_worlds.pos_to_dimension(player_pos) - - if dimension == "void" or dimension == "default" then - break -- ignore void and unloaded area - end - - local min, max = decypher_limits(player_pos.y) - - for i = 1, math_random(1,4) do - -- after this line each "break" means "continue" - local do_mob_algorithm = true - repeat - - local goal_pos = position_calculation(player_pos) - - local spawning_position_list = find_nodes_in_area_under_air(vector_new(goal_pos.x,min,goal_pos.z), vector_new(goal_pos.x,max,goal_pos.z), {"group:solid", "group:water", "group:lava"}) - - --couldn't find node - if #spawning_position_list <= 0 then - break - end - - local spawning_position = spawning_position_list[math_random(1,#spawning_position_list)] - - --Prevent strange behavior --- this is commented out: /too close to player --fixed with inner circle - if not spawning_position then -- or vector_distance(player_pos, spawning_position) < 15 - break - end - - --hard code mob limit in area to 5 for now - if count_mobs(spawning_position) >= 5 then - break - end - - local gotten_node = get_node(spawning_position).name - - if not gotten_node or gotten_node == "air" then --skip air nodes - break - end - - local gotten_biome = minetest.get_biome_data(spawning_position) - - if not gotten_biome then - break --skip if in unloaded area - end - - gotten_biome = get_biome_name(gotten_biome.biome) --makes it easier to work with - - --add this so mobs don't spawn inside nodes - spawning_position.y = spawning_position.y + 1 - - --only need to poll for node light if everything else worked - local gotten_light = get_node_light(spawning_position) - - local is_water = get_item_group(gotten_node, "water") ~= 0 - local is_lava = get_item_group(gotten_node, "lava") ~= 0 - - local mob_def = nil - - --create a disconnected clone of the spawn dictionary - --prevents memory leak - local mob_library_worker_table = table_copy(spawn_dictionary) - - --grab mob that fits into the spawning location - --randomly grab a mob, don't exclude any possibilities - local repeat_mob_search = true - repeat - - --do not infinite loop - if #mob_library_worker_table <= 0 then - --print("breaking infinite loop") - break - end - - local skip = false - - --use this for removing table elements of mobs that do not match - local temp_index = math_random(1,#mob_library_worker_table) - - local temp_def = mob_library_worker_table[temp_index] - - --skip if something ridiculous happens (nil mob def) - --something truly horrible has happened if skip gets - --activated at this point - if not temp_def then - skip = true - end - - if not skip and (spawning_position.y < temp_def.min_height or spawning_position.y > temp_def.max_height) then - skip = true - end - - --skip if not correct dimension - if not skip and (temp_def.dimension ~= dimension) then - skip = true - end - - --skip if not in correct biome - if not skip and (not biome_check(temp_def.biomes, gotten_biome)) then - skip = true - end - - --don't spawn if not in light limits - if not skip and (gotten_light < temp_def.min_light or gotten_light > temp_def.max_light) then - skip = true - end - - --skip if not in correct spawning type - if not skip and (temp_def.type_of_spawning == "ground" and is_water) then - skip = true - end - - if not skip and (temp_def.type_of_spawning == "ground" and is_lava) then - skip = true - end - - --found a mob, exit out of loop - if not skip then - --minetest.log("warning", "found mob:"..temp_def.name) - --print("found mob:"..temp_def.name) - mob_def = table_copy(temp_def) - break - else - --minetest.log("warning", "deleting temp index "..temp_index) - --print("deleting temp index") - table_remove(mob_library_worker_table, temp_index) - end - - until repeat_mob_search == false --this is needed to sort through mobs randomly - - - --catch if went through all mobs and something went horribly wrong - --could not find a valid mob to spawn that fits the environment - if not mob_def then - break - end - - --adjust the position for water and lava mobs - if mob_def.type_of_spawning == "water" or mob_def.type_of_spawning == "lava" then - spawning_position.y = spawning_position.y - 1 - end - - --print("spawning: " .. mob_def.name) - - --everything is correct, spawn mob - minetest.add_entity(spawning_position, mob_def.name) - - break - until do_mob_algorithm == false --this is a safety catch - end - - break - until do_mob_spawning == false --this is a performance catch + if timer < 10 then return end + timer = 0 + for _, player in pairs(get_connected_players()) do + local pos = player:get_pos() + local dimension = mcl_worlds.pos_to_dimension(pos) + -- ignore void and unloaded area + if dimension ~= "void" and dimension ~= "default" then + local y_min, y_max = decypher_limits(pos.y) + for i = 1, math_random(1, 4) do + spawn_a_mob(pos, dimension, y_min, y_max) + end end end end) diff --git a/mods/ENTITIES/mcl_paintings/init.lua b/mods/ENTITIES/mcl_paintings/init.lua index 26bd2c61b..74e7341ca 100644 --- a/mods/ENTITIES/mcl_paintings/init.lua +++ b/mods/ENTITIES/mcl_paintings/init.lua @@ -160,6 +160,7 @@ minetest.register_entity("mcl_paintings:painting", { set_entity(self.object) end, get_staticdata = function(self) + if not self then return end local data = { _facing = self._facing, _pos = self._pos, diff --git a/mods/ENTITIES/mobs_mc/silverfish.lua b/mods/ENTITIES/mobs_mc/silverfish.lua index ac3991ad1..70f358fb2 100644 --- a/mods/ENTITIES/mobs_mc/silverfish.lua +++ b/mods/ENTITIES/mobs_mc/silverfish.lua @@ -2,6 +2,8 @@ --################### SILVERFISH --################### +local PLAYER_SCAN_RADIUS = 5 + local S = minetest.get_translator(minetest.get_current_modname()) mobs:register_mob("mobs_mc:silverfish", { @@ -46,6 +48,20 @@ mobs:register_mob("mobs_mc:silverfish", { view_range = 16, attack_type = "punch", damage = 1, + do_custom = function(self, dtime) + self.do_custom_time = (self.do_custom_time or 0) + dtime + if self.do_custom_time < 1.5 then return end + self.do_custom_time = 0 + local selfpos = self.object:get_pos() + local objects = minetest.get_objects_inside_radius(selfpos, PLAYER_SCAN_RADIUS) + for _, obj in pairs(objects) do + if obj:is_player() and not minetest.is_creative_enabled(obj:get_player_name()) then + self.attacking = obj + mobs.group_attack_initialization(self) + return + end + end + end }) mobs:register_egg("mobs_mc:silverfish", S("Silverfish"), "mobs_mc_spawn_icon_silverfish.png", 0) diff --git a/mods/ENTITIES/mobs_mc/zombie.lua b/mods/ENTITIES/mobs_mc/zombie.lua index e1247d8bd..3eb0122a7 100644 --- a/mods/ENTITIES/mobs_mc/zombie.lua +++ b/mods/ENTITIES/mobs_mc/zombie.lua @@ -9,6 +9,95 @@ local S = minetest.get_translator(minetest.get_current_modname()) --################### ZOMBIE --################### +local husk_biomes = { + "Desert", + "SavannaM", + "Savanna", + "Savanna_beach", +} + +local zombie_biomes = { + "FlowerForest_underground", + "JungleEdge_underground", + "StoneBeach_underground", + "MesaBryce_underground", + "Mesa_underground", + "RoofedForest_underground", + "Jungle_underground", + "Swampland_underground", + "MushroomIsland_underground", + "BirchForest_underground", + "Plains_underground", + "MesaPlateauF_underground", + "ExtremeHills_underground", + "MegaSpruceTaiga_underground", + "BirchForestM_underground", + "SavannaM_underground", + "MesaPlateauFM_underground", + "Desert_underground", + "Savanna_underground", + "Forest_underground", + "SunflowerPlains_underground", + "ColdTaiga_underground", + "IcePlains_underground", + "IcePlainsSpikes_underground", + "MegaTaiga_underground", + "Taiga_underground", + "ExtremeHills+_underground", + "JungleM_underground", + "ExtremeHillsM_underground", + "JungleEdgeM_underground", + "Mesa", + "FlowerForest", + "Swampland", + "Taiga", + "ExtremeHills", + "Jungle", + "Savanna", + "BirchForest", + "MegaSpruceTaiga", + "MegaTaiga", + "ExtremeHills+", + "Forest", + "Plains", + "Desert", + "ColdTaiga", + "MushroomIsland", + "IcePlainsSpikes", + "SunflowerPlains", + "IcePlains", + "RoofedForest", + "ExtremeHills+_snowtop", + "MesaPlateauFM_grasstop", + "JungleEdgeM", + "ExtremeHillsM", + "JungleM", + "BirchForestM", + "MesaPlateauF", + "MesaPlateauFM", + "MesaPlateauF_grasstop", + "MesaBryce", + "JungleEdge", + "SavannaM", + "FlowerForest_beach", + "Forest_beach", + "StoneBeach", + "ColdTaiga_beach_water", + "Taiga_beach", + "Savanna_beach", + "Plains_beach", + "ExtremeHills_beach", + "ColdTaiga_beach", + "Swampland_shore", + "MushroomIslandShore", + "JungleM_shore", + "Jungle_shore", + "MesaPlateauFM_sandlevel", + "MesaPlateauF_sandlevel", + "MesaBryce_sandlevel", + "Mesa_sandlevel", +} + local drops_common = { {name = mobs_mc.items.rotten_flesh, chance = 1, @@ -166,230 +255,36 @@ baby_husk.child = 1 mobs:register_mob("mobs_mc:baby_husk", baby_husk) - -- Spawning -mobs:spawn_specific( -"mobs_mc:zombie", -"overworld", -"ground", -{ -"FlowerForest_underground", -"JungleEdge_underground", -"StoneBeach_underground", -"MesaBryce_underground", -"Mesa_underground", -"RoofedForest_underground", -"Jungle_underground", -"Swampland_underground", -"MushroomIsland_underground", -"BirchForest_underground", -"Plains_underground", -"MesaPlateauF_underground", -"ExtremeHills_underground", -"MegaSpruceTaiga_underground", -"BirchForestM_underground", -"SavannaM_underground", -"MesaPlateauFM_underground", -"Desert_underground", -"Savanna_underground", -"Forest_underground", -"SunflowerPlains_underground", -"ColdTaiga_underground", -"IcePlains_underground", -"IcePlainsSpikes_underground", -"MegaTaiga_underground", -"Taiga_underground", -"ExtremeHills+_underground", -"JungleM_underground", -"ExtremeHillsM_underground", -"JungleEdgeM_underground", -"Mesa", -"FlowerForest", -"Swampland", -"Taiga", -"ExtremeHills", -"Jungle", -"Savanna", -"BirchForest", -"MegaSpruceTaiga", -"MegaTaiga", -"ExtremeHills+", -"Forest", -"Plains", -"Desert", -"ColdTaiga", -"MushroomIsland", -"IcePlainsSpikes", -"SunflowerPlains", -"IcePlains", -"RoofedForest", -"ExtremeHills+_snowtop", -"MesaPlateauFM_grasstop", -"JungleEdgeM", -"ExtremeHillsM", -"JungleM", -"BirchForestM", -"MesaPlateauF", -"MesaPlateauFM", -"MesaPlateauF_grasstop", -"MesaBryce", -"JungleEdge", -"SavannaM", -"FlowerForest_beach", -"Forest_beach", -"StoneBeach", -"ColdTaiga_beach_water", -"Taiga_beach", -"Savanna_beach", -"Plains_beach", -"ExtremeHills_beach", -"ColdTaiga_beach", -"Swampland_shore", -"MushroomIslandShore", -"JungleM_shore", -"Jungle_shore", -"MesaPlateauFM_sandlevel", -"MesaPlateauF_sandlevel", -"MesaBryce_sandlevel", -"Mesa_sandlevel", -}, -0, -7, -30, -6000, -4, -mobs_mc.spawn_height.overworld_min, -mobs_mc.spawn_height.overworld_max) +mobs:spawn_setup({ + name = "mobs_mc:zombie", + biomes = zombie_biomes, + max_light = 7, + chance = 2000, +}) + -- Baby zombie is 20 times less likely than regular zombies -mobs:spawn_specific( -"mobs_mc:baby_zombie", -"overworld", -"ground", -{ -"FlowerForest_underground", -"JungleEdge_underground", -"StoneBeach_underground", -"MesaBryce_underground", -"Mesa_underground", -"RoofedForest_underground", -"Jungle_underground", -"Swampland_underground", -"MushroomIsland_underground", -"BirchForest_underground", -"Plains_underground", -"MesaPlateauF_underground", -"ExtremeHills_underground", -"MegaSpruceTaiga_underground", -"BirchForestM_underground", -"SavannaM_underground", -"MesaPlateauFM_underground", -"Desert_underground", -"Savanna_underground", -"Forest_underground", -"SunflowerPlains_underground", -"ColdTaiga_underground", -"IcePlains_underground", -"IcePlainsSpikes_underground", -"MegaTaiga_underground", -"Taiga_underground", -"ExtremeHills+_underground", -"JungleM_underground", -"ExtremeHillsM_underground", -"JungleEdgeM_underground", -"Mesa", -"FlowerForest", -"Swampland", -"Taiga", -"ExtremeHills", -"Jungle", -"Savanna", -"BirchForest", -"MegaSpruceTaiga", -"MegaTaiga", -"ExtremeHills+", -"Forest", -"Plains", -"Desert", -"ColdTaiga", -"MushroomIsland", -"IcePlainsSpikes", -"SunflowerPlains", -"IcePlains", -"RoofedForest", -"ExtremeHills+_snowtop", -"MesaPlateauFM_grasstop", -"JungleEdgeM", -"ExtremeHillsM", -"JungleM", -"BirchForestM", -"MesaPlateauF", -"MesaPlateauFM", -"MesaPlateauF_grasstop", -"MesaBryce", -"JungleEdge", -"SavannaM", -"FlowerForest_beach", -"Forest_beach", -"StoneBeach", -"ColdTaiga_beach_water", -"Taiga_beach", -"Savanna_beach", -"Plains_beach", -"ExtremeHills_beach", -"ColdTaiga_beach", -"Swampland_shore", -"MushroomIslandShore", -"JungleM_shore", -"Jungle_shore", -"MesaPlateauFM_sandlevel", -"MesaPlateauF_sandlevel", -"MesaBryce_sandlevel", -"Mesa_sandlevel", -}, -0, -7, -30, -60000, -4, -mobs_mc.spawn_height.overworld_min, -mobs_mc.spawn_height.overworld_max) +mobs:spawn_setup({ + name = "mobs_mc:baby_zombie", + biomes = zombie_biomes, + max_lignt = 7, + chance = 100, +}) +mobs:spawn_setup({ + name = "mobs_mc:husk", + biomes = husk_biomes, + max_light = 7, + chance = 2000, +}) -mobs:spawn_specific( -"mobs_mc:husk", -"overworld", -"ground", -{ -"Desert", -"SavannaM", -"Savanna", -"Savanna_beach", -}, -0, -7, -30, -6500, -4, -mobs_mc.spawn_height.overworld_min, -mobs_mc.spawn_height.overworld_max) -mobs:spawn_specific( -"mobs_mc:baby_husk", -"overworld", -"ground", -{ -"Desert", -"SavannaM", -"Savanna", -"Savanna_beach", -}, -0, -7, -30, -65000, -4, -mobs_mc.spawn_height.overworld_min, -mobs_mc.spawn_height.overworld_max) +mobs:spawn_setup({ + name = "mobs_mc:baby_husk", + biomes = husk_biomes, + max_light = 7, + chance = 100, +}) -- Spawn eggs mobs:register_egg("mobs_mc:husk", S("Husk"), "mobs_mc_spawn_icon_husk.png", 0) diff --git a/mods/ITEMS/REDSTONE/mesecons/util.lua b/mods/ITEMS/REDSTONE/mesecons/util.lua index b6602526a..498c446cb 100644 --- a/mods/ITEMS/REDSTONE/mesecons/util.lua +++ b/mods/ITEMS/REDSTONE/mesecons/util.lua @@ -346,8 +346,16 @@ function mesecon.vm_abort() vm_cache = nil end +local function is_player_close(pos) + for k,p in pairs(minetest.get_connected_players()) do + local d=vector.distance(pos,p:get_pos()) + if d < 40 then return true end + end +end + -- Gets the cache entry covering a position, populating it if necessary. local function vm_get_or_create_entry(pos) + if not is_player_close(pos) then return end local hash = hash_blockpos(pos) local tbl = vm_cache[hash] if not tbl then @@ -364,6 +372,7 @@ end -- transaction. function mesecon.vm_get_node(pos) local tbl = vm_get_or_create_entry(pos) + if not tbl then return end local index = tbl.va:indexp(pos) local node_value = tbl.data[index] if node_value == minetest.CONTENT_IGNORE then @@ -380,6 +389,7 @@ end -- Existing param1, param2, and metadata are left alone. function mesecon.vm_swap_node(pos, name) local tbl = vm_get_or_create_entry(pos) + if not tbl then return end local index = tbl.va:indexp(pos) tbl.data[index] = minetest.get_content_id(name) tbl.dirty = true @@ -393,6 +403,7 @@ end -- -- Inside a VM transaction, the transaction’s VM cache is used. function mesecon.get_node_force(pos) + if not is_player_close(pos) then return end if vm_cache then return mesecon.vm_get_node(pos) else @@ -424,6 +435,7 @@ function mesecon.swap_node_force(pos, name) -- This serves to both ensure the mapblock is loaded and also hand us -- the old node table so we can preserve param2. local node = mesecon.get_node_force(pos) + if not node then return end node.name = name minetest.swap_node(pos, node) end diff --git a/mods/ITEMS/mcl_banners/init.lua b/mods/ITEMS/mcl_banners/init.lua index cc0e02e66..0be8610f0 100644 --- a/mods/ITEMS/mcl_banners/init.lua +++ b/mods/ITEMS/mcl_banners/init.lua @@ -618,6 +618,7 @@ local entity_standing = { -- pattern: name of pattern (see list above) get_staticdata = function(self) + if not self then return end local out = { _base_color = self._base_color, _layers = self._layers, _name = self._name } return minetest.serialize(out) end, diff --git a/mods/ITEMS/mcl_bows/arrow.lua b/mods/ITEMS/mcl_bows/arrow.lua index baa4d633a..5cbafce62 100644 --- a/mods/ITEMS/mcl_bows/arrow.lua +++ b/mods/ITEMS/mcl_bows/arrow.lua @@ -441,6 +441,7 @@ function ARROW_ENTITY.on_punch(self) end function ARROW_ENTITY.get_staticdata(self) + if not self then return end local out = { lastpos = self._lastpos, startpos = self._startpos, diff --git a/mods/ITEMS/mcl_bows/rocket.lua b/mods/ITEMS/mcl_bows/rocket.lua index 678aba4d4..cac466376 100644 --- a/mods/ITEMS/mcl_bows/rocket.lua +++ b/mods/ITEMS/mcl_bows/rocket.lua @@ -38,11 +38,14 @@ local function damage_explosion(self, damagemulitplier) for _,obj in pairs(objects) do if obj:is_player() then mcl_util.deal_damage(obj, damagemulitplier - vector.distance(self.object:get_pos(), obj:get_pos()), {type = "explosion"}) - elseif obj:get_luaentity()._cmi_is_mob then - obj:punch(self.object, 1.0, { - full_punch_interval=1.0, - damage_groups={fleshy=damagemulitplier - vector.distance(self.object:get_pos(), obj:get_pos())}, - }, self.object:get_velocity()) + else + local entity = obj:get_luaentity() + if entity and entity._cmi_is_mob then + obj:punch(self.object, 1.0, { + full_punch_interval=1.0, + damage_groups={fleshy=damagemulitplier - vector.distance(self.object:get_pos(), obj:get_pos())}, + }, self.object:get_velocity()) + end end end end @@ -627,6 +630,7 @@ function ARROW_ENTITY.on_punch(self) end function ARROW_ENTITY.get_staticdata(self) + if not self then return end local out = { lastpos = self._lastpos, startpos = self._startpos, diff --git a/mods/ITEMS/mcl_chests/init.lua b/mods/ITEMS/mcl_chests/init.lua index 8b344d462..2ad6518a7 100644 --- a/mods/ITEMS/mcl_chests/init.lua +++ b/mods/ITEMS/mcl_chests/init.lua @@ -248,6 +248,51 @@ local function player_chest_close(player) open_chests[name] = nil end +local function drop_item_stack(pos, stack) + if not stack or stack:is_empty() then return end + local drop_offset = vector.new(math.random() - 0.5, 0, math.random() - 0.5) + minetest.add_item(vector.add(pos, drop_offset), stack) +end + +local function drop_items_chest(pos, oldnode, oldmetadata, digger) + if oldmetadata and oldmetadata.inventory then + -- process after_dig_node callback + local main = oldmetadata.inventory.main + if not main then return end + for _, stack in pairs(main) do + drop_item_stack(pos, stack) + end + else + local meta = minetest.get_meta(pos) + local inv = meta:get_inventory() + for i = 1, inv:get_size("main") do + drop_item_stack(pos, inv:get_stack("main", i)) + end + meta:from_table() + end +end + +local function on_chest_blast(pos, intensity) + local node = minetest.get_node(pos) + drop_items_chest(pos, node) + minetest.remove_node(pos) + -- drop node itself with some probability depended on explosion intensity (1 for TNT): + if math.random(1, math.floor((intensity or 1) * 2)) ~= 1 then return end + local node_def = minetest.registered_nodes[node.name] + if not node_def then return end + local node_name = node_def.drop or node_def.name + drop_item_stack(pos, ItemStack(node_name)) +end + +local function close_forms(canonical_basename, pos) + local players = minetest.get_connected_players() + for p=1, #players do + if vector.distance(players[p]:get_pos(), pos) <= 30 then + minetest.close_formspec(players[p]:get_player_name(), "mcl_chests:"..canonical_basename.."_"..pos.x.."_"..pos.y.."_"..pos.z) + end + end +end + -- This is a helper function to register both chests and trapped chests. Trapped chests will make use of the additional parameters local function register_chest(basename, desc, longdesc, usagehelp, tt_help, tiles_table, hidden, mesecons, on_rightclick_addendum, on_rightclick_addendum_left, on_rightclick_addendum_right, drop, canonical_basename) -- START OF register_chest FUNCTION BODY @@ -295,42 +340,6 @@ local function register_chest(basename, desc, longdesc, usagehelp, tt_help, tile end end - local function drop_item_stack(pos, stack) - if not stack or stack:is_empty() then return end - local drop_offset = vector.new(math.random() - 0.5, 0, math.random() - 0.5) - minetest.add_item(vector.add(pos, drop_offset), stack) - end - - local function drop_items_chest(pos, oldnode, oldmetadata, digger) - if oldmetadata and oldmetadata.inventory then - -- process after_dig_node callback - local main = oldmetadata.inventory.main - if not main then return end - for _, stack in pairs(main) do - drop_item_stack(pos, stack) - end - else - local meta = minetest.get_meta(pos) - local inv = meta:get_inventory() - for i = 1, inv:get_size("main") do - drop_item_stack(pos, inv:get_stack("main", i)) - end - meta:from_table() - end - end - - local function on_chest_blast(pos, intensity) - local node = minetest.get_node(pos) - drop_items_chest(pos, node) - minetest.remove_node(pos) - -- drop node itself with some probability depended on explosion intensity (1 for TNT): - if math.random(1, math.floor((intensity or 1) * 2)) ~= 1 then return end - local node_def = minetest.registered_nodes[node.name] - if not node_def then return end - local node_name = node_def.drop or node_def.name - drop_item_stack(pos, ItemStack(node_name)) - end - local function limit_put_list(stack, list) for _, other in ipairs(list) do stack = other:add_item(stack) @@ -380,15 +389,6 @@ local function register_chest(basename, desc, longdesc, usagehelp, tt_help, tile end, }) - local function close_forms(canonical_basename, pos) - local players = minetest.get_connected_players() - for p=1, #players do - if vector.distance(players[p]:get_pos(), pos) <= 30 then - minetest.close_formspec(players[p]:get_player_name(), "mcl_chests:"..canonical_basename.."_"..pos.x.."_"..pos.y.."_"..pos.z) - end - end - end - minetest.register_node(small_name, { description = desc, _tt_help = tt_help, @@ -1476,9 +1476,11 @@ minetest.register_node("mcl_chests:barrel", { minetest.get_meta(pos):set_string("name", itemstack:get_meta():get_string("name")) end, after_dig_node = drop_items_chest, - on_blast = on_blast, + on_blast = on_chest_blast, on_rightclick = barrel_open, - on_destruct = close_forms, + on_destruct = function(pos) + close_forms("barrel", pos) + end, _mcl_blast_resistance = 2.5, _mcl_hardness = 2.5, }) @@ -1497,9 +1499,11 @@ minetest.register_node("mcl_chests:barrel_open", { sounds = mcl_sounds.node_sound_wood_defaults(), groups = {handy = 1, axey = 1, container = 2, material_wood = 1, flammable = -1, deco_block = 1, not_in_creative_inventory = 1}, after_dig_node = drop_items_chest, - on_blast = on_blast, + on_blast = on_chest_blast, on_rightclick = barrel_open, - on_destruct = close_forms, + on_destruct = function(pos) + close_forms("barrel_open", pos) + end, _mcl_blast_resistance = 2.5, _mcl_hardness = 2.5, }) diff --git a/mods/ITEMS/mcl_core/nodes_liquid.lua b/mods/ITEMS/mcl_core/nodes_liquid.lua index 75314cf9f..47913be71 100644 --- a/mods/ITEMS/mcl_core/nodes_liquid.lua +++ b/mods/ITEMS/mcl_core/nodes_liquid.lua @@ -388,6 +388,12 @@ end ["mcl_core:whirlpool_source"] = -BUBBLE_COLUMN_SPEED, ["mcl_core:bubble_column_source"] = BUBBLE_COLUMN_SPEED, } + local bubble_source_fast_switch_from_to = { + ["mcl_nether:soul_sand"] = "mcl_core:bubble_column_source", + ["mcl_core:bubble_column_source"] = "mcl_core:bubble_column_source", + ["mcl_nether:magma"] = "mcl_core:whirlpool_source", + ["mcl_core:whirlpool_source"] = "mcl_core:whirlpool_source", + } minetest.register_abm({ label = "Process bubble columns and whirlpools", nodenames = {"mcl_core:whirlpool_source", "mcl_core:bubble_column_source"}, @@ -399,7 +405,7 @@ end local check = nether_node_to_check[name] local below = minetest.get_node({x = x, y = y - 1, z = z}).name if below ~= name and below ~= check then - minetest.swap_node(pos, {name = "mcl_core:water_source"}) + minetest.swap_node(pos, {name = bubble_source_fast_switch_from_to[below] or "mcl_core:water_source"}) return end local upper_pos = {x = x, y = y + 1, z = z} diff --git a/mods/ITEMS/mcl_end/eye_of_ender.lua b/mods/ITEMS/mcl_end/eye_of_ender.lua index 97dee9336..d2e273c8d 100644 --- a/mods/ITEMS/mcl_end/eye_of_ender.lua +++ b/mods/ITEMS/mcl_end/eye_of_ender.lua @@ -10,7 +10,7 @@ minetest.register_entity("mcl_end:ender_eye", { -- Save and restore age get_staticdata = function(self) - return tostring(self._age) or "0" + return tostring(self and self._age) or "0" end, on_activate = function(self, staticdata, dtime_s) local age = tonumber(staticdata) diff --git a/mods/ITEMS/mcl_itemframes/init.lua b/mods/ITEMS/mcl_itemframes/init.lua index d46a393b8..5dde560b7 100644 --- a/mods/ITEMS/mcl_itemframes/init.lua +++ b/mods/ITEMS/mcl_itemframes/init.lua @@ -33,6 +33,7 @@ minetest.register_entity("mcl_itemframes:item",{ end end, get_staticdata = function(self) + if not self then return end if self._nodename and self._texture then local ret = self._nodename .. ";" .. self._texture if self._scale then diff --git a/mods/ITEMS/mcl_mobspawners/init.lua b/mods/ITEMS/mcl_mobspawners/init.lua index 37720e1e8..11339e1a4 100644 --- a/mods/ITEMS/mcl_mobspawners/init.lua +++ b/mods/ITEMS/mcl_mobspawners/init.lua @@ -352,7 +352,7 @@ local doll_def = { } doll_def.get_staticdata = function(self) - return self._mob + return self and self._mob end doll_def.on_activate = function(self, staticdata, dtime_s) diff --git a/mods/ITEMS/mcl_mushroom/init.lua b/mods/ITEMS/mcl_mushroom/init.lua index c5243c88a..a1a2f45c2 100644 --- a/mods/ITEMS/mcl_mushroom/init.lua +++ b/mods/ITEMS/mcl_mushroom/init.lua @@ -470,8 +470,8 @@ minetest.register_abm({ }) function generate_warped_tree(pos) - breakgrow = false - breakgrow2 = false + local breakgrow = false + local breakgrow2 = false -- Baumgenerator -- erste und zweite Etage for x = pos.x - 2,pos.x + 2 do @@ -547,8 +547,8 @@ function generate_warped_tree(pos) end function generate_crimson_tree(pos) - breakgrow = false - breakgrow2 = false + local breakgrow = false + local breakgrow2 = false -- Baumgenerator -- erste und zweite Etage for x = pos.x - 2,pos.x + 2 do diff --git a/mods/ITEMS/mcl_portals/portal_nether.lua b/mods/ITEMS/mcl_portals/portal_nether.lua index 883f6e4fc..7390bbb2f 100644 --- a/mods/ITEMS/mcl_portals/portal_nether.lua +++ b/mods/ITEMS/mcl_portals/portal_nether.lua @@ -746,7 +746,7 @@ minetest.register_abm({ return end - if lower_node_name == OBSIDIAN and pos.y >= mcl_mapgen.overworld.min and random(1, 200) == 19 then + if lower_node_name == OBSIDIAN and pos.y >= mcl_mapgen.overworld.min and random(1, 750) == 19 then local pigman_obj = minetest.add_entity(pos, "mobs_mc:pigman") if pigman_obj then teleport_cooloff(pigman_obj) diff --git a/mods/ITEMS/mcl_potions/tipped_arrow.lua b/mods/ITEMS/mcl_potions/tipped_arrow.lua index 1717533a8..907580aef 100644 --- a/mods/ITEMS/mcl_potions/tipped_arrow.lua +++ b/mods/ITEMS/mcl_potions/tipped_arrow.lua @@ -391,6 +391,7 @@ function mcl_potions.register_arrow(name, desc, color, def) end function ARROW_ENTITY.get_staticdata(self) + if not self then return end local out = { lastpos = self._lastpos, startpos = self._startpos, diff --git a/mods/ITEMS/mcl_signs/init.lua b/mods/ITEMS/mcl_signs/init.lua index b6bfb3fe8..c3c779356 100644 --- a/mods/ITEMS/mcl_signs/init.lua +++ b/mods/ITEMS/mcl_signs/init.lua @@ -537,6 +537,7 @@ minetest.register_entity("mcl_signs:text", { self.object:set_armor_groups({ immortal = 1 }) end, get_staticdata = function(self) + if not self then return end local out = { _signnodename = self._signnodename } return minetest.serialize(out) end, diff --git a/mods/ITEMS/mcl_throwing/init.lua b/mods/ITEMS/mcl_throwing/init.lua index c468946dd..88c69a3f3 100644 --- a/mods/ITEMS/mcl_throwing/init.lua +++ b/mods/ITEMS/mcl_throwing/init.lua @@ -57,6 +57,7 @@ end -- Staticdata handling because objects may want to be reloaded function mcl_throwing.get_staticdata(self) + if not self then return end local thrower -- Only save thrower if it's a player name if type(self._thrower) == "string" then diff --git a/mods/MAPGEN/mcl_debrisgen/init.lua b/mods/MAPGEN/mcl_debrisgen/init.lua index b2b630626..9d177cdbc 100644 --- a/mods/MAPGEN/mcl_debrisgen/init.lua +++ b/mods/MAPGEN/mcl_debrisgen/init.lua @@ -17,16 +17,14 @@ mcl_mapgen.register_mapgen_block(function(minp, maxp) local nodes = minetest_find_nodes_in_area(minp, maxp, debris_name) if nodes then for _, pos in pairs(nodes) do - minetest.log("warning","debris found at "..minetest.pos_to_string(pos)) local x, y, z = pos.x, pos.y, pos.z - if minetest_get_node({x = x-1, y = y, z = z}) == air_name - or minetest_get_node({x = x+1, y = y, z = z}) == air_name - or minetest_get_node({x = x, y = y-1, z = z}) == air_name - or minetest_get_node({x = x, y = y+1, z = z}) == air_name - or minetest_get_node({x = x, y = y, z = z-1}) == air_name - or minetest_get_node({x = x, y = y, z = z+1}) == air_name then - minetest_set_node(pos, netherrack_name) - minetest.log("warning","debris at "..minetest.pos_to_string(pos) .. " replaced to netherrack") + if minetest_get_node({x = x-1, y = y, z = z}).name == air_name + or minetest_get_node({x = x+1, y = y, z = z}).name == air_name + or minetest_get_node({x = x, y = y-1, z = z}).name == air_name + or minetest_get_node({x = x, y = y+1, z = z}).name == air_name + or minetest_get_node({x = x, y = y, z = z-1}).name == air_name + or minetest_get_node({x = x, y = y, z = z+1}).name == air_name then + minetest_set_node(pos, {name = netherrack_name}) end end end diff --git a/mods/MAPGEN/mcl_structures/init.lua b/mods/MAPGEN/mcl_structures/init.lua index 83646179b..c99970813 100644 --- a/mods/MAPGEN/mcl_structures/init.lua +++ b/mods/MAPGEN/mcl_structures/init.lua @@ -289,9 +289,9 @@ function mcl_structures.place_schematic(def) pos = vector.new(pos), schematic = loaded_schematic, rotation = rotation, - replacements = replacements, - force_placement = force_placement, - flags = flags, + replacements = def.replacements, + force_placement = def.force_placement, + flags = def.flags, size = vector.new(size), pr = pr, on_placed = on_placed, diff --git a/mods/MAPGEN/mcl_villages/init.lua b/mods/MAPGEN/mcl_villages/init.lua index e837ab027..37052a9b6 100644 --- a/mods/MAPGEN/mcl_villages/init.lua +++ b/mods/MAPGEN/mcl_villages/init.lua @@ -436,8 +436,7 @@ local function build_a_village(minp, maxp, pr, placer) end -- Disable natural generation in singlenode. -if mg_name ~= "singlenode" then - local mg_name = minetest.get_mapgen_setting("mg_name") +if not mcl_mapgen.singlenode then local scan_last_node = mcl_mapgen.LAST_BLOCK * mcl_mapgen.BS - 1 local scan_offset = mcl_mapgen.BS mcl_mapgen.register_mapgen(function(minp, maxp, chunkseed) diff --git a/mods/PLAYER/mcl_anticheat/init.lua b/mods/PLAYER/mcl_anticheat/init.lua index bd76e2818..2e3f427a6 100644 --- a/mods/PLAYER/mcl_anticheat/init.lua +++ b/mods/PLAYER/mcl_anticheat/init.lua @@ -199,11 +199,10 @@ local function step() end end elseif #players < 26 then - if should_be_banned then - minetest.chat_send_all("Player " .. first .. " has been banned for having more than 9 connections at once") - minetest.ban_player(first) - else - for _, player_name in pairs(players) do + for _, player_name in pairs(players) do + if should_be_banned then + minetest.ban_player(player_name) + else if (player_doesnt_move[player_name] or 0) > 90/step_seconds then minetest.kick_player(player_name, "Didn't move during 1.5 minutes being connected multiple times") ban_next_time[ip] = 1 @@ -211,18 +210,18 @@ local function step() end end elseif #players <= 100 then - if should_be_banned then - minetest.ban_player(first) - minetest.chat_send_all("Player " .. first .. " has been banned for having more than 25 connections at once") - else - for _, player_name in pairs(players) do + for _, player_name in pairs(players) do + if should_be_banned then + minetest.ban_player(player_name) + else minetest.kick_player(player_name, "More than 25 connections from IP address " .. ip) + ban_next_time[ip] = 1 end - ban_next_time[ip] = 1 end else - minetest.ban_player(first) - minetest.chat_send_all("Player " .. first .. " has been banned for having more than 100 connections at once") + for _, player_name in pairs(players) do + minetest.ban_player(player_name) + end end end end diff --git a/mods/PLAYER/mcl_playerplus/init.lua b/mods/PLAYER/mcl_playerplus/init.lua index bdd2748b7..85755e0de 100644 --- a/mods/PLAYER/mcl_playerplus/init.lua +++ b/mods/PLAYER/mcl_playerplus/init.lua @@ -2,7 +2,6 @@ mcl_playerplus = { elytra = {}, } -local player_velocity_old = {x=0, y=0, z=0} local get_connected_players = minetest.get_connected_players local dir_to_yaw = minetest.dir_to_yaw local get_item_group = minetest.get_item_group @@ -336,9 +335,6 @@ minetest.register_globalstep(function(dtime) set_bone_position_conditional(player,"Wield_Item", vector.new(-1.5,4.9,1.8), vector.new(135,0,90)) end - player_velocity_old = player:get_velocity() or player:get_player_velocity() - - -- controls right and left arms pitch when shooting a bow or blocking if mcl_shields.is_blocking(player) == 2 then set_bone_position_conditional(player, "Arm_Right_Pitch_Control", vector.new(-3, 5.785, 0), vector.new(20, -20, 0)) @@ -460,13 +456,56 @@ minetest.register_globalstep(function(dtime) local bubble_column_feet = node_feet == "mcl_core:bubble_column_source" if bubble_column_feet then - if not player_pos_for_bubble_columns[name] then - player_pos_for_bubble_columns[name] = fly_pos + local bubble_column_head = node_head == "mcl_core:bubble_column_source" + if bubble_column_head then + if not player_pos_for_bubble_columns[name] then + player_pos_for_bubble_columns[name] = fly_pos + else + local head_alt_1 = fly_pos.y + 1.5 + local head_alt_2 = head_alt_1 + time + while head_alt_1 < head_alt_2 do + local next_alt = math.min(head_alt_1 + 1, head_alt_2) + local next_node_head = minetest.get_node({x = fly_pos.x, y = next_alt, z = fly_pos.z}).name + if next_node_head == "mcl_core:bubble_column_source" then + head_alt_1 = next_alt + else + local ndef = minetest.registered_nodes[next_node_head] + if (ndef.walkable == nil or ndef.walkable == true) + and (ndef.collision_box == nil or ndef.collision_box.type == "regular") + and (ndef.node_box == nil or ndef.node_box.type == "regular") + and (ndef.groups.disable_suffocation ~= 1) + and (ndef.groups.opaque == 1) + then + break + else + -- pull head slightly above water level: + head_alt_1 = head_alt_1 + (next_alt - head_alt_1) * 0.5 + break + end + end + end + local new_alt = head_alt_1 - 1.5 + local delta_y = new_alt - fly_pos.y + if delta_y > 0 then + fly_pos.y = new_alt + player:set_pos(fly_pos) + local velocity_y = player_velocity.y + local add_velocity_y + if velocity_y > 1 then + add_velocity_y = -velocity_y/5 + elseif velocity_y >= -1 then + add_velocity_y = -velocity_y/2.5 + else + add_velocity_y = -velocity_y/2 + end + player:add_velocity({x = 0, y = add_velocity_y, z = 0}) + player_pos_for_bubble_columns[name] = fly_pos + else + player_pos_for_bubble_columns[name] = nil + end + end else - local bubble_column_head = node_head == "mcl_core:bubble_column_source" - fly_pos.y = player_pos_for_bubble_columns[name].y + (bubble_column_head and time or time/10) - player:set_pos(fly_pos) - player_pos_for_bubble_columns[name] = fly_pos + player_pos_for_bubble_columns[name] = nil end else local whirlpool_feet = node_feet == "mcl_core:whirlpool_source" @@ -479,20 +518,43 @@ minetest.register_globalstep(function(dtime) if stands_on == "mcl_nether:magma" then fly_pos.y = math.floor(fly_pos.y) + (control.sneak and 0.51 or 0.5) player:set_pos(fly_pos) + local add_velocity_y + local velocity_y = player_velocity.y + if velocity_y < -1 then + add_velocity_y = -velocity_y/5 + elseif velocity_y <= 1 then + add_velocity_y = -velocity_y/2.5 + else + add_velocity_y = -velocity_y/2 + end + player:add_velocity({x = 0, y = add_velocity_y, z = 0}) + player_pos_for_bubble_columns[name] = fly_pos + elseif stands_on == "mcl_core:whirlpool_source" then + local estimated_pos_y = player_pos_for_bubble_columns[name].y - (whirlpool_head and time/2 or time/5) + local next_pos_y = fly_pos.y + while next_pos_y > estimated_pos_y do + next_pos_y = next_pos_y - math.min(1, next_pos_y - estimated_pos_y) + local will_stand_on = minetest.get_node({x = fly_pos.x, y = next_pos_y - 0.0001, z = fly_pos.z}).name + if will_stand_on ~= "mcl_core:whirlpool_source" then + next_pos_y = math.floor(next_pos_y - 0.0001) + (control.sneak and 0.51 or 0.5) + break + end + end + fly_pos.y = next_pos_y + player:set_pos(fly_pos) + local add_velocity_y + local velocity_y = player_velocity.y + if velocity_y < -1 then + add_velocity_y = -velocity_y/5 + elseif velocity_y <= 1 then + add_velocity_y = -velocity_y/2.5 + else + add_velocity_y = -velocity_y/2 + end + player:add_velocity({x = 0, y = add_velocity_y, z = 0}) player_pos_for_bubble_columns[name] = fly_pos else - fly_pos.y = player_pos_for_bubble_columns[name].y - (whirlpool_head and time/2 or time/5) - local will_stand_on = minetest.get_node({x = fly_pos.x, y = fly_pos.y - 0.0001, z = fly_pos.z}).name - if will_stand_on == "mcl_nether:magma" then - fly_pos.y = math.floor(fly_pos.y) + (control.sneak and 0.51 or 0.5) - player:set_pos(fly_pos) - player_pos_for_bubble_columns[name] = fly_pos - elseif will_stand_on == "mcl_core:whirlpool_source" then - player:set_pos(fly_pos) - player_pos_for_bubble_columns[name] = fly_pos - else - player_pos_for_bubble_columns[name] = nil - end + player_pos_for_bubble_columns[name] = nil end end elseif player_pos_for_bubble_columns[name] then