Implemented mass drivers

This commit is contained in:
Anuken
2018-06-16 14:34:11 -04:00
parent ff542a9946
commit 6c620182ea
13 changed files with 447 additions and 40 deletions

View File

@@ -39,6 +39,7 @@ public class Recipes implements ContentList{
new Recipe(distribution, StorageBlocks.sortedunloader, new ItemStack(Items.steel, 5));
new Recipe(distribution, DistributionBlocks.bridgeconveyor, new ItemStack(Items.steel, 5));
new Recipe(distribution, DistributionBlocks.laserconveyor, new ItemStack(Items.steel, 5));
new Recipe(distribution, DistributionBlocks.massdriver, new ItemStack(Items.steel, 1));
new Recipe(weapon, WeaponBlocks.duo, new ItemStack(Items.iron, 7));
new Recipe(weapon, WeaponBlocks.scatter, new ItemStack(Items.iron, 8));

View File

@@ -5,7 +5,8 @@ import io.anuke.mindustry.world.Block;
import io.anuke.mindustry.world.blocks.distribution.*;
public class DistributionBlocks extends BlockList implements ContentList{
public static Block conveyor, steelconveyor, pulseconveyor, router, multiplexer, junction, bridgeconveyor, laserconveyor, sorter, splitter, overflowgate;
public static Block conveyor, steelconveyor, pulseconveyor, router, multiplexer, junction,
bridgeconveyor, laserconveyor, sorter, splitter, overflowgate, massdriver;
@Override
public void load() {
@@ -51,5 +52,11 @@ public class DistributionBlocks extends BlockList implements ContentList{
splitter = new Splitter("splitter");
overflowgate = new OverflowGate("overflowgate");
massdriver = new MassDriver("mass-driver"){{
size = 3;
itemCapacity = 80;
range = 300f;
}};
}
}

View File

@@ -3,29 +3,34 @@ package io.anuke.mindustry.content.bullets;
import com.badlogic.gdx.graphics.Color;
import io.anuke.mindustry.content.Liquids;
import io.anuke.mindustry.content.StatusEffects;
import io.anuke.mindustry.content.fx.BlockFx;
import io.anuke.mindustry.content.fx.BulletFx;
import io.anuke.mindustry.content.fx.EnvironmentFx;
import io.anuke.mindustry.content.fx.Fx;
import io.anuke.mindustry.entities.Damage;
import io.anuke.mindustry.entities.bullet.Bullet;
import io.anuke.mindustry.entities.bullet.BulletType;
import io.anuke.mindustry.entities.bullet.LiquidBulletType;
import io.anuke.mindustry.entities.Damage;
import io.anuke.mindustry.entities.effect.Fire;
import io.anuke.mindustry.entities.effect.Lightning;
import io.anuke.mindustry.gen.CallEntity;
import io.anuke.mindustry.graphics.Palette;
import io.anuke.mindustry.type.ContentList;
import io.anuke.mindustry.type.Item;
import io.anuke.mindustry.world.Tile;
import io.anuke.mindustry.world.blocks.distribution.MassDriver.DriverBulletData;
import io.anuke.ucore.core.Effects;
import io.anuke.ucore.core.Timers;
import io.anuke.ucore.graphics.Draw;
import io.anuke.ucore.graphics.Fill;
import io.anuke.ucore.graphics.Lines;
import io.anuke.ucore.util.Angles;
import io.anuke.ucore.util.Mathf;
import static io.anuke.mindustry.Vars.world;
public class TurretBullets extends BulletList implements ContentList {
public static BulletType fireball, basicFlame, lancerLaser, fuseShot, waterShot, cryoShot, lavaShot, oilShot, lightning;
public static BulletType fireball, basicFlame, lancerLaser, fuseShot, waterShot, cryoShot, lavaShot, oilShot, lightning, driverBolt;
@Override
public void load() {
@@ -171,5 +176,96 @@ public class TurretBullets extends BulletList implements ContentList {
Lightning.create(b.getTeam(), hiteffect, Palette.lancerLaser, damage, b.x, b.y, b.angle(), 30);
}
};
driverBolt = new BulletType(5f, 20) {
{
collidesTiles = false;
lifetime = 200f;
despawneffect = BlockFx.smeltsmoke;
hiteffect = BulletFx.hitBulletBig;
drag = 0.02f;
}
@Override
public void draw(Bullet b) {
Draw.color(Color.LIGHT_GRAY);
Fill.square(b.x, b.y, 3f, b.angle());
Draw.color(Palette.lighterOrange);
Fill.square(b.x, b.y, 2f, b.angle());
Draw.reset();
}
@Override
public void update(Bullet b) {
//data MUST be an instance of DriverBulletData
if(!(b.getData() instanceof DriverBulletData)){
hit(b);
return;
}
float hitDst = 7f;
DriverBulletData data = (DriverBulletData)b.getData();
//if the target is dead, just keep flying until the bullet explodes
if(data.to.isDead()){
return;
}
float baseDst = data.from.distanceTo(data.to);
float dst1 = b.distanceTo(data.from);
float dst2 = b.distanceTo(data.to);
boolean intersect = false;
//bullet has gone past the destination point: but did it intersect it?
if(dst1 > baseDst){
float angleTo = b.angleTo(data.to);
float baseAngle = data.to.angleTo(data.from);
//if angles are nearby, then yes, it did
if(Mathf.angNear(angleTo, baseAngle, 2f)){
intersect = true;
//snap bullet position back; this is used for low-FPS situations
b.set(data.to.x + Angles.trnsx(baseAngle, hitDst), data.to.y + Angles.trnsy(baseAngle, hitDst));
}
}
//if on course and it's in range of the target
if(Math.abs(dst1 + dst2 - baseDst) < 4f && dst2 <= hitDst){
intersect = true;
} //else, bullet has gone off course, does not get recieved.
if(intersect){
data.to.handlePayload(b, data);
}
}
@Override
public void despawned(Bullet b) {
super.despawned(b);
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]);
if(amountDropped > 0){
float angle = b.angle() + Mathf.range(100f);
float vs = Mathf.random(0f, 4f);
CallEntity.createItemDrop(Item.getByID(i), amountDropped, b.x, b.y, Angles.trnsx(angle, vs), Angles.trnsy(angle, vs));
}
}
}
@Override
public void hit(Bullet b, float hitx, float hity) {
super.hit(b, hitx, hity);
despawned(b);
}
};
}
}

View File

@@ -628,6 +628,7 @@ public class Player extends Unit implements BuilderTrait, CarryTrait {
if(local){
int index = stream.readByte();
players[index].readSaveSuper(stream);
dead = false;
}
}

View File

@@ -30,6 +30,7 @@ public class Bullet extends BulletEntity<BulletType> implements TeamTrait, SyncT
private static Vector2 vector = new Vector2();
private Team team;
private Object data;
private boolean supressCollision;
public Timer timer = new Timer(3);
@@ -43,9 +44,14 @@ public class Bullet extends BulletEntity<BulletType> implements TeamTrait, SyncT
}
public static void create (BulletType type, Entity owner, Team team, float x, float y, float angle, float velocityScl){
create(type, owner, team, x, y, angle, velocityScl, null);
}
public static void create (BulletType type, Entity owner, Team team, float x, float y, float angle, float velocityScl, Object data){
Bullet bullet = Pools.obtain(Bullet.class);
bullet.type = type;
bullet.owner = owner;
bullet.data = data;
bullet.velocity.set(0, type.speed).setAngle(angle).scl(velocityScl);
bullet.velocity.add(owner instanceof VelocityTrait ? ((VelocityTrait)owner).getVelocity() : Vector2.Zero);
@@ -90,6 +96,10 @@ public class Bullet extends BulletEntity<BulletType> implements TeamTrait, SyncT
time += add;
}
public Object getData() {
return data;
}
@Override
public int getTypeID() {
return typeID;
@@ -185,6 +195,7 @@ public class Bullet extends BulletEntity<BulletType> implements TeamTrait, SyncT
super.reset();
timer.clear();
team = null;
data = null;
}
@Override

View File

@@ -55,6 +55,11 @@ public class ItemDrop extends SolidEntity implements SyncTrait, DrawTrait, Veloc
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){
create(item, amount, x, y, 0).getVelocity().set(velocityX, velocityY);
}
@Remote(called = Loc.server, in = In.entities)
public static void onPickup(int itemid){
itemGroup.removeByID(itemid);

View File

@@ -77,8 +77,7 @@ public class TeamInfo {
}
/**Returns a set of all teams that are enemies of this team.
* For teams not active, an empty set is returned.
*/
* For teams not active, an empty set is returned.*/
public ObjectSet<TeamData> enemyDataOf(Team team) {
boolean ally = allies.contains(team);
boolean enemy = enemies.contains(team);

View File

@@ -25,7 +25,6 @@ public class DeflectorWall extends Wall {
public DeflectorWall(String name) {
super(name);
update = false;
}
@Override

View File

@@ -1,13 +1,280 @@
package io.anuke.mindustry.world.blocks.distribution;
import com.badlogic.gdx.utils.ObjectSet;
import com.badlogic.gdx.utils.Pool.Poolable;
import com.badlogic.gdx.utils.Pools;
import io.anuke.annotations.Annotations.Loc;
import io.anuke.annotations.Annotations.Remote;
import io.anuke.mindustry.content.bullets.TurretBullets;
import io.anuke.mindustry.content.fx.BlockFx;
import io.anuke.mindustry.content.fx.ShootFx;
import io.anuke.mindustry.entities.Player;
import io.anuke.mindustry.entities.TileEntity;
import io.anuke.mindustry.entities.bullet.Bullet;
import io.anuke.mindustry.gen.CallBlocks;
import io.anuke.mindustry.gen.CallEntity;
import io.anuke.mindustry.graphics.Layer;
import io.anuke.mindustry.graphics.Palette;
import io.anuke.mindustry.net.In;
import io.anuke.mindustry.type.Item;
import io.anuke.mindustry.world.Block;
import io.anuke.mindustry.world.Tile;
import io.anuke.ucore.core.Effects;
import io.anuke.ucore.core.Effects.Effect;
import io.anuke.ucore.core.Timers;
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 java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import static io.anuke.mindustry.Vars.*;
public class MassDriver extends Block {
protected float range;
protected float rotateSpeed = 0.04f;
protected float translation = 7f;
//minimum amount of items needed to begin firing
protected int minDistribute = 10;
protected float knockback = 4f;
protected float reloadTime = 80f;
protected Effect shootEffect = ShootFx.shootBig2;
protected Effect smokeEffect = ShootFx.shootBigSmoke2;
protected Effect recieveEffect = BlockFx.smeltsmoke;
protected float shake = 3f;
public MassDriver(String name) {
super(name);
update = true;
solid = true;
configurable = true;
hasItems = true;
itemCapacity = 50;
layer = Layer.turret;
hasPower = true;
}
@Override
public void update(Tile tile) {
MassDriverEntity entity = tile.entity();
Tile link = world.tile(entity.link);
if(entity.isUnloading){
tryDump(tile);
if(entity.items.totalItems() <= 0){
entity.isUnloading = false;
}
}
if(entity.reload > 0f){
entity.reload = Mathf.clamp(entity.reload - Timers.delta()/reloadTime);
}
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.totalItems() >= minDistribute &&
linkValid(tile) && //only fire when at least at half-capacity and power
tile.entity.power.amount >= powerCapacity &&
link.block().itemCapacity - link.entity.items.totalItems() >= 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 (Mathf.angNear(entity.rotation, target, 1f) &&
Mathf.angNear(other.rotation, target + 180f, 1f)) {
CallBlocks.onMassDriverFire(tile, link);
}
}
}
entity.waiting.clear();
}
@Override
public void drawLayer(Tile tile) {
MassDriverEntity entity = tile.entity();
Draw.rect(name + "-turret",
tile.drawx() + Angles.trnsx(entity.rotation + 180f, entity.reload * knockback),
tile.drawy() + Angles.trnsy(entity.rotation + 180f, entity.reload * knockback),
entity.rotation - 90);
}
@Override
public void drawConfigure(Tile tile) {
super.drawConfigure(tile);
MassDriverEntity entity = tile.entity();
if(linkValid(tile)){
Tile target = world.tile(entity.link);
Draw.color(Palette.place);
Lines.square(target.drawx(), target.drawy(),
target.block().size * tilesize / 2f + 1f);
Draw.reset();
}
Draw.color(Palette.accent);
Lines.dashCircle(tile.drawx(), tile.drawy(), range);
Draw.color();
}
@Override
public boolean onConfigureTileTapped(Tile tile, Tile other){
MassDriverEntity entity = tile.entity();
if(entity.link == other.packedPosition()) {
CallBlocks.linkMassDriver(null, tile, -1);
return false;
}else if(other.block() instanceof MassDriver && other.distanceTo(tile) <= range){
CallBlocks.linkMassDriver(null, tile, other.packedPosition());
return false;
}
return true;
}
@Override
public boolean acceptItem(Item item, Tile tile, Tile source) {
return tile.entity.items.totalItems() < itemCapacity;
}
@Override
public TileEntity getEntity() {
return new MassDriverEntity();
}
protected boolean linkValid(Tile tile){
MassDriverEntity entity = tile.entity();
if(entity.link == -1) return false;
Tile link = world.tile(entity.link);
return link != null && link.block() instanceof MassDriver && tile.distanceTo(link) <= range;
}
@Remote(targets = Loc.both, called = Loc.server, in = In.blocks, forward = true)
public static void linkMassDriver(Player player, Tile tile, int position){
MassDriverEntity entity = tile.entity();
//called in main thread to prevent issues
threads.run(() -> entity.link = position);
}
@Remote(called = Loc.server, in = In.blocks)
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;
DriverBulletData data = Pools.obtain(DriverBulletData.class);
data.from = entity;
data.to = other;
System.arraycopy(entity.items.items, 0, data.items, 0, data.items.length);
entity.items.clear();
float angle = tile.angleTo(target);
other.isRecieving = true;
Bullet.create(TurretBullets.driverBolt, entity, entity.getTeam(),
tile.drawx() + Angles.trnsx(angle, driver.translation), tile.drawy() + Angles.trnsy(angle, driver.translation),
angle, 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);
}
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<Tile> 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;
public float reload = 0f;
public void handlePayload(Bullet bullet, DriverBulletData data){
int totalItems = items.totalItems();
//add all the items possible
for(int i = 0; i < data.items.length; i ++){
int maxAdd = Math.min(data.items[i], itemCapacity - totalItems);
items.items[i] += maxAdd;
data.items[i] -= maxAdd;
totalItems += maxAdd;
if(totalItems >= itemCapacity){
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);
float vs = Mathf.random(0f, 4f);
CallEntity.createItemDrop(Item.getByID(i), amountDropped, bullet.x, bullet.y, Angles.trnsx(angle, vs), Angles.trnsy(angle, vs));
}
}
reload = 1f;
Effects.shake(shake, shake, this);
Effects.effect(recieveEffect, bullet);
isRecieving = false;
bullet.remove();
if(!linkValid(tile)){
isUnloading = true;
}
}
@Override
public void write(DataOutputStream stream) throws IOException {
stream.writeInt(link);
}
@Override
public void read(DataInputStream stream) throws IOException {
link = stream.readInt();
}
}
public static class DriverBulletData implements Poolable{
public MassDriverEntity from, to;
public int[] items = new int[Item.all().size];
@Override
public void reset() {
from = null;
to = null;;
}
}
}