Unit system rewrite, new drone AI

This commit is contained in:
Anuken
2018-06-03 12:12:18 -04:00
parent 12c746a4e4
commit 270dc351a1
29 changed files with 591 additions and 590 deletions

View File

@@ -63,7 +63,7 @@ public class Vars{
//whether turrets have infinite ammo (only with debug) //whether turrets have infinite ammo (only with debug)
public static boolean infiniteAmmo = true; public static boolean infiniteAmmo = true;
//whether to show paths of enemies //whether to show paths of enemies
public static boolean showPaths = true; public static boolean showPaths = false;
//if false, player is always hidden //if false, player is always hidden
public static boolean showPlayer = true; public static boolean showPlayer = true;
//whether to hide ui, only on debug //whether to hide ui, only on debug

View File

@@ -11,8 +11,29 @@ public class UnitTypes implements ContentList {
@Override @Override
public void load() { public void load() {
drone = new Drone(); drone = new UnitType("drone", team -> new Drone(drone, team)){{
scout = new Scout(); isFlying = true;
vtol = new Vtol(); drag = 0.01f;
speed = 0.2f;
maxVelocity = 0.8f;
range = 50f;
}};
scout = new UnitType("scout", team -> new Scout(scout, team)){{
maxVelocity = 1.1f;
speed = 0.1f;
drag = 0.4f;
range = 40f;
setAmmo(AmmoTypes.bulletIron);
}};
vtol = new UnitType("vtol", team -> new Vtol(vtol, team)){{
speed = 0.3f;
maxVelocity = 2f;
drag = 0.01f;
isFlying = true;
reload = 7;
setAmmo(AmmoTypes.bulletIron);
}};
} }
} }

View File

@@ -82,7 +82,7 @@ public class Logic extends Module {
//TODO spawn enemies //TODO spawn enemies
for(int i = 0; i < 10; i ++){ for(int i = 0; i < 10; i ++){
BaseUnit unit = new BaseUnit(UnitTypes.vtol, Team.red); BaseUnit unit = UnitTypes.vtol.create(Team.red);
Vector2 offset = new Vector2().setToRandomDirection().scl(world.width()/2f*tilesize).add(world.width()/2f*tilesize, world.height()/2f*tilesize); Vector2 offset = new Vector2().setToRandomDirection().scl(world.width()/2f*tilesize).add(world.width()/2f*tilesize, world.height()/2f*tilesize);
unit.inventory.addAmmo(AmmoTypes.bulletIron); unit.inventory.addAmmo(AmmoTypes.bulletIron);
unit.inventory.setInfiniteAmmo(true); unit.inventory.setInfiniteAmmo(true);

View File

@@ -6,6 +6,7 @@ import io.anuke.mindustry.Vars;
import io.anuke.mindustry.content.blocks.Blocks; import io.anuke.mindustry.content.blocks.Blocks;
import io.anuke.mindustry.content.fx.BlockFx; import io.anuke.mindustry.content.fx.BlockFx;
import io.anuke.mindustry.entities.effect.ItemTransfer; import io.anuke.mindustry.entities.effect.ItemTransfer;
import io.anuke.mindustry.game.EventType.BlockBuildEvent;
import io.anuke.mindustry.graphics.Palette; import io.anuke.mindustry.graphics.Palette;
import io.anuke.mindustry.type.Item; import io.anuke.mindustry.type.Item;
import io.anuke.mindustry.type.Recipe; import io.anuke.mindustry.type.Recipe;
@@ -14,6 +15,7 @@ import io.anuke.mindustry.world.Tile;
import io.anuke.mindustry.world.blocks.types.BuildBlock; import io.anuke.mindustry.world.blocks.types.BuildBlock;
import io.anuke.mindustry.world.blocks.types.BuildBlock.BuildEntity; import io.anuke.mindustry.world.blocks.types.BuildBlock.BuildEntity;
import io.anuke.ucore.core.Effects; import io.anuke.ucore.core.Effects;
import io.anuke.ucore.core.Events;
import io.anuke.ucore.core.Timers; import io.anuke.ucore.core.Timers;
import io.anuke.ucore.graphics.Draw; import io.anuke.ucore.graphics.Draw;
import io.anuke.ucore.graphics.Fill; import io.anuke.ucore.graphics.Fill;
@@ -26,8 +28,7 @@ import io.anuke.ucore.util.Translator;
import java.util.Arrays; import java.util.Arrays;
import static io.anuke.mindustry.Vars.tilesize; import static io.anuke.mindustry.Vars.*;
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 BlockBuilder { public interface BlockBuilder {
@@ -140,6 +141,9 @@ public interface BlockBuilder {
if(Build.validPlace(unit.team, current.x, current.y, current.recipe.result, current.rotation)){ if(Build.validPlace(unit.team, current.x, current.y, current.recipe.result, current.rotation)){
//if it's valid, place it //if it's valid, place it
Build.placeBlock(unit.team, current.x, current.y, current.recipe, current.rotation); Build.placeBlock(unit.team, current.x, current.y, current.recipe, current.rotation);
//fire place event.
threads.run(() -> Events.fire(BlockBuildEvent.class, unit.team, world.tile(current.x, current.y)));
}else{ }else{
//otherwise, skip it //otherwise, skip it
getPlaceQueue().removeFirst(); getPlaceQueue().removeFirst();

View File

@@ -69,8 +69,7 @@ public class Player extends Unit implements BlockBuilder {
public Player(){ public Player(){
hitbox.setSize(5); hitbox.setSize(5);
hitboxTile.setSize(4f); hitboxTile.setSize(4f);
maxhealth = 200;
heal(); heal();
} }
@@ -78,6 +77,10 @@ public class Player extends Unit implements BlockBuilder {
//region unit and event overrides, utility methods //region unit and event overrides, utility methods
@Override
public float getMaxHealth() {
return 200;
}
@Override @Override
public Tile getMineTile() { public Tile getMineTile() {

View File

@@ -41,7 +41,7 @@ public abstract class Unit extends SyncEntity implements SerializableEntity, Tar
@Override @Override
public boolean collides(SolidEntity other){ public boolean collides(SolidEntity other){
return other instanceof io.anuke.mindustry.entities.bullet.Bullet && state.teams.areEnemies((((Bullet) other).team), team); return other instanceof Bullet && state.teams.areEnemies((((Bullet) other).team), team);
} }
@Override @Override

View File

@@ -22,6 +22,10 @@ public class UnitInventory {
this.ammoCapacity = ammoCapacity; this.ammoCapacity = ammoCapacity;
} }
public boolean isInfiniteAmmo() {
return infiniteAmmo;
}
public void setInfiniteAmmo(boolean infinite){ public void setInfiniteAmmo(boolean infinite){
infiniteAmmo = infinite; infiniteAmmo = infinite;
} }
@@ -29,6 +33,7 @@ public class UnitInventory {
public void write(DataOutputStream stream) throws IOException { public void write(DataOutputStream stream) throws IOException {
stream.writeInt(item == null ? 0 : item.amount); stream.writeInt(item == null ? 0 : item.amount);
stream.writeByte(item == null ? 0 : item.item.id); stream.writeByte(item == null ? 0 : item.item.id);
stream.writeBoolean(infiniteAmmo);
stream.writeInt(totalAmmo); stream.writeInt(totalAmmo);
stream.writeByte(ammos.size); stream.writeByte(ammos.size);
for(int i = 0; i < ammos.size; i ++){ for(int i = 0; i < ammos.size; i ++){
@@ -40,6 +45,7 @@ public class UnitInventory {
public void read(DataInputStream stream) throws IOException { public void read(DataInputStream stream) throws IOException {
int iamount = stream.readInt(); int iamount = stream.readInt();
byte iid = stream.readByte(); byte iid = stream.readByte();
infiniteAmmo = stream.readBoolean();
this.totalAmmo = stream.readInt(); this.totalAmmo = stream.readInt();
byte ammoa = stream.readByte(); byte ammoa = stream.readByte();
for(int i = 0; i < ammoa; i ++){ for(int i = 0; i < ammoa; i ++){

View File

@@ -1,16 +1,22 @@
package io.anuke.mindustry.entities.units; package io.anuke.mindustry.entities.units;
import io.anuke.mindustry.content.fx.ExplosionFx;
import io.anuke.mindustry.entities.Targetable; import io.anuke.mindustry.entities.Targetable;
import io.anuke.mindustry.entities.TileEntity; import io.anuke.mindustry.entities.TileEntity;
import io.anuke.mindustry.entities.Unit; import io.anuke.mindustry.entities.Unit;
import io.anuke.mindustry.entities.bullet.Bullet; import io.anuke.mindustry.entities.bullet.Bullet;
import io.anuke.mindustry.entities.bullet.BulletType; import io.anuke.mindustry.entities.bullet.BulletType;
import io.anuke.mindustry.game.Team; import io.anuke.mindustry.game.Team;
import io.anuke.mindustry.net.Net;
import io.anuke.mindustry.type.AmmoType;
import io.anuke.mindustry.type.Item; import io.anuke.mindustry.type.Item;
import io.anuke.mindustry.world.BlockFlag; import io.anuke.mindustry.world.BlockFlag;
import io.anuke.mindustry.world.Tile;
import io.anuke.ucore.core.Effects; import io.anuke.ucore.core.Effects;
import io.anuke.ucore.core.Effects.Effect; import io.anuke.ucore.core.Effects.Effect;
import io.anuke.ucore.core.Timers;
import io.anuke.ucore.util.Angles; import io.anuke.ucore.util.Angles;
import io.anuke.ucore.util.Geometry;
import io.anuke.ucore.util.Mathf; import io.anuke.ucore.util.Mathf;
import io.anuke.ucore.util.Timer; import io.anuke.ucore.util.Timer;
@@ -19,14 +25,19 @@ import java.io.DataOutputStream;
import java.io.IOException; import java.io.IOException;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import static io.anuke.mindustry.Vars.unitGroups; import static io.anuke.mindustry.Vars.*;
public class BaseUnit extends Unit{ public abstract class BaseUnit extends Unit{
public UnitType type; private static int timerIndex = 0;
public Timer timer = new Timer(5);
public float walkTime = 0f; protected static final int timerTarget = timerIndex++;
public StateMachine state = new StateMachine(); protected static final int timerBoost = timerIndex++;
public Targetable target; protected static final int timerReload = timerIndex++;
protected UnitType type;
protected Timer timer = new Timer(5);
protected StateMachine state = new StateMachine();
protected Targetable target;
public BaseUnit(UnitType type, Team team){ public BaseUnit(UnitType type, Team team){
this.type = type; this.type = type;
@@ -52,15 +63,51 @@ public class BaseUnit extends Unit{
} }
public void setState(UnitState state){ public void setState(UnitState state){
this.state.set(this, state); this.state.set(state);
} }
public void retarget(Runnable run){ public void retarget(Runnable run){
if(timer.get(UnitType.timerTarget, 20)){ if(timer.get(timerTarget, 20)){
run.run(); run.run();
} }
} }
/**Only runs when the unit has a target.*/
public void behavior(){
}
public void updateTargeting(){
if(target == null || (target instanceof Unit && (target.isDead() || ((Unit)target).team == team))
|| (target instanceof TileEntity && ((TileEntity) target).tile.entity == null)){
target = null;
}
}
public void shoot(AmmoType type, float rotation, float translation){
Bullet.create(type.bullet, this,
x + Angles.trnsx(rotation, translation),
y + Angles.trnsy(rotation, translation), rotation);
Effects.effect(type.shootEffect, x + Angles.trnsx(rotation, translation),
y + Angles.trnsy(rotation, translation), rotation, this);
Effects.effect(type.smokeEffect, x + Angles.trnsx(rotation, translation),
y + Angles.trnsy(rotation, translation), rotation, this);
}
public void targetClosestAllyFlag(BlockFlag flag){
Tile target = Geometry.findClosest(x, y, world.indexer().getAllied(team, flag));
if (target != null) this.target = target.entity;
}
public UnitState getStartState(){
return null;
}
@Override
public float getMaxHealth() {
return type.health;
}
@Override @Override
public float getArmor() { public float getArmor() {
return type.armor; return type.armor;
@@ -99,22 +146,41 @@ public class BaseUnit extends Unit{
@Override @Override
public void update(){ public void update(){
type.update(this); if(hitTime > 0){
hitTime -= Timers.delta();
}
if(hitTime < 0) hitTime = 0;
if(Net.client()){
interpolate();
return;
}
updateTargeting();
state.update();
updateVelocityStatus(type.drag, type.maxVelocity);
if(target != null) behavior();
x = Mathf.clamp(x, 0, world.width() * tilesize);
y = Mathf.clamp(y, 0, world.height() * tilesize);
} }
@Override @Override
public void drawSmooth(){ public void drawSmooth(){
type.draw(this);
} }
@Override @Override
public void drawUnder(){ public void drawUnder(){
type.drawUnder(this);
} }
@Override @Override
public void drawOver(){ public void drawOver(){
type.drawOver(this);
} }
@Override @Override
@@ -130,27 +196,29 @@ public class BaseUnit extends Unit{
@Override @Override
public void onDeath(){ public void onDeath(){
super.onDeath(); super.onDeath();
type.onDeath(this);
Effects.effect(ExplosionFx.explosion, this);
Effects.shake(2f, 2f, this);
remove();
} }
@Override @Override
public void onRemoteDeath(){ public void onRemoteDeath(){
type.onRemoteDeath(this); onDeath();
} }
@Override @Override
public void removed(){ public void removed(){
type.removed(this);
} }
@Override @Override
public void added(){ public void added(){
maxhealth = type.health;
hitbox.solid = !isFlying(); hitbox.solid = !isFlying();
hitbox.setSize(type.hitsize); hitbox.setSize(type.hitsize);
hitboxTile.setSize(type.hitsizeTile); hitboxTile.setSize(type.hitsizeTile);
state.set(this, type.getStartState()); state.set(getStartState());
heal(); heal();
} }

View File

@@ -0,0 +1,168 @@
package io.anuke.mindustry.entities.units;
import io.anuke.mindustry.entities.Unit;
import io.anuke.mindustry.entities.Units;
import io.anuke.mindustry.game.Team;
import io.anuke.mindustry.graphics.Palette;
import io.anuke.mindustry.graphics.Trail;
import io.anuke.mindustry.type.AmmoType;
import io.anuke.mindustry.world.BlockFlag;
import io.anuke.mindustry.world.Tile;
import io.anuke.ucore.core.Timers;
import io.anuke.ucore.graphics.Draw;
import io.anuke.ucore.util.Angles;
import io.anuke.ucore.util.Geometry;
import io.anuke.ucore.util.Mathf;
import io.anuke.ucore.util.Translator;
import static io.anuke.mindustry.Vars.world;
public class FlyingUnit extends BaseUnit {
protected static Translator vec = new Translator();
protected static float maxAim = 30f;
protected static float wobblyness = 0.6f;
protected Trail trail = new Trail(16);
public FlyingUnit(UnitType type, Team team) {
super(type, team);
}
@Override
public void update() {
super.update();
rotation = velocity.angle();
trail.update(x + Angles.trnsx(rotation + 180f, 6f) + Mathf.range(wobblyness),
y + Angles.trnsy(rotation + 180f, 6f) + Mathf.range(wobblyness));
}
@Override
public void drawSmooth() {
Draw.alpha(hitTime / hitDuration);
Draw.rect(type.name, x, y, rotation - 90);
Draw.alpha(1f);
}
@Override
public void drawOver() {
trail.draw(Palette.lighterOrange, Palette.lightishOrange, 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 attack;
}
protected void circle(float circleLength){
vec.set(target.getX() - x, target.getY() - y);
if(vec.len() < circleLength){
vec.rotate((circleLength-vec.len())/circleLength * 180f);
}
vec.setLength(type.speed * Timers.delta());
velocity.add(vec);
}
protected void attack(float circleLength){
vec.set(target.getX() - x, target.getY() - y);
float ang = angleTo(target);
float diff = Angles.angleDist(ang, rotation);
if(diff > 100f && vec.len() < circleLength){
vec.setAngle(velocity.angle());
}else{
vec.setAngle(Mathf.slerpDelta(velocity.angle(), vec.angle(), 0.44f));
}
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);
}
}
},
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){
if(timer.get(timerTarget, 20)) {
Unit closest = Units.getClosestEnemy(team, x, y,
inventory.getAmmo().getRange(), other -> distanceTo(other) < 60f);
if(closest != null){
target = closest;
}else {
Tile target = Geometry.findClosest(x, y, world.indexer().getEnemy(team, BlockFlag.target));
if (target != null) FlyingUnit.this.target = target.entity;
}
}
}else{
attack(150f);
if (timer.get(timerReload, type.reload) && Mathf.angNear(angleTo(target), rotation, 13f)
&& distanceTo(target) < inventory.getAmmo().getRange()) {
AmmoType ammo = inventory.getAmmo();
inventory.useAmmo();
shoot(ammo, Angles.moveToward(rotation, angleTo(target), maxAim), 4f);
}
}
}
},
retreat = new UnitState() {
public void entered() {
target = null;
}
public void update() {
if(health >= health){
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);
}
}
};
}

View File

@@ -1,160 +0,0 @@
package io.anuke.mindustry.entities.units;
import io.anuke.mindustry.entities.Unit;
import io.anuke.mindustry.entities.Units;
import io.anuke.mindustry.type.AmmoType;
import io.anuke.mindustry.world.BlockFlag;
import io.anuke.mindustry.world.Tile;
import io.anuke.ucore.core.Timers;
import io.anuke.ucore.graphics.Draw;
import io.anuke.ucore.util.Angles;
import io.anuke.ucore.util.Geometry;
import io.anuke.ucore.util.Mathf;
import io.anuke.ucore.util.Translator;
import static io.anuke.mindustry.Vars.world;
public class FlyingUnitType extends UnitType {
protected static Translator vec = new Translator();
protected float maxAim = 30f;
public FlyingUnitType(String name) {
super(name);
speed = 0.2f;
maxVelocity = 2f;
drag = 0.01f;
isFlying = true;
}
@Override
public void update(BaseUnit unit) {
super.update(unit);
unit.rotation = unit.velocity.angle();
}
@Override
public void draw(BaseUnit unit) {
Draw.alpha(unit.hitTime / Unit.hitDuration);
Draw.rect(name, unit.x, unit.y, unit.rotation - 90);
Draw.alpha(1f);
}
@Override
public void behavior(BaseUnit unit) {
if(unit.health <= health * retreatPercent &&
Geometry.findClosest(unit.x, unit.y, world.indexer().getAllied(unit.team, BlockFlag.repair)) != null){
unit.setState(retreat);
}
}
@Override
public UnitState getStartState(){
return attack;
}
protected void circle(BaseUnit unit, float circleLength){
vec.set(unit.target.getX() - unit.x, unit.target.getY() - unit.y);
if(vec.len() < circleLength){
vec.rotate((circleLength-vec.len())/circleLength * 180f);
}
vec.setLength(speed * Timers.delta());
unit.velocity.add(vec);
}
protected void attack(BaseUnit unit, float circleLength){
vec.set(unit.target.getX() - unit.x, unit.target.getY() - unit.y);
float ang = unit.angleTo(unit.target);
float diff = Angles.angleDist(ang, unit.rotation);
if(diff > 100f && vec.len() < circleLength){
vec.setAngle(unit.velocity.angle());
}else{
vec.setAngle(Mathf.slerpDelta(unit.velocity.angle(), vec.angle(), 0.44f));
}
vec.setLength(speed*Timers.delta());
unit.velocity.add(vec);
}
public final UnitState
resupply = new UnitState(){
public void entered(BaseUnit unit) {
unit.target = null;
}
public void update(BaseUnit unit) {
if(unit.inventory.totalAmmo() + 10 >= unit.inventory.ammoCapacity()){
unit.state.set(unit, attack);
}else if(!unit.targetHasFlag(BlockFlag.resupplyPoint)){
unit.retarget(() -> targetClosestAllyFlag(unit, BlockFlag.resupplyPoint));
}else{
circle(unit, 20f);
}
}
},
attack = new UnitState(){
public void entered(BaseUnit unit) {
unit.target = null;
}
public void update(BaseUnit unit) {
if(Units.invalidateTarget(unit.target, unit.team, unit.x, unit.y)){
unit.target = null;
}
if(!unit.inventory.hasAmmo()) {
unit.state.set(unit, resupply);
}else if (unit.target == null){
if(unit.timer.get(timerTarget, 20)) {
Unit closest = Units.getClosestEnemy(unit.team, unit.x, unit.y,
unit.inventory.getAmmo().getRange(), other -> other.distanceTo(unit) < 60f);
if(closest != null){
unit.target = closest;
}else {
Tile target = Geometry.findClosest(unit.x, unit.y, world.indexer().getEnemy(unit.team, BlockFlag.target));
if (target != null) unit.target = target.entity;
}
}
}else{
attack(unit, 150f);
if (unit.timer.get(timerReload, reload) && Mathf.angNear(unit.angleTo(unit.target), unit.rotation, 13f)
&& unit.distanceTo(unit.target) < unit.inventory.getAmmo().getRange()) {
AmmoType ammo = unit.inventory.getAmmo();
unit.inventory.useAmmo();
shoot(unit, ammo, Angles.moveToward(unit.rotation, unit.angleTo(unit.target), maxAim), 4f);
}
}
}
},
retreat = new UnitState() {
public void entered(BaseUnit unit) {
unit.target = null;
}
public void update(BaseUnit unit) {
if(unit.health >= health){
unit.state.set(unit, attack);
}else if(!unit.targetHasFlag(BlockFlag.repair)){
if(unit.timer.get(timerTarget, 20)) {
Tile target = Geometry.findClosest(unit.x, unit.y, world.indexer().getAllied(unit.team, BlockFlag.repair));
if (target != null) unit.target = target.entity;
}
}else{
circle(unit, 20f);
}
}
};
}

View File

@@ -1,8 +1,10 @@
package io.anuke.mindustry.entities.units; package io.anuke.mindustry.entities.units;
import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.graphics.Color;
import io.anuke.mindustry.Vars;
import io.anuke.mindustry.entities.Unit; import io.anuke.mindustry.entities.Unit;
import io.anuke.mindustry.entities.Units; import io.anuke.mindustry.entities.Units;
import io.anuke.mindustry.game.Team;
import io.anuke.mindustry.type.AmmoType; import io.anuke.mindustry.type.AmmoType;
import io.anuke.mindustry.world.BlockFlag; import io.anuke.mindustry.world.BlockFlag;
import io.anuke.mindustry.world.Tile; import io.anuke.mindustry.world.Tile;
@@ -15,20 +17,16 @@ import io.anuke.ucore.util.Geometry;
import io.anuke.ucore.util.Mathf; import io.anuke.ucore.util.Mathf;
import io.anuke.ucore.util.Translator; import io.anuke.ucore.util.Translator;
import static io.anuke.mindustry.Vars.state;
import static io.anuke.mindustry.Vars.world; import static io.anuke.mindustry.Vars.world;
public abstract class GroundUnitType extends UnitType{ public abstract class GroundUnitType extends BaseUnit {
protected static Translator vec = new Translator(); protected static Translator vec = new Translator();
protected static float maxAim = 30f;
protected float maxAim = 30f; protected float walkTime;
public GroundUnitType(String name) { public GroundUnitType(UnitType type, Team team) {
super(name); super(type, team);
maxVelocity = 1.1f;
speed = 0.1f;
drag = 0.4f;
range = 40f;
} }
@Override @Override
@@ -37,158 +35,158 @@ public abstract class GroundUnitType extends UnitType{
} }
@Override @Override
public void update(BaseUnit unit) { public void update() {
super.update(unit); super.update();
if(!unit.velocity.isZero(0.0001f) && (unit.target == null if(!velocity.isZero(0.0001f) && (target == null
|| (unit.inventory.hasAmmo() && unit.distanceTo(unit.target) > unit.inventory.getAmmo().getRange()))){ || (inventory.hasAmmo() && distanceTo(target) > inventory.getAmmo().getRange()))){
unit.rotation = unit.velocity.angle(); rotation = velocity.angle();
} }
} }
@Override @Override
public void draw(BaseUnit unit) { public void drawSmooth() {
Draw.alpha(unit.hitTime / Unit.hitDuration); Draw.alpha(hitTime / hitDuration);
float walktime = unit.walkTime; float walktime = walkTime;
float ft = Mathf.sin(walktime, 6f, 2f); float ft = Mathf.sin(walktime, 6f, 2f);
Floor floor = unit.getFloorOn(); Floor floor = getFloorOn();
if(floor.liquid){ if(floor.liquid){
Draw.tint(Hue.mix(Color.WHITE, floor.liquidColor, 0.5f)); Draw.tint(Hue.mix(Color.WHITE, floor.liquidColor, 0.5f));
} }
for (int i : Mathf.signs) { for (int i : Mathf.signs) {
Draw.rect(name + "-leg", Draw.rect(type.name + "-leg",
unit.x + Angles.trnsx(unit.baseRotation, ft * i), x + Angles.trnsx(baseRotation, ft * i),
unit.y + Angles.trnsy(unit.baseRotation, ft * i), y + Angles.trnsy(baseRotation, ft * i),
12f * i, 12f - Mathf.clamp(ft * i, 0, 2), unit.baseRotation - 90); 12f * i, 12f - Mathf.clamp(ft * i, 0, 2), baseRotation - 90);
} }
if(floor.liquid) { if(floor.liquid) {
Draw.tint(Color.WHITE, floor.liquidColor, unit.drownTime * 0.4f); Draw.tint(Color.WHITE, floor.liquidColor, drownTime * 0.4f);
}else { }else {
Draw.tint(Color.WHITE); Draw.tint(Color.WHITE);
} }
Draw.rect(name + "-base", unit.x, unit.y, unit.baseRotation- 90); Draw.rect(type.name + "-base", x, y, baseRotation- 90);
Draw.rect(name, unit.x, unit.y, unit.rotation -90); Draw.rect(type.name, x, y, rotation -90);
Draw.alpha(1f); Draw.alpha(1f);
} }
@Override @Override
public void behavior(BaseUnit unit) { public void behavior() {
if(unit.health <= health * retreatPercent){ if(health <= health * type.retreatPercent && !inventory.isInfiniteAmmo()){
unit.setState(retreat); setState(retreat);
} }
} }
@Override @Override
public void updateTargeting(BaseUnit unit) { public void updateTargeting() {
super.updateTargeting(unit); super.updateTargeting();
if(Units.invalidateTarget(unit.target, unit)){ if(Units.invalidateTarget(target, this)){
unit.target = null; target = null;
} }
} }
protected void moveToCore(BaseUnit unit){ protected void moveToCore(){
Tile tile = world.tileWorld(unit.x, unit.y); Tile tile = world.tileWorld(x, y);
Tile targetTile = world.pathfinder().getTargetTile(unit.team, tile); Tile targetTile = world.pathfinder().getTargetTile(team, tile);
if(tile == targetTile) return; if(tile == targetTile) return;
vec.trns(unit.baseRotation, speed); vec.trns(baseRotation, type.speed);
unit.baseRotation = Mathf.slerpDelta(unit.baseRotation, unit.angleTo(targetTile), 0.05f); baseRotation = Mathf.slerpDelta(baseRotation, angleTo(targetTile), 0.05f);
unit.walkTime += Timers.delta(); walkTime += Timers.delta();
unit.velocity.add(vec); velocity.add(vec);
} }
protected void moveAwayFromCore(BaseUnit unit){ protected void moveAwayFromCore(){
Tile tile = world.tileWorld(unit.x, unit.y); Tile tile = world.tileWorld(x, y);
Tile targetTile = world.pathfinder().getTargetTile(state.teams.enemiesOf(unit.team).first(), tile); Tile targetTile = world.pathfinder().getTargetTile(Vars.state.teams.enemiesOf(team).first(), tile);
if(tile == targetTile) return; if(tile == targetTile) return;
vec.trns(unit.baseRotation, speed); vec.trns(baseRotation, type.speed);
unit.baseRotation = Mathf.slerpDelta(unit.baseRotation, unit.angleTo(targetTile), 0.05f); baseRotation = Mathf.slerpDelta(baseRotation, angleTo(targetTile), 0.05f);
unit.walkTime += Timers.delta(); walkTime += Timers.delta();
unit.velocity.add(vec); velocity.add(vec);
} }
public final UnitState public final UnitState
resupply = new UnitState(){ resupply = new UnitState(){
public void entered(BaseUnit unit) { public void entered() {
unit.target = null; target = null;
} }
public void update(BaseUnit unit) { public void update() {
Tile tile = Geometry.findClosest(unit.x, unit.y, world.indexer().getAllied(unit.team, BlockFlag.resupplyPoint)); Tile tile = Geometry.findClosest(x, y, world.indexer().getAllied(team, BlockFlag.resupplyPoint));
if (tile != null && unit.distanceTo(tile) > 40) { if (tile != null && distanceTo(tile) > 40) {
moveAwayFromCore(unit); moveAwayFromCore();
} }
//TODO move toward resupply point //TODO move toward resupply point
if(unit.inventory.totalAmmo() + 10 >= unit.inventory.ammoCapacity()){ if(inventory.totalAmmo() + 10 >= inventory.ammoCapacity()){
unit.state.set(unit, attack); state.set(attack);
} }
} }
}, },
attack = new UnitState(){ attack = new UnitState(){
public void entered(BaseUnit unit) { public void entered() {
unit.target = null; target = null;
} }
public void update(BaseUnit unit) { public void update() {
unit.retarget(() -> { retarget(() -> {
Unit closest = Units.getClosestEnemy(unit.team, unit.x, unit.y, unit.inventory.getAmmo().getRange(), other -> true); Unit closest = Units.getClosestEnemy(team, x, y, inventory.getAmmo().getRange(), other -> true);
if(closest != null){ if(closest != null){
unit.target = closest; target = closest;
}else { }else {
Tile target = Geometry.findClosest(unit.x, unit.y, world.indexer().getEnemy(unit.team, BlockFlag.target)); Tile target = Geometry.findClosest(x, y, world.indexer().getEnemy(team, BlockFlag.target));
if (target != null) unit.target = target.entity; if (target != null) GroundUnitType.this.target = target.entity;
} }
}); });
if(!unit.inventory.hasAmmo()) { if(!inventory.hasAmmo()) {
unit.state.set(unit, resupply); state.set(resupply);
}else{ }else{
if(unit.distanceTo(unit.target) > unit.inventory.getAmmo().getRange() * 0.7f){ if(distanceTo(target) > inventory.getAmmo().getRange() * 0.7f){
moveToCore(unit); moveToCore();
}else{ }else{
unit.rotate(unit.angleTo(unit.target)); rotate(angleTo(target));
} }
if (unit.timer.get(timerReload, reload) && Mathf.angNear(unit.angleTo(unit.target), unit.rotation, 13f) if (timer.get(timerReload, type.reload) && Mathf.angNear(angleTo(target), rotation, 13f)
&& unit.distanceTo(unit.target) < unit.inventory.getAmmo().getRange()) { && distanceTo(target) < inventory.getAmmo().getRange()) {
AmmoType ammo = unit.inventory.getAmmo(); AmmoType ammo = inventory.getAmmo();
unit.inventory.useAmmo(); inventory.useAmmo();
unit.rotate(unit.angleTo(unit.target)); rotate(angleTo(target));
shoot(unit, ammo, Angles.moveToward(unit.rotation, unit.angleTo(unit.target), maxAim), 4f); shoot(ammo, Angles.moveToward(rotation, angleTo(target), maxAim), 4f);
} }
} }
} }
}, },
retreat = new UnitState() { retreat = new UnitState() {
public void entered(BaseUnit unit) { public void entered() {
unit.target = null; target = null;
} }
public void update(BaseUnit unit) { public void update() {
if(unit.health >= health){ if(health >= health){
unit.state.set(unit, attack); state.set(attack);
} }
moveAwayFromCore(unit); moveAwayFromCore();
} }
}; };
} }

View File

@@ -3,15 +3,15 @@ package io.anuke.mindustry.entities.units;
public class StateMachine { public class StateMachine {
private UnitState state; private UnitState state;
public void update(BaseUnit unit){ public void update(){
if(state != null) state.update(unit); if(state != null) state.update();
} }
public void set(BaseUnit unit, UnitState next){ public void set( UnitState next){
if(next == state) return; if(next == state) return;
if(state != null) state.exited(unit); if(state != null) state.exited();
this.state = next; this.state = next;
if(next != null) next.entered(unit); if(next != null) next.entered();
} }
public boolean is(UnitState state){ public boolean is(UnitState state){

View File

@@ -1,7 +1,7 @@
package io.anuke.mindustry.entities.units; package io.anuke.mindustry.entities.units;
public interface UnitState { public interface UnitState {
default void entered(BaseUnit unit){} default void entered(){}
default void exited(BaseUnit unit){} default void exited(){}
default void update(BaseUnit unit){} default void update(){}
} }

View File

@@ -2,146 +2,55 @@ package io.anuke.mindustry.entities.units;
import com.badlogic.gdx.utils.Array; import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.ObjectMap; import com.badlogic.gdx.utils.ObjectMap;
import io.anuke.mindustry.content.fx.ExplosionFx; import io.anuke.mindustry.game.Team;
import io.anuke.mindustry.entities.TileEntity;
import io.anuke.mindustry.entities.Unit;
import io.anuke.mindustry.entities.bullet.Bullet;
import io.anuke.mindustry.net.Net;
import io.anuke.mindustry.net.NetEvents;
import io.anuke.mindustry.type.AmmoType; import io.anuke.mindustry.type.AmmoType;
import io.anuke.mindustry.type.Item; import io.anuke.mindustry.type.Item;
import io.anuke.mindustry.world.BlockFlag;
import io.anuke.mindustry.world.Tile;
import io.anuke.ucore.core.Effects;
import io.anuke.ucore.core.Timers;
import io.anuke.ucore.util.Angles;
import io.anuke.ucore.util.Geometry;
import io.anuke.ucore.util.Mathf;
import static io.anuke.mindustry.Vars.tilesize; public class UnitType {
import static io.anuke.mindustry.Vars.world;
public abstract class UnitType {
private static byte lastid = 0; private static byte lastid = 0;
private static Array<UnitType> types = new Array<>(); private static Array<UnitType> types = new Array<>();
private static int timerIndex = 0; protected final UnitCreator creator;
protected static final int timerTarget = timerIndex++;
protected static final int timerBoost = timerIndex++;
protected static final int timerReload = timerIndex++;
public final String name; public final String name;
public final byte id; public final byte id;
protected float health = 60; public float health = 60;
protected float hitsize = 5f; public float hitsize = 5f;
protected float hitsizeTile = 4f; public float hitsizeTile = 4f;
protected float speed = 0.4f; public float speed = 0.4f;
protected float range = 160; public float range = 160;
protected float rotatespeed = 0.1f; public float rotatespeed = 0.1f;
protected float baseRotateSpeed = 0.1f; public float baseRotateSpeed = 0.1f;
protected float mass = 1f; public float mass = 1f;
protected boolean isFlying; public boolean isFlying;
protected float drag = 0.1f; public float drag = 0.1f;
protected float maxVelocity = 5f; public float maxVelocity = 5f;
protected float reload = 40f; public float reload = 40f;
protected float retreatPercent = 0.2f; public float retreatPercent = 0.2f;
protected float armor = 0f; public float armor = 0f;
protected ObjectMap<Item, AmmoType> ammo = new ObjectMap<>(); public ObjectMap<Item, AmmoType> ammo = new ObjectMap<>();
public UnitType(String name){ public UnitType(String name, UnitCreator creator){
this.id = lastid++; this.id = lastid++;
this.name = name; this.name = name;
this.creator = creator;
types.add(this); types.add(this);
} }
public BaseUnit create(Team team){
return creator.create(team);
}
protected void setAmmo(AmmoType... types){ protected void setAmmo(AmmoType... types){
for(AmmoType type : types){ for(AmmoType type : types){
ammo.put(type.item, type); ammo.put(type.item, type);
} }
} }
public abstract void draw(BaseUnit unit); public interface UnitCreator{
BaseUnit create(Team team);
public void drawUnder(BaseUnit unit){}
public void drawOver(BaseUnit unit){}
public UnitState getStartState(){
return null;
}
public boolean isFlying(){
return isFlying;
}
public void update(BaseUnit unit){
if(unit.hitTime > 0){
unit.hitTime -= Timers.delta();
}
if(unit.hitTime < 0) unit.hitTime = 0;
if(Net.client()){
unit.interpolate();
return;
}
updateTargeting(unit);
unit.state.update(unit);
unit.updateVelocityStatus(drag, maxVelocity);
if(unit.target != null) behavior(unit);
unit.x = Mathf.clamp(unit.x, 0, world.width() * tilesize);
unit.y = Mathf.clamp(unit.y, 0, world.height() * tilesize);
}
/**Only runs when the unit has a target.*/
public abstract void behavior(BaseUnit unit);
public void updateTargeting(BaseUnit unit){
if(unit.target == null || (unit.target instanceof Unit && (unit.target.isDead() || ((Unit)unit.target).team == unit.team))
|| (unit.target instanceof TileEntity && ((TileEntity) unit.target).tile.entity == null)){
unit.target = null;
}
}
public void shoot(BaseUnit unit, AmmoType type, float rotation, float translation){
Bullet.create(type.bullet, unit,
unit.x + Angles.trnsx(rotation, translation),
unit.y + Angles.trnsy(rotation, translation), rotation);
Effects.effect(type.shootEffect, unit.x + Angles.trnsx(rotation, translation),
unit.y + Angles.trnsy(rotation, translation), rotation, unit);
Effects.effect(type.smokeEffect, unit.x + Angles.trnsx(rotation, translation),
unit.y + Angles.trnsy(rotation, translation), rotation, unit);
}
public void targetClosestAllyFlag(BaseUnit unit, BlockFlag flag){
Tile target = Geometry.findClosest(unit.x, unit.y, world.indexer().getAllied(unit.team, flag));
if (target != null) unit.target = target.entity;
}
public void onDeath(BaseUnit unit){
//TODO other things, such as enemies?
Effects.effect(ExplosionFx.explosion, unit);
Effects.shake(2f, 2f, unit);
if(Net.server()){
NetEvents.handleUnitDeath(unit);
}
unit.remove();
}
public void onRemoteDeath(BaseUnit unit){
onDeath(unit);
}
public void removed(BaseUnit unit){
//TODO
} }
public static UnitType getByID(byte id){ public static UnitType getByID(byte id){

View File

@@ -1,13 +0,0 @@
package io.anuke.mindustry.entities.units.types;
import io.anuke.mindustry.content.AmmoTypes;
import io.anuke.mindustry.entities.units.GroundUnitType;
public class Brute extends GroundUnitType {
public Brute(String name) {
super(name);
setAmmo(AmmoTypes.bulletIron);
}
}

View File

@@ -1,69 +0,0 @@
package io.anuke.mindustry.entities.units.types;
import io.anuke.mindustry.content.AmmoTypes;
import io.anuke.mindustry.entities.Unit;
import io.anuke.mindustry.entities.units.BaseUnit;
import io.anuke.mindustry.entities.units.FlyingUnitType;
import io.anuke.mindustry.entities.units.UnitState;
import io.anuke.mindustry.graphics.Palette;
import io.anuke.ucore.core.Timers;
import io.anuke.ucore.graphics.Draw;
import io.anuke.ucore.util.Angles;
import io.anuke.ucore.util.Mathf;
public class Cruiser extends FlyingUnitType {
public Cruiser(){
super("vtol");
setAmmo(AmmoTypes.bulletIron);
speed = 0.2f;
maxVelocity = 1.4f;
health = 300f;
}
@Override
public void drawUnder(BaseUnit unit) {
float rotation = unit.rotation - 90;
float scl = 0.6f + Mathf.absin(Timers.time(), 1f, 0.3f);
float dy = -6f*scl;
Draw.color(Palette.lighterOrange, Palette.lightFlame, Mathf.absin(Timers.time(), 3f, 0.7f));
Draw.rect("vtol-flame",
unit.x + Angles.trnsx(rotation, 0, dy),
unit.y + Angles.trnsy(rotation, 0, dy), Mathf.atan2(0, dy) + rotation);
Draw.color();
}
@Override
public void draw(BaseUnit unit) {
Draw.alpha(unit.hitTime / Unit.hitDuration);
Draw.rect(name, unit.x, unit.y, unit.rotation - 90);
Draw.alpha(1f);
}
@Override
public void update(BaseUnit unit) {
super.update(unit);
unit.x += Mathf.sin(Timers.time() + unit.id * 999, 25f, 0.06f);
unit.y += Mathf.cos(Timers.time() + unit.id * 999, 25f, 0.06f);
if(unit.velocity.len() <= 0.2f){
unit.rotation += Mathf.sin(Timers.time() + unit.id * 99, 10f, 8f);
}
if(unit.timer.get(timerBoost, 2)){
// unit.effectAt(UnitFx.vtolHover, unit.rotation + 180f, 4f, 0);
}
}
@Override
public UnitState getStartState(){
return resupply;
}
}

View File

@@ -1,58 +1,117 @@
package io.anuke.mindustry.entities.units.types; package io.anuke.mindustry.entities.units.types;
import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.utils.Queue;
import io.anuke.mindustry.entities.BlockBuilder;
import io.anuke.mindustry.entities.TileEntity; import io.anuke.mindustry.entities.TileEntity;
import io.anuke.mindustry.entities.Units; import io.anuke.mindustry.entities.Units;
import io.anuke.mindustry.entities.units.BaseUnit; import io.anuke.mindustry.entities.units.BaseUnit;
import io.anuke.mindustry.entities.units.FlyingUnitType; import io.anuke.mindustry.entities.units.FlyingUnit;
import io.anuke.mindustry.entities.units.UnitState; import io.anuke.mindustry.entities.units.UnitState;
import io.anuke.mindustry.entities.units.UnitType;
import io.anuke.mindustry.game.EventType.BlockBuildEvent;
import io.anuke.mindustry.game.Team;
import io.anuke.mindustry.graphics.Palette;
import io.anuke.mindustry.world.BlockFlag; import io.anuke.mindustry.world.BlockFlag;
import io.anuke.mindustry.world.Tile; import io.anuke.mindustry.world.Tile;
import io.anuke.mindustry.world.blocks.types.BuildBlock;
import io.anuke.mindustry.world.blocks.types.BuildBlock.BuildEntity;
import io.anuke.ucore.core.Events;
import io.anuke.ucore.core.Timers; import io.anuke.ucore.core.Timers;
import io.anuke.ucore.entities.EntityGroup;
import io.anuke.ucore.graphics.Draw; import io.anuke.ucore.graphics.Draw;
import io.anuke.ucore.graphics.Shapes; import io.anuke.ucore.graphics.Shapes;
import io.anuke.ucore.util.Angles; import io.anuke.ucore.util.Angles;
import io.anuke.ucore.util.Geometry; import io.anuke.ucore.util.Geometry;
import io.anuke.ucore.util.Mathf; import io.anuke.ucore.util.Mathf;
import static io.anuke.mindustry.Vars.unitGroups;
import static io.anuke.mindustry.Vars.world; import static io.anuke.mindustry.Vars.world;
public class Drone extends FlyingUnitType { public class Drone extends FlyingUnit implements BlockBuilder{
protected float healSpeed = 0.1f; protected static float healSpeed = 0.1f;
protected float discoverRange = 120f; protected static float discoverRange = 120f;
protected static boolean initialized;
public Drone() { protected Tile mineTile;
super("drone"); protected Queue<BuildRequest> placeQueue = new Queue<>();
speed = 0.2f;
maxVelocity = 0.8f; private static void initEvents(){
range = 50f; 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);
}
}
});
}
public Drone(UnitType type, Team team) {
super(type, team);
if(!initialized){
initEvents();
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){
target = entity;
setState(build);
}
} }
@Override @Override
public void update(BaseUnit unit) { public Queue<BuildRequest> getPlaceQueue() {
float rot = unit.rotation; return placeQueue;
super.update(unit); }
unit.rotation = rot;
if(unit.target != null && unit.state.is(repair)){ @Override
unit.rotation = Mathf.slerpDelta(rot, unit.angleTo(unit.target), 0.3f); public Tile getMineTile() {
return mineTile;
}
@Override
public void setMineTile(Tile tile) {
this.mineTile = tile;
}
@Override
public void update() {
float rot = rotation;
super.update();
rotation = rot;
if(target != null && state.is(repair)){
rotation = Mathf.slerpDelta(rot, angleTo(target), 0.3f);
}else{ }else{
unit.rotation = Mathf.slerpDelta(rot, unit.velocity.angle(), 0.3f); rotation = Mathf.slerpDelta(rot, velocity.angle(), 0.3f);
} }
unit.x += Mathf.sin(Timers.time() + unit.id * 999, 25f, 0.07f); x += Mathf.sin(Timers.time() + id * 999, 25f, 0.07f);
unit.y += Mathf.cos(Timers.time() + unit.id * 999, 25f, 0.07f); y += Mathf.cos(Timers.time() + id * 999, 25f, 0.07f);
if(unit.velocity.len() <= 0.2f && !(unit.state.is(repair) && unit.target != null)){ if(velocity.len() <= 0.2f && !(state.is(repair) && target != null)){
unit.rotation += Mathf.sin(Timers.time() + unit.id * 99, 10f, 5f); rotation += Mathf.sin(Timers.time() + id * 99, 10f, 5f);
} }
updateBuilding(this);
} }
@Override @Override
public void behavior(BaseUnit unit) { public void behavior() {
if(unit.health <= health * retreatPercent && if(health <= health * type.retreatPercent &&
Geometry.findClosest(unit.x, unit.y, world.indexer().getAllied(unit.team, BlockFlag.repair)) != null){ Geometry.findClosest(x, y, world.indexer().getAllied(team, BlockFlag.repair)) != null){
unit.setState(retreat); setState(retreat);
} }
} }
@@ -62,60 +121,86 @@ public class Drone extends FlyingUnitType {
} }
@Override @Override
public void drawOver(BaseUnit unit) { public void drawOver() {
if(unit.target instanceof TileEntity && unit.state.is(repair)){ trail.draw(Palette.lighterOrange, Palette.lightishOrange, 3f);
if(target instanceof TileEntity && state.is(repair)){
float len = 5f; float len = 5f;
Draw.color(Color.BLACK, Color.WHITE, 0.95f + Mathf.absin(Timers.time(), 0.8f, 0.05f)); Draw.color(Color.BLACK, Color.WHITE, 0.95f + Mathf.absin(Timers.time(), 0.8f, 0.05f));
Shapes.laser("beam", "beam-end", Shapes.laser("beam", "beam-end",
unit.x + Angles.trnsx(unit.rotation, len), x + Angles.trnsx(rotation, len),
unit.y + Angles.trnsy(unit.rotation, len), y + Angles.trnsy(rotation, len),
unit.target.getX(), unit.target.getY()); target.getX(), target.getY());
Draw.color(); Draw.color();
} }
drawBuilding(this);
}
@Override
public float drawSize() {
return isBuilding() ? placeDistance*2f : 30f;
} }
public final UnitState public final UnitState
build = new UnitState(){
public void update() {
BuildEntity entity = (BuildEntity)target;
if(entity.progress() < 1f && entity.tile.block() instanceof BuildBlock){ //building is valid
if(!isBuilding() && distanceTo(target) < placeDistance * 0.9f){ //within distance, begin placing
getPlaceQueue().addLast(new BuildRequest(entity.tile.x, entity.tile.y, entity.tile.getRotation(), entity.recipe));
}
circle(placeDistance * 0.7f);
}else{ //building isn't valid
setState(repair);
}
}
},
repair = new UnitState(){ repair = new UnitState(){
public void entered(BaseUnit unit) { public void entered() {
unit.target = null; target = null;
} }
public void update(BaseUnit unit) { public void update() {
if(unit.target != null && (((TileEntity)unit.target).health >= ((TileEntity)unit.target).tile.block().health if(target != null && (((TileEntity)target).health >= ((TileEntity)target).tile.block().health
|| unit.target.distanceTo(unit) > discoverRange)){ || target.distanceTo(Drone.this) > discoverRange)){
unit.target = null; target = null;
} }
if (unit.target == null) { if (target == null) {
if (unit.timer.get(timerTarget, 20)) { retarget(() -> {
unit.target = Units.findAllyTile(unit.team, unit.x, unit.y, discoverRange, target = Units.findAllyTile(team, x, y, discoverRange,
tile -> tile.entity != null && tile.entity.health + 0.0001f < tile.block().health); tile -> tile.entity != null && tile.entity.health + 0.0001f < tile.block().health);
} });
}else if(unit.target.distanceTo(unit) > range){ }else if(target.distanceTo(Drone.this) > type.range){
circle(unit, range); circle(type.range);
}else{ }else{
TileEntity entity = (TileEntity) unit.target; TileEntity entity = (TileEntity) target;
entity.health += healSpeed * Timers.delta(); entity.health += healSpeed * Timers.delta();
entity.health = Mathf.clamp(entity.health, 0, entity.tile.block().health); entity.health = Mathf.clamp(entity.health, 0, entity.tile.block().health);
} }
} }
}, },
retreat = new UnitState() { retreat = new UnitState() {
public void entered(BaseUnit unit) { public void entered() {
unit.target = null; target = null;
} }
public void update(BaseUnit unit) { public void update() {
if(unit.health >= health){ if(health >= health){
unit.state.set(unit, attack); state.set(attack);
}else if(!unit.targetHasFlag(BlockFlag.repair)){ }else if(!targetHasFlag(BlockFlag.repair)){
if(unit.timer.get(timerTarget, 20)) { if(timer.get(timerTarget, 20)) {
Tile target = Geometry.findClosest(unit.x, unit.y, world.indexer().getAllied(unit.team, BlockFlag.repair)); Tile target = Geometry.findClosest(x, y, world.indexer().getAllied(team, BlockFlag.repair));
if (target != null) unit.target = target.entity; if (target != null) Drone.this.target = target.entity;
} }
}else{ }else{
circle(unit, 40f); circle(40f);
} }
} }
}; };

View File

@@ -1,4 +0,0 @@
package io.anuke.mindustry.entities.units.types;
public class Eradicator {
}

View File

@@ -1,10 +0,0 @@
package io.anuke.mindustry.entities.units.types;
import io.anuke.mindustry.entities.units.FlyingUnitType;
public class Reaper extends FlyingUnitType {
public Reaper(String name) {
super(name);
}
}

View File

@@ -1,12 +1,12 @@
package io.anuke.mindustry.entities.units.types; package io.anuke.mindustry.entities.units.types;
import io.anuke.mindustry.content.AmmoTypes;
import io.anuke.mindustry.entities.units.GroundUnitType; import io.anuke.mindustry.entities.units.GroundUnitType;
import io.anuke.mindustry.entities.units.UnitType;
import io.anuke.mindustry.game.Team;
public class Scout extends GroundUnitType { public class Scout extends GroundUnitType {
public Scout(){ public Scout(UnitType type, Team team) {
super("scout"); super(type, team);
setAmmo(AmmoTypes.bulletIron);
} }
} }

View File

@@ -1,4 +0,0 @@
package io.anuke.mindustry.entities.units.types;
public class Sheller {
}

View File

@@ -1,67 +1,62 @@
package io.anuke.mindustry.entities.units.types; package io.anuke.mindustry.entities.units.types;
import io.anuke.mindustry.content.AmmoTypes;
import io.anuke.mindustry.content.fx.UnitFx; import io.anuke.mindustry.content.fx.UnitFx;
import io.anuke.mindustry.entities.Unit; import io.anuke.mindustry.entities.units.FlyingUnit;
import io.anuke.mindustry.entities.units.BaseUnit; import io.anuke.mindustry.entities.units.UnitType;
import io.anuke.mindustry.entities.units.FlyingUnitType; import io.anuke.mindustry.game.Team;
import io.anuke.mindustry.graphics.Palette; import io.anuke.mindustry.graphics.Palette;
import io.anuke.ucore.core.Timers; import io.anuke.ucore.core.Timers;
import io.anuke.ucore.graphics.Draw; import io.anuke.ucore.graphics.Draw;
import io.anuke.ucore.util.Angles; import io.anuke.ucore.util.Angles;
import io.anuke.ucore.util.Mathf; import io.anuke.ucore.util.Mathf;
public class Vtol extends FlyingUnitType { public class Vtol extends FlyingUnit {
public Vtol(){ public Vtol(UnitType type, Team team) {
super("vtol"); super(type, team);
setAmmo(AmmoTypes.bulletIron);
speed = 0.3f;
maxVelocity = 2f;
reload = 7;
} }
@Override @Override
public void drawUnder(BaseUnit unit) { public void drawUnder() {
float rotation = unit.rotation - 90; float rotation = this.rotation - 90;
float scl = 0.6f + Mathf.absin(Timers.time(), 1f, 0.3f); float scl = 0.6f + Mathf.absin(Timers.time(), 1f, 0.3f);
float dy = -6f*scl; float dy = -6f*scl;
Draw.color(Palette.lighterOrange, Palette.lightFlame, Mathf.absin(Timers.time(), 3f, 0.7f)); Draw.color(Palette.lighterOrange, Palette.lightFlame, Mathf.absin(Timers.time(), 3f, 0.7f));
Draw.rect("vtol-flame", Draw.rect("vtol-flame",
unit.x + Angles.trnsx(rotation, 0, dy), x + Angles.trnsx(rotation, 0, dy),
unit.y + Angles.trnsy(rotation, 0, dy), Mathf.atan2(0, dy) + rotation); y + Angles.trnsy(rotation, 0, dy), Mathf.atan2(0, dy) + rotation);
Draw.color(); Draw.color();
} }
@Override @Override
public void draw(BaseUnit unit) { public void drawSmooth() {
Draw.alpha(unit.hitTime / Unit.hitDuration); Draw.alpha(hitTime / hitDuration);
Draw.rect(name, unit.x, unit.y, unit.rotation - 90); Draw.rect(type.name, x, y, rotation - 90);
for(int i : Mathf.signs){ for(int i : Mathf.signs){
Draw.rect(name + "-booster-1", unit.x, unit.y, 12*i, 12, unit.rotation - 90); Draw.rect(type.name + "-booster-1", x, y, 12*i, 12, rotation - 90);
Draw.rect(name + "-booster-2", unit.x, unit.y, 12*i, 12, unit.rotation - 90); Draw.rect(type.name + "-booster-2", x, y, 12*i, 12, rotation - 90);
} }
Draw.alpha(1f); Draw.alpha(1f);
} }
@Override @Override
public void update(BaseUnit unit) { public void update() {
super.update(unit); super.update();
unit.x += Mathf.sin(Timers.time() + unit.id * 999, 25f, 0.07f); x += Mathf.sin(Timers.time() + id * 999, 25f, 0.07f);
unit.y += Mathf.cos(Timers.time() + unit.id * 999, 25f, 0.07f); y += Mathf.cos(Timers.time() + id * 999, 25f, 0.07f);
if(unit.velocity.len() <= 0.2f){ if(velocity.len() <= 0.2f){
unit.rotation += Mathf.sin(Timers.time() + unit.id * 99, 10f, 8f); rotation += Mathf.sin(Timers.time() + id * 99, 10f, 8f);
} }
if(unit.timer.get(timerBoost, 2)){ if(timer.get(timerBoost, 2)){
unit.effectAt(UnitFx.vtolHover, unit.rotation + 180f, 4f, 0); effectAt(UnitFx.vtolHover, rotation + 180f, 4f, 0);
} }
} }

View File

@@ -50,5 +50,9 @@ public class EventType {
public interface UnlockEvent extends Event{ public interface UnlockEvent extends Event{
void handle(Content content); void handle(Content content);
} }
public interface BlockBuildEvent extends Event{
void handle(Team team, Tile tile);
}
} }

View File

@@ -189,7 +189,7 @@ public class OverlayRenderer {
} }
drawEncloser(x, y - 8f, 2f); drawEncloser(x, y - 8f, 2f);
drawBar(Color.SCARLET, x, y - 8f, unit.health / unit.maxhealth); drawBar(Color.SCARLET, x, y - 8f, unit.healthfrac());
drawBar(Color.valueOf("32cf6d"), x, y - 9f, unit.inventory.totalAmmo() / (float) unit.inventory.ammoCapacity()); drawBar(Color.valueOf("32cf6d"), x, y - 9f, unit.inventory.totalAmmo() / (float) unit.inventory.ammoCapacity());
} }

View File

@@ -32,7 +32,7 @@ public class Item implements Comparable<Item>, Content{
public Color flameColor = Palette.darkFlame.cpy(); public Color flameColor = Palette.darkFlame.cpy();
/**base material cost of this item, used for calculating place times /**base material cost of this item, used for calculating place times
* 1 cost = 1 tick added to build time*/ * 1 cost = 1 tick added to build time*/
public float cost = 1f; public float cost = 3f;
public Item(String name, Color color) { public Item(String name, Color color) {
this.id = items.size; this.id = items.size;

View File

@@ -4,10 +4,9 @@ import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.files.FileHandle; import com.badlogic.gdx.files.FileHandle;
import io.anuke.mindustry.content.UnitTypes; import io.anuke.mindustry.content.UnitTypes;
import io.anuke.mindustry.content.bullets.TurretBullets; import io.anuke.mindustry.content.bullets.TurretBullets;
import io.anuke.mindustry.entities.bullet.Bullet;
import io.anuke.mindustry.entities.Player; import io.anuke.mindustry.entities.Player;
import io.anuke.mindustry.entities.TileEntity; import io.anuke.mindustry.entities.TileEntity;
import io.anuke.mindustry.entities.units.BaseUnit; import io.anuke.mindustry.entities.bullet.Bullet;
import io.anuke.mindustry.game.Team; import io.anuke.mindustry.game.Team;
import io.anuke.mindustry.net.Net; import io.anuke.mindustry.net.Net;
import io.anuke.ucore.core.Timers; import io.anuke.ucore.core.Timers;
@@ -70,9 +69,9 @@ public class DebugFragment implements Fragment {
row(); row();
new button("death", () -> player.damage(99999, false)); new button("death", () -> player.damage(99999, false));
row(); row();
new button("spawnf", () -> new BaseUnit(UnitTypes.drone, player.team).set(player.x, player.y).add()); new button("spawnf", () -> UnitTypes.drone.create(player.team).set(player.x, player.y).add());
row(); row();
new button("spawng", () -> new BaseUnit(UnitTypes.scout, player.team).set(player.x, player.y).add()); new button("spawng", () -> UnitTypes.scout.create(player.team).set(player.x, player.y).add());
row(); row();
}}.end(); }}.end();

View File

@@ -47,6 +47,7 @@ public class NuclearReactor extends LiquidBurnerGenerator {
itemCapacity = 30; itemCapacity = 30;
liquidCapacity = 50; liquidCapacity = 50;
powerCapacity = 80f; powerCapacity = 80f;
hasItems = true;
} }
@Override @Override

View File

@@ -75,7 +75,7 @@ public class RepairPoint extends Block{
RepairPointEntity entity = tile.entity(); RepairPointEntity entity = tile.entity();
if(entity.target != null && (entity.target.isDead() || entity.target.distanceTo(tile) > repairRadius || if(entity.target != null && (entity.target.isDead() || entity.target.distanceTo(tile) > repairRadius ||
entity.target.health >= entity.target.maxhealth)){ entity.target.health >= entity.target.getMaxHealth())){
entity.target = null; entity.target = null;
}else if(entity.target != null){ }else if(entity.target != null){
entity.target.health += repairSpeed * Timers.delta() * entity.strength; entity.target.health += repairSpeed * Timers.delta() * entity.strength;
@@ -95,7 +95,7 @@ public class RepairPoint extends Block{
if(entity.timer.get(timerTarget, 20)) { if(entity.timer.get(timerTarget, 20)) {
rect.setSize(repairRadius * 2).setCenter(tile.drawx(), tile.drawy()); rect.setSize(repairRadius * 2).setCenter(tile.drawx(), tile.drawy());
entity.target = Units.getClosest(tile.getTeam(), tile.drawx(), tile.drawy(), repairRadius, entity.target = Units.getClosest(tile.getTeam(), tile.drawx(), tile.drawy(), repairRadius,
unit -> unit.health < unit.maxhealth); unit -> unit.health < unit.getMaxHealth());
} }
} }

View File

@@ -51,7 +51,7 @@ public class UnitFactory extends Block {
@Override @Override
public boolean isSolidFor(Tile tile) { public boolean isSolidFor(Tile tile) {
UnitFactoryEntity entity = tile.entity(); UnitFactoryEntity entity = tile.entity();
return type.isFlying() || !entity.open; return type.isFlying || !entity.open;
} }
@Override @Override
@@ -114,7 +114,7 @@ public class UnitFactory extends Block {
if(entity.openCountdown > Timers.delta()){ if(entity.openCountdown > Timers.delta()){
entity.openCountdown -= Timers.delta(); entity.openCountdown -= Timers.delta();
}else{ }else{
if(type.isFlying() || !anyEntities(tile)) { if(type.isFlying || !anyEntities(tile)) {
entity.open = false; entity.open = false;
entity.openCountdown = -1; entity.openCountdown = -1;
}else{ }else{
@@ -141,7 +141,7 @@ public class UnitFactory extends Block {
Effects.shake(2f, 3f, entity); Effects.shake(2f, 3f, entity);
Effects.effect(BlockFx.producesmoke, tile.drawx(), tile.drawy()); Effects.effect(BlockFx.producesmoke, tile.drawx(), tile.drawy());
BaseUnit unit = new BaseUnit(type, tile.getTeam()); BaseUnit unit = type.create(tile.getTeam());
unit.set(tile.drawx(), tile.drawy()).add(); unit.set(tile.drawx(), tile.drawy()).add();
unit.velocity.y = launchVelocity; unit.velocity.y = launchVelocity;
}); });