Merge remote-tracking branch 'origin/4.0' into 4.0
# Conflicts: # core/src/io/anuke/mindustry/Vars.java
This commit is contained in:
@@ -24,84 +24,90 @@ import io.anuke.ucore.util.Translator;
|
||||
|
||||
import static io.anuke.mindustry.Vars.*;
|
||||
|
||||
/**Utility class for damaging in an area.*/
|
||||
public class Damage {
|
||||
private static Rectangle rect = new Rectangle();
|
||||
private static Rectangle hitrect = new Rectangle();
|
||||
private static Translator tr = new Translator();
|
||||
/**
|
||||
* Utility class for damaging in an area.
|
||||
*/
|
||||
public class Damage{
|
||||
private static Rectangle rect = new Rectangle();
|
||||
private static Rectangle hitrect = new Rectangle();
|
||||
private static Translator tr = new Translator();
|
||||
|
||||
/**Creates a dynamic explosion based on specified parameters.*/
|
||||
public static void dynamicExplosion(float x, float y, float flammability, float explosiveness, float power, float radius, Color color){
|
||||
for(int i = 0; i < Mathf.clamp(power / 20, 0, 6); i ++){
|
||||
int branches = 5 + Mathf.clamp((int)(power/30), 1, 20);
|
||||
Timers.run(i*2f + Mathf.random(4f), () -> Lightning.create(Team.none, Fx.none, Palette.power, 3,
|
||||
x, y, Mathf.random(360f), branches + Mathf.range(2)));
|
||||
}
|
||||
/**
|
||||
* Creates a dynamic explosion based on specified parameters.
|
||||
*/
|
||||
public static void dynamicExplosion(float x, float y, float flammability, float explosiveness, float power, float radius, Color color){
|
||||
for(int i = 0; i < Mathf.clamp(power / 20, 0, 6); i++){
|
||||
int branches = 5 + Mathf.clamp((int) (power / 30), 1, 20);
|
||||
Timers.run(i * 2f + Mathf.random(4f), () -> Lightning.create(Team.none, Fx.none, Palette.power, 3,
|
||||
x, y, Mathf.random(360f), branches + Mathf.range(2)));
|
||||
}
|
||||
|
||||
for(int i = 0; i < Mathf.clamp(flammability / 4, 0, 30); i ++){
|
||||
Timers.run(i/2, () -> CallEntity.createBullet(TurretBullets.fireball, x, y, Mathf.random(360f)));
|
||||
}
|
||||
for(int i = 0; i < Mathf.clamp(flammability / 4, 0, 30); i++){
|
||||
Timers.run(i / 2, () -> CallEntity.createBullet(TurretBullets.fireball, x, y, Mathf.random(360f)));
|
||||
}
|
||||
|
||||
int waves = Mathf.clamp((int)(explosiveness / 4), 0, 30);
|
||||
int waves = Mathf.clamp((int) (explosiveness / 4), 0, 30);
|
||||
|
||||
for(int i = 0; i < waves; i ++){
|
||||
int f = i;
|
||||
Timers.run(i*2f, () -> {
|
||||
Damage.damage(x, y, Mathf.clamp(radius + explosiveness, 0, 50f) * ((f + 1f)/waves), explosiveness/2f);
|
||||
Effects.effect(ExplosionFx.blockExplosionSmoke, x + Mathf.range(radius), y + Mathf.range(radius));
|
||||
});
|
||||
}
|
||||
for(int i = 0; i < waves; i++){
|
||||
int f = i;
|
||||
Timers.run(i * 2f, () -> {
|
||||
Damage.damage(x, y, Mathf.clamp(radius + explosiveness, 0, 50f) * ((f + 1f) / waves), explosiveness / 2f);
|
||||
Effects.effect(ExplosionFx.blockExplosionSmoke, x + Mathf.range(radius), y + Mathf.range(radius));
|
||||
});
|
||||
}
|
||||
|
||||
if(explosiveness > 15f){
|
||||
Effects.effect(ExplosionFx.shockwave, x, y);
|
||||
}
|
||||
if(explosiveness > 15f){
|
||||
Effects.effect(ExplosionFx.shockwave, x, y);
|
||||
}
|
||||
|
||||
if(explosiveness > 30f){
|
||||
Effects.effect(ExplosionFx.bigShockwave, x, y);
|
||||
}
|
||||
if(explosiveness > 30f){
|
||||
Effects.effect(ExplosionFx.bigShockwave, x, y);
|
||||
}
|
||||
|
||||
float shake = Math.min(explosiveness/4f + 3f, 9f);
|
||||
Effects.shake(shake, shake, x, y);
|
||||
Effects.effect(ExplosionFx.blockExplosion, x, y);
|
||||
}
|
||||
float shake = Math.min(explosiveness / 4f + 3f, 9f);
|
||||
Effects.shake(shake, shake, x, y);
|
||||
Effects.effect(ExplosionFx.blockExplosion, x, y);
|
||||
}
|
||||
|
||||
public static void createIncend(float x, float y, float range, int amount){
|
||||
for (int i = 0; i < amount; i++) {
|
||||
float cx = x + Mathf.range(range);
|
||||
float cy = y + Mathf.range(range);
|
||||
Tile tile = world.tileWorld(cx, cy);
|
||||
if(tile != null){
|
||||
Fire.create(tile);
|
||||
}
|
||||
}
|
||||
}
|
||||
public static void createIncend(float x, float y, float range, int amount){
|
||||
for(int i = 0; i < amount; i++){
|
||||
float cx = x + Mathf.range(range);
|
||||
float cy = y + Mathf.range(range);
|
||||
Tile tile = world.tileWorld(cx, cy);
|
||||
if(tile != null){
|
||||
Fire.create(tile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**Damages entities in a line.
|
||||
* Only enemies of the specified team are damaged.*/
|
||||
public static void collideLine(SolidEntity hitter, Team team, Effect effect, float x, float y, float angle, float length){
|
||||
tr.trns(angle, length);
|
||||
rect.setPosition(x, y).setSize(tr.x, tr.y);
|
||||
float x2 = tr.x + x, y2 = tr.y + y;
|
||||
/**
|
||||
* Damages entities in a line.
|
||||
* Only enemies of the specified team are damaged.
|
||||
*/
|
||||
public static void collideLine(SolidEntity hitter, Team team, Effect effect, float x, float y, float angle, float length){
|
||||
tr.trns(angle, length);
|
||||
rect.setPosition(x, y).setSize(tr.x, tr.y);
|
||||
float x2 = tr.x + x, y2 = tr.y + y;
|
||||
|
||||
if(rect.width < 0){
|
||||
rect.x += rect.width;
|
||||
rect.width *= -1;
|
||||
}
|
||||
if(rect.width < 0){
|
||||
rect.x += rect.width;
|
||||
rect.width *= -1;
|
||||
}
|
||||
|
||||
if(rect.height < 0){
|
||||
rect.y += rect.height;
|
||||
rect.height *= -1;
|
||||
}
|
||||
if(rect.height < 0){
|
||||
rect.y += rect.height;
|
||||
rect.height *= -1;
|
||||
}
|
||||
|
||||
float expand = 3f;
|
||||
float expand = 3f;
|
||||
|
||||
rect.y -= expand;
|
||||
rect.x -= expand;
|
||||
rect.width += expand*2;
|
||||
rect.height += expand*2;
|
||||
rect.y -= expand;
|
||||
rect.x -= expand;
|
||||
rect.width += expand * 2;
|
||||
rect.height += expand * 2;
|
||||
|
||||
Consumer<Unit> cons = e -> {
|
||||
e.getHitbox(hitrect);
|
||||
e.getHitbox(hitrect);
|
||||
Rectangle other = hitrect;
|
||||
other.y -= expand;
|
||||
other.x -= expand;
|
||||
@@ -110,79 +116,85 @@ public class Damage {
|
||||
|
||||
Vector2 vec = Physics.raycastRect(x, y, x2, y2, other);
|
||||
|
||||
if (vec != null) {
|
||||
if(vec != null){
|
||||
Effects.effect(effect, vec.x, vec.y);
|
||||
e.collision(hitter, vec.x, vec.y);
|
||||
hitter.collision(e, vec.x, vec.y);
|
||||
}
|
||||
};
|
||||
|
||||
Units.getNearbyEnemies(team, rect, cons);
|
||||
}
|
||||
Units.getNearbyEnemies(team, rect, cons);
|
||||
}
|
||||
|
||||
/**Damages all entities and blocks in a radius that are enemies of the team.*/
|
||||
public static void damageUnits(Team team, float x, float y, float size, float damage, Predicate<Unit> predicate, Consumer<Unit> acceptor) {
|
||||
Consumer<Unit> cons = entity -> {
|
||||
if(!predicate.test(entity)) return;
|
||||
/**
|
||||
* Damages all entities and blocks in a radius that are enemies of the team.
|
||||
*/
|
||||
public static void damageUnits(Team team, float x, float y, float size, float damage, Predicate<Unit> predicate, Consumer<Unit> acceptor){
|
||||
Consumer<Unit> cons = entity -> {
|
||||
if(!predicate.test(entity)) return;
|
||||
|
||||
entity.getHitbox(hitrect);
|
||||
if (!hitrect.overlaps(rect)) {
|
||||
return;
|
||||
}
|
||||
entity.damage(damage);
|
||||
acceptor.accept(entity);
|
||||
};
|
||||
entity.getHitbox(hitrect);
|
||||
if(!hitrect.overlaps(rect)){
|
||||
return;
|
||||
}
|
||||
entity.damage(damage);
|
||||
acceptor.accept(entity);
|
||||
};
|
||||
|
||||
rect.setSize(size * 2).setCenter(x, y);
|
||||
if (team != null) {
|
||||
Units.getNearbyEnemies(team, rect, cons);
|
||||
} else {
|
||||
Units.getNearby(rect, cons);
|
||||
}
|
||||
}
|
||||
rect.setSize(size * 2).setCenter(x, y);
|
||||
if(team != null){
|
||||
Units.getNearbyEnemies(team, rect, cons);
|
||||
}else{
|
||||
Units.getNearby(rect, cons);
|
||||
}
|
||||
}
|
||||
|
||||
/**Damages everything in a radius.*/
|
||||
public static void damage(float x, float y, float radius, float damage){
|
||||
damage(null, x, y, radius, damage);
|
||||
}
|
||||
/**
|
||||
* Damages everything in a radius.
|
||||
*/
|
||||
public static void damage(float x, float y, float radius, float damage){
|
||||
damage(null, x, y, radius, damage);
|
||||
}
|
||||
|
||||
/**Damages all entities and blocks in a radius that are enemies of the team.*/
|
||||
public static void damage(Team team, float x, float y, float radius, float damage){
|
||||
Consumer<Unit> cons = entity -> {
|
||||
if(entity.team == team || entity.distanceTo(x, y) > radius){
|
||||
return;
|
||||
}
|
||||
float amount = calculateDamage(x, y, entity.x, entity.y, radius, damage);
|
||||
entity.damage(amount);
|
||||
//TODO better velocity displacement
|
||||
float dst = tr.set(entity.x - x, entity.y - y).len();
|
||||
entity.getVelocity().add(tr.setLength((1f-dst/radius) * 2f));
|
||||
};
|
||||
/**
|
||||
* Damages all entities and blocks in a radius that are enemies of the team.
|
||||
*/
|
||||
public static void damage(Team team, float x, float y, float radius, float damage){
|
||||
Consumer<Unit> cons = entity -> {
|
||||
if(entity.team == team || entity.distanceTo(x, y) > radius){
|
||||
return;
|
||||
}
|
||||
float amount = calculateDamage(x, y, entity.x, entity.y, radius, damage);
|
||||
entity.damage(amount);
|
||||
//TODO better velocity displacement
|
||||
float dst = tr.set(entity.x - x, entity.y - y).len();
|
||||
entity.getVelocity().add(tr.setLength((1f - dst / radius) * 2f));
|
||||
};
|
||||
|
||||
rect.setSize(radius *2).setCenter(x, y);
|
||||
if(team != null) {
|
||||
Units.getNearbyEnemies(team, rect, cons);
|
||||
}else{
|
||||
Units.getNearby(rect, cons);
|
||||
}
|
||||
rect.setSize(radius * 2).setCenter(x, y);
|
||||
if(team != null){
|
||||
Units.getNearbyEnemies(team, rect, cons);
|
||||
}else{
|
||||
Units.getNearby(rect, cons);
|
||||
}
|
||||
|
||||
int trad = (int)(radius / tilesize);
|
||||
for(int dx = -trad; dx <= trad; dx ++){
|
||||
for(int dy= -trad; dy <= trad; dy ++){
|
||||
Tile tile = world.tile(Mathf.scl2(x, tilesize) + dx, Mathf.scl2(y, tilesize) + dy);
|
||||
if(tile != null && tile.entity != null && (team == null || state.teams.areEnemies(team, tile.getTeam())) && Vector2.dst(dx, dy, 0, 0) <= trad){
|
||||
float amount = calculateDamage(x, y, tile.worldx(), tile.worldy(), radius, damage);
|
||||
tile.entity.damage(amount);
|
||||
}
|
||||
}
|
||||
}
|
||||
int trad = (int) (radius / tilesize);
|
||||
for(int dx = -trad; dx <= trad; dx++){
|
||||
for(int dy = -trad; dy <= trad; dy++){
|
||||
Tile tile = world.tile(Mathf.scl2(x, tilesize) + dx, Mathf.scl2(y, tilesize) + dy);
|
||||
if(tile != null && tile.entity != null && (team == null || state.teams.areEnemies(team, tile.getTeam())) && Vector2.dst(dx, dy, 0, 0) <= trad){
|
||||
float amount = calculateDamage(x, y, tile.worldx(), tile.worldy(), radius, damage);
|
||||
tile.entity.damage(amount);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static float calculateDamage(float x, float y, float tx, float ty, float radius, float damage){
|
||||
float dist = Vector2.dst(x, y, tx, ty);
|
||||
float falloff = 0.4f;
|
||||
float scaled = Mathf.lerp(1f - dist/radius, 1f, falloff);
|
||||
return damage * scaled;
|
||||
}
|
||||
}
|
||||
|
||||
private static float calculateDamage(float x, float y, float tx, float ty, float radius, float damage){
|
||||
float dist = Vector2.dst(x, y, tx, ty);
|
||||
float falloff = 0.4f;
|
||||
float scaled = Mathf.lerp(1f - dist / radius, 1f, falloff);
|
||||
return damage * scaled;
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -4,12 +4,16 @@ import com.badlogic.gdx.math.Vector2;
|
||||
import io.anuke.mindustry.entities.traits.TargetTrait;
|
||||
import io.anuke.ucore.util.Mathf;
|
||||
|
||||
/**Class for predicting shoot angles based on velocities of targets.*/
|
||||
public class Predict {
|
||||
/**
|
||||
* Class for predicting shoot angles based on velocities of targets.
|
||||
*/
|
||||
public class Predict{
|
||||
private static Vector2 vec = new Vector2();
|
||||
private static Vector2 vresult = new Vector2();
|
||||
|
||||
/**Calculates of intercept of a stationary and moving target. Do not call from multiple threads!
|
||||
/**
|
||||
* Calculates of intercept of a stationary and moving target. Do not call from multiple threads!
|
||||
*
|
||||
* @param srcx X of shooter
|
||||
* @param srcy Y of shooter
|
||||
* @param dstx X of target
|
||||
@@ -17,52 +21,55 @@ public class Predict {
|
||||
* @param dstvx X velocity of target (subtract shooter X velocity if needed)
|
||||
* @param dstvy Y velocity of target (subtract shooter Y velocity if needed)
|
||||
* @param v speed of bullet
|
||||
* @return the intercept location*/
|
||||
public static Vector2 intercept(float srcx, float srcy, float dstx, float dsty, float dstvx, float dstvy, float v) {
|
||||
* @return the intercept location
|
||||
*/
|
||||
public static Vector2 intercept(float srcx, float srcy, float dstx, float dsty, float dstvx, float dstvy, float v){
|
||||
float tx = dstx - srcx,
|
||||
ty = dsty - srcy;
|
||||
|
||||
// Get quadratic equation components
|
||||
float a = dstvx * dstvx + dstvy * dstvy - v*v;
|
||||
float a = dstvx * dstvx + dstvy * dstvy - v * v;
|
||||
float b = 2 * (dstvx * tx + dstvy * ty);
|
||||
float c = tx*tx + ty*ty;
|
||||
float c = tx * tx + ty * ty;
|
||||
|
||||
// Solve quadratic
|
||||
Vector2 ts = quad(a, b, c);
|
||||
|
||||
// Find smallest positive solution
|
||||
Vector2 sol = vresult.set(0, 0);
|
||||
if (ts != null) {
|
||||
if(ts != null){
|
||||
float t0 = ts.x, t1 = ts.y;
|
||||
float t = Math.min(t0, t1);
|
||||
if (t < 0) t = Math.max(t0, t1);
|
||||
if (t > 0) {
|
||||
sol.set(dstx + dstvx*t, dsty + dstvy*t);
|
||||
if(t < 0) t = Math.max(t0, t1);
|
||||
if(t > 0){
|
||||
sol.set(dstx + dstvx * t, dsty + dstvy * t);
|
||||
}
|
||||
}
|
||||
|
||||
return sol;
|
||||
}
|
||||
|
||||
/**See {@link #intercept(float, float, float, float, float, float, float)}.*/
|
||||
public static Vector2 intercept(TargetTrait src, TargetTrait dst, float v) {
|
||||
/**
|
||||
* See {@link #intercept(float, float, float, float, float, float, float)}.
|
||||
*/
|
||||
public static Vector2 intercept(TargetTrait src, TargetTrait dst, float v){
|
||||
return intercept(src.getX(), src.getY(), dst.getX(), dst.getY(), dst.getVelocity().x - src.getVelocity().x, dst.getVelocity().x - src.getVelocity().y, v);
|
||||
}
|
||||
|
||||
private static Vector2 quad(float a, float b, float c) {
|
||||
private static Vector2 quad(float a, float b, float c){
|
||||
Vector2 sol = null;
|
||||
if (Math.abs(a) < 1e-6) {
|
||||
if (Math.abs(b) < 1e-6) {
|
||||
sol = Math.abs(c) < 1e-6 ? vec.set(0,0) : null;
|
||||
} else {
|
||||
vec.set(-c/b, -c/b);
|
||||
if(Math.abs(a) < 1e-6){
|
||||
if(Math.abs(b) < 1e-6){
|
||||
sol = Math.abs(c) < 1e-6 ? vec.set(0, 0) : null;
|
||||
}else{
|
||||
vec.set(-c / b, -c / b);
|
||||
}
|
||||
} else {
|
||||
float disc = b*b - 4*a*c;
|
||||
if (disc >= 0) {
|
||||
}else{
|
||||
float disc = b * b - 4 * a * c;
|
||||
if(disc >= 0){
|
||||
disc = Mathf.sqrt(disc);
|
||||
a = 2*a;
|
||||
sol = vec.set((-b-disc)/a, (-b+disc)/a);
|
||||
a = 2 * a;
|
||||
sol = vec.set((-b - disc) / a, (-b + disc) / a);
|
||||
}
|
||||
}
|
||||
return sol;
|
||||
|
||||
@@ -12,7 +12,9 @@ import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
|
||||
/**Class for controlling status effects on an entity.*/
|
||||
/**
|
||||
* Class for controlling status effects on an entity.
|
||||
*/
|
||||
public class StatusController implements Saveable{
|
||||
private static final StatusEntry globalResult = new StatusEntry();
|
||||
private static final Array<StatusEntry> removals = new ThreadArray<>();
|
||||
@@ -26,20 +28,20 @@ public class StatusController implements Saveable{
|
||||
public void handleApply(Unit unit, StatusEffect effect, float intensity){
|
||||
if(effect == StatusEffects.none) return; //don't apply empty effects
|
||||
|
||||
float newTime = effect.baseDuration*intensity;
|
||||
float newTime = effect.baseDuration * intensity;
|
||||
|
||||
if(statuses.size > 0){
|
||||
//check for opposite effects
|
||||
for(StatusEntry entry : statuses){
|
||||
//extend effect
|
||||
if(entry.effect == effect) {
|
||||
if(entry.effect == effect){
|
||||
entry.time = Math.max(entry.time, newTime);
|
||||
return;
|
||||
}else if(entry.effect.isOpposite(effect)){ //find opposite
|
||||
entry.effect.getTransition(unit, effect, entry.time, newTime, globalResult);
|
||||
entry.time = globalResult.time;
|
||||
|
||||
if (globalResult.effect != entry.effect) {
|
||||
if(globalResult.effect != entry.effect){
|
||||
entry.effect.onTransition(unit, globalResult.effect);
|
||||
entry.effect = globalResult.effect;
|
||||
}
|
||||
@@ -94,7 +96,7 @@ public class StatusController implements Saveable{
|
||||
return damageMultiplier;
|
||||
}
|
||||
|
||||
public float getArmorMultiplier() {
|
||||
public float getArmorMultiplier(){
|
||||
return armorMultiplier;
|
||||
}
|
||||
|
||||
@@ -106,24 +108,24 @@ public class StatusController implements Saveable{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeSave(DataOutput stream) throws IOException {
|
||||
public void writeSave(DataOutput stream) throws IOException{
|
||||
stream.writeByte(statuses.size);
|
||||
for(StatusEntry entry : statuses){
|
||||
stream.writeByte(entry.effect.id);
|
||||
stream.writeShort((short)(entry.time * 2));
|
||||
stream.writeShort((short) (entry.time * 2));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readSave(DataInput stream) throws IOException {
|
||||
for (StatusEntry effect : statuses){
|
||||
public void readSave(DataInput stream) throws IOException{
|
||||
for(StatusEntry effect : statuses){
|
||||
Pooling.free(effect);
|
||||
}
|
||||
|
||||
statuses.clear();
|
||||
|
||||
byte amount = stream.readByte();
|
||||
for (int i = 0; i < amount; i++) {
|
||||
for(int i = 0; i < amount; i++){
|
||||
byte id = stream.readByte();
|
||||
float time = stream.readShort() / 2f;
|
||||
StatusEntry entry = Pooling.obtain(StatusEntry.class);
|
||||
@@ -132,7 +134,7 @@ public class StatusController implements Saveable{
|
||||
}
|
||||
}
|
||||
|
||||
public static class StatusEntry {
|
||||
public static class StatusEntry{
|
||||
public StatusEffect effect;
|
||||
public float time;
|
||||
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package io.anuke.mindustry.entities;
|
||||
|
||||
import com.badlogic.gdx.math.GridPoint2;
|
||||
import com.badlogic.gdx.math.Vector2;
|
||||
import com.badlogic.gdx.utils.Array;
|
||||
import com.badlogic.gdx.utils.ObjectSet;
|
||||
import io.anuke.annotations.Annotations.Loc;
|
||||
import io.anuke.annotations.Annotations.Remote;
|
||||
import io.anuke.mindustry.content.fx.Fx;
|
||||
@@ -10,11 +13,14 @@ import io.anuke.mindustry.game.Team;
|
||||
import io.anuke.mindustry.gen.CallBlocks;
|
||||
import io.anuke.mindustry.net.In;
|
||||
import io.anuke.mindustry.world.Block;
|
||||
import io.anuke.mindustry.world.Edges;
|
||||
import io.anuke.mindustry.world.Tile;
|
||||
import io.anuke.mindustry.world.blocks.Wall;
|
||||
import io.anuke.mindustry.world.blocks.modules.InventoryModule;
|
||||
import io.anuke.mindustry.world.blocks.modules.LiquidModule;
|
||||
import io.anuke.mindustry.world.blocks.modules.PowerModule;
|
||||
import io.anuke.mindustry.world.consumers.Consume;
|
||||
import io.anuke.mindustry.world.modules.ConsumeModule;
|
||||
import io.anuke.mindustry.world.modules.InventoryModule;
|
||||
import io.anuke.mindustry.world.modules.LiquidModule;
|
||||
import io.anuke.mindustry.world.modules.PowerModule;
|
||||
import io.anuke.ucore.core.Effects;
|
||||
import io.anuke.ucore.core.Timers;
|
||||
import io.anuke.ucore.entities.EntityGroup;
|
||||
@@ -29,153 +35,219 @@ import java.io.IOException;
|
||||
import static io.anuke.mindustry.Vars.tileGroup;
|
||||
import static io.anuke.mindustry.Vars.world;
|
||||
|
||||
public class TileEntity extends BaseEntity implements TargetTrait {
|
||||
public static final float timeToSleep = 60f*4; //4 seconds to fall asleep
|
||||
/**This value is only used for debugging.*/
|
||||
public static int sleepingEntities = 0;
|
||||
public class TileEntity extends BaseEntity implements TargetTrait{
|
||||
public static final float timeToSleep = 60f * 4; //4 seconds to fall asleep
|
||||
private static final ObjectSet<Tile> tmpTiles = new ObjectSet<>();
|
||||
/**
|
||||
* This value is only used for debugging.
|
||||
*/
|
||||
public static int sleepingEntities = 0;
|
||||
public Tile tile;
|
||||
public Timer timer;
|
||||
public float health;
|
||||
|
||||
public Tile tile;
|
||||
public Timer timer;
|
||||
public float health;
|
||||
public PowerModule power;
|
||||
public InventoryModule items;
|
||||
public LiquidModule liquids;
|
||||
public ConsumeModule cons;
|
||||
|
||||
public PowerModule power;
|
||||
public InventoryModule items;
|
||||
public LiquidModule liquids;
|
||||
/**List of (cached) tiles with entities in proximity, used for outputting to*/
|
||||
private Array<Tile> proximity = new Array<>(8);
|
||||
private boolean dead = false;
|
||||
private boolean sleeping;
|
||||
private float sleepTime;
|
||||
|
||||
private boolean dead = false;
|
||||
private boolean sleeping;
|
||||
private float sleepTime;
|
||||
|
||||
/**Sets this tile entity data to this tile, and adds it if necessary.*/
|
||||
public TileEntity init(Tile tile, boolean added){
|
||||
this.tile = tile;
|
||||
x = tile.drawx();
|
||||
y = tile.drawy();
|
||||
@Remote(called = Loc.server, in = In.blocks)
|
||||
public static void onTileDamage(Tile tile, float health){
|
||||
if(tile.entity != null){
|
||||
tile.entity.health = health;
|
||||
}
|
||||
}
|
||||
|
||||
health = tile.block().health;
|
||||
|
||||
timer = new Timer(tile.block().timers);
|
||||
|
||||
if(added){
|
||||
//if(!tile.block().autoSleep) { //TODO only autosleep when creating a fresh block!
|
||||
add();
|
||||
/*}else{
|
||||
sleeping = true;
|
||||
sleepingEntities ++;
|
||||
}*/
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
@Remote(called = Loc.server, in = In.blocks)
|
||||
public static void onTileDestroyed(Tile tile){
|
||||
if(tile.entity == null) return;
|
||||
tile.entity.onDeath();
|
||||
}
|
||||
|
||||
/**Call when nothing is happening to the entity.
|
||||
* This increments the internal sleep timer.*/
|
||||
public void sleep(){
|
||||
sleepTime += Timers.delta();
|
||||
if(!sleeping && sleepTime >= timeToSleep){
|
||||
remove();
|
||||
sleeping = true;
|
||||
sleepingEntities ++;
|
||||
}
|
||||
}
|
||||
/**Sets this tile entity data to this tile, and adds it if necessary.*/
|
||||
public TileEntity init(Tile tile, boolean added){
|
||||
this.tile = tile;
|
||||
x = tile.drawx();
|
||||
y = tile.drawy();
|
||||
|
||||
/**Call when something just happened to the entity.
|
||||
* If the entity was sleeping, this enables it. This also resets the sleep timer.*/
|
||||
public void wakeUp(){
|
||||
sleepTime = 0f;
|
||||
if(sleeping){
|
||||
add();
|
||||
sleeping = false;
|
||||
sleepingEntities --;
|
||||
}
|
||||
}
|
||||
health = tile.block().health;
|
||||
|
||||
public boolean isSleeping(){
|
||||
return sleeping;
|
||||
}
|
||||
timer = new Timer(tile.block().timers);
|
||||
|
||||
public boolean isDead() {
|
||||
return dead;
|
||||
}
|
||||
if(added){
|
||||
add();
|
||||
}
|
||||
|
||||
public void write(DataOutputStream stream) throws IOException{}
|
||||
public void read(DataInputStream stream) throws IOException{}
|
||||
return this;
|
||||
}
|
||||
|
||||
private void onDeath(){
|
||||
if(!dead) {
|
||||
dead = true;
|
||||
Block block = tile.block();
|
||||
/**
|
||||
* Call when nothing is happening to the entity.
|
||||
* This increments the internal sleep timer.
|
||||
*/
|
||||
public void sleep(){
|
||||
sleepTime += Timers.delta();
|
||||
if(!sleeping && sleepTime >= timeToSleep){
|
||||
remove();
|
||||
sleeping = true;
|
||||
sleepingEntities++;
|
||||
}
|
||||
}
|
||||
|
||||
block.onDestroyed(tile);
|
||||
world.removeBlock(tile);
|
||||
block.afterDestroyed(tile, this);
|
||||
remove();
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Call when something just happened to the entity.
|
||||
* If the entity was sleeping, this enables it. This also resets the sleep timer.
|
||||
*/
|
||||
public void wakeUp(){
|
||||
sleepTime = 0f;
|
||||
if(sleeping){
|
||||
add();
|
||||
sleeping = false;
|
||||
sleepingEntities--;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean collide(Bullet other){
|
||||
return true;
|
||||
}
|
||||
|
||||
public void collision(Bullet other){
|
||||
tile.block().handleBulletHit(this, other);
|
||||
}
|
||||
|
||||
public void damage(float damage){
|
||||
if(dead) return;
|
||||
public boolean isSleeping(){
|
||||
return sleeping;
|
||||
}
|
||||
|
||||
CallBlocks.onTileDamage(tile, health - tile.block().handleDamage(tile, damage));
|
||||
public boolean isDead(){
|
||||
return dead;
|
||||
}
|
||||
|
||||
if(health <= 0){
|
||||
CallBlocks.onTileDestroyed(tile);
|
||||
}
|
||||
}
|
||||
public void write(DataOutputStream stream) throws IOException{
|
||||
}
|
||||
|
||||
public Tile getTile(){
|
||||
return tile;
|
||||
}
|
||||
public void read(DataInputStream stream) throws IOException{
|
||||
}
|
||||
|
||||
@Override
|
||||
public Team getTeam() {
|
||||
return tile.getTeam();
|
||||
}
|
||||
private void onDeath(){
|
||||
if(!dead){
|
||||
dead = true;
|
||||
Block block = tile.block();
|
||||
|
||||
@Override
|
||||
public Vector2 getVelocity() {
|
||||
return Vector2.Zero;
|
||||
}
|
||||
block.onDestroyed(tile);
|
||||
world.removeBlock(tile);
|
||||
block.afterDestroyed(tile, this);
|
||||
remove();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(){
|
||||
synchronized (Tile.tileSetLock) {
|
||||
//TODO better smoke effect, this one is awful
|
||||
if (health != 0 && health < tile.block().health && !(tile.block() instanceof Wall) &&
|
||||
Mathf.chance(0.009f * Timers.delta() * (1f - health / tile.block().health))) {
|
||||
public boolean collide(Bullet other){
|
||||
return true;
|
||||
}
|
||||
|
||||
Effects.effect(Fx.smoke, x + Mathf.range(4), y + Mathf.range(4));
|
||||
}
|
||||
public void collision(Bullet other){
|
||||
tile.block().handleBulletHit(this, other);
|
||||
}
|
||||
|
||||
if (health <= 0) {
|
||||
onDeath();
|
||||
}
|
||||
public void damage(float damage){
|
||||
if(dead) return;
|
||||
|
||||
tile.block().update(tile);
|
||||
}
|
||||
}
|
||||
CallBlocks.onTileDamage(tile, health - tile.block().handleDamage(tile, damage));
|
||||
|
||||
@Override
|
||||
public EntityGroup targetGroup() {
|
||||
return tileGroup;
|
||||
}
|
||||
if(health <= 0){
|
||||
CallBlocks.onTileDestroyed(tile);
|
||||
}
|
||||
}
|
||||
|
||||
@Remote(called = Loc.server, in = In.blocks)
|
||||
public static void onTileDamage(Tile tile, float health){
|
||||
tile.entity.health = health;
|
||||
}
|
||||
public Tile getTile(){
|
||||
return tile;
|
||||
}
|
||||
|
||||
@Remote(called = Loc.server, in = In.blocks)
|
||||
public static void onTileDestroyed(Tile tile){
|
||||
if(tile.entity == null) return;
|
||||
tile.entity.onDeath();
|
||||
}
|
||||
public boolean consumed(Class<? extends Consume> type){
|
||||
return tile.block().consumes.get(type).valid(tile.block(), this);
|
||||
}
|
||||
|
||||
public void removeFromProximity(){
|
||||
GridPoint2[] nearby = Edges.getEdges(tile.block().size);
|
||||
for(GridPoint2 point : nearby){
|
||||
Tile other = world.tile(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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void updateProximity(){
|
||||
tmpTiles.clear();
|
||||
proximity.clear();
|
||||
|
||||
GridPoint2[] nearby = Edges.getEdges(tile.block().size);
|
||||
for(GridPoint2 point : nearby){
|
||||
Tile other = world.tile(tile.x + point.x, tile.y + point.y);
|
||||
|
||||
if(other != null){
|
||||
other.block().onProximityUpdate(other);
|
||||
other = other.target();
|
||||
}
|
||||
|
||||
if(other != null && other.entity != null){
|
||||
tmpTiles.add(other);
|
||||
|
||||
//add this tile to proximity of nearby tiles
|
||||
if(!other.entity.proximity.contains(tile, true)){
|
||||
other.entity.proximity.add(tile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//using a set to prevent duplicates
|
||||
for(Tile tile : tmpTiles){
|
||||
proximity.add(tile);
|
||||
}
|
||||
|
||||
tile.block().onProximityUpdate(tile);
|
||||
}
|
||||
|
||||
public Array<Tile> proximity(){
|
||||
return proximity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Team getTeam(){
|
||||
return tile.getTeam();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vector2 getVelocity(){
|
||||
return Vector2.Zero;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(){
|
||||
synchronized(Tile.tileSetLock){
|
||||
//TODO better smoke effect, this one is awful
|
||||
if(health != 0 && health < tile.block().health && !(tile.block() instanceof Wall) &&
|
||||
Mathf.chance(0.009f * Timers.delta() * (1f - health / tile.block().health))){
|
||||
|
||||
Effects.effect(Fx.smoke, x + Mathf.range(4), y + Mathf.range(4));
|
||||
}
|
||||
|
||||
if(health <= 0){
|
||||
onDeath();
|
||||
}
|
||||
|
||||
tile.block().update(tile);
|
||||
if(cons != null){
|
||||
cons.update(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityGroup targetGroup(){
|
||||
return tileGroup;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,13 +31,20 @@ import java.io.IOException;
|
||||
import static io.anuke.mindustry.Vars.state;
|
||||
import static io.anuke.mindustry.Vars.world;
|
||||
|
||||
public abstract class Unit extends DestructibleEntity implements SaveTrait, TargetTrait, SyncTrait, DrawTrait, TeamTrait, CarriableTrait, InventoryTrait {
|
||||
/**total duration of hit flash effect*/
|
||||
public abstract class Unit extends DestructibleEntity implements SaveTrait, TargetTrait, SyncTrait, DrawTrait, TeamTrait, CarriableTrait, InventoryTrait{
|
||||
/**
|
||||
* total duration of hit flash effect
|
||||
*/
|
||||
public static final float hitDuration = 9f;
|
||||
/**Percision divisor of velocity, used when writing. For example a value of '2' would mean the percision is 1/2 = 0.5-size chunks.*/
|
||||
/**
|
||||
* Percision divisor of velocity, used when writing. For example a value of '2' would mean the percision is 1/2 = 0.5-size chunks.
|
||||
*/
|
||||
public static final float velocityPercision = 8f;
|
||||
/**Maximum absolute value of a velocity vector component.*/
|
||||
public static final float maxAbsVelocity = 127f/velocityPercision;
|
||||
/**
|
||||
* Maximum absolute value of a velocity vector component.
|
||||
*/
|
||||
public static final float maxAbsVelocity = 127f / velocityPercision;
|
||||
public static final float elevationScale = 4f;
|
||||
|
||||
private static final Vector2 moveVector = new Vector2();
|
||||
|
||||
@@ -52,30 +59,31 @@ public abstract class Unit extends DestructibleEntity implements SaveTrait, Targ
|
||||
protected Vector2 velocity = new Translator(0f, 0.0001f);
|
||||
protected float hitTime;
|
||||
protected float drownTime;
|
||||
protected float elevation;
|
||||
|
||||
@Override
|
||||
public UnitInventory getInventory() {
|
||||
public UnitInventory getInventory(){
|
||||
return inventory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getRotation() {
|
||||
public float getRotation(){
|
||||
return rotation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRotation(float rotation) {
|
||||
public void setRotation(float rotation){
|
||||
this.rotation = rotation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCarrier(CarryTrait carrier) {
|
||||
this.carrier = carrier;
|
||||
public CarryTrait getCarrier(){
|
||||
return carrier;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CarryTrait getCarrier() {
|
||||
return carrier;
|
||||
public void setCarrier(CarryTrait carrier){
|
||||
this.carrier = carrier;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -84,7 +92,7 @@ public abstract class Unit extends DestructibleEntity implements SaveTrait, Targ
|
||||
}
|
||||
|
||||
@Override
|
||||
public void interpolate() {
|
||||
public void interpolate(){
|
||||
interpolator.update();
|
||||
|
||||
x = interpolator.pos.x;
|
||||
@@ -96,7 +104,7 @@ public abstract class Unit extends DestructibleEntity implements SaveTrait, Targ
|
||||
}
|
||||
|
||||
@Override
|
||||
public Interpolator getInterpolator() {
|
||||
public Interpolator getInterpolator(){
|
||||
return interpolator;
|
||||
}
|
||||
|
||||
@@ -113,31 +121,31 @@ public abstract class Unit extends DestructibleEntity implements SaveTrait, Targ
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDeath() {
|
||||
public void onDeath(){
|
||||
inventory.clear();
|
||||
drownTime = 0f;
|
||||
status.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vector2 getVelocity() {
|
||||
public Vector2 getVelocity(){
|
||||
return velocity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeSave(DataOutput stream) throws IOException {
|
||||
public void writeSave(DataOutput stream) throws IOException{
|
||||
writeSave(stream, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readSave(DataInput stream) throws IOException {
|
||||
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();
|
||||
byte yv = stream.readByte();
|
||||
float rotation = stream.readShort()/2f;
|
||||
float rotation = stream.readShort() / 2f;
|
||||
int health = stream.readShort();
|
||||
|
||||
this.status.readSave(stream);
|
||||
@@ -151,21 +159,21 @@ public abstract class Unit extends DestructibleEntity implements SaveTrait, Targ
|
||||
this.rotation = rotation;
|
||||
}
|
||||
|
||||
public void writeSave(DataOutput stream, boolean net) throws IOException {
|
||||
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));
|
||||
stream.writeByte((byte)(Mathf.clamp(velocity.y, -maxAbsVelocity, maxAbsVelocity) * velocityPercision));
|
||||
stream.writeShort((short)(rotation*2));
|
||||
stream.writeShort((short)health);
|
||||
stream.writeByte((byte) (Mathf.clamp(velocity.x, -maxAbsVelocity, maxAbsVelocity) * velocityPercision));
|
||||
stream.writeByte((byte) (Mathf.clamp(velocity.y, -maxAbsVelocity, maxAbsVelocity) * velocityPercision));
|
||||
stream.writeShort((short) (rotation * 2));
|
||||
stream.writeShort((short) health);
|
||||
status.writeSave(stream);
|
||||
inventory.writeSave(stream);
|
||||
}
|
||||
|
||||
public float calculateDamage(float amount){
|
||||
return amount * Mathf.clamp(1f-getArmor()/100f*status.getArmorMultiplier());
|
||||
return amount * Mathf.clamp(1f - getArmor() / 100f * status.getArmorMultiplier());
|
||||
}
|
||||
|
||||
public float getDamageMultipler(){
|
||||
@@ -180,7 +188,7 @@ public abstract class Unit extends DestructibleEntity implements SaveTrait, Targ
|
||||
if(state.teams.has(team)){
|
||||
TeamData data = state.teams.get(team);
|
||||
|
||||
Tile tile = Geometry.findClosest(x, y, data.cores);
|
||||
Tile tile = Geometry.findClosest(x, y, data.cores);
|
||||
if(tile == null){
|
||||
return null;
|
||||
}else{
|
||||
@@ -198,15 +206,18 @@ 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()) || ((Unit) t).getCarrier() == this) || getCarrier() == t)) return;
|
||||
EntityPhysics.getNearby(getGroup(), x, y, avoidRange * 2f, t -> {
|
||||
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))));
|
||||
});
|
||||
}
|
||||
|
||||
/**Updates velocity and status effects.*/
|
||||
/**
|
||||
* Updates velocity and status effects.
|
||||
*/
|
||||
public void updateVelocityStatus(float drag, float maxVelocity){
|
||||
if(isCarried()){ //carried units do not take into account velocity normally
|
||||
set(carrier.getX(), carrier.getY());
|
||||
@@ -221,9 +232,13 @@ public abstract class Unit extends DestructibleEntity implements SaveTrait, Targ
|
||||
|
||||
velocity.limit(maxVelocity).scl(status.getSpeedMultiplier());
|
||||
|
||||
if(isFlying()) {
|
||||
if(isFlying()){
|
||||
x += velocity.x / getMass() * Timers.delta();
|
||||
y += velocity.y / getMass() * Timers.delta();
|
||||
|
||||
if(tile != null){
|
||||
elevation = Mathf.lerpDelta(elevation, tile.elevation, 0.04f);
|
||||
}
|
||||
}else{
|
||||
boolean onLiquid = floor.isLiquid;
|
||||
|
||||
@@ -239,7 +254,7 @@ public abstract class Unit extends DestructibleEntity implements SaveTrait, Targ
|
||||
}
|
||||
}
|
||||
|
||||
if(onLiquid && velocity.len() > 0.4f && Timers.get(this, "flooreffect", 14 - (velocity.len() * floor.speedMultiplier)*2f)){
|
||||
if(onLiquid && velocity.len() > 0.4f && Timers.get(this, "flooreffect", 14 - (velocity.len() * floor.speedMultiplier) * 2f)){
|
||||
Effects.effect(floor.walkEffect, floor.liquidColor, x, y);
|
||||
}
|
||||
|
||||
@@ -250,7 +265,7 @@ public abstract class Unit extends DestructibleEntity implements SaveTrait, Targ
|
||||
}
|
||||
|
||||
if(onLiquid && floor.drownTime > 0){
|
||||
drownTime += Timers.delta() * 1f/floor.drownTime;
|
||||
drownTime += Timers.delta() * 1f / floor.drownTime;
|
||||
if(Timers.get(this, "drowneffect", 15)){
|
||||
Effects.effect(floor.drownUpdateEffect, floor.liquidColor, x, y);
|
||||
}
|
||||
@@ -270,7 +285,7 @@ public abstract class Unit extends DestructibleEntity implements SaveTrait, Targ
|
||||
if(Math.abs(py - y) <= 0.0001f) velocity.y = 0f;
|
||||
}
|
||||
|
||||
velocity.scl(Mathf.clamp(1f-drag* floor.dragMultiplier* Timers.delta()));
|
||||
velocity.scl(Mathf.clamp(1f - drag * floor.dragMultiplier * Timers.delta()));
|
||||
}
|
||||
|
||||
public void applyEffect(StatusEffect effect, float intensity){
|
||||
@@ -293,11 +308,17 @@ public abstract class Unit extends DestructibleEntity implements SaveTrait, Targ
|
||||
}
|
||||
|
||||
public float getAmmoFraction(){
|
||||
return inventory.totalAmmo() / (float)inventory.ammoCapacity();
|
||||
return inventory.totalAmmo() / (float) inventory.ammoCapacity();
|
||||
}
|
||||
|
||||
public void drawUnder(){}
|
||||
public void drawOver(){}
|
||||
public void drawUnder(){
|
||||
}
|
||||
|
||||
public void drawOver(){
|
||||
}
|
||||
|
||||
public void drawShadow(){
|
||||
}
|
||||
|
||||
public void drawView(){
|
||||
Fill.circle(x, y, getViewDistance());
|
||||
@@ -312,12 +333,20 @@ public abstract class Unit extends DestructibleEntity implements SaveTrait, Targ
|
||||
}
|
||||
|
||||
public abstract TextureRegion getIconRegion();
|
||||
|
||||
public abstract int getItemCapacity();
|
||||
|
||||
public abstract int getAmmoCapacity();
|
||||
|
||||
public abstract float getArmor();
|
||||
|
||||
public abstract boolean acceptsAmmo(Item item);
|
||||
|
||||
public abstract void addAmmo(Item item);
|
||||
|
||||
public abstract float getMass();
|
||||
|
||||
public abstract boolean isFlying();
|
||||
|
||||
public abstract float getSize();
|
||||
}
|
||||
|
||||
@@ -8,16 +8,17 @@ import io.anuke.mindustry.type.AmmoType;
|
||||
import io.anuke.mindustry.type.Item;
|
||||
import io.anuke.mindustry.type.ItemStack;
|
||||
|
||||
import java.io.*;
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
|
||||
public class UnitInventory implements Saveable{
|
||||
private final Unit unit;
|
||||
private Array<AmmoEntry> ammos = new Array<>();
|
||||
private int totalAmmo;
|
||||
private ItemStack item = new ItemStack(Items.stone, 0);
|
||||
|
||||
private final Unit unit;
|
||||
|
||||
public UnitInventory(Unit unit) {
|
||||
public UnitInventory(Unit unit){
|
||||
this.unit = unit;
|
||||
}
|
||||
|
||||
@@ -26,24 +27,24 @@ public class UnitInventory implements Saveable{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeSave(DataOutput stream) throws IOException {
|
||||
public void writeSave(DataOutput stream) throws IOException{
|
||||
stream.writeShort(item.amount);
|
||||
stream.writeByte(item.item.id);
|
||||
stream.writeShort(totalAmmo);
|
||||
stream.writeByte(ammos.size);
|
||||
for(int i = 0; i < ammos.size; i ++){
|
||||
for(int i = 0; i < ammos.size; i++){
|
||||
stream.writeByte(ammos.get(i).type.id);
|
||||
stream.writeShort(ammos.get(i).amount);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readSave(DataInput stream) throws IOException {
|
||||
public void readSave(DataInput stream) throws IOException{
|
||||
short iamount = stream.readShort();
|
||||
byte iid = stream.readByte();
|
||||
this.totalAmmo = stream.readShort();
|
||||
byte ammoa = stream.readByte();
|
||||
for(int i = 0; i < ammoa; i ++){
|
||||
for(int i = 0; i < ammoa; i++){
|
||||
byte aid = stream.readByte();
|
||||
int am = stream.readShort();
|
||||
ammos.add(new AmmoEntry(AmmoType.getByID(aid), am));
|
||||
@@ -53,12 +54,14 @@ public class UnitInventory implements Saveable{
|
||||
item.amount = iamount;
|
||||
}
|
||||
|
||||
/**Returns ammo range, or MAX_VALUE if this inventory has no ammo.*/
|
||||
/**
|
||||
* Returns ammo range, or MAX_VALUE if this inventory has no ammo.
|
||||
*/
|
||||
public float getAmmoRange(){
|
||||
return hasAmmo() ? getAmmo().getRange() : Float.MAX_VALUE;
|
||||
}
|
||||
|
||||
public AmmoType getAmmo() {
|
||||
public AmmoType getAmmo(){
|
||||
return ammos.size == 0 ? null : ammos.peek().type;
|
||||
}
|
||||
|
||||
@@ -69,9 +72,9 @@ public class UnitInventory implements Saveable{
|
||||
public void useAmmo(){
|
||||
if(unit.isInfiniteAmmo()) return;
|
||||
AmmoEntry entry = ammos.peek();
|
||||
entry.amount --;
|
||||
entry.amount--;
|
||||
if(entry.amount == 0) ammos.pop();
|
||||
totalAmmo --;
|
||||
totalAmmo--;
|
||||
}
|
||||
|
||||
public int totalAmmo(){
|
||||
@@ -87,22 +90,23 @@ public class UnitInventory implements Saveable{
|
||||
}
|
||||
|
||||
public void addAmmo(AmmoType type){
|
||||
if(type == null) return;
|
||||
totalAmmo += type.quantityMultiplier;
|
||||
|
||||
//find ammo entry by type
|
||||
for(int i = ammos.size - 1; i >= 0; i --){
|
||||
for(int i = ammos.size - 1; i >= 0; i--){
|
||||
AmmoEntry entry = ammos.get(i);
|
||||
|
||||
//if found, put it to the right
|
||||
if(entry.type == type){
|
||||
entry.amount += type.quantityMultiplier;
|
||||
ammos.swap(i, ammos.size-1);
|
||||
ammos.swap(i, ammos.size - 1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//must not be found
|
||||
AmmoEntry entry = new AmmoEntry(type, (int)type.quantityMultiplier);
|
||||
AmmoEntry entry = new AmmoEntry(type, (int) type.quantityMultiplier);
|
||||
ammos.add(entry);
|
||||
}
|
||||
|
||||
|
||||
@@ -15,12 +15,19 @@ import io.anuke.ucore.function.Predicate;
|
||||
|
||||
import static io.anuke.mindustry.Vars.*;
|
||||
|
||||
/**Utility class for unit and team interactions.*/
|
||||
public class Units {
|
||||
/**
|
||||
* Utility class for unit and team interactions.
|
||||
*/
|
||||
public class Units{
|
||||
private static Rectangle rect = new Rectangle();
|
||||
private static Rectangle hitrect = new Rectangle();
|
||||
private static Unit result;
|
||||
private static float cdist;
|
||||
private static boolean boolResult;
|
||||
|
||||
/**Validates a target.
|
||||
/**
|
||||
* Validates a target.
|
||||
*
|
||||
* @param target The target to validate
|
||||
* @param team The team of the thing doing tha targeting
|
||||
* @param x The X position of the thing doign the targeting
|
||||
@@ -28,44 +35,52 @@ public class Units {
|
||||
* @param range The maximum distance from the target X/Y the targeter can be for it to be valid
|
||||
* @return whether the target is invalid
|
||||
*/
|
||||
public static boolean invalidateTarget(TargetTrait target, Team team, float x, float y, float range) {
|
||||
public static boolean invalidateTarget(TargetTrait target, Team team, float x, float y, float range){
|
||||
return target == null || (range != Float.MAX_VALUE && target.distanceTo(x, y) > range) || target.getTeam() == team || !target.isValid();
|
||||
|
||||
}
|
||||
|
||||
/**See {@link #invalidateTarget(TargetTrait, Team, float, float, float)}*/
|
||||
/**
|
||||
* See {@link #invalidateTarget(TargetTrait, Team, float, float, float)}
|
||||
*/
|
||||
public static boolean invalidateTarget(TargetTrait target, Team team, float x, float y){
|
||||
return invalidateTarget(target, team, x, y, Float.MAX_VALUE);
|
||||
}
|
||||
|
||||
/**See {@link #invalidateTarget(TargetTrait, Team, float, float, float)}*/
|
||||
/**
|
||||
* See {@link #invalidateTarget(TargetTrait, Team, float, float, float)}
|
||||
*/
|
||||
public static boolean invalidateTarget(TargetTrait target, Unit targeter){
|
||||
return invalidateTarget(target, targeter.team, targeter.x, targeter.y, targeter.inventory.getAmmoRange());
|
||||
}
|
||||
|
||||
/**Returns whether there are any entities on this tile.*/
|
||||
/**
|
||||
* Returns whether there are any entities on this tile.
|
||||
*/
|
||||
public static boolean anyEntities(Tile tile){
|
||||
Block type = tile.block();
|
||||
rect.setSize(type.size * tilesize, type.size * tilesize);
|
||||
rect.setCenter(tile.drawx(), tile.drawy());
|
||||
|
||||
boolean[] value = new boolean[1];
|
||||
boolResult = false;
|
||||
|
||||
Units.getNearby(rect, unit -> {
|
||||
if (value[0]) return;
|
||||
if (!unit.isFlying()) {
|
||||
if(boolResult) return;
|
||||
if(!unit.isFlying()){
|
||||
unit.getHitbox(hitrect);
|
||||
|
||||
if (hitrect.overlaps(rect)) {
|
||||
value[0] = true;
|
||||
if(hitrect.overlaps(rect)){
|
||||
boolResult = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return value[0];
|
||||
return boolResult;
|
||||
}
|
||||
|
||||
/**Returns whether there are any entities on this tile, with the hitbox expanded.*/
|
||||
/**
|
||||
* Returns whether there are any entities on this tile, with the hitbox expanded.
|
||||
*/
|
||||
public static boolean anyEntities(Tile tile, float expansion, Predicate<Unit> pred){
|
||||
Block type = tile.block();
|
||||
rect.setSize(type.size * tilesize + expansion, type.size * tilesize + expansion);
|
||||
@@ -78,7 +93,7 @@ public class Units {
|
||||
if(!unit.isFlying()){
|
||||
unit.getHitbox(hitrect);
|
||||
|
||||
if(hitrect.overlaps(rect)) {
|
||||
if(hitrect.overlaps(rect)){
|
||||
value[0] = true;
|
||||
}
|
||||
}
|
||||
@@ -87,7 +102,9 @@ public class Units {
|
||||
return value[0];
|
||||
}
|
||||
|
||||
/**Returns the neareset ally tile in a range.*/
|
||||
/**
|
||||
* Returns the neareset ally tile in a range.
|
||||
*/
|
||||
public static TileEntity findAllyTile(Team team, float x, float y, float range, Predicate<Tile> pred){
|
||||
for(Team enemy : state.teams.alliesOf(team)){
|
||||
TileEntity entity = world.indexer().findTile(enemy, x, y, range, pred);
|
||||
@@ -98,7 +115,9 @@ public class Units {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**Returns the neareset enemy tile in a range.*/
|
||||
/**
|
||||
* Returns the neareset enemy tile in a range.
|
||||
*/
|
||||
public static TileEntity findEnemyTile(Team team, float x, float y, float range, Predicate<Tile> pred){
|
||||
for(Team enemy : state.teams.enemiesOf(team)){
|
||||
TileEntity entity = world.indexer().findTile(enemy, x, y, range, pred);
|
||||
@@ -109,7 +128,9 @@ public class Units {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**Iterates over all units on all teams, including players.*/
|
||||
/**
|
||||
* Iterates over all units on all teams, including players.
|
||||
*/
|
||||
public static void allUnits(Consumer<Unit> cons){
|
||||
//check all unit groups first
|
||||
for(EntityGroup<BaseUnit> group : unitGroups){
|
||||
@@ -126,7 +147,9 @@ public class Units {
|
||||
}
|
||||
}
|
||||
|
||||
/**Returns the closest target enemy. First, units are checked, then tile entities.*/
|
||||
/**
|
||||
* Returns the closest target enemy. First, units are checked, then tile entities.
|
||||
*/
|
||||
public static TargetTrait getClosestTarget(Team team, float x, float y, float range){
|
||||
Unit unit = getClosestEnemy(team, x, y, range, u -> true);
|
||||
if(unit != null){
|
||||
@@ -136,74 +159,82 @@ public class Units {
|
||||
}
|
||||
}
|
||||
|
||||
/**Returns the closest enemy of this team. Filter by predicate.*/
|
||||
/**
|
||||
* Returns the closest enemy of this team. Filter by predicate.
|
||||
*/
|
||||
public static Unit getClosestEnemy(Team team, float x, float y, float range, Predicate<Unit> predicate){
|
||||
Unit[] result = {null};
|
||||
float[] cdist = {0};
|
||||
result = null;
|
||||
cdist = 0f;
|
||||
|
||||
rect.setSize(range*2f).setCenter(x, y);
|
||||
rect.setSize(range * 2f).setCenter(x, y);
|
||||
|
||||
getNearbyEnemies(team, rect, e -> {
|
||||
if (e.isDead() || !predicate.test(e))
|
||||
if(e.isDead() || !predicate.test(e))
|
||||
return;
|
||||
|
||||
float dist = Vector2.dst(e.x, e.y, x, y);
|
||||
if (dist < range) {
|
||||
if (result[0] == null || dist < cdist[0]) {
|
||||
result[0] = e;
|
||||
cdist[0] = dist;
|
||||
if(dist < range){
|
||||
if(result == null || dist < cdist){
|
||||
result = e;
|
||||
cdist = dist;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return result[0];
|
||||
return result;
|
||||
}
|
||||
|
||||
/**Returns the closest ally of this team. Filter by predicate.*/
|
||||
/**
|
||||
* Returns the closest ally of this team. Filter by predicate.
|
||||
*/
|
||||
public static Unit getClosest(Team team, float x, float y, float range, Predicate<Unit> predicate){
|
||||
Unit[] result = {null};
|
||||
float[] cdist = {0};
|
||||
result = null;
|
||||
cdist = 0f;
|
||||
|
||||
rect.setSize(range*2f).setCenter(x, y);
|
||||
rect.setSize(range * 2f).setCenter(x, y);
|
||||
|
||||
getNearby(team, rect, e -> {
|
||||
if (!predicate.test(e))
|
||||
if(!predicate.test(e))
|
||||
return;
|
||||
|
||||
float dist = Vector2.dst(e.x, e.y, x, y);
|
||||
if (dist < range) {
|
||||
if (result[0] == null || dist < cdist[0]) {
|
||||
result[0] = e;
|
||||
cdist[0] = dist;
|
||||
if(dist < range){
|
||||
if(result == null || dist < cdist){
|
||||
result = e;
|
||||
cdist = dist;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return result[0];
|
||||
return result;
|
||||
}
|
||||
|
||||
/**Iterates over all units in a rectangle.*/
|
||||
/**
|
||||
* Iterates over all units in a rectangle.
|
||||
*/
|
||||
public static void getNearby(Team team, Rectangle rect, Consumer<Unit> cons){
|
||||
|
||||
EntityGroup<BaseUnit> group = unitGroups[team.ordinal()];
|
||||
if(!group.isEmpty()){
|
||||
EntityPhysics.getNearby(group, rect, entity -> cons.accept((Unit)entity));
|
||||
EntityPhysics.getNearby(group, rect, entity -> cons.accept((Unit) entity));
|
||||
}
|
||||
|
||||
//now check all players
|
||||
EntityPhysics.getNearby(playerGroup, rect, player -> {
|
||||
if(((Unit)player).team == team) cons.accept((Unit)player);
|
||||
if(((Unit) player).team == team) cons.accept((Unit) player);
|
||||
});
|
||||
}
|
||||
|
||||
/**Iterates over all units in a circle around this position.*/
|
||||
/**
|
||||
* Iterates over all units in a circle around this position.
|
||||
*/
|
||||
public static void getNearby(Team team, float x, float y, float radius, Consumer<Unit> cons){
|
||||
rect.setSize(radius * 2).setCenter(x, y);
|
||||
|
||||
EntityGroup<BaseUnit> group = unitGroups[team.ordinal()];
|
||||
if(!group.isEmpty()){
|
||||
EntityPhysics.getNearby(group, rect, entity -> {
|
||||
if(entity.distanceTo(x, y) <= radius) {
|
||||
if(entity.distanceTo(x, y) <= radius){
|
||||
cons.accept((Unit) entity);
|
||||
}
|
||||
});
|
||||
@@ -211,46 +242,52 @@ public class Units {
|
||||
|
||||
//now check all players
|
||||
EntityPhysics.getNearby(playerGroup, rect, player -> {
|
||||
if(((Unit)player).team == team && player.distanceTo(x, y) <= radius){
|
||||
cons.accept((Unit)player);
|
||||
if(((Unit) player).team == team && player.distanceTo(x, y) <= radius){
|
||||
cons.accept((Unit) player);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**Iterates over all units in a rectangle.*/
|
||||
/**
|
||||
* Iterates over all units in a rectangle.
|
||||
*/
|
||||
public static void getNearby(Rectangle rect, Consumer<Unit> cons){
|
||||
|
||||
for(Team team : Team.all){
|
||||
EntityGroup<BaseUnit> group = unitGroups[team.ordinal()];
|
||||
if(!group.isEmpty()){
|
||||
EntityPhysics.getNearby(group, rect, entity -> cons.accept((Unit)entity));
|
||||
EntityPhysics.getNearby(group, rect, entity -> cons.accept((Unit) entity));
|
||||
}
|
||||
}
|
||||
|
||||
//now check all enemy players
|
||||
EntityPhysics.getNearby(playerGroup, rect, player -> cons.accept((Unit)player));
|
||||
EntityPhysics.getNearby(playerGroup, rect, player -> cons.accept((Unit) player));
|
||||
}
|
||||
|
||||
/**Iterates over all units that are enemies of this team.*/
|
||||
/**
|
||||
* Iterates over all units that are enemies of this team.
|
||||
*/
|
||||
public static void getNearbyEnemies(Team team, Rectangle rect, Consumer<Unit> cons){
|
||||
ObjectSet<Team> targets = state.teams.enemiesOf(team);
|
||||
|
||||
for(Team other : targets){
|
||||
EntityGroup<BaseUnit> group = unitGroups[other.ordinal()];
|
||||
if(!group.isEmpty()){
|
||||
EntityPhysics.getNearby(group, rect, entity -> cons.accept((Unit)entity));
|
||||
EntityPhysics.getNearby(group, rect, entity -> cons.accept((Unit) entity));
|
||||
}
|
||||
}
|
||||
|
||||
//now check all enemy players
|
||||
EntityPhysics.getNearby(playerGroup, rect, player -> {
|
||||
if(targets.contains(((Player)player).team)){
|
||||
cons.accept((Unit)player);
|
||||
if(targets.contains(((Player) player).team)){
|
||||
cons.accept((Unit) player);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**Iterates over all units.*/
|
||||
/**
|
||||
* Iterates over all units.
|
||||
*/
|
||||
public static void getAllUnits(Consumer<Unit> cons){
|
||||
|
||||
for(Team team : Team.all){
|
||||
|
||||
@@ -6,10 +6,10 @@ import io.anuke.ucore.core.Effects.Effect;
|
||||
import io.anuke.ucore.graphics.Draw;
|
||||
|
||||
//TODO scale velocity depending on fslope()
|
||||
public class ArtilleryBulletType extends BasicBulletType {
|
||||
public class ArtilleryBulletType extends BasicBulletType{
|
||||
protected Effect trailEffect = BulletFx.artilleryTrail;
|
||||
|
||||
public ArtilleryBulletType(float speed, float damage, String bulletSprite) {
|
||||
public ArtilleryBulletType(float speed, float damage, String bulletSprite){
|
||||
super(speed, damage, bulletSprite);
|
||||
collidesTiles = false;
|
||||
collides = false;
|
||||
@@ -17,18 +17,18 @@ public class ArtilleryBulletType extends BasicBulletType {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(Bullet b) {
|
||||
public void update(Bullet b){
|
||||
super.update(b);
|
||||
|
||||
if(b.timer.get(0, 3 + b.fslope()*2f)){
|
||||
if(b.timer.get(0, 3 + b.fslope() * 2f)){
|
||||
Effects.effect(trailEffect, backColor, b.x, b.y, b.fslope() * 4f);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Bullet b) {
|
||||
public void draw(Bullet b){
|
||||
float baseScale = 0.7f;
|
||||
float scale = (baseScale + b.fslope()*(1f-baseScale));
|
||||
float scale = (baseScale + b.fslope() * (1f - baseScale));
|
||||
|
||||
float height = bulletHeight * ((1f - bulletShrink) + bulletShrink * b.fout());
|
||||
|
||||
|
||||
@@ -12,8 +12,10 @@ import io.anuke.ucore.graphics.Draw;
|
||||
import io.anuke.ucore.util.Angles;
|
||||
import io.anuke.ucore.util.Mathf;
|
||||
|
||||
/**A BulletType for most ammo-based bullets shot from turrets and units.*/
|
||||
public class BasicBulletType extends BulletType {
|
||||
/**
|
||||
* A BulletType for most ammo-based bullets shot from turrets and units.
|
||||
*/
|
||||
public class BasicBulletType extends BulletType{
|
||||
public Color backColor = Palette.bulletYellowBack, frontColor = Palette.bulletYellow;
|
||||
public float bulletWidth = 5f, bulletHeight = 7f;
|
||||
public float bulletShrink = 0.5f;
|
||||
@@ -23,7 +25,9 @@ public class BasicBulletType extends BulletType {
|
||||
public float fragVelocityMin = 0.2f, fragVelocityMax = 1f;
|
||||
public BulletType fragBullet = null;
|
||||
|
||||
/**Use a negative value to disable splash damage.*/
|
||||
/**
|
||||
* Use a negative value to disable splash damage.
|
||||
*/
|
||||
public float splashDamageRadius = -1f;
|
||||
public float splashDamage = 6f;
|
||||
|
||||
@@ -39,19 +43,19 @@ public class BasicBulletType extends BulletType {
|
||||
|
||||
public float hitShake = 0f;
|
||||
|
||||
public BasicBulletType(float speed, float damage, String bulletSprite) {
|
||||
public BasicBulletType(float speed, float damage, String bulletSprite){
|
||||
super(speed, damage);
|
||||
this.bulletSprite = bulletSprite;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load() {
|
||||
public void load(){
|
||||
backRegion = Draw.region(bulletSprite + "-back");
|
||||
frontRegion = Draw.region(bulletSprite);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Bullet b) {
|
||||
public void draw(Bullet b){
|
||||
float height = bulletHeight * ((1f - bulletShrink) + bulletShrink * b.fout());
|
||||
|
||||
Draw.color(backColor);
|
||||
@@ -62,7 +66,7 @@ public class BasicBulletType extends BulletType {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(Bullet b) {
|
||||
public void update(Bullet b){
|
||||
super.update(b);
|
||||
|
||||
if(homingPower > 0.0001f){
|
||||
@@ -74,20 +78,20 @@ public class BasicBulletType extends BulletType {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hit(Bullet b, float x, float y) {
|
||||
public void hit(Bullet b, float x, float y){
|
||||
super.hit(b, x, y);
|
||||
|
||||
Effects.shake(hitShake, hitShake, b);
|
||||
|
||||
if(fragBullet != null) {
|
||||
for (int i = 0; i < fragBullets; i++) {
|
||||
if(fragBullet != null){
|
||||
for(int i = 0; i < fragBullets; i++){
|
||||
float len = Mathf.random(1f, 7f);
|
||||
float a = Mathf.random(360f);
|
||||
Bullet.create(fragBullet, b, x + Angles.trnsx(a, len), y + Angles.trnsy(a, len), a, Mathf.random(fragVelocityMin, fragVelocityMax));
|
||||
}
|
||||
}
|
||||
|
||||
if(Mathf.chance(incendChance)) {
|
||||
if(Mathf.chance(incendChance)){
|
||||
Damage.createIncend(x, y, incendSpread, incendAmount);
|
||||
}
|
||||
|
||||
@@ -97,7 +101,7 @@ public class BasicBulletType extends BulletType {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void despawned(Bullet b) {
|
||||
public void despawned(Bullet b){
|
||||
if(fragBullet != null || splashDamageRadius > 0){
|
||||
hit(b);
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package io.anuke.mindustry.entities.bullet;
|
||||
|
||||
public class BombBulletType extends BasicBulletType {
|
||||
public class BombBulletType extends BasicBulletType{
|
||||
|
||||
public BombBulletType(float damage, float radius, String sprite) {
|
||||
public BombBulletType(float damage, float radius, String sprite){
|
||||
super(0.7f, 0, sprite);
|
||||
splashDamageRadius = radius;
|
||||
splashDamage = damage;
|
||||
|
||||
@@ -27,198 +27,199 @@ import static io.anuke.mindustry.Vars.bulletGroup;
|
||||
import static io.anuke.mindustry.Vars.world;
|
||||
|
||||
public class Bullet extends BulletEntity<BulletType> implements TeamTrait, SyncTrait{
|
||||
private static Vector2 vector = new Vector2();
|
||||
private static Vector2 vector = new Vector2();
|
||||
public Timer timer = new Timer(3);
|
||||
private Team team;
|
||||
private Object data;
|
||||
private boolean supressCollision;
|
||||
|
||||
private Team team;
|
||||
private Object data;
|
||||
private boolean supressCollision;
|
||||
/**
|
||||
* Internal use only!
|
||||
*/
|
||||
public Bullet(){
|
||||
}
|
||||
|
||||
public Timer timer = new Timer(3);
|
||||
public static void create(BulletType type, TeamTrait owner, float x, float y, float angle){
|
||||
create(type, owner, owner.getTeam(), x, y, angle);
|
||||
}
|
||||
|
||||
public static void create (BulletType type, TeamTrait owner, float x, float y, float angle){
|
||||
create(type, owner, owner.getTeam(), x, y, angle);
|
||||
}
|
||||
public static void create(BulletType type, Entity owner, Team team, float x, float y, float angle){
|
||||
create(type, owner, team, x, y, angle, 1f);
|
||||
}
|
||||
|
||||
public static void create (BulletType type, Entity owner, Team team, float x, float y, float angle){
|
||||
create(type, owner, team, x, y, angle, 1f);
|
||||
}
|
||||
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){
|
||||
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 = Pooling.obtain(Bullet.class);
|
||||
bullet.type = type;
|
||||
bullet.owner = owner;
|
||||
bullet.data = data;
|
||||
|
||||
public static void create (BulletType type, Entity owner, Team team, float x, float y, float angle, float velocityScl, Object data){
|
||||
Bullet bullet = Pooling.obtain(Bullet.class);
|
||||
bullet.type = type;
|
||||
bullet.owner = owner;
|
||||
bullet.data = data;
|
||||
bullet.velocity.set(0, type.speed).setAngle(angle).scl(velocityScl);
|
||||
if(type.keepVelocity){
|
||||
bullet.velocity.add(owner instanceof VelocityTrait ? ((VelocityTrait) owner).getVelocity() : Vector2.Zero);
|
||||
}
|
||||
bullet.hitbox.setSize(type.hitsize);
|
||||
|
||||
bullet.velocity.set(0, type.speed).setAngle(angle).scl(velocityScl);
|
||||
if(type.keepVelocity){
|
||||
bullet.velocity.add(owner instanceof VelocityTrait ? ((VelocityTrait)owner).getVelocity() : Vector2.Zero);
|
||||
}
|
||||
bullet.hitbox.setSize(type.hitsize);
|
||||
bullet.team = team;
|
||||
bullet.type = type;
|
||||
|
||||
bullet.team = team;
|
||||
bullet.type = type;
|
||||
//translate bullets backwards, purely for visual reasons
|
||||
float backDelta = Timers.delta();
|
||||
|
||||
//translate bullets backwards, purely for visual reasons
|
||||
float backDelta = Timers.delta();
|
||||
bullet.lastPosition().set(x - bullet.velocity.x * backDelta, y - bullet.velocity.y * backDelta, bullet.angle());
|
||||
bullet.setLastUpdated(TimeUtils.millis());
|
||||
bullet.setUpdateSpacing((long) ((Timers.delta() / 60f) * 1000));
|
||||
bullet.set(x - bullet.velocity.x * backDelta, y - bullet.velocity.y * backDelta);
|
||||
|
||||
bullet.lastPosition().set(x-bullet.velocity.x * backDelta, y-bullet.velocity.y * backDelta, bullet.angle());
|
||||
bullet.setLastUpdated(TimeUtils.millis());
|
||||
bullet.setUpdateSpacing((long)((Timers.delta() / 60f) * 1000));
|
||||
bullet.set(x-bullet.velocity.x * backDelta, y-bullet.velocity.y * backDelta);
|
||||
bullet.add();
|
||||
}
|
||||
|
||||
bullet.add();
|
||||
}
|
||||
public static void create(BulletType type, Bullet parent, float x, float y, float angle){
|
||||
create(type, parent.owner, parent.team, x, y, angle);
|
||||
}
|
||||
|
||||
public static void create(BulletType type, Bullet parent, float x, float y, float angle){
|
||||
create(type, parent.owner, parent.team, x, y, angle);
|
||||
}
|
||||
public static void create(BulletType type, Bullet parent, float x, float y, float angle, float velocityScl){
|
||||
create(type, parent.owner, parent.team, x, y, angle, velocityScl);
|
||||
}
|
||||
|
||||
public static void create(BulletType type, Bullet parent, float x, float y, float angle, float velocityScl){
|
||||
create(type, parent.owner, parent.team, x, y, angle, velocityScl);
|
||||
}
|
||||
@Remote(called = Loc.server, in = In.entities)
|
||||
public static void createBullet(BulletType type, float x, float y, float angle){
|
||||
create(type, null, Team.none, x, y, angle);
|
||||
}
|
||||
|
||||
@Remote(called = Loc.server, in = In.entities)
|
||||
public static void createBullet(BulletType type, float x, float y, float angle){
|
||||
create(type, null, Team.none, x, y, angle);
|
||||
}
|
||||
public boolean collidesTiles(){
|
||||
return type.collidesTiles;
|
||||
}
|
||||
|
||||
/**Internal use only!*/
|
||||
public Bullet(){}
|
||||
public void supressCollision(){
|
||||
supressCollision = true;
|
||||
}
|
||||
|
||||
public boolean collidesTiles(){
|
||||
return type.collidesTiles;
|
||||
}
|
||||
public void resetOwner(Entity entity, Team team){
|
||||
this.owner = entity;
|
||||
this.team = team;
|
||||
}
|
||||
|
||||
public void supressCollision(){
|
||||
supressCollision = true;
|
||||
}
|
||||
public void scaleTime(float add){
|
||||
time += add;
|
||||
}
|
||||
|
||||
public void resetOwner(Entity entity, Team team){
|
||||
this.owner = entity;
|
||||
this.team = team;
|
||||
}
|
||||
public Object getData(){
|
||||
return data;
|
||||
}
|
||||
|
||||
public void scaleTime(float add){
|
||||
time += add;
|
||||
}
|
||||
@Override
|
||||
public float getDamage(){
|
||||
if(owner instanceof Unit){
|
||||
return super.getDamage() * ((Unit) owner).getDamageMultipler();
|
||||
}
|
||||
|
||||
public Object getData() {
|
||||
return data;
|
||||
}
|
||||
return super.getDamage();
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getDamage(){
|
||||
if(owner instanceof Unit){
|
||||
return super.getDamage() * ((Unit) owner).getDamageMultipler();
|
||||
}
|
||||
@Override
|
||||
public boolean isSyncing(){
|
||||
return type.syncable;
|
||||
}
|
||||
|
||||
return super.getDamage();
|
||||
}
|
||||
@Override
|
||||
public void write(DataOutput data) throws IOException{
|
||||
data.writeFloat(x);
|
||||
data.writeFloat(y);
|
||||
data.writeFloat(velocity.x);
|
||||
data.writeFloat(velocity.y);
|
||||
data.writeByte(team.ordinal());
|
||||
data.writeByte(type.id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSyncing(){
|
||||
return type.syncable;
|
||||
}
|
||||
@Override
|
||||
public void read(DataInput data, long time) throws IOException{
|
||||
x = data.readFloat();
|
||||
y = data.readFloat();
|
||||
velocity.x = data.readFloat();
|
||||
velocity.y = data.readFloat();
|
||||
team = Team.all[data.readByte()];
|
||||
type = BulletType.getByID(data.readByte());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(DataOutput data) throws IOException {
|
||||
data.writeFloat(x);
|
||||
data.writeFloat(y);
|
||||
data.writeFloat(velocity.x);
|
||||
data.writeFloat(velocity.y);
|
||||
data.writeByte(team.ordinal());
|
||||
data.writeByte(type.id);
|
||||
}
|
||||
@Override
|
||||
public Team getTeam(){
|
||||
return team;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(DataInput data, long time) throws IOException{
|
||||
x = data.readFloat();
|
||||
y = data.readFloat();
|
||||
velocity.x = data.readFloat();
|
||||
velocity.y = data.readFloat();
|
||||
team = Team.all[data.readByte()];
|
||||
type = BulletType.getByID(data.readByte());
|
||||
}
|
||||
@Override
|
||||
public void draw(){
|
||||
type.draw(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Team getTeam() {
|
||||
return team;
|
||||
}
|
||||
@Override
|
||||
public float drawSize(){
|
||||
return 8;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(){
|
||||
type.draw(this);
|
||||
}
|
||||
@Override
|
||||
public boolean collides(SolidTrait other){
|
||||
return type.collides && super.collides(other);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float drawSize(){
|
||||
return 8;
|
||||
}
|
||||
@Override
|
||||
public void collision(SolidTrait other, float x, float y){
|
||||
super.collision(other, x, y);
|
||||
|
||||
@Override
|
||||
public boolean collides(SolidTrait other){
|
||||
return type.collides && super.collides(other);
|
||||
}
|
||||
if(other instanceof Unit){
|
||||
Unit unit = (Unit) other;
|
||||
unit.getVelocity().add(vector.set(other.getX(), other.getY()).sub(x, y).setLength(type.knockback / unit.getMass()));
|
||||
unit.applyEffect(type.status, type.statusIntensity);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void collision(SolidTrait other, float x, float y){
|
||||
super.collision(other, x, y);
|
||||
@Override
|
||||
public void update(){
|
||||
super.update();
|
||||
|
||||
if(other instanceof Unit){
|
||||
Unit unit = (Unit)other;
|
||||
unit.getVelocity().add(vector.set(other.getX(), other.getY()).sub(x, y).setLength(type.knockback / unit.getMass()));
|
||||
unit.applyEffect(type.status, type.statusIntensity);
|
||||
}
|
||||
}
|
||||
if(type.hitTiles && collidesTiles() && !supressCollision){
|
||||
world.raycastEach(world.toTile(lastPosition().x), world.toTile(lastPosition().y), world.toTile(x), world.toTile(y), (x, y) -> {
|
||||
|
||||
@Override
|
||||
public void update(){
|
||||
super.update();
|
||||
Tile tile = world.tile(x, y);
|
||||
if(tile == null) return false;
|
||||
tile = tile.target();
|
||||
|
||||
if (type.hitTiles && collidesTiles() && !supressCollision) {
|
||||
world.raycastEach(world.toTile(lastPosition().x), world.toTile(lastPosition().y), world.toTile(x), world.toTile(y), (x, y) -> {
|
||||
if(tile.entity != null && tile.entity.collide(this) && !tile.entity.isDead() && tile.entity.tile.getTeam() != team){
|
||||
tile.entity.collision(this);
|
||||
|
||||
Tile tile = world.tile(x, y);
|
||||
if (tile == null) return false;
|
||||
tile = tile.target();
|
||||
if(!supressCollision){
|
||||
type.hit(this);
|
||||
remove();
|
||||
}
|
||||
|
||||
if (tile.entity != null && tile.entity.collide(this) && !tile.entity.isDead() && tile.entity.tile.getTeam() != team) {
|
||||
tile.entity.collision(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
if(!supressCollision){
|
||||
type.hit(this);
|
||||
remove();
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
supressCollision = false;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
@Override
|
||||
public void reset(){
|
||||
super.reset();
|
||||
timer.clear();
|
||||
team = null;
|
||||
data = null;
|
||||
}
|
||||
|
||||
supressCollision = false;
|
||||
}
|
||||
@Override
|
||||
public void removed(){
|
||||
Pooling.free(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
super.reset();
|
||||
timer.clear();
|
||||
team = null;
|
||||
data = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removed() {
|
||||
Pooling.free(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityGroup targetGroup() {
|
||||
return bulletGroup;
|
||||
}
|
||||
@Override
|
||||
public EntityGroup targetGroup(){
|
||||
return bulletGroup;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,65 +9,83 @@ import io.anuke.ucore.core.Effects;
|
||||
import io.anuke.ucore.entities.impl.BaseBulletType;
|
||||
|
||||
public abstract class BulletType extends BaseBulletType<Bullet> implements Content{
|
||||
private static int lastid = 0;
|
||||
private static Array<BulletType> types = new Array<>();
|
||||
private static int lastid = 0;
|
||||
private static Array<BulletType> types = new Array<>();
|
||||
|
||||
public final int id;
|
||||
/**Knockback in velocity.*/
|
||||
public float knockback;
|
||||
/**Whether this bullet hits tiles.*/
|
||||
public boolean hitTiles = true;
|
||||
/**Status effect applied on hit.*/
|
||||
public StatusEffect status = StatusEffects.none;
|
||||
/**Intensity of applied status effect in terms of duration.*/
|
||||
public float statusIntensity = 0.5f;
|
||||
/**What fraction of armor is pierced, 0-1*/
|
||||
public float armorPierce = 0f;
|
||||
/**Whether to sync this bullet to clients.*/
|
||||
public boolean syncable;
|
||||
/**Whether this bullet type collides with tiles.*/
|
||||
public boolean collidesTiles = true;
|
||||
/**Whether this bullet types collides with anything at all.*/
|
||||
public boolean collides = true;
|
||||
/**Whether velocity is inherited from the shooter.*/
|
||||
public boolean keepVelocity = true;
|
||||
public final int id;
|
||||
/**
|
||||
* Knockback in velocity.
|
||||
*/
|
||||
public float knockback;
|
||||
/**
|
||||
* Whether this bullet hits tiles.
|
||||
*/
|
||||
public boolean hitTiles = true;
|
||||
/**
|
||||
* Status effect applied on hit.
|
||||
*/
|
||||
public StatusEffect status = StatusEffects.none;
|
||||
/**
|
||||
* Intensity of applied status effect in terms of duration.
|
||||
*/
|
||||
public float statusIntensity = 0.5f;
|
||||
/**
|
||||
* What fraction of armor is pierced, 0-1
|
||||
*/
|
||||
public float armorPierce = 0f;
|
||||
/**
|
||||
* Whether to sync this bullet to clients.
|
||||
*/
|
||||
public boolean syncable;
|
||||
/**
|
||||
* Whether this bullet type collides with tiles.
|
||||
*/
|
||||
public boolean collidesTiles = true;
|
||||
/**
|
||||
* Whether this bullet types collides with anything at all.
|
||||
*/
|
||||
public boolean collides = true;
|
||||
/**
|
||||
* Whether velocity is inherited from the shooter.
|
||||
*/
|
||||
public boolean keepVelocity = true;
|
||||
|
||||
public BulletType(float speed, float damage){
|
||||
this.id = lastid ++;
|
||||
this.speed = speed;
|
||||
this.damage = damage;
|
||||
lifetime = 40f;
|
||||
hiteffect = BulletFx.hitBulletSmall;
|
||||
despawneffect = BulletFx.despawn;
|
||||
public BulletType(float speed, float damage){
|
||||
this.id = lastid++;
|
||||
this.speed = speed;
|
||||
this.damage = damage;
|
||||
lifetime = 40f;
|
||||
hiteffect = BulletFx.hitBulletSmall;
|
||||
despawneffect = BulletFx.despawn;
|
||||
|
||||
types.add(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hit(Bullet b, float hitx, float hity){
|
||||
Effects.effect(hiteffect, hitx, hity, b.angle());
|
||||
}
|
||||
types.add(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void despawned(Bullet b){
|
||||
Effects.effect(despawneffect, b.x, b.y, b.angle());
|
||||
}
|
||||
public static BulletType getByID(int id){
|
||||
return types.get(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getContentTypeName() {
|
||||
return "bullettype";
|
||||
}
|
||||
public static Array<BulletType> all(){
|
||||
return types;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Array<? extends Content> getAll() {
|
||||
return types;
|
||||
}
|
||||
@Override
|
||||
public void hit(Bullet b, float hitx, float hity){
|
||||
Effects.effect(hiteffect, hitx, hity, b.angle());
|
||||
}
|
||||
|
||||
public static BulletType getByID(int id){
|
||||
return types.get(id);
|
||||
}
|
||||
@Override
|
||||
public void despawned(Bullet b){
|
||||
Effects.effect(despawneffect, b.x, b.y, b.angle());
|
||||
}
|
||||
|
||||
public static Array<BulletType> all(){
|
||||
return types;
|
||||
}
|
||||
@Override
|
||||
public String getContentTypeName(){
|
||||
return "bullettype";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Array<? extends Content> getAll(){
|
||||
return types;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,10 +16,10 @@ import io.anuke.ucore.util.Mathf;
|
||||
import static io.anuke.mindustry.Vars.tilesize;
|
||||
import static io.anuke.mindustry.Vars.world;
|
||||
|
||||
public class LiquidBulletType extends BulletType {
|
||||
public class LiquidBulletType extends BulletType{
|
||||
Liquid liquid;
|
||||
|
||||
public LiquidBulletType(Liquid liquid) {
|
||||
public LiquidBulletType(Liquid liquid){
|
||||
super(2.5f, 0);
|
||||
this.liquid = liquid;
|
||||
|
||||
@@ -31,14 +31,14 @@ public class LiquidBulletType extends BulletType {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Bullet b) {
|
||||
public void draw(Bullet b){
|
||||
Draw.color(liquid.color, Color.WHITE, b.fout() / 100f + Mathf.randomSeedRange(b.id, 0.1f));
|
||||
|
||||
Fill.circle(b.x, b.y, 0.5f + b.fout()*2.5f);
|
||||
Fill.circle(b.x, b.y, 0.5f + b.fout() * 2.5f);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hit(Bullet b, float hitx, float hity) {
|
||||
public void hit(Bullet b, float hitx, float hity){
|
||||
Effects.effect(hiteffect, liquid.color, hitx, hity);
|
||||
Puddle.deposit(world.tileWorld(hitx, hity), liquid, 5f);
|
||||
|
||||
@@ -46,7 +46,7 @@ public class LiquidBulletType extends BulletType {
|
||||
float intensity = 400f;
|
||||
Fire.extinguish(world.tileWorld(hitx, hity), intensity);
|
||||
for(GridPoint2 p : Geometry.d4){
|
||||
Fire.extinguish(world.tileWorld(hitx + p.x*tilesize, hity + p.y*tilesize), intensity);
|
||||
Fire.extinguish(world.tileWorld(hitx + p.x * tilesize, hity + p.y * tilesize), intensity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,9 +4,9 @@ import io.anuke.mindustry.content.fx.BulletFx;
|
||||
import io.anuke.mindustry.graphics.Palette;
|
||||
import io.anuke.ucore.core.Effects;
|
||||
|
||||
public class MissileBulletType extends BasicBulletType {
|
||||
public class MissileBulletType extends BasicBulletType{
|
||||
|
||||
public MissileBulletType(float speed, float damage, String bulletSprite) {
|
||||
public MissileBulletType(float speed, float damage, String bulletSprite){
|
||||
super(speed, damage, bulletSprite);
|
||||
backColor = Palette.missileYellowBack;
|
||||
frontColor = Palette.missileYellow;
|
||||
@@ -14,7 +14,7 @@ public class MissileBulletType extends BasicBulletType {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(Bullet b) {
|
||||
public void update(Bullet b){
|
||||
super.update(b);
|
||||
|
||||
if(b.timer.get(0, 4f)){
|
||||
|
||||
@@ -10,24 +10,26 @@ import io.anuke.ucore.util.Mathf;
|
||||
|
||||
import static io.anuke.mindustry.Vars.groundEffectGroup;
|
||||
|
||||
/**Class for creating block rubble on the ground.*/
|
||||
public abstract class Decal extends TimedEntity implements BelowLiquidTrait, DrawTrait {
|
||||
/**
|
||||
* Class for creating block rubble on the ground.
|
||||
*/
|
||||
public abstract class Decal extends TimedEntity implements BelowLiquidTrait, DrawTrait{
|
||||
private static final Color color = Color.valueOf("52504e");
|
||||
|
||||
@Override
|
||||
public float lifetime() {
|
||||
public float lifetime(){
|
||||
return 8200f;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(){
|
||||
Draw.color(color.r, color.g, color.b, 1f-Mathf.curve(fin(), 0.98f));
|
||||
Draw.color(color.r, color.g, color.b, 1f - Mathf.curve(fin(), 0.98f));
|
||||
drawDecal();
|
||||
Draw.color();
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityGroup targetGroup() {
|
||||
public EntityGroup targetGroup(){
|
||||
return groundEffectGroup;
|
||||
}
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ import java.io.IOException;
|
||||
|
||||
import static io.anuke.mindustry.Vars.*;
|
||||
|
||||
public class Fire extends TimedEntity implements SaveTrait, SyncTrait, Poolable {
|
||||
public class Fire extends TimedEntity implements SaveTrait, SyncTrait, Poolable{
|
||||
private static final IntMap<Fire> map = new IntMap<>();
|
||||
private static final float baseLifetime = 1000f;
|
||||
|
||||
@@ -41,7 +41,15 @@ public class Fire extends TimedEntity implements SaveTrait, SyncTrait, Poolable
|
||||
private float baseFlammability = -1, puddleFlammability;
|
||||
private float lifetime;
|
||||
|
||||
/**Start a fire on the tile. If there already is a file there, refreshes its lifetime.*/
|
||||
/**
|
||||
* Deserialization use only!
|
||||
*/
|
||||
public Fire(){
|
||||
}
|
||||
|
||||
/**
|
||||
* 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() || tile == null) return; //not clientside.
|
||||
|
||||
@@ -60,24 +68,28 @@ 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 (tile != null && map.containsKey(tile.packedPosition())) {
|
||||
/**
|
||||
* 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(tile != null && map.containsKey(tile.packedPosition())){
|
||||
map.get(tile.packedPosition()).time += intensity * Timers.delta();
|
||||
}
|
||||
}
|
||||
|
||||
/**Deserialization use only!*/
|
||||
public Fire(){}
|
||||
@Remote(called = Loc.server, in = In.entities)
|
||||
public static void onFireRemoved(int fireid){
|
||||
fireGroup.removeByID(fireid);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float lifetime() {
|
||||
public float lifetime(){
|
||||
return lifetime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update() {
|
||||
if(Mathf.chance(0.1 * Timers.delta())) {
|
||||
public void update(){
|
||||
if(Mathf.chance(0.1 * Timers.delta())){
|
||||
Effects.effect(EnvironmentFx.fire, x + Mathf.range(4f), y + Mathf.range(4f));
|
||||
}
|
||||
|
||||
@@ -91,9 +103,10 @@ public class Fire extends TimedEntity implements SaveTrait, SyncTrait, Poolable
|
||||
|
||||
time = Mathf.clamp(time + Timers.delta(), 0, lifetime());
|
||||
|
||||
if(time >= lifetime()){
|
||||
if(time >= lifetime() || tile == null){
|
||||
CallEntity.onFireRemoved(getID());
|
||||
remove();
|
||||
return;
|
||||
}
|
||||
|
||||
TileEntity entity = tile.target().entity;
|
||||
@@ -102,19 +115,19 @@ public class Fire extends TimedEntity implements SaveTrait, SyncTrait, Poolable
|
||||
float flammability = baseFlammability + puddleFlammability;
|
||||
|
||||
if(!damage && flammability <= 0){
|
||||
time += Timers.delta()*8;
|
||||
time += Timers.delta() * 8;
|
||||
}
|
||||
|
||||
if (baseFlammability < 0 || block != tile.block()){
|
||||
if(baseFlammability < 0 || block != tile.block()){
|
||||
baseFlammability = tile.block().getFlammability(tile);
|
||||
block = tile.block();
|
||||
}
|
||||
|
||||
if(damage) {
|
||||
if(damage){
|
||||
lifetime += Mathf.clamp(flammability / 8f, 0f, 0.6f) * Timers.delta();
|
||||
}
|
||||
|
||||
if (flammability > 1f && Mathf.chance(0.03 * Timers.delta() * Mathf.clamp(flammability/5f, 0.3f, 2f))) {
|
||||
if(flammability > 1f && Mathf.chance(0.03 * Timers.delta() * Mathf.clamp(flammability / 5f, 0.3f, 2f))){
|
||||
GridPoint2 p = Mathf.select(Geometry.d4);
|
||||
Tile other = world.tile(tile.x + p.x, tile.y + p.y);
|
||||
create(other);
|
||||
@@ -127,7 +140,7 @@ public class Fire extends TimedEntity implements SaveTrait, SyncTrait, Poolable
|
||||
if(Mathf.chance(0.1 * Timers.delta())){
|
||||
Puddle p = Puddle.getPuddle(tile);
|
||||
if(p != null){
|
||||
puddleFlammability = p.getFlammability()/3f;
|
||||
puddleFlammability = p.getFlammability() / 3f;
|
||||
}else{
|
||||
puddleFlammability = 0;
|
||||
}
|
||||
@@ -140,14 +153,14 @@ public class Fire extends TimedEntity implements SaveTrait, SyncTrait, Poolable
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeSave(DataOutput stream) throws IOException {
|
||||
public void writeSave(DataOutput stream) throws IOException{
|
||||
stream.writeInt(tile.packedPosition());
|
||||
stream.writeFloat(lifetime);
|
||||
stream.writeFloat(time);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readSave(DataInput stream) throws IOException {
|
||||
public void readSave(DataInput stream) throws IOException{
|
||||
this.loadedPosition = stream.readInt();
|
||||
this.lifetime = stream.readFloat();
|
||||
this.time = stream.readFloat();
|
||||
@@ -155,19 +168,19 @@ public class Fire extends TimedEntity implements SaveTrait, SyncTrait, Poolable
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(DataOutput data) throws IOException {
|
||||
public void write(DataOutput data) throws IOException{
|
||||
data.writeFloat(x);
|
||||
data.writeFloat(y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(DataInput data, long time) throws IOException {
|
||||
public void read(DataInput data, long time) throws IOException{
|
||||
x = data.readFloat();
|
||||
y = data.readFloat();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
public void reset(){
|
||||
loadedPosition = -1;
|
||||
tile = null;
|
||||
baseFlammability = -1;
|
||||
@@ -175,7 +188,7 @@ public class Fire extends TimedEntity implements SaveTrait, SyncTrait, Poolable
|
||||
}
|
||||
|
||||
@Override
|
||||
public void added() {
|
||||
public void added(){
|
||||
if(loadedPosition != -1){
|
||||
map.put(loadedPosition, this);
|
||||
tile = world.tile(loadedPosition);
|
||||
@@ -184,7 +197,7 @@ public class Fire extends TimedEntity implements SaveTrait, SyncTrait, Poolable
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removed() {
|
||||
public void removed(){
|
||||
if(tile != null){
|
||||
map.remove(tile.packedPosition());
|
||||
}
|
||||
@@ -192,12 +205,7 @@ public class Fire extends TimedEntity implements SaveTrait, SyncTrait, Poolable
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityGroup targetGroup() {
|
||||
public EntityGroup targetGroup(){
|
||||
return fireGroup;
|
||||
}
|
||||
|
||||
@Remote(called = Loc.server, in = In.entities)
|
||||
public static void onFireRemoved(int fireid){
|
||||
fireGroup.removeByID(fireid);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,27 +9,29 @@ import io.anuke.ucore.entities.impl.EffectEntity;
|
||||
import io.anuke.ucore.function.EffectRenderer;
|
||||
import io.anuke.ucore.util.Mathf;
|
||||
|
||||
/**A ground effect contains an effect that is rendered on the ground layer as opposed to the top layer.*/
|
||||
public class GroundEffectEntity extends EffectEntity {
|
||||
/**
|
||||
* A ground effect contains an effect that is rendered on the ground layer as opposed to the top layer.
|
||||
*/
|
||||
public class GroundEffectEntity extends EffectEntity{
|
||||
private boolean once;
|
||||
|
||||
@Override
|
||||
public void update(){
|
||||
GroundEffect effect = (GroundEffect)this.effect;
|
||||
GroundEffect effect = (GroundEffect) this.effect;
|
||||
|
||||
if(effect.isStatic) {
|
||||
if(effect.isStatic){
|
||||
time += Timers.delta();
|
||||
|
||||
time = Mathf.clamp(time, 0, effect.staticLife);
|
||||
|
||||
if (!once && time >= lifetime()) {
|
||||
if(!once && time >= lifetime()){
|
||||
once = true;
|
||||
time = 0f;
|
||||
Tile tile = Vars.world.tileWorld(x, y);
|
||||
if(tile != null && tile.floor().isLiquid){
|
||||
remove();
|
||||
}
|
||||
} else if (once && time >= effect.staticLife) {
|
||||
}else if(once && time >= effect.staticLife){
|
||||
remove();
|
||||
}
|
||||
}else{
|
||||
@@ -39,7 +41,7 @@ public class GroundEffectEntity extends EffectEntity {
|
||||
|
||||
@Override
|
||||
public void draw(){
|
||||
GroundEffect effect = (GroundEffect)this.effect;
|
||||
GroundEffect effect = (GroundEffect) this.effect;
|
||||
|
||||
if(once && effect.isStatic)
|
||||
Effects.renderEffect(id, effect, color, lifetime(), rotation, x, y, data);
|
||||
@@ -48,32 +50,38 @@ public class GroundEffectEntity extends EffectEntity {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
public void reset(){
|
||||
super.reset();
|
||||
once = false;
|
||||
}
|
||||
|
||||
/**An effect that is rendered on the ground layer as opposed to the top layer.*/
|
||||
/**
|
||||
* An effect that is rendered on the ground layer as opposed to the top layer.
|
||||
*/
|
||||
public static class GroundEffect extends Effect{
|
||||
/**How long this effect stays on the ground when static.*/
|
||||
/**
|
||||
* How long this effect stays on the ground when static.
|
||||
*/
|
||||
public final float staticLife;
|
||||
/**If true, this effect will stop and lie on the ground for a specific duration,
|
||||
* after its initial lifetime is over.*/
|
||||
/**
|
||||
* If true, this effect will stop and lie on the ground for a specific duration,
|
||||
* after its initial lifetime is over.
|
||||
*/
|
||||
public final boolean isStatic;
|
||||
|
||||
public GroundEffect(float life, float staticLife, EffectRenderer draw) {
|
||||
public GroundEffect(float life, float staticLife, EffectRenderer draw){
|
||||
super(life, draw);
|
||||
this.staticLife = staticLife;
|
||||
this.isStatic = true;
|
||||
}
|
||||
|
||||
public GroundEffect(boolean isStatic, float life, EffectRenderer draw) {
|
||||
public GroundEffect(boolean isStatic, float life, EffectRenderer draw){
|
||||
super(life, draw);
|
||||
this.staticLife = 0f;
|
||||
this.isStatic = isStatic;
|
||||
}
|
||||
|
||||
public GroundEffect(float life, EffectRenderer draw) {
|
||||
public GroundEffect(float life, EffectRenderer draw){
|
||||
super(life, draw);
|
||||
this.staticLife = 0f;
|
||||
this.isStatic = false;
|
||||
|
||||
@@ -35,7 +35,7 @@ import java.io.IOException;
|
||||
|
||||
import static io.anuke.mindustry.Vars.*;
|
||||
|
||||
public class ItemDrop extends SolidEntity implements SaveTrait, SyncTrait, DrawTrait, VelocityTrait, TimeTrait, TargetTrait, Poolable {
|
||||
public class ItemDrop extends SolidEntity implements SaveTrait, SyncTrait, DrawTrait, VelocityTrait, TimeTrait, TargetTrait, Poolable{
|
||||
private static final float sinkLifetime = 80f;
|
||||
|
||||
private Interpolator interpolator = new Interpolator();
|
||||
@@ -46,6 +46,14 @@ public class ItemDrop extends SolidEntity implements SaveTrait, SyncTrait, DrawT
|
||||
private float time;
|
||||
private float sinktime;
|
||||
|
||||
/**
|
||||
* Internal use only!
|
||||
*/
|
||||
public ItemDrop(){
|
||||
hitbox.setSize(5f);
|
||||
hitboxTile.setSize(5f);
|
||||
}
|
||||
|
||||
public static ItemDrop create(Item item, int amount, float x, float y, float angle){
|
||||
ItemDrop drop = new ItemDrop();
|
||||
drop.item = item;
|
||||
@@ -73,13 +81,7 @@ public class ItemDrop extends SolidEntity implements SaveTrait, SyncTrait, DrawT
|
||||
}
|
||||
}
|
||||
|
||||
/**Internal use only!*/
|
||||
public ItemDrop(){
|
||||
hitbox.setSize(5f);
|
||||
hitboxTile.setSize(5f);
|
||||
}
|
||||
|
||||
public Item getItem() {
|
||||
public Item getItem(){
|
||||
return item;
|
||||
}
|
||||
|
||||
@@ -88,66 +90,66 @@ public class ItemDrop extends SolidEntity implements SaveTrait, SyncTrait, DrawT
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDead() {
|
||||
public boolean isDead(){
|
||||
return !isAdded();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Team getTeam() {
|
||||
public Team getTeam(){
|
||||
return Team.none;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float lifetime() {
|
||||
return 60*60;
|
||||
public float lifetime(){
|
||||
return 60 * 60;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void time(float time) {
|
||||
public void time(float time){
|
||||
this.time = time;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float time() {
|
||||
public float time(){
|
||||
return time;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vector2 getVelocity() {
|
||||
public Vector2 getVelocity(){
|
||||
return velocity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean collides(SolidTrait other) {
|
||||
public boolean collides(SolidTrait other){
|
||||
return other instanceof Player && time > 20f;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void collision(SolidTrait other, float x, float y) {
|
||||
Unit player = (Unit)other;
|
||||
public void collision(SolidTrait other, float x, float y){
|
||||
Unit player = (Unit) other;
|
||||
if(player.inventory.canAcceptItem(item, 1)){
|
||||
int used = Math.min(amount, player.inventory.capacity() - player.inventory.getItem().amount);
|
||||
player.inventory.addItem(item, used);
|
||||
amount -= used;
|
||||
|
||||
if(amount <= 0) {
|
||||
if(amount <= 0){
|
||||
CallEntity.onPickup(getID());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw() {
|
||||
float size = itemSize * (1f - sinktime/sinkLifetime) * (1f-Mathf.curve(fin(), 0.98f));
|
||||
public void draw(){
|
||||
float size = itemSize * (1f - sinktime / sinkLifetime) * (1f - Mathf.curve(fin(), 0.98f));
|
||||
|
||||
Tile tile = world.tileWorld(x, y);
|
||||
|
||||
Draw.color(Color.WHITE, tile == null || !tile.floor().isLiquid ? Color.WHITE : tile.floor().liquidColor, sinktime/sinkLifetime);
|
||||
Draw.color(Color.WHITE, tile == null || !tile.floor().isLiquid ? Color.WHITE : tile.floor().liquidColor, sinktime / sinkLifetime);
|
||||
Draw.rect(item.region, x, y, size, size);
|
||||
|
||||
int stored = Mathf.clamp(amount / 6, 1, 8);
|
||||
|
||||
for(int i = 0; i < stored; i ++) {
|
||||
for(int i = 0; i < stored; i++){
|
||||
float px = stored == 1 ? 0 : Mathf.randomSeedRange(i + 1, 4f);
|
||||
float py = stored == 1 ? 0 : Mathf.randomSeedRange(i + 2, 4f);
|
||||
Draw.rect(item.region, x + px, y + py, size, size);
|
||||
@@ -157,8 +159,8 @@ public class ItemDrop extends SolidEntity implements SaveTrait, SyncTrait, DrawT
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update() {
|
||||
if(Net.client()) {
|
||||
public void update(){
|
||||
if(Net.client()){
|
||||
interpolate();
|
||||
}else{
|
||||
updateVelocity(0.2f);
|
||||
@@ -190,28 +192,28 @@ public class ItemDrop extends SolidEntity implements SaveTrait, SyncTrait, DrawT
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
public void reset(){
|
||||
time = 0f;
|
||||
interpolator.reset();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Interpolator getInterpolator() {
|
||||
public Interpolator getInterpolator(){
|
||||
return interpolator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float drawSize() {
|
||||
public float drawSize(){
|
||||
return 10;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityGroup targetGroup() {
|
||||
public EntityGroup targetGroup(){
|
||||
return itemGroup;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeSave(DataOutput data) throws IOException {
|
||||
public void writeSave(DataOutput data) throws IOException{
|
||||
data.writeFloat(x);
|
||||
data.writeFloat(y);
|
||||
data.writeByte(item.id);
|
||||
@@ -219,7 +221,7 @@ public class ItemDrop extends SolidEntity implements SaveTrait, SyncTrait, DrawT
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readSave(DataInput data) throws IOException {
|
||||
public void readSave(DataInput data) throws IOException{
|
||||
x = data.readFloat();
|
||||
y = data.readFloat();
|
||||
item = Item.getByID(data.readByte());
|
||||
|
||||
@@ -32,17 +32,22 @@ public class ItemTransfer extends TimedEntity implements DrawTrait{
|
||||
private PosTrait to;
|
||||
private Runnable done;
|
||||
|
||||
public ItemTransfer(){
|
||||
}
|
||||
|
||||
@Remote(in = In.entities, called = Loc.server, unreliable = true)
|
||||
public static void transferAmmo(Item item, float x, float y, Unit to){
|
||||
if(to == null) return;
|
||||
to.addAmmo(item);
|
||||
create(item, x, y, to, () -> {});
|
||||
create(item, x, y, to, () -> {
|
||||
});
|
||||
}
|
||||
|
||||
@Remote(in = In.entities, called = Loc.server, unreliable = true)
|
||||
public static void transferItemEffect(Item item, float x, float y, Unit to){
|
||||
if(to == null) return;
|
||||
create(item, x, y, to, () -> {});
|
||||
create(item, x, y, to, () -> {
|
||||
});
|
||||
}
|
||||
|
||||
@Remote(in = In.entities, called = Loc.server, unreliable = true)
|
||||
@@ -54,10 +59,11 @@ public class ItemTransfer extends TimedEntity implements DrawTrait{
|
||||
@Remote(in = In.entities, called = Loc.server)
|
||||
public static void transferItemTo(Item item, int amount, float x, float y, Tile tile){
|
||||
if(tile == null) return;
|
||||
for (int i = 0; i < Mathf.clamp(amount/3, 1, 8); i++) {
|
||||
Timers.run(i*3, () -> create(item, x, y, tile, () -> {}));
|
||||
for(int i = 0; i < Mathf.clamp(amount / 3, 1, 8); i++){
|
||||
Timers.run(i * 3, () -> create(item, x, y, tile, () -> {
|
||||
}));
|
||||
}
|
||||
tile.entity.items.addItem(item, amount);
|
||||
tile.entity.items.add(item, amount);
|
||||
}
|
||||
|
||||
public static void create(Item item, float fromx, float fromy, PosTrait to, Runnable done){
|
||||
@@ -70,15 +76,13 @@ public class ItemTransfer extends TimedEntity implements DrawTrait{
|
||||
tr.add();
|
||||
}
|
||||
|
||||
public ItemTransfer(){}
|
||||
|
||||
@Override
|
||||
public float lifetime() {
|
||||
public float lifetime(){
|
||||
return 60;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
public void reset(){
|
||||
super.reset();
|
||||
item = null;
|
||||
to = null;
|
||||
@@ -89,7 +93,7 @@ public class ItemTransfer extends TimedEntity implements DrawTrait{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removed() {
|
||||
public void removed(){
|
||||
if(done != null){
|
||||
threads.run(done);
|
||||
}
|
||||
@@ -97,7 +101,7 @@ public class ItemTransfer extends TimedEntity implements DrawTrait{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update() {
|
||||
public void update(){
|
||||
if(to == null){
|
||||
remove();
|
||||
return;
|
||||
@@ -110,24 +114,24 @@ public class ItemTransfer extends TimedEntity implements DrawTrait{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw() {
|
||||
float length = fslope()*6f;
|
||||
public void draw(){
|
||||
float length = fslope() * 6f;
|
||||
float angle = current.set(x, y).sub(from).angle();
|
||||
Draw.color(Palette.accent);
|
||||
Lines.stroke(fslope()*2f);
|
||||
Lines.stroke(fslope() * 2f);
|
||||
|
||||
Lines.circle(x, y, fslope()*2f);
|
||||
Lines.circle(x, y, fslope() * 2f);
|
||||
Lines.lineAngleCenter(x, y, angle, length);
|
||||
Lines.lineAngle(x, y, angle, fout()*6f);
|
||||
Lines.lineAngle(x, y, angle, fout() * 6f);
|
||||
|
||||
Draw.color(item.color);
|
||||
Fill.circle(x, y, fslope()*1.5f);
|
||||
Fill.circle(x, y, fslope() * 1.5f);
|
||||
|
||||
Draw.reset();
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityGroup targetGroup() {
|
||||
public EntityGroup targetGroup(){
|
||||
return effectGroup;
|
||||
}
|
||||
|
||||
|
||||
@@ -46,7 +46,15 @@ public class Lightning extends TimedEntity implements Poolable, DrawTrait, SyncT
|
||||
private Color color = Palette.lancerLaser;
|
||||
private SeedRandom random = new SeedRandom();
|
||||
|
||||
/**Create a lighting branch at a location. Use Team.none to damage everyone.*/
|
||||
/**
|
||||
* For pooling use only. Do not call directly!
|
||||
*/
|
||||
public Lightning(){
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a lighting branch at a location. Use Team.none to damage everyone.
|
||||
*/
|
||||
public static void create(Team team, Effect effect, Color color, float damage, float x, float y, float targetAngle, int length){
|
||||
CallEntity.createLighting(lastSeed++, team, effect, color, damage, x, y, targetAngle, length);
|
||||
}
|
||||
@@ -69,7 +77,7 @@ public class Lightning extends TimedEntity implements Poolable, DrawTrait, SyncT
|
||||
|
||||
Units.getNearbyEnemies(team, rect, entities::add);
|
||||
|
||||
for(int i = 0; i < length; i ++){
|
||||
for(int i = 0; i < length; i++){
|
||||
l.lines.add(new Vector2(x, y));
|
||||
|
||||
float fx = x, fy = y;
|
||||
@@ -82,15 +90,15 @@ public class Lightning extends TimedEntity implements Poolable, DrawTrait, SyncT
|
||||
|
||||
Units.getNearbyEnemies(team, rect, entity -> {
|
||||
float dst = entity.distanceTo(x2, y2);
|
||||
if(dst < attractRange) {
|
||||
if(dst < attractRange){
|
||||
angle = Mathf.slerp(angle, Angles.angle(x2, y2, entity.x, entity.y), (attractRange - dst) / attractRange / 4f);
|
||||
}
|
||||
|
||||
entity.getHitbox(hitrect);
|
||||
hitrect.x -= range/2f;
|
||||
hitrect.y -= range/2f;
|
||||
hitrect.width += range/2f;
|
||||
hitrect.height += range/2f;
|
||||
hitrect.x -= range / 2f;
|
||||
hitrect.y -= range / 2f;
|
||||
hitrect.width += range / 2f;
|
||||
hitrect.height += range / 2f;
|
||||
|
||||
if(hitrect.contains(x2, y2) || hitrect.contains(fx, fy)){
|
||||
float result = damage;
|
||||
@@ -104,7 +112,7 @@ public class Lightning extends TimedEntity implements Poolable, DrawTrait, SyncT
|
||||
});
|
||||
|
||||
if(l.random.chance(0.1f)){
|
||||
createLighting(l.random.nextInt(), team, effect, color, damage, x2, y2, angle + l.random.range(100f), length/3);
|
||||
createLighting(l.random.nextInt(), team, effect, color, damage, x2, y2, angle + l.random.range(100f), length / 3);
|
||||
}
|
||||
|
||||
x = x2;
|
||||
@@ -115,47 +123,44 @@ public class Lightning extends TimedEntity implements Poolable, DrawTrait, SyncT
|
||||
l.add();
|
||||
}
|
||||
|
||||
/**For pooling use only. Do not call directly!*/
|
||||
public Lightning(){}
|
||||
|
||||
@Override
|
||||
public boolean isSyncing() {
|
||||
public boolean isSyncing(){
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(DataOutput data) throws IOException {
|
||||
public void write(DataOutput data) throws IOException{
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(DataInput data, long time) throws IOException {
|
||||
public void read(DataInput data, long time) throws IOException{
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public float lifetime() {
|
||||
public float lifetime(){
|
||||
return 10;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
public void reset(){
|
||||
color = Palette.lancerLaser;
|
||||
lines.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removed() {
|
||||
public void removed(){
|
||||
Pooling.free(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw() {
|
||||
public void draw(){
|
||||
float lx = x, ly = y;
|
||||
Draw.color(color, Color.WHITE, fin());
|
||||
for(int i = 0; i < lines.size; i ++){
|
||||
for(int i = 0; i < lines.size; i++){
|
||||
Vector2 v = lines.get(i);
|
||||
Lines.stroke(fout() * 3f + 1f-(float)i/lines.size);
|
||||
Lines.stroke(fout() * 3f + 1f - (float) i / lines.size);
|
||||
Lines.line(lx, ly, v.x, v.y);
|
||||
lx = v.x;
|
||||
ly = v.y;
|
||||
@@ -164,7 +169,7 @@ public class Lightning extends TimedEntity implements Poolable, DrawTrait, SyncT
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityGroup targetGroup() {
|
||||
public EntityGroup targetGroup(){
|
||||
return bulletGroup;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ import java.io.IOException;
|
||||
import static io.anuke.mindustry.Vars.puddleGroup;
|
||||
import static io.anuke.mindustry.Vars.world;
|
||||
|
||||
public class Puddle extends BaseEntity implements SaveTrait, Poolable, DrawTrait, SyncTrait {
|
||||
public class Puddle extends BaseEntity implements SaveTrait, Poolable, DrawTrait, SyncTrait{
|
||||
private static final IntMap<Puddle> map = new IntMap<>();
|
||||
private static final float maxLiquid = 70f;
|
||||
private static final int maxGeneration = 2;
|
||||
@@ -58,17 +58,29 @@ public class Puddle extends BaseEntity implements SaveTrait, Poolable, DrawTrait
|
||||
private float accepting;
|
||||
private byte generation;
|
||||
|
||||
/**Deposists a puddle between tile and source.*/
|
||||
/**
|
||||
* Deserialization use only!
|
||||
*/
|
||||
public Puddle(){
|
||||
}
|
||||
|
||||
/**
|
||||
* Deposists a puddle between tile and source.
|
||||
*/
|
||||
public static void deposit(Tile tile, Tile source, Liquid liquid, float amount){
|
||||
deposit(tile, source, liquid, amount, 0);
|
||||
}
|
||||
|
||||
/**Deposists a puddle at a tile.*/
|
||||
/**
|
||||
* Deposists a puddle at a tile.
|
||||
*/
|
||||
public static void deposit(Tile tile, Liquid liquid, float amount){
|
||||
deposit(tile, tile, liquid, amount, 0);
|
||||
}
|
||||
|
||||
/**Returns the puddle on the specified tile. May return null.*/
|
||||
/**
|
||||
* Returns the puddle on the specified tile. May return null.
|
||||
*/
|
||||
public static Puddle getPuddle(Tile tile){
|
||||
return map.get(tile.packedPosition());
|
||||
}
|
||||
@@ -76,11 +88,11 @@ public class Puddle extends BaseEntity implements SaveTrait, Poolable, DrawTrait
|
||||
private static void deposit(Tile tile, Tile source, Liquid liquid, float amount, int generation){
|
||||
if(tile.floor().isLiquid && !canStayOn(liquid, tile.floor().liquidDrop)){
|
||||
reactPuddle(tile.floor().liquidDrop, liquid, amount, tile,
|
||||
(tile.worldx() + source.worldx())/2f, (tile.worldy() + source.worldy())/2f);
|
||||
(tile.worldx() + source.worldx()) / 2f, (tile.worldy() + source.worldy()) / 2f);
|
||||
|
||||
if(generation == 0 && Timers.get(tile, "ripple", 50)){
|
||||
Effects.effect(BlockFx.ripple, tile.floor().liquidDrop.color,
|
||||
(tile.worldx() + source.worldx())/2f, (tile.worldy() + source.worldy())/2f);
|
||||
(tile.worldx() + source.worldx()) / 2f, (tile.worldy() + source.worldy()) / 2f);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -93,28 +105,32 @@ public class Puddle extends BaseEntity implements SaveTrait, Poolable, DrawTrait
|
||||
puddle.tile = tile;
|
||||
puddle.liquid = liquid;
|
||||
puddle.amount = amount;
|
||||
puddle.generation = (byte)generation;
|
||||
puddle.set((tile.worldx() + source.worldx())/2f, (tile.worldy() + source.worldy())/2f);
|
||||
puddle.generation = (byte) generation;
|
||||
puddle.set((tile.worldx() + source.worldx()) / 2f, (tile.worldy() + source.worldy()) / 2f);
|
||||
puddle.add();
|
||||
map.put(tile.packedPosition(), puddle);
|
||||
}else if(p.liquid == liquid){
|
||||
p.accepting = Math.max(amount, p.accepting);
|
||||
|
||||
if(generation == 0 && Timers.get(p, "ripple2", 50) && p.amount >= maxLiquid/2f){
|
||||
Effects.effect(BlockFx.ripple, p.liquid.color, (tile.worldx() + source.worldx())/2f, (tile.worldy() + source.worldy())/2f);
|
||||
if(generation == 0 && Timers.get(p, "ripple2", 50) && p.amount >= maxLiquid / 2f){
|
||||
Effects.effect(BlockFx.ripple, p.liquid.color, (tile.worldx() + source.worldx()) / 2f, (tile.worldy() + source.worldy()) / 2f);
|
||||
}
|
||||
}else{
|
||||
p.amount -= reactPuddle(p.liquid, liquid, amount, p.tile, p.x, p.y);
|
||||
}
|
||||
}
|
||||
|
||||
/**Returns whether the first liquid can 'stay' on the second one.
|
||||
* Currently, the only place where this can happen is oil on water.*/
|
||||
/**
|
||||
* Returns whether the first liquid can 'stay' on the second one.
|
||||
* Currently, the only place where this can happen is oil on water.
|
||||
*/
|
||||
private static boolean canStayOn(Liquid liquid, Liquid other){
|
||||
return liquid == Liquids.oil && other == Liquids.water;
|
||||
}
|
||||
|
||||
/**Reacts two liquids together at a location.*/
|
||||
/**
|
||||
* Reacts two liquids together at a location.
|
||||
*/
|
||||
private static float reactPuddle(Liquid dest, Liquid liquid, float amount, Tile tile, float x, float y){
|
||||
if((dest.flammability > 0.3f && liquid.temperature > 0.7f) ||
|
||||
(liquid.flammability > 0.3f && dest.temperature > 0.7f)){ //flammable liquid + hot liquid
|
||||
@@ -126,25 +142,27 @@ public class Puddle extends BaseEntity implements SaveTrait, Poolable, DrawTrait
|
||||
if(Mathf.chance(0.5f * amount)){
|
||||
Effects.effect(EnvironmentFx.steam, x, y);
|
||||
}
|
||||
return - 0.1f * amount;
|
||||
return -0.1f * amount;
|
||||
}else if(liquid.temperature > 0.7f && dest.temperature < 0.55f){ //hot liquid poured onto cold puddle
|
||||
if(Mathf.chance(0.8f * amount)){
|
||||
Effects.effect(EnvironmentFx.steam, x, y);
|
||||
}
|
||||
return - 0.4f * amount;
|
||||
return -0.4f * amount;
|
||||
}
|
||||
return 0f;
|
||||
}
|
||||
|
||||
/**Deserialization use only!*/
|
||||
public Puddle(){}
|
||||
@Remote(called = Loc.server, in = In.entities)
|
||||
public static void onPuddleRemoved(int puddleid){
|
||||
puddleGroup.removeByID(puddleid);
|
||||
}
|
||||
|
||||
public float getFlammability(){
|
||||
return liquid.flammability * amount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update() {
|
||||
public void update(){
|
||||
|
||||
//no updating happens clientside
|
||||
if(Net.client()){
|
||||
@@ -158,11 +176,11 @@ public class Puddle extends BaseEntity implements SaveTrait, Poolable, DrawTrait
|
||||
amount += accepting;
|
||||
accepting = 0f;
|
||||
|
||||
if (amount >= maxLiquid / 1.5f && generation < maxGeneration) {
|
||||
if(amount >= maxLiquid / 1.5f && generation < maxGeneration){
|
||||
float deposited = Math.min((amount - maxLiquid / 1.5f) / 4f, 0.3f) * Timers.delta();
|
||||
for (GridPoint2 point : Geometry.d4) {
|
||||
for(GridPoint2 point : Geometry.d4){
|
||||
Tile other = world.tile(tile.x + point.x, tile.y + point.y);
|
||||
if (other.block() == Blocks.air && other.cliffs == 0) {
|
||||
if(other.block() == Blocks.air && other.cliffs == 0){
|
||||
deposit(other, tile, liquid, deposited, generation + 1);
|
||||
amount -= deposited / 2f; //tweak to speed up/slow down puddle propagation
|
||||
}
|
||||
@@ -171,14 +189,14 @@ public class Puddle extends BaseEntity implements SaveTrait, Poolable, DrawTrait
|
||||
|
||||
amount = Mathf.clamp(amount, 0, maxLiquid);
|
||||
|
||||
if (amount <= 0f) {
|
||||
if(amount <= 0f){
|
||||
CallEntity.onPuddleRemoved(getID());
|
||||
}
|
||||
}
|
||||
|
||||
//effects-only code
|
||||
if(amount >= maxLiquid/2f && updateTime <= 0f){
|
||||
Units.getNearby(rect.setSize(Mathf.clamp(amount/(maxLiquid/1.5f))*10f).setCenter(x, y), unit -> {
|
||||
if(amount >= maxLiquid / 2f && updateTime <= 0f){
|
||||
Units.getNearby(rect.setSize(Mathf.clamp(amount / (maxLiquid / 1.5f)) * 10f).setCenter(x, y), unit -> {
|
||||
if(unit.isFlying()) return;
|
||||
|
||||
unit.getHitbox(rect2);
|
||||
@@ -186,7 +204,7 @@ public class Puddle extends BaseEntity implements SaveTrait, Poolable, DrawTrait
|
||||
|
||||
unit.applyEffect(liquid.effect, 0.5f);
|
||||
|
||||
if(unit.getVelocity().len() > 0.1) {
|
||||
if(unit.getVelocity().len() > 0.1){
|
||||
Effects.effect(BlockFx.ripple, liquid.color, unit.x, unit.y);
|
||||
}
|
||||
});
|
||||
@@ -202,30 +220,30 @@ public class Puddle extends BaseEntity implements SaveTrait, Poolable, DrawTrait
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw() {
|
||||
public void draw(){
|
||||
seeds = id;
|
||||
boolean onLiquid = tile.floor().isLiquid;
|
||||
float f = Mathf.clamp(amount/(maxLiquid/1.5f));
|
||||
float f = Mathf.clamp(amount / (maxLiquid / 1.5f));
|
||||
float smag = onLiquid ? 0.8f : 0f;
|
||||
float sscl = 20f;
|
||||
|
||||
Draw.color(Hue.shift(tmp.set(liquid.color), 2, -0.05f));
|
||||
Fill.circle(x + Mathf.sin(Timers.time() + seeds*532, sscl, smag), y + Mathf.sin(Timers.time() + seeds*53, sscl, smag), f * 8f);
|
||||
Fill.circle(x + Mathf.sin(Timers.time() + seeds * 532, sscl, smag), y + Mathf.sin(Timers.time() + seeds * 53, sscl, smag), f * 8f);
|
||||
Angles.randLenVectors(id, 3, f * 6f, (ex, ey) -> {
|
||||
Fill.circle(x + ex + Mathf.sin(Timers.time() + seeds*532, sscl, smag),
|
||||
y + ey + Mathf.sin(Timers.time() + seeds*53, sscl, smag), f * 5f);
|
||||
seeds ++;
|
||||
Fill.circle(x + ex + Mathf.sin(Timers.time() + seeds * 532, sscl, smag),
|
||||
y + ey + Mathf.sin(Timers.time() + seeds * 53, sscl, smag), f * 5f);
|
||||
seeds++;
|
||||
});
|
||||
Draw.color();
|
||||
}
|
||||
|
||||
@Override
|
||||
public float drawSize() {
|
||||
public float drawSize(){
|
||||
return 20;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeSave(DataOutput stream) throws IOException {
|
||||
public void writeSave(DataOutput stream) throws IOException{
|
||||
stream.writeInt(tile.packedPosition());
|
||||
stream.writeFloat(x);
|
||||
stream.writeFloat(y);
|
||||
@@ -235,7 +253,7 @@ public class Puddle extends BaseEntity implements SaveTrait, Poolable, DrawTrait
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readSave(DataInput stream) throws IOException {
|
||||
public void readSave(DataInput stream) throws IOException{
|
||||
this.loadedPosition = stream.readInt();
|
||||
this.x = stream.readFloat();
|
||||
this.y = stream.readFloat();
|
||||
@@ -246,7 +264,7 @@ public class Puddle extends BaseEntity implements SaveTrait, Poolable, DrawTrait
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
public void reset(){
|
||||
loadedPosition = -1;
|
||||
tile = null;
|
||||
liquid = null;
|
||||
@@ -256,7 +274,7 @@ public class Puddle extends BaseEntity implements SaveTrait, Poolable, DrawTrait
|
||||
}
|
||||
|
||||
@Override
|
||||
public void added() {
|
||||
public void added(){
|
||||
if(loadedPosition != -1){
|
||||
map.put(loadedPosition, this);
|
||||
tile = world.tile(loadedPosition);
|
||||
@@ -264,38 +282,33 @@ public class Puddle extends BaseEntity implements SaveTrait, Poolable, DrawTrait
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removed() {
|
||||
public void removed(){
|
||||
map.remove(tile.packedPosition());
|
||||
reset();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(DataOutput data) throws IOException {
|
||||
public void write(DataOutput data) throws IOException{
|
||||
data.writeFloat(x);
|
||||
data.writeFloat(y);
|
||||
data.writeByte(liquid.id);
|
||||
data.writeShort((short)(amount * 4));
|
||||
data.writeShort((short) (amount * 4));
|
||||
data.writeInt(tile.packedPosition());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(DataInput data, long time) throws IOException {
|
||||
public void read(DataInput data, long time) throws IOException{
|
||||
x = data.readFloat();
|
||||
y = data.readFloat();
|
||||
liquid = Liquid.getByID(data.readByte());
|
||||
targetAmount = data.readShort()/4f;
|
||||
targetAmount = data.readShort() / 4f;
|
||||
tile = world.tile(data.readInt());
|
||||
|
||||
map.put(tile.packedPosition(), this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityGroup targetGroup() {
|
||||
public EntityGroup targetGroup(){
|
||||
return puddleGroup;
|
||||
}
|
||||
|
||||
@Remote(called = Loc.server, in = In.entities)
|
||||
public static void onPuddleRemoved(int puddleid){
|
||||
puddleGroup.removeByID(puddleid);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,10 +3,12 @@ package io.anuke.mindustry.entities.effect;
|
||||
import io.anuke.ucore.graphics.Draw;
|
||||
import io.anuke.ucore.util.Mathf;
|
||||
|
||||
public class RubbleDecal extends Decal {
|
||||
public class RubbleDecal extends Decal{
|
||||
private int size;
|
||||
|
||||
/**Creates a rubble effect at a position. Provide a block size to use.*/
|
||||
/**
|
||||
* Creates a rubble effect at a position. Provide a block size to use.
|
||||
*/
|
||||
public static void create(float x, float y, int size){
|
||||
RubbleDecal decal = new RubbleDecal();
|
||||
decal.size = size;
|
||||
|
||||
@@ -8,21 +8,21 @@ import io.anuke.ucore.util.Mathf;
|
||||
|
||||
import static io.anuke.mindustry.Vars.world;
|
||||
|
||||
public class ScorchDecal extends Decal {
|
||||
public class ScorchDecal extends Decal{
|
||||
private static final int scorches = 5;
|
||||
private static final TextureRegion[] regions = new TextureRegion[scorches];
|
||||
|
||||
public static void create(float x, float y){
|
||||
if(regions[0] == null){
|
||||
for (int i = 0; i < regions.length; i++) {
|
||||
regions[i] = Draw.region("scorch" + (i+1));
|
||||
for(int i = 0; i < regions.length; i++){
|
||||
regions[i] = Draw.region("scorch" + (i + 1));
|
||||
}
|
||||
}
|
||||
|
||||
Tile tile = world.tileWorld(x, y);
|
||||
|
||||
if(tile == null || tile.floor().liquidDrop != null) return;
|
||||
|
||||
|
||||
ScorchDecal decal = new ScorchDecal();
|
||||
decal.set(x, y);
|
||||
decal.add();
|
||||
@@ -31,10 +31,10 @@ public class ScorchDecal extends Decal {
|
||||
@Override
|
||||
public void drawDecal(){
|
||||
|
||||
for (int i = 0; i < 5; i++) {
|
||||
TextureRegion region = regions[Mathf.randomSeed(id - i, 0, scorches-1)];
|
||||
for(int i = 0; i < 5; i++){
|
||||
TextureRegion region = regions[Mathf.randomSeed(id - i, 0, scorches - 1)];
|
||||
float rotation = Mathf.randomSeed(id + i, 0, 360);
|
||||
float space = 1.5f + Mathf.randomSeed(id + i + 1, 0, 20)/10f;
|
||||
float space = 1.5f + Mathf.randomSeed(id + i + 1, 0, 20) / 10f;
|
||||
Draw.grect(region, x + Angles.trnsx(rotation, space), y + Angles.trnsy(rotation, space), rotation - 90);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,44 +13,43 @@ import io.anuke.ucore.util.Mathf;
|
||||
import static io.anuke.mindustry.Vars.shieldGroup;
|
||||
|
||||
//todo re-implement
|
||||
public class Shield extends BaseEntity implements DrawTrait {
|
||||
public boolean active;
|
||||
public boolean hitPlayers = false;
|
||||
public float radius = 0f;
|
||||
|
||||
private float uptime = 0f;
|
||||
private final Tile tile;
|
||||
|
||||
public Shield(Tile tile){
|
||||
this.tile = tile;
|
||||
this.x = tile.worldx();
|
||||
this.y = tile.worldy();
|
||||
}
|
||||
|
||||
public float drawSize(){
|
||||
return 150;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(){
|
||||
float alpha = 0.1f;
|
||||
Interpolation interp = Interpolation.fade;
|
||||
|
||||
if(active){
|
||||
uptime = interp.apply(uptime, 1f, alpha * Timers.delta());
|
||||
}else{
|
||||
uptime = interp.apply(uptime, 0f, alpha * Timers.delta());
|
||||
if(uptime <= 0.05f)
|
||||
remove();
|
||||
}
|
||||
uptime = Mathf.clamp(uptime);
|
||||
|
||||
if(!(tile.block() instanceof ShieldBlock)){
|
||||
remove();
|
||||
return;
|
||||
}
|
||||
|
||||
ShieldBlock block = (ShieldBlock)tile.block();
|
||||
public class Shield extends BaseEntity implements DrawTrait{
|
||||
private final Tile tile;
|
||||
public boolean active;
|
||||
public boolean hitPlayers = false;
|
||||
public float radius = 0f;
|
||||
private float uptime = 0f;
|
||||
|
||||
public Shield(Tile tile){
|
||||
this.tile = tile;
|
||||
this.x = tile.worldx();
|
||||
this.y = tile.worldy();
|
||||
}
|
||||
|
||||
public float drawSize(){
|
||||
return 150;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(){
|
||||
float alpha = 0.1f;
|
||||
Interpolation interp = Interpolation.fade;
|
||||
|
||||
if(active){
|
||||
uptime = interp.apply(uptime, 1f, alpha * Timers.delta());
|
||||
}else{
|
||||
uptime = interp.apply(uptime, 0f, alpha * Timers.delta());
|
||||
if(uptime <= 0.05f)
|
||||
remove();
|
||||
}
|
||||
uptime = Mathf.clamp(uptime);
|
||||
|
||||
if(!(tile.block() instanceof ShieldBlock)){
|
||||
remove();
|
||||
return;
|
||||
}
|
||||
|
||||
ShieldBlock block = (ShieldBlock) tile.block();
|
||||
|
||||
/*
|
||||
Entities.getNearby(bulletGroup, x, y, block.shieldRadius * 2*uptime + 10, entity->{
|
||||
@@ -64,39 +63,39 @@ public class Shield extends BaseEntity implements DrawTrait {
|
||||
}
|
||||
}
|
||||
});*/
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(){
|
||||
if(!(tile.block() instanceof ShieldBlock) || radius <= 1f){
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Fill.circle(x, y, drawRadius());
|
||||
}
|
||||
|
||||
float drawRadius(){
|
||||
return (radius + Mathf.sin(Timers.time(), 25f, 1f));
|
||||
}
|
||||
|
||||
public void removeDelay(){
|
||||
active = false;
|
||||
}
|
||||
@Override
|
||||
public void draw(){
|
||||
if(!(tile.block() instanceof ShieldBlock) || radius <= 1f){
|
||||
return;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityGroup targetGroup() {
|
||||
return shieldGroup;
|
||||
}
|
||||
Fill.circle(x, y, drawRadius());
|
||||
}
|
||||
|
||||
float drawRadius(){
|
||||
return (radius + Mathf.sin(Timers.time(), 25f, 1f));
|
||||
}
|
||||
|
||||
public void removeDelay(){
|
||||
active = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityGroup targetGroup(){
|
||||
return shieldGroup;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void added(){
|
||||
active = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removed(){
|
||||
active = false;
|
||||
uptime = 0f;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void added(){
|
||||
active = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removed(){
|
||||
active = false;
|
||||
uptime = 0f;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package io.anuke.mindustry.entities.traits;
|
||||
|
||||
/**A flag interface for marking an effect as appearing below liquids.*/
|
||||
public interface BelowLiquidTrait {
|
||||
/**
|
||||
* A flag interface for marking an effect as appearing below liquids.
|
||||
*/
|
||||
public interface BelowLiquidTrait{
|
||||
}
|
||||
|
||||
@@ -37,27 +37,46 @@ import static io.anuke.mindustry.Vars.tilesize;
|
||||
import static io.anuke.mindustry.Vars.tmptr;
|
||||
import static io.anuke.mindustry.Vars.world;
|
||||
|
||||
/**Interface for units that build, break or mine things.*/
|
||||
/**
|
||||
* Interface for units that build, break or mine things.
|
||||
*/
|
||||
public interface BuilderTrait extends Entity{
|
||||
//these are not instance variables!
|
||||
float placeDistance = 140f;
|
||||
float mineDistance = 70f;
|
||||
|
||||
/**Returns the queue for storing build requests.*/
|
||||
/**
|
||||
* Returns the queue for storing build requests.
|
||||
*/
|
||||
Queue<BuildRequest> getPlaceQueue();
|
||||
|
||||
/**Returns the tile this builder is currently mining.*/
|
||||
/**
|
||||
* Returns the tile this builder is currently mining.
|
||||
*/
|
||||
Tile getMineTile();
|
||||
|
||||
/**Sets the tile this builder is currently mining.*/
|
||||
/**
|
||||
* Sets the tile this builder is currently mining.
|
||||
*/
|
||||
void setMineTile(Tile tile);
|
||||
|
||||
/**Returns the minining speed of this miner. 1 = standard, 0.5 = half speed, 2 = double speed, etc.*/
|
||||
/**
|
||||
* Returns the minining speed of this miner. 1 = standard, 0.5 = half speed, 2 = double speed, etc.
|
||||
*/
|
||||
float getMinePower();
|
||||
|
||||
/**Build power, can be any float. 1 = builds recipes in normal time, 0 = doesn't build at all.*/
|
||||
/**
|
||||
* Build power, can be any float. 1 = builds recipes in normal time, 0 = doesn't build at all.
|
||||
*/
|
||||
float getBuildPower(Tile tile);
|
||||
|
||||
/**
|
||||
* Whether this type of builder can begin creating new blocks.
|
||||
*/
|
||||
default boolean canCreateBlocks(){
|
||||
return true;
|
||||
}
|
||||
|
||||
default void writeBuilding(DataOutput output) throws IOException{
|
||||
BuildRequest request = getCurrentRequest();
|
||||
|
||||
@@ -78,17 +97,17 @@ public interface BuilderTrait extends Entity{
|
||||
}
|
||||
|
||||
default void readBuilding(DataInput input, boolean applyChanges) throws IOException{
|
||||
synchronized (getPlaceQueue()) {
|
||||
synchronized(getPlaceQueue()){
|
||||
if(applyChanges) getPlaceQueue().clear();
|
||||
|
||||
byte type = input.readByte();
|
||||
if (type != -1) {
|
||||
if(type != -1){
|
||||
int position = input.readInt();
|
||||
BuildRequest request;
|
||||
|
||||
if (type == 1) { //remove
|
||||
if(type == 1){ //remove
|
||||
request = new BuildRequest(position % world.width(), position / world.width());
|
||||
} else { //place
|
||||
}else{ //place
|
||||
byte recipe = input.readByte();
|
||||
byte rotation = input.readByte();
|
||||
request = new BuildRequest(position % world.width(), position / world.width(), rotation, Recipe.getByID(recipe));
|
||||
@@ -101,17 +120,21 @@ public interface BuilderTrait extends Entity{
|
||||
}
|
||||
}
|
||||
|
||||
/**Return whether this builder's place queue contains items.*/
|
||||
/**
|
||||
* Return whether this builder's place queue contains items.
|
||||
*/
|
||||
default boolean isBuilding(){
|
||||
return getPlaceQueue().size != 0;
|
||||
}
|
||||
|
||||
/**If a place request matching this signature is present, it is removed.
|
||||
* Otherwise, a new place request is added to the queue.*/
|
||||
/**
|
||||
* If a place request matching this signature is present, it is removed.
|
||||
* Otherwise, a new place request is added to the queue.
|
||||
*/
|
||||
default void replaceBuilding(int x, int y, int rotation, Recipe recipe){
|
||||
synchronized (getPlaceQueue()) {
|
||||
for (BuildRequest request : getPlaceQueue()) {
|
||||
if (request.x == x && request.y == y) {
|
||||
synchronized(getPlaceQueue()){
|
||||
for(BuildRequest request : getPlaceQueue()){
|
||||
if(request.x == x && request.y == y){
|
||||
clearBuilding();
|
||||
addBuildRequest(request);
|
||||
return;
|
||||
@@ -122,16 +145,20 @@ public interface BuilderTrait extends Entity{
|
||||
addBuildRequest(new BuildRequest(x, y, rotation, recipe));
|
||||
}
|
||||
|
||||
/**Clears the placement queue.*/
|
||||
/**
|
||||
* Clears the placement queue.
|
||||
*/
|
||||
default void clearBuilding(){
|
||||
getPlaceQueue().clear();
|
||||
}
|
||||
|
||||
/**Add another build requests to the tail of the queue, if it doesn't exist there yet.*/
|
||||
/**
|
||||
* Add another build requests to the tail of the queue, if it doesn't exist there yet.
|
||||
*/
|
||||
default void addBuildRequest(BuildRequest place){
|
||||
synchronized (getPlaceQueue()) {
|
||||
for (BuildRequest request : getPlaceQueue()) {
|
||||
if (request.x == place.x && request.y == place.y) {
|
||||
synchronized(getPlaceQueue()){
|
||||
for(BuildRequest request : getPlaceQueue()){
|
||||
if(request.x == place.x && request.y == place.y){
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -139,16 +166,20 @@ public interface BuilderTrait extends Entity{
|
||||
}
|
||||
}
|
||||
|
||||
/**Return the build requests currently active, or the one at the top of the queue.
|
||||
* May return null.*/
|
||||
/**
|
||||
* Return the build requests currently active, or the one at the top of the queue.
|
||||
* May return null.
|
||||
*/
|
||||
default BuildRequest getCurrentRequest(){
|
||||
synchronized (getPlaceQueue()) {
|
||||
synchronized(getPlaceQueue()){
|
||||
return getPlaceQueue().size == 0 ? null : getPlaceQueue().first();
|
||||
}
|
||||
}
|
||||
|
||||
/**Update building mechanism for this unit.
|
||||
* This includes mining.*/
|
||||
/**
|
||||
* Update building mechanism for this unit.
|
||||
* This includes mining.
|
||||
*/
|
||||
default void updateBuilding(Unit unit){
|
||||
BuildRequest current = getCurrentRequest();
|
||||
|
||||
@@ -171,10 +202,10 @@ public interface BuilderTrait extends Entity{
|
||||
|
||||
Tile tile = world.tile(current.x, current.y);
|
||||
|
||||
if (!(tile.block() instanceof BuildBlock)) {
|
||||
if(!current.remove && Build.validPlace(unit.getTeam(), current.x, current.y, current.recipe.result, current.rotation)) {
|
||||
if(!(tile.block() instanceof BuildBlock)){
|
||||
if(canCreateBlocks() && !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)){
|
||||
}else if(canCreateBlocks() && current.remove && Build.validBreak(unit.getTeam(), current.x, current.y)){
|
||||
Build.beginBreak(unit.getTeam(), current.x, current.y);
|
||||
}else{
|
||||
getPlaceQueue().removeFirst();
|
||||
@@ -198,7 +229,9 @@ public interface BuilderTrait extends Entity{
|
||||
current.progress = entity.progress();
|
||||
}
|
||||
|
||||
/**Do not call directly.*/
|
||||
/**
|
||||
* Do not call directly.
|
||||
*/
|
||||
default void updateMining(Unit unit){
|
||||
Tile tile = getMineTile();
|
||||
|
||||
@@ -211,26 +244,28 @@ public interface BuilderTrait extends Entity{
|
||||
if(unit.inventory.canAcceptItem(item) &&
|
||||
Mathf.chance(Timers.delta() * (0.06 - item.hardness * 0.01) * getMinePower())){
|
||||
CallEntity.transferItemToUnit(item,
|
||||
tile.worldx() + Mathf.range(tilesize/2f),
|
||||
tile.worldy() + Mathf.range(tilesize/2f),
|
||||
tile.worldx() + Mathf.range(tilesize / 2f),
|
||||
tile.worldy() + Mathf.range(tilesize / 2f),
|
||||
unit);
|
||||
}
|
||||
|
||||
if(Mathf.chance(0.06 * Timers.delta())){
|
||||
Effects.effect(BlockFx.pulverizeSmall,
|
||||
tile.worldx() + Mathf.range(tilesize/2f),
|
||||
tile.worldy() + Mathf.range(tilesize/2f), 0f, item.color);
|
||||
tile.worldx() + Mathf.range(tilesize / 2f),
|
||||
tile.worldy() + Mathf.range(tilesize / 2f), 0f, item.color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**Draw placement effects for an entity. This includes mining*/
|
||||
/**
|
||||
* Draw placement effects for an entity. This includes mining
|
||||
*/
|
||||
default void drawBuilding(Unit unit){
|
||||
BuildRequest request;
|
||||
|
||||
synchronized (getPlaceQueue()) {
|
||||
if (!isBuilding()) {
|
||||
if (getMineTile() != null) {
|
||||
synchronized(getPlaceQueue()){
|
||||
if(!isBuilding()){
|
||||
if(getMineTile() != null){
|
||||
drawMining(unit);
|
||||
}
|
||||
return;
|
||||
@@ -250,7 +285,7 @@ public interface BuilderTrait extends Entity{
|
||||
float px = unit.x + Angles.trnsx(unit.rotation, focusLen);
|
||||
float py = unit.y + Angles.trnsy(unit.rotation, focusLen);
|
||||
|
||||
float sz = Vars.tilesize*tile.block().size/2f;
|
||||
float sz = Vars.tilesize * tile.block().size / 2f;
|
||||
float ang = unit.angleTo(tile);
|
||||
|
||||
tmptr[0].set(tile.drawx() - sz, tile.drawy() - sz);
|
||||
@@ -281,14 +316,16 @@ public interface BuilderTrait extends Entity{
|
||||
Draw.color();
|
||||
}
|
||||
|
||||
/**Internal use only.*/
|
||||
/**
|
||||
* Internal use only.
|
||||
*/
|
||||
default void drawMining(Unit unit){
|
||||
Tile tile = getMineTile();
|
||||
|
||||
if(tile == null) return;
|
||||
|
||||
float focusLen = 4f + Mathf.absin(Timers.time(), 1.1f, 0.5f);
|
||||
float swingScl = 12f, swingMag = tilesize/8f;
|
||||
float swingScl = 12f, swingMag = tilesize / 8f;
|
||||
float flashScl = 0.3f;
|
||||
|
||||
float px = unit.x + Angles.trnsx(unit.rotation, focusLen);
|
||||
@@ -297,10 +334,10 @@ public interface BuilderTrait extends Entity{
|
||||
float ex = tile.worldx() + Mathf.sin(Timers.time() + 48, swingScl, swingMag);
|
||||
float ey = tile.worldy() + Mathf.sin(Timers.time() + 48, swingScl + 2f, swingMag);
|
||||
|
||||
Draw.color(Color.LIGHT_GRAY, Color.WHITE, 1f-flashScl + Mathf.absin(Timers.time(), 0.5f, flashScl));
|
||||
Draw.color(Color.LIGHT_GRAY, Color.WHITE, 1f - flashScl + Mathf.absin(Timers.time(), 0.5f, flashScl));
|
||||
Shapes.laser("minelaser", "minelaser-end", px, py, ex, ey);
|
||||
|
||||
if(unit instanceof Player && ((Player) unit).isLocal) {
|
||||
if(unit instanceof Player && ((Player) unit).isLocal){
|
||||
Draw.color(Palette.accent);
|
||||
Lines.poly(tile.worldx(), tile.worldy(), 4, tilesize / 2f * Mathf.sqrt2, Timers.time());
|
||||
}
|
||||
@@ -308,16 +345,20 @@ public interface BuilderTrait extends Entity{
|
||||
Draw.color();
|
||||
}
|
||||
|
||||
/**Class for storing build requests. Can be either a place or remove request.*/
|
||||
class BuildRequest {
|
||||
/**
|
||||
* Class for storing build requests. Can be either a place or remove request.
|
||||
*/
|
||||
class BuildRequest{
|
||||
public final int x, y, rotation;
|
||||
public final Recipe recipe;
|
||||
public final boolean remove;
|
||||
|
||||
public float progress;
|
||||
|
||||
/**This creates a build request.*/
|
||||
public BuildRequest(int x, int y, int rotation, Recipe recipe) {
|
||||
/**
|
||||
* This creates a build request.
|
||||
*/
|
||||
public BuildRequest(int x, int y, int rotation, Recipe recipe){
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.rotation = rotation;
|
||||
@@ -325,8 +366,10 @@ public interface BuilderTrait extends Entity{
|
||||
this.remove = false;
|
||||
}
|
||||
|
||||
/**This creates a remove request.*/
|
||||
public BuildRequest(int x, int y) {
|
||||
/**
|
||||
* This creates a remove request.
|
||||
*/
|
||||
public BuildRequest(int x, int y){
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.rotation = -1;
|
||||
|
||||
@@ -8,6 +8,7 @@ public interface CarriableTrait extends TeamTrait, TargetTrait, SolidTrait{
|
||||
return getCarrier() != null;
|
||||
}
|
||||
|
||||
void setCarrier(CarryTrait carrier);
|
||||
CarryTrait getCarrier();
|
||||
|
||||
void setCarrier(CarryTrait carrier);
|
||||
}
|
||||
|
||||
@@ -10,28 +10,6 @@ import io.anuke.ucore.core.Effects;
|
||||
import io.anuke.ucore.entities.trait.SolidTrait;
|
||||
|
||||
public interface CarryTrait extends TeamTrait, SolidTrait, TargetTrait{
|
||||
/**Returns the thing this carrier is carrying.*/
|
||||
CarriableTrait getCarry();
|
||||
/**Sets the carrying unit. Internal use only! Use {@link #carry(CarriableTrait)} to set state.*/
|
||||
void setCarry(CarriableTrait unit);
|
||||
/**Returns maximum mass this carrier can carry.*/
|
||||
float getCarryWeight();
|
||||
|
||||
/**Drops the unit that is being carried, if applicable.*/
|
||||
default void dropCarry(){
|
||||
carry(null);
|
||||
}
|
||||
|
||||
default void dropCarryLocal(){
|
||||
setCarryOf(null, this, null);
|
||||
}
|
||||
|
||||
/**Do not override unless absolutely necessary.
|
||||
* Carries a unit. To drop a unit, call with {@code null}.*/
|
||||
default void carry(CarriableTrait unit){
|
||||
CallEntity.setCarryOf(this instanceof Player ? (Player)this : null, this, unit);
|
||||
}
|
||||
|
||||
@Remote(called = Loc.both, targets = Loc.both, forward = true, in = In.entities)
|
||||
static void dropSelf(Player player){
|
||||
if(player.getCarrier() != null){
|
||||
@@ -41,6 +19,7 @@ public interface CarryTrait extends TeamTrait, SolidTrait, TargetTrait{
|
||||
|
||||
@Remote(called = Loc.both, targets = Loc.both, forward = true, in = In.entities)
|
||||
static void setCarryOf(Player player, CarryTrait trait, CarriableTrait unit){
|
||||
if(trait == null) return;
|
||||
if(player != null){ //when a server recieves this called from a player, set the carrier to the player.
|
||||
trait = player;
|
||||
}
|
||||
@@ -61,4 +40,38 @@ public interface CarryTrait extends TeamTrait, SolidTrait, TargetTrait{
|
||||
Effects.effect(UnitFx.unitPickup, trait);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the thing this carrier is carrying.
|
||||
*/
|
||||
CarriableTrait getCarry();
|
||||
|
||||
/**
|
||||
* Sets the carrying unit. Internal use only! Use {@link #carry(CarriableTrait)} to set state.
|
||||
*/
|
||||
void setCarry(CarriableTrait unit);
|
||||
|
||||
/**
|
||||
* Returns maximum mass this carrier can carry.
|
||||
*/
|
||||
float getCarryWeight();
|
||||
|
||||
/**
|
||||
* Drops the unit that is being carried, if applicable.
|
||||
*/
|
||||
default void dropCarry(){
|
||||
carry(null);
|
||||
}
|
||||
|
||||
default void dropCarryLocal(){
|
||||
setCarryOf(null, this, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Do not override unless absolutely necessary.
|
||||
* Carries a unit. To drop a unit, call with {@code null}.
|
||||
*/
|
||||
default void carry(CarriableTrait unit){
|
||||
CallEntity.setCarryOf(this instanceof Player ? (Player) this : null, this, unit);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,6 @@ package io.anuke.mindustry.entities.traits;
|
||||
|
||||
import io.anuke.mindustry.entities.UnitInventory;
|
||||
|
||||
public interface InventoryTrait {
|
||||
public interface InventoryTrait{
|
||||
UnitInventory getInventory();
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ package io.anuke.mindustry.entities.traits;
|
||||
import io.anuke.ucore.entities.trait.HealthTrait;
|
||||
|
||||
//TODO implement
|
||||
public interface RepairTrait extends TeamTrait {
|
||||
public interface RepairTrait extends TeamTrait{
|
||||
|
||||
HealthTrait getRepairing();
|
||||
|
||||
|
||||
@@ -2,6 +2,8 @@ package io.anuke.mindustry.entities.traits;
|
||||
|
||||
import io.anuke.ucore.entities.trait.Entity;
|
||||
|
||||
/**Marks an entity as serializable.*/
|
||||
/**
|
||||
* Marks an entity as serializable.
|
||||
*/
|
||||
public interface SaveTrait extends Entity, TypeTrait, Saveable{
|
||||
}
|
||||
|
||||
@@ -4,7 +4,8 @@ import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
|
||||
public interface Saveable {
|
||||
public interface Saveable{
|
||||
void writeSave(DataOutput stream) throws IOException;
|
||||
|
||||
void readSave(DataInput stream) throws IOException;
|
||||
}
|
||||
|
||||
@@ -7,6 +7,8 @@ import io.anuke.ucore.util.Timer;
|
||||
public interface ShooterTrait extends VelocityTrait, TeamTrait, InventoryTrait{
|
||||
|
||||
Timer getTimer();
|
||||
|
||||
int getShootTimer(boolean left);
|
||||
|
||||
Weapon getWeapon();
|
||||
}
|
||||
|
||||
@@ -3,8 +3,10 @@ package io.anuke.mindustry.entities.traits;
|
||||
import io.anuke.mindustry.entities.Unit;
|
||||
import io.anuke.mindustry.world.Tile;
|
||||
|
||||
public interface SpawnerTrait {
|
||||
public interface SpawnerTrait{
|
||||
Tile getTile();
|
||||
|
||||
void updateSpawning(Unit unit);
|
||||
|
||||
float getSpawnProgress();
|
||||
}
|
||||
|
||||
@@ -10,18 +10,22 @@ import java.io.IOException;
|
||||
|
||||
import static io.anuke.mindustry.Vars.threads;
|
||||
|
||||
public interface SyncTrait extends Entity, TypeTrait {
|
||||
public interface SyncTrait extends Entity, TypeTrait{
|
||||
|
||||
/**Whether smoothing of entities is enabled when using multithreading; not yet implemented.*/
|
||||
/**
|
||||
* Whether smoothing of entities is enabled when using multithreading; not yet implemented.
|
||||
*/
|
||||
static boolean isSmoothing(){
|
||||
return threads.isEnabled() && threads.getTPS() <= Gdx.graphics.getFramesPerSecond() / 2f;
|
||||
}
|
||||
|
||||
/**Sets the position of this entity and updated the interpolator.*/
|
||||
/**
|
||||
* Sets the position of this entity and updated the interpolator.
|
||||
*/
|
||||
default void setNet(float x, float y){
|
||||
set(x, y);
|
||||
|
||||
if(getInterpolator() != null) {
|
||||
if(getInterpolator() != null){
|
||||
getInterpolator().target.set(x, y);
|
||||
getInterpolator().last.set(x, y);
|
||||
getInterpolator().pos.set(0, 0);
|
||||
@@ -30,9 +34,12 @@ public interface SyncTrait extends Entity, TypeTrait {
|
||||
}
|
||||
}
|
||||
|
||||
/**Interpolate entity position only. Override if you need to interpolate rotations or other values.*/
|
||||
/**
|
||||
* Interpolate entity position only. Override if you need to interpolate rotations or other values.
|
||||
*/
|
||||
default void interpolate(){
|
||||
if(getInterpolator() == null) throw new RuntimeException("This entity must have an interpolator to interpolate()!");
|
||||
if(getInterpolator() == null)
|
||||
throw new RuntimeException("This entity must have an interpolator to interpolate()!");
|
||||
|
||||
getInterpolator().update();
|
||||
|
||||
@@ -40,17 +47,22 @@ public interface SyncTrait extends Entity, TypeTrait {
|
||||
setY(getInterpolator().pos.y);
|
||||
}
|
||||
|
||||
/**Return the interpolator used for smoothing the position. Optional.*/
|
||||
/**
|
||||
* Return the interpolator used for smoothing the position. Optional.
|
||||
*/
|
||||
default Interpolator getInterpolator(){
|
||||
return null;
|
||||
}
|
||||
|
||||
/**Whether syncing is enabled for this entity; true by default.*/
|
||||
/**
|
||||
* Whether syncing is enabled for this entity; true by default.
|
||||
*/
|
||||
default boolean isSyncing(){
|
||||
return true;
|
||||
}
|
||||
|
||||
//Read and write sync data, usually position
|
||||
void write(DataOutput data) throws IOException;
|
||||
|
||||
void read(DataInput data, long time) throws IOException;
|
||||
}
|
||||
|
||||
@@ -1,16 +1,21 @@
|
||||
package io.anuke.mindustry.entities.traits;
|
||||
|
||||
import io.anuke.mindustry.game.Team;
|
||||
import io.anuke.ucore.entities.trait.VelocityTrait;
|
||||
import io.anuke.ucore.entities.trait.PosTrait;
|
||||
import io.anuke.ucore.entities.trait.VelocityTrait;
|
||||
|
||||
/**Base interface for targetable entities.*/
|
||||
public interface TargetTrait extends PosTrait, VelocityTrait {
|
||||
/**
|
||||
* Base interface for targetable entities.
|
||||
*/
|
||||
public interface TargetTrait extends PosTrait, VelocityTrait{
|
||||
|
||||
boolean isDead();
|
||||
|
||||
Team getTeam();
|
||||
|
||||
/**Whether this entity is a valid target.*/
|
||||
/**
|
||||
* Whether this entity is a valid target.
|
||||
*/
|
||||
default boolean isValid(){
|
||||
return !isDead();
|
||||
}
|
||||
|
||||
@@ -3,6 +3,6 @@ package io.anuke.mindustry.entities.traits;
|
||||
import io.anuke.mindustry.game.Team;
|
||||
import io.anuke.ucore.entities.trait.Entity;
|
||||
|
||||
public interface TeamTrait extends Entity {
|
||||
public interface TeamTrait extends Entity{
|
||||
Team getTeam();
|
||||
}
|
||||
|
||||
@@ -4,12 +4,14 @@ import com.badlogic.gdx.utils.Array;
|
||||
import com.badlogic.gdx.utils.ObjectIntMap;
|
||||
import io.anuke.ucore.function.Supplier;
|
||||
|
||||
public interface TypeTrait {
|
||||
public interface TypeTrait{
|
||||
int[] lastRegisteredID = {0};
|
||||
Array<Supplier<? extends TypeTrait>> registeredTypes = new Array<>();
|
||||
ObjectIntMap<Class<? extends TypeTrait>> typeToID = new ObjectIntMap<>();
|
||||
|
||||
/**Register and return a type ID. The supplier should return a fresh instace of that type.*/
|
||||
/**
|
||||
* Register and return a type ID. The supplier should return a fresh instace of that type.
|
||||
*/
|
||||
static <T extends TypeTrait> void registerType(Class<T> type, Supplier<T> supplier){
|
||||
if(typeToID.get(type, -1) != -1){
|
||||
throw new RuntimeException("Type is already registered: '" + type + "'!");
|
||||
@@ -18,10 +20,12 @@ public interface TypeTrait {
|
||||
registeredTypes.add(supplier);
|
||||
int result = lastRegisteredID[0];
|
||||
typeToID.put(type, result);
|
||||
lastRegisteredID[0] ++;
|
||||
lastRegisteredID[0]++;
|
||||
}
|
||||
|
||||
/**Registers a syncable type by ID.*/
|
||||
/**
|
||||
* Registers 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()?");
|
||||
@@ -29,11 +33,14 @@ public interface TypeTrait {
|
||||
return registeredTypes.get(id);
|
||||
}
|
||||
|
||||
/**Returns the type ID of this entity used for intstantiation. Should be < BYTE_MAX.
|
||||
* Do not override!*/
|
||||
/**
|
||||
* Returns the type ID of this entity used for intstantiation. Should be < BYTE_MAX.
|
||||
* Do not override!
|
||||
*/
|
||||
default int getTypeID(){
|
||||
int id = typeToID.get(getClass(), -1);
|
||||
if(id == -1) throw new RuntimeException("Class of type '" + getClass() + "' is not registered! Did you forget to register it in ContentLoader#registerTypes()?");
|
||||
if(id == -1)
|
||||
throw new RuntimeException("Class of type '" + getClass() + "' is not registered! Did you forget to register it in ContentLoader#registerTypes()?");
|
||||
return id;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,10 @@ import io.anuke.ucore.core.Effects;
|
||||
import io.anuke.ucore.core.Timers;
|
||||
import io.anuke.ucore.entities.EntityGroup;
|
||||
import io.anuke.ucore.graphics.Draw;
|
||||
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.Timer;
|
||||
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
@@ -39,376 +42,392 @@ import java.io.IOException;
|
||||
import static io.anuke.mindustry.Vars.*;
|
||||
|
||||
public abstract class BaseUnit extends Unit implements ShooterTrait{
|
||||
protected static int timerIndex = 0;
|
||||
protected static int timerIndex = 0;
|
||||
|
||||
protected static final int timerTarget = timerIndex++;
|
||||
protected static final int timerTarget = timerIndex++;
|
||||
|
||||
protected static final int timerShootLeft = timerIndex++;
|
||||
protected static final int timerShootRight = timerIndex++;
|
||||
protected static final int timerShootLeft = timerIndex++;
|
||||
protected static final int timerShootRight = timerIndex++;
|
||||
|
||||
protected UnitType type;
|
||||
protected Timer timer = new Timer(5);
|
||||
protected StateMachine state = new StateMachine();
|
||||
protected TargetTrait target;
|
||||
protected UnitType type;
|
||||
protected Timer timer = new Timer(5);
|
||||
protected StateMachine state = new StateMachine();
|
||||
protected TargetTrait target;
|
||||
|
||||
protected boolean isWave;
|
||||
protected Squad squad;
|
||||
protected int spawner;
|
||||
protected boolean isWave;
|
||||
protected Squad squad;
|
||||
protected int spawner;
|
||||
|
||||
/**Initialize the type and team of this unit. Only call once!*/
|
||||
public void init(UnitType type, Team team){
|
||||
if(this.type != null) throw new RuntimeException("This unit is already initialized!");
|
||||
/**
|
||||
* internal constructor used for deserialization, DO NOT USE
|
||||
*/
|
||||
public BaseUnit(){
|
||||
}
|
||||
|
||||
this.type = type;
|
||||
this.team = team;
|
||||
}
|
||||
@Remote(called = Loc.server, in = In.entities)
|
||||
public static void onUnitDeath(BaseUnit unit){
|
||||
if(unit == null) return;
|
||||
|
||||
public void setSpawner(Tile tile) {
|
||||
this.spawner = tile.packedPosition();
|
||||
}
|
||||
if(Net.server() || !Net.active()){
|
||||
UnitDrops.dropItems(unit);
|
||||
}
|
||||
|
||||
public UnitType getType() {
|
||||
return type;
|
||||
}
|
||||
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);
|
||||
Damage.dynamicExplosion(unit.x, unit.y, flammability, explosiveness, 0f, unit.getSize() / 2f, Palette.darkFlame);
|
||||
|
||||
/**internal constructor used for deserialization, DO NOT USE*/
|
||||
public BaseUnit(){}
|
||||
unit.onSuperDeath();
|
||||
|
||||
/**Sets this to a 'wave' unit, which means it has slightly different AI and will not run out of ammo.*/
|
||||
public void setWave(){
|
||||
isWave = true;
|
||||
}
|
||||
ScorchDecal.create(unit.x, unit.y);
|
||||
Effects.effect(ExplosionFx.explosion, unit);
|
||||
Effects.shake(2f, 2f, unit);
|
||||
|
||||
public void setSquad(Squad squad) {
|
||||
this.squad = squad;
|
||||
squad.units ++;
|
||||
}
|
||||
//must run afterwards so the unit's group is not null
|
||||
threads.runDelay(unit::remove);
|
||||
}
|
||||
|
||||
public void rotate(float angle){
|
||||
rotation = Mathf.slerpDelta(rotation, angle, type.rotatespeed);
|
||||
}
|
||||
/**
|
||||
* Initialize the type and team of this unit. Only call once!
|
||||
*/
|
||||
public void init(UnitType type, Team team){
|
||||
if(this.type != null) throw new RuntimeException("This unit is already initialized!");
|
||||
|
||||
public boolean targetHasFlag(BlockFlag flag){
|
||||
return target instanceof TileEntity &&
|
||||
((TileEntity)target).tile.block().flags.contains(flag);
|
||||
}
|
||||
this.type = type;
|
||||
this.team = team;
|
||||
}
|
||||
|
||||
public void updateRespawning(){
|
||||
if(spawner == -1) return;
|
||||
public UnitType getType(){
|
||||
return type;
|
||||
}
|
||||
|
||||
Tile tile = world.tile(spawner);
|
||||
if(tile != null && tile.entity != null){
|
||||
if(tile.entity instanceof SpawnerTrait){
|
||||
((SpawnerTrait) tile.entity).updateSpawning(this);
|
||||
public Tile getSpawner(){
|
||||
return world.tile(spawner);
|
||||
}
|
||||
|
||||
public void setSpawner(Tile tile){
|
||||
this.spawner = tile.packedPosition();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets this to a 'wave' unit, which means it has slightly different AI and will not run out of ammo.
|
||||
*/
|
||||
public void setWave(){
|
||||
isWave = true;
|
||||
}
|
||||
|
||||
public void setSquad(Squad squad){
|
||||
this.squad = squad;
|
||||
squad.units++;
|
||||
}
|
||||
|
||||
public void rotate(float angle){
|
||||
rotation = Mathf.slerpDelta(rotation, angle, type.rotatespeed);
|
||||
}
|
||||
|
||||
public boolean targetHasFlag(BlockFlag flag){
|
||||
return target instanceof TileEntity &&
|
||||
((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);
|
||||
}
|
||||
|
||||
public void retarget(Runnable run){
|
||||
if(timer.get(timerTarget, 20)){
|
||||
run.run();
|
||||
}
|
||||
}
|
||||
|
||||
/**Only runs when the unit has a target.*/
|
||||
public void behavior(){
|
||||
|
||||
}
|
||||
|
||||
public void updateTargeting(){
|
||||
if(target == null || (target instanceof Unit && (target.isDead() || target.getTeam() == team))
|
||||
|| (target instanceof TileEntity && ((TileEntity) target).tile.entity == null)){
|
||||
target = null;
|
||||
}
|
||||
}
|
||||
public void targetClosestAllyFlag(BlockFlag flag){
|
||||
Tile target = Geometry.findClosest(x, y, world.indexer().getAllied(team, flag));
|
||||
if (target != null) this.target = target.entity;
|
||||
}
|
||||
|
||||
public void targetClosestEnemyFlag(BlockFlag flag){
|
||||
Tile target = Geometry.findClosest(x, y, world.indexer().getEnemy(team, flag));
|
||||
if (target != null) this.target = target.entity;
|
||||
}
|
||||
|
||||
public void targetClosest(){
|
||||
target = Units.getClosestTarget(team, x, y, inventory.getAmmoRange());
|
||||
}
|
||||
|
||||
public TileEntity getClosestEnemyCore(){
|
||||
if(Vars.state.teams.has(team)){
|
||||
ObjectSet<TeamData> datas = Vars.state.teams.enemyDataOf(team);
|
||||
|
||||
for(TeamData data : datas){
|
||||
Tile tile = Geometry.findClosest(x, y, data.cores);
|
||||
if(tile != null){
|
||||
return tile.entity;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public UnitState getStartState(){
|
||||
return null;
|
||||
}
|
||||
|
||||
protected void drawItems(){
|
||||
float backTrns = 4f, itemSize = 5f;
|
||||
if(inventory.hasItem()){
|
||||
ItemStack stack = inventory.getItem();
|
||||
int stored = Mathf.clamp(stack.amount / 6, 1, 8);
|
||||
|
||||
for(int i = 0; i < stored; i ++) {
|
||||
float angT = i == 0 ? 0 : Mathf.randomSeedRange(i + 2, 60f);
|
||||
float lenT = i == 0 ? 0 : Mathf.randomSeedRange(i + 3, 1f) - 1f;
|
||||
Draw.rect(stack.item.region,
|
||||
x + Angles.trnsx(rotation + 180f + angT, backTrns + lenT),
|
||||
y + Angles.trnsy(rotation + 180f + angT, backTrns + lenT),
|
||||
itemSize, itemSize, rotation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid() {
|
||||
return super.isValid() && isAdded();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Timer getTimer() {
|
||||
return timer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getShootTimer(boolean left) {
|
||||
return left ? timerShootLeft : timerShootRight;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Weapon getWeapon() {
|
||||
return type.weapon;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TextureRegion getIconRegion() {
|
||||
return type.iconRegion;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCapacity() {
|
||||
return type.itemCapacity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAmmoCapacity() {
|
||||
return type.ammoCapacity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInfiniteAmmo() {
|
||||
return isWave;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void interpolate() {
|
||||
super.interpolate();
|
||||
|
||||
if(interpolator.values.length > 0){
|
||||
rotation = interpolator.values[0];
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public float maxHealth() {
|
||||
return type.health;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getArmor() {
|
||||
return type.armor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean acceptsAmmo(Item item) {
|
||||
return getWeapon().getAmmoType(item) != null && inventory.canAcceptAmmo(getWeapon().getAmmoType(item));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addAmmo(Item item) {
|
||||
inventory.addAmmo(getWeapon().getAmmoType(item));
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getSize() {
|
||||
return 8;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getMass() {
|
||||
return type.mass;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFlying() {
|
||||
return type.isFlying;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(){
|
||||
if(hitTime > 0){
|
||||
hitTime -= Timers.delta();
|
||||
}
|
||||
|
||||
if(hitTime < 0) hitTime = 0;
|
||||
|
||||
if(isDead()){
|
||||
updateRespawning();
|
||||
return;
|
||||
}
|
||||
|
||||
if(Net.client()){
|
||||
interpolate();
|
||||
status.update(this);
|
||||
return;
|
||||
}
|
||||
|
||||
avoidOthers(8f);
|
||||
|
||||
if(squad != null){
|
||||
squad.update();
|
||||
}
|
||||
|
||||
updateTargeting();
|
||||
|
||||
state.update();
|
||||
updateVelocityStatus(type.drag, type.maxVelocity);
|
||||
|
||||
if(target != null) behavior();
|
||||
|
||||
if(!isWave) {
|
||||
x = Mathf.clamp(x, 0, world.width() * tilesize);
|
||||
y = Mathf.clamp(y, 0, world.height() * tilesize);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(){
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawUnder(){
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawOver(){
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removed() {
|
||||
Tile tile = world.tile(spawner);
|
||||
|
||||
if(tile != null && tile.entity instanceof UnitFactoryEntity){
|
||||
UnitFactoryEntity factory = (UnitFactoryEntity)tile.entity;
|
||||
factory.hasSpawned = false;
|
||||
}
|
||||
|
||||
spawner = -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float drawSize(){
|
||||
return 14;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDeath(){
|
||||
CallEntity.onUnitDeath(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void added(){
|
||||
hitbox.setSize(type.hitsize);
|
||||
hitboxTile.setSize(type.hitsizeTile);
|
||||
state.set(getStartState());
|
||||
|
||||
health(maxHealth());
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityGroup targetGroup() {
|
||||
return unitGroups[team.ordinal()];
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeSave(DataOutput stream) throws IOException {
|
||||
super.writeSave(stream);
|
||||
stream.writeByte(type.id);
|
||||
stream.writeBoolean(isWave);
|
||||
stream.writeInt(spawner);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readSave(DataInput stream) throws IOException {
|
||||
super.readSave(stream);
|
||||
byte type = stream.readByte();
|
||||
this.isWave = stream.readBoolean();
|
||||
this.spawner = stream.readInt();
|
||||
|
||||
this.type = UnitType.getByID(type);
|
||||
add();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(DataOutput data) throws IOException{
|
||||
super.writeSave(data);
|
||||
data.writeByte(type.id);
|
||||
data.writeInt(spawner);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(DataInput data, long time) throws IOException{
|
||||
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;
|
||||
}
|
||||
|
||||
public void onSuperDeath(){
|
||||
super.onDeath();
|
||||
}
|
||||
|
||||
@Remote(called = Loc.server, in = In.entities)
|
||||
public static void onUnitDeath(BaseUnit unit){
|
||||
if(unit == null) return;
|
||||
|
||||
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);
|
||||
Damage.dynamicExplosion(unit.x, unit.y, flammability, explosiveness, 0f, unit.getSize()/2f, Palette.darkFlame);
|
||||
|
||||
unit.onSuperDeath();
|
||||
|
||||
ScorchDecal.create(unit.x, unit.y);
|
||||
Effects.effect(ExplosionFx.explosion, unit);
|
||||
Effects.shake(2f, 2f, unit);
|
||||
|
||||
//must run afterwards so the unit's group is not null
|
||||
threads.runDelay(unit::remove);
|
||||
}
|
||||
}else{
|
||||
spawner = -1;
|
||||
}
|
||||
}
|
||||
|
||||
public void setState(UnitState state){
|
||||
this.state.set(state);
|
||||
}
|
||||
|
||||
public void retarget(Runnable run){
|
||||
if(timer.get(timerTarget, 20)){
|
||||
run.run();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Only runs when the unit has a target.
|
||||
*/
|
||||
public void behavior(){
|
||||
|
||||
}
|
||||
|
||||
public void updateTargeting(){
|
||||
if(target == null || (target instanceof Unit && (target.isDead() || target.getTeam() == team))
|
||||
|| (target instanceof TileEntity && ((TileEntity) target).tile.entity == null)){
|
||||
target = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void targetClosestAllyFlag(BlockFlag flag){
|
||||
Tile target = Geometry.findClosest(x, y, world.indexer().getAllied(team, flag));
|
||||
if(target != null) this.target = target.entity;
|
||||
}
|
||||
|
||||
public void targetClosestEnemyFlag(BlockFlag flag){
|
||||
Tile target = Geometry.findClosest(x, y, world.indexer().getEnemy(team, flag));
|
||||
if(target != null) this.target = target.entity;
|
||||
}
|
||||
|
||||
public void targetClosest(){
|
||||
target = Units.getClosestTarget(team, x, y, inventory.getAmmoRange());
|
||||
}
|
||||
|
||||
public TileEntity getClosestEnemyCore(){
|
||||
if(Vars.state.teams.has(team)){
|
||||
ObjectSet<TeamData> datas = Vars.state.teams.enemyDataOf(team);
|
||||
|
||||
for(TeamData data : datas){
|
||||
Tile tile = Geometry.findClosest(x, y, data.cores);
|
||||
if(tile != null){
|
||||
return tile.entity;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public UnitState getStartState(){
|
||||
return null;
|
||||
}
|
||||
|
||||
protected void drawItems(){
|
||||
float backTrns = 4f, itemSize = 5f;
|
||||
if(inventory.hasItem()){
|
||||
ItemStack stack = inventory.getItem();
|
||||
int stored = Mathf.clamp(stack.amount / 6, 1, 8);
|
||||
|
||||
for(int i = 0; i < stored; i++){
|
||||
float angT = i == 0 ? 0 : Mathf.randomSeedRange(i + 2, 60f);
|
||||
float lenT = i == 0 ? 0 : Mathf.randomSeedRange(i + 3, 1f) - 1f;
|
||||
Draw.rect(stack.item.region,
|
||||
x + Angles.trnsx(rotation + 180f + angT, backTrns + lenT),
|
||||
y + Angles.trnsy(rotation + 180f + angT, backTrns + lenT),
|
||||
itemSize, itemSize, rotation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid(){
|
||||
return super.isValid() && isAdded();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Timer getTimer(){
|
||||
return timer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getShootTimer(boolean left){
|
||||
return left ? timerShootLeft : timerShootRight;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Weapon getWeapon(){
|
||||
return type.weapon;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TextureRegion getIconRegion(){
|
||||
return type.iconRegion;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCapacity(){
|
||||
return type.itemCapacity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAmmoCapacity(){
|
||||
return type.ammoCapacity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInfiniteAmmo(){
|
||||
return isWave;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void interpolate(){
|
||||
super.interpolate();
|
||||
|
||||
if(interpolator.values.length > 0){
|
||||
rotation = interpolator.values[0];
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public float maxHealth(){
|
||||
return type.health;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getArmor(){
|
||||
return type.armor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean acceptsAmmo(Item item){
|
||||
return getWeapon().getAmmoType(item) != null && inventory.canAcceptAmmo(getWeapon().getAmmoType(item));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addAmmo(Item item){
|
||||
inventory.addAmmo(getWeapon().getAmmoType(item));
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getSize(){
|
||||
return 8;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getMass(){
|
||||
return type.mass;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFlying(){
|
||||
return type.isFlying;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(){
|
||||
if(hitTime > 0){
|
||||
hitTime -= Timers.delta();
|
||||
}
|
||||
|
||||
if(hitTime < 0) hitTime = 0;
|
||||
|
||||
if(isDead()){
|
||||
updateRespawning();
|
||||
return;
|
||||
}
|
||||
|
||||
if(Net.client()){
|
||||
interpolate();
|
||||
status.update(this);
|
||||
return;
|
||||
}
|
||||
|
||||
if(!Net.client()){
|
||||
avoidOthers(8f);
|
||||
}
|
||||
|
||||
if(squad != null){
|
||||
squad.update();
|
||||
}
|
||||
|
||||
updateTargeting();
|
||||
|
||||
state.update();
|
||||
updateVelocityStatus(type.drag, type.maxVelocity);
|
||||
|
||||
if(target != null) behavior();
|
||||
|
||||
if(!isWave){
|
||||
x = Mathf.clamp(x, 0, world.width() * tilesize);
|
||||
y = Mathf.clamp(y, 0, world.height() * tilesize);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(){
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawUnder(){
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawOver(){
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removed(){
|
||||
Tile tile = world.tile(spawner);
|
||||
|
||||
if(tile != null && tile.entity instanceof UnitFactoryEntity){
|
||||
UnitFactoryEntity factory = (UnitFactoryEntity) tile.entity;
|
||||
factory.hasSpawned = false;
|
||||
}
|
||||
|
||||
spawner = -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float drawSize(){
|
||||
return 14;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDeath(){
|
||||
CallEntity.onUnitDeath(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void added(){
|
||||
hitbox.setSize(type.hitsize);
|
||||
hitboxTile.setSize(type.hitsizeTile);
|
||||
state.set(getStartState());
|
||||
|
||||
health(maxHealth());
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityGroup targetGroup(){
|
||||
return unitGroups[team.ordinal()];
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeSave(DataOutput stream) throws IOException{
|
||||
super.writeSave(stream);
|
||||
stream.writeByte(type.id);
|
||||
stream.writeBoolean(isWave);
|
||||
stream.writeInt(spawner);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readSave(DataInput stream) throws IOException{
|
||||
super.readSave(stream);
|
||||
byte type = stream.readByte();
|
||||
this.isWave = stream.readBoolean();
|
||||
this.spawner = stream.readInt();
|
||||
|
||||
this.type = UnitType.getByID(type);
|
||||
add();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(DataOutput data) throws IOException{
|
||||
super.writeSave(data);
|
||||
data.writeByte(type.id);
|
||||
data.writeInt(spawner);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(DataInput data, long time) throws IOException{
|
||||
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;
|
||||
}
|
||||
|
||||
public void onSuperDeath(){
|
||||
super.onDeath();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,28 +27,124 @@ public abstract class FlyingUnit extends BaseUnit implements CarryTrait{
|
||||
protected Trail trail = new Trail(8);
|
||||
protected CarriableTrait carrying;
|
||||
|
||||
protected final UnitState
|
||||
|
||||
resupply = new UnitState(){
|
||||
public void entered(){
|
||||
target = null;
|
||||
}
|
||||
|
||||
public void update(){
|
||||
if(inventory.totalAmmo() + 10 >= inventory.ammoCapacity()){
|
||||
state.set(attack);
|
||||
}else if(!targetHasFlag(BlockFlag.resupplyPoint)){
|
||||
retarget(() -> targetClosestAllyFlag(BlockFlag.resupplyPoint));
|
||||
}else{
|
||||
circle(20f);
|
||||
}
|
||||
}
|
||||
},
|
||||
idle = new UnitState(){
|
||||
public void update(){
|
||||
retarget(() -> {
|
||||
targetClosest();
|
||||
targetClosestEnemyFlag(BlockFlag.target);
|
||||
|
||||
if(target != null){
|
||||
setState(attack);
|
||||
}
|
||||
});
|
||||
|
||||
target = getClosestCore();
|
||||
if(target != null){
|
||||
circle(50f);
|
||||
}
|
||||
velocity.scl(0.8f);
|
||||
}
|
||||
},
|
||||
attack = new UnitState(){
|
||||
public void entered(){
|
||||
target = null;
|
||||
}
|
||||
|
||||
public void update(){
|
||||
if(Units.invalidateTarget(target, team, x, y)){
|
||||
target = null;
|
||||
}
|
||||
|
||||
if(!inventory.hasAmmo()){
|
||||
state.set(resupply);
|
||||
}else if(target == null){
|
||||
retarget(() -> {
|
||||
targetClosest();
|
||||
targetClosestEnemyFlag(BlockFlag.target);
|
||||
targetClosestEnemyFlag(BlockFlag.producer);
|
||||
|
||||
if(target == null){
|
||||
setState(idle);
|
||||
}
|
||||
});
|
||||
}else{
|
||||
attack(150f);
|
||||
|
||||
if((Mathf.angNear(angleTo(target), rotation, 15f) || !inventory.getAmmo().bullet.keepVelocity) //bombers don't care about rotation
|
||||
&& distanceTo(target) < inventory.getAmmo().getRange()){
|
||||
AmmoType ammo = inventory.getAmmo();
|
||||
inventory.useAmmo();
|
||||
|
||||
Vector2 to = Predict.intercept(FlyingUnit.this, target, ammo.bullet.speed);
|
||||
|
||||
getWeapon().update(FlyingUnit.this, to.x, to.y);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
retreat = new UnitState(){
|
||||
public void entered(){
|
||||
target = null;
|
||||
}
|
||||
|
||||
public void update(){
|
||||
if(health >= maxHealth()){
|
||||
state.set(attack);
|
||||
}else if(!targetHasFlag(BlockFlag.repair)){
|
||||
retarget(() -> {
|
||||
Tile target = Geometry.findClosest(x, y, world.indexer().getAllied(team, BlockFlag.repair));
|
||||
if(target != null) FlyingUnit.this.target = target.entity;
|
||||
});
|
||||
}else{
|
||||
circle(20f);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
//instantiation only
|
||||
public FlyingUnit(){
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public CarriableTrait getCarry() {
|
||||
public void drawShadow(){
|
||||
Draw.rect(type.region, x + elevation * elevationScale, y - elevation * elevationScale, rotation - 90);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CarriableTrait getCarry(){
|
||||
return carrying;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCarry(CarriableTrait unit) {
|
||||
public void setCarry(CarriableTrait unit){
|
||||
this.carrying = unit;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getCarryWeight() {
|
||||
public float getCarryWeight(){
|
||||
return type.carryWeight;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update() {
|
||||
public void update(){
|
||||
super.update();
|
||||
|
||||
updateRotation();
|
||||
@@ -59,7 +155,7 @@ public abstract class FlyingUnit extends BaseUnit implements CarryTrait{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw() {
|
||||
public void draw(){
|
||||
Draw.alpha(hitTime / hitDuration);
|
||||
|
||||
Draw.rect(type.name, x, y, rotation - 90);
|
||||
@@ -70,12 +166,12 @@ public abstract class FlyingUnit extends BaseUnit implements CarryTrait{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawOver() {
|
||||
trail.draw(Palette.lightTrail, Palette.lightTrail, 5f);
|
||||
public void drawOver(){
|
||||
trail.draw(Palette.lightTrail, 5f);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void behavior() {
|
||||
public void behavior(){
|
||||
if(health <= health * type.retreatPercent && !isWave &&
|
||||
Geometry.findClosest(x, y, world.indexer().getAllied(team, BlockFlag.repair)) != null){
|
||||
setState(retreat);
|
||||
@@ -93,7 +189,7 @@ public abstract class FlyingUnit extends BaseUnit implements CarryTrait{
|
||||
}
|
||||
|
||||
@Override
|
||||
public float drawSize() {
|
||||
public float drawSize(){
|
||||
return 60;
|
||||
}
|
||||
|
||||
@@ -103,7 +199,7 @@ public abstract class FlyingUnit extends BaseUnit implements CarryTrait{
|
||||
x += Mathf.sin(Timers.time() + id * 999, 25f, 0.07f);
|
||||
y += Mathf.cos(Timers.time() + id * 999, 25f, 0.07f);
|
||||
|
||||
if (velocity.len() <= 0.2f) {
|
||||
if(velocity.len() <= 0.2f){
|
||||
rotation += Mathf.sin(Timers.time() + id * 99, 10f, 8f);
|
||||
}
|
||||
}
|
||||
@@ -122,7 +218,7 @@ public abstract class FlyingUnit extends BaseUnit implements CarryTrait{
|
||||
vec.set(target.getX() - x, target.getY() - y);
|
||||
|
||||
if(vec.len() < circleLength){
|
||||
vec.rotate((circleLength-vec.len())/circleLength * 180f);
|
||||
vec.rotate((circleLength - vec.len()) / circleLength * 180f);
|
||||
}
|
||||
|
||||
vec.setLength(speed * Timers.delta());
|
||||
@@ -135,7 +231,7 @@ public abstract class FlyingUnit extends BaseUnit implements CarryTrait{
|
||||
|
||||
vec.set(target.getX() - x, target.getY() - y);
|
||||
|
||||
float length = Mathf.clamp((distanceTo(target) - circleLength)/100f, -1f, 1f);
|
||||
float length = Mathf.clamp((distanceTo(target) - circleLength) / 100f, -1f, 1f);
|
||||
|
||||
vec.setLength(type.speed * Timers.delta() * length);
|
||||
if(length < 0) vec.rotate(180f);
|
||||
@@ -152,102 +248,11 @@ public abstract class FlyingUnit extends BaseUnit implements CarryTrait{
|
||||
if(diff > 100f && vec.len() < circleLength){
|
||||
vec.setAngle(velocity.angle());
|
||||
}else{
|
||||
vec.setAngle(Mathf.slerpDelta(velocity.angle(), vec.angle(), 0.44f));
|
||||
vec.setAngle(Mathf.slerpDelta(velocity.angle(), vec.angle(), 0.44f));
|
||||
}
|
||||
|
||||
vec.setLength(type.speed*Timers.delta());
|
||||
vec.setLength(type.speed * Timers.delta());
|
||||
|
||||
velocity.add(vec);
|
||||
}
|
||||
|
||||
public final UnitState
|
||||
|
||||
resupply = new UnitState(){
|
||||
public void entered() {
|
||||
target = null;
|
||||
}
|
||||
|
||||
public void update() {
|
||||
if(inventory.totalAmmo() + 10 >= inventory.ammoCapacity()){
|
||||
state.set(attack);
|
||||
}else if(!targetHasFlag(BlockFlag.resupplyPoint)){
|
||||
retarget(() -> targetClosestAllyFlag(BlockFlag.resupplyPoint));
|
||||
}else{
|
||||
circle(20f);
|
||||
}
|
||||
}
|
||||
},
|
||||
idle = new UnitState() {
|
||||
public void update() {
|
||||
retarget(() -> {
|
||||
targetClosest();
|
||||
targetClosestEnemyFlag(BlockFlag.target);
|
||||
|
||||
if(target != null){
|
||||
setState(attack);
|
||||
}
|
||||
});
|
||||
|
||||
target = getClosestCore();
|
||||
if(target != null){
|
||||
circle(50f);
|
||||
}
|
||||
velocity.scl(0.8f);
|
||||
}
|
||||
},
|
||||
attack = new UnitState(){
|
||||
public void entered() {
|
||||
target = null;
|
||||
}
|
||||
|
||||
public void update() {
|
||||
if(Units.invalidateTarget(target, team, x, y)){
|
||||
target = null;
|
||||
}
|
||||
|
||||
if(!inventory.hasAmmo()) {
|
||||
state.set(resupply);
|
||||
}else if (target == null){
|
||||
retarget(() -> {
|
||||
targetClosest();
|
||||
targetClosestEnemyFlag(BlockFlag.target);
|
||||
targetClosestEnemyFlag(BlockFlag.producer);
|
||||
|
||||
if(target == null){
|
||||
setState(idle);
|
||||
}
|
||||
});
|
||||
}else{
|
||||
attack(150f);
|
||||
|
||||
if ((Mathf.angNear(angleTo(target), rotation, 15f) || !inventory.getAmmo().bullet.keepVelocity) //bombers don't care about rotation
|
||||
&& distanceTo(target) < inventory.getAmmo().getRange()) {
|
||||
AmmoType ammo = inventory.getAmmo();
|
||||
inventory.useAmmo();
|
||||
|
||||
Vector2 to = Predict.intercept(FlyingUnit.this, target, ammo.bullet.speed);
|
||||
|
||||
getWeapon().update(FlyingUnit.this, to.x, to.y);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
retreat = new UnitState() {
|
||||
public void entered() {
|
||||
target = null;
|
||||
}
|
||||
|
||||
public void update() {
|
||||
if(health >= maxHealth()){
|
||||
state.set(attack);
|
||||
}else if(!targetHasFlag(BlockFlag.repair)){
|
||||
retarget(() -> {
|
||||
Tile target = Geometry.findClosest(x, y, world.indexer().getAllied(team, BlockFlag.repair));
|
||||
if (target != null) FlyingUnit.this.target = target.entity;
|
||||
});
|
||||
}else{
|
||||
circle(20f);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -26,24 +26,101 @@ import java.io.IOException;
|
||||
|
||||
import static io.anuke.mindustry.Vars.world;
|
||||
|
||||
public abstract class GroundUnit extends BaseUnit {
|
||||
public abstract class GroundUnit extends BaseUnit{
|
||||
protected static Translator vec = new Translator();
|
||||
|
||||
protected float walkTime;
|
||||
protected float baseRotation;
|
||||
public final UnitState
|
||||
|
||||
resupply = new UnitState(){
|
||||
public void entered(){
|
||||
target = null;
|
||||
}
|
||||
|
||||
public void update(){
|
||||
Tile tile = Geometry.findClosest(x, y, world.indexer().getAllied(team, BlockFlag.resupplyPoint));
|
||||
|
||||
if(tile != null && distanceTo(tile) > 40){
|
||||
moveAwayFromCore();
|
||||
}
|
||||
|
||||
//TODO move toward resupply point
|
||||
if(isWave || inventory.totalAmmo() + 10 >= inventory.ammoCapacity()){
|
||||
state.set(attack);
|
||||
}
|
||||
}
|
||||
},
|
||||
attack = new UnitState(){
|
||||
public void entered(){
|
||||
target = null;
|
||||
}
|
||||
|
||||
public void update(){
|
||||
TileEntity core = getClosestEnemyCore();
|
||||
float dst = core == null ? 0 : distanceTo(core);
|
||||
|
||||
if(core != null && inventory.hasAmmo() && dst < inventory.getAmmo().getRange() / 1.1f){
|
||||
target = core;
|
||||
}else{
|
||||
retarget(() -> targetClosest());
|
||||
}
|
||||
|
||||
if(!inventory.hasAmmo()){
|
||||
state.set(resupply);
|
||||
}else if(target != null){
|
||||
if(core != null){
|
||||
if(dst > inventory.getAmmo().getRange() * 0.5f){
|
||||
moveToCore();
|
||||
}
|
||||
|
||||
}else{
|
||||
moveToCore();
|
||||
}
|
||||
|
||||
if(distanceTo(target) < inventory.getAmmo().getRange()){
|
||||
rotate(angleTo(target));
|
||||
|
||||
if(Mathf.angNear(angleTo(target), rotation, 13f)){
|
||||
AmmoType ammo = inventory.getAmmo();
|
||||
|
||||
Vector2 to = Predict.intercept(GroundUnit.this, target, ammo.bullet.speed);
|
||||
|
||||
getWeapon().update(GroundUnit.this, to.x, to.y);
|
||||
}
|
||||
}
|
||||
|
||||
}else{
|
||||
moveToCore();
|
||||
}
|
||||
}
|
||||
},
|
||||
retreat = new UnitState(){
|
||||
public void entered(){
|
||||
target = null;
|
||||
}
|
||||
|
||||
public void update(){
|
||||
if(health >= health){
|
||||
state.set(attack);
|
||||
}
|
||||
|
||||
moveAwayFromCore();
|
||||
}
|
||||
};
|
||||
protected Weapon weapon;
|
||||
|
||||
@Override
|
||||
public void init(UnitType type, Team team) {
|
||||
public void init(UnitType type, Team team){
|
||||
super.init(type, team);
|
||||
this.weapon = type.weapon;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void interpolate() {
|
||||
public void interpolate(){
|
||||
super.interpolate();
|
||||
|
||||
if(interpolator.values.length > 1) {
|
||||
if(interpolator.values.length > 1){
|
||||
baseRotation = interpolator.values[1];
|
||||
}
|
||||
}
|
||||
@@ -57,12 +134,12 @@ public abstract class GroundUnit extends BaseUnit {
|
||||
}
|
||||
|
||||
@Override
|
||||
public UnitState getStartState() {
|
||||
public UnitState getStartState(){
|
||||
return resupply;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update() {
|
||||
public void update(){
|
||||
super.update();
|
||||
|
||||
if(!velocity.isZero(0.0001f) && (target == null || !inventory.hasAmmo() || (inventory.hasAmmo() && distanceTo(target) > inventory.getAmmoRange()))){
|
||||
@@ -71,12 +148,16 @@ public abstract class GroundUnit extends BaseUnit {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Weapon getWeapon() {
|
||||
public Weapon getWeapon(){
|
||||
return weapon;
|
||||
}
|
||||
|
||||
public void setWeapon(Weapon weapon){
|
||||
this.weapon = weapon;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw() {
|
||||
public void draw(){
|
||||
Draw.alpha(hitTime / hitDuration);
|
||||
|
||||
float walktime = walkTime;
|
||||
@@ -89,25 +170,25 @@ public abstract class GroundUnit extends BaseUnit {
|
||||
Draw.tint(Color.WHITE, floor.liquidColor, 0.5f);
|
||||
}
|
||||
|
||||
for (int i : Mathf.signs) {
|
||||
for(int i : Mathf.signs){
|
||||
Draw.rect(type.legRegion,
|
||||
x + Angles.trnsx(baseRotation, ft * i),
|
||||
y + Angles.trnsy(baseRotation, ft * i),
|
||||
12f * i, 12f - Mathf.clamp(ft * i, 0, 2), baseRotation - 90);
|
||||
}
|
||||
|
||||
if(floor.isLiquid) {
|
||||
if(floor.isLiquid){
|
||||
Draw.tint(Color.WHITE, floor.liquidColor, drownTime * 0.4f);
|
||||
}else {
|
||||
}else{
|
||||
Draw.tint(Color.WHITE);
|
||||
}
|
||||
|
||||
Draw.rect(type.baseRegion, x, y, baseRotation- 90);
|
||||
Draw.rect(type.baseRegion, x, y, baseRotation - 90);
|
||||
|
||||
Draw.rect(type.region, x, y, rotation -90);
|
||||
Draw.rect(type.region, x, y, rotation - 90);
|
||||
|
||||
for (int i : Mathf.signs) {
|
||||
float tra = rotation - 90, trY = - weapon.getRecoil(this, i > 0) + type.weaponOffsetY;
|
||||
for(int i : Mathf.signs){
|
||||
float tra = rotation - 90, trY = -weapon.getRecoil(this, i > 0) + type.weaponOffsetY;
|
||||
float w = i > 0 ? -12 : 12;
|
||||
Draw.rect(weapon.equipRegion,
|
||||
x + Angles.trnsx(tra, type.weaponOffsetX * i, trY),
|
||||
@@ -120,35 +201,35 @@ public abstract class GroundUnit extends BaseUnit {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void behavior() {
|
||||
public void behavior(){
|
||||
if(health <= health * type.retreatPercent && !isWave){
|
||||
setState(retreat);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateTargeting() {
|
||||
public void updateTargeting(){
|
||||
super.updateTargeting();
|
||||
|
||||
if(Units.invalidateTarget(target, team, x, y, Float.MAX_VALUE)){
|
||||
if(Units.invalidateTarget(target, team, x, y, Float.MAX_VALUE)){
|
||||
target = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(DataOutput data) throws IOException {
|
||||
public void write(DataOutput data) throws IOException{
|
||||
super.write(data);
|
||||
data.writeByte(weapon.id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(DataInput data, long time) throws IOException {
|
||||
public void read(DataInput data, long time) throws IOException{
|
||||
super.read(data, time);
|
||||
weapon = Upgrade.getByID(data.readByte());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeSave(DataOutput stream) throws IOException {
|
||||
public void writeSave(DataOutput stream) throws IOException{
|
||||
stream.writeByte(weapon.id);
|
||||
super.writeSave(stream);
|
||||
}
|
||||
@@ -159,10 +240,6 @@ public abstract class GroundUnit extends BaseUnit {
|
||||
super.readSave(stream);
|
||||
}
|
||||
|
||||
public void setWeapon(Weapon weapon){
|
||||
this.weapon = weapon;
|
||||
}
|
||||
|
||||
protected void moveToCore(){
|
||||
Tile tile = world.tileWorld(x, y);
|
||||
if(tile == null) return;
|
||||
@@ -189,82 +266,4 @@ public abstract class GroundUnit extends BaseUnit {
|
||||
walkTime += Timers.delta();
|
||||
velocity.add(vec);
|
||||
}
|
||||
|
||||
public final UnitState
|
||||
|
||||
resupply = new UnitState(){
|
||||
public void entered() {
|
||||
target = null;
|
||||
}
|
||||
|
||||
public void update() {
|
||||
Tile tile = Geometry.findClosest(x, y, world.indexer().getAllied(team, BlockFlag.resupplyPoint));
|
||||
|
||||
if (tile != null && distanceTo(tile) > 40) {
|
||||
moveAwayFromCore();
|
||||
}
|
||||
|
||||
//TODO move toward resupply point
|
||||
if(isWave || inventory.totalAmmo() + 10 >= inventory.ammoCapacity()){
|
||||
state.set(attack);
|
||||
}
|
||||
}
|
||||
},
|
||||
attack = new UnitState(){
|
||||
public void entered() {
|
||||
target = null;
|
||||
}
|
||||
|
||||
public void update() {
|
||||
TileEntity core = getClosestEnemyCore();
|
||||
float dst = core == null ? 0 :distanceTo(core);
|
||||
|
||||
if(core != null && inventory.hasAmmo() && dst < inventory.getAmmo().getRange()/1.1f){
|
||||
target = core;
|
||||
}else {
|
||||
retarget(() -> targetClosest());
|
||||
}
|
||||
|
||||
if(!inventory.hasAmmo()) {
|
||||
state.set(resupply);
|
||||
}else if(target != null){
|
||||
if(core != null){
|
||||
if(dst > inventory.getAmmo().getRange() * 0.5f){
|
||||
moveToCore();
|
||||
}
|
||||
|
||||
}else{
|
||||
moveToCore();
|
||||
}
|
||||
|
||||
if(distanceTo(target) < inventory.getAmmo().getRange()){
|
||||
rotate(angleTo(target));
|
||||
|
||||
if (Mathf.angNear(angleTo(target), rotation, 13f)) {
|
||||
AmmoType ammo = inventory.getAmmo();
|
||||
|
||||
Vector2 to = Predict.intercept(GroundUnit.this, target, ammo.bullet.speed);
|
||||
|
||||
getWeapon().update(GroundUnit.this, to.x, to.y);
|
||||
}
|
||||
}
|
||||
|
||||
}else{
|
||||
moveToCore();
|
||||
}
|
||||
}
|
||||
},
|
||||
retreat = new UnitState() {
|
||||
public void entered() {
|
||||
target = null;
|
||||
}
|
||||
|
||||
public void update() {
|
||||
if(health >= health){
|
||||
state.set(attack);
|
||||
}
|
||||
|
||||
moveAwayFromCore();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -5,9 +5,11 @@ import io.anuke.ucore.util.Translator;
|
||||
|
||||
import static io.anuke.mindustry.Vars.threads;
|
||||
|
||||
/**Used to group entities together, for formations and such.
|
||||
* Usually, squads are used by units spawned in the same wave.*/
|
||||
public class Squad {
|
||||
/**
|
||||
* Used to group entities together, for formations and such.
|
||||
* Usually, squads are used by units spawned in the same wave.
|
||||
*/
|
||||
public class Squad{
|
||||
public Vector2 direction = new Translator();
|
||||
public int units;
|
||||
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
package io.anuke.mindustry.entities.units;
|
||||
|
||||
public class StateMachine {
|
||||
public class StateMachine{
|
||||
private UnitState state;
|
||||
|
||||
public void update(){
|
||||
if(state != null) state.update();
|
||||
}
|
||||
|
||||
public void set( UnitState next){
|
||||
public void set(UnitState next){
|
||||
if(next == state) return;
|
||||
if(state != null) state.exited();
|
||||
this.state = next;
|
||||
|
||||
@@ -6,7 +6,7 @@ import io.anuke.mindustry.entities.effect.ItemDrop;
|
||||
import io.anuke.mindustry.type.Item;
|
||||
import io.anuke.ucore.util.Mathf;
|
||||
|
||||
public class UnitDrops {
|
||||
public class UnitDrops{
|
||||
private static final int maxItems = 200;
|
||||
private static Item[] dropTable;
|
||||
|
||||
@@ -19,7 +19,7 @@ public class UnitDrops {
|
||||
dropTable = new Item[]{Items.tungsten, Items.lead, Items.carbide};
|
||||
}
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
for(int i = 0; i < 3; i++){
|
||||
for(Item item : dropTable){
|
||||
if(Mathf.chance(0.03)){
|
||||
int amount = Mathf.random(20, 40);
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
package io.anuke.mindustry.entities.units;
|
||||
|
||||
public interface UnitState {
|
||||
default void entered(){}
|
||||
default void exited(){}
|
||||
default void update(){}
|
||||
public interface UnitState{
|
||||
default void entered(){
|
||||
}
|
||||
|
||||
default void exited(){
|
||||
}
|
||||
|
||||
default void update(){
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,12 +18,9 @@ import io.anuke.ucore.util.Bundles;
|
||||
public class UnitType implements UnlockableContent{
|
||||
private static byte lastid = 0;
|
||||
private static Array<UnitType> types = new Array<>();
|
||||
|
||||
protected final Supplier<? extends BaseUnit> constructor;
|
||||
|
||||
public final String name;
|
||||
public final byte id;
|
||||
|
||||
protected final Supplier<? extends BaseUnit> constructor;
|
||||
public float health = 60;
|
||||
public float hitsize = 5f;
|
||||
public float hitsizeTile = 4f;
|
||||
@@ -57,44 +54,52 @@ public class UnitType implements UnlockableContent{
|
||||
TypeTrait.registerType(type, mainConstructor);
|
||||
}
|
||||
|
||||
public static UnitType getByID(byte id){
|
||||
return types.get(id);
|
||||
}
|
||||
|
||||
public static Array<UnitType> all(){
|
||||
return types;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void displayInfo(Table table) {
|
||||
public void displayInfo(Table table){
|
||||
ContentDisplay.displayUnit(table, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String localizedName() {
|
||||
public String localizedName(){
|
||||
return Bundles.get("unit." + name + ".name");
|
||||
}
|
||||
|
||||
@Override
|
||||
public TextureRegion getContentIcon() {
|
||||
public TextureRegion getContentIcon(){
|
||||
return iconRegion;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load() {
|
||||
public void load(){
|
||||
iconRegion = Draw.region("unit-icon-" + name);
|
||||
region = Draw.region(name);
|
||||
|
||||
if(!isFlying) {
|
||||
if(!isFlying){
|
||||
legRegion = Draw.region(name + "-leg");
|
||||
baseRegion = Draw.region(name + "-base");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getContentTypeName() {
|
||||
public String getContentTypeName(){
|
||||
return "unit-type";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getContentName() {
|
||||
public String getContentName(){
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Array<? extends Content> getAll() {
|
||||
public Array<? extends Content> getAll(){
|
||||
return types;
|
||||
}
|
||||
|
||||
@@ -103,12 +108,4 @@ public class UnitType implements UnlockableContent{
|
||||
unit.init(this, team);
|
||||
return unit;
|
||||
}
|
||||
|
||||
public static UnitType getByID(byte id){
|
||||
return types.get(id);
|
||||
}
|
||||
|
||||
public static Array<UnitType> all(){
|
||||
return types;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ import io.anuke.mindustry.entities.units.UnitState;
|
||||
import io.anuke.mindustry.game.EventType.BlockBuildEvent;
|
||||
import io.anuke.mindustry.gen.CallEntity;
|
||||
import io.anuke.mindustry.graphics.Palette;
|
||||
import io.anuke.mindustry.net.Net;
|
||||
import io.anuke.mindustry.type.Item;
|
||||
import io.anuke.mindustry.type.ItemStack;
|
||||
import io.anuke.mindustry.world.Tile;
|
||||
@@ -39,7 +40,7 @@ import java.io.IOException;
|
||||
|
||||
import static io.anuke.mindustry.Vars.*;
|
||||
|
||||
public class Drone extends FlyingUnit implements BuilderTrait {
|
||||
public class Drone extends FlyingUnit implements BuilderTrait{
|
||||
protected static ObjectSet<Item> toMine;
|
||||
protected static float discoverRange = 120f;
|
||||
protected static boolean initialized;
|
||||
@@ -48,189 +49,18 @@ public class Drone extends FlyingUnit implements BuilderTrait {
|
||||
protected Tile mineTile;
|
||||
protected Queue<BuildRequest> placeQueue = new ThreadQueue<>();
|
||||
|
||||
/**Initialize placement event notifier system.
|
||||
* Static initialization is to be avoided, thus, this is done lazily.*/
|
||||
private static void initEvents(){
|
||||
if(initialized) return;
|
||||
|
||||
toMine = ObjectSet.with(Items.lead, Items.tungsten);
|
||||
|
||||
Events.on(BlockBuildEvent.class, (team, tile) -> {
|
||||
EntityGroup<BaseUnit> group = unitGroups[team.ordinal()];
|
||||
|
||||
if(!(tile.entity instanceof BuildEntity)) return;
|
||||
BuildEntity entity = tile.entity();
|
||||
|
||||
for(BaseUnit unit : group.all()){
|
||||
if(unit instanceof Drone){
|
||||
((Drone) unit).notifyPlaced(entity);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
{
|
||||
initEvents();
|
||||
}
|
||||
|
||||
private void notifyPlaced(BuildEntity entity){
|
||||
float timeToBuild = entity.recipe.cost;
|
||||
float dist = Math.min(entity.distanceTo(x, y) - placeDistance, 0);
|
||||
|
||||
if(dist / type.maxVelocity < timeToBuild * 0.9f){
|
||||
//CallEntity.onDroneBeginBuild(this, entity.tile, entity.recipe);
|
||||
target = entity;
|
||||
setState(build);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getBuildPower(Tile tile) {
|
||||
return type.buildPower;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getMinePower() {
|
||||
return type.minePower;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Queue<BuildRequest> getPlaceQueue() {
|
||||
return placeQueue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Tile getMineTile() {
|
||||
return mineTile;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMineTile(Tile tile) {
|
||||
mineTile = tile;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update() {
|
||||
super.update();
|
||||
|
||||
x += Mathf.sin(Timers.time() + id * 999, 25f, 0.07f);
|
||||
y += Mathf.cos(Timers.time() + id * 999, 25f, 0.07f);
|
||||
|
||||
updateBuilding(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateRotation() {
|
||||
if(target != null && (state.is(repair) || state.is(mine))){
|
||||
rotation = Mathf.slerpDelta(rotation, angleTo(target), 0.3f);
|
||||
}else{
|
||||
rotation = Mathf.slerpDelta(rotation, velocity.angle(), 0.3f);
|
||||
}
|
||||
|
||||
if(velocity.len() <= 0.2f && !(state.is(repair) && target != null)){
|
||||
rotation += Mathf.sin(Timers.time() + id * 99, 10f, 5f);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void behavior() {
|
||||
if(health <= health * type.retreatPercent &&
|
||||
Geometry.findClosest(x, y, world.indexer().getAllied(team, BlockFlag.repair)) != null){
|
||||
setState(retreat);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public UnitState getStartState() {
|
||||
return repair;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawOver() {
|
||||
trail.draw(Palette.lightTrail, Palette.lightTrail, 3f);
|
||||
|
||||
TargetTrait entity = target;
|
||||
|
||||
if(entity instanceof TileEntity && state.is(repair)){
|
||||
float len = 5f;
|
||||
Draw.color(Color.BLACK, Color.WHITE, 0.95f + Mathf.absin(Timers.time(), 0.8f, 0.05f));
|
||||
Shapes.laser("beam", "beam-end",
|
||||
x + Angles.trnsx(rotation, len),
|
||||
y + Angles.trnsy(rotation, len),
|
||||
entity.getX(), entity.getY());
|
||||
Draw.color();
|
||||
}
|
||||
|
||||
drawBuilding(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float drawSize() {
|
||||
return isBuilding() ? placeDistance*2f : 30f;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getAmmoFraction() {
|
||||
return inventory.getItem().amount / (float)type.itemCapacity;
|
||||
}
|
||||
|
||||
protected void findItem(){
|
||||
TileEntity entity = getClosestCore();
|
||||
if(entity == null){
|
||||
return;
|
||||
}
|
||||
targetItem = Mathf.findMin(toMine, (a, b) -> -Integer.compare(entity.items.getItem(a), entity.items.getItem(b)));
|
||||
}
|
||||
|
||||
protected boolean findItemDrop(){
|
||||
TileEntity core = getClosestCore();
|
||||
|
||||
if(core == null) return false;
|
||||
|
||||
//find nearby dropped items to pick up if applicable
|
||||
ItemDrop drop = EntityPhysics.getClosest(itemGroup, x, y, 60f,
|
||||
item -> core.tile.block().acceptStack(item.getItem(), item.getAmount(), core.tile, Drone.this) == item.getAmount() &&
|
||||
inventory.canAcceptItem(item.getItem(), 1));
|
||||
if(drop != null){
|
||||
setState(pickup);
|
||||
target = drop;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(DataOutput data) throws IOException {
|
||||
super.write(data);
|
||||
data.writeInt(mineTile == null ? -1 : mineTile.packedPosition());
|
||||
writeBuilding(data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(DataInput data, long time) throws IOException {
|
||||
super.read(data, time);
|
||||
int mined = data.readInt();
|
||||
|
||||
readBuilding(data);
|
||||
|
||||
if(mined != -1){
|
||||
mineTile = world.tile(mined);
|
||||
}
|
||||
}
|
||||
|
||||
public final UnitState
|
||||
|
||||
build = new UnitState(){
|
||||
public void entered() {
|
||||
|
||||
public void entered(){
|
||||
if(!(target instanceof BuildEntity)){
|
||||
target = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void update() {
|
||||
BuildEntity entity = (BuildEntity)target;
|
||||
public void update(){
|
||||
BuildEntity entity = (BuildEntity) target;
|
||||
TileEntity core = getClosestCore();
|
||||
|
||||
if(entity == null){
|
||||
@@ -247,7 +77,7 @@ public class Drone extends FlyingUnit implements BuilderTrait {
|
||||
|
||||
//if it's missing requirements, try and mine them
|
||||
for(ItemStack stack : entity.recipe.requirements){
|
||||
if(!core.items.hasItem(stack.item, stack.amount) && toMine.contains(stack.item)){
|
||||
if(!core.items.has(stack.item, stack.amount) && toMine.contains(stack.item)){
|
||||
targetItem = stack.item;
|
||||
getPlaceQueue().clear();
|
||||
setState(mine);
|
||||
@@ -263,17 +93,18 @@ public class Drone extends FlyingUnit implements BuilderTrait {
|
||||
},
|
||||
|
||||
repair = new UnitState(){
|
||||
public void entered() {
|
||||
|
||||
public void entered(){
|
||||
target = null;
|
||||
}
|
||||
|
||||
public void update() {
|
||||
if(target != null && (((TileEntity)target).health >= ((TileEntity)target).tile.block().health
|
||||
public void update(){
|
||||
if(target != null && (((TileEntity) target).health >= ((TileEntity) target).tile.block().health
|
||||
|| target.distanceTo(Drone.this) > discoverRange)){
|
||||
target = null;
|
||||
}
|
||||
|
||||
if (target == null) {
|
||||
if(target == null){
|
||||
retarget(() -> {
|
||||
target = Units.findAllyTile(team, x, y, discoverRange,
|
||||
tile -> tile.entity != null && tile.entity.health + 0.0001f < tile.block().health);
|
||||
@@ -291,17 +122,18 @@ public class Drone extends FlyingUnit implements BuilderTrait {
|
||||
}
|
||||
}
|
||||
},
|
||||
mine = new UnitState() {
|
||||
public void entered() {
|
||||
|
||||
mine = new UnitState(){
|
||||
public void entered(){
|
||||
target = null;
|
||||
}
|
||||
|
||||
public void update() {
|
||||
public void update(){
|
||||
TileEntity entity = getClosestCore();
|
||||
|
||||
if(entity == null) return;
|
||||
|
||||
if(targetItem == null) {
|
||||
if(targetItem == null){
|
||||
findItem();
|
||||
}
|
||||
|
||||
@@ -334,31 +166,31 @@ public class Drone extends FlyingUnit implements BuilderTrait {
|
||||
target = world.indexer().findClosestOre(x, y, targetItem);
|
||||
});
|
||||
|
||||
if(target instanceof Tile) {
|
||||
moveTo(type.range/1.5f);
|
||||
if(target instanceof Tile){
|
||||
moveTo(type.range / 1.5f);
|
||||
|
||||
if (distanceTo(target) < type.range && mineTile != target) {
|
||||
setMineTile((Tile)target);
|
||||
if(distanceTo(target) < type.range && mineTile != target){
|
||||
setMineTile((Tile) target);
|
||||
}
|
||||
|
||||
if(((Tile)target).block() != Blocks.air){
|
||||
if(((Tile) target).block() != Blocks.air){
|
||||
setState(drop);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void exited() {
|
||||
public void exited(){
|
||||
setMineTile(null);
|
||||
}
|
||||
},
|
||||
pickup = new UnitState() {
|
||||
public void entered() {
|
||||
pickup = new UnitState(){
|
||||
public void entered(){
|
||||
target = null;
|
||||
}
|
||||
|
||||
public void update() {
|
||||
ItemDrop item = (ItemDrop)target;
|
||||
public void update(){
|
||||
ItemDrop item = (ItemDrop) target;
|
||||
|
||||
if(inventory.isFull() || !inventory.canAcceptItem(item.getItem(), 1)){
|
||||
setState(drop);
|
||||
@@ -379,12 +211,12 @@ public class Drone extends FlyingUnit implements BuilderTrait {
|
||||
moveTo(0f);
|
||||
}
|
||||
},
|
||||
drop = new UnitState() {
|
||||
public void entered() {
|
||||
drop = new UnitState(){
|
||||
public void entered(){
|
||||
target = null;
|
||||
}
|
||||
|
||||
public void update() {
|
||||
public void update(){
|
||||
if(inventory.isEmpty()){
|
||||
setState(mine);
|
||||
return;
|
||||
@@ -394,10 +226,10 @@ public class Drone extends FlyingUnit implements BuilderTrait {
|
||||
|
||||
if(target == null) return;
|
||||
|
||||
TileEntity tile = (TileEntity)target;
|
||||
TileEntity tile = (TileEntity) target;
|
||||
|
||||
if(distanceTo(target) < type.range){
|
||||
if(tile.tile.block().acceptStack(inventory.getItem().item, inventory.getItem().amount, tile.tile, Drone.this) == inventory.getItem().amount) {
|
||||
if(tile.tile.block().acceptStack(inventory.getItem().item, inventory.getItem().amount, tile.tile, Drone.this) == inventory.getItem().amount){
|
||||
CallEntity.transferItemTo(inventory.getItem().item, inventory.getItem().amount, x, y, tile.tile);
|
||||
inventory.clearItem();
|
||||
}
|
||||
@@ -405,21 +237,21 @@ public class Drone extends FlyingUnit implements BuilderTrait {
|
||||
setState(repair);
|
||||
}
|
||||
|
||||
circle(type.range/1.8f);
|
||||
circle(type.range / 1.8f);
|
||||
}
|
||||
},
|
||||
retreat = new UnitState() {
|
||||
public void entered() {
|
||||
retreat = new UnitState(){
|
||||
public void entered(){
|
||||
target = null;
|
||||
}
|
||||
|
||||
public void update() {
|
||||
public void update(){
|
||||
if(health >= health){
|
||||
state.set(attack);
|
||||
}else if(!targetHasFlag(BlockFlag.repair)){
|
||||
if(timer.get(timerTarget, 20)) {
|
||||
if(timer.get(timerTarget, 20)){
|
||||
Tile target = Geometry.findClosest(x, y, world.indexer().getAllied(team, BlockFlag.repair));
|
||||
if (target != null) Drone.this.target = target.entity;
|
||||
if(target != null) Drone.this.target = target.entity;
|
||||
}
|
||||
}else{
|
||||
circle(40f);
|
||||
@@ -427,4 +259,199 @@ public class Drone extends FlyingUnit implements BuilderTrait {
|
||||
}
|
||||
};
|
||||
|
||||
{
|
||||
initEvents();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize placement event notifier system.
|
||||
* Static initialization is to be avoided, thus, this is done lazily.
|
||||
*/
|
||||
private static void initEvents(){
|
||||
if(initialized) return;
|
||||
|
||||
toMine = ObjectSet.with(Items.lead, Items.tungsten);
|
||||
|
||||
Events.on(BlockBuildEvent.class, (team, tile) -> {
|
||||
EntityGroup<BaseUnit> group = unitGroups[team.ordinal()];
|
||||
|
||||
if(!(tile.entity instanceof BuildEntity)) return;
|
||||
BuildEntity entity = tile.entity();
|
||||
|
||||
for(BaseUnit unit : group.all()){
|
||||
if(unit instanceof Drone){
|
||||
((Drone) unit).notifyPlaced(entity);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
private void notifyPlaced(BuildEntity entity){
|
||||
float timeToBuild = entity.recipe.cost;
|
||||
float dist = Math.min(entity.distanceTo(x, y) - placeDistance, 0);
|
||||
|
||||
if(dist / type.maxVelocity < timeToBuild * 0.9f){
|
||||
//CallEntity.onDroneBeginBuild(this, entity.tile, entity.recipe);
|
||||
target = entity;
|
||||
setState(build);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getBuildPower(Tile tile){
|
||||
return type.buildPower;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getMinePower(){
|
||||
return type.minePower;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Queue<BuildRequest> getPlaceQueue(){
|
||||
return placeQueue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Tile getMineTile(){
|
||||
return mineTile;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMineTile(Tile tile){
|
||||
mineTile = tile;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(){
|
||||
super.update();
|
||||
|
||||
if(Net.client() && state.is(repair) && target instanceof TileEntity){
|
||||
TileEntity entity = (TileEntity) target;
|
||||
entity.health += type.healSpeed * Timers.delta();
|
||||
entity.health = Mathf.clamp(entity.health, 0, entity.tile.block().health);
|
||||
}
|
||||
|
||||
x += Mathf.sin(Timers.time() + id * 999, 25f, 0.07f);
|
||||
y += Mathf.cos(Timers.time() + id * 999, 25f, 0.07f);
|
||||
|
||||
updateBuilding(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateRotation(){
|
||||
if(target != null && (state.is(repair) || state.is(mine))){
|
||||
rotation = Mathf.slerpDelta(rotation, angleTo(target), 0.3f);
|
||||
}else{
|
||||
rotation = Mathf.slerpDelta(rotation, velocity.angle(), 0.3f);
|
||||
}
|
||||
|
||||
if(velocity.len() <= 0.2f && !(state.is(repair) && target != null)){
|
||||
rotation += Mathf.sin(Timers.time() + id * 99, 10f, 5f);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void behavior(){
|
||||
if(health <= health * type.retreatPercent &&
|
||||
Geometry.findClosest(x, y, world.indexer().getAllied(team, BlockFlag.repair)) != null){
|
||||
setState(retreat);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public UnitState getStartState(){
|
||||
return repair;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawOver(){
|
||||
trail.draw(Palette.lightTrail, 3f);
|
||||
|
||||
TargetTrait entity = target;
|
||||
|
||||
if(entity instanceof TileEntity && state.is(repair)){
|
||||
float len = 5f;
|
||||
Draw.color(Color.BLACK, Color.WHITE, 0.95f + Mathf.absin(Timers.time(), 0.8f, 0.05f));
|
||||
Shapes.laser("beam", "beam-end",
|
||||
x + Angles.trnsx(rotation, len),
|
||||
y + Angles.trnsy(rotation, len),
|
||||
entity.getX(), entity.getY());
|
||||
Draw.color();
|
||||
}
|
||||
|
||||
drawBuilding(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float drawSize(){
|
||||
return isBuilding() ? placeDistance * 2f : 30f;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getAmmoFraction(){
|
||||
return inventory.getItem().amount / (float) type.itemCapacity;
|
||||
}
|
||||
|
||||
protected void findItem(){
|
||||
TileEntity entity = getClosestCore();
|
||||
if(entity == null){
|
||||
return;
|
||||
}
|
||||
targetItem = Mathf.findMin(toMine, (a, b) -> -Integer.compare(entity.items.get(a), entity.items.get(b)));
|
||||
}
|
||||
|
||||
protected boolean findItemDrop(){
|
||||
TileEntity core = getClosestCore();
|
||||
|
||||
if(core == null) return false;
|
||||
|
||||
//find nearby dropped items to pick up if applicable
|
||||
ItemDrop drop = EntityPhysics.getClosest(itemGroup, x, y, 60f,
|
||||
item -> core.tile.block().acceptStack(item.getItem(), item.getAmount(), core.tile, Drone.this) == item.getAmount() &&
|
||||
inventory.canAcceptItem(item.getItem(), 1));
|
||||
if(drop != null){
|
||||
setState(pickup);
|
||||
target = drop;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canCreateBlocks(){
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(DataOutput data) throws IOException{
|
||||
super.write(data);
|
||||
data.writeInt(mineTile == null ? -1 : mineTile.packedPosition());
|
||||
data.writeInt(state.is(repair) && target instanceof TileEntity ? ((TileEntity)target).tile.packedPosition() : -1);
|
||||
writeBuilding(data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(DataInput data, long time) throws IOException{
|
||||
super.read(data, time);
|
||||
int mined = data.readInt();
|
||||
int repairing = data.readInt();
|
||||
|
||||
readBuilding(data);
|
||||
|
||||
if(mined != -1){
|
||||
mineTile = world.tile(mined);
|
||||
}
|
||||
|
||||
if(repairing != -1){
|
||||
Tile tile = world.tile(repairing);
|
||||
target = tile.entity;
|
||||
state.set(repair);
|
||||
}else{
|
||||
state.set(retreat);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
package io.anuke.mindustry.entities.units.types;
|
||||
|
||||
public class Fabricator extends Drone {
|
||||
public class Fabricator extends Drone{
|
||||
|
||||
}
|
||||
|
||||
@@ -2,6 +2,6 @@ package io.anuke.mindustry.entities.units.types;
|
||||
|
||||
import io.anuke.mindustry.entities.units.FlyingUnit;
|
||||
|
||||
public class Monsoon extends FlyingUnit {
|
||||
public class Monsoon extends FlyingUnit{
|
||||
|
||||
}
|
||||
|
||||
@@ -2,6 +2,6 @@ package io.anuke.mindustry.entities.units.types;
|
||||
|
||||
import io.anuke.mindustry.entities.units.GroundUnit;
|
||||
|
||||
public class Scout extends GroundUnit {
|
||||
public class Scout extends GroundUnit{
|
||||
|
||||
}
|
||||
|
||||
@@ -2,6 +2,6 @@ package io.anuke.mindustry.entities.units.types;
|
||||
|
||||
import io.anuke.mindustry.entities.units.FlyingUnit;
|
||||
|
||||
public class Vtol extends FlyingUnit {
|
||||
public class Vtol extends FlyingUnit{
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user