Auto-rebuilding enemy drones / GC improvements

This commit is contained in:
Anuken
2019-06-16 18:50:51 -04:00
parent 273c74b275
commit 625fbdb2d7
23 changed files with 159 additions and 72 deletions

Binary file not shown.

View File

@@ -792,8 +792,9 @@ public class Fx implements ContentList{
Draw.color(Color.LIGHT_GRAY, Color.GRAY, e.fin()); Draw.color(Color.LIGHT_GRAY, Color.GRAY, e.fin());
for(int i : Mathf.signs){ for(int i : Mathf.signs){
float ex = e.x, ey = e.y, fout = e.fout();
Angles.randLenVectors(e.id, 4, 1f + e.finpow() * 11f, e.rotation + 90f * i, 20f, (x, y) -> { Angles.randLenVectors(e.id, 4, 1f + e.finpow() * 11f, e.rotation + 90f * i, 20f, (x, y) -> {
Fill.circle(e.x + x, e.y + y, e.fout() * 1.5f); Fill.circle(ex + x, ey + y, fout * 1.5f);
}); });
} }
@@ -816,8 +817,9 @@ public class Fx implements ContentList{
Draw.color(Color.LIGHT_GRAY); Draw.color(Color.LIGHT_GRAY);
for(int i : Mathf.signs){ for(int i : Mathf.signs){
float ex = e.x, ey = e.y, fout = e.fout();
Angles.randLenVectors(e.id, 4, -e.finpow() * 15f, e.rotation + 90f * i, 25f, (x, y) -> { Angles.randLenVectors(e.id, 4, -e.finpow() * 15f, e.rotation + 90f * i, 25f, (x, y) -> {
Fill.circle(e.x + x, e.y + y, e.fout() * 2f); Fill.circle(ex + x, ey + y, fout * 2f);
}); });
} }

View File

@@ -61,7 +61,7 @@ public class Logic implements ApplicationListener{
} }
TeamData data = state.teams.get(tile.getTeam()); TeamData data = state.teams.get(tile.getTeam());
data.brokenBlocks.addFirst(BrokenBlock.get(tile.x, tile.y, block.id, tile.rotation())); data.brokenBlocks.addFirst(BrokenBlock.get(tile.x, tile.y, tile.rotation(), block.id));
}); });
} }

View File

@@ -360,11 +360,11 @@ public class NetClient implements ApplicationListener{
if(timer.get(0, playerSyncTime)){ if(timer.get(0, playerSyncTime)){
BuildRequest[] requests; BuildRequest[] requests;
//limit to 10 to prevent buffer overflows //limit to 10 to prevent buffer overflows
int usedRequests = Math.min(player.getPlaceQueue().size, 10); int usedRequests = Math.min(player.buildQueue().size, 10);
requests = new BuildRequest[usedRequests]; requests = new BuildRequest[usedRequests];
for(int i = 0; i < usedRequests; i++){ for(int i = 0; i < usedRequests; i++){
requests[i] = player.getPlaceQueue().get(i); requests[i] = player.buildQueue().get(i);
} }
Call.onClientShapshot(lastSent++, player.x, player.y, Call.onClientShapshot(lastSent++, player.x, player.y,

View File

@@ -282,7 +282,7 @@ public class NetServer implements ApplicationListener{
player.isTyping = chatting; player.isTyping = chatting;
player.isBoosting = boosting; player.isBoosting = boosting;
player.isShooting = shooting; player.isShooting = shooting;
player.getPlaceQueue().clear(); player.buildQueue().clear();
for(BuildRequest req : requests){ for(BuildRequest req : requests){
Tile tile = world.tile(req.x, req.y); Tile tile = world.tile(req.x, req.y);
if(tile == null) continue; if(tile == null) continue;
@@ -292,7 +292,7 @@ public class NetServer implements ApplicationListener{
}else if(!req.breaking && tile.block() == req.block && (!req.block.rotate || tile.rotation() == req.rotation)){ }else if(!req.breaking && tile.block() == req.block && (!req.block.rotate || tile.rotation() == req.rotation)){
continue; continue;
} }
player.getPlaceQueue().addLast(req); player.buildQueue().addLast(req);
} }
vector.set(x - player.getInterpolator().target.x, y - player.getInterpolator().target.y); vector.set(x - player.getInterpolator().target.x, y - player.getInterpolator().target.y);

View File

@@ -13,7 +13,6 @@ import io.anuke.mindustry.entities.traits.Entity;
import static io.anuke.mindustry.Vars.collisions; import static io.anuke.mindustry.Vars.collisions;
public class Entities{ public class Entities{
public static final int maxLeafObjects = 4;
private static final Array<EntityGroup<?>> groupArray = new Array<>(); private static final Array<EntityGroup<?>> groupArray = new Array<>();
private static final IntMap<EntityGroup<?>> groups = new IntMap<>(); private static final IntMap<EntityGroup<?>> groups = new IntMap<>();
private static final Rectangle viewport = new Rectangle(); private static final Rectangle viewport = new Rectangle();

View File

@@ -17,7 +17,7 @@ public class EntityGroup<T extends Entity>{
private final Array<T> entitiesToRemove = new Array<>(false, 16); private final Array<T> entitiesToRemove = new Array<>(false, 16);
private final Array<T> entitiesToAdd = new Array<>(false, 16); private final Array<T> entitiesToAdd = new Array<>(false, 16);
private IntMap<T> map; private IntMap<T> map;
private QuadTree<T> tree; private QuadTree tree;
private Consumer<T> removeListener; private Consumer<T> removeListener;
private Consumer<T> addListener; private Consumer<T> addListener;
@@ -27,7 +27,7 @@ public class EntityGroup<T extends Entity>{
this.type = type; this.type = type;
if(useTree){ if(useTree){
tree = new QuadTree<>(Entities.maxLeafObjects, new Rectangle(0, 0, 0, 0)); tree = new QuadTree<>(new Rectangle(0, 0, 0, 0));
} }
} }
@@ -124,7 +124,7 @@ public class EntityGroup<T extends Entity>{
/** Resizes the internal quadtree, if it is enabled.*/ /** Resizes the internal quadtree, if it is enabled.*/
public void resize(float x, float y, float w, float h){ public void resize(float x, float y, float w, float h){
if(useTree){ if(useTree){
tree = new QuadTree<>(Entities.maxLeafObjects, new Rectangle(x, y, w, h)); tree = new QuadTree<>(new Rectangle(x, y, w, h));
} }
} }

View File

@@ -7,7 +7,7 @@ public interface BuilderMinerTrait extends MinerTrait, BuilderTrait{
updateBuilding(); updateBuilding();
//mine only when not building //mine only when not building
if(getCurrentRequest() == null){ if(buildRequest() == null){
updateMining(); updateMining();
} }
} }

View File

@@ -35,21 +35,21 @@ public interface BuilderTrait extends Entity, TeamTrait{
Unit unit = (Unit)this; Unit unit = (Unit)this;
//remove already completed build requests //remove already completed build requests
removal.clear(); removal.clear();
for(BuildRequest req : getPlaceQueue()){ for(BuildRequest req : buildQueue()){
removal.add(req); removal.add(req);
} }
getPlaceQueue().clear(); buildQueue().clear();
for(BuildRequest request : removal){ for(BuildRequest request : removal){
if(!((request.breaking && world.tile(request.x, request.y).block() == Blocks.air) || if(!((request.breaking && world.tile(request.x, request.y).block() == Blocks.air) ||
(!request.breaking && (world.tile(request.x, request.y).rotation() == request.rotation || !request.block.rotate) (!request.breaking && (world.tile(request.x, request.y).rotation() == request.rotation || !request.block.rotate)
&& world.tile(request.x, request.y).block() == request.block))){ && world.tile(request.x, request.y).block() == request.block))){
getPlaceQueue().addLast(request); buildQueue().addLast(request);
} }
} }
BuildRequest current = getCurrentRequest(); BuildRequest current = buildRequest();
if(current == null){ if(current == null){
return; return;
@@ -58,9 +58,9 @@ public interface BuilderTrait extends Entity, TeamTrait{
Tile tile = world.tile(current.x, current.y); Tile tile = world.tile(current.x, current.y);
if(dst(tile) > finalPlaceDst){ if(dst(tile) > finalPlaceDst){
if(getPlaceQueue().size > 1){ if(buildQueue().size > 1){
getPlaceQueue().removeFirst(); buildQueue().removeFirst();
getPlaceQueue().addLast(current); buildQueue().addLast(current);
} }
return; return;
} }
@@ -71,7 +71,7 @@ public interface BuilderTrait extends Entity, TeamTrait{
}else if(canCreateBlocks() && current.breaking && Build.validBreak(getTeam(), current.x, current.y)){ }else if(canCreateBlocks() && current.breaking && Build.validBreak(getTeam(), current.x, current.y)){
Call.beginBreak(getTeam(), current.x, current.y); Call.beginBreak(getTeam(), current.x, current.y);
}else{ }else{
getPlaceQueue().removeFirst(); buildQueue().removeFirst();
return; return;
} }
} }
@@ -115,7 +115,7 @@ public interface BuilderTrait extends Entity, TeamTrait{
} }
/** Returns the queue for storing build requests. */ /** Returns the queue for storing build requests. */
Queue<BuildRequest> getPlaceQueue(); Queue<BuildRequest> buildQueue();
/** Build power, can be any float. 1 = builds recipes in normal time, 0 = doesn't build at all. */ /** Build power, can be any float. 1 = builds recipes in normal time, 0 = doesn't build at all. */
float getBuildPower(Tile tile); float getBuildPower(Tile tile);
@@ -126,7 +126,7 @@ public interface BuilderTrait extends Entity, TeamTrait{
} }
default void writeBuilding(DataOutput output) throws IOException{ default void writeBuilding(DataOutput output) throws IOException{
BuildRequest request = getCurrentRequest(); BuildRequest request = buildRequest();
if(request != null){ if(request != null){
output.writeByte(request.breaking ? 1 : 0); output.writeByte(request.breaking ? 1 : 0);
@@ -146,7 +146,7 @@ public interface BuilderTrait extends Entity, TeamTrait{
} }
default void readBuilding(DataInput input, boolean applyChanges) throws IOException{ default void readBuilding(DataInput input, boolean applyChanges) throws IOException{
if(applyChanges) getPlaceQueue().clear(); if(applyChanges) buildQueue().clear();
byte type = input.readByte(); byte type = input.readByte();
if(type != -1){ if(type != -1){
@@ -165,26 +165,26 @@ public interface BuilderTrait extends Entity, TeamTrait{
request.progress = progress; request.progress = progress;
if(applyChanges){ if(applyChanges){
getPlaceQueue().addLast(request); buildQueue().addLast(request);
}else if(isBuilding()){ }else if(isBuilding()){
getCurrentRequest().progress = progress; buildRequest().progress = progress;
} }
} }
} }
/** 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 buildQueue().size != 0;
} }
/** Clears the placement queue. */ /** Clears the placement queue. */
default void clearBuilding(){ default void clearBuilding(){
getPlaceQueue().clear(); buildQueue().clear();
} }
/** Add another build requests to the tail of the queue, if it doesn't exist there yet. */ /** Add another build requests to the tail of the queue, if it doesn't exist there yet. */
default void addBuildRequest(BuildRequest place){ default void addBuildRequest(BuildRequest place){
for(BuildRequest request : getPlaceQueue()){ for(BuildRequest request : buildQueue()){
if(request.x == place.x && request.y == place.y){ if(request.x == place.x && request.y == place.y){
return; return;
} }
@@ -193,15 +193,15 @@ public interface BuilderTrait extends Entity, TeamTrait{
if(tile != null && tile.entity instanceof BuildEntity){ if(tile != null && tile.entity instanceof BuildEntity){
place.progress = tile.<BuildEntity>entity().progress; place.progress = tile.<BuildEntity>entity().progress;
} }
getPlaceQueue().addLast(place); buildQueue().addLast(place);
} }
/** /**
* Return the build requests currently active, or the one at the top of the queue. * Return the build requests currently active, or the one at the top of the queue.
* May return null. * May return null.
*/ */
default BuildRequest getCurrentRequest(){ default BuildRequest buildRequest(){
return getPlaceQueue().size == 0 ? null : getPlaceQueue().first(); return buildQueue().size == 0 ? null : buildQueue().first();
} }
//due to iOS weirdness, this is apparently required //due to iOS weirdness, this is apparently required
@@ -215,7 +215,7 @@ public interface BuilderTrait extends Entity, TeamTrait{
if(!isBuilding()) return; if(!isBuilding()) return;
Unit unit = (Unit)this; Unit unit = (Unit)this;
BuildRequest request = getCurrentRequest(); BuildRequest request = buildRequest();
Tile tile = world.tile(request.x, request.y); Tile tile = world.tile(request.x, request.y);
if(dst(tile) > placeDistance && !state.isEditor()){ if(dst(tile) > placeDistance && !state.isEditor()){

View File

@@ -112,10 +112,8 @@ public abstract class BaseUnit extends Unit implements ShooterTrait{
this.state.set(state); this.state.set(state);
} }
public void retarget(Runnable run){ public boolean retarget(){
if(timer.get(timerTarget, 20)){ return timer.get(timerTarget, 20);
run.run();
}
} }
/** Only runs when the unit has a target. */ /** Only runs when the unit has a target. */

View File

@@ -32,7 +32,7 @@ public abstract class FlyingUnit extends BaseUnit{
target = null; target = null;
} }
retarget(() -> { if(retarget()){
targetClosest(); targetClosest();
if(target == null) targetClosestEnemyFlag(BlockFlag.producer); if(target == null) targetClosestEnemyFlag(BlockFlag.producer);
@@ -41,7 +41,7 @@ public abstract class FlyingUnit extends BaseUnit{
if(target == null){ if(target == null){
setState(patrol); setState(patrol);
} }
}); };
if(target != null){ if(target != null){
attack(type.attackLength); attack(type.attackLength);
@@ -71,7 +71,7 @@ public abstract class FlyingUnit extends BaseUnit{
}, },
patrol = new UnitState(){ patrol = new UnitState(){
public void update(){ public void update(){
retarget(() -> { if(retarget()){
targetClosest(); targetClosest();
targetClosestEnemyFlag(BlockFlag.target); targetClosestEnemyFlag(BlockFlag.target);
@@ -81,7 +81,7 @@ public abstract class FlyingUnit extends BaseUnit{
} }
target = getClosestCore(); target = getClosestCore();
}); };
if(target != null){ if(target != null){
circle(60f + Mathf.absin(Time.time() + Mathf.randomSeed(id) * 1200f, 70f, 1200f)); circle(60f + Mathf.absin(Time.time() + Mathf.randomSeed(id) * 1200f, 70f, 1200f));

View File

@@ -176,7 +176,9 @@ public abstract class GroundUnit extends BaseUnit{
target = null; target = null;
} }
retarget(this::targetClosest); if(retarget()){
targetClosest();
}
} }
protected void patrol(){ protected void patrol(){

View File

@@ -241,7 +241,7 @@ public class Player extends Unit implements BuilderMinerTrait, ShooterTrait{
} }
@Override @Override
public Queue<BuildRequest> getPlaceQueue(){ public Queue<BuildRequest> buildQueue(){
return placeQueue; return placeQueue;
} }
@@ -428,8 +428,8 @@ public class Player extends Unit implements BuilderMinerTrait, ShooterTrait{
/** Draw all current build requests. Does not draw the beam effect, only the positions. */ /** Draw all current build requests. Does not draw the beam effect, only the positions. */
public void drawBuildRequests(){ public void drawBuildRequests(){
BuildRequest last = null; BuildRequest last = null;
for(BuildRequest request : getPlaceQueue()){ for(BuildRequest request : buildQueue()){
if(request.progress > 0.01f || (getCurrentRequest() == request && (dst(request.x * tilesize, request.y * tilesize) <= placeDistance || state.isEditor()))) continue; if(request.progress > 0.01f || (buildRequest() == request && (dst(request.x * tilesize, request.y * tilesize) <= placeDistance || state.isEditor()))) continue;
if(request.breaking){ if(request.breaking){
Block block = world.ltile(request.x, request.y).block(); Block block = world.ltile(request.x, request.y).block();

View File

@@ -19,14 +19,14 @@ public abstract class BaseDrone extends FlyingUnit{
if(health >= maxHealth()){ if(health >= maxHealth()){
state.set(attack); state.set(attack);
}else if(!targetHasFlag(BlockFlag.repair)){ }else if(!targetHasFlag(BlockFlag.repair)){
retarget(() -> { if(retarget()){
Tile repairPoint = Geometry.findClosest(x, y, world.indexer.getAllied(team, BlockFlag.repair)); Tile repairPoint = Geometry.findClosest(x, y, world.indexer.getAllied(team, BlockFlag.repair));
if(repairPoint != null){ if(repairPoint != null){
target = repairPoint; target = repairPoint;
}else{ }else{
setState(getStartState()); setState(getStartState());
} }
}); }
}else{ }else{
circle(40f); circle(40f);
} }

View File

@@ -6,12 +6,15 @@ import io.anuke.arc.collection.IntIntMap;
import io.anuke.arc.collection.Queue; import io.anuke.arc.collection.Queue;
import io.anuke.arc.math.Mathf; import io.anuke.arc.math.Mathf;
import io.anuke.arc.util.*; import io.anuke.arc.util.*;
import io.anuke.mindustry.Vars;
import io.anuke.mindustry.entities.EntityGroup; import io.anuke.mindustry.entities.EntityGroup;
import io.anuke.mindustry.entities.traits.BuilderTrait; import io.anuke.mindustry.entities.traits.BuilderTrait;
import io.anuke.mindustry.entities.traits.TargetTrait; import io.anuke.mindustry.entities.traits.TargetTrait;
import io.anuke.mindustry.entities.type.*; import io.anuke.mindustry.entities.type.*;
import io.anuke.mindustry.entities.units.UnitState; import io.anuke.mindustry.entities.units.UnitState;
import io.anuke.mindustry.game.EventType.BuildSelectEvent; import io.anuke.mindustry.game.EventType.BuildSelectEvent;
import io.anuke.mindustry.game.Teams.TeamData;
import io.anuke.mindustry.gen.BrokenBlock;
import io.anuke.mindustry.world.Tile; import io.anuke.mindustry.world.Tile;
import io.anuke.mindustry.world.blocks.BuildBlock; import io.anuke.mindustry.world.blocks.BuildBlock;
import io.anuke.mindustry.world.blocks.BuildBlock.BuildEntity; import io.anuke.mindustry.world.blocks.BuildBlock.BuildEntity;
@@ -20,7 +23,6 @@ import java.io.*;
import static io.anuke.mindustry.Vars.*; import static io.anuke.mindustry.Vars.*;
//TODO follow players
public class BuilderDrone extends BaseDrone implements BuilderTrait{ public class BuilderDrone extends BaseDrone implements BuilderTrait{
private static final StaticReset reset = new StaticReset(); private static final StaticReset reset = new StaticReset();
private static final IntIntMap totals = new IntIntMap(); private static final IntIntMap totals = new IntIntMap();
@@ -43,12 +45,22 @@ public class BuilderDrone extends BaseDrone implements BuilderTrait{
BuildEntity entity = (BuildEntity)target; BuildEntity entity = (BuildEntity)target;
TileEntity core = getClosestCore(); TileEntity core = getClosestCore();
if(entity != null && core != null && (entity.progress < 1f || entity.progress > 0f) && entity.tile.block() instanceof BuildBlock){ //building is valid if(isBuilding() && entity == null && isRebuild()){
target = world.tile(buildRequest().x, buildRequest().y);
circle(placeDistance * 0.7f);
target = null;
BuildRequest request = buildRequest();
if(world.tile(request.x, request.y).entity instanceof BuildEntity){
target = world.tile(request.x, request.y).entity;
}
}else 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(!isBuilding() && dst(target) < placeDistance * 0.9f){ //within distance, begin placing
if(isBreaking){ if(isBreaking){
getPlaceQueue().addLast(new BuildRequest(entity.tile.x, entity.tile.y)); buildQueue().addLast(new BuildRequest(entity.tile.x, entity.tile.y));
}else{ }else{
getPlaceQueue().addLast(new BuildRequest(entity.tile.x, entity.tile.y, entity.tile.rotation(), entity.cblock)); buildQueue().addLast(new BuildRequest(entity.tile.x, entity.tile.y, entity.tile.rotation(), entity.cblock));
} }
} }
@@ -58,7 +70,7 @@ public class BuilderDrone extends BaseDrone implements BuilderTrait{
if(playerTarget == null || playerTarget.getTeam() != team || !playerTarget.isValid()){ if(playerTarget == null || playerTarget.getTeam() != team || !playerTarget.isValid()){
playerTarget = null; playerTarget = null;
retarget(() -> { if(retarget()){
float minDst = Float.POSITIVE_INFINITY; float minDst = Float.POSITIVE_INFINITY;
int minDrones = Integer.MAX_VALUE; int minDrones = Integer.MAX_VALUE;
@@ -75,7 +87,13 @@ public class BuilderDrone extends BaseDrone implements BuilderTrait{
} }
} }
} }
}); }
if(getSpawner() != null){
target = getSpawner();
circle(40f);
target = null;
}
}else{ }else{
incDrones(playerTarget); incDrones(playerTarget);
TargetTrait prev = target; TargetTrait prev = target;
@@ -103,7 +121,7 @@ public class BuilderDrone extends BaseDrone implements BuilderTrait{
BuilderDrone drone = (BuilderDrone)unit; BuilderDrone drone = (BuilderDrone)unit;
if(drone.isBuilding()){ if(drone.isBuilding()){
//stop building if opposite building begins. //stop building if opposite building begins.
BuildRequest req = drone.getCurrentRequest(); BuildRequest req = drone.buildRequest();
if(req.breaking != event.breaking && req.x == event.tile.x && req.y == event.tile.y){ if(req.breaking != event.breaking && req.x == event.tile.x && req.y == event.tile.y){
drone.clearBuilding(); drone.clearBuilding();
drone.target = null; drone.target = null;
@@ -131,13 +149,17 @@ public class BuilderDrone extends BaseDrone implements BuilderTrait{
} }
} }
boolean isRebuild(){
return Vars.state.rules.enemyCheat && team == waveTeam;
}
@Override @Override
public float getBuildPower(Tile tile){ public float getBuildPower(Tile tile){
return type.buildPower; return type.buildPower;
} }
@Override @Override
public Queue<BuildRequest> getPlaceQueue(){ public Queue<BuildRequest> buildQueue(){
return placeQueue; return placeQueue;
} }
@@ -147,8 +169,8 @@ public class BuilderDrone extends BaseDrone implements BuilderTrait{
if(!isBuilding() && timer.get(timerTarget2, 15)){ if(!isBuilding() && timer.get(timerTarget2, 15)){
for(Player player : playerGroup.all()){ for(Player player : playerGroup.all()){
if(player.getTeam() == team && player.getCurrentRequest() != null){ if(player.getTeam() == team && player.buildRequest() != null){
BuildRequest req = player.getCurrentRequest(); BuildRequest req = player.buildRequest();
Tile tile = world.tile(req.x, req.y); Tile tile = world.tile(req.x, req.y);
if(tile != null && tile.entity instanceof BuildEntity){ if(tile != null && tile.entity instanceof BuildEntity){
BuildEntity b = tile.entity(); BuildEntity b = tile.entity();
@@ -162,6 +184,16 @@ public class BuilderDrone extends BaseDrone implements BuilderTrait{
} }
} }
} }
if(isRebuild()){
TeamData data = Vars.state.teams.get(team);
if(!data.brokenBlocks.isEmpty()){
long block = data.brokenBlocks.removeLast();
placeQueue.addFirst(new BuildRequest(BrokenBlock.x(block), BrokenBlock.y(block), BrokenBlock.rotation(block), content.block(BrokenBlock.block(block))));
setState(build);
}
}
} }
updateBuilding(); updateBuilding();

View File

@@ -52,13 +52,13 @@ public class MinerDrone extends BaseDrone implements MinerTrait{
return; return;
} }
retarget(() -> { if(retarget()){
findItem(); findItem();
if(targetItem == null) return; if(targetItem == null) return;
target = world.indexer.findClosestOre(x, y, targetItem); target = world.indexer.findClosestOre(x, y, targetItem);
}); };
if(target instanceof Tile){ if(target instanceof Tile){
moveTo(type.range / 1.5f); moveTo(type.range / 1.5f);

View File

@@ -19,7 +19,9 @@ public class RepairDrone extends BaseDrone{
public void update(){ public void update(){
retarget(() -> target = Units.findDamagedTile(team, x, y)); if(retarget()){
target = Units.findDamagedTile(team, x, y);
}
if(target != null){ if(target != null){
if(target.dst(RepairDrone.this) > type.range){ if(target.dst(RepairDrone.this) > type.range){

View File

@@ -42,11 +42,17 @@ public class Teams{
return enemiesOf(team).contains(other); return enemiesOf(team).contains(other);
} }
public class TeamData{ /** Allocates a new array with the active teams.
* Never call in the main game loop.*/
public Array<TeamData> getActive(){
return Array.select(map, t -> t != null);
}
public static class TeamData{
public final ObjectSet<Tile> cores = new ObjectSet<>(); public final ObjectSet<Tile> cores = new ObjectSet<>();
public final LongQueue brokenBlocks = new LongQueue();
public final EnumSet<Team> enemies; public final EnumSet<Team> enemies;
public final Team team; public final Team team;
public LongQueue brokenBlocks = new LongQueue();
public TeamData(Team team, EnumSet<Team> enemies){ public TeamData(Team team, EnumSet<Team> enemies){
this.team = team; this.team = team;

View File

@@ -214,7 +214,7 @@ public class DesktopInput extends InputHandler{
mode = placing; mode = placing;
}else if(selected != null){ }else if(selected != null){
//only begin shooting if there's no cursor event //only begin shooting if there's no cursor event
if(!tileTapped(selected) && !tryTapPlayer(Core.input.mouseWorld().x, Core.input.mouseWorld().y) && player.getPlaceQueue().size == 0 && !droppingItem && if(!tileTapped(selected) && !tryTapPlayer(Core.input.mouseWorld().x, Core.input.mouseWorld().y) && player.buildQueue().size == 0 && !droppingItem &&
!tryBeginMine(selected) && player.getMineTile() == null && !ui.chatfrag.chatOpen()){ !tryBeginMine(selected) && player.getMineTile() == null && !ui.chatfrag.chatOpen()){
player.isShooting = true; player.isShooting = true;
} }
@@ -222,7 +222,7 @@ public class DesktopInput extends InputHandler{
player.isShooting = true; player.isShooting = true;
} }
}else if(Core.input.keyTap(Binding.deselect) && (block != null || mode != none || player.isBuilding()) && }else if(Core.input.keyTap(Binding.deselect) && (block != null || mode != none || player.isBuilding()) &&
!(player.getCurrentRequest() != null && player.getCurrentRequest().breaking && Core.keybinds.get(Binding.deselect) == Core.keybinds.get(Binding.break_block))){ !(player.buildRequest() != null && player.buildRequest().breaking && Core.keybinds.get(Binding.deselect) == Core.keybinds.get(Binding.break_block))){
if(block == null){ if(block == null){
player.clearBuilding(); player.clearBuilding();
} }

View File

@@ -1,10 +1,12 @@
package io.anuke.mindustry.io; package io.anuke.mindustry.io;
import io.anuke.arc.collection.EnumSet;
import io.anuke.arc.collection.LongQueue;
import io.anuke.arc.util.serialization.Json; import io.anuke.arc.util.serialization.Json;
import io.anuke.arc.util.serialization.JsonValue; import io.anuke.arc.util.serialization.JsonValue;
import io.anuke.mindustry.Vars; import io.anuke.mindustry.Vars;
import io.anuke.mindustry.game.Rules; import io.anuke.mindustry.game.*;
import io.anuke.mindustry.game.SpawnGroup; import io.anuke.mindustry.game.Teams.TeamData;
import io.anuke.mindustry.type.*; import io.anuke.mindustry.type.*;
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@@ -36,6 +38,25 @@ public class JsonIO{
return Vars.content.getByName(ContentType.item, jsonData.asString()); return Vars.content.getByName(ContentType.item, jsonData.asString());
} }
}); });
setSerializer(TeamData.class, new Serializer<TeamData>(){
@Override
public void write(Json json, TeamData object, Class knownType){
json.writeObjectStart();
json.writeValue("brokenBlocks", object.brokenBlocks.toArray());
json.writeValue("team", object.team.ordinal());
json.writeObjectEnd();
}
@Override
public TeamData read(Json json, JsonValue jsonData, Class type){
long[] blocks = jsonData.get("brokenBlocks").asLongArray();
Team team = Team.all[jsonData.getInt("team", 0)];
TeamData out = new TeamData(team, EnumSet.of(new Team[]{}));
out.brokenBlocks = new LongQueue(blocks);
return out;
}
});
}}; }};
public static String write(Object object){ public static String write(Object object){

View File

@@ -1,13 +1,14 @@
package io.anuke.mindustry.io; package io.anuke.mindustry.io;
import io.anuke.arc.collection.Array; import io.anuke.arc.collection.*;
import io.anuke.arc.collection.StringMap;
import io.anuke.arc.util.Time; import io.anuke.arc.util.Time;
import io.anuke.arc.util.io.CounterInputStream; import io.anuke.arc.util.io.CounterInputStream;
import io.anuke.mindustry.entities.Entities; import io.anuke.mindustry.entities.Entities;
import io.anuke.mindustry.entities.EntityGroup; import io.anuke.mindustry.entities.EntityGroup;
import io.anuke.mindustry.entities.traits.*; import io.anuke.mindustry.entities.traits.*;
import io.anuke.mindustry.game.*; import io.anuke.mindustry.game.*;
import io.anuke.mindustry.game.Teams.TeamData;
import io.anuke.mindustry.gen.BrokenBlock;
import io.anuke.mindustry.maps.Map; import io.anuke.mindustry.maps.Map;
import io.anuke.mindustry.type.ContentType; import io.anuke.mindustry.type.ContentType;
import io.anuke.mindustry.world.*; import io.anuke.mindustry.world.*;
@@ -64,6 +65,7 @@ public abstract class SaveVersion extends SaveFileReader{
"wavetime", state.wavetime, "wavetime", state.wavetime,
"stats", JsonIO.write(state.stats), "stats", JsonIO.write(state.stats),
"rules", JsonIO.write(state.rules), "rules", JsonIO.write(state.rules),
"teamdata", JsonIO.write(state.teams.getActive().toArray(TeamData.class)),
"width", world.width(), "width", world.width(),
"height", world.height() "height", world.height()
).merge(tags)); ).merge(tags));
@@ -77,6 +79,13 @@ public abstract class SaveVersion extends SaveFileReader{
state.stats = JsonIO.read(Stats.class, map.get("stats", "{}")); state.stats = JsonIO.read(Stats.class, map.get("stats", "{}"));
state.rules = JsonIO.read(Rules.class, map.get("rules", "{}")); state.rules = JsonIO.read(Rules.class, map.get("rules", "{}"));
if(state.rules.spawns.isEmpty()) state.rules.spawns = defaultWaves.get(); if(state.rules.spawns.isEmpty()) state.rules.spawns = defaultWaves.get();
//only broken blocks are transferred over right now; nothing else
TeamData[] teams = JsonIO.read(TeamData[].class, map.get("teamdata", "[]"));
for(TeamData data : teams){
state.teams.get(data.team).brokenBlocks = data.brokenBlocks;
}
Map worldmap = world.maps.byName(map.get("mapname", "\\\\\\")); Map worldmap = world.maps.byName(map.get("mapname", "\\\\\\"));
world.setMap(worldmap == null ? new Map(StringMap.of( world.setMap(worldmap == null ? new Map(StringMap.of(
"name", map.get("mapname", "Unknown"), "name", map.get("mapname", "Unknown"),
@@ -92,13 +101,13 @@ public abstract class SaveVersion extends SaveFileReader{
//floor + overlay //floor + overlay
for(int i = 0; i < world.width() * world.height(); i++){ for(int i = 0; i < world.width() * world.height(); i++){
Tile tile = world.tile(i % world.width(), i / world.width()); Tile tile = world.rawTile(i % world.width(), i / world.width());
stream.writeShort(tile.floorID()); stream.writeShort(tile.floorID());
stream.writeShort(tile.overlayID()); stream.writeShort(tile.overlayID());
int consecutives = 0; int consecutives = 0;
for(int j = i + 1; j < world.width() * world.height() && consecutives < 255; j++){ for(int j = i + 1; j < world.width() * world.height() && consecutives < 255; j++){
Tile nextTile = world.tile(j % world.width(), j / world.width()); Tile nextTile = world.rawTile(j % world.width(), j / world.width());
if(nextTile.floorID() != tile.floorID() || nextTile.overlayID() != tile.overlayID()){ if(nextTile.floorID() != tile.floorID() || nextTile.overlayID() != tile.overlayID()){
break; break;
@@ -113,7 +122,7 @@ public abstract class SaveVersion extends SaveFileReader{
//blocks //blocks
for(int i = 0; i < world.width() * world.height(); i++){ for(int i = 0; i < world.width() * world.height(); i++){
Tile tile = world.tile(i % world.width(), i / world.width()); Tile tile = world.rawTile(i % world.width(), i / world.width());
stream.writeShort(tile.blockID()); stream.writeShort(tile.blockID());
if(tile.entity != null){ if(tile.entity != null){
@@ -126,7 +135,7 @@ public abstract class SaveVersion extends SaveFileReader{
int consecutives = 0; int consecutives = 0;
for(int j = i + 1; j < world.width() * world.height() && consecutives < 255; j++){ for(int j = i + 1; j < world.width() * world.height() && consecutives < 255; j++){
Tile nextTile = world.tile(j % world.width(), j / world.width()); Tile nextTile = world.rawTile(j % world.width(), j / world.width());
if(nextTile.blockID() != tile.blockID()){ if(nextTile.blockID() != tile.blockID()){
break; break;
@@ -264,6 +273,8 @@ public abstract class SaveVersion extends SaveFileReader{
} }
content.setTemporaryMapper(map); content.setTemporaryMapper(map);
remapContent();
} }
public void writeContentHeader(DataOutput stream) throws IOException{ public void writeContentHeader(DataOutput stream) throws IOException{
@@ -287,4 +298,17 @@ public abstract class SaveVersion extends SaveFileReader{
} }
} }
} }
/** sometimes it's necessary to remap IDs after the content header is read.*/
public void remapContent(){
for(Team team : Team.all){
if(state.teams.isActive(team)){
LongQueue queue = state.teams.get(team).brokenBlocks;
for(int i = 0; i < queue.size; i++){
//remap broken block IDs
queue.set(i, BrokenBlock.block(queue.get(i), content.block(BrokenBlock.block(queue.get(i))).id));
}
}
}
}
} }

View File

@@ -69,6 +69,7 @@ public class CustomRulesDialog extends FloatingDialog{
title("$rules.title.enemy"); title("$rules.title.enemy");
check("$rules.attack", b -> rules.attackMode = b, () -> rules.attackMode); check("$rules.attack", b -> rules.attackMode = b, () -> rules.attackMode);
check("$rules.enemyCheat", b -> rules.enemyCheat = b, () -> rules.enemyCheat);
number("$rules.enemycorebuildradius", f -> rules.enemyCoreBuildRadius = f * tilesize, () -> Math.min(rules.enemyCoreBuildRadius / tilesize, 200)); number("$rules.enemycorebuildradius", f -> rules.enemyCoreBuildRadius = f * tilesize, () -> Math.min(rules.enemyCoreBuildRadius / tilesize, 200));
} }

View File

@@ -123,7 +123,6 @@ public class SettingsMenuDialog extends SettingsDialog{
sound.add("[LIGHT_GRAY]there is no sound implemented in v4 yet"); sound.add("[LIGHT_GRAY]there is no sound implemented in v4 yet");
game.screenshakePref(); game.screenshakePref();
game.checkPref("effects", true);
if(mobile){ if(mobile){
game.checkPref("autotarget", true); game.checkPref("autotarget", true);
} }
@@ -212,6 +211,7 @@ public class SettingsMenuDialog extends SettingsDialog{
} }
} }
graphics.checkPref("effects", true);
graphics.checkPref("playerchat", true); graphics.checkPref("playerchat", true);
graphics.checkPref("minimap", !mobile); graphics.checkPref("minimap", !mobile);
graphics.checkPref("fps", false); graphics.checkPref("fps", false);