-- ============================================================================ -- 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 " 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 " 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)