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)
public static boolean infiniteAmmo = true;
//whether to show paths of enemies
public static boolean showPaths = true;
public static boolean showPaths = false;
//if false, player is always hidden
public static boolean showPlayer = true;
//whether to hide ui, only on debug

View File

@ -11,8 +11,29 @@ public class UnitTypes implements ContentList {
@Override
public void load() {
drone = new Drone();
scout = new Scout();
vtol = new Vtol();
drone = new UnitType("drone", team -> new Drone(drone, team)){{
isFlying = true;
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
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);
unit.inventory.addAmmo(AmmoTypes.bulletIron);
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.fx.BlockFx;
import io.anuke.mindustry.entities.effect.ItemTransfer;
import io.anuke.mindustry.game.EventType.BlockBuildEvent;
import io.anuke.mindustry.graphics.Palette;
import io.anuke.mindustry.type.Item;
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.BuildEntity;
import io.anuke.ucore.core.Effects;
import io.anuke.ucore.core.Events;
import io.anuke.ucore.core.Timers;
import io.anuke.ucore.graphics.Draw;
import io.anuke.ucore.graphics.Fill;
@ -26,8 +28,7 @@ import io.anuke.ucore.util.Translator;
import java.util.Arrays;
import static io.anuke.mindustry.Vars.tilesize;
import static io.anuke.mindustry.Vars.world;
import static io.anuke.mindustry.Vars.*;
/**Interface for units that build, break or mine things.*/
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 it's valid, place it
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{
//otherwise, skip it
getPlaceQueue().removeFirst();

View File

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

View File

@ -41,7 +41,7 @@ public abstract class Unit extends SyncEntity implements SerializableEntity, Tar
@Override
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

View File

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

View File

@ -1,16 +1,22 @@
package io.anuke.mindustry.entities.units;
import io.anuke.mindustry.content.fx.ExplosionFx;
import io.anuke.mindustry.entities.Targetable;
import io.anuke.mindustry.entities.TileEntity;
import io.anuke.mindustry.entities.Unit;
import io.anuke.mindustry.entities.bullet.Bullet;
import io.anuke.mindustry.entities.bullet.BulletType;
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.world.BlockFlag;
import io.anuke.mindustry.world.Tile;
import io.anuke.ucore.core.Effects;
import io.anuke.ucore.core.Effects.Effect;
import io.anuke.ucore.core.Timers;
import io.anuke.ucore.util.Angles;
import io.anuke.ucore.util.Geometry;
import io.anuke.ucore.util.Mathf;
import io.anuke.ucore.util.Timer;
@ -19,14 +25,19 @@ import java.io.DataOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import static io.anuke.mindustry.Vars.unitGroups;
import static io.anuke.mindustry.Vars.*;
public class BaseUnit extends Unit{
public UnitType type;
public Timer timer = new Timer(5);
public float walkTime = 0f;
public StateMachine state = new StateMachine();
public Targetable target;
public abstract class BaseUnit extends Unit{
private static int timerIndex = 0;
protected static final int timerTarget = timerIndex++;
protected static final int timerBoost = timerIndex++;
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){
this.type = type;
@ -52,15 +63,51 @@ public class BaseUnit extends Unit{
}
public void setState(UnitState state){
this.state.set(this, state);
this.state.set(state);
}
public void retarget(Runnable run){
if(timer.get(UnitType.timerTarget, 20)){
if(timer.get(timerTarget, 20)){
run.run();
}
}
/**Only runs when the unit has a target.*/
public void behavior(){
}
public void updateTargeting(){
if(target == null || (target instanceof Unit && (target.isDead() || ((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
public float getArmor() {
return type.armor;
@ -99,22 +146,41 @@ public class BaseUnit extends Unit{
@Override
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
public void drawSmooth(){
type.draw(this);
}
@Override
public void drawUnder(){
type.drawUnder(this);
}
@Override
public void drawOver(){
type.drawOver(this);
}
@Override
@ -130,27 +196,29 @@ public class BaseUnit extends Unit{
@Override
public void onDeath(){
super.onDeath();
type.onDeath(this);
Effects.effect(ExplosionFx.explosion, this);
Effects.shake(2f, 2f, this);
remove();
}
@Override
public void onRemoteDeath(){
type.onRemoteDeath(this);
onDeath();
}
@Override
public void removed(){
type.removed(this);
}
@Override
public void added(){
maxhealth = type.health;
hitbox.solid = !isFlying();
hitbox.setSize(type.hitsize);
hitboxTile.setSize(type.hitsizeTile);
state.set(this, type.getStartState());
state.set(getStartState());
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;
import com.badlogic.gdx.graphics.Color;
import io.anuke.mindustry.Vars;
import io.anuke.mindustry.entities.Unit;
import io.anuke.mindustry.entities.Units;
import io.anuke.mindustry.game.Team;
import io.anuke.mindustry.type.AmmoType;
import io.anuke.mindustry.world.BlockFlag;
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.Translator;
import static io.anuke.mindustry.Vars.state;
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 float maxAim = 30f;
protected float maxAim = 30f;
protected float walkTime;
public GroundUnitType(String name) {
super(name);
maxVelocity = 1.1f;
speed = 0.1f;
drag = 0.4f;
range = 40f;
public GroundUnitType(UnitType type, Team team) {
super(type, team);
}
@Override
@ -37,158 +35,158 @@ public abstract class GroundUnitType extends UnitType{
}
@Override
public void update(BaseUnit unit) {
super.update(unit);
public void update() {
super.update();
if(!unit.velocity.isZero(0.0001f) && (unit.target == null
|| (unit.inventory.hasAmmo() && unit.distanceTo(unit.target) > unit.inventory.getAmmo().getRange()))){
unit.rotation = unit.velocity.angle();
if(!velocity.isZero(0.0001f) && (target == null
|| (inventory.hasAmmo() && distanceTo(target) > inventory.getAmmo().getRange()))){
rotation = velocity.angle();
}
}
@Override
public void draw(BaseUnit unit) {
Draw.alpha(unit.hitTime / Unit.hitDuration);
public void drawSmooth() {
Draw.alpha(hitTime / hitDuration);
float walktime = unit.walkTime;
float walktime = walkTime;
float ft = Mathf.sin(walktime, 6f, 2f);
Floor floor = unit.getFloorOn();
Floor floor = getFloorOn();
if(floor.liquid){
Draw.tint(Hue.mix(Color.WHITE, floor.liquidColor, 0.5f));
}
for (int i : Mathf.signs) {
Draw.rect(name + "-leg",
unit.x + Angles.trnsx(unit.baseRotation, ft * i),
unit.y + Angles.trnsy(unit.baseRotation, ft * i),
12f * i, 12f - Mathf.clamp(ft * i, 0, 2), unit.baseRotation - 90);
Draw.rect(type.name + "-leg",
x + Angles.trnsx(baseRotation, ft * i),
y + Angles.trnsy(baseRotation, ft * i),
12f * i, 12f - Mathf.clamp(ft * i, 0, 2), baseRotation - 90);
}
if(floor.liquid) {
Draw.tint(Color.WHITE, floor.liquidColor, unit.drownTime * 0.4f);
Draw.tint(Color.WHITE, floor.liquidColor, drownTime * 0.4f);
}else {
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);
}
@Override
public void behavior(BaseUnit unit) {
if(unit.health <= health * retreatPercent){
unit.setState(retreat);
public void behavior() {
if(health <= health * type.retreatPercent && !inventory.isInfiniteAmmo()){
setState(retreat);
}
}
@Override
public void updateTargeting(BaseUnit unit) {
super.updateTargeting(unit);
public void updateTargeting() {
super.updateTargeting();
if(Units.invalidateTarget(unit.target, unit)){
unit.target = null;
if(Units.invalidateTarget(target, this)){
target = null;
}
}
protected void moveToCore(BaseUnit unit){
Tile tile = world.tileWorld(unit.x, unit.y);
Tile targetTile = world.pathfinder().getTargetTile(unit.team, tile);
protected void moveToCore(){
Tile tile = world.tileWorld(x, y);
Tile targetTile = world.pathfinder().getTargetTile(team, tile);
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);
unit.walkTime += Timers.delta();
unit.velocity.add(vec);
baseRotation = Mathf.slerpDelta(baseRotation, angleTo(targetTile), 0.05f);
walkTime += Timers.delta();
velocity.add(vec);
}
protected void moveAwayFromCore(BaseUnit unit){
Tile tile = world.tileWorld(unit.x, unit.y);
Tile targetTile = world.pathfinder().getTargetTile(state.teams.enemiesOf(unit.team).first(), tile);
protected void moveAwayFromCore(){
Tile tile = world.tileWorld(x, y);
Tile targetTile = world.pathfinder().getTargetTile(Vars.state.teams.enemiesOf(team).first(), tile);
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);
unit.walkTime += Timers.delta();
unit.velocity.add(vec);
baseRotation = Mathf.slerpDelta(baseRotation, angleTo(targetTile), 0.05f);
walkTime += Timers.delta();
velocity.add(vec);
}
public final UnitState
resupply = new UnitState(){
public void entered(BaseUnit unit) {
unit.target = null;
public void entered() {
target = null;
}
public void update(BaseUnit unit) {
Tile tile = Geometry.findClosest(unit.x, unit.y, world.indexer().getAllied(unit.team, BlockFlag.resupplyPoint));
public void update() {
Tile tile = Geometry.findClosest(x, y, world.indexer().getAllied(team, BlockFlag.resupplyPoint));
if (tile != null && unit.distanceTo(tile) > 40) {
moveAwayFromCore(unit);
if (tile != null && distanceTo(tile) > 40) {
moveAwayFromCore();
}
//TODO move toward resupply point
if(unit.inventory.totalAmmo() + 10 >= unit.inventory.ammoCapacity()){
unit.state.set(unit, attack);
if(inventory.totalAmmo() + 10 >= inventory.ammoCapacity()){
state.set(attack);
}
}
},
attack = new UnitState(){
public void entered(BaseUnit unit) {
unit.target = null;
public void entered() {
target = null;
}
public void update(BaseUnit unit) {
unit.retarget(() -> {
Unit closest = Units.getClosestEnemy(unit.team, unit.x, unit.y, unit.inventory.getAmmo().getRange(), other -> true);
public void update() {
retarget(() -> {
Unit closest = Units.getClosestEnemy(team, x, y, inventory.getAmmo().getRange(), other -> true);
if(closest != null){
unit.target = closest;
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;
Tile target = Geometry.findClosest(x, y, world.indexer().getEnemy(team, BlockFlag.target));
if (target != null) GroundUnitType.this.target = target.entity;
}
});
if(!unit.inventory.hasAmmo()) {
unit.state.set(unit, resupply);
if(!inventory.hasAmmo()) {
state.set(resupply);
}else{
if(unit.distanceTo(unit.target) > unit.inventory.getAmmo().getRange() * 0.7f){
moveToCore(unit);
if(distanceTo(target) > inventory.getAmmo().getRange() * 0.7f){
moveToCore();
}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)
&& unit.distanceTo(unit.target) < unit.inventory.getAmmo().getRange()) {
AmmoType ammo = unit.inventory.getAmmo();
unit.inventory.useAmmo();
unit.rotate(unit.angleTo(unit.target));
if (timer.get(timerReload, type.reload) && Mathf.angNear(angleTo(target), rotation, 13f)
&& distanceTo(target) < inventory.getAmmo().getRange()) {
AmmoType ammo = inventory.getAmmo();
inventory.useAmmo();
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() {
public void entered(BaseUnit unit) {
unit.target = null;
public void entered() {
target = null;
}
public void update(BaseUnit unit) {
if(unit.health >= health){
unit.state.set(unit, attack);
public void update() {
if(health >= health){
state.set(attack);
}
moveAwayFromCore(unit);
moveAwayFromCore();
}
};
}

View File

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

View File

@ -1,7 +1,7 @@
package io.anuke.mindustry.entities.units;
public interface UnitState {
default void entered(BaseUnit unit){}
default void exited(BaseUnit unit){}
default void update(BaseUnit unit){}
default void entered(){}
default void exited(){}
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.ObjectMap;
import io.anuke.mindustry.content.fx.ExplosionFx;
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.game.Team;
import io.anuke.mindustry.type.AmmoType;
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;
import static io.anuke.mindustry.Vars.world;
public abstract class UnitType {
public class UnitType {
private static byte lastid = 0;
private static Array<UnitType> types = new Array<>();
private static int timerIndex = 0;
protected static final int timerTarget = timerIndex++;
protected static final int timerBoost = timerIndex++;
protected static final int timerReload = timerIndex++;
protected final UnitCreator creator;
public final String name;
public final byte id;
protected float health = 60;
protected float hitsize = 5f;
protected float hitsizeTile = 4f;
protected float speed = 0.4f;
protected float range = 160;
protected float rotatespeed = 0.1f;
protected float baseRotateSpeed = 0.1f;
protected float mass = 1f;
protected boolean isFlying;
protected float drag = 0.1f;
protected float maxVelocity = 5f;
protected float reload = 40f;
protected float retreatPercent = 0.2f;
protected float armor = 0f;
protected ObjectMap<Item, AmmoType> ammo = new ObjectMap<>();
public float health = 60;
public float hitsize = 5f;
public float hitsizeTile = 4f;
public float speed = 0.4f;
public float range = 160;
public float rotatespeed = 0.1f;
public float baseRotateSpeed = 0.1f;
public float mass = 1f;
public boolean isFlying;
public float drag = 0.1f;
public float maxVelocity = 5f;
public float reload = 40f;
public float retreatPercent = 0.2f;
public float armor = 0f;
public ObjectMap<Item, AmmoType> ammo = new ObjectMap<>();
public UnitType(String name){
public UnitType(String name, UnitCreator creator){
this.id = lastid++;
this.name = name;
this.creator = creator;
types.add(this);
}
public BaseUnit create(Team team){
return creator.create(team);
}
protected void setAmmo(AmmoType... types){
for(AmmoType type : types){
ammo.put(type.item, type);
}
}
public abstract void draw(BaseUnit unit);
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 interface UnitCreator{
BaseUnit create(Team team);
}
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;
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.Units;
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.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.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.entities.EntityGroup;
import io.anuke.ucore.graphics.Draw;
import io.anuke.ucore.graphics.Shapes;
import io.anuke.ucore.util.Angles;
import io.anuke.ucore.util.Geometry;
import io.anuke.ucore.util.Mathf;
import static io.anuke.mindustry.Vars.unitGroups;
import static io.anuke.mindustry.Vars.world;
public class Drone extends FlyingUnitType {
protected float healSpeed = 0.1f;
protected float discoverRange = 120f;
public class Drone extends FlyingUnit implements BlockBuilder{
protected static float healSpeed = 0.1f;
protected static float discoverRange = 120f;
protected static boolean initialized;
public Drone() {
super("drone");
speed = 0.2f;
maxVelocity = 0.8f;
range = 50f;
protected Tile mineTile;
protected Queue<BuildRequest> placeQueue = new Queue<>();
private static void initEvents(){
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
public void update(BaseUnit unit) {
float rot = unit.rotation;
super.update(unit);
unit.rotation = rot;
public Queue<BuildRequest> getPlaceQueue() {
return placeQueue;
}
if(unit.target != null && unit.state.is(repair)){
unit.rotation = Mathf.slerpDelta(rot, unit.angleTo(unit.target), 0.3f);
@Override
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{
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);
unit.y += Mathf.cos(Timers.time() + unit.id * 999, 25f, 0.07f);
x += Mathf.sin(Timers.time() + 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)){
unit.rotation += Mathf.sin(Timers.time() + unit.id * 99, 10f, 5f);
if(velocity.len() <= 0.2f && !(state.is(repair) && target != null)){
rotation += Mathf.sin(Timers.time() + id * 99, 10f, 5f);
}
updateBuilding(this);
}
@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);
public void behavior() {
if(health <= health * type.retreatPercent &&
Geometry.findClosest(x, y, world.indexer().getAllied(team, BlockFlag.repair)) != null){
setState(retreat);
}
}
@ -62,60 +121,86 @@ public class Drone extends FlyingUnitType {
}
@Override
public void drawOver(BaseUnit unit) {
if(unit.target instanceof TileEntity && unit.state.is(repair)){
public void drawOver() {
trail.draw(Palette.lighterOrange, Palette.lightishOrange, 3f);
if(target instanceof TileEntity && state.is(repair)){
float len = 5f;
Draw.color(Color.BLACK, Color.WHITE, 0.95f + Mathf.absin(Timers.time(), 0.8f, 0.05f));
Shapes.laser("beam", "beam-end",
unit.x + Angles.trnsx(unit.rotation, len),
unit.y + Angles.trnsy(unit.rotation, len),
unit.target.getX(), unit.target.getY());
x + Angles.trnsx(rotation, len),
y + Angles.trnsy(rotation, len),
target.getX(), target.getY());
Draw.color();
}
drawBuilding(this);
}
@Override
public float drawSize() {
return isBuilding() ? placeDistance*2f : 30f;
}
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(){
public void entered(BaseUnit unit) {
unit.target = null;
public void entered() {
target = null;
}
public void update(BaseUnit unit) {
if(unit.target != null && (((TileEntity)unit.target).health >= ((TileEntity)unit.target).tile.block().health
|| unit.target.distanceTo(unit) > discoverRange)){
unit.target = null;
public void update() {
if(target != null && (((TileEntity)target).health >= ((TileEntity)target).tile.block().health
|| target.distanceTo(Drone.this) > discoverRange)){
target = null;
}
if (unit.target == null) {
if (unit.timer.get(timerTarget, 20)) {
unit.target = Units.findAllyTile(unit.team, unit.x, unit.y, discoverRange,
tile -> tile.entity != null && tile.entity.health + 0.0001f < tile.block().health);
}
}else if(unit.target.distanceTo(unit) > range){
circle(unit, range);
if (target == null) {
retarget(() -> {
target = Units.findAllyTile(team, x, y, discoverRange,
tile -> tile.entity != null && tile.entity.health + 0.0001f < tile.block().health);
});
}else if(target.distanceTo(Drone.this) > type.range){
circle(type.range);
}else{
TileEntity entity = (TileEntity) unit.target;
TileEntity entity = (TileEntity) target;
entity.health += healSpeed * Timers.delta();
entity.health = Mathf.clamp(entity.health, 0, entity.tile.block().health);
}
}
},
retreat = new UnitState() {
public void entered(BaseUnit unit) {
unit.target = null;
public void entered() {
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;
public void update() {
if(health >= health){
state.set(attack);
}else if(!targetHasFlag(BlockFlag.repair)){
if(timer.get(timerTarget, 20)) {
Tile target = Geometry.findClosest(x, y, world.indexer().getAllied(team, BlockFlag.repair));
if (target != null) Drone.this.target = target.entity;
}
}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;
import io.anuke.mindustry.content.AmmoTypes;
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 Scout(){
super("scout");
setAmmo(AmmoTypes.bulletIron);
public Scout(UnitType type, Team team) {
super(type, team);
}
}

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;
import io.anuke.mindustry.content.AmmoTypes;
import io.anuke.mindustry.content.fx.UnitFx;
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.FlyingUnit;
import io.anuke.mindustry.entities.units.UnitType;
import io.anuke.mindustry.game.Team;
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 Vtol extends FlyingUnitType {
public class Vtol extends FlyingUnit {
public Vtol(){
super("vtol");
setAmmo(AmmoTypes.bulletIron);
speed = 0.3f;
maxVelocity = 2f;
reload = 7;
public Vtol(UnitType type, Team team) {
super(type, team);
}
@Override
public void drawUnder(BaseUnit unit) {
float rotation = unit.rotation - 90;
public void drawUnder() {
float rotation = this.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);
x + Angles.trnsx(rotation, 0, dy),
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);
public void drawSmooth() {
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){
Draw.rect(name + "-booster-1", unit.x, unit.y, 12*i, 12, unit.rotation - 90);
Draw.rect(name + "-booster-2", 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(type.name + "-booster-2", x, y, 12*i, 12, rotation - 90);
}
Draw.alpha(1f);
}
@Override
public void update(BaseUnit unit) {
super.update(unit);
public void update() {
super.update();
unit.x += Mathf.sin(Timers.time() + unit.id * 999, 25f, 0.07f);
unit.y += Mathf.cos(Timers.time() + unit.id * 999, 25f, 0.07f);
x += Mathf.sin(Timers.time() + id * 999, 25f, 0.07f);
y += Mathf.cos(Timers.time() + id * 999, 25f, 0.07f);
if(unit.velocity.len() <= 0.2f){
unit.rotation += Mathf.sin(Timers.time() + unit.id * 99, 10f, 8f);
if(velocity.len() <= 0.2f){
rotation += Mathf.sin(Timers.time() + id * 99, 10f, 8f);
}
if(unit.timer.get(timerBoost, 2)){
unit.effectAt(UnitFx.vtolHover, unit.rotation + 180f, 4f, 0);
if(timer.get(timerBoost, 2)){
effectAt(UnitFx.vtolHover, rotation + 180f, 4f, 0);
}
}

View File

@ -50,5 +50,9 @@ public class EventType {
public interface UnlockEvent extends Event{
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);
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());
}

View File

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

View File

@ -4,10 +4,9 @@ import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.files.FileHandle;
import io.anuke.mindustry.content.UnitTypes;
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.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.net.Net;
import io.anuke.ucore.core.Timers;
@ -70,9 +69,9 @@ public class DebugFragment implements Fragment {
row();
new button("death", () -> player.damage(99999, false));
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();
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();
}}.end();

View File

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

View File

@ -75,7 +75,7 @@ public class RepairPoint extends Block{
RepairPointEntity entity = tile.entity();
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;
}else if(entity.target != null){
entity.target.health += repairSpeed * Timers.delta() * entity.strength;
@ -95,7 +95,7 @@ public class RepairPoint extends Block{
if(entity.timer.get(timerTarget, 20)) {
rect.setSize(repairRadius * 2).setCenter(tile.drawx(), tile.drawy());
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
public boolean isSolidFor(Tile tile) {
UnitFactoryEntity entity = tile.entity();
return type.isFlying() || !entity.open;
return type.isFlying || !entity.open;
}
@Override
@ -114,7 +114,7 @@ public class UnitFactory extends Block {
if(entity.openCountdown > Timers.delta()){
entity.openCountdown -= Timers.delta();
}else{
if(type.isFlying() || !anyEntities(tile)) {
if(type.isFlying || !anyEntities(tile)) {
entity.open = false;
entity.openCountdown = -1;
}else{
@ -141,7 +141,7 @@ public class UnitFactory extends Block {
Effects.shake(2f, 3f, entity);
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.velocity.y = launchVelocity;
});