New team system / Prototype dedicated PvP gamemode
This commit is contained in:
@@ -34,6 +34,10 @@ public class Vars{
|
||||
public static final float wavespace = 60 * 60 * 1.5f;
|
||||
//set ridiculously high for now
|
||||
public static final float coreBuildRange = 800999f;
|
||||
//team of the player by default
|
||||
public static final Team defaultTeam = Team.blue;
|
||||
//team of the enemy in waves
|
||||
public static final Team waveTeam = Team.red;
|
||||
|
||||
public static final float enemyCoreBuildRange = 400f;
|
||||
//discord group URL
|
||||
|
||||
@@ -1,17 +1,14 @@
|
||||
package io.anuke.mindustry.ai;
|
||||
|
||||
import com.badlogic.gdx.math.Vector2;
|
||||
import com.badlogic.gdx.utils.Bits;
|
||||
import com.badlogic.gdx.utils.IntMap;
|
||||
import com.badlogic.gdx.utils.ObjectMap;
|
||||
import com.badlogic.gdx.utils.ObjectSet;
|
||||
import com.badlogic.gdx.utils.*;
|
||||
import io.anuke.mindustry.content.Items;
|
||||
import io.anuke.mindustry.content.blocks.Blocks;
|
||||
import io.anuke.mindustry.entities.TileEntity;
|
||||
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.game.Teams.TeamData;
|
||||
import io.anuke.mindustry.type.Item;
|
||||
import io.anuke.mindustry.world.Tile;
|
||||
import io.anuke.mindustry.world.meta.BlockFlag;
|
||||
@@ -21,66 +18,43 @@ import io.anuke.ucore.function.Predicate;
|
||||
import io.anuke.ucore.util.EnumSet;
|
||||
import io.anuke.ucore.util.Geometry;
|
||||
import io.anuke.ucore.util.Mathf;
|
||||
import io.anuke.ucore.util.ThreadArray;
|
||||
|
||||
import static io.anuke.mindustry.Vars.*;
|
||||
|
||||
//TODO consider using quadtrees for finding specific types of blocks within an area
|
||||
//TODO maybe use Arrays instead of ObjectSets?
|
||||
|
||||
/**
|
||||
* Class used for indexing special target blocks for AI.
|
||||
*/
|
||||
/**Class used for indexing special target blocks for AI.*/
|
||||
public class BlockIndexer{
|
||||
/**
|
||||
* Size of one ore quadrant.
|
||||
*/
|
||||
/**Size of one ore quadrant.*/
|
||||
private final static int oreQuadrantSize = 20;
|
||||
/**
|
||||
* Size of one structure quadrant.
|
||||
*/
|
||||
/**Size of one structure quadrant.*/
|
||||
private final static int structQuadrantSize = 12;
|
||||
|
||||
/**
|
||||
* Set of all ores that are being scanned.
|
||||
*/
|
||||
/**Set of all ores that are being scanned.*/
|
||||
private final ObjectSet<Item> scanOres = ObjectSet.with(Items.copper, Items.coal, Items.lead, Items.thorium, Items.titanium);
|
||||
private final ObjectSet<Item> itemSet = new ObjectSet<>();
|
||||
/**
|
||||
* Stores all ore quadtrants on the map.
|
||||
*/
|
||||
/**Stores all ore quadtrants on the map.*/
|
||||
private ObjectMap<Item, ObjectSet<Tile>> ores;
|
||||
/**
|
||||
* Tags all quadrants.
|
||||
*/
|
||||
/**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.
|
||||
*/
|
||||
private ObjectMap<BlockFlag, ObjectSet<Tile>> allyMap = new ObjectMap<>();
|
||||
/**
|
||||
* Empty map for invalid teams.
|
||||
*/
|
||||
private ObjectMap<BlockFlag, ObjectSet<Tile>> emptyMap = new ObjectMap<>();
|
||||
/**
|
||||
* Maps tile positions to their last known tile index data.
|
||||
*/
|
||||
/**Maps teams to a map of flagged tiles by type.*/
|
||||
private ObjectSet<Tile>[][] flagMap = new ObjectSet[Team.all.length][BlockFlag.all.length];
|
||||
/**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<>();
|
||||
/**Empty set used for returning.*/
|
||||
private ObjectSet<Tile> emptySet = new ObjectSet<>();
|
||||
/**Array used for returning and reusing.*/
|
||||
private Array<Tile> returnArray = new ThreadArray<>();
|
||||
|
||||
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);
|
||||
getFlagged(index.team)[flag.ordinal()].remove(tile);
|
||||
}
|
||||
}
|
||||
process(tile);
|
||||
@@ -88,8 +62,12 @@ public class BlockIndexer{
|
||||
});
|
||||
|
||||
Events.on(WorldLoadEvent.class, () -> {
|
||||
enemyMap.clear();
|
||||
allyMap.clear();
|
||||
flagMap = new ObjectSet[Team.all.length][BlockFlag.all.length];
|
||||
for(int i = 0; i < flagMap.length; i++){
|
||||
for(int j = 0; j < BlockFlag.all.length; j++){
|
||||
flagMap[i][j] = new ObjectSet<>();
|
||||
}
|
||||
}
|
||||
typeMap.clear();
|
||||
ores = null;
|
||||
|
||||
@@ -115,18 +93,26 @@ public class BlockIndexer{
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all allied blocks with a flag.
|
||||
*/
|
||||
public ObjectSet<Tile> getAllied(Team team, BlockFlag type){
|
||||
return state.teams.has(team) ? (state.teams.get(team).ally ? allyMap : enemyMap).get(type, emptyArray) : emptyArray;
|
||||
private ObjectSet<Tile>[] getFlagged(Team team){
|
||||
return flagMap[team.ordinal()];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all enemy blocks with a flag.
|
||||
*/
|
||||
public ObjectSet<Tile> getEnemy(Team team, BlockFlag type){
|
||||
return (!state.teams.get(team).ally ? allyMap : enemyMap).get(type, emptyArray);
|
||||
/**Get all allied blocks with a flag.*/
|
||||
public ObjectSet<Tile> getAllied(Team team, BlockFlag type){
|
||||
return flagMap[team.ordinal()][type.ordinal()];
|
||||
}
|
||||
|
||||
/**Get all enemy blocks with a flag.*/
|
||||
public Array<Tile> getEnemy(Team team, BlockFlag type){
|
||||
returnArray.clear();
|
||||
for(Team enemy : state.teams.enemiesOf(team)){
|
||||
if(state.teams.isActive(enemy)){
|
||||
for(Tile tile : getFlagged(enemy)[type.ordinal()]){
|
||||
returnArray.add(tile);
|
||||
}
|
||||
}
|
||||
}
|
||||
return returnArray;
|
||||
}
|
||||
|
||||
public TileEntity findTile(Team team, float x, float y, float range, Predicate<Tile> pred){
|
||||
@@ -166,7 +152,7 @@ public class BlockIndexer{
|
||||
* Only specific ore types are scanned. See {@link #scanOres}.
|
||||
*/
|
||||
public ObjectSet<Tile> getOrePositions(Item item){
|
||||
return ores.get(item, emptyArray);
|
||||
return ores.get(item, emptySet);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -192,19 +178,15 @@ public class BlockIndexer{
|
||||
private void process(Tile tile){
|
||||
if(tile.block().flags != null &&
|
||||
tile.getTeam() != Team.none){
|
||||
ObjectMap<BlockFlag, ObjectSet<Tile>> map = getMap(tile.getTeam());
|
||||
ObjectSet<Tile>[] map = getFlagged(tile.getTeam());
|
||||
|
||||
for(BlockFlag flag : tile.block().flags){
|
||||
|
||||
ObjectSet<Tile> arr = map.get(flag);
|
||||
if(arr == null){
|
||||
arr = new ObjectSet<>();
|
||||
map.put(flag, arr);
|
||||
}
|
||||
ObjectSet<Tile> arr = map[flag.ordinal()];
|
||||
|
||||
arr.add(tile);
|
||||
|
||||
map.put(flag, arr);
|
||||
map[flag.ordinal()] = arr;
|
||||
}
|
||||
typeMap.put(tile.packedPosition(), new TileIndex(tile.block().flags, tile.getTeam()));
|
||||
}
|
||||
@@ -247,7 +229,8 @@ public class BlockIndexer{
|
||||
int quadrantY = tile.y / structQuadrantSize;
|
||||
int index = quadrantX + quadrantY * quadWidth();
|
||||
|
||||
for(TeamData data : state.teams.getTeams()){
|
||||
for(Team team : Team.all){
|
||||
TeamData data = state.teams.get(team);
|
||||
|
||||
//fast-set this quadrant to 'occupied' if the tile just placed is already of this team
|
||||
if(tile.getTeam() == data.team && tile.entity != null){
|
||||
@@ -284,11 +267,6 @@ public class BlockIndexer{
|
||||
return Mathf.ceil(world.height() / (float) structQuadrantSize);
|
||||
}
|
||||
|
||||
private ObjectMap<BlockFlag, ObjectSet<Tile>> getMap(Team team){
|
||||
if(!state.teams.has(team)) return emptyMap;
|
||||
return state.teams.get(team).ally ? allyMap : enemyMap;
|
||||
}
|
||||
|
||||
private void scanOres(){
|
||||
ores = new ObjectMap<>();
|
||||
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
package io.anuke.mindustry.ai;
|
||||
|
||||
import com.badlogic.gdx.math.GridPoint2;
|
||||
import com.badlogic.gdx.utils.Array;
|
||||
import com.badlogic.gdx.utils.IntArray;
|
||||
import com.badlogic.gdx.utils.ObjectSet;
|
||||
import com.badlogic.gdx.utils.ObjectSet.ObjectSetIterator;
|
||||
import com.badlogic.gdx.utils.Queue;
|
||||
import com.badlogic.gdx.utils.TimeUtils;
|
||||
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.game.Teams.TeamData;
|
||||
import io.anuke.mindustry.net.Net;
|
||||
import io.anuke.mindustry.world.Tile;
|
||||
import io.anuke.mindustry.world.meta.BlockFlag;
|
||||
@@ -30,8 +29,9 @@ public class Pathfinder{
|
||||
Events.on(TileChangeEvent.class, tile -> {
|
||||
if(Net.client()) return;
|
||||
|
||||
for(TeamData data : state.teams.getTeams()){
|
||||
if(data.team != tile.getTeam() && paths[data.team.ordinal()].weights[tile.x][tile.y] >= Float.MAX_VALUE){
|
||||
for(Team team : Team.all){
|
||||
TeamData data = state.teams.get(team);
|
||||
if(state.teams.isActive(team) && data.team != tile.getTeam() && paths[data.team.ordinal()].weights[tile.x][tile.y] >= Float.MAX_VALUE){
|
||||
update(tile, data.team);
|
||||
}
|
||||
}
|
||||
@@ -43,10 +43,10 @@ public class Pathfinder{
|
||||
public void update(){
|
||||
if(Net.client()) return;
|
||||
|
||||
ObjectSetIterator<TeamData> iterator = new ObjectSetIterator<>(state.teams.getTeams());
|
||||
|
||||
for(TeamData team : iterator){
|
||||
updateFrontier(team.team, maxUpdate);
|
||||
for(Team team : Team.all){
|
||||
if(state.teams.isActive(team)){
|
||||
updateFrontier(team, maxUpdate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,7 +107,7 @@ public class Pathfinder{
|
||||
|
||||
path.lastSearchTime = TimeUtils.millis();
|
||||
|
||||
ObjectSet<Tile> set = world.indexer().getEnemy(team, BlockFlag.target);
|
||||
Array<Tile> set = world.indexer().getEnemy(team, BlockFlag.target);
|
||||
for(Tile other : set){
|
||||
path.weights[other.x][other.y] = 0;
|
||||
path.searches[other.x][other.y] = path.search;
|
||||
@@ -173,11 +173,13 @@ public class Pathfinder{
|
||||
paths = new PathData[Team.all.length];
|
||||
blocked.clear();
|
||||
|
||||
for(TeamData data : state.teams.getTeams()){
|
||||
for(Team team : Team.all){
|
||||
PathData path = new PathData();
|
||||
paths[data.team.ordinal()] = path;
|
||||
paths[team.ordinal()] = path;
|
||||
|
||||
createFor(data.team);
|
||||
if(state.teams.isActive(team)){
|
||||
createFor(team);
|
||||
}
|
||||
}
|
||||
|
||||
state.spawner.checkAllQuadrants();
|
||||
|
||||
@@ -4,7 +4,7 @@ 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;
|
||||
import io.anuke.mindustry.game.TeamInfo;
|
||||
import io.anuke.mindustry.game.Teams;
|
||||
import io.anuke.mindustry.net.Net;
|
||||
import io.anuke.ucore.core.Events;
|
||||
|
||||
@@ -16,7 +16,7 @@ public class GameState{
|
||||
public Difficulty difficulty = Difficulty.normal;
|
||||
public boolean friendlyFire;
|
||||
public WaveSpawner spawner = new WaveSpawner();
|
||||
public TeamInfo teams = new TeamInfo();
|
||||
public Teams teams = new Teams();
|
||||
private State state = State.menu;
|
||||
|
||||
public void set(State astate){
|
||||
|
||||
@@ -9,8 +9,7 @@ import io.anuke.mindustry.game.EventType.PlayEvent;
|
||||
import io.anuke.mindustry.game.EventType.ResetEvent;
|
||||
import io.anuke.mindustry.game.EventType.WaveEvent;
|
||||
import io.anuke.mindustry.game.Team;
|
||||
import io.anuke.mindustry.game.TeamInfo;
|
||||
import io.anuke.mindustry.game.TeamInfo.TeamData;
|
||||
import io.anuke.mindustry.game.Teams;
|
||||
import io.anuke.mindustry.net.Net;
|
||||
import io.anuke.mindustry.type.Item;
|
||||
import io.anuke.mindustry.type.ItemStack;
|
||||
@@ -50,26 +49,25 @@ public class Logic extends Module{
|
||||
state.set(State.playing);
|
||||
state.wavetime = wavespace * state.difficulty.timeScaling * 2;
|
||||
|
||||
for(TeamData team : state.teams.getTeams(true)){
|
||||
for(Tile tile : team.cores){
|
||||
if(debug){
|
||||
for(Item item : Item.all()){
|
||||
if(item.type == ItemType.material){
|
||||
tile.entity.items.set(item, 1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(world.getSector() != null){
|
||||
Array<ItemStack> items = world.getSector().startingItems;
|
||||
for(ItemStack stack : items){
|
||||
tile.entity.items.add(stack.item, stack.amount);
|
||||
for(Tile tile : state.teams.get(defaultTeam).cores){
|
||||
if(debug){
|
||||
for(Item item : Item.all()){
|
||||
if(item.type == ItemType.material){
|
||||
tile.entity.items.set(item, 1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(world.getSector() != null){
|
||||
Array<ItemStack> items = world.getSector().startingItems;
|
||||
for(ItemStack stack : items){
|
||||
tile.entity.items.add(stack.item, stack.amount);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Events.fire(PlayEvent.class);
|
||||
}
|
||||
|
||||
@@ -77,9 +75,7 @@ public class Logic extends Module{
|
||||
state.wave = 1;
|
||||
state.wavetime = wavespace * state.difficulty.timeScaling;
|
||||
state.gameOver = false;
|
||||
state.teams = new TeamInfo();
|
||||
state.teams.add(Team.blue, true);
|
||||
state.teams.add(Team.red, false);
|
||||
state.teams = new Teams();
|
||||
|
||||
Timers.clear();
|
||||
Entities.clear();
|
||||
@@ -96,11 +92,12 @@ public class Logic extends Module{
|
||||
Events.fire(WaveEvent.class);
|
||||
}
|
||||
|
||||
//for gameOver to trigger, there must not be no cores remaining at all; obviously this never triggers in PvP
|
||||
private void checkGameOver(){
|
||||
boolean gameOver = true;
|
||||
|
||||
for(TeamData data : state.teams.getTeams(true)){
|
||||
if(data.cores.size > 0){
|
||||
for(Team team : Team.all){
|
||||
if(state.teams.get(team).cores.size > 0){
|
||||
gameOver = false;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ import io.anuke.mindustry.core.GameState.State;
|
||||
import io.anuke.mindustry.entities.Player;
|
||||
import io.anuke.mindustry.entities.traits.BuilderTrait.BuildRequest;
|
||||
import io.anuke.mindustry.entities.traits.SyncTrait;
|
||||
import io.anuke.mindustry.game.Team;
|
||||
import io.anuke.mindustry.game.Version;
|
||||
import io.anuke.mindustry.gen.Call;
|
||||
import io.anuke.mindustry.gen.RemoteReadServer;
|
||||
@@ -31,6 +32,7 @@ import io.anuke.ucore.io.delta.ByteMatcherHash;
|
||||
import io.anuke.ucore.io.delta.DEZEncoder;
|
||||
import io.anuke.ucore.modules.Module;
|
||||
import io.anuke.ucore.util.Log;
|
||||
import io.anuke.ucore.util.Mathf;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
@@ -166,6 +168,24 @@ public class NetServer extends Module{
|
||||
player.setNet(player.x, player.y);
|
||||
player.color.set(packet.color);
|
||||
player.color.a = 1f;
|
||||
|
||||
if(state.mode.isPvp){
|
||||
//find team with minimum amount of players and auto-assign player to that.
|
||||
Team min = Mathf.findMin(Team.all, team -> {
|
||||
if(state.teams.isActive(team)){
|
||||
int count = 0;
|
||||
for(Player other : playerGroup.all()){
|
||||
if(other.getTeam() == team){
|
||||
count ++;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
return Integer.MAX_VALUE;
|
||||
});
|
||||
player.setTeam(min);
|
||||
}
|
||||
|
||||
connections.put(id, player);
|
||||
|
||||
trace.playerid = player.id;
|
||||
|
||||
@@ -244,6 +244,10 @@ public class Player extends Unit implements BuilderTrait, CarryTrait, ShooterTra
|
||||
return playerGroup;
|
||||
}
|
||||
|
||||
public void setTeam(Team team){
|
||||
this.team = team;
|
||||
}
|
||||
|
||||
//endregion
|
||||
|
||||
//region draw methods
|
||||
|
||||
@@ -6,7 +6,7 @@ import com.badlogic.gdx.math.Vector2;
|
||||
import io.anuke.mindustry.content.blocks.Blocks;
|
||||
import io.anuke.mindustry.entities.traits.*;
|
||||
import io.anuke.mindustry.game.Team;
|
||||
import io.anuke.mindustry.game.TeamInfo.TeamData;
|
||||
import io.anuke.mindustry.game.Teams.TeamData;
|
||||
import io.anuke.mindustry.net.Interpolator;
|
||||
import io.anuke.mindustry.net.Net;
|
||||
import io.anuke.mindustry.type.StatusEffect;
|
||||
@@ -179,17 +179,13 @@ public abstract class Unit extends DestructibleEntity implements SaveTrait, Targ
|
||||
}
|
||||
|
||||
public TileEntity getClosestCore(){
|
||||
if(state.teams.has(team)){
|
||||
TeamData data = state.teams.get(team);
|
||||
TeamData data = state.teams.get(team);
|
||||
|
||||
Tile tile = Geometry.findClosest(x, y, data.cores);
|
||||
if(tile == null){
|
||||
return null;
|
||||
}else{
|
||||
return tile.entity;
|
||||
}
|
||||
}else{
|
||||
Tile tile = Geometry.findClosest(x, y, data.cores);
|
||||
if(tile == null){
|
||||
return null;
|
||||
}else{
|
||||
return tile.entity;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@ package io.anuke.mindustry.entities;
|
||||
|
||||
import com.badlogic.gdx.math.Rectangle;
|
||||
import com.badlogic.gdx.math.Vector2;
|
||||
import com.badlogic.gdx.utils.ObjectSet;
|
||||
import io.anuke.mindustry.entities.traits.TargetTrait;
|
||||
import io.anuke.mindustry.entities.units.BaseUnit;
|
||||
import io.anuke.mindustry.game.Team;
|
||||
@@ -12,6 +11,7 @@ import io.anuke.ucore.entities.EntityGroup;
|
||||
import io.anuke.ucore.entities.EntityPhysics;
|
||||
import io.anuke.ucore.function.Consumer;
|
||||
import io.anuke.ucore.function.Predicate;
|
||||
import io.anuke.ucore.util.EnumSet;
|
||||
|
||||
import static io.anuke.mindustry.Vars.*;
|
||||
|
||||
@@ -106,13 +106,7 @@ public class Units{
|
||||
* Returns the neareset ally tile in a range.
|
||||
*/
|
||||
public static TileEntity findAllyTile(Team team, float x, float y, float range, Predicate<Tile> pred){
|
||||
for(Team enemy : state.teams.alliesOf(team)){
|
||||
TileEntity entity = world.indexer().findTile(enemy, x, y, range, pred);
|
||||
if(entity != null){
|
||||
return entity;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
return world.indexer().findTile(team, x, y, range, pred);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -271,7 +265,7 @@ public class Units{
|
||||
* Iterates over all units that are enemies of this team.
|
||||
*/
|
||||
public static void getNearbyEnemies(Team team, Rectangle rect, Consumer<Unit> cons){
|
||||
ObjectSet<Team> targets = state.teams.enemiesOf(team);
|
||||
EnumSet<Team> targets = state.teams.enemiesOf(team);
|
||||
|
||||
for(Team other : targets){
|
||||
EntityGroup<BaseUnit> group = unitGroups[other.ordinal()];
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package io.anuke.mindustry.entities.units;
|
||||
|
||||
import com.badlogic.gdx.graphics.g2d.TextureRegion;
|
||||
import com.badlogic.gdx.utils.ObjectSet;
|
||||
import io.anuke.annotations.Annotations.Loc;
|
||||
import io.anuke.annotations.Annotations.Remote;
|
||||
import io.anuke.mindustry.Vars;
|
||||
@@ -15,7 +14,6 @@ import io.anuke.mindustry.entities.traits.ShooterTrait;
|
||||
import io.anuke.mindustry.entities.traits.SpawnerTrait;
|
||||
import io.anuke.mindustry.entities.traits.TargetTrait;
|
||||
import io.anuke.mindustry.game.Team;
|
||||
import io.anuke.mindustry.game.TeamInfo.TeamData;
|
||||
import io.anuke.mindustry.gen.Call;
|
||||
import io.anuke.mindustry.graphics.Palette;
|
||||
import io.anuke.mindustry.net.Net;
|
||||
@@ -188,16 +186,14 @@ public abstract class BaseUnit extends Unit implements ShooterTrait{
|
||||
}
|
||||
|
||||
public TileEntity getClosestEnemyCore(){
|
||||
if(Vars.state.teams.has(team)){
|
||||
ObjectSet<TeamData> datas = Vars.state.teams.enemyDataOf(team);
|
||||
|
||||
for(TeamData data : datas){
|
||||
Tile tile = Geometry.findClosest(x, y, data.cores);
|
||||
if(tile != null){
|
||||
return tile.entity;
|
||||
}
|
||||
for(Team enemy : Vars.state.teams.enemiesOf(team)){
|
||||
Tile tile = Geometry.findClosest(x, y, Vars.state.teams.get(enemy).cores);
|
||||
if(tile != null){
|
||||
return tile.entity;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -241,8 +241,18 @@ public abstract class GroundUnit extends BaseUnit{
|
||||
}
|
||||
|
||||
protected void moveAwayFromCore(){
|
||||
Team enemy = null;
|
||||
for(Team team : Vars.state.teams.enemiesOf(team)){
|
||||
if(Vars.state.teams.isActive(team)){
|
||||
enemy = team;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(enemy == null) return;
|
||||
|
||||
Tile tile = world.tileWorld(x, y);
|
||||
Tile targetTile = world.pathfinder().getTargetTile(Vars.state.teams.enemiesOf(team).first(), tile);
|
||||
Tile targetTile = world.pathfinder().getTargetTile(enemy, tile);
|
||||
TileEntity core = getClosestCore();
|
||||
|
||||
if(tile == targetTile || core == null || distanceTo(core) < 90f) return;
|
||||
|
||||
@@ -14,11 +14,15 @@ public enum GameMode{
|
||||
noWaves{{
|
||||
disableWaves = true;
|
||||
hidden = true;
|
||||
autoSpawn = true;
|
||||
}},
|
||||
pvp{{
|
||||
disableWaves = true;
|
||||
isPvp = true;
|
||||
hidden = true;
|
||||
}};
|
||||
public boolean infiniteResources;
|
||||
public boolean disableWaveTimer;
|
||||
public boolean disableWaves;
|
||||
public boolean hidden;
|
||||
|
||||
public boolean infiniteResources, disableWaveTimer, disableWaves, hidden, autoSpawn, isPvp;
|
||||
|
||||
public String description(){
|
||||
return Bundles.get("mode." + name() + ".description");
|
||||
|
||||
@@ -1,143 +0,0 @@
|
||||
package io.anuke.mindustry.game;
|
||||
|
||||
import com.badlogic.gdx.utils.Array;
|
||||
import com.badlogic.gdx.utils.ObjectMap;
|
||||
import com.badlogic.gdx.utils.ObjectSet;
|
||||
import io.anuke.mindustry.world.Tile;
|
||||
import io.anuke.ucore.util.ThreadArray;
|
||||
import io.anuke.ucore.util.ThreadSet;
|
||||
|
||||
/**
|
||||
* Class for various team-based utilities.
|
||||
*/
|
||||
public class TeamInfo{
|
||||
private ObjectMap<Team, TeamData> map = new ObjectMap<>();
|
||||
private ThreadSet<Team> allies = new ThreadSet<>(),
|
||||
enemies = new ThreadSet<>();
|
||||
private ThreadSet<TeamData> allyData = new ThreadSet<>(),
|
||||
enemyData = new ThreadSet<>();
|
||||
private ThreadSet<TeamData> allTeamData = new ThreadSet<>();
|
||||
private ThreadSet<Team> allTeams = new ThreadSet<>();
|
||||
private int allyBits = 0;
|
||||
private int enemyBits = 0;
|
||||
|
||||
/**
|
||||
* Returns all teams on a side.
|
||||
*/
|
||||
public ObjectSet<TeamData> getTeams(boolean ally){
|
||||
return ally ? allyData : enemyData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all team data.
|
||||
*/
|
||||
public ObjectSet<TeamData> getTeams(){
|
||||
return allTeamData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a team.
|
||||
*
|
||||
* @param team The team type enum.
|
||||
* @param ally Whether this team is an ally with the player or an enemy with the player.
|
||||
* In PvP situations with dedicated servers, the sides can be arbitrary.
|
||||
*/
|
||||
public void add(Team team, boolean ally){
|
||||
if(has(team)) throw new RuntimeException("Can't define team information twice!");
|
||||
|
||||
TeamData data = new TeamData(team, ally);
|
||||
|
||||
if(ally){
|
||||
allies.add(team);
|
||||
allyData.add(data);
|
||||
allyBits |= (1 << team.ordinal());
|
||||
}else{
|
||||
enemies.add(team);
|
||||
enemyData.add(data);
|
||||
enemyBits |= (1 << team.ordinal());
|
||||
}
|
||||
|
||||
allTeamData.add(data);
|
||||
allTeams.add(team);
|
||||
|
||||
map.put(team, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns team data by type. Call {@link #has(Team)} first to make sure it's active!
|
||||
*/
|
||||
public TeamData get(Team team){
|
||||
if(!has(team)) throw new RuntimeException("This team is not active! Check has() before calling get().");
|
||||
return map.get(team);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the specified team is active, e.g. whether it is participating in the game.
|
||||
*/
|
||||
public boolean has(Team team){
|
||||
return map.containsKey(team);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a set of all teams that are enemies of this team.
|
||||
* For teams not active, an empty set is returned.
|
||||
*/
|
||||
public ObjectSet<Team> enemiesOf(Team team){
|
||||
boolean ally = allies.contains(team);
|
||||
boolean enemy = enemies.contains(team);
|
||||
|
||||
//this team isn't even in the game, so target everything!
|
||||
if(!ally && !enemy) return allTeams;
|
||||
|
||||
return ally ? enemies : allies;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a set of all teams that are allies of this team.
|
||||
* For teams not active, an empty set is returned.
|
||||
*/
|
||||
public ObjectSet<Team> alliesOf(Team team){
|
||||
boolean ally = allies.contains(team);
|
||||
boolean enemy = enemies.contains(team);
|
||||
|
||||
//this team isn't even in the game, so target everything!
|
||||
if(!ally && !enemy) return allTeams;
|
||||
|
||||
return !ally ? enemies : allies;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a set of all teams that are enemies of this team.
|
||||
* For teams not active, an empty set is returned.
|
||||
*/
|
||||
public ObjectSet<TeamData> enemyDataOf(Team team){
|
||||
boolean ally = allies.contains(team);
|
||||
boolean enemy = enemies.contains(team);
|
||||
|
||||
//this team isn't even in the game, so target everything!
|
||||
if(!ally && !enemy) return allTeamData;
|
||||
|
||||
return ally ? enemyData : allyData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not these two teams are enemies.
|
||||
*/
|
||||
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 enemy = (enemyBits & (1 << other.ordinal())) != 0;
|
||||
return (ally == enemy) || !ally; //if it's not in the game, target everything.
|
||||
}
|
||||
|
||||
public class TeamData{
|
||||
public final Array<Tile> cores = new ThreadArray<>();
|
||||
public final Team team;
|
||||
public final boolean ally;
|
||||
|
||||
public TeamData(Team team, boolean ally){
|
||||
this.team = team;
|
||||
this.ally = ally;
|
||||
}
|
||||
}
|
||||
}
|
||||
66
core/src/io/anuke/mindustry/game/Teams.java
Normal file
66
core/src/io/anuke/mindustry/game/Teams.java
Normal file
@@ -0,0 +1,66 @@
|
||||
package io.anuke.mindustry.game;
|
||||
|
||||
import com.badlogic.gdx.utils.Array;
|
||||
import io.anuke.mindustry.Vars;
|
||||
import io.anuke.mindustry.world.Tile;
|
||||
import io.anuke.ucore.util.EnumSet;
|
||||
import io.anuke.ucore.util.ThreadArray;
|
||||
|
||||
/**
|
||||
* Class for various team-based utilities.
|
||||
*/
|
||||
public class Teams{
|
||||
private TeamData[] map = new TeamData[Team.all.length];
|
||||
|
||||
/**
|
||||
* Register a team.
|
||||
*
|
||||
* @param team The team type enum.
|
||||
* @param enemies The array of enemies of this team. Any team not in this array is considered neutral.
|
||||
*/
|
||||
public void add(Team team, Team... enemies){
|
||||
if(map[team.ordinal()] != null) throw new RuntimeException("Can't define team information twice!");
|
||||
|
||||
map[team.ordinal()] = new TeamData(team, EnumSet.of(enemies));
|
||||
}
|
||||
|
||||
/**Returns team data by type.*/
|
||||
public TeamData get(Team team){
|
||||
if(map[team.ordinal()] == null){
|
||||
//By default, a non-defined team will be enemies of everything.
|
||||
Team[] others = new Team[Team.all.length-1];
|
||||
for(int i = 0, j = 0; i < Team.all.length; i++){
|
||||
if(Team.all[i] != team) others[j++] = Team.all[i];
|
||||
}
|
||||
add(team, others);
|
||||
}
|
||||
return map[team.ordinal()];
|
||||
}
|
||||
|
||||
/**Returns whether a team is active, e.g. whether it has any cores remaining.*/
|
||||
public boolean isActive(Team team){
|
||||
//the enemy wave team is always active
|
||||
return (!Vars.state.mode.disableWaves && team == Vars.waveTeam) || get(team).cores.size > 0;
|
||||
}
|
||||
|
||||
/**Returns a set of all teams that are enemies of this team.*/
|
||||
public EnumSet<Team> enemiesOf(Team team){
|
||||
return get(team).enemies;
|
||||
}
|
||||
|
||||
/**Returns whether {@param other} is an enemy of {@param #team}.*/
|
||||
public boolean areEnemies(Team team, Team other){
|
||||
return enemiesOf(team).contains(other);
|
||||
}
|
||||
|
||||
public class TeamData{
|
||||
public final Array<Tile> cores = new ThreadArray<>();
|
||||
public final EnumSet<Team> enemies;
|
||||
public final Team team;
|
||||
|
||||
public TeamData(Team team, EnumSet<Team> enemies){
|
||||
this.team = team;
|
||||
this.enemies = enemies;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,7 @@ import com.badlogic.gdx.utils.Array;
|
||||
import io.anuke.mindustry.content.blocks.Blocks;
|
||||
import io.anuke.mindustry.entities.Player;
|
||||
import io.anuke.mindustry.entities.TileEntity;
|
||||
import io.anuke.mindustry.game.TeamInfo.TeamData;
|
||||
import io.anuke.mindustry.game.Team;
|
||||
import io.anuke.mindustry.input.InputHandler;
|
||||
import io.anuke.mindustry.world.Block;
|
||||
import io.anuke.mindustry.world.Tile;
|
||||
@@ -63,13 +63,13 @@ public class OverlayRenderer{
|
||||
Lines.stroke(buildFadeTime*2f);
|
||||
|
||||
if(buildFadeTime > 0.005f){
|
||||
for(TeamData data : state.teams.enemyDataOf(player.getTeam())){
|
||||
for(Tile core : data.cores){
|
||||
for(Team enemy : state.teams.enemiesOf(player.getTeam())){
|
||||
for(Tile core : state.teams.get(enemy).cores){
|
||||
float dst = Vector2.dst(player.x, player.y, core.drawx(), core.drawy());
|
||||
if(dst < enemyCoreBuildRange * 1.5f){
|
||||
Draw.color(Color.DARK_GRAY);
|
||||
Lines.poly(core.drawx(), core.drawy() - 2, 200, enemyCoreBuildRange);
|
||||
Draw.color(Palette.accent, data.team.color, 0.5f + Mathf.absin(Timers.time(), 10f, 0.5f));
|
||||
Draw.color(Palette.accent, enemy.color, 0.5f + Mathf.absin(Timers.time(), 10f, 0.5f));
|
||||
Lines.poly(core.drawx(), core.drawy(), 200, enemyCoreBuildRange);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,8 +117,7 @@ public class Save16 extends SaveFileVersion{
|
||||
|
||||
tile.entity.read(stream);
|
||||
|
||||
if(tile.block() == StorageBlocks.core &&
|
||||
state.teams.has(t)){
|
||||
if(tile.block() == StorageBlocks.core){
|
||||
state.teams.get(t).cores.add(tile);
|
||||
}
|
||||
}else if(wallid == 0){
|
||||
|
||||
@@ -84,8 +84,7 @@ public class WorldGenerator{
|
||||
|
||||
Team team = tile.getTeam();
|
||||
|
||||
if(tile.block() == StorageBlocks.core &&
|
||||
state.teams.has(team)){
|
||||
if(tile.block() == StorageBlocks.core){
|
||||
state.teams.get(team).cores.add(tile);
|
||||
}
|
||||
|
||||
|
||||
@@ -39,8 +39,12 @@ public class BattleMission implements Mission{
|
||||
|
||||
@Override
|
||||
public boolean isComplete(){
|
||||
//TODO check all enemy teams, not just the first
|
||||
return Vars.state.teams.getTeams(false).first().cores.size == 0;
|
||||
for(Team team : Vars.state.teams.enemiesOf(Vars.defaultTeam)){
|
||||
if(Vars.state.teams.isActive(team)){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -27,7 +27,7 @@ public class ResourceMission implements Mission{
|
||||
|
||||
@Override
|
||||
public boolean isComplete(){
|
||||
return Vars.state.teams.getTeams(true).first().cores.first().entity.items.has(item, amount);
|
||||
return Vars.state.teams.get(Vars.defaultTeam).cores.first().entity.items.has(item, amount);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -7,11 +7,11 @@ import io.anuke.mindustry.content.blocks.Blocks;
|
||||
import io.anuke.mindustry.entities.Player;
|
||||
import io.anuke.mindustry.game.GameMode;
|
||||
import io.anuke.mindustry.game.Team;
|
||||
import io.anuke.mindustry.game.TeamInfo;
|
||||
import io.anuke.mindustry.game.TeamInfo.TeamData;
|
||||
import io.anuke.mindustry.game.Teams;
|
||||
import io.anuke.mindustry.game.Teams.TeamData;
|
||||
import io.anuke.mindustry.game.Version;
|
||||
import io.anuke.mindustry.maps.Map;
|
||||
import io.anuke.mindustry.maps.MapMeta;
|
||||
import io.anuke.mindustry.game.Version;
|
||||
import io.anuke.mindustry.world.Tile;
|
||||
import io.anuke.mindustry.world.blocks.BlockPart;
|
||||
import io.anuke.ucore.core.Core;
|
||||
@@ -119,11 +119,16 @@ public class NetworkIO{
|
||||
}
|
||||
|
||||
//write team data
|
||||
stream.writeByte(state.teams.getTeams().size);
|
||||
for(TeamData data : state.teams.getTeams()){
|
||||
for(Team team : Team.all){
|
||||
TeamData data = state.teams.get(team);
|
||||
stream.writeByte(data.team.ordinal());
|
||||
stream.writeBoolean(data.ally);
|
||||
stream.writeShort(data.cores.size);
|
||||
|
||||
stream.writeByte(data.enemies.size());
|
||||
for(Team enemy : data.enemies){
|
||||
stream.writeByte(enemy.ordinal());
|
||||
}
|
||||
|
||||
stream.writeByte(data.cores.size);
|
||||
for(Tile tile : data.cores){
|
||||
stream.writeInt(tile.packedPosition());
|
||||
}
|
||||
@@ -253,14 +258,21 @@ public class NetworkIO{
|
||||
}
|
||||
|
||||
player.reset();
|
||||
state.teams = new TeamInfo();
|
||||
state.teams = new Teams();
|
||||
|
||||
byte teams = stream.readByte();
|
||||
for(int i = 0; i < teams; i++){
|
||||
Team team = Team.all[stream.readByte()];
|
||||
boolean ally = stream.readBoolean();
|
||||
short cores = stream.readShort();
|
||||
state.teams.add(team, ally);
|
||||
|
||||
byte enemies = stream.readByte();
|
||||
Team[] enemyArr = new Team[enemies];
|
||||
for(int j = 0; j < enemies; j++){
|
||||
enemyArr[j] = Team.all[stream.readByte()];
|
||||
}
|
||||
|
||||
state.teams.add(team, enemyArr);
|
||||
|
||||
byte cores = stream.readByte();
|
||||
|
||||
for(int j = 0; j < cores; j++){
|
||||
state.teams.get(team).cores.add(world.tile(stream.readInt()));
|
||||
|
||||
@@ -6,7 +6,6 @@ import io.anuke.mindustry.content.blocks.Blocks;
|
||||
import io.anuke.mindustry.entities.Units;
|
||||
import io.anuke.mindustry.game.EventType.BlockBuildEvent;
|
||||
import io.anuke.mindustry.game.Team;
|
||||
import io.anuke.mindustry.game.TeamInfo.TeamData;
|
||||
import io.anuke.mindustry.type.Recipe;
|
||||
import io.anuke.mindustry.world.blocks.BuildBlock.BuildEntity;
|
||||
import io.anuke.ucore.core.Events;
|
||||
@@ -138,8 +137,8 @@ public class Build{
|
||||
}
|
||||
|
||||
//check for enemy cores
|
||||
for(TeamData data : state.teams.enemyDataOf(team)){
|
||||
for(Tile core : data.cores){
|
||||
for(Team enemy : state.teams.enemiesOf(team)){
|
||||
for(Tile core : state.teams.get(enemy).cores){
|
||||
if(Vector2.dst(x*tilesize + type.offset(), y*tilesize + type.offset(), core.drawx(), core.drawy()) < enemyCoreBuildRange + type.size*tilesize/2f){
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -176,9 +176,7 @@ public class CoreBlock extends StorageBlock{
|
||||
//TODO more dramatic effects
|
||||
super.onDestroyed(tile);
|
||||
|
||||
if(state.teams.has(tile.getTeam())){
|
||||
state.teams.get(tile.getTeam()).cores.removeValue(tile, true);
|
||||
}
|
||||
state.teams.get(tile.getTeam()).cores.removeValue(tile, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -8,7 +8,6 @@ import io.anuke.mindustry.content.fx.BlockFx;
|
||||
import io.anuke.mindustry.entities.TileEntity;
|
||||
import io.anuke.mindustry.entities.units.BaseUnit;
|
||||
import io.anuke.mindustry.entities.units.UnitType;
|
||||
import io.anuke.mindustry.game.Team;
|
||||
import io.anuke.mindustry.gen.Call;
|
||||
import io.anuke.mindustry.graphics.Palette;
|
||||
import io.anuke.mindustry.graphics.Shaders;
|
||||
@@ -36,6 +35,9 @@ import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
import static io.anuke.mindustry.Vars.state;
|
||||
import static io.anuke.mindustry.Vars.waveTeam;
|
||||
|
||||
public class UnitPad extends Block{
|
||||
protected float gracePeriodMultiplier = 23f;
|
||||
protected float speedupTime = 60f * 60f * 20;
|
||||
@@ -142,7 +144,7 @@ public class UnitPad extends Block{
|
||||
|
||||
entity.time += Timers.delta() * entity.speedScl;
|
||||
|
||||
boolean isEnemy = tile.getTeam() == Team.red;
|
||||
boolean isEnemy = tile.getTeam() == waveTeam && state.mode.autoSpawn;
|
||||
|
||||
if(isEnemy){
|
||||
entity.warmup += Timers.delta();
|
||||
|
||||
@@ -18,6 +18,8 @@ public enum BlockFlag{
|
||||
/**Special flag for command center blocks.*/
|
||||
comandCenter(Float.MAX_VALUE);
|
||||
|
||||
public final static BlockFlag[] all = values();
|
||||
|
||||
public final float cost;
|
||||
|
||||
BlockFlag(float cost){
|
||||
|
||||
Reference in New Issue
Block a user