diff --git a/core/src/io/anuke/mindustry/entities/bullet/MassDriverBolt.java b/core/src/io/anuke/mindustry/entities/bullet/MassDriverBolt.java index 01902bd9bc..a3bd379542 100644 --- a/core/src/io/anuke/mindustry/entities/bullet/MassDriverBolt.java +++ b/core/src/io/anuke/mindustry/entities/bullet/MassDriverBolt.java @@ -88,7 +88,6 @@ public class MassDriverBolt extends BulletType{ if(!(b.getData() instanceof DriverBulletData)) return; DriverBulletData data = (DriverBulletData)b.getData(); - data.to.isRecieving = false; for(int i = 0; i < data.items.length; i++){ int amountDropped = Mathf.random(0, data.items[i]); diff --git a/core/src/io/anuke/mindustry/world/blocks/distribution/MassDriver.java b/core/src/io/anuke/mindustry/world/blocks/distribution/MassDriver.java index 20e0cc40f0..cdf537563d 100644 --- a/core/src/io/anuke/mindustry/world/blocks/distribution/MassDriver.java +++ b/core/src/io/anuke/mindustry/world/blocks/distribution/MassDriver.java @@ -3,8 +3,7 @@ package io.anuke.mindustry.world.blocks.distribution; import io.anuke.annotations.Annotations.Loc; import io.anuke.annotations.Annotations.Remote; import io.anuke.arc.Core; -import io.anuke.arc.collection.ObjectSet; -import io.anuke.arc.graphics.Color; +import io.anuke.arc.collection.OrderedSet; import io.anuke.arc.graphics.g2d.*; import io.anuke.arc.math.Angles; import io.anuke.arc.math.Mathf; @@ -63,47 +62,6 @@ public class MassDriver extends Block{ entity.link = position; } - @Remote(called = Loc.server) - public static void onMassDriverFire(Tile tile, Tile target){ - //just in case the client has invalid data - if(!(tile.entity instanceof MassDriverEntity) || !(target.entity instanceof MassDriverEntity)) return; - - MassDriver driver = (MassDriver)tile.block(); - - MassDriverEntity entity = tile.entity(); - MassDriverEntity other = target.entity(); - - entity.reload = 1f; - - entity.power.satisfaction -= Math.min(entity.power.satisfaction, driver.powerPercentageUsed); - - DriverBulletData data = Pools.obtain(DriverBulletData.class, DriverBulletData::new); - data.from = entity; - data.to = other; - int totalUsed = 0; - for(int i = 0; i < content.items().size; i++){ - int maxTransfer = Math.min(entity.items.get(content.item(i)), ((MassDriver)tile.block()).itemCapacity - totalUsed); - data.items[i] = maxTransfer; - totalUsed += maxTransfer; - } - entity.items.clear(); - - float angle = tile.angleTo(target); - - other.isRecieving = true; - Bullet.create(Bullets.driverBolt, entity, entity.getTeam(), - tile.drawx() + Angles.trnsx(angle, driver.translation), tile.drawy() + Angles.trnsy(angle, driver.translation), - angle, 1f, 1f, data); - - Effects.effect(driver.shootEffect, tile.drawx() + Angles.trnsx(angle, driver.translation), - tile.drawy() + Angles.trnsy(angle, driver.translation), angle); - - Effects.effect(driver.smokeEffect, tile.drawx() + Angles.trnsx(angle, driver.translation), - tile.drawy() + Angles.trnsy(angle, driver.translation), angle); - - Effects.shake(driver.shake, driver.shake, entity); - } - @Override public TextureRegion[] generateIcons(){ return new TextureRegion[]{Core.atlas.find(name + "-base"), Core.atlas.find(name)}; @@ -126,51 +84,78 @@ public class MassDriver extends Block{ @Override public void update(Tile tile){ MassDriverEntity entity = tile.entity(); - Tile link = world.tile(entity.link); + boolean hasLink = linkValid(tile); - if(entity.isUnloading){ - tryDump(tile); - if(entity.items.total() <= 0){ - entity.isUnloading = false; - } - } - + //reload regardless of state if(entity.reload > 0f){ entity.reload = Mathf.clamp(entity.reload - entity.delta() / reloadTime); } - //unload when dest is full - if(!linkValid(tile) || (link.entity.items.total() >= itemCapacity) && entity.items.total() > 0){ - entity.isUnloading = true; + //cleanup waiting shooters that are not valid + if(!shooterValid(tile, entity.currentShooter())){ + entity.waitingShooters.remove(entity.currentShooter()); } - if(!entity.isRecieving){ - - if(entity.waiting.size > 0){ //accepting takes priority over shooting - Tile waiter = entity.waiting.first(); - - entity.rotation = Mathf.slerpDelta(entity.rotation, tile.angleTo(waiter), rotateSpeed); - }else if(tile.entity.items.total() >= minDistribute && - linkValid(tile) && - tile.entity.power.satisfaction >= powerPercentageUsed && - link.block().itemCapacity - link.entity.items.total() >= minDistribute && entity.reload <= 0.0001f){ - - MassDriverEntity other = link.entity(); - other.waiting.add(tile); - - float target = tile.angleTo(link); - - entity.rotation = Mathf.slerpDelta(entity.rotation, target, rotateSpeed); - - if(Angles.near(entity.rotation, target, 1f) && - Angles.near(other.rotation, target + 180f, 1f)){ - Call.onMassDriverFire(tile, link); - } + //switch states + if(entity.state == DriverState.idle){ + //start accepting when idle + if(!entity.waitingShooters.isEmpty()){ + entity.state = DriverState.accepting; + }else if(hasLink){ //switch to shooting if there's a valid link. + entity.state = DriverState.shooting; } } - entity.waiting.clear(); + //dump when idle or accepting + if(entity.state == DriverState.idle || entity.state == DriverState.accepting){ + tryDump(tile); + } + + if(entity.state == DriverState.accepting){ + //if there's nothing shooting at this, bail + if(entity.currentShooter() == null){ + entity.state = DriverState.idle; + return; + } + + //align to shooter rotation + entity.rotation = Mathf.slerpDelta(entity.rotation, tile.angleTo(entity.currentShooter()), rotateSpeed); + }else if(entity.state == DriverState.shooting){ + //if there's nothing to shoot at OR someone wants to shoot at this thing, bail + if(!hasLink || !entity.waitingShooters.isEmpty()){ + entity.state = DriverState.idle; + return; + } + + float targetRotation = tile.angleTo(link); + + if( + tile.entity.items.total() >= minDistribute && //must shoot minimum amount of items + tile.entity.power.satisfaction >= powerPercentageUsed && //must have power + link.block().itemCapacity - link.entity.items.total() >= minDistribute && //must have minimum amount of space + entity.reload <= 0.0001f //must have reloaded + ){ + MassDriverEntity other = link.entity(); + other.waitingShooters.add(tile); + + //align to target location + entity.rotation = Mathf.slerpDelta(entity.rotation, targetRotation, rotateSpeed); + + //fire when it's the first in the queue and angles are ready. + if(other.currentShooter() == tile && + other.state == DriverState.accepting && + Angles.near(entity.rotation, targetRotation, 2f) && Angles.near(other.rotation, targetRotation + 180f, 2f)){ + //actually fire + fire(tile, link); + //remove waiting shooters, it's done firing + other.waitingShooters.remove(tile); + //set both states to idle + entity.state = DriverState.idle; + other.state = DriverState.idle; + } + } + } } @Override @@ -244,7 +229,71 @@ public class MassDriver extends Block{ return new MassDriverEntity(); } + protected void fire(Tile tile, Tile target){ + MassDriverEntity entity = tile.entity(); + MassDriverEntity other = target.entity(); + + //reset reload, use power. + entity.reload = 1f; + entity.power.satisfaction -= Math.min(entity.power.satisfaction, powerPercentageUsed); + + DriverBulletData data = Pools.obtain(DriverBulletData.class, DriverBulletData::new); + data.from = entity; + data.to = other; + int totalUsed = 0; + for(int i = 0; i < content.items().size; i++){ + int maxTransfer = Math.min(entity.items.get(content.item(i)), ((MassDriver)tile.block()).itemCapacity - totalUsed); + data.items[i] = maxTransfer; + totalUsed += maxTransfer; + } + entity.items.clear(); + + float angle = tile.angleTo(target); + + Bullet.create(Bullets.driverBolt, entity, entity.getTeam(), + tile.drawx() + Angles.trnsx(angle, translation), tile.drawy() + Angles.trnsy(angle, translation), + angle, 1f, 1f, data); + + Effects.effect(shootEffect, tile.drawx() + Angles.trnsx(angle, translation), + tile.drawy() + Angles.trnsy(angle, translation), angle); + + Effects.effect(smokeEffect, tile.drawx() + Angles.trnsx(angle, translation), + tile.drawy() + Angles.trnsy(angle, translation), angle); + + Effects.shake(shake, shake, entity); + } + + protected void handlePayload(MassDriverEntity entity, Bullet bullet, DriverBulletData data){ + int totalItems = entity.items.total(); + + //add all the items possible + for(int i = 0; i < data.items.length; i++){ + int maxAdd = Math.min(data.items[i], itemCapacity * 2 - totalItems); + entity.items.add(content.item(i), maxAdd); + data.items[i] -= maxAdd; + totalItems += maxAdd; + + if(totalItems >= itemCapacity * 2){ + break; + } + } + + Effects.shake(shake, shake, entity); + Effects.effect(recieveEffect, bullet); + + entity.reload = 1f; + bullet.remove(); + } + + protected boolean shooterValid(Tile tile, Tile other){ + if(other == null) return true; + if(!(other.block() instanceof MassDriver)) return false; + MassDriverEntity entity = other.entity(); + return entity.link == tile.pos() && tile.dst(other) <= range; + } + protected boolean linkValid(Tile tile){ + if(tile == null) return false; MassDriverEntity entity = tile.entity(); if(entity == null || entity.link == -1) return false; Tile link = world.tile(entity.link); @@ -264,63 +313,39 @@ public class MassDriver extends Block{ } public class MassDriverEntity extends TileEntity{ - public int link = -1; - public float rotation = 90; - //set of tiles that currently want to distribute to this tile - public ObjectSet waiting = new ObjectSet<>(); - //whether this mass driver is waiting for a bullet to hit it and deliver items - public boolean isRecieving; - //whether this driver just recieved some items and is now unloading - public boolean isUnloading = true; + int link = -1; + float rotation = 90; + float reload = 0f; + DriverState state = DriverState.idle; + OrderedSet waitingShooters = new OrderedSet<>(); - public float reload = 0f; + Tile currentShooter(){ + return waitingShooters.isEmpty() ? null : waitingShooters.first(); + } public void handlePayload(Bullet bullet, DriverBulletData data){ - int totalItems = items.total(); - - //add all the items possible - for(int i = 0; i < data.items.length; i++){ - int maxAdd = Math.min(data.items[i], itemCapacity * 2 - totalItems); - items.add(content.item(i), maxAdd); - data.items[i] -= maxAdd; - totalItems += maxAdd; - - if(totalItems >= itemCapacity * 2){ - break; - } - } - - //drop all items remaining on the ground - for(int i = 0; i < data.items.length; i++){ - int amountDropped = Mathf.random(0, data.items[i]); - if(amountDropped > 0){ - float angle = Mathf.range(180f); - Effects.effect(Fx.dropItem, Color.WHITE, bullet.x, bullet.y, angle, content.item(i)); - } - } - - reload = 1f; - Effects.shake(shake, shake, this); - Effects.effect(recieveEffect, bullet); - - isRecieving = false; - bullet.remove(); - - if(!linkValid(tile)){ - isUnloading = true; - } + ((MassDriver)block).handlePayload(this, bullet, data); } @Override public void write(DataOutput stream) throws IOException{ stream.writeInt(link); stream.writeFloat(rotation); + stream.writeByte((byte)state.ordinal()); } @Override public void read(DataInput stream) throws IOException{ link = stream.readInt(); rotation = stream.readFloat(); + state = DriverState.values()[stream.readByte()]; } } + + enum DriverState{ + idle, //nothing is shooting at this mass driver and it does not have any target + accepting, //currently getting shot at, unload items + shooting, + unloading + } }