Cleanup continues

This commit is contained in:
Anuken
2020-02-04 18:00:32 -05:00
parent 012421afcb
commit a7b39e56bd
106 changed files with 2266 additions and 4543 deletions
+6 -6
View File
@@ -73,7 +73,7 @@ public class Damage{
}
}
public static void collideLine(Bullet hitter, Team team, Effect effect, float x, float y, float angle, float length){
public static void collideLine(Bulletc hitter, Team team, Effect effect, float x, float y, float angle, float length){
collideLine(hitter, team, effect, x, y, angle, length, false);
}
@@ -81,7 +81,7 @@ public class Damage{
* Damages entities in a line.
* Only enemies of the specified team are damaged.
*/
public static void collideLine(Bullet hitter, Team team, Effect effect, float x, float y, float angle, float length, boolean large){
public static void collideLine(Bulletc hitter, Team team, Effect effect, float x, float y, float angle, float length, boolean large){
collidedBlocks.clear();
tr.trns(angle, length);
Intc2 collider = (cx, cy) -> {
@@ -89,7 +89,7 @@ public class Damage{
if(tile != null && !collidedBlocks.contains(tile.pos()) && tile.entity != null && tile.getTeamID() != team.id && tile.entity.collide(hitter)){
tile.entity.collision(hitter);
collidedBlocks.add(tile.pos());
hitter.getBulletType().hit(hitter, tile.worldx(), tile.worldy());
hitter.type().hit(hitter, tile.worldx(), tile.worldy());
}
};
@@ -184,7 +184,7 @@ public class Damage{
entity.damage(amount);
//TODO better velocity displacement
float dst = tr.set(entity.getX() - x, entity.getY() - y).len();
entity.velocity().add(tr.setLength((1f - dst / radius) * 2f / entity.mass()));
entity.vel().add(tr.setLength((1f - dst / radius) * 2f / entity.mass()));
if(complete && damage >= 9999999f && entity == player){
Events.fire(Trigger.exclusionDeath);
@@ -235,8 +235,8 @@ public class Damage{
//apply damage to entity if needed
if(tile.entity != null && tile.getTeam() != team){
int health = (int)tile.entity.health;
if(tile.entity.health > 0){
int health = (int)tile.entity.health();
if(tile.entity.health() > 0){
tile.entity.damage(scaledDamage);
scaledDamage -= health;
+14
View File
@@ -16,6 +16,9 @@ public class Effect{
/** Clip size. */
public float size;
public boolean ground;
public float groundDuration;
public Effect(float life, float clipsize, Cons<EffectContainer> renderer){
this.id = lastid++;
this.lifetime = life;
@@ -27,6 +30,17 @@ public class Effect{
this(life, 28f, renderer);
}
public Effect ground(){
ground = true;
return this;
}
public Effect ground(float duration){
ground = true;
this.groundDuration = duration;
return this;
}
public void at(Position pos){
Effects.createEffect(this, pos.getX(), pos.getY(), 0, Color.white, null);
}
+8 -9
View File
@@ -5,9 +5,7 @@ import arc.graphics.*;
import arc.math.*;
import arc.math.geom.*;
import arc.util.*;
import arc.util.pooling.*;
import mindustry.content.*;
import mindustry.entities.type.*;
import static mindustry.Vars.*;
@@ -40,13 +38,14 @@ public class Effects{
Rect pos = Tmp.r2.setSize(effect.size).setCenter(x, y);
if(view.overlaps(pos)){
EffectEntity entity = Pools.obtain(EffectEntity.class, EffectEntity::new);
entity.effect = effect;
entity.color.set(color);
entity.rotation = rotation;
entity.data = data;
entity.id++;
entity.set(x, y);
//TODO implement create() method here.
//EffectEntity entity = Pools.obtain(EffectEntity.class, EffectEntity::new);
//entity.effect = effect;
//entity.color.set(color);
//entity.rotation = rotation;
//entity.data = data;
//entity.id++;
//entity.set(x, y);
//if(data instanceof Entity){
// entity.setParent((Entity)data);
//}
+9 -9
View File
@@ -3,7 +3,7 @@ package mindustry.entities;
import arc.func.*;
import arc.math.*;
import arc.math.geom.*;
import mindustry.entities.type.*;
import mindustry.gen.*;
import mindustry.game.*;
import mindustry.gen.*;
import mindustry.world.*;
@@ -18,7 +18,7 @@ public class Units{
private static boolean boolResult;
/** @return whether this player can interact with a specific tile. if either of these are null, returns true.*/
public static boolean canInteract(Player player, Tile tile){
public static boolean canInteract(Playerc player, Tile tile){
return player == null || tile == null || tile.interactable(player.team());
}
@@ -148,7 +148,7 @@ public class Units{
/** Iterates over all units in a rectangle. */
public static void nearby(Team team, float x, float y, float width, float height, Cons<Unitc> cons){
unitGroup.intersect(x, y, width, height, u -> {
Groups.unit.intersect(x, y, width, height, u -> {
if(u.getTeam() == team){
cons.get(u);
}
@@ -157,7 +157,7 @@ public class Units{
/** Iterates over all units in a circle around this position. */
public static void nearby(Team team, float x, float y, float radius, Cons<Unitc> cons){
unitGroup.intersect(x - radius, y - radius, radius*2f, radius*2f, unit -> {
Groups.unit.intersect(x - radius, y - radius, radius*2f, radius*2f, unit -> {
if(unit.getTeam() == team && unit.withinDst(x, y, radius)){
cons.get(unit);
}
@@ -166,7 +166,7 @@ public class Units{
/** Iterates over all units in a rectangle. */
public static void nearby(float x, float y, float width, float height, Cons<Unitc> cons){
unitGroup.intersect(x, y, width, height, cons);
Groups.unit.intersect(x, y, width, height, cons);
}
/** Iterates over all units in a rectangle. */
@@ -176,7 +176,7 @@ public class Units{
/** Iterates over all units that are enemies of this team. */
public static void nearbyEnemies(Team team, float x, float y, float width, float height, Cons<Unitc> cons){
unitGroup.intersect(x, y, width, height, u -> {
Groups.unit.intersect(x, y, width, height, u -> {
if(team.isEnemy(u.getTeam())){
cons.get(u);
}
@@ -190,11 +190,11 @@ public class Units{
/** Iterates over all units. */
public static void all(Cons<Unitc> cons){
unitGroup.all().each(cons);
Groups.unit.all().each(cons);
}
public static void each(Team team, Cons<BaseUnit> cons){
unitGroup.all().each(t -> t.getTeam() == team, cons);
public static void each(Team team, Cons<Unitc> cons){
Groups.unit.all().each(t -> t.getTeam() == team, cons);
}
}
@@ -27,7 +27,7 @@ public class ArtilleryBulletType extends BasicBulletType{
super.update(b);
if(b.timer.get(0, 3 + b.fslope() * 2f)){
trailEffect.at(b.x, b.y, b.fslope() * 4f, backColor);
trailEffect.at(b.x(), b.y(), b.fslope() * 4f, backColor);
}
}
@@ -39,9 +39,9 @@ public class ArtilleryBulletType extends BasicBulletType{
float height = bulletHeight * ((1f - bulletShrink) + bulletShrink * b.fout());
Draw.color(backColor);
Draw.rect(backRegion, b.x, b.y, bulletWidth * scale, height * scale, b.rot() - 90);
Draw.rect(backRegion, b.x(), b.y(), bulletWidth * scale, height * scale, b.rotation() - 90);
Draw.color(frontColor);
Draw.rect(frontRegion, b.x, b.y, bulletWidth * scale, height * scale, b.rot() - 90);
Draw.rect(frontRegion, b.x(), b.y(), bulletWidth * scale, height * scale, b.rotation() - 90);
Draw.color();
}
}
@@ -38,9 +38,9 @@ public class BasicBulletType extends BulletType{
float height = bulletHeight * ((1f - bulletShrink) + bulletShrink * b.fout());
Draw.color(backColor);
Draw.rect(backRegion, b.x, b.y, bulletWidth, height, b.rot() - 90);
Draw.rect(backRegion, b.x(), b.y(), bulletWidth, height, b.rotation() - 90);
Draw.color(frontColor);
Draw.rect(frontRegion, b.x, b.y, bulletWidth, height, b.rot() - 90);
Draw.rect(frontRegion, b.x(), b.y(), bulletWidth, height, b.rotation() - 90);
Draw.color();
}
}
@@ -210,7 +210,7 @@ public abstract class BulletType extends Content{
bullet.velocity.set(0, type.speed).setAngle(angle).scl(velocityScl);
if(type.keepVelocity){
bullet.velocity.add(owner instanceof VelocityTrait ? ((VelocityTrait)owner).velocity() : Vec2.ZERO);
bullet.velocity.add(owner instanceof VelocityTrait ? ((VelocityTrait)owner).vel() : Vec2.ZERO);
}
bullet.team = team;
@@ -29,7 +29,7 @@ public class FlakBulletType extends BasicBulletType{
if(b.getData() instanceof Integer) return;
if(b.timer.get(2, 6)){
Units.nearbyEnemies(b.team(), rect.setSize(explodeRange * 2f).setCenter(b.x, b.y), unit -> {
Units.nearbyEnemies(b.team(), rect.setSize(explodeRange * 2f).setCenter(b.x(), b.y()), unit -> {
if(b.getData() instanceof Float) return;
if(unit.dst(b) < explodeRange){
@@ -36,9 +36,9 @@ public class HealBulletType extends BulletType{
public void draw(Bulletc b){
Draw.color(backColor);
Lines.stroke(bulletWidth);
Lines.lineAngleCenter(b.x, b.y, b.rot(), bulletHeight);
Lines.lineAngleCenter(b.x(), b.y(), b.rotation(), bulletHeight);
Draw.color(frontColor);
Lines.lineAngleCenter(b.x, b.y, b.rot(), bulletHeight / 2f);
Lines.lineAngleCenter(b.x(), b.y(), b.rotation(), bulletHeight / 2f);
Draw.reset();
}
@@ -41,7 +41,7 @@ public class LaserBulletType extends BulletType{
@Override
public void init(Bulletc b){
Damage.collideLine(b, b.team(), hitEffect, b.x, b.y, b.rot(), length);
Damage.collideLine(b, b.team(), hitEffect, b.x(), b.y(), b.rotation(), length);
}
@Override
@@ -51,18 +51,18 @@ public class LaserBulletType extends BulletType{
float cwidth = width;
float compound = 1f;
Lines.lineAngle(b.x, b.y, b.rot(), baseLen);
Lines.lineAngle(b.x(), b.y(), b.rotation(), baseLen);
Lines.precise(true);
for(Color color : colors){
Draw.color(color);
Lines.stroke((cwidth *= lengthFalloff) * b.fout());
Lines.lineAngle(b.x, b.y, b.rot(), baseLen, CapStyle.none);
Tmp.v1.trns(b.rot(), baseLen);
Drawf.tri(b.x + Tmp.v1.x, b.y + Tmp.v1.y, Lines.getStroke() * 1.22f, cwidth * 2f + width / 2f, b.rot());
Lines.lineAngle(b.x(), b.y(), b.rotation(), baseLen, CapStyle.none);
Tmp.v1.trns(b.rotation(), baseLen);
Drawf.tri(b.x + Tmp.v1.x, b.y + Tmp.v1.y, Lines.getStroke() * 1.22f, cwidth * 2f + width / 2f, b.rotation());
Fill.circle(b.x, b.y, 1f * cwidth * b.fout());
Fill.circle(b.x(), b.y(), 1f * cwidth * b.fout());
for(int i : Mathf.signs){
Drawf.tri(b.x, b.y, sideWidth * b.fout() * cwidth, sideLength * compound, b.rot() + sideAngle * i);
Drawf.tri(b.x(), b.y(), sideWidth * b.fout() * cwidth, sideLength * compound, b.rotation() + sideAngle * i);
}
compound *= lengthFalloff;
@@ -25,6 +25,6 @@ public class LightningBulletType extends BulletType{
@Override
public void init(Bulletc b){
Lightning.create(b.team(), lightningColor, damage, b.x, b.y, b.rot(), lightningLength);
Lightning.create(b.team(), lightningColor, damage, b.x(), b.y(), b.rotation(), lightningLength);
}
}
@@ -48,7 +48,7 @@ public class LiquidBulletType extends BulletType{
super.update(b);
if(liquid.canExtinguish()){
Tile tile = world.tileWorld(b.x, b.y);
Tile tile = world.tileWorld(b.x(), b.y());
if(tile != null && Fire.has(tile.x, tile.y)){
Fire.extinguish(tile, 100f);
b.remove();
@@ -61,7 +61,7 @@ public class LiquidBulletType extends BulletType{
public void draw(Bulletc b){
Draw.color(liquid.color, Color.white, b.fout() / 100f);
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
@@ -27,10 +27,10 @@ public class MassDriverBolt extends BulletType{
float w = 11f, h = 13f;
Draw.color(Pal.bulletYellowBack);
Draw.rect("shell-back", b.x, b.y, w, h, b.rot() + 90);
Draw.rect("shell-back", b.x(), b.y(), w, h, b.rotation() + 90);
Draw.color(Pal.bulletYellow);
Draw.rect("shell", b.x, b.y, w, h, b.rot() + 90);
Draw.rect("shell", b.x(), b.y(), w, h, b.rotation() + 90);
Draw.reset();
}
@@ -92,8 +92,8 @@ public class MassDriverBolt extends BulletType{
for(int i = 0; i < data.items.length; i++){
int amountDropped = Mathf.random(0, data.items[i]);
if(amountDropped > 0){
float angle = b.rot() + Mathf.range(100f);
Fx.dropItem.at(b.x, b.y, angle, Color.white, content.item(i));
float angle = b.rotation() + Mathf.range(100f);
Fx.dropItem.at(b.x(), b.y(), angle, Color.white, content.item(i));
}
}
}
@@ -30,11 +30,11 @@ public class MissileBulletType extends BasicBulletType{
super.update(b);
if(Mathf.chance(Time.delta() * 0.2)){
Fx.missileTrail.at(b.x, b.y, 2f, trailColor);
Fx.missileTrail.at(b.x(), b.y(), 2f, trailColor);
}
if(weaveMag > 0){
b.velocity().rotate(Mathf.sin(Time.time() + b.id * 4422, weaveScale, weaveMag) * Time.delta());
b.vel().rotate(Mathf.sin(Time.time() + b.id * 4422, weaveScale, weaveMag) * Time.delta());
}
}
}
@@ -16,6 +16,7 @@ import arc.util.pooling.*;
import mindustry.*;
import mindustry.annotations.Annotations.*;
import mindustry.content.*;
import mindustry.core.*;
import mindustry.ctype.*;
import mindustry.entities.*;
import mindustry.entities.bullet.*;
@@ -25,7 +26,9 @@ import mindustry.game.EventType.*;
import mindustry.game.*;
import mindustry.gen.*;
import mindustry.graphics.*;
import mindustry.net.Administration.*;
import mindustry.net.*;
import mindustry.net.Packets.*;
import mindustry.type.*;
import mindustry.ui.*;
import mindustry.world.*;
@@ -179,6 +182,11 @@ public class EntityComps{
Object data;
BulletType type;
Interval timer = new Interval(6);
public boolean timer(int index, float time){
return timer.get(index, time);
}
@Override
public float getDamage(){
@@ -662,6 +670,136 @@ public class EntityComps{
}
}
@Component
abstract class PlayerComp implements UnitController, Entityc, Syncc{
@Nullable Unitc unit;
@ReadOnly Team team = Team.sharded;
String name = "noname";
@Nullable NetConnection con;
boolean admin, typing;
Color color = new Color();
float mouseX, mouseY;
@Nullable String lastText;
float textFadeTime;
public void update(){
if(unit != null){
x(unit.x());
y(unit.y());
unit.team(team);
}
textFadeTime -= Time.delta() / (60 * 5);
}
public void team(Team team){
if(unit != null){
unit.team(team);
}
}
public void unit(Unitc unit){
this.unit = unit;
unit.team(team);
}
String uuid(){
return con == null ? "AAAAAAAA" : con.uuid;
}
String usid(){
return con == null ? "AAAAAAAA" : con.usid;
}
void kick(KickReason reason){
con.kick(reason);
}
void kick(String reason){
con.kick(reason);
}
void drawName(){
BitmapFont font = Fonts.def;
GlyphLayout layout = Pools.obtain(GlyphLayout.class, GlyphLayout::new);
final float nameHeight = 11;
final float textHeight = 15;
boolean ints = font.usesIntegerPositions();
font.setUseIntegerPositions(false);
font.getData().setScale(0.25f / Scl.scl(1f));
layout.setText(font, name);
if(!isLocal()){
Draw.color(0f, 0f, 0f, 0.3f);
Fill.rect(unit.x(), unit.y() + nameHeight - layout.height / 2, layout.width + 2, layout.height + 3);
Draw.color();
font.setColor(color);
font.draw(name, unit.x(), unit.y() + nameHeight, 0, Align.center, false);
if(admin){
float s = 3f;
Draw.color(color.r * 0.5f, color.g * 0.5f, color.b * 0.5f, 1f);
Draw.rect(Icon.adminSmall.getRegion(), unit.x() + layout.width / 2f + 2 + 1, unit.y() + nameHeight - 1.5f, s, s);
Draw.color(color);
Draw.rect(Icon.adminSmall.getRegion(), unit.x() + layout.width / 2f + 2 + 1, unit.y() + nameHeight - 1f, s, s);
}
}
if(Core.settings.getBool("playerchat") && ((textFadeTime > 0 && lastText != null) || typing)){
String text = textFadeTime <= 0 || lastText == null ? "[LIGHT_GRAY]" + Strings.animated(Time.time(), 4, 15f, ".") : lastText;
float width = 100f;
float visualFadeTime = 1f - Mathf.curve(1f - textFadeTime, 0.9f);
font.setColor(1f, 1f, 1f, textFadeTime <= 0 || lastText == null ? 1f : visualFadeTime);
layout.setText(font, text, Color.white, width, Align.bottom, true);
Draw.color(0f, 0f, 0f, 0.3f * (textFadeTime <= 0 || lastText == null ? 1f : visualFadeTime));
Fill.rect(unit.x(), unit.y() + textHeight + layout.height - layout.height/2f, layout.width + 2, layout.height + 3);
font.draw(text, unit.x() - width/2f, unit.y() + textHeight + layout.height, width, Align.center, true);
}
Draw.reset();
Pools.free(layout);
font.getData().setScale(1f);
font.setColor(Color.white);
font.setUseIntegerPositions(ints);
}
void sendMessage(String text){
if(isLocal()){
if(ui != null){
ui.chatfrag.addMessage(text, null);
}
}else{
Call.sendMessage(con, text, null, null);
}
}
void sendMessage(String text, Playerc from){
sendMessage(text, from, NetClient.colorizeName(from.id(), from.name()));
}
void sendMessage(String text, Playerc from, String fromName){
if(isLocal()){
if(ui != null){
ui.chatfrag.addMessage(text, fromName);
}
}else{
Call.sendMessage(con, text, fromName, from);
}
}
PlayerInfo getInfo(){
if(isLocal()){
throw new IllegalArgumentException("Local players cannot be traced and do not have info.");
}else{
return netServer.admins.getInfo(uuid());
}
}
}
@Component
abstract class TeamComp implements Posc, Healthc{
transient float x, y;
@@ -1194,6 +1332,25 @@ public class EntityComps{
current.progress = entity.progress;
}
/** Draw all current build requests. Does not draw the beam effect, only the positions. */
void drawBuildRequests(){
for(BuildRequest request : requests){
if(request.progress > 0.01f || (buildRequest() == request && request.initialized && (dst(request.x * tilesize, request.y * tilesize) <= placeDistance || state.isEditor()))) continue;
request.animScale = 1f;
if(request.breaking){
control.input.drawBreaking(request);
}else{
request.block.drawRequest(request, control.input.allRequests(),
Build.validPlace(team(), request.x, request.y, request.block, request.rotation) || control.input.requestMatches(request));
}
}
Draw.reset();
}
/** @return whether this request should be skipped, in favor of the next one. */
boolean shouldSkip(BuildRequest request, @Nullable Tilec core){
//requests that you have at least *started* are considered
@@ -1557,7 +1714,7 @@ public class EntityComps{
}
boolean isLocal(){
return this == Vars.player;
return this instanceof Unitc && ((Unitc)this).controller() == player || this == player;
}
<T> T as(Class<T> type){
@@ -1566,5 +1723,13 @@ public class EntityComps{
@InternalImpl
abstract int classId();
void read(DataInput input) throws IOException{
//TODO dynamic io
}
void write(DataOutput output) throws IOException{
//TODO dynamic io
}
}
}
@@ -16,4 +16,7 @@ class EntityDefs{
@EntityDef({DecalComp.class})
class DecalDef{}
@EntityDef({PlayerComp.class})
class PlayerDef{}
}
@@ -5,6 +5,11 @@ import mindustry.entities.def.EntityComps.*;
public class EntityGroupDefs{
@GroupDef(PlayerComp.class)
void player(){
}
@GroupDef(UnitComp.class)
void unit(){
@@ -16,7 +21,12 @@ public class EntityGroupDefs{
}
@GroupDef(DrawComp.class)
void drawers(){
void drawer(){
}
@GroupDef(SyncComp.class)
void sync(){
}
}
@@ -1,89 +0,0 @@
package mindustry.entities.effect;
import arc.math.Mathf;
import arc.util.Time;
import mindustry.Vars;
import mindustry.entities.Effects;
import mindustry.entities.*;
import mindustry.entities.Effects.EffectRenderer;
import mindustry.world.Tile;
/**
* 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;
if(effect.isStatic){
time += Time.delta();
time = Mathf.clamp(time, 0, effect.staticLife);
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){
remove();
}
}else{
super.update();
}
}
@Override
public void draw(){
GroundEffect effect = (GroundEffect)this.effect;
if(once && effect.isStatic)
Effects.renderEffect(id, effect, color, lifetime(), rotation, x, y, data);
else
Effects.renderEffect(id, effect, color, time, rotation, x, y, data);
}
@Override
public void reset(){
super.reset();
once = false;
}
/**
* 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.
*/
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.
*/
public final boolean isStatic;
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){
super(life, draw);
this.staticLife = 0f;
this.isStatic = isStatic;
}
public GroundEffect(float life, EffectRenderer draw){
super(life, draw);
this.staticLife = 0f;
this.isStatic = false;
}
}
}
@@ -9,7 +9,7 @@ import arc.util.pooling.*;
import mindustry.annotations.Annotations.*;
import mindustry.content.*;
import mindustry.entities.*;
import mindustry.entities.type.*;
import mindustry.gen.*;
import mindustry.game.*;
import mindustry.gen.*;
import mindustry.graphics.*;
@@ -201,7 +201,7 @@ public class Puddle extends SolidEntity implements SaveTrait, Poolable, DrawTrai
unit.applyEffect(liquid.effect, 60 * 2);
if(unit.velocity().len() > 0.1){
if(unit.vel().len() > 0.1){
Fx.ripple.at(liquid.color, unit.x, unit.y);
}
});
@@ -1,914 +0,0 @@
package mindustry.entities.type;
import arc.*;
import mindustry.annotations.Annotations.*;
import arc.struct.*;
import arc.graphics.*;
import arc.graphics.g2d.*;
import arc.math.*;
import arc.math.geom.*;
import arc.scene.ui.*;
import arc.scene.ui.layout.*;
import arc.util.*;
import arc.util.ArcAnnotate.*;
import arc.util.pooling.*;
import mindustry.*;
import mindustry.content.*;
import mindustry.core.*;
import mindustry.ctype.ContentType;
import mindustry.entities.*;
import mindustry.entities.units.*;
import mindustry.game.*;
import mindustry.gen.*;
import mindustry.input.*;
import mindustry.io.*;
import mindustry.net.Administration.*;
import mindustry.net.*;
import mindustry.type.*;
import mindustry.ui.*;
import mindustry.world.*;
import mindustry.world.blocks.*;
import java.io.*;
import static mindustry.Vars.*;
public class Player extends Unitc implements BuilderMinerTrait, ShooterTrait{
public static final int timerSync = 2;
public static final int timerAbility = 3;
private static final float liftoffBoost = 0.2f;
private static final Rect rect = new Rect();
//region instance variables
public float baseRotation;
public float pointerX, pointerY;
public String name = "noname";
public @Nullable String uuid, usid;
public boolean isAdmin, isTransferring, isShooting, isBoosting, isMobile, isTyping, isBuilding = true;
public boolean buildWasAutoPaused = false;
public float boostHeat, shootHeat, destructTime;
public boolean achievedFlight;
public Color color = new Color();
public UnitDef mech = Mechs.starter;
public SpawnerTrait spawner, lastSpawner;
public int respawns;
public @Nullable NetConnection con;
public boolean isLocal = false;
public Interval timer = new Interval(6);
public Teamc target;
public Teamc moveTarget;
public @Nullable String lastText;
public float textFadeTime;
private float walktime, itemtime;
private Queue<BuildRequest> placeQueue = new Queue<>();
private Tile mining;
private Vec2 movement = new Vec2();
private boolean moved;
//endregion
//region unit and event overrides, utility methods
@Remote(targets = Loc.server, called = Loc.server)
public static void onPlayerDeath(Player player){
if(player == null) return;
player.dead = true;
player.placeQueue.clear();
player.onDeath();
}
@Override
public float getDamageMultipler(){
return status.getDamageMultiplier() * state.rules.playerDamageMultiplier;
}
@Override
public void onRespawn(Tile tile){
velocity.setZero();
boostHeat = 1f;
achievedFlight = true;
rotation = 90f;
baseRotation = 90f;
dead = false;
spawner = null;
respawns --;
Sounds.respawn.at(tile);
setNet(tile.drawx(), tile.drawy());
clearItem();
heal();
}
@Override
public boolean offloadImmediately(){
return true;
}
@Override
public TypeID getTypeID(){
return TypeIDs.player;
}
@Override
public UnitDef type(){
return mech;
}
@Override
public Weapons getWeapons(){
return null;
}
@Override
public void move(float x, float y){
if(!mech.flying){
collisions.move(this, x, y);
}else{
moveBy(x, y);
}
}
@Override
public float getMinePower(){
return mech.minePower;
}
@Override
public TextureRegion getIconRegion(){
return mech.icon(Cicon.full);
}
@Override
public void interpolate(){
super.interpolate();
if(interpolator.values.length > 1){
baseRotation = interpolator.values[1];
}
if(interpolator.target.dst(interpolator.last) > 1f){
walktime += Time.delta();
}
}
@Override
public float getBuildPower(Tile tile){
return mech.buildPower;
}
@Override
public float maxHealth(){
return mech.health * state.rules.playerHealthMultiplier;
}
@Override
public Tile getMineTile(){
return mining;
}
@Override
public void setMineTile(Tile tile){
this.mining = tile;
}
@Override
public boolean canMine(Item item){
return item.hardness <= mech.drillTier;
}
@Override
public float calculateDamage(float amount){
return amount * Mathf.clamp(1f - (status.getArmorMultiplier() + mech.getExtraArmor(this)) / 100f);
}
@Override
public void added(){
baseRotation = 90f;
}
@Override
public float mass(){
return mech.mass;
}
@Override
public boolean isFlying(){
return mech.flying || boostHeat > liftoffBoost;
}
@Override
public void damage(float amount){
hitTime = hitDuration;
if(!net.client()){
health -= calculateDamage(amount);
}
if(health <= 0 && !dead){
Call.onPlayerDeath(this);
}
}
@Override
public void set(float x, float y){
this.x = x;
this.y = y;
}
@Override
public Queue<BuildRequest> buildQueue(){
return placeQueue;
}
@Override
public String toString(){
return "Player{" + name + ", mech=" + mech.name + ", id=" + id + ", local=" + isLocal + ", " + x + ", " + y + "}";
}
@Override
public EntityGroup targetGroup(){
return playerGroup;
}
public void team(Team team){
this.team = team;
}
//endregion
//region draw methods
@Override
public float drawSize(){
return isLocal ? Float.MAX_VALUE : 40 + placeDistance;
}
@Override
public void drawShadow(float offsetX, float offsetY){
float scl = mech.flying ? 1f : boostHeat / 2f;
Draw.rect(getIconRegion(), x + offsetX * scl, y + offsetY * scl, rotation - 90);
}
@Override
public void draw(){
if(dead) return;
if(!movement.isZero() && moved && !state.isPaused()){
walktime += movement.len() * getFloorOn().speedMultiplier * 2f;
baseRotation = Mathf.slerpDelta(baseRotation, movement.angle(), 0.13f);
}
float ft = Mathf.sin(walktime, 6f, 2f) * (1f - boostHeat);
Floor floor = getFloorOn();
Draw.color();
Draw.mixcol(Color.white, hitTime / hitDuration);
if(!mech.flying){
if(floor.isLiquid){
Draw.color(Color.white, floor.color, 0.5f);
}
float boostTrnsY = -boostHeat * 3f;
float boostTrnsX = boostHeat * 3f;
float boostAng = boostHeat * 40f;
float light = 0.2f;
for(int i : Mathf.signs){
Draw.colorl(1f-light + Mathf.clamp(ft * i, 0, 1) *light);
Draw.rect(mech.legRegion,
x + Angles.trnsx(baseRotation, ft * i + boostTrnsY, -boostTrnsX * i),
y + Angles.trnsy(baseRotation, ft * i + boostTrnsY, -boostTrnsX * i),
mech.legRegion.getWidth() * i * Draw.scl,
(mech.legRegion.getHeight() - Mathf.clamp(ft * i, 0, 2)) * Draw.scl,
baseRotation - 90 + boostAng * i);
}
Draw.color();
Draw.rect(mech.baseRegion, x, y, baseRotation - 90);
}
if(floor.isLiquid){
Draw.color(Color.white, floor.color, drownTime);
}else{
Draw.color(Color.white);
}
Draw.rect(mech.region, x, y, rotation - 90);
mech.draw(this);
weapons.draw(this);
Draw.reset();
}
public void drawBackItems(){
drawBackItems(itemtime, isLocal);
}
@Override
public void drawStats(){
mech.drawStats(this);
}
@Override
public void drawOver(){
if(dead) return;
if(isBuilding() && isBuilding){
if(!state.isPaused()){
drawBuilding();
}
}else{
drawMining();
}
}
@Override
public void drawUnder(){
if(dead) return;
float size = mech.engineSize * (mech.flying ? 1f : boostHeat);
Draw.color(mech.engineColor);
Fill.circle(x + Angles.trnsx(rotation + 180, mech.engineOffset), y + Angles.trnsy(rotation + 180, mech.engineOffset),
size + Mathf.absin(Time.time(), 2f, size / 4f));
Draw.color(Color.white);
Fill.circle(x + Angles.trnsx(rotation + 180, mech.engineOffset - 1f), y + Angles.trnsy(rotation + 180, mech.engineOffset - 1f),
(size + Mathf.absin(Time.time(), 2f, size / 4f)) / 2f);
Draw.color();
}
public void drawName(){
BitmapFont font = Fonts.def;
GlyphLayout layout = Pools.obtain(GlyphLayout.class, GlyphLayout::new);
final float nameHeight = 11;
final float textHeight = 15;
boolean ints = font.usesIntegerPositions();
font.setUseIntegerPositions(false);
font.getData().setScale(0.25f / Scl.scl(1f));
layout.setText(font, name);
if(!isLocal){
Draw.color(0f, 0f, 0f, 0.3f);
Fill.rect(x, y + nameHeight - layout.height / 2, layout.width + 2, layout.height + 3);
Draw.color();
font.setColor(color);
font.draw(name, x, y + nameHeight, 0, Align.center, false);
if(isAdmin){
float s = 3f;
Draw.color(color.r * 0.5f, color.g * 0.5f, color.b * 0.5f, 1f);
Draw.rect(Icon.adminSmall.getRegion(), x + layout.width / 2f + 2 + 1, y + nameHeight - 1.5f, s, s);
Draw.color(color);
Draw.rect(Icon.adminSmall.getRegion(), x + layout.width / 2f + 2 + 1, y + nameHeight - 1f, s, s);
}
}
if(Core.settings.getBool("playerchat") && ((textFadeTime > 0 && lastText != null) || isTyping)){
String text = textFadeTime <= 0 || lastText == null ? "[LIGHT_GRAY]" + Strings.animated(Time.time(), 4, 15f, ".") : lastText;
float width = 100f;
float visualFadeTime = 1f - Mathf.curve(1f - textFadeTime, 0.9f);
font.setColor(1f, 1f, 1f, textFadeTime <= 0 || lastText == null ? 1f : visualFadeTime);
layout.setText(font, text, Color.white, width, Align.bottom, true);
Draw.color(0f, 0f, 0f, 0.3f * (textFadeTime <= 0 || lastText == null ? 1f : visualFadeTime));
Fill.rect(x, y + textHeight + layout.height - layout.height/2f, layout.width + 2, layout.height + 3);
font.draw(text, x - width/2f, y + textHeight + layout.height, width, Align.center, true);
}
Draw.reset();
Pools.free(layout);
font.getData().setScale(1f);
font.setColor(Color.white);
font.setUseIntegerPositions(ints);
}
/** Draw all current build requests. Does not draw the beam effect, only the positions. */
public void drawBuildRequests(){
if(!isLocal) return;
for(BuildRequest request : buildQueue()){
if(request.progress > 0.01f || (buildRequest() == request && request.initialized && (dst(request.x * tilesize, request.y * tilesize) <= placeDistance || state.isEditor()))) continue;
request.animScale = 1f;
if(request.breaking){
control.input.drawBreaking(request);
}else{
request.block.drawRequest(request, control.input.allRequests(),
Build.validPlace(team(), request.x, request.y, request.block, request.rotation) || control.input.requestMatches(request));
}
}
Draw.reset();
}
//endregion
//region update methods
@Override
public void updateMechanics(){
if(isBuilding){
updateBuilding();
}
//mine only when not building
if(buildRequest() == null || !isBuilding){
updateMining();
}
}
@Override
public void update(){
hitTime -= Time.delta();
textFadeTime -= Time.delta() / (60 * 5);
itemtime = Mathf.lerpDelta(itemtime, Mathf.num(item.amount > 0), 0.1f);
if(Float.isNaN(x) || Float.isNaN(y)){
velocity.set(0f, 0f);
x = 0;
y = 0;
dead(true);
}
if(netServer.isWaitingForPlayers()){
dead(true);
}
if(!dead() && isOutOfBounds()){
destructTime += Time.delta();
if(destructTime >= boundsCountdown){
kill();
}
}else{
destructTime = 0f;
}
if(!dead() && isFlying()){
loops.play(Sounds.thruster, this, Mathf.clamp(velocity.len() * 2f) * 0.3f);
}
BuildRequest request = buildRequest();
if(isBuilding() && isBuilding && request.tile() != null && (request.tile().withinDst(x, y, placeDistance) || state.isEditor())){
loops.play(Sounds.build, request.tile(), 0.75f);
}
if(dead()){
isBoosting = false;
boostHeat = 0f;
if(respawns > 0 || !state.rules.limitedRespawns){
updateRespawning();
}
return;
}else{
spawner = null;
}
if(isLocal || net.server()){
avoidOthers();
}
Tile tile = world.tileWorld(x, y);
boostHeat = Mathf.lerpDelta(boostHeat, (tile != null && tile.solid()) || (isBoosting && ((!movement.isZero() && moved) || !isLocal)) ? 1f : 0f, 0.08f);
shootHeat = Mathf.lerpDelta(shootHeat, isShooting() ? 1f : 0f, 0.06f);
mech.update(this); //updated regardless
if(boostHeat > liftoffBoost + 0.1f){
achievedFlight = true;
}
if(boostHeat <= liftoffBoost + 0.05f && achievedFlight && !mech.flying){
if(tile != null){
Fx.unitLand.at(x, y, tile.floor().isLiquid ? 1f : 0.5f, tile.floor().color);
}
mech.onLand(this);
achievedFlight = false;
}
if(!isLocal){
interpolate();
updateMechanics(); //building happens even with non-locals
status.update(this); //status effect updating also happens with non locals for effect purposes
updateVelocityStatus(); //velocity too, for visual purposes
if(net.server()){
updateShooting(); //server simulates player shooting
}
return;
}else if(world.isZone()){
//unlock mech when used
data.unlockContent(mech);
}
if(control.input instanceof MobileInput){
updateTouch();
}else{
updateKeyboard();
}
isTyping = ui.chatfrag.shown();
updateMechanics();
if(!mech.flying){
clampPosition();
}
}
protected void updateKeyboard(){
Tile tile = world.tileWorld(x, y);
boolean canMove = !Core.scene.hasKeyboard() || ui.minimapfrag.shown();
isBoosting = Core.input.keyDown(Binding.dash) && !mech.flying;
//if player is in solid block
if(tile != null && tile.solid()){
isBoosting = true;
}
float speed = isBoosting && !mech.flying ? mech.boostSpeed : mech.speed;
if(mech.flying){
//prevent strafing backwards, have a penalty for doing so
float penalty = 0.2f; //when going 180 degrees backwards, reduce speed to 0.2x
speed *= Mathf.lerp(1f, penalty, Angles.angleDist(rotation, velocity.angle()) / 180f);
}
movement.setZero();
float xa = Core.input.axis(Binding.move_x);
float ya = Core.input.axis(Binding.move_y);
if(!(Core.scene.getKeyboardFocus() instanceof TextField)){
movement.y += ya * speed;
movement.x += xa * speed;
}
if(Core.input.keyDown(Binding.mouse_move)){
movement.x += Mathf.clamp((Core.input.mouseX() - Core.graphics.getWidth() / 2f) * 0.005f, -1, 1) * speed;
movement.y += Mathf.clamp((Core.input.mouseY() - Core.graphics.getHeight() / 2f) * 0.005f, -1, 1) * speed;
}
Vec2 vec = Core.input.mouseWorld(control.input.getMouseX(), control.input.getMouseY());
pointerX = vec.x;
pointerY = vec.y;
updateShooting();
movement.limit(speed).scl(Time.delta());
if(canMove){
velocity.add(movement.x, movement.y);
}else{
isShooting = false;
}
float prex = x, prey = y;
updateVelocityStatus();
moved = dst(prex, prey) > 0.001f;
if(canMove){
float baseLerp = mech.getRotationAlpha(this);
if(!isShooting() || !mech.faceTarget){
if(!movement.isZero()){
rotation = Mathf.slerpDelta(rotation, mech.flying ? velocity.angle() : movement.angle(), 0.13f * baseLerp);
}
}else{
float angle = control.input.mouseAngle(x, y);
this.rotation = Mathf.slerpDelta(this.rotation, angle, 0.1f * baseLerp);
}
}
}
protected void updateShooting(){
if(!state.isEditor() && isShooting() && mech.canShoot(this)){
weapons.update(this);
//if(!mech.turnCursor){
//shoot forward ignoring cursor
//mech.weapon.update(this, x + Angles.trnsx(rotation, mech.weapon.targetDistance), y + Angles.trnsy(rotation, mech.weapon.targetDistance));
//}else{
//mech.weapon.update(this, pointerX, pointerY);
//}
}
}
protected void updateTouch(){
if(Units.invalidateTarget(target, this) &&
!(target instanceof Tilec && ((Tilec)target).damaged() && target.isValid() && target.team() == team && mech.canHeal && dst(target) < mech.range && !(((Tilec)target).block instanceof BuildBlock))){
target = null;
}
if(state.isEditor()){
target = null;
}
float targetX = Core.camera.position.x, targetY = Core.camera.position.y;
float attractDst = 15f;
float speed = isBoosting && !mech.flying ? mech.boostSpeed : mech.speed;
if(moveTarget != null && !moveTarget.dead()){
targetX = moveTarget.getX();
targetY = moveTarget.getY();
boolean tapping = moveTarget instanceof Tilec && moveTarget.team() == team;
attractDst = 0f;
if(tapping){
velocity.setAngle(angleTo(moveTarget));
}
if(dst(moveTarget) <= 2f * Time.delta()){
if(tapping && !dead()){
Tile tile = ((Tilec)moveTarget).tile;
tile.block().tapped(tile, this);
}
moveTarget = null;
}
}else{
moveTarget = null;
}
movement.set((targetX - x) / Time.delta(), (targetY - y) / Time.delta()).limit(speed);
movement.setAngle(Mathf.slerp(movement.angle(), velocity.angle(), 0.05f));
if(dst(targetX, targetY) < attractDst){
movement.setZero();
}
float expansion = 3f;
hitbox(rect);
rect.x -= expansion;
rect.y -= expansion;
rect.width += expansion * 2f;
rect.height += expansion * 2f;
isBoosting = collisions.overlapsTile(rect) || dst(targetX, targetY) > 85f;
velocity.add(movement.scl(Time.delta()));
if(velocity.len() <= 0.2f && mech.flying){
rotation += Mathf.sin(Time.time() + id * 99, 10f, 1f);
}else if(target == null){
rotation = Mathf.slerpDelta(rotation, velocity.angle(), velocity.len() / 10f);
}
float lx = x, ly = y;
updateVelocityStatus();
moved = dst(lx, ly) > 0.001f;
if(mech.flying){
//hovering effect
x += Mathf.sin(Time.time() + id * 999, 25f, 0.08f);
y += Mathf.cos(Time.time() + id * 999, 25f, 0.08f);
}
//update shooting if not building, not mining and there's ammo left
if(!isBuilding() && getMineTile() == null){
//autofire
if(target == null){
isShooting = false;
if(Core.settings.getBool("autotarget")){
target = Units.closestTarget(team, x, y, mech.range, u -> u.team() != Team.derelict, u -> u.getTeam() != Team.derelict);
if(mech.canHeal && target == null){
target = Geometry.findClosest(x, y, indexer.getDamaged(Team.sharded));
if(target != null && dst(target) > mech.range){
target = null;
}else if(target != null){
target = ((Tile)target).entity;
}
}
if(target != null){
setMineTile(null);
}
}
}else if(target.isValid() || (target instanceof Tilec && ((Tilec)target).damaged() && target.team() == team && mech.canHeal && dst(target) < mech.range)){
//rotate toward and shoot the target
if(mech.faceTarget){
rotation = Mathf.slerpDelta(rotation, angleTo(target), 0.2f);
}
Vec2 intercept = Predict.intercept(this, target, getWeapon().bullet.speed);
pointerX = intercept.x;
pointerY = intercept.y;
updateShooting();
isShooting = true;
}
}
}
//endregion
//region utility methods
public void sendMessage(String text){
if(isLocal){
if(Vars.ui != null){
Vars.ui.chatfrag.addMessage(text, null);
}
}else{
Call.sendMessage(con, text, null, null);
}
}
public void sendMessage(String text, Player from){
sendMessage(text, from, NetClient.colorizeName(from.id, from.name));
}
public void sendMessage(String text, Player from, String fromName){
if(isLocal){
if(Vars.ui != null){
Vars.ui.chatfrag.addMessage(text, fromName);
}
}else{
Call.sendMessage(con, text, fromName, from);
}
}
public PlayerInfo getInfo(){
if(uuid == null){
throw new IllegalArgumentException("Local players cannot be traced and do not have info.");
}else{
return netServer.admins.getInfo(uuid);
}
}
/** Resets all values of the player. */
public void reset(){
resetNoAdd();
add();
}
public void resetNoAdd(){
status.clear();
team = Team.sharded;
item.amount = 0;
placeQueue.clear();
dead = true;
lastText = null;
isBuilding = true;
textFadeTime = 0f;
target = null;
moveTarget = null;
isShooting = isBoosting = isTransferring = isTyping = false;
spawner = lastSpawner = null;
health = maxHealth();
mining = null;
boostHeat = drownTime = hitTime = 0f;
mech = Mechs.starter;
placeQueue.clear();
respawns = state.rules.respawns;
}
public boolean isShooting(){
return isShooting && (boostHeat < 0.1f || mech.flying) && mining == null;
}
public void updateRespawning(){
if(state.isEditor()){
//instant respawn at center of map.
set(world.width() * tilesize/2f, world.height() * tilesize/2f);
dead(false);
}else if(spawner != null && spawner.isValid()){
spawner.updateSpawning(this);
}else if(!netServer.isWaitingForPlayers()){
if(!net.client()){
if(lastSpawner != null && lastSpawner.isValid()){
this.spawner = lastSpawner;
}else if(closestCore() != null){
this.spawner = (SpawnerTrait)closestCore();
}
}
}else if(closestCore() != null){
set(closestCore().getX(), closestCore().getY());
}
}
public void beginRespawning(SpawnerTrait spawner){
this.spawner = spawner;
this.lastSpawner = spawner;
this.dead = true;
setNet(spawner.getX(), spawner.getY());
spawner.updateSpawning(this);
}
//endregion
//region read and write methods
@Override
public byte version(){
return 0;
}
@Override
public void writeSave(DataOutput stream) throws IOException{
stream.writeBoolean(isLocal);
if(isLocal){
stream.writeByte(mech.id);
stream.writeInt(lastSpawner == null ? noSpawner : lastSpawner.getTile().pos());
super.writeSave(stream, false);
}
}
@Override
public void readSave(DataInput stream, byte version) throws IOException{
boolean local = stream.readBoolean();
if(local){
byte mechid = stream.readByte();
int spawner = stream.readInt();
Tile stile = world.tile(spawner);
Player player = headless ? this : Vars.player;
player.readSaveSuper(stream, version);
player.mech = content.getByID(ContentType.mech, mechid);
player.dead = false;
if(stile != null && stile.entity instanceof SpawnerTrait){
player.lastSpawner = (SpawnerTrait)stile.entity;
}
}
}
private void readSaveSuper(DataInput stream, byte version) throws IOException{
super.readSave(stream, version);
add();
}
@Override
public void write(DataOutput buffer) throws IOException{
super.writeSave(buffer, !isLocal);
TypeIO.writeStringData(buffer, name);
buffer.writeByte(Pack.byteValue(isAdmin) | (Pack.byteValue(dead) << 1) | (Pack.byteValue(isBoosting) << 2) | (Pack.byteValue(isTyping) << 3)| (Pack.byteValue(isBuilding) << 4));
buffer.writeInt(Color.rgba8888(color));
buffer.writeByte(mech.id);
buffer.writeInt(mining == null ? noSpawner : mining.pos());
buffer.writeInt(spawner == null || !spawner.hasUnit(this) ? noSpawner : spawner.getTile().pos());
buffer.writeShort((short)(baseRotation * 2));
writeBuilding(buffer);
}
@Override
public void read(DataInput buffer) throws IOException{
float lastx = x, lasty = y, lastrot = rotation, lastvx = velocity.x, lastvy = velocity.y;
super.readSave(buffer, version());
name = TypeIO.readStringData(buffer);
byte bools = buffer.readByte();
isAdmin = (bools & 1) != 0;
dead = (bools & 2) != 0;
boolean boosting = (bools & 4) != 0;
isTyping = (bools & 8) != 0;
boolean building = (bools & 16) != 0;
color.set(buffer.readInt());
mech = content.getByID(ContentType.mech, buffer.readByte());
int mine = buffer.readInt();
int spawner = buffer.readInt();
float baseRotation = buffer.readShort() / 2f;
readBuilding(buffer, !isLocal);
interpolator.read(lastx, lasty, x, y, rotation, baseRotation);
rotation = lastrot;
x = lastx;
y = lasty;
if(isLocal){
velocity.x = lastvx;
velocity.y = lastvy;
}else{
mining = world.tile(mine);
isBuilding = building;
isBoosting = boosting;
}
Tile tile = world.tile(spawner);
if(tile != null && tile.entity instanceof SpawnerTrait){
this.spawner = (SpawnerTrait)tile.entity;
}else{
this.spawner = null;
}
}
//endregion
}
@@ -1,68 +0,0 @@
package mindustry.entities.type.base;
import arc.math.Mathf;
import arc.math.geom.Geometry;
import mindustry.entities.units.*;
import mindustry.world.Tile;
import mindustry.world.meta.BlockFlag;
import static mindustry.Vars.*;
public abstract class BaseDrone extends FlyingUnit{
public final StateMachine.UnitState retreat = new StateMachine.UnitState(){
public void entered(){
target = null;
}
public void update(){
if(health >= maxHealth()){
state.set(getStartState());
}else if(!targetHasFlag(BlockFlag.repair)){
if(retarget()){
Tile repairPoint = Geometry.findClosest(x, y, indexer.getAllied(team, BlockFlag.repair));
if(repairPoint != null){
target = repairPoint;
}else{
setState(getStartState());
}
}
}else{
circle(40f);
}
}
};
@Override
public boolean countsAsEnemy(){
return false;
}
@Override
public void onCommand(UnitCommand command){
//do nothing, normal commands are not applicable here
}
@Override
protected void updateRotation(){
if(target != null && shouldRotate() && target.dst(this) < type.range){
rotation = Mathf.slerpDelta(rotation, angleTo(target), 0.3f);
}else{
rotation = Mathf.slerpDelta(rotation, velocity.angle(), 0.3f);
}
}
@Override
public void behavior(){
if(health <= maxHealth() * type.retreatPercent && !state.is(retreat) && Geometry.findClosest(x, y, indexer.getAllied(team, BlockFlag.repair)) != null){
setState(retreat);
}
}
public boolean shouldRotate(){
return state.is(getStartState());
}
@Override
public abstract StateMachine.UnitState getStartState();
}
@@ -1,238 +0,0 @@
package mindustry.entities.type.base;
import arc.*;
import arc.math.*;
import arc.struct.*;
import arc.util.*;
import mindustry.entities.*;
import mindustry.entities.type.*;
import mindustry.entities.units.*;
import mindustry.game.EventType.*;
import mindustry.game.Teams.*;
import mindustry.world.*;
import mindustry.world.blocks.*;
import mindustry.world.blocks.BuildBlock.*;
import java.io.*;
import static mindustry.Vars.*;
public class BuilderDrone extends BaseDrone implements BuilderTrait{
private static final StaticReset reset = new StaticReset();
private static final IntIntMap totals = new IntIntMap();
protected Queue<BuildRequest> placeQueue = new Queue<>();
protected BuildRequest lastFound;
protected boolean isBreaking;
protected Player playerTarget;
public final StateMachine.UnitState
build = new StateMachine.UnitState(){
public void entered(){
if(!(target instanceof BuildEntity)){
target = null;
}
}
public void update(){
BuildEntity entity = (BuildEntity)target;
Tilec core = getClosestCore();
if(isBuilding() && entity == null && canRebuild()){
target = world.tile(buildRequest().x, buildRequest().y);
circle(placeDistance * 0.7f);
target = null;
BuildRequest request = buildRequest();
if(world.tile(request.x, request.y).entity instanceof BuildEntity){
target = world.tile(request.x, request.y).entity;
}
}else if(entity != null && core != null && (entity.progress < 1f || entity.progress > 0f) && entity.tile.block() instanceof BuildBlock){ //building is valid
if(!isBuilding() && dst(target) < placeDistance * 0.9f){ //within distance, begin placing
if(isBreaking){
buildQueue().addLast(new BuildRequest(entity.tile.x, entity.tile.y));
}else{
buildQueue().addLast(new BuildRequest(entity.tile.x, entity.tile.y, entity.tile.rotation(), entity.cblock));
if(lastFound != null && lastFound.hasConfig){
buildQueue().last().configure(lastFound.config);
}
}
}
circle(placeDistance * 0.7f);
velocity.scl(0.74f);
}else{ //else, building isn't valid, follow a player
target = null;
if(playerTarget == null || playerTarget.team() != team || !playerTarget.isValid()){
playerTarget = null;
if(retarget()){
float minDst = Float.POSITIVE_INFINITY;
int minDrones = Integer.MAX_VALUE;
//find player with min amount of drones
for(Player player : playerGroup.all()){
if(player.team() == team){
int drones = getDrones(player);
float dst = dst2(player);
if(playerTarget == null || drones < minDrones || (drones == minDrones && dst < minDst)){
minDrones = drones;
minDst = dst;
playerTarget = player;
}
}
}
}
if(getSpawner() != null){
target = getSpawner();
circle(40f);
target = null;
}
}else{
incDrones(playerTarget);
Teamc prev = target;
target = playerTarget;
float dst = 90f + (id % 10)*3;
float tdst = dst(target);
float scale = (Mathf.lerp(1f, 0.2f, 1f - Mathf.clamp((tdst - dst) / dst)));
circle(dst);
velocity.scl(scale);
target = prev;
}
}
}
};
public BuilderDrone(){
if(reset.check()){
Events.on(BuildSelectEvent.class, event -> {
if(!(event.tile.entity instanceof BuildEntity)) return;
for(BaseUnit unit : unitGroup.all()){
if(unit instanceof BuilderDrone && unit.getTeam() == getTeam()){
BuilderDrone drone = (BuilderDrone)unit;
if(drone.isBuilding()){
//stop building if opposite building begins.
BuildRequest req = drone.buildRequest();
if(req.breaking != event.breaking && req.x == event.tile.x && req.y == event.tile.y){
drone.clearBuilding();
drone.target = null;
}
}
}
}
});
}
}
int getDrones(Player player){
return Pack.leftShort(totals.get(player.id, 0));
}
void incDrones(Player player){
int num = totals.get(player.id, 0);
int amount = Pack.leftShort(num), frame = Pack.rightShort(num);
short curFrame = (short)(Core.graphics.getFrameId() % Short.MAX_VALUE);
if(frame != curFrame){
totals.put(player.id, Pack.shortInt((short)1, curFrame));
}else{
totals.put(player.id, Pack.shortInt((short)(amount + 1), curFrame));
}
}
boolean canRebuild(){
return true;
}
@Override
public float getBuildPower(Tile tile){
return type.buildPower;
}
@Override
public Queue<BuildRequest> buildQueue(){
return placeQueue;
}
@Override
public void update(){
super.update();
if(!isBuilding() && timer.get(timerTarget2, 15)){
for(Player player : playerGroup.all()){
if(player.team() == team && player.buildRequest() != null){
BuildRequest req = player.buildRequest();
Tile tile = world.tile(req.x, req.y);
if(tile != null && tile.entity instanceof BuildEntity){
BuildEntity b = tile.ent();
float dist = Math.min(b.dst(x, y) - placeDistance, 0);
if(dist / type.maxVelocity < b.buildCost * 0.9f){
lastFound = req;
target = b;
this.isBreaking = req.breaking;
setState(build);
break;
}
}
}
}
if(timer.get(timerTarget, 80) && Units.closestEnemy(getTeam(), x, y, 100f, u -> !(u instanceof BaseDrone)) == null && !isBuilding()){
TeamData data = team.data();
if(!data.brokenBlocks.isEmpty()){
BrokenBlock block = data.brokenBlocks.removeLast();
if(Build.validPlace(getTeam(), block.x, block.y, content.block(block.block), block.rotation)){
placeQueue.addFirst(new BuildRequest(block.x, block.y, block.rotation, content.block(block.block)).configure(block.config));
setState(build);
}
}
}
}
updateBuilding();
}
@Override
public boolean shouldRotate(){
return isBuilding();
}
@Override
public StateMachine.UnitState getStartState(){
return build;
}
@Override
public void drawOver(){
drawBuilding();
}
@Override
public float drawSize(){
return isBuilding() ? placeDistance * 2f : 30f;
}
@Override
public boolean canCreateBlocks(){
return true;
}
@Override
public void write(DataOutput data) throws IOException{
super.write(data);
writeBuilding(data);
}
@Override
public void read(DataInput data) throws IOException{
super.read(data);
readBuilding(data);
}
}
@@ -1,255 +0,0 @@
package mindustry.entities.type.base;
import arc.graphics.*;
import arc.graphics.g2d.*;
import arc.math.*;
import arc.math.geom.*;
import arc.util.*;
import mindustry.*;
import mindustry.entities.*;
import mindustry.entities.bullet.*;
import mindustry.entities.units.*;
import mindustry.graphics.*;
import mindustry.world.*;
import mindustry.world.meta.*;
import static mindustry.Vars.*;
public class FlyingUnit extends BaseUnit{
protected float[] weaponAngles = {0,0};
protected final StateMachine.UnitState
attack = new StateMachine.UnitState(){
public void entered(){
target = null;
}
public void update(){
if(Units.invalidateTarget(target, team, x, y)){
target = null;
}
if(retarget()){
targetClosest();
if(target == null) targetClosestEnemyFlag(BlockFlag.producer);
if(target == null) targetClosestEnemyFlag(BlockFlag.turret);
if(target == null && isCommanded() && getCommand() != UnitCommand.attack){
onCommand(getCommand());
}
}
if(getClosestSpawner() == null && getSpawner() != null && target == null){
target = getSpawner();
circle(80f + Mathf.randomSeed(id) * 120);
}else if(target != null){
attack(type.attackLength);
if((Angles.near(angleTo(target), rotation, type.shootCone) || getWeapon().ignoreRotation) //bombers and such don't care about rotation
&& dst(target) < getWeapon().bullet.range()){
BulletType ammo = getWeapon().bullet;
if(type.rotateWeapon){
for(boolean left : Mathf.booleans){
int wi = Mathf.num(left);
float wx = x + Angles.trnsx(rotation - 90, getWeapon().width * Mathf.sign(left));
float wy = y + Angles.trnsy(rotation - 90, getWeapon().width * Mathf.sign(left));
weaponAngles[wi] = Mathf.slerpDelta(weaponAngles[wi], Angles.angle(wx, wy, target.getX(), target.getY()), 0.1f);
Tmp.v2.trns(weaponAngles[wi], getWeapon().length);
getWeapon().update(FlyingUnit.this, wx + Tmp.v2.x, wy + Tmp.v2.y, weaponAngles[wi], left);
}
}else{
Vec2 to = Predict.intercept(FlyingUnit.this, target, ammo.speed);
getWeapon().update(FlyingUnit.this, to.x, to.y);
}
}
}else{
target = getClosestSpawner();
moveTo(Vars.state.rules.dropZoneRadius + 120f);
}
}
},
rally = new StateMachine.UnitState(){
public void update(){
if(retarget()){
targetClosestAllyFlag(BlockFlag.rally);
targetClosest();
if(target != null && !Units.invalidateTarget(target, team, x, y)){
setState(attack);
return;
}
if(target == null) target = getSpawner();
}
if(target != null){
circle(65f + Mathf.randomSeed(id) * 100);
}
}
},
retreat = new StateMachine.UnitState(){
public void entered(){
target = null;
}
public void update(){
if(retarget()){
target = getSpawner();
Tile repair = Geometry.findClosest(x, y, indexer.getAllied(team, BlockFlag.repair));
if(repair != null && damaged()) FlyingUnit.this.target = repair.entity;
if(target == null) target = getClosestCore();
}
circle(targetHasFlag(BlockFlag.repair) ? 20f : 60f + Mathf.randomSeed(id) * 50, 0.65f * type.speed);
}
};;
@Override
public void onCommand(UnitCommand command){
state.set(command == UnitCommand.retreat ? retreat :
command == UnitCommand.attack ? attack :
command == UnitCommand.rally ? rally :
null);
}
@Override
public void move(float x, float y){
moveBy(x, y);
}
@Override
public void update(){
super.update();
if(!net.client()){
updateRotation();
}
wobble();
}
@Override
public void drawUnder(){
drawEngine();
}
@Override
public void draw(){
Draw.mixcol(Color.white, hitTime / hitDuration);
Draw.rect(type.region, x, y, rotation - 90);
drawWeapons();
Draw.mixcol();
}
public void drawWeapons(){
for(int i : Mathf.signs){
float tra = rotation - 90, trY = -type.weapon.getRecoil(this, i > 0) + type.weaponOffsetY;
float w = -i * type.weapon.region.getWidth() * Draw.scl;
Draw.rect(type.weapon.region,
x + Angles.trnsx(tra, getWeapon().width * i, trY),
y + Angles.trnsy(tra, getWeapon().width * i, trY), w, type.weapon.region.getHeight() * Draw.scl, rotation - 90);
}
}
public void drawEngine(){
Draw.color(Pal.engine);
Fill.circle(x + Angles.trnsx(rotation + 180, type.engineOffset), y + Angles.trnsy(rotation + 180, type.engineOffset),
type.engineSize + Mathf.absin(Time.time(), 2f, type.engineSize / 4f));
Draw.color(Color.white);
Fill.circle(x + Angles.trnsx(rotation + 180, type.engineOffset - 1f), y + Angles.trnsy(rotation + 180, type.engineOffset - 1f),
(type.engineSize + Mathf.absin(Time.time(), 2f, type.engineSize / 4f)) / 2f);
Draw.color();
}
@Override
public void behavior(){
if(Units.invalidateTarget(target, this)){
for(boolean left : Mathf.booleans){
int wi = Mathf.num(left);
weaponAngles[wi] = Mathf.slerpDelta(weaponAngles[wi], rotation, 0.1f);
}
}
}
@Override
public StateMachine.UnitState getStartState(){
return attack;
}
protected void wobble(){
if(net.client()) return;
x += Mathf.sin(Time.time() + id * 999, 25f, 0.05f) * Time.delta();
y += Mathf.cos(Time.time() + id * 999, 25f, 0.05f) * Time.delta();
if(velocity.len() <= 0.05f){
//rotation += Mathf.sin(Time.time() + id * 99, 10f, 2f * type.speed)*Time.delta();
}
}
protected void updateRotation(){
rotation = velocity.angle();
}
protected void circle(float circleLength){
circle(circleLength, type.speed);
}
protected void circle(float circleLength, float speed){
if(target == null) return;
Tmp.v1.set(target.getX() - x, target.getY() - y);
if(Tmp.v1.len() < circleLength){
Tmp.v1.rotate((circleLength - Tmp.v1.len()) / circleLength * 180f);
}
Tmp.v1.setLength(speed * Time.delta());
velocity.add(Tmp.v1);
}
protected void moveTo(float circleLength){
if(target == null) return;
Tmp.v1.set(target.getX() - x, target.getY() - y);
float length = circleLength <= 0.001f ? 1f : Mathf.clamp((dst(target) - circleLength) / 100f, -1f, 1f);
Tmp.v1.setLength(type.speed * Time.delta() * length);
if(length < -0.5f){
Tmp.v1.rotate(180f);
}else if(length < 0){
Tmp.v1.setZero();
}
velocity.add(Tmp.v1);
}
protected void attack(float circleLength){
Tmp.v1.set(target.getX() - x, target.getY() - y);
float ang = angleTo(target);
float diff = Angles.angleDist(ang, rotation);
if(diff > 100f && Tmp.v1.len() < circleLength){
Tmp.v1.setAngle(velocity.angle());
}else{
Tmp.v1.setAngle(Mathf.slerpDelta(velocity.angle(), Tmp.v1.angle(), 0.44f));
}
Tmp.v1.setLength(type.speed * Time.delta());
velocity.add(Tmp.v1);
}
}
@@ -1,259 +0,0 @@
package mindustry.entities.type.base;
import arc.graphics.*;
import arc.graphics.g2d.*;
import arc.math.*;
import arc.math.geom.*;
import arc.util.*;
import mindustry.*;
import mindustry.ai.Pathfinder.*;
import mindustry.entities.*;
import mindustry.entities.bullet.*;
import mindustry.entities.units.*;
import mindustry.game.*;
import mindustry.world.*;
import mindustry.world.blocks.*;
import mindustry.world.meta.*;
import static mindustry.Vars.*;
public class GroundUnit extends BaseUnit{
protected static Vec2 vec = new Vec2();
protected float walkTime;
protected float stuckTime;
protected float baseRotation;
public final StateMachine.UnitState
attack = new StateMachine.UnitState(){
public void entered(){
target = null;
}
public void update(){
Tilec core = getClosestEnemyCore();
if(core == null){
Tile closestSpawn = getClosestSpawner();
if(closestSpawn == null || !withinDst(closestSpawn, Vars.state.rules.dropZoneRadius + 85f)){
moveToCore(PathTarget.enemyCores);
}
}else{
float dst = dst(core);
if(dst < getWeapon().bullet.range() / 1.1f){
target = core;
}
if(dst > getWeapon().bullet.range() * 0.5f){
moveToCore(PathTarget.enemyCores);
}
}
}
},
rally = new StateMachine.UnitState(){
public void update(){
Tile target = getClosest(BlockFlag.rally);
if(target != null && dst(target) > 80f){
moveToCore(PathTarget.rallyPoints);
}
}
},
retreat = new StateMachine.UnitState(){
public void entered(){
target = null;
}
public void update(){
moveAwayFromCore();
}
};
@Override
public void onCommand(UnitCommand command){
state.set(command == UnitCommand.retreat ? retreat :
command == UnitCommand.attack ? attack :
command == UnitCommand.rally ? rally :
null);
}
@Override
public void interpolate(){
super.interpolate();
if(interpolator.values.length > 1){
baseRotation = interpolator.values[1];
}
}
@Override
public void move(float x, float y){
float dst = Mathf.dst(x, y);
if(dst > 0.01f){
baseRotation = Mathf.slerp(baseRotation, Mathf.angle(x, y), type.baseRotateSpeed * (dst / type.speed));
}
super.move(x, y);
}
@Override
public StateMachine.UnitState getStartState(){
return attack;
}
@Override
public void update(){
super.update();
stuckTime = !vec.set(x, y).sub(lastPosition()).isZero(0.0001f) ? 0f : stuckTime + Time.delta();
if(!velocity.isZero()){
baseRotation = Mathf.slerpDelta(baseRotation, velocity.angle(), 0.05f);
}
if(stuckTime < 1f){
walkTime += Time.delta();
}
}
@Override
public void draw(){
Draw.mixcol(Color.white, hitTime / hitDuration);
float ft = Mathf.sin(walkTime * type.speed * 5f, 6f, 2f + type.hitsize / 15f);
Floor floor = getFloorOn();
if(floor.isLiquid){
Draw.color(Color.white, floor.color, 0.5f);
}
for(int i : Mathf.signs){
Draw.rect(type.legRegion,
x + Angles.trnsx(baseRotation, ft * i),
y + Angles.trnsy(baseRotation, ft * i),
type.legRegion.getWidth() * i * Draw.scl, type.legRegion.getHeight() * Draw.scl - Mathf.clamp(ft * i, 0, 2), baseRotation - 90);
}
if(floor.isLiquid){
Draw.color(Color.white, floor.color, drownTime * 0.4f);
}else{
Draw.color(Color.white);
}
Draw.rect(type.baseRegion, x, y, baseRotation - 90);
Draw.rect(type.region, x, y, rotation - 90);
for(int i : Mathf.signs){
float tra = rotation - 90, trY = -type.weapon.getRecoil(this, i > 0) + type.weaponOffsetY;
float w = -i * type.weapon.region.getWidth() * Draw.scl;
Draw.rect(type.weapon.region,
x + Angles.trnsx(tra, getWeapon().width * i, trY),
y + Angles.trnsy(tra, getWeapon().width * i, trY), w, type.weapon.region.getHeight() * Draw.scl, rotation - 90);
}
Draw.mixcol();
}
@Override
public void behavior(){
if(!Units.invalidateTarget(target, this)){
if(dst(target) < getWeapon().bullet.range()){
rotate(angleTo(target));
if(Angles.near(angleTo(target), rotation, 13f)){
BulletType ammo = getWeapon().bullet;
Vec2 to = Predict.intercept(GroundUnit.this, target, ammo.speed);
getWeapon().update(GroundUnit.this, to.x, to.y);
}
}
}
}
@Override
public void updateTargeting(){
super.updateTargeting();
if(Units.invalidateTarget(target, team, x, y, Float.MAX_VALUE)){
target = null;
}
if(retarget()){
targetClosest();
}
}
protected void patrol(){
vec.trns(baseRotation, type.speed * Time.delta());
velocity.add(vec.x, vec.y);
vec.trns(baseRotation, type.hitsizeTile * 5);
Tile tile = world.tileWorld(x + vec.x, y + vec.y);
if((tile == null || tile.solid() || tile.floor().drownTime > 0 || tile.floor().isLiquid) || stuckTime > 10f){
baseRotation += Mathf.sign(id % 2 - 0.5f) * Time.delta() * 3f;
}
rotation = Mathf.slerpDelta(rotation, velocity.angle(), type.rotatespeed);
}
protected void circle(float circleLength){
if(target == null) return;
vec.set(target.getX() - x, target.getY() - y);
if(vec.len() < circleLength){
vec.rotate((circleLength - vec.len()) / circleLength * 180f);
}
vec.setLength(type.speed * Time.delta());
velocity.add(vec);
}
protected void moveToCore(PathTarget path){
Tile tile = world.tileWorld(x, y);
if(tile == null) return;
Tile targetTile = pathfinder.getTargetTile(tile, team, path);
if(tile == targetTile) return;
velocity.add(vec.trns(angleTo(targetTile), type.speed * Time.delta()));
if(Units.invalidateTarget(target, this)){
rotation = Mathf.slerpDelta(rotation, baseRotation, type.rotatespeed);
}
}
protected void moveAwayFromCore(){
Team enemy = null;
for(Team team : team.enemies()){
if(team.active()){
enemy = team;
break;
}
}
if(enemy == null){
for(Team team : team.enemies()){
enemy = team;
break;
}
}
if(enemy == null) return;
Tile tile = world.tileWorld(x, y);
if(tile == null) return;
Tile targetTile = pathfinder.getTargetTile(tile, enemy, PathTarget.enemyCores);
Tilec core = getClosestCore();
if(tile == targetTile || core == null || dst(core) < 120f) return;
velocity.add(vec.trns(angleTo(targetTile), type.speed * Time.delta()));
rotation = Mathf.slerpDelta(rotation, baseRotation, type.rotatespeed);
}
}
@@ -1,34 +0,0 @@
package mindustry.entities.type.base;
import arc.graphics.g2d.Draw;
import arc.math.Angles;
import arc.math.Mathf;
import mindustry.entities.Units;
public class HoverUnit extends FlyingUnit{
@Override
public void drawWeapons(){
for(int i : Mathf.signs){
float tra = rotation - 90, trY = -getWeapon().getRecoil(this, i > 0) + type.weaponOffsetY;
float w = i > 0 ? -12 : 12;
float wx = x + Angles.trnsx(tra, getWeapon().width * i, trY), wy = y + Angles.trnsy(tra, getWeapon().width * i, trY);
int wi = (i + 1) / 2;
Draw.rect(getWeapon().region, wx, wy, w, 12, weaponAngles[wi] - 90);
}
}
@Override
protected void attack(float circleLength){
moveTo(circleLength);
}
@Override
protected void updateRotation(){
if(!Units.invalidateTarget(target, this)){
rotation = Mathf.slerpDelta(rotation, angleTo(target), type.rotatespeed);
}else{
rotation = Mathf.slerpDelta(rotation, velocity.angle(), type.baseRotateSpeed);
}
}
}
@@ -1,178 +0,0 @@
package mindustry.entities.type.base;
import arc.math.Mathf;
import arc.util.Structs;
import mindustry.content.Blocks;
import mindustry.gen.*;
import mindustry.entities.units.StateMachine.UnitState;
import mindustry.gen.Call;
import mindustry.type.Item;
import mindustry.type.ItemType;
import mindustry.world.Pos;
import mindustry.world.Tile;
import java.io.*;
import static mindustry.Vars.*;
/** A drone that only mines.*/
public class MinerDrone extends BaseDrone implements MinerTrait{
protected Item targetItem;
protected Tile mineTile;
public final UnitState
mine = new UnitState(){
public void entered(){
target = null;
}
public void update(){
Tilec entity = getClosestCore();
if(entity == null) return;
findItem();
//core full of the target item, do nothing
if(targetItem != null && entity.block.acceptStack(targetItem, 1, entity.tile, MinerDrone.this) == 0){
MinerDrone.this.clearItem();
return;
}
//if inventory is full, drop it off.
if(item.amount >= getItemCapacity() || (targetItem != null && !acceptsItem(targetItem))){
setState(drop);
}else{
if(retarget() && targetItem != null){
target = indexer.findClosestOre(x, y, targetItem);
}
if(target instanceof Tile){
moveTo(type.range / 1.5f);
if(dst(target) < type.range && mineTile != target){
setMineTile((Tile)target);
}
if(((Tile)target).block() != Blocks.air){
setState(drop);
}
}else{
//nothing to mine anymore, core full: circle spawnpoint
if(getSpawner() != null){
target = getSpawner();
circle(40f);
}
}
}
}
public void exited(){
setMineTile(null);
}
},
drop = new UnitState(){
public void entered(){
target = null;
}
public void update(){
if(item.amount == 0 || item.item.type != ItemType.material){
clearItem();
setState(mine);
return;
}
target = getClosestCore();
if(target == null) return;
Tilec tile = (Tilec)target;
if(dst(target) < type.range){
if(tile.tile.block().acceptStack(item.item, item.amount, tile.tile, MinerDrone.this) > 0){
Call.transferItemTo(item.item, item.amount, x, y, tile.tile);
}
clearItem();
setState(mine);
}
circle(type.range / 1.8f);
}
};
@Override
public UnitState getStartState(){
return mine;
}
@Override
public void update(){
super.update();
updateMining();
}
@Override
protected void updateRotation(){
if(mineTile != null && shouldRotate() && mineTile.dst(this) < type.range){
rotation = Mathf.slerpDelta(rotation, angleTo(mineTile), 0.3f);
}else{
rotation = Mathf.slerpDelta(rotation, velocity.angle(), 0.3f);
}
}
@Override
public boolean shouldRotate(){
return isMining();
}
@Override
public void drawOver(){
drawMining();
}
@Override
public boolean canMine(Item item){
return type.toMine.contains(item);
}
@Override
public float getMinePower(){
return type.minePower;
}
@Override
public Tile getMineTile(){
return mineTile;
}
@Override
public void setMineTile(Tile tile){
mineTile = tile;
}
@Override
public void write(DataOutput data) throws IOException{
super.write(data);
data.writeInt(mineTile == null || !state.is(mine) ? Pos.invalid : mineTile.pos());
}
@Override
public void read(DataInput data) throws IOException{
super.read(data);
mineTile = world.tile(data.readInt());
}
protected void findItem(){
Tilec entity = getClosestCore();
if(entity == null){
return;
}
targetItem = Structs.findMin(type.toMine, indexer::hasOre, (a, b) -> -Integer.compare(entity.items().get(a), entity.items().get(b)));
}
}
@@ -1,73 +0,0 @@
package mindustry.entities.type.base;
import mindustry.entities.Units;
import mindustry.gen.*;
import mindustry.entities.units.StateMachine.UnitState;
import mindustry.world.Pos;
import mindustry.world.Tile;
import mindustry.world.blocks.*;
import java.io.*;
import static mindustry.Vars.world;
public class RepairDrone extends BaseDrone{
public final UnitState repair = new UnitState(){
public void entered(){
target = null;
}
public void update(){
if(retarget()){
target = Units.findDamagedTile(team, x, y);
}
if(target instanceof Tilec && ((Tilec)target).block instanceof BuildBlock){
target = null;
}
if(target != null){
if(target.dst(RepairDrone.this) > type.range){
circle(type.range * 0.9f);
}else{
getWeapon().update(RepairDrone.this, target.getX(), target.getY());
}
}else{
//circle spawner if there's nothing to repair
if(getSpawner() != null){
target = getSpawner();
circle(type.range * 1.5f, type.speed/2f);
target = null;
}
}
}
};
@Override
public boolean shouldRotate(){
return target != null;
}
@Override
public UnitState getStartState(){
return repair;
}
@Override
public void write(DataOutput data) throws IOException{
super.write(data);
data.writeInt(state.is(repair) && target instanceof Tilec ? ((Tilec)target).tile.pos() : Pos.invalid);
}
@Override
public void read(DataInput data) throws IOException{
super.read(data);
Tile repairing = world.tile(data.readInt());
if(repairing != null){
target = repairing.entity;
}
}
}