Further targeting improvements for AI

This commit is contained in:
Anuken
2018-06-03 20:03:53 -04:00
parent 270dc351a1
commit 3202d62a92
26 changed files with 420 additions and 242 deletions

View File

Before

Width:  |  Height:  |  Size: 310 B

After

Width:  |  Height:  |  Size: 310 B

View File

Before

Width:  |  Height:  |  Size: 306 B

After

Width:  |  Height:  |  Size: 306 B

View File

Before

Width:  |  Height:  |  Size: 266 B

After

Width:  |  Height:  |  Size: 266 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 259 B

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 110 KiB

After

Width:  |  Height:  |  Size: 110 KiB

View File

@@ -63,7 +63,7 @@ public class Vars{
//whether turrets have infinite ammo (only with debug) //whether turrets have infinite ammo (only with debug)
public static boolean infiniteAmmo = true; public static boolean infiniteAmmo = true;
//whether to show paths of enemies //whether to show paths of enemies
public static boolean showPaths = false; public static boolean showPaths = true;
//if false, player is always hidden //if false, player is always hidden
public static boolean showPlayer = true; public static boolean showPlayer = true;
//whether to hide ui, only on debug //whether to hide ui, only on debug

View File

@@ -3,20 +3,31 @@ package io.anuke.mindustry.ai;
import com.badlogic.gdx.utils.IntMap; import com.badlogic.gdx.utils.IntMap;
import com.badlogic.gdx.utils.ObjectMap; import com.badlogic.gdx.utils.ObjectMap;
import com.badlogic.gdx.utils.ObjectSet; import com.badlogic.gdx.utils.ObjectSet;
import io.anuke.mindustry.content.Items;
import io.anuke.mindustry.game.EventType.TileChangeEvent; import io.anuke.mindustry.game.EventType.TileChangeEvent;
import io.anuke.mindustry.game.EventType.WorldLoadEvent; import io.anuke.mindustry.game.EventType.WorldLoadEvent;
import io.anuke.mindustry.game.Team; import io.anuke.mindustry.game.Team;
import io.anuke.mindustry.type.Item;
import io.anuke.mindustry.world.BlockFlag; import io.anuke.mindustry.world.BlockFlag;
import io.anuke.mindustry.world.Tile; import io.anuke.mindustry.world.Tile;
import io.anuke.ucore.core.Events; import io.anuke.ucore.core.Events;
import io.anuke.ucore.util.EnumSet; import io.anuke.ucore.util.EnumSet;
import io.anuke.ucore.util.Mathf;
import static io.anuke.mindustry.Vars.state; import static io.anuke.mindustry.Vars.state;
import static io.anuke.mindustry.Vars.world; 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. /**Class used for indexing special target blocks for AI.
* TODO maybe use Arrays instead of ObjectSets?*/ * TODO maybe use Arrays instead of ObjectSets?*/
public class BlockIndexer { public class BlockIndexer {
/**Size of one ore quadrant.*/
private final static int quadrantSize = 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<>();
/**Maps teams to a map of flagged tiles by type.*/ /**Maps teams to a map of flagged tiles by type.*/
private ObjectMap<BlockFlag, ObjectSet<Tile>> enemyMap = new ObjectMap<>(); private ObjectMap<BlockFlag, ObjectSet<Tile>> enemyMap = new ObjectMap<>();
/**Maps teams to a map of flagged tiles by type.*/ /**Maps teams to a map of flagged tiles by type.*/
@@ -43,22 +54,35 @@ public class BlockIndexer {
enemyMap.clear(); enemyMap.clear();
allyMap.clear(); allyMap.clear();
typeMap.clear(); typeMap.clear();
ores.clear();
for(int x = 0; x < world.width(); x ++){ for(int x = 0; x < world.width(); x ++){
for (int y = 0; y < world.height(); y++) { for (int y = 0; y < world.height(); y++) {
process(world.tile(x, y)); process(world.tile(x, y));
} }
} }
scanOres();
}); });
} }
/**Get all allied blocks with a flag.*/
public ObjectSet<Tile> getAllied(Team team, BlockFlag type){ public ObjectSet<Tile> getAllied(Team team, BlockFlag type){
return (state.teams.get(team).ally ? allyMap : enemyMap).get(type, emptyArray); return (state.teams.get(team).ally ? allyMap : enemyMap).get(type, emptyArray);
} }
/**Get all enemy blocks with a flag.*/
public ObjectSet<Tile> getEnemy(Team team, BlockFlag type){ public ObjectSet<Tile> getEnemy(Team team, BlockFlag type){
return (!state.teams.get(team).ally ? allyMap : enemyMap).get(type, emptyArray); return (!state.teams.get(team).ally ? allyMap : enemyMap).get(type, emptyArray);
} }
/**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.
* Only specific ore types are scanned. See {@link #scanOres}.*/
public ObjectSet<Tile> getOrePositions(Item item){
return ores.get(item, emptyArray);
}
private void process(Tile tile){ private void process(Tile tile){
if(tile.block().flags != null && if(tile.block().flags != null &&
tile.getTeam() != Team.none){ tile.getTeam() != Team.none){
@@ -85,6 +109,30 @@ public class BlockIndexer {
return state.teams.get(team).ally ? allyMap : enemyMap; return state.teams.get(team).ally ? allyMap : enemyMap;
} }
private void scanOres(){
//initialize ore map with empty sets
for(Item item : scanOres){
ores.put(item, new ObjectSet<>());
}
for(int x = 0; x < world.width(); x ++){
for (int y = 0; y < world.height(); y++) {
int qx = (x/quadrantSize);
int qy = (y/quadrantSize);
Tile tile = world.tile(x, y);
//add position of quadrant to list when an ore is found
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)));
}
}
}
}
private class TileIndex{ private class TileIndex{
public final EnumSet<BlockFlag> flags; public final EnumSet<BlockFlag> flags;
public final Team team; public final Team team;

View File

@@ -63,7 +63,8 @@ public class Pathfinder {
if(other == null) continue; if(other == null) continue;
if(values[dx][dy] < value && (target == null || values[dx][dy] < tl) && if(values[dx][dy] < value && (target == null || values[dx][dy] < tl) &&
(!other.solid() || state.teams.areEnemies(team, other.getTeam()))){ !other.solid() &&
!(point.x != 0 && point.y != 0 && (world.solid(tile.x + point.x, tile.y) || world.solid(tile.x, tile.y + point.y)))){ //diagonal corner trap
target = other; target = other;
tl = values[dx][dy]; tl = values[dx][dy];
} }

View File

@@ -110,9 +110,12 @@ public class Recipes implements ContentList{
new Recipe(liquid, LiquidBlocks.fluxpump, new ItemStack(Items.steel, 10), new ItemStack(Items.surgealloy, 5)); new Recipe(liquid, LiquidBlocks.fluxpump, new ItemStack(Items.steel, 10), new ItemStack(Items.surgealloy, 5));
new Recipe(units, UnitBlocks.repairPoint, new ItemStack(Items.steel, 10)); new Recipe(units, UnitBlocks.repairPoint, new ItemStack(Items.steel, 10));
new Recipe(units, UnitBlocks.dropPoint, new ItemStack(Items.steel, 10));
new Recipe(units, UnitBlocks.resupplyPoint, new ItemStack(Items.steel, 10)); new Recipe(units, UnitBlocks.resupplyPoint, new ItemStack(Items.steel, 10));
//new Recipe(units, UnitBlocks.droneFactory, new ItemStack(Items.steel, 10)); new Recipe(units, UnitBlocks.droneFactory, new ItemStack(Items.iron, 50));
//new Recipe(units, UnitBlocks.vtolFactory, new ItemStack(Items.steel, 10)); //new Recipe(units, UnitBlocks.vtolFactory, new ItemStack(Items.steel, 10));
//new Recipe(units, UnitBlocks.droneFactory, new ItemStack(Items.steel, 10)); //new Recipe(units, UnitBlocks.droneFactory, new ItemStack(Items.steel, 10));
//new Recipe(units, UnitBlocks.droneFactory, new ItemStack(Items.steel, 10)); //new Recipe(units, UnitBlocks.droneFactory, new ItemStack(Items.steel, 10));

View File

@@ -1,30 +1,39 @@
package io.anuke.mindustry.content.blocks; package io.anuke.mindustry.content.blocks;
import io.anuke.mindustry.content.Items;
import io.anuke.mindustry.content.UnitTypes;
import io.anuke.mindustry.type.ContentList; import io.anuke.mindustry.type.ContentList;
import io.anuke.mindustry.type.ItemStack;
import io.anuke.mindustry.world.Block; import io.anuke.mindustry.world.Block;
import io.anuke.mindustry.world.blocks.types.units.DropPoint;
import io.anuke.mindustry.world.blocks.types.units.RepairPoint; import io.anuke.mindustry.world.blocks.types.units.RepairPoint;
import io.anuke.mindustry.world.blocks.types.units.ResupplyPoint; import io.anuke.mindustry.world.blocks.types.units.ResupplyPoint;
import io.anuke.mindustry.world.blocks.types.units.UnitFactory;
public class UnitBlocks implements ContentList { public class UnitBlocks implements ContentList {
public static Block resupplyPoint, repairPoint, droneFactory; public static Block resupplyPoint, repairPoint, droneFactory, dropPoint;
@Override @Override
public void load() { public void load() {
/*
droneFactory = new UnitFactory("dronefactory") {{ droneFactory = new UnitFactory("dronefactory") {{
type = UnitTypes.drone; type = UnitTypes.drone;
produceTime = 200; produceTime = 300;
size = 2; size = 2;
requirements = new ItemStack[]{ requirements = new ItemStack[]{
new ItemStack(Items.stone, 5) new ItemStack(Items.iron, 20)
}; };
}};*/ }};
resupplyPoint = new ResupplyPoint("resupplypoint") {{ resupplyPoint = new ResupplyPoint("resupplypoint") {{
shadow = "shadow-round-1"; shadow = "shadow-round-1";
itemCapacity = 30; itemCapacity = 30;
}}; }};
dropPoint = new DropPoint("droppoint") {{
shadow = "shadow-round-1";
itemCapacity = 40;
}};
repairPoint = new RepairPoint("repairpoint") {{ repairPoint = new RepairPoint("repairpoint") {{
shadow = "shadow-round-1"; shadow = "shadow-round-1";
repairSpeed = 0.1f; repairSpeed = 0.1f;

View File

@@ -53,6 +53,9 @@ public class ContentLoader {
//weapons //weapons
new Weapons(), new Weapons(),
//units
new UnitTypes(),
//blocks //blocks
new Blocks(), new Blocks(),
new DefenseBlocks(), new DefenseBlocks(),
@@ -68,9 +71,6 @@ public class ContentLoader {
//recipes //recipes
new Recipes(), new Recipes(),
//units
new UnitTypes(),
}; };

View File

@@ -46,6 +46,9 @@ public interface BlockBuilder {
/**Sets the tile this builder is currently mining.*/ /**Sets the tile this builder is currently mining.*/
void setMineTile(Tile tile); void setMineTile(Tile tile);
/**Build power, can be any float. 1 = builds recipes in normal time, 0 = doesn't build at all.*/
float getBuildPower(Tile tile);
/**Return whether this builder's place queue contains items.*/ /**Return whether this builder's place queue contains items.*/
default boolean isBuilding(){ default boolean isBuilding(){
return getPlaceQueue().size != 0; return getPlaceQueue().size != 0;

View File

@@ -77,6 +77,12 @@ public class Player extends Unit implements BlockBuilder {
//region unit and event overrides, utility methods //region unit and event overrides, utility methods
@Override
public float getBuildPower(Tile tile) {
return 1f;
}
@Override @Override
public float getMaxHealth() { public float getMaxHealth() {
return 200; return 200;
@@ -507,6 +513,8 @@ public class Player extends Unit implements BlockBuilder {
inventory.clear(); inventory.clear();
upgrades.clear(); upgrades.clear();
placeQueue.clear(); placeQueue.clear();
dead = true;
respawning = false;
add(); add();
heal(); heal();

View File

@@ -14,6 +14,7 @@ public class UnitInventory {
private Array<AmmoEntry> ammos = new Array<>(); private Array<AmmoEntry> ammos = new Array<>();
private int totalAmmo; private int totalAmmo;
private ItemStack item; private ItemStack item;
//TODO move these somewhere else so they're not variables?
private int capacity, ammoCapacity; private int capacity, ammoCapacity;
private boolean infiniteAmmo; private boolean infiniteAmmo;
@@ -22,6 +23,10 @@ public class UnitInventory {
this.ammoCapacity = ammoCapacity; this.ammoCapacity = ammoCapacity;
} }
public boolean isFull(){
return item != null && item.amount >= capacity;
}
public boolean isInfiniteAmmo() { public boolean isInfiniteAmmo() {
return infiniteAmmo; return infiniteAmmo;
} }

View File

@@ -29,11 +29,7 @@ public class Units {
* @return whether the target is invalid * @return whether the target is invalid
*/ */
public static boolean invalidateTarget(Targetable target, Team team, float x, float y, float range) { public static boolean invalidateTarget(Targetable target, Team team, float x, float y, float range) {
if (target == null) { return target == null || (range != Float.MAX_VALUE && target.distanceTo(x, y) > range) || target.getTeam() == team || !target.isValid();
return false;
}
return (!(range != Float.MAX_VALUE) || !(target.distanceTo(x, y) > range)) && (target.getTeam() == team || !target.isValid());
} }

View File

@@ -4,6 +4,7 @@ import io.anuke.mindustry.content.fx.ExplosionFx;
import io.anuke.mindustry.entities.Targetable; import io.anuke.mindustry.entities.Targetable;
import io.anuke.mindustry.entities.TileEntity; import io.anuke.mindustry.entities.TileEntity;
import io.anuke.mindustry.entities.Unit; import io.anuke.mindustry.entities.Unit;
import io.anuke.mindustry.entities.Units;
import io.anuke.mindustry.entities.bullet.Bullet; import io.anuke.mindustry.entities.bullet.Bullet;
import io.anuke.mindustry.entities.bullet.BulletType; import io.anuke.mindustry.entities.bullet.BulletType;
import io.anuke.mindustry.game.Team; import io.anuke.mindustry.game.Team;
@@ -95,10 +96,25 @@ public abstract class BaseUnit extends Unit{
} }
public void targetClosestAllyFlag(BlockFlag flag){ public void targetClosestAllyFlag(BlockFlag flag){
if(target != null) return;
Tile target = Geometry.findClosest(x, y, world.indexer().getAllied(team, flag)); Tile target = Geometry.findClosest(x, y, world.indexer().getAllied(team, flag));
if (target != null) this.target = target.entity; if (target != null) this.target = target.entity;
} }
public void targetClosestEnemyFlag(BlockFlag flag){
if(target != null) return;
Tile target = Geometry.findClosest(x, y, world.indexer().getEnemy(team, flag));
if (target != null) this.target = target.entity;
}
public void targetClosest(){
if(target != null) return;
target = Units.getClosestTarget(team, x, y, inventory.getAmmoRange());
}
public UnitState getStartState(){ public UnitState getStartState(){
return null; return null;
} }

View File

@@ -1,6 +1,5 @@
package io.anuke.mindustry.entities.units; package io.anuke.mindustry.entities.units;
import io.anuke.mindustry.entities.Unit;
import io.anuke.mindustry.entities.Units; import io.anuke.mindustry.entities.Units;
import io.anuke.mindustry.game.Team; import io.anuke.mindustry.game.Team;
import io.anuke.mindustry.graphics.Palette; import io.anuke.mindustry.graphics.Palette;
@@ -48,7 +47,7 @@ public class FlyingUnit extends BaseUnit {
@Override @Override
public void drawOver() { public void drawOver() {
trail.draw(Palette.lighterOrange, Palette.lightishOrange, 5f); trail.draw(Palette.lightFlame, Palette.lightOrange, 5f);
} }
@Override @Override
@@ -65,6 +64,11 @@ public class FlyingUnit extends BaseUnit {
return attack; return attack;
} }
@Override
public float drawSize() {
return 60;
}
protected void circle(float circleLength){ protected void circle(float circleLength){
vec.set(target.getX() - x, target.getY() - y); vec.set(target.getX() - x, target.getY() - y);
@@ -111,6 +115,24 @@ public class FlyingUnit extends BaseUnit {
} }
} }
}, },
idle = new UnitState() {
public void update() {
retarget(() -> {
targetClosest();
targetClosestEnemyFlag(BlockFlag.target);
if(target != null){
setState(attack);
}
});
target = getClosestCore();
if(target != null){
circle(50f);
}
velocity.scl(0.8f);
}
},
attack = new UnitState(){ attack = new UnitState(){
public void entered() { public void entered() {
target = null; target = null;
@@ -124,16 +146,15 @@ public class FlyingUnit extends BaseUnit {
if(!inventory.hasAmmo()) { if(!inventory.hasAmmo()) {
state.set(resupply); state.set(resupply);
}else if (target == null){ }else if (target == null){
if(timer.get(timerTarget, 20)) { retarget(() -> {
Unit closest = Units.getClosestEnemy(team, x, y, targetClosest();
inventory.getAmmo().getRange(), other -> distanceTo(other) < 60f); targetClosestEnemyFlag(BlockFlag.target);
if(closest != null){ targetClosestEnemyFlag(BlockFlag.producer);
target = closest;
}else { if(target == null){
Tile target = Geometry.findClosest(x, y, world.indexer().getEnemy(team, BlockFlag.target)); setState(idle);
if (target != null) FlyingUnit.this.target = target.entity;
} }
} });
}else{ }else{
attack(150f); attack(150f);
@@ -153,7 +174,7 @@ public class FlyingUnit extends BaseUnit {
} }
public void update() { public void update() {
if(health >= health){ if(health >= getMaxHealth()){
state.set(attack); state.set(attack);
}else if(!targetHasFlag(BlockFlag.repair)){ }else if(!targetHasFlag(BlockFlag.repair)){
retarget(() -> { retarget(() -> {

View File

@@ -2,7 +2,6 @@ package io.anuke.mindustry.entities.units;
import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.graphics.Color;
import io.anuke.mindustry.Vars; import io.anuke.mindustry.Vars;
import io.anuke.mindustry.entities.Unit;
import io.anuke.mindustry.entities.Units; import io.anuke.mindustry.entities.Units;
import io.anuke.mindustry.game.Team; import io.anuke.mindustry.game.Team;
import io.anuke.mindustry.type.AmmoType; import io.anuke.mindustry.type.AmmoType;
@@ -12,20 +11,17 @@ import io.anuke.mindustry.world.blocks.types.Floor;
import io.anuke.ucore.core.Timers; import io.anuke.ucore.core.Timers;
import io.anuke.ucore.graphics.Draw; import io.anuke.ucore.graphics.Draw;
import io.anuke.ucore.graphics.Hue; import io.anuke.ucore.graphics.Hue;
import io.anuke.ucore.util.Angles; import io.anuke.ucore.util.*;
import io.anuke.ucore.util.Geometry;
import io.anuke.ucore.util.Mathf;
import io.anuke.ucore.util.Translator;
import static io.anuke.mindustry.Vars.world; import static io.anuke.mindustry.Vars.world;
public abstract class GroundUnitType extends BaseUnit { public abstract class GroundUnit extends BaseUnit {
protected static Translator vec = new Translator(); protected static Translator vec = new Translator();
protected static float maxAim = 30f; protected static float maxAim = 30f;
protected float walkTime; protected float walkTime;
public GroundUnitType(UnitType type, Team team) { public GroundUnit(UnitType type, Team team) {
super(type, team); super(type, team);
} }
@@ -38,9 +34,8 @@ public abstract class GroundUnitType extends BaseUnit {
public void update() { public void update() {
super.update(); super.update();
if(!velocity.isZero(0.0001f) && (target == null if(target == null){
|| (inventory.hasAmmo() && distanceTo(target) > inventory.getAmmo().getRange()))){ rotation = Mathf.lerpDelta(rotation, velocity.angle(), 0.2f);
rotation = velocity.angle();
} }
} }
@@ -89,7 +84,8 @@ public abstract class GroundUnitType extends BaseUnit {
public void updateTargeting() { public void updateTargeting() {
super.updateTargeting(); super.updateTargeting();
if(Units.invalidateTarget(target, this)){ if(Units.invalidateTarget(target, team, x, y, Float.MAX_VALUE)){
if(target != null) Log.info("Invalidating target {0}", target);
target = null; target = null;
} }
} }
@@ -146,19 +142,11 @@ public abstract class GroundUnitType extends BaseUnit {
} }
public void update() { public void update() {
retarget(() -> { retarget(() -> targetClosest());
Unit closest = Units.getClosestEnemy(team, x, y, inventory.getAmmo().getRange(), other -> true);
if(closest != null){
target = closest;
}else {
Tile target = Geometry.findClosest(x, y, world.indexer().getEnemy(team, BlockFlag.target));
if (target != null) GroundUnitType.this.target = target.entity;
}
});
if(!inventory.hasAmmo()) { if(!inventory.hasAmmo()) {
state.set(resupply); state.set(resupply);
}else{ }else if(target != null){
if(distanceTo(target) > inventory.getAmmo().getRange() * 0.7f){ if(distanceTo(target) > inventory.getAmmo().getRange() * 0.7f){
moveToCore(); moveToCore();
}else{ }else{
@@ -173,6 +161,8 @@ public abstract class GroundUnitType extends BaseUnit {
shoot(ammo, Angles.moveToward(rotation, angleTo(target), maxAim), 4f); shoot(ammo, Angles.moveToward(rotation, angleTo(target), maxAim), 4f);
} }
}else{
moveToCore();
} }
} }
}, },

View File

@@ -2,6 +2,7 @@ package io.anuke.mindustry.entities.units.types;
import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.utils.Queue; import com.badlogic.gdx.utils.Queue;
import io.anuke.mindustry.content.Items;
import io.anuke.mindustry.entities.BlockBuilder; import io.anuke.mindustry.entities.BlockBuilder;
import io.anuke.mindustry.entities.TileEntity; import io.anuke.mindustry.entities.TileEntity;
import io.anuke.mindustry.entities.Units; import io.anuke.mindustry.entities.Units;
@@ -36,6 +37,8 @@ public class Drone extends FlyingUnit implements BlockBuilder{
protected Tile mineTile; protected Tile mineTile;
protected Queue<BuildRequest> placeQueue = new Queue<>(); protected Queue<BuildRequest> placeQueue = new Queue<>();
/**Initialize placement event notifier system.
* Static initialization is to be avoided, thus, this is done lazily.*/
private static void initEvents(){ private static void initEvents(){
Events.on(BlockBuildEvent.class, (team, tile) -> { Events.on(BlockBuildEvent.class, (team, tile) -> {
EntityGroup<BaseUnit> group = unitGroups[team.ordinal()]; EntityGroup<BaseUnit> group = unitGroups[team.ordinal()];
@@ -70,6 +73,11 @@ public class Drone extends FlyingUnit implements BlockBuilder{
} }
} }
@Override
public float getBuildPower(Tile tile) {
return 0.3f;
}
@Override @Override
public Queue<BuildRequest> getPlaceQueue() { public Queue<BuildRequest> getPlaceQueue() {
return placeQueue; return placeQueue;
@@ -176,6 +184,10 @@ public class Drone extends FlyingUnit implements BlockBuilder{
retarget(() -> { retarget(() -> {
target = Units.findAllyTile(team, x, y, discoverRange, target = Units.findAllyTile(team, x, y, discoverRange,
tile -> tile.entity != null && tile.entity.health + 0.0001f < tile.block().health); tile -> tile.entity != null && tile.entity.health + 0.0001f < tile.block().health);
if(target == null){
setState(mine);
}
}); });
}else if(target.distanceTo(Drone.this) > type.range){ }else if(target.distanceTo(Drone.this) > type.range){
circle(type.range); circle(type.range);
@@ -186,6 +198,22 @@ public class Drone extends FlyingUnit implements BlockBuilder{
} }
} }
}, },
mine = new UnitState() {
public void update() {
//if inventory is full, drop it off.
if(inventory.isFull()){
setState(drop);
}else{
//only mines iron for now
retarget(() -> target = Geometry.findClosest(x, y, world.indexer().getOrePositions(Items.iron)));
}
}
},
drop = new UnitState() {
public void update() {
}
},
retreat = new UnitState() { retreat = new UnitState() {
public void entered() { public void entered() {
target = null; target = null;

View File

@@ -1,10 +1,10 @@
package io.anuke.mindustry.entities.units.types; package io.anuke.mindustry.entities.units.types;
import io.anuke.mindustry.entities.units.GroundUnitType; import io.anuke.mindustry.entities.units.GroundUnit;
import io.anuke.mindustry.entities.units.UnitType; import io.anuke.mindustry.entities.units.UnitType;
import io.anuke.mindustry.game.Team; import io.anuke.mindustry.game.Team;
public class Scout extends GroundUnitType { public class Scout extends GroundUnit {
public Scout(UnitType type, Team team) { public Scout(UnitType type, Team team) {
super(type, team); super(type, team);

View File

@@ -1,6 +1,5 @@
package io.anuke.mindustry.entities.units.types; package io.anuke.mindustry.entities.units.types;
import io.anuke.mindustry.content.fx.UnitFx;
import io.anuke.mindustry.entities.units.FlyingUnit; import io.anuke.mindustry.entities.units.FlyingUnit;
import io.anuke.mindustry.entities.units.UnitType; import io.anuke.mindustry.entities.units.UnitType;
import io.anuke.mindustry.game.Team; import io.anuke.mindustry.game.Team;
@@ -54,10 +53,6 @@ public class Vtol extends FlyingUnit {
if(velocity.len() <= 0.2f){ if(velocity.len() <= 0.2f){
rotation += Mathf.sin(Timers.time() + id * 99, 10f, 8f); rotation += Mathf.sin(Timers.time() + id * 99, 10f, 8f);
} }
if(timer.get(timerBoost, 2)){
effectAt(UnitFx.vtolHover, rotation + 180f, 4f, 0);
}
} }
} }

View File

@@ -69,7 +69,7 @@ public class DebugFragment implements Fragment {
row(); row();
new button("death", () -> player.damage(99999, false)); new button("death", () -> player.damage(99999, false));
row(); row();
new button("spawnf", () -> UnitTypes.drone.create(player.team).set(player.x, player.y).add()); new button("spawnf", () -> UnitTypes.vtol.create(player.team).set(player.x, player.y).add());
row(); row();
new button("spawng", () -> UnitTypes.scout.create(player.team).set(player.x, player.y).add()); new button("spawng", () -> UnitTypes.scout.create(player.team).set(player.x, player.y).add());
row(); row();

View File

@@ -1,9 +1,17 @@
package io.anuke.mindustry.world; package io.anuke.mindustry.world;
public enum BlockFlag { public enum BlockFlag {
/**General important target for all types of units.*/
target(0), target(0),
/**Point to resupply resources.*/
resupplyPoint(Float.MAX_VALUE), resupplyPoint(Float.MAX_VALUE),
producer(Float.MAX_VALUE), /**Point to drop off resources.*/
dropPoint(Float.MAX_VALUE),
/**Producer of important goods.*/
producer(20),
/**Producer or storage unit of volatile materials.*/
explosive(10),
/**Repair point.*/
repair(Float.MAX_VALUE); repair(Float.MAX_VALUE);
public final float cost; public final float cost;

View File

@@ -1,9 +1,11 @@
package io.anuke.mindustry.world; package io.anuke.mindustry.world;
import com.badlogic.gdx.math.GridPoint2; import com.badlogic.gdx.math.GridPoint2;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.utils.Array; import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.reflect.ClassReflection; import com.badlogic.gdx.utils.reflect.ClassReflection;
import io.anuke.mindustry.content.blocks.Blocks; import io.anuke.mindustry.content.blocks.Blocks;
import io.anuke.mindustry.entities.Targetable;
import io.anuke.mindustry.entities.TileEntity; import io.anuke.mindustry.entities.TileEntity;
import io.anuke.mindustry.game.Team; import io.anuke.mindustry.game.Team;
import io.anuke.mindustry.type.Recipe; import io.anuke.mindustry.type.Recipe;
@@ -19,7 +21,7 @@ import static io.anuke.mindustry.Vars.tilesize;
import static io.anuke.mindustry.Vars.world; import static io.anuke.mindustry.Vars.world;
public class Tile implements Position{ public class Tile implements Position, Targetable{
public static final Object tileSetLock = new Object(); public static final Object tileSetLock = new Object();
/**Block ID data.*/ /**Block ID data.*/
@@ -338,6 +340,16 @@ public class Tile implements Position{
world.notifyChanged(this); world.notifyChanged(this);
} }
@Override
public boolean isDead() {
return false; //tiles never die
}
@Override
public Vector2 getVelocity() {
return Vector2.Zero;
}
@Override @Override
public float getX() { public float getX() {
return drawx(); return drawx();

View File

@@ -0,0 +1,28 @@
package io.anuke.mindustry.world.blocks.types.units;
import io.anuke.mindustry.type.Item;
import io.anuke.mindustry.world.Block;
import io.anuke.mindustry.world.Tile;
public class DropPoint extends Block {
public DropPoint(String name) {
super(name);
hasItems = true;
solid = true;
update = true;
}
@Override
public boolean acceptItem(Item item, Tile tile, Tile source) {
return false;
}
@Override
public void update(Tile tile) {
if (tile.entity.items.totalItems() > 0) {
tryDump(tile);
}
}
}