This commit is contained in:
Anuken
2018-07-05 17:11:27 -04:00
93 changed files with 1338 additions and 1273 deletions

View File

@@ -30,10 +30,7 @@ import io.anuke.ucore.entities.EntityGroup;
import io.anuke.ucore.entities.trait.SolidTrait;
import io.anuke.ucore.graphics.Draw;
import io.anuke.ucore.graphics.Lines;
import io.anuke.ucore.util.Angles;
import io.anuke.ucore.util.Mathf;
import io.anuke.ucore.util.ThreadQueue;
import io.anuke.ucore.util.Timer;
import io.anuke.ucore.util.*;
import java.io.DataInput;
import java.io.DataOutput;
@@ -58,6 +55,7 @@ public class Player extends Unit implements BuilderTrait, CarryTrait, ShooterTra
public float boostHeat;
public Color color = new Color();
public Mech mech;
public int spawner;
public NetConnection con;
public int playerIndex = 0;
@@ -66,7 +64,6 @@ public class Player extends Unit implements BuilderTrait, CarryTrait, ShooterTra
public TargetTrait target;
public TargetTrait moveTarget;
private boolean respawning;
private float walktime;
private Queue<BuildRequest> placeQueue = new ThreadQueue<>();
private Tile mining;
@@ -178,6 +175,11 @@ public class Player extends Unit implements BuilderTrait, CarryTrait, ShooterTra
return mech.weapon.getAmmoType(item) != null && inventory.canAcceptAmmo(mech.weapon.getAmmoType(item));
}
@Override
public void added() {
baseRotation = 90f;
}
@Override
public void addAmmo(Item item) {
inventory.addAmmo(mech.weapon.getAmmoType(item));
@@ -202,7 +204,7 @@ public class Player extends Unit implements BuilderTrait, CarryTrait, ShooterTra
public void damage(float amount){
CallEntity.onPlayerDamage(this, calculateDamage(amount));
if(health <= 0 && !dead && isLocal){
if(health <= 0 && !dead){
CallEntity.onPlayerDeath(this);
}
}
@@ -225,7 +227,6 @@ public class Player extends Unit implements BuilderTrait, CarryTrait, ShooterTra
if(player == null) return;
player.dead = true;
player.respawning = false;
player.placeQueue.clear();
player.dropCarry();
@@ -438,12 +439,10 @@ public class Player extends Unit implements BuilderTrait, CarryTrait, ShooterTra
if(isDead()){
isBoosting = false;
boostHeat = 0f;
CoreEntity entity = (CoreEntity)getClosestCore();
if (!respawning && entity != null) {
entity.trySetPlayer(this);
}
updateRespawning();
return;
}else{
spawner = -1;
}
if(!isLocal){
@@ -451,6 +450,11 @@ public class Player extends Unit implements BuilderTrait, CarryTrait, ShooterTra
updateBuilding(this); //building happens even with non-locals
status.update(this); //status effect updating also happens with non locals for effect purposes
if(getCarrier() != null){
x = getCarrier().getX();
y = getCarrier().getY();
}
if(Net.server()){
updateShooting(); //server simulates player shooting
}
@@ -532,11 +536,16 @@ public class Player extends Unit implements BuilderTrait, CarryTrait, ShooterTra
movement.limit(speed);
velocity.add(movement);
float prex = x, prey = y;
updateVelocityStatus(mech.drag, 10f);
moved = distanceTo(prex, prey) > 0.01f;
if(getCarrier() == null){
velocity.add(movement);
float prex = x, prey = y;
updateVelocityStatus(mech.drag, 10f);
moved = distanceTo(prex, prey) > 0.01f;
}else{
velocity.setZero();
x = Mathf.lerpDelta(x, getCarrier().getX(), 0.1f);
y = Mathf.lerpDelta(y, getCarrier().getY(), 0.1f);
}
if(!isShooting()){
if(!movement.isZero()) {
@@ -610,7 +619,7 @@ public class Player extends Unit implements BuilderTrait, CarryTrait, ShooterTra
if (target == null) {
isShooting = false;
target = Units.getClosestTarget(team, x, y, inventory.getAmmoRange());
} else {
} else if(target.isValid()){
//rotate toward and shoot the target
rotation = Mathf.slerpDelta(rotation, angleTo(target), 0.2f);
@@ -649,7 +658,6 @@ public class Player extends Unit implements BuilderTrait, CarryTrait, ShooterTra
inventory.clear();
placeQueue.clear();
dead = true;
respawning = false;
trail.clear();
health = maxHealth();
@@ -660,12 +668,21 @@ public class Player extends Unit implements BuilderTrait, CarryTrait, ShooterTra
return isShooting && inventory.hasAmmo() && (!isBoosting || mech.flying);
}
public void setRespawning(){
respawning = true;
public void updateRespawning(){
if (spawner != -1 && world.tile(spawner) != null && world.tile(spawner).entity instanceof SpawnerTrait) {
((SpawnerTrait) world.tile(spawner).entity).updateSpawning(this);
}else{
CoreEntity entity = (CoreEntity)getClosestCore();
if(entity != null){
this.spawner = entity.tile.id();
}
}
}
public void setRespawning(boolean respawning){
this.respawning = respawning;
public void beginRespawning(SpawnerTrait spawner){
this.spawner = spawner.getTile().packedPosition();
this.dead = true;
}
@Override
@@ -697,12 +714,18 @@ public class Player extends Unit implements BuilderTrait, CarryTrait, ShooterTra
public void readSave(DataInput stream) throws IOException {
boolean local = stream.readBoolean();
if(local){
if(local && !headless){
byte mechid = stream.readByte();
int index = stream.readByte();
players[index].readSaveSuper(stream);
players[index].mech = Upgrade.getByID(mechid);
players[index].dead = false;
}else if(local){
byte mechid = stream.readByte();
stream.readByte();
readSaveSuper(stream);
mech = Upgrade.getByID(mechid);
dead = false;
}
}
@@ -722,6 +745,9 @@ public class Player extends Unit implements BuilderTrait, CarryTrait, ShooterTra
buffer.writeByte(mech.id);
buffer.writeBoolean(isBoosting);
buffer.writeInt(mining == null ? -1 : mining.packedPosition());
buffer.writeInt(spawner);
writeBuilding(buffer);
}
@Override
@@ -735,6 +761,10 @@ public class Player extends Unit implements BuilderTrait, CarryTrait, ShooterTra
mech = Upgrade.getByID(buffer.readByte());
boolean boosting = buffer.readBoolean();
int mine = buffer.readInt();
spawner = buffer.readInt();
readBuilding(buffer, !isLocal);
interpolator.read(lastx, lasty, x, y, time, rotation);
rotation = lastrot;

View File

@@ -131,6 +131,10 @@ public class TileEntity extends BaseEntity implements TargetTrait {
}
}
public Tile getTile(){
return tile;
}
@Override
public Team getTeam() {
return tile.getTeam();

View File

@@ -132,6 +132,7 @@ public abstract class Unit extends DestructibleEntity implements SaveTrait, Targ
@Override
public void readSave(DataInput stream) throws IOException {
byte team = stream.readByte();
boolean dead = stream.readBoolean();
float x = stream.readFloat();
float y = stream.readFloat();
byte xv = stream.readByte();
@@ -141,6 +142,7 @@ public abstract class Unit extends DestructibleEntity implements SaveTrait, Targ
this.status.readSave(stream);
this.inventory.readSave(stream);
this.dead = dead;
this.team = Team.all[team];
this.health = health;
this.x = x;
@@ -151,6 +153,7 @@ public abstract class Unit extends DestructibleEntity implements SaveTrait, Targ
public void writeSave(DataOutput stream, boolean net) throws IOException {
stream.writeByte(team.ordinal());
stream.writeBoolean(isDead());
stream.writeFloat(net ? interpolator.target.x : x);
stream.writeFloat(net ? interpolator.target.y : y);
stream.writeByte((byte)(Mathf.clamp(velocity.x, -maxAbsVelocity, maxAbsVelocity) * velocityPercision));
@@ -196,7 +199,7 @@ public abstract class Unit extends DestructibleEntity implements SaveTrait, Targ
public void avoidOthers(float avoidRange){
EntityPhysics.getNearby(getGroup(), x, y, avoidRange*2f, t -> {
if(t == this || (t instanceof Unit && (((Unit) t).isDead() || (((Unit) t).isFlying() != isFlying())))) return;
if(t == this || (t instanceof Unit && (((Unit) t).isDead() || (((Unit) t).isFlying() != isFlying()) || ((Unit) t).getCarrier() == this) || getCarrier() == t)) return;
float dst = distanceTo(t);
if(dst > avoidRange) return;
velocity.add(moveVector.set(x, y).sub(t.getX(), t.getY()).setLength(1f * (1f - (dst / avoidRange))));

View File

@@ -43,7 +43,7 @@ public class Fire extends TimedEntity implements SaveTrait, SyncTrait, Poolable
/**Start a fire on the tile. If there already is a file there, refreshes its lifetime.*/
public static void create(Tile tile){
if(Net.client()) return; //not clientside.
if(Net.client() || tile == null) return; //not clientside.
Fire fire = map.get(tile.packedPosition());
@@ -62,7 +62,7 @@ public class Fire extends TimedEntity implements SaveTrait, SyncTrait, Poolable
/**Attempts to extinguish a fire by shortening its life. If there is no fire here, does nothing.*/
public static void extinguish(Tile tile, float intensity) {
if (map.containsKey(tile.packedPosition())) {
if (tile != null && map.containsKey(tile.packedPosition())) {
map.get(tile.packedPosition()).time += intensity * Timers.delta();
}
}

View File

@@ -57,8 +57,7 @@ public class ItemDrop extends SolidEntity implements SaveTrait, SyncTrait, DrawT
return drop;
}
@Remote(called = Loc.server, in = In.entities)
public static void createItemDrop(Item item, int amount, float x, float y, float velocityX, float velocityY){
public static void create(Item item, int amount, float x, float y, float velocityX, float velocityY){
create(item, amount, x, y, 0).getVelocity().set(velocityX, velocityY);
}
@@ -69,6 +68,9 @@ public class ItemDrop extends SolidEntity implements SaveTrait, SyncTrait, DrawT
Effects.effect(UnitFx.pickup, drop);
}
itemGroup.removeByID(itemid);
if(Net.client()){
netClient.addRemovedEntity(itemid);
}
}
/**Internal use only!*/

View File

@@ -8,25 +8,29 @@ import io.anuke.mindustry.content.fx.BlockFx;
import io.anuke.mindustry.entities.Player;
import io.anuke.mindustry.entities.TileEntity;
import io.anuke.mindustry.entities.Unit;
import io.anuke.mindustry.gen.CallBlocks;
import io.anuke.mindustry.gen.CallEntity;
import io.anuke.mindustry.graphics.Palette;
import io.anuke.mindustry.type.Item;
import io.anuke.mindustry.type.Recipe;
import io.anuke.mindustry.world.Build;
import io.anuke.mindustry.world.Tile;
import io.anuke.mindustry.world.blocks.BreakBlock;
import io.anuke.mindustry.world.blocks.BreakBlock.BreakEntity;
import io.anuke.mindustry.world.blocks.BuildBlock;
import io.anuke.mindustry.world.blocks.BuildBlock.BuildEntity;
import io.anuke.ucore.core.Effects;
import io.anuke.ucore.core.Timers;
import io.anuke.ucore.entities.trait.Entity;
import io.anuke.ucore.graphics.Draw;
import io.anuke.ucore.graphics.Fill;
import io.anuke.ucore.graphics.Lines;
import io.anuke.ucore.graphics.Shapes;
import io.anuke.ucore.util.*;
import io.anuke.ucore.util.Angles;
import io.anuke.ucore.util.Geometry;
import io.anuke.ucore.util.Mathf;
import io.anuke.ucore.util.Translator;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.util.Arrays;
import static io.anuke.mindustry.Vars.tilesize;
@@ -34,7 +38,7 @@ import static io.anuke.mindustry.Vars.tmptr;
import static io.anuke.mindustry.Vars.world;
/**Interface for units that build, break or mine things.*/
public interface BuilderTrait {
public interface BuilderTrait extends Entity{
//these are not instance variables!
float placeDistance = 140f;
float mineDistance = 70f;
@@ -54,6 +58,49 @@ public interface BuilderTrait {
/**Build power, can be any float. 1 = builds recipes in normal time, 0 = doesn't build at all.*/
float getBuildPower(Tile tile);
default void writeBuilding(DataOutput output) throws IOException{
BuildRequest request = getCurrentRequest();
if(request != null){
output.writeByte(request.remove ? 1 : 0);
output.writeInt(world.toPacked(request.x, request.y));
if(!request.remove){
output.writeByte(request.recipe.id);
output.writeByte(request.rotation);
}
}else{
output.writeByte(-1);
}
}
default void readBuilding(DataInput input) throws IOException{
readBuilding(input, true);
}
default void readBuilding(DataInput input, boolean applyChanges) throws IOException{
synchronized (getPlaceQueue()) {
if(applyChanges) getPlaceQueue().clear();
byte type = input.readByte();
if (type != -1) {
int position = input.readInt();
BuildRequest request;
if (type == 1) { //remove
request = new BuildRequest(position % world.width(), position / world.width());
} else { //place
byte recipe = input.readByte();
byte rotation = input.readByte();
request = new BuildRequest(position % world.width(), position / world.width(), rotation, Recipe.getByID(recipe));
}
if(applyChanges){
getPlaceQueue().addLast(request);
}
}
}
}
/**Return whether this builder's place queue contains items.*/
default boolean isBuilding(){
return getPlaceQueue().size != 0;
@@ -77,11 +124,7 @@ public interface BuilderTrait {
/**Clears the placement queue.*/
default void clearBuilding(){
if(this instanceof Player) {
CallBlocks.onBuildDeselect((Player) this);
}else{
getPlaceQueue().clear();
}
getPlaceQueue().clear();
}
/**Add another build requests to the tail of the queue, if it doesn't exist there yet.*/
@@ -119,75 +162,40 @@ public interface BuilderTrait {
setMineTile(null);
}
TileEntity core = unit.getClosestCore();
//if there is no core to build with, stop building!
if(core == null){
return;
}
Tile tile = world.tile(current.x, current.y);
if(unit.distanceTo(tile) > placeDistance || //out of range, skip it
(current.lastEntity != null && current.lastEntity.isDead())) { //build/destroy request has died, skip it
getPlaceQueue().removeFirst();
}else if(current.remove){
if (!(tile.block() instanceof BreakBlock)) { //check if haven't started placing
if(Build.validBreak(unit.getTeam(), current.x, current.y)){
//if it's valid, place it
if(!current.requested && unit instanceof Player){
CallBlocks.breakBlock((Player)unit, unit.getTeam(), current.x, current.y);
current.requested = true;
}
}else{
//otherwise, skip it
getPlaceQueue().removeFirst();
}
if (!(tile.block() instanceof BuildBlock)) {
if(!current.remove && Build.validPlace(unit.getTeam(), current.x, current.y, current.recipe.result, current.rotation)) {
Build.beginPlace(unit.getTeam(), current.x, current.y, current.recipe, current.rotation);
}else if(current.remove && Build.validBreak(unit.getTeam(), current.x, current.y)){
Build.beginBreak(unit.getTeam(), current.x, current.y);
}else{
TileEntity core = unit.getClosestCore();
//if there is no core to build with, stop building!
if(core == null){
return;
}
//otherwise, update it.
BreakEntity entity = tile.entity();
current.lastEntity = entity;
entity.addProgress(core, unit, 1f / entity.breakTime * Timers.delta() * getBuildPower(tile));
unit.rotation = Mathf.slerpDelta(unit.rotation, unit.angleTo(entity), 0.4f);
getCurrentRequest().progress = entity.progress();
}
}else{
if (!(tile.block() instanceof BuildBlock)) { //check if haven't started placing
if(Build.validPlace(unit.getTeam(), current.x, current.y, current.recipe.result, current.rotation)){
//if it's valid, place it
if(!current.requested && unit instanceof Player){
CallBlocks.placeBlock((Player)unit, unit.getTeam(), current.x, current.y, current.recipe, current.rotation);
current.requested = true;
}
}else{
//otherwise, skip it
getPlaceQueue().removeFirst();
}
}else{
TileEntity core = unit.getClosestCore();
//if there is no core to build with, stop building!
if(core == null){
return;
}
//otherwise, update it.
BuildEntity entity = tile.entity();
current.lastEntity = entity;
entity.addProgress(core.items, 1f / entity.recipe.cost * Timers.delta() * getBuildPower(tile));
if(unit instanceof Player){
entity.lastBuilder = (Player)unit;
}
unit.rotation = Mathf.slerpDelta(unit.rotation, unit.angleTo(entity), 0.4f);
getCurrentRequest().progress = entity.progress();
getPlaceQueue().removeFirst();
return;
}
}
//otherwise, update it.
BuildEntity entity = tile.entity();
//deconstructing is 2x as fast
if(current.remove){
entity.deconstruct(unit, core, 2f / entity.buildCost * Timers.delta() * getBuildPower(tile));
}else{
entity.construct(unit, core, 1f / entity.buildCost * Timers.delta() * getBuildPower(tile));
}
if(unit.distanceTo(tile) <= placeDistance){
unit.rotation = Mathf.slerpDelta(unit.rotation, unit.angleTo(entity), 0.4f);
}
current.progress = entity.progress();
}
/**Do not call directly.*/
@@ -233,7 +241,11 @@ public interface BuilderTrait {
Tile tile = world.tile(request.x, request.y);
Draw.color(unit.distanceTo(tile) > placeDistance || request.remove ? Palette.remove : Palette.accent);
if(unit.distanceTo(tile) > placeDistance){
return;
}
Draw.color(Palette.accent);
float focusLen = 3.8f + Mathf.absin(Timers.time(), 1.1f, 0.6f);
float px = unit.x + Angles.trnsx(unit.rotation, focusLen);
float py = unit.y + Angles.trnsy(unit.rotation, focusLen);
@@ -302,9 +314,6 @@ public interface BuilderTrait {
public final Recipe recipe;
public final boolean remove;
public boolean requested;
public TileEntity lastEntity;
public float progress;
/**This creates a build request.*/

View File

@@ -32,14 +32,14 @@ public interface CarryTrait extends TeamTrait, SolidTrait, TargetTrait{
CallEntity.setCarryOf(this instanceof Player ? (Player)this : null, this, unit);
}
@Remote(called = Loc.server, targets = Loc.both, forward = true, in = In.entities)
@Remote(called = Loc.both, targets = Loc.both, forward = true, in = In.entities)
static void dropSelf(Player player){
if(player.getCarrier() != null){
player.getCarrier().dropCarry();
}
}
@Remote(called = Loc.server, targets = Loc.both, forward = true, in = In.entities)
@Remote(called = Loc.both, targets = Loc.both, forward = true, in = In.entities)
static void setCarryOf(Player player, CarryTrait trait, CarriableTrait unit){
if(player != null){ //when a server recieves this called from a player, set the carrier to the player.
trait = player;

View File

@@ -0,0 +1,10 @@
package io.anuke.mindustry.entities.traits;
import io.anuke.mindustry.entities.Unit;
import io.anuke.mindustry.world.Tile;
public interface SpawnerTrait {
Tile getTile();
void updateSpawning(Unit unit);
float getSpawnProgress();
}

View File

@@ -12,6 +12,7 @@ import io.anuke.mindustry.entities.Unit;
import io.anuke.mindustry.entities.Units;
import io.anuke.mindustry.entities.effect.ScorchDecal;
import io.anuke.mindustry.entities.traits.ShooterTrait;
import io.anuke.mindustry.entities.traits.SpawnerTrait;
import io.anuke.mindustry.entities.traits.TargetTrait;
import io.anuke.mindustry.game.Team;
import io.anuke.mindustry.game.TeamInfo.TeamData;
@@ -52,7 +53,7 @@ public abstract class BaseUnit extends Unit implements ShooterTrait{
protected boolean isWave;
protected Squad squad;
protected int spawner = -1;
protected int spawner;
/**Initialize the type and team of this unit. Only call once!*/
public void init(UnitType type, Team team){
@@ -62,8 +63,8 @@ public abstract class BaseUnit extends Unit implements ShooterTrait{
this.team = team;
}
public void setSpawner(UnitFactoryEntity spawner) {
this.spawner = spawner.tile.packedPosition();
public void setSpawner(Tile tile) {
this.spawner = tile.packedPosition();
}
public UnitType getType() {
@@ -92,6 +93,19 @@ public abstract class BaseUnit extends Unit implements ShooterTrait{
((TileEntity)target).tile.block().flags.contains(flag);
}
public void updateRespawning(){
if(spawner == -1) return;
Tile tile = world.tile(spawner);
if(tile != null && tile.entity != null){
if(tile.entity instanceof SpawnerTrait){
((SpawnerTrait) tile.entity).updateSpawning(this);
}
}else{
spawner = -1;
}
}
public void setState(UnitState state){
this.state.set(state);
}
@@ -162,6 +176,11 @@ public abstract class BaseUnit extends Unit implements ShooterTrait{
}
}
@Override
public boolean isValid() {
return super.isValid() && isAdded();
}
@Override
public Timer getTimer() {
return timer;
@@ -249,6 +268,11 @@ public abstract class BaseUnit extends Unit implements ShooterTrait{
if(hitTime < 0) hitTime = 0;
if(isDead()){
updateRespawning();
return;
}
if(Net.client()){
interpolate();
status.update(this);
@@ -317,7 +341,7 @@ public abstract class BaseUnit extends Unit implements ShooterTrait{
hitboxTile.setSize(type.hitsizeTile);
state.set(getStartState());
heal();
health(maxHealth());
}
@Override
@@ -348,6 +372,7 @@ public abstract class BaseUnit extends Unit implements ShooterTrait{
public void write(DataOutput data) throws IOException{
super.writeSave(data);
data.writeByte(type.id);
data.writeInt(spawner);
}
@Override
@@ -355,6 +380,7 @@ public abstract class BaseUnit extends Unit implements ShooterTrait{
float lastx = x, lasty = y, lastrot = rotation;
super.readSave(data);
this.type = UnitType.getByID(data.readByte());
this.spawner = data.readInt();
interpolator.read(lastx, lasty, x, y, time, rotation);
rotation = lastrot;
@@ -368,7 +394,9 @@ public abstract class BaseUnit extends Unit implements ShooterTrait{
public static void onUnitDeath(BaseUnit unit){
if(unit == null) return;
UnitDrops.dropItems(unit);
if(Net.server() || !Net.active()){
UnitDrops.dropItems(unit);
}
float explosiveness = 2f + (unit.inventory.hasItem() ? unit.inventory.getItem().item.explosiveness * unit.inventory.getItem().amount : 0f);
float flammability = (unit.inventory.hasItem() ? unit.inventory.getItem().item.flammability * unit.inventory.getItem().amount : 0f);

View File

@@ -22,7 +22,6 @@ import static io.anuke.mindustry.Vars.world;
public abstract class FlyingUnit extends BaseUnit implements CarryTrait{
protected static Translator vec = new Translator();
protected static float maxAim = 30f;
protected static float wobblyness = 0.6f;
protected Trail trail = new Trail(8);

View File

@@ -2,7 +2,7 @@ package io.anuke.mindustry.entities.units;
import io.anuke.mindustry.Vars;
import io.anuke.mindustry.content.Items;
import io.anuke.mindustry.gen.CallEntity;
import io.anuke.mindustry.entities.effect.ItemDrop;
import io.anuke.mindustry.type.Item;
import io.anuke.ucore.util.Mathf;
@@ -21,9 +21,9 @@ public class UnitDrops {
for (int i = 0; i < 3; i++) {
for(Item item : dropTable){
if(Mathf.chance(0.2)){
int amount = Mathf.random(1, 5);
CallEntity.createItemDrop(item, amount, unit.x + Mathf.range(2f), unit.y + Mathf.range(2f),
if(Mathf.chance(0.03)){
int amount = Mathf.random(20, 40);
ItemDrop.create(item, amount, unit.x + Mathf.range(2f), unit.y + Mathf.range(2f),
unit.getVelocity().x + Mathf.range(3f), unit.getVelocity().y + Mathf.range(3f));
}
}

View File

@@ -18,7 +18,6 @@ import io.anuke.mindustry.gen.CallEntity;
import io.anuke.mindustry.graphics.Palette;
import io.anuke.mindustry.type.Item;
import io.anuke.mindustry.type.ItemStack;
import io.anuke.mindustry.type.Recipe;
import io.anuke.mindustry.world.Tile;
import io.anuke.mindustry.world.blocks.BuildBlock;
import io.anuke.mindustry.world.blocks.BuildBlock.BuildEntity;
@@ -206,26 +205,19 @@ public class Drone extends FlyingUnit implements BuilderTrait {
public void write(DataOutput data) throws IOException {
super.write(data);
data.writeInt(mineTile == null ? -1 : mineTile.packedPosition());
data.writeInt(placeQueue.size == 0 ? -1 : world.tile(placeQueue.last().x, placeQueue.last().y).packedPosition());
data.writeByte(placeQueue.size == 0 ? -1 : placeQueue.last().recipe.id);
writeBuilding(data);
}
@Override
public void read(DataInput data, long time) throws IOException {
super.read(data, time);
int mined = data.readInt();
int pp = data.readInt();
byte rid = data.readByte();
readBuilding(data);
if(mined != -1){
mineTile = world.tile(mined);
}
if(pp != -1){
Tile tile = world.tile(pp);
placeQueue.clear();
placeQueue.addLast(new BuildRequest(tile.x, tile.y, tile.getRotation(), Recipe.getByID(rid)));
}
}
public final UnitState