diff --git a/.luacheckrc b/.luacheckrc index 2ac2aac..7da9228 100644 --- a/.luacheckrc +++ b/.luacheckrc @@ -105,6 +105,7 @@ stds.creative_mod = { "creative_lab", "creative_mode_defines", "creative_provider_chest", + "creative_thruster", "duplicating_cargo_wagon", "duplicating_chest", "duplicating_chest_util", diff --git a/changelog.txt b/changelog.txt index 42d4673..8710a54 100644 --- a/changelog.txt +++ b/changelog.txt @@ -5,6 +5,9 @@ Date: ???? - Add an admin-only Surface tab. Admins can create a new blank surface by typing a name; the surface immediately appears in the Surface Cheats target list for every connected player allowed to see other surfaces. - Add space-platform creation to the Surface tab (Space Age only). Admins can type a name and pick the planet to orbit; the platform comes up with a hub already placed and its surface immediately appears in the Surface Cheats target list. - Add planet-surface creation to the Surface tab (Space Age only). Admins can pick a planet and create its surface; if the surface already exists the action is a harmless no-op, and any newly created surface immediately appears in the Surface Cheats target list. + - Add a creative wall: an indestructible red wall. Enemies still target and attack it, but it instantly heals back to full and can never be destroyed. It works on any surface, including space platforms, and is enabled by the normal Creative tools' recipe sweep. + - Add a creative thruster (Space Age only): a thruster that refuels itself every tick, keeping a space platform travelling at vanilla speed with no fuel or oxidizer chain to supply. Place it on a platform, set a route, and it travels with no fuel management. + - Add an "Asteroid spawning rate" input to the Game Settings cheats (Space Age only): a global multiplier for how often asteroids spawn around space platforms. Default is 1 (vanilla); set it to 0 to stop asteroids spawning entirely. Applies game-wide, not per surface. Changes: - Move the Surface Cheats menu from the Cheats tab into the Surface tab, grouping it with the surface-creation tools. Bugfixes: diff --git a/control.lua b/control.lua index 773d956..df6f9f6 100644 --- a/control.lua +++ b/control.lua @@ -6,6 +6,7 @@ require("scripts.cheats") require("scripts.configurable-super-boiler") require("scripts.creative-cargo-wagon") require("scripts.creative-chest-util") +require("scripts.creative-thruster") require("scripts.creative-lab") require("scripts.void-lab") require("scripts.duplicating-cargo-wagon") @@ -55,6 +56,13 @@ script.on_event(defines.events.on_player_main_inventory_changed, events.on_playe script.on_event(defines.events.on_entity_logistic_slot_changed, events.on_entity_logistic_slot_changed) script.on_event(defines.events.on_player_trash_inventory_changed, events.on_player_trash_inventory_changed) +-- on_entity_damaged is registered separately with event filters (the creative wall's name), so it +-- must be excluded from the generic filterless loop below - otherwise this loop would re-register +-- it without filters. Phase 3 will OR an impact damage-type filter into this same registration. +script.on_event(defines.events.on_entity_damaged, events.on_entity_damaged, { + { filter = "name", name = creative_mode_defines.names.entities.creative_wall }, +}) + -- Other events. local events_except_on_tick = {} for _, event in pairs(defines.events) do @@ -63,6 +71,7 @@ for _, event in pairs(defines.events) do and event ~= defines.events.on_player_main_inventory_changed and event ~= defines.events.on_entity_logistic_slot_changed and event ~= defines.events.on_player_trash_inventory_changed + and event ~= defines.events.on_entity_damaged then table.insert(events_except_on_tick, event) end diff --git a/data-final-fixes.lua b/data-final-fixes.lua index 2c93c14..ed420a2 100644 --- a/data-final-fixes.lua +++ b/data-final-fixes.lua @@ -321,3 +321,34 @@ for _, name in pairs(finite_resource_names) do data:extend({ resource }) end + +-------------------------------------------------------------- + +-- Creative thruster: a clone of the vanilla Space Age thruster that keeps the normal performance +-- curve (so it produces real thrust at vanilla speed). Its fuel + oxidizer fluidboxes are topped +-- off every tick by scripts/creative-thruster.lua, so the platform travels with no fuel chain to +-- manage. Defined only when Space Age is present (its filtered fluidboxes reference the +-- thruster-fuel / thruster-oxidizer fluids, which only exist in the space-age mod). Done in +-- data-final-fixes so the vanilla "thruster" prototype is guaranteed to exist before we clone it. +if mods["space-age"] and data.raw["thruster"] and data.raw["thruster"]["thruster"] then + local creative_thruster = util.table.deepcopy(data.raw["thruster"]["thruster"]) + creative_thruster.name = creative_mode_defines.names.entities.creative_thruster + -- Keep the vanilla thruster's entity flags. ("spawnable" is an *item* flag and lives on the + -- creative thruster item, letting the remote controller hold it for placement in remote view.) + creative_thruster.flags = { + "placeable-neutral", + "placeable-player", + "player-creation", + "not-rotatable", + } + creative_thruster.minable = { + mining_time = 0.1, + result = creative_mode_defines.names.items.creative_thruster, + } + creative_thruster.fast_replaceable_group = nil + -- The placed thruster keeps its vanilla graphics: its detailed animated body doesn't take a flat + -- tint cleanly (only the integration patch picked it up, leaving a patchy half-red look). The + -- creative variant is instead marked by its red item icon (see prototypes/item.lua), matching the + -- convention used for the mod's other creative entities. + data:extend({ creative_thruster }) +end diff --git a/defines.lua b/defines.lua index 25fe117..316f54d 100644 --- a/defines.lua +++ b/defines.lua @@ -142,6 +142,8 @@ creative_mode_defines.names.entities = { alien_attractor_proxy_medium = creative_mode_defines.name_prefix .. "alien-attractor-proxy-medium", alien_attractor_proxy_large = creative_mode_defines.name_prefix .. "alien-attractor-proxy-large", super_beacon = creative_mode_defines.name_prefix .. "super-beacon", + creative_wall = creative_mode_defines.name_prefix .. "creative-wall", + creative_thruster = creative_mode_defines.name_prefix .. "creative-thruster", } -- Technology names creative_mode_defines.names.technology = { @@ -209,6 +211,8 @@ creative_mode_defines.names.items = { alien_attractor_medium = creative_mode_defines.name_prefix .. "alien-attractor-medium", alien_attractor_large = creative_mode_defines.name_prefix .. "alien-attractor-large", super_beacon = creative_mode_defines.name_prefix .. "super-beacon", + creative_wall = creative_mode_defines.name_prefix .. "creative-wall", + creative_thruster = creative_mode_defines.name_prefix .. "creative-thruster", super_speed_module = creative_mode_defines.name_prefix .. "super-speed-module", super_effectivity_module = creative_mode_defines.name_prefix .. "super-effectivity-module", super_productivity_module = creative_mode_defines.name_prefix .. "super-productivity-module", @@ -294,6 +298,8 @@ creative_mode_defines.names.recipes = { alien_attractor_medium = creative_mode_defines.name_prefix .. "alien-attractor-medium", alien_attractor_large = creative_mode_defines.name_prefix .. "alien-attractor-large", super_beacon = creative_mode_defines.name_prefix .. "super-beacon", + creative_wall = creative_mode_defines.name_prefix .. "creative-wall", + creative_thruster = creative_mode_defines.name_prefix .. "creative-thruster", super_speed_module = creative_mode_defines.name_prefix .. "super-speed-module", super_effectivity_module = creative_mode_defines.name_prefix .. "super-effectivity-module", super_productivity_module = creative_mode_defines.name_prefix .. "super-productivity-module", @@ -680,6 +686,11 @@ creative_mode_defines.names.gui = { enemy_expansion_label = creative_mode_defines.name_prefix .. "enemy-expansion-label", enemy_expansion_on_button = creative_mode_defines.name_prefix .. "enemy-expansion-on-button", enemy_expansion_off_button = creative_mode_defines.name_prefix .. "enemy-expansion-off-button", + asteroid_spawning_rate_container = creative_mode_defines.name_prefix .. "asteroid-spawning-rate-container", + asteroid_spawning_rate_label = creative_mode_defines.name_prefix .. "asteroid-spawning-rate-label", + asteroid_spawning_rate_textfield = creative_mode_defines.name_prefix .. "asteroid-spawning-rate-textfield", + asteroid_spawning_rate_separator = creative_mode_defines.name_prefix .. "asteroid-spawning-rate-separator", + asteroid_spawning_rate_apply_button = creative_mode_defines.name_prefix .. "asteroid-spawning-rate-apply-button", enemy_expansion_min_cooldown_container = creative_mode_defines.name_prefix .. "enemy-expansion-min-cooldown-container", enemy_expansion_min_cooldown_label = creative_mode_defines.name_prefix .. "enemy-expansion-min-cooldown-label", diff --git a/locale/en/base.cfg b/locale/en/base.cfg index 6b4cf0a..30f6255 100644 --- a/locale/en/base.cfg +++ b/locale/en/base.cfg @@ -48,6 +48,8 @@ creative-mod_alien-attractor-proxy-small=Small alien attractor creative-mod_alien-attractor-proxy-medium=Medium alien attractor creative-mod_alien-attractor-proxy-large=Large alien attractor creative-mod_super-beacon=Super beacon +creative-mod_creative-wall=Creative wall +creative-mod_creative-thruster=Creative thruster creative-mode_enemy-object=__1__ (Creative Mode) creative-mode_infinite-resource=__1__ (Infinite) @@ -99,6 +101,8 @@ creative-mod_passive-energy-void=Constantly absorbs exceeded energy from the net creative-mod_super-radar=Keeps the large area around it to be always revealed. Doesn't really need energy despite the warning signal.\n(Placing it before the sectors are generated may interrupt the generation process and cause the whole map to be blacked out temporarily.) creative-mod_super-radar-2=Keeps the extremely large area around it to be always revealed. Doesn't really need energy despite the warning signal.\n(Placing it before the sectors are generated may interrupt the generation process, causing lag and the whole map to be blacked out temporarily.\nUsually it takes a few minutes to generate sections for such a large area.) creative-mod_super-beacon=Super large effective area. Also accepts productivity modules. Doesn't really need energy despite the warning signal. +creative-mod_creative-wall=An indestructible wall that cannot be damaged or destroyed. Works on any surface, including space platforms. +creative-mod_creative-thruster=A thruster that refuels itself every tick, keeping a space platform travelling at vanilla speed with no fuel or oxidizer chain to supply. [technology-name] creative-mod_void-technology=Void Technology @@ -310,6 +314,7 @@ creative-mode_enemy-expansion-enabled=__1__ has enabled enemy expansion. creative-mode_enemy-expansion-disabled=__1__ has disabled enemy expansion. creative-mode_enemy-expansion-min-cooldown-updated=__1__ has updated the enemies' minimum expansion cooldown to __2__. creative-mode_enemy-expansion-max-cooldown-updated=__1__ has updated the enemies' maximum expansion cooldown to __2__. +creative-mode_asteroid-spawning-rate-updated=__1__ has set the asteroid spawning rate to __2__. creative-mode_game-speed-updated=__1__ has updated game speed to __2__. creative-mode_build-active-enabled-by-admin=__1__ has enabled build options - active for you. @@ -547,6 +552,8 @@ creative-mode_enemy-expansion-min-cooldown=Expansion min CD [?] creative-mode_enemy-expansion-min-cooldown-tooltip=Minimum number of ticks before the enemies expand a base. Default is __1__.\nEffective only when enemy expansion is on. creative-mode_enemy-expansion-max-cooldown=Expansion max CD [?] creative-mode_enemy-expansion-max-cooldown-tooltip=Maximum number of ticks before the enemies expand a base. Default is __1__.\nEffective only when enemy expansion is on. +creative-mode_asteroid-spawning-rate=Asteroid spawning rate [?] +creative-mode_asteroid-spawning-rate-tooltip=Multiplier for how often asteroids spawn around space platforms. Default is __1__. Set to 0 to stop asteroids spawning entirely. Applies game-wide, not per surface. creative-mode_game-speed=Game speed [?] creative-mode_game-speed-tooltip=Speed to update the map. Default is __2__ (60 UPS). Minimum is __1__. diff --git a/prototypes/entity.lua b/prototypes/entity.lua index f9efb00..63167a2 100644 --- a/prototypes/entity.lua +++ b/prototypes/entity.lua @@ -2898,6 +2898,52 @@ data:extend({ }, }) +-- Creative wall: a clone of the base stone-wall. It stays a normal *destructible* target so +-- biters path to it and asteroids hit it, but it is healed back to full on every hit by the +-- on_entity_damaged handler in scripts/events.lua, so it can never be destroyed. (Setting +-- destructible = false was rejected: it removes the entity from the targeting system entirely, +-- so nothing ever attacks it.) The very high max_health is defense-in-depth so no single hit +-- one-shots it before the heal fires. It is a general creative building block, usable on any +-- surface (including space platforms via the item's "spawnable" flag). Not Space-Age gated - +-- it has no Space Age data dependency, so it loads and is craftable everywhere. +local creative_wall = table.deepcopy(data.raw["wall"]["stone-wall"]) +creative_wall.name = creative_mode_defines.names.entities.creative_wall +creative_wall.flags = { "placeable-player", "player-creation" } +creative_wall.minable = { + mining_time = 0.2, + result = creative_mode_defines.names.items.creative_wall, +} +creative_wall.fast_replaceable_group = nil +creative_wall.se_allow_in_space = true +creative_wall.max_health = 1000000 +-- Tint the wall red to match the mod's other creative entities. Walls use a deeply nested +-- WallPictures structure, so recursively tint every non-shadow sprite layer. +local creative_entity_tint = { r = 1, g = 0.3, b = 0.3 } +local function apply_creative_tint(sprite) + if type(sprite) ~= "table" then + return + end + if sprite.layers then + for _, layer in pairs(sprite.layers) do + apply_creative_tint(layer) + end + return + end + if sprite.filename then + -- Leave shadows untinted; only tint the visible sprite layers. + if not sprite.draw_as_shadow then + sprite.tint = table.deepcopy(creative_entity_tint) + sprite.apply_runtime_tint = false + end + return + end + for _, value in pairs(sprite) do + apply_creative_tint(value) + end +end +apply_creative_tint(creative_wall.pictures) +data:extend({ creative_wall }) + -- Vanilla infinity chest already exists but has no description, so we add one. data.raw["infinity-container"]["infinity-chest"].localised_description = { "entity-description.infinity-chest" } -- It also needs it's gui mode set to match our setting. diff --git a/prototypes/item.lua b/prototypes/item.lua index 4006a86..37c3dce 100644 --- a/prototypes/item.lua +++ b/prototypes/item.lua @@ -741,6 +741,30 @@ data:extend({ place_result = creative_mode_defines.names.entities.super_beacon, stack_size = 50, }, + { + -- Creative wall + type = "item", + name = creative_mode_defines.names.items.creative_wall, + icon_size = 64, + -- Tinted red to match the mod's other creative entities. + icons = { + { + icon = "__base__/graphics/icons/wall.png", + tint = { + r = 1, + g = 0.3, + b = 0.3, + }, + }, + }, + hidden = hidden, + subgroup = creative_mode_defines.names.item_subgroups.advanced, + order = "g", + place_result = creative_mode_defines.names.entities.creative_wall, + stack_size = 50, + -- "spawnable" lets the remote controller hold the item, so it can be placed in remote view (e.g. on space platforms). + flags = { "spawnable" }, + }, { -- Super speed module. type = "module", @@ -866,3 +890,34 @@ data:extend({ stack_size = 50, }, }) + +-- Creative thruster item. Defined only when Space Age is present, since its place_result entity +-- (the creative thruster) only exists with Space Age. +if mods["space-age"] then + data:extend({ + { + -- Creative thruster + type = "item", + name = creative_mode_defines.names.items.creative_thruster, + icon_size = 64, + -- Tinted red to match the mod's other creative entities. + icons = { + { + icon = "__space-age__/graphics/icons/thruster.png", + tint = { + r = 1, + g = 0.3, + b = 0.3, + }, + }, + }, + hidden = hidden, + subgroup = creative_mode_defines.names.item_subgroups.advanced, + order = "h", + place_result = creative_mode_defines.names.entities.creative_thruster, + stack_size = 50, + -- "spawnable" lets the remote controller hold the item, so it can be placed in remote view on space platforms. + flags = { "spawnable" }, + }, + }) +end diff --git a/prototypes/recipe.lua b/prototypes/recipe.lua index 29b47c1..afed19b 100644 --- a/prototypes/recipe.lua +++ b/prototypes/recipe.lua @@ -459,6 +459,14 @@ data:extend({ results = { { type = "item", name = creative_mode_defines.names.items.super_beacon, amount = 1 } }, enabled = false, }, + { + -- Creative wall + type = "recipe", + name = creative_mode_defines.names.recipes.creative_wall, + ingredients = {}, + results = { { type = "item", name = creative_mode_defines.names.items.creative_wall, amount = 1 } }, + enabled = false, + }, { -- Super speed module type = "recipe", @@ -544,3 +552,18 @@ data:extend({ enabled = false, }, }) + +-- Creative thruster recipe. Defined only when Space Age is present, since its result item only +-- exists with Space Age. It is auto-enabled at runtime by the creative_tools_recipes sweep. +if mods["space-age"] then + data:extend({ + { + -- Creative thruster + type = "recipe", + name = creative_mode_defines.names.recipes.creative_thruster, + ingredients = {}, + results = { { type = "item", name = creative_mode_defines.names.items.creative_thruster, amount = 1 } }, + enabled = false, + }, + }) +end diff --git a/scripts/cheats.lua b/scripts/cheats.lua index 1b03e41..3e74156 100644 --- a/scripts/cheats.lua +++ b/scripts/cheats.lua @@ -1919,6 +1919,28 @@ cheats.global_cheats_data = { end, get_player_can_access_function = nil, }, + asteroid_spawning_rate = { + is_default = false, + default_enable_value = 1, + default_disable_value = 1, + get_value_function = function(target) + return game.map_settings.asteroids.spawning_rate + end, + limit_value_before_apply_function = function(value) + -- Rate is a multiplier; clamp to a non-negative, safe range. 0 = no asteroids, 1 = vanilla. + return util.clamp(value, 0, 4294967296) + end, + apply_to_target_function = function(target, value, source_player) + game.map_settings.asteroids.spawning_rate = value + return nil + end, + print_applied_by_admin_message_function = function(source_player, target, value) + game.print({ "message.creative-mode_asteroid-spawning-rate-updated", source_player.name, value }) + end, + get_player_can_access_function = function(player) + return script.feature_flags["space_travel"] == true + end, + }, game_speed = { is_default = false, default_enable_value = 1, diff --git a/scripts/creative-thruster.lua b/scripts/creative-thruster.lua new file mode 100644 index 0000000..55e5360 --- /dev/null +++ b/scripts/creative-thruster.lua @@ -0,0 +1,35 @@ +-- This file contains variables or functions that are related to the Creative Thruster in this mod. +if not creative_thruster then + creative_thruster = {} +end + +-- The creative thruster is cloned from the vanilla Space Age thruster, which has exactly two +-- filtered fluidboxes: fuel (thruster-fuel) and oxidizer (thruster-oxidizer), each with a volume +-- of 1000. The thruster entity does not expose a writable `fluidbox` property, so we top the two +-- fluids up via insert_fluid every tick (the engine clamps inserts at the per-fluid capacity). +local thruster_fluids = { + { name = "thruster-fuel", capacity = 1000 }, + { name = "thruster-oxidizer", capacity = 1000 }, +} + +-- Processes the table of creative_thruster in storage. +function creative_thruster.tick() + -- Loop through the table of creative thrusters and top off fuel + oxidizer to capacity, so the + -- thruster always has fuel and the platform travels with no fuel chain to supply. + -- Iterate backwards so table.remove of invalid entries doesn't skip elements. + for index = #storage.creative_mode.creative_thruster, 1, -1 do + local entity = storage.creative_mode.creative_thruster[index] + if entity.valid then + if not entity.to_be_deconstructed() then + for _, fluid in ipairs(thruster_fluids) do + local missing = fluid.capacity - entity.get_fluid_count(fluid.name) + if missing > 0 then + entity.insert_fluid({ name = fluid.name, amount = missing }) + end + end + end + else + table.remove(storage.creative_mode.creative_thruster, index) + end + end +end diff --git a/scripts/events.lua b/scripts/events.lua index e20804f..f79cea3 100644 --- a/scripts/events.lua +++ b/scripts/events.lua @@ -321,6 +321,7 @@ function events.on_tick() void_cargo_wagon.tick() fluid_void.tick() super_boiler.tick() + creative_thruster.tick() super_cooler.tick() configurable_super_boiler.tick() item_source.tick() @@ -1426,3 +1427,18 @@ end function events.on_player_trash_inventory_changed(data) handle_player_trash_inventory_changed(data) end + +-- The mod's single on_entity_damaged handler. It is registered in control.lua with combined +-- event filters (the creative wall's name, plus - once Phase 3 lands - impact damage) so it only +-- fires for the cases we care about. Branches: +-- * Creative wall: healed back to full on every hit so it can never be destroyed while still +-- being a normal attack target. Ungated - works on vanilla. +function events.on_entity_damaged(event) + local entity = event.entity + if not entity.valid then + return + end + if entity.name == creative_mode_defines.names.entities.creative_wall then + entity.health = entity.max_health + end +end diff --git a/scripts/global-util.lua b/scripts/global-util.lua index c05d448..ca70802 100644 --- a/scripts/global-util.lua +++ b/scripts/global-util.lua @@ -78,6 +78,9 @@ function global_util.initialize_or_update_global() if not storage.creative_mode.super_boiler then storage.creative_mode.super_boiler = {} end + if not storage.creative_mode.creative_thruster then + storage.creative_mode.creative_thruster = {} + end if not storage.creative_mode.super_cooler then storage.creative_mode.super_cooler = {} end @@ -712,6 +715,9 @@ local register_entity_look_up_functions = { [creative_mode_defines.names.entities.super_boiler] = function(entity) table.insert(storage.creative_mode.super_boiler, entity) end, + [creative_mode_defines.names.entities.creative_thruster] = function(entity) + table.insert(storage.creative_mode.creative_thruster, entity) + end, [creative_mode_defines.names.entities.super_cooler] = function(entity) table.insert(storage.creative_mode.super_cooler, entity) end, diff --git a/scripts/gui-menu-cheats.lua b/scripts/gui-menu-cheats.lua index c116031..1da5161 100644 --- a/scripts/gui-menu-cheats.lua +++ b/scripts/gui-menu-cheats.lua @@ -1265,6 +1265,18 @@ local global_cheats_menu_gui_data = { off_button_name = creative_mode_defines.names.gui.enemy_expansion_off_button, off_button_caption = creative_mode_defines.names.gui_captions.off, }, + asteroid_spawning_rate = { + type = gui_menu_cheats.cheat_gui_type.numeric_apply, + cheat_data = cheats.global_cheats_data.cheats.asteroid_spawning_rate, + container_name = creative_mode_defines.names.gui.asteroid_spawning_rate_container, + label_name = creative_mode_defines.names.gui.asteroid_spawning_rate_label, + label_caption = { "gui.creative-mode_asteroid-spawning-rate" }, + label_tooltip = { "gui.creative-mode_asteroid-spawning-rate-tooltip", 1 }, + textfield_name = creative_mode_defines.names.gui.asteroid_spawning_rate_textfield, + separator_name = creative_mode_defines.names.gui.asteroid_spawning_rate_separator, + apply_button_name = creative_mode_defines.names.gui.asteroid_spawning_rate_apply_button, + apply_button_caption = creative_mode_defines.names.gui_captions.ok, + }, enemy_expansion_min_cooldown = { type = gui_menu_cheats.cheat_gui_type.numeric_apply, cheat_data = cheats.global_cheats_data.cheats.enemy_expansion_min_cooldown, diff --git a/verify.py b/verify.py index 3fe7f65..1186efc 100644 --- a/verify.py +++ b/verify.py @@ -402,7 +402,65 @@ def cmd_behavior(args: argparse.Namespace) -> int: "1", "create_planet_surface_no_duplicate", ), + # creative_wall_indestructible: place the creative wall, then deal a large damage hit. + # The wall stays a normal *destructible* target, but the mod's on_entity_damaged handler + # heals it back to full, so it survives and ends at max_health. (Damage is well below + # max_health, matching how the wall actually survives in play: it heals every hit.) + _assert_rcon( + sb, + '/c local surf = game.surfaces["nauvis"] ' + 'local e = surf.create_entity{name="creative-mod_creative-wall", position={200, 200}, force="player", raise_built=true} ' + 'e.damage(5000, game.forces.player, "impact") ' + "rcon.print(tostring(e.valid and e.destructible == true and e.health == e.max_health))", + "true", + "creative_wall_indestructible", + ), + # creative_thruster_placed: place the creative thruster on the platform surface, with + # raise_built=true so the mod registers it into the tick loop. Stash the entity globally + # so a later command (after the server has ticked) can read its fluidboxes. + _assert_rcon( + sb, + '/c local s = nil for _, surf in pairs(game.surfaces) do if surf.platform and surf.platform.name == "cm_platform" then s = surf end end ' + 'local e = s.create_entity{name="creative-mod_creative-thruster", position=s.platform.hub.position, force="player", raise_built=true} ' + "storage = storage or {} storage.cm_verify_thruster = e " + "rcon.print(tostring(e ~= nil and e.valid))", + "true", + "creative_thruster_placed", + ), + # asteroid_spawning_rate_set_zero: setting the rate to 0 stops new asteroids spawning + # anywhere. Mirrors the numeric cheat's apply_to_target_function(value=0). + _assert_rcon( + sb, + "/c game.map_settings.asteroids.spawning_rate = 0 " + "rcon.print(tostring(game.map_settings.asteroids.spawning_rate == 0))", + "true", + "asteroid_spawning_rate_set_zero", + ), + # asteroid_spawning_rate_restore: setting it back to the vanilla rate of 1 sticks. + _assert_rcon( + sb, + "/c game.map_settings.asteroids.spawning_rate = 1 " + "rcon.print(tostring(game.map_settings.asteroids.spawning_rate == 1))", + "true", + "asteroid_spawning_rate_restore", + ), ] + # Let the server tick so the per-tick refill runs on the just-placed thruster. + time.sleep(1.0) + results.append( + # creative_thruster_refuels: the thruster's fuel + oxidizer start empty; the per-tick + # refill tops both up to capacity (1000 each), proving the platform can travel with no + # player-supplied fuel chain. + _assert_rcon( + sb, + "/c local e = storage.cm_verify_thruster " + "if not (e and e.valid) then rcon.print('no-entity') return end " + "rcon.print(tostring(e.get_fluid_count('thruster-fuel') >= 1000 " + "and e.get_fluid_count('thruster-oxidizer') >= 1000))", + "true", + "creative_thruster_refuels", + ) + ) finally: _terminate_server(server) @@ -422,6 +480,11 @@ def cmd_behavior(args: argparse.Namespace) -> int: "create_planet_surface_exists", "create_planet_surface_noop", "create_planet_surface_no_duplicate", + "creative_wall_indestructible", + "creative_thruster_placed", + "asteroid_spawning_rate_set_zero", + "asteroid_spawning_rate_restore", + "creative_thruster_refuels", ), results, )