From 8cb528264b5bd8227a3bdab6e97d10a31993e1ab Mon Sep 17 00:00:00 2001 From: Anuken Date: Sun, 7 Sep 2025 14:14:24 -0400 Subject: [PATCH] New system for Serpulo sector loss (WIP) --- core/src/mindustry/core/Control.java | 105 ++++++++++-------- core/src/mindustry/core/World.java | 14 +-- .../mindustry/editor/MapProcessorsDialog.java | 2 +- .../editor/SectorGenerateDialog.java | 6 +- .../mindustry/entities/comp/BuilderComp.java | 33 +++++- .../mindustry/entities/comp/BuildingComp.java | 6 + .../mindustry/entities/units/BuildPlan.java | 6 + core/src/mindustry/game/SectorInfo.java | 3 - core/src/mindustry/maps/SectorDamage.java | 4 +- .../maps/generators/BasicGenerator.java | 2 +- .../maps/generators/BlankPlanetGenerator.java | 6 +- .../maps/generators/FileMapGenerator.java | 28 ++++- .../maps/generators/PlanetGenerator.java | 8 +- .../maps/generators/WorldGenerator.java | 2 +- .../ui/dialogs/IconSelectDialog.java | 1 - .../mindustry/ui/dialogs/PlanetDialog.java | 6 +- core/src/mindustry/world/Build.java | 21 ++-- core/src/mindustry/world/WorldParams.java | 11 ++ .../world/blocks/defense/turrets/Turret.java | 8 +- tests/src/test/java/ApplicationTests.java | 2 +- 20 files changed, 176 insertions(+), 98 deletions(-) create mode 100644 core/src/mindustry/world/WorldParams.java diff --git a/core/src/mindustry/core/Control.java b/core/src/mindustry/core/Control.java index 90bf9be3c6..daca09fe30 100644 --- a/core/src/mindustry/core/Control.java +++ b/core/src/mindustry/core/Control.java @@ -20,6 +20,7 @@ import mindustry.game.*; import mindustry.game.EventType.*; import mindustry.game.Objectives.*; import mindustry.game.Saves.*; +import mindustry.game.Teams.*; import mindustry.gen.*; import mindustry.input.*; import mindustry.io.*; @@ -419,7 +420,7 @@ public class Control implements ApplicationListener, Loadable{ ui.planet.hide(); SaveSlot slot = sector.save; sector.planet.setLastSector(sector); - if(slot != null && !clearSectors && (!(sector.planet.clearSectorOnLose || sector.info.hasWorldProcessor) || sector.info.hasCore)){ + if(slot != null && !clearSectors && (!sector.planet.clearSectorOnLose || sector.info.hasCore)){ try{ boolean hadNoCore = !sector.info.hasCore; @@ -433,7 +434,7 @@ public class Control implements ApplicationListener, Loadable{ //if there is no base, simulate a new game and place the right loadout at the spawn position if(state.rules.defaultTeam.cores().isEmpty() || hadNoCore){ - if(sector.planet.clearSectorOnLose || sector.info.hasWorldProcessor){ + if(sector.planet.clearSectorOnLose){ playNewSector(origin, sector, reloader); }else{ //no spawn set -> delete the sector save @@ -446,61 +447,63 @@ public class Control implements ApplicationListener, Loadable{ return; } + int spawnPos = sector.info.spawnPosition; + //set spawn for sector damage to use - Tile spawn = world.tile(sector.info.spawnPosition); + Tile spawn = world.tile(spawnPos); spawn.setBlock(sector.planet.defaultCore, state.rules.defaultTeam); - //add extra damage. + //apply damage to simulate the sector being lost SectorDamage.apply(1f); - //reset wave so things are more fair - state.wave = 1; - //set up default wave time - state.wavetime = state.rules.initialWaveSpacing <= 0f ? (state.rules.waveSpacing * (sector.preset == null ? 2f : sector.preset.startWaveTimeMultiplier)) : state.rules.initialWaveSpacing; - state.wavetime *= sector.planet.campaignRules.difficulty.waveTimeMultiplier; - //reset captured state - sector.info.wasCaptured = false; + //save the plans and buildings from the previous save; they will be used to re-populate the sector + var previousPlans = state.rules.defaultTeam.data().plans.toArray(BlockPlan.class); + var previousBuildings = state.rules.defaultTeam.data().buildings.toArray(Building.class); + var previousDerelicts = Team.derelict.data().buildings.toArray(Building.class); - if(state.rules.sector.planet.allowWaves){ - //re-enable waves - state.rules.waves = true; - //reset win wave?? - state.rules.winWave = state.rules.attackMode ? -1 : sector.preset != null && sector.preset.captureWave > 0 ? sector.preset.captureWave : state.rules.winWave > state.wave ? state.rules.winWave : 30; - } + logic.reset(); - //if there's still an enemy base left, fix it - if(state.rules.attackMode){ - //replace all broken blocks - for(var plan : state.rules.waveTeam.data().plans){ - Tile tile = world.tile(plan.x, plan.y); - if(tile != null){ - tile.setBlock(plan.block, state.rules.waveTeam, plan.rotation); - if(plan.config != null && tile.build != null){ - tile.build.configureAny(plan.config); - } + //now, load a fresh save; the old one was only used to grab previous building data + playNewSector(origin, sector, reloader, new WorldParams(){{ + corePositionOverride = spawnPos; + }}, () -> { + var teamData = state.rules.defaultTeam.data(); + + //all the old derelicts have to be removed. + for(var generatedDerelict : Team.derelict.data().buildings.toArray(Building.class)){ + generatedDerelict.tile.remove(); + } + + //retain old derelicts from the previous save. + for(var build : previousDerelicts){ + Tile tile = world.tile(build.tileX(), build.tileY()); + if(tile != null && tile.build == null && Build.validPlace(build.block, Team.derelict, build.tileX(), build.tileY(), build.rotation, false, false)){ + tile.setBlock(build.block, Team.derelict, build.rotation, () -> build); } } - state.rules.waveTeam.data().plans.clear(); - } - //kill all units, since they should be dead anyway - Groups.unit.clear(); - Groups.fire.clear(); - Groups.puddle.clear(); + //copy over all buildings from the previous save, retaining config and health, and making them derelict. contents are not saved (derelict repair clears them anyway) + for(var build : previousBuildings){ + Tile tile = world.tile(build.tileX(), build.tileY()); + if(tile != null && tile.build == null && Build.validPlace(build.block, state.rules.defaultTeam, build.tileX(), build.tileY(), build.rotation, false, false)){ + tile.setBlock(build.block, state.rules.defaultTeam, build.rotation, () -> build); + build.changeTeam(Team.derelict); + build.dropped(); //TODO: call pickedUp too? this may screw up power networks in a major way as they refer to potentially deleted entities + } + } - //reset to 0, so replaced cores don't count - state.rules.defaultTeam.data().unitCap = 0; - Schematics.placeLaunchLoadout(spawn.x, spawn.y); + for(var build : previousBuildings){ + build.updateProximity(); + } - //set up camera/player locations - player.set(spawn.x * tilesize, spawn.y * tilesize); - camera.position.set(player); - - Events.fire(new SectorLaunchEvent(sector)); - Events.fire(Trigger.newGame); - - state.set(State.playing); - reloader.end(); + //carry over all previous plans that don't already have the corresponding block at their position + for(var plan : previousPlans){ + var build = world.build(plan.x, plan.y); + if(!(build != null && build.block == plan.block && build.tileX() == plan.x && build.tileY() == plan.y && build.team != state.rules.waveTeam)){ + teamData.plans.add(plan); + } + } + }); } }else{ state.set(State.playing); @@ -522,12 +525,20 @@ public class Control implements ApplicationListener, Loadable{ } public void playNewSector(@Nullable Sector origin, Sector sector, WorldReloader reloader){ + playNewSector(origin, sector, reloader, new WorldParams(), null); + } + + public void playNewSector(@Nullable Sector origin, Sector sector, WorldReloader reloader, WorldParams params, @Nullable Runnable beforePlay){ reloader.begin(); - world.loadSector(sector); + world.loadSector(sector, params); state.rules.sector = sector; - //assign origin when launching sector.info.origin = origin; sector.info.destination = origin; + + if(beforePlay != null){ + beforePlay.run(); + } + logic.play(); control.saves.saveSector(sector); Events.fire(new SectorLaunchEvent(sector)); diff --git a/core/src/mindustry/core/World.java b/core/src/mindustry/core/World.java index 1ad6f1176d..4775af7cbc 100644 --- a/core/src/mindustry/core/World.java +++ b/core/src/mindustry/core/World.java @@ -259,19 +259,19 @@ public class World{ } public void loadSector(Sector sector){ - loadSector(sector, 0, true); + loadSector(sector, new WorldParams()); } - public void loadSector(Sector sector, int seedOffset, boolean saveInfo){ - setSectorRules(sector, saveInfo); + public void loadSector(Sector sector, WorldParams params){ + setSectorRules(sector, params.saveInfo); int size = sector.getSize(); loadGenerator(size, size, tiles -> { if(sector.preset != null){ - sector.preset.generator.generate(tiles); + sector.preset.generator.generate(tiles, params); sector.preset.rules.get(state.rules); //apply extra rules }else if(sector.planet.generator != null){ - sector.planet.generator.generate(tiles, sector, seedOffset); + sector.planet.generator.generate(tiles, sector, params); }else{ throw new RuntimeException("Sector " + sector.id + " on planet " + sector.planet.name + " has no generator or preset defined. Provide a planet generator or preset map."); } @@ -279,7 +279,7 @@ public class World{ state.rules.sector = sector; }); - if(saveInfo && state.rules.waves){ + if(params.saveInfo && state.rules.waves){ sector.info.waves = state.rules.waves; } @@ -289,7 +289,7 @@ public class World{ } //reset rules - setSectorRules(sector, saveInfo); + setSectorRules(sector, params.saveInfo); if(state.rules.defaultTeam.core() != null){ sector.info.spawnPosition = state.rules.defaultTeam.core().pos(); diff --git a/core/src/mindustry/editor/MapProcessorsDialog.java b/core/src/mindustry/editor/MapProcessorsDialog.java index 9c687f08a8..ea24aa0243 100644 --- a/core/src/mindustry/editor/MapProcessorsDialog.java +++ b/core/src/mindustry/editor/MapProcessorsDialog.java @@ -143,7 +143,7 @@ public class MapProcessorsDialog extends BaseDialog{ processors.clear(); - //scan the entire world for processor (Groups.build can be empty, indexer is probably inaccurate) + //scan the entire world for processors (Groups.build can be empty, indexer is probably inaccurate) Vars.world.tiles.eachTile(t -> { if(t.isCenter() && t.block() == Blocks.worldProcessor){ processors.add(t.build); diff --git a/core/src/mindustry/editor/SectorGenerateDialog.java b/core/src/mindustry/editor/SectorGenerateDialog.java index 4d0b8bcb37..0de57b6313 100644 --- a/core/src/mindustry/editor/SectorGenerateDialog.java +++ b/core/src/mindustry/editor/SectorGenerateDialog.java @@ -7,6 +7,7 @@ import mindustry.graphics.*; import mindustry.type.*; import mindustry.ui.*; import mindustry.ui.dialogs.*; +import mindustry.world.*; import static mindustry.Vars.*; @@ -94,7 +95,10 @@ public class SectorGenerateDialog extends BaseDialog{ sectorobj.preset = null; logic.reset(); //TODO: is this a good idea? all rules and map state are cleared, but it fixes inconsistent gen - world.loadSector(sectorobj, seed, false); + var params = new WorldParams(); + params.seedOffset = seed; + params.saveInfo = false; + world.loadSector(sectorobj, params); sectorobj.preset = preset; diff --git a/core/src/mindustry/entities/comp/BuilderComp.java b/core/src/mindustry/entities/comp/BuilderComp.java index 3ab723995c..09026d223f 100644 --- a/core/src/mindustry/entities/comp/BuilderComp.java +++ b/core/src/mindustry/entities/comp/BuilderComp.java @@ -114,12 +114,35 @@ abstract class BuilderComp implements Posc, Statusc, Teamc, Rotc{ if(plans.size > 1){ int total = 0; int size = plans.size; - BuildPlan plan; - while((!within((plan = buildPlan()).tile(), finalPlaceDst) || shouldSkip(plan, core)) && total < size){ + float bestDst = Float.MAX_VALUE; + boolean foundAny = false; + int bestIndex = -1; + while(total < size){ + var plan = buildPlan(); + + float dst = plan.dst2(this); + boolean within = dst <= finalPlaceDst*finalPlaceDst; + //if it's a valid plan within range, break out of the loop + if(within && !shouldSkip(plan, core)){ + foundAny = true; + break; + }else if(within && dst < bestDst){ //it's still bad, but at least it's within build radius + bestIndex = total; + bestDst = dst; + } + plans.removeFirst(); plans.addLast(plan); total++; } + + //all the plans were useless, and the current one can't be reached. skip to the closest one, if applicable + if(!foundAny && bestIndex > 0 && !within(buildPlan(), finalPlaceDst)){ + //this is slow, but should be rare in practice + for(int i = 0; i < bestIndex; i++){ + plans.addLast(plans.removeFirst()); + } + } } BuildPlan current = buildPlan(); @@ -236,10 +259,10 @@ abstract class BuilderComp implements Posc, Statusc, Teamc, Rotc{ /** @return whether this plan should be skipped, in favor of the next one. */ boolean shouldSkip(BuildPlan plan, @Nullable Building core){ - //plans that you have at least *started* are considered - if(state.rules.infiniteResources || team.rules().infiniteResources || plan.breaking || core == null || plan.isRotation(team) || (isBuilding() && !within(plans.last(), type.buildRange))) return false; + if(state.rules.infiniteResources || team.rules().infiniteResources || plan.breaking || core == null || plan.isRotation(team) || plan.isDerelictRepair()) return false; - return (plan.stuck && !core.items.has(plan.block.requirements)) || (Structs.contains(plan.block.requirements, i -> !core.items.has(i.item, Math.min(i.amount, 15)) && Mathf.round(i.amount * state.rules.buildCostMultiplier) > 0) && !plan.initialized); + return (plan.stuck && !core.items.has(plan.block.requirements)) || + (Structs.contains(plan.block.requirements, i -> !core.items.has(i.item, Math.min(i.amount, 15)) && Mathf.round(i.amount * state.rules.buildCostMultiplier) > 0)); } void removeBuild(int x, int y, boolean breaking){ diff --git a/core/src/mindustry/entities/comp/BuildingComp.java b/core/src/mindustry/entities/comp/BuildingComp.java index 78c14a2ffe..1da4abd811 100644 --- a/core/src/mindustry/entities/comp/BuildingComp.java +++ b/core/src/mindustry/entities/comp/BuildingComp.java @@ -1333,6 +1333,12 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc, } } + /** Called when this block is derelict repaired. */ + @CallSuper + public void onRepaired(){ + placed(); + } + public boolean isCommandable(){ return block.commandable; } diff --git a/core/src/mindustry/entities/units/BuildPlan.java b/core/src/mindustry/entities/units/BuildPlan.java index 8dfb53de7a..1949aa1c72 100644 --- a/core/src/mindustry/entities/units/BuildPlan.java +++ b/core/src/mindustry/entities/units/BuildPlan.java @@ -74,6 +74,12 @@ public class BuildPlan implements Position, QuadTreeObject{ return tile != null && tile.team() == team && tile.block() == block && tile.build != null && tile.build.rotation != rotation; } + public boolean isDerelictRepair(){ + if(breaking || !state.rules.derelictRepair) return false; + Tile tile = tile(); + return tile != null && tile.team() == Team.derelict && tile.block() == block && tile.build != null; + } + public boolean samePos(BuildPlan other){ return x == other.x && y == other.y; } diff --git a/core/src/mindustry/game/SectorInfo.java b/core/src/mindustry/game/SectorInfo.java index 4366a46d8a..7a393a8784 100644 --- a/core/src/mindustry/game/SectorInfo.java +++ b/core/src/mindustry/game/SectorInfo.java @@ -41,8 +41,6 @@ public class SectorInfo{ public int storageCapacity = 0; /** Whether a core is available here. */ public boolean hasCore = true; - /** Whether a world processor is on this map - implies that the map will get cleared. */ - public boolean hasWorldProcessor; /** Whether this sector was ever fully captured. */ public boolean wasCaptured = false; /** Sector that was launched from. */ @@ -213,7 +211,6 @@ public class SectorInfo{ spawnPosition = entity.pos(); } - hasWorldProcessor = state.teams.present.contains(t -> t.getBuildings(Blocks.worldProcessor).any()); waveSpacing = state.rules.waveSpacing; wave = state.wave; winWave = state.rules.winWave; diff --git a/core/src/mindustry/maps/SectorDamage.java b/core/src/mindustry/maps/SectorDamage.java index c3bea3c0a6..64784b16de 100644 --- a/core/src/mindustry/maps/SectorDamage.java +++ b/core/src/mindustry/maps/SectorDamage.java @@ -483,8 +483,8 @@ public class SectorDamage{ Tile other = tiles.getn(cx, cy); float resultDamage = currDamage; - //damage the tile if it's not friendly - if(other.build != null && other.team() != state.rules.waveTeam){ + //damage the tile if it's the player team (derelict blocks get ignored) + if(other.build != null && other.team() == state.rules.defaultTeam){ resultDamage -= other.build.health(); other.build.health -= currDamage; diff --git a/core/src/mindustry/maps/generators/BasicGenerator.java b/core/src/mindustry/maps/generators/BasicGenerator.java index 3daa39c68f..9ec4023f74 100644 --- a/core/src/mindustry/maps/generators/BasicGenerator.java +++ b/core/src/mindustry/maps/generators/BasicGenerator.java @@ -29,7 +29,7 @@ public abstract class BasicGenerator implements WorldGenerator{ public Schematic defaultLoadout = Loadouts.basicShard; @Override - public void generate(Tiles tiles){ + public void generate(Tiles tiles, WorldParams params){ this.tiles = tiles; this.width = tiles.width; this.height = tiles.height; diff --git a/core/src/mindustry/maps/generators/BlankPlanetGenerator.java b/core/src/mindustry/maps/generators/BlankPlanetGenerator.java index 7b8aeed5ce..08b17459ec 100644 --- a/core/src/mindustry/maps/generators/BlankPlanetGenerator.java +++ b/core/src/mindustry/maps/generators/BlankPlanetGenerator.java @@ -13,14 +13,14 @@ public class BlankPlanetGenerator extends PlanetGenerator{ } @Override - public void generate(Tiles tiles, Sector sec, int seed){ + public void generate(Tiles tiles, Sector sec, WorldParams params){ this.tiles = tiles; this.sector = sec; - this.rand.setSeed(sec.id + seed + baseSeed); + this.rand.setSeed(sec.id + params.seedOffset + baseSeed); tiles.fill(); - generate(tiles); + generate(tiles, params); } } diff --git a/core/src/mindustry/maps/generators/FileMapGenerator.java b/core/src/mindustry/maps/generators/FileMapGenerator.java index da5f39fc8a..c750786e79 100644 --- a/core/src/mindustry/maps/generators/FileMapGenerator.java +++ b/core/src/mindustry/maps/generators/FileMapGenerator.java @@ -9,6 +9,7 @@ import mindustry.maps.*; import mindustry.type.*; import mindustry.world.*; import mindustry.world.blocks.storage.*; +import mindustry.world.blocks.storage.CoreBlock.*; import static mindustry.Vars.*; @@ -38,7 +39,7 @@ public class FileMapGenerator implements WorldGenerator{ } @Override - public void generate(Tiles tiles){ + public void generate(Tiles tiles, WorldParams params){ if(map == null) throw new RuntimeException("Generator has null map, cannot be used."); Sector sector = state.rules.sector; @@ -72,6 +73,9 @@ public class FileMapGenerator implements WorldGenerator{ boolean anyCores = false; + //TODO: unsure if indexer even works at this stage + Block coreTypeToUse = state.rules.defaultTeam.cores().isEmpty() ? sector.planet.defaultCore : state.rules.defaultTeam.core().block; + for(Tile tile : tiles){ if(tile.overlay() == Blocks.spawn){ @@ -83,8 +87,26 @@ public class FileMapGenerator implements WorldGenerator{ }); } - if(tile.isCenter() && tile.block() instanceof CoreBlock && tile.team() == state.rules.defaultTeam && !anyCores){ - if(state.rules.sector != null && state.rules.sector.allowLaunchLoadout()){ + if(params.corePositionOverride != 0 && sector != null){ + if(tile.pos() == params.corePositionOverride){ + if(sector.allowLaunchLoadout()){ + Schematics.placeLaunchLoadout(tile.x, tile.y); + }else{ + //if there's an override and no loadout schematic is allowed, try to place a fitting core instead. + tile.setBlock(coreTypeToUse, state.rules.defaultTeam, 0); + } + anyCores = true; + + if(preset.addStartingItems || !preset.planet.allowLaunchLoadout){ + tile.build.items.clear(); + tile.build.items.add(state.rules.loadout); + } + }else if(tile.build instanceof CoreBuild && tile.build.pos() != params.corePositionOverride){ + //other cores placed must be cleared; they have been overridden + tile.remove(); + } + }else if(tile.isCenter() && tile.block() instanceof CoreBlock && tile.team() == state.rules.defaultTeam && !anyCores){ + if(sector != null && sector.allowLaunchLoadout()){ Schematics.placeLaunchLoadout(tile.x, tile.y); } anyCores = true; diff --git a/core/src/mindustry/maps/generators/PlanetGenerator.java b/core/src/mindustry/maps/generators/PlanetGenerator.java index e8ab32da68..d5a45e747d 100644 --- a/core/src/mindustry/maps/generators/PlanetGenerator.java +++ b/core/src/mindustry/maps/generators/PlanetGenerator.java @@ -178,13 +178,13 @@ public abstract class PlanetGenerator extends BasicGenerator implements HexMeshe return res % 2 == 0 ? res : res + 1; } - public void generate(Tiles tiles, Sector sec, int seed){ + public void generate(Tiles tiles, Sector sec, WorldParams params){ this.tiles = tiles; - this.seed = seed + baseSeed; + this.seed = params.seedOffset + baseSeed; this.sector = sec; this.width = tiles.width; this.height = tiles.height; - this.rand.setSeed(sec.id + seed + baseSeed); + this.rand.setSeed(sec.id + params.seedOffset + baseSeed); TileGen gen = new TileGen(); for(int y = 0; y < height; y++){ @@ -197,6 +197,6 @@ public abstract class PlanetGenerator extends BasicGenerator implements HexMeshe } } - generate(tiles); + generate(tiles, params); } } diff --git a/core/src/mindustry/maps/generators/WorldGenerator.java b/core/src/mindustry/maps/generators/WorldGenerator.java index 71f5c23d34..f352c247b7 100644 --- a/core/src/mindustry/maps/generators/WorldGenerator.java +++ b/core/src/mindustry/maps/generators/WorldGenerator.java @@ -3,7 +3,7 @@ package mindustry.maps.generators; import mindustry.world.*; public interface WorldGenerator{ - void generate(Tiles tiles); + void generate(Tiles tiles, WorldParams params); /** Do not modify tiles here. This is only for specialized configuration. */ default void postGenerate(Tiles tiles){} diff --git a/core/src/mindustry/ui/dialogs/IconSelectDialog.java b/core/src/mindustry/ui/dialogs/IconSelectDialog.java index 33766e6bfb..459fbda0d6 100644 --- a/core/src/mindustry/ui/dialogs/IconSelectDialog.java +++ b/core/src/mindustry/ui/dialogs/IconSelectDialog.java @@ -15,7 +15,6 @@ import static mindustry.Vars.*; public class IconSelectDialog extends Dialog{ private Intc consumer = i -> Log.info("you have mere seconds"); - private boolean allowLocked; public IconSelectDialog(){ this(true); diff --git a/core/src/mindustry/ui/dialogs/PlanetDialog.java b/core/src/mindustry/ui/dialogs/PlanetDialog.java index dcc5a66567..1d6656a7d7 100644 --- a/core/src/mindustry/ui/dialogs/PlanetDialog.java +++ b/core/src/mindustry/ui/dialogs/PlanetDialog.java @@ -1122,10 +1122,8 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{ } }); }else{ - if(sector.save != null){ - sector.save.delete(); - } - sector.save = null; + sector.info.hasCore = false; + sector.saveInfo(); } updateSelected(); rebuildList(); diff --git a/core/src/mindustry/world/Build.java b/core/src/mindustry/world/Build.java index 41ba14c115..9de54662ae 100644 --- a/core/src/mindustry/world/Build.java +++ b/core/src/mindustry/world/Build.java @@ -92,26 +92,21 @@ public class Build{ //repair derelict tile if(tile.team() == Team.derelict && team != Team.derelict && tile.block == result && tile.build != null && tile.block.allowDerelictRepair && state.rules.derelictRepair){ - float healthf = tile.build.healthf(); - var config = tile.build.config(); - - tile.setBlock(result, team, rotation); + tile.build.rotation = rotation; + tile.build.changeTeam(team); + tile.build.enabled = true; + tile.build.checkAllowUpdate(); + tile.build.onRepaired(); if(unit != null && unit.getControllerName() != null) tile.build.lastAccessed = unit.getControllerName(); - if(config != null){ - tile.build.configured(unit, config); - } - //keep health - tile.build.health = result.health * healthf; - if(fogControl.isVisibleTile(team, tile.x, tile.y)){ result.placeEffect.at(tile.drawx(), tile.drawy(), result.size); Fx.rotateBlock.at(tile.build.x, tile.build.y, tile.build.block.size); //doesn't play a sound } - Events.fire(new BlockBuildEndEvent(tile, unit, team, false, config)); + Events.fire(new BlockBuildEndEvent(tile, unit, team, false, tile.build.config())); return; } @@ -168,8 +163,8 @@ public class Build{ } /** @return whether a tile can be placed at this location by this team. */ - public static boolean validPlace(Block type, Team team, int x, int y, int rotation, boolean checkVisible, boolean ignoreCoreRadius){ - return validPlaceIgnoreUnits(type, team, x, y, rotation, checkVisible, ignoreCoreRadius) && checkNoUnitOverlap(type, x, y); + public static boolean validPlace(Block type, Team team, int x, int y, int rotation, boolean checkVisible, boolean checkCoreRadius){ + return validPlaceIgnoreUnits(type, team, x, y, rotation, checkVisible, checkCoreRadius) && checkNoUnitOverlap(type, x, y); } /** @return whether a tile can be placed at this location by this team. */ diff --git a/core/src/mindustry/world/WorldParams.java b/core/src/mindustry/world/WorldParams.java new file mode 100644 index 0000000000..8d5c47bdb2 --- /dev/null +++ b/core/src/mindustry/world/WorldParams.java @@ -0,0 +1,11 @@ +package mindustry.world; + +/** Parameters for loading or generating a world. */ +public class WorldParams{ + /** For sectors: World generator seed offset. */ + public int seedOffset; + /** For sectors: Whether to save the info once the map is generated. A value of 'false' is used for editor generation. */ + public boolean saveInfo = true; + /** Position in packed x/y format - not array format. Overrides the core position when generating with a FileMapGenerator. 0 to disable. */ + public int corePositionOverride; +} diff --git a/core/src/mindustry/world/blocks/defense/turrets/Turret.java b/core/src/mindustry/world/blocks/defense/turrets/Turret.java index fd13645410..2bf9e2c756 100644 --- a/core/src/mindustry/world/blocks/defense/turrets/Turret.java +++ b/core/src/mindustry/world/blocks/defense/turrets/Turret.java @@ -283,6 +283,12 @@ public class Turret extends ReloadTurret{ } } + //overridden so that the rotation isn't affected during repairs (standard placed() code isn't called) + @Override + public void onRepaired(){ + super.placed(); + } + @Override public void remove(){ super.remove(); @@ -519,7 +525,7 @@ public class Turret extends ReloadTurret{ } if(validateTarget()){ - boolean canShoot = true; + boolean canShoot; if(isControlled()){ //player behavior targetPos.set(unit.aimX(), unit.aimY()); diff --git a/tests/src/test/java/ApplicationTests.java b/tests/src/test/java/ApplicationTests.java index 307ed0e636..ed6e6af538 100644 --- a/tests/src/test/java/ApplicationTests.java +++ b/tests/src/test/java/ApplicationTests.java @@ -892,7 +892,7 @@ public class ApplicationTests{ logic.reset(); state.rules.sector = zone.sector; - world.loadGenerator(zone.generator.map.width, zone.generator.map.height, zone.generator::generate); + world.loadGenerator(zone.generator.map.width, zone.generator.map.height, tiles -> zone.generator.generate(tiles, new WorldParams())); zone.rules.get(state.rules); ObjectSet resources = new ObjectSet<>(); boolean hasSpawnPoint = false;