Implemented mass drivers
This commit is contained in:
@@ -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));
|
||||
|
||||
@@ -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;
|
||||
}};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -628,6 +628,7 @@ public class Player extends Unit implements BuilderTrait, CarryTrait {
|
||||
if(local){
|
||||
int index = stream.readByte();
|
||||
players[index].readSaveSuper(stream);
|
||||
dead = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -25,7 +25,6 @@ public class DeflectorWall extends Wall {
|
||||
|
||||
public DeflectorWall(String name) {
|
||||
super(name);
|
||||
update = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -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;;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user