Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- -- ============================================================================
- -- CONFIGURATION
- -- ============================================================================
- local DEBUG_MODE = false
- local DEBRIS_CONFIG = {
- surfaces = {"nauvis"},
- fragments = {
- {"debris-c", 40},
- {"debris-b", 30},
- {"debris-a", 30},
- },
- max_debris = 80,
- min_interval = 60 * 60 * 10,
- max_interval = 60 * 60 * 20,
- spawn_radius = 300,
- scatter_radius = 10,
- min_fragments = 8,
- max_fragments = 16,
- bonus_loot_chance = 0.1,
- bonus_loot_table = {
- {"productivity-module-3", 1, 1},
- {"speed-module-3", 1, 2},
- {"efficiency-module-3", 1, 3},
- {"cliff-explosives", 2, 3},
- },
- tag_cleanup_interval = 60 * 60 * 15,
- tag_check_radius = 20
- }
- local POLLUTION_CONFIG = {
- surfaces = {"eneas"},
- check_interval = 60 * 60 * 15,
- check_radius = 200,
- vent_pollution_min = 6000,
- vent_pollution_max = 12000,
- vent_search_radius = 1000,
- thresholds = {
- low = 500,
- medium = 1000,
- high = 2500,
- critical = 5000
- }
- }
- local SUPPLY_REWARDS = {
- base = {
- ["ancient-material"] = {min = 50, max = 100},
- ["ancient-circuit"] = {min = 50, max = 100},
- ["ancient-frame"] = {min = 30, max = 60},
- },
- early_memory = {
- tech_required = "supply-tech-1",
- items = {["ancient-core"] = {min = 5, max = 5}},
- },
- mid_memory = {
- tech_required = "codex-tech",
- items = {["notable-codex"] = {min = 5, max = 10}},
- },
- late_memory = {
- tech_required = "oil-tech",
- items = {["heavy-oil-barrel"] = {min = 20, max = 35}},
- }
- }
- local YIELD_BOOST_TECHS = {
- ["supply-tech-1"] = 1.25,
- ["supply-tech-2"] = 1.5,
- ["supply-techs-3"] = 2.0,
- }
- local POLLUTION_REWARD_MULTIPLIERS = {
- clean = 2.0,
- low = 1.5,
- medium = 1.0,
- high = 0.5,
- critical = 0.0
- }
- -- ============================================================================
- -- STORAGE INITIALIZATION
- -- ============================================================================
- function init_debris()
- storage.events = storage.events or {}
- if not storage.events.debris then
- storage.events.debris = {
- enabled = true,
- -- FIX: storage.rng.debris is used here instead of math.random().
- -- But the generators are created just below, so we initialise
- -- events.debris *after* we know the generators exist.
- -- We defer the next_tick assignment to after rng init below.
- }
- end
- if not storage.events.pollution then
- storage.events.pollution = {
- enabled = true,
- next_tick = game.tick + POLLUTION_CONFIG.check_interval
- }
- end
- if not storage.events.tag_cleanup then
- storage.events.tag_cleanup = {
- enabled = true,
- next_tick = game.tick + DEBRIS_CONFIG.tag_cleanup_interval
- }
- end
- -- -----------------------------------------------------------------------
- -- FIX: Create deterministic random generators and store them in `storage`.
- --
- -- game.create_random_generator() produces a LuaRandomGenerator seeded from
- -- the map's own random state, which is identical on every client because it
- -- comes from the synchronized game seed — not from the local system clock.
- --
- -- We store each generator in storage so that:
- -- 1. Its internal state is included in the save file.
- -- 2. A player joining mid-game loads the save and gets the same generator
- -- state the server has, keeping their RNG in sync going forward.
- --
- -- We create one generator per system so their sequences are independent.
- -- The `or` guards ensure we only create them once; on_configuration_changed
- -- calls init_debris() again but should not reset existing generators.
- -- -----------------------------------------------------------------------
- storage.rng = storage.rng or {}
- storage.rng.debris = storage.rng.debris or game.create_random_generator()
- storage.rng.pollution = storage.rng.pollution or game.create_random_generator()
- storage.rng.loot = storage.rng.loot or game.create_random_generator()
- storage.rng.talk = storage.rng.talk or game.create_random_generator()
- storage.rng.spawn = storage.rng.spawn or game.create_random_generator()
- -- Now that the rng exists, assign the first debris next_tick if not set yet.
- if not storage.events.debris.next_tick then
- -- FIX: storage.rng.debris(min, max) instead of math.random(min, max)
- storage.events.debris.next_tick = game.tick +
- storage.rng.debris(DEBRIS_CONFIG.min_interval, DEBRIS_CONFIG.max_interval)
- end
- end
- -- ============================================================================
- -- DEBRIS SYSTEM
- -- ============================================================================
- local function count_debris_on_surface(surface)
- local total = 0
- for i = 1, #DEBRIS_CONFIG.fragments do
- local debris_name = DEBRIS_CONFIG.fragments[i][1]
- total = total + #surface.find_entities_filtered{name = debris_name}
- end
- return total
- end
- local function select_random_debris()
- local total_weight = 0
- for i = 1, #DEBRIS_CONFIG.fragments do
- total_weight = total_weight + DEBRIS_CONFIG.fragments[i][2]
- end
- -- FIX: storage.rng.debris() instead of math.random()
- -- storage.rng.debris() with no arguments returns a float in [0,1),
- -- matching the old math.random() behaviour used here.
- local rand = storage.rng.debris() * total_weight
- local cumulative = 0
- for i = 1, #DEBRIS_CONFIG.fragments do
- cumulative = cumulative + DEBRIS_CONFIG.fragments[i][2]
- if rand <= cumulative then
- return DEBRIS_CONFIG.fragments[i][1]
- end
- end
- return DEBRIS_CONFIG.fragments[1][1]
- end
- local function find_valid_spawn_location(surface, max_attempts)
- max_attempts = max_attempts or 50
- for attempt = 1, max_attempts do
- -- FIX: storage.rng.debris(min, max) instead of math.random(min, max)
- local x = storage.rng.debris(-DEBRIS_CONFIG.spawn_radius, DEBRIS_CONFIG.spawn_radius)
- local y = storage.rng.debris(-DEBRIS_CONFIG.spawn_radius, DEBRIS_CONFIG.spawn_radius)
- local position = {x = x, y = y}
- local area = {
- {x - DEBRIS_CONFIG.scatter_radius, y - DEBRIS_CONFIG.scatter_radius},
- {x + DEBRIS_CONFIG.scatter_radius, y + DEBRIS_CONFIG.scatter_radius}
- }
- if not surface.can_place_entity{name = DEBRIS_CONFIG.fragments[1][1], position = position} then
- goto continue
- end
- local cliffs = surface.find_entities_filtered{area = area, type = "cliff"}
- if #cliffs > 0 then goto continue end
- local entities = surface.find_entities_filtered{area = area}
- local has_blocking_entity = false
- for i = 1, #entities do
- local entity = entities[i]
- local t = entity.type
- if t ~= "tree" and t ~= "simple-entity" and t ~= "decorative"
- and t ~= "resource" and t ~= "fish" then
- has_blocking_entity = true
- break
- end
- end
- if not has_blocking_entity then
- return position
- end
- ::continue::
- end
- return nil
- end
- local function create_crash_effects(surface, position)
- surface.create_entity{name = "explosion", position = position}
- surface.play_sound{path = "utility/build_large", position = position, volume_modifier = 1.0}
- end
- local function scatter_fragments(surface, crash_position, fragment_count)
- local placed_positions = {}
- local fragments_placed = 0
- for attempt = 1, fragment_count * 5 do
- if fragments_placed >= fragment_count then break end
- -- FIX: storage.rng.debris() instead of math.random()
- local angle = storage.rng.debris() * 2 * math.pi
- local distance = storage.rng.debris() * DEBRIS_CONFIG.scatter_radius
- local fragment_pos = {
- x = crash_position.x + math.cos(angle) * distance,
- y = crash_position.y + math.sin(angle) * distance
- }
- local too_close = false
- for i = 1, #placed_positions do
- local ep = placed_positions[i]
- local dx = fragment_pos.x - ep.x
- local dy = fragment_pos.y - ep.y
- if dx*dx + dy*dy < 2 then
- too_close = true
- break
- end
- end
- if not too_close then
- local debris_name = select_random_debris()
- if surface.can_place_entity{name = debris_name, position = fragment_pos} then
- local entity = surface.create_entity{
- name = debris_name,
- position = fragment_pos,
- force = "neutral"
- }
- if entity and entity.valid then
- table.insert(placed_positions, fragment_pos)
- fragments_placed = fragments_placed + 1
- end
- end
- end
- end
- return fragments_placed
- end
- local function execute_debris_crash()
- local available_surfaces = {}
- for i = 1, #DEBRIS_CONFIG.surfaces do
- local surface_name = DEBRIS_CONFIG.surfaces[i]
- if game.surfaces[surface_name] then
- table.insert(available_surfaces, surface_name)
- end
- end
- -- FIX: storage.rng.debris(i) instead of math.random(i) for the shuffle
- for i = #available_surfaces, 2, -1 do
- local j = storage.rng.debris(i)
- available_surfaces[i], available_surfaces[j] = available_surfaces[j], available_surfaces[i]
- end
- for i = 1, #available_surfaces do
- local surface_name = available_surfaces[i]
- local surface = game.surfaces[surface_name]
- local debris_count = count_debris_on_surface(surface)
- if debris_count >= DEBRIS_CONFIG.max_debris then
- storage.events.debris.enabled = false
- game.print(
- "[color=orange][Debris System][/color] Maximum debris limit reached (" ..
- debris_count .. "/" .. DEBRIS_CONFIG.max_debris ..
- "). System paused until debris is cleared."
- )
- return false
- end
- local crash_position = find_valid_spawn_location(surface)
- if crash_position then
- create_crash_effects(surface, crash_position)
- -- FIX: storage.rng.debris(min, max) instead of math.random(min, max)
- local fragment_count = storage.rng.debris(DEBRIS_CONFIG.min_fragments, DEBRIS_CONFIG.max_fragments)
- local placed_count = scatter_fragments(surface, crash_position, fragment_count)
- game.print(
- "[color=yellow][Nauvis][/color] " ..
- placed_count .. " fragments crashed on " .. surface_name ..
- " [gps=" .. math.floor(crash_position.x) .. "," ..
- math.floor(crash_position.y) .. "," .. surface_name .. "] (" ..
- (debris_count + placed_count) .. "/" .. DEBRIS_CONFIG.max_debris .. " total)"
- )
- game.forces["player"].add_chart_tag(surface, {
- position = crash_position,
- text = "Debris [entity=debris-a] "
- })
- return true
- end
- end
- return false
- end
- -- ============================================================================
- -- POLLUTION MONITORING SYSTEM (unchanged logic, only rng calls replaced)
- -- ============================================================================
- local function get_pollution_level(pollution_value)
- if pollution_value < POLLUTION_CONFIG.thresholds.low then return "clean"
- elseif pollution_value < POLLUTION_CONFIG.thresholds.medium then return "low"
- elseif pollution_value < POLLUTION_CONFIG.thresholds.high then return "medium"
- elseif pollution_value < POLLUTION_CONFIG.thresholds.critical then return "high"
- else return "critical"
- end
- end
- local function check_pollution_on_surface(surface)
- if not surface or not surface.valid then return 0 end
- local total_pollution = 0
- for chunk in surface.get_chunks() do
- local chunk_center = {x = chunk.x * 32 + 16, y = chunk.y * 32 + 16}
- total_pollution = total_pollution + surface.get_pollution(chunk_center)
- end
- return total_pollution
- end
- local function find_nearest_vent(surface, origin)
- local area = {
- {origin.x - POLLUTION_CONFIG.vent_search_radius, origin.y - POLLUTION_CONFIG.vent_search_radius},
- {origin.x + POLLUTION_CONFIG.vent_search_radius, origin.y + POLLUTION_CONFIG.vent_search_radius}
- }
- local vents = surface.find_entities_filtered{area = area, name = "eneas-vent"}
- if #vents == 0 then return nil end
- local closest_vent, closest_distance = nil, math.huge
- for i = 1, #vents do
- local vent = vents[i]
- local dx = vent.position.x - origin.x
- local dy = vent.position.y - origin.y
- local distance = math.sqrt(dx*dx + dy*dy)
- if distance < closest_distance then
- closest_distance = distance
- closest_vent = vent
- end
- end
- return closest_vent
- end
- local function execute_gas_venting(surface)
- local origin = {x = 0, y = 0}
- local vent = find_nearest_vent(surface, origin)
- -- FIX: storage.rng.pollution(min, max) instead of math.random(min, max)
- local pollution_amount = storage.rng.pollution(
- POLLUTION_CONFIG.vent_pollution_min,
- POLLUTION_CONFIG.vent_pollution_max
- )
- if not vent then
- surface.pollute(origin, pollution_amount)
- game.print("[color=red][Eneas][/color] Eneas core venting: " ..
- pollution_amount .. " pollution released from Eneas depths.")
- return true
- end
- surface.pollute(vent.position, pollution_amount)
- surface.create_entity{name = "poison-cloud", position = vent.position}
- surface.play_sound{path = "utility/axe_mining_ore", position = vent.position, volume_modifier = 0.8}
- game.print(
- "[color=red][Eneas][/color] Eneas venting cycle: " ..
- pollution_amount .. " pollution at " ..
- "[gps=" .. math.floor(vent.position.x) .. "," ..
- math.floor(vent.position.y) .. ",eneas]"
- )
- game.forces["player"].add_chart_tag(surface, {
- position = vent.position,
- text = "Vent [item=poison-capsule]"
- })
- return true
- end
- -- ============================================================================
- -- SUPPLY SYSTEM (unchanged logic, only rng calls replaced)
- -- ============================================================================
- local function calculate_yield_multiplier(force)
- local total_multiplier = 1.0
- for tech_name, multiplier in pairs(YIELD_BOOST_TECHS) do
- if force.technologies[tech_name] and force.technologies[tech_name].researched then
- total_multiplier = total_multiplier * multiplier
- end
- end
- return total_multiplier
- end
- local function build_reward_table(force)
- local rewards = {}
- for item, amounts in pairs(SUPPLY_REWARDS.base) do
- rewards[item] = amounts
- end
- if force.technologies[SUPPLY_REWARDS.early_memory.tech_required] and
- force.technologies[SUPPLY_REWARDS.early_memory.tech_required].researched then
- for item, amounts in pairs(SUPPLY_REWARDS.early_memory.items) do
- rewards[item] = amounts
- end
- end
- if force.technologies[SUPPLY_REWARDS.mid_memory.tech_required] and
- force.technologies[SUPPLY_REWARDS.mid_memory.tech_required].researched then
- for item, amounts in pairs(SUPPLY_REWARDS.mid_memory.items) do
- rewards[item] = amounts
- end
- end
- if force.technologies[SUPPLY_REWARDS.late_memory.tech_required] and
- force.technologies[SUPPLY_REWARDS.late_memory.tech_required].researched then
- for item, amounts in pairs(SUPPLY_REWARDS.late_memory.items) do
- rewards[item] = amounts
- end
- end
- return rewards
- end
- local function create_scaled_rewards(available_items, pollution_multiplier, yield_multiplier)
- local final_rewards = {}
- for item, amounts in pairs(available_items) do
- -- FIX: storage.rng.pollution(min, max) instead of math.random(min, max).
- -- We reuse the pollution generator here since rewards are calculated
- -- as part of the pollution check cycle. Using the same generator for
- -- the whole pollution event keeps the logic grouped naturally.
- local base_amount = storage.rng.pollution(amounts.min, amounts.max)
- local scaled_amount = math.floor(base_amount * pollution_multiplier * yield_multiplier)
- if scaled_amount > 0 then
- final_rewards[item] = scaled_amount
- end
- end
- return final_rewards
- end
- local function deliver_supplies_to_lift(surface, force, rewards, pollution_level)
- -- Find the eneas-lift entity (should be at or near origin)
- local lift = surface.find_entity("eneas-lift", {0, 0})
- if not lift then
- -- Search in a small area around origin if not exactly at 0,0
- local lifts = surface.find_entities_filtered{
- name = "eneas-lift",
- position = {0, 0},
- radius = 50
- }
- if #lifts > 0 then
- lift = lifts[1]
- end
- end
- if not lift or not lift.valid then
- game.print("[color=orange][Unit-05][/color] Supply delivery failed - transport hub offline.")
- return false
- end
- -- Get the lift's inventory
- local inventory = lift.get_inventory(defines.inventory.chest)
- if not inventory then
- game.print("[color=orange][Unit-05][/color] Supply delivery failed - transport hub malfunction.")
- return false
- end
- -- Insert all rewards
- local items_delivered = {}
- for item, count in pairs(rewards) do
- local inserted = inventory.insert{name = item, count = count}
- if inserted > 0 then
- table.insert(items_delivered, inserted .. "x [item=" .. item .. "]")
- end
- end
- if #items_delivered > 0 then
- -- Visual and sound feedback at lift location
- surface.create_trivial_smoke{name = "smoke-fast", position = lift.position}
- surface.play_sound{path = "entity-open/steel-chest", position = lift.position}
- -- Notification
- local level_color = "green"
- if pollution_level == "low" then level_color = "cyan"
- elseif pollution_level == "medium" then level_color = "yellow"
- elseif pollution_level == "high" then level_color = "orange"
- end
- game.print(
- "[color=" .. level_color .. "][Unit-05][/color] Supplies delivered to transport hub: " ..
- table.concat(items_delivered, ", ")
- )
- -- Alert all players
- for _, player in pairs(force.connected_players) do
- player.add_custom_alert(lift, {type = "item", name = "eneas-lift"}, "Supply Delivery", true)
- player.play_sound{path = "utility/achievement_unlocked"}
- end
- return true
- else
- game.print("[color=orange][Unit-05][/color] Transport hub inventory full - supply delivery delayed.")
- return false
- end
- end
- local function execute_pollution_check()
- for i = 1, #POLLUTION_CONFIG.surfaces do
- local surface_name = POLLUTION_CONFIG.surfaces[i]
- local surface = game.surfaces[surface_name]
- if surface then
- -- First check CURRENT pollution levels (before venting)
- local pollution = check_pollution_on_surface(surface)
- local level = get_pollution_level(pollution)
- -- Find unit-05 at origin
- local supercomputer = surface.find_entity("unit-05", {0, 0})
- -- Handle different pollution levels
- if level == "clean" then
- game.print("[color=green][Eneas][/color] Air quality is excellent. Pollution: " ..
- math.floor(pollution) .. " units")
- if supercomputer and supercomputer.valid then
- supercomputer.active = true
- end
- -- Deliver supplies at clean air multiplier
- local force = game.forces["player"]
- local available_items = build_reward_table(force)
- local yield_multiplier = calculate_yield_multiplier(force)
- local rewards = create_scaled_rewards(available_items, POLLUTION_REWARD_MULTIPLIERS.clean, yield_multiplier)
- deliver_supplies_to_lift(surface, force, rewards, "clean")
- elseif level == "low" then
- game.print("[color=cyan][Eneas][/color] Minor contamination detected. Pollution: " ..
- math.floor(pollution) .. " units")
- if supercomputer and supercomputer.valid then
- supercomputer.active = true
- end
- -- Deliver supplies at low pollution multiplier
- local force = game.forces["player"]
- local available_items = build_reward_table(force)
- local yield_multiplier = calculate_yield_multiplier(force)
- local rewards = create_scaled_rewards(available_items, POLLUTION_REWARD_MULTIPLIERS.low, yield_multiplier)
- deliver_supplies_to_lift(surface, force, rewards, "low")
- elseif level == "medium" then
- game.print("[color=yellow][Eneas][/color] Moderate contamination levels detected. Pollution: " ..
- math.floor(pollution) .. " units")
- if supercomputer and supercomputer.valid then
- supercomputer.active = true
- end
- -- Deliver supplies at normal multiplier
- local force = game.forces["player"]
- local available_items = build_reward_table(force)
- local yield_multiplier = calculate_yield_multiplier(force)
- local rewards = create_scaled_rewards(available_items, POLLUTION_REWARD_MULTIPLIERS.medium, yield_multiplier)
- deliver_supplies_to_lift(surface, force, rewards, "medium")
- elseif level == "high" then
- game.print("[color=orange][Eneas][/color] Warning: High contamination detected! Pollution: " ..
- math.floor(pollution) .. " units")
- if supercomputer and supercomputer.valid then
- supercomputer.active = true
- end
- -- Deliver reduced supplies
- local force = game.forces["player"]
- local available_items = build_reward_table(force)
- local yield_multiplier = calculate_yield_multiplier(force)
- local rewards = create_scaled_rewards(available_items, POLLUTION_REWARD_MULTIPLIERS.high, yield_multiplier)
- deliver_supplies_to_lift(surface, force, rewards, "high")
- else -- critical
- game.print("[color=red][Eneas][/color] CRITICAL: Dangerous pollution levels! Pollution: " ..
- math.floor(pollution) .. " units")
- -- Disable supercomputer AND low supplies
- if supercomputer and supercomputer.valid then
- supercomputer.active = false
- game.print("[color=red][Unit-05][/color] Argh I cannot think like this! Please help turn down the contamination it is driving me crazy.")
- end
- local force = game.forces["player"]
- local available_items = build_reward_table(force)
- local yield_multiplier = calculate_yield_multiplier(force)
- local rewards = create_scaled_rewards(available_items, POLLUTION_REWARD_MULTIPLIERS.high, yield_multiplier)
- deliver_supplies_to_lift(surface, force, rewards, "high")
- end
- -- AFTER checking and rewarding, Eneas takes its breath (vents pollution for next cycle)
- execute_gas_venting(surface)
- end
- end
- return true
- end
- -- ============================================================================
- -- SUPPLY DROP SYSTEM (OLD - kept for compatibility, not used for Eneas)
- -- ============================================================================
- local SUPPLY_DROP_SPAWN_RADIUS = 100
- local function find_supply_drop_position(surface)
- -- Try to find a valid position within radius of origin
- for attempt = 1, 50 do
- local angle = storage.rng.debris() * 2 * math.pi
- local distance = storage.rng.debris() * SUPPLY_DROP_SPAWN_RADIUS
- local test_position = {
- x = math.cos(angle) * distance,
- y = math.sin(angle) * distance
- }
- local drop_position = surface.find_non_colliding_position(
- "steel-chest",
- test_position,
- 10,
- 1
- )
- if drop_position then
- return drop_position
- end
- end
- return nil
- end
- function create_supply_drop_for_player(player, target_surface, drop_contents, message)
- if not player.valid then
- return false
- end
- local drop_position = find_supply_drop_position(target_surface)
- if drop_position then
- local chest = target_surface.create_entity{
- name = "steel-chest",
- position = drop_position,
- force = player.force
- }
- if chest and chest.valid then
- chest.backer_name = string.format("Supply Crate for %s", player.name)
- for item, count in pairs(drop_contents) do
- chest.insert{name = item, count = count}
- end
- -- Visual and sound feedback
- target_surface.create_trivial_smoke{name = "smoke-fast", position = drop_position}
- target_surface.play_sound{path = "entity-open/steel-chest", position = drop_position}
- -- Alert player with custom message or default
- local alert_message = message or "[color=green][Supply Drop][/color] Unit-05 has delivered a supply crate!"
- player.print(alert_message)
- player.add_custom_alert(chest, {type = "item", name = "steel-chest"}, "Supply Drop", true)
- player.play_sound{path = "utility/achievement_unlocked"}
- -- Add chart tag
- player.force.add_chart_tag(target_surface, {
- position = drop_position,
- text = string.format("%s's Supply", player.name)
- })
- return true
- end
- else
- player.print("[color=red]Warning: Could not find safe location for supply drop![/color]")
- return false
- end
- return false
- end
- -- ============================================================================
- -- MAP TAG CLEANUP SYSTEM
- -- ============================================================================
- local function cleanup_debris_tags()
- for i = 1, #DEBRIS_CONFIG.surfaces do
- local surface_name = DEBRIS_CONFIG.surfaces[i]
- local surface = game.surfaces[surface_name]
- if surface then
- local force = game.forces["player"]
- local tags = force.find_chart_tags(surface)
- local removed_count = 0
- for _, tag in pairs(tags) do
- -- Check if this is a debris tag (contains "Debris" in the text)
- if tag.text and string.find(tag.text, "Debris") then
- -- Check for debris entities near this tag
- local area = {
- {tag.position.x - DEBRIS_CONFIG.tag_check_radius, tag.position.y - DEBRIS_CONFIG.tag_check_radius},
- {tag.position.x + DEBRIS_CONFIG.tag_check_radius, tag.position.y + DEBRIS_CONFIG.tag_check_radius}
- }
- local has_debris = false
- for j = 1, #DEBRIS_CONFIG.fragments do
- local debris_name = DEBRIS_CONFIG.fragments[j][1]
- local debris_nearby = surface.find_entities_filtered{
- area = area,
- name = debris_name
- }
- if #debris_nearby > 0 then
- has_debris = true
- break
- end
- end
- -- If no debris found, remove the tag
- if not has_debris then
- tag.destroy()
- removed_count = removed_count + 1
- end
- end
- end
- if removed_count > 0 then
- if DEBUG_MODE then
- game.print("[color=gray][Debug] Removed " .. removed_count .. " empty debris tags from " .. surface_name .. "[/color]")
- end
- end
- end
- end
- return true
- end
- -- ============================================================================
- -- EVENT DISPATCHER
- -- ============================================================================
- local function check_timed_events(event)
- -- Check debris event
- if storage.events.debris.enabled and
- storage.events.debris.next_tick and
- game.tick >= storage.events.debris.next_tick then
- local success = execute_debris_crash()
- if success then
- storage.events.debris.next_tick = game.tick +
- storage.rng.debris(DEBRIS_CONFIG.min_interval, DEBRIS_CONFIG.max_interval)
- else
- storage.events.debris.next_tick = game.tick + storage.rng.debris(5 * 60 * 60, 15 * 60 * 60)
- end
- end
- -- Check pollution event
- if storage.events.pollution.enabled and
- storage.events.pollution.next_tick and
- game.tick >= storage.events.pollution.next_tick then
- execute_pollution_check()
- storage.events.pollution.next_tick = game.tick + POLLUTION_CONFIG.check_interval
- end
- -- Check tag cleanup event
- if storage.events.tag_cleanup and
- storage.events.tag_cleanup.enabled and
- storage.events.tag_cleanup.next_tick and
- game.tick >= storage.events.tag_cleanup.next_tick then
- cleanup_debris_tags()
- storage.events.tag_cleanup.next_tick = game.tick + DEBRIS_CONFIG.tag_cleanup_interval
- end
- end
- -- ============================================================================
- -- DEBRIS MINING HANDLER
- -- ============================================================================
- local function on_debris_mined(event)
- local entity = event.entity
- if not entity or not entity.valid then return end
- local player = event.player_index and game.get_player(event.player_index)
- local is_debris = false
- for i = 1, #DEBRIS_CONFIG.fragments do
- if entity.name == DEBRIS_CONFIG.fragments[i][1] then
- is_debris = true
- storage.events.debris.enabled = true
- break
- end
- end
- if is_debris and player and player.valid and player.character then
- -- FIX: storage.rng.loot() instead of math.random()
- -- We use a dedicated loot generator so the loot rolls are independent
- -- from debris spawn rolls — they won't interfere with each other's
- -- sequences even if one system fires more often than the other.
- local roll = storage.rng.loot()
- if roll < DEBRIS_CONFIG.bonus_loot_chance then
- -- FIX: storage.rng.loot(N) instead of math.random(N)
- local loot_entry = DEBRIS_CONFIG.bonus_loot_table[
- storage.rng.loot(#DEBRIS_CONFIG.bonus_loot_table)
- ]
- local item_name = loot_entry[1]
- local min_count = loot_entry[2]
- local max_count = loot_entry[3]
- -- FIX: storage.rng.loot(min, max) instead of math.random(min, max)
- local count = storage.rng.loot(min_count, max_count)
- local inserted = player.insert{name = item_name, count = count}
- if inserted < count then
- player.surface.spill_item_stack{
- position = player.position,
- stack = {name = item_name, count = count - inserted}
- }
- end
- player.print("[color=yellow]You find something valuable among the rubble![/color]")
- player.play_sound{path = "utility/scenario_message"}
- end
- end
- end
- -- ============================================================================
- -- EVENT REGISTRATION (unchanged)
- -- ============================================================================
- script.on_nth_tick(60, check_timed_events)
- script.on_event(defines.events.on_player_mined_entity, on_debris_mined)
- script.on_event(defines.events.on_robot_mined_entity, on_debris_mined)
- -- ============================================================================
- -- COMMANDS
- -- ============================================================================
- commands.add_command("debris-crash", "Manually trigger a debris crash event", function(command)
- local player = command.player_index and game.get_player(command.player_index)
- if command.player_index and (not player or not player.admin) then
- player.print("[color=red]Admin privileges required.[/color]")
- return
- end
- local success = execute_debris_crash()
- local message = success and "Debris crash triggered." or "Failed to find valid spawn location or limit reached."
- if player then player.print(message) else print(message) end
- end)
- commands.add_command("pollution-check", "Manually trigger a pollution check", function(command)
- local player = command.player_index and game.get_player(command.player_index)
- if command.player_index and (not player or not player.admin) then
- player.print("[color=red]Admin privileges required.[/color]")
- return
- end
- execute_pollution_check()
- end)
- commands.add_command("gas-vent", "Manually trigger a gas venting event", function(command)
- local player = command.player_index and game.get_player(command.player_index)
- if command.player_index and (not player or not player.admin) then
- player.print("[color=red]Admin privileges required.[/color]")
- return
- end
- local surface = game.surfaces["eneas"]
- if surface then
- execute_gas_venting(surface)
- else
- local msg = "Eneas surface not found."
- if player then player.print(msg) else print(msg) end
- end
- end)
- commands.add_command("debris-schedule", "Set next debris crash (in minutes)", function(command)
- local player = command.player_index and game.get_player(command.player_index)
- if command.player_index and (not player or not player.admin) then
- player.print("[color=red]Admin privileges required.[/color]")
- return
- end
- local minutes = tonumber(command.parameter)
- if not minutes or minutes < 0 then
- local msg = "Usage: /debris-schedule <minutes>"
- if player then player.print(msg) else print(msg) end
- return
- end
- storage.events.debris.next_tick = game.tick + (minutes * 60 * 60)
- local msg = "Next debris crash scheduled in " .. minutes .. " minutes."
- if player then player.print(msg) else print(msg) end
- end)
- commands.add_command("pollution-schedule", "Set next pollution check (in minutes)", function(command)
- local player = command.player_index and game.get_player(command.player_index)
- if command.player_index and (not player or not player.admin) then
- player.print("[color=red]Admin privileges required.[/color]")
- return
- end
- local minutes = tonumber(command.parameter)
- if not minutes or minutes < 0 then
- local msg = "Usage: /pollution-schedule <minutes>"
- if player then player.print(msg) else print(msg) end
- return
- end
- storage.events.pollution.next_tick = game.tick + (minutes * 60 * 60)
- local msg = "Next pollution check scheduled in " .. minutes .. " minutes."
- if player then player.print(msg) else print(msg) end
- end)
- commands.add_command("debris-count", "Check current debris count on all surfaces", function(command)
- local player = command.player_index and game.get_player(command.player_index)
- for i = 1, #DEBRIS_CONFIG.surfaces do
- local surface_name = DEBRIS_CONFIG.surfaces[i]
- if game.surfaces[surface_name] then
- local count = count_debris_on_surface(game.surfaces[surface_name])
- local msg = surface_name .. ": " .. count .. "/" .. DEBRIS_CONFIG.max_debris .. " debris"
- if player then player.print(msg) else print(msg) end
- end
- end
- local status = storage.events.debris.enabled and "enabled" or "disabled"
- local msg = "Debris system: " .. status
- if player then player.print(msg) else print(msg) end
- end)
- commands.add_command("event-status", "Check status of all timed events", function(command)
- local player = command.player_index and game.get_player(command.player_index)
- local function print_msg(msg)
- if player then player.print(msg) else print(msg) end
- end
- print_msg("=== Timed Events Status ===")
- -- Debris status
- local debris_status = storage.events.debris.enabled and "enabled" or "disabled"
- local debris_time = storage.events.debris.next_tick and
- math.floor((storage.events.debris.next_tick - game.tick) / 60 / 60) or "N/A"
- print_msg("Debris: " .. debris_status .. " | Next in: " .. debris_time .. " minutes")
- -- tag status
- local tag_status = storage.events.tag_cleanup.enabled and "enabled" or "disabled"
- local tag_time = storage.events.tag_cleanup.next_tick and
- math.floor((storage.events.tag_cleanup.next_tick - game.tick) / 60 / 60) or "N/A"
- print_msg("Tag cleanup: " .. tag_status .. " | Next in: " .. tag_time .. " minutes")
- -- Pollution status
- local pollution_status = storage.events.pollution.enabled and "enabled" or "disabled"
- local pollution_time = storage.events.pollution.next_tick and
- math.floor((storage.events.pollution.next_tick - game.tick) / 60 / 60) or "N/A"
- print_msg("Pollution: " .. pollution_status .. " | Next in: " .. pollution_time .. " minutes")
- end)
Advertisement
Add Comment
Please, Sign In to add comment