Further drone splitting / Even drone player distribution
This commit is contained in:
@@ -0,0 +1,59 @@
|
||||
package io.anuke.mindustry.entities.type.base;
|
||||
|
||||
import io.anuke.arc.math.Mathf;
|
||||
import io.anuke.arc.math.geom.Geometry;
|
||||
import io.anuke.mindustry.entities.type.FlyingUnit;
|
||||
import io.anuke.mindustry.entities.units.UnitState;
|
||||
import io.anuke.mindustry.world.Tile;
|
||||
import io.anuke.mindustry.world.meta.BlockFlag;
|
||||
|
||||
import static io.anuke.mindustry.Vars.world;
|
||||
|
||||
public abstract class BaseDrone extends FlyingUnit{
|
||||
public final UnitState retreat = new UnitState(){
|
||||
public void entered(){
|
||||
target = null;
|
||||
}
|
||||
|
||||
public void update(){
|
||||
if(health >= maxHealth()){
|
||||
state.set(attack);
|
||||
}else if(!targetHasFlag(BlockFlag.repair)){
|
||||
retarget(() -> {
|
||||
Tile repairPoint = Geometry.findClosest(x, y, world.indexer.getAllied(team, BlockFlag.repair));
|
||||
if(repairPoint != null){
|
||||
target = repairPoint;
|
||||
}else{
|
||||
setState(getStartState());
|
||||
}
|
||||
});
|
||||
}else{
|
||||
circle(40f);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
protected void updateRotation(){
|
||||
if(target != null && shouldRotate() && target.dst(this) < type.range){
|
||||
rotation = Mathf.slerpDelta(rotation, angleTo(target), 0.3f);
|
||||
}else{
|
||||
rotation = Mathf.slerpDelta(rotation, velocity.angle(), 0.3f);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void behavior(){
|
||||
if(health <= health * type.retreatPercent){
|
||||
setState(retreat);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean shouldRotate(){
|
||||
return state.is(getStartState());
|
||||
}
|
||||
|
||||
@Override
|
||||
public abstract UnitState getStartState();
|
||||
|
||||
}
|
||||
201
core/src/io/anuke/mindustry/entities/type/base/BuilderDrone.java
Normal file
201
core/src/io/anuke/mindustry/entities/type/base/BuilderDrone.java
Normal file
@@ -0,0 +1,201 @@
|
||||
package io.anuke.mindustry.entities.type.base;
|
||||
|
||||
import io.anuke.arc.Core;
|
||||
import io.anuke.arc.Events;
|
||||
import io.anuke.arc.collection.IntIntMap;
|
||||
import io.anuke.arc.collection.Queue;
|
||||
import io.anuke.arc.math.Mathf;
|
||||
import io.anuke.arc.util.Pack;
|
||||
import io.anuke.arc.util.StaticReset;
|
||||
import io.anuke.mindustry.entities.EntityGroup;
|
||||
import io.anuke.mindustry.entities.traits.BuilderTrait;
|
||||
import io.anuke.mindustry.entities.type.*;
|
||||
import io.anuke.mindustry.entities.units.UnitState;
|
||||
import io.anuke.mindustry.game.EventType.BuildSelectEvent;
|
||||
import io.anuke.mindustry.world.Tile;
|
||||
import io.anuke.mindustry.world.blocks.BuildBlock;
|
||||
import io.anuke.mindustry.world.blocks.BuildBlock.BuildEntity;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
import static io.anuke.mindustry.Vars.*;
|
||||
|
||||
//TODO follow players
|
||||
public class BuilderDrone extends BaseDrone implements BuilderTrait{
|
||||
private static final StaticReset reset = new StaticReset();
|
||||
private static final IntIntMap totals = new IntIntMap();
|
||||
|
||||
protected Queue<BuildRequest> placeQueue = new Queue<>();
|
||||
protected boolean isBreaking;
|
||||
protected Player playerTarget;
|
||||
|
||||
public final UnitState
|
||||
|
||||
build = new UnitState(){
|
||||
|
||||
public void entered(){
|
||||
if(!(target instanceof BuildEntity)){
|
||||
target = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void update(){
|
||||
BuildEntity entity = (BuildEntity)target;
|
||||
TileEntity core = getClosestCore();
|
||||
|
||||
if(entity != null && core != null && (entity.progress < 1f || entity.progress > 0f) && entity.tile.block() instanceof BuildBlock){ //building is valid
|
||||
if(!isBuilding() && dst(target) < placeDistance * 0.9f){ //within distance, begin placing
|
||||
if(isBreaking){
|
||||
getPlaceQueue().addLast(new BuildRequest(entity.tile.x, entity.tile.y));
|
||||
}else{
|
||||
getPlaceQueue().addLast(new BuildRequest(entity.tile.x, entity.tile.y, entity.tile.rotation(), entity.cblock));
|
||||
}
|
||||
}
|
||||
|
||||
circle(placeDistance * 0.7f);
|
||||
}else{ //else, building isn't valid, follow a player
|
||||
if(playerTarget == null || playerTarget.getTeam() != team || !playerTarget.isValid()){
|
||||
playerTarget = null;
|
||||
|
||||
retarget(() -> {
|
||||
float minDst = Float.POSITIVE_INFINITY;
|
||||
int minDrones = Integer.MAX_VALUE;
|
||||
|
||||
//find player with min amount of drones
|
||||
for(Player player : playerGroup.all()){
|
||||
if(player.getTeam() == team){
|
||||
int drones = getDrones(player);
|
||||
float dst = dst2(player);
|
||||
|
||||
if(playerTarget == null || drones < minDrones || (drones == minDrones && dst < minDst)){
|
||||
minDrones = drones;
|
||||
minDst = dst;
|
||||
playerTarget = player;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}else{
|
||||
incDrones(playerTarget);
|
||||
target = playerTarget;
|
||||
float dst = 100f + (id % 4)*15;
|
||||
float tdst = dst(target);
|
||||
circle(dst, type.speed * (Mathf.lerp(1f, 0.3f, 1f - Mathf.clamp((tdst - dst) / dst))));
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
public BuilderDrone(){
|
||||
if(reset.check()){
|
||||
Events.on(BuildSelectEvent.class, event -> {
|
||||
EntityGroup<BaseUnit> group = unitGroups[event.team.ordinal()];
|
||||
|
||||
if(!(event.tile.entity instanceof BuildEntity)) return;
|
||||
|
||||
for(BaseUnit unit : group.all()){
|
||||
if(unit instanceof BuilderDrone){
|
||||
BuilderDrone drone = (BuilderDrone)unit;
|
||||
if(drone.isBuilding()){
|
||||
//stop building if opposite building begins.
|
||||
BuildRequest req = drone.getCurrentRequest();
|
||||
if(req.breaking != event.breaking && req.x == event.tile.x && req.y == event.tile.y){
|
||||
drone.clearBuilding();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
int getDrones(Player player){
|
||||
int num = totals.get(player.id, 0);
|
||||
return Pack.leftShort(totals.get(player.id, 0));
|
||||
}
|
||||
|
||||
void incDrones(Player player){
|
||||
int num = totals.get(player.id, 0);
|
||||
int amount = Pack.leftShort(num), frame = Pack.rightShort(num);
|
||||
short curFrame = (short)(Core.graphics.getFrameId() % Short.MAX_VALUE);
|
||||
|
||||
if(frame != curFrame){
|
||||
totals.put(player.id, Pack.shortInt((short)1, curFrame));
|
||||
}else{
|
||||
totals.put(player.id, Pack.shortInt((short)(amount + 1), curFrame));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getBuildPower(Tile tile){
|
||||
return type.buildPower;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Queue<BuildRequest> getPlaceQueue(){
|
||||
return placeQueue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(){
|
||||
super.update();
|
||||
|
||||
if(!state.is(build) && timer.get(timerTarget2, 15)){
|
||||
for(Player player : playerGroup.all()){
|
||||
if(player.getTeam() == team && player.getCurrentRequest() != null){
|
||||
BuildRequest req = player.getCurrentRequest();
|
||||
Tile tile = world.tile(req.x, req.y);
|
||||
if(tile != null && tile.entity instanceof BuildEntity){
|
||||
BuildEntity b = tile.entity();
|
||||
float dist = Math.min(b.dst(x, y) - placeDistance, 0);
|
||||
if(dist / type.maxVelocity < b.buildCost * 0.9f){
|
||||
target = b;
|
||||
this.isBreaking = req.breaking;
|
||||
setState(build);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
updateBuilding();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldRotate(){
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UnitState getStartState(){
|
||||
return build;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawOver(){
|
||||
drawBuilding();
|
||||
}
|
||||
|
||||
@Override
|
||||
public float drawSize(){
|
||||
return isBuilding() ? placeDistance * 2f : 30f;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canCreateBlocks(){
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(DataOutput data) throws IOException{
|
||||
super.write(data);
|
||||
writeBuilding(data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(DataInput data) throws IOException{
|
||||
super.read(data);
|
||||
readBuilding(data);
|
||||
}
|
||||
}
|
||||
@@ -1,23 +1,22 @@
|
||||
package io.anuke.mindustry.entities.type.base;
|
||||
|
||||
import io.anuke.arc.math.Mathf;
|
||||
import io.anuke.arc.math.geom.Geometry;
|
||||
import io.anuke.arc.util.Structs;
|
||||
import io.anuke.mindustry.content.Blocks;
|
||||
import io.anuke.mindustry.entities.traits.MinerTrait;
|
||||
import io.anuke.mindustry.entities.type.*;
|
||||
import io.anuke.mindustry.entities.type.TileEntity;
|
||||
import io.anuke.mindustry.entities.units.UnitState;
|
||||
import io.anuke.mindustry.gen.Call;
|
||||
import io.anuke.mindustry.type.*;
|
||||
import io.anuke.mindustry.world.*;
|
||||
import io.anuke.mindustry.world.meta.BlockFlag;
|
||||
import io.anuke.mindustry.type.Item;
|
||||
import io.anuke.mindustry.type.ItemType;
|
||||
import io.anuke.mindustry.world.Pos;
|
||||
import io.anuke.mindustry.world.Tile;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
import static io.anuke.mindustry.Vars.world;
|
||||
|
||||
/** A drone that only mines.*/
|
||||
public class MinerDrone extends FlyingUnit implements MinerTrait{
|
||||
public class MinerDrone extends BaseDrone implements MinerTrait{
|
||||
protected Item targetItem;
|
||||
protected Tile mineTile;
|
||||
|
||||
@@ -120,29 +119,6 @@ public class MinerDrone extends FlyingUnit implements MinerTrait{
|
||||
|
||||
circle(type.range / 1.8f);
|
||||
}
|
||||
},
|
||||
|
||||
retreat = new UnitState(){
|
||||
public void entered(){
|
||||
target = null;
|
||||
}
|
||||
|
||||
public void update(){
|
||||
if(health >= maxHealth()){
|
||||
state.set(attack);
|
||||
}else if(!targetHasFlag(BlockFlag.repair)){
|
||||
retarget(() -> {
|
||||
Tile repairPoint = Geometry.findClosest(x, y, world.indexer.getAllied(team, BlockFlag.repair));
|
||||
if(repairPoint != null){
|
||||
target = repairPoint;
|
||||
}else{
|
||||
setState(mine);
|
||||
}
|
||||
});
|
||||
}else{
|
||||
circle(40f);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
@@ -158,19 +134,8 @@ public class MinerDrone extends FlyingUnit implements MinerTrait{
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateRotation(){
|
||||
if(target != null && state.is(mine)){
|
||||
rotation = Mathf.slerpDelta(rotation, angleTo(target), 0.3f);
|
||||
}else{
|
||||
rotation = Mathf.slerpDelta(rotation, velocity.angle(), 0.3f);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void behavior(){
|
||||
if(health <= health * type.retreatPercent){
|
||||
setState(retreat);
|
||||
}
|
||||
public boolean shouldRotate(){
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
package io.anuke.mindustry.entities.type.base;
|
||||
|
||||
public class Phantom extends UtilityDrone{
|
||||
public class Phantom extends BuilderDrone{
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
package io.anuke.mindustry.entities.type.base;
|
||||
|
||||
import io.anuke.mindustry.entities.Units;
|
||||
import io.anuke.mindustry.entities.type.FlyingUnit;
|
||||
import io.anuke.mindustry.entities.type.TileEntity;
|
||||
import io.anuke.mindustry.entities.units.UnitState;
|
||||
import io.anuke.mindustry.world.Pos;
|
||||
import io.anuke.mindustry.world.Tile;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
import static io.anuke.mindustry.Vars.world;
|
||||
|
||||
public class RepairDrone extends FlyingUnit{
|
||||
public final UnitState repair = new UnitState(){
|
||||
|
||||
public void entered(){
|
||||
target = null;
|
||||
}
|
||||
|
||||
public void update(){
|
||||
|
||||
retarget(() -> target = Units.findDamagedTile(team, x, y));
|
||||
|
||||
if(target != null){
|
||||
if(target.dst(RepairDrone.this) > type.range){
|
||||
circle(type.range * 0.9f);
|
||||
}else{
|
||||
getWeapon().update(RepairDrone.this, target.getX(), target.getY());
|
||||
}
|
||||
}else{
|
||||
//circle spawner if there's nothing to repair
|
||||
if(getSpawner() != null){
|
||||
target = getSpawner();
|
||||
circle(type.range * 0.9f);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public UnitState getStartState(){
|
||||
return repair;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(DataOutput data) throws IOException{
|
||||
super.write(data);
|
||||
data.writeInt(state.is(repair) && target instanceof TileEntity ? ((TileEntity)target).tile.pos() : Pos.invalid);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(DataInput data) throws IOException{
|
||||
super.read(data);
|
||||
Tile repairing = world.tile(data.readInt());
|
||||
|
||||
if(repairing != null){
|
||||
target = repairing.entity;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package io.anuke.mindustry.entities.type.base;
|
||||
|
||||
public class Spirit extends UtilityDrone{
|
||||
public class Spirit extends RepairDrone{
|
||||
}
|
||||
|
||||
@@ -1,234 +0,0 @@
|
||||
package io.anuke.mindustry.entities.type.base;
|
||||
|
||||
import io.anuke.arc.Events;
|
||||
import io.anuke.arc.collection.Queue;
|
||||
import io.anuke.arc.math.Mathf;
|
||||
import io.anuke.arc.math.geom.Geometry;
|
||||
import io.anuke.mindustry.entities.*;
|
||||
import io.anuke.mindustry.entities.traits.BuilderTrait;
|
||||
import io.anuke.mindustry.entities.type.*;
|
||||
import io.anuke.mindustry.entities.units.UnitState;
|
||||
import io.anuke.mindustry.game.EventType.BuildSelectEvent;
|
||||
import io.anuke.mindustry.world.Tile;
|
||||
import io.anuke.mindustry.world.blocks.BuildBlock;
|
||||
import io.anuke.mindustry.world.blocks.BuildBlock.BuildEntity;
|
||||
import io.anuke.mindustry.world.meta.BlockFlag;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
import static io.anuke.mindustry.Vars.*;
|
||||
|
||||
/** A drone that only builds and/or repairs.*/
|
||||
public class UtilityDrone extends FlyingUnit implements BuilderTrait{
|
||||
protected Queue<BuildRequest> placeQueue = new Queue<>();
|
||||
protected boolean isBreaking;
|
||||
|
||||
public final UnitState
|
||||
|
||||
build = new UnitState(){
|
||||
|
||||
//TODO follow players
|
||||
public void entered(){
|
||||
if(!(target instanceof BuildEntity)){
|
||||
target = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void update(){
|
||||
BuildEntity entity = (BuildEntity)target;
|
||||
TileEntity core = getClosestCore();
|
||||
|
||||
if(entity == null){
|
||||
setState(repair);
|
||||
return;
|
||||
}
|
||||
|
||||
if(core == null) return;
|
||||
|
||||
if((entity.progress < 1f || entity.progress > 0f) && entity.tile.block() instanceof BuildBlock){ //building is valid
|
||||
if(!isBuilding() && dst(target) < placeDistance * 0.9f){ //within distance, begin placing
|
||||
if(isBreaking){
|
||||
getPlaceQueue().addLast(new BuildRequest(entity.tile.x, entity.tile.y));
|
||||
}else{
|
||||
getPlaceQueue().addLast(new BuildRequest(entity.tile.x, entity.tile.y, entity.tile.rotation(), entity.cblock));
|
||||
}
|
||||
}
|
||||
|
||||
circle(placeDistance * 0.7f);
|
||||
}else{ //building isn't valid
|
||||
setState(repair);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
repair = new UnitState(){
|
||||
|
||||
public void entered(){
|
||||
target = null;
|
||||
}
|
||||
|
||||
public void update(){
|
||||
|
||||
retarget(() -> target = Units.findDamagedTile(team, x, y));
|
||||
|
||||
if(target != null){
|
||||
if(target.dst(UtilityDrone.this) > type.range){
|
||||
circle(type.range * 0.9f);
|
||||
}else{
|
||||
getWeapon().update(UtilityDrone.this, target.getX(), target.getY());
|
||||
}
|
||||
}else{
|
||||
//circle spawner if there's nothing to repair
|
||||
if(getSpawner() != null){
|
||||
target = getSpawner();
|
||||
circle(type.range * 0.9f);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
retreat = new UnitState(){
|
||||
public void entered(){
|
||||
target = null;
|
||||
}
|
||||
|
||||
public void update(){
|
||||
if(health >= maxHealth()){
|
||||
state.set(attack);
|
||||
}else if(!targetHasFlag(BlockFlag.repair)){
|
||||
retarget(() -> {
|
||||
Tile repairPoint = Geometry.findClosest(x, y, world.indexer.getAllied(team, BlockFlag.repair));
|
||||
if(repairPoint != null){
|
||||
target = repairPoint;
|
||||
}else{
|
||||
setState(repair);
|
||||
}
|
||||
});
|
||||
}else{
|
||||
circle(40f);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static{
|
||||
|
||||
Events.on(BuildSelectEvent.class, event -> {
|
||||
EntityGroup<BaseUnit> group = unitGroups[event.team.ordinal()];
|
||||
|
||||
if(!(event.tile.entity instanceof BuildEntity)) return;
|
||||
|
||||
for(BaseUnit unit : group.all()){
|
||||
if(unit instanceof UtilityDrone){
|
||||
UtilityDrone drone = (UtilityDrone)unit;
|
||||
if(drone.isBuilding()){
|
||||
//stop building if opposite building begins.
|
||||
BuildRequest req = drone.getCurrentRequest();
|
||||
if(req.breaking != event.breaking && req.x == event.tile.x && req.y == event.tile.y){
|
||||
drone.clearBuilding();
|
||||
drone.setState(drone.repair);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getBuildPower(Tile tile){
|
||||
return type.buildPower;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Queue<BuildRequest> getPlaceQueue(){
|
||||
return placeQueue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(){
|
||||
super.update();
|
||||
|
||||
if(state.is(repair) && target != null && target.getTeam() != team){
|
||||
target = null;
|
||||
}
|
||||
|
||||
if(!state.is(build) && timer.get(timerTarget2, 15)){
|
||||
for(Player player : playerGroup.all()){
|
||||
if(player.getTeam() == team && player.getCurrentRequest() != null){
|
||||
BuildRequest req = player.getCurrentRequest();
|
||||
Tile tile = world.tile(req.x, req.y);
|
||||
if(tile != null && tile.entity instanceof BuildEntity){
|
||||
BuildEntity b = tile.entity();
|
||||
float dist = Math.min(b.dst(x, y) - placeDistance, 0);
|
||||
if(dist / type.maxVelocity < b.buildCost * 0.9f){
|
||||
target = b;
|
||||
this.isBreaking = req.breaking;
|
||||
setState(build);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
updateBuilding();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateRotation(){
|
||||
if(target != null && state.is(repair) && target.dst(this) < type.range){
|
||||
rotation = Mathf.slerpDelta(rotation, angleTo(target), 0.3f);
|
||||
}else{
|
||||
rotation = Mathf.slerpDelta(rotation, velocity.angle(), 0.3f);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void behavior(){
|
||||
if(health <= health * type.retreatPercent){
|
||||
setState(retreat);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public UnitState getStartState(){
|
||||
return repair;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawOver(){
|
||||
drawBuilding();
|
||||
}
|
||||
|
||||
@Override
|
||||
public float drawSize(){
|
||||
return isBuilding() ? placeDistance * 2f : 30f;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canCreateBlocks(){
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(DataOutput data) throws IOException{
|
||||
super.write(data);
|
||||
data.writeInt(state.is(repair) && target instanceof TileEntity ? ((TileEntity)target).tile.pos() : -1);
|
||||
writeBuilding(data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(DataInput data) throws IOException{
|
||||
super.read(data);
|
||||
int repairing = data.readInt();
|
||||
|
||||
readBuilding(data);
|
||||
|
||||
if(repairing != -1){
|
||||
Tile tile = world.tile(repairing);
|
||||
target = tile.entity;
|
||||
state.set(repair);
|
||||
}else{
|
||||
state.set(retreat);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user