New seed-based AI bases

This commit is contained in:
Anuken
2018-09-01 14:44:06 -04:00
parent a2960f5c50
commit dcc061af3e
115 changed files with 1107 additions and 1138 deletions

View File

@@ -54,7 +54,7 @@ public class PowerBlocks extends BlockList implements ContentList{
thoriumReactor = new NuclearReactor("thorium-reactor"){{
size = 3;
health = 700;
powerMultiplier = 0.8f;
powerMultiplier = 1.1f;
}};
fusionReactor = new FusionReactor("fusion-reactor"){{

View File

@@ -307,7 +307,7 @@ public class Player extends Unit implements BuilderTrait, CarryTrait, ShooterTra
}
if(floor.isLiquid){
Draw.tint(Color.WHITE, floor.liquidColor, drownTime * 0.4f);
Draw.tint(Color.WHITE, floor.liquidColor, drownTime);
}else{
Draw.tint(Color.WHITE);
}

View File

@@ -1,30 +1,42 @@
package io.anuke.mindustry.maps.generation;
import com.badlogic.gdx.math.GridPoint2;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.IntIntMap;
import com.badlogic.gdx.utils.Predicate;
import io.anuke.mindustry.content.Items;
import io.anuke.mindustry.content.blocks.DistributionBlocks;
import io.anuke.mindustry.content.blocks.StorageBlocks;
import io.anuke.mindustry.content.Liquids;
import io.anuke.mindustry.content.blocks.*;
import io.anuke.mindustry.game.Team;
import io.anuke.mindustry.maps.generation.pathfinding.AStarPathFinder;
import io.anuke.mindustry.type.AmmoType;
import io.anuke.mindustry.type.Item;
import io.anuke.mindustry.type.Recipe;
import io.anuke.mindustry.world.Block;
import io.anuke.mindustry.world.Edges;
import io.anuke.mindustry.world.Tile;
import io.anuke.mindustry.world.blocks.defense.Door;
import io.anuke.mindustry.world.blocks.defense.MendProjector;
import io.anuke.mindustry.world.blocks.defense.Wall;
import io.anuke.mindustry.world.blocks.defense.turrets.ItemTurret;
import io.anuke.mindustry.world.blocks.defense.turrets.PowerTurret;
import io.anuke.mindustry.world.blocks.distribution.Conveyor;
import io.anuke.mindustry.world.blocks.power.SolarGenerator;
import io.anuke.mindustry.world.blocks.defense.turrets.Turret;
import io.anuke.mindustry.world.blocks.power.NuclearReactor;
import io.anuke.mindustry.world.blocks.power.PowerGenerator;
import io.anuke.mindustry.world.blocks.production.Drill;
import io.anuke.mindustry.world.blocks.production.Pump;
import io.anuke.mindustry.world.blocks.storage.CoreBlock;
import io.anuke.mindustry.world.blocks.storage.StorageBlock;
import io.anuke.mindustry.world.blocks.units.UnitPad;
import io.anuke.mindustry.world.consumers.ConsumePower;
import io.anuke.ucore.function.BiFunction;
import io.anuke.ucore.function.IntPositionConsumer;
import io.anuke.ucore.function.TriFunction;
import io.anuke.ucore.util.Mathf;
public class FortressGenerator{
private final static int coreDst = 60;
private final static int coreDst = 120;
private int enemyX, enemyY, coreX, coreY;
private Team team;
@@ -41,143 +53,186 @@ public class FortressGenerator{
gen();
}
enum Pass{
walls((x, y) -> {
});
final IntPositionConsumer cons;
Pass(IntPositionConsumer cons){
this.cons = cons;
}
}
void gen(){
gen.setBlock(enemyX, enemyY, StorageBlocks.core, team);
float difficultyScl = Mathf.clamp(gen.sector.difficulty / 20f + gen.random.range(1f/2f), 0f, 0.9999f);
int coreDst = FortressGenerator.coreDst*gen.sector.size;
Array<Block> turrets = find(b -> (b instanceof ItemTurret && accepts(((ItemTurret) b).getAmmoTypes(), Items.copper) || b instanceof PowerTurret));
Array<Block> turrets = find(b -> b instanceof ItemTurret);
Array<Block> powerTurrets = find(b -> b instanceof PowerTurret);
Array<Block> drills = find(b -> b instanceof Drill && !b.consumes.has(ConsumePower.class));
Array<Block> gens = find(b -> b instanceof SolarGenerator);
Array<Block> powerDrills = find(b -> b instanceof Drill && b.consumes.has(ConsumePower.class));
Array<Block> walls = find(b -> b instanceof Wall && !(b instanceof Door) && b.size == 1);
Array<Block> wallsLarge = find(b -> b instanceof Wall && !(b instanceof Door) && b.size == 2);
Block wall = walls.get((int)(difficultyScl * walls.size));
Block wallLarge = wallsLarge.get((int)(difficultyScl * walls.size));
Drill drill = (Drill) drills.get((int)(difficultyScl * drills.size));
Item[] items = {Items.copper, Items.lead};
Drill powerDrill = (Drill) powerDrills.get((int)(difficultyScl * powerDrills.size));
Turret powerTurret = (Turret) powerTurrets.get((int)(difficultyScl * powerTurrets.size));
Turret bigTurret = (Turret) turrets.get(Mathf.clamp((int)((difficultyScl+0.3f+gen.random.range(0.2f)) * turrets.size), 0, turrets.size-1));
Turret turret1 = (Turret) turrets.get(Mathf.clamp((int)((difficultyScl+gen.random.range(0.2f)) * turrets.size), 0, turrets.size-1));
Turret turret2 = (Turret) turrets.get(Mathf.clamp((int)((difficultyScl+gen.random.range(0.2f)) * turrets.size), 0, turrets.size-1));
AStarPathFinder finder = new AStarPathFinder(gen.tiles);
//place down drills
for(int x = 0; x < gen.width; x++){
for(int y = 0; y < gen.height; y++){
if(Vector2.dst(x, y, enemyX, enemyY) > coreDst){
continue;
}
Item item = gen.drillItem(x, y, drill);
Block generator = gens.get(gen.random.random(0, gens.size-1));
boolean contains = false;
if(item != null){
for(Item other : items){
if(other == item){
contains = true;
break;
}
}
}
if(item != null && contains && gen.canPlace(x, y, drill) && !gen.random.chance(0.5)){
Array<Tile> out = new Array<>();
finder.search(gen.tile(x, y), gen.tile(enemyX, enemyY - 2),
tile -> (tile.block() instanceof Conveyor || (Math.abs(tile.x - enemyX) <= 2 && Math.abs(tile.y - enemyY) <= 2))
&& (Math.abs(tile.x - enemyX) != Math.abs(tile.y - enemyY)),
out);
byte last = 0;
for (int i = 0; i < out.size; i++) {
Tile current = out.get(i);
if(i != out.size - 1){
Tile next = out.get(i + 1);
byte rotation = current.relativeTo(next.x, next.y);
current.setBlock(DistributionBlocks.conveyor, team);
current.setRotation(rotation);
last = rotation;
}else{
current.setBlock(DistributionBlocks.conveyor, team);
current.setRotation(last);
}
}
gen.setBlock(x, y, drill, team);
}else if(gen.canPlace(x, y, generator) && gen.random.chance(0.01)){
gen.setBlock(x, y, generator, team);
}
}
IntIntMap ammoPerType = new IntIntMap();
for(Block turret : turrets){
if(!(turret instanceof ItemTurret)) continue;
ItemTurret t = (ItemTurret)turret;
int size = t.getAmmoTypes().length;
ammoPerType.put(t.id, Mathf.clamp((int)(size* difficultyScl) + gen.random.range(1), 0, size - 1));
}
/*
TriFunction<Tile, Block, Predicate<Tile>, Boolean> checker = (current, block, pred) -> {
for(GridPoint2 point : Edges.getEdges(block.size)){
Tile tile = gen.tile(current.x + point.x, current.y + point.y);
if(tile != null){
tile = tile.target();
if(tile.getTeamID() == team.ordinal() && pred.evaluate(tile)){
return true;
}
}
}
return false;
};
//Turret turret = (Turret) turrets.first();
BiFunction<Block, Predicate<Tile>, IntPositionConsumer> seeder = (block, pred) -> (x, y) -> {
if(gen.canPlace(x, y, block) && ((block instanceof Wall && block.size == 1) || gen.random.chance(difficultyScl/2f+0.5f)) && checker.get(gen.tile(x, y), block, pred)){
gen.setBlock(x, y, block, team);
}
};
//place down turrets
for(int x = 0; x < gen.width; x++){
for(int y = 0; y < gen.height; y++){
for(Block block : turrets){
Turret turret = (Turret)block;
if(Vector2.dst(x, y, enemyX, enemyY) > coreDst + 4 || !gen.canPlace(x, y, turret) || !gen.random.chance(0.5)){
Array<IntPositionConsumer> passes = Array.with(
//initial seeding solar panels
(x, y) -> {
Block block = PowerBlocks.largeSolarPanel;
if(gen.random.chance(0.001) && gen.canPlace(x, y, block)){
gen.setBlock(x, y, block, team);
}
},
//extra seeding
seeder.get(PowerBlocks.solarPanel, tile -> tile.block() == PowerBlocks.largeSolarPanel && gen.random.chance(0.3)),
//drills (not powered)
(x, y) -> {
if(!gen.random.chance(0.1)) return;
Item item = gen.drillItem(x, y, drill);
if(item != null && item != Items.stone && item != Items.sand && gen.canPlace(x, y, drill)){
gen.setBlock(x, y, drill, team);
}
},
//drills (not powered)
(x, y) -> {
if(!gen.random.chance(0.1)) return;
if(gen.tile(x, y).floor().isLiquid && gen.tile(x, y).floor().liquidDrop == Liquids.water){
gen.setBlock(x, y, LiquidBlocks.mechanicalPump, team);
}
},
//coal gens
seeder.get(PowerBlocks.combustionGenerator, tile -> tile.block() instanceof Drill && gen.drillItem(tile.x, tile.y, (Drill)tile.block()) == Items.coal && gen.random.chance(0.2)),
//drills (powered)
(x, y) -> {
if(gen.random.chance(0.4) && gen.canPlace(x, y, powerDrill) && gen.drillItem(x, y, powerDrill) == Items.thorium && checker.get(gen.tile(x, y), powerDrill, other -> other.block() instanceof PowerGenerator)){
gen.setBlock(x, y, powerDrill, team);
}
},
//nuclear reactors
seeder.get(PowerBlocks.thoriumReactor, tile -> tile.block() instanceof Drill && gen.random.chance(0.2) && gen.drillItem(tile.x, tile.y, (Drill)tile.block()) == Items.thorium && gen.random.chance(0.3)),
//water extractors
seeder.get(ProductionBlocks.waterextractor, tile -> tile.block() instanceof NuclearReactor && gen.random.chance(0.5)),
//mend cores
seeder.get(DefenseBlocks.mendProjector, tile -> tile.block() instanceof PowerGenerator && gen.random.chance(0.03)),
//unit pads (assorted)
seeder.get(UnitBlocks.daggerPad, tile -> tile.block() instanceof MendProjector && gen.random.chance(0.3)),
//unit pads (assorted)
seeder.get(UnitBlocks.interceptorPad, tile -> tile.block() instanceof MendProjector && gen.random.chance(0.3)),
//unit pads (assorted)
seeder.get(UnitBlocks.titanPad, tile -> tile.block() instanceof MendProjector && gen.random.chance(0.23)),
//unit pads (assorted)
seeder.get(UnitBlocks.monsoonPad, tile -> tile.block() instanceof MendProjector && gen.random.chance(0.23)),
//power turrets
seeder.get(powerTurret, tile -> tile.block() instanceof PowerGenerator && gen.random.chance(0.04)),
//repair point
seeder.get(UnitBlocks.repairPoint, tile -> tile.block() instanceof PowerGenerator && gen.random.chance(0.1)),
//turrets1
seeder.get(turret1, tile -> tile.block() instanceof Pump && gen.random.chance(0.22 - turret1.size*0.02)),
//turrets2
seeder.get(turret2, tile -> tile.block() instanceof Drill && gen.random.chance(0.12 - turret2.size*0.02)),
//vaults
seeder.get(StorageBlocks.vault, tile -> tile.block() instanceof CoreBlock && gen.random.chance(0.4)),
//big turrets
seeder.get(bigTurret, tile -> tile.block() instanceof StorageBlock && gen.random.chance(0.65)),
//walls (large)
seeder.get(wallLarge, tile -> !(tile.block() instanceof Wall) && !(tile.block() instanceof UnitPad)),
//walls
seeder.get(wall, tile -> !(tile.block() instanceof Wall) && !(tile.block() instanceof UnitPad)),
//fill up turrets w/ ammo
(x, y) -> {
Tile tile = gen.tile(x, y);
Block block = tile.block();
if(block instanceof PowerTurret){
tile.entity.power.amount = block.powerCapacity;
}else if(block instanceof ItemTurret){
ItemTurret turret = (ItemTurret)block;
AmmoType[] type = turret.getAmmoTypes();
int index = ammoPerType.get(block.id, 0);
block.handleStack(type[index].item, block.acceptStack(type[index].item, 1000, tile, null), tile, null);
}else if(block instanceof NuclearReactor){
tile.entity.items.add(Items.thorium, 30);
}
}
);
for(IntPositionConsumer i : passes){
for(int x = 0; x < gen.width; x++){
for(int y = 0; y < gen.height; y++){
if(Vector2.dst(x, y, enemyX, enemyY) > coreDst){
continue;
}
boolean found = false;
for(GridPoint2 point : Edges.getEdges(turret.size)){
Tile tile = gen.tile(x + point.x, y + point.y);
if(tile != null){
tile = tile.target();
if(turret instanceof PowerTurret && tile.target().block() instanceof PowerGenerator){
found = true;
break;
}else if(turret instanceof ItemTurret && tile.block() instanceof Drill && accepts(((ItemTurret) turret).getAmmoTypes(), gen.drillItem(tile.x, tile.y, (Drill) tile.block()))){
found = true;
break;
}
}
}
if(found){
gen.setBlock(x, y, turret, team);
break;
}
i.accept(x, y);
}
}
}
//place down drills
for(int x = 0; x < gen.width; x++){
for(int y = 0; y < gen.height; y++){
if(Vector2.dst(x, y, enemyX, enemyY) > coreDst || !gen.canPlace(x, y, wall)){
continue;
}
boolean found = false;
for(GridPoint2 point : Edges.getEdges(wall.size)){
Tile tile = gen.tile(x + point.x, y + point.y);
if(tile != null){
tile = tile.target();
if(tile.getTeamID() == team.ordinal() && !(tile.block() instanceof Wall)){
found = true;
break;
}
}
}
if(found){
gen.setBlock(x, y, wall, team);
}
}
}*/
}
boolean accepts(AmmoType[] types, Item item){
for(AmmoType type : types){
if(type.item == item){
return true;
}
}
return false;
}
Array<Block> find(Predicate<Block> pred){

View File

@@ -19,8 +19,7 @@ import io.anuke.ucore.function.Consumer;
import io.anuke.ucore.util.Bits;
import io.anuke.ucore.util.Geometry;
import static io.anuke.mindustry.Vars.tilesize;
import static io.anuke.mindustry.Vars.world;
import static io.anuke.mindustry.Vars.*;
public class Tile implements PosTrait, TargetTrait{
@@ -261,6 +260,10 @@ public class Tile implements PosTrait, TargetTrait{
}
}
public boolean isEnemyCheat(){
return getTeam() == waveTeam && state.mode.autoSpawn;
}
public boolean isLinked(){
return link != 0;
}

View File

@@ -53,6 +53,7 @@ public abstract class LiquidTurret extends Turret{
@Override
public AmmoType useAmmo(Tile tile){
TurretEntity entity = tile.entity();
if(tile.isEnemyCheat()) return liquidAmmoMap.get(entity.liquids.current());
AmmoType type = liquidAmmoMap.get(entity.liquids.current());
entity.liquids.remove(type.liquid, type.quantityMultiplier);
return type;

View File

@@ -28,6 +28,7 @@ public abstract class PowerTurret extends CooledTurret{
@Override
public AmmoType useAmmo(Tile tile){
if(tile.isEnemyCheat()) return shootType;
tile.entity.power.amount -= powerUsed;
return shootType;
}

View File

@@ -226,10 +226,10 @@ public abstract class Turret extends Block{
return true;
}
/**
* Consume ammo and return a type.
*/
/**Consume ammo and return a type.*/
public AmmoType useAmmo(Tile tile){
if(tile.isEnemyCheat()) return peekAmmo(tile);
TurretEntity entity = tile.entity();
AmmoEntry entry = entity.ammo.peek();
entry.amount -= ammoPerShot;

View File

@@ -1,26 +0,0 @@
package io.anuke.mindustry.world.blocks.effect;
import io.anuke.mindustry.graphics.Palette;
import io.anuke.mindustry.world.Block;
import io.anuke.mindustry.world.Tile;
import io.anuke.ucore.graphics.Draw;
import io.anuke.ucore.graphics.Lines;
import static io.anuke.mindustry.Vars.tilesize;
public abstract class EffectCore extends Block{
protected int range = 7;
public EffectCore(String name){
super(name);
update = true;
solid = true;
}
@Override
public void drawSelect(Tile tile){
Draw.color(Palette.accent);
Lines.dashCircle(tile.drawx(), tile.drawy(), range * tilesize);
Draw.reset();
}
}

View File

@@ -1,32 +0,0 @@
package io.anuke.mindustry.world.blocks.effect;
import io.anuke.mindustry.world.Tile;
import io.anuke.ucore.core.Timers;
import io.anuke.ucore.util.Mathf;
import static io.anuke.mindustry.Vars.world;
public class MendingCore extends EffectCore{
/**
* Mending speed as a percentage of block health, per frame.
*/
protected float mendSpeed = 0.8f;
public MendingCore(String name){
super(name);
}
@Override
public void update(Tile tile){
for(int dx = Math.max(-range + tile.x, 0); dx <= Math.min(range + tile.y, world.width() - 1); dx++){
for(int dy = Math.max(-range + tile.y, 0); dy <= Math.min(range + tile.y, world.height() - 1); dy++){
Tile other = world.tile(dx, dy);
if(other.entity != null){
other.entity.health = Mathf.clamp(other.entity.health + 1f / other.block().health * mendSpeed * Timers.delta(), 0, other.block().health);
}
}
}
}
}

View File

@@ -35,9 +35,6 @@ import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import static io.anuke.mindustry.Vars.state;
import static io.anuke.mindustry.Vars.waveTeam;
public class UnitPad extends Block{
protected float gracePeriodMultiplier = 23f;
protected float speedupTime = 60f * 60f * 20;
@@ -152,13 +149,11 @@ public class UnitPad extends Block{
entity.time += Timers.delta() * entity.speedScl;
boolean isEnemy = tile.getTeam() == waveTeam && state.mode.autoSpawn;
if(isEnemy){
if(tile.isEnemyCheat()){
entity.warmup += Timers.delta();
}
if(!isEnemy){
if(!tile.isEnemyCheat()){
//player-made spawners have default behavior
if(hasRequirements(entity.items, entity.buildTime / produceTime) && entity.cons.valid()){