Merging of map saves

This commit is contained in:
Anuken
2019-05-14 11:54:22 -04:00
168 changed files with 2942 additions and 2604 deletions
@@ -85,9 +85,8 @@ public class Damage{
public static void collideLine(Bullet hitter, Team team, Effect effect, float x, float y, float angle, float length){
tr.trns(angle, length);
world.raycastEachWorld(x, y, x + tr.x, y + tr.y, (cx, cy) -> {
Tile tile = world.tile(cx, cy);
if(tile != null) tile = tile.target();
if(tile != null && tile.entity != null && tile.target().getTeamID() != team.ordinal() && tile.entity.collide(hitter)){
Tile tile = world.ltile(cx, cy);
if(tile != null && tile.entity != null && tile.getTeamID() != team.ordinal() && tile.entity.collide(hitter)){
tile.entity.collision(hitter);
hitter.getBulletType().hit(hitter, tile.worldx(), tile.worldy());
}
@@ -216,12 +215,10 @@ public class Damage{
int scaledDamage = (int)(damage * (1f - (float)dst / radius));
bits.set(bitOffset + x, bitOffset + y);
Tile tile = world.tile(startx + x, starty + y);
Tile tile = world.ltile(startx + x, starty + y);
if(scaledDamage <= 0 || tile == null) continue;
tile = tile.target();
//apply damage to entity if needed
if(tile.entity != null && tile.getTeam() != team){
int health = (int)tile.entity.health;
@@ -7,10 +7,11 @@ import io.anuke.arc.function.Consumer;
import io.anuke.arc.function.Predicate;
import io.anuke.arc.graphics.Camera;
import io.anuke.arc.math.geom.Rectangle;
import io.anuke.mindustry.Vars;
import io.anuke.mindustry.entities.traits.DrawTrait;
import io.anuke.mindustry.entities.traits.Entity;
import static io.anuke.mindustry.Vars.collisions;
public class Entities{
public static final int maxLeafObjects = 4;
private static final Array<EntityGroup<?>> groupArray = new Array<>();
@@ -48,7 +49,7 @@ public class Entities{
group.updateEvents();
if(group.useTree()){
Vars.collisions.updatePhysics(group);
collisions.updatePhysics(group);
}
for(Entity e : group.all()){
@@ -225,9 +225,8 @@ public class Bullet extends SolidEntity implements DamageTrait, ScaleTrait, Pool
if(type.hitTiles && collidesTiles() && !supressCollision && initialized){
world.raycastEach(world.toTile(lastPosition().x), world.toTile(lastPosition().y), world.toTile(x), world.toTile(y), (x, y) -> {
Tile tile = world.tile(x, y);
Tile tile = world.ltile(x, y);
if(tile == null) return false;
tile = tile.target();
if(tile.entity != null && tile.entity.collide(this) && type.collides(this, tile) && !tile.entity.isDead() && (type.collidesTeam || tile.getTeam() != team)){
if(tile.getTeam() != team){
@@ -72,6 +72,11 @@ public class Fire extends TimedEntity implements SaveTrait, SyncTrait, Poolable{
}
}
@Override
public byte version(){
return 0;
}
@Override
public float lifetime(){
return lifetime;
@@ -98,7 +103,7 @@ public class Fire extends TimedEntity implements SaveTrait, SyncTrait, Poolable{
return;
}
TileEntity entity = tile.target().entity;
TileEntity entity = tile.link().entity;
boolean damage = entity != null;
float flammability = baseFlammability + puddleFlammability;
@@ -151,7 +156,7 @@ public class Fire extends TimedEntity implements SaveTrait, SyncTrait, Poolable{
}
@Override
public void readSave(DataInput stream) throws IOException{
public void readSave(DataInput stream, byte version) throws IOException{
this.loadedPosition = stream.readInt();
this.lifetime = stream.readFloat();
this.time = stream.readFloat();
@@ -143,6 +143,11 @@ public class Puddle extends SolidEntity implements SaveTrait, Poolable, DrawTrai
return liquid.flammability * amount;
}
@Override
public byte version(){
return 0;
}
@Override
public void hitbox(Rectangle rectangle){
rectangle.setCenter(x, y).setSize(tilesize);
@@ -201,7 +206,7 @@ public class Puddle extends SolidEntity implements SaveTrait, Poolable, DrawTrai
}
});
if(liquid.temperature > 0.7f && (tile.target().entity != null) && Mathf.chance(0.3 * Time.delta())){
if(liquid.temperature > 0.7f && (tile.link().entity != null) && Mathf.chance(0.3 * Time.delta())){
Fire.create(tile);
}
@@ -245,7 +250,7 @@ public class Puddle extends SolidEntity implements SaveTrait, Poolable, DrawTrai
}
@Override
public void readSave(DataInput stream) throws IOException{
public void readSave(DataInput stream, byte version) throws IOException{
this.loadedPosition = stream.readInt();
this.x = stream.readFloat();
this.y = stream.readFloat();
@@ -40,10 +40,98 @@ public interface BuilderTrait extends Entity, TeamTrait{
float placeDistance = 220f;
float mineDistance = 70f;
//due to iOS wierdness
class BuildDataStatic{
static Array<BuildRequest> removal = new Array<>();
static Vector2[] tmptr = new Vector2[]{new Vector2(), new Vector2(), new Vector2(), new Vector2()};
/**
* Update building mechanism for this unit.
* This includes mining.
*/
default void updateBuilding(){
float finalPlaceDst = state.rules.infiniteResources ? Float.MAX_VALUE : placeDistance;
Unit unit = (Unit)this;
//remove already completed build requests
removal.clear();
for(BuildRequest req : getPlaceQueue()){
removal.add(req);
}
getPlaceQueue().clear();
for(BuildRequest request : removal){
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)
&& world.tile(request.x, request.y).block() == request.block))){
getPlaceQueue().addLast(request);
}
}
BuildRequest current = getCurrentRequest();
//update mining here
if(current == null){
if(getMineTile() != null){
updateMining();
}
return;
}else{
setMineTile(null);
}
Tile tile = world.tile(current.x, current.y);
if(dst(tile) > finalPlaceDst){
if(getPlaceQueue().size > 1){
getPlaceQueue().removeFirst();
getPlaceQueue().addLast(current);
}
return;
}
if(!(tile.block() instanceof BuildBlock)){
if(canCreateBlocks() && !current.breaking && Build.validPlace(getTeam(), current.x, current.y, current.block, current.rotation)){
Call.beginPlace(getTeam(), current.x, current.y, current.block, current.rotation);
}else if(canCreateBlocks() && current.breaking && Build.validBreak(getTeam(), current.x, current.y)){
Call.beginBreak(getTeam(), current.x, current.y);
}else{
getPlaceQueue().removeFirst();
return;
}
}
TileEntity core = unit.getClosestCore();
//if there is no core to build with or no build entity, stop building!
if((core == null && !state.rules.infiniteResources) || !(tile.entity instanceof BuildEntity)){
return;
}
//otherwise, update it.
BuildEntity entity = tile.entity();
if(entity == null){
return;
}
if(unit.dst(tile) <= finalPlaceDst){
unit.rotation = Mathf.slerpDelta(unit.rotation, unit.angleTo(entity), 0.4f);
}
//progress is synced, thus not updated clientside
if(!Net.client()){
//deconstructing is 2x as fast
if(current.breaking){
entity.deconstruct(unit, core, 2f / entity.buildCost * Time.delta() * getBuildPower(tile) * state.rules.buildSpeedMultiplier);
}else{
entity.construct(unit, core, 1f / entity.buildCost * Time.delta() * getBuildPower(tile) * state.rules.buildSpeedMultiplier);
}
current.progress = entity.progress();
}else{
entity.progress = current.progress;
}
if(!current.initialized){
Core.app.post(() -> Events.fire(new BuildSelectEvent(tile, unit.getTeam(), this, current.breaking)));
current.initialized = true;
}
}
/** Returns the queue for storing build requests. */
@@ -148,97 +236,10 @@ public interface BuilderTrait extends Entity, TeamTrait{
return getPlaceQueue().size == 0 ? null : getPlaceQueue().first();
}
/**
* Update building mechanism for this unit.
* This includes mining.
*/
default void updateBuilding(){
Unit unit = (Unit)this;
//remove already completed build requests
removal.clear();
for(BuildRequest req : getPlaceQueue()){
removal.add(req);
}
getPlaceQueue().clear();
for(BuildRequest request : removal){
if(!((request.breaking && world.tile(request.x, request.y).block() == Blocks.air) ||
(!request.breaking && (world.tile(request.x, request.y).getRotation() == request.rotation || !request.block.rotate)
&& world.tile(request.x, request.y).block() == request.block))){
getPlaceQueue().addLast(request);
}
}
BuildRequest current = getCurrentRequest();
//update mining here
if(current == null){
if(getMineTile() != null){
updateMining();
}
return;
}else{
setMineTile(null);
}
Tile tile = world.tile(current.x, current.y);
if(dst(tile) > placeDistance){
if(getPlaceQueue().size > 1){
getPlaceQueue().removeFirst();
getPlaceQueue().addLast(current);
}
return;
}
if(!(tile.block() instanceof BuildBlock)){
if(canCreateBlocks() && !current.breaking && Build.validPlace(getTeam(), current.x, current.y, current.block, current.rotation)){
Call.beginPlace(getTeam(), current.x, current.y, current.block, current.rotation);
}else if(canCreateBlocks() && current.breaking && Build.validBreak(getTeam(), current.x, current.y)){
Call.beginBreak(getTeam(), current.x, current.y);
}else{
getPlaceQueue().removeFirst();
return;
}
}
TileEntity core = unit.getClosestCore();
//if there is no core to build with or no build entity, stop building!
if(core == null || !(tile.entity instanceof BuildEntity)){
return;
}
//otherwise, update it.
BuildEntity entity = tile.entity();
if(entity == null){
return;
}
if(unit.dst(tile) <= placeDistance){
unit.rotation = Mathf.slerpDelta(unit.rotation, unit.angleTo(entity), 0.4f);
}
//progress is synced, thus not updated clientside
if(!Net.client()){
//deconstructing is 2x as fast
if(current.breaking){
entity.deconstruct(unit, core, 2f / entity.buildCost * Time.delta() * getBuildPower(tile) * state.rules.buildSpeedMultiplier);
}else{
entity.construct(unit, core, 1f / entity.buildCost * Time.delta() * getBuildPower(tile) * state.rules.buildSpeedMultiplier);
}
current.progress = entity.progress();
}else{
entity.progress = current.progress;
}
if(!current.initialized){
Core.app.post(() -> Events.fire(new BuildSelectEvent(tile, unit.getTeam(), this, current.breaking)));
current.initialized = true;
}
//due to iOS wierdness, this is apparently required
class BuildDataStatic{
static Array<BuildRequest> removal = new Array<>();
static Vector2[] tmptr = new Vector2[]{new Vector2(), new Vector2(), new Vector2(), new Vector2()};
}
/** Do not call directly. */
@@ -291,7 +292,7 @@ public interface BuilderTrait extends Entity, TeamTrait{
Tile tile = world.tile(request.x, request.y);
if(dst(tile) > placeDistance){
if(dst(tile) > placeDistance && !state.isEditor()){
return;
}
@@ -4,4 +4,5 @@ package io.anuke.mindustry.entities.traits;
* Marks an entity as serializable.
*/
public interface SaveTrait extends Entity, TypeTrait, Saveable{
byte version();
}
@@ -4,6 +4,5 @@ import java.io.*;
public interface Saveable{
void writeSave(DataOutput stream) throws IOException;
void readSave(DataInput stream) throws IOException;
void readSave(DataInput stream, byte version) throws IOException;
}
@@ -41,15 +41,6 @@ public interface SyncTrait extends Entity, TypeTrait{
return true;
}
/** Whether this entity is clipped and not synced when out of viewport. */
default boolean isClipped(){
return true;
}
default float clipSize(){
return (this instanceof DrawTrait ? ((DrawTrait)this).drawSize() : 8f);
}
//Read and write sync data, usually position
void write(DataOutput data) throws IOException;
@@ -23,9 +23,7 @@ public interface TypeTrait{
lastRegisteredID[0]++;
}
/**
* Registers a syncable type by ID.
*/
/**Gets a syncable type by ID.*/
static Supplier<? extends TypeTrait> getTypeByID(int id){
if(id == -1){
throw new IllegalArgumentException("Attempt to retrieve invalid entity type ID! Did you forget to set it in ContentLoader.registerTypes()?");
@@ -306,11 +306,6 @@ public abstract class BaseUnit extends Unit implements ShooterTrait{
return type.hitsize * 10;
}
@Override
public float clipSize(){
return isBoss() ? 10000000000f : super.clipSize();
}
@Override
public void onDeath(){
Call.onUnitDeath(this);
@@ -338,6 +333,11 @@ public abstract class BaseUnit extends Unit implements ShooterTrait{
return unitGroups[team.ordinal()];
}
@Override
public byte version(){
return 0;
}
@Override
public void writeSave(DataOutput stream) throws IOException{
super.writeSave(stream);
@@ -346,8 +346,8 @@ public abstract class BaseUnit extends Unit implements ShooterTrait{
}
@Override
public void readSave(DataInput stream) throws IOException{
super.readSave(stream);
public void readSave(DataInput stream, byte version) throws IOException{
super.readSave(stream, version);
byte type = stream.readByte();
this.spawner = stream.readInt();
@@ -365,7 +365,9 @@ public abstract class BaseUnit extends Unit implements ShooterTrait{
@Override
public void read(DataInput data) throws IOException{
float lastx = x, lasty = y, lastrot = rotation;
super.readSave(data);
super.readSave(data, version());
this.type = content.getByID(ContentType.unit, data.readByte());
this.spawner = data.readInt();
@@ -432,7 +432,7 @@ public class Player extends Unit implements BuilderTrait, ShooterTrait{
if(getCurrentRequest() == request && request.progress > 0.001f) continue;
if(request.breaking){
Block block = world.tile(request.x, request.y).target().block();
Block block = world.ltile(request.x, request.y).block();
//draw removal request
Lines.stroke(2f, Pal.removeBack);
@@ -636,13 +636,17 @@ public class Player extends Unit implements BuilderTrait, ShooterTrait{
}
protected void updateShooting(){
if(isShooting() && mech.canShoot(this)){
if(!state.isEditor() && isShooting() && mech.canShoot(this)){
mech.weapon.update(this, pointerX, pointerY);
}
}
protected void updateFlying(){
if(Units.invalidateTarget(target, this) && !(target instanceof TileEntity && ((TileEntity)target).damaged() && target.isValid() && ((TileEntity)target).isAdded() && target.getTeam() == team && mech.canHeal && dst(target) < getWeapon().bullet.range())){
if(Units.invalidateTarget(target, this) && !(target instanceof TileEntity && ((TileEntity)target).damaged() && target.isValid() && target.getTeam() == team && mech.canHeal && dst(target) < getWeapon().bullet.range())){
target = null;
}
if(state.isEditor()){
target = null;
}
@@ -790,7 +794,11 @@ public class Player extends Unit implements BuilderTrait, ShooterTrait{
public void updateRespawning(){
if(spawner != null && spawner.isValid()){
if(state.isEditor()){
//instant respawn at center of map.
set(world.width() * tilesize/2f, world.height() * tilesize/2f);
setDead(false);
}else if(spawner != null && spawner.isValid()){
spawner.updateSpawning(this);
}else if(!netServer.isWaitingForPlayers()){
if(!Net.client()){
@@ -817,8 +825,8 @@ public class Player extends Unit implements BuilderTrait, ShooterTrait{
//region read and write methods
@Override
public boolean isClipped(){
return false;
public byte version(){
return 0;
}
@Override
@@ -833,7 +841,7 @@ public class Player extends Unit implements BuilderTrait, ShooterTrait{
}
@Override
public void readSave(DataInput stream) throws IOException{
public void readSave(DataInput stream, byte version) throws IOException{
boolean local = stream.readBoolean();
if(local){
@@ -844,14 +852,14 @@ public class Player extends Unit implements BuilderTrait, ShooterTrait{
lastSpawner = (SpawnerTrait)stile.entity;
}
Player player = headless ? this : Vars.player;
player.readSaveSuper(stream);
player.readSaveSuper(stream, version);
player.mech = content.getByID(ContentType.mech, mechid);
player.dead = false;
}
}
private void readSaveSuper(DataInput stream) throws IOException{
super.readSave(stream);
private void readSaveSuper(DataInput stream, byte version) throws IOException{
super.readSave(stream, version);
add();
}
@@ -859,7 +867,7 @@ public class Player extends Unit implements BuilderTrait, ShooterTrait{
@Override
public void write(DataOutput buffer) throws IOException{
super.writeSave(buffer, !isLocal);
TypeIO.writeStringData(buffer, name); //TODO writing strings is very inefficient
TypeIO.writeStringData(buffer, name);
buffer.writeByte(Pack.byteValue(isAdmin) | (Pack.byteValue(dead) << 1) | (Pack.byteValue(isBoosting) << 2) | (Pack.byteValue(isTyping) << 3));
buffer.writeInt(Color.rgba8888(color));
buffer.writeByte(mech.id);
@@ -873,7 +881,9 @@ public class Player extends Unit implements BuilderTrait, ShooterTrait{
@Override
public void read(DataInput buffer) throws IOException{
float lastx = x, lasty = y, lastrot = rotation, lastvx = velocity.x, lastvy = velocity.y;
super.readSave(buffer);
super.readSave(buffer, version());
name = TypeIO.readStringData(buffer);
byte bools = buffer.readByte();
isAdmin = (bools & 1) != 0;
@@ -1,14 +1,12 @@
package io.anuke.mindustry.entities.type;
import io.anuke.annotations.Annotations.Loc;
import io.anuke.annotations.Annotations.Remote;
import io.anuke.annotations.Annotations.*;
import io.anuke.arc.Events;
import io.anuke.arc.collection.Array;
import io.anuke.arc.collection.ObjectSet;
import io.anuke.arc.math.geom.Point2;
import io.anuke.arc.math.geom.Vector2;
import io.anuke.arc.util.Interval;
import io.anuke.arc.util.Time;
import io.anuke.arc.util.*;
import io.anuke.mindustry.entities.EntityGroup;
import io.anuke.mindustry.entities.bullet.Bullet;
import io.anuke.mindustry.entities.impl.BaseEntity;
@@ -115,16 +113,35 @@ public class TileEntity extends BaseEntity implements TargetTrait, HealthTrait{
return dead || tile.entity != this;
}
@CallSuper
public void write(DataOutput stream) throws IOException{
stream.writeShort((short)health);
stream.writeByte(Pack.byteByte(tile.getTeamID(), tile.rotation())); //team + rotation
if(items != null) items.write(stream);
if(power != null) power.write(stream);
if(liquids != null) liquids.write(stream);
if(cons != null) cons.write(stream);
}
public void writeConfig(DataOutput stream) throws IOException{
@CallSuper
public void read(DataInput stream, byte revision) throws IOException{
health = stream.readUnsignedShort();
byte tr = stream.readByte();
byte team = Pack.leftByte(tr);
byte rotation = Pack.rightByte(tr);
tile.setTeam(Team.all[team]);
tile.rotation(rotation);
if(items != null) items.read(stream);
if(power != null) power.read(stream);
if(liquids != null) liquids.read(stream);
if(cons != null) cons.read(stream);
}
public void read(DataInput stream) throws IOException{
}
public void readConfig(DataInput stream) throws IOException{
/** Returns the version of this TileEntity IO code.*/
public byte version(){
return 0;
}
public boolean collide(Bullet other){
@@ -168,14 +185,14 @@ public class TileEntity extends BaseEntity implements TargetTrait, HealthTrait{
Point2[] nearby = Edges.getEdges(block.size);
for(Point2 point : nearby){
Tile other = world.tile(tile.x + point.x, tile.y + point.y);
Tile other = world.ltile(tile.x + point.x, tile.y + point.y);
//remove this tile from all nearby tile's proximities
if(other != null){
other = other.target();
other.block().onProximityUpdate(other);
}
if(other != null && other.entity != null){
other.entity.proximity.removeValue(tile, true);
if(other.entity != null){
other.entity.proximity.removeValue(tile, true);
}
}
}
}
@@ -186,10 +203,9 @@ public class TileEntity extends BaseEntity implements TargetTrait, HealthTrait{
Point2[] nearby = Edges.getEdges(block.size);
for(Point2 point : nearby){
Tile other = world.tile(tile.x + point.x, tile.y + point.y);
Tile other = world.ltile(tile.x + point.x, tile.y + point.y);
if(other == null) continue;
other = other.target();
if(other.entity == null || !(other.interactable(tile.getTeam()))) continue;
other.block().onProximityUpdate(other);
@@ -280,6 +296,11 @@ public class TileEntity extends BaseEntity implements TargetTrait, HealthTrait{
}
}
@Override
public boolean isValid(){
return !isDead() && tile.entity == this;
}
@Override
public EntityGroup targetGroup(){
return tileGroup;
@@ -1,5 +1,6 @@
package io.anuke.mindustry.entities.type;
import io.anuke.annotations.Annotations.Nullable;
import io.anuke.arc.Core;
import io.anuke.arc.Events;
import io.anuke.arc.graphics.Color;
@@ -8,7 +9,8 @@ import io.anuke.arc.graphics.g2d.TextureRegion;
import io.anuke.arc.math.Mathf;
import io.anuke.arc.math.geom.Geometry;
import io.anuke.arc.math.geom.Vector2;
import io.anuke.arc.util.*;
import io.anuke.arc.util.Time;
import io.anuke.arc.util.Tmp;
import io.anuke.mindustry.content.Blocks;
import io.anuke.mindustry.content.Fx;
import io.anuke.mindustry.entities.*;
@@ -138,7 +140,7 @@ public abstract class Unit extends DestructibleEntity implements SaveTrait, Targ
}
@Override
public void readSave(DataInput stream) throws IOException{
public void readSave(DataInput stream, byte version) throws IOException{
byte team = stream.readByte();
boolean dead = stream.readBoolean();
float x = stream.readFloat();
@@ -150,7 +152,7 @@ public abstract class Unit extends DestructibleEntity implements SaveTrait, Targ
byte itemID = stream.readByte();
short itemAmount = stream.readShort();
this.status.readSave(stream);
this.status.readSave(stream, version);
this.item.amount = itemAmount;
this.item.item = content.item(itemID);
this.dead = dead;
@@ -221,7 +223,7 @@ public abstract class Unit extends DestructibleEntity implements SaveTrait, Targ
velocity.add(moveVector.x / mass() * Time.delta(), moveVector.y / mass() * Time.delta());
}
public TileEntity getClosestCore(){
public @Nullable TileEntity getClosestCore(){
TeamData data = state.teams.get(team);
Tile tile = Geometry.findClosest(x, y, data.cores);
@@ -58,7 +58,7 @@ public class Drone extends FlyingUnit implements BuilderTrait{
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.getRotation(), entity.cblock));
getPlaceQueue().addLast(new BuildRequest(entity.tile.x, entity.tile.y, entity.tile.rotation(), entity.cblock));
}
}
@@ -130,7 +130,7 @@ public class Statuses implements Saveable{
}
@Override
public void readSave(DataInput stream) throws IOException{
public void readSave(DataInput stream, byte version) throws IOException{
for(StatusEntry effect : statuses){
Pools.free(effect);
}