New system for Serpulo sector loss (WIP)

This commit is contained in:
Anuken
2025-09-07 14:14:24 -04:00
parent a20cfcf9ea
commit 8cb528264b
20 changed files with 176 additions and 98 deletions

View File

@@ -20,6 +20,7 @@ import mindustry.game.*;
import mindustry.game.EventType.*; import mindustry.game.EventType.*;
import mindustry.game.Objectives.*; import mindustry.game.Objectives.*;
import mindustry.game.Saves.*; import mindustry.game.Saves.*;
import mindustry.game.Teams.*;
import mindustry.gen.*; import mindustry.gen.*;
import mindustry.input.*; import mindustry.input.*;
import mindustry.io.*; import mindustry.io.*;
@@ -419,7 +420,7 @@ public class Control implements ApplicationListener, Loadable{
ui.planet.hide(); ui.planet.hide();
SaveSlot slot = sector.save; SaveSlot slot = sector.save;
sector.planet.setLastSector(sector); 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{ try{
boolean hadNoCore = !sector.info.hasCore; 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 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(state.rules.defaultTeam.cores().isEmpty() || hadNoCore){
if(sector.planet.clearSectorOnLose || sector.info.hasWorldProcessor){ if(sector.planet.clearSectorOnLose){
playNewSector(origin, sector, reloader); playNewSector(origin, sector, reloader);
}else{ }else{
//no spawn set -> delete the sector save //no spawn set -> delete the sector save
@@ -446,61 +447,63 @@ public class Control implements ApplicationListener, Loadable{
return; return;
} }
int spawnPos = sector.info.spawnPosition;
//set spawn for sector damage to use //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); spawn.setBlock(sector.planet.defaultCore, state.rules.defaultTeam);
//add extra damage. //apply damage to simulate the sector being lost
SectorDamage.apply(1f); SectorDamage.apply(1f);
//reset wave so things are more fair //save the plans and buildings from the previous save; they will be used to re-populate the sector
state.wave = 1; var previousPlans = state.rules.defaultTeam.data().plans.toArray(BlockPlan.class);
//set up default wave time var previousBuildings = state.rules.defaultTeam.data().buildings.<Building>toArray(Building.class);
state.wavetime = state.rules.initialWaveSpacing <= 0f ? (state.rules.waveSpacing * (sector.preset == null ? 2f : sector.preset.startWaveTimeMultiplier)) : state.rules.initialWaveSpacing; var previousDerelicts = Team.derelict.data().buildings.<Building>toArray(Building.class);
state.wavetime *= sector.planet.campaignRules.difficulty.waveTimeMultiplier;
//reset captured state
sector.info.wasCaptured = false;
if(state.rules.sector.planet.allowWaves){ logic.reset();
//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;
}
//if there's still an enemy base left, fix it //now, load a fresh save; the old one was only used to grab previous building data
if(state.rules.attackMode){ playNewSector(origin, sector, reloader, new WorldParams(){{
//replace all broken blocks corePositionOverride = spawnPos;
for(var plan : state.rules.waveTeam.data().plans){ }}, () -> {
Tile tile = world.tile(plan.x, plan.y); var teamData = state.rules.defaultTeam.data();
if(tile != null){
tile.setBlock(plan.block, state.rules.waveTeam, plan.rotation); //all the old derelicts have to be removed.
if(plan.config != null && tile.build != null){ for(var generatedDerelict : Team.derelict.data().buildings.<Building>toArray(Building.class)){
tile.build.configureAny(plan.config); 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 //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)
Groups.unit.clear(); for(var build : previousBuildings){
Groups.fire.clear(); Tile tile = world.tile(build.tileX(), build.tileY());
Groups.puddle.clear(); 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 for(var build : previousBuildings){
state.rules.defaultTeam.data().unitCap = 0; build.updateProximity();
Schematics.placeLaunchLoadout(spawn.x, spawn.y); }
//set up camera/player locations //carry over all previous plans that don't already have the corresponding block at their position
player.set(spawn.x * tilesize, spawn.y * tilesize); for(var plan : previousPlans){
camera.position.set(player); 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)){
Events.fire(new SectorLaunchEvent(sector)); teamData.plans.add(plan);
Events.fire(Trigger.newGame); }
}
state.set(State.playing); });
reloader.end();
} }
}else{ }else{
state.set(State.playing); state.set(State.playing);
@@ -522,12 +525,20 @@ public class Control implements ApplicationListener, Loadable{
} }
public void playNewSector(@Nullable Sector origin, Sector sector, WorldReloader reloader){ 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(); reloader.begin();
world.loadSector(sector); world.loadSector(sector, params);
state.rules.sector = sector; state.rules.sector = sector;
//assign origin when launching
sector.info.origin = origin; sector.info.origin = origin;
sector.info.destination = origin; sector.info.destination = origin;
if(beforePlay != null){
beforePlay.run();
}
logic.play(); logic.play();
control.saves.saveSector(sector); control.saves.saveSector(sector);
Events.fire(new SectorLaunchEvent(sector)); Events.fire(new SectorLaunchEvent(sector));

View File

@@ -259,19 +259,19 @@ public class World{
} }
public void loadSector(Sector sector){ public void loadSector(Sector sector){
loadSector(sector, 0, true); loadSector(sector, new WorldParams());
} }
public void loadSector(Sector sector, int seedOffset, boolean saveInfo){ public void loadSector(Sector sector, WorldParams params){
setSectorRules(sector, saveInfo); setSectorRules(sector, params.saveInfo);
int size = sector.getSize(); int size = sector.getSize();
loadGenerator(size, size, tiles -> { loadGenerator(size, size, tiles -> {
if(sector.preset != null){ if(sector.preset != null){
sector.preset.generator.generate(tiles); sector.preset.generator.generate(tiles, params);
sector.preset.rules.get(state.rules); //apply extra rules sector.preset.rules.get(state.rules); //apply extra rules
}else if(sector.planet.generator != null){ }else if(sector.planet.generator != null){
sector.planet.generator.generate(tiles, sector, seedOffset); sector.planet.generator.generate(tiles, sector, params);
}else{ }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."); 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; state.rules.sector = sector;
}); });
if(saveInfo && state.rules.waves){ if(params.saveInfo && state.rules.waves){
sector.info.waves = state.rules.waves; sector.info.waves = state.rules.waves;
} }
@@ -289,7 +289,7 @@ public class World{
} }
//reset rules //reset rules
setSectorRules(sector, saveInfo); setSectorRules(sector, params.saveInfo);
if(state.rules.defaultTeam.core() != null){ if(state.rules.defaultTeam.core() != null){
sector.info.spawnPosition = state.rules.defaultTeam.core().pos(); sector.info.spawnPosition = state.rules.defaultTeam.core().pos();

View File

@@ -143,7 +143,7 @@ public class MapProcessorsDialog extends BaseDialog{
processors.clear(); 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 -> { Vars.world.tiles.eachTile(t -> {
if(t.isCenter() && t.block() == Blocks.worldProcessor){ if(t.isCenter() && t.block() == Blocks.worldProcessor){
processors.add(t.build); processors.add(t.build);

View File

@@ -7,6 +7,7 @@ import mindustry.graphics.*;
import mindustry.type.*; import mindustry.type.*;
import mindustry.ui.*; import mindustry.ui.*;
import mindustry.ui.dialogs.*; import mindustry.ui.dialogs.*;
import mindustry.world.*;
import static mindustry.Vars.*; import static mindustry.Vars.*;
@@ -94,7 +95,10 @@ public class SectorGenerateDialog extends BaseDialog{
sectorobj.preset = null; sectorobj.preset = null;
logic.reset(); //TODO: is this a good idea? all rules and map state are cleared, but it fixes inconsistent gen 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; sectorobj.preset = preset;

View File

@@ -114,12 +114,35 @@ abstract class BuilderComp implements Posc, Statusc, Teamc, Rotc{
if(plans.size > 1){ if(plans.size > 1){
int total = 0; int total = 0;
int size = plans.size; int size = plans.size;
BuildPlan plan; float bestDst = Float.MAX_VALUE;
while((!within((plan = buildPlan()).tile(), finalPlaceDst) || shouldSkip(plan, core)) && total < size){ 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.removeFirst();
plans.addLast(plan); plans.addLast(plan);
total++; 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(); 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. */ /** @return whether this plan should be skipped, in favor of the next one. */
boolean shouldSkip(BuildPlan plan, @Nullable Building core){ 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) || plan.isDerelictRepair()) return false;
if(state.rules.infiniteResources || team.rules().infiniteResources || plan.breaking || core == null || plan.isRotation(team) || (isBuilding() && !within(plans.last(), type.buildRange))) 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){ void removeBuild(int x, int y, boolean breaking){

View File

@@ -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(){ public boolean isCommandable(){
return block.commandable; return block.commandable;
} }

View File

@@ -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; 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){ public boolean samePos(BuildPlan other){
return x == other.x && y == other.y; return x == other.x && y == other.y;
} }

View File

@@ -41,8 +41,6 @@ public class SectorInfo{
public int storageCapacity = 0; public int storageCapacity = 0;
/** Whether a core is available here. */ /** Whether a core is available here. */
public boolean hasCore = true; 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. */ /** Whether this sector was ever fully captured. */
public boolean wasCaptured = false; public boolean wasCaptured = false;
/** Sector that was launched from. */ /** Sector that was launched from. */
@@ -213,7 +211,6 @@ public class SectorInfo{
spawnPosition = entity.pos(); spawnPosition = entity.pos();
} }
hasWorldProcessor = state.teams.present.contains(t -> t.getBuildings(Blocks.worldProcessor).any());
waveSpacing = state.rules.waveSpacing; waveSpacing = state.rules.waveSpacing;
wave = state.wave; wave = state.wave;
winWave = state.rules.winWave; winWave = state.rules.winWave;

View File

@@ -483,8 +483,8 @@ public class SectorDamage{
Tile other = tiles.getn(cx, cy); Tile other = tiles.getn(cx, cy);
float resultDamage = currDamage; float resultDamage = currDamage;
//damage the tile if it's not friendly //damage the tile if it's the player team (derelict blocks get ignored)
if(other.build != null && other.team() != state.rules.waveTeam){ if(other.build != null && other.team() == state.rules.defaultTeam){
resultDamage -= other.build.health(); resultDamage -= other.build.health();
other.build.health -= currDamage; other.build.health -= currDamage;

View File

@@ -29,7 +29,7 @@ public abstract class BasicGenerator implements WorldGenerator{
public Schematic defaultLoadout = Loadouts.basicShard; public Schematic defaultLoadout = Loadouts.basicShard;
@Override @Override
public void generate(Tiles tiles){ public void generate(Tiles tiles, WorldParams params){
this.tiles = tiles; this.tiles = tiles;
this.width = tiles.width; this.width = tiles.width;
this.height = tiles.height; this.height = tiles.height;

View File

@@ -13,14 +13,14 @@ public class BlankPlanetGenerator extends PlanetGenerator{
} }
@Override @Override
public void generate(Tiles tiles, Sector sec, int seed){ public void generate(Tiles tiles, Sector sec, WorldParams params){
this.tiles = tiles; this.tiles = tiles;
this.sector = sec; this.sector = sec;
this.rand.setSeed(sec.id + seed + baseSeed); this.rand.setSeed(sec.id + params.seedOffset + baseSeed);
tiles.fill(); tiles.fill();
generate(tiles); generate(tiles, params);
} }
} }

View File

@@ -9,6 +9,7 @@ import mindustry.maps.*;
import mindustry.type.*; import mindustry.type.*;
import mindustry.world.*; import mindustry.world.*;
import mindustry.world.blocks.storage.*; import mindustry.world.blocks.storage.*;
import mindustry.world.blocks.storage.CoreBlock.*;
import static mindustry.Vars.*; import static mindustry.Vars.*;
@@ -38,7 +39,7 @@ public class FileMapGenerator implements WorldGenerator{
} }
@Override @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."); if(map == null) throw new RuntimeException("Generator has null map, cannot be used.");
Sector sector = state.rules.sector; Sector sector = state.rules.sector;
@@ -72,6 +73,9 @@ public class FileMapGenerator implements WorldGenerator{
boolean anyCores = false; 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){ for(Tile tile : tiles){
if(tile.overlay() == Blocks.spawn){ 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(params.corePositionOverride != 0 && sector != null){
if(state.rules.sector != null && state.rules.sector.allowLaunchLoadout()){ 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); Schematics.placeLaunchLoadout(tile.x, tile.y);
} }
anyCores = true; anyCores = true;

View File

@@ -178,13 +178,13 @@ public abstract class PlanetGenerator extends BasicGenerator implements HexMeshe
return res % 2 == 0 ? res : res + 1; 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.tiles = tiles;
this.seed = seed + baseSeed; this.seed = params.seedOffset + baseSeed;
this.sector = sec; this.sector = sec;
this.width = tiles.width; this.width = tiles.width;
this.height = tiles.height; this.height = tiles.height;
this.rand.setSeed(sec.id + seed + baseSeed); this.rand.setSeed(sec.id + params.seedOffset + baseSeed);
TileGen gen = new TileGen(); TileGen gen = new TileGen();
for(int y = 0; y < height; y++){ for(int y = 0; y < height; y++){
@@ -197,6 +197,6 @@ public abstract class PlanetGenerator extends BasicGenerator implements HexMeshe
} }
} }
generate(tiles); generate(tiles, params);
} }
} }

View File

@@ -3,7 +3,7 @@ package mindustry.maps.generators;
import mindustry.world.*; import mindustry.world.*;
public interface WorldGenerator{ public interface WorldGenerator{
void generate(Tiles tiles); void generate(Tiles tiles, WorldParams params);
/** Do not modify tiles here. This is only for specialized configuration. */ /** Do not modify tiles here. This is only for specialized configuration. */
default void postGenerate(Tiles tiles){} default void postGenerate(Tiles tiles){}

View File

@@ -15,7 +15,6 @@ import static mindustry.Vars.*;
public class IconSelectDialog extends Dialog{ public class IconSelectDialog extends Dialog{
private Intc consumer = i -> Log.info("you have mere seconds"); private Intc consumer = i -> Log.info("you have mere seconds");
private boolean allowLocked;
public IconSelectDialog(){ public IconSelectDialog(){
this(true); this(true);

View File

@@ -1122,10 +1122,8 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
} }
}); });
}else{ }else{
if(sector.save != null){ sector.info.hasCore = false;
sector.save.delete(); sector.saveInfo();
}
sector.save = null;
} }
updateSelected(); updateSelected();
rebuildList(); rebuildList();

View File

@@ -92,26 +92,21 @@ public class Build{
//repair derelict tile //repair derelict tile
if(tile.team() == Team.derelict && team != Team.derelict && tile.block == result && tile.build != null && tile.block.allowDerelictRepair && state.rules.derelictRepair){ 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(); tile.build.rotation = rotation;
var config = tile.build.config(); tile.build.changeTeam(team);
tile.build.enabled = true;
tile.setBlock(result, team, rotation); tile.build.checkAllowUpdate();
tile.build.onRepaired();
if(unit != null && unit.getControllerName() != null) tile.build.lastAccessed = unit.getControllerName(); 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)){ if(fogControl.isVisibleTile(team, tile.x, tile.y)){
result.placeEffect.at(tile.drawx(), tile.drawy(), result.size); result.placeEffect.at(tile.drawx(), tile.drawy(), result.size);
Fx.rotateBlock.at(tile.build.x, tile.build.y, tile.build.block.size); Fx.rotateBlock.at(tile.build.x, tile.build.y, tile.build.block.size);
//doesn't play a sound //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; return;
} }
@@ -168,8 +163,8 @@ public class Build{
} }
/** @return whether a tile can be placed at this location by this team. */ /** @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){ 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, ignoreCoreRadius) && checkNoUnitOverlap(type, x, y); 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. */ /** @return whether a tile can be placed at this location by this team. */

View File

@@ -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;
}

View File

@@ -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 @Override
public void remove(){ public void remove(){
super.remove(); super.remove();
@@ -519,7 +525,7 @@ public class Turret extends ReloadTurret{
} }
if(validateTarget()){ if(validateTarget()){
boolean canShoot = true; boolean canShoot;
if(isControlled()){ //player behavior if(isControlled()){ //player behavior
targetPos.set(unit.aimX(), unit.aimY()); targetPos.set(unit.aimX(), unit.aimY());

View File

@@ -892,7 +892,7 @@ public class ApplicationTests{
logic.reset(); logic.reset();
state.rules.sector = zone.sector; 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); zone.rules.get(state.rules);
ObjectSet<Item> resources = new ObjectSet<>(); ObjectSet<Item> resources = new ObjectSet<>();
boolean hasSpawnPoint = false; boolean hasSpawnPoint = false;