Improved vtol effects, added state machines for behavior
This commit is contained in:
@@ -1,6 +1,93 @@
|
|||||||
package io.anuke.mindustry.ai;
|
package io.anuke.mindustry.ai;
|
||||||
|
|
||||||
|
import com.badlogic.gdx.utils.IntMap;
|
||||||
|
import com.badlogic.gdx.utils.ObjectMap;
|
||||||
|
import com.badlogic.gdx.utils.ObjectSet;
|
||||||
|
import io.anuke.mindustry.game.EventType.TileChangeEvent;
|
||||||
|
import io.anuke.mindustry.game.EventType.WorldLoadEvent;
|
||||||
|
import io.anuke.mindustry.game.Team;
|
||||||
|
import io.anuke.mindustry.world.Tile;
|
||||||
|
import io.anuke.mindustry.world.flags.BlockFlag;
|
||||||
|
import io.anuke.ucore.core.Events;
|
||||||
|
import io.anuke.ucore.util.EnumSet;
|
||||||
|
|
||||||
|
import static io.anuke.mindustry.Vars.state;
|
||||||
|
import static io.anuke.mindustry.Vars.world;
|
||||||
|
|
||||||
/**Class used for indexing special target blocks for AI.
|
/**Class used for indexing special target blocks for AI.
|
||||||
* TODO implement this class*/
|
* TODO maybe use Arrays instead of ObjectSets?*/
|
||||||
public class BlockIndexer {
|
public class BlockIndexer {
|
||||||
|
/**Maps teams to a map of flagged tiles by type.*/
|
||||||
|
private ObjectMap<BlockFlag, ObjectSet<Tile>> enemyMap = new ObjectMap<>();
|
||||||
|
/**Maps teams to a map of flagged tiles by type.*/
|
||||||
|
private ObjectMap<BlockFlag, ObjectSet<Tile>> allyMap = new ObjectMap<>();
|
||||||
|
/**Maps tile positions to their last known tile index data.*/
|
||||||
|
private IntMap<TileIndex> typeMap = new IntMap<>();
|
||||||
|
/**Empty array used for returning.*/
|
||||||
|
private ObjectSet<Tile> emptyArray = new ObjectSet<>();
|
||||||
|
|
||||||
|
public BlockIndexer(){
|
||||||
|
Events.on(TileChangeEvent.class, tile -> {
|
||||||
|
if(typeMap.get(tile.packedPosition()) != null){
|
||||||
|
TileIndex index = typeMap.get(tile.packedPosition());
|
||||||
|
for(BlockFlag flag : index.flags){
|
||||||
|
getMap(index.team).get(flag).remove(tile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
process(tile);
|
||||||
|
});
|
||||||
|
|
||||||
|
Events.on(WorldLoadEvent.class, () -> {
|
||||||
|
enemyMap.clear();
|
||||||
|
allyMap.clear();
|
||||||
|
typeMap.clear();
|
||||||
|
for(int x = 0; x < world.width(); x ++){
|
||||||
|
for (int y = 0; y < world.height(); y++) {
|
||||||
|
process(world.tile(x, y));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public ObjectSet<Tile> getAllied(Team team, BlockFlag type){
|
||||||
|
return (state.teams.get(team).ally ? allyMap : enemyMap).get(type, emptyArray);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ObjectSet<Tile> getEnemy(Team team, BlockFlag type){
|
||||||
|
return (!state.teams.get(team).ally ? allyMap : enemyMap).get(type, emptyArray);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void process(Tile tile){
|
||||||
|
if(tile.block().flags != null){
|
||||||
|
ObjectMap<BlockFlag, ObjectSet<Tile>> map = getMap(tile.getTeam());
|
||||||
|
|
||||||
|
for(BlockFlag flag : tile.block().flags){
|
||||||
|
|
||||||
|
ObjectSet<Tile> arr = map.get(flag);
|
||||||
|
if(arr == null){
|
||||||
|
arr = new ObjectSet<>();
|
||||||
|
map.put(flag, arr);
|
||||||
|
}
|
||||||
|
|
||||||
|
arr.add(tile);
|
||||||
|
|
||||||
|
map.put(flag, arr);
|
||||||
|
}
|
||||||
|
typeMap.put(tile.packedPosition(), new TileIndex(tile.block().flags, tile.getTeam()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private ObjectMap<BlockFlag, ObjectSet<Tile>> getMap(Team team){
|
||||||
|
return state.teams.get(team).ally ? allyMap : enemyMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TileIndex{
|
||||||
|
public final EnumSet<BlockFlag> flags;
|
||||||
|
public final Team team;
|
||||||
|
|
||||||
|
public TileIndex(EnumSet<BlockFlag> flags, Team team) {
|
||||||
|
this.flags = flags;
|
||||||
|
this.team = team;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ public class UnitFx {
|
|||||||
vtolHover = new Effect(40f, e -> {
|
vtolHover = new Effect(40f, e -> {
|
||||||
float len = e.finpow()*10f;
|
float len = e.finpow()*10f;
|
||||||
float ang = e.rotation + Mathf.randomSeedRange(e.id, 30f);
|
float ang = e.rotation + Mathf.randomSeedRange(e.id, 30f);
|
||||||
Draw.color(Palette.lightFlame);
|
Draw.color(Palette.lightFlame, Palette.lightOrange, e.fin());
|
||||||
Fill.circle(e.x + Angles.trnsx(ang, len), e.y + Angles.trnsy(ang, len), 2f * e.fout());
|
Fill.circle(e.x + Angles.trnsx(ang, len), e.y + Angles.trnsy(ang, len), 2f * e.fout());
|
||||||
Draw.reset();
|
Draw.reset();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import io.anuke.mindustry.entities.effect.GroundEffectEntity;
|
|||||||
import io.anuke.mindustry.entities.effect.GroundEffectEntity.GroundEffect;
|
import io.anuke.mindustry.entities.effect.GroundEffectEntity.GroundEffect;
|
||||||
import io.anuke.mindustry.entities.units.BaseUnit;
|
import io.anuke.mindustry.entities.units.BaseUnit;
|
||||||
import io.anuke.mindustry.game.Team;
|
import io.anuke.mindustry.game.Team;
|
||||||
|
import io.anuke.mindustry.game.TeamInfo.TeamData;
|
||||||
import io.anuke.mindustry.graphics.BlockRenderer;
|
import io.anuke.mindustry.graphics.BlockRenderer;
|
||||||
import io.anuke.mindustry.graphics.Layer;
|
import io.anuke.mindustry.graphics.Layer;
|
||||||
import io.anuke.mindustry.graphics.MinimapRenderer;
|
import io.anuke.mindustry.graphics.MinimapRenderer;
|
||||||
@@ -519,8 +520,8 @@ public class Renderer extends RendererModule{
|
|||||||
}
|
}
|
||||||
|
|
||||||
if((!debug || showUI) && Settings.getBool("healthbars")){
|
if((!debug || showUI) && Settings.getBool("healthbars")){
|
||||||
for(EntityGroup<BaseUnit> group : unitGroups){
|
for(TeamData ally : state.teams.getTeams(true)){
|
||||||
for(Unit e : group.all()){
|
for(Unit e : unitGroups[ally.team.ordinal()].all()){
|
||||||
drawStats(e);
|
drawStats(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import com.badlogic.gdx.Gdx;
|
|||||||
import com.badlogic.gdx.math.GridPoint2;
|
import com.badlogic.gdx.math.GridPoint2;
|
||||||
import com.badlogic.gdx.math.MathUtils;
|
import com.badlogic.gdx.math.MathUtils;
|
||||||
import com.badlogic.gdx.utils.Array;
|
import com.badlogic.gdx.utils.Array;
|
||||||
|
import io.anuke.mindustry.ai.BlockIndexer;
|
||||||
import io.anuke.mindustry.ai.Pathfinder;
|
import io.anuke.mindustry.ai.Pathfinder;
|
||||||
import io.anuke.mindustry.content.blocks.Blocks;
|
import io.anuke.mindustry.content.blocks.Blocks;
|
||||||
import io.anuke.mindustry.game.EventType.TileChangeEvent;
|
import io.anuke.mindustry.game.EventType.TileChangeEvent;
|
||||||
@@ -28,6 +29,7 @@ public class World extends Module{
|
|||||||
private Map currentMap;
|
private Map currentMap;
|
||||||
private Tile[][] tiles;
|
private Tile[][] tiles;
|
||||||
private Pathfinder pathfinder = new Pathfinder();
|
private Pathfinder pathfinder = new Pathfinder();
|
||||||
|
private BlockIndexer indexer = new BlockIndexer();
|
||||||
private Maps maps = new Maps();
|
private Maps maps = new Maps();
|
||||||
|
|
||||||
private Array<Tile> tempTiles = new Array<>();
|
private Array<Tile> tempTiles = new Array<>();
|
||||||
@@ -46,6 +48,10 @@ public class World extends Module{
|
|||||||
return maps;
|
return maps;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public BlockIndexer indexer() {
|
||||||
|
return indexer;
|
||||||
|
}
|
||||||
|
|
||||||
public Pathfinder pathfinder(){
|
public Pathfinder pathfinder(){
|
||||||
return pathfinder;
|
return pathfinder;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ public abstract class Unit extends SyncEntity implements SerializableEntity {
|
|||||||
stream.writeShort((short)health);
|
stream.writeShort((short)health);
|
||||||
stream.writeByte(status.current().id);
|
stream.writeByte(status.current().id);
|
||||||
stream.writeFloat(status.getTime());
|
stream.writeFloat(status.getTime());
|
||||||
|
inventory.write(stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -67,6 +68,7 @@ public abstract class Unit extends SyncEntity implements SerializableEntity {
|
|||||||
byte effect = stream.readByte();
|
byte effect = stream.readByte();
|
||||||
float etime = stream.readFloat();
|
float etime = stream.readFloat();
|
||||||
|
|
||||||
|
this.inventory.read(stream);
|
||||||
this.team = Team.values()[team];
|
this.team = Team.values()[team];
|
||||||
this.health = health;
|
this.health = health;
|
||||||
this.x = x;
|
this.x = x;
|
||||||
@@ -125,7 +127,10 @@ public abstract class Unit extends SyncEntity implements SerializableEntity {
|
|||||||
damage(health + 1, false);
|
damage(health + 1, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float px = x, py = y;
|
||||||
move(velocity.x / getMass() * floor.speedMultiplier * Timers.delta(), velocity.y / getMass() * floor.speedMultiplier * Timers.delta());
|
move(velocity.x / getMass() * floor.speedMultiplier * Timers.delta(), velocity.y / getMass() * floor.speedMultiplier * Timers.delta());
|
||||||
|
if(Math.abs(px - x) <= 0.0001f) velocity.x = 0f;
|
||||||
|
if(Math.abs(py - y) <= 0.0001f) velocity.y = 0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
velocity.scl(Mathf.clamp(1f-drag* floor.dragMultiplier* Timers.delta()));
|
velocity.scl(Mathf.clamp(1f-drag* floor.dragMultiplier* Timers.delta()));
|
||||||
|
|||||||
@@ -1,7 +1,14 @@
|
|||||||
package io.anuke.mindustry.entities;
|
package io.anuke.mindustry.entities;
|
||||||
|
|
||||||
import com.badlogic.gdx.utils.Array;
|
import com.badlogic.gdx.utils.Array;
|
||||||
import io.anuke.mindustry.resource.*;
|
import io.anuke.mindustry.resource.AmmoEntry;
|
||||||
|
import io.anuke.mindustry.resource.AmmoType;
|
||||||
|
import io.anuke.mindustry.resource.Item;
|
||||||
|
import io.anuke.mindustry.resource.ItemStack;
|
||||||
|
|
||||||
|
import java.io.DataInputStream;
|
||||||
|
import java.io.DataOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
public class UnitInventory {
|
public class UnitInventory {
|
||||||
private Array<AmmoEntry> ammos = new Array<>();
|
private Array<AmmoEntry> ammos = new Array<>();
|
||||||
@@ -14,6 +21,33 @@ public class UnitInventory {
|
|||||||
this.ammoCapacity = ammoCapacity;
|
this.ammoCapacity = ammoCapacity;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void write(DataOutputStream stream) throws IOException {
|
||||||
|
stream.writeInt(item == null ? 0 : item.amount);
|
||||||
|
stream.writeByte(item == null ? 0 : item.item.id);
|
||||||
|
stream.writeInt(totalAmmo);
|
||||||
|
stream.writeByte(ammos.size);
|
||||||
|
for(int i = 0; i < ammos.size; i ++){
|
||||||
|
stream.writeByte(ammos.get(i).type.id);
|
||||||
|
stream.writeInt(ammos.get(i).amount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void read(DataInputStream stream) throws IOException {
|
||||||
|
int iamount = stream.readInt();
|
||||||
|
byte iid = stream.readByte();
|
||||||
|
this.totalAmmo = stream.readInt();
|
||||||
|
byte ammoa = stream.readByte();
|
||||||
|
for(int i = 0; i < ammoa; i ++){
|
||||||
|
byte aid = stream.readByte();
|
||||||
|
int am = stream.readInt();
|
||||||
|
ammos.add(new AmmoEntry(AmmoType.getByID(aid), am));
|
||||||
|
}
|
||||||
|
|
||||||
|
if(iamount != 0){
|
||||||
|
item = new ItemStack(Item.getByID(iid), iamount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public AmmoType getAmmo() {
|
public AmmoType getAmmo() {
|
||||||
return ammos.size == 0 ? null : ammos.peek().type;
|
return ammos.size == 0 ? null : ammos.peek().type;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,9 +4,11 @@ import io.anuke.mindustry.ai.OptimizedPathFinder;
|
|||||||
import io.anuke.mindustry.ai.SmoothGraphPath;
|
import io.anuke.mindustry.ai.SmoothGraphPath;
|
||||||
import io.anuke.mindustry.entities.Bullet;
|
import io.anuke.mindustry.entities.Bullet;
|
||||||
import io.anuke.mindustry.entities.BulletType;
|
import io.anuke.mindustry.entities.BulletType;
|
||||||
|
import io.anuke.mindustry.entities.TileEntity;
|
||||||
import io.anuke.mindustry.entities.Unit;
|
import io.anuke.mindustry.entities.Unit;
|
||||||
import io.anuke.mindustry.game.Team;
|
import io.anuke.mindustry.game.Team;
|
||||||
import io.anuke.mindustry.resource.Item;
|
import io.anuke.mindustry.resource.Item;
|
||||||
|
import io.anuke.mindustry.world.flags.BlockFlag;
|
||||||
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.entities.Entity;
|
import io.anuke.ucore.entities.Entity;
|
||||||
@@ -25,6 +27,7 @@ public class BaseUnit extends Unit{
|
|||||||
public UnitType type;
|
public UnitType type;
|
||||||
public Timer timer = new Timer(5);
|
public Timer timer = new Timer(5);
|
||||||
public float walkTime = 0f;
|
public float walkTime = 0f;
|
||||||
|
public StateMachine state = new StateMachine();
|
||||||
public Entity target;
|
public Entity target;
|
||||||
|
|
||||||
protected OptimizedPathFinder finder;
|
protected OptimizedPathFinder finder;
|
||||||
@@ -46,7 +49,12 @@ public class BaseUnit extends Unit{
|
|||||||
public void effectAt(Effect effect, float rotation, float dx, float dy){
|
public void effectAt(Effect effect, float rotation, float dx, float dy){
|
||||||
Effects.effect(effect,
|
Effects.effect(effect,
|
||||||
x + Angles.trnsx(rotation, dx, dy),
|
x + Angles.trnsx(rotation, dx, dy),
|
||||||
y + Angles.trnsy(rotation, dx, dy), Mathf.atan2(dx, dy));
|
y + Angles.trnsy(rotation, dx, dy), Mathf.atan2(dx, dy) + rotation);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean targetHasFlag(BlockFlag flag){
|
||||||
|
return target instanceof TileEntity &&
|
||||||
|
((TileEntity)target).tile.block().flags.contains(flag);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -129,6 +137,7 @@ public class BaseUnit extends Unit{
|
|||||||
hitbox.solid = true;
|
hitbox.solid = true;
|
||||||
hitbox.setSize(type.hitsize);
|
hitbox.setSize(type.hitsize);
|
||||||
hitboxTile.setSize(type.hitsizeTile);
|
hitboxTile.setSize(type.hitsizeTile);
|
||||||
|
state.set(this, type.getStartState());
|
||||||
|
|
||||||
if(!isFlying()){
|
if(!isFlying()){
|
||||||
finder = new OptimizedPathFinder();
|
finder = new OptimizedPathFinder();
|
||||||
|
|||||||
@@ -1,16 +1,9 @@
|
|||||||
package io.anuke.mindustry.entities.units;
|
package io.anuke.mindustry.entities.units;
|
||||||
|
|
||||||
import com.badlogic.gdx.math.Vector2;
|
import com.badlogic.gdx.math.Vector2;
|
||||||
import com.badlogic.gdx.utils.ObjectSet;
|
|
||||||
import io.anuke.mindustry.entities.Unit;
|
import io.anuke.mindustry.entities.Unit;
|
||||||
import io.anuke.mindustry.game.TeamInfo.TeamData;
|
|
||||||
import io.anuke.mindustry.resource.AmmoType;
|
|
||||||
import io.anuke.mindustry.world.Tile;
|
|
||||||
import io.anuke.ucore.core.Timers;
|
|
||||||
import io.anuke.ucore.graphics.Draw;
|
import io.anuke.ucore.graphics.Draw;
|
||||||
|
|
||||||
import static io.anuke.mindustry.Vars.state;
|
|
||||||
|
|
||||||
public class FlyingUnitType extends UnitType {
|
public class FlyingUnitType extends UnitType {
|
||||||
protected static Vector2 vec = new Vector2();
|
protected static Vector2 vec = new Vector2();
|
||||||
|
|
||||||
@@ -29,11 +22,7 @@ public class FlyingUnitType extends UnitType {
|
|||||||
super.update(unit);
|
super.update(unit);
|
||||||
|
|
||||||
unit.rotation = unit.velocity.angle();
|
unit.rotation = unit.velocity.angle();
|
||||||
|
unit.state.update(unit);
|
||||||
if(unit.velocity.len() > 0.2f && unit.timer.get(timerBoost, 2f)){
|
|
||||||
//Effects.effect(UnitFx.vtolHover, unit.x + Angles.trnsx(unit.rotation + 180f, boosterLength),
|
|
||||||
// unit.y + Angles.trnsy(unit.rotation + 180f, boosterLength));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -47,51 +36,6 @@ public class FlyingUnitType extends UnitType {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void behavior(BaseUnit unit) {
|
public void behavior(BaseUnit unit) {
|
||||||
vec.set(unit.target.x - unit.x, unit.target.y - unit.y);
|
|
||||||
|
|
||||||
float len = vec.len();
|
|
||||||
|
|
||||||
float circleLength = 40f;
|
|
||||||
|
|
||||||
if(vec.len() < circleLength){
|
|
||||||
vec.rotate((circleLength-vec.len())/circleLength * 180f);
|
|
||||||
}
|
|
||||||
|
|
||||||
vec.setLength(speed * Timers.delta());
|
|
||||||
|
|
||||||
unit.velocity.add(vec); //TODO clamp it so it doesn't glitch out at low fps
|
|
||||||
|
|
||||||
if(unit.inventory.hasAmmo() && unit.timer.get(timerReload, reload) && len < range){
|
|
||||||
AmmoType ammo = unit.inventory.getAmmo();
|
|
||||||
unit.inventory.useAmmo();
|
|
||||||
|
|
||||||
shoot(unit, ammo, unit.rotation, 4f);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void updateTargeting(BaseUnit unit) {
|
|
||||||
if(!unit.timer.get(timerTarget, 20)) return;
|
|
||||||
|
|
||||||
ObjectSet<TeamData> teams = state.teams.enemyDataOf(unit.team);
|
|
||||||
|
|
||||||
Tile closest = null;
|
|
||||||
float cdist = 0f;
|
|
||||||
|
|
||||||
for(TeamData data : teams){
|
|
||||||
for(Tile tile : data.cores){
|
|
||||||
float dist = Vector2.dst(unit.x, unit.y, tile.drawx(), tile.drawy());
|
|
||||||
if(closest == null || dist < cdist){
|
|
||||||
closest = tile;
|
|
||||||
cdist = dist;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(closest != null){
|
|
||||||
unit.target = closest.entity;
|
|
||||||
}else{
|
|
||||||
unit.target = null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,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 set(BaseUnit unit, UnitState next){
|
||||||
|
if(state != null) state.exited(unit);
|
||||||
|
this.state = next;
|
||||||
|
if(next != null) next.entered(unit);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +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){}
|
||||||
|
}
|
||||||
@@ -5,7 +5,6 @@ import com.badlogic.gdx.utils.ObjectMap;
|
|||||||
import io.anuke.mindustry.content.fx.ExplosionFx;
|
import io.anuke.mindustry.content.fx.ExplosionFx;
|
||||||
import io.anuke.mindustry.entities.Bullet;
|
import io.anuke.mindustry.entities.Bullet;
|
||||||
import io.anuke.mindustry.entities.Unit;
|
import io.anuke.mindustry.entities.Unit;
|
||||||
import io.anuke.mindustry.entities.Units;
|
|
||||||
import io.anuke.mindustry.net.Net;
|
import io.anuke.mindustry.net.Net;
|
||||||
import io.anuke.mindustry.net.NetEvents;
|
import io.anuke.mindustry.net.NetEvents;
|
||||||
import io.anuke.mindustry.resource.AmmoType;
|
import io.anuke.mindustry.resource.AmmoType;
|
||||||
@@ -59,6 +58,10 @@ public abstract class UnitType {
|
|||||||
|
|
||||||
public abstract void draw(BaseUnit unit);
|
public abstract void draw(BaseUnit unit);
|
||||||
|
|
||||||
|
public UnitState getStartState(){
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
public void drawUnder(BaseUnit unit){}
|
public void drawUnder(BaseUnit unit){}
|
||||||
|
|
||||||
public boolean isFlying(){
|
public boolean isFlying(){
|
||||||
@@ -94,10 +97,6 @@ public abstract class UnitType {
|
|||||||
if(unit.target == null || (unit.target instanceof Unit && ((Unit)unit.target).isDead())){
|
if(unit.target == null || (unit.target instanceof Unit && ((Unit)unit.target).isDead())){
|
||||||
unit.target = null;
|
unit.target = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(unit.timer.get(timerTarget, 20)){
|
|
||||||
unit.target = Units.getClosestEnemy(unit.team, unit.x, unit.y, range, e -> true);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void shoot(BaseUnit unit, AmmoType type, float rotation, float translation){
|
public void shoot(BaseUnit unit, AmmoType type, float rotation, float translation){
|
||||||
|
|||||||
@@ -1,26 +1,49 @@
|
|||||||
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.AmmoTypes;
|
||||||
|
import io.anuke.mindustry.content.fx.UnitFx;
|
||||||
import io.anuke.mindustry.entities.Unit;
|
import io.anuke.mindustry.entities.Unit;
|
||||||
|
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.FlyingUnitType;
|
||||||
|
import io.anuke.mindustry.entities.units.UnitState;
|
||||||
import io.anuke.mindustry.graphics.Palette;
|
import io.anuke.mindustry.graphics.Palette;
|
||||||
|
import io.anuke.mindustry.resource.AmmoType;
|
||||||
|
import io.anuke.mindustry.world.Tile;
|
||||||
|
import io.anuke.mindustry.world.flags.BlockFlag;
|
||||||
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.Geometry;
|
||||||
import io.anuke.ucore.util.Mathf;
|
import io.anuke.ucore.util.Mathf;
|
||||||
|
|
||||||
|
import static io.anuke.mindustry.Vars.world;
|
||||||
|
|
||||||
public class Vtol extends FlyingUnitType {
|
public class Vtol extends FlyingUnitType {
|
||||||
|
|
||||||
public Vtol(){
|
public Vtol(){
|
||||||
super("vtol");
|
super("vtol");
|
||||||
setAmmo(AmmoTypes.basicIron);
|
setAmmo(AmmoTypes.basicIron);
|
||||||
|
speed = 0.3f;
|
||||||
|
maxVelocity = 2f;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void drawUnder(BaseUnit unit) {
|
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();
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
for(int i : Mathf.signs) {
|
for(int i : Mathf.signs) {
|
||||||
|
|
||||||
float rotation = unit.rotation - 90;
|
float rotation = unit.rotation - 90;
|
||||||
@@ -32,16 +55,14 @@ public class Vtol extends FlyingUnitType {
|
|||||||
|
|
||||||
Draw.rect("vtol-flame",
|
Draw.rect("vtol-flame",
|
||||||
unit.x + Angles.trnsx(rotation, dx, dy),
|
unit.x + Angles.trnsx(rotation, dx, dy),
|
||||||
unit.y + Angles.trnsy(rotation, dx, dy), Mathf.atan2(dx, dy));
|
unit.y + Angles.trnsy(rotation, dx, dy), Mathf.atan2(dx, dy) + rotation);
|
||||||
|
|
||||||
Draw.rect("vtol-flame",
|
Draw.rect("vtol-flame",
|
||||||
unit.x + Angles.trnsx(rotation, dx2, dy2),
|
unit.x + Angles.trnsx(rotation, dx2, dy2),
|
||||||
unit.y + Angles.trnsy(rotation, dx2, dy2), Mathf.atan2(dx2, dy2));
|
unit.y + Angles.trnsy(rotation, dx2, dy2), Mathf.atan2(dx2, dy2) + rotation);
|
||||||
|
|
||||||
Draw.color();
|
Draw.color();
|
||||||
}
|
}*/
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -50,8 +71,8 @@ public class Vtol extends FlyingUnitType {
|
|||||||
|
|
||||||
Draw.rect(name, unit.x, unit.y, unit.rotation - 90);
|
Draw.rect(name, unit.x, unit.y, unit.rotation - 90);
|
||||||
for(int i : Mathf.signs){
|
for(int i : Mathf.signs){
|
||||||
Draw.rect(name + "-booster-1", unit.x + i, unit.y, 12*i, 12, unit.rotation - 90);
|
Draw.rect(name + "-booster-1", unit.x, unit.y, 12*i, 12, unit.rotation - 90);
|
||||||
Draw.rect(name + "-booster-2", unit.x + i, unit.y, 12*i, 12, unit.rotation - 90);
|
Draw.rect(name + "-booster-2", unit.x, unit.y, 12*i, 12, unit.rotation - 90);
|
||||||
}
|
}
|
||||||
|
|
||||||
Draw.alpha(1f);
|
Draw.alpha(1f);
|
||||||
@@ -61,14 +82,103 @@ public class Vtol extends FlyingUnitType {
|
|||||||
public void update(BaseUnit unit) {
|
public void update(BaseUnit unit) {
|
||||||
super.update(unit);
|
super.update(unit);
|
||||||
|
|
||||||
unit.x += Mathf.sin(Timers.time() + unit.id*999, 25f, 0.07f);
|
unit.x += Mathf.sin(Timers.time() + unit.id * 999, 25f, 0.07f);
|
||||||
unit.y += Mathf.cos(Timers.time() + unit.id*999, 25f, 0.07f);
|
unit.y += Mathf.cos(Timers.time() + unit.id * 999, 25f, 0.07f);
|
||||||
unit.rotation += Mathf.sin(Timers.time() + unit.id*99, 10f, 8f);
|
|
||||||
|
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(){
|
||||||
public void behavior(BaseUnit unit) {
|
return resupply;
|
||||||
//super.behavior(unit);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void circle(BaseUnit unit, float circleLength){
|
||||||
|
vec.set(unit.target.x - unit.x, unit.target.y - unit.y);
|
||||||
|
|
||||||
|
if(vec.len() < circleLength){
|
||||||
|
vec.rotate((circleLength-vec.len())/circleLength * 180f);
|
||||||
|
}
|
||||||
|
|
||||||
|
vec.setLength(speed * Timers.delta());
|
||||||
|
|
||||||
|
unit.velocity.add(vec);
|
||||||
|
}
|
||||||
|
|
||||||
|
void attack(BaseUnit unit, float circleLength){
|
||||||
|
vec.set(unit.target.x - unit.x, unit.target.y - 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.4f));
|
||||||
|
}
|
||||||
|
|
||||||
|
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)){
|
||||||
|
if(unit.timer.get(timerTarget, 20)) {
|
||||||
|
Tile target = Geometry.findClosest(unit.x, unit.y, world.indexer().getAllied(unit.team, BlockFlag.resupplyPoint));
|
||||||
|
if (target != null) unit.target = target.entity;
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
circle(unit, 20f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
attack = new UnitState(){
|
||||||
|
public void entered(BaseUnit unit) {
|
||||||
|
unit.target = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void update(BaseUnit unit) {
|
||||||
|
|
||||||
|
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 -> true);
|
||||||
|
if(closest != null){
|
||||||
|
unit.target = closest;
|
||||||
|
}else {
|
||||||
|
Tile target = Geometry.findClosest(unit.x, unit.y, world.indexer().getEnemy(unit.team, BlockFlag.resupplyPoint));
|
||||||
|
if (target != null) unit.target = target.entity;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
attack(unit, 150f);
|
||||||
|
|
||||||
|
if (unit.timer.get(timerReload, 7) && Mathf.angNear(unit.angleTo(unit.target), unit.rotation, 16f)
|
||||||
|
&& unit.distanceTo(unit.target) < unit.inventory.getAmmo().getRange()) {
|
||||||
|
AmmoType ammo = unit.inventory.getAmmo();
|
||||||
|
unit.inventory.useAmmo();
|
||||||
|
|
||||||
|
shoot(unit, ammo, unit.rotation, 4f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,6 +53,10 @@ public class AmmoType {
|
|||||||
this.quantityMultiplier = multiplier;
|
this.quantityMultiplier = multiplier;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public float getRange(){
|
||||||
|
return bullet.speed * bullet.lifetime;
|
||||||
|
}
|
||||||
|
|
||||||
public static Array<AmmoType> getAllTypes() {
|
public static Array<AmmoType> getAllTypes() {
|
||||||
return allTypes;
|
return allTypes;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,12 +20,14 @@ import io.anuke.mindustry.net.NetEvents;
|
|||||||
import io.anuke.mindustry.resource.Item;
|
import io.anuke.mindustry.resource.Item;
|
||||||
import io.anuke.mindustry.resource.ItemStack;
|
import io.anuke.mindustry.resource.ItemStack;
|
||||||
import io.anuke.mindustry.resource.Liquid;
|
import io.anuke.mindustry.resource.Liquid;
|
||||||
|
import io.anuke.mindustry.world.flags.BlockFlag;
|
||||||
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.Hue;
|
import io.anuke.ucore.graphics.Hue;
|
||||||
import io.anuke.ucore.graphics.Lines;
|
import io.anuke.ucore.graphics.Lines;
|
||||||
import io.anuke.ucore.scene.ui.layout.Table;
|
import io.anuke.ucore.scene.ui.layout.Table;
|
||||||
import io.anuke.ucore.util.Bundles;
|
import io.anuke.ucore.util.Bundles;
|
||||||
|
import io.anuke.ucore.util.EnumSet;
|
||||||
import io.anuke.ucore.util.Mathf;
|
import io.anuke.ucore.util.Mathf;
|
||||||
|
|
||||||
import static io.anuke.mindustry.Vars.*;
|
import static io.anuke.mindustry.Vars.*;
|
||||||
@@ -105,6 +107,8 @@ public class Block extends BaseBlock {
|
|||||||
public BlockBars bars = new BlockBars();
|
public BlockBars bars = new BlockBars();
|
||||||
/**List of block stats.*/
|
/**List of block stats.*/
|
||||||
public BlockStats stats = new BlockStats();
|
public BlockStats stats = new BlockStats();
|
||||||
|
/**List of block flags. Used for AI indexing.*/
|
||||||
|
public EnumSet<BlockFlag> flags;
|
||||||
/**Whether to automatically set the entity to 'sleeping' when created.*/
|
/**Whether to automatically set the entity to 'sleeping' when created.*/
|
||||||
public boolean autoSleep;
|
public boolean autoSleep;
|
||||||
|
|
||||||
|
|||||||
@@ -11,12 +11,13 @@ import io.anuke.mindustry.world.blocks.types.modules.LiquidModule;
|
|||||||
import io.anuke.mindustry.world.blocks.types.modules.PowerModule;
|
import io.anuke.mindustry.world.blocks.types.modules.PowerModule;
|
||||||
import io.anuke.ucore.function.Consumer;
|
import io.anuke.ucore.function.Consumer;
|
||||||
import io.anuke.ucore.util.Bits;
|
import io.anuke.ucore.util.Bits;
|
||||||
|
import io.anuke.ucore.util.Position;
|
||||||
|
|
||||||
import static io.anuke.mindustry.Vars.tilesize;
|
import static io.anuke.mindustry.Vars.tilesize;
|
||||||
import static io.anuke.mindustry.Vars.world;
|
import static io.anuke.mindustry.Vars.world;
|
||||||
|
|
||||||
|
|
||||||
public class Tile{
|
public class Tile implements Position{
|
||||||
public static final Object tileSetLock = new Object();
|
public static final Object tileSetLock = new Object();
|
||||||
|
|
||||||
/**Block ID data.*/
|
/**Block ID data.*/
|
||||||
@@ -300,6 +301,7 @@ public class Tile{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void changed(){
|
public void changed(){
|
||||||
|
|
||||||
synchronized (tileSetLock) {
|
synchronized (tileSetLock) {
|
||||||
if (entity != null) {
|
if (entity != null) {
|
||||||
entity.remove();
|
entity.remove();
|
||||||
@@ -323,6 +325,16 @@ public class Tile{
|
|||||||
world.notifyChanged(this);
|
world.notifyChanged(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public float getX() {
|
||||||
|
return drawx();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public float getY() {
|
||||||
|
return drawy();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString(){
|
public String toString(){
|
||||||
Block block = block();
|
Block block = block();
|
||||||
|
|||||||
@@ -5,13 +5,16 @@ import io.anuke.mindustry.entities.TileEntity;
|
|||||||
import io.anuke.mindustry.world.Edges;
|
import io.anuke.mindustry.world.Edges;
|
||||||
import io.anuke.mindustry.world.Tile;
|
import io.anuke.mindustry.world.Tile;
|
||||||
import io.anuke.mindustry.world.blocks.types.PowerBlock;
|
import io.anuke.mindustry.world.blocks.types.PowerBlock;
|
||||||
|
import io.anuke.mindustry.world.flags.BlockFlag;
|
||||||
import io.anuke.ucore.core.Timers;
|
import io.anuke.ucore.core.Timers;
|
||||||
|
import io.anuke.ucore.util.EnumSet;
|
||||||
|
|
||||||
public class PowerGenerator extends PowerBlock {
|
public class PowerGenerator extends PowerBlock {
|
||||||
|
|
||||||
public PowerGenerator(String name) {
|
public PowerGenerator(String name) {
|
||||||
super(name);
|
super(name);
|
||||||
baseExplosiveness = 5f;
|
baseExplosiveness = 5f;
|
||||||
|
flags = EnumSet.of(BlockFlag.producer);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void distributePower(Tile tile){
|
protected void distributePower(Tile tile){
|
||||||
|
|||||||
@@ -6,8 +6,10 @@ import io.anuke.mindustry.entities.effect.ItemTransferEffect;
|
|||||||
import io.anuke.mindustry.net.Net;
|
import io.anuke.mindustry.net.Net;
|
||||||
import io.anuke.mindustry.resource.Item;
|
import io.anuke.mindustry.resource.Item;
|
||||||
import io.anuke.mindustry.world.Tile;
|
import io.anuke.mindustry.world.Tile;
|
||||||
|
import io.anuke.mindustry.world.flags.BlockFlag;
|
||||||
import io.anuke.ucore.graphics.Draw;
|
import io.anuke.ucore.graphics.Draw;
|
||||||
import io.anuke.ucore.graphics.Lines;
|
import io.anuke.ucore.graphics.Lines;
|
||||||
|
import io.anuke.ucore.util.EnumSet;
|
||||||
|
|
||||||
import static io.anuke.mindustry.Vars.state;
|
import static io.anuke.mindustry.Vars.state;
|
||||||
|
|
||||||
@@ -28,6 +30,7 @@ public class CoreBlock extends StorageBlock {
|
|||||||
size = 3;
|
size = 3;
|
||||||
hasItems = true;
|
hasItems = true;
|
||||||
itemCapacity = 2000;
|
itemCapacity = 2000;
|
||||||
|
flags = EnumSet.of(BlockFlag.resupplyPoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ import io.anuke.mindustry.entities.effect.ItemTransferEffect;
|
|||||||
import io.anuke.mindustry.resource.Item;
|
import io.anuke.mindustry.resource.Item;
|
||||||
import io.anuke.mindustry.world.Block;
|
import io.anuke.mindustry.world.Block;
|
||||||
import io.anuke.mindustry.world.Tile;
|
import io.anuke.mindustry.world.Tile;
|
||||||
|
import io.anuke.mindustry.world.flags.BlockFlag;
|
||||||
|
import io.anuke.ucore.util.EnumSet;
|
||||||
|
|
||||||
public class ResupplyPoint extends Block{
|
public class ResupplyPoint extends Block{
|
||||||
private static Rectangle rect = new Rectangle();
|
private static Rectangle rect = new Rectangle();
|
||||||
@@ -19,6 +21,7 @@ public class ResupplyPoint extends Block{
|
|||||||
super(name);
|
super(name);
|
||||||
update = true;
|
update = true;
|
||||||
solid = true;
|
solid = true;
|
||||||
|
flags = EnumSet.of(BlockFlag.resupplyPoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
package io.anuke.mindustry.world.flags;
|
||||||
|
|
||||||
|
public enum BlockFlag {
|
||||||
|
resupplyPoint, producer
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user