mirror of
https://github.com/Anuken/Mindustry.git
synced 2025-01-30 09:30:39 +07:00
Added unit squads, new spawning, WaveSpawner class
This commit is contained in:
parent
e79494b5cb
commit
33a278ccb4
@ -1,5 +1,6 @@
|
||||
package io.anuke.mindustry.ai;
|
||||
|
||||
import com.badlogic.gdx.utils.Bits;
|
||||
import com.badlogic.gdx.utils.IntMap;
|
||||
import com.badlogic.gdx.utils.ObjectMap;
|
||||
import com.badlogic.gdx.utils.ObjectSet;
|
||||
@ -7,9 +8,10 @@ import io.anuke.mindustry.content.Items;
|
||||
import io.anuke.mindustry.game.EventType.TileChangeEvent;
|
||||
import io.anuke.mindustry.game.EventType.WorldLoadEvent;
|
||||
import io.anuke.mindustry.game.Team;
|
||||
import io.anuke.mindustry.game.TeamInfo.TeamData;
|
||||
import io.anuke.mindustry.type.Item;
|
||||
import io.anuke.mindustry.world.meta.BlockFlag;
|
||||
import io.anuke.mindustry.world.Tile;
|
||||
import io.anuke.mindustry.world.meta.BlockFlag;
|
||||
import io.anuke.ucore.core.Events;
|
||||
import io.anuke.ucore.util.EnumSet;
|
||||
import io.anuke.ucore.util.Mathf;
|
||||
@ -18,16 +20,22 @@ import static io.anuke.mindustry.Vars.state;
|
||||
import static io.anuke.mindustry.Vars.world;
|
||||
|
||||
//TODO consider using quadtrees for finding specific types of blocks within an area
|
||||
/**Class used for indexing special target blocks for AI.
|
||||
* TODO maybe use Arrays instead of ObjectSets?*/
|
||||
//TODO maybe use Arrays instead of ObjectSets?
|
||||
/**Class used for indexing special target blocks for AI.*/
|
||||
public class BlockIndexer {
|
||||
/**Size of one ore quadrant.*/
|
||||
private final static int quadrantSize = 12;
|
||||
private final static int oreQuadrantSize = 12;
|
||||
/**Size of one structure quadrant.*/
|
||||
private final static int structQuadrantSize = 12;
|
||||
|
||||
/**Set of all ores that are being scanned.*/
|
||||
private final ObjectSet<Item> scanOres = ObjectSet.with(Items.iron, Items.coal, Items.lead, Items.thorium, Items.titanium);
|
||||
/**Stores all ore quadtrants on the map.*/
|
||||
private ObjectMap<Item, ObjectSet<Tile>> ores = new ObjectMap<>();
|
||||
|
||||
/**Tags all quadrants.*/
|
||||
private Bits[] structQuadrants;
|
||||
|
||||
/**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.*/
|
||||
@ -48,6 +56,7 @@ public class BlockIndexer {
|
||||
}
|
||||
}
|
||||
process(tile);
|
||||
updateQuadrant(tile);
|
||||
});
|
||||
|
||||
Events.on(WorldLoadEvent.class, () -> {
|
||||
@ -55,6 +64,13 @@ public class BlockIndexer {
|
||||
allyMap.clear();
|
||||
typeMap.clear();
|
||||
ores.clear();
|
||||
|
||||
//create bitset for each team type that contains each quadrant
|
||||
structQuadrants = new Bits[Team.values().length];
|
||||
for(int i = 0; i < Team.values().length; i ++){
|
||||
structQuadrants[i] = new Bits(Mathf.ceil(world.width() / (float)structQuadrantSize) * Mathf.ceil(world.height() / (float)structQuadrantSize));
|
||||
}
|
||||
|
||||
for(int x = 0; x < world.width(); x ++){
|
||||
for (int y = 0; y < world.height(); y++) {
|
||||
process(world.tile(x, y));
|
||||
@ -77,7 +93,7 @@ public class BlockIndexer {
|
||||
|
||||
/**Returns a set of tiles that have ores of the specified type nearby.
|
||||
* While each tile in the set is not guaranteed to have an ore directly on it,
|
||||
* each tile will at least have an ore within {@link #quadrantSize} / 2 blocks of it.
|
||||
* each tile will at least have an ore within {@link #oreQuadrantSize} / 2 blocks of it.
|
||||
* Only specific ore types are scanned. See {@link #scanOres}.*/
|
||||
public ObjectSet<Tile> getOrePositions(Item item){
|
||||
return ores.get(item, emptyArray);
|
||||
@ -104,6 +120,36 @@ public class BlockIndexer {
|
||||
}
|
||||
}
|
||||
|
||||
private void updateQuadrant(Tile tile){
|
||||
//this quadrant is now 'dirty', re-scan the whole thing
|
||||
int quadrantX = tile.x / structQuadrantSize;
|
||||
int quadrantY = tile.y / structQuadrantSize;
|
||||
int index = quadrantX * Mathf.ceil(world.width() / (float)structQuadrantSize) + quadrantY;
|
||||
|
||||
for(TeamData data : state.teams.getTeams()) {
|
||||
|
||||
//fast-set this quadrant to 'occupied' if the tile just placed is already of this team
|
||||
if(tile.getTeam() == data.team && tile.entity != null){
|
||||
structQuadrants[data.team.ordinal()].set(index);
|
||||
continue; //no need to process futher
|
||||
}
|
||||
|
||||
structQuadrants[data.team.ordinal()].clear(index);
|
||||
|
||||
outer:
|
||||
for (int x = quadrantX * structQuadrantSize; x < world.width() && x < (quadrantX + 1) * structQuadrantSize; x++) {
|
||||
for (int y = quadrantY * structQuadrantSize; y < world.height() && y < (quadrantY + 1) * structQuadrantSize; y++) {
|
||||
Tile result = world.tile(x, y);
|
||||
//when a targetable block is found, mark this quadrant as occupied and stop searching
|
||||
if(result.entity != null && result.getTeam() == data.team){
|
||||
structQuadrants[data.team.ordinal()].set(index);
|
||||
break outer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private ObjectMap<BlockFlag, ObjectSet<Tile>> getMap(Team team){
|
||||
if(!state.teams.has(team)) return emptyMap;
|
||||
return state.teams.get(team).ally ? allyMap : enemyMap;
|
||||
@ -117,8 +163,8 @@ public class BlockIndexer {
|
||||
|
||||
for(int x = 0; x < world.width(); x ++){
|
||||
for (int y = 0; y < world.height(); y++) {
|
||||
int qx = (x/quadrantSize);
|
||||
int qy = (y/quadrantSize);
|
||||
int qx = (x/ oreQuadrantSize);
|
||||
int qy = (y/ oreQuadrantSize);
|
||||
|
||||
Tile tile = world.tile(x, y);
|
||||
|
||||
@ -126,8 +172,8 @@ public class BlockIndexer {
|
||||
if(tile.floor().drops != null && scanOres.contains(tile.floor().drops.item)){
|
||||
ores.get(tile.floor().drops.item).add(world.tile(
|
||||
//make sure to clamp quadrant middle position, since it might go off bounds
|
||||
Mathf.clamp(qx * quadrantSize + quadrantSize/2, 0, world.width() - 1),
|
||||
Mathf.clamp(qy * quadrantSize + quadrantSize/2, 0, world.height() - 1)));
|
||||
Mathf.clamp(qx * oreQuadrantSize + oreQuadrantSize /2, 0, world.width() - 1),
|
||||
Mathf.clamp(qy * oreQuadrantSize + oreQuadrantSize /2, 0, world.height() - 1)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,27 +0,0 @@
|
||||
package io.anuke.mindustry.ai;
|
||||
|
||||
import io.anuke.mindustry.game.EventType.WorldLoadEvent;
|
||||
import io.anuke.ucore.core.Events;
|
||||
|
||||
import static io.anuke.mindustry.Vars.world;
|
||||
|
||||
public class SpawnSelector {
|
||||
private static final int quadsize = 15;
|
||||
|
||||
public SpawnSelector(){
|
||||
Events.on(WorldLoadEvent.class, this::reset);
|
||||
}
|
||||
|
||||
public void calculateSpawn(){
|
||||
|
||||
for(int x = 0; x < world.width(); x += quadsize){
|
||||
for(int y = 0; y < world.height(); y += quadsize){
|
||||
//TODO quadrant operations, etc
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void reset(){
|
||||
|
||||
}
|
||||
}
|
93
core/src/io/anuke/mindustry/ai/WaveSpawner.java
Normal file
93
core/src/io/anuke/mindustry/ai/WaveSpawner.java
Normal file
@ -0,0 +1,93 @@
|
||||
package io.anuke.mindustry.ai;
|
||||
|
||||
import com.badlogic.gdx.utils.Array;
|
||||
import io.anuke.mindustry.content.AmmoTypes;
|
||||
import io.anuke.mindustry.content.UnitTypes;
|
||||
import io.anuke.mindustry.entities.units.BaseUnit;
|
||||
import io.anuke.mindustry.entities.units.Squad;
|
||||
import io.anuke.mindustry.game.EventType.WorldLoadEvent;
|
||||
import io.anuke.mindustry.game.Team;
|
||||
import io.anuke.ucore.core.Events;
|
||||
import io.anuke.ucore.util.Mathf;
|
||||
|
||||
import static io.anuke.mindustry.Vars.*;
|
||||
|
||||
public class WaveSpawner {
|
||||
private static final int quadsize = 15;
|
||||
|
||||
private Array<FlyerSpawn> flySpawns = new Array<>();
|
||||
private Array<GroundSpawn> groundSpawns = new Array<>();
|
||||
|
||||
public WaveSpawner(){
|
||||
Events.on(WorldLoadEvent.class, this::reset);
|
||||
}
|
||||
|
||||
public void spawnEnemies(){
|
||||
int spawned = 10;
|
||||
int groundGroups = Math.min(1 + state.wave / 20, 4);
|
||||
int flyGroups = Math.min(1 + state.wave / 20, 4);
|
||||
|
||||
//add extra groups if necessary
|
||||
for (int i = 0; i < groundGroups - groundSpawns.size; i++) {
|
||||
GroundSpawn spawn = new GroundSpawn();
|
||||
}
|
||||
|
||||
for (int i = 0; i < flyGroups - flySpawns.size; i++) {
|
||||
FlyerSpawn spawn = new FlyerSpawn();
|
||||
spawn.angle = Mathf.random(360f);
|
||||
|
||||
flySpawns.add(spawn);
|
||||
}
|
||||
|
||||
for(GroundSpawn spawn : groundSpawns){
|
||||
|
||||
}
|
||||
|
||||
for(FlyerSpawn spawn : flySpawns){
|
||||
Squad squad = new Squad();
|
||||
float addition = 40f;
|
||||
float spread = addition / 1.5f;
|
||||
|
||||
float baseX = world.width() *tilesize/2f + Mathf.sqrwavex(spawn.angle) * (world.width()/2f*tilesize + addition),
|
||||
baseY = world.height() * tilesize/2f + Mathf.sqrwavey(spawn.angle) * (world.height()/2f*tilesize + addition);
|
||||
|
||||
for(int i = 0; i < spawned; i ++){
|
||||
BaseUnit unit = UnitTypes.vtol.create(Team.red);
|
||||
unit.inventory.addAmmo(AmmoTypes.bulletIron);
|
||||
unit.setWave();
|
||||
unit.setSquad(squad);
|
||||
unit.set(baseX + Mathf.range(spread), baseY + Mathf.range(spread));
|
||||
unit.add();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void calculateSpawn(){
|
||||
|
||||
for(int x = 0; x < world.width(); x += quadsize){
|
||||
for(int y = 0; y < world.height(); y += quadsize){
|
||||
//TODO quadrant operations, etc
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void reset(){
|
||||
flySpawns.clear();
|
||||
groundSpawns.clear();
|
||||
}
|
||||
|
||||
private class FlyerSpawn{
|
||||
float angle;
|
||||
|
||||
FlyerSpawn(){
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private class GroundSpawn{
|
||||
|
||||
GroundSpawn(){
|
||||
|
||||
}
|
||||
}
|
||||
}
|
@ -31,7 +31,8 @@ public class WeaponBlocks extends BlockList implements ContentList {
|
||||
reload = 60f;
|
||||
restitution = 0.03f;
|
||||
recoil = 1.5f;
|
||||
burstSpacing = 6f;
|
||||
burstSpacing = 1f;
|
||||
inaccuracy = 7f;
|
||||
ammoUseEffect = ShootFx.shellEjectSmall;
|
||||
}};
|
||||
|
||||
@ -47,6 +48,7 @@ public class WeaponBlocks extends BlockList implements ContentList {
|
||||
|
||||
hail = new ItemTurret("hail") {{
|
||||
ammoTypes = new AmmoType[]{AmmoTypes.artilleryLead, AmmoTypes.artilleryHoming, AmmoTypes.artilleryIncindiary};
|
||||
reload = 40f;
|
||||
}};
|
||||
|
||||
wave = new LiquidTurret("wave") {{
|
||||
|
@ -1,5 +1,6 @@
|
||||
package io.anuke.mindustry.core;
|
||||
|
||||
import io.anuke.mindustry.ai.WaveSpawner;
|
||||
import io.anuke.mindustry.game.Difficulty;
|
||||
import io.anuke.mindustry.game.EventType.StateChangeEvent;
|
||||
import io.anuke.mindustry.game.GameMode;
|
||||
@ -17,6 +18,7 @@ public class GameState{
|
||||
public GameMode mode = GameMode.waves;
|
||||
public Difficulty difficulty = Difficulty.normal;
|
||||
public boolean friendlyFire;
|
||||
public WaveSpawner spawner = new WaveSpawner();
|
||||
public TeamInfo teams = new TeamInfo();
|
||||
|
||||
public void set(State astate){
|
||||
|
@ -1,11 +1,8 @@
|
||||
package io.anuke.mindustry.core;
|
||||
|
||||
import com.badlogic.gdx.math.Vector2;
|
||||
import io.anuke.mindustry.content.AmmoTypes;
|
||||
import io.anuke.mindustry.content.UnitTypes;
|
||||
import io.anuke.mindustry.core.GameState.State;
|
||||
import io.anuke.mindustry.entities.TileEntity;
|
||||
import io.anuke.mindustry.entities.units.BaseUnit;
|
||||
import io.anuke.mindustry.game.EventType.GameOverEvent;
|
||||
import io.anuke.mindustry.game.EventType.PlayEvent;
|
||||
import io.anuke.mindustry.game.EventType.ResetEvent;
|
||||
import io.anuke.mindustry.game.EventType.WaveEvent;
|
||||
@ -30,8 +27,7 @@ import static io.anuke.mindustry.Vars.*;
|
||||
* Handles game state events.
|
||||
* Does not store any game state itself.
|
||||
*
|
||||
* This class should <i>not</i> call any outside methods to change state of modules, but instead fire events.
|
||||
*/
|
||||
* This class should <i>not</i> call any outside methods to change state of modules, but instead fire events.*/
|
||||
public class Logic extends Module {
|
||||
public boolean doUpdate = true;
|
||||
|
||||
@ -80,17 +76,7 @@ public class Logic extends Module {
|
||||
}
|
||||
|
||||
public void runWave(){
|
||||
|
||||
//TODO spawn enemies properly
|
||||
for(int i = 0; i < 10; i ++){
|
||||
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.setWave();
|
||||
unit.set(offset.x, offset.y);
|
||||
unit.add();
|
||||
}
|
||||
|
||||
state.spawner.spawnEnemies();
|
||||
state.wave ++;
|
||||
state.wavetime = wavespace * state.difficulty.timeScaling;
|
||||
state.extrawavetime = maxwavespace * state.difficulty.maxTimeScaling;
|
||||
@ -98,6 +84,22 @@ public class Logic extends Module {
|
||||
Events.fire(WaveEvent.class);
|
||||
}
|
||||
|
||||
private void checkGameOver(){
|
||||
boolean gameOver = true;
|
||||
|
||||
for(TeamData data : state.teams.getTeams(true)){
|
||||
if(data.cores.size > 0){
|
||||
gameOver = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(gameOver && !state.gameOver){
|
||||
state.gameOver = true;
|
||||
Events.fire(GameOverEvent.class);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(){
|
||||
if(!doUpdate) return;
|
||||
@ -110,22 +112,10 @@ public class Logic extends Module {
|
||||
Timers.update();
|
||||
}
|
||||
|
||||
/*
|
||||
boolean gameOver = true;
|
||||
|
||||
for(TeamData data : state.teams.getTeams(true)){
|
||||
if(data.cores.size > 0){
|
||||
gameOver = false;
|
||||
break;
|
||||
}
|
||||
if(!debug){
|
||||
checkGameOver();
|
||||
}
|
||||
|
||||
if(gameOver && !state.gameOver){ //TODO better gameover state, victory state?
|
||||
state.gameOver = true;
|
||||
if(Net.server()) NetEvents.handleGameOver();
|
||||
Events.fire(GameOverEvent.class);
|
||||
}*/
|
||||
|
||||
if(!state.is(State.paused) || Net.active()){
|
||||
|
||||
if(!state.mode.disableWaveTimer){
|
||||
@ -158,11 +148,12 @@ public class Logic extends Module {
|
||||
if(!group.isEmpty()){
|
||||
EntityPhysics.collideGroups(bulletGroup, group);
|
||||
|
||||
/*
|
||||
for(EntityGroup other : unitGroups){
|
||||
if(!other.isEmpty()){
|
||||
EntityPhysics.collideGroups(group, other);
|
||||
}
|
||||
}
|
||||
}*/
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -414,6 +414,8 @@ public class Player extends Unit implements BuilderTrait, CarryTrait {
|
||||
updateMech();
|
||||
}
|
||||
|
||||
avoidOthers(8f);
|
||||
|
||||
float wobblyness = 0.6f;
|
||||
|
||||
trail.update(x + Angles.trnsx(rotation + 180f, 6f) + Mathf.range(wobblyness),
|
||||
@ -489,7 +491,7 @@ public class Player extends Unit implements BuilderTrait, CarryTrait {
|
||||
|
||||
velocity.add(movement);
|
||||
|
||||
updateVelocityStatus(mech.drag, mech.maxSpeed);
|
||||
updateVelocityStatus(mech.drag, debug ? speed : mech.maxSpeed);
|
||||
|
||||
if(!movement.isZero()){
|
||||
walktime += Timers.delta() * velocity.len()*(1f/0.5f)/speed * getFloorOn().speedMultiplier;
|
||||
|
@ -31,6 +31,7 @@ import static io.anuke.mindustry.Vars.world;
|
||||
|
||||
public class TileEntity extends BaseEntity implements TargetTrait {
|
||||
public static final float timeToSleep = 60f*4; //4 seconds to fall asleep
|
||||
/**This value is only used for debugging.*/
|
||||
public static int sleepingEntities = 0;
|
||||
|
||||
public Tile tile;
|
||||
|
@ -13,12 +13,14 @@ import io.anuke.mindustry.world.Tile;
|
||||
import io.anuke.mindustry.world.blocks.Floor;
|
||||
import io.anuke.ucore.core.Effects;
|
||||
import io.anuke.ucore.core.Timers;
|
||||
import io.anuke.ucore.entities.EntityPhysics;
|
||||
import io.anuke.ucore.entities.impl.DestructibleEntity;
|
||||
import io.anuke.ucore.entities.trait.DamageTrait;
|
||||
import io.anuke.ucore.entities.trait.DrawTrait;
|
||||
import io.anuke.ucore.entities.trait.SolidTrait;
|
||||
import io.anuke.ucore.util.Geometry;
|
||||
import io.anuke.ucore.util.Mathf;
|
||||
import io.anuke.ucore.util.Translator;
|
||||
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
@ -35,6 +37,8 @@ public abstract class Unit extends DestructibleEntity implements SaveTrait, Targ
|
||||
/**Maximum absolute value of a velocity vector component.*/
|
||||
public static final float maxAbsVelocity = 127f/velocityPercision;
|
||||
|
||||
private static final Vector2 moveVector = new Vector2();
|
||||
|
||||
public UnitInventory inventory = new UnitInventory(this);
|
||||
public float rotation;
|
||||
|
||||
@ -43,7 +47,7 @@ public abstract class Unit extends DestructibleEntity implements SaveTrait, Targ
|
||||
protected Team team = Team.blue;
|
||||
|
||||
protected CarryTrait carrier;
|
||||
protected Vector2 velocity = new Vector2(0f, 0.0001f);
|
||||
protected Vector2 velocity = new Translator(0f, 0.0001f);
|
||||
protected float hitTime;
|
||||
protected float drownTime;
|
||||
|
||||
@ -172,6 +176,16 @@ public abstract class Unit extends DestructibleEntity implements SaveTrait, Targ
|
||||
return tile == null ? (Floor) Blocks.air : tile.floor();
|
||||
}
|
||||
|
||||
public void avoidOthers(float avoidRange){
|
||||
|
||||
EntityPhysics.getNearby(getGroup(), x, y, avoidRange*2f, t -> {
|
||||
if(t == this || (t instanceof Unit && ((Unit) t).isDead())) return;
|
||||
float dst = distanceTo(t);
|
||||
if(dst > avoidRange) return;
|
||||
velocity.add(moveVector.set(x, y).sub(t.getX(), t.getY()).setLength(1f * (1f - (dst / avoidRange))));
|
||||
});
|
||||
}
|
||||
|
||||
/**Updates velocity and status effects.*/
|
||||
public void updateVelocityStatus(float drag, float maxVelocity){
|
||||
if(isCarried()){ //carried units do not take into account velocity normally
|
||||
|
@ -10,7 +10,7 @@ import io.anuke.mindustry.world.Block;
|
||||
import io.anuke.mindustry.world.Tile;
|
||||
import io.anuke.ucore.entities.EntityGroup;
|
||||
import io.anuke.ucore.entities.EntityPhysics;
|
||||
import io.anuke.ucore.entities.impl.BaseEntity;
|
||||
import io.anuke.ucore.entities.trait.Entity;
|
||||
import io.anuke.ucore.function.Consumer;
|
||||
import io.anuke.ucore.function.Predicate;
|
||||
import io.anuke.ucore.util.Mathf;
|
||||
@ -99,9 +99,10 @@ public class Units {
|
||||
return findTile(x, y, range, tile -> state.teams.areEnemies(team, tile.getTeam()) && pred.test(tile));
|
||||
}
|
||||
|
||||
//TODO optimize, spatial caching of tiles
|
||||
/**Returns the neareset tile entity in a range.*/
|
||||
public static TileEntity findTile(float x, float y, float range, Predicate<Tile> pred){
|
||||
BaseEntity closest = null;
|
||||
Entity closest = null;
|
||||
float dst = 0;
|
||||
|
||||
int rad = (int)(range/tilesize)+1;
|
||||
|
@ -1,5 +1,6 @@
|
||||
package io.anuke.mindustry.entities.units;
|
||||
|
||||
import com.badlogic.gdx.math.Vector2;
|
||||
import io.anuke.annotations.Annotations.Loc;
|
||||
import io.anuke.annotations.Annotations.Remote;
|
||||
import io.anuke.mindustry.content.fx.ExplosionFx;
|
||||
@ -19,10 +20,7 @@ import io.anuke.mindustry.world.meta.BlockFlag;
|
||||
import io.anuke.ucore.core.Effects;
|
||||
import io.anuke.ucore.core.Timers;
|
||||
import io.anuke.ucore.entities.EntityGroup;
|
||||
import io.anuke.ucore.util.Angles;
|
||||
import io.anuke.ucore.util.Geometry;
|
||||
import io.anuke.ucore.util.Mathf;
|
||||
import io.anuke.ucore.util.Timer;
|
||||
import io.anuke.ucore.util.*;
|
||||
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
@ -32,6 +30,7 @@ import static io.anuke.mindustry.Vars.*;
|
||||
|
||||
public abstract class BaseUnit extends Unit{
|
||||
private static int timerIndex = 0;
|
||||
private static Vector2 moveVector = new Translator();
|
||||
|
||||
protected static final int timerTarget = timerIndex++;
|
||||
protected static final int timerReload = timerIndex++;
|
||||
@ -39,9 +38,11 @@ public abstract class BaseUnit extends Unit{
|
||||
protected UnitType type;
|
||||
protected Timer timer = new Timer(5);
|
||||
protected StateMachine state = new StateMachine();
|
||||
protected boolean isWave;
|
||||
protected TargetTrait target;
|
||||
|
||||
protected boolean isWave;
|
||||
protected Squad squad;
|
||||
|
||||
public BaseUnit(UnitType type, Team team){
|
||||
this.type = type;
|
||||
this.team = team;
|
||||
@ -55,6 +56,11 @@ public abstract class BaseUnit extends Unit{
|
||||
isWave = true;
|
||||
}
|
||||
|
||||
public void setSquad(Squad squad) {
|
||||
this.squad = squad;
|
||||
squad.units ++;
|
||||
}
|
||||
|
||||
public void rotate(float angle){
|
||||
rotation = Mathf.slerpDelta(rotation, angle, type.rotatespeed);
|
||||
}
|
||||
@ -186,6 +192,12 @@ public abstract class BaseUnit extends Unit{
|
||||
return;
|
||||
}
|
||||
|
||||
avoidOthers(8f);
|
||||
|
||||
if(squad != null){
|
||||
squad.update();
|
||||
}
|
||||
|
||||
updateTargeting();
|
||||
|
||||
state.update();
|
||||
|
@ -23,7 +23,7 @@ public abstract class FlyingUnit extends BaseUnit implements CarryTrait{
|
||||
protected static float maxAim = 30f;
|
||||
protected static float wobblyness = 0.6f;
|
||||
|
||||
protected Trail trail = new Trail(16);
|
||||
protected Trail trail = new Trail(8);
|
||||
protected CarriableTrait carrying;
|
||||
|
||||
public FlyingUnit(UnitType type, Team team) {
|
||||
@ -79,6 +79,11 @@ public abstract class FlyingUnit extends BaseUnit implements CarryTrait{
|
||||
Geometry.findClosest(x, y, world.indexer().getAllied(team, BlockFlag.repair)) != null){
|
||||
setState(retreat);
|
||||
}
|
||||
|
||||
if(squad != null){
|
||||
squad.direction.add(velocity.x / squad.units, velocity.y / squad.units);
|
||||
velocity.setAngle(Mathf.slerpDelta(velocity.angle(), squad.direction.angle(), 0.3f));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
22
core/src/io/anuke/mindustry/entities/units/Squad.java
Normal file
22
core/src/io/anuke/mindustry/entities/units/Squad.java
Normal file
@ -0,0 +1,22 @@
|
||||
package io.anuke.mindustry.entities.units;
|
||||
|
||||
import com.badlogic.gdx.math.Vector2;
|
||||
import io.anuke.ucore.util.Translator;
|
||||
|
||||
import static io.anuke.mindustry.Vars.threads;
|
||||
|
||||
/**Used to group entities together, for formations and such.
|
||||
* Usually, squads are used by units spawned in the same wave.*/
|
||||
public class Squad {
|
||||
public Vector2 direction = new Translator();
|
||||
public int units;
|
||||
|
||||
private long lastUpdated;
|
||||
|
||||
protected void update(){
|
||||
if(threads.getFrameID() != lastUpdated){
|
||||
direction.setZero();
|
||||
lastUpdated = threads.getFrameID();
|
||||
}
|
||||
}
|
||||
}
|
@ -92,8 +92,8 @@ public class TeamInfo {
|
||||
public boolean areEnemies(Team team, Team other){
|
||||
if(team == other) return false; //fast fail to be more efficient
|
||||
boolean ally = (allyBits & (1 << team.ordinal())) != 0;
|
||||
boolean ally2 = (enemyBits & (1 << other.ordinal())) != 0;
|
||||
return (ally == ally2) || !ally; //if it's not in the game, target everything.
|
||||
boolean enemy = (enemyBits & (1 << other.ordinal())) != 0;
|
||||
return (ally == enemy) || !ally; //if it's not in the game, target everything.
|
||||
}
|
||||
|
||||
public class TeamData {
|
||||
|
@ -10,6 +10,7 @@ import io.anuke.mindustry.entities.bullet.Bullet;
|
||||
import io.anuke.mindustry.entities.units.BaseUnit;
|
||||
import io.anuke.mindustry.net.Net;
|
||||
import io.anuke.ucore.core.Timers;
|
||||
import io.anuke.ucore.entities.EntityGroup;
|
||||
import io.anuke.ucore.scene.Group;
|
||||
import io.anuke.ucore.scene.builders.button;
|
||||
import io.anuke.ucore.scene.builders.label;
|
||||
@ -46,7 +47,7 @@ public class DebugFragment implements Fragment {
|
||||
new table(){{
|
||||
visible(() -> debug);
|
||||
|
||||
atop().aright();
|
||||
abottom().aleft();
|
||||
|
||||
new table("pane"){{
|
||||
defaults().fillX().width(100f);
|
||||
@ -133,11 +134,20 @@ public class DebugFragment implements Fragment {
|
||||
}
|
||||
|
||||
public static String debugInfo(){
|
||||
int totalUnits = 0;
|
||||
for(EntityGroup<?> group : unitGroups){
|
||||
totalUnits += group.size();
|
||||
}
|
||||
|
||||
totalUnits += playerGroup.size();
|
||||
|
||||
StringBuilder result = join(
|
||||
"net.active: " + Net.active(),
|
||||
"net.server: " + Net.server(),
|
||||
"net.client: " + Net.client(),
|
||||
"state: " + state.getState(),
|
||||
"units: " + totalUnits,
|
||||
"bullets: " + bulletGroup.size(),
|
||||
Net.client() ?
|
||||
"chat.open: " + ui.chatfrag.chatOpen() + "\n" +
|
||||
"chat.messages: " + ui.chatfrag.getMessagesSize() + "\n" +
|
||||
|
@ -56,6 +56,11 @@ public class CoreBlock extends StorageBlock {
|
||||
flags = EnumSet.of(BlockFlag.resupplyPoint, BlockFlag.target);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float handleDamage(Tile tile, float amount) {
|
||||
return debug ? 0 : amount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Tile tile) {
|
||||
CoreEntity entity = tile.entity();
|
||||
|
Loading…
Reference in New Issue
Block a user