Merge branch '6.0' into crater
# Conflicts: # core/assets/icons/icons.properties # core/assets/sprites/block_colors.png # core/assets/sprites/sprites.atlas # core/assets/sprites/sprites.png # core/assets/sprites/sprites3.png # core/assets/sprites/sprites5.png # core/src/mindustry/content/Blocks.java # core/src/mindustry/ui/fragments/PlayerListFragment.java # core/src/mindustry/world/BlockStorage.java
This commit is contained in:
@@ -92,10 +92,7 @@ public abstract class ClientLauncher extends ApplicationCore implements Platform
|
||||
assets.load(mods);
|
||||
assets.load(schematics);
|
||||
|
||||
assets.loadRun("contentinit", ContentLoader.class, () -> {
|
||||
content.init();
|
||||
content.load();
|
||||
});
|
||||
assets.loadRun("contentinit", ContentLoader.class, () -> content.init(), () -> content.load());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -13,18 +13,15 @@ import arc.util.io.*;
|
||||
import mindustry.ai.*;
|
||||
import mindustry.core.*;
|
||||
import mindustry.entities.*;
|
||||
import mindustry.entities.effect.*;
|
||||
import mindustry.entities.traits.*;
|
||||
import mindustry.entities.type.*;
|
||||
import mindustry.game.*;
|
||||
import mindustry.game.EventType.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.input.*;
|
||||
import mindustry.maps.*;
|
||||
import mindustry.maps.Map;
|
||||
import mindustry.mod.*;
|
||||
import mindustry.net.Net;
|
||||
import mindustry.net.*;
|
||||
import mindustry.world.blocks.defense.ForceProjector.*;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.charset.*;
|
||||
@@ -77,6 +74,10 @@ public class Vars implements Loadable{
|
||||
public static final float worldBounds = 100f;
|
||||
/** units outside of this bound will simply die instantly */
|
||||
public static final float finalWorldBounds = worldBounds + 500;
|
||||
/** mining range for manual miners */
|
||||
public static final float miningRange = 70f;
|
||||
/** range for building */
|
||||
public static final float buildingRange = 220f;
|
||||
/** ticks spent out of bound until self destruct. */
|
||||
public static final float boundsCountdown = 60 * 7;
|
||||
/** for map generator dialog */
|
||||
@@ -142,6 +143,8 @@ public class Vars implements Loadable{
|
||||
public static Fi schematicDirectory;
|
||||
/** data subdirectory used for bleeding edge build versions */
|
||||
public static Fi bebuildDirectory;
|
||||
/** empty map, indicates no current map */
|
||||
public static Map emptyMap;
|
||||
/** map file extension */
|
||||
public static final String mapExtension = "msav";
|
||||
/** save file extension */
|
||||
@@ -152,7 +155,7 @@ public class Vars implements Loadable{
|
||||
/** list of all locales that can be switched to */
|
||||
public static Locale[] locales;
|
||||
|
||||
public static FileTree tree;
|
||||
public static FileTree tree = new FileTree();
|
||||
public static Net net;
|
||||
public static ContentLoader content;
|
||||
public static GameState state;
|
||||
@@ -165,6 +168,7 @@ public class Vars implements Loadable{
|
||||
public static Schematics schematics = new Schematics();
|
||||
public static BeControl becontrol;
|
||||
|
||||
public static Universe universe;
|
||||
public static World world;
|
||||
public static Maps maps;
|
||||
public static WaveSpawner spawner;
|
||||
@@ -178,18 +182,7 @@ public class Vars implements Loadable{
|
||||
public static NetServer netServer;
|
||||
public static NetClient netClient;
|
||||
|
||||
public static Entities entities;
|
||||
public static EntityGroup<Player> playerGroup;
|
||||
public static EntityGroup<TileEntity> tileGroup;
|
||||
public static EntityGroup<Bullet> bulletGroup;
|
||||
public static EntityGroup<EffectEntity> effectGroup;
|
||||
public static EntityGroup<DrawTrait> groundEffectGroup;
|
||||
public static EntityGroup<ShieldEntity> shieldGroup;
|
||||
public static EntityGroup<Puddle> puddleGroup;
|
||||
public static EntityGroup<Fire> fireGroup;
|
||||
public static EntityGroup<BaseUnit> unitGroup;
|
||||
|
||||
public static Player player;
|
||||
public static Playerc player;
|
||||
|
||||
@Override
|
||||
public void loadAsync(){
|
||||
@@ -199,6 +192,7 @@ public class Vars implements Loadable{
|
||||
|
||||
public static void init(){
|
||||
Serialization.init();
|
||||
Groups.init();
|
||||
DefaultSerializers.typeMappings.put("mindustry.type.ContentType", "mindustry.ctype.ContentType");
|
||||
|
||||
if(loadLocales){
|
||||
@@ -219,47 +213,6 @@ public class Vars implements Loadable{
|
||||
|
||||
Version.init();
|
||||
|
||||
if(tree == null) tree = new FileTree();
|
||||
if(mods == null) mods = new Mods();
|
||||
|
||||
content = new ContentLoader();
|
||||
loops = new LoopControl();
|
||||
defaultWaves = new DefaultWaves();
|
||||
collisions = new EntityCollisions();
|
||||
world = new World();
|
||||
becontrol = new BeControl();
|
||||
|
||||
maps = new Maps();
|
||||
spawner = new WaveSpawner();
|
||||
indexer = new BlockIndexer();
|
||||
pathfinder = new Pathfinder();
|
||||
|
||||
entities = new Entities();
|
||||
playerGroup = entities.add(Player.class).enableMapping();
|
||||
tileGroup = entities.add(TileEntity.class, false);
|
||||
bulletGroup = entities.add(Bullet.class).enableMapping();
|
||||
effectGroup = entities.add(EffectEntity.class, false);
|
||||
groundEffectGroup = entities.add(DrawTrait.class, false);
|
||||
puddleGroup = entities.add(Puddle.class).enableMapping();
|
||||
shieldGroup = entities.add(ShieldEntity.class, false);
|
||||
fireGroup = entities.add(Fire.class).enableMapping();
|
||||
unitGroup = entities.add(BaseUnit.class).enableMapping();
|
||||
|
||||
for(EntityGroup<?> group : entities.all()){
|
||||
group.setRemoveListener(entity -> {
|
||||
if(entity instanceof SyncTrait && net.client()){
|
||||
netClient.addRemovedEntity((entity).getID());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
state = new GameState();
|
||||
data = new GlobalData();
|
||||
|
||||
mobile = Core.app.getType() == ApplicationType.Android || Core.app.getType() == ApplicationType.iOS || testMobile;
|
||||
ios = Core.app.getType() == ApplicationType.iOS;
|
||||
android = Core.app.getType() == ApplicationType.Android;
|
||||
|
||||
dataDirectory = Core.settings.getDataDirectory();
|
||||
screenshotDirectory = dataDirectory.child("screenshots/");
|
||||
customMapDirectory = dataDirectory.child("maps/");
|
||||
@@ -269,6 +222,30 @@ public class Vars implements Loadable{
|
||||
modDirectory = dataDirectory.child("mods/");
|
||||
schematicDirectory = dataDirectory.child("schematics/");
|
||||
bebuildDirectory = dataDirectory.child("be_builds/");
|
||||
emptyMap = new Map(new StringMap());
|
||||
|
||||
if(tree == null) tree = new FileTree();
|
||||
if(mods == null) mods = new Mods();
|
||||
|
||||
content = new ContentLoader();
|
||||
loops = new LoopControl();
|
||||
defaultWaves = new DefaultWaves();
|
||||
collisions = new EntityCollisions();
|
||||
world = new World();
|
||||
universe = new Universe();
|
||||
becontrol = new BeControl();
|
||||
|
||||
maps = new Maps();
|
||||
spawner = new WaveSpawner();
|
||||
indexer = new BlockIndexer();
|
||||
pathfinder = new Pathfinder();
|
||||
|
||||
state = new GameState();
|
||||
data = new GlobalData();
|
||||
|
||||
mobile = Core.app.getType() == ApplicationType.Android || Core.app.getType() == ApplicationType.iOS || testMobile;
|
||||
ios = Core.app.getType() == ApplicationType.iOS;
|
||||
android = Core.app.getType() == ApplicationType.Android;
|
||||
|
||||
modDirectory.mkdirs();
|
||||
|
||||
|
||||
@@ -4,17 +4,20 @@ import arc.*;
|
||||
import arc.func.*;
|
||||
import arc.math.*;
|
||||
import arc.math.geom.*;
|
||||
import arc.struct.EnumSet;
|
||||
import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.entities.type.*;
|
||||
import mindustry.game.EventType.*;
|
||||
import mindustry.game.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.blocks.*;
|
||||
import mindustry.world.meta.*;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
/** Class used for indexing special target blocks for AI. */
|
||||
@@ -25,24 +28,24 @@ public class BlockIndexer{
|
||||
|
||||
/** Set of all ores that are being scanned. */
|
||||
private final ObjectSet<Item> scanOres = new ObjectSet<>();
|
||||
private final IntSet intSet = new IntSet();
|
||||
private final ObjectSet<Item> itemSet = new ObjectSet<>();
|
||||
/** Stores all ore quadtrants on the map. */
|
||||
private ObjectMap<Item, ObjectSet<Tile>> ores = new ObjectMap<>();
|
||||
private ObjectMap<Item, TileArray> ores = new ObjectMap<>();
|
||||
/** Maps each team ID to a quarant. A quadrant is a grid of bits, where each bit is set if and only if there is a block of that team in that quadrant. */
|
||||
private GridBits[] structQuadrants;
|
||||
/** Stores all damaged tile entities by team. */
|
||||
private ObjectSet<Tile>[] damagedTiles = new ObjectSet[Team.all().length];
|
||||
private TileArray[] damagedTiles = new TileArray[Team.all().length];
|
||||
/**All ores available on this map.*/
|
||||
private ObjectSet<Item> allOres = new ObjectSet<>();
|
||||
/**Stores teams that are present here as tiles.*/
|
||||
private ObjectSet<Team> activeTeams = new ObjectSet<>();
|
||||
|
||||
private Array<Team> activeTeams = new Array<>();
|
||||
/** Maps teams to a map of flagged tiles by type. */
|
||||
private ObjectSet<Tile>[][] flagMap = new ObjectSet[Team.all().length][BlockFlag.all.length];
|
||||
private TileArray[][] flagMap = new TileArray[Team.all().length][BlockFlag.all.length];
|
||||
/** Maps tile positions to their last known tile index data. */
|
||||
private IntMap<TileIndex> typeMap = new IntMap<>();
|
||||
/** Empty set used for returning. */
|
||||
private ObjectSet<Tile> emptySet = new ObjectSet<>();
|
||||
private TileArray emptySet = new TileArray();
|
||||
/** Array used for returning and reusing. */
|
||||
private Array<Tile> returnArray = new Array<>();
|
||||
|
||||
@@ -61,12 +64,12 @@ public class BlockIndexer{
|
||||
Events.on(WorldLoadEvent.class, event -> {
|
||||
scanOres.clear();
|
||||
scanOres.addAll(Item.getAllOres());
|
||||
damagedTiles = new ObjectSet[Team.all().length];
|
||||
flagMap = new ObjectSet[Team.all().length][BlockFlag.all.length];
|
||||
damagedTiles = new TileArray[Team.all().length];
|
||||
flagMap = new TileArray[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<>();
|
||||
flagMap[i][j] = new TileArray();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,18 +80,14 @@ public class BlockIndexer{
|
||||
//create bitset for each team type that contains each quadrant
|
||||
structQuadrants = new GridBits[Team.all().length];
|
||||
|
||||
for(int x = 0; x < world.width(); x++){
|
||||
for(int y = 0; y < world.height(); y++){
|
||||
Tile tile = world.tile(x, y);
|
||||
for(Tile tile : world.tiles){
|
||||
process(tile);
|
||||
|
||||
process(tile);
|
||||
|
||||
if(tile.entity != null && tile.entity.damaged()){
|
||||
notifyTileDamaged(tile.entity);
|
||||
}
|
||||
|
||||
if(tile.drop() != null) allOres.add(tile.drop());
|
||||
if(tile.entity != null && tile.entity.damaged()){
|
||||
notifyTileDamaged(tile.entity);
|
||||
}
|
||||
|
||||
if(tile.drop() != null) allOres.add(tile.drop());
|
||||
}
|
||||
|
||||
for(int x = 0; x < quadWidth(); x++){
|
||||
@@ -101,7 +100,7 @@ public class BlockIndexer{
|
||||
});
|
||||
}
|
||||
|
||||
private ObjectSet<Tile>[] getFlagged(Team team){
|
||||
private TileArray[] getFlagged(Team team){
|
||||
return flagMap[team.id];
|
||||
}
|
||||
|
||||
@@ -118,14 +117,11 @@ public class BlockIndexer{
|
||||
if(structQuadrants == null) return;
|
||||
|
||||
//go through every tile... ouch
|
||||
for(int x = 0; x < world.width(); x++){
|
||||
for(int y = 0; y < world.height(); y++){
|
||||
Tile tile = world.tile(x, y);
|
||||
if(tile.getTeam() == team){
|
||||
int quadrantX = tile.x / quadrantSize;
|
||||
int quadrantY = tile.y / quadrantSize;
|
||||
structQuadrant(team).set(quadrantX, quadrantY);
|
||||
}
|
||||
for(Tile tile : world.tiles){
|
||||
if(tile.team() == team){
|
||||
int quadrantX = tile.x / quadrantSize;
|
||||
int quadrantY = tile.y / quadrantSize;
|
||||
structQuadrant(team).set(quadrantX, quadrantY);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -136,16 +132,16 @@ public class BlockIndexer{
|
||||
}
|
||||
|
||||
/** Returns all damaged tiles by team. */
|
||||
public ObjectSet<Tile> getDamaged(Team team){
|
||||
public TileArray getDamaged(Team team){
|
||||
returnArray.clear();
|
||||
|
||||
if(damagedTiles[team.id] == null){
|
||||
damagedTiles[team.id] = new ObjectSet<>();
|
||||
damagedTiles[team.id] = new TileArray();
|
||||
}
|
||||
|
||||
ObjectSet<Tile> set = damagedTiles[team.id];
|
||||
TileArray set = damagedTiles[team.id];
|
||||
for(Tile tile : set){
|
||||
if((tile.entity == null || tile.entity.getTeam() != team || !tile.entity.damaged()) || tile.block() instanceof BuildBlock){
|
||||
if((tile.entity == null || tile.entity.team() != team || !tile.entity.damaged()) || tile.block() instanceof BuildBlock){
|
||||
returnArray.add(tile);
|
||||
}
|
||||
}
|
||||
@@ -158,16 +154,49 @@ public class BlockIndexer{
|
||||
}
|
||||
|
||||
/** Get all allied blocks with a flag. */
|
||||
public ObjectSet<Tile> getAllied(Team team, BlockFlag type){
|
||||
public TileArray getAllied(Team team, BlockFlag type){
|
||||
return flagMap[team.id][type.ordinal()];
|
||||
}
|
||||
|
||||
public boolean eachBlock(Teamc team, float range, Boolf<Tilec> pred, Cons<Tilec> cons){
|
||||
return eachBlock(team.team(), team.getX(), team.getY(), range, pred, cons);
|
||||
}
|
||||
|
||||
public boolean eachBlock(Team team, float wx, float wy, float range, Boolf<Tilec> pred, Cons<Tilec> cons){
|
||||
intSet.clear();
|
||||
|
||||
int tx = world.toTile(wx);
|
||||
int ty = world.toTile(wy);
|
||||
|
||||
int tileRange = (int)(range / tilesize + 1);
|
||||
intSet.clear();
|
||||
boolean any = false;
|
||||
|
||||
for(int x = -tileRange + tx; x <= tileRange + tx; x++){
|
||||
for(int y = -tileRange + ty; y <= tileRange + ty; y++){
|
||||
if(!Mathf.within(x * tilesize, y * tilesize, wx, wy, range)) continue;
|
||||
|
||||
Tilec other = world.ent(x, y);
|
||||
|
||||
if(other == null) continue;
|
||||
|
||||
if(other.team() == team && !intSet.contains(other.pos()) && pred.get(other)){
|
||||
cons.get(other);
|
||||
any = true;
|
||||
intSet.add(other.pos());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return any;
|
||||
}
|
||||
|
||||
/** Get all enemy blocks with a flag. */
|
||||
public Array<Tile> getEnemy(Team team, BlockFlag type){
|
||||
returnArray.clear();
|
||||
for(Team enemy : team.enemies()){
|
||||
if(state.teams.isActive(enemy)){
|
||||
ObjectSet<Tile> set = getFlagged(enemy)[type.ordinal()];
|
||||
TileArray set = getFlagged(enemy)[type.ordinal()];
|
||||
if(set != null){
|
||||
for(Tile tile : set){
|
||||
returnArray.add(tile);
|
||||
@@ -178,20 +207,20 @@ public class BlockIndexer{
|
||||
return returnArray;
|
||||
}
|
||||
|
||||
public void notifyTileDamaged(TileEntity entity){
|
||||
if(damagedTiles[(int)entity.getTeam().id] == null){
|
||||
damagedTiles[(int)entity.getTeam().id] = new ObjectSet<>();
|
||||
public void notifyTileDamaged(Tilec entity){
|
||||
if(damagedTiles[(int)entity.team().id] == null){
|
||||
damagedTiles[(int)entity.team().id] = new TileArray();
|
||||
}
|
||||
|
||||
ObjectSet<Tile> set = damagedTiles[(int)entity.getTeam().id];
|
||||
set.add(entity.tile);
|
||||
TileArray set = damagedTiles[(int)entity.team().id];
|
||||
set.add(entity.tile());
|
||||
}
|
||||
|
||||
public TileEntity findEnemyTile(Team team, float x, float y, float range, Boolf<Tile> pred){
|
||||
public Tilec findEnemyTile(Team team, float x, float y, float range, Boolf<Tilec> pred){
|
||||
for(Team enemy : activeTeams){
|
||||
if(!team.isEnemy(enemy)) continue;
|
||||
|
||||
TileEntity entity = indexer.findTile(enemy, x, y, range, pred, true);
|
||||
Tilec entity = indexer.findTile(enemy, x, y, range, pred, true);
|
||||
if(entity != null){
|
||||
return entity;
|
||||
}
|
||||
@@ -200,12 +229,12 @@ public class BlockIndexer{
|
||||
return null;
|
||||
}
|
||||
|
||||
public TileEntity findTile(Team team, float x, float y, float range, Boolf<Tile> pred){
|
||||
public Tilec findTile(Team team, float x, float y, float range, Boolf<Tilec> pred){
|
||||
return findTile(team, x, y, range, pred, false);
|
||||
}
|
||||
|
||||
public TileEntity findTile(Team team, float x, float y, float range, Boolf<Tile> pred, boolean usePriority){
|
||||
TileEntity closest = null;
|
||||
public Tilec findTile(Team team, float x, float y, float range, Boolf<Tilec> pred, boolean usePriority){
|
||||
Tilec closest = null;
|
||||
float dst = 0;
|
||||
float range2 = range*range;
|
||||
|
||||
@@ -216,21 +245,19 @@ public class BlockIndexer{
|
||||
|
||||
for(int tx = rx * quadrantSize; tx < (rx + 1) * quadrantSize && tx < world.width(); tx++){
|
||||
for(int ty = ry * quadrantSize; ty < (ry + 1) * quadrantSize && ty < world.height(); ty++){
|
||||
Tile other = world.ltile(tx, ty);
|
||||
Tilec e = world.ent(tx, ty);
|
||||
|
||||
if(other == null) continue;
|
||||
if(e == null) continue;
|
||||
|
||||
if(other.entity == null || other.getTeam() != team || !pred.get(other) || !other.block().targetable)
|
||||
if(e.team() != team || !pred.get(e) || !e.block().targetable)
|
||||
continue;
|
||||
|
||||
TileEntity e = other.entity;
|
||||
|
||||
float ndst = Mathf.dst2(x, y, e.x, e.y);
|
||||
float ndst = e.dst2(x, y);
|
||||
if(ndst < range2 && (closest == null ||
|
||||
//this one is closer, and it is at least of equal priority
|
||||
(ndst < dst && (!usePriority || closest.block.priority.ordinal() <= e.block.priority.ordinal())) ||
|
||||
(ndst < dst && (!usePriority || closest.block().priority.ordinal() <= e.block().priority.ordinal())) ||
|
||||
//priority is used, and new block has higher priority regardless of range
|
||||
(usePriority && closest.block.priority.ordinal() < e.block.priority.ordinal()))){
|
||||
(usePriority && closest.block().priority.ordinal() < e.block().priority.ordinal()))){
|
||||
dst = ndst;
|
||||
closest = e;
|
||||
}
|
||||
@@ -248,7 +275,7 @@ public class BlockIndexer{
|
||||
* each tile will at least have an ore within {@link #quadrantSize} / 2 blocks of it.
|
||||
* Only specific ore types are scanned. See {@link #scanOres}.
|
||||
*/
|
||||
public ObjectSet<Tile> getOrePositions(Item item){
|
||||
public TileArray getOrePositions(Item item){
|
||||
return ores.get(item, emptySet);
|
||||
}
|
||||
|
||||
@@ -271,20 +298,22 @@ public class BlockIndexer{
|
||||
}
|
||||
|
||||
private void process(Tile tile){
|
||||
if(tile.block().flags.size() > 0 && tile.getTeam() != Team.derelict){
|
||||
ObjectSet<Tile>[] map = getFlagged(tile.getTeam());
|
||||
if(tile.block().flags.size() > 0 && tile.team() != Team.derelict){
|
||||
TileArray[] map = getFlagged(tile.team());
|
||||
|
||||
for(BlockFlag flag : tile.block().flags){
|
||||
|
||||
ObjectSet<Tile> arr = map[flag.ordinal()];
|
||||
TileArray arr = map[flag.ordinal()];
|
||||
|
||||
arr.add(tile);
|
||||
|
||||
map[flag.ordinal()] = arr;
|
||||
}
|
||||
typeMap.put(tile.pos(), new TileIndex(tile.block().flags, tile.getTeam()));
|
||||
typeMap.put(tile.pos(), new TileIndex(tile.block().flags, tile.team()));
|
||||
}
|
||||
if(!activeTeams.contains(tile.team())){
|
||||
activeTeams.add(tile.team());
|
||||
}
|
||||
activeTeams.add(tile.getTeam());
|
||||
|
||||
if(ores == null) return;
|
||||
|
||||
@@ -306,7 +335,7 @@ public class BlockIndexer{
|
||||
|
||||
//update quadrant at this position
|
||||
for(Item item : scanOres){
|
||||
ObjectSet<Tile> set = ores.get(item);
|
||||
TileArray set = ores.get(item);
|
||||
|
||||
//update quadrant status depending on whether the item is in it
|
||||
if(!itemSet.contains(item)){
|
||||
@@ -328,7 +357,7 @@ public class BlockIndexer{
|
||||
GridBits bits = structQuadrant(team);
|
||||
|
||||
//fast-set this quadrant to 'occupied' if the tile just placed is already of this team
|
||||
if(tile.getTeam() == team && tile.entity != null && tile.block().targetable){
|
||||
if(tile.team() == team && tile.entity != null && tile.block().targetable){
|
||||
bits.set(quadrantX, quadrantY);
|
||||
continue; //no need to process futher
|
||||
}
|
||||
@@ -338,9 +367,9 @@ public class BlockIndexer{
|
||||
outer:
|
||||
for(int x = quadrantX * quadrantSize; x < world.width() && x < (quadrantX + 1) * quadrantSize; x++){
|
||||
for(int y = quadrantY * quadrantSize; y < world.height() && y < (quadrantY + 1) * quadrantSize; y++){
|
||||
Tile result = world.ltile(x, y);
|
||||
Tilec result = world.ent(x, y);
|
||||
//when a targetable block is found, mark this quadrant as occupied and stop searching
|
||||
if(result.entity != null && result.getTeam() == team){
|
||||
if(result!= null && result.team() == team){
|
||||
bits.set(quadrantX, quadrantY);
|
||||
break outer;
|
||||
}
|
||||
@@ -366,23 +395,19 @@ public class BlockIndexer{
|
||||
|
||||
//initialize ore map with empty sets
|
||||
for(Item item : scanOres){
|
||||
ores.put(item, new ObjectSet<>());
|
||||
ores.put(item, new TileArray());
|
||||
}
|
||||
|
||||
for(int x = 0; x < world.width(); x++){
|
||||
for(int y = 0; y < world.height(); y++){
|
||||
int qx = (x / quadrantSize);
|
||||
int qy = (y / quadrantSize);
|
||||
for(Tile tile : world.tiles){
|
||||
int qx = (tile.x / quadrantSize);
|
||||
int qy = (tile.y / quadrantSize);
|
||||
|
||||
Tile tile = world.tile(x, y);
|
||||
|
||||
//add position of quadrant to list when an ore is found
|
||||
if(tile.drop() != null && scanOres.contains(tile.drop()) && tile.block() == Blocks.air){
|
||||
ores.get(tile.drop()).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)));
|
||||
}
|
||||
//add position of quadrant to list when an ore is found
|
||||
if(tile.drop() != null && scanOres.contains(tile.drop()) && tile.block() == Blocks.air){
|
||||
ores.get(tile.drop()).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)));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -396,4 +421,34 @@ public class BlockIndexer{
|
||||
this.team = team;
|
||||
}
|
||||
}
|
||||
|
||||
public static class TileArray implements Iterable<Tile>{
|
||||
private Array<Tile> tiles = new Array<>(false, 16);
|
||||
private IntSet contained = new IntSet();
|
||||
|
||||
public void add(Tile tile){
|
||||
if(contained.add(tile.pos())){
|
||||
tiles.add(tile);
|
||||
}
|
||||
}
|
||||
|
||||
public void remove(Tile tile){
|
||||
if(contained.remove(tile.pos())){
|
||||
tiles.remove(tile);
|
||||
}
|
||||
}
|
||||
|
||||
public int size(){
|
||||
return tiles.size;
|
||||
}
|
||||
|
||||
public Tile first(){
|
||||
return tiles.first();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<Tile> iterator(){
|
||||
return tiles.iterator();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
50
core/src/mindustry/ai/NewBlockIndexer.java
Normal file
50
core/src/mindustry/ai/NewBlockIndexer.java
Normal file
@@ -0,0 +1,50 @@
|
||||
package mindustry.ai;
|
||||
|
||||
//new indexer implementation, uses quadtrees
|
||||
public class NewBlockIndexer{
|
||||
|
||||
/*
|
||||
public ObjectSet<Tile> getOrePositions(Item item){
|
||||
|
||||
}
|
||||
|
||||
public Tile findClosestOre(float xp, float yp, Item item){
|
||||
|
||||
}
|
||||
|
||||
public ObjectSet<Tile> getDamaged(Team team){
|
||||
|
||||
}
|
||||
|
||||
public ObjectSet<Tile> getAllied(Team team, BlockFlag type){
|
||||
|
||||
}
|
||||
|
||||
public boolean eachBlock(Teamc team, float range, Boolf<Tilec> pred, Cons<Tilec> cons){
|
||||
return eachBlock(team.team(), team.getX(), team.getY(), range, pred, cons);
|
||||
}
|
||||
|
||||
public boolean eachBlock(Team team, float wx, float wy, float range, Boolf<Tilec> pred, Cons<Tilec> cons){
|
||||
|
||||
}
|
||||
|
||||
public Array<Tile> getEnemy(Team team, BlockFlag type){
|
||||
|
||||
}
|
||||
|
||||
public void notifyTileDamaged(Tilec entity){
|
||||
|
||||
}
|
||||
|
||||
public Tilec findEnemyTile(Team team, float x, float y, float range, Boolf<Tilec> pred){
|
||||
|
||||
}
|
||||
|
||||
public Tilec findTile(Team team, float x, float y, float range, Boolf<Tilec> pred, boolean usePriority){
|
||||
|
||||
}
|
||||
|
||||
public Tilec findTile(Team team, float x, float y, float range, Boolf<Tilec> pred){
|
||||
return findTile(team, x, y, range, pred, false);
|
||||
}*/
|
||||
}
|
||||
@@ -1,13 +1,13 @@
|
||||
package mindustry.ai;
|
||||
|
||||
import arc.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import arc.struct.*;
|
||||
import arc.func.*;
|
||||
import arc.math.geom.*;
|
||||
import arc.util.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.ArcAnnotate.*;
|
||||
import arc.util.*;
|
||||
import arc.util.async.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.game.EventType.*;
|
||||
import mindustry.game.*;
|
||||
import mindustry.gen.*;
|
||||
@@ -45,10 +45,8 @@ public class Pathfinder implements Runnable{
|
||||
created = new GridBits(Team.all().length, PathTarget.all.length);
|
||||
list = new Array<>();
|
||||
|
||||
for(int x = 0; x < world.width(); x++){
|
||||
for(int y = 0; y < world.height(); y++){
|
||||
tiles[x][y] = packTile(world.rawTile(x, y));
|
||||
}
|
||||
for(Tile tile : world.tiles){
|
||||
tiles[tile.x][tile.y] = packTile(tile);
|
||||
}
|
||||
|
||||
//special preset which may help speed things up; this is optional
|
||||
@@ -218,7 +216,7 @@ public class Pathfinder implements Runnable{
|
||||
//add targets
|
||||
for(int i = 0; i < path.targets.size; i++){
|
||||
int pos = path.targets.get(i);
|
||||
int tx = Pos.x(pos), ty = Pos.y(pos);
|
||||
int tx = Point2.x(pos), ty = Point2.y(pos);
|
||||
|
||||
path.weights[tx][ty] = 0;
|
||||
path.searches[tx][ty] = (short)path.search;
|
||||
@@ -255,7 +253,7 @@ public class Pathfinder implements Runnable{
|
||||
//add targets
|
||||
for(int i = 0; i < path.targets.size; i++){
|
||||
int pos = path.targets.get(i);
|
||||
path.weights[Pos.x(pos)][Pos.y(pos)] = 0;
|
||||
path.weights[Point2.x(pos)][Point2.y(pos)] = 0;
|
||||
path.frontier.addFirst(pos);
|
||||
}
|
||||
|
||||
@@ -285,7 +283,7 @@ public class Pathfinder implements Runnable{
|
||||
|
||||
if(other != null && (path.weights[dx][dy] > cost + other.cost || path.searches[dx][dy] < path.search) && passable(dx, dy, path.team)){
|
||||
if(other.cost < 0) throw new IllegalArgumentException("Tile cost cannot be negative! " + other);
|
||||
path.frontier.addFirst(Pos.get(dx, dy));
|
||||
path.frontier.addFirst(Point2.pack(dx, dy));
|
||||
path.weights[dx][dy] = cost + other.cost;
|
||||
path.searches[dx][dy] = (short)path.search;
|
||||
}
|
||||
@@ -303,7 +301,7 @@ public class Pathfinder implements Runnable{
|
||||
|
||||
//spawn points are also enemies.
|
||||
if(state.rules.waves && team == state.rules.defaultTeam){
|
||||
for(Tile other : spawner.getGroundSpawns()){
|
||||
for(Tile other : spawner.getSpawns()){
|
||||
out.add(other.pos());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,28 +1,23 @@
|
||||
package mindustry.ai;
|
||||
|
||||
import arc.Events;
|
||||
import arc.struct.Array;
|
||||
import arc.func.Floatc2;
|
||||
import arc.math.Angles;
|
||||
import arc.math.Mathf;
|
||||
import arc.util.Time;
|
||||
import arc.util.Tmp;
|
||||
import mindustry.content.Blocks;
|
||||
import mindustry.content.Fx;
|
||||
import mindustry.entities.Damage;
|
||||
import mindustry.entities.Effects;
|
||||
import mindustry.entities.type.*;
|
||||
import mindustry.game.EventType.WorldLoadEvent;
|
||||
import mindustry.game.SpawnGroup;
|
||||
import mindustry.world.Tile;
|
||||
import arc.*;
|
||||
import arc.func.*;
|
||||
import arc.math.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.entities.*;
|
||||
import mindustry.game.EventType.*;
|
||||
import mindustry.game.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.world.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class WaveSpawner{
|
||||
private static final float margin = 40f, coreMargin = tilesize * 3; //how far away from the edge flying units spawn
|
||||
|
||||
private Array<FlyerSpawn> flySpawns = new Array<>();
|
||||
private Array<Tile> groundSpawns = new Array<>();
|
||||
private Array<Tile> spawns = new Array<>();
|
||||
private boolean spawning = false;
|
||||
|
||||
public WaveSpawner(){
|
||||
@@ -30,16 +25,16 @@ public class WaveSpawner{
|
||||
}
|
||||
|
||||
public int countSpawns(){
|
||||
return groundSpawns.size;
|
||||
return spawns.size;
|
||||
}
|
||||
|
||||
public Array<Tile> getGroundSpawns(){
|
||||
return groundSpawns;
|
||||
public Array<Tile> getSpawns(){
|
||||
return spawns;
|
||||
}
|
||||
|
||||
/** @return true if the player is near a ground spawn point. */
|
||||
public boolean playerNear(){
|
||||
return groundSpawns.contains(g -> Mathf.dst(g.x * tilesize, g.y * tilesize, player.x, player.y) < state.rules.dropZoneRadius && player.getTeam() != state.rules.waveTeam);
|
||||
return !player.dead() && spawns.contains(g -> Mathf.dst(g.x * tilesize, g.y * tilesize, player.x(), player.y()) < state.rules.dropZoneRadius && player.team() != state.rules.waveTeam);
|
||||
}
|
||||
|
||||
public void spawnEnemies(){
|
||||
@@ -53,7 +48,7 @@ public class WaveSpawner{
|
||||
|
||||
eachFlyerSpawn((spawnX, spawnY) -> {
|
||||
for(int i = 0; i < spawned; i++){
|
||||
BaseUnit unit = group.createUnit(state.rules.waveTeam);
|
||||
Unitc unit = group.createUnit(state.rules.waveTeam);
|
||||
unit.set(spawnX + Mathf.range(spread), spawnY + Mathf.range(spread));
|
||||
unit.add();
|
||||
}
|
||||
@@ -66,9 +61,8 @@ public class WaveSpawner{
|
||||
for(int i = 0; i < spawned; i++){
|
||||
Tmp.v1.rnd(spread);
|
||||
|
||||
BaseUnit unit = group.createUnit(state.rules.waveTeam);
|
||||
Unitc unit = group.createUnit(state.rules.waveTeam);
|
||||
unit.set(spawnX + Tmp.v1.x, spawnY + Tmp.v1.y);
|
||||
|
||||
Time.run(Math.min(i * 5, 60 * 2), () -> spawnEffect(unit));
|
||||
}
|
||||
});
|
||||
@@ -77,7 +71,7 @@ public class WaveSpawner{
|
||||
|
||||
eachGroundSpawn((spawnX, spawnY, doShockwave) -> {
|
||||
if(doShockwave){
|
||||
Time.run(20f, () -> Effects.effect(Fx.spawnShockwave, spawnX, spawnY, state.rules.dropZoneRadius));
|
||||
Time.run(20f, () -> Fx.spawnShockwave.at(spawnX, spawnY, state.rules.dropZoneRadius));
|
||||
Time.run(40f, () -> Damage.damage(state.rules.waveTeam, spawnX, spawnY, state.rules.dropZoneRadius, 99999999f, true));
|
||||
}
|
||||
});
|
||||
@@ -86,30 +80,32 @@ public class WaveSpawner{
|
||||
}
|
||||
|
||||
private void eachGroundSpawn(SpawnConsumer cons){
|
||||
for(Tile spawn : groundSpawns){
|
||||
for(Tile spawn : spawns){
|
||||
cons.accept(spawn.worldx(), spawn.worldy(), true);
|
||||
}
|
||||
|
||||
if(state.rules.attackMode && state.teams.isActive(state.rules.waveTeam) && !state.teams.playerCores().isEmpty()){
|
||||
TileEntity firstCore = state.teams.playerCores().first();
|
||||
for(TileEntity core : state.rules.waveTeam.cores()){
|
||||
Tmp.v1.set(firstCore).sub(core.x, core.y).limit(coreMargin + core.block.size*tilesize);
|
||||
cons.accept(core.x + Tmp.v1.x, core.y + Tmp.v1.y, false);
|
||||
Tilec firstCore = state.teams.playerCores().first();
|
||||
for(Tilec core : state.rules.waveTeam.cores()){
|
||||
Tmp.v1.set(firstCore).sub(core).limit(coreMargin + core.block().size*tilesize);
|
||||
cons.accept(core.x() + Tmp.v1.x, core.y() + Tmp.v1.y, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void eachFlyerSpawn(Floatc2 cons){
|
||||
for(FlyerSpawn spawn : flySpawns){
|
||||
float trns = (world.width() + world.height()) * tilesize;
|
||||
float spawnX = Mathf.clamp(world.width() * tilesize / 2f + Angles.trnsx(spawn.angle, trns), -margin, world.width() * tilesize + margin);
|
||||
float spawnY = Mathf.clamp(world.height() * tilesize / 2f + Angles.trnsy(spawn.angle, trns), -margin, world.height() * tilesize + margin);
|
||||
for(Tile tile : spawns){
|
||||
float angle = Angles.angle(tile.x, tile.y, world.width()/2, world.height()/2);
|
||||
|
||||
float trns = Math.max(world.width(), world.height()) * Mathf.sqrt2 * tilesize;
|
||||
float spawnX = Mathf.clamp(world.width() * tilesize / 2f + Angles.trnsx(angle, trns), -margin, world.width() * tilesize + margin);
|
||||
float spawnY = Mathf.clamp(world.height() * tilesize / 2f + Angles.trnsy(angle, trns), -margin, world.height() * tilesize + margin);
|
||||
cons.get(spawnX, spawnY);
|
||||
}
|
||||
|
||||
if(state.rules.attackMode && state.teams.isActive(state.rules.waveTeam)){
|
||||
for(TileEntity core : state.teams.get(state.rules.waveTeam).cores){
|
||||
cons.get(core.x, core.y);
|
||||
for(Tilec core : state.teams.get(state.rules.waveTeam).cores){
|
||||
cons.get(core.x(), core.y());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -119,41 +115,24 @@ public class WaveSpawner{
|
||||
}
|
||||
|
||||
private void reset(){
|
||||
spawns.clear();
|
||||
|
||||
flySpawns.clear();
|
||||
groundSpawns.clear();
|
||||
|
||||
for(int x = 0; x < world.width(); x++){
|
||||
for(int y = 0; y < world.height(); y++){
|
||||
|
||||
if(world.tile(x, y).overlay() == Blocks.spawn){
|
||||
addSpawns(x, y);
|
||||
}
|
||||
for(Tile tile : world.tiles){
|
||||
if(tile.overlay() == Blocks.spawn){
|
||||
spawns.add(tile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void addSpawns(int x, int y){
|
||||
groundSpawns.add(world.tile(x, y));
|
||||
|
||||
FlyerSpawn fspawn = new FlyerSpawn();
|
||||
fspawn.angle = Angles.angle(world.width() / 2f, world.height() / 2f, x, y);
|
||||
flySpawns.add(fspawn);
|
||||
}
|
||||
|
||||
private void spawnEffect(BaseUnit unit){
|
||||
Effects.effect(Fx.unitSpawn, unit.x, unit.y, 0f, unit);
|
||||
private void spawnEffect(Unitc unit){
|
||||
Fx.unitSpawn.at(unit.x(), unit.y(), 0f, unit);
|
||||
Time.run(30f, () -> {
|
||||
unit.add();
|
||||
Effects.effect(Fx.spawn, unit);
|
||||
Fx.spawn.at(unit);
|
||||
});
|
||||
}
|
||||
|
||||
private interface SpawnConsumer{
|
||||
void accept(float x, float y, boolean shockwave);
|
||||
}
|
||||
|
||||
private class FlyerSpawn{
|
||||
float angle;
|
||||
}
|
||||
}
|
||||
|
||||
102
core/src/mindustry/ai/types/FlyingAI.java
Normal file
102
core/src/mindustry/ai/types/FlyingAI.java
Normal file
@@ -0,0 +1,102 @@
|
||||
package mindustry.ai.types;
|
||||
|
||||
import arc.math.*;
|
||||
import arc.math.geom.*;
|
||||
import arc.util.*;
|
||||
import mindustry.*;
|
||||
import mindustry.entities.*;
|
||||
import mindustry.entities.units.*;
|
||||
import mindustry.world.meta.*;
|
||||
|
||||
public class FlyingAI extends AIController{
|
||||
|
||||
@Override
|
||||
public void update(){
|
||||
unit.rotation(unit.vel().angle());
|
||||
|
||||
if(unit.isFlying()){
|
||||
unit.wobble();
|
||||
}
|
||||
|
||||
if(Units.invalidateTarget(target, unit.team(), unit.x(), unit.y())){
|
||||
target = null;
|
||||
}
|
||||
|
||||
if(retarget()){
|
||||
targetClosest();
|
||||
|
||||
if(target == null) targetClosestEnemyFlag(BlockFlag.producer);
|
||||
if(target == null) targetClosestEnemyFlag(BlockFlag.turret);
|
||||
}
|
||||
|
||||
boolean shoot = false;
|
||||
|
||||
if(target != null){
|
||||
attack(80f);
|
||||
|
||||
shoot = unit.inRange(target);
|
||||
|
||||
if(shoot && unit.type().hasWeapons()){
|
||||
Vec2 to = Predict.intercept(unit, target, unit.type().weapons.first().bullet.speed);
|
||||
unit.aim(to);
|
||||
}
|
||||
}else{
|
||||
target = unit.closestCore();
|
||||
moveTo(Vars.state.rules.dropZoneRadius + 120f);
|
||||
}
|
||||
|
||||
unit.controlWeapons(shoot, shoot);
|
||||
}
|
||||
|
||||
protected void circle(float circleLength){
|
||||
circle(circleLength, unit.type().speed);
|
||||
}
|
||||
|
||||
protected void circle(float circleLength, float speed){
|
||||
if(target == null) return;
|
||||
|
||||
vec.set(target).sub(unit);
|
||||
|
||||
if(vec.len() < circleLength){
|
||||
vec.rotate((circleLength - vec.len()) / circleLength * 180f);
|
||||
}
|
||||
|
||||
vec.setLength(speed * Time.delta());
|
||||
|
||||
unit.moveAt(vec);
|
||||
}
|
||||
|
||||
protected void moveTo(float circleLength){
|
||||
if(target == null) return;
|
||||
|
||||
vec.set(target).sub(unit);
|
||||
|
||||
float length = circleLength <= 0.001f ? 1f : Mathf.clamp((unit.dst(target) - circleLength) / 100f, -1f, 1f);
|
||||
|
||||
vec.setLength(unit.type().speed * Time.delta() * length);
|
||||
if(length < -0.5f){
|
||||
vec.rotate(180f);
|
||||
}else if(length < 0){
|
||||
vec.setZero();
|
||||
}
|
||||
|
||||
unit.moveAt(vec);
|
||||
}
|
||||
|
||||
protected void attack(float circleLength){
|
||||
vec.set(target).sub(unit);
|
||||
|
||||
float ang = unit.angleTo(target);
|
||||
float diff = Angles.angleDist(ang, unit.rotation());
|
||||
|
||||
if(diff > 100f && vec.len() < circleLength){
|
||||
vec.setAngle(unit.vel().angle());
|
||||
}else{
|
||||
vec.setAngle(Mathf.slerpDelta(unit.vel().angle(), vec.angle(), 0.6f));
|
||||
}
|
||||
|
||||
vec.setLength(unit.type().speed * Time.delta());
|
||||
|
||||
unit.moveAt(vec);
|
||||
}
|
||||
}
|
||||
95
core/src/mindustry/ai/types/GroundAI.java
Normal file
95
core/src/mindustry/ai/types/GroundAI.java
Normal file
@@ -0,0 +1,95 @@
|
||||
package mindustry.ai.types;
|
||||
|
||||
import arc.util.*;
|
||||
import mindustry.ai.Pathfinder.*;
|
||||
import mindustry.entities.*;
|
||||
import mindustry.entities.units.*;
|
||||
import mindustry.game.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.world.*;
|
||||
|
||||
import static mindustry.Vars.pathfinder;
|
||||
|
||||
public class GroundAI extends AIController{
|
||||
|
||||
@Override
|
||||
public void update(){
|
||||
if(Units.invalidateTarget(target, unit.team(), unit.x(), unit.y(), Float.MAX_VALUE)){
|
||||
target = null;
|
||||
|
||||
//TODO this is hacky, cleanup
|
||||
if(unit instanceof Legsc){
|
||||
unit.lookAt(((Legsc)unit).baseRotation());
|
||||
}
|
||||
}
|
||||
|
||||
if(retarget()){
|
||||
targetClosest();
|
||||
}
|
||||
|
||||
Tilec core = unit.closestEnemyCore();
|
||||
|
||||
if(core == null) return;
|
||||
|
||||
float dst = unit.dst(core);
|
||||
|
||||
if(dst < unit.range() / 1.1f){
|
||||
target = core;
|
||||
}
|
||||
|
||||
if(dst > unit.range() * 0.5f){
|
||||
moveToCore(PathTarget.enemyCores);
|
||||
}
|
||||
|
||||
boolean rotate = false, shoot = false;
|
||||
|
||||
if(!Units.invalidateTarget(target, unit, unit.range())){
|
||||
rotate = true;
|
||||
shoot = unit.within(target, unit.range());
|
||||
|
||||
if(unit.type().hasWeapons()){
|
||||
unit.aimLook(Predict.intercept(unit, target, unit.type().weapons.first().bullet.speed));
|
||||
}
|
||||
}
|
||||
|
||||
unit.controlWeapons(rotate, shoot);
|
||||
}
|
||||
|
||||
protected void moveToCore(PathTarget path){
|
||||
Tile tile = unit.tileOn();
|
||||
if(tile == null) return;
|
||||
Tile targetTile = pathfinder.getTargetTile(tile, unit.team(), path);
|
||||
|
||||
if(tile == targetTile) return;
|
||||
|
||||
unit.moveAt(vec.trns(unit.angleTo(targetTile), unit.type().speed * Time.delta()));
|
||||
}
|
||||
|
||||
protected void moveAwayFromCore(){
|
||||
Team enemy = null;
|
||||
for(Team team : unit.team().enemies()){
|
||||
if(team.active()){
|
||||
enemy = team;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(enemy == null){
|
||||
for(Team team : unit.team().enemies()){
|
||||
enemy = team;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(enemy == null) return;
|
||||
|
||||
Tile tile = unit.tileOn();
|
||||
if(tile == null) return;
|
||||
Tile targetTile = pathfinder.getTargetTile(tile, enemy, PathTarget.enemyCores);
|
||||
Tilec core = unit.closestCore();
|
||||
|
||||
if(tile == targetTile || core == null || unit.within(core, 120f)) return;
|
||||
|
||||
unit.moveAt(vec.trns(unit.angleTo(targetTile), unit.type().speed * Time.delta()));
|
||||
}
|
||||
}
|
||||
31
core/src/mindustry/ai/types/MimicAI.java
Normal file
31
core/src/mindustry/ai/types/MimicAI.java
Normal file
@@ -0,0 +1,31 @@
|
||||
package mindustry.ai.types;
|
||||
|
||||
import arc.util.ArcAnnotate.*;
|
||||
import arc.util.*;
|
||||
import mindustry.entities.units.*;
|
||||
import mindustry.gen.*;
|
||||
|
||||
public class MimicAI extends AIController{
|
||||
public @Nullable Unitc control;
|
||||
|
||||
public MimicAI(@Nullable Unitc control){
|
||||
this.control = control;
|
||||
}
|
||||
|
||||
public MimicAI(){
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(){
|
||||
if(control != null){
|
||||
unit.controlWeapons(control.isRotate(), control.isShooting());
|
||||
//TODO this isn't accurate
|
||||
unit.moveAt(Tmp.v1.set(control.vel()).limit(unit.type().speed));
|
||||
if(control.isShooting()){
|
||||
unit.aimLook(control.aimX(), control.aimY());
|
||||
}else{
|
||||
unit.lookAt(unit.vel().angle());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
package mindustry.content;
|
||||
|
||||
import arc.*;
|
||||
import arc.struct.*;
|
||||
import arc.graphics.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.math.*;
|
||||
@@ -10,7 +9,6 @@ import mindustry.*;
|
||||
import mindustry.ctype.*;
|
||||
import mindustry.entities.*;
|
||||
import mindustry.entities.bullet.*;
|
||||
import mindustry.entities.type.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.type.*;
|
||||
@@ -19,6 +17,8 @@ import mindustry.world.blocks.*;
|
||||
import mindustry.world.blocks.defense.*;
|
||||
import mindustry.world.blocks.defense.turrets.*;
|
||||
import mindustry.world.blocks.distribution.*;
|
||||
import mindustry.world.blocks.environment.*;
|
||||
import mindustry.world.blocks.legacy.*;
|
||||
import mindustry.world.blocks.liquid.*;
|
||||
import mindustry.world.blocks.logic.*;
|
||||
import mindustry.world.blocks.power.*;
|
||||
@@ -28,13 +28,12 @@ import mindustry.world.blocks.storage.*;
|
||||
import mindustry.world.blocks.units.*;
|
||||
import mindustry.world.consumers.*;
|
||||
import mindustry.world.meta.*;
|
||||
import mindustry.world.modules.*;
|
||||
|
||||
public class Blocks implements ContentList{
|
||||
public static Block
|
||||
|
||||
//environment
|
||||
air, spawn, deepwater, water, taintedWater, tar, stone, craters, charr, sand, darksand, ice, snow, darksandTaintedWater,
|
||||
air, spawn, cliff, deepwater, water, taintedWater, tar, slag, stone, craters, charr, sand, darksand, ice, snow, darksandTaintedWater,
|
||||
holostone, rocks, sporerocks, icerocks, cliffs, sporePine, snowPine, pine, shrubs, whiteTree, whiteTreeDead, sporeCluster,
|
||||
iceSnow, sandWater, darksandWater, duneRocks, sandRocks, moss, sporeMoss, shale, shaleRocks, shaleBoulder, sandBoulder, grass, salt,
|
||||
metalFloor, metalFloorDamaged, metalFloor2, metalFloor3, metalFloor5, ignarock, magmarock, hotrock, snowrocks, rock, snowrock, saltRocks,
|
||||
@@ -57,7 +56,8 @@ public class Blocks implements ContentList{
|
||||
scrapWall, scrapWallLarge, scrapWallHuge, scrapWallGigantic, thruster, //ok, these names are getting ridiculous, but at least I don't have humongous walls yet
|
||||
|
||||
//transport
|
||||
conveyor, titaniumConveyor, plastaniumConveyor, armoredConveyor, distributor, junction, itemBridge, phaseConveyor, sorter, invertedSorter, router, overflowGate, underflowGate, massDriver,
|
||||
conveyor, titaniumConveyor, plastaniumConveyor, armoredConveyor, distributor, junction, itemBridge, phaseConveyor, sorter, invertedSorter, router,
|
||||
overflowGate, underflowGate, massDriver, massConveyor,
|
||||
|
||||
//liquid
|
||||
mechanicalPump, rotaryPump, thermalPump, conduit, pulseConduit, platedConduit, liquidRouter, liquidTank, liquidJunction, bridgeConduit, phaseConduit,
|
||||
@@ -76,11 +76,9 @@ public class Blocks implements ContentList{
|
||||
duo, scatter, scorch, hail, arc, wave, lancer, swarmer, salvo, fuse, ripple, cyclone, spectre, meltdown,
|
||||
|
||||
//units
|
||||
commandCenter, draugFactory, spiritFactory, phantomFactory, wraithFactory, ghoulFactory, revenantFactory, daggerFactory, crawlerFactory, titanFactory,
|
||||
fortressFactory, repairPoint,
|
||||
groundFactory, repairPoint
|
||||
|
||||
//upgrades
|
||||
dartPad, deltaPad, tauPad, omegaPad, javelinPad, tridentPad, glaivePad;
|
||||
;
|
||||
|
||||
@Override
|
||||
public void load(){
|
||||
@@ -92,7 +90,7 @@ public class Blocks implements ContentList{
|
||||
hasShadow = false;
|
||||
}
|
||||
|
||||
public void draw(Tile tile){}
|
||||
public void drawBase(Tile tile){}
|
||||
public void load(){}
|
||||
public void init(){}
|
||||
public boolean isHidden(){
|
||||
@@ -107,23 +105,16 @@ public class Blocks implements ContentList{
|
||||
}
|
||||
};
|
||||
|
||||
//create special blockpart variants
|
||||
for(int dx = 0; dx < BlockPart.maxSize; dx++){
|
||||
for(int dy = 0; dy < BlockPart.maxSize; dy++){
|
||||
int fx = dx - BlockPart.maxSize/2, fy = dy - BlockPart.maxSize/2;
|
||||
if(fx != 0 || fy != 0){
|
||||
new BlockPart(fx, fy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
spawn = new OverlayFloor("spawn"){
|
||||
{
|
||||
variants = 0;
|
||||
}
|
||||
public void draw(Tile tile){}
|
||||
@Override
|
||||
public void drawBase(Tile tile){}
|
||||
};
|
||||
|
||||
cliff = new Cliff("cliff");
|
||||
|
||||
//Registers build blocks
|
||||
//no reference is needed here since they can be looked up by name later
|
||||
for(int i = 1; i <= BuildBlock.maxSize; i++){
|
||||
@@ -139,6 +130,7 @@ public class Blocks implements ContentList{
|
||||
statusDuration = 120f;
|
||||
drownTime = 140f;
|
||||
cacheLayer = CacheLayer.water;
|
||||
albedo = 0.5f;
|
||||
}};
|
||||
|
||||
water = new Floor("water"){{
|
||||
@@ -149,6 +141,7 @@ public class Blocks implements ContentList{
|
||||
liquidDrop = Liquids.water;
|
||||
isLiquid = true;
|
||||
cacheLayer = CacheLayer.water;
|
||||
albedo = 0.5f;
|
||||
}};
|
||||
|
||||
taintedWater = new Floor("tainted-water"){{
|
||||
@@ -160,36 +153,25 @@ public class Blocks implements ContentList{
|
||||
liquidDrop = Liquids.water;
|
||||
isLiquid = true;
|
||||
cacheLayer = CacheLayer.water;
|
||||
albedo = 0.5f;
|
||||
}};
|
||||
|
||||
darksandTaintedWater = new Floor("darksand-tainted-water"){{
|
||||
darksandTaintedWater = new ShallowLiquid("darksand-tainted-water"){{
|
||||
speedMultiplier = 0.75f;
|
||||
variants = 0;
|
||||
status = StatusEffects.wet;
|
||||
statusDuration = 60f;
|
||||
liquidDrop = Liquids.water;
|
||||
isLiquid = true;
|
||||
cacheLayer = CacheLayer.water;
|
||||
albedo = 0.5f;
|
||||
}};
|
||||
|
||||
sandWater = new Floor("sand-water"){{
|
||||
sandWater = new ShallowLiquid("sand-water"){{
|
||||
speedMultiplier = 0.8f;
|
||||
variants = 0;
|
||||
status = StatusEffects.wet;
|
||||
statusDuration = 50f;
|
||||
liquidDrop = Liquids.water;
|
||||
isLiquid = true;
|
||||
cacheLayer = CacheLayer.water;
|
||||
albedo = 0.5f;
|
||||
}};
|
||||
|
||||
darksandWater = new Floor("darksand-water"){{
|
||||
darksandWater = new ShallowLiquid("darksand-water"){{
|
||||
speedMultiplier = 0.8f;
|
||||
variants = 0;
|
||||
status = StatusEffects.wet;
|
||||
statusDuration = 50f;
|
||||
liquidDrop = Liquids.water;
|
||||
isLiquid = true;
|
||||
cacheLayer = CacheLayer.water;
|
||||
albedo = 0.5f;
|
||||
}};
|
||||
|
||||
tar = new Floor("tar"){{
|
||||
@@ -203,6 +185,17 @@ public class Blocks implements ContentList{
|
||||
cacheLayer = CacheLayer.tar;
|
||||
}};
|
||||
|
||||
slag = new Floor("slag"){{
|
||||
drownTime = 150f;
|
||||
status = StatusEffects.melting;
|
||||
statusDuration = 240f;
|
||||
speedMultiplier = 0.19f;
|
||||
variants = 0;
|
||||
liquidDrop = Liquids.slag;
|
||||
isLiquid = true;
|
||||
cacheLayer = CacheLayer.slag;
|
||||
}};
|
||||
|
||||
stone = new Floor("stone"){{
|
||||
|
||||
}};
|
||||
@@ -217,16 +210,18 @@ public class Blocks implements ContentList{
|
||||
}};
|
||||
|
||||
ignarock = new Floor("ignarock"){{
|
||||
|
||||
attributes.set(Attribute.water, -0.1f);
|
||||
}};
|
||||
|
||||
hotrock = new Floor("hotrock"){{
|
||||
attributes.set(Attribute.heat, 0.5f);
|
||||
attributes.set(Attribute.water, -0.2f);
|
||||
blendGroup = ignarock;
|
||||
}};
|
||||
|
||||
magmarock = new Floor("magmarock"){{
|
||||
attributes.set(Attribute.heat, 0.75f);
|
||||
attributes.set(Attribute.water, -0.5f);
|
||||
updateEffect = Fx.magmasmoke;
|
||||
blendGroup = ignarock;
|
||||
}};
|
||||
@@ -241,6 +236,10 @@ public class Blocks implements ContentList{
|
||||
playerUnmineable = true;
|
||||
}};
|
||||
|
||||
((ShallowLiquid)darksandTaintedWater).set(Blocks.taintedWater, Blocks.darksand);
|
||||
((ShallowLiquid)sandWater).set(Blocks.water, Blocks.sand);
|
||||
((ShallowLiquid)darksandWater).set(Blocks.water, Blocks.darksand);
|
||||
|
||||
holostone = new Floor("holostone"){{
|
||||
|
||||
}};
|
||||
@@ -251,6 +250,7 @@ public class Blocks implements ContentList{
|
||||
|
||||
salt = new Floor("salt"){{
|
||||
variants = 0;
|
||||
attributes.set(Attribute.water, -0.2f);
|
||||
}};
|
||||
|
||||
snow = new Floor("snow"){{
|
||||
@@ -258,13 +258,13 @@ public class Blocks implements ContentList{
|
||||
}};
|
||||
|
||||
ice = new Floor("ice"){{
|
||||
//TODO fix drag/speed
|
||||
dragMultiplier = 1f;
|
||||
speedMultiplier = 1f;
|
||||
dragMultiplier = 0.35f;
|
||||
speedMultiplier = 0.9f;
|
||||
attributes.set(Attribute.water, 0.4f);
|
||||
}};
|
||||
|
||||
iceSnow = new Floor("ice-snow"){{
|
||||
dragMultiplier = 0.6f;
|
||||
variants = 3;
|
||||
attributes.set(Attribute.water, 0.3f);
|
||||
}};
|
||||
@@ -292,6 +292,7 @@ public class Blocks implements ContentList{
|
||||
|
||||
icerocks = new StaticWall("icerocks"){{
|
||||
variants = 2;
|
||||
iceSnow.asFloor().wall = this;
|
||||
}};
|
||||
|
||||
snowrocks = new StaticWall("snowrocks"){{
|
||||
@@ -355,11 +356,13 @@ public class Blocks implements ContentList{
|
||||
moss = new Floor("moss"){{
|
||||
variants = 3;
|
||||
attributes.set(Attribute.spores, 0.15f);
|
||||
wall = sporePine;
|
||||
}};
|
||||
|
||||
sporeMoss = new Floor("spore-moss"){{
|
||||
variants = 3;
|
||||
attributes.set(Attribute.spores, 0.3f);
|
||||
wall = sporerocks;
|
||||
}};
|
||||
|
||||
metalFloor = new Floor("metal-floor"){{
|
||||
@@ -506,13 +509,10 @@ public class Blocks implements ContentList{
|
||||
|
||||
int topRegion = reg("-top");
|
||||
|
||||
drawer = tile -> {
|
||||
Draw.rect(region, tile.drawx(), tile.drawy());
|
||||
|
||||
GenericCrafterEntity entity = tile.ent();
|
||||
|
||||
drawer = entity -> {
|
||||
Draw.rect(region, entity.x(), entity.y());
|
||||
Draw.alpha(Mathf.absin(entity.totalProgress, 3f, 0.9f) * entity.warmup);
|
||||
Draw.rect(reg(topRegion), tile.drawx(), tile.drawy());
|
||||
Draw.rect(reg(topRegion), entity.x(), entity.y());
|
||||
Draw.reset();
|
||||
};
|
||||
}};
|
||||
@@ -533,24 +533,22 @@ public class Blocks implements ContentList{
|
||||
|
||||
drawIcons = () -> new TextureRegion[]{Core.atlas.find(name + "-bottom"), Core.atlas.find(name), Core.atlas.find(name + "-weave")};
|
||||
|
||||
drawer = tile -> {
|
||||
GenericCrafterEntity entity = tile.ent();
|
||||
|
||||
Draw.rect(reg(bottomRegion), tile.drawx(), tile.drawy());
|
||||
Draw.rect(reg(weaveRegion), tile.drawx(), tile.drawy(), entity.totalProgress);
|
||||
drawer = entity -> {
|
||||
Draw.rect(reg(bottomRegion), entity.x(), entity.y());
|
||||
Draw.rect(reg(weaveRegion), entity.x(), entity.y(), entity.totalProgress);
|
||||
|
||||
Draw.color(Pal.accent);
|
||||
Draw.alpha(entity.warmup);
|
||||
|
||||
Lines.lineAngleCenter(
|
||||
tile.drawx() + Mathf.sin(entity.totalProgress, 6f, Vars.tilesize / 3f * size),
|
||||
tile.drawy(),
|
||||
entity.x() + Mathf.sin(entity.totalProgress, 6f, Vars.tilesize / 3f * size),
|
||||
entity.y(),
|
||||
90,
|
||||
size * Vars.tilesize / 2f);
|
||||
|
||||
Draw.reset();
|
||||
|
||||
Draw.rect(region, tile.drawx(), tile.drawy());
|
||||
Draw.rect(region, entity.x(), entity.y());
|
||||
};
|
||||
}};
|
||||
|
||||
@@ -586,21 +584,19 @@ public class Blocks implements ContentList{
|
||||
|
||||
drawIcons = () -> new TextureRegion[]{Core.atlas.find(name + "-bottom"), Core.atlas.find(name + "-top")};
|
||||
|
||||
drawer = tile -> {
|
||||
LiquidModule mod = tile.entity.liquids;
|
||||
drawer = entity -> {
|
||||
int rotation = rotate ? entity.rotation() * 90 : 0;
|
||||
|
||||
int rotation = rotate ? tile.rotation() * 90 : 0;
|
||||
Draw.rect(reg(bottomRegion), entity.x(), entity.y(), rotation);
|
||||
|
||||
Draw.rect(reg(bottomRegion), tile.drawx(), tile.drawy(), rotation);
|
||||
|
||||
if(mod.total() > 0.001f){
|
||||
if(entity.liquids().total() > 0.001f){
|
||||
Draw.color(outputLiquid.liquid.color);
|
||||
Draw.alpha(mod.get(outputLiquid.liquid) / liquidCapacity);
|
||||
Draw.rect(reg(liquidRegion), tile.drawx(), tile.drawy(), rotation);
|
||||
Draw.alpha(entity.liquids().get(outputLiquid.liquid) / liquidCapacity);
|
||||
Draw.rect(reg(liquidRegion), entity.x(), entity.y(), rotation);
|
||||
Draw.color();
|
||||
}
|
||||
|
||||
Draw.rect(reg(topRegion), tile.drawx(), tile.drawy(), rotation);
|
||||
Draw.rect(reg(topRegion), entity.x(), entity.y(), rotation);
|
||||
};
|
||||
}};
|
||||
|
||||
@@ -678,15 +674,13 @@ public class Blocks implements ContentList{
|
||||
int topRegion = reg("-top");
|
||||
|
||||
drawIcons = () -> new TextureRegion[]{Core.atlas.find(name), Core.atlas.find(name + "-top")};
|
||||
drawer = tile -> {
|
||||
GenericCrafterEntity entity = tile.ent();
|
||||
|
||||
Draw.rect(region, tile.drawx(), tile.drawy());
|
||||
Draw.rect(reg(frameRegions[(int)Mathf.absin(entity.totalProgress, 5f, 2.999f)]), tile.drawx(), tile.drawy());
|
||||
Draw.color(Color.clear, tile.entity.liquids.current().color, tile.entity.liquids.total() / liquidCapacity);
|
||||
Draw.rect(reg(liquidRegion), tile.drawx(), tile.drawy());
|
||||
drawer = entity -> {
|
||||
Draw.rect(region, entity.x(), entity.y());
|
||||
Draw.rect(reg(frameRegions[(int)Mathf.absin(entity.totalProgress, 5f, 2.999f)]), entity.x(), entity.y());
|
||||
Draw.color(Color.clear, entity.liquids().current().color, entity.liquids().total() / liquidCapacity);
|
||||
Draw.rect(reg(liquidRegion), entity.x(), entity.y());
|
||||
Draw.color();
|
||||
Draw.rect(reg(topRegion), tile.drawx(), tile.drawy());
|
||||
Draw.rect(reg(topRegion), entity.x(), entity.y());
|
||||
};
|
||||
}};
|
||||
|
||||
@@ -705,11 +699,9 @@ public class Blocks implements ContentList{
|
||||
|
||||
drawIcons = () -> new TextureRegion[]{Core.atlas.find(name), Core.atlas.find(name + "-rotator")};
|
||||
|
||||
drawer = tile -> {
|
||||
GenericCrafterEntity entity = tile.ent();
|
||||
|
||||
Draw.rect(region, tile.drawx(), tile.drawy());
|
||||
Draw.rect(reg(rotatorRegion), tile.drawx(), tile.drawy(), entity.totalProgress * 2f);
|
||||
drawer = entity -> {
|
||||
Draw.rect(region, entity.x(), entity.y());
|
||||
Draw.rect(reg(rotatorRegion), entity.x(), entity.y(), entity.totalProgress * 2f);
|
||||
};
|
||||
}};
|
||||
|
||||
@@ -990,6 +982,10 @@ public class Blocks implements ContentList{
|
||||
consumes.power(1.75f);
|
||||
}};
|
||||
|
||||
massConveyor = new MassConveyor("mass-conveyor"){{
|
||||
requirements(Category.distribution, ItemStack.with(Items.copper, 1));
|
||||
}};
|
||||
|
||||
//endregion
|
||||
//region liquid
|
||||
|
||||
@@ -1040,7 +1036,7 @@ public class Blocks implements ContentList{
|
||||
liquidCapacity = 20f;
|
||||
}};
|
||||
|
||||
liquidTank = new LiquidTank("liquid-tank"){{
|
||||
liquidTank = new LiquidRouter("liquid-tank"){{
|
||||
requirements(Category.liquid, ItemStack.with(Items.titanium, 25, Items.metaglass, 25));
|
||||
size = 3;
|
||||
liquidCapacity = 1500f;
|
||||
@@ -1297,13 +1293,13 @@ public class Blocks implements ContentList{
|
||||
size = 5;
|
||||
}};
|
||||
|
||||
vault = new Vault("vault"){{
|
||||
vault = new StorageBlock("vault"){{
|
||||
requirements(Category.effect, ItemStack.with(Items.titanium, 250, Items.thorium, 125));
|
||||
size = 3;
|
||||
itemCapacity = 1000;
|
||||
}};
|
||||
|
||||
container = new Vault("container"){{
|
||||
container = new StorageBlock("container"){{
|
||||
requirements(Category.effect, ItemStack.with(Items.titanium, 100));
|
||||
size = 2;
|
||||
itemCapacity = 300;
|
||||
@@ -1336,7 +1332,7 @@ public class Blocks implements ContentList{
|
||||
//endregion
|
||||
//region turrets
|
||||
|
||||
duo = new DoubleTurret("duo"){{
|
||||
duo = new ItemTurret("duo"){{
|
||||
requirements(Category.turret, ItemStack.with(Items.copper, 35), true);
|
||||
ammo(
|
||||
Items.copper, Bullets.standardCopper,
|
||||
@@ -1344,7 +1340,11 @@ public class Blocks implements ContentList{
|
||||
Items.pyratite, Bullets.standardIncendiary,
|
||||
Items.silicon, Bullets.standardHoming
|
||||
);
|
||||
reload = 20f;
|
||||
|
||||
spread = 4f;
|
||||
shots = 2;
|
||||
alternate = true;
|
||||
reloadTime = 20f;
|
||||
restitution = 0.03f;
|
||||
range = 100;
|
||||
shootCone = 15f;
|
||||
@@ -1361,14 +1361,14 @@ public class Blocks implements ContentList{
|
||||
Items.lead, Bullets.flakLead,
|
||||
Items.metaglass, Bullets.flakGlass
|
||||
);
|
||||
reload = 18f;
|
||||
reloadTime = 18f;
|
||||
range = 170f;
|
||||
size = 2;
|
||||
burstSpacing = 5f;
|
||||
shots = 2;
|
||||
targetGround = false;
|
||||
|
||||
recoil = 2f;
|
||||
recoilAmount = 2f;
|
||||
rotatespeed = 15f;
|
||||
inaccuracy = 17f;
|
||||
shootCone = 35f;
|
||||
@@ -1383,8 +1383,8 @@ public class Blocks implements ContentList{
|
||||
Items.coal, Bullets.basicFlame,
|
||||
Items.pyratite, Bullets.pyraFlame
|
||||
);
|
||||
recoil = 0f;
|
||||
reload = 5f;
|
||||
recoilAmount = 0f;
|
||||
reloadTime = 5f;
|
||||
coolantMultiplier = 2f;
|
||||
range = 60f;
|
||||
shootCone = 50f;
|
||||
@@ -1401,8 +1401,8 @@ public class Blocks implements ContentList{
|
||||
Items.silicon, Bullets.artilleryHoming,
|
||||
Items.pyratite, Bullets.artilleryIncendiary
|
||||
);
|
||||
reload = 60f;
|
||||
recoil = 2f;
|
||||
reloadTime = 60f;
|
||||
recoilAmount = 2f;
|
||||
range = 230f;
|
||||
inaccuracy = 1f;
|
||||
shootCone = 10f;
|
||||
@@ -1419,8 +1419,8 @@ public class Blocks implements ContentList{
|
||||
Liquids.oil, Bullets.oilShot
|
||||
);
|
||||
size = 2;
|
||||
recoil = 0f;
|
||||
reload = 2f;
|
||||
recoilAmount = 0f;
|
||||
reloadTime = 2f;
|
||||
inaccuracy = 5f;
|
||||
shootCone = 50f;
|
||||
shootEffect = Fx.shootLiquid;
|
||||
@@ -1436,8 +1436,8 @@ public class Blocks implements ContentList{
|
||||
chargeMaxDelay = 30f;
|
||||
chargeEffects = 7;
|
||||
shootType = Bullets.lancerLaser;
|
||||
recoil = 2f;
|
||||
reload = 90f;
|
||||
recoilAmount = 2f;
|
||||
reloadTime = 90f;
|
||||
cooldown = 0.03f;
|
||||
powerUse = 2.5f;
|
||||
shootShake = 2f;
|
||||
@@ -1455,7 +1455,7 @@ public class Blocks implements ContentList{
|
||||
arc = new PowerTurret("arc"){{
|
||||
requirements(Category.turret, ItemStack.with(Items.copper, 35, Items.lead, 50));
|
||||
shootType = Bullets.arc;
|
||||
reload = 35f;
|
||||
reloadTime = 35f;
|
||||
shootCone = 40f;
|
||||
rotatespeed = 8f;
|
||||
powerUse = 1.5f;
|
||||
@@ -1463,7 +1463,7 @@ public class Blocks implements ContentList{
|
||||
range = 90f;
|
||||
shootEffect = Fx.lightningShoot;
|
||||
heatColor = Color.red;
|
||||
recoil = 1f;
|
||||
recoilAmount = 1f;
|
||||
size = 1;
|
||||
health = 260;
|
||||
shootSound = Sounds.spark;
|
||||
@@ -1476,7 +1476,7 @@ public class Blocks implements ContentList{
|
||||
Items.pyratite, Bullets.missileIncendiary,
|
||||
Items.surgealloy, Bullets.missileSurge
|
||||
);
|
||||
reload = 40f;
|
||||
reloadTime = 40f;
|
||||
shots = 4;
|
||||
burstSpacing = 5;
|
||||
inaccuracy = 10f;
|
||||
@@ -1499,11 +1499,11 @@ public class Blocks implements ContentList{
|
||||
|
||||
size = 2;
|
||||
range = 150f;
|
||||
reload = 38f;
|
||||
reloadTime = 38f;
|
||||
restitution = 0.03f;
|
||||
ammoEjectBack = 3f;
|
||||
cooldown = 0.03f;
|
||||
recoil = 3f;
|
||||
recoilAmount = 3f;
|
||||
shootShake = 2f;
|
||||
burstSpacing = 3f;
|
||||
shots = 4;
|
||||
@@ -1515,10 +1515,10 @@ public class Blocks implements ContentList{
|
||||
fuse = new ItemTurret("fuse"){{
|
||||
requirements(Category.turret, ItemStack.with(Items.copper, 225, Items.graphite, 225, Items.thorium, 100));
|
||||
|
||||
reload = 35f;
|
||||
reloadTime = 35f;
|
||||
shootShake = 4f;
|
||||
range = 90f;
|
||||
recoil = 5f;
|
||||
recoilAmount = 5f;
|
||||
shots = 3;
|
||||
spread = 20f;
|
||||
restitution = 0.1f;
|
||||
@@ -1541,25 +1541,25 @@ public class Blocks implements ContentList{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(mindustry.entities.type.Bullet b){
|
||||
public void init(Bulletc b){
|
||||
for(int i = 0; i < rays; i++){
|
||||
Damage.collideLine(b, b.getTeam(), hitEffect, b.x, b.y, b.rot(), rayLength - Math.abs(i - (rays / 2)) * 20f);
|
||||
Damage.collideLine(b, b.team(), hitEffect, b.x(), b.y(), b.rotation(), rayLength - Math.abs(i - (rays / 2)) * 20f);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Bullet b){
|
||||
public void draw(Bulletc b){
|
||||
super.draw(b);
|
||||
Draw.color(Color.white, Pal.lancerLaser, b.fin());
|
||||
//Draw.alpha(b.fout());
|
||||
for(int i = 0; i < 7; i++){
|
||||
Tmp.v1.trns(b.rot(), i * 8f);
|
||||
Tmp.v1.trns(b.rotation(), i * 8f);
|
||||
float sl = Mathf.clamp(b.fout() - 0.5f) * (80f - i * 10);
|
||||
Drawf.tri(b.x + Tmp.v1.x, b.y + Tmp.v1.y, 4f, sl, b.rot() + 90);
|
||||
Drawf.tri(b.x + Tmp.v1.x, b.y + Tmp.v1.y, 4f, sl, b.rot() - 90);
|
||||
Drawf.tri(b.x() + Tmp.v1.x, b.y() + Tmp.v1.y, 4f, sl, b.rotation() + 90);
|
||||
Drawf.tri(b.x() + Tmp.v1.x, b.y() + Tmp.v1.y, 4f, sl, b.rotation() - 90);
|
||||
}
|
||||
Drawf.tri(b.x, b.y, 20f * b.fout(), (rayLength + 50), b.rot());
|
||||
Drawf.tri(b.x, b.y, 20f * b.fout(), 10f, b.rot() + 180f);
|
||||
Drawf.tri(b.x(), b.y(), 20f * b.fout(), (rayLength + 50), b.rotation());
|
||||
Drawf.tri(b.x(), b.y(), 20f * b.fout(), 10f, b.rotation() + 180f);
|
||||
Draw.reset();
|
||||
}
|
||||
});
|
||||
@@ -1577,13 +1577,13 @@ public class Blocks implements ContentList{
|
||||
size = 3;
|
||||
shots = 4;
|
||||
inaccuracy = 12f;
|
||||
reload = 60f;
|
||||
reloadTime = 60f;
|
||||
ammoEjectBack = 5f;
|
||||
ammoUseEffect = Fx.shellEjectBig;
|
||||
cooldown = 0.03f;
|
||||
velocityInaccuracy = 0.2f;
|
||||
restitution = 0.02f;
|
||||
recoil = 6f;
|
||||
recoilAmount = 6f;
|
||||
shootShake = 2f;
|
||||
range = 290f;
|
||||
|
||||
@@ -1600,10 +1600,10 @@ public class Blocks implements ContentList{
|
||||
Items.surgealloy, Bullets.flakSurge
|
||||
);
|
||||
xRand = 4f;
|
||||
reload = 6f;
|
||||
reloadTime = 6f;
|
||||
range = 200f;
|
||||
size = 3;
|
||||
recoil = 3f;
|
||||
recoilAmount = 3f;
|
||||
rotatespeed = 10f;
|
||||
inaccuracy = 10f;
|
||||
shootCone = 30f;
|
||||
@@ -1612,22 +1612,22 @@ public class Blocks implements ContentList{
|
||||
health = 145 * size * size;
|
||||
}};
|
||||
|
||||
spectre = new DoubleTurret("spectre"){{
|
||||
spectre = new ItemTurret("spectre"){{
|
||||
requirements(Category.turret, ItemStack.with(Items.copper, 350, Items.graphite, 300, Items.surgealloy, 250, Items.plastanium, 175, Items.thorium, 250));
|
||||
ammo(
|
||||
Items.graphite, Bullets.standardDenseBig,
|
||||
Items.pyratite, Bullets.standardIncendiaryBig,
|
||||
Items.thorium, Bullets.standardThoriumBig
|
||||
);
|
||||
reload = 6f;
|
||||
reloadTime = 6f;
|
||||
coolantMultiplier = 0.5f;
|
||||
restitution = 0.1f;
|
||||
ammoUseEffect = Fx.shellEjectBig;
|
||||
range = 200f;
|
||||
inaccuracy = 3f;
|
||||
recoil = 3f;
|
||||
xRand = 3f;
|
||||
shotWidth = 4f;
|
||||
recoilAmount = 3f;
|
||||
spread = 8f;
|
||||
alternate = true;
|
||||
shootShake = 2f;
|
||||
shots = 2;
|
||||
size = 4;
|
||||
@@ -1643,11 +1643,11 @@ public class Blocks implements ContentList{
|
||||
shootType = Bullets.meltdownLaser;
|
||||
shootEffect = Fx.shootBigSmoke2;
|
||||
shootCone = 40f;
|
||||
recoil = 4f;
|
||||
recoilAmount = 4f;
|
||||
size = 4;
|
||||
shootShake = 2f;
|
||||
range = 190f;
|
||||
reload = 80f;
|
||||
reloadTime = 80f;
|
||||
firingMoveFract = 0.5f;
|
||||
shootDuration = 220f;
|
||||
powerUse = 14f;
|
||||
@@ -1662,106 +1662,16 @@ public class Blocks implements ContentList{
|
||||
//endregion
|
||||
//region units
|
||||
|
||||
draugFactory = new UnitFactory("draug-factory"){{
|
||||
//for testing only.
|
||||
groundFactory = new UnitFactory("ground-factory"){{
|
||||
requirements(Category.units, ItemStack.with(Items.copper, 30, Items.lead, 70));
|
||||
unitType = UnitTypes.draug;
|
||||
produceTime = 2500;
|
||||
size = 2;
|
||||
maxSpawn = 1;
|
||||
consumes.power(1.2f);
|
||||
consumes.items();
|
||||
}};
|
||||
|
||||
spiritFactory = new UnitFactory("spirit-factory"){{
|
||||
requirements(Category.units, ItemStack.with(Items.metaglass, 45, Items.lead, 55, Items.silicon, 45));
|
||||
unitType = UnitTypes.spirit;
|
||||
produceTime = 4000;
|
||||
size = 2;
|
||||
maxSpawn = 1;
|
||||
consumes.power(1.2f);
|
||||
consumes.items(new ItemStack(Items.silicon, 30), new ItemStack(Items.lead, 30));
|
||||
}};
|
||||
|
||||
phantomFactory = new UnitFactory("phantom-factory"){{
|
||||
requirements(Category.units, ItemStack.with(Items.titanium, 50, Items.thorium, 60, Items.lead, 65, Items.silicon, 105));
|
||||
unitType = UnitTypes.phantom;
|
||||
produceTime = 4400;
|
||||
size = 2;
|
||||
maxSpawn = 1;
|
||||
consumes.power(2.5f);
|
||||
consumes.items(new ItemStack(Items.silicon, 50), new ItemStack(Items.lead, 30), new ItemStack(Items.titanium, 20));
|
||||
}};
|
||||
|
||||
commandCenter = new CommandCenter("command-center"){{
|
||||
requirements(Category.units, ItemStack.with(Items.copper, 200, Items.lead, 250, Items.silicon, 250, Items.graphite, 100));
|
||||
flags = EnumSet.of(BlockFlag.rally, BlockFlag.comandCenter);
|
||||
size = 2;
|
||||
health = size * size * 55;
|
||||
}};
|
||||
|
||||
wraithFactory = new UnitFactory("wraith-factory"){{
|
||||
requirements(Category.units, ItemStack.with(Items.titanium, 30, Items.lead, 40, Items.silicon, 45));
|
||||
unitType = UnitTypes.wraith;
|
||||
produceTime = 700;
|
||||
size = 2;
|
||||
consumes.power(0.5f);
|
||||
consumes.items(new ItemStack(Items.silicon, 10), new ItemStack(Items.titanium, 5));
|
||||
}};
|
||||
|
||||
ghoulFactory = new UnitFactory("ghoul-factory"){{
|
||||
requirements(Category.units, ItemStack.with(Items.titanium, 75, Items.lead, 65, Items.silicon, 110));
|
||||
unitType = UnitTypes.ghoul;
|
||||
produceTime = 1150;
|
||||
plans = new UnitPlan[]{
|
||||
new UnitPlan(UnitTypes.dagger, 60f, ItemStack.with(Items.silicon, 10)),
|
||||
new UnitPlan(UnitTypes.wraith, 60f, ItemStack.with(Items.silicon, 10)),
|
||||
};
|
||||
size = 3;
|
||||
consumes.power(1.2f);
|
||||
consumes.items(new ItemStack(Items.silicon, 15), new ItemStack(Items.titanium, 10));
|
||||
}};
|
||||
|
||||
revenantFactory = new UnitFactory("revenant-factory"){{
|
||||
requirements(Category.units, ItemStack.with(Items.plastanium, 50, Items.titanium, 150, Items.lead, 150, Items.silicon, 200));
|
||||
unitType = UnitTypes.revenant;
|
||||
produceTime = 2000;
|
||||
size = 4;
|
||||
consumes.power(3f);
|
||||
consumes.items(new ItemStack(Items.silicon, 40), new ItemStack(Items.titanium, 30));
|
||||
}};
|
||||
|
||||
daggerFactory = new UnitFactory("dagger-factory"){{
|
||||
requirements(Category.units, ItemStack.with(Items.lead, 55, Items.silicon, 35));
|
||||
unitType = UnitTypes.dagger;
|
||||
produceTime = 850;
|
||||
size = 2;
|
||||
consumes.power(0.5f);
|
||||
consumes.items(new ItemStack(Items.silicon, 6));
|
||||
}};
|
||||
|
||||
crawlerFactory = new UnitFactory("crawler-factory"){{
|
||||
requirements(Category.units, ItemStack.with(Items.lead, 45, Items.silicon, 30));
|
||||
unitType = UnitTypes.crawler;
|
||||
produceTime = 300;
|
||||
size = 2;
|
||||
maxSpawn = 6;
|
||||
consumes.power(0.5f);
|
||||
consumes.items(new ItemStack(Items.coal, 10));
|
||||
}};
|
||||
|
||||
titanFactory = new UnitFactory("titan-factory"){{
|
||||
requirements(Category.units, ItemStack.with(Items.graphite, 50, Items.lead, 50, Items.silicon, 45));
|
||||
unitType = UnitTypes.titan;
|
||||
produceTime = 1050;
|
||||
size = 3;
|
||||
consumes.power(0.60f);
|
||||
consumes.items(new ItemStack(Items.silicon, 12));
|
||||
}};
|
||||
|
||||
fortressFactory = new UnitFactory("fortress-factory"){{
|
||||
requirements(Category.units, ItemStack.with(Items.thorium, 40, Items.lead, 110, Items.silicon, 75));
|
||||
unitType = UnitTypes.fortress;
|
||||
produceTime = 2000;
|
||||
size = 3;
|
||||
maxSpawn = 3;
|
||||
consumes.power(1.4f);
|
||||
consumes.items(new ItemStack(Items.silicon, 20), new ItemStack(Items.graphite, 10));
|
||||
consumes.items(new ItemStack(Items.silicon, 10));
|
||||
}};
|
||||
|
||||
repairPoint = new RepairPoint("repair-point"){{
|
||||
@@ -1771,58 +1681,6 @@ public class Blocks implements ContentList{
|
||||
powerUse = 1f;
|
||||
}};
|
||||
|
||||
//endregion
|
||||
//region upgrades
|
||||
|
||||
dartPad = new MechPad("dart-mech-pad"){{
|
||||
requirements(Category.upgrade, ItemStack.with(Items.lead, 100, Items.graphite, 50, Items.copper, 75));
|
||||
mech = Mechs.alpha;
|
||||
size = 2;
|
||||
consumes.power(0.5f);
|
||||
}};
|
||||
|
||||
deltaPad = new MechPad("delta-mech-pad"){{
|
||||
requirements(Category.upgrade, ItemStack.with(Items.lead, 175, Items.titanium, 175, Items.copper, 200, Items.silicon, 225, Items.thorium, 150));
|
||||
mech = Mechs.delta;
|
||||
size = 2;
|
||||
consumes.power(0.7f);
|
||||
}};
|
||||
|
||||
tauPad = new MechPad("tau-mech-pad"){{
|
||||
requirements(Category.upgrade, ItemStack.with(Items.lead, 125, Items.titanium, 125, Items.copper, 125, Items.silicon, 125));
|
||||
mech = Mechs.tau;
|
||||
size = 2;
|
||||
consumes.power(1f);
|
||||
}};
|
||||
|
||||
omegaPad = new MechPad("omega-mech-pad"){{
|
||||
requirements(Category.upgrade, ItemStack.with(Items.lead, 225, Items.graphite, 275, Items.silicon, 325, Items.thorium, 300, Items.surgealloy, 120));
|
||||
mech = Mechs.omega;
|
||||
size = 3;
|
||||
consumes.power(1.2f);
|
||||
}};
|
||||
|
||||
javelinPad = new MechPad("javelin-ship-pad"){{
|
||||
requirements(Category.upgrade, ItemStack.with(Items.lead, 175, Items.silicon, 225, Items.titanium, 250, Items.plastanium, 200, Items.phasefabric, 100));
|
||||
mech = Mechs.javelin;
|
||||
size = 2;
|
||||
consumes.power(0.8f);
|
||||
}};
|
||||
|
||||
tridentPad = new MechPad("trident-ship-pad"){{
|
||||
requirements(Category.upgrade, ItemStack.with(Items.lead, 125, Items.copper, 125, Items.silicon, 125, Items.titanium, 150, Items.plastanium, 100));
|
||||
mech = Mechs.trident;
|
||||
size = 2;
|
||||
consumes.power(1f);
|
||||
}};
|
||||
|
||||
glaivePad = new MechPad("glaive-ship-pad"){{
|
||||
requirements(Category.upgrade, ItemStack.with(Items.lead, 225, Items.silicon, 325, Items.titanium, 350, Items.plastanium, 300, Items.surgealloy, 100));
|
||||
mech = Mechs.glaive;
|
||||
size = 3;
|
||||
consumes.power(1.2f);
|
||||
}};
|
||||
|
||||
//endregion
|
||||
//region sandbox
|
||||
|
||||
@@ -1867,6 +1725,14 @@ public class Blocks implements ContentList{
|
||||
consumes.power(0.05f);
|
||||
}};
|
||||
|
||||
//endregion
|
||||
//region legacy
|
||||
|
||||
//looked up by name, no ref needed
|
||||
new LegacyMechPad("legacy-mech-pad");
|
||||
new LegacyUnitFactory("legacy-unit-factory");
|
||||
new LegacyCommandCenter("legacy-command-center");
|
||||
|
||||
//endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,11 +4,10 @@ import arc.graphics.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.math.*;
|
||||
import arc.util.*;
|
||||
import mindustry.ctype.ContentList;
|
||||
import mindustry.ctype.*;
|
||||
import mindustry.entities.*;
|
||||
import mindustry.entities.bullet.*;
|
||||
import mindustry.entities.effect.*;
|
||||
import mindustry.entities.type.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.world.*;
|
||||
|
||||
@@ -384,7 +383,7 @@ public class Bullets implements ContentList{
|
||||
}};
|
||||
|
||||
damageLightning = new BulletType(0.0001f, 0f){{
|
||||
lifetime = Lightning.lifetime;
|
||||
lifetime = Fx.lightning.lifetime;
|
||||
hitEffect = Fx.hitLancer;
|
||||
despawnEffect = Fx.none;
|
||||
status = StatusEffects.shocked;
|
||||
@@ -410,32 +409,32 @@ public class Bullets implements ContentList{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(Bullet b){
|
||||
b.velocity().setLength(0.6f + Mathf.random(2f));
|
||||
public void init(Bulletc b){
|
||||
b.vel().setLength(0.6f + Mathf.random(2f));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Bullet b){
|
||||
public void draw(Bulletc b){
|
||||
Draw.color(Pal.lightFlame, Pal.darkFlame, Color.gray, b.fin());
|
||||
Fill.circle(b.x, b.y, 3f * b.fout());
|
||||
Fill.circle(b.x(), b.y(), 3f * b.fout());
|
||||
Draw.reset();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(Bullet b){
|
||||
public void update(Bulletc b){
|
||||
if(Mathf.chance(0.04 * Time.delta())){
|
||||
Tile tile = world.tileWorld(b.x, b.y);
|
||||
Tile tile = world.tileWorld(b.x(), b.y());
|
||||
if(tile != null){
|
||||
Fire.create(tile);
|
||||
Fires.create(tile);
|
||||
}
|
||||
}
|
||||
|
||||
if(Mathf.chance(0.1 * Time.delta())){
|
||||
Effects.effect(Fx.fireballsmoke, b.x, b.y);
|
||||
Fx.fireballsmoke.at(b.x(), b.y());
|
||||
}
|
||||
|
||||
if(Mathf.chance(0.1 * Time.delta())){
|
||||
Effects.effect(Fx.ballfire, b.x, b.y);
|
||||
Fx.ballfire.at(b.x(), b.y());
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -452,6 +451,7 @@ public class Bullets implements ContentList{
|
||||
hitEffect = Fx.hitFlameSmall;
|
||||
despawnEffect = Fx.none;
|
||||
status = StatusEffects.burning;
|
||||
keepVelocity = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -460,7 +460,7 @@ public class Bullets implements ContentList{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Bullet b){
|
||||
public void draw(Bulletc b){
|
||||
}
|
||||
};
|
||||
|
||||
@@ -479,50 +479,17 @@ public class Bullets implements ContentList{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Bullet b){
|
||||
public void draw(Bulletc b){
|
||||
}
|
||||
};
|
||||
|
||||
lancerLaser = new BulletType(0.001f, 140){
|
||||
Color[] colors = {Pal.lancerLaser.cpy().mul(1f, 1f, 1f, 0.4f), Pal.lancerLaser, Color.white};
|
||||
float[] tscales = {1f, 0.7f, 0.5f, 0.2f};
|
||||
float[] lenscales = {1f, 1.1f, 1.13f, 1.14f};
|
||||
float length = 160f;
|
||||
|
||||
{
|
||||
hitEffect = Fx.hitLancer;
|
||||
despawnEffect = Fx.none;
|
||||
hitSize = 4;
|
||||
lifetime = 16f;
|
||||
pierce = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float range(){
|
||||
return length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(Bullet b){
|
||||
Damage.collideLine(b, b.getTeam(), hitEffect, b.x, b.y, b.rot(), length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Bullet b){
|
||||
float f = Mathf.curve(b.fin(), 0f, 0.2f);
|
||||
float baseLen = length * f;
|
||||
|
||||
Lines.lineAngle(b.x, b.y, b.rot(), baseLen);
|
||||
for(int s = 0; s < 3; s++){
|
||||
Draw.color(colors[s]);
|
||||
for(int i = 0; i < tscales.length; i++){
|
||||
Lines.stroke(7f * b.fout() * (s == 0 ? 1.5f : s == 1 ? 1f : 0.3f) * tscales[i]);
|
||||
Lines.lineAngle(b.x, b.y, b.rot(), baseLen * lenscales[i]);
|
||||
}
|
||||
}
|
||||
Draw.reset();
|
||||
}
|
||||
};
|
||||
lancerLaser = new LaserBulletType(140){{
|
||||
colors = new Color[]{Pal.lancerLaser.cpy().mul(1f, 1f, 1f, 0.4f), Pal.lancerLaser, Color.white};
|
||||
hitEffect = Fx.hitLancer;
|
||||
despawnEffect = Fx.none;
|
||||
hitSize = 4;
|
||||
lifetime = 16f;
|
||||
}};
|
||||
|
||||
meltdownLaser = new BulletType(0.001f, 70){
|
||||
Color tmpColor = new Color();
|
||||
@@ -542,32 +509,32 @@ public class Bullets implements ContentList{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(Bullet b){
|
||||
if(b.timer.get(1, 5f)){
|
||||
Damage.collideLine(b, b.getTeam(), hitEffect, b.x, b.y, b.rot(), length, true);
|
||||
public void update(Bulletc b){
|
||||
if(b.timer(1, 5f)){
|
||||
Damage.collideLine(b, b.team(), hitEffect, b.x(), b.y(), b.rotation(), length, true);
|
||||
}
|
||||
Effects.shake(1f, 1f, b.x, b.y);
|
||||
Effects.shake(1f, 1f, b.x(), b.y());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hit(Bullet b, float hitx, float hity){
|
||||
Effects.effect(hitEffect, colors[2], hitx, hity);
|
||||
public void hit(Bulletc b, float hitx, float hity){
|
||||
hitEffect.at(hitx, hity, colors[2]);
|
||||
if(Mathf.chance(0.4)){
|
||||
Fire.create(world.tileWorld(hitx + Mathf.range(5f), hity + Mathf.range(5f)));
|
||||
Fires.create(world.tileWorld(hitx + Mathf.range(5f), hity + Mathf.range(5f)));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Bullet b){
|
||||
public void draw(Bulletc b){
|
||||
float baseLen = (length) * b.fout();
|
||||
|
||||
Lines.lineAngle(b.x, b.y, b.rot(), baseLen);
|
||||
Lines.lineAngle(b.x(), b.y(), b.rotation(), baseLen);
|
||||
for(int s = 0; s < colors.length; s++){
|
||||
Draw.color(tmpColor.set(colors[s]).mul(1f + Mathf.absin(Time.time(), 1f, 0.1f)));
|
||||
for(int i = 0; i < tscales.length; i++){
|
||||
Tmp.v1.trns(b.rot() + 180f, (lenscales[i] - 1f) * 35f);
|
||||
Tmp.v1.trns(b.rotation() + 180f, (lenscales[i] - 1f) * 35f);
|
||||
Lines.stroke((9f + Mathf.absin(Time.time(), 0.8f, 1.5f)) * b.fout() * strokes[s] * tscales[i]);
|
||||
Lines.lineAngle(b.x + Tmp.v1.x, b.y + Tmp.v1.y, b.rot(), baseLen * lenscales[i], CapStyle.none);
|
||||
Lines.lineAngle(b.x() + Tmp.v1.x, b.y() + Tmp.v1.y, b.rotation(), baseLen * lenscales[i], CapStyle.none);
|
||||
}
|
||||
}
|
||||
Draw.reset();
|
||||
@@ -613,31 +580,20 @@ public class Bullets implements ContentList{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Bullet b){
|
||||
public void draw(Bulletc b){
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(Bullet b){
|
||||
Lightning.create(b.getTeam(), Pal.lancerLaser, damage * (b.getOwner() instanceof Player ? state.rules.playerDamageMultiplier : 1f), b.x, b.y, b.rot(), 30);
|
||||
public void init(Bulletc b){
|
||||
//TODO owners are never players...
|
||||
Lightning.create(b.team(), Pal.lancerLaser, damage * (b.owner() instanceof Playerc ? state.rules.playerDamageMultiplier : 1f), b.x(), b.y(), b.rotation(), 30);
|
||||
}
|
||||
};
|
||||
|
||||
arc = new BulletType(0.001f, 21){
|
||||
{
|
||||
lifetime = 1;
|
||||
despawnEffect = Fx.none;
|
||||
hitEffect = Fx.hitLancer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Bullet b){
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(Bullet b){
|
||||
Lightning.create(b.getTeam(), Pal.lancerLaser, damage, b.x, b.y, b.rot(), 25);
|
||||
}
|
||||
};
|
||||
arc = new LightningBulletType(){{
|
||||
damage = 21;
|
||||
lightningLength = 25;
|
||||
}};
|
||||
|
||||
driverBolt = new MassDriverBolt();
|
||||
|
||||
@@ -678,12 +634,12 @@ public class Bullets implements ContentList{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hit(Bullet b, float x, float y){
|
||||
public void hit(Bulletc b, float x, float y){
|
||||
super.hit(b, x, y);
|
||||
|
||||
for(int i = 0; i < 3; i++){
|
||||
Tile tile = world.tileWorld(x + Mathf.range(8f), y + Mathf.range(8f));
|
||||
Puddle.deposit(tile, Liquids.oil, 5f);
|
||||
Puddles.deposit(tile, Liquids.oil, 5f);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,378 +0,0 @@
|
||||
package mindustry.content;
|
||||
|
||||
import arc.*;
|
||||
import arc.graphics.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.math.*;
|
||||
import arc.util.*;
|
||||
import mindustry.*;
|
||||
import mindustry.ctype.ContentList;
|
||||
import mindustry.entities.*;
|
||||
import mindustry.entities.bullet.*;
|
||||
import mindustry.entities.effect.*;
|
||||
import mindustry.entities.type.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.type.*;
|
||||
|
||||
public class Mechs implements ContentList{
|
||||
public static Mech alpha, delta, tau, omega, dart, javelin, trident, glaive;
|
||||
|
||||
public static Mech starter;
|
||||
|
||||
@Override
|
||||
public void load(){
|
||||
|
||||
alpha = new Mech("alpha-mech", false){
|
||||
{
|
||||
drillPower = 1;
|
||||
mineSpeed = 1.5f;
|
||||
mass = 1.2f;
|
||||
speed = 0.5f;
|
||||
itemCapacity = 40;
|
||||
boostSpeed = 0.95f;
|
||||
buildPower = 1.2f;
|
||||
engineColor = Color.valueOf("ffd37f");
|
||||
health = 250f;
|
||||
|
||||
weapon = new Weapon("blaster"){{
|
||||
length = 1.5f;
|
||||
reload = 14f;
|
||||
alternate = true;
|
||||
ejectEffect = Fx.shellEjectSmall;
|
||||
bullet = Bullets.standardMechSmall;
|
||||
}};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateAlt(Player player){
|
||||
player.healBy(Time.delta() * 0.09f);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
delta = new Mech("delta-mech", false){
|
||||
float cooldown = 120;
|
||||
|
||||
{
|
||||
drillPower = -1;
|
||||
speed = 0.75f;
|
||||
boostSpeed = 0.95f;
|
||||
itemCapacity = 15;
|
||||
mass = 0.9f;
|
||||
health = 150f;
|
||||
buildPower = 0.9f;
|
||||
weaponOffsetX = -1;
|
||||
weaponOffsetY = -1;
|
||||
engineColor = Color.valueOf("d3ddff");
|
||||
|
||||
weapon = new Weapon("shockgun"){{
|
||||
shake = 2f;
|
||||
length = 1f;
|
||||
reload = 55f;
|
||||
shotDelay = 3f;
|
||||
alternate = true;
|
||||
shots = 2;
|
||||
inaccuracy = 0f;
|
||||
ejectEffect = Fx.none;
|
||||
bullet = Bullets.lightning;
|
||||
shootSound = Sounds.spark;
|
||||
}};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLand(Player player){
|
||||
if(player.timer.get(Player.timerAbility, cooldown)){
|
||||
Effects.shake(1f, 1f, player);
|
||||
Effects.effect(Fx.landShock, player);
|
||||
for(int i = 0; i < 8; i++){
|
||||
Time.run(Mathf.random(8f), () -> Lightning.create(player.getTeam(), Pal.lancerLaser, 17f * Vars.state.rules.playerDamageMultiplier, player.x, player.y, Mathf.random(360f), 14));
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
tau = new Mech("tau-mech", false){
|
||||
float healRange = 60f;
|
||||
float healAmount = 10f;
|
||||
float healReload = 160f;
|
||||
boolean wasHealed;
|
||||
|
||||
{
|
||||
drillPower = 4;
|
||||
mineSpeed = 3f;
|
||||
itemCapacity = 70;
|
||||
weaponOffsetY = -1;
|
||||
weaponOffsetX = 1;
|
||||
mass = 1.75f;
|
||||
speed = 0.44f;
|
||||
drag = 0.35f;
|
||||
boostSpeed = 0.8f;
|
||||
canHeal = true;
|
||||
health = 200f;
|
||||
buildPower = 1.6f;
|
||||
engineColor = Pal.heal;
|
||||
|
||||
weapon = new Weapon("heal-blaster"){{
|
||||
length = 1.5f;
|
||||
reload = 24f;
|
||||
alternate = false;
|
||||
ejectEffect = Fx.none;
|
||||
recoil = 2f;
|
||||
bullet = Bullets.healBullet;
|
||||
shootSound = Sounds.pew;
|
||||
}};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateAlt(Player player){
|
||||
|
||||
if(player.timer.get(Player.timerAbility, healReload)){
|
||||
wasHealed = false;
|
||||
|
||||
Units.nearby(player.getTeam(), player.x, player.y, healRange, unit -> {
|
||||
if(unit.health < unit.maxHealth()){
|
||||
Effects.effect(Fx.heal, unit);
|
||||
wasHealed = true;
|
||||
}
|
||||
unit.healBy(healAmount);
|
||||
});
|
||||
|
||||
if(wasHealed){
|
||||
Effects.effect(Fx.healWave, player);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
omega = new Mech("omega-mech", false){
|
||||
protected TextureRegion armorRegion;
|
||||
|
||||
{
|
||||
drillPower = 2;
|
||||
mineSpeed = 1.5f;
|
||||
itemCapacity = 80;
|
||||
speed = 0.36f;
|
||||
boostSpeed = 0.6f;
|
||||
mass = 4f;
|
||||
shake = 4f;
|
||||
weaponOffsetX = 1;
|
||||
weaponOffsetY = 0;
|
||||
engineColor = Color.valueOf("feb380");
|
||||
health = 350f;
|
||||
buildPower = 1.5f;
|
||||
weapon = new Weapon("swarmer"){{
|
||||
length = 1.5f;
|
||||
recoil = 4f;
|
||||
reload = 38f;
|
||||
shots = 4;
|
||||
spacing = 8f;
|
||||
inaccuracy = 8f;
|
||||
alternate = true;
|
||||
ejectEffect = Fx.none;
|
||||
shake = 3f;
|
||||
bullet = Bullets.missileSwarm;
|
||||
shootSound = Sounds.shootBig;
|
||||
}};
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getRotationAlpha(Player player){
|
||||
return 0.6f - player.shootHeat * 0.3f;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float spreadX(Player player){
|
||||
return player.shootHeat * 2f;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load(){
|
||||
super.load();
|
||||
armorRegion = Core.atlas.find(name + "-armor");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateAlt(Player player){
|
||||
float scl = 1f - player.shootHeat / 2f*Time.delta();
|
||||
player.velocity().scl(scl);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getExtraArmor(Player player){
|
||||
return player.shootHeat * 30f;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Player player){
|
||||
if(player.shootHeat <= 0.01f) return;
|
||||
|
||||
Shaders.build.progress = player.shootHeat;
|
||||
Shaders.build.region = armorRegion;
|
||||
Shaders.build.time = Time.time() / 10f;
|
||||
Shaders.build.color.set(Pal.accent).a = player.shootHeat;
|
||||
Draw.shader(Shaders.build);
|
||||
Draw.rect(armorRegion, player.x, player.y, player.rotation);
|
||||
Draw.shader();
|
||||
}
|
||||
};
|
||||
|
||||
dart = new Mech("dart-ship", true){
|
||||
{
|
||||
drillPower = 1;
|
||||
mineSpeed = 3f;
|
||||
speed = 0.5f;
|
||||
drag = 0.09f;
|
||||
health = 200f;
|
||||
weaponOffsetX = -1;
|
||||
weaponOffsetY = -1;
|
||||
engineColor = Pal.lightTrail;
|
||||
cellTrnsY = 1f;
|
||||
buildPower = 1.1f;
|
||||
weapon = new Weapon("blaster"){{
|
||||
length = 1.5f;
|
||||
reload = 15f;
|
||||
alternate = true;
|
||||
ejectEffect = Fx.shellEjectSmall;
|
||||
bullet = Bullets.standardCopper;
|
||||
}};
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean alwaysUnlocked(){
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
javelin = new Mech("javelin-ship", true){
|
||||
float minV = 3.6f;
|
||||
float maxV = 6f;
|
||||
TextureRegion shield;
|
||||
|
||||
{
|
||||
drillPower = -1;
|
||||
speed = 0.11f;
|
||||
drag = 0.01f;
|
||||
mass = 2f;
|
||||
health = 170f;
|
||||
engineColor = Color.valueOf("d3ddff");
|
||||
cellTrnsY = 1f;
|
||||
weapon = new Weapon("missiles"){{
|
||||
length = 1.5f;
|
||||
reload = 70f;
|
||||
shots = 4;
|
||||
inaccuracy = 2f;
|
||||
alternate = true;
|
||||
ejectEffect = Fx.none;
|
||||
velocityRnd = 0.2f;
|
||||
spacing = 1f;
|
||||
bullet = Bullets.missileJavelin;
|
||||
shootSound = Sounds.missile;
|
||||
}};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load(){
|
||||
super.load();
|
||||
shield = Core.atlas.find(name + "-shield");
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getRotationAlpha(Player player){
|
||||
return 0.5f;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateAlt(Player player){
|
||||
float scl = scld(player);
|
||||
if(Mathf.chance(Time.delta() * (0.15 * scl))){
|
||||
Effects.effect(Fx.hitLancer, Pal.lancerLaser, player.x, player.y);
|
||||
Lightning.create(player.getTeam(), Pal.lancerLaser, 10f * Vars.state.rules.playerDamageMultiplier,
|
||||
player.x + player.velocity().x, player.y + player.velocity().y, player.rotation, 14);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Player player){
|
||||
float scl = scld(player);
|
||||
if(scl < 0.01f) return;
|
||||
Draw.color(Pal.lancerLaser);
|
||||
Draw.alpha(scl / 2f);
|
||||
Draw.blend(Blending.additive);
|
||||
Draw.rect(shield, player.x + Mathf.range(scl / 2f), player.y + Mathf.range(scl / 2f), player.rotation - 90);
|
||||
Draw.blend();
|
||||
}
|
||||
|
||||
float scld(Player player){
|
||||
return Mathf.clamp((player.velocity().len() - minV) / (maxV - minV));
|
||||
}
|
||||
};
|
||||
|
||||
trident = new Mech("trident-ship", true){
|
||||
{
|
||||
drillPower = 2;
|
||||
speed = 0.15f;
|
||||
drag = 0.034f;
|
||||
mass = 2.5f;
|
||||
turnCursor = false;
|
||||
health = 250f;
|
||||
itemCapacity = 30;
|
||||
engineColor = Color.valueOf("84f491");
|
||||
cellTrnsY = 1f;
|
||||
buildPower = 2.5f;
|
||||
weapon = new Weapon("bomber"){{
|
||||
length = 0f;
|
||||
width = 2f;
|
||||
reload = 25f;
|
||||
shots = 2;
|
||||
shotDelay = 1f;
|
||||
shots = 8;
|
||||
alternate = true;
|
||||
ejectEffect = Fx.none;
|
||||
velocityRnd = 1f;
|
||||
inaccuracy = 20f;
|
||||
ignoreRotation = true;
|
||||
bullet = new BombBulletType(16f, 25f, "shell"){{
|
||||
bulletWidth = 10f;
|
||||
bulletHeight = 14f;
|
||||
hitEffect = Fx.flakExplosion;
|
||||
shootEffect = Fx.none;
|
||||
smokeEffect = Fx.none;
|
||||
shootSound = Sounds.artillery;
|
||||
}};
|
||||
}};
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canShoot(Player player){
|
||||
return player.velocity().len() > 1.2f;
|
||||
}
|
||||
};
|
||||
|
||||
glaive = new Mech("glaive-ship", true){
|
||||
{
|
||||
drillPower = 4;
|
||||
mineSpeed = 1.3f;
|
||||
speed = 0.32f;
|
||||
drag = 0.06f;
|
||||
mass = 3f;
|
||||
health = 240f;
|
||||
itemCapacity = 60;
|
||||
engineColor = Color.valueOf("feb380");
|
||||
cellTrnsY = 1f;
|
||||
buildPower = 1.2f;
|
||||
|
||||
weapon = new Weapon("bomber"){{
|
||||
length = 1.5f;
|
||||
reload = 13f;
|
||||
alternate = true;
|
||||
ejectEffect = Fx.shellEjectSmall;
|
||||
bullet = Bullets.standardGlaive;
|
||||
shootSound = Sounds.shootSnap;
|
||||
}};
|
||||
}
|
||||
};
|
||||
|
||||
starter = dart;
|
||||
}
|
||||
}
|
||||
45
core/src/mindustry/content/Planets.java
Normal file
45
core/src/mindustry/content/Planets.java
Normal file
@@ -0,0 +1,45 @@
|
||||
package mindustry.content;
|
||||
|
||||
import arc.graphics.*;
|
||||
import mindustry.ctype.*;
|
||||
import mindustry.graphics.g3d.*;
|
||||
import mindustry.maps.planet.*;
|
||||
import mindustry.type.*;
|
||||
|
||||
public class Planets implements ContentList{
|
||||
public static Planet
|
||||
sun,
|
||||
starter;
|
||||
|
||||
@Override
|
||||
public void load(){
|
||||
sun = new Planet("sun", null, 0, 2){{
|
||||
bloom = true;
|
||||
//lightColor = Color.valueOf("f4ee8e");
|
||||
meshLoader = () -> new SunMesh(this, 3){{
|
||||
setColors(
|
||||
1.1f,
|
||||
Color.valueOf("ff7a38"),
|
||||
Color.valueOf("ff9638"),
|
||||
Color.valueOf("ffc64c"),
|
||||
Color.valueOf("ffc64c"),
|
||||
Color.valueOf("ffe371"),
|
||||
Color.valueOf("f4ee8e")
|
||||
);
|
||||
|
||||
scale = 1f;
|
||||
speed = 1000f;
|
||||
falloff = 0.3f;
|
||||
octaves = 4;
|
||||
spread = 1.2f;
|
||||
magnitude = 0f;
|
||||
}};
|
||||
}};
|
||||
|
||||
starter = new Planet("TODO", sun, 3, 1){{
|
||||
generator = new TODOPlanetGenerator();
|
||||
meshLoader = () -> new HexMesh(this, 6);
|
||||
atmosphereColor = Color.valueOf("3c1b8f");
|
||||
}};
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
package mindustry.content;
|
||||
|
||||
import arc.*;
|
||||
import arc.graphics.*;
|
||||
import arc.math.Mathf;
|
||||
import mindustry.entities.Effects;
|
||||
import mindustry.ctype.ContentList;
|
||||
import mindustry.game.EventType.*;
|
||||
import mindustry.type.StatusEffect;
|
||||
@@ -24,7 +24,7 @@ public class StatusEffects implements ContentList{
|
||||
opposite(wet,freezing);
|
||||
trans(tarred, ((unit, time, newTime, result) -> {
|
||||
unit.damage(1f);
|
||||
Effects.effect(Fx.burning, unit.x + Mathf.range(unit.getSize() / 2f), unit.y + Mathf.range(unit.getSize() / 2f));
|
||||
Fx.burning.at(unit.x() + Mathf.range(unit.bounds() / 2f), unit.y() + Mathf.range(unit.bounds() / 2f));
|
||||
result.set(this, Math.min(time + newTime, 300f));
|
||||
}));
|
||||
});
|
||||
@@ -41,13 +41,14 @@ public class StatusEffects implements ContentList{
|
||||
}};
|
||||
|
||||
wet = new StatusEffect("wet"){{
|
||||
color = Color.royal;
|
||||
speedMultiplier = 0.9f;
|
||||
effect = Fx.wet;
|
||||
|
||||
init(() -> {
|
||||
trans(shocked, ((unit, time, newTime, result) -> {
|
||||
unit.damage(20f);
|
||||
if(unit.getTeam() == state.rules.waveTeam){
|
||||
if(unit.team() == state.rules.waveTeam){
|
||||
Events.fire(Trigger.shock);
|
||||
}
|
||||
result.set(this, time);
|
||||
|
||||
@@ -280,6 +280,7 @@ public class TechTree implements ContentList{
|
||||
});
|
||||
});
|
||||
|
||||
/*
|
||||
node(draugFactory, () -> {
|
||||
node(spiritFactory, () -> {
|
||||
node(phantomFactory);
|
||||
@@ -305,6 +306,7 @@ public class TechTree implements ContentList{
|
||||
});
|
||||
});
|
||||
|
||||
/*
|
||||
node(dartPad, () -> {
|
||||
node(deltaPad, () -> {
|
||||
|
||||
@@ -320,7 +322,7 @@ public class TechTree implements ContentList{
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});*/
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -347,6 +349,7 @@ public class TechTree implements ContentList{
|
||||
public static class TechNode{
|
||||
static TechNode context;
|
||||
|
||||
public TechNode parent;
|
||||
public final Block block;
|
||||
public final ItemStack[] requirements;
|
||||
public final Array<TechNode> children = new Array<>();
|
||||
@@ -356,6 +359,7 @@ public class TechTree implements ContentList{
|
||||
ccontext.children.add(this);
|
||||
}
|
||||
|
||||
this.parent = ccontext;
|
||||
this.block = block;
|
||||
this.requirements = requirements;
|
||||
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
package mindustry.content;
|
||||
|
||||
import mindustry.entities.effect.Fire;
|
||||
import mindustry.entities.effect.Puddle;
|
||||
import mindustry.entities.type.Player;
|
||||
import mindustry.ctype.ContentList;
|
||||
import mindustry.type.TypeID;
|
||||
|
||||
public class TypeIDs implements ContentList{
|
||||
public static TypeID fire, puddle, player;
|
||||
|
||||
@Override
|
||||
public void load(){
|
||||
fire = new TypeID("fire", Fire::new);
|
||||
puddle = new TypeID("puddle", Puddle::new);
|
||||
player = new TypeID("player", Player::new);
|
||||
}
|
||||
}
|
||||
@@ -1,95 +1,154 @@
|
||||
package mindustry.content;
|
||||
|
||||
import arc.struct.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.ctype.*;
|
||||
import mindustry.entities.bullet.*;
|
||||
import mindustry.entities.type.base.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.type.*;
|
||||
|
||||
public class UnitTypes implements ContentList{
|
||||
//TODO reimplement - DO NOT USE
|
||||
public static UnitType
|
||||
draug, spirit, phantom,
|
||||
wraith, ghoul, revenant, lich, reaper,
|
||||
dagger, crawler, titan, fortress, eruptor, chaosArray, eradicator;
|
||||
ghoul, revenant, lich,
|
||||
crawler, titan, fortress, eruptor, chaosArray, eradicator;
|
||||
|
||||
public static @EntityDef({Unitc.class, Legsc.class}) UnitType dagger;
|
||||
public static @EntityDef({Unitc.class, WaterMovec.class}) UnitType vanguard;
|
||||
public static @EntityDef({Unitc.class, Minerc.class}) UnitType draug;
|
||||
public static @EntityDef({Unitc.class}) UnitType wraith;
|
||||
public static @EntityDef({Unitc.class}) UnitType reaper;
|
||||
public static @EntityDef({Unitc.class}) UnitType spirit;
|
||||
public static @EntityDef({Unitc.class, Builderc.class}) UnitType phantom;
|
||||
|
||||
//TODO remove
|
||||
public static UnitType alpha, delta, tau, omega, dart, javelin, trident, glaive;
|
||||
public static UnitType starter;
|
||||
|
||||
|
||||
@Override
|
||||
public void load(){
|
||||
draug = new UnitType("draug", MinerDrone::new){{
|
||||
flying = true;
|
||||
drag = 0.01f;
|
||||
speed = 0.3f;
|
||||
maxVelocity = 1.2f;
|
||||
range = 50f;
|
||||
health = 80;
|
||||
minePower = 0.9f;
|
||||
engineSize = 1.8f;
|
||||
engineOffset = 5.7f;
|
||||
weapon = new Weapon("you have incurred my wrath. prepare to die."){{
|
||||
bullet = Bullets.lancerLaser;
|
||||
}};
|
||||
|
||||
dagger = new UnitType("dagger"){{
|
||||
speed = 0.5f;
|
||||
drag = 0.3f;
|
||||
hitsize = 8f;
|
||||
mass = 1.75f;
|
||||
health = 130;
|
||||
weapons.add(new Weapon("chain-blaster"){{
|
||||
reload = 14f;
|
||||
x = 4f;
|
||||
alternate = true;
|
||||
ejectEffect = Fx.shellEjectSmall;
|
||||
bullet = Bullets.standardCopper;
|
||||
}});
|
||||
}};
|
||||
|
||||
spirit = new UnitType("spirit", RepairDrone::new){{
|
||||
wraith = new UnitType("wraith"){{
|
||||
speed = 3f;
|
||||
accel = 0.08f;
|
||||
drag = 0f;
|
||||
mass = 1.5f;
|
||||
flying = true;
|
||||
drag = 0.01f;
|
||||
speed = 0.42f;
|
||||
maxVelocity = 1.6f;
|
||||
health = 75;
|
||||
engineOffset = 5.5f;
|
||||
range = 140f;
|
||||
weapons.add(new Weapon(){{
|
||||
y = 1.5f;
|
||||
reload = 28f;
|
||||
alternate = true;
|
||||
ejectEffect = Fx.shellEjectSmall;
|
||||
bullet = Bullets.standardCopper;
|
||||
shootSound = Sounds.shoot;
|
||||
}});
|
||||
}};
|
||||
|
||||
reaper = new UnitType("reaper"){{
|
||||
speed = 1f;
|
||||
accel = 0.08f;
|
||||
drag = 0f;
|
||||
mass = 2f;
|
||||
flying = true;
|
||||
health = 75000;
|
||||
engineOffset = 40;
|
||||
engineSize = 7.3f;
|
||||
|
||||
weapons.add(new Weapon(){{
|
||||
y = 1.5f;
|
||||
reload = 28f;
|
||||
alternate = true;
|
||||
ejectEffect = Fx.shellEjectSmall;
|
||||
bullet = Bullets.standardCopper;
|
||||
shootSound = Sounds.shoot;
|
||||
}});
|
||||
}};
|
||||
|
||||
|
||||
vanguard = new UnitType("vanguard"){{
|
||||
speed = 1.3f;
|
||||
drag = 0.1f;
|
||||
hitsize = 8f;
|
||||
mass = 1.75f;
|
||||
health = 130;
|
||||
immunities = ObjectSet.with(StatusEffects.wet);
|
||||
weapons.add(new Weapon("chain-blaster"){{
|
||||
reload = 10f;
|
||||
x = 1.25f;
|
||||
alternate = true;
|
||||
rotate = true;
|
||||
ejectEffect = Fx.shellEjectSmall;
|
||||
bullet = Bullets.standardCopper;
|
||||
}});
|
||||
}};
|
||||
|
||||
draug = new UnitType("draug"){{
|
||||
flying = true;
|
||||
drag = 0.05f;
|
||||
speed = 2f;
|
||||
range = 50f;
|
||||
accel = 0.2f;
|
||||
health = 80;
|
||||
mineSpeed = 0.9f;
|
||||
engineSize = 1.8f;
|
||||
engineOffset = 5.7f;
|
||||
drillTier = 1;
|
||||
}};
|
||||
|
||||
spirit = new UnitType("spirit"){{
|
||||
flying = true;
|
||||
drag = 0.05f;
|
||||
accel = 0.2f;
|
||||
speed = 2f;
|
||||
range = 50f;
|
||||
health = 100;
|
||||
engineSize = 1.8f;
|
||||
engineOffset = 5.7f;
|
||||
weapon = new Weapon(){{
|
||||
length = 1.5f;
|
||||
weapons.add(new Weapon(){{
|
||||
y = 1.5f;
|
||||
reload = 40f;
|
||||
width = 0.5f;
|
||||
x = 0.5f;
|
||||
alternate = true;
|
||||
ejectEffect = Fx.none;
|
||||
recoil = 2f;
|
||||
bullet = Bullets.healBulletBig;
|
||||
shootSound = Sounds.pew;
|
||||
}};
|
||||
}});
|
||||
}};
|
||||
|
||||
phantom = new UnitType("phantom", BuilderDrone::new){{
|
||||
phantom = new UnitType("phantom"){{
|
||||
flying = true;
|
||||
drag = 0.01f;
|
||||
drag = 0.05f;
|
||||
mass = 2f;
|
||||
speed = 0.45f;
|
||||
maxVelocity = 1.9f;
|
||||
speed = 4f;
|
||||
rotateSpeed = 12f;
|
||||
accel = 0.3f;
|
||||
range = 70f;
|
||||
itemCapacity = 70;
|
||||
health = 400;
|
||||
buildPower = 0.4f;
|
||||
buildSpeed = 0.4f;
|
||||
engineOffset = 6.5f;
|
||||
toMine = ObjectSet.with(Items.lead, Items.copper, Items.titanium);
|
||||
weapon = new Weapon(){{
|
||||
length = 1.5f;
|
||||
reload = 20f;
|
||||
width = 0.5f;
|
||||
alternate = true;
|
||||
ejectEffect = Fx.none;
|
||||
recoil = 2f;
|
||||
bullet = Bullets.healBullet;
|
||||
}};
|
||||
}};
|
||||
|
||||
dagger = new UnitType("dagger", GroundUnit::new){{
|
||||
maxVelocity = 1.1f;
|
||||
speed = 0.2f;
|
||||
drag = 0.4f;
|
||||
hitsize = 8f;
|
||||
mass = 1.75f;
|
||||
health = 130;
|
||||
weapon = new Weapon("chain-blaster"){{
|
||||
length = 1.5f;
|
||||
reload = 28f;
|
||||
alternate = true;
|
||||
ejectEffect = Fx.shellEjectSmall;
|
||||
bullet = Bullets.standardCopper;
|
||||
}};
|
||||
}};
|
||||
|
||||
|
||||
/*
|
||||
crawler = new UnitType("crawler", GroundUnit::new){{
|
||||
maxVelocity = 1.27f;
|
||||
speed = 0.285f;
|
||||
@@ -97,7 +156,7 @@ public class UnitTypes implements ContentList{
|
||||
hitsize = 8f;
|
||||
mass = 1.75f;
|
||||
health = 120;
|
||||
weapon = new Weapon(){{
|
||||
weapons.add(new Weapon(){{
|
||||
reload = 12f;
|
||||
ejectEffect = Fx.none;
|
||||
shootSound = Sounds.explosion;
|
||||
@@ -110,7 +169,7 @@ public class UnitTypes implements ContentList{
|
||||
splashDamage = 30f;
|
||||
killShooter = true;
|
||||
}};
|
||||
}};
|
||||
}});
|
||||
}};
|
||||
|
||||
titan = new UnitType("titan", GroundUnit::new){{
|
||||
@@ -123,7 +182,7 @@ public class UnitTypes implements ContentList{
|
||||
rotatespeed = 0.1f;
|
||||
health = 460;
|
||||
immunities.add(StatusEffects.burning);
|
||||
weapon = new Weapon("flamethrower"){{
|
||||
weapons.add(new Weapon("flamethrower"){{
|
||||
shootSound = Sounds.flame;
|
||||
length = 1f;
|
||||
reload = 14f;
|
||||
@@ -131,7 +190,7 @@ public class UnitTypes implements ContentList{
|
||||
recoil = 1f;
|
||||
ejectEffect = Fx.none;
|
||||
bullet = Bullets.basicFlame;
|
||||
}};
|
||||
}});
|
||||
}};
|
||||
|
||||
fortress = new UnitType("fortress", GroundUnit::new){{
|
||||
@@ -143,7 +202,7 @@ public class UnitTypes implements ContentList{
|
||||
rotatespeed = 0.06f;
|
||||
targetAir = false;
|
||||
health = 750;
|
||||
weapon = new Weapon("artillery"){{
|
||||
weapons.add(new Weapon("artillery"){{
|
||||
length = 1f;
|
||||
reload = 60f;
|
||||
width = 10f;
|
||||
@@ -153,7 +212,7 @@ public class UnitTypes implements ContentList{
|
||||
ejectEffect = Fx.shellEjectMedium;
|
||||
bullet = Bullets.artilleryUnit;
|
||||
shootSound = Sounds.artillery;
|
||||
}};
|
||||
}});
|
||||
}};
|
||||
|
||||
eruptor = new UnitType("eruptor", GroundUnit::new){{
|
||||
@@ -166,7 +225,7 @@ public class UnitTypes implements ContentList{
|
||||
targetAir = false;
|
||||
health = 600;
|
||||
immunities = ObjectSet.with(StatusEffects.burning, StatusEffects.melting);
|
||||
weapon = new Weapon("eruption"){{
|
||||
weapons.add(new Weapon("eruption"){{
|
||||
length = 3f;
|
||||
reload = 10f;
|
||||
alternate = true;
|
||||
@@ -175,7 +234,7 @@ public class UnitTypes implements ContentList{
|
||||
recoil = 1f;
|
||||
width = 7f;
|
||||
shootSound = Sounds.flame;
|
||||
}};
|
||||
}});
|
||||
}};
|
||||
|
||||
chaosArray = new UnitType("chaos-array", GroundUnit::new){{
|
||||
@@ -186,7 +245,7 @@ public class UnitTypes implements ContentList{
|
||||
hitsize = 20f;
|
||||
rotatespeed = 0.06f;
|
||||
health = 3000;
|
||||
weapon = new Weapon("chaos"){{
|
||||
weapons.add(new Weapon("chaos"){{
|
||||
length = 8f;
|
||||
reload = 50f;
|
||||
width = 17f;
|
||||
@@ -199,7 +258,7 @@ public class UnitTypes implements ContentList{
|
||||
ejectEffect = Fx.shellEjectMedium;
|
||||
bullet = Bullets.flakSurge;
|
||||
shootSound = Sounds.shootBig;
|
||||
}};
|
||||
}});
|
||||
}};
|
||||
|
||||
eradicator = new UnitType("eradicator", GroundUnit::new){{
|
||||
@@ -210,7 +269,7 @@ public class UnitTypes implements ContentList{
|
||||
hitsize = 20f;
|
||||
rotatespeed = 0.06f;
|
||||
health = 9000;
|
||||
weapon = new Weapon("eradication"){{
|
||||
weapons.add(new Weapon("eradication"){{
|
||||
length = 13f;
|
||||
reload = 30f;
|
||||
width = 22f;
|
||||
@@ -224,7 +283,7 @@ public class UnitTypes implements ContentList{
|
||||
ejectEffect = Fx.shellEjectMedium;
|
||||
bullet = Bullets.standardThoriumBig;
|
||||
shootSound = Sounds.shootBig;
|
||||
}};
|
||||
}});
|
||||
}};
|
||||
|
||||
wraith = new UnitType("wraith", FlyingUnit::new){{
|
||||
@@ -236,14 +295,14 @@ public class UnitTypes implements ContentList{
|
||||
health = 75;
|
||||
engineOffset = 5.5f;
|
||||
range = 140f;
|
||||
weapon = new Weapon(){{
|
||||
weapons.add(new Weapon(){{
|
||||
length = 1.5f;
|
||||
reload = 28f;
|
||||
alternate = true;
|
||||
ejectEffect = Fx.shellEjectSmall;
|
||||
bullet = Bullets.standardCopper;
|
||||
shootSound = Sounds.shoot;
|
||||
}};
|
||||
}});
|
||||
}};
|
||||
|
||||
ghoul = new UnitType("ghoul", FlyingUnit::new){{
|
||||
@@ -256,7 +315,7 @@ public class UnitTypes implements ContentList{
|
||||
targetAir = false;
|
||||
engineOffset = 7.8f;
|
||||
range = 140f;
|
||||
weapon = new Weapon(){{
|
||||
weapons.add(new Weapon(){{
|
||||
length = 0f;
|
||||
width = 2f;
|
||||
reload = 12f;
|
||||
@@ -267,7 +326,7 @@ public class UnitTypes implements ContentList{
|
||||
ignoreRotation = true;
|
||||
bullet = Bullets.bombExplosive;
|
||||
shootSound = Sounds.none;
|
||||
}};
|
||||
}});
|
||||
}};
|
||||
|
||||
revenant = new UnitType("revenant", HoverUnit::new){{
|
||||
@@ -280,13 +339,13 @@ public class UnitTypes implements ContentList{
|
||||
range = 80f;
|
||||
shootCone = 40f;
|
||||
flying = true;
|
||||
rotateWeapon = true;
|
||||
//rotateWeapons = true;
|
||||
engineOffset = 12f;
|
||||
engineSize = 3f;
|
||||
rotatespeed = 0.01f;
|
||||
attackLength = 90f;
|
||||
baseRotateSpeed = 0.06f;
|
||||
weapon = new Weapon("revenant-missiles"){{
|
||||
weapons.add(new Weapon("revenant-missiles"){{
|
||||
length = 3f;
|
||||
reload = 70f;
|
||||
width = 10f;
|
||||
@@ -298,7 +357,7 @@ public class UnitTypes implements ContentList{
|
||||
spacing = 1f;
|
||||
shootSound = Sounds.missile;
|
||||
bullet = Bullets.missileRevenant;
|
||||
}};
|
||||
}});
|
||||
}};
|
||||
|
||||
lich = new UnitType("lich", HoverUnit::new){{
|
||||
@@ -311,13 +370,13 @@ public class UnitTypes implements ContentList{
|
||||
range = 80f;
|
||||
shootCone = 20f;
|
||||
flying = true;
|
||||
rotateWeapon = true;
|
||||
//rotateWeapons = true;
|
||||
engineOffset = 21;
|
||||
engineSize = 5.3f;
|
||||
rotatespeed = 0.01f;
|
||||
attackLength = 90f;
|
||||
baseRotateSpeed = 0.04f;
|
||||
weapon = new Weapon("lich-missiles"){{
|
||||
weapons.add(new Weapon("lich-missiles"){{
|
||||
length = 4f;
|
||||
reload = 160f;
|
||||
width = 22f;
|
||||
@@ -331,7 +390,7 @@ public class UnitTypes implements ContentList{
|
||||
spacing = 1f;
|
||||
bullet = Bullets.missileRevenant;
|
||||
shootSound = Sounds.artillery;
|
||||
}};
|
||||
}});
|
||||
}};
|
||||
|
||||
reaper = new UnitType("reaper", HoverUnit::new){{
|
||||
@@ -344,12 +403,12 @@ public class UnitTypes implements ContentList{
|
||||
range = 80f;
|
||||
shootCone = 30f;
|
||||
flying = true;
|
||||
rotateWeapon = true;
|
||||
//rotateWeapons = true;
|
||||
engineOffset = 40;
|
||||
engineSize = 7.3f;
|
||||
rotatespeed = 0.01f;
|
||||
baseRotateSpeed = 0.04f;
|
||||
weapon = new Weapon("reaper-gun"){{
|
||||
weapons.add(new Weapon("reaper-gun"){{
|
||||
length = 3f;
|
||||
reload = 10f;
|
||||
width = 32f;
|
||||
@@ -373,7 +432,442 @@ public class UnitTypes implements ContentList{
|
||||
}
|
||||
};
|
||||
shootSound = Sounds.shootBig;
|
||||
}};
|
||||
}});
|
||||
}};
|
||||
|
||||
|
||||
/*
|
||||
vanguard = new UnitType("vanguard-ship"){
|
||||
float healRange = 60f;
|
||||
float healReload = 200f;
|
||||
float healPercent = 10f;
|
||||
|
||||
{
|
||||
flying = true;
|
||||
drillTier = 1;
|
||||
mineSpeed = 4f;
|
||||
speed = 0.49f;
|
||||
drag = 0.09f;
|
||||
health = 200f;
|
||||
weaponOffsetX = -1;
|
||||
engineSize = 2.3f;
|
||||
weaponOffsetY = -1;
|
||||
engineColor = Pal.lightTrail;
|
||||
cellTrnsY = 1f;
|
||||
buildSpeed = 1.2f;
|
||||
weapons.add(new Weapon("vanguard-blaster"){{
|
||||
length = 1.5f;
|
||||
reload = 30f;
|
||||
alternate = true;
|
||||
inaccuracy = 6f;
|
||||
velocityRnd = 0.1f;
|
||||
ejectEffect = Fx.none;
|
||||
bullet = new HealBulletType(){{
|
||||
healPercent = 3f;
|
||||
backColor = engineColor;
|
||||
homingPower = 20f;
|
||||
bulletHeight = 4f;
|
||||
bulletWidth = 1.5f;
|
||||
damage = 3f;
|
||||
speed = 4f;
|
||||
lifetime = 40f;
|
||||
shootEffect = Fx.shootHealYellow;
|
||||
smokeEffect = hitEffect = despawnEffect = Fx.hitYellowLaser;
|
||||
}});
|
||||
}};
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean alwaysUnlocked(){
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(Playerc player){
|
||||
if(player.timer.get(Playerc.timerAbility, healReload)){
|
||||
if(indexer.eachBlock(player, healRange, other -> other.entity.damaged(), other -> {
|
||||
other.entity.heal(other.entity.maxHealth() * healPercent / 100f);
|
||||
Fx.healBlockFull.at(other.drawx(), other.drawy(), other.block().size, Pal.heal);
|
||||
})){
|
||||
Fx.healWave.at(player);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
alpha = new UnitType("alpha-mech", false){
|
||||
{
|
||||
drillTier = -1;
|
||||
speed = 0.5f;
|
||||
boostSpeed = 0.95f;
|
||||
itemCapacity = 15;
|
||||
mass = 0.9f;
|
||||
health = 150f;
|
||||
buildSpeed = 0.9f;
|
||||
weaponOffsetX = 1;
|
||||
weaponOffsetY = -1;
|
||||
engineColor = Pal.heal;
|
||||
|
||||
weapons.add(new Weapon("shockgun"){{
|
||||
shake = 2f;
|
||||
length = 0.5f;
|
||||
reload = 70f;
|
||||
alternate = true;
|
||||
recoil = 4f;
|
||||
width = 5f;
|
||||
shootSound = Sounds.laser;
|
||||
|
||||
bullet = new LaserBulletType(){{
|
||||
damage = 20f;
|
||||
recoil = 1f;
|
||||
sideAngle = 45f;
|
||||
sideWidth = 1f;
|
||||
sideLength = 70f;
|
||||
colors = new Color[]{Pal.heal.cpy().a(0.4f), Pal.heal, Color.white};
|
||||
}});
|
||||
}};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(Playerc player){
|
||||
player.heal(Time.delta() * 0.09f);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
delta = new UnitType("delta-mech", false){
|
||||
{
|
||||
drillPower = 1;
|
||||
mineSpeed = 1.5f;
|
||||
mass = 1.2f;
|
||||
speed = 0.5f;
|
||||
itemCapacity = 40;
|
||||
boostSpeed = 0.95f;
|
||||
buildSpeed = 1.2f;
|
||||
engineColor = Color.valueOf("ffd37f");
|
||||
health = 250f;
|
||||
weaponOffsetX = 4f;
|
||||
|
||||
weapons.add(new Weapon("flamethrower"){{
|
||||
length = 1.5f;
|
||||
reload = 30f;
|
||||
width = 4f;
|
||||
alternate = true;
|
||||
shots = 3;
|
||||
inaccuracy = 40f;
|
||||
shootSound = Sounds.spark;
|
||||
bullet = new LightningBulletType(){{
|
||||
damage = 5;
|
||||
lightningLength = 10;
|
||||
lightningColor = Pal.lightFlame;
|
||||
}});
|
||||
}};
|
||||
}
|
||||
};
|
||||
|
||||
tau = new UnitType("tau-mech", false){
|
||||
float healRange = 60f;
|
||||
float healAmount = 10f;
|
||||
float healReload = 160f;
|
||||
boolean wasHealed;
|
||||
|
||||
{
|
||||
drillPower = 4;
|
||||
mineSpeed = 3f;
|
||||
itemCapacity = 70;
|
||||
weaponOffsetY = -1;
|
||||
weaponOffsetX = 1;
|
||||
mass = 1.75f;
|
||||
speed = 0.44f;
|
||||
drag = 0.35f;
|
||||
boostSpeed = 0.8f;
|
||||
canHeal = true;
|
||||
health = 200f;
|
||||
buildSpeed = 1.6f;
|
||||
engineColor = Pal.heal;
|
||||
|
||||
weapons.add(new Weapon("heal-blaster"){{
|
||||
length = 1.5f;
|
||||
reload = 24f;
|
||||
alternate = false;
|
||||
ejectEffect = Fx.none;
|
||||
recoil = 2f;
|
||||
bullet = Bullets.healBullet;
|
||||
shootSound = Sounds.pew;
|
||||
}};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(Playerc player){
|
||||
|
||||
if(player.timer.get(Playerc.timerAbility, healReload)){
|
||||
wasHealed = false;
|
||||
|
||||
Units.nearby(player.team(), player.x, player.y, healRange, unit -> {
|
||||
if(unit.health < unit.maxHealth()){
|
||||
Fx.heal.at(unit);
|
||||
wasHealed = true;
|
||||
}
|
||||
unit.heal(healAmount);
|
||||
});
|
||||
|
||||
if(wasHealed){
|
||||
Fx.healWave.at(player);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
omega = new UnitType("omega-mech", false){
|
||||
protected TextureRegion armorRegion;
|
||||
|
||||
{
|
||||
drillPower = 2;
|
||||
mineSpeed = 1.5f;
|
||||
itemCapacity = 80;
|
||||
speed = 0.36f;
|
||||
boostSpeed = 0.6f;
|
||||
mass = 4f;
|
||||
shake = 4f;
|
||||
weaponOffsetX = 1;
|
||||
weaponOffsetY = 0;
|
||||
engineColor = Color.valueOf("feb380");
|
||||
health = 350f;
|
||||
buildSpeed = 1.5f;
|
||||
weapons.add(new Weapon("swarmer"){{
|
||||
length = 1.5f;
|
||||
recoil = 4f;
|
||||
reload = 38f;
|
||||
shots = 4;
|
||||
spacing = 8f;
|
||||
inaccuracy = 8f;
|
||||
alternate = true;
|
||||
ejectEffect = Fx.none;
|
||||
shake = 3f;
|
||||
bullet = Bullets.missileSwarm;
|
||||
shootSound = Sounds.shootBig;
|
||||
}};
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getRotationAlpha(Playerc player){
|
||||
return 0.6f - player.shootHeat * 0.3f;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float spreadX(Playerc player){
|
||||
return player.shootHeat * 2f;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load(){
|
||||
super.load();
|
||||
armorRegion = Core.atlas.find(name + "-armor");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(Playerc player){
|
||||
float scl = 1f - player.shootHeat / 2f*Time.delta();
|
||||
player.vel().scl(scl);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getExtraArmor(Playerc player){
|
||||
return player.shootHeat * 30f;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Playerc player){
|
||||
if(player.shootHeat <= 0.01f) return;
|
||||
|
||||
Shaders.build.progress = player.shootHeat;
|
||||
Shaders.build.region = armorRegion;
|
||||
Shaders.build.time = Time.time() / 10f;
|
||||
Shaders.build.color.set(Pal.accent).a = player.shootHeat;
|
||||
Draw.shader(Shaders.build);
|
||||
Draw.rect(armorRegion, player.x, player.y, player.rotation);
|
||||
Draw.shader();
|
||||
}
|
||||
};
|
||||
|
||||
dart = new UnitType("dart-ship"){
|
||||
float effectRange = 60f;
|
||||
float effectReload = 60f * 5;
|
||||
float effectDuration = 60f * 10f;
|
||||
|
||||
{
|
||||
flying = true;
|
||||
drillPower = 1;
|
||||
mineSpeed = 2f;
|
||||
speed = 0.5f;
|
||||
drag = 0.09f;
|
||||
health = 200f;
|
||||
weaponOffsetX = -1;
|
||||
weaponOffsetY = -1;
|
||||
engineColor = Pal.lightTrail;
|
||||
cellTrnsY = 1f;
|
||||
buildSpeed = 1.1f;
|
||||
weapons.add(new Weapon("blaster"){{
|
||||
length = 1.5f;
|
||||
reload = 15f;
|
||||
alternate = true;
|
||||
ejectEffect = Fx.shellEjectSmall;
|
||||
bullet = Bullets.standardCopper;
|
||||
}};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(Playerc player){
|
||||
super.update(player);
|
||||
|
||||
if(player.timer.get(Playerc.timerAbility, effectReload)){
|
||||
|
||||
Units.nearby(player.team(), player.x, player.y, effectRange, unit -> {
|
||||
//unit.applyEffect(StatusEffects.overdrive, effectDuration);
|
||||
});
|
||||
|
||||
indexer.eachBlock(player, effectRange, other -> other.entity.damaged(), other -> {
|
||||
other.entity.applyBoost(1.5f, effectDuration);
|
||||
Fx.healBlockFull.at(other.drawx(), other.drawy(), other.block().size, Pal.heal);
|
||||
});
|
||||
|
||||
Fx.overdriveWave.at(player);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
javelin = new UnitType("javelin-ship"){
|
||||
float minV = 3.6f;
|
||||
float maxV = 6f;
|
||||
TextureRegion shield;
|
||||
|
||||
{
|
||||
flying = true;
|
||||
drillPower = -1;
|
||||
speed = 0.11f;
|
||||
drag = 0.01f;
|
||||
mass = 2f;
|
||||
health = 170f;
|
||||
engineColor = Color.valueOf("d3ddff");
|
||||
cellTrnsY = 1f;
|
||||
weapons.add(new Weapon("missiles"){{
|
||||
length = 1.5f;
|
||||
reload = 70f;
|
||||
shots = 4;
|
||||
inaccuracy = 2f;
|
||||
alternate = true;
|
||||
ejectEffect = Fx.none;
|
||||
velocityRnd = 0.2f;
|
||||
spacing = 1f;
|
||||
bullet = Bullets.missileJavelin;
|
||||
shootSound = Sounds.missile;
|
||||
}};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load(){
|
||||
super.load();
|
||||
shield = Core.atlas.find(name + "-shield");
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getRotationAlpha(Playerc player){
|
||||
return 0.5f;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(Playerc player){
|
||||
float scl = scld(player);
|
||||
if(Mathf.chance(Time.delta() * (0.15 * scl))){
|
||||
Fx.hitLancer.at(Pal.lancerLaser, player.x, player.y);
|
||||
Lightning.create(player.team(), Pal.lancerLaser, 10f * Vars.state.rules.playerDamageMultiplier,
|
||||
player.x + player.vel().x, player.y + player.vel().y, player.rotation, 14);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Playerc player){
|
||||
float scl = scld(player);
|
||||
if(scl < 0.01f) return;
|
||||
Draw.color(Pal.lancerLaser);
|
||||
Draw.alpha(scl / 2f);
|
||||
Draw.blend(Blending.additive);
|
||||
Draw.rect(shield, player.x + Mathf.range(scl / 2f), player.y + Mathf.range(scl / 2f), player.rotation - 90);
|
||||
Draw.blend();
|
||||
}
|
||||
|
||||
float scld(Playerc player){
|
||||
return Mathf.clamp((player.vel().len() - minV) / (maxV - minV));
|
||||
}
|
||||
};
|
||||
|
||||
trident = new UnitType("trident-ship"){
|
||||
{
|
||||
flying = true;
|
||||
drillPower = 2;
|
||||
speed = 0.15f;
|
||||
drag = 0.034f;
|
||||
mass = 2.5f;
|
||||
turnCursor = false;
|
||||
health = 250f;
|
||||
itemCapacity = 30;
|
||||
engineColor = Color.valueOf("84f491");
|
||||
cellTrnsY = 1f;
|
||||
buildSpeed = 2.5f;
|
||||
weapons.add(new Weapon("bomber"){{
|
||||
length = 0f;
|
||||
width = 2f;
|
||||
reload = 25f;
|
||||
shots = 2;
|
||||
shotDelay = 1f;
|
||||
shots = 8;
|
||||
alternate = true;
|
||||
ejectEffect = Fx.none;
|
||||
velocityRnd = 1f;
|
||||
inaccuracy = 20f;
|
||||
ignoreRotation = true;
|
||||
bullet = new BombBulletType(16f, 25f, "shell"){{
|
||||
bulletWidth = 10f;
|
||||
bulletHeight = 14f;
|
||||
hitEffect = Fx.flakExplosion;
|
||||
shootEffect = Fx.none;
|
||||
smokeEffect = Fx.none;
|
||||
shootSound = Sounds.artillery;
|
||||
}});
|
||||
}};
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canShoot(Playerc player){
|
||||
return player.vel().len() > 1.2f;
|
||||
}
|
||||
};
|
||||
|
||||
glaive = new UnitType("glaive-ship"){
|
||||
{
|
||||
flying = true;
|
||||
drillPower = 4;
|
||||
mineSpeed = 1.3f;
|
||||
speed = 0.32f;
|
||||
drag = 0.06f;
|
||||
mass = 3f;
|
||||
health = 240f;
|
||||
itemCapacity = 60;
|
||||
engineColor = Color.valueOf("feb380");
|
||||
cellTrnsY = 1f;
|
||||
buildSpeed = 1.2f;
|
||||
|
||||
weapons.add(new Weapon("bomber"){{
|
||||
length = 1.5f;
|
||||
reload = 13f;
|
||||
alternate = true;
|
||||
ejectEffect = Fx.shellEjectSmall;
|
||||
bullet = Bullets.standardGlaive;
|
||||
shootSound = Sounds.shootSnap;
|
||||
}};
|
||||
}
|
||||
};
|
||||
|
||||
starter = vanguard;*/
|
||||
}
|
||||
}
|
||||
|
||||
53
core/src/mindustry/content/Weathers.java
Normal file
53
core/src/mindustry/content/Weathers.java
Normal file
@@ -0,0 +1,53 @@
|
||||
package mindustry.content;
|
||||
|
||||
import arc.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.math.*;
|
||||
import arc.util.*;
|
||||
import mindustry.ctype.*;
|
||||
import mindustry.type.*;
|
||||
|
||||
import static mindustry.Vars.world;
|
||||
|
||||
public class Weathers implements ContentList{
|
||||
public static Weather
|
||||
rain,
|
||||
snow;
|
||||
|
||||
@Override
|
||||
public void load(){
|
||||
snow = new Weather("snow"){
|
||||
Rand rand = new Rand();
|
||||
|
||||
@Override
|
||||
public void draw(){
|
||||
rand.setSeed(0);
|
||||
float yspeed = 2f, xspeed = 0.25f;
|
||||
float padding = 16f;
|
||||
float size = 12f;
|
||||
Core.camera.bounds(Tmp.r1);
|
||||
Tmp.r1.grow(padding);
|
||||
|
||||
for(int i = 0; i < 100; i++){
|
||||
float scl = rand.random(0.5f, 1f);
|
||||
float scl2 = rand.random(0.5f, 1f);
|
||||
float sscl = rand.random(0.2f, 1f);
|
||||
float x = (rand.random(0f, world.unitWidth()) + Time.time() * xspeed * scl2);
|
||||
float y = (rand.random(0f, world.unitHeight()) - Time.time() * yspeed * scl);
|
||||
|
||||
x += Mathf.sin(y, rand.random(30f, 80f), rand.random(1f, 7f));
|
||||
|
||||
x -= Tmp.r1.x;
|
||||
y -= Tmp.r1.y;
|
||||
x = Mathf.mod(x, Tmp.r1.width);
|
||||
y = Mathf.mod(y, Tmp.r1.height);
|
||||
x += Tmp.r1.x;
|
||||
y += Tmp.r1.y;
|
||||
|
||||
Draw.rect("circle-shadow", x, y, size * sscl, size * sscl);
|
||||
}
|
||||
//TODO
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,19 +1,17 @@
|
||||
package mindustry.content;
|
||||
|
||||
import mindustry.ctype.ContentList;
|
||||
import mindustry.game.*;
|
||||
import mindustry.ctype.*;
|
||||
import mindustry.game.Objectives.*;
|
||||
import mindustry.maps.generators.*;
|
||||
import mindustry.maps.generators.MapGenerator.*;
|
||||
import mindustry.maps.zonegen.*;
|
||||
import mindustry.type.*;
|
||||
|
||||
import static arc.struct.Array.with;
|
||||
import static mindustry.content.Items.*;
|
||||
import static mindustry.content.Planets.starter;
|
||||
import static mindustry.type.ItemStack.list;
|
||||
|
||||
public class Zones implements ContentList{
|
||||
public static Zone
|
||||
public static SectorPreset
|
||||
groundZero, desertWastes,
|
||||
craters, frozenForest, ruinousShores, stainedMountains, tarFields, fungalPass,
|
||||
saltFlats, overgrowth, impact0078, crags,
|
||||
@@ -22,7 +20,7 @@ public class Zones implements ContentList{
|
||||
@Override
|
||||
public void load(){
|
||||
|
||||
groundZero = new Zone("groundZero", new MapGenerator("groundZero", 1)){{
|
||||
groundZero = new SectorPreset("groundZero", starter, new FileMapGenerator("groundZero")){{
|
||||
baseLaunchCost = list(copper, -60);
|
||||
startingItems = list(copper, 60);
|
||||
alwaysUnlocked = true;
|
||||
@@ -31,7 +29,9 @@ public class Zones implements ContentList{
|
||||
resources = with(copper, scrap, lead);
|
||||
}};
|
||||
|
||||
desertWastes = new Zone("desertWastes", new DesertWastesGenerator(260, 260)){{
|
||||
//TODO remove
|
||||
/*
|
||||
desertWastes = new Zone("desertWastes", starter, new FileMapGenerator("groundZero")){{
|
||||
startingItems = list(copper, 120);
|
||||
conditionWave = 20;
|
||||
launchPeriod = 10;
|
||||
@@ -80,9 +80,9 @@ public class Zones implements ContentList{
|
||||
new ZoneWave(groundZero, 20),
|
||||
new Unlock(Blocks.combustionGenerator)
|
||||
);
|
||||
}};
|
||||
}};*/
|
||||
|
||||
saltFlats = new Zone("saltFlats", new MapGenerator("saltFlats")){{
|
||||
saltFlats = new SectorPreset("saltFlats", starter, new FileMapGenerator("saltFlats")){{
|
||||
startingItems = list(copper, 200, Items.silicon, 200, lead, 200);
|
||||
loadout = Loadouts.basicFoundation;
|
||||
conditionWave = 10;
|
||||
@@ -91,15 +91,14 @@ public class Zones implements ContentList{
|
||||
resources = with(copper, scrap, lead, coal, sand, titanium);
|
||||
requirements = with(
|
||||
new ZoneWave(desertWastes, 60),
|
||||
new Unlock(Blocks.daggerFactory),
|
||||
new Unlock(Blocks.draugFactory),
|
||||
//new Unlock(Blocks.daggerFactory),
|
||||
//new Unlock(Blocks.draugFactory),
|
||||
new Unlock(Blocks.door),
|
||||
new Unlock(Blocks.waterExtractor)
|
||||
);
|
||||
}};
|
||||
|
||||
frozenForest = new Zone("frozenForest", new MapGenerator("frozenForest", 1)
|
||||
.decor(new Decoration(Blocks.snow, Blocks.sporeCluster, 0.02))){{
|
||||
frozenForest = new SectorPreset("frozenForest", starter, new FileMapGenerator("frozenForest")){{
|
||||
loadout = Loadouts.basicFoundation;
|
||||
startingItems = list(copper, 250);
|
||||
conditionWave = 10;
|
||||
@@ -111,7 +110,7 @@ public class Zones implements ContentList{
|
||||
);
|
||||
}};
|
||||
|
||||
craters = new Zone("craters", new MapGenerator("craters", 1).decor(new Decoration(Blocks.snow, Blocks.sporeCluster, 0.004))){{
|
||||
craters = new SectorPreset("craters", starter, new FileMapGenerator("craters")){{
|
||||
startingItems = list(copper, 100);
|
||||
conditionWave = 10;
|
||||
resources = with(copper, lead, coal, sand, scrap);
|
||||
@@ -122,7 +121,7 @@ public class Zones implements ContentList{
|
||||
);
|
||||
}};
|
||||
|
||||
ruinousShores = new Zone("ruinousShores", new MapGenerator("ruinousShores", 1)){{
|
||||
ruinousShores = new SectorPreset("ruinousShores", starter, new FileMapGenerator("ruinousShores")){{
|
||||
loadout = Loadouts.basicFoundation;
|
||||
startingItems = list(copper, 140, lead, 50);
|
||||
conditionWave = 20;
|
||||
@@ -138,8 +137,7 @@ public class Zones implements ContentList{
|
||||
);
|
||||
}};
|
||||
|
||||
stainedMountains = new Zone("stainedMountains", new MapGenerator("stainedMountains", 2)
|
||||
.decor(new Decoration(Blocks.shale, Blocks.shaleBoulder, 0.02))){{
|
||||
stainedMountains = new SectorPreset("stainedMountains", starter, new FileMapGenerator("stainedMountains")){{
|
||||
loadout = Loadouts.basicFoundation;
|
||||
startingItems = list(copper, 200, lead, 50);
|
||||
conditionWave = 10;
|
||||
@@ -153,20 +151,20 @@ public class Zones implements ContentList{
|
||||
);
|
||||
}};
|
||||
|
||||
fungalPass = new Zone("fungalPass", new MapGenerator("fungalPass")){{
|
||||
fungalPass = new SectorPreset("fungalPass", starter, new FileMapGenerator("fungalPass")){{
|
||||
startingItems = list(copper, 250, lead, 250, Items.metaglass, 100, Items.graphite, 100);
|
||||
resources = with(copper, lead, coal, titanium, sand);
|
||||
configureObjective = new Launched(this);
|
||||
requirements = with(
|
||||
new ZoneWave(stainedMountains, 15),
|
||||
new Unlock(Blocks.daggerFactory),
|
||||
new Unlock(Blocks.crawlerFactory),
|
||||
//new Unlock(Blocks.daggerFactory),
|
||||
//new Unlock(Blocks.crawlerFactory),
|
||||
new Unlock(Blocks.door),
|
||||
new Unlock(Blocks.siliconSmelter)
|
||||
);
|
||||
}};
|
||||
|
||||
overgrowth = new Zone("overgrowth", new MapGenerator("overgrowth")){{
|
||||
overgrowth = new SectorPreset("overgrowth", starter, new FileMapGenerator("overgrowth")){{
|
||||
startingItems = list(copper, 1500, lead, 1000, Items.silicon, 500, Items.metaglass, 250);
|
||||
conditionWave = 12;
|
||||
launchPeriod = 4;
|
||||
@@ -177,14 +175,13 @@ public class Zones implements ContentList{
|
||||
new ZoneWave(craters, 40),
|
||||
new Launched(fungalPass),
|
||||
new Unlock(Blocks.cultivator),
|
||||
new Unlock(Blocks.sporePress),
|
||||
new Unlock(Blocks.titanFactory),
|
||||
new Unlock(Blocks.wraithFactory)
|
||||
new Unlock(Blocks.sporePress)
|
||||
//new Unlock(Blocks.titanFactory),
|
||||
//new Unlock(Blocks.wraithFactory)
|
||||
);
|
||||
}};
|
||||
|
||||
tarFields = new Zone("tarFields", new MapGenerator("tarFields")
|
||||
.decor(new Decoration(Blocks.shale, Blocks.shaleBoulder, 0.02))){{
|
||||
tarFields = new SectorPreset("tarFields", starter, new FileMapGenerator("tarFields")){{
|
||||
loadout = Loadouts.basicFoundation;
|
||||
startingItems = list(copper, 250, lead, 100);
|
||||
conditionWave = 15;
|
||||
@@ -198,7 +195,7 @@ public class Zones implements ContentList{
|
||||
);
|
||||
}};
|
||||
|
||||
desolateRift = new Zone("desolateRift", new MapGenerator("desolateRift")){{
|
||||
desolateRift = new SectorPreset("desolateRift", starter, new FileMapGenerator("desolateRift")){{
|
||||
loadout = Loadouts.basicNucleus;
|
||||
startingItems = list(copper, 1000, lead, 1000, Items.graphite, 250, titanium, 250, Items.silicon, 250);
|
||||
conditionWave = 3;
|
||||
@@ -223,8 +220,7 @@ public class Zones implements ContentList{
|
||||
resources = Array.with(Items.copper, Items.scrap, Items.lead, Items.coal, Items.sand};
|
||||
}};*/
|
||||
|
||||
nuclearComplex = new Zone("nuclearComplex", new MapGenerator("nuclearProductionComplex", 1)
|
||||
.decor(new Decoration(Blocks.snow, Blocks.sporeCluster, 0.01))){{
|
||||
nuclearComplex = new SectorPreset("nuclearComplex", starter, new FileMapGenerator("nuclearProductionComplex")){{
|
||||
loadout = Loadouts.basicNucleus;
|
||||
startingItems = list(copper, 1250, lead, 1500, Items.silicon, 400, Items.metaglass, 250);
|
||||
conditionWave = 30;
|
||||
|
||||
@@ -30,21 +30,17 @@ public class ContentLoader{
|
||||
private @Nullable Content lastAdded;
|
||||
private ObjectSet<Cons<Content>> initialization = new ObjectSet<>();
|
||||
private ContentList[] content = {
|
||||
new Fx(),
|
||||
new Items(),
|
||||
new StatusEffects(),
|
||||
new Liquids(),
|
||||
new Bullets(),
|
||||
new Mechs(),
|
||||
new UnitTypes(),
|
||||
new Blocks(),
|
||||
new Loadouts(),
|
||||
new TechTree(),
|
||||
new Zones(),
|
||||
new TypeIDs(),
|
||||
|
||||
//these are not really content classes, but this makes initialization easier
|
||||
new LegacyColorMapper(),
|
||||
new Weathers(),
|
||||
new Planets(),
|
||||
new Zones()
|
||||
};
|
||||
|
||||
public ContentLoader(){
|
||||
@@ -137,13 +133,15 @@ public class ContentLoader{
|
||||
if(blocks().size > i){
|
||||
int color = pixmap.getPixel(i, 0);
|
||||
|
||||
if(color == 0) continue;
|
||||
if(color == 0 || color == 255) continue;
|
||||
|
||||
Block block = block(i);
|
||||
block.color.set(color);
|
||||
block.mapColor.rgba8888(color);
|
||||
block.hasColor = true;
|
||||
}
|
||||
}
|
||||
pixmap.dispose();
|
||||
ColorMapper.load();
|
||||
}
|
||||
|
||||
public void dispose(){
|
||||
@@ -239,6 +237,10 @@ public class ContentLoader{
|
||||
return (Block)getByID(ContentType.block, id);
|
||||
}
|
||||
|
||||
public Block block(String name){
|
||||
return (Block)getByName(ContentType.block, name);
|
||||
}
|
||||
|
||||
public Array<Item> items(){
|
||||
return getBy(ContentType.item);
|
||||
}
|
||||
@@ -263,11 +265,15 @@ public class ContentLoader{
|
||||
return (BulletType)getByID(ContentType.bullet, id);
|
||||
}
|
||||
|
||||
public Array<Zone> zones(){
|
||||
public Array<SectorPreset> zones(){
|
||||
return getBy(ContentType.zone);
|
||||
}
|
||||
|
||||
public Array<UnitType> units(){
|
||||
return getBy(ContentType.unit);
|
||||
}
|
||||
|
||||
public Array<Planet> planets(){
|
||||
return getBy(ContentType.planet);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,26 +3,24 @@ package mindustry.core;
|
||||
import arc.*;
|
||||
import arc.assets.*;
|
||||
import arc.audio.*;
|
||||
import arc.graphics.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.input.*;
|
||||
import arc.math.geom.*;
|
||||
import arc.scene.ui.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.core.GameState.*;
|
||||
import mindustry.entities.*;
|
||||
import mindustry.entities.type.*;
|
||||
import mindustry.game.EventType.*;
|
||||
import mindustry.game.*;
|
||||
import mindustry.game.Saves.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.input.*;
|
||||
import mindustry.io.SaveIO.*;
|
||||
import mindustry.maps.Map;
|
||||
import mindustry.type.*;
|
||||
import mindustry.ui.dialogs.*;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.blocks.storage.*;
|
||||
|
||||
import java.io.*;
|
||||
import java.text.*;
|
||||
@@ -63,21 +61,19 @@ public class Control implements ApplicationListener, Loadable{
|
||||
});
|
||||
|
||||
Events.on(PlayEvent.class, event -> {
|
||||
player.setTeam(netServer.assignTeam(player, playerGroup.all()));
|
||||
player.setDead(true);
|
||||
player.team(netServer.assignTeam(player));
|
||||
player.add();
|
||||
|
||||
state.set(State.playing);
|
||||
});
|
||||
|
||||
Events.on(WorldLoadEvent.class, event -> {
|
||||
Core.app.post(() -> Core.app.post(() -> {
|
||||
if(net.active() && player.getClosestCore() != null){
|
||||
//set to closest core since that's where the player will probably respawn; prevents camera jumps
|
||||
Core.camera.position.set(player.isDead() ? player.getClosestCore() : player);
|
||||
}else{
|
||||
//locally, set to player position since respawning occurs immediately
|
||||
Core.camera.position.set(player);
|
||||
//TODO test this
|
||||
app.post(() -> app.post(() -> {
|
||||
//TODO 0,0 seems like a bad choice?
|
||||
Tilec core = state.teams.closestCore(0, 0, player.team());
|
||||
if(core != null){
|
||||
camera.position.set(core);
|
||||
}
|
||||
}));
|
||||
});
|
||||
@@ -92,9 +88,9 @@ public class Control implements ApplicationListener, Loadable{
|
||||
});
|
||||
|
||||
Events.on(WaveEvent.class, event -> {
|
||||
if(world.getMap().getHightScore() < state.wave){
|
||||
if(state.map.getHightScore() < state.wave){
|
||||
hiscore = true;
|
||||
world.getMap().setHighScore(state.wave);
|
||||
state.map.setHighScore(state.wave);
|
||||
}
|
||||
|
||||
Sounds.wave.play();
|
||||
@@ -105,20 +101,23 @@ public class Control implements ApplicationListener, Loadable{
|
||||
Effects.shake(5, 6, Core.camera.position.x, Core.camera.position.y);
|
||||
//the restart dialog can show info for any number of scenarios
|
||||
Call.onGameOver(event.winner);
|
||||
//TODO set meta to indicate game over
|
||||
/*
|
||||
if(state.rules.zone != null && !net.client()){
|
||||
//remove zone save on game over
|
||||
if(saves.getZoneSlot() != null && !state.rules.tutorial){
|
||||
saves.getZoneSlot().delete();
|
||||
}
|
||||
}
|
||||
}*/
|
||||
});
|
||||
|
||||
//autohost for pvp maps
|
||||
Events.on(WorldLoadEvent.class, event -> app.post(() -> {
|
||||
player.add();
|
||||
if(state.rules.pvp && !net.active()){
|
||||
try{
|
||||
net.host(port);
|
||||
player.isAdmin = true;
|
||||
player.admin(true);
|
||||
}catch(IOException e){
|
||||
ui.showException("$server.error", e);
|
||||
state.set(State.menu);
|
||||
@@ -129,7 +128,7 @@ public class Control implements ApplicationListener, Loadable{
|
||||
Events.on(UnlockEvent.class, e -> ui.hudfrag.showUnlock(e.content));
|
||||
|
||||
Events.on(BlockBuildEndEvent.class, e -> {
|
||||
if(e.team == player.getTeam()){
|
||||
if(e.team == player.team()){
|
||||
if(e.breaking){
|
||||
state.stats.buildingsDeconstructed++;
|
||||
}else{
|
||||
@@ -139,13 +138,13 @@ public class Control implements ApplicationListener, Loadable{
|
||||
});
|
||||
|
||||
Events.on(BlockDestroyEvent.class, e -> {
|
||||
if(e.tile.getTeam() == player.getTeam()){
|
||||
if(e.tile.team() == player.team()){
|
||||
state.stats.buildingsDestroyed++;
|
||||
}
|
||||
});
|
||||
|
||||
Events.on(UnitDestroyEvent.class, e -> {
|
||||
if(e.unit.getTeam() != player.getTeam()){
|
||||
if(e.unit.team() != player.team()){
|
||||
state.stats.enemyUnitsDestroyed++;
|
||||
}
|
||||
});
|
||||
@@ -163,22 +162,29 @@ public class Control implements ApplicationListener, Loadable{
|
||||
});
|
||||
|
||||
Events.on(Trigger.newGame, () -> {
|
||||
TileEntity core = player.getClosestCore();
|
||||
Tilec core = player.closestCore();
|
||||
|
||||
if(core == null) return;
|
||||
|
||||
//TODO this sounds pretty bad due to conflict
|
||||
if(settings.getInt("musicvol") > 0){
|
||||
Musics.land.stop();
|
||||
Musics.land.play();
|
||||
Musics.land.setVolume(settings.getInt("musicvol") / 100f);
|
||||
}
|
||||
|
||||
app.post(() -> ui.hudfrag.showLand());
|
||||
renderer.zoomIn(Fx.coreLand.lifetime);
|
||||
app.post(() -> Effects.effect(Fx.coreLand, core.x, core.y, 0, core.block));
|
||||
app.post(() -> Fx.coreLand.at(core.getX(), core.getY(), 0, core.block()));
|
||||
Time.run(Fx.coreLand.lifetime, () -> {
|
||||
Effects.effect(Fx.launch, core);
|
||||
Fx.launch.at(core);
|
||||
Effects.shake(5f, 5f, core);
|
||||
});
|
||||
});
|
||||
|
||||
Events.on(UnitDestroyEvent.class, e -> {
|
||||
if(e.unit instanceof BaseUnit && world.isZone()){
|
||||
data.unlockContent(((BaseUnit)e.unit).getType());
|
||||
if(state.isCampaign()){
|
||||
data.unlockContent(e.unit.type());
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -204,11 +210,9 @@ public class Control implements ApplicationListener, Loadable{
|
||||
}
|
||||
|
||||
void createPlayer(){
|
||||
player = new Player();
|
||||
player.name = Core.settings.getString("name");
|
||||
player.color.set(Core.settings.getInt("color-0"));
|
||||
player.isLocal = true;
|
||||
player.isMobile = mobile;
|
||||
player = PlayerEntity.create();
|
||||
player.name(Core.settings.getString("name"));
|
||||
player.color().set(Core.settings.getInt("color-0"));
|
||||
|
||||
if(mobile){
|
||||
input = new MobileInput();
|
||||
@@ -216,7 +220,7 @@ public class Control implements ApplicationListener, Loadable{
|
||||
input = new DesktopInput();
|
||||
}
|
||||
|
||||
if(!state.is(State.menu)){
|
||||
if(state.isGame()){
|
||||
player.add();
|
||||
}
|
||||
|
||||
@@ -239,7 +243,7 @@ public class Control implements ApplicationListener, Loadable{
|
||||
logic.reset();
|
||||
world.loadMap(map, rules);
|
||||
state.rules = rules;
|
||||
state.rules.zone = null;
|
||||
state.rules.sector = null;
|
||||
state.rules.editor = false;
|
||||
logic.play();
|
||||
if(settings.getBool("savecreate") && !world.isInvalidMap()){
|
||||
@@ -249,27 +253,42 @@ public class Control implements ApplicationListener, Loadable{
|
||||
});
|
||||
}
|
||||
|
||||
public void playZone(Zone zone){
|
||||
public void playSector(Sector sector){
|
||||
ui.loadAnd(() -> {
|
||||
logic.reset();
|
||||
net.reset();
|
||||
world.loadGenerator(zone.generator);
|
||||
zone.rules.get(state.rules);
|
||||
state.rules.zone = zone;
|
||||
for(TileEntity core : state.teams.playerCores()){
|
||||
for(ItemStack stack : zone.getStartingItems()){
|
||||
core.items.add(stack.item, stack.amount);
|
||||
ui.planet.hide();
|
||||
SaveSlot slot = sector.save;
|
||||
//TODO comment for new sector states
|
||||
slot = null;
|
||||
if(slot != null){
|
||||
try{
|
||||
net.reset();
|
||||
slot.load();
|
||||
state.rules.sector = sector;
|
||||
state.set(State.playing);
|
||||
}catch(SaveException e){
|
||||
Log.err(e);
|
||||
sector.save = null;
|
||||
ui.showErrorMessage("$save.corrupted");
|
||||
slot.delete();
|
||||
playSector(sector);
|
||||
}
|
||||
ui.planet.hide();
|
||||
}else{
|
||||
net.reset();
|
||||
logic.reset();
|
||||
world.loadSector(sector);
|
||||
state.rules.sector = sector;
|
||||
logic.play();
|
||||
control.saves.saveSector(sector);
|
||||
Events.fire(Trigger.newGame);
|
||||
}
|
||||
state.set(State.playing);
|
||||
state.wavetime = state.rules.waveSpacing;
|
||||
control.saves.zoneSave();
|
||||
logic.play();
|
||||
Events.fire(Trigger.newGame);
|
||||
});
|
||||
}
|
||||
|
||||
public void playTutorial(){
|
||||
//TODO implement
|
||||
ui.showInfo("death");
|
||||
/*
|
||||
Zone zone = Zones.groundZero;
|
||||
ui.loadAnd(() -> {
|
||||
logic.reset();
|
||||
@@ -277,8 +296,8 @@ public class Control implements ApplicationListener, Loadable{
|
||||
|
||||
world.beginMapLoad();
|
||||
|
||||
world.createTiles(zone.generator.width, zone.generator.height);
|
||||
zone.generator.generate(world.getTiles());
|
||||
world.resize(zone.generator.width, zone.generator.height);
|
||||
zone.generator.generate(world.tiles);
|
||||
|
||||
Tile coreb = null;
|
||||
|
||||
@@ -294,7 +313,7 @@ public class Control implements ApplicationListener, Loadable{
|
||||
|
||||
Geometry.circle(coreb.x, coreb.y, 10, (cx, cy) -> {
|
||||
Tile tile = world.ltile(cx, cy);
|
||||
if(tile != null && tile.getTeam() == state.rules.defaultTeam && !(tile.block() instanceof CoreBlock)){
|
||||
if(tile != null && tile.team() == state.rules.defaultTeam && !(tile.block() instanceof CoreBlock)){
|
||||
tile.remove();
|
||||
}
|
||||
});
|
||||
@@ -304,14 +323,15 @@ public class Control implements ApplicationListener, Loadable{
|
||||
world.endMapLoad();
|
||||
|
||||
zone.rules.get(state.rules);
|
||||
state.rules.zone = zone;
|
||||
for(TileEntity core : state.teams.playerCores()){
|
||||
//TODO assign zone!!
|
||||
//state.rules.zone = zone;
|
||||
for(Tilec core : state.teams.playerCores()){
|
||||
for(ItemStack stack : zone.getStartingItems()){
|
||||
core.items.add(stack.item, stack.amount);
|
||||
core.items().add(stack.item, stack.amount);
|
||||
}
|
||||
}
|
||||
TileEntity core = state.teams.playerCores().first();
|
||||
core.items.clear();
|
||||
Tilec core = state.teams.playerCores().first();
|
||||
core.items().clear();
|
||||
|
||||
logic.play();
|
||||
state.rules.waveTimer = false;
|
||||
@@ -319,7 +339,7 @@ public class Control implements ApplicationListener, Loadable{
|
||||
state.rules.buildCostMultiplier = 0.3f;
|
||||
state.rules.tutorial = true;
|
||||
Events.fire(Trigger.newGame);
|
||||
});
|
||||
});*/
|
||||
}
|
||||
|
||||
public boolean isHighScore(){
|
||||
@@ -430,13 +450,13 @@ public class Control implements ApplicationListener, Loadable{
|
||||
settings.save();
|
||||
}
|
||||
|
||||
if(!state.is(State.menu)){
|
||||
if(state.isGame()){
|
||||
input.update();
|
||||
|
||||
if(world.isZone()){
|
||||
for(TileEntity tile : state.teams.cores(player.getTeam())){
|
||||
if(state.isCampaign()){
|
||||
for(Tilec tile : state.teams.cores(player.team())){
|
||||
for(Item item : content.items()){
|
||||
if(tile.items.has(item)){
|
||||
if(tile.items().has(item)){
|
||||
data.unlockContent(item);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,8 @@ public class FileTree implements FileHandleResolver{
|
||||
return files.get(path);
|
||||
}else if(files.containsKey("/" + path)){
|
||||
return files.get("/" + path);
|
||||
}else if(Core.files == null){ //headless
|
||||
return Fi.get(path);
|
||||
}else{
|
||||
return Core.files.internal(path);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
package mindustry.core;
|
||||
|
||||
import arc.*;
|
||||
import mindustry.entities.type.*;
|
||||
import arc.util.ArcAnnotate.*;
|
||||
import mindustry.game.EventType.*;
|
||||
import mindustry.game.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.maps.*;
|
||||
import mindustry.type.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
@@ -14,6 +17,8 @@ public class GameState{
|
||||
public float wavetime;
|
||||
/** Whether the game is in game over state. */
|
||||
public boolean gameOver = false, launched = false;
|
||||
/** Map that is currently being played on. */
|
||||
public @NonNull Map map = emptyMap;
|
||||
/** The current game rules. */
|
||||
public Rules rules = new Rules();
|
||||
/** Statistics for this save/game. Displayed after game over. */
|
||||
@@ -25,8 +30,8 @@ public class GameState{
|
||||
/** Current game state. */
|
||||
private State state = State.menu;
|
||||
|
||||
public BaseUnit boss(){
|
||||
return unitGroup.find(u -> u.isBoss() && u.getTeam() == rules.waveTeam);
|
||||
public Unitc boss(){
|
||||
return Groups.unit.find(u -> u.isBoss() && u.team() == rules.waveTeam);
|
||||
}
|
||||
|
||||
public void set(State astate){
|
||||
@@ -34,6 +39,20 @@ public class GameState{
|
||||
state = astate;
|
||||
}
|
||||
|
||||
/** Note that being in a campaign does not necessarily mean having a sector. */
|
||||
public boolean isCampaign(){
|
||||
return rules.sector != null || rules.region != null;
|
||||
}
|
||||
|
||||
public boolean hasSector(){
|
||||
return rules.sector != null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Sector getSector(){
|
||||
return rules.sector;
|
||||
}
|
||||
|
||||
public boolean isEditor(){
|
||||
return rules.editor;
|
||||
}
|
||||
@@ -42,6 +61,15 @@ public class GameState{
|
||||
return (is(State.paused) && !net.active()) || (gameOver && !net.active());
|
||||
}
|
||||
|
||||
/** @return whether the current state is *not* the menu. */
|
||||
public boolean isGame(){
|
||||
return state != State.menu;
|
||||
}
|
||||
|
||||
public boolean isMenu(){
|
||||
return state == State.menu;
|
||||
}
|
||||
|
||||
public boolean is(State astate){
|
||||
return state == astate;
|
||||
}
|
||||
|
||||
@@ -6,12 +6,10 @@ import mindustry.annotations.Annotations.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.core.GameState.*;
|
||||
import mindustry.ctype.*;
|
||||
import mindustry.entities.*;
|
||||
import mindustry.entities.type.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.game.EventType.*;
|
||||
import mindustry.game.*;
|
||||
import mindustry.game.Teams.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.blocks.*;
|
||||
@@ -32,13 +30,15 @@ import static mindustry.Vars.*;
|
||||
public class Logic implements ApplicationListener{
|
||||
|
||||
public Logic(){
|
||||
Events.on(WaveEvent.class, event -> {
|
||||
for(Player p : playerGroup.all()){
|
||||
p.respawns = state.rules.respawns;
|
||||
}
|
||||
Events.on(WorldLoadEvent.class, event -> {
|
||||
//TODO remove later
|
||||
//Weathers.snow.create();
|
||||
});
|
||||
|
||||
if(world.isZone()){
|
||||
world.getZone().updateWave(state.wave);
|
||||
Events.on(WaveEvent.class, event -> {
|
||||
if(state.isCampaign()){
|
||||
//TODO implement
|
||||
//state.getSector().updateWave(state.wave);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -62,7 +62,7 @@ public class Logic implements ApplicationListener{
|
||||
}
|
||||
}
|
||||
|
||||
TeamData data = state.teams.get(tile.getTeam());
|
||||
TeamData data = state.teams.get(tile.team());
|
||||
|
||||
//remove existing blocks that have been placed here.
|
||||
//painful O(n) iteration + copy
|
||||
@@ -99,19 +99,20 @@ public class Logic implements ApplicationListener{
|
||||
}
|
||||
}
|
||||
|
||||
/** Adds starting items, resets wave time, and sets state to playing. */
|
||||
public void play(){
|
||||
state.set(State.playing);
|
||||
state.wavetime = state.rules.waveSpacing * 2; //grace period of 2x wave time before game starts
|
||||
Events.fire(new PlayEvent());
|
||||
|
||||
//add starting items
|
||||
if(!world.isZone()){
|
||||
if(!state.isCampaign()){
|
||||
for(TeamData team : state.teams.getActive()){
|
||||
if(team.hasCore()){
|
||||
TileEntity entity = team.core();
|
||||
entity.items.clear();
|
||||
Tilec entity = team.core();
|
||||
entity.items().clear();
|
||||
for(ItemStack stack : state.rules.loadout){
|
||||
entity.items.add(stack.item, stack.amount);
|
||||
entity.items().add(stack.item, stack.amount);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -119,24 +120,21 @@ public class Logic implements ApplicationListener{
|
||||
}
|
||||
|
||||
public void reset(){
|
||||
state.wave = 1;
|
||||
state.wavetime = state.rules.waveSpacing;
|
||||
state.gameOver = state.launched = false;
|
||||
state.teams = new Teams();
|
||||
state.rules = new Rules();
|
||||
state.stats = new Stats();
|
||||
State prev = state.getState();
|
||||
//recreate gamestate - sets state to menu
|
||||
state = new GameState();
|
||||
//fire change event, since it was technically changed
|
||||
Events.fire(new StateChangeEvent(prev, State.menu));
|
||||
|
||||
entities.clear();
|
||||
Groups.all.clear();
|
||||
Time.clear();
|
||||
TileEntity.sleepingEntities = 0;
|
||||
|
||||
Events.fire(new ResetEvent());
|
||||
}
|
||||
|
||||
public void runWave(){
|
||||
spawner.spawnEnemies();
|
||||
state.wave++;
|
||||
state.wavetime = world.isZone() && world.getZone().isLaunchWave(state.wave) ? state.rules.waveSpacing * state.rules.launchWaveMultiplier : state.rules.waveSpacing;
|
||||
state.wavetime = state.hasSector() && state.getSector().isLaunchWave(state.wave) ? state.rules.waveSpacing * state.rules.launchWaveMultiplier : state.rules.waveSpacing;
|
||||
|
||||
Events.fire(new WaveEvent());
|
||||
}
|
||||
@@ -158,7 +156,7 @@ public class Logic implements ApplicationListener{
|
||||
}
|
||||
|
||||
if(alive != null && !state.gameOver){
|
||||
if(world.isZone() && alive == state.rules.defaultTeam){
|
||||
if(state.isCampaign() && alive == state.rules.defaultTeam){
|
||||
//in attack maps, a victorious game over is equivalent to a launch
|
||||
Call.launchZone();
|
||||
}else{
|
||||
@@ -175,21 +173,22 @@ public class Logic implements ApplicationListener{
|
||||
ui.hudfrag.showLaunch();
|
||||
}
|
||||
|
||||
for(TileEntity tile : state.teams.playerCores()){
|
||||
Effects.effect(Fx.launch, tile);
|
||||
for(Tilec tile : state.teams.playerCores()){
|
||||
Fx.launch.at(tile);
|
||||
}
|
||||
|
||||
if(world.getZone() != null){
|
||||
world.getZone().setLaunched();
|
||||
if(state.isCampaign()){
|
||||
//TODO implement
|
||||
//state.getSector().setLaunched();
|
||||
}
|
||||
|
||||
Time.runTask(30f, () -> {
|
||||
for(TileEntity entity : state.teams.playerCores()){
|
||||
for(Tilec entity : state.teams.playerCores()){
|
||||
for(Item item : content.items()){
|
||||
data.addItem(item, entity.items.get(item));
|
||||
Events.fire(new LaunchItemEvent(item, entity.items.get(item)));
|
||||
data.addItem(item, entity.items().get(item));
|
||||
Events.fire(new LaunchItemEvent(item, entity.items().get(item)));
|
||||
}
|
||||
entity.tile.remove();
|
||||
entity.tile().remove();
|
||||
}
|
||||
state.launched = true;
|
||||
state.gameOver = true;
|
||||
@@ -209,13 +208,17 @@ public class Logic implements ApplicationListener{
|
||||
@Override
|
||||
public void update(){
|
||||
Events.fire(Trigger.update);
|
||||
universe.updateGlobal();
|
||||
|
||||
if(!state.is(State.menu)){
|
||||
if(state.isGame()){
|
||||
if(!net.client()){
|
||||
state.enemies = unitGroup.count(b -> b.getTeam() == state.rules.waveTeam && b.countsAsEnemy());
|
||||
state.enemies = Groups.unit.count(b -> b.team() == state.rules.waveTeam && b.type().isCounted);
|
||||
}
|
||||
|
||||
if(!state.isPaused()){
|
||||
if(state.isCampaign()){
|
||||
universe.update();
|
||||
}
|
||||
Time.update();
|
||||
|
||||
if(state.rules.waves && state.rules.waveTimer && !state.gameOver){
|
||||
@@ -228,35 +231,7 @@ public class Logic implements ApplicationListener{
|
||||
runWave();
|
||||
}
|
||||
|
||||
if(!headless){
|
||||
effectGroup.update();
|
||||
groundEffectGroup.update();
|
||||
}
|
||||
|
||||
if(!state.isEditor()){
|
||||
unitGroup.update();
|
||||
puddleGroup.update();
|
||||
shieldGroup.update();
|
||||
bulletGroup.update();
|
||||
tileGroup.update();
|
||||
fireGroup.update();
|
||||
}else{
|
||||
unitGroup.updateEvents();
|
||||
collisions.updatePhysics(unitGroup);
|
||||
}
|
||||
|
||||
playerGroup.update();
|
||||
|
||||
//effect group only contains item transfers in the headless version, update it!
|
||||
if(headless){
|
||||
effectGroup.update();
|
||||
}
|
||||
|
||||
if(!state.isEditor()){
|
||||
//bulletGroup
|
||||
collisions.collideGroups(bulletGroup, unitGroup);
|
||||
collisions.collideGroups(bulletGroup, playerGroup);
|
||||
}
|
||||
Groups.update();
|
||||
}
|
||||
|
||||
if(!net.client() && !world.isInvalidMap() && !state.isEditor() && state.rules.canGameOver){
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package mindustry.core;
|
||||
|
||||
import arc.*;
|
||||
import arc.graphics.*;
|
||||
import arc.math.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.*;
|
||||
@@ -11,12 +10,7 @@ import arc.util.serialization.*;
|
||||
import mindustry.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.core.GameState.*;
|
||||
import mindustry.ctype.*;
|
||||
import mindustry.entities.*;
|
||||
import mindustry.entities.Effects.*;
|
||||
import mindustry.entities.traits.BuilderTrait.*;
|
||||
import mindustry.entities.traits.*;
|
||||
import mindustry.entities.type.*;
|
||||
import mindustry.entities.units.*;
|
||||
import mindustry.game.EventType.*;
|
||||
import mindustry.game.*;
|
||||
import mindustry.gen.*;
|
||||
@@ -24,7 +18,6 @@ import mindustry.net.Administration.*;
|
||||
import mindustry.net.Net.*;
|
||||
import mindustry.net.*;
|
||||
import mindustry.net.Packets.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.modules.*;
|
||||
|
||||
@@ -62,7 +55,7 @@ public class NetClient implements ApplicationListener{
|
||||
net.handleClient(Connect.class, packet -> {
|
||||
Log.info("Connecting to server: {0}", packet.addressTCP);
|
||||
|
||||
player.isAdmin = false;
|
||||
player.admin(false);
|
||||
|
||||
reset();
|
||||
|
||||
@@ -77,11 +70,11 @@ public class NetClient implements ApplicationListener{
|
||||
});
|
||||
|
||||
ConnectPacket c = new ConnectPacket();
|
||||
c.name = player.name;
|
||||
c.name = player.name();
|
||||
c.mods = mods.getModStrings();
|
||||
c.mobile = mobile;
|
||||
c.versionType = Version.type;
|
||||
c.color = player.color.rgba();
|
||||
c.color = player.color().rgba();
|
||||
c.usid = getUsid(packet.addressTCP);
|
||||
c.uuid = platform.getUUID();
|
||||
|
||||
@@ -99,11 +92,10 @@ public class NetClient implements ApplicationListener{
|
||||
if(quietReset) return;
|
||||
|
||||
connecting = false;
|
||||
state.set(State.menu);
|
||||
logic.reset();
|
||||
platform.updateRPC();
|
||||
player.name = Core.settings.getString("name");
|
||||
player.color.set(Core.settings.getInt("color-0"));
|
||||
player.name(Core.settings.getString("name"));
|
||||
player.color().set(Core.settings.getInt("color-0"));
|
||||
|
||||
if(quiet) return;
|
||||
|
||||
@@ -130,21 +122,20 @@ public class NetClient implements ApplicationListener{
|
||||
});
|
||||
|
||||
net.handleClient(InvokePacket.class, packet -> {
|
||||
packet.writeBuffer.position(0);
|
||||
RemoteReadClient.readPacket(packet.writeBuffer, packet.type);
|
||||
RemoteReadClient.readPacket(packet.reader(), packet.type);
|
||||
});
|
||||
}
|
||||
|
||||
//called on all clients
|
||||
@Remote(targets = Loc.server, variants = Variant.both)
|
||||
public static void sendMessage(String message, String sender, Player playersender){
|
||||
public static void sendMessage(String message, String sender, Playerc playersender){
|
||||
if(Vars.ui != null){
|
||||
Vars.ui.chatfrag.addMessage(message, sender);
|
||||
}
|
||||
|
||||
if(playersender != null){
|
||||
playersender.lastText = message;
|
||||
playersender.textFadeTime = 1f;
|
||||
playersender.lastText(message);
|
||||
playersender.textFadeTime(1f);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -158,7 +149,7 @@ public class NetClient implements ApplicationListener{
|
||||
|
||||
//called when a server recieves a chat message from a player
|
||||
@Remote(called = Loc.server, targets = Loc.client)
|
||||
public static void sendChatMessage(Player player, String message){
|
||||
public static void sendChatMessage(Playerc player, String message){
|
||||
if(message.length() > maxTextLength){
|
||||
throw new ValidateException(player, "Player has sent a message above the text limit.");
|
||||
}
|
||||
@@ -176,18 +167,18 @@ public class NetClient implements ApplicationListener{
|
||||
|
||||
//special case; graphical server needs to see its message
|
||||
if(!headless){
|
||||
sendMessage(message, colorizeName(player.id, player.name), player);
|
||||
sendMessage(message, colorizeName(player.id(), player.name()), player);
|
||||
}
|
||||
|
||||
//server console logging
|
||||
Log.info("&y{0}: &lb{1}", player.name, message);
|
||||
Log.info("&y{0}: &lb{1}", player.name(), message);
|
||||
|
||||
//invoke event for all clients but also locally
|
||||
//this is required so other clients get the correct name even if they don't know who's sending it yet
|
||||
Call.sendMessage(message, colorizeName(player.id, player.name), player);
|
||||
Call.sendMessage(message, colorizeName(player.id(), player.name()), player);
|
||||
}else{
|
||||
//log command to console but with brackets
|
||||
Log.info("<&y{0}: &lm{1}&lg>", player.name, message);
|
||||
Log.info("<&y{0}: &lm{1}&lg>", player.name(), message);
|
||||
|
||||
//a command was sent, now get the output
|
||||
if(response.type != ResponseType.valid){
|
||||
@@ -208,23 +199,22 @@ public class NetClient implements ApplicationListener{
|
||||
}
|
||||
|
||||
public static String colorizeName(int id, String name){
|
||||
Player player = playerGroup.getByID(id);
|
||||
Playerc player = Groups.player.getByID(id);
|
||||
if(name == null || player == null) return null;
|
||||
return "[#" + player.color.toString().toUpperCase() + "]" + name;
|
||||
return "[#" + player.color().toString().toUpperCase() + "]" + name;
|
||||
}
|
||||
|
||||
@Remote(called = Loc.client, variants = Variant.one)
|
||||
public static void onConnect(String ip, int port){
|
||||
netClient.disconnectQuietly();
|
||||
state.set(State.menu);
|
||||
logic.reset();
|
||||
|
||||
ui.join.connect(ip, port);
|
||||
}
|
||||
|
||||
@Remote(targets = Loc.client)
|
||||
public static void onPing(Player player, long time){
|
||||
Call.onPingResponse(player.con, time);
|
||||
public static void onPing(Playerc player, long time){
|
||||
Call.onPingResponse(player.con(), time);
|
||||
}
|
||||
|
||||
@Remote(variants = Variant.one)
|
||||
@@ -233,7 +223,7 @@ public class NetClient implements ApplicationListener{
|
||||
}
|
||||
|
||||
@Remote(variants = Variant.one)
|
||||
public static void onTraceInfo(Player player, TraceInfo info){
|
||||
public static void onTraceInfo(Playerc player, TraceInfo info){
|
||||
if(player != null){
|
||||
ui.traces.show(player, info);
|
||||
}
|
||||
@@ -242,7 +232,6 @@ public class NetClient implements ApplicationListener{
|
||||
@Remote(variants = Variant.one, priority = PacketPriority.high)
|
||||
public static void onKick(KickReason reason){
|
||||
netClient.disconnectQuietly();
|
||||
state.set(State.menu);
|
||||
logic.reset();
|
||||
|
||||
if(!reason.quiet){
|
||||
@@ -258,7 +247,6 @@ public class NetClient implements ApplicationListener{
|
||||
@Remote(variants = Variant.one, priority = PacketPriority.high)
|
||||
public static void onKick(String reason){
|
||||
netClient.disconnectQuietly();
|
||||
state.set(State.menu);
|
||||
logic.reset();
|
||||
ui.showText("$disconnect", reason, Align.left);
|
||||
ui.loadfrag.hide();
|
||||
@@ -303,17 +291,18 @@ public class NetClient implements ApplicationListener{
|
||||
ui.showLabel(message, duration, worldx, worldy);
|
||||
}
|
||||
|
||||
/*
|
||||
@Remote(variants = Variant.both, unreliable = true)
|
||||
public static void onEffect(Effect effect, float x, float y, float rotation, Color color){
|
||||
if(effect == null) return;
|
||||
|
||||
Effects.effect(effect, color, x, y, rotation);
|
||||
effect.at(x, y, rotation, color);
|
||||
}
|
||||
|
||||
@Remote(variants = Variant.both)
|
||||
public static void onEffectReliable(Effect effect, float x, float y, float rotation, Color color){
|
||||
onEffect(effect, x, y, rotation, color);
|
||||
}
|
||||
}*/
|
||||
|
||||
@Remote(variants = Variant.both)
|
||||
public static void onInfoToast(String message, float duration){
|
||||
@@ -329,7 +318,7 @@ public class NetClient implements ApplicationListener{
|
||||
|
||||
@Remote(variants = Variant.both)
|
||||
public static void onWorldDataBegin(){
|
||||
entities.clear();
|
||||
Groups.all.clear();
|
||||
netClient.removed.clear();
|
||||
logic.reset();
|
||||
|
||||
@@ -347,60 +336,54 @@ public class NetClient implements ApplicationListener{
|
||||
|
||||
@Remote(variants = Variant.one)
|
||||
public static void onPositionSet(float x, float y){
|
||||
player.x = x;
|
||||
player.y = y;
|
||||
player.set(x, y);
|
||||
}
|
||||
|
||||
@Remote
|
||||
public static void onPlayerDisconnect(int playerid){
|
||||
playerGroup.removeByID(playerid);
|
||||
Groups.player.removeByID(playerid);
|
||||
}
|
||||
|
||||
@Remote(variants = Variant.one, priority = PacketPriority.low, unreliable = true)
|
||||
public static void onEntitySnapshot(byte groupID, short amount, short dataLen, byte[] data){
|
||||
public static void onEntitySnapshot(short amount, short dataLen, byte[] data){
|
||||
try{
|
||||
netClient.byteStream.setBytes(net.decompressSnapshot(data, dataLen));
|
||||
DataInputStream input = netClient.dataStream;
|
||||
|
||||
EntityGroup group = entities.get(groupID);
|
||||
|
||||
//go through each entity
|
||||
for(int j = 0; j < amount; j++){
|
||||
int id = input.readInt();
|
||||
byte typeID = input.readByte();
|
||||
|
||||
SyncTrait entity = group == null ? null : (SyncTrait)group.getByID(id);
|
||||
Syncc entity = Groups.sync.getByID(id);
|
||||
boolean add = false, created = false;
|
||||
|
||||
if(entity == null && id == player.id){
|
||||
if(entity == null && id == player.id()){
|
||||
entity = player;
|
||||
add = true;
|
||||
}
|
||||
|
||||
//entity must not be added yet, so create it
|
||||
if(entity == null){
|
||||
entity = (SyncTrait)content.<TypeID>getByID(ContentType.typeid, typeID).constructor.get();
|
||||
entity.resetID(id);
|
||||
if(!netClient.isEntityUsed(entity.getID())){
|
||||
entity = (Syncc)EntityMapping.map(typeID).get();
|
||||
entity.id(id);
|
||||
if(!netClient.isEntityUsed(entity.id())){
|
||||
add = true;
|
||||
}
|
||||
created = true;
|
||||
}
|
||||
|
||||
//read the entity
|
||||
entity.read(input);
|
||||
entity.read(Reads.get(input));
|
||||
|
||||
if(created && entity.getInterpolator() != null && entity.getInterpolator().target != null){
|
||||
if(created && entity.interpolator().target != null){
|
||||
//set initial starting position
|
||||
entity.setNet(entity.getInterpolator().target.x, entity.getInterpolator().target.y);
|
||||
if(entity instanceof Unit && entity.getInterpolator().targets.length > 0){
|
||||
((Unit)entity).rotation = entity.getInterpolator().targets[0];
|
||||
}
|
||||
entity.setNet(entity.interpolator().target.x, entity.interpolator().target.y);
|
||||
}
|
||||
|
||||
if(add){
|
||||
entity.add();
|
||||
netClient.addRemovedEntity(entity.getID());
|
||||
netClient.addRemovedEntity(entity.id());
|
||||
}
|
||||
}
|
||||
}catch(IOException e){
|
||||
@@ -421,7 +404,7 @@ public class NetClient implements ApplicationListener{
|
||||
Log.warn("Missing entity at {0}. Skipping block snapshot.", tile);
|
||||
break;
|
||||
}
|
||||
tile.entity.read(input, tile.entity.version());
|
||||
tile.entity.readAll(Reads.get(input), tile.entity.version());
|
||||
}
|
||||
}catch(Exception e){
|
||||
e.printStackTrace();
|
||||
@@ -449,9 +432,9 @@ public class NetClient implements ApplicationListener{
|
||||
Tile tile = world.tile(pos);
|
||||
|
||||
if(tile != null && tile.entity != null){
|
||||
tile.entity.items.read(input);
|
||||
tile.entity.items().read(Reads.get(input));
|
||||
}else{
|
||||
new ItemModule().read(input);
|
||||
new ItemModule().read(Reads.get(input));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -464,7 +447,7 @@ public class NetClient implements ApplicationListener{
|
||||
public void update(){
|
||||
if(!net.client()) return;
|
||||
|
||||
if(!state.is(State.menu)){
|
||||
if(state.isGame()){
|
||||
if(!connecting) sync();
|
||||
}else if(!connecting){
|
||||
net.disconnect();
|
||||
@@ -508,7 +491,7 @@ public class NetClient implements ApplicationListener{
|
||||
quiet = false;
|
||||
lastSent = 0;
|
||||
|
||||
entities.clear();
|
||||
Groups.all.clear();
|
||||
ui.chatfrag.clearMessages();
|
||||
}
|
||||
|
||||
@@ -542,22 +525,28 @@ public class NetClient implements ApplicationListener{
|
||||
}
|
||||
|
||||
void sync(){
|
||||
|
||||
if(timer.get(0, playerSyncTime)){
|
||||
BuildRequest[] requests;
|
||||
//limit to 10 to prevent buffer overflows
|
||||
int usedRequests = Math.min(player.buildQueue().size, 10);
|
||||
BuildRequest[] requests = null;
|
||||
if(player.isBuilder() && control.input.isBuilding){
|
||||
//limit to 10 to prevent buffer overflows
|
||||
int usedRequests = Math.min(player.builder().requests().size, 10);
|
||||
|
||||
requests = new BuildRequest[usedRequests];
|
||||
for(int i = 0; i < usedRequests; i++){
|
||||
requests[i] = player.buildQueue().get(i);
|
||||
requests = new BuildRequest[usedRequests];
|
||||
for(int i = 0; i < usedRequests; i++){
|
||||
requests[i] = player.builder().requests().get(i);
|
||||
}
|
||||
}
|
||||
|
||||
Call.onClientShapshot(lastSent++, player.x, player.y,
|
||||
player.pointerX, player.pointerY, player.rotation, player.baseRotation,
|
||||
player.velocity().x, player.velocity().y,
|
||||
player.getMineTile(),
|
||||
player.isBoosting, player.isShooting, ui.chatfrag.shown(), player.isBuilding,
|
||||
Unitc unit = player.dead() ? Nulls.unit : player.unit();
|
||||
|
||||
Call.onClientShapshot(lastSent++,
|
||||
unit.x(), unit.y(),
|
||||
player.mouseX(), player.mouseY(),
|
||||
unit.rotation(),
|
||||
unit instanceof Legsc ? ((Legsc)unit).baseRotation() : 0,
|
||||
unit.vel().x, unit.vel().y,
|
||||
player.miner().mineTile(),
|
||||
/*player.isBoosting*/false, control.input.isShooting, ui.chatfrag.shown(),
|
||||
requests,
|
||||
Core.camera.position.x, Core.camera.position.y,
|
||||
Core.camera.width * viewScale, Core.camera.height * viewScale);
|
||||
|
||||
@@ -6,16 +6,14 @@ import arc.math.*;
|
||||
import arc.math.geom.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import arc.util.ArcAnnotate.*;
|
||||
import arc.util.CommandHandler.*;
|
||||
import arc.util.io.*;
|
||||
import arc.util.serialization.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.core.GameState.*;
|
||||
import mindustry.entities.*;
|
||||
import mindustry.entities.traits.BuilderTrait.*;
|
||||
import mindustry.entities.traits.*;
|
||||
import mindustry.entities.type.*;
|
||||
import mindustry.entities.units.*;
|
||||
import mindustry.game.EventType.*;
|
||||
import mindustry.game.*;
|
||||
import mindustry.game.Teams.*;
|
||||
@@ -51,8 +49,8 @@ public class NetServer implements ApplicationListener{
|
||||
if((state.rules.waveTeam == data.team && state.rules.waves) || !data.team.active()) return Integer.MAX_VALUE;
|
||||
|
||||
int count = 0;
|
||||
for(Player other : players){
|
||||
if(other.getTeam() == data.team && other != player){
|
||||
for(Playerc other : players){
|
||||
if(other.team() == data.team && other != player){
|
||||
count++;
|
||||
}
|
||||
}
|
||||
@@ -67,8 +65,8 @@ public class NetServer implements ApplicationListener{
|
||||
private boolean closing = false;
|
||||
private Interval timer = new Interval();
|
||||
|
||||
private ByteBuffer writeBuffer = ByteBuffer.allocate(127);
|
||||
private ByteBufferOutput outputBuffer = new ByteBufferOutput(writeBuffer);
|
||||
private ReusableByteOutStream writeBuffer = new ReusableByteOutStream(127);
|
||||
private Writes outputBuffer = new Writes(new DataOutputStream(writeBuffer));
|
||||
|
||||
/** Stream for writing player sync data to. */
|
||||
private ReusableByteOutStream syncStream = new ReusableByteOutStream();
|
||||
@@ -133,7 +131,7 @@ public class NetServer implements ApplicationListener{
|
||||
return;
|
||||
}
|
||||
|
||||
if(admins.getPlayerLimit() > 0 && playerGroup.size() >= admins.getPlayerLimit() && !netServer.admins.isAdmin(uuid, packet.usid)){
|
||||
if(admins.getPlayerLimit() > 0 && Groups.player.size() >= admins.getPlayerLimit() && !netServer.admins.isAdmin(uuid, packet.usid)){
|
||||
con.kick(KickReason.playerLimit);
|
||||
return;
|
||||
}
|
||||
@@ -174,16 +172,14 @@ public class NetServer implements ApplicationListener{
|
||||
boolean preventDuplicates = headless && netServer.admins.getStrict();
|
||||
|
||||
if(preventDuplicates){
|
||||
for(Player player : playerGroup.all()){
|
||||
if(player.name.trim().equalsIgnoreCase(packet.name.trim())){
|
||||
con.kick(KickReason.nameInUse);
|
||||
return;
|
||||
}
|
||||
if(Groups.player.contains(p -> p.name().trim().equalsIgnoreCase(packet.name.trim()))){
|
||||
con.kick(KickReason.nameInUse);
|
||||
return;
|
||||
}
|
||||
|
||||
if(player.uuid != null && player.usid != null && (player.uuid.equals(packet.uuid) || player.usid.equals(packet.usid))){
|
||||
con.kick(KickReason.idInUse);
|
||||
return;
|
||||
}
|
||||
if(Groups.player.contains(player -> player.uuid().equals(packet.uuid) || player.usid().equals(packet.usid))){
|
||||
con.kick(KickReason.idInUse);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -207,25 +203,22 @@ public class NetServer implements ApplicationListener{
|
||||
con.modclient = true;
|
||||
}
|
||||
|
||||
Player player = new Player();
|
||||
player.isAdmin = admins.isAdmin(uuid, packet.usid);
|
||||
player.con = con;
|
||||
player.usid = packet.usid;
|
||||
player.name = packet.name;
|
||||
player.uuid = uuid;
|
||||
player.isMobile = packet.mobile;
|
||||
player.dead = true;
|
||||
player.setNet(player.x, player.y);
|
||||
player.color.set(packet.color);
|
||||
player.color.a = 1f;
|
||||
Playerc player = PlayerEntity.create();
|
||||
player.admin(admins.isAdmin(uuid, packet.usid));
|
||||
player.con(con);
|
||||
player.con().usid = packet.usid;
|
||||
player.con().uuid = uuid;
|
||||
player.con().mobile = packet.mobile;
|
||||
player.name(packet.name);
|
||||
player.color().set(packet.color).a(1f);
|
||||
|
||||
//save admin ID but don't overwrite it
|
||||
if(!player.isAdmin && !info.admin){
|
||||
if(!player.admin() && !info.admin){
|
||||
info.adminUsid = packet.usid;
|
||||
}
|
||||
|
||||
try{
|
||||
writeBuffer.position(0);
|
||||
writeBuffer.reset();
|
||||
player.write(outputBuffer);
|
||||
}catch(Throwable t){
|
||||
t.printStackTrace();
|
||||
@@ -236,7 +229,7 @@ public class NetServer implements ApplicationListener{
|
||||
con.player = player;
|
||||
|
||||
//playing in pvp mode automatically assigns players to teams
|
||||
player.setTeam(assignTeam(player, playerGroup.all()));
|
||||
player.team(assignTeam(player));
|
||||
|
||||
sendWorldData(player);
|
||||
|
||||
@@ -248,7 +241,7 @@ public class NetServer implements ApplicationListener{
|
||||
net.handleServer(InvokePacket.class, (con, packet) -> {
|
||||
if(con.player == null) return;
|
||||
try{
|
||||
RemoteReadServer.readPacket(packet.writeBuffer, packet.type, con.player);
|
||||
RemoteReadServer.readPacket(packet.reader(), packet.type, con.player);
|
||||
}catch(ValidateException e){
|
||||
Log.debug("Validation failed for '{0}': {1}", e.player, e.getMessage());
|
||||
}catch(RuntimeException e){
|
||||
@@ -270,7 +263,7 @@ public class NetServer implements ApplicationListener{
|
||||
}
|
||||
|
||||
private void registerCommands(){
|
||||
clientCommands.<Player>register("help", "[page]", "Lists all commands.", (args, player) -> {
|
||||
clientCommands.<Playerc>register("help", "[page]", "Lists all commands.", (args, player) -> {
|
||||
if(args.length > 0 && !Strings.canParseInt(args[0])){
|
||||
player.sendMessage("[scarlet]'page' must be a number.");
|
||||
return;
|
||||
@@ -296,8 +289,8 @@ public class NetServer implements ApplicationListener{
|
||||
player.sendMessage(result.toString());
|
||||
});
|
||||
|
||||
clientCommands.<Player>register("t", "<message...>", "Send a message only to your teammates.", (args, player) -> {
|
||||
playerGroup.all().each(p -> p.getTeam() == player.getTeam(), o -> o.sendMessage(args[0], player, "[#" + player.getTeam().color.toString() + "]<T>" + NetClient.colorizeName(player.id, player.name)));
|
||||
clientCommands.<Playerc>register("t", "<message...>", "Send a message only to your teammates.", (args, player) -> {
|
||||
Groups.player.each(p -> p.team() == player.team(), o -> o.sendMessage(args[0], player, "[#" + player.team().color.toString() + "]<T>" + NetClient.colorizeName(player.id(), player.name())));
|
||||
});
|
||||
|
||||
//duration of a a kick in seconds
|
||||
@@ -308,37 +301,37 @@ public class NetServer implements ApplicationListener{
|
||||
int voteCooldown = 60 * 1;
|
||||
|
||||
class VoteSession{
|
||||
Player target;
|
||||
Playerc target;
|
||||
ObjectSet<String> voted = new ObjectSet<>();
|
||||
VoteSession[] map;
|
||||
Timer.Task task;
|
||||
int votes;
|
||||
|
||||
public VoteSession(VoteSession[] map, Player target){
|
||||
public VoteSession(VoteSession[] map, Playerc target){
|
||||
this.target = target;
|
||||
this.map = map;
|
||||
this.task = Timer.schedule(() -> {
|
||||
if(!checkPass()){
|
||||
Call.sendMessage(Strings.format("[lightgray]Vote failed. Not enough votes to kick[orange] {0}[lightgray].", target.name));
|
||||
Call.sendMessage(Strings.format("[lightgray]Vote failed. Not enough votes to kick[orange] {0}[lightgray].", target.name()));
|
||||
map[0] = null;
|
||||
task.cancel();
|
||||
}
|
||||
}, voteDuration);
|
||||
}
|
||||
|
||||
void vote(Player player, int d){
|
||||
void vote(Playerc player, int d){
|
||||
votes += d;
|
||||
voted.addAll(player.uuid, admins.getInfo(player.uuid).lastIP);
|
||||
voted.addAll(player.uuid(), admins.getInfo(player.uuid()).lastIP);
|
||||
|
||||
Call.sendMessage(Strings.format("[orange]{0}[lightgray] has voted on kicking[orange] {1}[].[accent] ({2}/{3})\n[lightgray]Type[orange] /vote <y/n>[] to agree.",
|
||||
player.name, target.name, votes, votesRequired()));
|
||||
player.name(), target.name(), votes, votesRequired()));
|
||||
}
|
||||
|
||||
boolean checkPass(){
|
||||
if(votes >= votesRequired()){
|
||||
Call.sendMessage(Strings.format("[orange]Vote passed.[scarlet] {0}[orange] will be banned from the server for {1} minutes.", target.name, (kickDuration/60)));
|
||||
Call.sendMessage(Strings.format("[orange]Vote passed.[scarlet] {0}[orange] will be banned from the server for {1} minutes.", target.name(), (kickDuration/60)));
|
||||
target.getInfo().lastKicked = Time.millis() + kickDuration*1000;
|
||||
playerGroup.all().each(p -> p.uuid != null && p.uuid.equals(target.uuid), p -> p.con.kick(KickReason.vote));
|
||||
Groups.player.each(p -> p.uuid().equals(target.uuid()), p -> p.kick(KickReason.vote));
|
||||
map[0] = null;
|
||||
task.cancel();
|
||||
return true;
|
||||
@@ -351,18 +344,18 @@ public class NetServer implements ApplicationListener{
|
||||
//current kick sessions
|
||||
VoteSession[] currentlyKicking = {null};
|
||||
|
||||
clientCommands.<Player>register("votekick", "[player...]", "Vote to kick a player, with a cooldown.", (args, player) -> {
|
||||
clientCommands.<Playerc>register("votekick", "[player...]", "Vote to kick a player, with a cooldown.", (args, player) -> {
|
||||
if(!Config.enableVotekick.bool()){
|
||||
player.sendMessage("[scarlet]Vote-kick is disabled on this server.");
|
||||
return;
|
||||
}
|
||||
|
||||
if(playerGroup.size() < 3){
|
||||
if(Groups.player.size() < 3){
|
||||
player.sendMessage("[scarlet]At least 3 players are needed to start a votekick.");
|
||||
return;
|
||||
}
|
||||
|
||||
if(player.isLocal){
|
||||
if(player.isLocal()){
|
||||
player.sendMessage("[scarlet]Just kick them yourself if you're the host.");
|
||||
return;
|
||||
}
|
||||
@@ -370,27 +363,26 @@ public class NetServer implements ApplicationListener{
|
||||
if(args.length == 0){
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append("[orange]Players to kick: \n");
|
||||
for(Player p : playerGroup.all()){
|
||||
if(p.isAdmin || p.con == null || p == player) continue;
|
||||
|
||||
builder.append("[lightgray] ").append(p.name).append("[accent] (#").append(p.id).append(")\n");
|
||||
}
|
||||
Groups.player.each(p -> !p.admin() && p.con() != null && p != player, p -> {
|
||||
builder.append("[lightgray] ").append(p.name()).append("[accent] (#").append(p.id()).append(")\n");
|
||||
});
|
||||
player.sendMessage(builder.toString());
|
||||
}else{
|
||||
Player found;
|
||||
Playerc found;
|
||||
if(args[0].length() > 1 && args[0].startsWith("#") && Strings.canParseInt(args[0].substring(1))){
|
||||
int id = Strings.parseInt(args[0].substring(1));
|
||||
found = playerGroup.find(p -> p.id == id);
|
||||
found = Groups.player.find(p -> p.id() == id);
|
||||
}else{
|
||||
found = playerGroup.find(p -> p.name.equalsIgnoreCase(args[0]));
|
||||
found = Groups.player.find(p -> p.name().equalsIgnoreCase(args[0]));
|
||||
}
|
||||
|
||||
if(found != null){
|
||||
if(found.isAdmin){
|
||||
if(found.admin()){
|
||||
player.sendMessage("[scarlet]Did you really expect to be able to kick an admin?");
|
||||
}else if(found.isLocal){
|
||||
}else if(found.isLocal()){
|
||||
player.sendMessage("[scarlet]Local players cannot be kicked.");
|
||||
}else if(found.getTeam() != player.getTeam()){
|
||||
}else if(found.team() != player.team()){
|
||||
player.sendMessage("[scarlet]Only players on your team can be kicked.");
|
||||
}else{
|
||||
if(!vtime.get()){
|
||||
@@ -409,17 +401,17 @@ public class NetServer implements ApplicationListener{
|
||||
}
|
||||
});
|
||||
|
||||
clientCommands.<Player>register("vote", "<y/n>", "Vote to kick the current player.", (arg, player) -> {
|
||||
clientCommands.<Playerc>register("vote", "<y/n>", "Vote to kick the current player.", (arg, player) -> {
|
||||
if(currentlyKicking[0] == null){
|
||||
player.sendMessage("[scarlet]Nobody is being voted on.");
|
||||
}else{
|
||||
if(player.isLocal){
|
||||
if(player.isLocal()){
|
||||
player.sendMessage("Local players can't vote. Kick the player yourself instead.");
|
||||
return;
|
||||
}
|
||||
|
||||
//hosts can vote all they want
|
||||
if(player.uuid != null && (currentlyKicking[0].voted.contains(player.uuid) || currentlyKicking[0].voted.contains(admins.getInfo(player.uuid).lastIP))){
|
||||
if((currentlyKicking[0].voted.contains(player.uuid()) || currentlyKicking[0].voted.contains(admins.getInfo(player.uuid()).lastIP))){
|
||||
player.sendMessage("[scarlet]You've already voted. Sit down.");
|
||||
return;
|
||||
}
|
||||
@@ -440,8 +432,8 @@ public class NetServer implements ApplicationListener{
|
||||
});
|
||||
|
||||
|
||||
clientCommands.<Player>register("sync", "Re-synchronize world state.", (args, player) -> {
|
||||
if(player.isLocal){
|
||||
clientCommands.<Playerc>register("sync", "Re-synchronize world state.", (args, player) -> {
|
||||
if(player.isLocal()){
|
||||
player.sendMessage("[scarlet]Re-synchronizing as the host is pointless.");
|
||||
}else{
|
||||
if(Time.timeSinceMillis(player.getInfo().lastSyncTime) < 1000 * 5){
|
||||
@@ -450,69 +442,73 @@ public class NetServer implements ApplicationListener{
|
||||
}
|
||||
|
||||
player.getInfo().lastSyncTime = Time.millis();
|
||||
Call.onWorldDataBegin(player.con);
|
||||
Call.onWorldDataBegin(player.con());
|
||||
netServer.sendWorldData(player);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public int votesRequired(){
|
||||
return 2 + (playerGroup.size() > 4 ? 1 : 0);
|
||||
return 2 + (Groups.player.size() > 4 ? 1 : 0);
|
||||
}
|
||||
|
||||
public Team assignTeam(Player current, Iterable<Player> players){
|
||||
public Team assignTeam(Playerc current){
|
||||
return assigner.assign(current, Groups.player);
|
||||
}
|
||||
|
||||
public Team assignTeam(Playerc current, Iterable<Playerc> players){
|
||||
return assigner.assign(current, players);
|
||||
}
|
||||
|
||||
public void sendWorldData(Player player){
|
||||
public void sendWorldData(Playerc player){
|
||||
ByteArrayOutputStream stream = new ByteArrayOutputStream();
|
||||
DeflaterOutputStream def = new FastDeflaterOutputStream(stream);
|
||||
NetworkIO.writeWorld(player, def);
|
||||
WorldStream data = new WorldStream();
|
||||
data.stream = new ByteArrayInputStream(stream.toByteArray());
|
||||
player.con.sendStream(data);
|
||||
player.con().sendStream(data);
|
||||
|
||||
Log.debug("Packed {0} compressed bytes of world data.", stream.size());
|
||||
}
|
||||
|
||||
public static void onDisconnect(Player player, String reason){
|
||||
public static void onDisconnect(Playerc player, String reason){
|
||||
//singleplayer multiplayer wierdness
|
||||
if(player.con == null){
|
||||
if(player.con() == null){
|
||||
player.remove();
|
||||
return;
|
||||
}
|
||||
|
||||
if(!player.con.hasDisconnected){
|
||||
if(player.con.hasConnected){
|
||||
if(!player.con().hasDisconnected){
|
||||
if(player.con().hasConnected){
|
||||
Events.fire(new PlayerLeave(player));
|
||||
if(Config.showConnectMessages.bool()) Call.sendMessage("[accent]" + player.name + "[accent] has disconnected.");
|
||||
Call.onPlayerDisconnect(player.id);
|
||||
if(Config.showConnectMessages.bool()) Call.sendMessage("[accent]" + player.name() + "[accent] has disconnected.");
|
||||
Call.onPlayerDisconnect(player.id());
|
||||
}
|
||||
|
||||
if(Config.showConnectMessages.bool()) Log.info("&lm[{1}] &lc{0} has disconnected. &lg&fi({2})", player.name, player.uuid, reason);
|
||||
if(Config.showConnectMessages.bool()) Log.info("&lm[{1}] &lc{0} has disconnected. &lg&fi({2})", player.name(), player.uuid(), reason);
|
||||
}
|
||||
|
||||
player.remove();
|
||||
player.con.hasDisconnected = true;
|
||||
player.con().hasDisconnected = true;
|
||||
}
|
||||
|
||||
@Remote(targets = Loc.client, unreliable = true)
|
||||
public static void onClientShapshot(
|
||||
Player player,
|
||||
Playerc player,
|
||||
int snapshotID,
|
||||
float x, float y,
|
||||
float pointerX, float pointerY,
|
||||
float rotation, float baseRotation,
|
||||
float xVelocity, float yVelocity,
|
||||
Tile mining,
|
||||
boolean boosting, boolean shooting, boolean chatting, boolean building,
|
||||
BuildRequest[] requests,
|
||||
boolean boosting, boolean shooting, boolean chatting,
|
||||
@Nullable BuildRequest[] requests,
|
||||
float viewX, float viewY, float viewWidth, float viewHeight
|
||||
){
|
||||
NetConnection connection = player.con;
|
||||
NetConnection connection = player.con();
|
||||
if(connection == null || snapshotID < connection.lastRecievedClientSnapshot) return;
|
||||
|
||||
boolean verifyPosition = !player.isDead() && netServer.admins.getStrict() && headless;
|
||||
boolean verifyPosition = !player.dead() && netServer.admins.getStrict() && headless;
|
||||
|
||||
if(connection.lastRecievedClientTime == 0) connection.lastRecievedClientTime = Time.millis() - 16;
|
||||
|
||||
@@ -521,91 +517,103 @@ public class NetServer implements ApplicationListener{
|
||||
connection.viewWidth = viewWidth;
|
||||
connection.viewHeight = viewHeight;
|
||||
|
||||
long elapsed = Time.timeSinceMillis(connection.lastRecievedClientTime);
|
||||
player.mouseX(pointerX);
|
||||
player.mouseY(pointerY);
|
||||
player.typing(chatting);
|
||||
|
||||
float maxSpeed = boosting && !player.mech.flying ? player.mech.compoundSpeedBoost : player.mech.compoundSpeed;
|
||||
float maxMove = elapsed / 1000f * 60f * Math.min(maxSpeed, player.mech.maxSpeed) * 1.2f;
|
||||
player.unit().controlWeapons(shooting, shooting);
|
||||
player.unit().aim(pointerX, pointerY);
|
||||
|
||||
player.pointerX = pointerX;
|
||||
player.pointerY = pointerY;
|
||||
player.setMineTile(mining);
|
||||
player.isTyping = chatting;
|
||||
player.isBoosting = boosting;
|
||||
player.isShooting = shooting;
|
||||
player.isBuilding = building;
|
||||
player.buildQueue().clear();
|
||||
if(player.isBuilder()){
|
||||
player.builder().clearBuilding();
|
||||
}
|
||||
|
||||
for(BuildRequest req : requests){
|
||||
if(req == null) continue;
|
||||
Tile tile = world.tile(req.x, req.y);
|
||||
if(tile == null || (!req.breaking && req.block == null)) continue;
|
||||
//auto-skip done requests
|
||||
if(req.breaking && tile.block() == Blocks.air){
|
||||
continue;
|
||||
}else if(!req.breaking && tile.block() == req.block && (!req.block.rotate || tile.rotation() == req.rotation)){
|
||||
continue;
|
||||
}else if(connection.rejectedRequests.contains(r -> r.breaking == req.breaking && r.x == req.x && r.y == req.y)){ //check if request was recently rejected, and skip it if so
|
||||
continue;
|
||||
}else if(!netServer.admins.allowAction(player, req.breaking ? ActionType.breakBlock : ActionType.placeBlock, tile, action -> { //make sure request is allowed by the server
|
||||
action.block = req.block;
|
||||
action.rotation = req.rotation;
|
||||
action.config = req.config;
|
||||
})){
|
||||
//force the player to remove this request if that's not the case
|
||||
Call.removeQueueBlock(player.con, req.x, req.y, req.breaking);
|
||||
connection.rejectedRequests.add(req);
|
||||
continue;
|
||||
if(player.isMiner()){
|
||||
player.miner().mineTile(mining);
|
||||
}
|
||||
|
||||
if(requests != null){
|
||||
for(BuildRequest req : requests){
|
||||
if(req == null) continue;
|
||||
Tile tile = world.tile(req.x, req.y);
|
||||
if(tile == null || (!req.breaking && req.block == null)) continue;
|
||||
//auto-skip done requests
|
||||
if(req.breaking && tile.block() == Blocks.air){
|
||||
continue;
|
||||
}else if(!req.breaking && tile.block() == req.block && (!req.block.rotate || tile.rotation() == req.rotation)){
|
||||
continue;
|
||||
}else if(connection.rejectedRequests.contains(r -> r.breaking == req.breaking && r.x == req.x && r.y == req.y)){ //check if request was recently rejected, and skip it if so
|
||||
continue;
|
||||
}else if(!netServer.admins.allowAction(player, req.breaking ? ActionType.breakBlock : ActionType.placeBlock, tile, action -> { //make sure request is allowed by the server
|
||||
action.block = req.block;
|
||||
action.rotation = req.rotation;
|
||||
action.config = req.config;
|
||||
})){
|
||||
//force the player to remove this request if that's not the case
|
||||
Call.removeQueueBlock(player.con(), req.x, req.y, req.breaking);
|
||||
connection.rejectedRequests.add(req);
|
||||
continue;
|
||||
}
|
||||
player.builder().requests().addLast(req);
|
||||
}
|
||||
player.buildQueue().addLast(req);
|
||||
}
|
||||
|
||||
connection.rejectedRequests.clear();
|
||||
|
||||
vector.set(x - player.getInterpolator().target.x, y - player.getInterpolator().target.y);
|
||||
vector.limit(maxMove);
|
||||
if(!player.dead()){
|
||||
Unitc unit = player.unit();
|
||||
long elapsed = Time.timeSinceMillis(connection.lastRecievedClientTime);
|
||||
float maxSpeed = player.dead() ? Float.MAX_VALUE : player.unit().type().speed;
|
||||
float maxMove = elapsed / 1000f * 60f * maxSpeed * 1.1f;
|
||||
|
||||
float prevx = player.x, prevy = player.y;
|
||||
player.set(player.getInterpolator().target.x, player.getInterpolator().target.y);
|
||||
if(!player.mech.flying && player.boostHeat < 0.01f){
|
||||
player.move(vector.x, vector.y);
|
||||
vector.set(x - unit.interpolator().target.x, y - unit.interpolator().target.y);
|
||||
vector.limit(maxMove);
|
||||
|
||||
float prevx = unit.x(), prevy = unit.y();
|
||||
unit.set(unit.interpolator().target.x, unit.interpolator().target.y);
|
||||
if(!unit.isFlying()){
|
||||
unit.move(vector.x, vector.y);
|
||||
}else{
|
||||
unit.trns(vector.x, vector.y);
|
||||
}
|
||||
float newx = unit.x(), newy = unit.y();
|
||||
|
||||
if(!verifyPosition){
|
||||
unit.x(prevx);
|
||||
unit.y(prevy);
|
||||
newx = x;
|
||||
newy = y;
|
||||
}else if(Mathf.dst(x, y, newx, newy) > correctDist){
|
||||
Call.onPositionSet(player.con(), newx, newy); //teleport and correct position when necessary
|
||||
}
|
||||
|
||||
//reset player to previous synced position so it gets interpolated
|
||||
unit.x(prevx);
|
||||
unit.y(prevy);
|
||||
|
||||
//set interpolator target to *new* position so it moves toward it
|
||||
unit.interpolator().read(unit.x(), unit.y(), newx, newy, rotation, baseRotation);
|
||||
unit.vel().set(xVelocity, yVelocity); //only for visual calculation purposes, doesn't actually update the player
|
||||
}else{
|
||||
player.x += vector.x;
|
||||
player.y += vector.y;
|
||||
player.x(x);
|
||||
player.y(y);
|
||||
}
|
||||
float newx = player.x, newy = player.y;
|
||||
|
||||
if(!verifyPosition){
|
||||
player.x = prevx;
|
||||
player.y = prevy;
|
||||
newx = x;
|
||||
newy = y;
|
||||
}else if(Mathf.dst(x, y, newx, newy) > correctDist){
|
||||
Call.onPositionSet(player.con, newx, newy); //teleport and correct position when necessary
|
||||
}
|
||||
|
||||
//reset player to previous synced position so it gets interpolated
|
||||
player.x = prevx;
|
||||
player.y = prevy;
|
||||
|
||||
//set interpolator target to *new* position so it moves toward it
|
||||
player.getInterpolator().read(player.x, player.y, newx, newy, rotation, baseRotation);
|
||||
player.velocity().set(xVelocity, yVelocity); //only for visual calculation purposes, doesn't actually update the player
|
||||
|
||||
connection.lastRecievedClientSnapshot = snapshotID;
|
||||
connection.lastRecievedClientTime = Time.millis();
|
||||
}
|
||||
|
||||
@Remote(targets = Loc.client, called = Loc.server)
|
||||
public static void onAdminRequest(Player player, Player other, AdminAction action){
|
||||
public static void onAdminRequest(Playerc player, Playerc other, AdminAction action){
|
||||
|
||||
if(!player.isAdmin){
|
||||
if(!player.admin()){
|
||||
Log.warn("ACCESS DENIED: Player {0} / {1} attempted to perform admin action without proper security access.",
|
||||
player.name, player.con.address);
|
||||
player.name(), player.con().address);
|
||||
return;
|
||||
}
|
||||
|
||||
if(other == null || ((other.isAdmin && !player.isLocal) && other != player)){
|
||||
Log.warn("{0} attempted to perform admin action on nonexistant or admin player.", player.name);
|
||||
if(other == null || ((other.admin() && !player.isLocal()) && other != player)){
|
||||
Log.warn("{0} attempted to perform admin action on nonexistant or admin player.", player.name());
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -614,32 +622,32 @@ public class NetServer implements ApplicationListener{
|
||||
//not a real issue, because server owners may want to do just that
|
||||
state.wavetime = 0f;
|
||||
}else if(action == AdminAction.ban){
|
||||
netServer.admins.banPlayerIP(other.con.address);
|
||||
other.con.kick(KickReason.banned);
|
||||
Log.info("&lc{0} has banned {1}.", player.name, other.name);
|
||||
netServer.admins.banPlayerIP(other.con().address);
|
||||
other.kick(KickReason.banned);
|
||||
Log.info("&lc{0} has banned {1}.", player.name(), other.name());
|
||||
}else if(action == AdminAction.kick){
|
||||
other.con.kick(KickReason.kick);
|
||||
Log.info("&lc{0} has kicked {1}.", player.name, other.name);
|
||||
other.kick(KickReason.kick);
|
||||
Log.info("&lc{0} has kicked {1}.", player.name(), other.name());
|
||||
}else if(action == AdminAction.trace){
|
||||
TraceInfo info = new TraceInfo(other.con.address, other.uuid, other.con.modclient, other.con.mobile);
|
||||
if(player.con != null){
|
||||
Call.onTraceInfo(player.con, other, info);
|
||||
TraceInfo info = new TraceInfo(other.con().address, other.uuid(), other.con().modclient, other.con().mobile);
|
||||
if(player.con() != null){
|
||||
Call.onTraceInfo(player.con(), other, info);
|
||||
}else{
|
||||
NetClient.onTraceInfo(other, info);
|
||||
}
|
||||
Log.info("&lc{0} has requested trace info of {1}.", player.name, other.name);
|
||||
Log.info("&lc{0} has requested trace info of {1}.", player.name(), other.name());
|
||||
}
|
||||
}
|
||||
|
||||
@Remote(targets = Loc.client)
|
||||
public static void connectConfirm(Player player){
|
||||
if(player.con == null || player.con.hasConnected) return;
|
||||
public static void connectConfirm(Playerc player){
|
||||
if(player.con() == null || player.con().hasConnected) return;
|
||||
|
||||
player.add();
|
||||
player.con.hasConnected = true;
|
||||
player.con().hasConnected = true;
|
||||
if(Config.showConnectMessages.bool()){
|
||||
Call.sendMessage("[accent]" + player.name + "[accent] has connected.");
|
||||
Log.info("&lm[{1}] &y{0} has connected. ", player.name, player.uuid);
|
||||
Call.sendMessage("[accent]" + player.name() + "[accent] has connected.");
|
||||
Log.info("&lm[{1}] &y{0} has connected. ", player.name(), player.uuid());
|
||||
}
|
||||
|
||||
if(!Config.motd.string().equalsIgnoreCase("off")){
|
||||
@@ -653,7 +661,7 @@ public class NetServer implements ApplicationListener{
|
||||
if(state.rules.pvp){
|
||||
int used = 0;
|
||||
for(TeamData t : state.teams.getActive()){
|
||||
if(playerGroup.count(p -> p.getTeam() == t.team) > 0){
|
||||
if(Groups.player.count(p -> p.team() == t.team) > 0){
|
||||
used++;
|
||||
}
|
||||
}
|
||||
@@ -665,7 +673,7 @@ public class NetServer implements ApplicationListener{
|
||||
@Override
|
||||
public void update(){
|
||||
|
||||
if(!headless && !closing && net.server() && state.is(State.menu)){
|
||||
if(!headless && !closing && net.server() && state.isMenu()){
|
||||
closing = true;
|
||||
ui.loadfrag.show("$server.closing");
|
||||
Time.runTask(5f, () -> {
|
||||
@@ -675,7 +683,7 @@ public class NetServer implements ApplicationListener{
|
||||
});
|
||||
}
|
||||
|
||||
if(!state.is(State.menu) && net.server()){
|
||||
if(state.isGame() && net.server()){
|
||||
sync();
|
||||
}
|
||||
}
|
||||
@@ -705,12 +713,12 @@ public class NetServer implements ApplicationListener{
|
||||
syncStream.reset();
|
||||
|
||||
short sent = 0;
|
||||
for(TileEntity entity : tileGroup.all()){
|
||||
if(!entity.block.sync) continue;
|
||||
for(Tilec entity : Groups.tile){
|
||||
if(!entity.block().sync) continue;
|
||||
sent ++;
|
||||
|
||||
dataStream.writeInt(entity.tile.pos());
|
||||
entity.write(dataStream);
|
||||
dataStream.writeInt(entity.tile().pos());
|
||||
entity.writeAll(Writes.get(dataStream));
|
||||
|
||||
if(syncStream.size() > maxSnapshotSize){
|
||||
dataStream.close();
|
||||
@@ -728,65 +736,53 @@ public class NetServer implements ApplicationListener{
|
||||
}
|
||||
}
|
||||
|
||||
public void writeEntitySnapshot(Player player) throws IOException{
|
||||
public void writeEntitySnapshot(Playerc player) throws IOException{
|
||||
syncStream.reset();
|
||||
Array<CoreEntity> cores = state.teams.cores(player.getTeam());
|
||||
Array<CoreEntity> cores = state.teams.cores(player.team());
|
||||
|
||||
dataStream.writeByte(cores.size);
|
||||
|
||||
for(CoreEntity entity : cores){
|
||||
dataStream.writeInt(entity.tile.pos());
|
||||
entity.items.write(dataStream);
|
||||
dataStream.writeInt(entity.tile().pos());
|
||||
entity.items().write(Writes.get(dataStream));
|
||||
}
|
||||
|
||||
dataStream.close();
|
||||
byte[] stateBytes = syncStream.toByteArray();
|
||||
|
||||
//write basic state data.
|
||||
Call.onStateSnapshot(player.con, state.wavetime, state.wave, state.enemies, (short)stateBytes.length, net.compressSnapshot(stateBytes));
|
||||
Call.onStateSnapshot(player.con(), state.wavetime, state.wave, state.enemies, (short)stateBytes.length, net.compressSnapshot(stateBytes));
|
||||
|
||||
viewport.setSize(player.con.viewWidth, player.con.viewHeight).setCenter(player.con.viewX, player.con.viewY);
|
||||
viewport.setSize(player.con().viewWidth, player.con().viewHeight).setCenter(player.con().viewX, player.con().viewY);
|
||||
|
||||
//check for syncable groups
|
||||
for(EntityGroup<?> group : entities.all()){
|
||||
if(group.isEmpty() || !(group.all().get(0) instanceof SyncTrait)) continue;
|
||||
syncStream.reset();
|
||||
|
||||
//make sure mapping is enabled for this group
|
||||
if(!group.mappingEnabled()){
|
||||
throw new RuntimeException("Entity group '" + group.getType() + "' contains SyncTrait entities, yet mapping is not enabled. In order for syncing to work, you must enable mapping for this group.");
|
||||
}
|
||||
int sent = 0;
|
||||
|
||||
syncStream.reset();
|
||||
for(Syncc entity : Groups.sync){
|
||||
//write all entities now
|
||||
dataStream.writeInt(entity.id()); //write id
|
||||
dataStream.writeByte(entity.classId()); //write type ID
|
||||
entity.write(Writes.get(dataStream)); //write entity
|
||||
|
||||
int sent = 0;
|
||||
sent++;
|
||||
|
||||
for(Entity entity : group.all()){
|
||||
SyncTrait sync = (SyncTrait)entity;
|
||||
if(!sync.isSyncing()) continue;
|
||||
|
||||
//write all entities now
|
||||
dataStream.writeInt(entity.getID()); //write id
|
||||
dataStream.writeByte(sync.getTypeID().id); //write type ID
|
||||
sync.write(dataStream); //write entity
|
||||
|
||||
sent++;
|
||||
|
||||
if(syncStream.size() > maxSnapshotSize){
|
||||
dataStream.close();
|
||||
byte[] syncBytes = syncStream.toByteArray();
|
||||
Call.onEntitySnapshot(player.con, (byte)group.getID(), (short)sent, (short)syncBytes.length, net.compressSnapshot(syncBytes));
|
||||
sent = 0;
|
||||
syncStream.reset();
|
||||
}
|
||||
}
|
||||
|
||||
if(sent > 0){
|
||||
if(syncStream.size() > maxSnapshotSize){
|
||||
dataStream.close();
|
||||
|
||||
byte[] syncBytes = syncStream.toByteArray();
|
||||
Call.onEntitySnapshot(player.con, (byte)group.getID(), (short)sent, (short)syncBytes.length, net.compressSnapshot(syncBytes));
|
||||
Call.onEntitySnapshot(player.con(), (short)sent, (short)syncBytes.length, net.compressSnapshot(syncBytes));
|
||||
sent = 0;
|
||||
syncStream.reset();
|
||||
}
|
||||
}
|
||||
|
||||
if(sent > 0){
|
||||
dataStream.close();
|
||||
|
||||
byte[] syncBytes = syncStream.toByteArray();
|
||||
Call.onEntitySnapshot(player.con(), (short)sent, (short)syncBytes.length, net.compressSnapshot(syncBytes));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
String fixName(String name){
|
||||
@@ -842,24 +838,24 @@ public class NetServer implements ApplicationListener{
|
||||
void sync(){
|
||||
|
||||
try{
|
||||
//iterate through each player
|
||||
for(int i = 0; i < playerGroup.size(); i++){
|
||||
Player player = playerGroup.all().get(i);
|
||||
if(player.isLocal) continue;
|
||||
|
||||
if(player.con == null || !player.con.isConnected()){
|
||||
Groups.player.each(p -> !p.isLocal(), player -> {
|
||||
if(player.con() == null || !player.con().isConnected()){
|
||||
onDisconnect(player, "disappeared");
|
||||
continue;
|
||||
return;
|
||||
}
|
||||
|
||||
NetConnection connection = player.con;
|
||||
NetConnection connection = player.con();
|
||||
|
||||
if(!player.timer.get(Player.timerSync, serverSyncTime) || !connection.hasConnected) continue;
|
||||
if(!player.timer(0, serverSyncTime) || !connection.hasConnected) return;
|
||||
|
||||
writeEntitySnapshot(player);
|
||||
}
|
||||
try{
|
||||
writeEntitySnapshot(player);
|
||||
}catch(IOException e){
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
|
||||
if(playerGroup.size() > 0 && Core.settings.getBool("blocksync") && timer.get(timerBlockSync, blockSyncTime)){
|
||||
if(Groups.player.size() > 0 && Core.settings.getBool("blocksync") && timer.get(timerBlockSync, blockSyncTime)){
|
||||
writeBlockSnapshots();
|
||||
}
|
||||
|
||||
@@ -869,6 +865,6 @@ public class NetServer implements ApplicationListener{
|
||||
}
|
||||
|
||||
public interface TeamAssigner{
|
||||
Team assign(Player player, Iterable<Player> players);
|
||||
Team assign(Playerc player, Iterable<Playerc> players);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,11 +2,12 @@ package mindustry.core;
|
||||
|
||||
import arc.*;
|
||||
import arc.Input.*;
|
||||
import arc.struct.*;
|
||||
import arc.files.*;
|
||||
import arc.func.*;
|
||||
import arc.math.*;
|
||||
import arc.scene.ui.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import arc.util.serialization.*;
|
||||
import mindustry.mod.*;
|
||||
import mindustry.net.*;
|
||||
@@ -15,7 +16,7 @@ import mindustry.type.*;
|
||||
import mindustry.ui.dialogs.*;
|
||||
import org.mozilla.javascript.*;
|
||||
|
||||
import static mindustry.Vars.mobile;
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public interface Platform{
|
||||
|
||||
@@ -103,6 +104,32 @@ public interface Platform{
|
||||
default void shareFile(Fi file){
|
||||
}
|
||||
|
||||
default void export(String name, String extension, FileWriter writer){
|
||||
if(!ios){
|
||||
platform.showFileChooser(false, extension, file -> {
|
||||
ui.loadAnd(() -> {
|
||||
try{
|
||||
writer.write(file);
|
||||
}catch(Throwable e){
|
||||
ui.showException(e);
|
||||
Log.err(e);
|
||||
}
|
||||
});
|
||||
});
|
||||
}else{
|
||||
ui.loadAnd(() -> {
|
||||
try{
|
||||
Fi result = Core.files.local(name+ "." + extension);
|
||||
writer.write(result);
|
||||
platform.shareFile(result);
|
||||
}catch(Throwable e){
|
||||
ui.showException(e);
|
||||
Log.err(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Show a file chooser.
|
||||
* @param cons Selection listener
|
||||
@@ -130,4 +157,8 @@ public interface Platform{
|
||||
/** Stops forcing the app into landscape orientation.*/
|
||||
default void endForceLandscape(){
|
||||
}
|
||||
|
||||
interface FileWriter{
|
||||
void write(Fi file) throws Throwable;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,27 +2,18 @@ package mindustry.core;
|
||||
|
||||
import arc.*;
|
||||
import arc.files.*;
|
||||
import arc.func.*;
|
||||
import arc.fx.*;
|
||||
import arc.graphics.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.graphics.gl.*;
|
||||
import arc.math.*;
|
||||
import arc.math.geom.*;
|
||||
import arc.scene.ui.layout.*;
|
||||
import arc.util.*;
|
||||
import arc.util.pooling.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.core.GameState.*;
|
||||
import mindustry.entities.*;
|
||||
import mindustry.entities.effect.*;
|
||||
import mindustry.entities.effect.GroundEffectEntity.*;
|
||||
import mindustry.entities.traits.*;
|
||||
import mindustry.entities.type.*;
|
||||
import mindustry.game.EventType.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.input.*;
|
||||
import mindustry.ui.*;
|
||||
import mindustry.world.blocks.defense.ForceProjector.*;
|
||||
|
||||
import static arc.Core.*;
|
||||
import static mindustry.Vars.*;
|
||||
@@ -34,63 +25,26 @@ public class Renderer implements ApplicationListener{
|
||||
public final LightRenderer lights = new LightRenderer();
|
||||
public final Pixelator pixelator = new Pixelator();
|
||||
|
||||
public FrameBuffer shieldBuffer = new FrameBuffer(2, 2);
|
||||
public FrameBuffer effectBuffer = new FrameBuffer(2, 2);
|
||||
private Bloom bloom;
|
||||
private Color clearColor;
|
||||
private FxProcessor fx = new FxProcessor();
|
||||
private Color clearColor = new Color(0f, 0f, 0f, 1f);
|
||||
private float targetscale = Scl.scl(4);
|
||||
private float camerascale = targetscale;
|
||||
private float landscale = 0f, landTime;
|
||||
private float minZoomScl = Scl.scl(0.01f);
|
||||
private Rect rect = new Rect(), rect2 = new Rect();
|
||||
private float shakeIntensity, shaketime;
|
||||
|
||||
public Renderer(){
|
||||
camera = new Camera();
|
||||
Shaders.init();
|
||||
|
||||
Effects.setScreenShakeProvider((intensity, duration) -> {
|
||||
shakeIntensity = Math.max(intensity, shakeIntensity);
|
||||
shaketime = Math.max(shaketime, duration);
|
||||
});
|
||||
//fx.addEffect(new SnowFilter());
|
||||
}
|
||||
|
||||
Effects.setEffectProvider((effect, color, x, y, rotation, data) -> {
|
||||
if(effect == Fx.none) return;
|
||||
if(Core.settings.getBool("effects")){
|
||||
Rect view = camera.bounds(rect);
|
||||
Rect pos = rect2.setSize(effect.size).setCenter(x, y);
|
||||
|
||||
if(view.overlaps(pos)){
|
||||
|
||||
if(!(effect instanceof GroundEffect)){
|
||||
EffectEntity entity = Pools.obtain(EffectEntity.class, EffectEntity::new);
|
||||
entity.effect = effect;
|
||||
entity.color.set(color);
|
||||
entity.rotation = rotation;
|
||||
entity.data = data;
|
||||
entity.id++;
|
||||
entity.set(x, y);
|
||||
if(data instanceof Entity){
|
||||
entity.setParent((Entity)data);
|
||||
}
|
||||
effectGroup.add(entity);
|
||||
}else{
|
||||
GroundEffectEntity entity = Pools.obtain(GroundEffectEntity.class, GroundEffectEntity::new);
|
||||
entity.effect = effect;
|
||||
entity.color.set(color);
|
||||
entity.rotation = rotation;
|
||||
entity.id++;
|
||||
entity.data = data;
|
||||
entity.set(x, y);
|
||||
if(data instanceof Entity){
|
||||
entity.setParent((Entity)data);
|
||||
}
|
||||
groundEffectGroup.add(entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
clearColor = new Color(0f, 0f, 0f, 1f);
|
||||
public void shake(float intensity, float duration){
|
||||
shakeIntensity = Math.max(shakeIntensity, intensity);
|
||||
shaketime = Math.max(shaketime, duration);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -115,25 +69,10 @@ public class Renderer implements ApplicationListener{
|
||||
camera.width = graphics.getWidth() / camerascale;
|
||||
camera.height = graphics.getHeight() / camerascale;
|
||||
|
||||
if(state.is(State.menu)){
|
||||
if(state.isMenu()){
|
||||
landTime = 0f;
|
||||
graphics.clear(Color.black);
|
||||
}else{
|
||||
Vec2 position = Tmp.v3.set(player);
|
||||
|
||||
if(player.isDead()){
|
||||
TileEntity core = player.getClosestCore();
|
||||
if(core != null){
|
||||
if(player.spawner == null){
|
||||
camera.position.lerpDelta(core.x, core.y, 0.08f);
|
||||
}else{
|
||||
camera.position.lerpDelta(position, 0.08f);
|
||||
}
|
||||
}
|
||||
}else if(control.input instanceof DesktopInput && !state.isPaused()){
|
||||
camera.position.lerpDelta(position, 0.08f);
|
||||
}
|
||||
|
||||
updateShake(0.75f);
|
||||
if(pixelator.enabled()){
|
||||
pixelator.drawPixelate();
|
||||
@@ -150,7 +89,7 @@ public class Renderer implements ApplicationListener{
|
||||
@Override
|
||||
public void dispose(){
|
||||
minimap.dispose();
|
||||
shieldBuffer.dispose();
|
||||
effectBuffer.dispose();
|
||||
blocks.dispose();
|
||||
if(bloom != null){
|
||||
bloom.dispose();
|
||||
@@ -164,6 +103,8 @@ public class Renderer implements ApplicationListener{
|
||||
if(settings.getBool("bloom")){
|
||||
setupBloom();
|
||||
}
|
||||
|
||||
fx.resize(width, height);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -181,7 +122,7 @@ public class Renderer implements ApplicationListener{
|
||||
}
|
||||
bloom = new Bloom(true);
|
||||
bloom.setClearColor(0f, 0f, 0f, 0f);
|
||||
}catch(Exception e){
|
||||
}catch(Throwable e){
|
||||
e.printStackTrace();
|
||||
settings.put("bloom", false);
|
||||
settings.save();
|
||||
@@ -202,6 +143,23 @@ public class Renderer implements ApplicationListener{
|
||||
}
|
||||
}
|
||||
|
||||
void beginFx(){
|
||||
if(!fx.hasEnabledEffects()) return;
|
||||
|
||||
Draw.flush();
|
||||
fx.clear();
|
||||
fx.begin();
|
||||
}
|
||||
|
||||
void endFx(){
|
||||
if(!fx.hasEnabledEffects()) return;
|
||||
|
||||
Draw.flush();
|
||||
fx.end();
|
||||
fx.applyEffects();
|
||||
fx.render(0, 0, fx.getWidth(), fx.getHeight());
|
||||
}
|
||||
|
||||
void updateShake(float scale){
|
||||
if(shaketime > 0){
|
||||
float intensity = shakeIntensity * (settings.getInt("screenshake", 4) / 4f) * scale;
|
||||
@@ -218,26 +176,28 @@ public class Renderer implements ApplicationListener{
|
||||
camera.update();
|
||||
|
||||
if(Float.isNaN(camera.position.x) || Float.isNaN(camera.position.y)){
|
||||
camera.position.x = player.x;
|
||||
camera.position.y = player.y;
|
||||
camera.position.set(player);
|
||||
}
|
||||
|
||||
graphics.clear(clearColor);
|
||||
|
||||
if(!graphics.isHidden() && (Core.settings.getBool("animatedwater") || Core.settings.getBool("animatedshields")) && (shieldBuffer.getWidth() != graphics.getWidth() || shieldBuffer.getHeight() != graphics.getHeight())){
|
||||
shieldBuffer.resize(graphics.getWidth(), graphics.getHeight());
|
||||
if(!graphics.isHidden() && (Core.settings.getBool("animatedwater") || Core.settings.getBool("animatedshields")) && (effectBuffer.getWidth() != graphics.getWidth() || effectBuffer.getHeight() != graphics.getHeight())){
|
||||
effectBuffer.resize(graphics.getWidth(), graphics.getHeight());
|
||||
}
|
||||
|
||||
Draw.proj(camera.projection());
|
||||
Draw.proj(camera);
|
||||
|
||||
beginFx();
|
||||
|
||||
drawBackground();
|
||||
|
||||
blocks.floor.checkChanges();
|
||||
blocks.floor.drawFloor();
|
||||
|
||||
groundEffectGroup.draw(e -> e instanceof BelowLiquidTrait);
|
||||
puddleGroup.draw();
|
||||
groundEffectGroup.draw(e -> !(e instanceof BelowLiquidTrait));
|
||||
Groups.drawFloor();
|
||||
Groups.drawFloorOver();
|
||||
|
||||
blocks.processBlocks();
|
||||
|
||||
blocks.drawShadows();
|
||||
Draw.color();
|
||||
|
||||
@@ -246,7 +206,9 @@ public class Renderer implements ApplicationListener{
|
||||
blocks.floor.endDraw();
|
||||
|
||||
blocks.drawBlocks(Layer.block);
|
||||
blocks.drawFog();
|
||||
if(state.rules.drawFog){
|
||||
blocks.drawFog();
|
||||
}
|
||||
|
||||
blocks.drawDestroyed();
|
||||
|
||||
@@ -256,57 +218,45 @@ public class Renderer implements ApplicationListener{
|
||||
|
||||
blocks.drawBlocks(Layer.overlay);
|
||||
|
||||
drawGroundShadows();
|
||||
|
||||
drawAllTeams(false);
|
||||
Groups.drawGroundShadows();
|
||||
Groups.drawGroundUnder();
|
||||
Groups.drawGround();
|
||||
|
||||
blocks.drawBlocks(Layer.turret);
|
||||
|
||||
drawFlyerShadows();
|
||||
|
||||
blocks.drawBlocks(Layer.power);
|
||||
blocks.drawBlocks(Layer.lights);
|
||||
|
||||
drawAllTeams(true);
|
||||
overlays.drawBottom();
|
||||
|
||||
Groups.drawFlyingShadows();
|
||||
|
||||
Groups.drawFlying();
|
||||
|
||||
Draw.flush();
|
||||
if(bloom != null){
|
||||
bloom.capture();
|
||||
}
|
||||
|
||||
bulletGroup.draw();
|
||||
effectGroup.draw();
|
||||
Groups.drawBullets();
|
||||
Groups.drawEffects();
|
||||
|
||||
Draw.flush();
|
||||
if(bloom != null){
|
||||
bloom.render();
|
||||
}
|
||||
|
||||
overlays.drawBottom();
|
||||
playerGroup.draw(p -> p.isLocal, Player::drawBuildRequests);
|
||||
|
||||
if(shieldGroup.countInBounds() > 0){
|
||||
if(settings.getBool("animatedshields") && Shaders.shield != null){
|
||||
Draw.flush();
|
||||
shieldBuffer.begin();
|
||||
graphics.clear(Color.clear);
|
||||
shieldGroup.draw();
|
||||
shieldGroup.draw(shield -> true, ShieldEntity::drawOver);
|
||||
Draw.flush();
|
||||
shieldBuffer.end();
|
||||
Draw.shader(Shaders.shield);
|
||||
Draw.color(Pal.accent);
|
||||
Draw.rect(Draw.wrap(shieldBuffer.getTexture()), camera.position.x, camera.position.y, camera.width, -camera.height);
|
||||
Draw.color();
|
||||
Draw.shader();
|
||||
}else{
|
||||
shieldGroup.draw(shield -> true, ShieldEntity::drawSimple);
|
||||
}
|
||||
}
|
||||
Groups.drawOverlays();
|
||||
|
||||
overlays.drawTop();
|
||||
|
||||
playerGroup.draw(p -> !p.isDead(), Player::drawName);
|
||||
Groups.drawWeather();
|
||||
|
||||
endFx();
|
||||
|
||||
if(!pixelator.enabled()){
|
||||
Groups.drawNames();
|
||||
}
|
||||
|
||||
if(state.rules.lighting){
|
||||
lights.draw();
|
||||
@@ -318,70 +268,35 @@ public class Renderer implements ApplicationListener{
|
||||
Draw.flush();
|
||||
}
|
||||
|
||||
private void drawLanding(){
|
||||
if(landTime > 0 && player.getClosestCore() != null){
|
||||
float fract = landTime / Fx.coreLand.lifetime;
|
||||
TileEntity entity = player.getClosestCore();
|
||||
private void drawBackground(){
|
||||
|
||||
TextureRegion reg = entity.block.icon(Cicon.full);
|
||||
}
|
||||
|
||||
private void drawLanding(){
|
||||
if(landTime > 0 && player.closestCore() != null){
|
||||
float fract = landTime / Fx.coreLand.lifetime;
|
||||
Tilec entity = player.closestCore();
|
||||
|
||||
TextureRegion reg = entity.block().icon(Cicon.full);
|
||||
float scl = Scl.scl(4f) / camerascale;
|
||||
float s = reg.getWidth() * Draw.scl * scl * 4f * fract;
|
||||
|
||||
Draw.color(Pal.lightTrail);
|
||||
Draw.rect("circle-shadow", entity.x, entity.y, s, s);
|
||||
Draw.rect("circle-shadow", entity.getX(), entity.getY(), s, s);
|
||||
|
||||
Angles.randLenVectors(1, (1f- fract), 100, 1000f * scl * (1f-fract), (x, y, fin, fout) -> {
|
||||
Lines.stroke(scl * fin);
|
||||
Lines.lineAngle(entity.x + x, entity.y + y, Mathf.angle(x, y), (fin * 20 + 1f) * scl);
|
||||
Lines.lineAngle(entity.getX() + x, entity.getY() + y, Mathf.angle(x, y), (fin * 20 + 1f) * scl);
|
||||
});
|
||||
|
||||
Draw.color();
|
||||
Draw.mixcol(Color.white, fract);
|
||||
Draw.rect(reg, entity.x, entity.y, reg.getWidth() * Draw.scl * scl, reg.getHeight() * Draw.scl * scl, fract * 135f);
|
||||
Draw.rect(reg, entity.getX(), entity.getY(), reg.getWidth() * Draw.scl * scl, reg.getHeight() * Draw.scl * scl, fract * 135f);
|
||||
|
||||
Draw.reset();
|
||||
}
|
||||
}
|
||||
|
||||
private void drawGroundShadows(){
|
||||
Draw.color(0, 0, 0, 0.4f);
|
||||
float rad = 1.6f;
|
||||
|
||||
Cons<Unit> draw = u -> {
|
||||
float size = Math.max(u.getIconRegion().getWidth(), u.getIconRegion().getHeight()) * Draw.scl;
|
||||
Draw.rect("circle-shadow", u.x, u.y, size * rad, size * rad);
|
||||
};
|
||||
|
||||
unitGroup.draw(unit -> !unit.isDead(), draw::get);
|
||||
|
||||
if(!playerGroup.isEmpty()){
|
||||
playerGroup.draw(unit -> !unit.isDead(), draw::get);
|
||||
}
|
||||
|
||||
Draw.color();
|
||||
}
|
||||
|
||||
private void drawFlyerShadows(){
|
||||
float trnsX = -12, trnsY = -13;
|
||||
Draw.color(0, 0, 0, 0.22f);
|
||||
|
||||
unitGroup.draw(unit -> unit.isFlying() && !unit.isDead(), baseUnit -> baseUnit.drawShadow(trnsX, trnsY));
|
||||
playerGroup.draw(unit -> unit.isFlying() && !unit.isDead(), player -> player.drawShadow(trnsX, trnsY));
|
||||
|
||||
Draw.color();
|
||||
}
|
||||
|
||||
private void drawAllTeams(boolean flying){
|
||||
unitGroup.draw(u -> u.isFlying() == flying && !u.isDead(), Unit::drawUnder);
|
||||
playerGroup.draw(p -> p.isFlying() == flying && !p.isDead(), Unit::drawUnder);
|
||||
|
||||
unitGroup.draw(u -> u.isFlying() == flying && !u.isDead(), Unit::drawAll);
|
||||
playerGroup.draw(p -> p.isFlying() == flying, Unit::drawAll);
|
||||
|
||||
unitGroup.draw(u -> u.isFlying() == flying && !u.isDead(), Unit::drawOver);
|
||||
playerGroup.draw(p -> p.isFlying() == flying, Unit::drawOver);
|
||||
}
|
||||
|
||||
public void scaleCamera(float amount){
|
||||
targetscale += amount;
|
||||
clampScale();
|
||||
@@ -407,7 +322,7 @@ public class Renderer implements ApplicationListener{
|
||||
}
|
||||
|
||||
public void takeMapScreenshot(){
|
||||
drawGroundShadows();
|
||||
Groups.drawGroundShadows();
|
||||
|
||||
int w = world.width() * tilesize, h = world.height() * tilesize;
|
||||
int memory = w * h * 4 / 1024 / 1024;
|
||||
@@ -425,10 +340,8 @@ public class Renderer implements ApplicationListener{
|
||||
camera.height = h;
|
||||
camera.position.x = w / 2f + tilesize / 2f;
|
||||
camera.position.y = h / 2f + tilesize / 2f;
|
||||
Draw.flush();
|
||||
buffer.begin();
|
||||
draw();
|
||||
Draw.flush();
|
||||
buffer.end();
|
||||
disableUI = false;
|
||||
camera.width = vpW;
|
||||
|
||||
@@ -20,7 +20,6 @@ import arc.scene.ui.Tooltip.*;
|
||||
import arc.scene.ui.layout.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import mindustry.core.GameState.*;
|
||||
import mindustry.editor.*;
|
||||
import mindustry.game.EventType.*;
|
||||
import mindustry.gen.*;
|
||||
@@ -63,7 +62,7 @@ public class UI implements ApplicationListener, Loadable{
|
||||
public TraceDialog traces;
|
||||
public DatabaseDialog database;
|
||||
public ContentInfoDialog content;
|
||||
public DeployDialog deploy;
|
||||
public PlanetDialog planet;
|
||||
public TechTreeDialog tech;
|
||||
//public MinimapDialog minimap;
|
||||
public SchematicsDialog schematics;
|
||||
@@ -176,7 +175,7 @@ public class UI implements ApplicationListener, Loadable{
|
||||
traces = new TraceDialog();
|
||||
maps = new MapsDialog();
|
||||
content = new ContentInfoDialog();
|
||||
deploy = new DeployDialog();
|
||||
planet = new PlanetDialog();
|
||||
tech = new TechTreeDialog();
|
||||
mods = new ModsDialog();
|
||||
schematics = new SchematicsDialog();
|
||||
@@ -185,10 +184,10 @@ public class UI implements ApplicationListener, Loadable{
|
||||
|
||||
menuGroup.setFillParent(true);
|
||||
menuGroup.touchable(Touchable.childrenOnly);
|
||||
menuGroup.visible(() -> state.is(State.menu));
|
||||
menuGroup.visible(() -> state.isMenu());
|
||||
hudGroup.setFillParent(true);
|
||||
hudGroup.touchable(Touchable.childrenOnly);
|
||||
hudGroup.visible(() -> !state.is(State.menu));
|
||||
hudGroup.visible(() -> state.isGame());
|
||||
|
||||
Core.scene.add(menuGroup);
|
||||
Core.scene.add(hudGroup);
|
||||
@@ -296,7 +295,7 @@ public class UI implements ApplicationListener, Loadable{
|
||||
table.setFillParent(true);
|
||||
table.touchable(Touchable.disabled);
|
||||
table.update(() -> {
|
||||
if(state.is(State.menu)) table.remove();
|
||||
if(state.isMenu()) table.remove();
|
||||
});
|
||||
table.actions(Actions.delay(duration * 0.9f), Actions.fadeOut(duration * 0.1f, Interpolation.fade), Actions.remove());
|
||||
table.top().table(Styles.black3, t -> t.margin(4).add(info).style(Styles.outlineLabel)).padTop(10);
|
||||
@@ -309,7 +308,7 @@ public class UI implements ApplicationListener, Loadable{
|
||||
table.setFillParent(true);
|
||||
table.touchable(Touchable.disabled);
|
||||
table.update(() -> {
|
||||
if(state.is(State.menu)) table.remove();
|
||||
if(state.isMenu()) table.remove();
|
||||
});
|
||||
table.actions(Actions.delay(duration), Actions.remove());
|
||||
table.align(align).table(Styles.black3, t -> t.margin(4).add(info).style(Styles.outlineLabel)).pad(top, left, bottom, right);
|
||||
@@ -322,7 +321,7 @@ public class UI implements ApplicationListener, Loadable{
|
||||
table.setFillParent(true);
|
||||
table.touchable(Touchable.disabled);
|
||||
table.update(() -> {
|
||||
if(state.is(State.menu)) table.remove();
|
||||
if(state.isMenu()) table.remove();
|
||||
});
|
||||
table.actions(Actions.delay(duration), Actions.remove());
|
||||
table.align(Align.center).table(Styles.black3, t -> t.margin(4).add(info).style(Styles.outlineLabel)).update(t -> {
|
||||
|
||||
@@ -1,31 +1,32 @@
|
||||
package mindustry.core;
|
||||
|
||||
import arc.*;
|
||||
import arc.func.*;
|
||||
import arc.math.*;
|
||||
import arc.math.geom.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.ArcAnnotate.*;
|
||||
import arc.util.*;
|
||||
import arc.util.noise.*;
|
||||
import mindustry.core.GameState.*;
|
||||
import mindustry.game.EventType.*;
|
||||
import mindustry.game.*;
|
||||
import mindustry.game.Teams.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.io.*;
|
||||
import mindustry.maps.*;
|
||||
import mindustry.maps.filters.*;
|
||||
import mindustry.maps.filters.GenerateFilter.*;
|
||||
import mindustry.maps.generators.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.blocks.*;
|
||||
import mindustry.world.blocks.legacy.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class World{
|
||||
public final Context context = new Context();
|
||||
|
||||
private Map currentMap;
|
||||
private Tile[][] tiles;
|
||||
public @NonNull Tiles tiles = new Tiles(0, 0);
|
||||
|
||||
private boolean generating, invalidMap;
|
||||
|
||||
@@ -58,20 +59,12 @@ public class World{
|
||||
return !wallSolid(x, y - 1) || !wallSolid(x, y + 1) || !wallSolid(x - 1, y) || !wallSolid(x + 1, y);
|
||||
}
|
||||
|
||||
public Map getMap(){
|
||||
return currentMap;
|
||||
}
|
||||
|
||||
public void setMap(Map map){
|
||||
this.currentMap = map;
|
||||
}
|
||||
|
||||
public int width(){
|
||||
return tiles == null ? 0 : tiles.length;
|
||||
return tiles.width;
|
||||
}
|
||||
|
||||
public int height(){
|
||||
return tiles == null ? 0 : tiles[0].length;
|
||||
return tiles.height;
|
||||
}
|
||||
|
||||
public int unitWidth(){
|
||||
@@ -82,51 +75,61 @@ public class World{
|
||||
return height()*tilesize;
|
||||
}
|
||||
|
||||
public @Nullable
|
||||
Tile tile(int pos){
|
||||
return tiles == null ? null : tile(Pos.x(pos), Pos.y(pos));
|
||||
@Nullable
|
||||
public Tile tile(int pos){
|
||||
return tile(Point2.x(pos), Point2.y(pos));
|
||||
}
|
||||
|
||||
public @Nullable Tile tile(int x, int y){
|
||||
if(tiles == null){
|
||||
return null;
|
||||
}
|
||||
if(!Structs.inBounds(x, y, tiles)) return null;
|
||||
return tiles[x][y];
|
||||
@Nullable
|
||||
public Tile tile(int x, int y){
|
||||
return tiles.get(x, y);
|
||||
}
|
||||
|
||||
public @Nullable Tile ltile(int x, int y){
|
||||
@Nullable
|
||||
public Tile tilec(int x, int y){
|
||||
Tile tile = tiles.get(x, y);
|
||||
if(tile == null) return null;
|
||||
if(tile.entity != null) return tile.entity.tile();
|
||||
return tile;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Tilec ent(int x, int y){
|
||||
Tile tile = tile(x, y);
|
||||
if(tile == null) return null;
|
||||
return tile.block().linked(tile);
|
||||
return tile.entity;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Tilec ent(int pos){
|
||||
Tile tile = tile(pos);
|
||||
if(tile == null) return null;
|
||||
return tile.entity;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public Tile rawTile(int x, int y){
|
||||
return tiles[x][y];
|
||||
return tiles.getn(x, y);
|
||||
}
|
||||
|
||||
public @Nullable Tile tileWorld(float x, float y){
|
||||
@Nullable
|
||||
public Tile tileWorld(float x, float y){
|
||||
return tile(Math.round(x / tilesize), Math.round(y / tilesize));
|
||||
}
|
||||
|
||||
public @Nullable Tile ltileWorld(float x, float y){
|
||||
return ltile(Math.round(x / tilesize), Math.round(y / tilesize));
|
||||
@Nullable
|
||||
public Tilec entWorld(float x, float y){
|
||||
return ent(Math.round(x / tilesize), Math.round(y / tilesize));
|
||||
}
|
||||
|
||||
public int toTile(float coord){
|
||||
return Math.round(coord / tilesize);
|
||||
}
|
||||
|
||||
public Tile[][] getTiles(){
|
||||
return tiles;
|
||||
}
|
||||
|
||||
private void clearTileEntities(){
|
||||
for(int x = 0; x < tiles.length; x++){
|
||||
for(int y = 0; y < tiles[0].length; y++){
|
||||
if(tiles[x][y] != null && tiles[x][y].entity != null){
|
||||
tiles[x][y].entity.remove();
|
||||
}
|
||||
for(Tile tile : tiles){
|
||||
if(tile != null && tile.entity != null){
|
||||
tile.entity.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -135,15 +138,11 @@ public class World{
|
||||
* Resizes the tile array to the specified size and returns the resulting tile array.
|
||||
* Only use for loading saves!
|
||||
*/
|
||||
public Tile[][] createTiles(int width, int height){
|
||||
if(tiles != null){
|
||||
clearTileEntities();
|
||||
public Tiles resize(int width, int height){
|
||||
clearTileEntities();
|
||||
|
||||
if(tiles.length != width || tiles[0].length != height){
|
||||
tiles = new Tile[width][height];
|
||||
}
|
||||
}else{
|
||||
tiles = new Tile[width][height];
|
||||
if(tiles.width != width || tiles.height != height){
|
||||
tiles = new Tiles(width, height);
|
||||
}
|
||||
|
||||
return tiles;
|
||||
@@ -162,16 +161,18 @@ public class World{
|
||||
* A WorldLoadEvent will be fire.
|
||||
*/
|
||||
public void endMapLoad(){
|
||||
prepareTiles(tiles);
|
||||
|
||||
for(int x = 0; x < tiles.length; x++){
|
||||
for(int y = 0; y < tiles[0].length; y++){
|
||||
Tile tile = tiles[x][y];
|
||||
tile.updateOcclusion();
|
||||
for(Tile tile : tiles){
|
||||
//remove legacy blocks; they need to stop existing
|
||||
if(tile.block() instanceof LegacyBlock){
|
||||
tile.remove();
|
||||
continue;
|
||||
}
|
||||
|
||||
if(tile.entity != null){
|
||||
tile.entity.updateProximity();
|
||||
}
|
||||
tile.updateOcclusion();
|
||||
|
||||
if(tile.entity != null){
|
||||
tile.entity.updateProximity();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -179,7 +180,7 @@ public class World{
|
||||
addDarkness(tiles);
|
||||
}
|
||||
|
||||
entities.all().each(group -> group.resize(-finalWorldBounds, -finalWorldBounds, tiles.length * tilesize + finalWorldBounds * 2, tiles[0].length * tilesize + finalWorldBounds * 2));
|
||||
Groups.resize(-finalWorldBounds, -finalWorldBounds, tiles.width * tilesize + finalWorldBounds * 2, tiles.height * tilesize + finalWorldBounds * 2);
|
||||
|
||||
generating = false;
|
||||
Events.fire(new WorldLoadEvent());
|
||||
@@ -193,23 +194,22 @@ public class World{
|
||||
return generating;
|
||||
}
|
||||
|
||||
public boolean isZone(){
|
||||
return getZone() != null;
|
||||
}
|
||||
|
||||
public Zone getZone(){
|
||||
return state.rules.zone;
|
||||
}
|
||||
|
||||
public void loadGenerator(Generator generator){
|
||||
public void loadGenerator(int width, int height, Cons<Tiles> generator){
|
||||
beginMapLoad();
|
||||
|
||||
createTiles(generator.width, generator.height);
|
||||
generator.generate(tiles);
|
||||
resize(width, height);
|
||||
generator.get(tiles);
|
||||
|
||||
endMapLoad();
|
||||
}
|
||||
|
||||
public void loadSector(Sector sector){
|
||||
state.map = new Map(StringMap.of("name", sector.planet.localizedName + "; Sector " + sector.id));
|
||||
state.rules.sector = sector;
|
||||
int size = sector.getSize();
|
||||
loadGenerator(size, size, tiles -> sector.planet.generator.generate(tiles, sector));
|
||||
}
|
||||
|
||||
public void loadMap(Map map){
|
||||
loadMap(map, new Rules());
|
||||
}
|
||||
@@ -228,7 +228,7 @@ public class World{
|
||||
return;
|
||||
}
|
||||
|
||||
this.currentMap = map;
|
||||
state.map = map;
|
||||
|
||||
invalidMap = false;
|
||||
|
||||
@@ -297,109 +297,99 @@ public class World{
|
||||
}
|
||||
}
|
||||
|
||||
public void addDarkness(Tile[][] tiles){
|
||||
byte[][] dark = new byte[tiles.length][tiles[0].length];
|
||||
byte[][] writeBuffer = new byte[tiles.length][tiles[0].length];
|
||||
public void addDarkness(Tiles tiles){
|
||||
byte[] dark = new byte[tiles.width * tiles.height];
|
||||
byte[] writeBuffer = new byte[tiles.width * tiles.height];
|
||||
|
||||
byte darkIterations = 4;
|
||||
for(int x = 0; x < tiles.length; x++){
|
||||
for(int y = 0; y < tiles[0].length; y++){
|
||||
Tile tile = tiles[x][y];
|
||||
if(tile.isDarkened()){
|
||||
dark[x][y] = darkIterations;
|
||||
}
|
||||
|
||||
for(int i = 0; i < dark.length; i++){
|
||||
Tile tile = tiles.geti(i);
|
||||
if(tile.isDarkened()){
|
||||
dark[i] = darkIterations;
|
||||
}
|
||||
}
|
||||
|
||||
for(int i = 0; i < darkIterations; i++){
|
||||
for(int x = 0; x < tiles.length; x++){
|
||||
for(int y = 0; y < tiles[0].length; y++){
|
||||
boolean min = false;
|
||||
for(Point2 point : Geometry.d4){
|
||||
int newX = x + point.x, newY = y + point.y;
|
||||
if(Structs.inBounds(newX, newY, tiles) && dark[newX][newY] < dark[x][y]){
|
||||
min = true;
|
||||
break;
|
||||
}
|
||||
for(Tile tile : tiles){
|
||||
int idx = tile.y * tiles.width + tile.x;
|
||||
boolean min = false;
|
||||
for(Point2 point : Geometry.d4){
|
||||
int newX = tile.x + point.x, newY = tile.y + point.y;
|
||||
int nidx = newY * tiles.width + newX;
|
||||
if(tiles.in(newX, newY) && dark[nidx] < dark[idx]){
|
||||
min = true;
|
||||
break;
|
||||
}
|
||||
writeBuffer[x][y] = (byte)Math.max(0, dark[x][y] - Mathf.num(min));
|
||||
}
|
||||
writeBuffer[idx] = (byte)Math.max(0, dark[idx] - Mathf.num(min));
|
||||
}
|
||||
|
||||
for(int x = 0; x < tiles.length; x++){
|
||||
System.arraycopy(writeBuffer[x], 0, dark[x], 0, tiles[0].length);
|
||||
}
|
||||
System.arraycopy(writeBuffer, 0, dark, 0, writeBuffer.length);
|
||||
}
|
||||
|
||||
for(int x = 0; x < tiles.length; x++){
|
||||
for(int y = 0; y < tiles[0].length; y++){
|
||||
Tile tile = tiles[x][y];
|
||||
if(tile.isDarkened()){
|
||||
tiles[x][y].rotation(dark[x][y]);
|
||||
}
|
||||
if(dark[x][y] == 4){
|
||||
boolean full = true;
|
||||
for(Point2 p : Geometry.d4){
|
||||
int px = p.x + x, py = p.y + y;
|
||||
if(Structs.inBounds(px, py, tiles) && !(tiles[px][py].isDarkened() && dark[px][py] == 4)){
|
||||
full = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
for(Tile tile : tiles){
|
||||
int idx = tile.y * tiles.width + tile.x;
|
||||
|
||||
if(full) tiles[x][y].rotation(5);
|
||||
if(tile.isDarkened()){
|
||||
tile.rotation(dark[idx]);
|
||||
}
|
||||
|
||||
if(dark[idx] == 4){
|
||||
boolean full = true;
|
||||
for(Point2 p : Geometry.d4){
|
||||
int px = p.x + tile.x, py = p.y + tile.y;
|
||||
int nidx = py * tiles.width + px;
|
||||
if(tiles.in(px, py) && !(tile.isDarkened() && dark[nidx] == 4)){
|
||||
full = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(full) tile.rotation(5);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 'Prepares' a tile array by:<br>
|
||||
* - setting up multiblocks<br>
|
||||
* - updating occlusion<br>
|
||||
* Usually used before placing structures on a tile array.
|
||||
*/
|
||||
public void prepareTiles(Tile[][] tiles){
|
||||
public float getDarkness(int x, int y){
|
||||
int edgeBlend = 2;
|
||||
|
||||
//find multiblocks
|
||||
IntArray multiblocks = new IntArray();
|
||||
float dark = 0;
|
||||
int edgeDst = Math.min(x, Math.min(y, Math.min(Math.abs(x - (tiles.width - 1)), Math.abs(y - (tiles.height - 1)))));
|
||||
if(edgeDst <= edgeBlend){
|
||||
dark = Math.max((edgeBlend - edgeDst) * (4f / edgeBlend), dark);
|
||||
}
|
||||
|
||||
for(int x = 0; x < tiles.length; x++){
|
||||
for(int y = 0; y < tiles[0].length; y++){
|
||||
Tile tile = tiles[x][y];
|
||||
if(state.hasSector()){
|
||||
int circleBlend = 14;
|
||||
//quantized angle
|
||||
float offset = state.getSector().rect.rotation + 90;
|
||||
float angle = Angles.angle(x, y, tiles.width/2, tiles.height/2) + offset;
|
||||
//polygon sides, depends on sector
|
||||
int sides = state.getSector().tile.corners.length;
|
||||
float step = 360f / sides;
|
||||
//prev and next angles of poly
|
||||
float prev = Mathf.round(angle, step);
|
||||
float next = prev + step;
|
||||
//raw line length to be translated
|
||||
float length = tiles.width/2f;
|
||||
float rawDst = Intersector.distanceLinePoint(Tmp.v1.trns(prev, length), Tmp.v2.trns(next, length), Tmp.v3.set(x - tiles.width/2, y - tiles.height/2).rotate(offset)) / Mathf.sqrt3 - 1;
|
||||
|
||||
if(tile.block().isMultiblock()){
|
||||
multiblocks.add(tile.pos());
|
||||
}
|
||||
//noise
|
||||
rawDst += Noise.noise(x, y, 11f, 7f) + Noise.noise(x, y, 22f, 15f);
|
||||
|
||||
int circleDst = (int)(rawDst - (tiles.width / 2 - circleBlend));
|
||||
if(circleDst > 0){
|
||||
dark = Math.max(circleDst / 1f, dark);
|
||||
}
|
||||
}
|
||||
|
||||
//place multiblocks now
|
||||
for(int i = 0; i < multiblocks.size; i++){
|
||||
int pos = multiblocks.get(i);
|
||||
|
||||
int x = Pos.x(pos);
|
||||
int y = Pos.y(pos);
|
||||
|
||||
Block result = tiles[x][y].block();
|
||||
Team team = tiles[x][y].getTeam();
|
||||
|
||||
int offsetx = -(result.size - 1) / 2;
|
||||
int offsety = -(result.size - 1) / 2;
|
||||
|
||||
for(int dx = 0; dx < result.size; dx++){
|
||||
for(int dy = 0; dy < result.size; dy++){
|
||||
int worldx = dx + offsetx + x;
|
||||
int worldy = dy + offsety + y;
|
||||
if(!(worldx == x && worldy == y)){
|
||||
Tile toplace = world.tile(worldx, worldy);
|
||||
if(toplace != null){
|
||||
toplace.setBlock(BlockPart.get(dx + offsetx, dy + offsety), team);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Tile tile = world.tile(x, y);
|
||||
if(tile != null && tile.block().solid && tile.block().fillsTile && !tile.block().synthetic()){
|
||||
dark = Math.max(dark, tile.rotation());
|
||||
}
|
||||
|
||||
return dark;
|
||||
}
|
||||
|
||||
public interface Raycaster{
|
||||
@@ -408,18 +398,20 @@ public class World{
|
||||
|
||||
private class Context implements WorldContext{
|
||||
@Override
|
||||
public Tile tile(int x, int y){
|
||||
return tiles[x][y];
|
||||
public Tile tile(int index){
|
||||
return tiles.geti(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resize(int width, int height){
|
||||
createTiles(width, height);
|
||||
World.this.resize(width, height);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Tile create(int x, int y, int floorID, int overlayID, int wallID){
|
||||
return (tiles[x][y] = new Tile(x, y, floorID, overlayID, wallID));
|
||||
Tile tile = new Tile(x, y, floorID, overlayID, wallID);
|
||||
tiles.set(x, y, tile);
|
||||
return tile;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -454,23 +446,8 @@ public class World{
|
||||
GenerateInput input = new GenerateInput();
|
||||
|
||||
for(GenerateFilter filter : filters){
|
||||
input.begin(filter, width(), height(), (x, y) -> tiles[x][y]);
|
||||
|
||||
//actually apply the filter
|
||||
for(int x = 0; x < width(); x++){
|
||||
for(int y = 0; y < height(); y++){
|
||||
Tile tile = rawTile(x, y);
|
||||
input.apply(x, y, tile.floor(), tile.block(), tile.overlay());
|
||||
filter.apply(input);
|
||||
|
||||
tile.setFloor((Floor)input.floor);
|
||||
tile.setOverlay(input.ore);
|
||||
|
||||
if(!tile.block().synthetic() && !input.block.synthetic()){
|
||||
tile.setBlock(input.block);
|
||||
}
|
||||
}
|
||||
}
|
||||
input.begin(filter, width(), height(), (x, y) -> tiles.getn(x, y));
|
||||
filter.apply(tiles, input);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,17 +4,18 @@ package mindustry.ctype;
|
||||
public enum ContentType{
|
||||
item,
|
||||
block,
|
||||
mech,
|
||||
mech_UNUSED,
|
||||
bullet,
|
||||
liquid,
|
||||
status,
|
||||
unit,
|
||||
weather,
|
||||
effect,
|
||||
effect_UNUSED,
|
||||
zone,
|
||||
loadout,
|
||||
typeid,
|
||||
error;
|
||||
loadout_UNUSED,
|
||||
typeid_UNUSED,
|
||||
error,
|
||||
planet;
|
||||
|
||||
public static final ContentType[] all = values();
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ import mindustry.ui.Cicon;
|
||||
|
||||
/** Base interface for an unlockable content type. */
|
||||
public abstract class UnlockableContent extends MappableContent{
|
||||
/** Localized, formal name. Never null. Set to block name if not found in bundle. */
|
||||
/** Localized, formal name. Never null. Set to internal name if not found in bundle. */
|
||||
public String localizedName;
|
||||
/** Localized description. May be null. */
|
||||
public @Nullable String description;
|
||||
@@ -54,7 +54,7 @@ public abstract class UnlockableContent extends MappableContent{
|
||||
public void onUnlock(){
|
||||
}
|
||||
|
||||
/** Whether this content is always hidden in the content info dialog. */
|
||||
/** Whether this content is always hidden in the content database dialog. */
|
||||
public boolean isHidden(){
|
||||
return false;
|
||||
}
|
||||
@@ -70,7 +70,7 @@ public abstract class UnlockableContent extends MappableContent{
|
||||
|
||||
/** @return whether this content is unlocked, or the player is in a custom game. */
|
||||
public final boolean unlockedCur(){
|
||||
return Vars.data.isUnlocked(this) || !Vars.world.isZone();
|
||||
return Vars.data.isUnlocked(this) || !Vars.state.isCampaign();
|
||||
}
|
||||
|
||||
public final boolean locked(){
|
||||
|
||||
@@ -6,7 +6,7 @@ import mindustry.game.Team;
|
||||
import mindustry.gen.TileOp;
|
||||
import mindustry.world.Block;
|
||||
import mindustry.world.Tile;
|
||||
import mindustry.world.blocks.Floor;
|
||||
import mindustry.world.blocks.environment.Floor;
|
||||
|
||||
import static mindustry.Vars.content;
|
||||
|
||||
@@ -65,7 +65,7 @@ public class DrawOperation{
|
||||
tile.setFloor((Floor)content.block(to));
|
||||
}else if(type == OpType.block.ordinal()){
|
||||
Block block = content.block(to);
|
||||
tile.setBlock(block, tile.getTeam(), tile.rotation());
|
||||
tile.setBlock(block, tile.team(), tile.rotation());
|
||||
}else if(type == OpType.rotation.ordinal()){
|
||||
tile.rotation(to);
|
||||
}else if(type == OpType.team.ordinal()){
|
||||
|
||||
@@ -1,19 +1,16 @@
|
||||
package mindustry.editor;
|
||||
|
||||
import mindustry.content.Blocks;
|
||||
import mindustry.core.GameState.State;
|
||||
import mindustry.editor.DrawOperation.OpType;
|
||||
import mindustry.game.Team;
|
||||
import mindustry.gen.TileOp;
|
||||
import mindustry.world.Block;
|
||||
import mindustry.world.Tile;
|
||||
import mindustry.world.blocks.*;
|
||||
import arc.util.ArcAnnotate.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.editor.DrawOperation.*;
|
||||
import mindustry.game.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.blocks.environment.*;
|
||||
import mindustry.world.modules.*;
|
||||
|
||||
import static mindustry.Vars.state;
|
||||
import static mindustry.Vars.ui;
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
//TODO somehow remove or replace this class with a more flexible solution
|
||||
public class EditorTile extends Tile{
|
||||
|
||||
public EditorTile(int x, int y, int floor, int overlay, int wall){
|
||||
@@ -21,8 +18,8 @@ public class EditorTile extends Tile{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFloor(Floor type){
|
||||
if(state.is(State.playing)){
|
||||
public void setFloor(@NonNull Floor type){
|
||||
if(state.isGame()){
|
||||
super.setFloor(type);
|
||||
return;
|
||||
}
|
||||
@@ -41,35 +38,22 @@ public class EditorTile extends Tile{
|
||||
super.setFloor(type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBlock(Block type){
|
||||
if(state.is(State.playing)){
|
||||
super.setBlock(type);
|
||||
return;
|
||||
}
|
||||
|
||||
if(block == type) return;
|
||||
op(OpType.block, block.id);
|
||||
if(rotation != 0) op(OpType.rotation, rotation);
|
||||
if(team != 0) op(OpType.team, team);
|
||||
super.setBlock(type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBlock(Block type, Team team, int rotation){
|
||||
if(state.is(State.playing)){
|
||||
if(state.isGame()){
|
||||
super.setBlock(type, team, rotation);
|
||||
return;
|
||||
}
|
||||
|
||||
setBlock(type);
|
||||
setTeam(team);
|
||||
rotation(rotation);
|
||||
op(OpType.block, block.id);
|
||||
if(rotation != 0) op(OpType.rotation, (byte)rotation);
|
||||
if(team() != Team.derelict) op(OpType.team, team().id);
|
||||
super.setBlock(type, team, rotation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTeam(Team team){
|
||||
if(state.is(State.playing)){
|
||||
if(state.isGame()){
|
||||
super.setTeam(team);
|
||||
return;
|
||||
}
|
||||
@@ -81,7 +65,7 @@ public class EditorTile extends Tile{
|
||||
|
||||
@Override
|
||||
public void rotation(int rotation){
|
||||
if(state.is(State.playing)){
|
||||
if(state.isGame()){
|
||||
super.rotation(rotation);
|
||||
return;
|
||||
}
|
||||
@@ -93,57 +77,49 @@ public class EditorTile extends Tile{
|
||||
|
||||
@Override
|
||||
public void setOverlay(Block overlay){
|
||||
setOverlayID(overlay.id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOverlayID(short overlay){
|
||||
if(state.is(State.playing)){
|
||||
super.setOverlayID(overlay);
|
||||
if(state.isGame()){
|
||||
super.setOverlay(overlay);
|
||||
return;
|
||||
}
|
||||
|
||||
if(floor.isLiquid) return;
|
||||
if(overlayID() == overlay) return;
|
||||
if(overlay() == overlay) return;
|
||||
op(OpType.overlay, this.overlay.id);
|
||||
super.setOverlayID(overlay);
|
||||
super.setOverlay(overlay);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void preChanged(){
|
||||
if(state.is(State.playing)){
|
||||
super.preChanged();
|
||||
return;
|
||||
}
|
||||
|
||||
super.setTeam(Team.derelict);
|
||||
super.preChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void changed(){
|
||||
if(state.is(State.playing)){
|
||||
super.changed();
|
||||
public void recache(){
|
||||
if(state.isGame()){
|
||||
super.recache();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void changed(Team team){
|
||||
if(state.isGame()){
|
||||
super.changed(team);
|
||||
return;
|
||||
}
|
||||
|
||||
entity = null;
|
||||
|
||||
if(block == null){
|
||||
block = Blocks.air;
|
||||
}
|
||||
|
||||
if(floor == null){
|
||||
floor = (Floor)Blocks.air;
|
||||
}
|
||||
|
||||
if(block == null) block = Blocks.air;
|
||||
if(floor == null) floor = (Floor)Blocks.air;
|
||||
|
||||
Block block = block();
|
||||
|
||||
if(block.hasEntity()){
|
||||
entity = block.newEntity().init(this, false);
|
||||
entity.cons = new ConsumeModule(entity);
|
||||
if(block.hasItems) entity.items = new ItemModule();
|
||||
if(block.hasLiquids) entity.liquids = new LiquidModule();
|
||||
if(block.hasPower) entity.power = new PowerModule();
|
||||
entity = block.newEntity().init(this, team, false);
|
||||
entity.cons(new ConsumeModule(entity));
|
||||
if(block.hasItems) entity.items(new ItemModule());
|
||||
if(block.hasLiquids) entity.liquids(new LiquidModule());
|
||||
if(block.hasPower) entity.power(new PowerModule());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@ import arc.util.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.game.*;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.blocks.*;
|
||||
|
||||
public enum EditorTool{
|
||||
zoom,
|
||||
@@ -16,7 +15,7 @@ public enum EditorTool{
|
||||
public void touched(MapEditor editor, int x, int y){
|
||||
if(!Structs.inBounds(x, y, editor.width(), editor.height())) return;
|
||||
|
||||
Tile tile = editor.tile(x, y).link();
|
||||
Tile tile = editor.tile(x, y);
|
||||
editor.drawBlock = tile.block() == Blocks.air ? tile.overlay() == Blocks.air ? tile.floor() : tile.overlay() : tile.block();
|
||||
}
|
||||
},
|
||||
@@ -63,7 +62,7 @@ public enum EditorTool{
|
||||
editor.drawBlocks(x, y, true, tile -> true);
|
||||
}else if(mode == 2){
|
||||
//draw teams
|
||||
editor.drawCircle(x, y, tile -> tile.link().setTeam(editor.drawTeam));
|
||||
editor.drawCircle(x, y, tile -> tile.setTeam(editor.drawTeam));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -108,7 +107,7 @@ public enum EditorTool{
|
||||
//mode 0 or 1, fill everything with the floor/tile or replace it
|
||||
if(mode == 0 || mode == -1){
|
||||
//can't fill parts or multiblocks
|
||||
if(tile.block() instanceof BlockPart || tile.block().isMultiblock()){
|
||||
if(tile.block().isMultiblock()){
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -137,10 +136,10 @@ public enum EditorTool{
|
||||
}else if(mode == 1){ //mode 1 is team fill
|
||||
|
||||
//only fill synthetic blocks, it's meaningless otherwise
|
||||
if(tile.link().synthetic()){
|
||||
Team dest = tile.getTeam();
|
||||
if(tile.synthetic()){
|
||||
Team dest = tile.team();
|
||||
if(dest == editor.drawTeam) return;
|
||||
fill(editor, x, y, false, t -> t.getTeamID() == (int)dest.id && t.link().synthetic(), t -> t.setTeam(editor.drawTeam));
|
||||
fill(editor, x, y, false, t -> t.getTeamID() == (int)dest.id && t.synthetic(), t -> t.setTeam(editor.drawTeam));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -164,35 +163,44 @@ public enum EditorTool{
|
||||
int x1;
|
||||
|
||||
stack.clear();
|
||||
stack.add(Pos.get(x, y));
|
||||
stack.add(Point2.pack(x, y));
|
||||
|
||||
while(stack.size > 0){
|
||||
int popped = stack.pop();
|
||||
x = Pos.x(popped);
|
||||
y = Pos.y(popped);
|
||||
try{
|
||||
while(stack.size > 0 && stack.size < width*height){
|
||||
int popped = stack.pop();
|
||||
x = Point2.x(popped);
|
||||
y = Point2.y(popped);
|
||||
|
||||
x1 = x;
|
||||
while(x1 >= 0 && tester.get(editor.tile(x1, y))) x1--;
|
||||
x1++;
|
||||
boolean spanAbove = false, spanBelow = false;
|
||||
while(x1 < width && tester.get(editor.tile(x1, y))){
|
||||
filler.get(editor.tile(x1, y));
|
||||
|
||||
if(!spanAbove && y > 0 && tester.get(editor.tile(x1, y - 1))){
|
||||
stack.add(Pos.get(x1, y - 1));
|
||||
spanAbove = true;
|
||||
}else if(spanAbove && !tester.get(editor.tile(x1, y - 1))){
|
||||
spanAbove = false;
|
||||
}
|
||||
|
||||
if(!spanBelow && y < height - 1 && tester.get(editor.tile(x1, y + 1))){
|
||||
stack.add(Pos.get(x1, y + 1));
|
||||
spanBelow = true;
|
||||
}else if(spanBelow && y < height - 1 && !tester.get(editor.tile(x1, y + 1))){
|
||||
spanBelow = false;
|
||||
}
|
||||
x1 = x;
|
||||
while(x1 >= 0 && tester.get(editor.tile(x1, y))) x1--;
|
||||
x1++;
|
||||
boolean spanAbove = false, spanBelow = false;
|
||||
while(x1 < width && tester.get(editor.tile(x1, y))){
|
||||
filler.get(editor.tile(x1, y));
|
||||
|
||||
if(!spanAbove && y > 0 && tester.get(editor.tile(x1, y - 1))){
|
||||
stack.add(Point2.pack(x1, y - 1));
|
||||
spanAbove = true;
|
||||
}else if(spanAbove && !tester.get(editor.tile(x1, y - 1))){
|
||||
spanAbove = false;
|
||||
}
|
||||
|
||||
if(!spanBelow && y < height - 1 && tester.get(editor.tile(x1, y + 1))){
|
||||
stack.add(Point2.pack(x1, y + 1));
|
||||
spanBelow = true;
|
||||
}else if(spanBelow && y < height - 1 && !tester.get(editor.tile(x1, y + 1))){
|
||||
spanBelow = false;
|
||||
}
|
||||
x1++;
|
||||
}
|
||||
}
|
||||
stack.clear();
|
||||
}catch(OutOfMemoryError e){
|
||||
//hack
|
||||
stack = null;
|
||||
System.gc();
|
||||
e.printStackTrace();
|
||||
stack = new IntArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +1,16 @@
|
||||
package mindustry.editor;
|
||||
|
||||
import arc.struct.StringMap;
|
||||
import arc.files.Fi;
|
||||
import arc.func.Cons;
|
||||
import arc.func.Boolf;
|
||||
import arc.graphics.Pixmap;
|
||||
import arc.math.Mathf;
|
||||
import arc.util.Structs;
|
||||
import mindustry.content.Blocks;
|
||||
import mindustry.game.Team;
|
||||
import mindustry.gen.TileOp;
|
||||
import mindustry.io.MapIO;
|
||||
import mindustry.maps.Map;
|
||||
import arc.files.*;
|
||||
import arc.func.*;
|
||||
import arc.graphics.*;
|
||||
import arc.math.*;
|
||||
import arc.struct.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.game.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.io.*;
|
||||
import mindustry.maps.*;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.blocks.BlockPart;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
@@ -55,7 +52,6 @@ public class MapEditor{
|
||||
tags.put("steamid", map.file.parent().name());
|
||||
}
|
||||
MapIO.loadMap(map, context);
|
||||
checkLinkedTiles();
|
||||
renderer.resize(width(), height());
|
||||
loading = false;
|
||||
}
|
||||
@@ -64,33 +60,10 @@ public class MapEditor{
|
||||
reset();
|
||||
|
||||
createTiles(pixmap.getWidth(), pixmap.getHeight());
|
||||
load(() -> MapIO.readPixmap(pixmap, tiles()));
|
||||
load(() -> MapIO.readImage(pixmap, tiles()));
|
||||
renderer.resize(width(), height());
|
||||
}
|
||||
|
||||
//adds missing blockparts
|
||||
public void checkLinkedTiles(){
|
||||
Tile[][] tiles = world.getTiles();
|
||||
|
||||
//clear block parts first
|
||||
for(int x = 0; x < width(); x++){
|
||||
for(int y = 0; y < height(); y++){
|
||||
if(tiles[x][y].block() instanceof BlockPart){
|
||||
tiles[x][y].setBlock(Blocks.air);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//set up missing blockparts
|
||||
for(int x = 0; x < width(); x++){
|
||||
for(int y = 0; y < height(); y++){
|
||||
if(tiles[x][y].block().isMultiblock()){
|
||||
tiles[x][y].set(tiles[x][y].block(), tiles[x][y].getTeam());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void load(Runnable r){
|
||||
loading = true;
|
||||
r.run();
|
||||
@@ -99,11 +72,11 @@ public class MapEditor{
|
||||
|
||||
/** Creates a 2-D array of EditorTiles with stone as the floor block. */
|
||||
private void createTiles(int width, int height){
|
||||
Tile[][] tiles = world.createTiles(width, height);
|
||||
Tiles tiles = world.resize(width, height);
|
||||
|
||||
for(int x = 0; x < width; x++){
|
||||
for(int y = 0; y < height; y++){
|
||||
tiles[x][y] = new EditorTile(x, y, Blocks.stone.id, (short)0, (short)0);
|
||||
tiles.set(x, y, new EditorTile(x, y, Blocks.stone.id, (short)0, (short)0));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -119,8 +92,8 @@ public class MapEditor{
|
||||
tags = new StringMap();
|
||||
}
|
||||
|
||||
public Tile[][] tiles(){
|
||||
return world.getTiles();
|
||||
public Tiles tiles(){
|
||||
return world.tiles;
|
||||
}
|
||||
|
||||
public Tile tile(int x, int y){
|
||||
@@ -151,42 +124,13 @@ public class MapEditor{
|
||||
if(drawBlock.isMultiblock()){
|
||||
x = Mathf.clamp(x, (drawBlock.size - 1) / 2, width() - drawBlock.size / 2 - 1);
|
||||
y = Mathf.clamp(y, (drawBlock.size - 1) / 2, height() - drawBlock.size / 2 - 1);
|
||||
|
||||
int offsetx = -(drawBlock.size - 1) / 2;
|
||||
int offsety = -(drawBlock.size - 1) / 2;
|
||||
|
||||
for(int dx = 0; dx < drawBlock.size; dx++){
|
||||
for(int dy = 0; dy < drawBlock.size; dy++){
|
||||
int worldx = dx + offsetx + x;
|
||||
int worldy = dy + offsety + y;
|
||||
|
||||
if(Structs.inBounds(worldx, worldy, width(), height())){
|
||||
Tile tile = tile(worldx, worldy);
|
||||
|
||||
Block block = tile.block();
|
||||
|
||||
//bail out if there's anything blocking the way
|
||||
if(block.isMultiblock() || block instanceof BlockPart){
|
||||
return;
|
||||
}
|
||||
|
||||
renderer.updatePoint(worldx, worldy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tile(x, y).set(drawBlock, drawTeam);
|
||||
tile(x, y).setBlock(drawBlock, drawTeam, 0);
|
||||
}else{
|
||||
boolean isFloor = drawBlock.isFloor() && drawBlock != Blocks.air;
|
||||
|
||||
Cons<Tile> drawer = tile -> {
|
||||
if(!tester.get(tile)) return;
|
||||
|
||||
//remove linked tiles blocking the way
|
||||
if(!isFloor && (tile.isLinked() || tile.block().isMultiblock())){
|
||||
tile.link().remove();
|
||||
}
|
||||
|
||||
if(isFloor){
|
||||
tile.setFloor(drawBlock.asFloor());
|
||||
}else{
|
||||
@@ -211,7 +155,7 @@ public class MapEditor{
|
||||
public void drawCircle(int x, int y, Cons<Tile> drawer){
|
||||
for(int rx = -brushSize; rx <= brushSize; rx++){
|
||||
for(int ry = -brushSize; ry <= brushSize; ry++){
|
||||
if(Mathf.dst2(rx, ry) <= (brushSize - 0.5f) * (brushSize - 0.5f)){
|
||||
if(Mathf.within(rx, ry, brushSize - 0.5f + 0.0001f)){
|
||||
int wx = x + rx, wy = y + ry;
|
||||
|
||||
if(wx < 0 || wy < 0 || wx >= width() || wy >= height()){
|
||||
@@ -245,20 +189,20 @@ public class MapEditor{
|
||||
public void resize(int width, int height){
|
||||
clearOp();
|
||||
|
||||
Tile[][] previous = world.getTiles();
|
||||
Tiles previous = world.tiles;
|
||||
int offsetX = -(width - width()) / 2, offsetY = -(height - height()) / 2;
|
||||
loading = true;
|
||||
|
||||
Tile[][] tiles = world.createTiles(width, height);
|
||||
Tiles tiles = world.resize(width, height);
|
||||
for(int x = 0; x < width; x++){
|
||||
for(int y = 0; y < height; y++){
|
||||
int px = offsetX + x, py = offsetY + y;
|
||||
if(Structs.inBounds(px, py, previous.length, previous[0].length)){
|
||||
tiles[x][y] = previous[px][py];
|
||||
tiles[x][y].x = (short)x;
|
||||
tiles[x][y].y = (short)y;
|
||||
if(previous.in(px, py)){
|
||||
tiles.set(x, y, previous.getn(px, py));
|
||||
tiles.getn(x, y).x = (short)x;
|
||||
tiles.getn(x, y).y = (short)y;
|
||||
}else{
|
||||
tiles[x][y] = new EditorTile(x, y, Blocks.stone.id, (short)0, (short)0);
|
||||
tiles.set(x, y, new EditorTile(x, y, Blocks.stone.id, (short)0, (short)0));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -308,18 +252,20 @@ public class MapEditor{
|
||||
|
||||
class Context implements WorldContext{
|
||||
@Override
|
||||
public Tile tile(int x, int y){
|
||||
return world.tile(x, y);
|
||||
public Tile tile(int index){
|
||||
return world.tiles.geti(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resize(int width, int height){
|
||||
world.createTiles(width, height);
|
||||
world.resize(width, height);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Tile create(int x, int y, int floorID, int overlayID, int wallID){
|
||||
return (tiles()[x][y] = new EditorTile(x, y, floorID, overlayID, wallID));
|
||||
Tile tile = new EditorTile(x, y, floorID, overlayID, wallID);
|
||||
tiles().set(x, y, tile);
|
||||
return tile;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -28,7 +28,7 @@ import mindustry.ui.*;
|
||||
import mindustry.ui.Cicon;
|
||||
import mindustry.ui.dialogs.*;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.blocks.*;
|
||||
import mindustry.world.blocks.environment.*;
|
||||
import mindustry.world.blocks.storage.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
@@ -87,62 +87,42 @@ public class MapEditorDialog extends Dialog implements Disposable{
|
||||
|
||||
t.row();
|
||||
|
||||
t.addImageTextButton("$editor.import", Icon.download, () ->
|
||||
createDialog("$editor.import",
|
||||
"$editor.importmap", "$editor.importmap.description", Icon.download, (Runnable)loadDialog::show,
|
||||
"$editor.importfile", "$editor.importfile.description", Icon.file, (Runnable)() ->
|
||||
platform.showFileChooser(true, mapExtension, file -> ui.loadAnd(() -> {
|
||||
maps.tryCatchMapError(() -> {
|
||||
if(MapIO.isImage(file)){
|
||||
ui.showInfo("$editor.errorimage");
|
||||
}else{
|
||||
editor.beginEdit(MapIO.createMap(file, true));
|
||||
}
|
||||
});
|
||||
})),
|
||||
|
||||
"$editor.importimage", "$editor.importimage.description", Icon.fileImage, (Runnable)() ->
|
||||
platform.showFileChooser(true, "png", file ->
|
||||
ui.loadAnd(() -> {
|
||||
try{
|
||||
Pixmap pixmap = new Pixmap(file);
|
||||
editor.beginEdit(pixmap);
|
||||
pixmap.dispose();
|
||||
}catch(Exception e){
|
||||
ui.showException("$editor.errorload", e);
|
||||
Log.err(e);
|
||||
}
|
||||
})))
|
||||
);
|
||||
|
||||
t.addImageTextButton("$editor.export", Icon.upload, () -> {
|
||||
if(!ios){
|
||||
platform.showFileChooser(false, mapExtension, file -> {
|
||||
ui.loadAnd(() -> {
|
||||
try{
|
||||
if(!editor.getTags().containsKey("name")){
|
||||
editor.getTags().put("name", file.nameWithoutExtension());
|
||||
}
|
||||
MapIO.writeMap(file, editor.createMap(file));
|
||||
}catch(Exception e){
|
||||
ui.showException("$editor.errorsave", e);
|
||||
Log.err(e);
|
||||
}
|
||||
});
|
||||
});
|
||||
}else{
|
||||
ui.loadAnd(() -> {
|
||||
try{
|
||||
Fi result = Core.files.local(editor.getTags().get("name", "unknown") + "." + mapExtension);
|
||||
MapIO.writeMap(result, editor.createMap(result));
|
||||
platform.shareFile(result);
|
||||
}catch(Exception e){
|
||||
ui.showException("$editor.errorsave", e);
|
||||
Log.err(e);
|
||||
t.addImageTextButton("$editor.import", Icon.download, () -> createDialog("$editor.import",
|
||||
"$editor.importmap", "$editor.importmap.description", Icon.download, (Runnable)loadDialog::show,
|
||||
"$editor.importfile", "$editor.importfile.description", Icon.file, (Runnable)() ->
|
||||
platform.showFileChooser(true, mapExtension, file -> ui.loadAnd(() -> {
|
||||
maps.tryCatchMapError(() -> {
|
||||
if(MapIO.isImage(file)){
|
||||
ui.showInfo("$editor.errorimage");
|
||||
}else{
|
||||
editor.beginEdit(MapIO.createMap(file, true));
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
})),
|
||||
|
||||
"$editor.importimage", "$editor.importimage.description", Icon.fileImage, (Runnable)() ->
|
||||
platform.showFileChooser(true, "png", file ->
|
||||
ui.loadAnd(() -> {
|
||||
try{
|
||||
Pixmap pixmap = new Pixmap(file);
|
||||
editor.beginEdit(pixmap);
|
||||
pixmap.dispose();
|
||||
}catch(Exception e){
|
||||
ui.showException("$editor.errorload", e);
|
||||
Log.err(e);
|
||||
}
|
||||
})))
|
||||
);
|
||||
|
||||
t.addImageTextButton("$editor.export", Icon.upload, () -> createDialog("$editor.export",
|
||||
"$editor.exportfile", "$editor.exportfile.description", Icon.file,
|
||||
(Runnable)() -> platform.export(editor.getTags().get("name", "unknown"), mapExtension, file -> MapIO.writeMap(file, editor.createMap(file))),
|
||||
"$editor.exportimage", "$editor.exportimage.description", Icon.fileImage,
|
||||
(Runnable)() -> platform.export(editor.getTags().get("name", "unknown"), "png", file -> {
|
||||
Pixmap out = MapIO.writeImage(editor.tiles());
|
||||
file.writePNG(out);
|
||||
out.dispose();
|
||||
})));
|
||||
});
|
||||
|
||||
menu.cont.row();
|
||||
@@ -176,7 +156,7 @@ public class MapEditorDialog extends Dialog implements Disposable{
|
||||
}
|
||||
|
||||
platform.publish(map);
|
||||
}).padTop(-3).size(swidth * 2f + 10, 60f).update(b -> b.setText(editor.getTags().containsKey("steamid") ? editor.getTags().get("author").equals(player.name) ? "$workshop.listing" : "$view.workshop" : "$editor.publish.workshop"));
|
||||
}).padTop(-3).size(swidth * 2f + 10, 60f).update(b -> b.setText(editor.getTags().containsKey("steamid") ? editor.getTags().get("author").equals(player.name()) ? "$workshop.listing" : "$view.workshop" : "$editor.publish.workshop"));
|
||||
|
||||
menu.cont.row();
|
||||
}
|
||||
@@ -266,24 +246,21 @@ public class MapEditorDialog extends Dialog implements Disposable{
|
||||
state.teams = new Teams();
|
||||
player.reset();
|
||||
state.rules = Gamemode.editor.apply(lastSavedRules.copy());
|
||||
state.rules.zone = null;
|
||||
world.setMap(new Map(StringMap.of(
|
||||
"name", "Editor Playtesting",
|
||||
"width", editor.width(),
|
||||
"height", editor.height()
|
||||
)));
|
||||
state.rules.sector = null;
|
||||
state.map = new Map(StringMap.of(
|
||||
"name", "Editor Playtesting",
|
||||
"width", editor.width(),
|
||||
"height", editor.height()
|
||||
));
|
||||
world.endMapLoad();
|
||||
//add entities so they update. is this really needed?
|
||||
for(int x = 0; x < world.width(); x++){
|
||||
for(int y = 0; y < world.height(); y++){
|
||||
Tile tile = world.rawTile(x, y);
|
||||
if(tile.entity != null){
|
||||
tile.entity.add();
|
||||
}
|
||||
for(Tile tile : world.tiles){
|
||||
if(tile.entity != null){
|
||||
tile.entity.add();
|
||||
}
|
||||
}
|
||||
player.set(world.width() * tilesize/2f, world.height() * tilesize/2f);
|
||||
player.setDead(false);
|
||||
player.clearUnit();
|
||||
logic.play();
|
||||
});
|
||||
}
|
||||
@@ -295,7 +272,8 @@ public class MapEditorDialog extends Dialog implements Disposable{
|
||||
editor.getTags().put("rules", JsonIO.write(state.rules));
|
||||
editor.getTags().remove("width");
|
||||
editor.getTags().remove("height");
|
||||
player.dead = true;
|
||||
//TODO unkill player
|
||||
//player.dead = true;
|
||||
|
||||
Map returned = null;
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ import mindustry.maps.filters.GenerateFilter.*;
|
||||
import mindustry.ui.*;
|
||||
import mindustry.ui.dialogs.*;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.blocks.*;
|
||||
import mindustry.world.blocks.environment.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
@@ -30,7 +30,7 @@ public class MapGenerateDialog extends FloatingDialog{
|
||||
private final Prov<GenerateFilter>[] filterTypes = new Prov[]{
|
||||
NoiseFilter::new, ScatterFilter::new, TerrainFilter::new, DistortFilter::new,
|
||||
RiverNoiseFilter::new, OreFilter::new, OreMedianFilter::new, MedianFilter::new,
|
||||
BlendFilter::new, MirrorFilter::new, ClearFilter::new
|
||||
BlendFilter::new, MirrorFilter::new, ClearFilter::new, CoreSpawnFilter::new, EnemySpawnFilter::new
|
||||
};
|
||||
private final MapEditor editor;
|
||||
private final boolean applied;
|
||||
@@ -52,7 +52,7 @@ public class MapGenerateDialog extends FloatingDialog{
|
||||
private CachedTile ctile = new CachedTile(){
|
||||
//nothing.
|
||||
@Override
|
||||
protected void changed(){
|
||||
protected void changed(Team team){
|
||||
|
||||
}
|
||||
};
|
||||
@@ -124,7 +124,7 @@ public class MapGenerateDialog extends FloatingDialog{
|
||||
Tile tile = editor.tile(x, y);
|
||||
input.apply(x, y, tile.floor(), tile.block(), tile.overlay());
|
||||
filter.apply(input);
|
||||
writeTiles[x][y].set(input.floor, input.block, input.ore, tile.getTeam(), tile.rotation());
|
||||
writeTiles[x][y].set(input.floor, input.block, input.ore, tile.team(), tile.rotation());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -146,7 +146,6 @@ public class MapGenerateDialog extends FloatingDialog{
|
||||
}
|
||||
|
||||
//reset undo stack as generation... messes things up
|
||||
editor.load(editor::checkLinkedTiles);
|
||||
editor.renderer().updateAll();
|
||||
editor.clearOp();
|
||||
}
|
||||
@@ -263,7 +262,7 @@ public class MapGenerateDialog extends FloatingDialog{
|
||||
//all the options
|
||||
c.table(f -> {
|
||||
f.left().top();
|
||||
for(FilterOption option : filter.options){
|
||||
for(FilterOption option : filter.options()){
|
||||
option.changed = this::update;
|
||||
|
||||
f.table(t -> {
|
||||
@@ -292,7 +291,7 @@ public class MapGenerateDialog extends FloatingDialog{
|
||||
for(Prov<GenerateFilter> gen : filterTypes){
|
||||
GenerateFilter filter = gen.get();
|
||||
|
||||
if(!applied && filter.buffered) continue;
|
||||
if((!applied && filter.isBuffered()) || (filter.isPost() && applied)) continue;
|
||||
|
||||
selection.cont.addButton(filter.name(), () -> {
|
||||
filters.add(filter);
|
||||
@@ -360,21 +359,17 @@ public class MapGenerateDialog extends FloatingDialog{
|
||||
|
||||
for(GenerateFilter filter : copy){
|
||||
input.begin(filter, editor.width(), editor.height(), (x, y) -> buffer1[Mathf.clamp(x / scaling, 0, pixmap.getWidth()-1)][Mathf.clamp(y / scaling, 0, pixmap.getHeight()-1)].tile());
|
||||
|
||||
//read from buffer1 and write to buffer2
|
||||
for(int px = 0; px < pixmap.getWidth(); px++){
|
||||
for(int py = 0; py < pixmap.getHeight(); py++){
|
||||
int x = px * scaling, y = py * scaling;
|
||||
GenTile tile = buffer1[px][py];
|
||||
input.apply(x, y, content.block(tile.floor), content.block(tile.block), content.block(tile.ore));
|
||||
filter.apply(input);
|
||||
buffer2[px][py].set(input.floor, input.block, input.ore, Team.get(tile.team), tile.rotation);
|
||||
}
|
||||
}
|
||||
for(int px = 0; px < pixmap.getWidth(); px++){
|
||||
for(int py = 0; py < pixmap.getHeight(); py++){
|
||||
buffer1[px][py].set(buffer2[px][py]);
|
||||
}
|
||||
}
|
||||
pixmap.each((px, py) -> {
|
||||
int x = px * scaling, y = py * scaling;
|
||||
GenTile tile = buffer1[px][py];
|
||||
input.apply(x, y, content.block(tile.floor), content.block(tile.block), content.block(tile.ore));
|
||||
filter.apply(input);
|
||||
buffer2[px][py].set(input.floor, input.block, input.ore, Team.get(tile.team), tile.rotation);
|
||||
});
|
||||
|
||||
pixmap.each((px, py) -> buffer1[px][py].set(buffer2[px][py]));
|
||||
}
|
||||
|
||||
for(int px = 0; px < pixmap.getWidth(); px++){
|
||||
@@ -428,7 +423,7 @@ public class MapGenerateDialog extends FloatingDialog{
|
||||
}
|
||||
|
||||
public GenTile set(Tile other){
|
||||
set(other.floor(), other.block(), other.overlay(), other.getTeam(), other.rotation());
|
||||
set(other.floor(), other.block(), other.overlay(), other.team(), other.rotation());
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,6 @@ import mindustry.game.Team;
|
||||
import mindustry.graphics.IndexedRenderer;
|
||||
import mindustry.world.Block;
|
||||
import mindustry.world.Tile;
|
||||
import mindustry.world.blocks.BlockPart;
|
||||
|
||||
import static mindustry.Vars.tilesize;
|
||||
|
||||
@@ -109,9 +108,9 @@ public class MapRenderer implements Disposable{
|
||||
private void render(int wx, int wy){
|
||||
int x = wx / chunkSize, y = wy / chunkSize;
|
||||
IndexedRenderer mesh = chunks[x][y];
|
||||
Tile tile = editor.tiles()[wx][wy];
|
||||
Tile tile = editor.tiles().getn(wx, wy);
|
||||
|
||||
Team team = tile.getTeam();
|
||||
Team team = tile.team();
|
||||
Block floor = tile.floor();
|
||||
Block wall = tile.block();
|
||||
|
||||
@@ -119,9 +118,10 @@ public class MapRenderer implements Disposable{
|
||||
|
||||
int idxWall = (wx % chunkSize) + (wy % chunkSize) * chunkSize;
|
||||
int idxDecal = (wx % chunkSize) + (wy % chunkSize) * chunkSize + chunkSize * chunkSize;
|
||||
boolean center = tile.isCenter();
|
||||
|
||||
if(wall != Blocks.air && (wall.synthetic() || wall instanceof BlockPart)){
|
||||
region = !Core.atlas.isFound(wall.editorIcon()) ? Core.atlas.find("clear-editor") : wall.editorIcon();
|
||||
if(wall != Blocks.air && wall.synthetic()){
|
||||
region = !Core.atlas.isFound(wall.editorIcon()) || !center ? Core.atlas.find("clear-editor") : wall.editorIcon();
|
||||
|
||||
if(wall.rotate){
|
||||
mesh.draw(idxWall, region,
|
||||
@@ -143,14 +143,14 @@ public class MapRenderer implements Disposable{
|
||||
|
||||
float offsetX = -(wall.size / 3) * tilesize, offsetY = -(wall.size / 3) * tilesize;
|
||||
|
||||
if(wall.update || wall.destructible){
|
||||
if((wall.update || wall.destructible) && center){
|
||||
mesh.setColor(team.color);
|
||||
region = Core.atlas.find("block-border-editor");
|
||||
}else if(!wall.synthetic() && wall != Blocks.air){
|
||||
}else if(!wall.synthetic() && wall != Blocks.air && center){
|
||||
region = !Core.atlas.isFound(wall.editorIcon()) ? Core.atlas.find("clear-editor") : wall.editorIcon();
|
||||
offsetX = tilesize / 2f - region.getWidth() / 2f * Draw.scl;
|
||||
offsetY = tilesize / 2f - region.getHeight() / 2f * Draw.scl;
|
||||
}else if(wall == Blocks.air && tile.overlay() != null){
|
||||
}else if(wall == Blocks.air){
|
||||
region = tile.overlay().editorVariantRegions()[Mathf.randomSeed(idxWall, 0, tile.overlay().editorVariantRegions().length - 1)];
|
||||
}else{
|
||||
region = Core.atlas.find("clear-editor");
|
||||
|
||||
@@ -236,7 +236,7 @@ public class MapView extends Element implements GestureListener{
|
||||
|
||||
image.setImageSize(editor.width(), editor.height());
|
||||
|
||||
if(!ScissorStack.pushScissors(rect.set(x, y, width, height))){
|
||||
if(!ScissorStack.push(rect.set(x, y, width, height))){
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -304,7 +304,7 @@ public class MapView extends Element implements GestureListener{
|
||||
Lines.rect(x, y, width, height);
|
||||
Draw.reset();
|
||||
|
||||
ScissorStack.popScissors();
|
||||
ScissorStack.pop();
|
||||
}
|
||||
|
||||
private boolean active(){
|
||||
|
||||
37
core/src/mindustry/entities/AllDefs.java
Normal file
37
core/src/mindustry/entities/AllDefs.java
Normal file
@@ -0,0 +1,37 @@
|
||||
package mindustry.entities;
|
||||
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.gen.*;
|
||||
|
||||
class AllDefs{
|
||||
|
||||
@GroupDef(Entityc.class)
|
||||
class all{
|
||||
|
||||
}
|
||||
|
||||
@GroupDef(value = Playerc.class, mapping = true)
|
||||
class player{
|
||||
|
||||
}
|
||||
|
||||
@GroupDef(value = Bulletc.class, spatial = true, collide = {unit.class})
|
||||
class bullet{
|
||||
|
||||
}
|
||||
|
||||
@GroupDef(value = Unitc.class, spatial = true, collide = {unit.class}, mapping = true)
|
||||
class unit{
|
||||
|
||||
}
|
||||
|
||||
@GroupDef(Tilec.class)
|
||||
class tile{
|
||||
|
||||
}
|
||||
|
||||
@GroupDef(value = Syncc.class, mapping = true)
|
||||
class sync{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,17 +1,14 @@
|
||||
package mindustry.entities;
|
||||
|
||||
import arc.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import arc.struct.*;
|
||||
import arc.func.*;
|
||||
import arc.graphics.*;
|
||||
import arc.math.*;
|
||||
import arc.math.geom.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.entities.Effects.*;
|
||||
import mindustry.entities.effect.*;
|
||||
import mindustry.entities.type.*;
|
||||
import mindustry.game.EventType.*;
|
||||
import mindustry.game.*;
|
||||
import mindustry.gen.*;
|
||||
@@ -33,12 +30,11 @@ public class Damage{
|
||||
public static void dynamicExplosion(float x, float y, float flammability, float explosiveness, float power, float radius, Color color){
|
||||
for(int i = 0; i < Mathf.clamp(power / 20, 0, 6); i++){
|
||||
int branches = 5 + Mathf.clamp((int)(power / 30), 1, 20);
|
||||
Time.run(i * 2f + Mathf.random(4f), () -> Lightning.create(Team.derelict, Pal.power, 3,
|
||||
x, y, Mathf.random(360f), branches + Mathf.range(2)));
|
||||
Time.run(i * 2f + Mathf.random(4f), () -> Lightning.create(Team.derelict, Pal.power, 3, x, y, Mathf.random(360f), branches + Mathf.range(2)));
|
||||
}
|
||||
|
||||
for(int i = 0; i < Mathf.clamp(flammability / 4, 0, 30); i++){
|
||||
Time.run(i / 2f, () -> Call.createBullet(Bullets.fireball, Team.derelict, x, y, Mathf.random(360f), 1, 1));
|
||||
Time.run(i / 2f, () -> Call.createBullet(Bullets.fireball, Team.derelict, x, y, -1f, Mathf.random(360f), 1, 1));
|
||||
}
|
||||
|
||||
int waves = Mathf.clamp((int)(explosiveness / 4), 0, 30);
|
||||
@@ -47,21 +43,21 @@ public class Damage{
|
||||
int f = i;
|
||||
Time.run(i * 2f, () -> {
|
||||
Damage.damage(x, y, Mathf.clamp(radius + explosiveness, 0, 50f) * ((f + 1f) / waves), explosiveness / 2f);
|
||||
Effects.effect(Fx.blockExplosionSmoke, x + Mathf.range(radius), y + Mathf.range(radius));
|
||||
Fx.blockExplosionSmoke.at(x + Mathf.range(radius), y + Mathf.range(radius));
|
||||
});
|
||||
}
|
||||
|
||||
if(explosiveness > 15f){
|
||||
Effects.effect(Fx.shockwave, x, y);
|
||||
Fx.shockwave.at(x, y);
|
||||
}
|
||||
|
||||
if(explosiveness > 30f){
|
||||
Effects.effect(Fx.bigShockwave, x, y);
|
||||
Fx.bigShockwave.at(x, y);
|
||||
}
|
||||
|
||||
float shake = Math.min(explosiveness / 4f + 3f, 9f);
|
||||
Effects.shake(shake, shake, x, y);
|
||||
Effects.effect(Fx.dynamicExplosion, x, y, radius / 8f);
|
||||
Fx.dynamicExplosion.at(x, y, radius / 8f);
|
||||
}
|
||||
|
||||
public static void createIncend(float x, float y, float range, int amount){
|
||||
@@ -70,12 +66,12 @@ public class Damage{
|
||||
float cy = y + Mathf.range(range);
|
||||
Tile tile = world.tileWorld(cx, cy);
|
||||
if(tile != null){
|
||||
Fire.create(tile);
|
||||
Fires.create(tile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void collideLine(Bullet hitter, Team team, Effect effect, float x, float y, float angle, float length){
|
||||
public static void collideLine(Bulletc hitter, Team team, Effect effect, float x, float y, float angle, float length){
|
||||
collideLine(hitter, team, effect, x, y, angle, length, false);
|
||||
}
|
||||
|
||||
@@ -83,15 +79,15 @@ public class Damage{
|
||||
* Damages entities in a line.
|
||||
* Only enemies of the specified team are damaged.
|
||||
*/
|
||||
public static void collideLine(Bullet hitter, Team team, Effect effect, float x, float y, float angle, float length, boolean large){
|
||||
public static void collideLine(Bulletc hitter, Team team, Effect effect, float x, float y, float angle, float length, boolean large){
|
||||
collidedBlocks.clear();
|
||||
tr.trns(angle, length);
|
||||
Intc2 collider = (cx, cy) -> {
|
||||
Tile tile = world.ltile(cx, cy);
|
||||
if(tile != null && !collidedBlocks.contains(tile.pos()) && tile.entity != null && tile.getTeamID() != team.id && tile.entity.collide(hitter)){
|
||||
tile.entity.collision(hitter);
|
||||
Tilec tile = world.ent(cx, cy);
|
||||
if(tile != null && !collidedBlocks.contains(tile.pos()) && tile.team() != team && tile.collide(hitter)){
|
||||
tile.collision(hitter);
|
||||
collidedBlocks.add(tile.pos());
|
||||
hitter.getBulletType().hit(hitter, tile.worldx(), tile.worldy());
|
||||
hitter.type().hit(hitter, tile.x(), tile.y());
|
||||
}
|
||||
};
|
||||
|
||||
@@ -125,7 +121,7 @@ public class Damage{
|
||||
rect.width += expand * 2;
|
||||
rect.height += expand * 2;
|
||||
|
||||
Cons<Unit> cons = e -> {
|
||||
Cons<Unitc> cons = e -> {
|
||||
e.hitbox(hitrect);
|
||||
Rect other = hitrect;
|
||||
other.y -= expand;
|
||||
@@ -136,7 +132,7 @@ public class Damage{
|
||||
Vec2 vec = Geometry.raycastRect(x, y, x2, y2, other);
|
||||
|
||||
if(vec != null){
|
||||
Effects.effect(effect, vec.x, vec.y);
|
||||
effect.at(vec.x, vec.y);
|
||||
e.collision(hitter, vec.x, vec.y);
|
||||
hitter.collision(e, vec.x, vec.y);
|
||||
}
|
||||
@@ -146,8 +142,8 @@ public class Damage{
|
||||
}
|
||||
|
||||
/** Damages all entities and blocks in a radius that are enemies of the team. */
|
||||
public static void damageUnits(Team team, float x, float y, float size, float damage, Boolf<Unit> predicate, Cons<Unit> acceptor){
|
||||
Cons<Unit> cons = entity -> {
|
||||
public static void damageUnits(Team team, float x, float y, float size, float damage, Boolf<Unitc> predicate, Cons<Unitc> acceptor){
|
||||
Cons<Unitc> cons = entity -> {
|
||||
if(!predicate.get(entity)) return;
|
||||
|
||||
entity.hitbox(hitrect);
|
||||
@@ -178,15 +174,15 @@ public class Damage{
|
||||
|
||||
/** Damages all entities and blocks in a radius that are enemies of the team. */
|
||||
public static void damage(Team team, float x, float y, float radius, float damage, boolean complete){
|
||||
Cons<Unit> cons = entity -> {
|
||||
if(entity.getTeam() == team || entity.dst(x, y) > radius){
|
||||
Cons<Unitc> cons = entity -> {
|
||||
if(entity.team() == team || entity.dst(x, y) > radius){
|
||||
return;
|
||||
}
|
||||
float amount = calculateDamage(x, y, entity.x, entity.y, radius, damage);
|
||||
float amount = calculateDamage(x, y, entity.getX(), entity.getY(), radius, damage);
|
||||
entity.damage(amount);
|
||||
//TODO better velocity displacement
|
||||
float dst = tr.set(entity.x - x, entity.y - y).len();
|
||||
entity.velocity().add(tr.setLength((1f - dst / radius) * 2f / entity.mass()));
|
||||
float dst = tr.set(entity.getX() - x, entity.getY() - y).len();
|
||||
entity.vel().add(tr.setLength((1f - dst / radius) * 2f / entity.mass()));
|
||||
|
||||
if(complete && damage >= 9999999f && entity == player){
|
||||
Events.fire(Trigger.exclusionDeath);
|
||||
@@ -231,15 +227,15 @@ public class Damage{
|
||||
int scaledDamage = (int)(damage * (1f - (float)dst / radius));
|
||||
|
||||
bits.set(bitOffset + x, bitOffset + y);
|
||||
Tile tile = world.ltile(startx + x, starty + y);
|
||||
Tilec tile = world.ent(startx + x, starty + y);
|
||||
|
||||
if(scaledDamage <= 0 || tile == null) continue;
|
||||
|
||||
//apply damage to entity if needed
|
||||
if(tile.entity != null && tile.getTeam() != team){
|
||||
int health = (int)tile.entity.health;
|
||||
if(tile.entity.health > 0){
|
||||
tile.entity.damage(scaledDamage);
|
||||
if(tile.team() != team){
|
||||
int health = (int)tile.health();
|
||||
if(tile.health() > 0){
|
||||
tile.damage(scaledDamage);
|
||||
scaledDamage -= health;
|
||||
|
||||
if(scaledDamage <= 0) continue;
|
||||
@@ -259,7 +255,7 @@ public class Damage{
|
||||
for(int dx = -trad; dx <= trad; dx++){
|
||||
for(int dy = -trad; dy <= trad; dy++){
|
||||
Tile tile = world.tile(Math.round(x / tilesize) + dx, Math.round(y / tilesize) + dy);
|
||||
if(tile != null && tile.entity != null && (team == null ||team.isEnemy(tile.getTeam())) && Mathf.dst(dx, dy) <= trad){
|
||||
if(tile != null && tile.entity != null && (team == null ||team.isEnemy(tile.team())) && Mathf.dst(dx, dy) <= trad){
|
||||
tile.entity.damage(damage);
|
||||
}
|
||||
}
|
||||
|
||||
118
core/src/mindustry/entities/Effect.java
Normal file
118
core/src/mindustry/entities/Effect.java
Normal file
@@ -0,0 +1,118 @@
|
||||
package mindustry.entities;
|
||||
|
||||
import arc.func.*;
|
||||
import arc.graphics.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.math.*;
|
||||
import arc.math.geom.*;
|
||||
|
||||
public class Effect{
|
||||
private static final EffectContainer container = new EffectContainer();
|
||||
private static int lastid = 0;
|
||||
|
||||
public final int id;
|
||||
public final Cons<EffectContainer> renderer;
|
||||
public final float lifetime;
|
||||
/** Clip size. */
|
||||
public float size;
|
||||
|
||||
public boolean ground;
|
||||
public float groundDuration;
|
||||
|
||||
public Effect(float life, float clipsize, Cons<EffectContainer> renderer){
|
||||
this.id = lastid++;
|
||||
this.lifetime = life;
|
||||
this.renderer = renderer;
|
||||
this.size = clipsize;
|
||||
}
|
||||
|
||||
public Effect(float life, Cons<EffectContainer> renderer){
|
||||
this(life, 28f, renderer);
|
||||
}
|
||||
|
||||
public Effect ground(){
|
||||
ground = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Effect ground(float duration){
|
||||
ground = true;
|
||||
this.groundDuration = duration;
|
||||
return this;
|
||||
}
|
||||
|
||||
public void at(Position pos){
|
||||
Effects.create(this, pos.getX(), pos.getY(), 0, Color.white, null);
|
||||
}
|
||||
|
||||
public void at(Position pos, float rotation){
|
||||
Effects.create(this, pos.getX(), pos.getY(), rotation, Color.white, null);
|
||||
}
|
||||
|
||||
public void at(float x, float y){
|
||||
Effects.create(this, x, y, 0, Color.white, null);
|
||||
}
|
||||
|
||||
public void at(float x, float y, float rotation){
|
||||
Effects.create(this, x, y, rotation, Color.white, null);
|
||||
}
|
||||
|
||||
public void at(float x, float y, float rotation, Color color){
|
||||
Effects.create(this, x, y, rotation, color, null);
|
||||
}
|
||||
|
||||
public void at(float x, float y, Color color){
|
||||
Effects.create(this, x, y, 0, color, null);
|
||||
}
|
||||
|
||||
public void at(float x, float y, float rotation, Color color, Object data){
|
||||
Effects.create(this, x, y, rotation, color, data);
|
||||
}
|
||||
|
||||
public void at(float x, float y, float rotation, Object data){
|
||||
Effects.create(this, x, y, rotation, Color.white, data);
|
||||
}
|
||||
|
||||
public void render(int id, Color color, float life, float rotation, float x, float y, Object data){
|
||||
container.set(id, color, life, lifetime, rotation, x, y, data);
|
||||
renderer.get(container);
|
||||
Draw.reset();
|
||||
}
|
||||
|
||||
public static class EffectContainer implements Scaled{
|
||||
public float x, y, time, lifetime, rotation;
|
||||
public Color color;
|
||||
public int id;
|
||||
public Object data;
|
||||
private EffectContainer innerContainer;
|
||||
|
||||
public void set(int id, Color color, float life, float lifetime, float rotation, float x, float y, Object data){
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.color = color;
|
||||
this.time = life;
|
||||
this.lifetime = lifetime;
|
||||
this.id = id;
|
||||
this.rotation = rotation;
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public <T> T data(){
|
||||
return (T)data;
|
||||
}
|
||||
|
||||
public void scaled(float lifetime, Cons<EffectContainer> cons){
|
||||
if(innerContainer == null) innerContainer = new EffectContainer();
|
||||
if(time <= lifetime){
|
||||
innerContainer.set(id, color, time, lifetime, rotation, x, y, data);
|
||||
cons.get(innerContainer);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public float fin(){
|
||||
return time / lifetime;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,90 +1,25 @@
|
||||
package mindustry.entities;
|
||||
|
||||
import arc.*;
|
||||
import arc.func.*;
|
||||
import arc.graphics.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.math.*;
|
||||
import arc.math.geom.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.pooling.*;
|
||||
import mindustry.entities.type.*;
|
||||
import arc.util.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.world.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class Effects{
|
||||
private static final EffectContainer container = new EffectContainer();
|
||||
private static Array<Effect> effects = new Array<>();
|
||||
private static ScreenshakeProvider shakeProvider;
|
||||
private static float shakeFalloff = 10000f;
|
||||
private static EffectProvider provider = (effect, color, x, y, rotation, data) -> {
|
||||
EffectEntity entity = Pools.obtain(EffectEntity.class, EffectEntity::new);
|
||||
entity.effect = effect;
|
||||
entity.color = color;
|
||||
entity.rotation = rotation;
|
||||
entity.data = data;
|
||||
entity.set(x, y);
|
||||
entity.add();
|
||||
};
|
||||
|
||||
public static void setEffectProvider(EffectProvider prov){
|
||||
provider = prov;
|
||||
}
|
||||
|
||||
public static void setScreenShakeProvider(ScreenshakeProvider provider){
|
||||
shakeProvider = provider;
|
||||
}
|
||||
|
||||
public static void renderEffect(int id, Effect render, Color color, float life, float rotation, float x, float y, Object data){
|
||||
container.set(id, color, life, render.lifetime, rotation, x, y, data);
|
||||
render.draw.render(container);
|
||||
Draw.reset();
|
||||
}
|
||||
|
||||
public static Effect getEffect(int id){
|
||||
if(id >= effects.size || id < 0)
|
||||
throw new IllegalArgumentException("The effect with ID \"" + id + "\" does not exist!");
|
||||
return effects.get(id);
|
||||
}
|
||||
|
||||
public static Array<Effect> all(){
|
||||
return effects;
|
||||
}
|
||||
|
||||
public static void effect(Effect effect, float x, float y, float rotation){
|
||||
provider.createEffect(effect, Color.white, x, y, rotation, null);
|
||||
}
|
||||
|
||||
public static void effect(Effect effect, float x, float y){
|
||||
effect(effect, x, y, 0);
|
||||
}
|
||||
|
||||
public static void effect(Effect effect, Color color, float x, float y){
|
||||
provider.createEffect(effect, color, x, y, 0f, null);
|
||||
}
|
||||
|
||||
public static void effect(Effect effect, Position loc){
|
||||
provider.createEffect(effect, Color.white, loc.getX(), loc.getY(), 0f, null);
|
||||
}
|
||||
|
||||
public static void effect(Effect effect, Color color, float x, float y, float rotation){
|
||||
provider.createEffect(effect, color, x, y, rotation, null);
|
||||
}
|
||||
|
||||
public static void effect(Effect effect, Color color, float x, float y, float rotation, Object data){
|
||||
provider.createEffect(effect, color, x, y, rotation, data);
|
||||
}
|
||||
|
||||
public static void effect(Effect effect, float x, float y, float rotation, Object data){
|
||||
provider.createEffect(effect, Color.white, x, y, rotation, data);
|
||||
}
|
||||
|
||||
/** Default value is 1000. Higher numbers mean more powerful shake (less falloff). */
|
||||
public static void setShakeFalloff(float falloff){
|
||||
shakeFalloff = falloff;
|
||||
}
|
||||
private static final float shakeFalloff = 10000f;
|
||||
|
||||
private static void shake(float intensity, float duration){
|
||||
if(shakeProvider == null) throw new RuntimeException("Screenshake provider is null! Set it first.");
|
||||
shakeProvider.accept(intensity, duration);
|
||||
if(!headless){
|
||||
renderer.shake(intensity, duration);
|
||||
}
|
||||
}
|
||||
|
||||
public static void shake(float intensity, float duration, float x, float y){
|
||||
@@ -100,68 +35,58 @@ public class Effects{
|
||||
shake(intensity, duration, loc.getX(), loc.getY());
|
||||
}
|
||||
|
||||
public interface ScreenshakeProvider{
|
||||
void accept(float intensity, float duration);
|
||||
}
|
||||
public static void create(Effect effect, float x, float y, float rotation, Color color, Object data){
|
||||
if(headless || effect == Fx.none) return;
|
||||
if(Core.settings.getBool("effects")){
|
||||
Rect view = Core.camera.bounds(Tmp.r1);
|
||||
Rect pos = Tmp.r2.setSize(effect.size).setCenter(x, y);
|
||||
|
||||
public static class Effect{
|
||||
private static int lastid = 0;
|
||||
public final int id;
|
||||
public final EffectRenderer draw;
|
||||
public final float lifetime;
|
||||
/** Clip size. */
|
||||
public float size;
|
||||
|
||||
public Effect(float life, float clipsize, EffectRenderer draw){
|
||||
this.id = lastid++;
|
||||
this.lifetime = life;
|
||||
this.draw = draw;
|
||||
this.size = clipsize;
|
||||
effects.add(this);
|
||||
}
|
||||
|
||||
public Effect(float life, EffectRenderer draw){
|
||||
this(life, 28f, draw);
|
||||
}
|
||||
}
|
||||
|
||||
public static class EffectContainer implements Scaled{
|
||||
public float x, y, time, lifetime, rotation;
|
||||
public Color color;
|
||||
public int id;
|
||||
public Object data;
|
||||
private EffectContainer innerContainer;
|
||||
|
||||
public void set(int id, Color color, float life, float lifetime, float rotation, float x, float y, Object data){
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.color = color;
|
||||
this.time = life;
|
||||
this.lifetime = lifetime;
|
||||
this.id = id;
|
||||
this.rotation = rotation;
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public void scaled(float lifetime, Cons<EffectContainer> cons){
|
||||
if(innerContainer == null) innerContainer = new EffectContainer();
|
||||
if(time <= lifetime){
|
||||
innerContainer.set(id, color, time, lifetime, rotation, x, y, data);
|
||||
cons.get(innerContainer);
|
||||
if(view.overlaps(pos)){
|
||||
Effectc entity = effect.ground ? GroundEffectEntity.create() : StandardEffectEntity.create();
|
||||
entity.effect(effect);
|
||||
entity.rotation(rotation);
|
||||
entity.data(data);
|
||||
entity.lifetime(effect.lifetime);
|
||||
entity.set(x, y);
|
||||
entity.color().set(color);
|
||||
if(data instanceof Posc) entity.parent((Posc)data);
|
||||
entity.add();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public float fin(){
|
||||
return time / lifetime;
|
||||
}
|
||||
}
|
||||
|
||||
public interface EffectProvider{
|
||||
void createEffect(Effect effect, Color color, float x, float y, float rotation, Object data);
|
||||
public static void decal(TextureRegion region, float x, float y, float rotation, float lifetime, Color color){
|
||||
if(headless || region == null || !Core.atlas.isFound(region)) return;
|
||||
|
||||
Decalc decal = DecalEntity.create();
|
||||
decal.set(x, y);
|
||||
decal.rotation(rotation);
|
||||
decal.lifetime(lifetime);
|
||||
decal.color().set(color);
|
||||
decal.region(region);
|
||||
decal.add();
|
||||
}
|
||||
|
||||
public interface EffectRenderer{
|
||||
void render(EffectContainer effect);
|
||||
public static void scorch(float x, float y, int size){
|
||||
if(headless) return;
|
||||
|
||||
Tile tile = world.tileWorld(x, y);
|
||||
if(tile == null || tile.floor().isLiquid) return;
|
||||
|
||||
size = Mathf.clamp(size, 0, 9);
|
||||
|
||||
TextureRegion region = Core.atlas.find("scorch-" + size + "-" + Mathf.random(2));
|
||||
decal(region, x, y, Mathf.random(4) * 90, 3600, Pal.rubble);
|
||||
|
||||
}
|
||||
|
||||
public static void rubble(float x, float y, int blockSize){
|
||||
if(headless) return;
|
||||
|
||||
Tile tile = world.tileWorld(x, y);
|
||||
if(tile == null || tile.floor().isLiquid) return;
|
||||
|
||||
TextureRegion region = Core.atlas.find("rubble-" + blockSize + "-" + (Core.atlas.has("rubble-" + blockSize + "-1") ? Mathf.random(0, 1) : "0"));
|
||||
decal(region, x, y, Mathf.random(4) * 90, 3600, Pal.rubble);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
package mindustry.entities;
|
||||
|
||||
import arc.struct.*;
|
||||
import mindustry.entities.traits.*;
|
||||
|
||||
/** Simple container for managing entity groups.*/
|
||||
public class Entities{
|
||||
private final Array<EntityGroup<?>> groupArray = new Array<>();
|
||||
|
||||
public void clear(){
|
||||
for(EntityGroup group : groupArray){
|
||||
group.clear();
|
||||
}
|
||||
}
|
||||
|
||||
public EntityGroup<?> get(int id){
|
||||
return groupArray.get(id);
|
||||
}
|
||||
|
||||
public Array<EntityGroup<?>> all(){
|
||||
return groupArray;
|
||||
}
|
||||
|
||||
public <T extends Entity> EntityGroup<T> add(Class<T> type){
|
||||
return add(type, true);
|
||||
}
|
||||
|
||||
public <T extends Entity> EntityGroup<T> add(Class<T> type, boolean useTree){
|
||||
EntityGroup<T> group = new EntityGroup<>(groupArray.size, type, useTree);
|
||||
groupArray.add(group);
|
||||
return group;
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,12 @@
|
||||
package mindustry.entities;
|
||||
|
||||
import arc.struct.Array;
|
||||
import arc.math.Mathf;
|
||||
import arc.math.*;
|
||||
import arc.math.geom.*;
|
||||
import mindustry.entities.traits.Entity;
|
||||
import mindustry.entities.traits.SolidTrait;
|
||||
import mindustry.world.Tile;
|
||||
import arc.struct.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.world.*;
|
||||
|
||||
import static mindustry.Vars.tilesize;
|
||||
import static mindustry.Vars.world;
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class EntityCollisions{
|
||||
//range for tile collision scanning
|
||||
@@ -24,15 +22,19 @@ public class EntityCollisions{
|
||||
private Rect r2 = new Rect();
|
||||
|
||||
//entity collisions
|
||||
private Array<SolidTrait> arrOut = new Array<>();
|
||||
private Array<Hitboxc> arrOut = new Array<>();
|
||||
|
||||
public void move(SolidTrait entity, float deltax, float deltay){
|
||||
public void move(Hitboxc entity, float deltax, float deltay){
|
||||
move(entity, deltax, deltay, EntityCollisions::solid);
|
||||
}
|
||||
|
||||
public void move(Hitboxc entity, float deltax, float deltay, SolidPred solidCheck){
|
||||
|
||||
boolean movedx = false;
|
||||
|
||||
while(Math.abs(deltax) > 0 || !movedx){
|
||||
movedx = true;
|
||||
moveDelta(entity, Math.min(Math.abs(deltax), seg) * Mathf.sign(deltax), 0, true);
|
||||
moveDelta(entity, Math.min(Math.abs(deltax), seg) * Mathf.sign(deltax), 0, true, solidCheck);
|
||||
|
||||
if(Math.abs(deltax) >= seg){
|
||||
deltax -= seg * Mathf.sign(deltax);
|
||||
@@ -45,7 +47,7 @@ public class EntityCollisions{
|
||||
|
||||
while(Math.abs(deltay) > 0 || !movedy){
|
||||
movedy = true;
|
||||
moveDelta(entity, 0, Math.min(Math.abs(deltay), seg) * Mathf.sign(deltay), false);
|
||||
moveDelta(entity, 0, Math.min(Math.abs(deltay), seg) * Mathf.sign(deltay), false, solidCheck);
|
||||
|
||||
if(Math.abs(deltay) >= seg){
|
||||
deltay -= seg * Mathf.sign(deltay);
|
||||
@@ -55,33 +57,30 @@ public class EntityCollisions{
|
||||
}
|
||||
}
|
||||
|
||||
public void moveDelta(SolidTrait entity, float deltax, float deltay, boolean x){
|
||||
|
||||
Rect rect = r1;
|
||||
entity.hitboxTile(rect);
|
||||
public void moveDelta(Hitboxc entity, float deltax, float deltay, boolean x, SolidPred solidCheck){
|
||||
entity.hitboxTile(r1);
|
||||
entity.hitboxTile(r2);
|
||||
rect.x += deltax;
|
||||
rect.y += deltay;
|
||||
r1.x += deltax;
|
||||
r1.y += deltay;
|
||||
|
||||
int tilex = Math.round((rect.x + rect.width / 2) / tilesize), tiley = Math.round((rect.y + rect.height / 2) / tilesize);
|
||||
int tilex = Math.round((r1.x + r1.width / 2) / tilesize), tiley = Math.round((r1.y + r1.height / 2) / tilesize);
|
||||
|
||||
for(int dx = -r; dx <= r; dx++){
|
||||
for(int dy = -r; dy <= r; dy++){
|
||||
int wx = dx + tilex, wy = dy + tiley;
|
||||
if(solid(wx, wy) && entity.collidesGrid(wx, wy)){
|
||||
if(solidCheck.solid(wx, wy)){
|
||||
tmp.setSize(tilesize).setCenter(wx * tilesize, wy * tilesize);
|
||||
|
||||
if(tmp.overlaps(rect)){
|
||||
Vec2 v = Geometry.overlap(rect, tmp, x);
|
||||
rect.x += v.x;
|
||||
rect.y += v.y;
|
||||
if(tmp.overlaps(r1)){
|
||||
Vec2 v = Geometry.overlap(r1, tmp, x);
|
||||
if(x) r1.x += v.x;
|
||||
if(!x) r1.y += v.y;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
entity.setX(entity.getX() + rect.x - r2.x);
|
||||
entity.setY(entity.getY() + rect.y - r2.y);
|
||||
entity.trns(r1.x - r2.x, r1.y - r2.y);
|
||||
}
|
||||
|
||||
public boolean overlapsTile(Rect rect){
|
||||
@@ -108,44 +107,43 @@ public class EntityCollisions{
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T extends Entity> void updatePhysics(EntityGroup<T> group){
|
||||
public <T extends Hitboxc> void updatePhysics(EntityGroup<T> group){
|
||||
|
||||
QuadTree tree = group.tree();
|
||||
tree.clear();
|
||||
|
||||
for(Entity entity : group.all()){
|
||||
if(entity instanceof SolidTrait){
|
||||
SolidTrait s = (SolidTrait)entity;
|
||||
s.lastPosition().set(s.getX(), s.getY());
|
||||
tree.insert(s);
|
||||
}
|
||||
}
|
||||
group.each(s -> {
|
||||
s.updateLastPosition();
|
||||
tree.insert(s);
|
||||
});
|
||||
}
|
||||
|
||||
private static boolean solid(int x, int y){
|
||||
public static boolean waterSolid(int x, int y){
|
||||
Tile tile = world.tile(x, y);
|
||||
return tile != null && (tile.solid() || !tile.floor().isLiquid);
|
||||
}
|
||||
|
||||
public static boolean solid(int x, int y){
|
||||
Tile tile = world.tile(x, y);
|
||||
return tile != null && tile.solid();
|
||||
}
|
||||
|
||||
private void checkCollide(Entity entity, Entity other){
|
||||
|
||||
SolidTrait a = (SolidTrait)entity;
|
||||
SolidTrait b = (SolidTrait)other;
|
||||
private void checkCollide(Hitboxc a, Hitboxc b){
|
||||
|
||||
a.hitbox(this.r1);
|
||||
b.hitbox(this.r2);
|
||||
|
||||
r1.x += (a.lastPosition().x - a.getX());
|
||||
r1.y += (a.lastPosition().y - a.getY());
|
||||
r2.x += (b.lastPosition().x - b.getX());
|
||||
r2.y += (b.lastPosition().y - b.getY());
|
||||
r1.x += (a.lastX() - a.getX());
|
||||
r1.y += (a.lastY() - a.getY());
|
||||
r2.x += (b.lastX() - b.getX());
|
||||
r2.y += (b.lastY() - b.getY());
|
||||
|
||||
float vax = a.getX() - a.lastPosition().x;
|
||||
float vay = a.getY() - a.lastPosition().y;
|
||||
float vbx = b.getX() - b.lastPosition().x;
|
||||
float vby = b.getY() - b.lastPosition().y;
|
||||
float vax = a.getX() - a.lastX();
|
||||
float vay = a.getY() - a.lastY();
|
||||
float vbx = b.getX() - b.lastX();
|
||||
float vby = b.getY() - b.lastY();
|
||||
|
||||
if(a != b && a.collides(b) && b.collides(a)){
|
||||
if(a != b && a.collides(b)){
|
||||
l1.set(a.getX(), a.getY());
|
||||
boolean collide = r1.overlaps(r2) || collide(r1.x, r1.y, r1.width, r1.height, vax, vay,
|
||||
r2.x, r2.y, r2.width, r2.height, vbx, vby, l1);
|
||||
@@ -207,17 +205,12 @@ public class EntityCollisions{
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void collideGroups(EntityGroup<?> groupa, EntityGroup<?> groupb){
|
||||
|
||||
for(Entity entity : groupa.all()){
|
||||
if(!(entity instanceof SolidTrait))
|
||||
continue;
|
||||
|
||||
SolidTrait solid = (SolidTrait)entity;
|
||||
public void collideGroups(EntityGroup<? extends Hitboxc> groupa, EntityGroup<? extends Hitboxc> groupb){
|
||||
|
||||
groupa.each(solid -> {
|
||||
solid.hitbox(r1);
|
||||
r1.x += (solid.lastPosition().x - solid.getX());
|
||||
r1.y += (solid.lastPosition().y - solid.getY());
|
||||
r1.x += (solid.lastX() - solid.getX());
|
||||
r1.y += (solid.lastY() - solid.getY());
|
||||
|
||||
solid.hitbox(r2);
|
||||
r2.merge(r1);
|
||||
@@ -225,12 +218,18 @@ public class EntityCollisions{
|
||||
arrOut.clear();
|
||||
groupb.tree().getIntersect(arrOut, r2);
|
||||
|
||||
for(SolidTrait sc : arrOut){
|
||||
for(Hitboxc sc : arrOut){
|
||||
sc.hitbox(r1);
|
||||
if(r2.overlaps(r1)){
|
||||
checkCollide(entity, sc);
|
||||
checkCollide(solid, sc);
|
||||
//break out of loop when this object hits something
|
||||
if(!solid.isAdded()) return;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public interface SolidPred{
|
||||
boolean solid(int x, int y);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
package mindustry.entities;
|
||||
|
||||
import arc.*;
|
||||
import arc.struct.*;
|
||||
import arc.func.*;
|
||||
import arc.graphics.*;
|
||||
import arc.math.geom.*;
|
||||
import mindustry.entities.traits.*;
|
||||
import arc.struct.*;
|
||||
import mindustry.gen.*;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
@@ -13,128 +12,82 @@ import static mindustry.Vars.collisions;
|
||||
|
||||
/** Represents a group of a certain type of entity.*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public class EntityGroup<T extends Entity> implements Iterable<T>{
|
||||
private final boolean useTree;
|
||||
private final int id;
|
||||
private final Class<T> type;
|
||||
private final Array<T> entityArray = new Array<>(false, 32);
|
||||
private final Array<T> entitiesToRemove = new Array<>(false, 32);
|
||||
private final Array<T> entitiesToAdd = new Array<>(false, 32);
|
||||
public class EntityGroup<T extends Entityc> implements Iterable<T>{
|
||||
private static int lastId = 0;
|
||||
|
||||
private final Array<T> array;
|
||||
private final Array<T> intersectArray = new Array<>();
|
||||
private final Rect viewport = new Rect();
|
||||
private final Rect intersectRect = new Rect();
|
||||
private IntMap<T> map;
|
||||
private QuadTree tree;
|
||||
private Cons<T> removeListener;
|
||||
private Cons<T> addListener;
|
||||
private boolean clearing;
|
||||
|
||||
private final Rect viewport = new Rect();
|
||||
private int count = 0;
|
||||
private int index;
|
||||
|
||||
public EntityGroup(int id, Class<T> type, boolean useTree){
|
||||
this.useTree = useTree;
|
||||
this.id = id;
|
||||
this.type = type;
|
||||
public static int nextId(){
|
||||
return lastId++;
|
||||
}
|
||||
|
||||
if(useTree){
|
||||
public EntityGroup(Class<T> type, boolean spatial, boolean mapping){
|
||||
array = new Array<>(false, 32, type);
|
||||
|
||||
if(spatial){
|
||||
tree = new QuadTree<>(new Rect(0, 0, 0, 0));
|
||||
}
|
||||
|
||||
if(mapping){
|
||||
map = new IntMap<>();
|
||||
}
|
||||
}
|
||||
|
||||
public void sort(Comparator<? super T> comp){
|
||||
array.sort(comp);
|
||||
}
|
||||
|
||||
public void collide(EntityGroup<? extends Hitboxc> other){
|
||||
collisions.collideGroups((EntityGroup<? extends Hitboxc>)this, other);
|
||||
}
|
||||
|
||||
public void updatePhysics(){
|
||||
collisions.updatePhysics((EntityGroup<? extends Hitboxc>)this);
|
||||
}
|
||||
|
||||
public void update(){
|
||||
updateEvents();
|
||||
each(Entityc::update);
|
||||
}
|
||||
|
||||
if(useTree()){
|
||||
collisions.updatePhysics(this);
|
||||
}
|
||||
|
||||
for(Entity e : all()){
|
||||
e.update();
|
||||
public void each(Cons<T> cons){
|
||||
for(index = 0; index < array.size; index++){
|
||||
cons.get(array.items[index]);
|
||||
}
|
||||
}
|
||||
|
||||
public int countInBounds(){
|
||||
count = 0;
|
||||
draw(e -> true, e -> count++);
|
||||
return count;
|
||||
public void each(Boolf<T> filter, Cons<T> cons){
|
||||
for(index = 0; index < array.size; index++){
|
||||
if(filter.get(array.items[index])) cons.get(array.items[index]);
|
||||
}
|
||||
}
|
||||
|
||||
public void draw(){
|
||||
draw(e -> true);
|
||||
}
|
||||
public void draw(Cons<T> cons){
|
||||
Core.camera.bounds(viewport);
|
||||
|
||||
public void draw(Boolf<T> toDraw){
|
||||
draw(toDraw, t -> ((DrawTrait)t).draw());
|
||||
}
|
||||
|
||||
public void draw(Boolf<T> toDraw, Cons<T> cons){
|
||||
Camera cam = Core.camera;
|
||||
viewport.set(cam.position.x - cam.width / 2, cam.position.y - cam.height / 2, cam.width, cam.height);
|
||||
|
||||
for(Entity e : all()){
|
||||
if(!(e instanceof DrawTrait) || !toDraw.get((T)e) || !e.isAdded()) continue;
|
||||
DrawTrait draw = (DrawTrait)e;
|
||||
|
||||
if(viewport.overlaps(draw.getX() - draw.drawSize()/2f, draw.getY() - draw.drawSize()/2f, draw.drawSize(), draw.drawSize())){
|
||||
cons.get((T)e);
|
||||
each(e -> {
|
||||
Drawc draw = (Drawc)e;
|
||||
if(viewport.overlaps(draw.x() - draw.clipSize()/2f, draw.y() - draw.clipSize()/2f, draw.clipSize(), draw.clipSize())){
|
||||
cons.get(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public boolean useTree(){
|
||||
return useTree;
|
||||
}
|
||||
|
||||
public void setRemoveListener(Cons<T> removeListener){
|
||||
this.removeListener = removeListener;
|
||||
}
|
||||
|
||||
public void setAddListener(Cons<T> addListener){
|
||||
this.addListener = addListener;
|
||||
}
|
||||
|
||||
public EntityGroup<T> enableMapping(){
|
||||
map = new IntMap<>();
|
||||
return this;
|
||||
return map != null;
|
||||
}
|
||||
|
||||
public boolean mappingEnabled(){
|
||||
return map != null;
|
||||
}
|
||||
|
||||
public Class<T> getType(){
|
||||
return type;
|
||||
}
|
||||
|
||||
public int getID(){
|
||||
return id;
|
||||
}
|
||||
|
||||
public void updateEvents(){
|
||||
|
||||
for(T e : entitiesToAdd){
|
||||
if(e == null)
|
||||
continue;
|
||||
entityArray.add(e);
|
||||
e.added();
|
||||
|
||||
if(map != null){
|
||||
map.put(e.getID(), e);
|
||||
}
|
||||
}
|
||||
|
||||
entitiesToAdd.clear();
|
||||
|
||||
for(T e : entitiesToRemove){
|
||||
entityArray.remove(e, true);
|
||||
if(map != null){
|
||||
map.remove(e.getID());
|
||||
}
|
||||
e.removed();
|
||||
}
|
||||
|
||||
entitiesToRemove.clear();
|
||||
}
|
||||
|
||||
public T getByID(int id){
|
||||
if(map == null) throw new RuntimeException("Mapping is not enabled for group " + id + "!");
|
||||
return map.get(id);
|
||||
@@ -145,123 +98,95 @@ public class EntityGroup<T extends Entity> implements Iterable<T>{
|
||||
T t = map.get(id);
|
||||
if(t != null){ //remove if present in map already
|
||||
remove(t);
|
||||
}else{ //maybe it's being queued?
|
||||
for(T check : entitiesToAdd){
|
||||
if(check.getID() == id){ //if it is indeed queued, remove it
|
||||
entitiesToAdd.remove(check, true);
|
||||
if(removeListener != null){
|
||||
removeListener.get(check);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void intersect(float x, float y, float width, float height, Cons<? super T> out){
|
||||
//don't waste time for empty groups
|
||||
if(isEmpty()) return;
|
||||
tree().getIntersect(out, x, y, width, height);
|
||||
tree.getIntersect(out, x, y, width, height);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public Array<T> intersect(float x, float y, float width, float height){
|
||||
intersectArray.clear();
|
||||
//don't waste time for empty groups
|
||||
if(isEmpty()) return intersectArray;
|
||||
tree().getIntersect(intersectArray, intersectRect.set(x, y, width, height));
|
||||
tree.getIntersect(intersectArray, intersectRect.set(x, y, width, height));
|
||||
return intersectArray;
|
||||
}
|
||||
|
||||
public QuadTree tree(){
|
||||
if(!useTree) throw new RuntimeException("This group does not support quadtrees! Enable quadtrees when creating it.");
|
||||
if(tree == null) throw new RuntimeException("This group does not support quadtrees! Enable quadtrees when creating it.");
|
||||
return tree;
|
||||
}
|
||||
|
||||
/** Resizes the internal quadtree, if it is enabled.*/
|
||||
public void resize(float x, float y, float w, float h){
|
||||
if(useTree){
|
||||
if(tree != null){
|
||||
tree = new QuadTree<>(new Rect(x, y, w, h));
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isEmpty(){
|
||||
return entityArray.size == 0;
|
||||
return array.size == 0;
|
||||
}
|
||||
|
||||
public T index(int i){
|
||||
return array.get(i);
|
||||
}
|
||||
|
||||
public int size(){
|
||||
return entityArray.size;
|
||||
return array.size;
|
||||
}
|
||||
|
||||
public boolean contains(Boolf<T> pred){
|
||||
return array.contains(pred);
|
||||
}
|
||||
|
||||
public int count(Boolf<T> pred){
|
||||
int count = 0;
|
||||
for(int i = 0; i < entityArray.size; i++){
|
||||
if(pred.get(entityArray.get(i))) count++;
|
||||
}
|
||||
return count;
|
||||
return array.count(pred);
|
||||
}
|
||||
|
||||
public void add(T type){
|
||||
if(type == null) throw new RuntimeException("Cannot add a null entity!");
|
||||
if(type.getGroup() != null) return;
|
||||
type.setGroup(this);
|
||||
entitiesToAdd.add(type);
|
||||
array.add(type);
|
||||
|
||||
if(mappingEnabled()){
|
||||
map.put(type.getID(), type);
|
||||
}
|
||||
|
||||
if(addListener != null){
|
||||
addListener.get(type);
|
||||
map.put(type.id(), type);
|
||||
}
|
||||
}
|
||||
|
||||
public void remove(T type){
|
||||
if(clearing) return;
|
||||
if(type == null) throw new RuntimeException("Cannot remove a null entity!");
|
||||
type.setGroup(null);
|
||||
entitiesToRemove.add(type);
|
||||
int idx = array.indexOf(type, true);
|
||||
if(idx != -1){
|
||||
array.remove(idx);
|
||||
|
||||
if(removeListener != null){
|
||||
removeListener.get(type);
|
||||
//fix iteration index when removing
|
||||
if(index >= idx){
|
||||
index --;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void clear(){
|
||||
for(T entity : entityArray){
|
||||
entity.removed();
|
||||
entity.setGroup(null);
|
||||
}
|
||||
clearing = true;
|
||||
|
||||
for(T entity : entitiesToAdd)
|
||||
entity.setGroup(null);
|
||||
|
||||
for(T entity : entitiesToRemove)
|
||||
entity.setGroup(null);
|
||||
|
||||
entitiesToAdd.clear();
|
||||
entitiesToRemove.clear();
|
||||
entityArray.clear();
|
||||
array.each(Entityc::remove);
|
||||
array.clear();
|
||||
if(map != null)
|
||||
map.clear();
|
||||
|
||||
clearing = false;
|
||||
}
|
||||
|
||||
public T find(Boolf<T> pred){
|
||||
|
||||
for(int i = 0; i < entityArray.size; i++){
|
||||
if(pred.get(entityArray.get(i))) return entityArray.get(i);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/** Returns the array for iteration. */
|
||||
public Array<T> all(){
|
||||
return entityArray;
|
||||
return array.find(pred);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<T> iterator(){
|
||||
return entityArray.iterator();
|
||||
return array.iterator();
|
||||
}
|
||||
}
|
||||
|
||||
70
core/src/mindustry/entities/Fires.java
Normal file
70
core/src/mindustry/entities/Fires.java
Normal file
@@ -0,0 +1,70 @@
|
||||
package mindustry.entities;
|
||||
|
||||
import arc.*;
|
||||
import arc.math.geom.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.game.EventType.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.world.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class Fires{
|
||||
private static final float baseLifetime = 1000f;
|
||||
private static final IntMap<Firec> map = new IntMap<>();
|
||||
|
||||
/** Start a fire on the tile. If there already is a file there, refreshes its lifetime. */
|
||||
public static void create(Tile tile){
|
||||
if(net.client() || tile == null) return; //not clientside.
|
||||
|
||||
Firec fire = map.get(tile.pos());
|
||||
|
||||
if(fire == null){
|
||||
fire = FireEntity.create();
|
||||
fire.tile(tile);
|
||||
fire.lifetime(baseLifetime);
|
||||
fire.set(tile.worldx(), tile.worldy());
|
||||
fire.add();
|
||||
map.put(tile.pos(), fire);
|
||||
}else{
|
||||
fire.lifetime(baseLifetime);
|
||||
fire.time(0f);
|
||||
}
|
||||
}
|
||||
|
||||
public static Firec get(int x, int y){
|
||||
return map.get(Point2.pack(x, y));
|
||||
}
|
||||
|
||||
public static boolean has(int x, int y){
|
||||
if(!Structs.inBounds(x, y, world.width(), world.height()) || !map.containsKey(Point2.pack(x, y))){
|
||||
return false;
|
||||
}
|
||||
Firec fire = map.get(Point2.pack(x, y));
|
||||
return fire.isAdded() && fire.fin() < 1f && fire.tile() != null && fire.tile().x == x && fire.tile().y == y;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to extinguish a fire by shortening its life. If there is no fire here, does nothing.
|
||||
*/
|
||||
public static void extinguish(Tile tile, float intensity){
|
||||
if(tile != null && map.containsKey(tile.pos())){
|
||||
Firec fire = map.get(tile.pos());
|
||||
fire.time(fire.time() + intensity * Time.delta());
|
||||
Fx.steam.at(fire);
|
||||
if(fire.time() >= fire.lifetime()){
|
||||
Events.fire(Trigger.fireExtinguish);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void remove(Tile tile){
|
||||
map.remove(tile.pos());
|
||||
}
|
||||
|
||||
public static void register(Firec fire){
|
||||
map.put(fire.tile().pos(), fire);
|
||||
}
|
||||
}
|
||||
86
core/src/mindustry/entities/Lightning.java
Normal file
86
core/src/mindustry/entities/Lightning.java
Normal file
@@ -0,0 +1,86 @@
|
||||
package mindustry.entities;
|
||||
|
||||
import arc.graphics.*;
|
||||
import arc.math.*;
|
||||
import arc.math.geom.*;
|
||||
import arc.struct.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.game.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.world.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
//TODO move into a different class
|
||||
public class Lightning{
|
||||
private static final Rand random = new Rand();
|
||||
private static final Rect rect = new Rect();
|
||||
private static final Array<Unitc> entities = new Array<>();
|
||||
private static final IntSet hit = new IntSet();
|
||||
private static final int maxChain = 8;
|
||||
private static final float hitRange = 30f;
|
||||
private static boolean bhit = false;
|
||||
private static int lastSeed = 0;
|
||||
|
||||
/** Create a lighting branch at a location. Use Team.none to damage everyone. */
|
||||
public static void create(Team team, Color color, float damage, float x, float y, float targetAngle, int length){
|
||||
createLightingInternal(lastSeed++, team, color, damage, x, y, targetAngle, length);
|
||||
}
|
||||
|
||||
//TODO remote method
|
||||
//@Remote(called = Loc.server, unreliable = true)
|
||||
private static void createLightingInternal(int seed, Team team, Color color, float damage, float x, float y, float rotation, int length){
|
||||
random.setSeed(seed);
|
||||
hit.clear();
|
||||
|
||||
Array<Vec2> lines = new Array<>();
|
||||
bhit = false;
|
||||
|
||||
for(int i = 0; i < length / 2; i++){
|
||||
Bullets.damageLightning.create(null, team, x, y, 0f, damage, 1f, 1f, null);
|
||||
lines.add(new Vec2(x + Mathf.range(3f), y + Mathf.range(3f)));
|
||||
|
||||
if(lines.size > 1){
|
||||
bhit = false;
|
||||
Vec2 from = lines.get(lines.size - 2);
|
||||
Vec2 to = lines.get(lines.size - 1);
|
||||
world.raycastEach(world.toTile(from.getX()), world.toTile(from.getY()), world.toTile(to.getX()), world.toTile(to.getY()), (wx, wy) -> {
|
||||
|
||||
Tile tile = world.tile(wx, wy);
|
||||
if(tile != null && tile.block().insulated){
|
||||
bhit = true;
|
||||
//snap it instead of removing
|
||||
lines.get(lines.size -1).set(wx * tilesize, wy * tilesize);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
if(bhit) break;
|
||||
}
|
||||
|
||||
rect.setSize(hitRange).setCenter(x, y);
|
||||
entities.clear();
|
||||
if(hit.size < maxChain){
|
||||
Units.nearbyEnemies(team, rect, u -> {
|
||||
if(!hit.contains(u.id())){
|
||||
entities.add(u);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Unitc furthest = Geometry.findFurthest(x, y, entities);
|
||||
|
||||
if(furthest != null){
|
||||
hit.add(furthest.id());
|
||||
x = furthest.x();
|
||||
y = furthest.y();
|
||||
}else{
|
||||
rotation += random.range(20f);
|
||||
x += Angles.trnsx(rotation, hitRange / 2f);
|
||||
y += Angles.trnsy(rotation, hitRange / 2f);
|
||||
}
|
||||
}
|
||||
|
||||
Fx.lightning.at(x, y, rotation, color, lines);
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@ package mindustry.entities;
|
||||
import arc.math.*;
|
||||
import arc.math.geom.*;
|
||||
import arc.util.*;
|
||||
import mindustry.entities.traits.*;
|
||||
import mindustry.gen.*;
|
||||
|
||||
/**
|
||||
* Class for predicting shoot angles based on velocities of targets.
|
||||
@@ -51,11 +51,24 @@ public class Predict{
|
||||
return sol;
|
||||
}
|
||||
|
||||
public static Vec2 intercept(Position src, Hitboxc dst, float v){
|
||||
return intercept(src.getX(), src.getY(), dst.getX(), dst.getY(), dst.deltaX(), dst.deltaY(), v);
|
||||
}
|
||||
|
||||
public static Vec2 intercept(Position src, Position dst, float v){
|
||||
float ddx = 0, ddy = 0;
|
||||
if(dst instanceof Hitboxc){
|
||||
ddx = ((Hitboxc)dst).deltaX();
|
||||
ddy = ((Hitboxc)dst).deltaY();
|
||||
}
|
||||
return intercept(src.getX(), src.getY(), dst.getX(), dst.getY(), ddx, ddy, v);
|
||||
}
|
||||
|
||||
/**
|
||||
* See {@link #intercept(float, float, float, float, float, float, float)}.
|
||||
*/
|
||||
public static Vec2 intercept(TargetTrait src, TargetTrait dst, float v){
|
||||
return intercept(src.getX(), src.getY(), dst.getX(), dst.getY(), dst.getTargetVelocityX() - src.getTargetVelocityX()/(2f*Time.delta()), dst.getTargetVelocityY() - src.getTargetVelocityY()/(2f*Time.delta()), v);
|
||||
public static Vec2 intercept(Hitboxc src, Hitboxc dst, float v){
|
||||
return intercept(src.getX(), src.getY(), dst.getX(), dst.getY(), dst.deltaX() - src.deltaX()/(2f*Time.delta()), dst.deltaY() - src.deltaX()/(2f*Time.delta()), v);
|
||||
}
|
||||
|
||||
private static Vec2 quad(float a, float b, float c){
|
||||
|
||||
109
core/src/mindustry/entities/Puddles.java
Normal file
109
core/src/mindustry/entities/Puddles.java
Normal file
@@ -0,0 +1,109 @@
|
||||
package mindustry.entities;
|
||||
|
||||
import arc.math.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.game.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.world.*;
|
||||
|
||||
public class Puddles{
|
||||
private static final IntMap<Puddlec> map = new IntMap<>();
|
||||
|
||||
public static final float maxLiquid = 70f;
|
||||
|
||||
/** Deposists a Puddlec between tile and source. */
|
||||
public static void deposit(Tile tile, Tile source, Liquid liquid, float amount){
|
||||
deposit(tile, source, liquid, amount, 0);
|
||||
}
|
||||
|
||||
/** Deposists a Puddlec at a tile. */
|
||||
public static void deposit(Tile tile, Liquid liquid, float amount){
|
||||
deposit(tile, tile, liquid, amount, 0);
|
||||
}
|
||||
|
||||
/** Returns the Puddlec on the specified tile. May return null. */
|
||||
public static Puddlec get(Tile tile){
|
||||
return map.get(tile.pos());
|
||||
}
|
||||
|
||||
public static void deposit(Tile tile, Tile source, Liquid liquid, float amount, int generation){
|
||||
if(tile == null) return;
|
||||
|
||||
if(tile.floor().isLiquid && !canStayOn(liquid, tile.floor().liquidDrop)){
|
||||
reactPuddle(tile.floor().liquidDrop, liquid, amount, tile,
|
||||
(tile.worldx() + source.worldx()) / 2f, (tile.worldy() + source.worldy()) / 2f);
|
||||
|
||||
Puddlec p = map.get(tile.pos());
|
||||
|
||||
if(generation == 0 && p != null && p.lastRipple() <= Time.time() - 40f){
|
||||
Fx.ripple.at((tile.worldx() + source.worldx()) / 2f, (tile.worldy() + source.worldy()) / 2f, tile.floor().liquidDrop.color);
|
||||
p.lastRipple(Time.time());
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
Puddlec p = map.get(tile.pos());
|
||||
if(p == null){
|
||||
Puddlec puddle = PuddleEntity.create();
|
||||
puddle.tile(tile);
|
||||
puddle.liquid(liquid);
|
||||
puddle.amount(amount);
|
||||
puddle.generation(generation);
|
||||
puddle.set((tile.worldx() + source.worldx()) / 2f, (tile.worldy() + source.worldy()) / 2f);
|
||||
puddle.add();
|
||||
map.put(tile.pos(), puddle);
|
||||
}else if(p.liquid() == liquid){
|
||||
p.accepting(Math.max(amount, p.accepting()));
|
||||
|
||||
if(generation == 0 && p.lastRipple() <= Time.time() - 40f && p.amount() >= maxLiquid / 2f){
|
||||
Fx.ripple.at((tile.worldx() + source.worldx()) / 2f, (tile.worldy() + source.worldy()) / 2f, p.liquid().color);
|
||||
p.lastRipple(Time.time());
|
||||
}
|
||||
}else{
|
||||
p.amount(p.amount() + reactPuddle(p.liquid(), liquid, amount, p.tile(), p.x(), p.y()));
|
||||
}
|
||||
}
|
||||
|
||||
public static void remove(Tile tile){
|
||||
if(tile == null) return;
|
||||
|
||||
map.remove(tile.pos());
|
||||
}
|
||||
|
||||
public static void register(Puddlec puddle){
|
||||
map.put(puddle.tile().pos(), puddle);
|
||||
}
|
||||
|
||||
/** Reacts two liquids together at a location. */
|
||||
private static float reactPuddle(Liquid dest, Liquid liquid, float amount, Tile tile, float x, float y){
|
||||
if((dest.flammability > 0.3f && liquid.temperature > 0.7f) ||
|
||||
(liquid.flammability > 0.3f && dest.temperature > 0.7f)){ //flammable liquid + hot liquid
|
||||
Fires.create(tile);
|
||||
if(Mathf.chance(0.006 * amount)){
|
||||
Call.createBullet(Bullets.fireball, Team.derelict, x, y, Mathf.random(360f), -1f, 1f, 1f);
|
||||
}
|
||||
}else if(dest.temperature > 0.7f && liquid.temperature < 0.55f){ //cold liquid poured onto hot Puddlec
|
||||
if(Mathf.chance(0.5f * amount)){
|
||||
Fx.steam.at(x, y);
|
||||
}
|
||||
return -0.1f * amount;
|
||||
}else if(liquid.temperature > 0.7f && dest.temperature < 0.55f){ //hot liquid poured onto cold Puddlec
|
||||
if(Mathf.chance(0.8f * amount)){
|
||||
Fx.steam.at(x, y);
|
||||
}
|
||||
return -0.4f * amount;
|
||||
}
|
||||
return 0f;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the first liquid can 'stay' on the second one.
|
||||
* Currently, the only place where this can happen is oil on water.
|
||||
*/
|
||||
private static boolean canStayOn(Liquid liquid, Liquid other){
|
||||
return liquid == Liquids.oil && other == Liquids.water;
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,9 @@
|
||||
package mindustry.entities;
|
||||
|
||||
import arc.func.*;
|
||||
import arc.math.*;
|
||||
import arc.math.geom.*;
|
||||
import mindustry.entities.traits.*;
|
||||
import mindustry.entities.type.*;
|
||||
import mindustry.game.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.world.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
@@ -13,13 +11,13 @@ import static mindustry.Vars.*;
|
||||
/** Utility class for unit and team interactions.*/
|
||||
public class Units{
|
||||
private static Rect hitrect = new Rect();
|
||||
private static Unit result;
|
||||
private static Unitc result;
|
||||
private static float cdist;
|
||||
private static boolean boolResult;
|
||||
|
||||
/** @return whether this player can interact with a specific tile. if either of these are null, returns true.*/
|
||||
public static boolean canInteract(Player player, Tile tile){
|
||||
return player == null || tile == null || tile.interactable(player.getTeam());
|
||||
public static boolean canInteract(Playerc player, Tilec tile){
|
||||
return player == null || tile == null || tile.interactable(player.team());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -31,18 +29,18 @@ public class Units{
|
||||
* @param range The maximum distance from the target X/Y the targeter can be for it to be valid
|
||||
* @return whether the target is invalid
|
||||
*/
|
||||
public static boolean invalidateTarget(TargetTrait target, Team team, float x, float y, float range){
|
||||
return target == null || (range != Float.MAX_VALUE && !target.withinDst(x, y, range)) || target.getTeam() == team || !target.isValid();
|
||||
public static boolean invalidateTarget(Posc target, Team team, float x, float y, float range){
|
||||
return target == null || !target.isAdded() || (range != Float.MAX_VALUE && !target.withinDst(x, y, range)) || (target instanceof Teamc && ((Teamc)target).team() == team) || (target instanceof Healthc && !((Healthc)target).isValid());
|
||||
}
|
||||
|
||||
/** See {@link #invalidateTarget(TargetTrait, Team, float, float, float)} */
|
||||
public static boolean invalidateTarget(TargetTrait target, Team team, float x, float y){
|
||||
/** See {@link #invalidateTarget(Posc, Team, float, float, float)} */
|
||||
public static boolean invalidateTarget(Posc target, Team team, float x, float y){
|
||||
return invalidateTarget(target, team, x, y, Float.MAX_VALUE);
|
||||
}
|
||||
|
||||
/** See {@link #invalidateTarget(TargetTrait, Team, float, float, float)} */
|
||||
public static boolean invalidateTarget(TargetTrait target, Unit targeter){
|
||||
return invalidateTarget(target, targeter.getTeam(), targeter.x, targeter.y, targeter.getWeapon().bullet.range());
|
||||
/** See {@link #invalidateTarget(Posc, Team, float, float, float)} */
|
||||
public static boolean invalidateTarget(Teamc target, Unitc targeter, float range){
|
||||
return invalidateTarget(target, targeter.team(), targeter.x(), targeter.y(), range);
|
||||
}
|
||||
|
||||
/** Returns whether there are any entities on this tile. */
|
||||
@@ -56,7 +54,7 @@ public class Units{
|
||||
|
||||
nearby(x, y, width, height, unit -> {
|
||||
if(boolResult) return;
|
||||
if(!unit.isFlying()){
|
||||
if(unit.isGrounded()){
|
||||
unit.hitbox(hitrect);
|
||||
|
||||
if(hitrect.overlaps(x, y, width, height)){
|
||||
@@ -69,38 +67,38 @@ public class Units{
|
||||
}
|
||||
|
||||
/** Returns the neareset damaged tile. */
|
||||
public static TileEntity findDamagedTile(Team team, float x, float y){
|
||||
public static Tilec findDamagedTile(Team team, float x, float y){
|
||||
Tile tile = Geometry.findClosest(x, y, indexer.getDamaged(team));
|
||||
return tile == null ? null : tile.entity;
|
||||
}
|
||||
|
||||
/** Returns the neareset ally tile in a range. */
|
||||
public static TileEntity findAllyTile(Team team, float x, float y, float range, Boolf<Tile> pred){
|
||||
public static Tilec findAllyTile(Team team, float x, float y, float range, Boolf<Tilec> pred){
|
||||
return indexer.findTile(team, x, y, range, pred);
|
||||
}
|
||||
|
||||
/** Returns the neareset enemy tile in a range. */
|
||||
public static TileEntity findEnemyTile(Team team, float x, float y, float range, Boolf<Tile> pred){
|
||||
public static Tilec findEnemyTile(Team team, float x, float y, float range, Boolf<Tilec> pred){
|
||||
if(team == Team.derelict) return null;
|
||||
|
||||
return indexer.findEnemyTile(team, x, y, range, pred);
|
||||
}
|
||||
|
||||
/** Returns the closest target enemy. First, units are checked, then tile entities. */
|
||||
public static TargetTrait closestTarget(Team team, float x, float y, float range){
|
||||
return closestTarget(team, x, y, range, Unit::isValid);
|
||||
public static Teamc closestTarget(Team team, float x, float y, float range){
|
||||
return closestTarget(team, x, y, range, Unitc::isValid);
|
||||
}
|
||||
|
||||
/** Returns the closest target enemy. First, units are checked, then tile entities. */
|
||||
public static TargetTrait closestTarget(Team team, float x, float y, float range, Boolf<Unit> unitPred){
|
||||
public static Teamc closestTarget(Team team, float x, float y, float range, Boolf<Unitc> unitPred){
|
||||
return closestTarget(team, x, y, range, unitPred, t -> true);
|
||||
}
|
||||
|
||||
/** Returns the closest target enemy. First, units are checked, then tile entities. */
|
||||
public static TargetTrait closestTarget(Team team, float x, float y, float range, Boolf<Unit> unitPred, Boolf<Tile> tilePred){
|
||||
public static Teamc closestTarget(Team team, float x, float y, float range, Boolf<Unitc> unitPred, Boolf<Tilec> tilePred){
|
||||
if(team == Team.derelict) return null;
|
||||
|
||||
Unit unit = closestEnemy(team, x, y, range, unitPred);
|
||||
Unitc unit = closestEnemy(team, x, y, range, unitPred);
|
||||
if(unit != null){
|
||||
return unit;
|
||||
}else{
|
||||
@@ -109,16 +107,16 @@ public class Units{
|
||||
}
|
||||
|
||||
/** Returns the closest enemy of this team. Filter by predicate. */
|
||||
public static Unit closestEnemy(Team team, float x, float y, float range, Boolf<Unit> predicate){
|
||||
public static Unitc closestEnemy(Team team, float x, float y, float range, Boolf<Unitc> predicate){
|
||||
if(team == Team.derelict) return null;
|
||||
|
||||
result = null;
|
||||
cdist = 0f;
|
||||
|
||||
nearbyEnemies(team, x - range, y - range, range*2f, range*2f, e -> {
|
||||
if(e.isDead() || !predicate.get(e)) return;
|
||||
if(e.dead() || !predicate.get(e)) return;
|
||||
|
||||
float dst2 = Mathf.dst2(e.x, e.y, x, y);
|
||||
float dst2 = e.dst2(x, y);
|
||||
if(dst2 < range*range && (result == null || dst2 < cdist)){
|
||||
result = e;
|
||||
cdist = dst2;
|
||||
@@ -129,14 +127,14 @@ public class Units{
|
||||
}
|
||||
|
||||
/** Returns the closest ally of this team. Filter by predicate. */
|
||||
public static Unit closest(Team team, float x, float y, float range, Boolf<Unit> predicate){
|
||||
public static Unitc closest(Team team, float x, float y, float range, Boolf<Unitc> predicate){
|
||||
result = null;
|
||||
cdist = 0f;
|
||||
|
||||
nearby(team, x, y, range, e -> {
|
||||
if(!predicate.get(e)) return;
|
||||
|
||||
float dist = Mathf.dst2(e.x, e.y, x, y);
|
||||
float dist = e.dst2(x, y);
|
||||
if(result == null || dist < cdist){
|
||||
result = e;
|
||||
cdist = dist;
|
||||
@@ -147,73 +145,45 @@ public class Units{
|
||||
}
|
||||
|
||||
/** Iterates over all units in a rectangle. */
|
||||
public static void nearby(Team team, float x, float y, float width, float height, Cons<Unit> cons){
|
||||
unitGroup.intersect(x, y, width, height, u -> {
|
||||
if(u.getTeam() == team){
|
||||
public static void nearby(Team team, float x, float y, float width, float height, Cons<Unitc> cons){
|
||||
Groups.unit.intersect(x, y, width, height, u -> {
|
||||
if(u.team() == team){
|
||||
cons.get(u);
|
||||
}
|
||||
});
|
||||
playerGroup.intersect(x, y, width, height, player -> {
|
||||
if(player.getTeam() == team){
|
||||
cons.get(player);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/** Iterates over all units in a circle around this position. */
|
||||
public static void nearby(Team team, float x, float y, float radius, Cons<Unit> cons){
|
||||
unitGroup.intersect(x - radius, y - radius, radius*2f, radius*2f, unit -> {
|
||||
if(unit.getTeam() == team && unit.withinDst(x, y, radius)){
|
||||
cons.get(unit);
|
||||
}
|
||||
});
|
||||
|
||||
playerGroup.intersect(x - radius, y - radius, radius*2f, radius*2f, unit -> {
|
||||
if(unit.getTeam() == team && unit.withinDst(x, y, radius)){
|
||||
public static void nearby(Team team, float x, float y, float radius, Cons<Unitc> cons){
|
||||
Groups.unit.intersect(x - radius, y - radius, radius*2f, radius*2f, unit -> {
|
||||
if(unit.team() == team && unit.withinDst(x, y, radius)){
|
||||
cons.get(unit);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/** Iterates over all units in a rectangle. */
|
||||
public static void nearby(float x, float y, float width, float height, Cons<Unit> cons){
|
||||
unitGroup.intersect(x, y, width, height, cons);
|
||||
playerGroup.intersect(x, y, width, height, cons);
|
||||
public static void nearby(float x, float y, float width, float height, Cons<Unitc> cons){
|
||||
Groups.unit.intersect(x, y, width, height, cons);
|
||||
}
|
||||
|
||||
/** Iterates over all units in a rectangle. */
|
||||
public static void nearby(Rect rect, Cons<Unit> cons){
|
||||
public static void nearby(Rect rect, Cons<Unitc> cons){
|
||||
nearby(rect.x, rect.y, rect.width, rect.height, cons);
|
||||
}
|
||||
|
||||
/** Iterates over all units that are enemies of this team. */
|
||||
public static void nearbyEnemies(Team team, float x, float y, float width, float height, Cons<Unit> cons){
|
||||
unitGroup.intersect(x, y, width, height, u -> {
|
||||
if(team.isEnemy(u.getTeam())){
|
||||
public static void nearbyEnemies(Team team, float x, float y, float width, float height, Cons<Unitc> cons){
|
||||
Groups.unit.intersect(x, y, width, height, u -> {
|
||||
if(team.isEnemy(u.team())){
|
||||
cons.get(u);
|
||||
}
|
||||
});
|
||||
|
||||
playerGroup.intersect(x, y, width, height, player -> {
|
||||
if(team.isEnemy(player.getTeam())){
|
||||
cons.get(player);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/** Iterates over all units that are enemies of this team. */
|
||||
public static void nearbyEnemies(Team team, Rect rect, Cons<Unit> cons){
|
||||
public static void nearbyEnemies(Team team, Rect rect, Cons<Unitc> cons){
|
||||
nearbyEnemies(team, rect.x, rect.y, rect.width, rect.height, cons);
|
||||
}
|
||||
|
||||
/** Iterates over all units. */
|
||||
public static void all(Cons<Unit> cons){
|
||||
unitGroup.all().each(cons);
|
||||
playerGroup.all().each(cons);
|
||||
}
|
||||
|
||||
public static void each(Team team, Cons<BaseUnit> cons){
|
||||
unitGroup.all().each(t -> t.getTeam() == team, cons);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -3,8 +3,6 @@ package mindustry.entities.bullet;
|
||||
import arc.graphics.g2d.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.entities.*;
|
||||
import mindustry.entities.Effects.*;
|
||||
import mindustry.entities.type.Bullet;
|
||||
import mindustry.gen.*;
|
||||
|
||||
//TODO scale velocity depending on fslope()
|
||||
@@ -25,25 +23,25 @@ public class ArtilleryBulletType extends BasicBulletType{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(Bullet b){
|
||||
public void update(Bulletc b){
|
||||
super.update(b);
|
||||
|
||||
if(b.timer.get(0, 3 + b.fslope() * 2f)){
|
||||
Effects.effect(trailEffect, backColor, b.x, b.y, b.fslope() * 4f);
|
||||
if(b.timer(0, 3 + b.fslope() * 2f)){
|
||||
trailEffect.at(b.x(), b.y(), b.fslope() * 4f, backColor);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Bullet b){
|
||||
public void draw(Bulletc b){
|
||||
float baseScale = 0.7f;
|
||||
float scale = (baseScale + b.fslope() * (1f - baseScale));
|
||||
|
||||
float height = bulletHeight * ((1f - bulletShrink) + bulletShrink * b.fout());
|
||||
|
||||
Draw.color(backColor);
|
||||
Draw.rect(backRegion, b.x, b.y, bulletWidth * scale, height * scale, b.rot() - 90);
|
||||
Draw.rect(backRegion, b.x(), b.y(), bulletWidth * scale, height * scale, b.rotation() - 90);
|
||||
Draw.color(frontColor);
|
||||
Draw.rect(frontRegion, b.x, b.y, bulletWidth * scale, height * scale, b.rot() - 90);
|
||||
Draw.rect(frontRegion, b.x(), b.y(), bulletWidth * scale, height * scale, b.rotation() - 90);
|
||||
Draw.color();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import arc.Core;
|
||||
import arc.graphics.Color;
|
||||
import arc.graphics.g2d.Draw;
|
||||
import arc.graphics.g2d.TextureRegion;
|
||||
import mindustry.entities.type.Bullet;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.Pal;
|
||||
|
||||
/** An extended BulletType for most ammo-based bullets shot from turrets and units. */
|
||||
@@ -34,13 +34,13 @@ public class BasicBulletType extends BulletType{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Bullet b){
|
||||
public void draw(Bulletc b){
|
||||
float height = bulletHeight * ((1f - bulletShrink) + bulletShrink * b.fout());
|
||||
|
||||
Draw.color(backColor);
|
||||
Draw.rect(backRegion, b.x, b.y, bulletWidth, height, b.rot() - 90);
|
||||
Draw.rect(backRegion, b.x(), b.y(), bulletWidth, height, b.rotation() - 90);
|
||||
Draw.color(frontColor);
|
||||
Draw.rect(frontRegion, b.x, b.y, bulletWidth, height, b.rot() - 90);
|
||||
Draw.rect(frontRegion, b.x(), b.y(), bulletWidth, height, b.rotation() - 90);
|
||||
Draw.color();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,17 +2,16 @@ package mindustry.entities.bullet;
|
||||
|
||||
import arc.audio.*;
|
||||
import arc.math.*;
|
||||
import arc.util.ArcAnnotate.*;
|
||||
import arc.util.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.ctype.*;
|
||||
import mindustry.entities.*;
|
||||
import mindustry.entities.Effects.*;
|
||||
import mindustry.entities.effect.*;
|
||||
import mindustry.entities.traits.*;
|
||||
import mindustry.entities.type.*;
|
||||
import mindustry.game.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.world.*;
|
||||
|
||||
public abstract class BulletType extends Content{
|
||||
public float lifetime;
|
||||
@@ -56,8 +55,8 @@ public abstract class BulletType extends Content{
|
||||
public boolean collidesTiles = true;
|
||||
/** Whether this bullet type collides with tiles that are of the same team. */
|
||||
public boolean collidesTeam = false;
|
||||
/** Whether this bullet type collides with air units. */
|
||||
public boolean collidesAir = true;
|
||||
/** Whether this bullet type collides with air/ground units. */
|
||||
public boolean collidesAir = true, collidesGround = true;
|
||||
/** Whether this bullet types collides with anything at all. */
|
||||
public boolean collides = true;
|
||||
/** Whether velocity is inherited from the shooter. */
|
||||
@@ -97,20 +96,20 @@ public abstract class BulletType extends Content{
|
||||
return speed * lifetime * (1f - drag);
|
||||
}
|
||||
|
||||
public boolean collides(Bullet bullet, Tile tile){
|
||||
public boolean collides(Bulletc bullet, Tilec tile){
|
||||
return true;
|
||||
}
|
||||
|
||||
public void hitTile(Bullet b, Tile tile){
|
||||
public void hitTile(Bulletc b, Tilec tile){
|
||||
hit(b);
|
||||
}
|
||||
|
||||
public void hit(Bullet b){
|
||||
hit(b, b.x, b.y);
|
||||
public void hit(Bulletc b){
|
||||
hit(b, b.getX(), b.getY());
|
||||
}
|
||||
|
||||
public void hit(Bullet b, float x, float y){
|
||||
Effects.effect(hitEffect, x, y, b.rot());
|
||||
public void hit(Bulletc b, float x, float y){
|
||||
hitEffect.at(x, y, b.rotation());
|
||||
hitSound.at(b);
|
||||
|
||||
Effects.shake(hitShake, hitShake, b);
|
||||
@@ -119,7 +118,7 @@ public abstract class BulletType extends Content{
|
||||
for(int i = 0; i < fragBullets; i++){
|
||||
float len = Mathf.random(1f, 7f);
|
||||
float a = Mathf.random(360f);
|
||||
Bullet.create(fragBullet, b, x + Angles.trnsx(a, len), y + Angles.trnsy(a, len), a, Mathf.random(fragVelocityMin, fragVelocityMax));
|
||||
fragBullet.create(b, x + Angles.trnsx(a, len), y + Angles.trnsy(a, len), a, Mathf.random(fragVelocityMin, fragVelocityMax));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -128,12 +127,12 @@ public abstract class BulletType extends Content{
|
||||
}
|
||||
|
||||
if(splashDamageRadius > 0){
|
||||
Damage.damage(b.getTeam(), x, y, splashDamageRadius, splashDamage * b.damageMultiplier());
|
||||
Damage.damage(b.team(), x, y, splashDamageRadius, splashDamage * b.damageMultiplier());
|
||||
}
|
||||
}
|
||||
|
||||
public void despawned(Bullet b){
|
||||
Effects.effect(despawnEffect, b.x, b.y, b.rot());
|
||||
public void despawned(Bulletc b){
|
||||
despawnEffect.at(b.getX(), b.getY(), b.rotation());
|
||||
hitSound.at(b);
|
||||
|
||||
if(fragBullet != null || splashDamageRadius > 0){
|
||||
@@ -141,16 +140,16 @@ public abstract class BulletType extends Content{
|
||||
}
|
||||
|
||||
for(int i = 0; i < lightining; i++){
|
||||
Lightning.createLighting(Lightning.nextSeed(), b.getTeam(), Pal.surge, damage, b.x, b.y, Mathf.random(360f), lightningLength);
|
||||
Lightning.create(b.team(), Pal.surge, damage, b.getX(), b.getY(), Mathf.random(360f), lightningLength);
|
||||
}
|
||||
}
|
||||
|
||||
public void draw(Bullet b){
|
||||
public void draw(Bulletc b){
|
||||
}
|
||||
|
||||
public void init(Bullet b){
|
||||
if(killShooter && b.getOwner() instanceof HealthTrait){
|
||||
((HealthTrait)b.getOwner()).kill();
|
||||
public void init(Bulletc b){
|
||||
if(killShooter && b.owner() instanceof Healthc){
|
||||
((Healthc)b.owner()).kill();
|
||||
}
|
||||
|
||||
if(instantDisappear){
|
||||
@@ -158,12 +157,11 @@ public abstract class BulletType extends Content{
|
||||
}
|
||||
}
|
||||
|
||||
public void update(Bullet b){
|
||||
|
||||
public void update(Bulletc b){
|
||||
if(homingPower > 0.0001f){
|
||||
TargetTrait target = Units.closestTarget(b.getTeam(), b.x, b.y, homingRange, e -> !e.isFlying() || collidesAir);
|
||||
Teamc target = Units.closestTarget(b.team(), b.getX(), b.getY(), homingRange, e -> e.isGrounded() || collidesAir);
|
||||
if(target != null){
|
||||
b.velocity().setAngle(Mathf.slerpDelta(b.velocity().angle(), b.angleTo(target), 0.08f));
|
||||
b.vel().setAngle(Mathf.slerpDelta(b.rotation(), b.angleTo(target), 0.08f));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -172,4 +170,58 @@ public abstract class BulletType extends Content{
|
||||
public ContentType getContentType(){
|
||||
return ContentType.bullet;
|
||||
}
|
||||
|
||||
//TODO change 'create' to 'at'
|
||||
|
||||
public Bulletc create(Teamc owner, float x, float y, float angle){
|
||||
return create(owner, owner.team(), x, y, angle);
|
||||
}
|
||||
|
||||
public Bulletc create(Entityc owner, Team team, float x, float y, float angle){
|
||||
return create(owner, team, x, y, angle, 1f);
|
||||
}
|
||||
|
||||
public Bulletc create(Entityc owner, Team team, float x, float y, float angle, float velocityScl){
|
||||
return create(owner, team, x, y, angle, -1, velocityScl, 1f, null);
|
||||
}
|
||||
|
||||
public Bulletc create(Entityc owner, Team team, float x, float y, float angle, float velocityScl, float lifetimeScl){
|
||||
return create(owner, team, x, y, angle, -1, velocityScl, lifetimeScl, null);
|
||||
}
|
||||
|
||||
public Bulletc create(Bulletc parent, float x, float y, float angle){
|
||||
return create(parent.owner(), parent.team(), x, y, angle);
|
||||
}
|
||||
|
||||
public Bulletc create(Bulletc parent, float x, float y, float angle, float velocityScl){
|
||||
return create(parent.owner(), parent.team(), x, y, angle, velocityScl);
|
||||
}
|
||||
|
||||
public Bulletc create(@Nullable Entityc owner, Team team, float x, float y, float angle, float damage, float velocityScl, float lifetimeScl, Object data){
|
||||
Bulletc bullet = BulletEntity.create();
|
||||
bullet.type(this);
|
||||
bullet.owner(owner);
|
||||
bullet.team(team);
|
||||
bullet.vel().trns(angle, speed * velocityScl);
|
||||
bullet.set(x - bullet.vel().x * Time.delta(), y - bullet.vel().y * Time.delta());
|
||||
bullet.lifetime(lifetime * lifetimeScl);
|
||||
bullet.data(data);
|
||||
bullet.drag(drag);
|
||||
bullet.hitSize(hitSize);
|
||||
bullet.damage(damage < 0 ? this.damage : damage);
|
||||
bullet.add();
|
||||
|
||||
if(keepVelocity && owner instanceof Velc) bullet.vel().add(((Velc)owner).vel());
|
||||
return bullet;
|
||||
|
||||
}
|
||||
|
||||
public void createNet(Team team, float x, float y, float angle, float damage, float velocityScl, float lifetimeScl){
|
||||
Call.createBullet(this, team, x, y, damage, angle, velocityScl, lifetimeScl);
|
||||
}
|
||||
|
||||
@Remote(called = Loc.server, unreliable = true)
|
||||
public static void createBullet(BulletType type, Team team, float x, float y, float angle, float damage, float velocityScl, float lifetimeScl){
|
||||
type.create(null, team, x, y, angle, damage, velocityScl, lifetimeScl, null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
package mindustry.entities.bullet;
|
||||
|
||||
import arc.math.geom.Rect;
|
||||
import arc.util.Time;
|
||||
import mindustry.content.Fx;
|
||||
import mindustry.entities.Units;
|
||||
import mindustry.entities.type.Bullet;
|
||||
import arc.util.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.entities.*;
|
||||
import mindustry.gen.*;
|
||||
|
||||
public class FlakBulletType extends BasicBulletType{
|
||||
protected static Rect rect = new Rect();
|
||||
protected float explodeRange = 30f;
|
||||
public float explodeRange = 30f;
|
||||
|
||||
public FlakBulletType(float speed, float damage){
|
||||
super(speed, damage, "shell");
|
||||
@@ -17,6 +15,7 @@ public class FlakBulletType extends BasicBulletType{
|
||||
hitEffect = Fx.flakExplosionBig;
|
||||
bulletWidth = 8f;
|
||||
bulletHeight = 10f;
|
||||
collidesGround = false;
|
||||
}
|
||||
|
||||
public FlakBulletType(){
|
||||
@@ -24,18 +23,18 @@ public class FlakBulletType extends BasicBulletType{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(Bullet b){
|
||||
public void update(Bulletc b){
|
||||
super.update(b);
|
||||
if(b.getData() instanceof Integer) return;
|
||||
if(b.data() instanceof Integer) return;
|
||||
|
||||
if(b.timer.get(2, 6)){
|
||||
Units.nearbyEnemies(b.getTeam(), rect.setSize(explodeRange * 2f).setCenter(b.x, b.y), unit -> {
|
||||
if(b.getData() instanceof Float) return;
|
||||
if(b.timer(2, 6)){
|
||||
Units.nearbyEnemies(b.team(), Tmp.r1.setSize(explodeRange * 2f).setCenter(b.x(), b.y()), unit -> {
|
||||
if(b.data() instanceof Float || (unit.isFlying() && !collidesAir) || (unit.isGrounded() && !collidesGround)) return;
|
||||
|
||||
if(unit.dst(b) < explodeRange){
|
||||
b.setData(0);
|
||||
b.data(0);
|
||||
Time.run(5f, () -> {
|
||||
if(b.getData() instanceof Integer){
|
||||
if(b.data() instanceof Integer){
|
||||
b.time(b.lifetime());
|
||||
}
|
||||
});
|
||||
|
||||
@@ -3,14 +3,14 @@ package mindustry.entities.bullet;
|
||||
import arc.graphics.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.entities.*;
|
||||
import mindustry.entities.type.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.blocks.*;
|
||||
|
||||
public class HealBulletType extends BulletType{
|
||||
protected float healPercent = 3f;
|
||||
protected float bulletHeight = 7f, bulletWidth = 2f;
|
||||
protected Color backColor = Pal.heal, frontColor = Color.white;
|
||||
|
||||
public HealBulletType(float speed, float damage){
|
||||
super(speed, damage);
|
||||
@@ -27,28 +27,27 @@ public class HealBulletType extends BulletType{
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean collides(Bullet b, Tile tile){
|
||||
return tile.getTeam() != b.getTeam() || tile.entity.healthf() < 1f;
|
||||
public boolean collides(Bulletc b, Tilec tile){
|
||||
return tile.team() != b.team() || tile.healthf() < 1f;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Bullet b){
|
||||
Draw.color(Pal.heal);
|
||||
Lines.stroke(2f);
|
||||
Lines.lineAngleCenter(b.x, b.y, b.rot(), 7f);
|
||||
Draw.color(Color.white);
|
||||
Lines.lineAngleCenter(b.x, b.y, b.rot(), 3f);
|
||||
public void draw(Bulletc b){
|
||||
Draw.color(backColor);
|
||||
Lines.stroke(bulletWidth);
|
||||
Lines.lineAngleCenter(b.x(), b.y(), b.rotation(), bulletHeight);
|
||||
Draw.color(frontColor);
|
||||
Lines.lineAngleCenter(b.x(), b.y(), b.rotation(), bulletHeight / 2f);
|
||||
Draw.reset();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hitTile(Bullet b, Tile tile){
|
||||
public void hitTile(Bulletc b, Tilec tile){
|
||||
super.hit(b);
|
||||
tile = tile.link();
|
||||
|
||||
if(tile.entity != null && tile.getTeam() == b.getTeam() && !(tile.block() instanceof BuildBlock)){
|
||||
Effects.effect(Fx.healBlockFull, Pal.heal, tile.drawx(), tile.drawy(), tile.block().size);
|
||||
tile.entity.healBy(healPercent / 100f * tile.entity.maxHealth());
|
||||
if(tile.team() == b.team() && !(tile.block() instanceof BuildBlock)){
|
||||
Fx.healBlockFull.at(tile.x(), tile.y(), tile.block().size, Pal.heal);
|
||||
tile.heal(healPercent / 100f * tile.maxHealth());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
73
core/src/mindustry/entities/bullet/LaserBulletType.java
Normal file
73
core/src/mindustry/entities/bullet/LaserBulletType.java
Normal file
@@ -0,0 +1,73 @@
|
||||
package mindustry.entities.bullet;
|
||||
|
||||
import arc.graphics.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.math.*;
|
||||
import arc.util.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.entities.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
|
||||
public class LaserBulletType extends BulletType{
|
||||
protected Color[] colors = {Pal.lancerLaser.cpy().mul(1f, 1f, 1f, 0.4f), Pal.lancerLaser, Color.white};
|
||||
protected float length = 160f;
|
||||
protected float width = 15f;
|
||||
protected float lengthFalloff = 0.5f;
|
||||
protected float sideLength = 29f, sideWidth = 0.7f;
|
||||
protected float sideAngle = 90f;
|
||||
|
||||
public LaserBulletType(float damage){
|
||||
super(0.01f, damage);
|
||||
|
||||
keepVelocity = false;
|
||||
hitEffect = Fx.hitLancer;
|
||||
despawnEffect = Fx.none;
|
||||
shootEffect = Fx.hitLancer;
|
||||
smokeEffect = Fx.lancerLaserShootSmoke;
|
||||
hitSize = 4;
|
||||
lifetime = 16f;
|
||||
pierce = true;
|
||||
}
|
||||
|
||||
public LaserBulletType(){
|
||||
this(1f);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float range(){
|
||||
return length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(Bulletc b){
|
||||
Damage.collideLine(b, b.team(), hitEffect, b.x(), b.y(), b.rotation(), length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Bulletc b){
|
||||
float f = Mathf.curve(b.fin(), 0f, 0.2f);
|
||||
float baseLen = length * f;
|
||||
float cwidth = width;
|
||||
float compound = 1f;
|
||||
|
||||
Lines.lineAngle(b.x(), b.y(), b.rotation(), baseLen);
|
||||
Lines.precise(true);
|
||||
for(Color color : colors){
|
||||
Draw.color(color);
|
||||
Lines.stroke((cwidth *= lengthFalloff) * b.fout());
|
||||
Lines.lineAngle(b.x(), b.y(), b.rotation(), baseLen, CapStyle.none);
|
||||
Tmp.v1.trns(b.rotation(), baseLen);
|
||||
Drawf.tri(b.x() + Tmp.v1.x, b.y() + Tmp.v1.y, Lines.getStroke() * 1.22f, cwidth * 2f + width / 2f, b.rotation());
|
||||
|
||||
Fill.circle(b.x(), b.y(), 1f * cwidth * b.fout());
|
||||
for(int i : Mathf.signs){
|
||||
Drawf.tri(b.x(), b.y(), sideWidth * b.fout() * cwidth, sideLength * compound, b.rotation() + sideAngle * i);
|
||||
}
|
||||
|
||||
compound *= lengthFalloff;
|
||||
}
|
||||
Lines.precise(false);
|
||||
Draw.reset();
|
||||
}
|
||||
}
|
||||
30
core/src/mindustry/entities/bullet/LightningBulletType.java
Normal file
30
core/src/mindustry/entities/bullet/LightningBulletType.java
Normal file
@@ -0,0 +1,30 @@
|
||||
package mindustry.entities.bullet;
|
||||
|
||||
import arc.graphics.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.entities.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
|
||||
public class LightningBulletType extends BulletType{
|
||||
protected Color lightningColor = Pal.lancerLaser;
|
||||
protected int lightningLength = 25;
|
||||
|
||||
public LightningBulletType(){
|
||||
super(0.0001f, 1f);
|
||||
|
||||
lifetime = 1;
|
||||
despawnEffect = Fx.none;
|
||||
hitEffect = Fx.hitLancer;
|
||||
keepVelocity = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Bulletc b){
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(Bulletc b){
|
||||
Lightning.create(b.team(), lightningColor, damage, b.x(), b.y(), b.rotation(), lightningLength);
|
||||
}
|
||||
}
|
||||
@@ -6,8 +6,7 @@ import arc.math.geom.*;
|
||||
import arc.util.ArcAnnotate.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.entities.*;
|
||||
import mindustry.entities.effect.*;
|
||||
import mindustry.entities.type.Bullet;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.world.*;
|
||||
|
||||
@@ -45,13 +44,13 @@ public class LiquidBulletType extends BulletType{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(Bullet b){
|
||||
public void update(Bulletc b){
|
||||
super.update(b);
|
||||
|
||||
if(liquid.canExtinguish()){
|
||||
Tile tile = world.tileWorld(b.x, b.y);
|
||||
if(tile != null && Fire.has(tile.x, tile.y)){
|
||||
Fire.extinguish(tile, 100f);
|
||||
Tile tile = world.tileWorld(b.x(), b.y());
|
||||
if(tile != null && Fires.has(tile.x, tile.y)){
|
||||
Fires.extinguish(tile, 100f);
|
||||
b.remove();
|
||||
hit(b);
|
||||
}
|
||||
@@ -59,22 +58,22 @@ public class LiquidBulletType extends BulletType{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Bullet b){
|
||||
public void draw(Bulletc b){
|
||||
Draw.color(liquid.color, Color.white, b.fout() / 100f);
|
||||
|
||||
Fill.circle(b.x, b.y, 0.5f + b.fout() * 2.5f);
|
||||
Fill.circle(b.x(), b.y(), 0.5f + b.fout() * 2.5f);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hit(Bullet b, float hitx, float hity){
|
||||
Effects.effect(hitEffect, liquid.color, hitx, hity);
|
||||
Puddle.deposit(world.tileWorld(hitx, hity), liquid, puddleSize);
|
||||
public void hit(Bulletc b, float hitx, float hity){
|
||||
hitEffect.at(hitx, hity, liquid.color);
|
||||
Puddles.deposit(world.tileWorld(hitx, hity), liquid, puddleSize);
|
||||
|
||||
if(liquid.temperature <= 0.5f && liquid.flammability < 0.3f){
|
||||
float intensity = 400f;
|
||||
Fire.extinguish(world.tileWorld(hitx, hity), intensity);
|
||||
Fires.extinguish(world.tileWorld(hitx, hity), intensity);
|
||||
for(Point2 p : Geometry.d4){
|
||||
Fire.extinguish(world.tileWorld(hitx + p.x * tilesize, hity + p.y * tilesize), intensity);
|
||||
Fires.extinguish(world.tileWorld(hitx + p.x * tilesize, hity + p.y * tilesize), intensity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,8 +5,7 @@ import arc.graphics.g2d.Draw;
|
||||
import arc.math.Angles;
|
||||
import arc.math.Mathf;
|
||||
import mindustry.content.Fx;
|
||||
import mindustry.entities.Effects;
|
||||
import mindustry.entities.type.Bullet;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.Pal;
|
||||
import mindustry.world.blocks.distribution.MassDriver.DriverBulletData;
|
||||
|
||||
@@ -24,32 +23,32 @@ public class MassDriverBolt extends BulletType{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(mindustry.entities.type.Bullet b){
|
||||
public void draw(Bulletc b){
|
||||
float w = 11f, h = 13f;
|
||||
|
||||
Draw.color(Pal.bulletYellowBack);
|
||||
Draw.rect("shell-back", b.x, b.y, w, h, b.rot() + 90);
|
||||
Draw.rect("shell-back", b.x(), b.y(), w, h, b.rotation() + 90);
|
||||
|
||||
Draw.color(Pal.bulletYellow);
|
||||
Draw.rect("shell", b.x, b.y, w, h, b.rot() + 90);
|
||||
Draw.rect("shell", b.x(), b.y(), w, h, b.rotation() + 90);
|
||||
|
||||
Draw.reset();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(mindustry.entities.type.Bullet b){
|
||||
public void update(Bulletc b){
|
||||
//data MUST be an instance of DriverBulletData
|
||||
if(!(b.getData() instanceof DriverBulletData)){
|
||||
if(!(b.data() instanceof DriverBulletData)){
|
||||
hit(b);
|
||||
return;
|
||||
}
|
||||
|
||||
float hitDst = 7f;
|
||||
|
||||
DriverBulletData data = (DriverBulletData)b.getData();
|
||||
DriverBulletData data = (DriverBulletData)b.data();
|
||||
|
||||
//if the target is dead, just keep flying until the bullet explodes
|
||||
if(data.to.isDead()){
|
||||
if(data.to.dead()){
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -68,7 +67,7 @@ public class MassDriverBolt extends BulletType{
|
||||
if(Angles.near(angleTo, baseAngle, 2f)){
|
||||
intersect = true;
|
||||
//snap bullet position back; this is used for low-FPS situations
|
||||
b.set(data.to.x + Angles.trnsx(baseAngle, hitDst), data.to.y + Angles.trnsy(baseAngle, hitDst));
|
||||
b.set(data.to.x() + Angles.trnsx(baseAngle, hitDst), data.to.y() + Angles.trnsy(baseAngle, hitDst));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,24 +82,24 @@ public class MassDriverBolt extends BulletType{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void despawned(mindustry.entities.type.Bullet b){
|
||||
public void despawned(Bulletc b){
|
||||
super.despawned(b);
|
||||
|
||||
if(!(b.getData() instanceof DriverBulletData)) return;
|
||||
if(!(b.data() instanceof DriverBulletData)) return;
|
||||
|
||||
DriverBulletData data = (DriverBulletData)b.getData();
|
||||
DriverBulletData data = (DriverBulletData)b.data();
|
||||
|
||||
for(int i = 0; i < data.items.length; i++){
|
||||
int amountDropped = Mathf.random(0, data.items[i]);
|
||||
if(amountDropped > 0){
|
||||
float angle = b.rot() + Mathf.range(100f);
|
||||
Effects.effect(Fx.dropItem, Color.white, b.x, b.y, angle, content.item(i));
|
||||
float angle = b.rotation() + Mathf.range(100f);
|
||||
Fx.dropItem.at(b.x(), b.y(), angle, Color.white, content.item(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hit(Bullet b, float hitx, float hity){
|
||||
public void hit(Bulletc b, float hitx, float hity){
|
||||
super.hit(b, hitx, hity);
|
||||
despawned(b);
|
||||
}
|
||||
|
||||
@@ -4,8 +4,6 @@ import arc.graphics.Color;
|
||||
import arc.math.Mathf;
|
||||
import arc.util.Time;
|
||||
import mindustry.content.Fx;
|
||||
import mindustry.entities.Effects;
|
||||
import mindustry.entities.type.Bullet;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.Pal;
|
||||
|
||||
@@ -28,15 +26,15 @@ public class MissileBulletType extends BasicBulletType{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(Bullet b){
|
||||
public void update(Bulletc b){
|
||||
super.update(b);
|
||||
|
||||
if(Mathf.chance(Time.delta() * 0.2)){
|
||||
Effects.effect(Fx.missileTrail, trailColor, b.x, b.y, 2f);
|
||||
Fx.missileTrail.at(b.x(), b.y(), 2f, trailColor);
|
||||
}
|
||||
|
||||
if(weaveMag > 0){
|
||||
b.velocity().rotate(Mathf.sin(Time.time() + b.id * 4422, weaveScale, weaveMag) * Time.delta());
|
||||
b.vel().rotate(Mathf.sin(Time.time() + b.id() * 442, weaveScale, weaveMag) * Time.delta());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
36
core/src/mindustry/entities/def/BoundedComp.java
Normal file
36
core/src/mindustry/entities/def/BoundedComp.java
Normal file
@@ -0,0 +1,36 @@
|
||||
package mindustry.entities.def;
|
||||
|
||||
import arc.math.*;
|
||||
import arc.math.geom.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.gen.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
@Component
|
||||
abstract class BoundedComp implements Velc, Posc, Healthc, Flyingc{
|
||||
static final float warpDst = 180f;
|
||||
|
||||
@Import float x, y;
|
||||
@Import Vec2 vel;
|
||||
|
||||
@Override
|
||||
public void update(){
|
||||
//repel unit out of bounds
|
||||
if(x < 0) vel.x += (-x/warpDst);
|
||||
if(y < 0) vel.y += (-y/warpDst);
|
||||
if(x > world.unitWidth()) vel.x -= (x - world.unitWidth())/warpDst;
|
||||
if(y > world.unitHeight()) vel.y -= (y - world.unitHeight())/warpDst;
|
||||
|
||||
//clamp position if not flying
|
||||
if(isGrounded()){
|
||||
x = Mathf.clamp(x, 0, world.width() * tilesize - tilesize);
|
||||
y = Mathf.clamp(y, 0, world.height() * tilesize - tilesize);
|
||||
}
|
||||
|
||||
//kill when out of bounds
|
||||
if(x < -finalWorldBounds || y < -finalWorldBounds || x >= world.width() * tilesize + finalWorldBounds || y >= world.height() * tilesize + finalWorldBounds){
|
||||
kill();
|
||||
}
|
||||
}
|
||||
}
|
||||
239
core/src/mindustry/entities/def/BuilderComp.java
Normal file
239
core/src/mindustry/entities/def/BuilderComp.java
Normal file
@@ -0,0 +1,239 @@
|
||||
package mindustry.entities.def;
|
||||
|
||||
import arc.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.math.*;
|
||||
import arc.math.geom.*;
|
||||
import arc.struct.Queue;
|
||||
import arc.util.ArcAnnotate.*;
|
||||
import arc.util.*;
|
||||
import mindustry.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.entities.units.*;
|
||||
import mindustry.game.EventType.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.blocks.*;
|
||||
import mindustry.world.blocks.BuildBlock.*;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
@Component
|
||||
abstract class BuilderComp implements Unitc, DrawLayerFlyingc{
|
||||
static final Vec2[] vecs = new Vec2[]{new Vec2(), new Vec2(), new Vec2(), new Vec2()};
|
||||
|
||||
@Import float x, y, rotation;
|
||||
|
||||
Queue<BuildRequest> requests = new Queue<>();
|
||||
transient float buildSpeed = 1f;
|
||||
//boolean building;
|
||||
|
||||
@Override
|
||||
public void update(){
|
||||
float finalPlaceDst = state.rules.infiniteResources ? Float.MAX_VALUE : buildingRange;
|
||||
|
||||
Iterator<BuildRequest> it = requests.iterator();
|
||||
while(it.hasNext()){
|
||||
BuildRequest req = it.next();
|
||||
Tile tile = world.tile(req.x, req.y);
|
||||
if(tile == null || (req.breaking && tile.block() == Blocks.air) || (!req.breaking && (tile.rotation() == req.rotation || !req.block.rotate) && tile.block() == req.block)){
|
||||
it.remove();
|
||||
}
|
||||
}
|
||||
|
||||
Tilec core = closestCore();
|
||||
|
||||
//nothing to build.
|
||||
if(buildRequest() == null) return;
|
||||
|
||||
//find the next build request
|
||||
if(requests.size > 1){
|
||||
int total = 0;
|
||||
BuildRequest req;
|
||||
while((dst((req = buildRequest()).tile()) > finalPlaceDst || shouldSkip(req, core)) && total < requests.size){
|
||||
requests.removeFirst();
|
||||
requests.addLast(req);
|
||||
total++;
|
||||
}
|
||||
}
|
||||
|
||||
BuildRequest current = buildRequest();
|
||||
|
||||
if(dst(current.tile()) > finalPlaceDst) return;
|
||||
|
||||
Tile tile = world.tile(current.x, current.y);
|
||||
|
||||
if(dst(tile) <= finalPlaceDst){
|
||||
rotation = Mathf.slerpDelta(rotation, angleTo(tile), 0.4f);
|
||||
}
|
||||
|
||||
if(!(tile.block() instanceof BuildBlock)){
|
||||
if(!current.initialized && !current.breaking && Build.validPlace(team(), current.x, current.y, current.block, current.rotation)){
|
||||
boolean hasAll = !Structs.contains(current.block.requirements, i -> !core.items().has(i.item));
|
||||
|
||||
if(hasAll){
|
||||
Build.beginPlace(team(), current.x, current.y, current.block, current.rotation);
|
||||
}else{
|
||||
current.stuck = true;
|
||||
}
|
||||
}else if(!current.initialized && current.breaking && Build.validBreak(team(), current.x, current.y)){
|
||||
Build.beginBreak(team(), current.x, current.y);
|
||||
}else{
|
||||
requests.removeFirst();
|
||||
return;
|
||||
}
|
||||
}else if(tile.team() != team()){
|
||||
requests.removeFirst();
|
||||
return;
|
||||
}
|
||||
|
||||
if(tile.entity instanceof BuildEntity && !current.initialized){
|
||||
Core.app.post(() -> Events.fire(new BuildSelectEvent(tile, team(), (Builderc)this, current.breaking)));
|
||||
current.initialized = true;
|
||||
}
|
||||
|
||||
//if there is no core to build with or no build entity, stop building!
|
||||
if((core == null && !state.rules.infiniteResources) || !(tile.entity instanceof BuildEntity)){
|
||||
return;
|
||||
}
|
||||
|
||||
//otherwise, update it.
|
||||
BuildEntity entity = tile.ent();
|
||||
|
||||
if(current.breaking){
|
||||
entity.deconstruct(this, core, 1f / entity.buildCost * Time.delta() * buildSpeed * state.rules.buildSpeedMultiplier);
|
||||
}else{
|
||||
if(entity.construct(this, core, 1f / entity.buildCost * Time.delta() * buildSpeed * state.rules.buildSpeedMultiplier, current.hasConfig)){
|
||||
if(current.hasConfig){
|
||||
Call.onTileConfig(null, tile.entity, current.config);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
current.stuck = Mathf.equal(current.progress, entity.progress);
|
||||
current.progress = entity.progress;
|
||||
}
|
||||
|
||||
|
||||
/** Draw all current build requests. Does not draw the beam effect, only the positions. */
|
||||
void drawBuildRequests(){
|
||||
|
||||
for(BuildRequest request : requests){
|
||||
if(request.progress > 0.01f || (buildRequest() == request && request.initialized && (dst(request.x * tilesize, request.y * tilesize) <= buildingRange || state.isEditor()))) continue;
|
||||
|
||||
request.animScale = 1f;
|
||||
if(request.breaking){
|
||||
control.input.drawBreaking(request);
|
||||
}else{
|
||||
request.block.drawRequest(request, control.input.allRequests(),
|
||||
Build.validPlace(team(), request.x, request.y, request.block, request.rotation) || control.input.requestMatches(request));
|
||||
}
|
||||
}
|
||||
|
||||
Draw.reset();
|
||||
}
|
||||
|
||||
/** @return whether this request should be skipped, in favor of the next one. */
|
||||
boolean shouldSkip(BuildRequest request, @Nullable Tilec core){
|
||||
//requests that you have at least *started* are considered
|
||||
if(state.rules.infiniteResources || request.breaking || core == null) return false;
|
||||
//TODO these are bad criteria
|
||||
return (request.stuck && !core.items().has(request.block.requirements)) || (Structs.contains(request.block.requirements, i -> !core.items().has(i.item)) && !request.initialized);
|
||||
}
|
||||
|
||||
void removeBuild(int x, int y, boolean breaking){
|
||||
//remove matching request
|
||||
int idx = requests.indexOf(req -> req.breaking == breaking && req.x == x && req.y == y);
|
||||
if(idx != -1){
|
||||
requests.removeIndex(idx);
|
||||
}
|
||||
}
|
||||
|
||||
/** Return whether this builder's place queue contains items. */
|
||||
boolean isBuilding(){
|
||||
return requests.size != 0;
|
||||
}
|
||||
|
||||
/** Clears the placement queue. */
|
||||
void clearBuilding(){
|
||||
requests.clear();
|
||||
}
|
||||
|
||||
/** Add another build requests to the tail of the queue, if it doesn't exist there yet. */
|
||||
void addBuild(BuildRequest place){
|
||||
addBuild(place, true);
|
||||
}
|
||||
|
||||
/** Add another build requests to the queue, if it doesn't exist there yet. */
|
||||
void addBuild(BuildRequest place, boolean tail){
|
||||
BuildRequest replace = null;
|
||||
for(BuildRequest request : requests){
|
||||
if(request.x == place.x && request.y == place.y){
|
||||
replace = request;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(replace != null){
|
||||
requests.remove(replace);
|
||||
}
|
||||
Tile tile = world.tile(place.x, place.y);
|
||||
if(tile != null && tile.entity instanceof BuildEntity){
|
||||
place.progress = tile.<BuildEntity>ent().progress;
|
||||
}
|
||||
if(tail){
|
||||
requests.addLast(place);
|
||||
}else{
|
||||
requests.addFirst(place);
|
||||
}
|
||||
}
|
||||
|
||||
/** Return the build requests currently active, or the one at the top of the queue.*/
|
||||
@Nullable BuildRequest buildRequest(){
|
||||
return requests.size == 0 ? null : requests.first();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawFlying(){
|
||||
if(!isBuilding()) return;
|
||||
BuildRequest request = buildRequest();
|
||||
Tile tile = world.tile(request.x, request.y);
|
||||
|
||||
if(dst(tile) > buildingRange && !state.isEditor()){
|
||||
return;
|
||||
}
|
||||
|
||||
int size = request.breaking ? tile.block().size : request.block.size;
|
||||
float tx = request.drawx(), ty = request.drawy();
|
||||
|
||||
Lines.stroke(1f, Pal.accent);
|
||||
float focusLen = 3.8f + Mathf.absin(Time.time(), 1.1f, 0.6f);
|
||||
float px = x + Angles.trnsx(rotation, focusLen);
|
||||
float py = y + Angles.trnsy(rotation, focusLen);
|
||||
|
||||
float sz = Vars.tilesize * size / 2f;
|
||||
float ang = angleTo(tx, ty);
|
||||
|
||||
vecs[0].set(tx - sz, ty - sz);
|
||||
vecs[1].set(tx + sz, ty - sz);
|
||||
vecs[2].set(tx - sz, ty + sz);
|
||||
vecs[3].set(tx + sz, ty + sz);
|
||||
|
||||
Arrays.sort(vecs, Structs.comparingFloat(vec -> -Angles.angleDist(angleTo(vec), ang)));
|
||||
|
||||
float x1 = vecs[0].x, y1 = vecs[0].y,
|
||||
x3 = vecs[1].x, y3 = vecs[1].y;
|
||||
|
||||
Draw.alpha(1f);
|
||||
|
||||
Lines.line(px, py, x1, y1);
|
||||
Lines.line(px, py, x3, y3);
|
||||
|
||||
Fill.circle(px, py, 1.6f + Mathf.absin(Time.time(), 0.8f, 1.5f));
|
||||
|
||||
Draw.color();
|
||||
}
|
||||
}
|
||||
130
core/src/mindustry/entities/def/BulletComp.java
Normal file
130
core/src/mindustry/entities/def/BulletComp.java
Normal file
@@ -0,0 +1,130 @@
|
||||
package mindustry.entities.def;
|
||||
|
||||
import arc.math.*;
|
||||
import arc.util.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.entities.bullet.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
@EntityDef(value = {Bulletc.class}, pooled = true)
|
||||
@Component
|
||||
abstract class BulletComp implements Timedc, Damagec, Hitboxc, Teamc, Posc, Drawc, Shielderc, Ownerc, Velc, Bulletc, Timerc, DrawLayerBulletsc{
|
||||
Object data;
|
||||
BulletType type;
|
||||
float damage;
|
||||
|
||||
@Override
|
||||
public void drawBullets(){
|
||||
type.draw(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(){
|
||||
type.init(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(){
|
||||
type.despawned(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float damageMultiplier(){
|
||||
if(owner() instanceof Unitc){
|
||||
return ((Unitc)owner()).damageMultiplier();
|
||||
}
|
||||
return 1f;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void absorb(){
|
||||
//TODO
|
||||
remove();
|
||||
}
|
||||
|
||||
@Override
|
||||
public float clipSize(){
|
||||
return type.drawSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float damage(){
|
||||
return damage * damageMultiplier();
|
||||
}
|
||||
|
||||
@Replace
|
||||
@Override
|
||||
public boolean collides(Hitboxc other){
|
||||
return type.collides && (other instanceof Teamc && ((Teamc)other).team() != team())
|
||||
&& !(other instanceof Flyingc && ((((Flyingc)other).isFlying() && !type.collidesAir) || (((Flyingc)other).isGrounded() && !type.collidesGround)));
|
||||
}
|
||||
|
||||
@MethodPriority(100)
|
||||
@Override
|
||||
public void collision(Hitboxc other, float x, float y){
|
||||
type.hit(this, x, y);
|
||||
|
||||
if(other instanceof Healthc){
|
||||
Healthc h = (Healthc)other;
|
||||
h.damage(damage);
|
||||
}
|
||||
|
||||
if(other instanceof Unitc){
|
||||
Unitc unit = (Unitc)other;
|
||||
unit.vel().add(Tmp.v3.set(other.x(), other.y()).sub(x, y).setLength(type.knockback / unit.mass()));
|
||||
unit.apply(type.status, type.statusDuration);
|
||||
}
|
||||
|
||||
//must be last.
|
||||
if(!type.pierce) remove();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(){
|
||||
type.update(this);
|
||||
|
||||
if(type.hitTiles){
|
||||
world.raycastEach(world.toTile(lastX()), world.toTile(lastY()), tileX(), tileY(), (x, y) -> {
|
||||
|
||||
Tilec tile = world.ent(x, y);
|
||||
if(tile == null) return false;
|
||||
|
||||
if(tile.collide(this) && type.collides(this, tile) && !tile.dead() && (type.collidesTeam || tile.team() != team())){
|
||||
if(tile.team() != team()){
|
||||
tile.collision(this);
|
||||
}
|
||||
|
||||
type.hitTile(this, tile);
|
||||
remove();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(){
|
||||
type.draw(this);
|
||||
//TODO refactor
|
||||
renderer.lights.add(x(), y(), 16f, Pal.powerLight, 0.3f);
|
||||
}
|
||||
|
||||
/** Sets the bullet's rotation in degrees. */
|
||||
@Override
|
||||
public void rotation(float angle){
|
||||
vel().setAngle(angle);
|
||||
}
|
||||
|
||||
/** @return the bullet's rotation. */
|
||||
@Override
|
||||
public float rotation(){
|
||||
float angle = Mathf.atan2(vel().x, vel().y) * Mathf.radiansToDegrees;
|
||||
if(angle < 0) angle += 360;
|
||||
return angle;
|
||||
}
|
||||
}
|
||||
29
core/src/mindustry/entities/def/ChildComp.java
Normal file
29
core/src/mindustry/entities/def/ChildComp.java
Normal file
@@ -0,0 +1,29 @@
|
||||
package mindustry.entities.def;
|
||||
|
||||
import arc.util.ArcAnnotate.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.gen.*;
|
||||
|
||||
@Component
|
||||
abstract class ChildComp implements Posc{
|
||||
@Import float x, y;
|
||||
|
||||
@Nullable Posc parent;
|
||||
float offsetX, offsetY;
|
||||
|
||||
@Override
|
||||
public void add(){
|
||||
if(parent != null){
|
||||
offsetX = x - parent.getX();
|
||||
offsetY = y - parent.getY();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(){
|
||||
if(parent != null){
|
||||
x = parent.getX() + offsetX;
|
||||
y = parent.getY() + offsetY;
|
||||
}
|
||||
}
|
||||
}
|
||||
8
core/src/mindustry/entities/def/DamageComp.java
Normal file
8
core/src/mindustry/entities/def/DamageComp.java
Normal file
@@ -0,0 +1,8 @@
|
||||
package mindustry.entities.def;
|
||||
|
||||
import mindustry.annotations.Annotations.*;
|
||||
|
||||
@Component
|
||||
abstract class DamageComp{
|
||||
abstract float damage();
|
||||
}
|
||||
30
core/src/mindustry/entities/def/DecalComp.java
Normal file
30
core/src/mindustry/entities/def/DecalComp.java
Normal file
@@ -0,0 +1,30 @@
|
||||
package mindustry.entities.def;
|
||||
|
||||
import arc.graphics.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.math.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.gen.*;
|
||||
|
||||
@EntityDef(value = {Decalc.class}, pooled = true)
|
||||
@Component
|
||||
abstract class DecalComp implements Drawc, Timedc, Rotc, Posc, DrawLayerFloorc{
|
||||
@Import float x, y, rotation;
|
||||
|
||||
Color color = new Color(1, 1, 1, 1);
|
||||
TextureRegion region;
|
||||
|
||||
@Override
|
||||
public void drawFloor(){
|
||||
Draw.color(color);
|
||||
Draw.alpha(1f - Mathf.curve(fin(), 0.98f));
|
||||
Draw.rect(region, x, y, rotation);
|
||||
Draw.color();
|
||||
}
|
||||
|
||||
@Override
|
||||
public float clipSize(){
|
||||
return region.getWidth()*2;
|
||||
}
|
||||
|
||||
}
|
||||
9
core/src/mindustry/entities/def/DrawComp.java
Normal file
9
core/src/mindustry/entities/def/DrawComp.java
Normal file
@@ -0,0 +1,9 @@
|
||||
package mindustry.entities.def;
|
||||
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.gen.*;
|
||||
|
||||
@Component
|
||||
abstract class DrawComp implements Posc{
|
||||
abstract float clipSize();
|
||||
}
|
||||
22
core/src/mindustry/entities/def/EffectComp.java
Normal file
22
core/src/mindustry/entities/def/EffectComp.java
Normal file
@@ -0,0 +1,22 @@
|
||||
package mindustry.entities.def;
|
||||
|
||||
import arc.graphics.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.entities.*;
|
||||
import mindustry.gen.*;
|
||||
|
||||
@Component
|
||||
abstract class EffectComp implements Posc, Drawc, Timedc, Rotc, Childc{
|
||||
Color color = new Color(Color.white);
|
||||
Effect effect;
|
||||
Object data;
|
||||
|
||||
void draw(){
|
||||
effect.render(id(), color, time(), rotation(), x(), y(), data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float clipSize(){
|
||||
return effect.size;
|
||||
}
|
||||
}
|
||||
22
core/src/mindustry/entities/def/ElevationMoveComp.java
Normal file
22
core/src/mindustry/entities/def/ElevationMoveComp.java
Normal file
@@ -0,0 +1,22 @@
|
||||
package mindustry.entities.def;
|
||||
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.gen.*;
|
||||
|
||||
import static mindustry.Vars.collisions;
|
||||
|
||||
@Component
|
||||
abstract class ElevationMoveComp implements Velc, Posc, Flyingc, Hitboxc{
|
||||
@Import float x, y;
|
||||
|
||||
@Replace
|
||||
@Override
|
||||
public void move(float cx, float cy){
|
||||
if(isFlying()){
|
||||
x += cx;
|
||||
y += cy;
|
||||
}else{
|
||||
collisions.move(this, cx, cy);
|
||||
}
|
||||
}
|
||||
}
|
||||
66
core/src/mindustry/entities/def/EntityComp.java
Normal file
66
core/src/mindustry/entities/def/EntityComp.java
Normal file
@@ -0,0 +1,66 @@
|
||||
package mindustry.entities.def;
|
||||
|
||||
import arc.func.*;
|
||||
import arc.util.io.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.entities.*;
|
||||
import mindustry.gen.*;
|
||||
|
||||
import static mindustry.Vars.player;
|
||||
|
||||
@Component
|
||||
@BaseComponent
|
||||
abstract class EntityComp{
|
||||
private transient boolean added;
|
||||
transient int id = EntityGroup.nextId();
|
||||
|
||||
boolean isAdded(){
|
||||
return added;
|
||||
}
|
||||
|
||||
void update(){}
|
||||
|
||||
void remove(){
|
||||
added = false;
|
||||
}
|
||||
|
||||
void add(){
|
||||
added = true;
|
||||
}
|
||||
|
||||
boolean isLocal(){
|
||||
return ((Object)this) == player || ((Object)this) instanceof Unitc && ((Unitc)((Object)this)).controller() == player;
|
||||
}
|
||||
|
||||
boolean isNull(){
|
||||
return false;
|
||||
}
|
||||
|
||||
<T> T as(Class<T> type){
|
||||
return (T)this;
|
||||
}
|
||||
|
||||
<T> T with(Cons<T> cons){
|
||||
cons.get((T)this);
|
||||
return (T)this;
|
||||
}
|
||||
|
||||
@InternalImpl
|
||||
abstract int classId();
|
||||
|
||||
@InternalImpl
|
||||
abstract boolean serialize();
|
||||
|
||||
@MethodPriority(1)
|
||||
void read(Reads read){
|
||||
afterRead();
|
||||
}
|
||||
|
||||
void write(Writes write){
|
||||
|
||||
}
|
||||
|
||||
void afterRead(){
|
||||
|
||||
}
|
||||
}
|
||||
102
core/src/mindustry/entities/def/FireComp.java
Normal file
102
core/src/mindustry/entities/def/FireComp.java
Normal file
@@ -0,0 +1,102 @@
|
||||
package mindustry.entities.def;
|
||||
|
||||
import arc.math.*;
|
||||
import arc.math.geom.*;
|
||||
import arc.util.*;
|
||||
import mindustry.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.entities.*;
|
||||
import mindustry.game.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.world.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
@EntityDef(value = {Firec.class}, pooled = true)
|
||||
@Component
|
||||
abstract class FireComp implements Timedc, Posc, Firec{
|
||||
private static final float spreadChance = 0.05f, fireballChance = 0.07f;
|
||||
|
||||
@Import float time, lifetime, x, y;
|
||||
|
||||
Tile tile;
|
||||
private Block block;
|
||||
private float baseFlammability = -1, puddleFlammability;
|
||||
|
||||
@Override
|
||||
public void update(){
|
||||
if(Mathf.chance(0.1 * Time.delta())){
|
||||
Fx.fire.at(x + Mathf.range(4f), y + Mathf.range(4f));
|
||||
}
|
||||
|
||||
if(Mathf.chance(0.05 * Time.delta())){
|
||||
Fx.fireSmoke.at(x + Mathf.range(4f), y + Mathf.range(4f));
|
||||
}
|
||||
|
||||
if(Mathf.chance(0.001 * Time.delta())){
|
||||
Sounds.fire.at(this);
|
||||
}
|
||||
|
||||
time = Mathf.clamp(time + Time.delta(), 0, lifetime());
|
||||
|
||||
if(Vars.net.client()){
|
||||
return;
|
||||
}
|
||||
|
||||
if(time >= lifetime() || tile == null){
|
||||
remove();
|
||||
return;
|
||||
}
|
||||
|
||||
Tilec entity = tile.entity;
|
||||
boolean damage = entity != null;
|
||||
|
||||
float flammability = baseFlammability + puddleFlammability;
|
||||
|
||||
if(!damage && flammability <= 0){
|
||||
time += Time.delta() * 8;
|
||||
}
|
||||
|
||||
if(baseFlammability < 0 || block != tile.block()){
|
||||
baseFlammability = tile.entity == null ? 0 : tile.entity.getFlammability();
|
||||
block = tile.block();
|
||||
}
|
||||
|
||||
if(damage){
|
||||
lifetime += Mathf.clamp(flammability / 8f, 0f, 0.6f) * Time.delta();
|
||||
}
|
||||
|
||||
if(flammability > 1f && Mathf.chance(spreadChance * Time.delta() * Mathf.clamp(flammability / 5f, 0.3f, 2f))){
|
||||
Point2 p = Geometry.d4[Mathf.random(3)];
|
||||
Tile other = world.tile(tile.x + p.x, tile.y + p.y);
|
||||
Fires.create(other);
|
||||
|
||||
if(Mathf.chance(fireballChance * Time.delta() * Mathf.clamp(flammability / 10f))){
|
||||
Bullets.fireball.createNet(Team.derelict, x, y, Mathf.random(360f), -1f, 1, 1);
|
||||
}
|
||||
}
|
||||
|
||||
if(Mathf.chance(0.1 * Time.delta())){
|
||||
Puddlec p = Puddles.get(tile);
|
||||
puddleFlammability = p != null ? p.getFlammability() / 3f : 0;
|
||||
|
||||
if(damage){
|
||||
entity.damage(0.4f);
|
||||
}
|
||||
Damage.damageUnits(null, tile.worldx(), tile.worldy(), tilesize, 3f,
|
||||
unit -> !unit.isFlying() && !unit.isImmune(StatusEffects.burning),
|
||||
unit -> unit.apply(StatusEffects.burning, 60 * 5));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(){
|
||||
Fires.remove(tile);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterRead(){
|
||||
Fires.register(this);
|
||||
}
|
||||
}
|
||||
79
core/src/mindustry/entities/def/FlyingComp.java
Normal file
79
core/src/mindustry/entities/def/FlyingComp.java
Normal file
@@ -0,0 +1,79 @@
|
||||
package mindustry.entities.def;
|
||||
|
||||
import arc.math.*;
|
||||
import arc.math.geom.*;
|
||||
import arc.util.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.world.blocks.environment.*;
|
||||
|
||||
import static mindustry.Vars.net;
|
||||
|
||||
@Component
|
||||
abstract class FlyingComp implements Posc, Velc, Healthc, Hitboxc{
|
||||
private static final Vec2 tmp1 = new Vec2(), tmp2 = new Vec2();
|
||||
|
||||
@Import float x, y, drag;
|
||||
@Import Vec2 vel;
|
||||
|
||||
float elevation;
|
||||
float drownTime;
|
||||
transient float splashTimer;
|
||||
|
||||
boolean isGrounded(){
|
||||
return elevation < 0.001f;
|
||||
}
|
||||
|
||||
boolean isFlying(){
|
||||
return elevation >= 0.001f;
|
||||
}
|
||||
|
||||
boolean canDrown(){
|
||||
return isGrounded();
|
||||
}
|
||||
|
||||
void wobble(){
|
||||
x += Mathf.sin(Time.time() + id() * 99, 25f, 0.05f) * Time.delta() * elevation;
|
||||
y += Mathf.cos(Time.time() + id() * 99, 25f, 0.05f) * Time.delta() * elevation;
|
||||
}
|
||||
|
||||
void moveAt(Vec2 vector, float acceleration){
|
||||
Vec2 t = tmp1.set(vector).scl(floorSpeedMultiplier()); //target vector
|
||||
tmp2.set(t).sub(vel).limit(acceleration * vector.len()); //delta vector
|
||||
vel.add(tmp2);
|
||||
}
|
||||
|
||||
float floorSpeedMultiplier(){
|
||||
Floor on = isFlying() ? Blocks.air.asFloor() : floorOn();
|
||||
return on.speedMultiplier;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(){
|
||||
Floor floor = floorOn();
|
||||
|
||||
if(isGrounded() && floor.isLiquid){
|
||||
if((splashTimer += Mathf.dst(deltaX(), deltaY())) >= 7f){
|
||||
floor.walkEffect.at(x, y, 0, floor.mapColor);
|
||||
splashTimer = 0f;
|
||||
}
|
||||
}
|
||||
|
||||
if(canDrown() && floor.isLiquid && floor.drownTime > 0){
|
||||
drownTime += Time.delta() * 1f / floor.drownTime;
|
||||
drownTime = Mathf.clamp(drownTime);
|
||||
if(Mathf.chance(Time.delta() * 0.05f)){
|
||||
floor.drownUpdateEffect.at(x, y, 0f, floor.mapColor);
|
||||
}
|
||||
|
||||
//TODO is the netClient check necessary?
|
||||
if(drownTime >= 0.999f && !net.client()){
|
||||
kill();
|
||||
//TODO drown event!
|
||||
}
|
||||
}else{
|
||||
drownTime = Mathf.lerpDelta(drownTime, 0f, 0.03f);
|
||||
}
|
||||
}
|
||||
}
|
||||
14
core/src/mindustry/entities/def/GroundEffectComp.java
Normal file
14
core/src/mindustry/entities/def/GroundEffectComp.java
Normal file
@@ -0,0 +1,14 @@
|
||||
package mindustry.entities.def;
|
||||
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.gen.*;
|
||||
|
||||
@EntityDef(value = {GroundEffectc.class, Childc.class}, pooled = true)
|
||||
@Component
|
||||
abstract class GroundEffectComp implements Effectc, DrawLayerFloorOverc{
|
||||
|
||||
@Override
|
||||
public void drawFloorOver(){
|
||||
draw();
|
||||
}
|
||||
}
|
||||
82
core/src/mindustry/entities/def/HealthComp.java
Normal file
82
core/src/mindustry/entities/def/HealthComp.java
Normal file
@@ -0,0 +1,82 @@
|
||||
package mindustry.entities.def;
|
||||
|
||||
import arc.math.*;
|
||||
import arc.util.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.gen.*;
|
||||
|
||||
@Component
|
||||
abstract class HealthComp implements Entityc{
|
||||
static final float hitDuration = 9f;
|
||||
|
||||
float health;
|
||||
transient float hitTime;
|
||||
transient float maxHealth = 1f;
|
||||
transient boolean dead;
|
||||
|
||||
boolean isValid(){
|
||||
return !dead && isAdded();
|
||||
}
|
||||
|
||||
float healthf(){
|
||||
return health / maxHealth;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(){
|
||||
hitTime -= Time.delta() / hitDuration;
|
||||
}
|
||||
|
||||
void killed(){
|
||||
//implement by other components
|
||||
}
|
||||
|
||||
void kill(){
|
||||
if(dead) return;
|
||||
|
||||
health = 0;
|
||||
dead = true;
|
||||
killed();
|
||||
remove();
|
||||
}
|
||||
|
||||
void heal(){
|
||||
dead = false;
|
||||
health = maxHealth;
|
||||
}
|
||||
|
||||
boolean damaged(){
|
||||
return health < maxHealth - 0.001f;
|
||||
}
|
||||
|
||||
void damage(float amount){
|
||||
health -= amount;
|
||||
hitTime = 1f;
|
||||
if(health <= 0 && !dead){
|
||||
kill();
|
||||
}
|
||||
}
|
||||
|
||||
void damage(float amount, boolean withEffect){
|
||||
float pre = hitTime;
|
||||
|
||||
damage(amount);
|
||||
|
||||
if(!withEffect){
|
||||
hitTime = pre;
|
||||
}
|
||||
}
|
||||
|
||||
void damageContinuous(float amount){
|
||||
damage(amount * Time.delta(), hitTime <= -20 + hitDuration);
|
||||
}
|
||||
|
||||
void clampHealth(){
|
||||
health = Mathf.clamp(health, 0, maxHealth);
|
||||
}
|
||||
|
||||
void heal(float amount){
|
||||
health += amount;
|
||||
clampHealth();
|
||||
}
|
||||
}
|
||||
59
core/src/mindustry/entities/def/HitboxComp.java
Normal file
59
core/src/mindustry/entities/def/HitboxComp.java
Normal file
@@ -0,0 +1,59 @@
|
||||
package mindustry.entities.def;
|
||||
|
||||
import arc.math.geom.*;
|
||||
import arc.math.geom.QuadTree.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.gen.*;
|
||||
|
||||
@Component
|
||||
abstract class HitboxComp implements Posc, QuadTreeObject{
|
||||
@Import float x, y;
|
||||
|
||||
transient float lastX, lastY, hitSize;
|
||||
|
||||
@Override
|
||||
public void update(){
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(){
|
||||
updateLastPosition();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterRead(){
|
||||
updateLastPosition();
|
||||
}
|
||||
|
||||
void updateLastPosition(){
|
||||
lastX = x;
|
||||
lastY = y;
|
||||
}
|
||||
|
||||
void collision(Hitboxc other, float x, float y){
|
||||
|
||||
}
|
||||
|
||||
float deltaX(){
|
||||
return x - lastX;
|
||||
}
|
||||
|
||||
float deltaY(){
|
||||
return y - lastY;
|
||||
}
|
||||
|
||||
boolean collides(Hitboxc other){
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hitbox(Rect rect){
|
||||
rect.setCentered(x, y, hitSize, hitSize);
|
||||
}
|
||||
|
||||
public void hitboxTile(Rect rect){
|
||||
float scale = 0.66f;
|
||||
rect.setCentered(x, y, hitSize * scale, hitSize * scale);
|
||||
}
|
||||
}
|
||||
50
core/src/mindustry/entities/def/ItemsComp.java
Normal file
50
core/src/mindustry/entities/def/ItemsComp.java
Normal file
@@ -0,0 +1,50 @@
|
||||
package mindustry.entities.def;
|
||||
|
||||
import arc.math.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.type.*;
|
||||
|
||||
@Component
|
||||
abstract class ItemsComp implements Posc{
|
||||
@ReadOnly ItemStack stack = new ItemStack();
|
||||
transient float itemTime;
|
||||
|
||||
abstract int itemCapacity();
|
||||
|
||||
@Override
|
||||
public void update(){
|
||||
stack.amount = Mathf.clamp(stack.amount, 0, itemCapacity());
|
||||
itemTime = Mathf.lerpDelta(itemTime, Mathf.num(hasItem()), 0.05f);
|
||||
}
|
||||
|
||||
Item item(){
|
||||
return stack.item;
|
||||
}
|
||||
|
||||
void clearItem(){
|
||||
stack.amount = 0;
|
||||
}
|
||||
|
||||
boolean acceptsItem(Item item){
|
||||
return !hasItem() || item == stack.item && stack.amount + 1 <= itemCapacity();
|
||||
}
|
||||
|
||||
boolean hasItem(){
|
||||
return stack.amount > 0;
|
||||
}
|
||||
|
||||
void addItem(Item item){
|
||||
addItem(item, 1);
|
||||
}
|
||||
|
||||
void addItem(Item item, int amount){
|
||||
stack.amount = stack.item == item ? stack.amount + amount : amount;
|
||||
stack.item = item;
|
||||
stack.amount = Mathf.clamp(stack.amount, 0, itemCapacity());
|
||||
}
|
||||
|
||||
int maxAccepted(Item item){
|
||||
return stack.item != item && stack.amount > 0 ? 0 : itemCapacity() - stack.amount;
|
||||
}
|
||||
}
|
||||
26
core/src/mindustry/entities/def/LegsComp.java
Normal file
26
core/src/mindustry/entities/def/LegsComp.java
Normal file
@@ -0,0 +1,26 @@
|
||||
package mindustry.entities.def;
|
||||
|
||||
import arc.math.*;
|
||||
import arc.util.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.gen.*;
|
||||
|
||||
@Component
|
||||
abstract class LegsComp implements Posc, Flyingc, Hitboxc, DrawLayerGroundUnderc, Unitc, Legsc, ElevationMovec{
|
||||
@Import float x, y;
|
||||
|
||||
float baseRotation;
|
||||
transient float walkTime;
|
||||
|
||||
@Override
|
||||
public void update(){
|
||||
float len = vel().len();
|
||||
baseRotation = Angles.moveToward(baseRotation, vel().angle(), type().baseRotateSpeed * Mathf.clamp(len / type().speed));
|
||||
walkTime += Time.delta()*len/1f;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawGroundUnder(){
|
||||
type().drawLegs(this);
|
||||
}
|
||||
}
|
||||
13
core/src/mindustry/entities/def/MassComp.java
Normal file
13
core/src/mindustry/entities/def/MassComp.java
Normal file
@@ -0,0 +1,13 @@
|
||||
package mindustry.entities.def;
|
||||
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.gen.*;
|
||||
|
||||
@Component
|
||||
abstract class MassComp implements Velc{
|
||||
transient float mass = 1f;
|
||||
|
||||
public void impulse(float x, float y){
|
||||
vel().add(x / mass, y / mass);
|
||||
}
|
||||
}
|
||||
107
core/src/mindustry/entities/def/MinerComp.java
Normal file
107
core/src/mindustry/entities/def/MinerComp.java
Normal file
@@ -0,0 +1,107 @@
|
||||
package mindustry.entities.def;
|
||||
|
||||
import arc.*;
|
||||
import arc.graphics.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.math.*;
|
||||
import arc.util.*;
|
||||
import arc.util.ArcAnnotate.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.input.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.world.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
@Component
|
||||
abstract class MinerComp implements Itemsc, Posc, Teamc, Rotc, DrawLayerGroundc{
|
||||
@Import float x, y, rotation;
|
||||
|
||||
transient float mineTimer;
|
||||
@Nullable Tile mineTile;
|
||||
|
||||
abstract boolean canMine(Item item);
|
||||
|
||||
abstract float miningSpeed();
|
||||
|
||||
abstract boolean offloadImmediately();
|
||||
|
||||
boolean mining(){
|
||||
return mineTile != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(){
|
||||
Tilec core = closestCore();
|
||||
|
||||
if(core != null && mineTile != null && mineTile.drop() != null && !acceptsItem(mineTile.drop()) && dst(core) < mineTransferRange){
|
||||
int accepted = core.acceptStack(item(), stack().amount, this);
|
||||
if(accepted > 0){
|
||||
Call.transferItemTo(item(), accepted,
|
||||
mineTile.worldx() + Mathf.range(tilesize / 2f),
|
||||
mineTile.worldy() + Mathf.range(tilesize / 2f), core.tile());
|
||||
clearItem();
|
||||
}
|
||||
}
|
||||
|
||||
if(mineTile == null || core == null || mineTile.block() != Blocks.air || dst(mineTile.worldx(), mineTile.worldy()) > miningRange
|
||||
|| (((Object)this) instanceof Builderc && ((Builderc)(Object)this).isBuilding())
|
||||
|| mineTile.drop() == null || !acceptsItem(mineTile.drop()) || !canMine(mineTile.drop())){
|
||||
mineTile = null;
|
||||
mineTimer = 0f;
|
||||
}else{
|
||||
Item item = mineTile.drop();
|
||||
rotation(Mathf.slerpDelta(rotation(), angleTo(mineTile.worldx(), mineTile.worldy()), 0.4f));
|
||||
mineTimer += Time.delta()*miningSpeed();
|
||||
|
||||
if(mineTimer >= 50f + item.hardness*10f){
|
||||
mineTimer = 0;
|
||||
|
||||
if(dst(core) < mineTransferRange && core.acceptStack(item, 1, this) == 1 && offloadImmediately()){
|
||||
Call.transferItemTo(item, 1,
|
||||
mineTile.worldx() + Mathf.range(tilesize / 2f),
|
||||
mineTile.worldy() + Mathf.range(tilesize / 2f), core.tile());
|
||||
}else if(acceptsItem(item)){
|
||||
//this is clientside, since items are synced anyway
|
||||
InputHandler.transferItemToUnit(item,
|
||||
mineTile.worldx() + Mathf.range(tilesize / 2f),
|
||||
mineTile.worldy() + Mathf.range(tilesize / 2f),
|
||||
this);
|
||||
}
|
||||
}
|
||||
|
||||
if(Mathf.chance(0.06 * Time.delta())){
|
||||
Fx.pulverizeSmall.at(mineTile.worldx() + Mathf.range(tilesize / 2f), mineTile.worldy() + Mathf.range(tilesize / 2f), 0f, item.color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawGround(){
|
||||
if(!mining()) return;
|
||||
float focusLen = 4f + Mathf.absin(Time.time(), 1.1f, 0.5f);
|
||||
float swingScl = 12f, swingMag = tilesize / 8f;
|
||||
float flashScl = 0.3f;
|
||||
|
||||
float px = x + Angles.trnsx(rotation, focusLen);
|
||||
float py = y + Angles.trnsy(rotation, focusLen);
|
||||
|
||||
float ex = mineTile.worldx() + Mathf.sin(Time.time() + 48, swingScl, swingMag);
|
||||
float ey = mineTile.worldy() + Mathf.sin(Time.time() + 48, swingScl + 2f, swingMag);
|
||||
|
||||
Draw.color(Color.lightGray, Color.white, 1f - flashScl + Mathf.absin(Time.time(), 0.5f, flashScl));
|
||||
|
||||
Drawf.laser(Core.atlas.find("minelaser"), Core.atlas.find("minelaser-end"), px, py, ex, ey, 0.75f);
|
||||
|
||||
//TODO hack?
|
||||
if(isLocal()){
|
||||
Lines.stroke(1f, Pal.accent);
|
||||
Lines.poly(mineTile.worldx(), mineTile.worldy(), 4, tilesize / 2f * Mathf.sqrt2, Time.time());
|
||||
}
|
||||
|
||||
Draw.color();
|
||||
}
|
||||
}
|
||||
9
core/src/mindustry/entities/def/OwnerComp.java
Normal file
9
core/src/mindustry/entities/def/OwnerComp.java
Normal file
@@ -0,0 +1,9 @@
|
||||
package mindustry.entities.def;
|
||||
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.gen.*;
|
||||
|
||||
@Component
|
||||
class OwnerComp{
|
||||
Entityc owner;
|
||||
}
|
||||
211
core/src/mindustry/entities/def/PlayerComp.java
Normal file
211
core/src/mindustry/entities/def/PlayerComp.java
Normal file
@@ -0,0 +1,211 @@
|
||||
package mindustry.entities.def;
|
||||
|
||||
import arc.*;
|
||||
import arc.graphics.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.math.*;
|
||||
import arc.scene.ui.layout.*;
|
||||
import arc.util.*;
|
||||
import arc.util.ArcAnnotate.*;
|
||||
import arc.util.pooling.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.core.*;
|
||||
import mindustry.entities.units.*;
|
||||
import mindustry.game.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.net.Administration.*;
|
||||
import mindustry.net.*;
|
||||
import mindustry.net.Packets.*;
|
||||
import mindustry.ui.*;
|
||||
import mindustry.world.blocks.storage.CoreBlock.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
@EntityDef(value = {Playerc.class}, serialize = false)
|
||||
@Component
|
||||
abstract class PlayerComp implements UnitController, Entityc, Syncc, Timerc{
|
||||
@NonNull @ReadOnly Unitc unit = Nulls.unit;
|
||||
|
||||
@ReadOnly Team team = Team.sharded;
|
||||
String name = "noname";
|
||||
@Nullable NetConnection con;
|
||||
boolean admin, typing;
|
||||
Color color = new Color();
|
||||
float mouseX, mouseY;
|
||||
|
||||
String lastText = "";
|
||||
float textFadeTime;
|
||||
|
||||
public boolean isBuilder(){
|
||||
return unit instanceof Builderc;
|
||||
}
|
||||
|
||||
public boolean isMiner(){
|
||||
return unit instanceof Minerc;
|
||||
}
|
||||
|
||||
public @Nullable CoreEntity closestCore(){
|
||||
return state.teams.closestCore(x(), y(), team);
|
||||
}
|
||||
|
||||
public void reset(){
|
||||
team = state.rules.defaultTeam;
|
||||
admin = typing = false;
|
||||
textFadeTime = 0f;
|
||||
if(!dead()){
|
||||
unit.controller(unit.type().createController());
|
||||
unit = Nulls.unit;
|
||||
}
|
||||
}
|
||||
|
||||
public void update(){
|
||||
if(unit.dead()){
|
||||
clearUnit();
|
||||
}
|
||||
|
||||
CoreEntity core = closestCore();
|
||||
|
||||
if(!dead()){
|
||||
x(unit.x());
|
||||
y(unit.y());
|
||||
unit.team(team);
|
||||
}else if(core != null){
|
||||
core.requestSpawn((Playerc)this);
|
||||
}
|
||||
|
||||
textFadeTime -= Time.delta() / (60 * 5);
|
||||
}
|
||||
|
||||
public void team(Team team){
|
||||
this.team = team;
|
||||
unit.team(team);
|
||||
}
|
||||
|
||||
public void clearUnit(){
|
||||
unit(Nulls.unit);
|
||||
}
|
||||
|
||||
public Unitc unit(){
|
||||
return unit;
|
||||
}
|
||||
|
||||
public Minerc miner(){
|
||||
return !(unit instanceof Minerc) ? Nulls.miner : (Minerc)unit;
|
||||
}
|
||||
|
||||
public Builderc builder(){
|
||||
return !(unit instanceof Builderc) ? Nulls.builder : (Builderc)unit;
|
||||
}
|
||||
|
||||
public void unit(Unitc unit){
|
||||
if(unit == null) throw new IllegalArgumentException("Unit cannot be null. Use clearUnit() instead.");
|
||||
if(this.unit != Nulls.unit){
|
||||
//un-control the old unit
|
||||
this.unit.controller(this.unit.type().createController());
|
||||
}
|
||||
this.unit = unit;
|
||||
if(unit != Nulls.unit){
|
||||
unit.team(team);
|
||||
unit.controller(this);
|
||||
}
|
||||
}
|
||||
|
||||
boolean dead(){
|
||||
return unit.isNull();
|
||||
}
|
||||
|
||||
String uuid(){
|
||||
return con == null ? "[LOCAL]" : con.uuid;
|
||||
}
|
||||
|
||||
String usid(){
|
||||
return con == null ? "[LOCAL]" : con.usid;
|
||||
}
|
||||
|
||||
void kick(KickReason reason){
|
||||
con.kick(reason);
|
||||
}
|
||||
|
||||
void kick(String reason){
|
||||
con.kick(reason);
|
||||
}
|
||||
|
||||
void drawName(){
|
||||
BitmapFont font = Fonts.def;
|
||||
GlyphLayout layout = Pools.obtain(GlyphLayout.class, GlyphLayout::new);
|
||||
final float nameHeight = 11;
|
||||
final float textHeight = 15;
|
||||
|
||||
boolean ints = font.usesIntegerPositions();
|
||||
font.setUseIntegerPositions(false);
|
||||
font.getData().setScale(0.25f / Scl.scl(1f));
|
||||
layout.setText(font, name);
|
||||
|
||||
if(!isLocal()){
|
||||
Draw.color(0f, 0f, 0f, 0.3f);
|
||||
Fill.rect(unit.x(), unit.y() + nameHeight - layout.height / 2, layout.width + 2, layout.height + 3);
|
||||
Draw.color();
|
||||
font.setColor(color);
|
||||
font.draw(name, unit.x(), unit.y() + nameHeight, 0, Align.center, false);
|
||||
|
||||
if(admin){
|
||||
float s = 3f;
|
||||
Draw.color(color.r * 0.5f, color.g * 0.5f, color.b * 0.5f, 1f);
|
||||
Draw.rect(Icon.adminSmall.getRegion(), unit.x() + layout.width / 2f + 2 + 1, unit.y() + nameHeight - 1.5f, s, s);
|
||||
Draw.color(color);
|
||||
Draw.rect(Icon.adminSmall.getRegion(), unit.x() + layout.width / 2f + 2 + 1, unit.y() + nameHeight - 1f, s, s);
|
||||
}
|
||||
}
|
||||
|
||||
if(Core.settings.getBool("playerchat") && ((textFadeTime > 0 && lastText != null) || typing)){
|
||||
String text = textFadeTime <= 0 || lastText == null ? "[LIGHT_GRAY]" + Strings.animated(Time.time(), 4, 15f, ".") : lastText;
|
||||
float width = 100f;
|
||||
float visualFadeTime = 1f - Mathf.curve(1f - textFadeTime, 0.9f);
|
||||
font.setColor(1f, 1f, 1f, textFadeTime <= 0 || lastText == null ? 1f : visualFadeTime);
|
||||
|
||||
layout.setText(font, text, Color.white, width, Align.bottom, true);
|
||||
|
||||
Draw.color(0f, 0f, 0f, 0.3f * (textFadeTime <= 0 || lastText == null ? 1f : visualFadeTime));
|
||||
Fill.rect(unit.x(), unit.y() + textHeight + layout.height - layout.height/2f, layout.width + 2, layout.height + 3);
|
||||
font.draw(text, unit.x() - width/2f, unit.y() + textHeight + layout.height, width, Align.center, true);
|
||||
}
|
||||
|
||||
Draw.reset();
|
||||
Pools.free(layout);
|
||||
font.getData().setScale(1f);
|
||||
font.setColor(Color.white);
|
||||
font.setUseIntegerPositions(ints);
|
||||
}
|
||||
|
||||
void sendMessage(String text){
|
||||
if(isLocal()){
|
||||
if(ui != null){
|
||||
ui.chatfrag.addMessage(text, null);
|
||||
}
|
||||
}else{
|
||||
Call.sendMessage(con, text, null, null);
|
||||
}
|
||||
}
|
||||
|
||||
void sendMessage(String text, Playerc from){
|
||||
sendMessage(text, from, NetClient.colorizeName(from.id(), from.name()));
|
||||
}
|
||||
|
||||
void sendMessage(String text, Playerc from, String fromName){
|
||||
if(isLocal()){
|
||||
if(ui != null){
|
||||
ui.chatfrag.addMessage(text, fromName);
|
||||
}
|
||||
}else{
|
||||
Call.sendMessage(con, text, fromName, from);
|
||||
}
|
||||
}
|
||||
|
||||
PlayerInfo getInfo(){
|
||||
if(isLocal()){
|
||||
throw new IllegalArgumentException("Local players cannot be traced and do not have info.");
|
||||
}else{
|
||||
return netServer.admins.getInfo(uuid());
|
||||
}
|
||||
}
|
||||
}
|
||||
58
core/src/mindustry/entities/def/PosComp.java
Normal file
58
core/src/mindustry/entities/def/PosComp.java
Normal file
@@ -0,0 +1,58 @@
|
||||
package mindustry.entities.def;
|
||||
|
||||
import arc.math.geom.*;
|
||||
import arc.util.ArcAnnotate.*;
|
||||
import mindustry.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.blocks.environment.*;
|
||||
|
||||
import static mindustry.Vars.world;
|
||||
|
||||
@Component
|
||||
abstract class PosComp implements Position{
|
||||
float x, y;
|
||||
|
||||
void set(float x, float y){
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
void set(Position pos){
|
||||
set(pos.getX(), pos.getY());
|
||||
}
|
||||
|
||||
void trns(float x, float y){
|
||||
set(this.x + x, this.y + y);
|
||||
}
|
||||
|
||||
int tileX(){
|
||||
return Vars.world.toTile(x);
|
||||
}
|
||||
|
||||
int tileY(){
|
||||
return Vars.world.toTile(y);
|
||||
}
|
||||
|
||||
/** Returns air if this unit is on a non-air top block. */
|
||||
public Floor floorOn(){
|
||||
Tile tile = tileOn();
|
||||
return tile == null || tile.block() != Blocks.air ? (Floor)Blocks.air : tile.floor();
|
||||
}
|
||||
|
||||
public @Nullable
|
||||
Tile tileOn(){
|
||||
return world.tileWorld(x, y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getX(){
|
||||
return x;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getY(){
|
||||
return y;
|
||||
}
|
||||
}
|
||||
129
core/src/mindustry/entities/def/PuddleComp.java
Normal file
129
core/src/mindustry/entities/def/PuddleComp.java
Normal file
@@ -0,0 +1,129 @@
|
||||
package mindustry.entities.def;
|
||||
|
||||
import arc.graphics.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.math.*;
|
||||
import arc.math.geom.*;
|
||||
import arc.util.*;
|
||||
import mindustry.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.entities.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.world.*;
|
||||
|
||||
import static mindustry.Vars.world;
|
||||
import static mindustry.entities.Puddles.maxLiquid;
|
||||
|
||||
@EntityDef(value = {Puddlec.class}, pooled = true)
|
||||
@Component
|
||||
abstract class PuddleComp implements Posc, DrawLayerFloorOverc, Puddlec{
|
||||
private static final int maxGeneration = 2;
|
||||
private static final Color tmp = new Color();
|
||||
private static final Rect rect = new Rect();
|
||||
private static final Rect rect2 = new Rect();
|
||||
private static int seeds;
|
||||
|
||||
@Import float x, y;
|
||||
|
||||
float amount, lastRipple, accepting, updateTime;
|
||||
int generation;
|
||||
Tile tile;
|
||||
Liquid liquid;
|
||||
|
||||
public float getFlammability(){
|
||||
return liquid.flammability * amount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(){
|
||||
//update code
|
||||
float addSpeed = accepting > 0 ? 3f : 0f;
|
||||
|
||||
amount -= Time.delta() * (1f - liquid.viscosity) / (5f + addSpeed);
|
||||
|
||||
amount += accepting;
|
||||
accepting = 0f;
|
||||
|
||||
if(amount >= maxLiquid / 1.5f && generation < maxGeneration){
|
||||
float deposited = Math.min((amount - maxLiquid / 1.5f) / 4f, 0.3f) * Time.delta();
|
||||
for(Point2 point : Geometry.d4){
|
||||
Tile other = world.tile(tile.x + point.x, tile.y + point.y);
|
||||
if(other != null && other.block() == Blocks.air){
|
||||
Puddles.deposit(other, tile, liquid, deposited, generation + 1);
|
||||
amount -= deposited / 2f; //tweak to speed up/slow down Puddlec propagation
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
amount = Mathf.clamp(amount, 0, maxLiquid);
|
||||
|
||||
if(amount <= 0f){
|
||||
remove();
|
||||
}
|
||||
|
||||
//effects-only code
|
||||
if(amount >= maxLiquid / 2f && updateTime <= 0f){
|
||||
Units.nearby(rect.setSize(Mathf.clamp(amount / (maxLiquid / 1.5f)) * 10f).setCenter(x, y), unit -> {
|
||||
if(unit.isGrounded()){
|
||||
unit.hitbox(rect2);
|
||||
if(rect.overlaps(rect2)){
|
||||
unit.apply(liquid.effect, 60 * 2);
|
||||
|
||||
if(unit.vel().len() > 0.1){
|
||||
Fx.ripple.at(unit.x(), unit.y(), liquid.color);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if(liquid.temperature > 0.7f && (tile.entity != null) && Mathf.chance(0.3 * Time.delta())){
|
||||
Fires.create(tile);
|
||||
}
|
||||
|
||||
updateTime = 20f;
|
||||
}
|
||||
|
||||
updateTime -= Time.delta();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawFloorOver(){
|
||||
seeds = id();
|
||||
boolean onLiquid = tile.floor().isLiquid;
|
||||
float f = Mathf.clamp(amount / (maxLiquid / 1.5f));
|
||||
float smag = onLiquid ? 0.8f : 0f;
|
||||
float sscl = 20f;
|
||||
|
||||
Draw.color(tmp.set(liquid.color).shiftValue(-0.05f));
|
||||
Fill.circle(x + Mathf.sin(Time.time() + seeds * 532, sscl, smag), y + Mathf.sin(Time.time() + seeds * 53, sscl, smag), f * 8f);
|
||||
Angles.randLenVectors(id(), 3, f * 6f, (ex, ey) -> {
|
||||
Fill.circle(x + ex + Mathf.sin(Time.time() + seeds * 532, sscl, smag),
|
||||
y + ey + Mathf.sin(Time.time() + seeds * 53, sscl, smag), f * 5f);
|
||||
seeds++;
|
||||
});
|
||||
Draw.color();
|
||||
|
||||
if(liquid.lightColor.a > 0.001f && f > 0){
|
||||
Color color = liquid.lightColor;
|
||||
float opacity = color.a * f;
|
||||
Vars.renderer.lights.add(tile.drawx(), tile.drawy(), 30f * f, color, opacity * 0.8f);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public float clipSize(){
|
||||
return 20;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(){
|
||||
Puddles.remove(tile);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterRead(){
|
||||
Puddles.register(this);
|
||||
}
|
||||
}
|
||||
17
core/src/mindustry/entities/def/RotComp.java
Normal file
17
core/src/mindustry/entities/def/RotComp.java
Normal file
@@ -0,0 +1,17 @@
|
||||
package mindustry.entities.def;
|
||||
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.gen.*;
|
||||
|
||||
@Component
|
||||
abstract class RotComp implements Entityc{
|
||||
float rotation;
|
||||
|
||||
void interpolate(){
|
||||
Syncc sync = as(Syncc.class);
|
||||
|
||||
if(sync.interpolator().values.length > 0){
|
||||
rotation = sync.interpolator().values[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
12
core/src/mindustry/entities/def/ShielderComp.java
Normal file
12
core/src/mindustry/entities/def/ShielderComp.java
Normal file
@@ -0,0 +1,12 @@
|
||||
package mindustry.entities.def;
|
||||
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.gen.*;
|
||||
|
||||
@Component
|
||||
abstract class ShielderComp implements Damagec, Teamc, Posc{
|
||||
|
||||
void absorb(){
|
||||
|
||||
}
|
||||
}
|
||||
14
core/src/mindustry/entities/def/StandardEffectComp.java
Normal file
14
core/src/mindustry/entities/def/StandardEffectComp.java
Normal file
@@ -0,0 +1,14 @@
|
||||
package mindustry.entities.def;
|
||||
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.gen.*;
|
||||
|
||||
@EntityDef(value = {StandardEffectc.class, Childc.class}, pooled = true)
|
||||
@Component
|
||||
abstract class StandardEffectComp implements Effectc, DrawLayerEffectsc{
|
||||
|
||||
@Override
|
||||
public void drawEffects(){
|
||||
draw();
|
||||
}
|
||||
}
|
||||
@@ -1,49 +1,52 @@
|
||||
package mindustry.entities.units;
|
||||
package mindustry.entities.def;
|
||||
|
||||
import arc.struct.Bits;
|
||||
import arc.struct.*;
|
||||
import arc.graphics.*;
|
||||
import arc.math.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import arc.util.pooling.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.ctype.ContentType;
|
||||
import mindustry.entities.traits.*;
|
||||
import mindustry.entities.type.*;
|
||||
import mindustry.ctype.*;
|
||||
import mindustry.entities.units.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.world.blocks.environment.*;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
import static mindustry.Vars.content;
|
||||
|
||||
/** Class for controlling status effects on an entity. */
|
||||
public class Statuses implements Saveable{
|
||||
private static final StatusEntry globalResult = new StatusEntry();
|
||||
private static final Array<StatusEntry> removals = new Array<>();
|
||||
|
||||
@Component
|
||||
abstract class StatusComp implements Posc, Flyingc{
|
||||
private Array<StatusEntry> statuses = new Array<>();
|
||||
private Bits applied = new Bits(content.getBy(ContentType.status).size);
|
||||
|
||||
private float speedMultiplier;
|
||||
private float damageMultiplier;
|
||||
private float armorMultiplier;
|
||||
@ReadOnly transient float speedMultiplier, damageMultiplier, armorMultiplier;
|
||||
|
||||
public void handleApply(Unit unit, StatusEffect effect, float duration){
|
||||
if(effect == StatusEffects.none || effect == null || unit.isImmune(effect)) return; //don't apply empty or immune effects
|
||||
/** @return damage taken based on status armor multipliers */
|
||||
float getShieldDamage(float amount){
|
||||
return amount * Mathf.clamp(1f - armorMultiplier / 100f);
|
||||
}
|
||||
|
||||
void apply(StatusEffect effect, float duration){
|
||||
if(effect == StatusEffects.none || effect == null || isImmune(effect)) return; //don't apply empty or immune effects
|
||||
|
||||
if(statuses.size > 0){
|
||||
//check for opposite effects
|
||||
for(StatusEntry entry : statuses){
|
||||
for(int i = 0; i < statuses.size; i ++){
|
||||
StatusEntry entry = statuses.get(i);
|
||||
//extend effect
|
||||
if(entry.effect == effect){
|
||||
entry.time = Math.max(entry.time, duration);
|
||||
return;
|
||||
}else if(entry.effect.reactsWith(effect)){ //find opposite
|
||||
globalResult.effect = entry.effect;
|
||||
entry.effect.getTransition(unit, effect, entry.time, duration, globalResult);
|
||||
entry.time = globalResult.time;
|
||||
StatusEntry.tmp.effect = entry.effect;
|
||||
entry.effect.getTransition((Unitc)this, effect, entry.time, duration, StatusEntry.tmp);
|
||||
entry.time = StatusEntry.tmp.time;
|
||||
|
||||
if(globalResult.effect != entry.effect){
|
||||
entry.effect = globalResult.effect;
|
||||
if(StatusEntry.tmp.effect != entry.effect){
|
||||
entry.effect = StatusEntry.tmp.effect;
|
||||
}
|
||||
|
||||
//stop looking when one is found
|
||||
@@ -58,70 +61,71 @@ public class Statuses implements Saveable{
|
||||
statuses.add(entry);
|
||||
}
|
||||
|
||||
public Color getStatusColor(){
|
||||
boolean isBoss(){
|
||||
return hasEffect(StatusEffects.boss);
|
||||
}
|
||||
|
||||
abstract boolean isImmune(StatusEffect effect);
|
||||
|
||||
Color statusColor(){
|
||||
if(statuses.size == 0){
|
||||
return Tmp.c1.set(Color.white);
|
||||
}
|
||||
|
||||
float r = 0f, g = 0f, b = 0f;
|
||||
float r = 1f, g = 1f, b = 1f, total = 0f;
|
||||
for(StatusEntry entry : statuses){
|
||||
r += entry.effect.color.r;
|
||||
g += entry.effect.color.g;
|
||||
b += entry.effect.color.b;
|
||||
float intensity = entry.time < 10f ? entry.time/10f : 1f;
|
||||
r += entry.effect.color.r * intensity;
|
||||
g += entry.effect.color.g * intensity;
|
||||
b += entry.effect.color.b * intensity;
|
||||
total += intensity;
|
||||
}
|
||||
return Tmp.c1.set(r / statuses.size, g / statuses.size, b / statuses.size, 1f);
|
||||
float count = statuses.size + total;
|
||||
return Tmp.c1.set(r / count, g / count, b / count, 1f);
|
||||
}
|
||||
|
||||
public void clear(){
|
||||
statuses.clear();
|
||||
}
|
||||
@Override
|
||||
public void update(){
|
||||
Floor floor = floorOn();
|
||||
if(isGrounded() && floor.status != null){
|
||||
//apply effect
|
||||
apply(floor.status, floor.statusDuration);
|
||||
}
|
||||
|
||||
public void update(Unit unit){
|
||||
applied.clear();
|
||||
speedMultiplier = damageMultiplier = armorMultiplier = 1f;
|
||||
|
||||
if(statuses.size == 0) return;
|
||||
if(statuses.isEmpty()) return;
|
||||
|
||||
removals.clear();
|
||||
int index = 0;
|
||||
|
||||
while(index < statuses.size){
|
||||
StatusEntry entry = statuses.get(index++);
|
||||
|
||||
for(StatusEntry entry : statuses){
|
||||
entry.time = Math.max(entry.time - Time.delta(), 0);
|
||||
applied.set(entry.effect.id);
|
||||
|
||||
if(entry.time <= 0){
|
||||
Pools.free(entry);
|
||||
removals.add(entry);
|
||||
index --;
|
||||
statuses.remove(index);
|
||||
}else{
|
||||
speedMultiplier *= entry.effect.speedMultiplier;
|
||||
armorMultiplier *= entry.effect.armorMultiplier;
|
||||
damageMultiplier *= entry.effect.damageMultiplier;
|
||||
entry.effect.update(unit, entry.time);
|
||||
//TODO ugly casting
|
||||
entry.effect.update((Unitc)this, entry.time);
|
||||
}
|
||||
}
|
||||
|
||||
if(removals.size > 0){
|
||||
statuses.removeAll(removals, true);
|
||||
}
|
||||
}
|
||||
|
||||
public float getSpeedMultiplier(){
|
||||
return speedMultiplier;
|
||||
}
|
||||
|
||||
public float getDamageMultiplier(){
|
||||
return damageMultiplier;
|
||||
}
|
||||
|
||||
public float getArmorMultiplier(){
|
||||
return armorMultiplier;
|
||||
}
|
||||
|
||||
public boolean hasEffect(StatusEffect effect){
|
||||
boolean hasEffect(StatusEffect effect){
|
||||
return applied.get(effect.id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeSave(DataOutput stream) throws IOException{
|
||||
//TODO autogen io code
|
||||
|
||||
void writeSave(DataOutput stream) throws IOException{
|
||||
stream.writeByte(statuses.size);
|
||||
for(StatusEntry entry : statuses){
|
||||
stream.writeByte(entry.effect.id);
|
||||
@@ -129,8 +133,7 @@ public class Statuses implements Saveable{
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readSave(DataInput stream, byte version) throws IOException{
|
||||
void readSave(DataInput stream, byte version) throws IOException{
|
||||
for(StatusEntry effect : statuses){
|
||||
Pools.free(effect);
|
||||
}
|
||||
@@ -146,5 +149,4 @@ public class Statuses implements Saveable{
|
||||
statuses.add(entry);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
37
core/src/mindustry/entities/def/SyncComp.java
Normal file
37
core/src/mindustry/entities/def/SyncComp.java
Normal file
@@ -0,0 +1,37 @@
|
||||
package mindustry.entities.def;
|
||||
|
||||
import mindustry.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.net.*;
|
||||
|
||||
@Component
|
||||
abstract class SyncComp implements Posc{
|
||||
@Import float x, y;
|
||||
|
||||
Interpolator interpolator = new Interpolator();
|
||||
|
||||
void setNet(float x, float y){
|
||||
set(x, y);
|
||||
|
||||
//TODO change interpolator API
|
||||
interpolator.target.set(x, y);
|
||||
interpolator.last.set(x, y);
|
||||
interpolator.pos.set(0, 0);
|
||||
interpolator.updateSpacing = 16;
|
||||
interpolator.lastUpdated = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(){
|
||||
if(Vars.net.client() && !isLocal()){
|
||||
interpolate();
|
||||
}
|
||||
}
|
||||
|
||||
void interpolate(){
|
||||
interpolator.update();
|
||||
x = interpolator.pos.x;
|
||||
y = interpolator.pos.y;
|
||||
}
|
||||
}
|
||||
23
core/src/mindustry/entities/def/TeamComp.java
Normal file
23
core/src/mindustry/entities/def/TeamComp.java
Normal file
@@ -0,0 +1,23 @@
|
||||
package mindustry.entities.def;
|
||||
|
||||
import arc.util.ArcAnnotate.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.game.*;
|
||||
import mindustry.gen.*;
|
||||
|
||||
import static mindustry.Vars.state;
|
||||
|
||||
@Component
|
||||
abstract class TeamComp implements Posc{
|
||||
@Import float x, y;
|
||||
|
||||
Team team = Team.derelict;
|
||||
|
||||
public @Nullable Tilec closestCore(){
|
||||
return state.teams.closestCore(x, y, team);
|
||||
}
|
||||
|
||||
public @Nullable Tilec closestEnemyCore(){
|
||||
return state.teams.closestEnemyCore(x, y, team);
|
||||
}
|
||||
}
|
||||
1015
core/src/mindustry/entities/def/TileComp.java
Normal file
1015
core/src/mindustry/entities/def/TileComp.java
Normal file
File diff suppressed because it is too large
Load Diff
27
core/src/mindustry/entities/def/TimedComp.java
Normal file
27
core/src/mindustry/entities/def/TimedComp.java
Normal file
@@ -0,0 +1,27 @@
|
||||
package mindustry.entities.def;
|
||||
|
||||
import arc.math.*;
|
||||
import arc.util.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.gen.*;
|
||||
|
||||
@Component
|
||||
abstract class TimedComp implements Entityc, Scaled{
|
||||
float time, lifetime;
|
||||
|
||||
//called last so pooling and removal happens then.
|
||||
@MethodPriority(100)
|
||||
@Override
|
||||
public void update(){
|
||||
time = Math.min(time + Time.delta(), lifetime);
|
||||
|
||||
if(time >= lifetime){
|
||||
remove();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public float fin(){
|
||||
return time / lifetime;
|
||||
}
|
||||
}
|
||||
13
core/src/mindustry/entities/def/TimerComp.java
Normal file
13
core/src/mindustry/entities/def/TimerComp.java
Normal file
@@ -0,0 +1,13 @@
|
||||
package mindustry.entities.def;
|
||||
|
||||
import arc.util.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
|
||||
@Component
|
||||
abstract class TimerComp{
|
||||
Interval timer = new Interval(6);
|
||||
|
||||
public boolean timer(int index, float time){
|
||||
return timer.get(index, time);
|
||||
}
|
||||
}
|
||||
240
core/src/mindustry/entities/def/UnitComp.java
Normal file
240
core/src/mindustry/entities/def/UnitComp.java
Normal file
@@ -0,0 +1,240 @@
|
||||
package mindustry.entities.def;
|
||||
|
||||
import arc.*;
|
||||
import arc.math.*;
|
||||
import arc.math.geom.*;
|
||||
import arc.util.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.entities.*;
|
||||
import mindustry.entities.units.*;
|
||||
import mindustry.game.EventType.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.blocks.environment.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
@Component
|
||||
abstract class UnitComp implements Healthc, Velc, Statusc, Teamc, Itemsc, Hitboxc, Rotc, Massc, Unitc, Weaponsc, Drawc, Boundedc,
|
||||
DrawLayerGroundc, DrawLayerFlyingc, DrawLayerGroundShadowsc, DrawLayerFlyingShadowsc, Syncc{
|
||||
@Import float x, y, rotation, elevation;
|
||||
|
||||
private UnitController controller;
|
||||
private UnitType type;
|
||||
|
||||
public void moveAt(Vec2 vector){
|
||||
moveAt(vector, type.accel);
|
||||
}
|
||||
|
||||
public void aimLook(Position pos){
|
||||
aim(pos);
|
||||
lookAt(pos);
|
||||
}
|
||||
|
||||
public void aimLook(float x, float y){
|
||||
aim(x, y);
|
||||
lookAt(x, y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float clipSize(){
|
||||
return type.region.getWidth() * 2f;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int itemCapacity(){
|
||||
return type.itemCapacity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float bounds(){
|
||||
return hitSize() * 2f;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void controller(UnitController controller){
|
||||
this.controller = controller;
|
||||
if(controller.unit() != this) controller.unit(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UnitController controller(){
|
||||
return controller;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(UnitType def, UnitController controller){
|
||||
type(type);
|
||||
controller(controller);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void collision(Hitboxc other, float x, float y){
|
||||
if(!(other instanceof Unitc)) return;
|
||||
Unitc unit = (Unitc)other;
|
||||
|
||||
if(isGrounded() != unit.isGrounded()) return;
|
||||
|
||||
float scale = 2f;
|
||||
hitbox(Tmp.r1);
|
||||
other.hitbox(Tmp.r2);
|
||||
Vec2 v = Geometry.overlap(Tmp.r1, Tmp.r2, true);
|
||||
float tm = mass() + unit.mass();
|
||||
float s1 = mass() / tm, s2 = unit.mass() / tm;
|
||||
move(v.x*s2/scale, v.y*s2/scale);
|
||||
unit.move(-v.x*s1/scale, -v.y*s1/scale);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void type(UnitType type){
|
||||
this.type = type;
|
||||
maxHealth(type.health);
|
||||
heal();
|
||||
drag(type.drag);
|
||||
hitSize(type.hitsize);
|
||||
controller(type.createController());
|
||||
setupWeapons(type);
|
||||
|
||||
elevation = type.flying ? 1f : 0f;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UnitType type(){
|
||||
return type;
|
||||
}
|
||||
|
||||
public void lookAt(float angle){
|
||||
rotation = Angles.moveToward(rotation, angle, type.rotateSpeed * Time.delta());
|
||||
}
|
||||
|
||||
public void lookAt(Position pos){
|
||||
lookAt(angleTo(pos));
|
||||
}
|
||||
|
||||
public void lookAt(float x, float y){
|
||||
lookAt(angleTo(x, y));
|
||||
}
|
||||
|
||||
public boolean isAI(){
|
||||
return controller instanceof AIController;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterRead(){
|
||||
//set up type info after reading
|
||||
type(this.type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(){
|
||||
drag(type.drag * (isGrounded() ? (floorOn().dragMultiplier) : 1f));
|
||||
|
||||
//apply knockback based on spawns
|
||||
if(team() != state.rules.waveTeam){
|
||||
float relativeSize = state.rules.dropZoneRadius + bounds()/2f + 1f;
|
||||
for(Tile spawn : spawner.getSpawns()){
|
||||
if(withinDst(spawn.worldx(), spawn.worldy(), relativeSize)){
|
||||
vel().add(Tmp.v1.set(this).sub(spawn.worldx(), spawn.worldy()).setLength(0.1f + 1f - dst(spawn) / relativeSize).scl(0.45f * Time.delta()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Tile tile = tileOn();
|
||||
Floor floor = floorOn();
|
||||
|
||||
if(tile != null && isGrounded()){
|
||||
//unit block update
|
||||
if(tile.entity != null){
|
||||
tile.entity.unitOn(this);
|
||||
}
|
||||
|
||||
//kill when stuck in wall
|
||||
if(tile.solid()){
|
||||
kill();
|
||||
}
|
||||
|
||||
//apply damage
|
||||
if(floor.damageTaken > 0f){
|
||||
damageContinuous(floor.damageTaken);
|
||||
}
|
||||
}
|
||||
|
||||
controller.update();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isImmune(StatusEffect effect){
|
||||
return type.immunities.contains(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(){
|
||||
type.drawEngine(this);
|
||||
type.drawBody(this);
|
||||
type.drawWeapons(this);
|
||||
if(type.drawCell) type.drawCell(this);
|
||||
if(type.drawItems) type.drawItems(this);
|
||||
type.drawLight(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawFlyingShadows(){
|
||||
if(isFlying()) type.drawShadow(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawGroundShadows(){
|
||||
type.drawOcclusion(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawFlying(){
|
||||
if(isFlying()) draw();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawGround(){
|
||||
if(isGrounded()) draw();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPlayer(){
|
||||
return controller instanceof Playerc;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void killed(){
|
||||
float explosiveness = 2f + item().explosiveness * stack().amount;
|
||||
float flammability = item().flammability * stack().amount;
|
||||
Damage.dynamicExplosion(x, y, flammability, explosiveness, 0f, bounds() / 2f, Pal.darkFlame);
|
||||
|
||||
Effects.scorch(x, y, (int)(hitSize() / 5));
|
||||
Fx.explosion.at(this);
|
||||
Effects.shake(2f, 2f, this);
|
||||
type.deathSound.at(this);
|
||||
|
||||
Events.fire(new UnitDestroyEvent(this));
|
||||
|
||||
if(explosiveness > 7f && isLocal()){
|
||||
Events.fire(Trigger.suicideBomb);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canMine(Item item){
|
||||
return type.drillTier >= item.hardness;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float miningSpeed(){
|
||||
return type.mineSpeed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean offloadImmediately(){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
27
core/src/mindustry/entities/def/VelComp.java
Normal file
27
core/src/mindustry/entities/def/VelComp.java
Normal file
@@ -0,0 +1,27 @@
|
||||
package mindustry.entities.def;
|
||||
|
||||
import arc.math.geom.*;
|
||||
import arc.util.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.gen.*;
|
||||
|
||||
@Component
|
||||
abstract class VelComp implements Posc{
|
||||
@Import float x, y;
|
||||
|
||||
final Vec2 vel = new Vec2();
|
||||
transient float drag = 0f;
|
||||
|
||||
//velocity needs to be called first, as it affects delta and lastPosition
|
||||
@MethodPriority(-1)
|
||||
@Override
|
||||
public void update(){
|
||||
move(vel.x * Time.delta(), vel.y * Time.delta());
|
||||
vel.scl(1f - drag * Time.delta());
|
||||
}
|
||||
|
||||
void move(float cx, float cy){
|
||||
x += cx;
|
||||
y += cy;
|
||||
}
|
||||
}
|
||||
41
core/src/mindustry/entities/def/WaterMoveComp.java
Normal file
41
core/src/mindustry/entities/def/WaterMoveComp.java
Normal file
@@ -0,0 +1,41 @@
|
||||
package mindustry.entities.def;
|
||||
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.entities.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.world.blocks.environment.*;
|
||||
|
||||
import static mindustry.Vars.collisions;
|
||||
|
||||
//just a proof of concept
|
||||
@Component
|
||||
abstract class WaterMoveComp implements Posc, Velc, Hitboxc, Flyingc{
|
||||
@Import float x, y;
|
||||
|
||||
@Replace
|
||||
@Override
|
||||
public void move(float cx, float cy){
|
||||
if(isGrounded()){
|
||||
if(!EntityCollisions.waterSolid(tileX(), tileY())){
|
||||
collisions.move(this, cx, cy, EntityCollisions::waterSolid);
|
||||
}
|
||||
}else{
|
||||
x += cx;
|
||||
y += cy;
|
||||
}
|
||||
}
|
||||
|
||||
@Replace
|
||||
@Override
|
||||
public boolean canDrown(){
|
||||
return false;
|
||||
}
|
||||
|
||||
@Replace
|
||||
public float floorSpeedMultiplier(){
|
||||
Floor on = isFlying() ? Blocks.air.asFloor() : floorOn();
|
||||
return on.isDeep() ? 1.3f : 1f;
|
||||
}
|
||||
}
|
||||
|
||||
153
core/src/mindustry/entities/def/WeaponsComp.java
Normal file
153
core/src/mindustry/entities/def/WeaponsComp.java
Normal file
@@ -0,0 +1,153 @@
|
||||
package mindustry.entities.def;
|
||||
|
||||
import arc.math.*;
|
||||
import arc.math.geom.*;
|
||||
import arc.util.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.entities.*;
|
||||
import mindustry.entities.bullet.*;
|
||||
import mindustry.entities.units.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.type.*;
|
||||
|
||||
@Component
|
||||
abstract class WeaponsComp implements Teamc, Posc, Rotc{
|
||||
@Import float x, y, rotation;
|
||||
|
||||
/** minimum cursor distance from player, fixes 'cross-eyed' shooting */
|
||||
static final float minAimDst = 20f;
|
||||
/** temporary weapon sequence number */
|
||||
static int sequenceNum = 0;
|
||||
|
||||
/** weapon mount array, never null */
|
||||
@ReadOnly WeaponMount[] mounts = {};
|
||||
@ReadOnly transient float range, aimX, aimY;
|
||||
@ReadOnly transient boolean isRotate, isShooting;
|
||||
|
||||
boolean inRange(Position other){
|
||||
return within(other, range);
|
||||
}
|
||||
|
||||
void setupWeapons(UnitType def){
|
||||
mounts = new WeaponMount[def.weapons.size];
|
||||
range = 0f;
|
||||
for(int i = 0; i < mounts.length; i++){
|
||||
mounts[i] = new WeaponMount(def.weapons.get(i));
|
||||
range = Math.max(range, def.weapons.get(i).bullet.range());
|
||||
}
|
||||
}
|
||||
|
||||
void controlWeapons(boolean rotate, boolean shoot){
|
||||
for(WeaponMount mount : mounts){
|
||||
mount.rotate = rotate;
|
||||
mount.shoot = shoot;
|
||||
}
|
||||
isRotate = rotate;
|
||||
isShooting = shoot;
|
||||
}
|
||||
|
||||
void aim(Position pos){
|
||||
aim(pos.getX(), pos.getY());
|
||||
}
|
||||
|
||||
/** Aim at something. This will make all mounts point at it. */
|
||||
void aim(float x, float y){
|
||||
Tmp.v1.set(x, y).sub(this.x, this.y);
|
||||
if(Tmp.v1.len() < minAimDst) Tmp.v1.setLength(minAimDst);
|
||||
|
||||
x = Tmp.v1.x + this.x;
|
||||
y = Tmp.v1.y + this.y;
|
||||
|
||||
for(WeaponMount mount : mounts){
|
||||
mount.aimX = x;
|
||||
mount.aimY = y;
|
||||
}
|
||||
|
||||
aimX = x;
|
||||
aimY = y;
|
||||
}
|
||||
|
||||
/** Update shooting and rotation for this unit. */
|
||||
@Override
|
||||
public void update(){
|
||||
for(WeaponMount mount : mounts){
|
||||
Weapon weapon = mount.weapon;
|
||||
mount.reload = Math.max(mount.reload - Time.delta(), 0);
|
||||
|
||||
//rotate if applicable
|
||||
if(weapon.rotate && (mount.rotate || mount.shoot)){
|
||||
float axisXOffset = weapon.mirror ? 0f : weapon.x;
|
||||
float axisX = this.x + Angles.trnsx(rotation, axisXOffset, weapon.y),
|
||||
axisY = this.y + Angles.trnsy(rotation, axisXOffset, weapon.y);
|
||||
|
||||
mount.targetRotation = Angles.angle(axisX, axisY, mount.aimX, mount.aimY) - rotation();
|
||||
mount.rotation = Angles.moveToward(mount.rotation, mount.targetRotation, weapon.rotateSpeed * Time.delta());
|
||||
}else{
|
||||
mount.rotation = this.rotation;
|
||||
mount.targetRotation = angleTo(mount.aimX, mount.aimY);
|
||||
}
|
||||
|
||||
if(mount.shoot){
|
||||
float rotation = this.rotation - 90;
|
||||
|
||||
//shoot if applicable
|
||||
if(mount.reload <= 0.0001f && Angles.within(mount.rotation, mount.targetRotation, 1.5f)){
|
||||
for(int i : (weapon.mirror && !weapon.alternate ? Mathf.signs : Mathf.one)){
|
||||
i *= Mathf.sign(weapon.flipped) * Mathf.sign(mount.side);
|
||||
|
||||
//m a t h
|
||||
float weaponRotation = rotation + (weapon.rotate ? mount.rotation : 0);
|
||||
float mountX = this.x + Angles.trnsx(rotation, weapon.x * i, weapon.y),
|
||||
mountY = this.y + Angles.trnsy(rotation, weapon.x * i, weapon.y);
|
||||
float shootX = mountX + Angles.trnsx(weaponRotation, weapon.shootX * i, weapon.shootY),
|
||||
shootY = mountY + Angles.trnsy(weaponRotation, weapon.shootX * i, weapon.shootY);
|
||||
float shootAngle = weapon.rotate ? weaponRotation + 90 : Angles.angle(shootX, shootY, mount.aimX, mount.aimY) + (this.rotation - angleTo(mount.aimX, mount.aimY));
|
||||
|
||||
shoot(weapon, shootX, shootY, shootAngle, -i);
|
||||
}
|
||||
|
||||
mount.side = !mount.side;
|
||||
mount.reload = weapon.reload;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void shoot(Weapon weapon, float x, float y, float rotation, int side){
|
||||
float baseX = this.x, baseY = this.y;
|
||||
|
||||
weapon.shootSound.at(x, y, Mathf.random(0.8f, 1.0f));
|
||||
|
||||
sequenceNum = 0;
|
||||
if(weapon.shotDelay > 0.01f){
|
||||
Angles.shotgun(weapon.shots, weapon.spacing, rotation, f -> {
|
||||
Time.run(sequenceNum * weapon.shotDelay, () -> bullet(weapon, x + this.x - baseX, y + this.y - baseY, f + Mathf.range(weapon.inaccuracy)));
|
||||
sequenceNum++;
|
||||
});
|
||||
}else{
|
||||
Angles.shotgun(weapon.shots, weapon.spacing, rotation, f -> bullet(weapon, x, y, f + Mathf.range(weapon.inaccuracy)));
|
||||
}
|
||||
|
||||
BulletType ammo = weapon.bullet;
|
||||
|
||||
Tmp.v1.trns(rotation + 180f, ammo.recoil);
|
||||
|
||||
if(this instanceof Velc){
|
||||
//TODO apply force?
|
||||
((Velc)this).vel().add(Tmp.v1);
|
||||
}
|
||||
|
||||
Tmp.v1.trns(rotation, 3f);
|
||||
boolean parentize = ammo.keepVelocity;
|
||||
|
||||
Effects.shake(weapon.shake, weapon.shake, x, y);
|
||||
weapon.ejectEffect.at(x, y, rotation * side);
|
||||
ammo.shootEffect.at(x + Tmp.v1.x, y + Tmp.v1.y, rotation, parentize ? this : null);
|
||||
ammo.smokeEffect.at(x + Tmp.v1.x, y + Tmp.v1.y, rotation, parentize ? this : null);
|
||||
}
|
||||
|
||||
private void bullet(Weapon weapon, float x, float y, float angle){
|
||||
Tmp.v1.trns(angle, 3f);
|
||||
weapon.bullet.create(this, team(), x + Tmp.v1.x, y + Tmp.v1.y, angle, (1f - weapon.velocityRnd) + Mathf.random(weapon.velocityRnd));
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
package mindustry.entities.effect;
|
||||
|
||||
import arc.graphics.g2d.Draw;
|
||||
import arc.math.Mathf;
|
||||
import mindustry.entities.EntityGroup;
|
||||
import mindustry.entities.type.TimedEntity;
|
||||
import mindustry.entities.traits.BelowLiquidTrait;
|
||||
import mindustry.entities.traits.DrawTrait;
|
||||
import mindustry.graphics.Pal;
|
||||
|
||||
import static mindustry.Vars.groundEffectGroup;
|
||||
|
||||
/**
|
||||
* Class for creating block rubble on the ground.
|
||||
*/
|
||||
public abstract class Decal extends TimedEntity implements BelowLiquidTrait, DrawTrait{
|
||||
|
||||
@Override
|
||||
public float lifetime(){
|
||||
return 3600;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(){
|
||||
Draw.color(Pal.rubble.r, Pal.rubble.g, Pal.rubble.b, 1f - Mathf.curve(fin(), 0.98f));
|
||||
drawDecal();
|
||||
Draw.color();
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityGroup targetGroup(){
|
||||
return groundEffectGroup;
|
||||
}
|
||||
|
||||
abstract void drawDecal();
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user