Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion changelog.txt
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
---------------------------------------------------------------------------------------------------
Version: 2.3.0
Date: ????
Date: 2026-06-29
Features:
- 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.
- "Create Surface → Blank surface" now creates a genuinely flat, empty test surface (no resources, water, cliffs, trees, decoratives, or enemies; always day) with a floor dropdown: Lab tiles, Concrete, or Refined concrete.
- Add an "Ignore surface conditions" on/off Surface Cheat that lets planet-gated buildings be placed and planet-gated recipes crafted on the selected surface(s).
Changes:
- Move the Surface Cheats menu from the Cheats tab into the Surface tab, grouping it with the surface-creation tools.
Bugfixes:
Expand Down
6 changes: 6 additions & 0 deletions defines.lua
Original file line number Diff line number Diff line change
Expand Up @@ -629,6 +629,10 @@ creative_mode_defines.names.gui = {
peaceful_mode_label = creative_mode_defines.name_prefix .. "peaceful-mode-label",
peaceful_mode_on_button = creative_mode_defines.name_prefix .. "peaceful-mode-on-button",
peaceful_mode_off_button = creative_mode_defines.name_prefix .. "peaceful-mode-off-button",
ignore_surface_conditions_container = creative_mode_defines.name_prefix .. "ignore-surface-conditions-container",
ignore_surface_conditions_label = creative_mode_defines.name_prefix .. "ignore-surface-conditions-label",
ignore_surface_conditions_on_button = creative_mode_defines.name_prefix .. "ignore-surface-conditions-on-button",
ignore_surface_conditions_off_button = creative_mode_defines.name_prefix .. "ignore-surface-conditions-off-button",
destroy_all_enemies_container = creative_mode_defines.name_prefix .. "destroy-all-enemies-container",
destroy_all_enemies_label = creative_mode_defines.name_prefix .. "destroy-all-enemies-label",
destroy_all_enemies_apply_button = creative_mode_defines.name_prefix .. "destroy-all-enemies-apply-button",
Expand Down Expand Up @@ -1307,6 +1311,8 @@ creative_mode_defines.names.gui = {
surface_blank_container = creative_mode_defines.name_prefix .. "surface-blank-container",
surface_blank_name_label = creative_mode_defines.name_prefix .. "surface-blank-name-label",
surface_blank_name_textfield = creative_mode_defines.name_prefix .. "surface-blank-name-textfield",
surface_blank_floor_label = creative_mode_defines.name_prefix .. "surface-blank-floor-label",
surface_blank_floor_drop_down = creative_mode_defines.name_prefix .. "surface-blank-floor-drop-down",
surface_blank_create_button = creative_mode_defines.name_prefix .. "surface-blank-create-button",
surface_platform_frame = creative_mode_defines.name_prefix .. "surface-platform-frame",
surface_platform_container = creative_mode_defines.name_prefix .. "surface-platform-container",
Expand Down
8 changes: 8 additions & 0 deletions locale/en/base.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,8 @@ creative-mode_daytime-unfrozen=__1__ has unfrozen the daytime on this surface.
creative-mode_daytime-updated=__1__ has set the current daytime on this surface to __2__.
creative-mode_peaceful-mode-enabled=__1__ has enabled peaceful mode on this surface.
creative-mode_peaceful-mode-disabled=__1__ has disabled peaceful mode on this surface.
creative-mode_ignore-surface-conditions-enabled=__1__ has enabled ignore surface conditions on this surface.
creative-mode_ignore-surface-conditions-disabled=__1__ has disabled ignore surface conditions on this surface.
creative-mode_destroy-all-enemies-applied=__1__ has killed all enemies on this surface.
creative-mode_remove-all-enemies-applied=__1__ has removed all enemies on this surface.
creative-mode_dont-generate-enemy-enabled=__1__ has enabled don't generate enemies on this surface.
Expand Down Expand Up @@ -411,6 +413,10 @@ creative-mode_surface-creation-nav-planet=Planet surface
creative-mode_surface-creation-blank-title=Create Blank Surface
creative-mode_surface-creation-blank-name-label=Name
creative-mode_surface-creation-blank-create-button=Create
creative-mode_surface-creation-blank-floor-label=Floor
creative-mode_surface-creation-blank-floor-lab=Lab tiles
creative-mode_surface-creation-blank-floor-concrete=Concrete
creative-mode_surface-creation-blank-floor-refined-concrete=Refined concrete
creative-mode_surface-creation-platform-title=Create Space Platform
creative-mode_surface-creation-platform-name-label=Name
creative-mode_surface-creation-platform-planet-label=Orbit
Expand Down Expand Up @@ -521,6 +527,8 @@ creative-mode_daytime=Daytime [?]
creative-mode_daytime-tooltip=Sets the current time of day.\n0 = __1__\n0.5 = __2__\n1 = __1__
creative-mode_peaceful-mode=Peaceful mode [?]
creative-mode_peaceful-mode-tooltip=Enemy will not attack first. You will have to kill all enemy units in order to refresh their status.
creative-mode_ignore-surface-conditions=Ignore surface conditions [?]
creative-mode_ignore-surface-conditions-tooltip=Disables planet/surface condition checks for building placement and recipe crafting on this surface (e.g. Foundry, Metallurgic science pack, Big mining drill, Turbo transport belt). Note: this only lifts condition gating - it does not add planet effects such as lightning, heating, or spoilage, and lightning rods still cannot be placed.
creative-mode_destroy-all-enemies=Kill all enemies [?]
creative-mode_destroy-all-enemies-tooltip=Kills all enemies, including both units and spawners, in all generated chunks, leaving loots on the ground.\nWarning: executing in a huge surface may result in temporary hang.
creative-mode_remove-all-enemies=Remove all enemies [?]
Expand Down
38 changes: 38 additions & 0 deletions scripts/cheats.lua
Original file line number Diff line number Diff line change
Expand Up @@ -1576,6 +1576,30 @@ cheats.surface_cheats_data = {
end,
get_player_can_access_function = nil,
},
ignore_surface_conditions = {
is_default = false,
default_enable_value = true,
default_disable_value = false,
get_value_function = function(surface)
if surface then
return surface.ignore_surface_conditions
end
return nil
end,
limit_value_before_apply_function = nil,
apply_to_target_function = function(surface, enable, source_player)
surface.ignore_surface_conditions = enable
return nil
end,
print_applied_by_admin_message_function = function(source_player, surface, enable)
if enable then
surface.print({ "message.creative-mode_ignore-surface-conditions-enabled", source_player.name })
else
surface.print({ "message.creative-mode_ignore-surface-conditions-disabled", source_player.name })
end
end,
get_player_can_access_function = nil,
},
destroy_all_enemies = {
is_default = false,
default_enable_value = true,
Expand Down Expand Up @@ -3078,6 +3102,20 @@ end
function cheats.on_chunk_generated(event)
local area = event.area
local surface = event.surface
-- Flat test surface with a concrete / refined-concrete floor: lay the tile over the whole
-- chunk and clear any stray decoratives. (Lab-tile surfaces use the engine flag instead.)
local floor_tile = storage.creative_mode.surface_cheats.flat_test_floor[surface.index]
if floor_tile then
local tiles = {}
local x1, y1 = area.left_top.x, area.left_top.y
for x = x1, area.right_bottom.x - 1 do
for y = y1, area.right_bottom.y - 1 do
tiles[#tiles + 1] = { name = floor_tile, position = { x, y } }
end
end
surface.set_tiles(tiles)
surface.destroy_decoratives({ area = area })
end
-- Surface cheat - don't generate enemy.
if storage.creative_mode.surface_cheats.dont_generate_enemy[surface.index] then
-- Remove the enemies in the on_tick event.
Expand Down
6 changes: 6 additions & 0 deletions scripts/global-util.lua
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,12 @@ function global_util.initialize_or_update_global()
if not storage.creative_mode.surface_cheats.dont_generate_enemy then
storage.creative_mode.surface_cheats.dont_generate_enemy = {}
end
-- Per-surface floor tile for flat test surfaces that need a chunk-generated floor
-- (concrete / refined-concrete). Keyed by surface.index → tile prototype name.
-- Lab-tile surfaces are handled by the engine flag and are not tracked here.
if not storage.creative_mode.surface_cheats.flat_test_floor then
storage.creative_mode.surface_cheats.flat_test_floor = {}
end

-- Table of build options for each player.
if not storage.creative_mode.build_options then
Expand Down
12 changes: 12 additions & 0 deletions scripts/gui-menu-cheats.lua
Original file line number Diff line number Diff line change
Expand Up @@ -938,6 +938,18 @@ local surface_cheats_menu_gui_data = {
off_button_name = creative_mode_defines.names.gui.peaceful_mode_off_button,
off_button_caption = creative_mode_defines.names.gui_captions.off,
},
ignore_surface_conditions = {
type = gui_menu_cheats.cheat_gui_type.on_off,
cheat_data = cheats.surface_cheats_data.cheats.ignore_surface_conditions,
container_name = creative_mode_defines.names.gui.ignore_surface_conditions_container,
label_name = creative_mode_defines.names.gui.ignore_surface_conditions_label,
label_caption = { "gui.creative-mode_ignore-surface-conditions" },
label_tooltip = { "gui.creative-mode_ignore-surface-conditions-tooltip" },
on_button_name = creative_mode_defines.names.gui.ignore_surface_conditions_on_button,
on_button_caption = creative_mode_defines.names.gui_captions.on,
off_button_name = creative_mode_defines.names.gui.ignore_surface_conditions_off_button,
off_button_caption = creative_mode_defines.names.gui_captions.off,
},
destroy_all_enemies = {
type = gui_menu_cheats.cheat_gui_type.apply,
cheat_data = cheats.surface_cheats_data.cheats.destroy_all_enemies,
Expand Down
23 changes: 22 additions & 1 deletion scripts/gui-menu-surface.lua
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,23 @@ local function build_blank_section_content(frame)
name = creative_mode_defines.names.gui.surface_blank_name_textfield,
style = creative_mode_defines.names.gui_styles.cheat_numeric_textfield,
})
blank_container.add({
type = "label",
name = creative_mode_defines.names.gui.surface_blank_floor_label,
style = creative_mode_defines.names.gui_styles.cheat_name_label,
caption = { "gui.creative-mode_surface-creation-blank-floor-label" },
})
-- Floor options, captions ordered to match surface_creation.floor_options.
local floor_items = {}
for i, option in ipairs(surface_creation.floor_options) do
floor_items[i] = { "gui.creative-mode_surface-creation-blank-floor-" .. option.key }
end
blank_container.add({
type = "drop-down",
name = creative_mode_defines.names.gui.surface_blank_floor_drop_down,
items = floor_items,
selected_index = 1,
})
frame.add({
type = "button",
name = creative_mode_defines.names.gui.surface_blank_create_button,
Expand Down Expand Up @@ -336,8 +353,11 @@ function gui_menu_surface.on_gui_click(element, element_name, player, button, al
local blank_container = blank_frame[creative_mode_defines.names.gui.surface_blank_container]
local textfield = blank_container[creative_mode_defines.names.gui.surface_blank_name_textfield]
local name = textfield.text
local floor_drop_down = blank_container[creative_mode_defines.names.gui.surface_blank_floor_drop_down]
local floor_option = surface_creation.floor_options[floor_drop_down.selected_index]
local floor = floor_option and floor_option.key

local success, result = surface_creation.create_blank_surface(name)
local success, result = surface_creation.create_blank_surface(name, floor)
if success then
player.print({ "message.creative-mode_surface-creation-blank-success", result.name })
-- Clear the field for convenience.
Expand Down Expand Up @@ -410,6 +430,7 @@ function gui_menu_surface.on_gui_selection_state_changed(element, element_name,
if
element_name == creative_mode_defines.names.gui.surface_platform_planet_drop_down
or element_name == creative_mode_defines.names.gui.surface_planet_planet_drop_down
or element_name == creative_mode_defines.names.gui.surface_blank_floor_drop_down
then
-- The selected planet is read directly from the drop-down at create time, so nothing to
-- record here. Consume the event so the chain stops.
Expand Down
49 changes: 47 additions & 2 deletions scripts/surface-creation.lua
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,32 @@ end

-- Creates a new blank surface with the given name.
-- The name is trimmed; empty and duplicate names are rejected (game.create_surface errors on a duplicate name).
-- floor selects the floor tile: one of surface_creation.floor_options keys ("lab", "concrete",
-- "refined-concrete"); defaults to "lab" when nil/unknown.
-- Returns:
-- on success: true, surface
-- on failure: false, localised-string error message
function surface_creation.create_blank_surface(name)

-- Floor options offered at flat-surface creation. Order is the dropdown order.
-- tile == nil means "lab tiles" (engine flag, no chunk handler); otherwise tile is the
-- tile prototype name laid by the on_chunk_generated handler.
surface_creation.floor_options = {
{ key = "lab", tile = nil },
{ key = "concrete", tile = "concrete" },
{ key = "refined-concrete", tile = "refined-concrete" },
}

-- Resolves a floor key to its floor_options entry, defaulting to the first (lab tiles).
local function resolve_floor(floor)
for _, option in ipairs(surface_creation.floor_options) do
if option.key == floor then
return option
end
end
return surface_creation.floor_options[1]
end

function surface_creation.create_blank_surface(name, floor)
-- Trim leading/trailing whitespace.
local trimmed_name
if type(name) == "string" then
Expand All @@ -25,11 +47,34 @@ function surface_creation.create_blank_surface(name)
return false, { "message.creative-mode_surface-creation-blank-duplicate-name", trimmed_name }
end

local surface = game.create_surface(trimmed_name)
local floor_option = resolve_floor(floor)

-- Flat MapGenSettings: disable every autoplace (resources/trees/decoratives/enemies),
-- force no enemies / peaceful, no starting area, and zero out cliffs + enemy bases.
local surface = game.create_surface(trimmed_name, {
default_enable_all_autoplace_controls = false,
no_enemies_mode = true,
peaceful_mode = true,
starting_area = "none",
property_expression_names = {
cliffiness = 0,
["enemy-base-intensity"] = 0,
},
})
if not surface then
return false, { "message.creative-mode_surface-creation-blank-failed", trimmed_name }
end

surface.always_day = true

if floor_option.tile == nil then
-- Lab tiles: engine fills each new chunk; no chunk handler required.
surface.generate_with_lab_tiles = true
else
-- Concrete / refined-concrete: record so on_chunk_generated lays the tile + clears decoratives.
storage.creative_mode.surface_cheats.flat_test_floor[surface.index] = floor_option.tile
end

return true, surface
end

Expand Down
Loading