From 5e7d9f81194d9f667369769f6a0fe04e7659102b Mon Sep 17 00:00:00 2001 From: Anuken Date: Tue, 2 Jun 2020 22:09:41 -0400 Subject: [PATCH] Awful enemy base generation --- .gitignore | 6 +- android/build.gradle | 2 + core/src/mindustry/ai/Astar.java | 97 +++++++ core/src/mindustry/content/Blocks.java | 2 +- core/src/mindustry/graphics/Drawf.java | 69 +---- core/src/mindustry/input/DesktopInput.java | 10 +- core/src/mindustry/input/Placement.java | 8 - .../maps/generators/BaseGenerator.java | 105 ++++++- .../maps/generators/BasicGenerator.java | 56 +--- .../maps/generators/SeedBaseGenerator.java | 266 ++++++++++++++++++ .../maps/planet/TODOPlanetGenerator.java | 3 +- .../mindustry/ui/dialogs/PlanetDialog.java | 10 + core/src/mindustry/world/Build.java | 21 +- core/src/mindustry/world/Tile.java | 9 + .../world/blocks/campaign/CoreLauncher.java | 19 +- .../world/blocks/experimental/BlockForge.java | 21 +- 16 files changed, 528 insertions(+), 176 deletions(-) create mode 100644 core/src/mindustry/ai/Astar.java create mode 100644 core/src/mindustry/maps/generators/SeedBaseGenerator.java diff --git a/.gitignore b/.gitignore index e9a73fdffd..868e2a4281 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,7 @@ logs/ /desktop/mindustry-saves/ /desktop/mindustry-maps/ /desktop/gifexport/ +/gifs/ /core/lib/ /ios/assets/ /core/assets-raw/sprites/generated/ @@ -28,7 +29,6 @@ core/assets/saves/ /core/assets/saves/ steam_appid.txt /test_files/ -/annotations/build/ /android/assets/mindustry-maps/ /android/assets/mindustry-saves/ /core/assets/gifexport/ @@ -40,11 +40,7 @@ steam_appid.txt ios/robovm.properties packr-out/ config/ -changelog *.gif -/core/assets/saves/ -/out/ -/core/assets-raw/fontgen/out/ version.properties diff --git a/android/build.gradle b/android/build.gradle index b83b64c15a..412ed1c441 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -8,6 +8,8 @@ buildscript{ } dependencies{ + //IMPORTANT NOTICE: any version of the plugin after 3.4.1 will break builds for every API level < 24, perhaps even higher. + //it appears abstract methods don't get desugared properly (if at all) classpath 'com.android.tools.build:gradle:3.4.1' } } diff --git a/core/src/mindustry/ai/Astar.java b/core/src/mindustry/ai/Astar.java new file mode 100644 index 0000000000..1499be5ce5 --- /dev/null +++ b/core/src/mindustry/ai/Astar.java @@ -0,0 +1,97 @@ +package mindustry.ai; + +import arc.func.*; +import arc.math.geom.*; +import arc.struct.*; +import arc.util.*; +import mindustry.world.*; + +import static mindustry.Vars.world; + +public class Astar{ + public static final DistanceHeuristic manhattan = (x1, y1, x2, y2) -> Math.abs(x1 - x2) + Math.abs(y1 - y2); + + private static final Array out = new Array<>(); + private static final PQueue queue = new PQueue<>(200 * 200 / 4, (a, b) -> 0); + private static final IntFloatMap costs = new IntFloatMap(); + private static byte[][] rotations; + + public static Array pathfind(Tile from, Tile to, TileHueristic th, Boolf passable){ + return pathfind(from.x, from.y, to.x, to.y, th, manhattan, passable); + } + + public static Array pathfind(int startX, int startY, int endX, int endY, TileHueristic th, Boolf passable){ + return pathfind(startX, startY, endX, endY, th, manhattan, passable); + } + + public static Array pathfind(int startX, int startY, int endX, int endY, TileHueristic th, DistanceHeuristic dh, Boolf passable){ + Tiles tiles = world.tiles; + + Tile start = tiles.getn(startX, startY); + Tile end = tiles.getn(endX, endY); + + GridBits closed = new GridBits(tiles.width, tiles.height); + + costs.clear(); + queue.clear(); + queue.comparator = Structs.comparingFloat(a -> costs.get(a.pos(), 0f) + dh.cost(a.x, a.y, end.x, end.y)); + queue.add(start); + if(rotations == null || rotations.length != world.width() || rotations[0].length != world.height()){ + rotations = new byte[world.width()][world.height()]; + } + + boolean found = false; + while(!queue.empty()){ + Tile next = queue.poll(); + float baseCost = costs.get(next.pos(), 0f); + if(next == end){ + found = true; + break; + } + closed.set(next.x, next.y); + for(Point2 point : Geometry.d4){ + int newx = next.x + point.x, newy = next.y + point.y; + if(Structs.inBounds(newx, newy, tiles.width, tiles.height)){ + Tile child = tiles.getn(newx, newy); + if(passable.get(child)){ + float newCost = th.cost(next, child) + baseCost; + if(!closed.get(child.x, child.y)){ + closed.set(child.x, child.y); + rotations[child.x][child.y] = child.relativeTo(next.x, next.y); + costs.put(child.pos(), newCost); + queue.add(child); + } + } + } + } + } + + out.clear(); + + if(!found) return out; + + Tile current = end; + while(current != start){ + out.add(current); + + byte rot = rotations[current.x][current.y]; + current = tiles.getn(current.x + Geometry.d4x[rot], current.y + Geometry.d4y[rot]); + } + + out.reverse(); + + return out; + } + + public interface DistanceHeuristic{ + float cost(int x1, int y1, int x2, int y2); + } + + public interface TileHueristic{ + float cost(Tile tile); + + default float cost(Tile from, Tile tile){ + return cost(tile); + } + } +} diff --git a/core/src/mindustry/content/Blocks.java b/core/src/mindustry/content/Blocks.java index c51238281c..f232526d49 100644 --- a/core/src/mindustry/content/Blocks.java +++ b/core/src/mindustry/content/Blocks.java @@ -1790,7 +1790,7 @@ public class Blocks implements ContentList{ hasPower = true; consumes.items(ItemStack.with(Items.copper, 500)); - consumes.power(4f); + consumes.power(3f); }}; dataProcessor = new ResearchBlock("data-processor"){{ diff --git a/core/src/mindustry/graphics/Drawf.java b/core/src/mindustry/graphics/Drawf.java index 658df5c128..61795a5e2c 100644 --- a/core/src/mindustry/graphics/Drawf.java +++ b/core/src/mindustry/graphics/Drawf.java @@ -5,11 +5,10 @@ import arc.graphics.*; import arc.graphics.g2d.*; import arc.math.*; import arc.math.geom.*; -import arc.util.ArcAnnotate.*; import arc.util.*; import mindustry.*; +import mindustry.ctype.*; import mindustry.gen.*; -import mindustry.type.*; import mindustry.ui.*; import mindustry.world.*; @@ -184,9 +183,11 @@ public class Drawf{ Draw.rect(Core.atlas.find("shape-3"), x, y - oy + length / 2f, width, length, width / 2f, oy, rotation - 90); } - public static void construct(Tilec t, UnitType unit, float rotation, float progress, float speed, float time){ - TextureRegion region = unit.icon(Cicon.full); + public static void construct(Tilec t, UnlockableContent content, float rotation, float progress, float speed, float time){ + construct(t, content.icon(Cicon.full), rotation, progress, speed, time); + } + public static void construct(Tilec t, TextureRegion region, float rotation, float progress, float speed, float time){ Shaders.build.region = region; Shaders.build.progress = progress; Shaders.build.color.set(Pal.accent); @@ -204,64 +205,4 @@ public class Drawf{ Draw.reset(); } - - public static void respawn(Tilec tile, float heat, float progress, float time, UnitType to, @Nullable Playerc player){ - float x = tile.x(), y = tile.y(); - progress = Mathf.clamp(progress); - - Draw.color(Pal.darkMetal); - Lines.stroke(2f * heat); - Fill.poly(x, y, 4, 10f * heat); - - Draw.reset(); - if(player != null){ - TextureRegion region = to.icon(Cicon.full); - - Draw.color(0f, 0f, 0f, 0.4f * progress); - Draw.rect("circle-shadow", x, y, region.getWidth() / 3f, region.getWidth() / 3f); - Draw.color(); - - Shaders.build.region = region; - Shaders.build.progress = progress; - Shaders.build.color.set(Pal.accent); - Shaders.build.time = -time / 10f; - - Draw.shader(Shaders.build, true); - Draw.rect(region, x, y); - Draw.shader(); - - Draw.color(Pal.accentBack); - - float pos = Mathf.sin(time, 6f, 8f); - - Lines.lineAngleCenter(x + pos, y, 90, 16f - Math.abs(pos) * 2f); - - Draw.reset(); - } - - Lines.stroke(2f * heat); - - Draw.color(Pal.accentBack); - Lines.poly(x, y, 4, 8f * heat); - - float oy = -7f, len = 6f * heat; - Lines.stroke(5f); - Draw.color(Pal.darkMetal); - Lines.line(x - len, y + oy, x + len, y + oy, CapStyle.none); - for(int i : Mathf.signs){ - Fill.tri(x + len * i, y + oy - Lines.getStroke()/2f, x + len * i, y + oy + Lines.getStroke()/2f, x + (len + Lines.getStroke() * heat) * i, y + oy); - } - - Lines.stroke(3f); - Draw.color(Pal.accent); - Lines.line(x - len, y + oy, x - len + len*2 * progress, y + oy, CapStyle.none); - for(int i : Mathf.signs){ - Fill.tri(x + len * i, y + oy - Lines.getStroke()/2f, x + len * i, y + oy + Lines.getStroke()/2f, x + (len + Lines.getStroke() * heat) * i, y + oy); - } - Draw.reset(); - - if(Vars.net.active() && player != null){ - tile.block().drawPlaceText(player.name(), tile.tileX(), tile.tileY() - (Math.max((tile.block().size-1)/2, 0)), true); - } - } } diff --git a/core/src/mindustry/input/DesktopInput.java b/core/src/mindustry/input/DesktopInput.java index ace825f121..6da45de40b 100644 --- a/core/src/mindustry/input/DesktopInput.java +++ b/core/src/mindustry/input/DesktopInput.java @@ -202,15 +202,15 @@ public class DesktopInput extends InputHandler{ shouldShoot = false; } } - - if(Core.input.keyDown(Binding.respawn) && !player.dead() && !player.unit().spawnedByCore()){ - Call.onUnitClear(player); - controlledType = null; - } } if(!player.dead() && !state.isPaused() && !(Core.scene.getKeyboardFocus() instanceof TextField)){ updateMovement(player.unit()); + + if(Core.input.keyDown(Binding.respawn) && !player.unit().spawnedByCore()){ + Call.onUnitClear(player); + controlledType = null; + } } if(Core.input.keyRelease(Binding.select)){ diff --git a/core/src/mindustry/input/Placement.java b/core/src/mindustry/input/Placement.java index 8a2d9ea499..6c2264d0fe 100644 --- a/core/src/mindustry/input/Placement.java +++ b/core/src/mindustry/input/Placement.java @@ -273,12 +273,4 @@ public class Placement{ return y + (x2 - x > y2 - y ? 0 : i); } } - - public interface DistanceHeuristic{ - float cost(int x1, int y1, int x2, int y2); - } - - public interface TileHueristic{ - float cost(Tile tile, Tile other); - } } diff --git a/core/src/mindustry/maps/generators/BaseGenerator.java b/core/src/mindustry/maps/generators/BaseGenerator.java index 922e6e9ef9..b8d42dc937 100644 --- a/core/src/mindustry/maps/generators/BaseGenerator.java +++ b/core/src/mindustry/maps/generators/BaseGenerator.java @@ -1,39 +1,70 @@ package mindustry.maps.generators; +import arc.math.geom.*; import arc.struct.*; +import mindustry.ai.*; +import mindustry.content.*; import mindustry.game.*; import mindustry.type.*; import mindustry.world.*; +import mindustry.world.blocks.production.*; +import mindustry.world.blocks.storage.*; +//makes terrible bases public class BaseGenerator{ + int width, height; + Cell[][] cells; + Queue frontier; + Array all; + ObjectMap> resources; + Team team; public void generate(Tiles tiles, Array cores, Tile spawn, Team team, Sector sector){ + if(true){ + SeedBaseGenerator gen = new SeedBaseGenerator(); + gen.generate(tiles, team, cores.first()); - /* - GridBits used = new GridBits(tiles.width, tiles.height); - Queue frontier = new Queue<>(); + return; + } + + this.team = team; + width = tiles.width; + height = tiles.height; + + cells = new Cell[width][height]; + for(int x = 0; x < width; x++){ + for(int y = 0; y < height; y++){ + cells[x][y] = new Cell(); + } + } + + all = new Array<>(); + frontier = new Queue<>(); for(Tile tile : cores){ frontier.add(tile); } - int count = 2000; + int count = 10000; int total = 0; + //create bounds while(total++ < count){ Tile tile = frontier.removeFirst(); + all.add(tile); for(int i = 0; i < 4; i++){ int cx = tile.x + Geometry.d4x[i], cy = tile.y + Geometry.d4y[i]; - if(tiles.in(cx, cy) && !used.get(cx, cy)){ + if(tiles.in(cx, cy) && !contained(cx, cy)){ Tile other = tiles.getn(cx, cy); if(!other.solid()){ frontier.addLast(other); } - used.set(cx, cy); + cells[cx][cy].contained = true; } } } + //walls, mostly for debugging for(Tile tile : frontier){ tile.setBlock(Blocks.copperWall, team); } @@ -41,9 +72,69 @@ public class BaseGenerator{ for(Tile tile : cores){ tile.clearOverlay(); tile.setBlock(Blocks.coreShard, team); + } - }*/ + Block pump = Blocks.mechanicalPump; + Drill drill = (Drill)Blocks.pneumaticDrill; + resources = new ObjectMap<>(); + //assign resource collection points + for(Tile tile : all){ + //place drills. + if(tile.drop() != null && tile.drop().type == ItemType.material && tile.drop().hardness <= drill.tier && Build.validPlace(team, tile.x, tile.y, drill, 0)){ + tile.setBlock(drill, team); + + //mark item outputs + tile.getLinkedTiles(t -> { + cell(t).item = tile.drop(); + resources.get(tile.drop(), Array::new).add(t); + }); + } + + //only water matters right now + //pumps are only placed on edges of water, even if it's shallow + if(tile.floor().liquidDrop == Liquids.water && Build.validPlace(team, tile.x, tile.y, pump, 0) && Build.contactsGround(tile.x, tile.y, pump)){ + tile.setBlock(pump, team); + } + } + + //clear path for cores + for(Tile start : cores){ + Array path = Astar.pathfind(start, spawn, (tile) -> tile.solid() ? 30f : 0f, tile -> !tile.block().isStatic() && !tile.floor().isDeep()); + + for(Tile tile : path){ + tile.circle(2, (x, y) -> { + Tile t = tiles.getn(x, y); + if(t.team() == team && t.solid() && !(t.block() instanceof CoreBlock)){ + t.setAir(); + } + cell(t).taken = true; + }); + } + } } + + Item contactRes(Tile tile){ + for(int i = 0; i < 4; i++){ + if(tile.getNearby(i) == null) continue; + Cell cell = cell(tile.getNearby(i)); + if(cell.item != null) return cell.item; + } + return null; + } + + Cell cell(Tile tile){ + return cells[tile.x][tile.y]; + } + + boolean contained(int x, int y){ + return cells[x][y].contained; + } + + static class Cell{ + boolean contained; + boolean taken; + Item item; + } } diff --git a/core/src/mindustry/maps/generators/BasicGenerator.java b/core/src/mindustry/maps/generators/BasicGenerator.java index f756a149b0..5035662d28 100644 --- a/core/src/mindustry/maps/generators/BasicGenerator.java +++ b/core/src/mindustry/maps/generators/BasicGenerator.java @@ -6,13 +6,14 @@ import arc.math.geom.*; import arc.struct.*; import arc.util.*; import mindustry.*; +import mindustry.ai.*; +import mindustry.ai.Astar.*; import mindustry.content.*; import mindustry.world.*; import static mindustry.Vars.*; public abstract class BasicGenerator implements WorldGenerator{ - protected static final DistanceHeuristic manhattan = (x1, y1, x2, y2) -> Math.abs(x1 - x2) + Math.abs(y1 - y2); protected static final ShortArray ints1 = new ShortArray(), ints2 = new ShortArray(); protected Rand rand = new Rand(); @@ -313,50 +314,7 @@ public abstract class BasicGenerator implements WorldGenerator{ } public Array pathfind(int startX, int startY, int endX, int endY, TileHueristic th, DistanceHeuristic dh){ - Tile start = tiles.getn(startX, startY); - Tile end = tiles.getn(endX, endY); - GridBits closed = new GridBits(width, height); - IntFloatMap costs = new IntFloatMap(); - PQueue queue = new PQueue<>(tiles.width * tiles.height / 4, Structs.comparingFloat(a -> costs.get(a.pos(), 0f) + dh.cost(a.x, a.y, end.x, end.y))); - queue.add(start); - boolean found = false; - while(!queue.empty()){ - Tile next = queue.poll(); - float baseCost = costs.get(next.pos(), 0f); - if(next == end){ - found = true; - break; - } - closed.set(next.x, next.y); - for(Point2 point : Geometry.d4){ - int newx = next.x + point.x, newy = next.y + point.y; - if(Structs.inBounds(newx, newy, width, height) && world.getDarkness(newx, newy) <= 1f){ - Tile child = tiles.getn(newx, newy); - float newCost = th.cost(child) + baseCost; - if(!closed.get(child.x, child.y)){ - closed.set(child.x, child.y); - child.rotation(child.relativeTo(next.x, next.y)); - costs.put(child.pos(), newCost); - queue.add(child); - } - } - } - } - - Array out = new Array<>(); - - if(!found) return out; - - Tile current = end; - while(current != start){ - out.add(current); - Point2 p = Geometry.d4(current.rotation()); - current = tiles.getn(current.x + p.x, current.y + p.y); - } - - out.reverse(); - - return out; + return Astar.pathfind(startX, startY, endX, endY, th, dh, tile -> world.getDarkness(tile.x, tile.y) <= 1f); } public void trimDark(){ @@ -397,12 +355,4 @@ public abstract class BasicGenerator implements WorldGenerator{ } } } - - public interface DistanceHeuristic{ - float cost(int x1, int y1, int x2, int y2); - } - - public interface TileHueristic{ - float cost(Tile tile); - } } diff --git a/core/src/mindustry/maps/generators/SeedBaseGenerator.java b/core/src/mindustry/maps/generators/SeedBaseGenerator.java new file mode 100644 index 0000000000..2e0ec5c654 --- /dev/null +++ b/core/src/mindustry/maps/generators/SeedBaseGenerator.java @@ -0,0 +1,266 @@ +package mindustry.maps.generators; + +import arc.func.*; +import arc.math.*; +import arc.math.geom.*; +import arc.struct.*; +import mindustry.*; +import mindustry.content.*; +import mindustry.game.*; +import mindustry.type.*; +import mindustry.world.*; +import mindustry.world.blocks.defense.*; +import mindustry.world.blocks.defense.turrets.*; +import mindustry.world.blocks.power.*; +import mindustry.world.blocks.production.*; +import mindustry.world.blocks.storage.*; +import mindustry.world.blocks.units.*; +import mindustry.world.consumers.*; + +//makes terrible seeded bases +public class SeedBaseGenerator{ + private final static int coreDst = 200; + + private Team team; + private Rand rand = new Rand(); + private Tiles tiles; + + public void generate(Tiles tiles, Team team, Tile core){ + this.team = team; + this.tiles = tiles; + + float difficulty = 1f; + + core.setBlock(Blocks.coreShard, team); + rand.nextBoolean(); + + float difficultyScl = Mathf.clamp(difficulty / 20f + rand.range(0.25f), 0f, 0.9999f); + float dscl2 = Mathf.clamp(0.5f + difficulty / 20f + rand.range(0.1f), 0f, 1.5f); + + Array turrets = find(b -> b instanceof ItemTurret); + Array powerTurrets = find(b -> b instanceof PowerTurret); + Array walls = find(b -> b instanceof Wall && !(b instanceof Door) && b.size == 1); + Array drills = find(b -> b instanceof Drill && !b.consumes.has(ConsumeType.power)); + Array powerDrills = find(b -> b instanceof Drill && b.consumes.has(ConsumeType.power)); + + Block wall = walls.get((int)(difficultyScl * walls.size)); + + Turret powerTurret = (Turret)powerTurrets.get((int)(difficultyScl * powerTurrets.size)); + Turret bigTurret = (Turret)turrets.get(Mathf.clamp((int)((difficultyScl + 0.2f + rand.range(0.2f)) * turrets.size), 0, turrets.size-1)); + Turret turret1 = (Turret)turrets.get(Mathf.clamp((int)((difficultyScl + rand.range(0.2f)) * turrets.size), 0, turrets.size-1)); + Turret turret2 = (Turret)turrets.get(Mathf.clamp((int)((difficultyScl + rand.range(0.2f)) * turrets.size), 0, turrets.size-1)); + Drill drill = (Drill)drills.get((int)(difficultyScl * drills.size)); + Drill powerDrill = (Drill)powerDrills.get((int)(difficultyScl * powerDrills.size)); + float placeChance = difficultyScl*0.75f+0.25f; + + IntIntMap ammoPerType = new IntIntMap(); + for(Block turret : turrets){ + if(!(turret instanceof ItemTurret)) continue; + ItemTurret t = (ItemTurret)turret; + int size = t.ammoTypes.size; + ammoPerType.put(t.id, Mathf.clamp((int)(size* difficultyScl) + rand.range(1), 0, size - 1)); + } + + Func3, Boolean> checker = (current, block, pred) -> { + for(Point2 point : Edges.getEdges(block.size)){ + Tile tile = tiles.get(current.x + point.x, current.y + point.y); + if(tile != null){ + if(tile.team() == team && pred.get(tile)){ + return true; + } + } + } + return false; + }; + + Func2, Intc2> seeder = (block, pred) -> (x, y) -> { + if(canPlace(x, y, block) && ((block instanceof Wall && block.size == 1) || rand.chance(placeChance)) && checker.get(tiles.get(x, y), block, pred)){ + setBlock(x, y, block); + } + }; + + Func2 placer = (block, chance) -> (x, y) -> { + if(canPlace(x, y, block) && rand.chance(chance)){ + setBlock(x, y, block); + } + }; + + Array passes = Array.with( + //initial seeding solar panels + (x, y) -> { + Block block = Blocks.largeSolarPanel; + + if(rand.chance(0.001*placeChance) && canPlace(x, y, block)){ + setBlock(x, y, block); + } + }, + + //extra seeding + seeder.get(Blocks.solarPanel, tile -> tile.block() == Blocks.largeSolarPanel && rand.chance(0.3)), + + //drills (not powered) + (x, y) -> { + //if(!rand.chance(0.1*placeChance)) return; + + Item item = drillItem(x, y, drill); + if(item != null && item != Items.sand && canPlace(x, y, drill)){ + setBlock(x, y, drill); + } + }, + + //pump + (x, y) -> { + if(!rand.chance(0.1*placeChance)) return; + + if(tiles.get(x, y).floor().isLiquid && tiles.get(x, y).floor().liquidDrop == Liquids.water){ + setBlock(x, y, Blocks.mechanicalPump); + } + }, + + //coal gens + seeder.get(Blocks.combustionGenerator, tile -> tile.block() instanceof Drill && drillItem(tile.entity.tileX(), tile.entity.tileY(), (Drill)tile.block()) == Items.coal && rand.chance(0.2)), + + //drills (powered) + (x, y) -> { + if(canPlace(x, y, powerDrill) && drillItem(x, y, powerDrill) == Items.thorium && checker.get(tiles.get(x, y), powerDrill, other -> other.block() instanceof PowerGenerator)){ + setBlock(x, y, powerDrill); + } + }, + + //water extractors + seeder.get(Blocks.waterExtractor, tile -> tile.block() instanceof NuclearReactor && rand.chance(0.5)), + + //mend projectors + seeder.get(Blocks.mendProjector, tile -> tile.block() instanceof PowerGenerator && rand.chance(0.04)), + + //power turrets + seeder.get(powerTurret, tile -> tile.block() instanceof PowerGenerator && rand.chance(0.04)), + + //repair point + seeder.get(Blocks.repairPoint, tile -> tile.block() instanceof PowerGenerator && rand.chance(0.1)), + + //turrets1 + seeder.get(turret1, tile -> tile.block() instanceof Drill && rand.chance(0.12)), + + //turrets2 + seeder.get(turret2, tile -> tile.block() instanceof Drill && rand.chance(0.12)), + + //shields + seeder.get(Blocks.forceProjector, tile -> (tile.block() instanceof CoreBlock || tile.block() instanceof UnitFactory) && rand.chance(0.2 * dscl2)), + + //unit pads (assorted) + //seeder.get(Blocks.daggerFactory, tile -> (tile.block() instanceof MendProjector || tile.block() instanceof ForceProjector) && rand.chance(0.3 * dscl2)), + + //unit pads (assorted) + //seeder.get(Blocks.wraithFactory, tile -> (tile.block() instanceof MendProjector || tile.block() instanceof ForceProjector) && rand.chance(0.3 * dscl2)), + + //unit pads (assorted) + //seeder.get(Blocks.titanFactory, tile -> (tile.block() instanceof MendProjector || tile.block() instanceof ForceProjector) && rand.chance(0.23 * dscl2)), + + //unit pads (assorted) + //seeder.get(Blocks.ghoulFactory, tile -> (tile.block() instanceof MendProjector || tile.block() instanceof ForceProjector) && rand.chance(0.23 * dscl2)), + + //vaults + seeder.get(Blocks.vault, tile -> (tile.block() instanceof CoreBlock || tile.block() instanceof ForceProjector) && rand.chance(0.4)), + + //big turrets + seeder.get(bigTurret, tile -> tile.block() instanceof StorageBlock && rand.chance(0.65)), + + //walls + (x, y) -> { + if(!canPlace(x, y, wall)) return; + + for(Point2 point : Geometry.d8){ + Tile tile = tiles.get(x + point.x, y + point.y); + if(tile != null){ + //tile = tile.target(); + if(tile.team() == team && !(tile.block() instanceof Wall) && !(tile.block() instanceof UnitFactory)){ + tiles.get(x, y).setBlock(wall, team); + break; + } + } + } + }, + + //mines + placer.get(Blocks.shockMine, 0.02f * difficultyScl), + + //fill up turrets w/ ammo + (x, y) -> { + Tile tile = tiles.get(x, y); + Block block = tile.block(); + + if(block instanceof ItemTurret){ + ItemTurret turret = (ItemTurret)block; + for(Item item : turret.ammoTypes.keys()){ + tile.entity.handleStack(item, tile.entity.acceptStack(item, 100, null), null); + } + }else if(block instanceof NuclearReactor){ + tile.entity.items().add(Items.thorium, 30); + }else if(block instanceof LiquidTurret){ + tile.entity.liquids().add(Liquids.water, tile.block().liquidCapacity); + } + } + ); + + for(Intc2 i : passes){ + for(int x = 0; x < tiles.width; x++){ + for(int y = 0; y < tiles.height; y++){ + if(!Mathf.within(x, y, core.x, core.y, coreDst)){ + continue; + } + + i.get(x, y); + } + } + } + } + + Item drillItem(int x, int y, Drill block){ + if(block.isMultiblock()){ + Item result = null; + int offsetx = -(block.size - 1) / 2; + int offsety = -(block.size - 1) / 2; + + for(int dx = 0; dx < block.size; dx++){ + for(int dy = 0; dy < block.size; dy++){ + int worldx = dx + offsetx + x; + int worldy = dy + offsety + y; + if(!tiles.in(worldx, worldy)){ + return null; + } + + if(!block.canPlaceOn(tiles.get(worldx, worldy)) || tiles.get(worldx, worldy).drop() == null) continue; + + Item drop = tiles.get(worldx, worldy).drop(); + + if(result == null || drop.id < result.id){ + result = drop; + } + + } + } + return result; + }else{ + return tiles.get(x, y).drop(); + } + } + + void setBlock(int x, int y, Block block){ + tiles.get(x, y).setBlock(block, team); + } + + boolean canPlace(int x, int y, Block block){ + return Build.validPlace(team, x, y, block, 0); + } + + Array find(Boolf pred){ + Array out = new Array<>(); + for(Block block : Vars.content.blocks()){ + if(pred.get(block) && block.isPlaceable()){ + out.add(block); + } + } + return out; + } +} diff --git a/core/src/mindustry/maps/planet/TODOPlanetGenerator.java b/core/src/mindustry/maps/planet/TODOPlanetGenerator.java index 7558f1a03f..41ca7cde2c 100644 --- a/core/src/mindustry/maps/planet/TODOPlanetGenerator.java +++ b/core/src/mindustry/maps/planet/TODOPlanetGenerator.java @@ -6,6 +6,7 @@ import arc.math.geom.*; import arc.struct.*; import arc.util.*; import arc.util.noise.*; +import mindustry.ai.*; import mindustry.content.*; import mindustry.game.*; import mindustry.maps.generators.*; @@ -131,7 +132,7 @@ public class TODOPlanetGenerator extends PlanetGenerator{ connected.add(to); float nscl = rand.random(20f, 60f); int stroke = rand.random(4, 12); - brush(pathfind(x, y, to.x, to.y, tile -> (tile.solid() ? 5f : 0f) + noise(tile.x, tile.y, 1, 1, 1f / nscl) * 60, manhattan), stroke); + brush(pathfind(x, y, to.x, to.y, tile -> (tile.solid() ? 5f : 0f) + noise(tile.x, tile.y, 1, 1, 1f / nscl) * 60, Astar.manhattan), stroke); } } diff --git a/core/src/mindustry/ui/dialogs/PlanetDialog.java b/core/src/mindustry/ui/dialogs/PlanetDialog.java index 4605d3f66a..a748879811 100644 --- a/core/src/mindustry/ui/dialogs/PlanetDialog.java +++ b/core/src/mindustry/ui/dialogs/PlanetDialog.java @@ -333,6 +333,16 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{ hide(); + //save before launch. + if(control.saves.getCurrent() != null && state.isGame()){ + try{ + control.saves.getCurrent().save(); + }catch(Throwable e){ + e.printStackTrace(); + ui.showException("[accent]" + Core.bundle.get("savefail"), e); + } + } + if(mode == launch){ launcher.launch(); zoom = 0.5f; diff --git a/core/src/mindustry/world/Build.java b/core/src/mindustry/world/Build.java index c4f925f138..701ddb20d1 100644 --- a/core/src/mindustry/world/Build.java +++ b/core/src/mindustry/world/Build.java @@ -91,7 +91,7 @@ public class Build{ } //TODO should water blocks be placeable here? - if(/*!type.requiresWater && */!contactsGround(tile.x, tile.y, type)){ + if(/*!type.requiresWater && */!contactsShallows(tile.x, tile.y, type)){ return false; } @@ -118,7 +118,7 @@ public class Build{ return true; }else{ return tile.interactable(team) - && contactsGround(tile.x, tile.y, type) + && contactsShallows(tile.x, tile.y, type) && (!tile.floor().isDeep() || type.floating || type.requiresWater) && tile.floor().placeableOn && (!type.requiresWater || tile.floor().liquidDrop == Liquids.water) @@ -128,7 +128,22 @@ public class Build{ } } - private static boolean contactsGround(int x, int y, Block block){ + public static boolean contactsGround(int x, int y, Block block){ + if(block.isMultiblock()){ + for(Point2 point : Edges.getEdges(block.size)){ + Tile tile = world.tile(x + point.x, y + point.y); + if(tile != null && !tile.floor().isLiquid) return true; + } + }else{ + for(Point2 point : Geometry.d4){ + Tile tile = world.tile(x + point.x, y + point.y); + if(tile != null && !tile.floor().isLiquid) return true; + } + } + return false; + } + + public static boolean contactsShallows(int x, int y, Block block){ if(block.isMultiblock()){ for(Point2 point : Edges.getInsideEdges(block.size)){ Tile tile = world.tile(x + point.x, y + point.y); diff --git a/core/src/mindustry/world/Tile.java b/core/src/mindustry/world/Tile.java index b773ff5e98..545378c23a 100644 --- a/core/src/mindustry/world/Tile.java +++ b/core/src/mindustry/world/Tile.java @@ -246,6 +246,15 @@ public class Tile implements Position, QuadTreeObject{ setOverlay(overlay); } + /** Sets the block to air. */ + public void setAir(){ + setBlock(Blocks.air); + } + + public void circle(int radius, Intc2 cons){ + Geometry.circle(x, y, world.width(), world.height(), radius, cons); + } + public void recache(){ if(!headless && !world.isGenerating()){ renderer.blocks.floor.recacheTile(this); diff --git a/core/src/mindustry/world/blocks/campaign/CoreLauncher.java b/core/src/mindustry/world/blocks/campaign/CoreLauncher.java index ba6a407b23..e61624d757 100644 --- a/core/src/mindustry/world/blocks/campaign/CoreLauncher.java +++ b/core/src/mindustry/world/blocks/campaign/CoreLauncher.java @@ -1,5 +1,7 @@ package mindustry.world.blocks.campaign; +import arc.Graphics.*; +import arc.Graphics.Cursor.*; import arc.graphics.g2d.*; import arc.math.*; import arc.util.*; @@ -11,7 +13,7 @@ import mindustry.graphics.*; import mindustry.ui.*; import mindustry.world.*; -import static mindustry.Vars.state; +import static mindustry.Vars.*; public class CoreLauncher extends Block{ public int range = 1; @@ -38,18 +40,23 @@ public class CoreLauncher extends Block{ if(state.isCampaign() && consValid()){ Vars.ui.planet.show(state.rules.sector, range, this); - - cons.trigger(); } return false; } + @Override + public Cursor getCursor(){ + return consValid() ? SystemCursor.hand : SystemCursor.arrow; + } + public void launch(){ LaunchCorec ent = LaunchCoreEntity.create(); ent.set(this); ent.block(Blocks.coreShard); ent.lifetime(Vars.launchDuration); ent.add(); + + cons.trigger(); } } @@ -116,11 +123,5 @@ public class CoreLauncher extends Block{ Fx.rocketSmokeLarge.at(cx() + Mathf.range(r), cy() + Mathf.range(r), fin()); } } - - @Override - public void remove(){ - - //TODO something - } } } diff --git a/core/src/mindustry/world/blocks/experimental/BlockForge.java b/core/src/mindustry/world/blocks/experimental/BlockForge.java index 8f05136ceb..22e415e609 100644 --- a/core/src/mindustry/world/blocks/experimental/BlockForge.java +++ b/core/src/mindustry/world/blocks/experimental/BlockForge.java @@ -124,26 +124,7 @@ public class BlockForge extends PayloadAcceptor{ Draw.rect(outRegion, x, y, rotdeg()); if(recipe != null){ - Draw.draw(Layer.blockOver, () -> { - TextureRegion region = recipe.icon(Cicon.full); - - Shaders.build.region = region; - Shaders.build.progress = progress / recipe.buildCost; - Shaders.build.color.set(Pal.accent); - Shaders.build.color.a = heat; - Shaders.build.time = -time / 20f; - - Draw.shader(Shaders.build); - Draw.rect(region, x, y); - Draw.shader(); - - Draw.color(Pal.accent); - Draw.alpha(heat); - - Lines.lineAngleCenter(x + Mathf.sin(time, 20f, Vars.tilesize / 2f * size - 2f), y, 90, size * Vars.tilesize - 4f); - - Draw.reset(); - }); + Draw.draw(Layer.blockOver, () -> Drawf.construct(this, recipe, 0, progress / recipe.buildCost, heat, time)); } drawPayload();