Added unit squads, new spawning, WaveSpawner class
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
package io.anuke.mindustry.ai;
|
||||
|
||||
import com.badlogic.gdx.utils.Bits;
|
||||
import com.badlogic.gdx.utils.IntMap;
|
||||
import com.badlogic.gdx.utils.ObjectMap;
|
||||
import com.badlogic.gdx.utils.ObjectSet;
|
||||
@@ -7,9 +8,10 @@ import io.anuke.mindustry.content.Items;
|
||||
import io.anuke.mindustry.game.EventType.TileChangeEvent;
|
||||
import io.anuke.mindustry.game.EventType.WorldLoadEvent;
|
||||
import io.anuke.mindustry.game.Team;
|
||||
import io.anuke.mindustry.game.TeamInfo.TeamData;
|
||||
import io.anuke.mindustry.type.Item;
|
||||
import io.anuke.mindustry.world.meta.BlockFlag;
|
||||
import io.anuke.mindustry.world.Tile;
|
||||
import io.anuke.mindustry.world.meta.BlockFlag;
|
||||
import io.anuke.ucore.core.Events;
|
||||
import io.anuke.ucore.util.EnumSet;
|
||||
import io.anuke.ucore.util.Mathf;
|
||||
@@ -18,16 +20,22 @@ import static io.anuke.mindustry.Vars.state;
|
||||
import static io.anuke.mindustry.Vars.world;
|
||||
|
||||
//TODO consider using quadtrees for finding specific types of blocks within an area
|
||||
/**Class used for indexing special target blocks for AI.
|
||||
* TODO maybe use Arrays instead of ObjectSets?*/
|
||||
//TODO maybe use Arrays instead of ObjectSets?
|
||||
/**Class used for indexing special target blocks for AI.*/
|
||||
public class BlockIndexer {
|
||||
/**Size of one ore quadrant.*/
|
||||
private final static int quadrantSize = 12;
|
||||
private final static int oreQuadrantSize = 12;
|
||||
/**Size of one structure quadrant.*/
|
||||
private final static int structQuadrantSize = 12;
|
||||
|
||||
/**Set of all ores that are being scanned.*/
|
||||
private final ObjectSet<Item> scanOres = ObjectSet.with(Items.iron, Items.coal, Items.lead, Items.thorium, Items.titanium);
|
||||
/**Stores all ore quadtrants on the map.*/
|
||||
private ObjectMap<Item, ObjectSet<Tile>> ores = new ObjectMap<>();
|
||||
|
||||
/**Tags all quadrants.*/
|
||||
private Bits[] structQuadrants;
|
||||
|
||||
/**Maps teams to a map of flagged tiles by type.*/
|
||||
private ObjectMap<BlockFlag, ObjectSet<Tile>> enemyMap = new ObjectMap<>();
|
||||
/**Maps teams to a map of flagged tiles by type.*/
|
||||
@@ -48,6 +56,7 @@ public class BlockIndexer {
|
||||
}
|
||||
}
|
||||
process(tile);
|
||||
updateQuadrant(tile);
|
||||
});
|
||||
|
||||
Events.on(WorldLoadEvent.class, () -> {
|
||||
@@ -55,6 +64,13 @@ public class BlockIndexer {
|
||||
allyMap.clear();
|
||||
typeMap.clear();
|
||||
ores.clear();
|
||||
|
||||
//create bitset for each team type that contains each quadrant
|
||||
structQuadrants = new Bits[Team.values().length];
|
||||
for(int i = 0; i < Team.values().length; i ++){
|
||||
structQuadrants[i] = new Bits(Mathf.ceil(world.width() / (float)structQuadrantSize) * Mathf.ceil(world.height() / (float)structQuadrantSize));
|
||||
}
|
||||
|
||||
for(int x = 0; x < world.width(); x ++){
|
||||
for (int y = 0; y < world.height(); y++) {
|
||||
process(world.tile(x, y));
|
||||
@@ -77,7 +93,7 @@ public class BlockIndexer {
|
||||
|
||||
/**Returns a set of tiles that have ores of the specified type nearby.
|
||||
* While each tile in the set is not guaranteed to have an ore directly on it,
|
||||
* each tile will at least have an ore within {@link #quadrantSize} / 2 blocks of it.
|
||||
* each tile will at least have an ore within {@link #oreQuadrantSize} / 2 blocks of it.
|
||||
* Only specific ore types are scanned. See {@link #scanOres}.*/
|
||||
public ObjectSet<Tile> getOrePositions(Item item){
|
||||
return ores.get(item, emptyArray);
|
||||
@@ -104,6 +120,36 @@ public class BlockIndexer {
|
||||
}
|
||||
}
|
||||
|
||||
private void updateQuadrant(Tile tile){
|
||||
//this quadrant is now 'dirty', re-scan the whole thing
|
||||
int quadrantX = tile.x / structQuadrantSize;
|
||||
int quadrantY = tile.y / structQuadrantSize;
|
||||
int index = quadrantX * Mathf.ceil(world.width() / (float)structQuadrantSize) + quadrantY;
|
||||
|
||||
for(TeamData data : state.teams.getTeams()) {
|
||||
|
||||
//fast-set this quadrant to 'occupied' if the tile just placed is already of this team
|
||||
if(tile.getTeam() == data.team && tile.entity != null){
|
||||
structQuadrants[data.team.ordinal()].set(index);
|
||||
continue; //no need to process futher
|
||||
}
|
||||
|
||||
structQuadrants[data.team.ordinal()].clear(index);
|
||||
|
||||
outer:
|
||||
for (int x = quadrantX * structQuadrantSize; x < world.width() && x < (quadrantX + 1) * structQuadrantSize; x++) {
|
||||
for (int y = quadrantY * structQuadrantSize; y < world.height() && y < (quadrantY + 1) * structQuadrantSize; y++) {
|
||||
Tile result = world.tile(x, y);
|
||||
//when a targetable block is found, mark this quadrant as occupied and stop searching
|
||||
if(result.entity != null && result.getTeam() == data.team){
|
||||
structQuadrants[data.team.ordinal()].set(index);
|
||||
break outer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private ObjectMap<BlockFlag, ObjectSet<Tile>> getMap(Team team){
|
||||
if(!state.teams.has(team)) return emptyMap;
|
||||
return state.teams.get(team).ally ? allyMap : enemyMap;
|
||||
@@ -117,8 +163,8 @@ public class BlockIndexer {
|
||||
|
||||
for(int x = 0; x < world.width(); x ++){
|
||||
for (int y = 0; y < world.height(); y++) {
|
||||
int qx = (x/quadrantSize);
|
||||
int qy = (y/quadrantSize);
|
||||
int qx = (x/ oreQuadrantSize);
|
||||
int qy = (y/ oreQuadrantSize);
|
||||
|
||||
Tile tile = world.tile(x, y);
|
||||
|
||||
@@ -126,8 +172,8 @@ public class BlockIndexer {
|
||||
if(tile.floor().drops != null && scanOres.contains(tile.floor().drops.item)){
|
||||
ores.get(tile.floor().drops.item).add(world.tile(
|
||||
//make sure to clamp quadrant middle position, since it might go off bounds
|
||||
Mathf.clamp(qx * quadrantSize + quadrantSize/2, 0, world.width() - 1),
|
||||
Mathf.clamp(qy * quadrantSize + quadrantSize/2, 0, world.height() - 1)));
|
||||
Mathf.clamp(qx * oreQuadrantSize + oreQuadrantSize /2, 0, world.width() - 1),
|
||||
Mathf.clamp(qy * oreQuadrantSize + oreQuadrantSize /2, 0, world.height() - 1)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
package io.anuke.mindustry.ai;
|
||||
|
||||
import io.anuke.mindustry.game.EventType.WorldLoadEvent;
|
||||
import io.anuke.ucore.core.Events;
|
||||
|
||||
import static io.anuke.mindustry.Vars.world;
|
||||
|
||||
public class SpawnSelector {
|
||||
private static final int quadsize = 15;
|
||||
|
||||
public SpawnSelector(){
|
||||
Events.on(WorldLoadEvent.class, this::reset);
|
||||
}
|
||||
|
||||
public void calculateSpawn(){
|
||||
|
||||
for(int x = 0; x < world.width(); x += quadsize){
|
||||
for(int y = 0; y < world.height(); y += quadsize){
|
||||
//TODO quadrant operations, etc
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void reset(){
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
package io.anuke.mindustry.ai;
|
||||
|
||||
import com.badlogic.gdx.utils.Array;
|
||||
import io.anuke.mindustry.content.AmmoTypes;
|
||||
import io.anuke.mindustry.content.UnitTypes;
|
||||
import io.anuke.mindustry.entities.units.BaseUnit;
|
||||
import io.anuke.mindustry.entities.units.Squad;
|
||||
import io.anuke.mindustry.game.EventType.WorldLoadEvent;
|
||||
import io.anuke.mindustry.game.Team;
|
||||
import io.anuke.ucore.core.Events;
|
||||
import io.anuke.ucore.util.Mathf;
|
||||
|
||||
import static io.anuke.mindustry.Vars.*;
|
||||
|
||||
public class WaveSpawner {
|
||||
private static final int quadsize = 15;
|
||||
|
||||
private Array<FlyerSpawn> flySpawns = new Array<>();
|
||||
private Array<GroundSpawn> groundSpawns = new Array<>();
|
||||
|
||||
public WaveSpawner(){
|
||||
Events.on(WorldLoadEvent.class, this::reset);
|
||||
}
|
||||
|
||||
public void spawnEnemies(){
|
||||
int spawned = 10;
|
||||
int groundGroups = Math.min(1 + state.wave / 20, 4);
|
||||
int flyGroups = Math.min(1 + state.wave / 20, 4);
|
||||
|
||||
//add extra groups if necessary
|
||||
for (int i = 0; i < groundGroups - groundSpawns.size; i++) {
|
||||
GroundSpawn spawn = new GroundSpawn();
|
||||
}
|
||||
|
||||
for (int i = 0; i < flyGroups - flySpawns.size; i++) {
|
||||
FlyerSpawn spawn = new FlyerSpawn();
|
||||
spawn.angle = Mathf.random(360f);
|
||||
|
||||
flySpawns.add(spawn);
|
||||
}
|
||||
|
||||
for(GroundSpawn spawn : groundSpawns){
|
||||
|
||||
}
|
||||
|
||||
for(FlyerSpawn spawn : flySpawns){
|
||||
Squad squad = new Squad();
|
||||
float addition = 40f;
|
||||
float spread = addition / 1.5f;
|
||||
|
||||
float baseX = world.width() *tilesize/2f + Mathf.sqrwavex(spawn.angle) * (world.width()/2f*tilesize + addition),
|
||||
baseY = world.height() * tilesize/2f + Mathf.sqrwavey(spawn.angle) * (world.height()/2f*tilesize + addition);
|
||||
|
||||
for(int i = 0; i < spawned; i ++){
|
||||
BaseUnit unit = UnitTypes.vtol.create(Team.red);
|
||||
unit.inventory.addAmmo(AmmoTypes.bulletIron);
|
||||
unit.setWave();
|
||||
unit.setSquad(squad);
|
||||
unit.set(baseX + Mathf.range(spread), baseY + Mathf.range(spread));
|
||||
unit.add();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void calculateSpawn(){
|
||||
|
||||
for(int x = 0; x < world.width(); x += quadsize){
|
||||
for(int y = 0; y < world.height(); y += quadsize){
|
||||
//TODO quadrant operations, etc
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void reset(){
|
||||
flySpawns.clear();
|
||||
groundSpawns.clear();
|
||||
}
|
||||
|
||||
private class FlyerSpawn{
|
||||
float angle;
|
||||
|
||||
FlyerSpawn(){
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private class GroundSpawn{
|
||||
|
||||
GroundSpawn(){
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user