Further drone splitting / Even drone player distribution

This commit is contained in:
Anuken
2019-06-13 22:22:25 -04:00
parent 2a8b8d040b
commit 548da3ea5f
7 changed files with 331 additions and 279 deletions

View File

@@ -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();
}

View 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);
}
}

View File

@@ -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

View File

@@ -1,5 +1,5 @@
package io.anuke.mindustry.entities.type.base;
public class Phantom extends UtilityDrone{
public class Phantom extends BuilderDrone{
}

View File

@@ -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;
}
}
}

View File

@@ -1,4 +1,4 @@
package io.anuke.mindustry.entities.type.base;
public class Spirit extends UtilityDrone{
public class Spirit extends RepairDrone{
}

View File

@@ -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);
}
}
}