XRWS           &Ó   ¶  &ext_01.cat ext_01.dat md/drjele_abandoned_ships_cleanup.xml 7392 1790265246 93a1e28a788887824a75480158b75321
md/drjele_abandoned_ships_cleanup_options.xml 2365 1790265246 486c630c13024f7938d43b6f5a7cb36a
<?xml version="1.0" encoding="utf-8"?>
<mdscript name="DrJele_AbandonedShipsCleanup" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="md.xsd">
    <cues>

        <cue name="Configuration" instantiate="true" namespace="static" version="1">
            <conditions>
                <event_cue_signalled cue="md.Setup.Start"/>
            </conditions>
            <actions>
                <set_value name="$Lifetime" exact="5h" comment="game time an abandoned ship survives once the mod has seen it"/>
                <set_value name="$ScanInterval" exact="5min" comment="game time between two galaxy scans"/>
                <set_value name="$GameStartGrace" exact="60s" comment="ships spawned before this point were placed while the universe was generated and are never touched"/>
                <set_value name="$StaleGap" exact="15min" comment="gap between two sightings after which the clock restarts instead of expiring"/>
                <set_value name="$BatchSize" exact="100" comment="ships evaluated per tick"/>
                <set_value name="$DebugChance" exact="0" comment="100 writes progress to the debug log"/>
                <debug_text text="'DrJele cleanup: configuration loaded, lifetime %s, scan every %s'.[$Lifetime, $ScanInterval]" chance="if global.$DrJeleAbandonedShipDebugChance? then global.$DrJeleAbandonedShipDebugChance else $DebugChance"/>
            </actions>
        </cue>

        <cue name="State" version="1">
            <actions>
                <set_value name="$Clock" exact="table[]"/>
                <set_value name="$NextScanTime" exact="0s"/>
            </actions>
        </cue>

        <cue name="Scan" namespace="this" checkinterval="15s" version="1">
            <conditions>
                <check_value value="Configuration.$Lifetime?"/>
                <check_value value="State.$NextScanTime?"/>
                <check_value value="player.age ge State.$NextScanTime" comment="checkinterval alone does not throttle a cue that resets itself, so the schedule is explicit"/>
                <check_value value="macro.xu_ep2_universe_macro == player.galaxy.macro" comment="main galaxy only - leaves Timelines scenarios, tutorials and the workshop untouched"/>
            </conditions>
            <actions>
                <set_value name="$Lifetime" exact="if global.$DrJeleAbandonedShipLifetime? then global.$DrJeleAbandonedShipLifetime else Configuration.$Lifetime"/>
                <set_value name="State.$NextScanTime" exact="player.age + [[Configuration.$ScanInterval, $Lifetime / 5].min, 30s].max"/>
                <set_value name="$DebugChance" exact="if global.$DrJeleAbandonedShipDebugChance? then global.$DrJeleAbandonedShipDebugChance else Configuration.$DebugChance"/>
                <find_ship_by_true_owner name="$Candidates" faction="faction.ownerless"
                                         space="player.galaxy" multiple="true"
                                         excluded="global.$IgnoredAbandonedShips"
                                         docked="false" unit="false" masstraffic="false"
                                         invulnerable="false" indestructible="false">
                    <match class="class.ship_xs" negate="true"/>
                    <match class="class.spacesuit" negate="true"/>
                    <match state="componentstate.wreck" negate="true"/>
                    <match_context class="class.highway" negate="true"/>
                </find_ship_by_true_owner>
                <do_if value="not @$Candidates.count">
                    <set_value name="$Candidates" exact="[]"/>
                </do_if>
                <set_value name="$DestroyedThisPass" exact="0"/>
                <debug_text text="'DrJele cleanup: evaluating %s abandoned ships, %s clocks held'.[$Candidates.count, State.$Clock.keys.count]" chance="Scan.$DebugChance"/>
            </actions>
            <cues>
                <!-- Batched, so a galaxy holding a thousand derelicts never costs a whole frame at once. -->
                <cue name="ProcessBatch">
                    <delay exact="1s"/>
                    <actions>
                        <do_all exact="[$Candidates.count, Configuration.$BatchSize].min">
                            <set_value name="$ship" exact="$Candidates.{$Candidates.count}"/>
                            <remove_value name="$Candidates.{$Candidates.count}"/>
                            <do_if value="@$ship.exists and $ship.isownerless
                            and $ship.spawntime ge Configuration.$GameStartGrace
                            and not $ship.ismission
                            and not $ship.hasbeenrenamed
                            and not @$ship.boardingoperations.count
                            and not @$ship.salvageclaimants.count">
                                <do_if value="@State.$Clock.{$ship} and (player.age - State.$Clock.{$ship}.{2}) le Configuration.$StaleGap">
                                    <set_value name="State.$Clock.{$ship}" exact="[State.$Clock.{$ship}.{1}, player.age]"/>
                                </do_if>
                                <do_else>
                                    <set_value name="State.$Clock.{$ship}" exact="[player.age, player.age]"/>
                                </do_else>

                                <!-- Never pop a ship out of existence in front of the player; retried next pass. -->
                                <do_if value="@State.$Clock.{$ship} and (player.age - State.$Clock.{$ship}.{1}) ge $Lifetime and $ship.attention lt attention.visible">
                                    <debug_text text="'DrJele cleanup: destroying %s %s in %s, abandoned for %s'.[$ship.idcode, $ship.knownname, @$ship.sector.knownname, player.age - State.$Clock.{$ship}.{1}]" chance="Scan.$DebugChance"/>
                                    <destroy_object object="$ship" explosion="true"/>
                                    <remove_value name="State.$Clock.{$ship}"/>
                                    <set_value name="$DestroyedThisPass" operation="add"/>
                                </do_if>
                            </do_if>
                        </do_all>
                        <do_if value="$Candidates.count">
                            <reset_cue cue="this"/>
                        </do_if>
                        <do_else>
                            <!-- Drop clocks for ships that are gone or no longer show up in the scan. -->
                            <do_all exact="State.$Clock.keys.count" counter="$i" reverse="true">
                                <set_value name="$key" exact="State.$Clock.keys.list.{$i}"/>
                                <do_if value="not @$key.exists or (player.age - State.$Clock.{$key}.{2}) gt Configuration.$StaleGap">
                                    <remove_value name="State.$Clock.{$key}"/>
                                </do_if>
                            </do_all>
                            <debug_text text="'DrJele cleanup: pass finished, %s ships destroyed, %s clocks held'.[$DestroyedThisPass, State.$Clock.keys.count]" chance="Scan.$DebugChance"/>
                            <reset_cue cue="Scan"/>
                        </do_else>
                    </actions>
                </cue>
            </cues>
        </cue>

    </cues>
</mdscript>
<?xml version="1.0" encoding="utf-8"?>
<mdscript name="DrJele_AbandonedShipsCleanupOptions" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="md.xsd">
    <cues>

        <cue name="RegisterOptions" instantiate="true" version="1">
            <conditions>
                <event_cue_signalled cue="md.Simple_Menu_Options.Reloaded"/>
            </conditions>
            <actions>
                <signal_cue_instantly
                        cue="md.Simple_Menu_Options.Register_Option"
                        param="table[
            $id        = 'drjele_abandoned_ship_lifetime_min',
            $category  = 'Abandoned Ships Cleanup',
            $name      = 'Abandoned ship lifetime',
            $mouseover = 'Minutes an abandoned ship survives after the mod first sees it. Game time, so SETA speeds it up.',
            $type      = 'slidercell',
            $default   = 300,
            $args      = table[$min = 5, $max = 300, $step = 5, $suffix = ' min'],
            $callback  = OnLifetimeChanged,
            ]"/>
                <signal_cue_instantly
                        cue="md.Simple_Menu_Options.Register_Option"
                        param="table[
            $id        = 'drjele_abandoned_ship_debug',
            $category  = 'Abandoned Ships Cleanup',
            $name      = 'Debug logging',
            $mouseover = 'Writes every scan and every destruction to the debug log.',
            $type      = 'button',
            $default   = 0,
            $callback  = OnDebugChanged,
            ]"/>
            </actions>
        </cue>

        <!-- The slider is in plain minutes; useTimeFormat would relabel them as hours. -->
        <cue name="OnLifetimeChanged" instantiate="true">
            <conditions>
                <event_cue_signalled/>
            </conditions>
            <actions>
                <set_value name="global.$DrJeleAbandonedShipLifetime" exact="event.param.$value * 1min"/>
            </actions>
        </cue>

        <cue name="OnDebugChanged" instantiate="true">
            <conditions>
                <event_cue_signalled/>
            </conditions>
            <actions>
                <set_value name="global.$DrJeleAbandonedShipDebugChance" exact="if event.param.$value then 100 else 0"/>
            </actions>
        </cue>

    </cues>
</mdscript>
<?xml version="1.0" encoding="utf-8"?>
<content id="" name="" description="" author="drjele" version="100" date="2026-09-24" save="0" lastupdate="">
  <text language="44" name="Abandoned Ships Cleanup" description="Puts an expiry timer on abandoned (ownerless) ships and destroys them when it runs out, sparing the ships placed at game start, story and mission derelicts, wrecks and anything currently being boarded or salvaged."/>
  <dependency version="900"/>
  <dependency id="ws_2042901274" optional="true" name="SirNukes Mod Support APIs" comment="only for the in-game options menu; the mod works without it"/>
</content>
