From c1bc97ec510aac9341777e796ebd2d62bcc3596d Mon Sep 17 00:00:00 2001 From: Anuken Date: Fri, 18 Feb 2022 21:38:24 -0500 Subject: [PATCH] Removed BaseAI --- core/assets/bundles/bundle.properties | 2 - core/src/mindustry/ai/BaseAI.java | 315 ------------------ core/src/mindustry/ai/RtsAI.java | 1 + core/src/mindustry/ai/types/BuilderAI.java | 6 +- core/src/mindustry/content/Blocks.java | 3 +- core/src/mindustry/content/Planets.java | 1 - core/src/mindustry/content/SectorPresets.java | 4 - core/src/mindustry/core/Logic.java | 10 - core/src/mindustry/game/Rules.java | 6 +- core/src/mindustry/game/Teams.java | 4 +- core/src/mindustry/type/Planet.java | 2 - core/src/mindustry/type/SectorPreset.java | 1 - .../ui/dialogs/CustomRulesDialog.java | 4 - 13 files changed, 7 insertions(+), 352 deletions(-) delete mode 100644 core/src/mindustry/ai/BaseAI.java diff --git a/core/assets/bundles/bundle.properties b/core/assets/bundles/bundle.properties index 52a4df78fa..6d56ab865a 100644 --- a/core/assets/bundles/bundle.properties +++ b/core/assets/bundles/bundle.properties @@ -1052,9 +1052,7 @@ rules.schematic = Schematics Allowed rules.wavetimer = Wave Timer rules.waves = Waves rules.attack = Attack Mode -rules.buildai = AI Building rules.rtsai = RTS AI -rules.aitier = AI Tier rules.cleanupdeadteams = Clean Up Defeated Team Buildings (PvP) rules.corecapture = Capture Core On Destruction rules.polygoncoreprotection = Polygonal Core Protection diff --git a/core/src/mindustry/ai/BaseAI.java b/core/src/mindustry/ai/BaseAI.java deleted file mode 100644 index d33bcae145..0000000000 --- a/core/src/mindustry/ai/BaseAI.java +++ /dev/null @@ -1,315 +0,0 @@ -package mindustry.ai; - -import arc.math.*; -import arc.math.geom.*; -import arc.struct.*; -import arc.util.*; -import mindustry.*; -import mindustry.ai.BaseRegistry.*; -import mindustry.content.*; -import mindustry.core.*; -import mindustry.game.*; -import mindustry.game.Schematic.*; -import mindustry.game.Teams.*; -import mindustry.gen.*; -import mindustry.maps.generators.*; -import mindustry.type.*; -import mindustry.world.*; -import mindustry.world.blocks.defense.*; -import mindustry.world.blocks.payloads.*; -import mindustry.world.blocks.production.*; -import mindustry.world.blocks.storage.*; -import mindustry.world.blocks.storage.CoreBlock.*; - -import static mindustry.Vars.*; - -public class BaseAI{ - private static final Vec2 axis = new Vec2(), rotator = new Vec2(); - private static final int attempts = 4; - private static final float emptyChance = 0.01f; - private static final int timerStep = 0, timerSpawn = 1, timerRefreshPath = 2; - private static final int pathStep = 50; - private static final Seq tmpTiles = new Seq<>(); - - private static int correct = 0, incorrect = 0; - - private int lastX, lastY, lastW, lastH; - private boolean triedWalls, foundPath; - - final TeamData data; - final Interval timer = new Interval(4); - - IntSet path = new IntSet(); - IntSet calcPath = new IntSet(); - @Nullable Tile calcTile; - boolean calculating, startedCalculating; - int calcCount = 0; - int totalCalcs = 0; - Block wallType; - - public BaseAI(TeamData data){ - this.data = data; - } - - public void update(){ - - if(wallType == null){ - wallType = BaseGenerator.getDifficultyWall(1, data.team.rules().aiTier / 0.8f); - } - - if(data.team.rules().aiCoreSpawn && timer.get(timerSpawn, 60 * 6f) && data.hasCore()){ - CoreBlock block = (CoreBlock)data.core().block; - int coreUnits = data.countType(block.unitType); - - //create AI core unit(s) - if(!state.isEditor() && coreUnits < data.cores.size){ - Unit unit = block.unitType.create(data.team); - unit.set(data.cores.random()); - unit.add(); - Fx.spawn.at(unit); - } - } - - //refresh path - if(!calculating && (timer.get(timerRefreshPath, 3f * Time.toMinutes) || !startedCalculating) && data.hasCore()){ - calculating = true; - startedCalculating = true; - calcPath.clear(); - } - - //didn't find tile in time - if(calculating && calcCount >= world.width() * world.height()){ - calculating = false; - calcCount = 0; - calcPath.clear(); - totalCalcs ++; - } - - //calculate path for units so schematics are not placed on it - if(calculating){ - if(calcTile == null){ - Vars.spawner.eachGroundSpawn((x, y) -> calcTile = world.tile(x, y)); - if(calcTile == null){ - calculating = false; - } - }else{ - var field = pathfinder.getField(data.team, Pathfinder.costGround, Pathfinder.fieldCore); - - if(field.weights != null){ - int[] weights = field.weights; - for(int i = 0; i < pathStep; i++){ - int minCost = Integer.MAX_VALUE; - int cx = calcTile.x, cy = calcTile.y; - boolean foundAny = false; - for(Point2 p : Geometry.d4){ - int nx = cx + p.x, ny = cy + p.y, packed = world.packArray(nx, ny); - - Tile other = world.tile(nx, ny); - if(other != null && weights[packed] < minCost && weights[packed] != -1){ - minCost = weights[packed]; - calcTile = other; - foundAny = true; - } - } - - //didn't find anything, break out of loop, this will trigger a clear later - if(!foundAny){ - calcCount = Integer.MAX_VALUE; - break; - } - - calcPath.add(calcTile.pos()); - for(Point2 p : Geometry.d8){ - calcPath.add(Point2.pack(p.x + calcTile.x, p.y + calcTile.y)); - } - - //found the end. - if(calcTile.build instanceof CoreBuild b && b.team != data.team){ - //clean up calculations and flush results - calculating = false; - calcCount = 0; - path.clear(); - path.addAll(calcPath); - calcPath.clear(); - calcTile = null; - totalCalcs ++; - foundPath = true; - - break; - } - - calcCount ++; - } - } - } - } - - //only schedule when there's something to build. - if(foundPath && data.blocks.isEmpty() && timer.get(timerStep, Mathf.lerp(20f, 4f, data.team.rules().aiTier))){ - if(!triedWalls){ - tryWalls(); - triedWalls = true; - } - - for(int i = 0; i < attempts; i++){ - int range = 150; - - Position pos = randomPosition(); - - //when there are no random positions, do nothing. - if(pos == null) return; - - Tmp.v1.rnd(Mathf.random(range)); - int wx = (int)(World.toTile(pos.getX()) + Tmp.v1.x), wy = (int)(World.toTile(pos.getY()) + Tmp.v1.y); - Tile tile = world.tiles.getc(wx, wy); - - //try not to block the spawn point - if(spawner.getSpawns().contains(t -> t.within(tile, tilesize * 40f))){ - continue; - } - - Seq parts = null; - - //pick a completely random base part, and place it a random location - //((yes, very intelligent)) - if(tile.drop() != null && Vars.bases.forResource(tile.drop()).any()){ - parts = Vars.bases.forResource(tile.drop()); - }else if(Mathf.chance(emptyChance)){ - parts = Vars.bases.parts; - } - - if(parts != null){ - BasePart part = parts.random(); - if(tryPlace(part, tile.x, tile.y)){ - break; - } - } - } - } - } - - /** @return a random position from which to seed building. */ - private Position randomPosition(){ - if(data.hasCore()){ - return data.cores.random(); - }else if(data.team == state.rules.waveTeam){ - return spawner.getSpawns().random(); - } - return null; - } - - private boolean tryPlace(BasePart part, int x, int y){ - int rotation = Mathf.range(2); - axis.set((int)(part.schematic.width / 2f), (int)(part.schematic.height / 2f)); - Schematic result = Schematics.rotate(part.schematic, rotation); - int rotdeg = rotation*90; - rotator.set(part.centerX, part.centerY).rotateAround(axis, rotdeg); - //bottom left schematic corner - int cx = x - (int)rotator.x; - int cy = y - (int)rotator.y; - - //check valid placeability - for(Stile tile : result.tiles){ - int realX = tile.x + cx, realY = tile.y + cy; - if(!Build.validPlace(tile.block, data.team, realX, realY, tile.rotation)){ - return false; - } - Tile wtile = world.tile(realX, realY); - - if(tile.block instanceof PayloadConveyor || tile.block instanceof PayloadBlock){ - //near a building - for(Point2 point : Edges.getEdges(tile.block.size)){ - var t = world.build(tile.x + point.x, tile.y + point.y); - if(t != null){ - return false; - } - } - } - - //may intersect AI path - tmpTiles.clear(); - if(tile.block.solid && wtile != null && wtile.getLinkedTilesAs(tile.block, tmpTiles).contains(t -> path.contains(t.pos()))){ - return false; - } - } - - //make sure at least X% of resource requirements are met - correct = incorrect = 0; - boolean anyDrills = false; - - if(part.required instanceof Item){ - for(Stile tile : result.tiles){ - if(tile.block instanceof Drill){ - anyDrills = true; - - tile.block.iterateTaken(tile.x + cx, tile.y + cy, (ex, ey) -> { - Tile res = world.rawTile(ex, ey); - if(res.drop() == part.required){ - correct ++; - }else if(res.drop() != null){ - incorrect ++; - } - }); - } - } - } - - //fail if not enough fit requirements - if(anyDrills && (incorrect != 0 || correct == 0)){ - return false; - } - - //queue it - for(Stile tile : result.tiles){ - data.blocks.add(new BlockPlan(cx + tile.x, cy + tile.y, tile.rotation, tile.block.id, tile.config)); - } - - lastX = cx - 1; - lastY = cy - 1; - lastW = result.width + 2; - lastH = result.height + 2; - - triedWalls = false; - - return true; - } - - private void tryWalls(){ - Block wall = wallType; - Building spawnt = state.rules.defaultTeam.core() != null ? state.rules.defaultTeam.core() : data.team.core(); - Tile spawn = spawnt == null ? null : spawnt.tile; - - if(spawn == null) return; - - for(int wx = lastX; wx <= lastX + lastW; wx++){ - outer: - for(int wy = lastY; wy <= lastY + lastH; wy++){ - Tile tile = world.tile(wx, wy); - - if(tile == null || !tile.block().alwaysReplace) continue; - - boolean any = false; - - for(Point2 p : Geometry.d8){ - if(Angles.angleDist(Angles.angle(p.x, p.y), spawn.angleTo(tile)) > 70){ - continue; - } - - Tile o = world.tile(tile.x + p.x, tile.y + p.y); - if(o != null && (o.block() instanceof PayloadBlock || o.block() instanceof PayloadConveyor || o.block() instanceof ShockMine)){ - continue outer; - } - - if(o != null && o.team() == data.team && !(o.block() instanceof Wall)){ - any = true; - } - } - - tmpTiles.clear(); - if(any && Build.validPlace(wall, data.team, tile.x, tile.y, 0) && !tile.getLinkedTilesAs(wall, tmpTiles).contains(t -> path.contains(t.pos()))){ - data.blocks.add(new BlockPlan(tile.x, tile.y, (short)0, wall.id, null)); - } - } - } - } -} diff --git a/core/src/mindustry/ai/RtsAI.java b/core/src/mindustry/ai/RtsAI.java index 5cc2ab9794..0d7fdcacbe 100644 --- a/core/src/mindustry/ai/RtsAI.java +++ b/core/src/mindustry/ai/RtsAI.java @@ -83,6 +83,7 @@ public class RtsAI{ } } + //TODO atrocious implementation void checkBuilding(){ if(data.team.rules().aiCoreSpawn && timer.get(timerSpawn, 60 * 7f) && data.hasCore()){ CoreBlock block = (CoreBlock)data.core().block; diff --git a/core/src/mindustry/ai/types/BuilderAI.java b/core/src/mindustry/ai/types/BuilderAI.java index 43c340e737..9b3d0f34d8 100644 --- a/core/src/mindustry/ai/types/BuilderAI.java +++ b/core/src/mindustry/ai/types/BuilderAI.java @@ -1,6 +1,5 @@ package mindustry.ai.types; -import arc.math.*; import arc.struct.*; import arc.util.*; import mindustry.entities.*; @@ -133,7 +132,8 @@ public class BuilderAI extends AIController{ }); } - float rebuildTime = (unit.team.rules().ai ? Mathf.lerp(15f, 2f, unit.team.rules().aiTier) : 2f) * 60f; + //TODO this is bad, rebuild time should not depend on AI here + float rebuildTime = (unit.team.rules().rtsAi ? 12f : 2f) * 60f; //find new request if(!unit.team.data().blocks.isEmpty() && following == null && timer.get(timerTarget3, rebuildTime)){ @@ -168,7 +168,7 @@ public class BuilderAI extends AIController{ @Override public boolean useFallback(){ - return state.rules.waves && unit.team == state.rules.waveTeam && !unit.team.rules().ai && !unit.team.rules().rtsAi; + return state.rules.waves && unit.team == state.rules.waveTeam && !unit.team.rules().rtsAi; } @Override diff --git a/core/src/mindustry/content/Blocks.java b/core/src/mindustry/content/Blocks.java index 31d37faf98..b1f8f40f46 100644 --- a/core/src/mindustry/content/Blocks.java +++ b/core/src/mindustry/content/Blocks.java @@ -3707,8 +3707,7 @@ public class Blocks{ size = 3; reloadTime = 130f; chargeTime = 90f; - //TODO 500 or 400? does it need to be better than the standard mass driver? - range = 450f; + range = 500f; maxPayloadSize = 2.5f; consumePower(0.5f); }}; diff --git a/core/src/mindustry/content/Planets.java b/core/src/mindustry/content/Planets.java index a44b9abb52..a38d07b8f5 100644 --- a/core/src/mindustry/content/Planets.java +++ b/core/src/mindustry/content/Planets.java @@ -114,7 +114,6 @@ public class Planets{ r.attributes.clear(); r.showSpawns = false; }; - defaultAI = true; atmosphereColor = Color.valueOf("3c1b8f"); atmosphereRadIn = 0.02f; atmosphereRadOut = 0.3f; diff --git a/core/src/mindustry/content/SectorPresets.java b/core/src/mindustry/content/SectorPresets.java index 3fe0dba5fa..71647cab35 100644 --- a/core/src/mindustry/content/SectorPresets.java +++ b/core/src/mindustry/content/SectorPresets.java @@ -28,7 +28,6 @@ public class SectorPresets{ saltFlats = new SectorPreset("saltFlats", serpulo, 101){{ difficulty = 5; - useAI = false; }}; frozenForest = new SectorPreset("frozenForest", serpulo, 86){{ @@ -63,7 +62,6 @@ public class SectorPresets{ extractionOutpost = new SectorPreset("extractionOutpost", serpulo, 165){{ difficulty = 5; - useAI = false; }}; coastline = new SectorPreset("coastline", serpulo, 108){{ @@ -77,12 +75,10 @@ public class SectorPresets{ fungalPass = new SectorPreset("fungalPass", serpulo, 21){{ difficulty = 4; - useAI = false; }}; overgrowth = new SectorPreset("overgrowth", serpulo, 134){{ difficulty = 5; - useAI = false; }}; tarFields = new SectorPreset("tarFields", serpulo, 23){{ diff --git a/core/src/mindustry/core/Logic.java b/core/src/mindustry/core/Logic.java index 9a30d5c22d..a4c72470a8 100644 --- a/core/src/mindustry/core/Logic.java +++ b/core/src/mindustry/core/Logic.java @@ -119,13 +119,8 @@ public class Logic implements ApplicationListener{ if(state.isCampaign()){ //enable building AI on campaign unless the preset disables it - //TODO should be (more) configurable, I don't want building AI everywhere. - if(state.getSector().planet.defaultAI && !(state.getSector().preset != null && !state.getSector().preset.useAI)){ - state.rules.waveTeam.rules().ai = true; - } state.rules.coreIncinerates = true; - state.rules.waveTeam.rules().aiTier = state.getSector().threat * 0.8f; state.rules.waveTeam.rules().infiniteResources = true; //fill enemy cores by default. @@ -419,11 +414,6 @@ public class Logic implements ApplicationListener{ updateWeather(); for(TeamData data : state.teams.getActive()){ - if(data.hasAI()){ - if(data.baseAi == null) data.baseAi = new BaseAI(data); - data.baseAi.update(); - } - if(data.team.rules().rtsAi){ if(data.rtsAi == null) data.rtsAi = new RtsAI(data); data.rtsAi.update(); diff --git a/core/src/mindustry/game/Rules.java b/core/src/mindustry/game/Rules.java index ef0c9a277a..d0a3ccc095 100644 --- a/core/src/mindustry/game/Rules.java +++ b/core/src/mindustry/game/Rules.java @@ -201,11 +201,7 @@ public class Rules{ /** A team-specific ruleset. */ public static class TeamRule{ - /** Whether to use building AI. TODO remove, it is terrible. */ - public boolean ai; - /** TODO Tier of blocks/designs that the AI uses for building. [0, 1] */ - public float aiTier = 1f; - /** Whether, when AI is enabled, ships should be spawned from the core. */ + /** Whether, when AI is enabled, ships should be spawned from the core. TODO remove / unnecessary? */ public boolean aiCoreSpawn = true; /** If true, blocks don't require power or resources. */ public boolean cheat; diff --git a/core/src/mindustry/game/Teams.java b/core/src/mindustry/game/Teams.java index d8d8b98d79..8e48786da5 100644 --- a/core/src/mindustry/game/Teams.java +++ b/core/src/mindustry/game/Teams.java @@ -223,8 +223,6 @@ public class Teams{ public final Seq cores = new Seq<>(); public final Team team; - /** Handles building ""bases"". */ - public @Nullable BaseAI baseAi; /** Handles RTS unit control. */ public @Nullable RtsAI rtsAi; @@ -330,7 +328,7 @@ public class Teams{ /** @return whether this team is controlled by the AI and builds bases. */ public boolean hasAI(){ - return team.rules().ai; + return team.rules().rtsAi; } @Override diff --git a/core/src/mindustry/type/Planet.java b/core/src/mindustry/type/Planet.java index 84a2a1f9eb..c6896544a4 100644 --- a/core/src/mindustry/type/Planet.java +++ b/core/src/mindustry/type/Planet.java @@ -91,8 +91,6 @@ public class Planet extends UnlockableContent{ public boolean allowWaveSimulation = false; /** Whether to simulate sector invasions from enemy bases. */ public boolean allowSectorInvasion = false; - /** If true, builder AI is turned on for all sectors on this planet by default. */ - public boolean defaultAI = false; /** If true, sectors saves are cleared when lost. */ public boolean clearSectorOnLose = false; /** Sets up rules on game load for any sector on this planet. */ diff --git a/core/src/mindustry/type/SectorPreset.java b/core/src/mindustry/type/SectorPreset.java index 63ff4fe509..a994cacfa9 100644 --- a/core/src/mindustry/type/SectorPreset.java +++ b/core/src/mindustry/type/SectorPreset.java @@ -13,7 +13,6 @@ public class SectorPreset extends UnlockableContent{ public int captureWave = 0; public Cons rules = rules -> rules.winWave = captureWave; - public boolean useAI = true; /** Difficulty, 0-10. */ public float difficulty; public float startWaveTimeMultiplier = 2f; diff --git a/core/src/mindustry/ui/dialogs/CustomRulesDialog.java b/core/src/mindustry/ui/dialogs/CustomRulesDialog.java index 15b57a4837..5bb0dc808d 100644 --- a/core/src/mindustry/ui/dialogs/CustomRulesDialog.java +++ b/core/src/mindustry/ui/dialogs/CustomRulesDialog.java @@ -182,7 +182,6 @@ public class CustomRulesDialog extends BaseDialog{ title("@rules.title.enemy"); check("@rules.attack", b -> rules.attackMode = b, () -> rules.attackMode); - check("@rules.buildai", b -> rules.teams.get(rules.waveTeam).ai = rules.teams.get(rules.waveTeam).infiniteResources = b, () -> rules.teams.get(rules.waveTeam).ai); check("@rules.corecapture", b -> rules.coreCapture = b, () -> rules.coreCapture); check("@rules.placerangecheck", b -> rules.placeRangeCheck = b, () -> rules.placeRangeCheck); check("@rules.polygoncoreprotection", b -> rules.polygonCoreProtection = b, () -> rules.polygonCoreProtection); @@ -257,9 +256,6 @@ public class CustomRulesDialog extends BaseDialog{ check("@rules.rtsai", b -> teams.rtsAi = b, () -> teams.rtsAi, () -> team != rules.defaultTeam); - check("@rules.buildai", b -> teams.ai = b, () -> teams.ai, () -> team != rules.defaultTeam); - number("@rules.aitier", false, f -> teams.aiTier = f, () -> teams.aiTier, () -> teams.ai, 0, 1); - check("@rules.infiniteresources", b -> teams.infiniteResources = b, () -> teams.infiniteResources); number("@rules.buildspeedmultiplier", f -> teams.buildSpeedMultiplier = f, () -> teams.buildSpeedMultiplier, 0.001f, 50f);