natural-spawner/api.lua

484 lines
15 KiB
Lua

local spawners = {}
spawner_count = 0
natspawner.on_mob_die = function(self, pos)
if self.spawn and self.spawn.pos then
local meta = minetest.get_meta(self.spawn.pos)
meta:set_int("entity_killed_count", meta:get_int("entity_killed_count") + 1)
if self.variation then
meta:set_int("spawned_variation_count", meta:get_int("spawned_variation_count") - 1)
end
end
end
local function spawn(pos, force)
local meta = minetest.get_meta(pos)
local spawner_name = meta:get_string("spawner_name")
if spawner_name then
local spawner = spawners[spawner_name]
local replacement_node_name = meta:get_string("replaced_node_name")
local timer = minetest.get_node_timer(pos)
local interval = math.random(spawner.min_spawn_interval, spawner.max_spawn_interval)
if replacement_node_name and replacement_node_name ~= "" then
-- Check if variations are forced
local force_variation = false
--minetest.log("Spawner data: "..dump(spawners[spawner_name].replacement_nodes[replacement_node_name]))
if spawners[spawner_name].replacement_nodes[replacement_node_name].variations_only == true then
force_variation = true
end
-- Entity names for which we are going to search
local search_names = {}
local variation_names = {}
local raw_var_names = spawners[spawner_name].replacement_nodes[replacement_node_name].variations
if raw_var_names then
for name,_ in pairs(raw_var_names) do
variation_names[#variation_names + 1] = name
end
end
-- If variations are forced, only take the variation names
if force_variation == true then
search_names = variation_names
else
search_names = spawners[spawner_name].entities.all_entity_names
end
-- Search for nearby players and entities
local objects = minetest.get_objects_inside_radius(pos, 10)
local entity_count = 0
for _,object in pairs(objects) do
if object then
if object:is_player() and not force then
minetest.log("Player too close")
-- Re-schedule
timer:start(interval)
minetest.log("Next spawning scheduled in "..interval.." seconds")
return
elseif object:get_luaentity()
and object:get_luaentity().entity_name then
-- Check if the found entity has the name of one the entities
-- that this spawner cares about
for i = 1, #search_names do
if object:get_luaentity().entity_name == search_names[i] then
entity_count = entity_count + 1
break
end
end
end
end
end
-- Create a list of names we are to spawn
local spawn_names = {}
if force_variation then
spawn_names = variation_names
elseif #variation_names > 0 then
spawn_names[#spawn_names + 1] = spawners[spawner_name].entities.default
for i = 1, #variation_names do
spawn_names[#spawn_names + 1] = variation_names[i]
end
else
spawn_names[#spawn_names + 1] = spawners[spawner_name].entities.default
end
-- Choose an entity name.
local is_variation = false
if force_variation == true then
entity_name = spawn_names[math.random(1, #spawn_names)]
--minetest.log("New entity name: "..dump(entity_name))
is_variation = true
minetest.log("Spawning variation "..entity_name.." (forced)")
else
local variation_chance = math.random(1, 10)
if variation_chance > 7 then
entity_name = spawn_names[math.random(1, #spawn_names)]
-- Increase variation count
meta:set_int("spawned_variation_count", meta:get_int("spawned_variation_count") + 1)
is_variation = true
minetest.log("Spawned variation count: "..meta:get_int("spawned_variation_count"))
minetest.log("Spawning variation "..entity_name.." (not forced)")
else
entity_name = spawners[spawner_name].entities.default
end
end
-- Validation: enforce entity max spawn count
local max_mob_count = spawner.max_mob_count
if force_variation then
--minetest.log("Entity name: "..dump(entity_name))
--minetest.log("Mob data: "..dump(spawners[spawner_name]
-- .replacement_nodes[replacement_node_name]
-- .variations[entity_name]))
max_mob_count = spawners[spawner_name]
.replacement_nodes[replacement_node_name]
.variations[entity_name]
.max_count or spawner.max_mob_count
entity_count = meta:get_int("spawned_variation_count")
end
minetest.log("Entity count: "..dump(entity_count))
minetest.log("Max mob count: "..dump(max_mob_count))
if force or (entity_count <= max_mob_count) then
-- Spawn
local spawn_pos = {
x=pos.x + math.random(0, spawner.spawn_radius),
y=pos.y+3,
z=pos.z + math.random(0, spawner.spawn_radius)
}
-- Check spawn position - if not air, then spawn just above the spawner
local spawn_node = minetest.get_node_or_nil(spawn_pos)
if spawn_node and spawn_node.name ~= "air" then
spawn_pos = pos
end
minetest.log("Spawning "..entity_name.." at pos "..minetest.pos_to_string(spawn_pos))
local entity = minetest.add_entity(spawn_pos, entity_name)
if entity then
entity:get_luaentity().entity_name = entity_name
entity:get_luaentity().spawn = {
pos = pos
}
if is_variation == true then
entity:get_luaentity().variation = true
end
end
minetest.log("Next spawning scheduled in "..interval.." seconds")
else
minetest.log("Max spawn limit reached")
-- Re-calulate interval using deactivation times
interval = math.random(spawner.min_deactivation_time, spawner.max_deactivation_time)
minetest.log("Deactivating spawner for "..interval.." seconds")
end
end
-- Re-schedule
timer:start(interval)
end
end
natspawner.register_spawner = function(spawner_name, def)
spawners[spawner_name] = {
entities = def.entities,
replacement_nodes = def.node_replacement_map,
min_player_distance = def.min_player_distance or 10,
max_spawn_interval = def.max_spawn_interval or 300, --300
min_spawn_interval = def.min_spawn_interval or 120, --120
spawn_radius = def.spawn_radius or 15,
min_kill_count = def.min_kill_count or 20,
max_kill_count = def.max_kill_count or 35,
min_deactivation_time = def.min_deactivation_time or 60,
max_deactivation_time = def.max_deactivation_time or 120,
max_mob_count = def.max_mob_count or 15,
spawn_on_dig = def.spawn_on_dig
}
-- Process all names
local all_names = {}
all_names[#all_names + 1] = def.entities.default
for i = 1, #def.entities.others do
all_names[#all_names + 1] = def.entities.others[i]
end
spawners[spawner_name].entities.all_entity_names = all_names
spawner_count = spawner_count + 1
-- Register all spawners
for _,val in pairs(def.node_replacement_map) do
minetest.register_node("natspawner:"..spawner_name..val.name, {
description = "Spawner",
drop = "natspawner:"..spawner_name.."_"..val.name,
tiles = val.tiles,
groups = {crumbly=2, soil = 2},
sounds = default.node_sound_sand_defaults(),
on_construct = function(pos)
local meta = minetest.get_meta(pos)
meta:set_string("spawner_name", spawner_name)
meta:set_int("entity_spawn_count", 0)
meta:set_int("entity_killed_count", 0)
meta:set_int("spawned_variation_count", 0)
meta:set_int("max_variation_count",
math.random(def.min_variation_count or 0, def.max_variation_count or 0))
meta:set_int("next_deactivation_count",
math.random(def.min_kill_count, def.max_kill_count))
meta:set_int("next_deactivation_time",
math.random(def.min_deactivation_time, def.max_deactivation_time))
local timer = minetest.get_node_timer(pos)
timer:start(def.min_spawn_interval)
end,
on_timer = function(pos)
spawn(pos)
end,
on_dig = function(pos, node, digger)
local meta = minetest.get_meta(pos)
local entity_killed_count = meta:get_int("entity_killed_count")
local next_deactivation_count = meta:get_int("next_deactivation_count")
if (entity_killed_count < next_deactivation_count) then
if def.spawn_on_dig then
spawn(pos, true)
end
minetest.chat_send_player(digger:get_player_name(), "You have killed "..entity_killed_count.." enemies!")
return false
else
minetest.node_dig(pos, node, digger)
end
end
})
end
end
natspawner.register_spawner("zombie", {
entities = {
default = "natspawner:zombie",
others = {
"natspawner:giant_zombie",
"natspawner:ice_zombie",
"natspawner:sand_zombie",
"natspawner:sand_zombie_small"
},
},
node_replacement_map = {
["default:stone"] = {
name = "stone_spawner",
tiles = {"default_stone.png^[cracko:1:2", "default_stone.png", {name = "default_stone.png"}}
},
["default:sand"] = {
name = "sand_spawner",
tiles = {"default_sand.png^[cracko:1:2", "default_sand.png", {name = "default_sand.png"}},
variations = {
["natspawner:sand_zombie"] = {},
["natspawner:sand_zombie_small"] = {
max_count = 5
}
},
variations_only = true
},
["default:desert_sand"] = {
name = "desert_sand_spawner",
tiles = {"default_desert_sand.png^[cracko:1:2", "default_desert_sand.png", {name = "default_desert_sand.png"}},
variations = {
["natspawner:sand_zombie"] = {},
["natspawner:sand_zombie_small"] = {
max_count = 5
}
},
variations_only = true
},
["default:silver_sand"] = {
name = "silver_sand_spawner",
tiles = {"default_silver_sand.png^[cracko:1:2", "default_silver_sand.png", {name = "default_silver_sand.png"}},
variations = {
["natspawner:sand_zombie"] = {},
["natspawner:sand_zombie_small"] = {
max_count = 5
}
},
variations_only = true
},
["default:ice"] = {
name = "ice_spawner",
tiles = {"default_ice.png^[cracko:1:2", "default_ice.png", {name = "default_ice.png"}},
variations = {
["natspawner:ice_zombie"] = {
max_count = 15,
min_count = 10
}
},
variations_only = true
},
["default:snowblock"] = {
name = "snowblock_spawner",
tiles = {"default_snow.png^[cracko:1:2", "default_snow.png", {name = "default_snow.png"}},
variations = {
["natspawner:ice_zombie"] = {
max_count = 15,
min_count = 10
}
},
variations_only = true
},
["default:dirt_with_grass"] = {
name = "dirt_with_grass_spawner",
tiles = {"default_grass.png^[cracko:1:2", "default_dirt.png", {name = "default_dirt.png^default_grass_side.png"}},
variations = {
["natspawner:giant_zombie"] = {
max_count = 1
}
}
},
["default:dirt_with_dry_grass"] = {
name = "dirt_with_dry_grass_spawner",
tiles = {"default_dry_grass.png^[cracko:1:2", "default_dirt.png", {name = "default_dirt.png^default_dry_grass_side.png"}}
},
["default:dirt_with_snow"] = {
name = "dirt_with_snow_spawner",
tiles = {"default_snow.png^[cracko:1:2", "default_dirt.png", {name = "default_dirt.png^default_snow_side.png"}},
variations = {
["natspawner:ice_zombie"] = {
max_count = 15,
min_count = 10
}
},
variations_only = true
},
["default:dirt_with_rainforest_litter"] = {
name = "dirt_with_rainforest_litter_spawner",
tiles = {"default_rainforest_litter.png^[cracko:1:2", "default_dirt.png", {name = "default_dirt.png^default_rainforest_litter_side.png"}},
variations = {
["natspawner:giant_zombie"] = {
max_count = 1
}
}
},
["default:dirt_with_coniferous_litter"] = {
name = "dirt_with_coniferous_litter_spawner",
tiles = {"default_coniferous_litter.png^[cracko:1:2", "default_dirt.png", {name = "default_dirt.png^default_coniferous_litter_side.png"}}
},
["default:permafrost"] = {
name = "permafrost_spawner",
tiles = {"default_permafrost.png^[cracko:1:2", "default_permafrost.png", {name = "default_permafrost.png"}},
variations = {
["natspawner:ice_zombie"] = {}
}
},
["default:permafrost_with_stones"] = {
name = "permafrost_with_stones_spawner",
tiles = {"default_permafrost.png^default_stones.png^[cracko:1:2", "default_permafrost.png", {name = "default_permafrost.png"}},
variations = {
["natspawner:ice_zombie"] = {}
}
},
["default:permafrost_with_moss"] = {
name = "permafrost_with_moss_spawner",
tiles = {"default_moss.png^[cracko:1:2", "default_permafrost.png", {name = "default_permafrost.png^default_moss_side.png"}},
variations = {
["natspawner:ice_zombie"] = {}
}
}
},
min_player_distance = 5, --20
max_mob_count = 5, --15
max_spawn_interval = 10, --300
min_spawn_interval = 5, --120
spawn_radius = 5,
min_kill_count = 5,
max_kill_count = 10,
min_deactivation_time = 5,
max_deactivation_time = 5,
spawn_on_dig = true
})
local perl1 = {SEED1 = 9130, OCTA1 = 3, PERS1 = 0.5, SCAL1 = 250} -- Values should match minetest mapgen V6 desert noise.
local function hlp_fnct(pos, name)
local n = minetest.get_node_or_nil(pos)
if n and n.name and n.name == name then
return true
else
return false
end
end
local function ground(pos, old)
local p2 = pos
while hlp_fnct(p2, "air") do
p2.y = p2.y -1
end
if p2.y < old.y then
return p2
else
return old
end
end
local function is_air_or_buildable(node)
return node
and node.name
and (node.name == "air"
or minetest.registered_nodes[node.name].buildable_to == true)
end
minetest.register_on_generated(function(minp, maxp, seed)
if spawner_count == 0 then return end
--minetest.log("Max pos: "..minetest.pos_to_string(maxp))
--minetest.log("Min pos: "..minetest.pos_to_string(minp))
local middle_pos = { x=(maxp.x + minp.x)/2, y=(maxp.y + minp.y)/2 , z=(maxp.z + minp.z)/2 }
--minetest.log("Mid pos: "..minetest.pos_to_string(middle_pos))
local spawn_pos = nil
spawn_pos = minetest.find_node_near(middle_pos, 40, {
"default:dirt_with_grass",
"default:dirt_with_snow",
"default:dirt_with_coniferous_litter",
"default:dirt_with_rainforest_litter",
"default:permafrost",
"default:permafrost_with_moss",
"default:permafrost_with_stones",
"default:sand",
"default:desert_sand",
"default:silver_sand"
})
if (spawn_pos ~= nil) then
minetest.log("Candidate pos: "..minetest.pos_to_string(spawn_pos))
-- Check pos above
local node_above = minetest.get_node_or_nil({x=spawn_pos.x, y=spawn_pos.y+1, z=spawn_pos.z})
if node_above and node_above.name then
if (is_air_or_buildable(node_above)) then
minetest.log("Found a good pos at: "..minetest.pos_to_string(spawn_pos))
-- Choose a spawner
for i = 1, spawner_count do
local spawner_name = next(spawners)
local chance = math.random(1, spawner_count)
if chance == i then
-- Create spawner
minetest.after(0.8, function(spawner_name, pos)
local node_map = spawners[spawner_name].replacement_nodes
local node_to_replace = minetest.get_node(pos)
if node_map[node_to_replace.name] then
minetest.log("Replacing "..node_to_replace.name.." with "..spawner_name)
minetest.log("Creating advanced spawner at "..minetest.pos_to_string(pos))
minetest.set_node(pos, {name="natspawner:"..spawner_name..node_map[node_to_replace.name].name})
local meta = minetest.get_meta(pos)
meta:set_string("replaced_node_name", node_to_replace.name)
else
minetest.log("Unable to find replacement for node '"..dump(node_to_replace.name).."'")
end
end, spawner_name, spawn_pos)
end
end
end
end
end
end)
--mobs:register_egg("natspawner:zombie", "Zombie", "zombie_head.png", 0)
minetest.register_craftitem("natspawner:rotten_flesh", {
description = "Rotten Flesh",
inventory_image = "mobs_rotten_flesh.png",
on_use = minetest.item_eat(-5),
})