From 6a0b35532ca948844c19507e4b0922d65ecc0db1 Mon Sep 17 00:00:00 2001 From: ElCeejo Date: Mon, 22 Aug 2022 18:34:07 -0700 Subject: [PATCH] Add Mapgen Spawning, Add ABM Spawning --- mob_meta.lua | 8 +- settingtypes.txt | 14 ++- spawning.lua | 249 +++++++++++++++++++++++++++++++++++++++++++---- 3 files changed, 247 insertions(+), 24 deletions(-) diff --git a/mob_meta.lua b/mob_meta.lua index 8d2be65..b3e6243 100644 --- a/mob_meta.lua +++ b/mob_meta.lua @@ -795,7 +795,8 @@ function mob:activate(staticdata, dtime) self._despawn = self:recall("_despawn") or false if self._despawn - and self.despawn_after then + and self.despawn_after + and self.object then self.object:remove() return end @@ -986,6 +987,7 @@ end local function water_physics(self, pos, node) -- Props local gravity = self._movement_data.gravity + local height = self.height -- Vectors pos.y = pos.y + 0.01 if minetest.get_item_group(node.name, "liquid") < 1 then @@ -1007,7 +1009,7 @@ local function water_physics(self, pos, node) }) local center = { x = pos.x, - y = pos.y + self.height * 0.5, + y = pos.y + height * 0.5, z = pos.z } if minetest.get_item_group(minetest.get_node(center).name, "liquid") < 1 then @@ -1016,7 +1018,7 @@ local function water_physics(self, pos, node) -- Calculate Physics local vel = self.object:get_velocity() local bouyancy_x = self.bouyancy_multiplier or 1 - local bouyancy = (abs(gravity * 0.5) / self.height) * bouyancy_x + local bouyancy = (abs(gravity * 0.5) / height) * bouyancy_x if bouyancy > 0 then if bouyancy > 4.9 then bouyancy = 4.9 end self.object:set_acceleration({ diff --git a/settingtypes.txt b/settingtypes.txt index 9ee0d3d..0454ea1 100644 --- a/settingtypes.txt +++ b/settingtypes.txt @@ -5,13 +5,19 @@ creatura_step_type (Step Type) enum simple simple,fancy # How often (in seconds) the spawn ABM is called -creatura_spawn_interval (Spawn ABM Interval) float 10 +creatura_spawn_interval (Spawn ABM Interval) int 10 # Time (in seconds) between spawn steps -creatura_spawn_step (Spawn Step Interval) float 15 +creatura_spawn_step (Spawn Step Interval) int 15 + +# Allows Mobs to spawn during chunk generation (If dependent mods use spawn_on_gen) +creatura_mapgen_spawning (Mapgen Spawning) bool true + +# How many chunks are generated before a Mob can spawn +creatura_mapgen_spawn_interval (Mapgen Spawning Interval) int 5 # Allotted time (in μs) per step for A* pathfinding (lower means less lag but slower pathfinding) -creatura_a_star_alloted_time (A* Pathfinding Alloted time per step) float 500 +creatura_a_star_alloted_time (A* Pathfinding Alloted time per step) int 500 # Allotted time (in μs) per step for Theta* pathfinding (lower means less lag but slower pathfinding) -creatura_theta_star_alloted_time (Theta* Pathfinding Alloted time per step) float 700 \ No newline at end of file +creatura_theta_star_alloted_time (Theta* Pathfinding Alloted time per step) int 700 \ No newline at end of file diff --git a/spawning.lua b/spawning.lua index 2542941..24dfa0f 100644 --- a/spawning.lua +++ b/spawning.lua @@ -125,6 +125,7 @@ function creatura.register_mob_spawn(name, def) nodes = def.nodes or nil, biomes = def.biomes or nil, spawn_cluster = def.spawn_cluster or false, + spawn_on_gen = def.spawn_on_gen or false, spawn_in_nodes = def.spawn_in_nodes or false, spawn_cap = def.spawn_cap or 5, send_debug = def.send_debug or false @@ -141,10 +142,9 @@ function creatura.register_on_spawn(name, func) table.insert(creatura.registered_on_spawns[name], func) end - -- Utility Functions -- -local function is_value_in_table(tbl, val) +local function table_contains(tbl, val) for _, v in pairs(tbl) do if v == val then return true @@ -153,22 +153,35 @@ local function is_value_in_table(tbl, val) return false end -local function get_biome_name(pos) - if not pos then return end - return minetest.get_biome_name(minetest.get_biome_data(pos).biome) -end - -local function get_spawnable_mobs(pos) - local biome = get_biome_name(pos) - if not biome then biome = "_nil" end - local spawnable = {} - for k, v in pairs(creatura.registered_mob_spawns) do - if not v.biomes - or is_value_in_table(v.biomes, biome) then - table.insert(spawnable, k) +local function get_spawnable_mobs(pos, mapgen) + local biome = minetest.get_biome_name(minetest.get_biome_data(pos).biome) + if not biome then return end + local mobs = {} + for mob, data in pairs(creatura.registered_mob_spawns) do + local biome_valid = data.biomes and table_contains(data.biomes, biome) + if (biome_valid + or data.nodes) + and (not mapgen + or data.spawn_on_gen) + and not data.spawn_in_nodes then + table.insert(mobs, mob) end end - return spawnable + return mobs +end + +local function is_spawn_node(name, spawn_nodes) + for _, val in ipairs(spawn_nodes) do + if val:match("^group:") then + local group = val:split(":")[2] + if minetest.get_item_group(name, group) > 0 then + return true + end + elseif val == name then + return true + end + end + return false end -- Spawning Function -- @@ -294,6 +307,80 @@ minetest.register_globalstep(function(dtime) end end) +-- Mapgen Spawning -- + +local mapgen_spawning = minetest.settings:get_bool("creatura_mapgen_spawning", true) + +local mapgen_spawning_int = tonumber(minetest.settings:get("creatura_mapgen_spawn_interval")) or 5 + +local spawn_cooldown = 1 + +minetest.register_on_generated(function(minp, maxp) + if not mapgen_spawning then return end + spawn_cooldown = spawn_cooldown - 1 + if spawn_cooldown <= 0 then + local min_x, max_x = minp.x, maxp.x + local min_y, max_y = minp.y, maxp.y + local min_z, max_z = minp.z, maxp.z + local mobs = get_spawnable_mobs({ + x = min_x + (max_x - min_x) * 0.5, + y = min_y + (max_y - min_y) * 0.5, + z = min_z + (max_z - min_z) * 0.5 + }, true) + if not mobs then spawn_cooldown = mapgen_spawning_int return end + local vm, emin, emax = minetest.get_mapgen_object("voxelmanip") + local area = VoxelArea:new{MinEdge = emin, MaxEdge = emax} + local data = vm:get_data() + for z = min_z + 8, max_z - 7, 8 do + for x = min_x + 8, max_x - 7, 8 do + local mob = #mobs > 0 and mobs[random(#mobs)] + if mob then + local spawn = creatura.registered_mob_spawns[mob] + for y = max_y, min_y, -1 do + if y > spawn.max_height + or y < spawn.min_height then + break + end + local vi = area:index(x, y, z) + local vi_name = minetest.get_name_from_content_id(data[vi]) + if spawn.spawn_in_nodes + and is_spawn_node(vi_name, spawn.nodes) then + -- Add Spawn Node to Map + data[vi] = minetest.get_content_id("creatura:spawn_node") + vm:set_data(data) + vm:write_to_map() + -- Apply Meta + local group_size = random(spawn.min_group or 1, spawn.max_group or 1) + local meta = minetest.get_meta({x = x, y = y, z = z}) + meta:set_string("mob", mob) + meta:set_int("cluster", group_size) + spawn_cooldown = mapgen_spawning_int + return + end + local vi_n = area:index(x, y - 1, z) + local vi_n_name = minetest.get_name_from_content_id(data[vi_n]) + if not creatura.get_node_def(vi_name).walkable + and ((spawn.nodes and is_spawn_node(vi_n_name, spawn.nodes)) + or (not spawn.nodes and creatura.get_node_def(vi_n_name).walkable)) then + -- Add Spawn Node to Map + data[vi] = minetest.get_content_id("creatura:spawn_node") + vm:set_data(data) + vm:write_to_map() + -- Apply Meta + local group_size = random(spawn.min_group or 1, spawn.max_group or 1) + local meta = minetest.get_meta({x = x, y = y, z = z}) + meta:set_string("mob", mob) + meta:set_int("cluster", group_size) + spawn_cooldown = mapgen_spawning_int + return + end + end + end + end + end + end +end) + -- Node -- minetest.register_node("creatura:spawn_node", { @@ -319,8 +406,12 @@ minetest.register_abm({ for _ = 1, amount do obj = minetest.add_entity(pos, name) end + minetest.log("action", + "[Creatura] Spawned " .. amount .. " " .. name .. " at " .. minetest.pos_to_string(pos)) else obj = minetest.add_entity(pos, name) + minetest.log("action", + "[Creatura] Spawned a " .. name .. " at " .. minetest.pos_to_string(pos)) end minetest.remove_node(pos) if obj @@ -360,4 +451,128 @@ minetest.register_abm({ end end end, -})]] \ No newline at end of file +})]] + +------------------ +-- ABM Spawning -- +------------------ + +--[[creatura.register_abm_spawn("mymod:mymob", { + chance = 3000, + interval = 30, + min_height = 0, + max_height = 128, + min_light = 1, + max_light = 15, + min_group = 1, + max_group = 4, + nodes = {"group:soil", "group:stone"}, + neighbors = {"air"}, + spawn_on_load = false, + spawn_in_nodes = false, + spawn_cap = 5 +})]] + +function creatura.register_abm_spawn(mob, def) + local chance = def.chance or 3000 + local interval = def.interval or 30 + local min_height = def.min_height or 0 + local max_height = def.max_height or 128 + local min_light = def.min_light or 1 + local max_light = def.max_light or 15 + local min_group = def.min_group or 1 + local max_group = def.max_group or 4 + local nodes = def.nodes or {"group:soil", "group:stone"} + local neighbors = def.neighbors or {"air"} + local spawn_on_load = def.spawn_on_load or false + local spawn_in_nodes = def.spawn_in_nodes or false + local spawn_cap = def.spawn_cap or 5 + + local function spawn_func(pos, _, _, aocw) + + if spawn_on_load + and random(chance) > 1 then + return + end + + if spawn_on_load + and not minetest.find_node_near(pos, 1, neighbors) then + return + end + + if pos.y > max_height + or pos.y < min_height then + return + end + + if not spawn_in_nodes then + pos = vec_raise(pos, 1) + end + + local light = minetest.get_node_light(pos) or 7 + + if light > max_light + or light < min_light then + return + end + + if aocw + and aocw >= spawn_cap then + return + end + + local mob_def = minetest.registered_entities[mob] + local mob_width = mob_def.collisionbox[4] + local mob_height = math.max(0, mob_def.collisionbox[5] - mob_def.collisionbox[2]) + + pos.y = pos.y - 0.49 + + if not creatura.is_pos_moveable(pos, mob_width, mob_height) then + return + end + + local group_size = random(min_group or 1, max_group or 1) + + local obj + + for _ = 1, group_size do + obj = minetest.add_entity(pos, mob) + end + + minetest.log("action", + "[Creatura] [ABM Spawning] Spawned " .. group_size .. " " .. mob .. " at " .. minetest.pos_to_string(pos)) + + if obj + and creatura.registered_on_spawns[mob] + and #creatura.registered_on_spawns[mob] > 0 then + for i = 1, #creatura.registered_on_spawns[mob] do + local func = creatura.registered_on_spawns[mob][i] + func(obj:get_luaentity(), pos) + end + end + end + + if spawn_on_load then + minetest.register_lbm({ + name = mob .. "_spawning", + label = mob .. " spawning", + nodenames = nodes, + run_at_every_load = false, + action = function(pos) + spawn_func(pos) + end + }) + else + minetest.register_abm({ + label = mob .. " spawning", + nodenames = nodes, + neighbors = neighbors, + interval = interval, + chance = chance, + catch_up = false, + action = function(pos, ...) + spawn_func(pos, ...) + end + }) + end +end \ No newline at end of file