Merge branch '6.0' of https://github.com/Anuken/Mindustry into object-config
# Conflicts: # core/src/mindustry/entities/traits/BuilderTrait.java # core/src/mindustry/entities/type/TileEntity.java # core/src/mindustry/game/EventType.java # core/src/mindustry/game/Schematics.java # core/src/mindustry/input/InputHandler.java # core/src/mindustry/io/TypeIO.java # core/src/mindustry/world/Block.java # core/src/mindustry/world/blocks/distribution/Sorter.java
This commit is contained in:
@@ -18,6 +18,7 @@ import mindustry.graphics.*;
|
||||
import mindustry.maps.*;
|
||||
import mindustry.mod.*;
|
||||
import mindustry.net.Net;
|
||||
import mindustry.ui.*;
|
||||
|
||||
import static arc.Core.*;
|
||||
import static mindustry.Vars.*;
|
||||
@@ -32,6 +33,8 @@ public abstract class ClientLauncher extends ApplicationCore implements Platform
|
||||
|
||||
@Override
|
||||
public void setup(){
|
||||
Events.fire(new ClientCreateEvent());
|
||||
|
||||
Vars.loadLogger();
|
||||
Vars.loadFileLogger();
|
||||
Vars.platform = this;
|
||||
@@ -55,14 +58,15 @@ public abstract class ClientLauncher extends ApplicationCore implements Platform
|
||||
Vars.net = new Net(platform.getNet());
|
||||
mods = new Mods();
|
||||
|
||||
UI.loadSystemCursors();
|
||||
Fonts.loadSystemCursors();
|
||||
|
||||
assets.load(new Vars());
|
||||
|
||||
UI.loadDefaultFont();
|
||||
Fonts.loadDefaultFont();
|
||||
|
||||
assets.load(new AssetDescriptor<>("sprites/sprites.atlas", TextureAtlas.class)).loaded = t -> {
|
||||
atlas = (TextureAtlas)t;
|
||||
Fonts.mergeFontAtlas(atlas);
|
||||
};
|
||||
|
||||
assets.loadRun("maps", Map.class, () -> maps.loadPreviews());
|
||||
@@ -88,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,9 +13,6 @@ 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.*;
|
||||
@@ -24,7 +21,6 @@ import mindustry.maps.*;
|
||||
import mindustry.mod.*;
|
||||
import mindustry.net.Net;
|
||||
import mindustry.net.*;
|
||||
import mindustry.world.blocks.defense.ForceProjector.*;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.charset.*;
|
||||
@@ -32,7 +28,6 @@ import java.util.*;
|
||||
|
||||
import static arc.Core.settings;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public class Vars implements Loadable{
|
||||
/** Whether to load locales.*/
|
||||
public static boolean loadLocales = true;
|
||||
@@ -60,7 +55,7 @@ public class Vars implements Loadable{
|
||||
public static final String serverJsonURL = "https://raw.githubusercontent.com/Anuken/Mindustry/master/servers.json";
|
||||
/** URL to the JSON file containing all the BE servers. Only queried in BE. */
|
||||
public static final String serverJsonBeURL = "https://raw.githubusercontent.com/Anuken/Mindustry/master/servers_be.json";
|
||||
/** URL the links to the wiki's modding guide.*/
|
||||
/** URL of the github issue report template.*/
|
||||
public static final String reportIssueURL = "https://github.com/Anuken/Mindustry/issues/new?template=bug_report.md";
|
||||
/** list of built-in servers.*/
|
||||
public static final Array<String> defaultServers = Array.with();
|
||||
@@ -78,6 +73,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 */
|
||||
@@ -88,7 +87,7 @@ public class Vars implements Loadable{
|
||||
public static final Color[] playerColors = {
|
||||
Color.valueOf("82759a"),
|
||||
Color.valueOf("c0c1c5"),
|
||||
Color.valueOf("fff0e7"),
|
||||
Color.valueOf("ffffff"),
|
||||
Color.valueOf("7d2953"),
|
||||
Color.valueOf("ff074e"),
|
||||
Color.valueOf("ff072a"),
|
||||
@@ -153,7 +152,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;
|
||||
@@ -166,6 +165,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;
|
||||
@@ -179,18 +179,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(){
|
||||
@@ -200,6 +189,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){
|
||||
@@ -228,6 +218,7 @@ public class Vars implements Loadable{
|
||||
defaultWaves = new DefaultWaves();
|
||||
collisions = new EntityCollisions();
|
||||
world = new World();
|
||||
universe = new Universe();
|
||||
becontrol = new BeControl();
|
||||
|
||||
maps = new Maps();
|
||||
@@ -235,25 +226,6 @@ public class Vars implements Loadable{
|
||||
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();
|
||||
|
||||
|
||||
@@ -7,9 +7,9 @@ import arc.math.geom.*;
|
||||
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.*;
|
||||
@@ -25,6 +25,7 @@ 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<>();
|
||||
@@ -77,18 +78,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++){
|
||||
@@ -118,14 +115,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -145,7 +139,7 @@ public class BlockIndexer{
|
||||
|
||||
ObjectSet<Tile> 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);
|
||||
}
|
||||
}
|
||||
@@ -162,6 +156,39 @@ public class BlockIndexer{
|
||||
return flagMap[team.id][type.ordinal()];
|
||||
}
|
||||
|
||||
public boolean eachBlock(Teamc team, float range, Boolf<Tile> pred, Cons<Tile> cons){
|
||||
return eachBlock(team.team(), team.getX(), team.getY(), range, pred, cons);
|
||||
}
|
||||
|
||||
public boolean eachBlock(Team team, float wx, float wy, float range, Boolf<Tile> pred, Cons<Tile> 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;
|
||||
|
||||
Tile other = world.ltile(x, y);
|
||||
|
||||
if(other == null) continue;
|
||||
|
||||
if(other.team() == team && !intSet.contains(other.pos()) && other.entity != null && 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();
|
||||
@@ -178,20 +205,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 ObjectSet<>();
|
||||
}
|
||||
|
||||
ObjectSet<Tile> set = damagedTiles[(int)entity.getTeam().id];
|
||||
set.add(entity.tile);
|
||||
ObjectSet<Tile> 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<Tile> 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 +227,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<Tile> 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<Tile> pred, boolean usePriority){
|
||||
Tilec closest = null;
|
||||
float dst = 0;
|
||||
float range2 = range*range;
|
||||
|
||||
@@ -220,17 +247,17 @@ public class BlockIndexer{
|
||||
|
||||
if(other == null) continue;
|
||||
|
||||
if(other.entity == null || other.getTeam() != team || !pred.get(other) || !other.block().targetable)
|
||||
if(other.entity == null || other.team() != team || !pred.get(other) || !other.block().targetable)
|
||||
continue;
|
||||
|
||||
TileEntity e = other.entity;
|
||||
Tilec 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;
|
||||
}
|
||||
@@ -271,8 +298,8 @@ 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){
|
||||
ObjectSet<Tile>[] map = getFlagged(tile.team());
|
||||
|
||||
for(BlockFlag flag : tile.block().flags){
|
||||
|
||||
@@ -282,9 +309,9 @@ public class BlockIndexer{
|
||||
|
||||
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()));
|
||||
}
|
||||
activeTeams.add(tile.getTeam());
|
||||
activeTeams.add(tile.team());
|
||||
|
||||
if(ores == null) return;
|
||||
|
||||
@@ -328,7 +355,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
|
||||
}
|
||||
@@ -340,7 +367,7 @@ public class BlockIndexer{
|
||||
for(int y = quadrantY * quadrantSize; y < world.height() && y < (quadrantY + 1) * quadrantSize; y++){
|
||||
Tile result = world.ltile(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.entity != null && result.team() == team){
|
||||
bits.set(quadrantX, quadrantY);
|
||||
break outer;
|
||||
}
|
||||
@@ -369,20 +396,16 @@ public class BlockIndexer{
|
||||
ores.put(item, new ObjectSet<>());
|
||||
}
|
||||
|
||||
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)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.*;
|
||||
@@ -33,8 +33,7 @@ public class Pathfinder implements Runnable{
|
||||
/** handles task scheduling on the update thread. */
|
||||
private TaskQueue queue = new TaskQueue();
|
||||
/** current pathfinding thread */
|
||||
private @Nullable
|
||||
Thread thread;
|
||||
private @Nullable Thread thread;
|
||||
|
||||
public Pathfinder(){
|
||||
Events.on(WorldLoadEvent.class, event -> {
|
||||
@@ -46,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
|
||||
|
||||
@@ -1,20 +1,16 @@
|
||||
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.*;
|
||||
|
||||
@@ -39,7 +35,7 @@ public class WaveSpawner{
|
||||
|
||||
/** @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);
|
||||
return !player.dead() && groundSpawns.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 +49,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 +62,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 +72,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));
|
||||
}
|
||||
});
|
||||
@@ -91,10 +86,10 @@ public class WaveSpawner{
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -108,8 +103,8 @@ public class WaveSpawner{
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -123,12 +118,9 @@ public class WaveSpawner{
|
||||
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){
|
||||
addSpawns(tile.x, tile.y);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -141,11 +133,11 @@ public class WaveSpawner{
|
||||
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);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,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.*;
|
||||
@@ -34,7 +33,7 @@ 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,9 +56,9 @@ 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, armoredConveyor, distributor, junction, itemBridge, phaseConveyor, sorter, invertedSorter, router, overflowGate, massDriver,
|
||||
conveyor, titaniumConveyor, armoredConveyor, distributor, junction, itemBridge, phaseConveyor, sorter, invertedSorter, router, overflowGate, underflowGate, massDriver,
|
||||
|
||||
//liquids
|
||||
//liquid
|
||||
mechanicalPump, rotaryPump, thermalPump, conduit, pulseConduit, platedConduit, liquidRouter, liquidTank, liquidJunction, bridgeConduit, phaseConduit,
|
||||
|
||||
//power
|
||||
@@ -80,7 +79,7 @@ public class Blocks implements ContentList{
|
||||
fortressFactory, repairPoint,
|
||||
|
||||
//upgrades
|
||||
dartPad, deltaPad, tauPad, omegaPad, javelinPad, tridentPad, glaivePad;
|
||||
dartPad, alphaPad, deltaPad, tauPad, omegaPad, javelinPad, tridentPad, glaivePad;
|
||||
|
||||
@Override
|
||||
public void load(){
|
||||
@@ -124,6 +123,8 @@ public class Blocks implements ContentList{
|
||||
public void draw(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++){
|
||||
@@ -203,6 +204,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"){{
|
||||
|
||||
}};
|
||||
@@ -258,13 +270,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 +304,7 @@ public class Blocks implements ContentList{
|
||||
|
||||
icerocks = new StaticWall("icerocks"){{
|
||||
variants = 2;
|
||||
iceSnow.asFloor().wall = this;
|
||||
}};
|
||||
|
||||
snowrocks = new StaticWall("snowrocks"){{
|
||||
@@ -355,11 +368,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"){{
|
||||
@@ -398,12 +413,37 @@ public class Blocks implements ContentList{
|
||||
//endregion
|
||||
//region ore
|
||||
|
||||
oreCopper = new OreBlock(Items.copper);
|
||||
oreLead = new OreBlock(Items.lead);
|
||||
oreCopper = new OreBlock(Items.copper){{
|
||||
oreDefault = true;
|
||||
oreThreshold = 0.81f;
|
||||
oreScale = 23.47619f;
|
||||
}};
|
||||
|
||||
oreLead = new OreBlock(Items.lead){{
|
||||
oreDefault = true;
|
||||
oreThreshold = 0.828f;
|
||||
oreScale = 23.952381f;
|
||||
}};
|
||||
|
||||
oreScrap = new OreBlock(Items.scrap);
|
||||
oreCoal = new OreBlock(Items.coal);
|
||||
oreTitanium = new OreBlock(Items.titanium);
|
||||
oreThorium = new OreBlock(Items.thorium);
|
||||
|
||||
oreCoal = new OreBlock(Items.coal){{
|
||||
oreDefault = true;
|
||||
oreThreshold = 0.846f;
|
||||
oreScale = 24.428572f;
|
||||
}};
|
||||
|
||||
oreTitanium = new OreBlock(Items.titanium){{
|
||||
oreDefault = true;
|
||||
oreThreshold = 0.864f;
|
||||
oreScale = 24.904762f;
|
||||
}};
|
||||
|
||||
oreThorium = new OreBlock(Items.thorium){{
|
||||
oreDefault = true;
|
||||
oreThreshold = 0.882f;
|
||||
oreScale = 25.380953f;
|
||||
}};
|
||||
|
||||
//endregion
|
||||
//region crafting
|
||||
@@ -562,7 +602,7 @@ 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;
|
||||
LiquidModule mod = tile.entity.liquids();
|
||||
|
||||
int rotation = rotate ? tile.rotation() * 90 : 0;
|
||||
|
||||
@@ -624,10 +664,6 @@ public class Blocks implements ContentList{
|
||||
);
|
||||
hasPower = true;
|
||||
craftTime = 35f;
|
||||
spinnerLength = 1.5f;
|
||||
spinnerRadius = 3.5f;
|
||||
spinnerThickness = 1.5f;
|
||||
spinnerSpeed = 3f;
|
||||
size = 2;
|
||||
|
||||
consumes.power(1f);
|
||||
@@ -657,13 +693,12 @@ 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.color(Color.clear, tile.entity.liquids().current().color, tile.entity.liquids().total() / liquidCapacity);
|
||||
Draw.rect(reg(liquidRegion), tile.drawx(), tile.drawy());
|
||||
Draw.color();
|
||||
Draw.rect(reg(topRegion), tile.drawx(), tile.drawy());
|
||||
@@ -949,6 +984,12 @@ public class Blocks implements ContentList{
|
||||
buildCostMultiplier = 3f;
|
||||
}};
|
||||
|
||||
underflowGate = new OverflowGate("underflow-gate"){{
|
||||
requirements(Category.distribution, ItemStack.with(Items.lead, 2, Items.copper, 4));
|
||||
buildCostMultiplier = 3f;
|
||||
invert = true;
|
||||
}};
|
||||
|
||||
massDriver = new MassDriver("mass-driver"){{
|
||||
requirements(Category.distribution, ItemStack.with(Items.titanium, 125, Items.silicon, 75, Items.lead, 125, Items.thorium, 50));
|
||||
size = 3;
|
||||
@@ -1096,13 +1137,13 @@ public class Blocks implements ContentList{
|
||||
differentialGenerator = new SingleTypeGenerator("differential-generator"){{
|
||||
requirements(Category.power, ItemStack.with(Items.copper, 70, Items.titanium, 50, Items.lead, 100, Items.silicon, 65, Items.metaglass, 50));
|
||||
powerProduction = 16f;
|
||||
itemDuration = 120f;
|
||||
itemDuration = 140f;
|
||||
hasLiquids = true;
|
||||
hasItems = true;
|
||||
size = 3;
|
||||
|
||||
consumes.item(Items.pyratite).optional(true, false);
|
||||
consumes.liquid(Liquids.cryofluid, 0.18f);
|
||||
consumes.liquid(Liquids.cryofluid, 0.15f);
|
||||
}};
|
||||
|
||||
rtgGenerator = new DecayGenerator("rtg-generator"){{
|
||||
@@ -1509,25 +1550,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();
|
||||
}
|
||||
});
|
||||
@@ -1681,6 +1722,7 @@ public class Blocks implements ContentList{
|
||||
unitType = UnitTypes.ghoul;
|
||||
produceTime = 1150;
|
||||
size = 3;
|
||||
maxSpawn = 2;
|
||||
consumes.power(1.2f);
|
||||
consumes.items(new ItemStack(Items.silicon, 15), new ItemStack(Items.titanium, 10));
|
||||
}};
|
||||
@@ -1690,6 +1732,7 @@ public class Blocks implements ContentList{
|
||||
unitType = UnitTypes.revenant;
|
||||
produceTime = 2000;
|
||||
size = 4;
|
||||
maxSpawn = 2;
|
||||
consumes.power(3f);
|
||||
consumes.items(new ItemStack(Items.silicon, 40), new ItemStack(Items.titanium, 30));
|
||||
}};
|
||||
@@ -1742,51 +1785,58 @@ public class Blocks implements ContentList{
|
||||
//endregion
|
||||
//region upgrades
|
||||
|
||||
dartPad = new MechPad("dart-mech-pad"){{
|
||||
dartPad = new MechPad("dart-ship-pad"){{
|
||||
requirements(Category.upgrade, ItemStack.with(Items.lead, 100, Items.graphite, 50, Items.copper, 75));
|
||||
mech = Mechs.alpha;
|
||||
mech = UnitTypes.dart;
|
||||
size = 2;
|
||||
consumes.power(0.5f);
|
||||
}};
|
||||
|
||||
alphaPad = new MechPad("alpha-mech-pad"){{
|
||||
requirements(Category.upgrade, ItemStack.with(Items.lead, 100, Items.graphite, 50, Items.copper, 75));
|
||||
mech = UnitTypes.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;
|
||||
mech = UnitTypes.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;
|
||||
mech = UnitTypes.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;
|
||||
mech = UnitTypes.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;
|
||||
mech = UnitTypes.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;
|
||||
mech = UnitTypes.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;
|
||||
mech = UnitTypes.glaive;
|
||||
size = 3;
|
||||
consumes.power(1.2f);
|
||||
}};
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
43
core/src/mindustry/content/Planets.java
Normal file
43
core/src/mindustry/content/Planets.java
Normal file
@@ -0,0 +1,43 @@
|
||||
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(
|
||||
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 TestPlanetGenerator();
|
||||
meshLoader = () -> new HexMesh(this, 6);
|
||||
}};
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -22,7 +22,6 @@ public class TechTree implements ContentList{
|
||||
node(conveyor, () -> {
|
||||
|
||||
node(junction, () -> {
|
||||
node(itemBridge);
|
||||
node(router, () -> {
|
||||
node(launchPad, () -> {
|
||||
node(launchPadLarge, () -> {
|
||||
@@ -34,7 +33,9 @@ public class TechTree implements ContentList{
|
||||
node(sorter, () -> {
|
||||
node(invertedSorter);
|
||||
node(message);
|
||||
node(overflowGate);
|
||||
node(overflowGate, () -> {
|
||||
node(underflowGate);
|
||||
});
|
||||
});
|
||||
node(container, () -> {
|
||||
node(unloader);
|
||||
@@ -43,16 +44,18 @@ public class TechTree implements ContentList{
|
||||
});
|
||||
});
|
||||
|
||||
node(titaniumConveyor, () -> {
|
||||
node(phaseConveyor, () -> {
|
||||
node(massDriver, () -> {
|
||||
node(itemBridge, () -> {
|
||||
node(titaniumConveyor, () -> {
|
||||
node(phaseConveyor, () -> {
|
||||
node(massDriver, () -> {
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
node(armoredConveyor, () -> {
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
node(armoredConveyor, () -> {
|
||||
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -100,23 +103,25 @@ public class TechTree implements ContentList{
|
||||
|
||||
|
||||
node(copperWall, () -> {
|
||||
node(copperWallLarge);
|
||||
node(titaniumWall, () -> {
|
||||
node(door, () -> {
|
||||
node(doorLarge);
|
||||
});
|
||||
node(plastaniumWall, () -> {
|
||||
node(plastaniumWallLarge, () -> {
|
||||
node(copperWallLarge, () -> {
|
||||
node(titaniumWall, () -> {
|
||||
node(titaniumWallLarge);
|
||||
|
||||
node(door, () -> {
|
||||
node(doorLarge);
|
||||
});
|
||||
});
|
||||
node(titaniumWallLarge);
|
||||
node(thoriumWall, () -> {
|
||||
node(thoriumWallLarge);
|
||||
node(surgeWall, () -> {
|
||||
node(surgeWallLarge);
|
||||
node(phaseWall, () -> {
|
||||
node(phaseWallLarge);
|
||||
node(plastaniumWall, () -> {
|
||||
node(plastaniumWallLarge, () -> {
|
||||
|
||||
});
|
||||
});
|
||||
node(thoriumWall, () -> {
|
||||
node(thoriumWallLarge);
|
||||
node(surgeWall, () -> {
|
||||
node(surgeWallLarge);
|
||||
node(phaseWall, () -> {
|
||||
node(phaseWallLarge);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -196,6 +201,8 @@ public class TechTree implements ContentList{
|
||||
node(liquidRouter, () -> {
|
||||
node(liquidTank);
|
||||
|
||||
node(bridgeConduit);
|
||||
|
||||
node(pulseConduit, () -> {
|
||||
node(phaseConduit, () -> {
|
||||
|
||||
@@ -212,7 +219,6 @@ public class TechTree implements ContentList{
|
||||
});
|
||||
});
|
||||
});
|
||||
node(bridgeConduit);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -337,6 +343,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<>();
|
||||
@@ -346,6 +353,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,97 +1,112 @@
|
||||
package mindustry.content;
|
||||
|
||||
import arc.struct.*;
|
||||
import mindustry.ctype.ContentList;
|
||||
import mindustry.entities.bullet.*;
|
||||
import mindustry.entities.type.*;
|
||||
import mindustry.entities.type.Bullet;
|
||||
import mindustry.entities.type.base.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.ctype.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.type.*;
|
||||
|
||||
public class UnitTypes implements ContentList{
|
||||
//TODO reimplement
|
||||
public static UnitType
|
||||
draug, spirit, phantom,
|
||||
wraith, ghoul, revenant, lich, reaper,
|
||||
dagger, crawler, titan, fortress, eruptor, chaosArray, eradicator;
|
||||
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 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 = 1f;
|
||||
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){{
|
||||
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.01f;
|
||||
speed = 0.42f;
|
||||
maxVelocity = 1.6f;
|
||||
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;
|
||||
@@ -99,28 +114,20 @@ 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;
|
||||
bullet = new BombBulletType(2f, 3f, "clear"){
|
||||
{
|
||||
hitEffect = Fx.pulverize;
|
||||
lifetime = 30f;
|
||||
speed = 1.1f;
|
||||
splashDamageRadius = 55f;
|
||||
splashDamage = 30f;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(Bullet b){
|
||||
if(b.getOwner() instanceof Unit){
|
||||
((Unit)b.getOwner()).kill();
|
||||
}
|
||||
b.time(b.lifetime());
|
||||
}
|
||||
};
|
||||
}};
|
||||
bullet = new BombBulletType(2f, 3f, "clear"){{
|
||||
hitEffect = Fx.pulverize;
|
||||
lifetime = 30f;
|
||||
speed = 1.1f;
|
||||
splashDamageRadius = 55f;
|
||||
instantDisappear = true;
|
||||
splashDamage = 30f;
|
||||
killShooter = true;
|
||||
}};
|
||||
}});
|
||||
}};
|
||||
|
||||
titan = new UnitType("titan", GroundUnit::new){{
|
||||
@@ -133,16 +140,15 @@ 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;
|
||||
range = 30f;
|
||||
alternate = true;
|
||||
recoil = 1f;
|
||||
ejectEffect = Fx.none;
|
||||
bullet = Bullets.basicFlame;
|
||||
}};
|
||||
}});
|
||||
}};
|
||||
|
||||
fortress = new UnitType("fortress", GroundUnit::new){{
|
||||
@@ -154,7 +160,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;
|
||||
@@ -164,7 +170,7 @@ public class UnitTypes implements ContentList{
|
||||
ejectEffect = Fx.shellEjectMedium;
|
||||
bullet = Bullets.artilleryUnit;
|
||||
shootSound = Sounds.artillery;
|
||||
}};
|
||||
}});
|
||||
}};
|
||||
|
||||
eruptor = new UnitType("eruptor", GroundUnit::new){{
|
||||
@@ -177,7 +183,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;
|
||||
@@ -186,7 +192,7 @@ public class UnitTypes implements ContentList{
|
||||
recoil = 1f;
|
||||
width = 7f;
|
||||
shootSound = Sounds.flame;
|
||||
}};
|
||||
}});
|
||||
}};
|
||||
|
||||
chaosArray = new UnitType("chaos-array", GroundUnit::new){{
|
||||
@@ -197,7 +203,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;
|
||||
@@ -210,7 +216,7 @@ public class UnitTypes implements ContentList{
|
||||
ejectEffect = Fx.shellEjectMedium;
|
||||
bullet = Bullets.flakSurge;
|
||||
shootSound = Sounds.shootBig;
|
||||
}};
|
||||
}});
|
||||
}};
|
||||
|
||||
eradicator = new UnitType("eradicator", GroundUnit::new){{
|
||||
@@ -221,7 +227,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;
|
||||
@@ -235,7 +241,7 @@ public class UnitTypes implements ContentList{
|
||||
ejectEffect = Fx.shellEjectMedium;
|
||||
bullet = Bullets.standardThoriumBig;
|
||||
shootSound = Sounds.shootBig;
|
||||
}};
|
||||
}});
|
||||
}};
|
||||
|
||||
wraith = new UnitType("wraith", FlyingUnit::new){{
|
||||
@@ -247,14 +253,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){{
|
||||
@@ -267,7 +273,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;
|
||||
@@ -278,7 +284,7 @@ public class UnitTypes implements ContentList{
|
||||
ignoreRotation = true;
|
||||
bullet = Bullets.bombExplosive;
|
||||
shootSound = Sounds.none;
|
||||
}};
|
||||
}});
|
||||
}};
|
||||
|
||||
revenant = new UnitType("revenant", HoverUnit::new){{
|
||||
@@ -291,13 +297,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;
|
||||
@@ -309,7 +315,7 @@ public class UnitTypes implements ContentList{
|
||||
spacing = 1f;
|
||||
shootSound = Sounds.missile;
|
||||
bullet = Bullets.missileRevenant;
|
||||
}};
|
||||
}});
|
||||
}};
|
||||
|
||||
lich = new UnitType("lich", HoverUnit::new){{
|
||||
@@ -322,13 +328,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;
|
||||
@@ -342,7 +348,7 @@ public class UnitTypes implements ContentList{
|
||||
spacing = 1f;
|
||||
bullet = Bullets.missileRevenant;
|
||||
shootSound = Sounds.artillery;
|
||||
}};
|
||||
}});
|
||||
}};
|
||||
|
||||
reaper = new UnitType("reaper", HoverUnit::new){{
|
||||
@@ -355,12 +361,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;
|
||||
@@ -384,7 +390,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;*/
|
||||
}
|
||||
}
|
||||
|
||||
15
core/src/mindustry/content/Weathers.java
Normal file
15
core/src/mindustry/content/Weathers.java
Normal file
@@ -0,0 +1,15 @@
|
||||
package mindustry.content;
|
||||
|
||||
import mindustry.ctype.*;
|
||||
import mindustry.type.*;
|
||||
|
||||
public class Weathers implements ContentList{
|
||||
public static Weather
|
||||
rain,
|
||||
snow;
|
||||
|
||||
@Override
|
||||
public void load(){
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,14 @@
|
||||
package mindustry.content;
|
||||
|
||||
import mindustry.ctype.ContentList;
|
||||
import mindustry.game.*;
|
||||
import mindustry.ctype.*;
|
||||
import mindustry.game.Objectives.*;
|
||||
import mindustry.game.*;
|
||||
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{
|
||||
@@ -22,7 +21,7 @@ public class Zones implements ContentList{
|
||||
@Override
|
||||
public void load(){
|
||||
|
||||
groundZero = new Zone("groundZero", new MapGenerator("groundZero", 1)){{
|
||||
groundZero = new Zone("groundZero", starter, new FileMapGenerator("groundZero")){{
|
||||
baseLaunchCost = list(copper, -60);
|
||||
startingItems = list(copper, 60);
|
||||
alwaysUnlocked = true;
|
||||
@@ -31,7 +30,8 @@ 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;
|
||||
@@ -82,7 +82,7 @@ public class Zones implements ContentList{
|
||||
);
|
||||
}};
|
||||
|
||||
saltFlats = new Zone("saltFlats", new MapGenerator("saltFlats")){{
|
||||
saltFlats = new Zone("saltFlats", starter, new FileMapGenerator("saltFlats")){{
|
||||
startingItems = list(copper, 200, Items.silicon, 200, lead, 200);
|
||||
loadout = Loadouts.basicFoundation;
|
||||
conditionWave = 10;
|
||||
@@ -98,8 +98,7 @@ public class Zones implements ContentList{
|
||||
);
|
||||
}};
|
||||
|
||||
frozenForest = new Zone("frozenForest", new MapGenerator("frozenForest", 1)
|
||||
.decor(new Decoration(Blocks.snow, Blocks.sporeCluster, 0.02))){{
|
||||
frozenForest = new Zone("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 Zone("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 Zone("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 Zone("stainedMountains", starter, new FileMapGenerator("stainedMountains")){{
|
||||
loadout = Loadouts.basicFoundation;
|
||||
startingItems = list(copper, 200, lead, 50);
|
||||
conditionWave = 10;
|
||||
@@ -153,7 +151,7 @@ public class Zones implements ContentList{
|
||||
);
|
||||
}};
|
||||
|
||||
fungalPass = new Zone("fungalPass", new MapGenerator("fungalPass")){{
|
||||
fungalPass = new Zone("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);
|
||||
@@ -166,7 +164,7 @@ public class Zones implements ContentList{
|
||||
);
|
||||
}};
|
||||
|
||||
overgrowth = new Zone("overgrowth", new MapGenerator("overgrowth")){{
|
||||
overgrowth = new Zone("overgrowth", starter, new FileMapGenerator("overgrowth")){{
|
||||
startingItems = list(copper, 1500, lead, 1000, Items.silicon, 500, Items.metaglass, 250);
|
||||
conditionWave = 12;
|
||||
launchPeriod = 4;
|
||||
@@ -183,8 +181,7 @@ public class Zones implements ContentList{
|
||||
);
|
||||
}};
|
||||
|
||||
tarFields = new Zone("tarFields", new MapGenerator("tarFields")
|
||||
.decor(new Decoration(Blocks.shale, Blocks.shaleBoulder, 0.02))){{
|
||||
tarFields = new Zone("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 Zone("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 Zone("nuclearComplex", starter, new FileMapGenerator("nuclearProductionComplex")){{
|
||||
loadout = Loadouts.basicNucleus;
|
||||
startingItems = list(copper, 1250, lead, 1500, Items.silicon, 400, Items.metaglass, 250);
|
||||
conditionWave = 30;
|
||||
|
||||
@@ -29,21 +29,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(){
|
||||
@@ -135,13 +131,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);
|
||||
Color.rgba8888ToColor(block.color, color);
|
||||
Color.rgba8888ToColor(block.mapColor, color);
|
||||
block.hasColor = true;
|
||||
}
|
||||
}
|
||||
pixmap.dispose();
|
||||
ColorMapper.load();
|
||||
}
|
||||
|
||||
public void dispose(){
|
||||
@@ -234,6 +232,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);
|
||||
}
|
||||
@@ -265,4 +267,8 @@ public class ContentLoader{
|
||||
public Array<UnitType> units(){
|
||||
return getBy(ContentType.unit);
|
||||
}
|
||||
|
||||
public Array<Planet> planets(){
|
||||
return getBy(ContentType.planet);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,26 +3,25 @@ package mindustry.core;
|
||||
import arc.*;
|
||||
import arc.assets.*;
|
||||
import arc.audio.*;
|
||||
import arc.struct.*;
|
||||
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 +62,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);
|
||||
}
|
||||
}));
|
||||
});
|
||||
@@ -105,12 +102,14 @@ 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
|
||||
@@ -118,7 +117,7 @@ public class Control implements ApplicationListener, Loadable{
|
||||
if(state.rules.pvp && !net.active()){
|
||||
try{
|
||||
net.host(port);
|
||||
player.isAdmin = true;
|
||||
player.admin(true);
|
||||
}catch(IOException e){
|
||||
ui.showException("$server.error", e);
|
||||
Core.app.post(() -> 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,22 @@ public class Control implements ApplicationListener, Loadable{
|
||||
});
|
||||
|
||||
Events.on(Trigger.newGame, () -> {
|
||||
TileEntity core = player.getClosestCore();
|
||||
Tilec core = player.closestCore();
|
||||
|
||||
if(core == null) return;
|
||||
|
||||
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 +203,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();
|
||||
@@ -239,7 +236,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 +246,44 @@ 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 remove for persistent sector slots
|
||||
slot = null;
|
||||
if(slot != null){
|
||||
try{
|
||||
net.reset();
|
||||
slot.load();
|
||||
state.rules.sector = sector;
|
||||
state.set(State.playing);
|
||||
}catch(SaveException e){
|
||||
Log.err(e);
|
||||
ui.showErrorMessage("$save.corrupted");
|
||||
slot.delete();
|
||||
playSector(sector);
|
||||
}
|
||||
ui.planet.hide();
|
||||
}else{
|
||||
net.reset();
|
||||
logic.reset();
|
||||
world.loadSector(sector);
|
||||
state.rules.sector = sector;
|
||||
//TODO enable for lighting
|
||||
//state.rules.lighting = true;
|
||||
logic.play();
|
||||
control.saves.saveSector(sector);
|
||||
//TODO uncomment for efffect
|
||||
//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 +291,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 +308,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 +318,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 +334,7 @@ public class Control implements ApplicationListener, Loadable{
|
||||
state.rules.buildCostMultiplier = 0.3f;
|
||||
state.rules.tutorial = true;
|
||||
Events.fire(Trigger.newGame);
|
||||
});
|
||||
});*/
|
||||
}
|
||||
|
||||
public boolean isHighScore(){
|
||||
@@ -433,10 +448,10 @@ public class Control implements ApplicationListener, Loadable{
|
||||
if(!state.is(State.menu)){
|
||||
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,11 +1,13 @@
|
||||
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.type.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
import static mindustry.Vars.net;
|
||||
|
||||
public class GameState{
|
||||
/** Current wave number, can be anything in non-wave modes. */
|
||||
@@ -25,8 +27,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 +36,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;
|
||||
}
|
||||
|
||||
@@ -6,17 +6,14 @@ 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.*;
|
||||
import mindustry.world.blocks.BuildBlock.*;
|
||||
import mindustry.world.blocks.power.*;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
@@ -34,12 +31,9 @@ public class Logic implements ApplicationListener{
|
||||
|
||||
public Logic(){
|
||||
Events.on(WaveEvent.class, event -> {
|
||||
for(Player p : playerGroup.all()){
|
||||
p.respawns = state.rules.respawns;
|
||||
}
|
||||
|
||||
if(world.isZone()){
|
||||
world.getZone().updateWave(state.wave);
|
||||
if(state.isCampaign()){
|
||||
//TODO implement
|
||||
//state.getSector().updateWave(state.wave);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -63,7 +57,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
|
||||
@@ -100,19 +94,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -127,17 +122,15 @@ public class Logic implements ApplicationListener{
|
||||
state.rules = new Rules();
|
||||
state.stats = new Stats();
|
||||
|
||||
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());
|
||||
}
|
||||
@@ -159,7 +152,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{
|
||||
@@ -176,21 +169,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;
|
||||
@@ -210,13 +204,17 @@ public class Logic implements ApplicationListener{
|
||||
@Override
|
||||
public void update(){
|
||||
Events.fire(Trigger.update);
|
||||
universe.updateGlobal();
|
||||
|
||||
if(!state.is(State.menu)){
|
||||
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){
|
||||
@@ -229,36 +227,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,29 +1,24 @@
|
||||
package mindustry.core;
|
||||
|
||||
import arc.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import arc.struct.*;
|
||||
import arc.graphics.*;
|
||||
import arc.math.*;
|
||||
import arc.util.CommandHandler.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import arc.util.CommandHandler.*;
|
||||
import arc.util.io.*;
|
||||
import arc.util.serialization.*;
|
||||
import mindustry.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.core.GameState.*;
|
||||
import mindustry.ctype.ContentType;
|
||||
import mindustry.entities.*;
|
||||
import mindustry.entities.traits.BuilderTrait.*;
|
||||
import mindustry.entities.traits.*;
|
||||
import mindustry.entities.type.*;
|
||||
import mindustry.game.*;
|
||||
import mindustry.entities.units.*;
|
||||
import mindustry.game.EventType.*;
|
||||
import mindustry.game.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.net.Administration.*;
|
||||
import mindustry.net.Net.*;
|
||||
import mindustry.net.*;
|
||||
import mindustry.net.Packets.*;
|
||||
import mindustry.type.TypeID;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.modules.*;
|
||||
|
||||
@@ -61,7 +56,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();
|
||||
|
||||
@@ -76,11 +71,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 = Color.rgba8888(player.color);
|
||||
c.color = Color.rgba8888(player.color());
|
||||
c.usid = getUsid(packet.addressTCP);
|
||||
c.uuid = platform.getUUID();
|
||||
|
||||
@@ -101,6 +96,7 @@ public class NetClient implements ApplicationListener{
|
||||
state.set(State.menu);
|
||||
logic.reset();
|
||||
platform.updateRPC();
|
||||
player.name(Core.settings.getString("name"));
|
||||
|
||||
if(quiet) return;
|
||||
|
||||
@@ -127,21 +123,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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,7 +150,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.");
|
||||
}
|
||||
@@ -171,15 +166,20 @@ public class NetClient implements ApplicationListener{
|
||||
return;
|
||||
}
|
||||
|
||||
//special case; graphical server needs to see its message
|
||||
if(!headless){
|
||||
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){
|
||||
@@ -200,9 +200,9 @@ 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)
|
||||
@@ -215,8 +215,8 @@ public class NetClient implements ApplicationListener{
|
||||
}
|
||||
|
||||
@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)
|
||||
@@ -225,7 +225,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);
|
||||
}
|
||||
@@ -256,11 +256,65 @@ public class NetClient implements ApplicationListener{
|
||||
ui.loadfrag.hide();
|
||||
}
|
||||
|
||||
@Remote(variants = Variant.both, unreliable = true)
|
||||
public static void setHudText(String message){
|
||||
if(message == null) return;
|
||||
|
||||
ui.hudfrag.setHudText(message);
|
||||
}
|
||||
|
||||
@Remote(variants = Variant.both)
|
||||
public static void hideHudText(){
|
||||
ui.hudfrag.toggleHudText(false);
|
||||
}
|
||||
|
||||
/** TCP version */
|
||||
@Remote(variants = Variant.both)
|
||||
public static void setHudTextReliable(String message){
|
||||
setHudText(message);
|
||||
}
|
||||
|
||||
@Remote(variants = Variant.both)
|
||||
public static void onInfoMessage(String message){
|
||||
if(message == null) return;
|
||||
|
||||
ui.showText("", message);
|
||||
}
|
||||
|
||||
@Remote(variants = Variant.both)
|
||||
public static void onInfoPopup(String message, float duration, int align, int top, int left, int bottom, int right){
|
||||
if(message == null) return;
|
||||
|
||||
ui.showInfoPopup(message, duration, align, top, left, bottom, right);
|
||||
}
|
||||
|
||||
@Remote(variants = Variant.both)
|
||||
public static void onLabel(String message, float duration, float worldx, float worldy){
|
||||
if(message == null) return;
|
||||
|
||||
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;
|
||||
|
||||
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){
|
||||
if(message == null) return;
|
||||
|
||||
ui.showInfoToast(message, duration);
|
||||
}
|
||||
|
||||
@Remote(variants = Variant.both)
|
||||
public static void onSetRules(Rules rules){
|
||||
state.rules = rules;
|
||||
@@ -268,11 +322,10 @@ public class NetClient implements ApplicationListener{
|
||||
|
||||
@Remote(variants = Variant.both)
|
||||
public static void onWorldDataBegin(){
|
||||
entities.clear();
|
||||
Groups.all.clear();
|
||||
netClient.removed.clear();
|
||||
logic.reset();
|
||||
|
||||
ui.chatfrag.clearMessages();
|
||||
net.setClientLoaded(false);
|
||||
|
||||
ui.loadfrag.show("$connecting.data");
|
||||
@@ -287,60 +340,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){
|
||||
@@ -361,7 +408,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();
|
||||
@@ -389,9 +436,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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -434,7 +481,8 @@ public class NetClient implements ApplicationListener{
|
||||
connecting = false;
|
||||
ui.join.hide();
|
||||
net.setClientLoaded(true);
|
||||
Core.app.post(Call::connectConfirm);
|
||||
//TODO connect confirm
|
||||
//Core.app.post(Call::connectConfirm);
|
||||
Time.runTask(40f, platform::updateRPC);
|
||||
Core.app.post(() -> ui.loadfrag.hide());
|
||||
}
|
||||
@@ -448,7 +496,7 @@ public class NetClient implements ApplicationListener{
|
||||
quiet = false;
|
||||
lastSent = 0;
|
||||
|
||||
entities.clear();
|
||||
Groups.all.clear();
|
||||
ui.chatfrag.clearMessages();
|
||||
}
|
||||
|
||||
@@ -482,22 +530,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);
|
||||
@@ -509,6 +563,11 @@ public class NetClient implements ApplicationListener{
|
||||
}
|
||||
|
||||
String getUsid(String ip){
|
||||
//consistently use the latter part of an IP, if possible
|
||||
if(ip.contains("/")){
|
||||
ip = ip.substring(ip.indexOf("/") + 1);
|
||||
}
|
||||
|
||||
if(Core.settings.getString("usid-" + ip, null) != null){
|
||||
return Core.settings.getString("usid-" + ip, null);
|
||||
}else{
|
||||
|
||||
@@ -8,14 +8,11 @@ import arc.struct.*;
|
||||
import arc.util.*;
|
||||
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.net.Administration;
|
||||
import mindustry.entities.units.*;
|
||||
import mindustry.game.EventType.*;
|
||||
import mindustry.game.*;
|
||||
import mindustry.game.Teams.*;
|
||||
@@ -48,9 +45,11 @@ public class NetServer implements ApplicationListener{
|
||||
if(state.rules.pvp){
|
||||
//find team with minimum amount of players and auto-assign player to that.
|
||||
TeamData re = state.teams.getActive().min(data -> {
|
||||
if(state.rules.waveTeam == data.team || !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++;
|
||||
}
|
||||
}
|
||||
@@ -65,8 +64,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();
|
||||
@@ -93,6 +92,16 @@ public class NetServer implements ApplicationListener{
|
||||
}
|
||||
|
||||
String uuid = packet.uuid;
|
||||
byte[] buuid = Base64Coder.decode(uuid);
|
||||
CRC32 crc = new CRC32();
|
||||
crc.update(buuid, 0, 8);
|
||||
ByteBuffer buff = ByteBuffer.allocate(8);
|
||||
buff.put(buuid, 8, 8);
|
||||
buff.position(0);
|
||||
if(crc.getValue() != buff.getLong()){
|
||||
con.kick(KickReason.clientOutdated);
|
||||
return;
|
||||
}
|
||||
|
||||
if(admins.isIPBanned(con.address) || admins.isSubnetBanned(con.address)) return;
|
||||
|
||||
@@ -121,7 +130,7 @@ public class NetServer implements ApplicationListener{
|
||||
return;
|
||||
}
|
||||
|
||||
if(admins.getPlayerLimit() > 0 && playerGroup.size() >= admins.getPlayerLimit()){
|
||||
if(admins.getPlayerLimit() > 0 && Groups.player.size() >= admins.getPlayerLimit() && !netServer.admins.isAdmin(uuid, packet.usid)){
|
||||
con.kick(KickReason.playerLimit);
|
||||
return;
|
||||
}
|
||||
@@ -162,16 +171,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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -195,20 +202,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.admin() && !info.admin){
|
||||
info.adminUsid = packet.usid;
|
||||
}
|
||||
|
||||
try{
|
||||
writeBuffer.position(0);
|
||||
writeBuffer.reset();
|
||||
player.write(outputBuffer);
|
||||
}catch(Throwable t){
|
||||
t.printStackTrace();
|
||||
@@ -219,7 +228,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);
|
||||
|
||||
@@ -231,9 +240,16 @@ 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){
|
||||
if(e.getCause() instanceof ValidateException){
|
||||
ValidateException v = (ValidateException)e.getCause();
|
||||
Log.debug("Validation failed for '{0}': {1}", v.player, v.getMessage());
|
||||
}else{
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -246,7 +262,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;
|
||||
@@ -257,7 +273,7 @@ public class NetServer implements ApplicationListener{
|
||||
|
||||
page --;
|
||||
|
||||
if(page > pages || page < 0){
|
||||
if(page >= pages || page < 0){
|
||||
player.sendMessage("[scarlet]'page' must be a number between[orange] 1[] and[orange] " + pages + "[scarlet].");
|
||||
return;
|
||||
}
|
||||
@@ -272,45 +288,49 @@ 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
|
||||
int kickDuration = 15 * 60;
|
||||
int kickDuration = 60 * 60;
|
||||
//voting round duration in seconds
|
||||
float voteDuration = 0.5f * 60;
|
||||
//cooldown between votes
|
||||
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();
|
||||
}
|
||||
}, 60 * 1);
|
||||
}, 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 to kick[orange] {1}[].[accent] ({2}/{3})\n[lightgray]Type[orange] /vote <y/n>[] to agree.",
|
||||
player.name, target.name, votes, votesRequired()));
|
||||
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()));
|
||||
}
|
||||
|
||||
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;
|
||||
@@ -319,24 +339,22 @@ public class NetServer implements ApplicationListener{
|
||||
}
|
||||
}
|
||||
|
||||
//cooldown between votes
|
||||
int voteTime = 60 * 3;
|
||||
Timekeeper vtime = new Timekeeper(voteTime);
|
||||
Timekeeper vtime = new Timekeeper(voteCooldown);
|
||||
//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;
|
||||
}
|
||||
@@ -344,31 +362,30 @@ 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()){
|
||||
player.sendMessage("[scarlet]You must wait " + voteTime/60 + " minutes between votekicks.");
|
||||
player.sendMessage("[scarlet]You must wait " + voteCooldown/60 + " minutes between votekicks.");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -383,17 +400,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;
|
||||
}
|
||||
@@ -414,8 +431,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){
|
||||
@@ -424,69 +441,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());
|
||||
}
|
||||
|
||||
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,
|
||||
boolean boosting, boolean shooting, boolean chatting,
|
||||
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;
|
||||
|
||||
@@ -495,91 +516,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;
|
||||
}
|
||||
|
||||
@@ -588,31 +621,33 @@ 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;
|
||||
if(Config.showConnectMessages.bool()) Call.sendMessage("[accent]" + player.name + "[accent] has connected.");
|
||||
Log.info("&lm[{1}] &y{0} has connected. ", player.name, player.uuid);
|
||||
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());
|
||||
}
|
||||
|
||||
if(!Config.motd.string().equalsIgnoreCase("off")){
|
||||
player.sendMessage(Config.motd.string());
|
||||
@@ -625,7 +660,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++;
|
||||
}
|
||||
}
|
||||
@@ -677,12 +712,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();
|
||||
@@ -700,65 +735,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){
|
||||
@@ -814,24 +837,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();
|
||||
}
|
||||
|
||||
@@ -841,6 +864,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.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,65 +25,27 @@ 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 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);
|
||||
});
|
||||
|
||||
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
|
||||
public void init(){
|
||||
if(settings.getBool("bloom")){
|
||||
@@ -119,21 +72,6 @@ public class Renderer implements ApplicationListener{
|
||||
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 +88,7 @@ public class Renderer implements ApplicationListener{
|
||||
@Override
|
||||
public void dispose(){
|
||||
minimap.dispose();
|
||||
shieldBuffer.dispose();
|
||||
effectBuffer.dispose();
|
||||
blocks.dispose();
|
||||
if(bloom != null){
|
||||
bloom.dispose();
|
||||
@@ -166,6 +104,13 @@ public class Renderer implements ApplicationListener{
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resume(){
|
||||
if(settings.getBool("bloom") && bloom != null){
|
||||
bloom.resume();
|
||||
}
|
||||
}
|
||||
|
||||
void setupBloom(){
|
||||
try{
|
||||
if(bloom != null){
|
||||
@@ -211,26 +156,26 @@ 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());
|
||||
|
||||
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();
|
||||
|
||||
@@ -239,7 +184,9 @@ public class Renderer implements ApplicationListener{
|
||||
blocks.floor.endDraw();
|
||||
|
||||
blocks.drawBlocks(Layer.block);
|
||||
blocks.drawFog();
|
||||
if(state.rules.drawFog){
|
||||
blocks.drawFog();
|
||||
}
|
||||
|
||||
blocks.drawDestroyed();
|
||||
|
||||
@@ -249,57 +196,42 @@ public class Renderer implements ApplicationListener{
|
||||
|
||||
blocks.drawBlocks(Layer.overlay);
|
||||
|
||||
drawGroundShadows();
|
||||
|
||||
drawAllTeams(false);
|
||||
Groups.drawGroundShadows();
|
||||
Groups.drawGroundUnder();
|
||||
Groups.drawGround();
|
||||
|
||||
blocks.drawBlocks(Layer.turret);
|
||||
|
||||
drawFlyerShadows();
|
||||
Groups.drawFlyingShadows();
|
||||
|
||||
blocks.drawBlocks(Layer.power);
|
||||
blocks.drawBlocks(Layer.lights);
|
||||
|
||||
drawAllTeams(true);
|
||||
Groups.drawFlying();
|
||||
|
||||
Draw.flush();
|
||||
if(bloom != null && !pixelator.enabled()){
|
||||
if(bloom != null){
|
||||
bloom.capture();
|
||||
}
|
||||
|
||||
bulletGroup.draw();
|
||||
effectGroup.draw();
|
||||
Groups.drawBullets();
|
||||
Groups.drawEffects();
|
||||
|
||||
Draw.flush();
|
||||
if(bloom != null && !pixelator.enabled()){
|
||||
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);
|
||||
}
|
||||
if(player.isBuilder()){
|
||||
player.builder().drawBuildRequests();
|
||||
}
|
||||
|
||||
overlays.drawTop();
|
||||
|
||||
playerGroup.draw(p -> !p.isDead(), Player::drawName);
|
||||
if(!pixelator.enabled()){
|
||||
Groups.drawNames();
|
||||
}
|
||||
|
||||
if(state.rules.lighting){
|
||||
lights.draw();
|
||||
@@ -311,70 +243,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();
|
||||
@@ -400,7 +297,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;
|
||||
@@ -410,11 +307,6 @@ public class Renderer implements ApplicationListener{
|
||||
return;
|
||||
}
|
||||
|
||||
boolean hadShields = Core.settings.getBool("animatedshields");
|
||||
boolean hadWater = Core.settings.getBool("animatedwater");
|
||||
Core.settings.put("animatedwater", false);
|
||||
Core.settings.put("animatedshields", false);
|
||||
|
||||
FrameBuffer buffer = new FrameBuffer(w, h);
|
||||
|
||||
float vpW = camera.width, vpH = camera.height, px = camera.position.x, py = camera.position.y;
|
||||
@@ -439,16 +331,13 @@ public class Renderer implements ApplicationListener{
|
||||
}
|
||||
buffer.end();
|
||||
Pixmap fullPixmap = new Pixmap(w, h, Pixmap.Format.RGBA8888);
|
||||
BufferUtils.copy(lines, 0, fullPixmap.getPixels(), lines.length);
|
||||
Buffers.copy(lines, 0, fullPixmap.getPixels(), lines.length);
|
||||
Fi file = screenshotDirectory.child("screenshot-" + Time.millis() + ".png");
|
||||
PixmapIO.writePNG(file, fullPixmap);
|
||||
fullPixmap.dispose();
|
||||
ui.showInfoFade(Core.bundle.format("screenshot", file.toString()));
|
||||
|
||||
buffer.dispose();
|
||||
|
||||
Core.settings.put("animatedwater", hadWater);
|
||||
Core.settings.put("animatedshields", hadShields);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,29 +2,23 @@ package mindustry.core;
|
||||
|
||||
import arc.*;
|
||||
import arc.Graphics.*;
|
||||
import arc.Graphics.Cursor.*;
|
||||
import arc.Input.*;
|
||||
import arc.assets.*;
|
||||
import arc.assets.loaders.*;
|
||||
import arc.assets.loaders.resolvers.*;
|
||||
import arc.struct.*;
|
||||
import arc.files.*;
|
||||
import arc.freetype.*;
|
||||
import arc.freetype.FreeTypeFontGenerator.*;
|
||||
import arc.freetype.FreetypeFontLoader.*;
|
||||
import arc.func.*;
|
||||
import arc.graphics.*;
|
||||
import arc.graphics.Texture.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.input.*;
|
||||
import arc.math.*;
|
||||
import arc.math.geom.*;
|
||||
import arc.scene.*;
|
||||
import arc.scene.actions.*;
|
||||
import arc.scene.event.*;
|
||||
import arc.scene.style.*;
|
||||
import arc.scene.ui.*;
|
||||
import arc.scene.ui.TextField.*;
|
||||
import arc.scene.ui.Tooltip.*;
|
||||
import arc.scene.ui.layout.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import mindustry.core.GameState.*;
|
||||
import mindustry.editor.*;
|
||||
@@ -39,6 +33,8 @@ import static arc.scene.actions.Actions.*;
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class UI implements ApplicationListener, Loadable{
|
||||
public static PixmapPacker packer;
|
||||
|
||||
public MenuFragment menufrag;
|
||||
public HudFragment hudfrag;
|
||||
public ChatFragment chatfrag;
|
||||
@@ -67,7 +63,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;
|
||||
@@ -77,7 +73,7 @@ public class UI implements ApplicationListener, Loadable{
|
||||
public Cursor drillCursor, unloadCursor;
|
||||
|
||||
public UI(){
|
||||
setupFonts();
|
||||
Fonts.loadFonts();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -99,6 +95,7 @@ public class UI implements ApplicationListener, Loadable{
|
||||
Icon.load();
|
||||
Styles.load();
|
||||
Tex.loadStyles();
|
||||
Fonts.loadContentIcons();
|
||||
|
||||
Dialog.setShowAction(() -> sequence(alpha(0f), fadeIn(0.1f)));
|
||||
Dialog.setHideAction(() -> sequence(fadeOut(0.1f)));
|
||||
@@ -116,7 +113,9 @@ public class UI implements ApplicationListener, Loadable{
|
||||
Colors.put("unlaunched", Color.valueOf("8982ed"));
|
||||
Colors.put("highlight", Pal.accent.cpy().lerp(Color.white, 0.3f));
|
||||
Colors.put("stat", Pal.stat);
|
||||
loadExtraCursors();
|
||||
|
||||
drillCursor = Core.graphics.newCursor("drill");
|
||||
unloadCursor = Core.graphics.newCursor("unload");
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -124,64 +123,6 @@ public class UI implements ApplicationListener, Loadable{
|
||||
return Array.with(new AssetDescriptor<>(Control.class), new AssetDescriptor<>("outline", BitmapFont.class), new AssetDescriptor<>("default", BitmapFont.class), new AssetDescriptor<>("chat", BitmapFont.class));
|
||||
}
|
||||
|
||||
/** Called from a static context to make the cursor appear immediately upon startup.*/
|
||||
public static void loadSystemCursors(){
|
||||
SystemCursor.arrow.set(Core.graphics.newCursor("cursor"));
|
||||
SystemCursor.hand.set(Core.graphics.newCursor("hand"));
|
||||
SystemCursor.ibeam.set(Core.graphics.newCursor("ibeam"));
|
||||
|
||||
Core.graphics.restoreCursor();
|
||||
}
|
||||
|
||||
/** Called from a static context for use in the loading screen.*/
|
||||
public static void loadDefaultFont(){
|
||||
FileHandleResolver resolver = new InternalFileHandleResolver();
|
||||
Core.assets.setLoader(FreeTypeFontGenerator.class, new FreeTypeFontGeneratorLoader(resolver));
|
||||
Core.assets.setLoader(BitmapFont.class, null, new FreetypeFontLoader(resolver){
|
||||
@Override
|
||||
public BitmapFont loadSync(AssetManager manager, String fileName, Fi file, FreeTypeFontLoaderParameter parameter){
|
||||
if(fileName.equals("outline")){
|
||||
parameter.fontParameters.borderWidth = Scl.scl(2f);
|
||||
parameter.fontParameters.spaceX -= parameter.fontParameters.borderWidth;
|
||||
}
|
||||
parameter.fontParameters.magFilter = TextureFilter.Linear;
|
||||
parameter.fontParameters.minFilter = TextureFilter.Linear;
|
||||
parameter.fontParameters.size = fontParameter().size;
|
||||
return super.loadSync(manager, fileName, file, parameter);
|
||||
}
|
||||
});
|
||||
|
||||
FreeTypeFontParameter param = new FreeTypeFontParameter(){{
|
||||
borderColor = Color.darkGray;
|
||||
incremental = true;
|
||||
}};
|
||||
|
||||
Core.assets.load("outline", BitmapFont.class, new FreeTypeFontLoaderParameter("fonts/font.ttf", param)).loaded = t -> Fonts.outline = (BitmapFont)t;
|
||||
}
|
||||
|
||||
void loadExtraCursors(){
|
||||
drillCursor = Core.graphics.newCursor("drill");
|
||||
unloadCursor = Core.graphics.newCursor("unload");
|
||||
}
|
||||
|
||||
public void setupFonts(){
|
||||
String fontName = "fonts/font.ttf";
|
||||
|
||||
FreeTypeFontParameter param = fontParameter();
|
||||
|
||||
Core.assets.load("default", BitmapFont.class, new FreeTypeFontLoaderParameter(fontName, param)).loaded = f -> Fonts.def = (BitmapFont)f;
|
||||
Core.assets.load("chat", BitmapFont.class, new FreeTypeFontLoaderParameter(fontName, param)).loaded = f -> Fonts.chat = (BitmapFont)f;
|
||||
}
|
||||
|
||||
static FreeTypeFontParameter fontParameter(){
|
||||
return new FreeTypeFontParameter(){{
|
||||
size = (int)(Scl.scl(18f));
|
||||
shadowColor = Color.darkGray;
|
||||
shadowOffsetY = 2;
|
||||
incremental = true;
|
||||
}};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(){
|
||||
if(disableUI || Core.scene == null) return;
|
||||
@@ -235,7 +176,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();
|
||||
@@ -271,7 +212,17 @@ public class UI implements ApplicationListener, Loadable{
|
||||
|
||||
@Override
|
||||
public void dispose(){
|
||||
//generator.dispose();
|
||||
if(packer != null){
|
||||
packer.dispose();
|
||||
packer = null;
|
||||
}
|
||||
}
|
||||
|
||||
public TextureRegionDrawable getIcon(String name){
|
||||
if(Icon.icons.containsKey(name)){
|
||||
return Icon.icons.get(name);
|
||||
}
|
||||
return Core.atlas.getDrawable("error");
|
||||
}
|
||||
|
||||
public void loadAnd(Runnable call){
|
||||
@@ -339,6 +290,49 @@ public class UI implements ApplicationListener, Loadable{
|
||||
Core.scene.add(table);
|
||||
}
|
||||
|
||||
/** Shows a fading label at the top of the screen. */
|
||||
public void showInfoToast(String info, float duration){
|
||||
Table table = new Table();
|
||||
table.setFillParent(true);
|
||||
table.touchable(Touchable.disabled);
|
||||
table.update(() -> {
|
||||
if(state.is(State.menu)) 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);
|
||||
Core.scene.add(table);
|
||||
}
|
||||
|
||||
/** Shows a label at some position on the screen. Does not fade. */
|
||||
public void showInfoPopup(String info, float duration, int align, int top, int left, int bottom, int right){
|
||||
Table table = new Table();
|
||||
table.setFillParent(true);
|
||||
table.touchable(Touchable.disabled);
|
||||
table.update(() -> {
|
||||
if(state.is(State.menu)) 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);
|
||||
Core.scene.add(table);
|
||||
}
|
||||
|
||||
/** Shows a label in the world. This label is behind everything. Does not fade. */
|
||||
public void showLabel(String info, float duration, float worldx, float worldy){
|
||||
Table table = new Table();
|
||||
table.setFillParent(true);
|
||||
table.touchable(Touchable.disabled);
|
||||
table.update(() -> {
|
||||
if(state.is(State.menu)) 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 -> {
|
||||
Vec2 v = Core.camera.project(worldx, worldy);
|
||||
t.setPosition(v.x, v.y, Align.center);
|
||||
});
|
||||
//make sure it's at the back
|
||||
Core.scene.root.addChildAt(0, table);
|
||||
}
|
||||
|
||||
public void showInfo(String info){
|
||||
new Dialog(""){{
|
||||
getCell(cont).growX();
|
||||
@@ -476,7 +470,6 @@ public class UI implements ApplicationListener, Loadable{
|
||||
dialog.show();
|
||||
}
|
||||
|
||||
|
||||
public void showCustomConfirm(String title, String text, String yes, String no, Runnable confirmed, Runnable denied){
|
||||
FloatingDialog dialog = new FloatingDialog(title);
|
||||
dialog.cont.add(text).width(mobile ? 400f : 500f).wrap().pad(4f).get().setAlignment(Align.center, Align.center);
|
||||
@@ -509,11 +502,11 @@ public class UI implements ApplicationListener, Loadable{
|
||||
|
||||
public String formatAmount(int number){
|
||||
if(number >= 1000000){
|
||||
return Strings.fixed(number / 1000000f, 1) + "[gray]" + Core.bundle.getOrNull("unit.millions") + "[]";
|
||||
return Strings.fixed(number / 1000000f, 1) + "[gray]" + Core.bundle.get("unit.millions") + "[]";
|
||||
}else if(number >= 10000){
|
||||
return number / 1000 + "[gray]k[]";
|
||||
return number / 1000 + "[gray]" + Core.bundle.get("unit.thousands") + "[]";
|
||||
}else if(number >= 1000){
|
||||
return Strings.fixed(number / 1000f, 1) + "[gray]" + Core.bundle.getOrNull("unit.thousands") + "[]";
|
||||
return Strings.fixed(number / 1000f, 1) + "[gray]" + Core.bundle.get("unit.thousands") + "[]";
|
||||
}else{
|
||||
return number + "";
|
||||
}
|
||||
|
||||
@@ -1,20 +1,22 @@
|
||||
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.*;
|
||||
@@ -25,7 +27,7 @@ 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;
|
||||
|
||||
@@ -67,11 +69,11 @@ public class World{
|
||||
}
|
||||
|
||||
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(){
|
||||
@@ -88,11 +90,7 @@ public class World{
|
||||
}
|
||||
|
||||
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];
|
||||
return tiles.get(x, y);
|
||||
}
|
||||
|
||||
public @Nullable Tile ltile(int x, int y){
|
||||
@@ -101,8 +99,9 @@ public class World{
|
||||
return tile.block().linked(tile);
|
||||
}
|
||||
|
||||
@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){
|
||||
@@ -117,16 +116,10 @@ public class World{
|
||||
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 +128,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;
|
||||
@@ -164,14 +153,11 @@ public class World{
|
||||
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){
|
||||
tile.updateOcclusion();
|
||||
|
||||
if(tile.entity != null){
|
||||
tile.entity.updateProximity();
|
||||
}
|
||||
if(tile.entity != null){
|
||||
tile.entity.updateProximity();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -179,7 +165,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 +179,21 @@ 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.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());
|
||||
}
|
||||
@@ -297,60 +281,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(Tile tile : tiles){
|
||||
int idx = tile.y * tiles.width + tile.x;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public float getDarkness(int x, int y){
|
||||
int edgeBlend = 2;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
//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);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
if(full) tiles[x][y].rotation(5);
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -359,18 +382,13 @@ public class World{
|
||||
* - updating occlusion<br>
|
||||
* Usually used before placing structures on a tile array.
|
||||
*/
|
||||
public void prepareTiles(Tile[][] tiles){
|
||||
public void prepareTiles(Tiles tiles){
|
||||
|
||||
//find multiblocks
|
||||
IntArray multiblocks = new IntArray();
|
||||
|
||||
for(int x = 0; x < tiles.length; x++){
|
||||
for(int y = 0; y < tiles[0].length; y++){
|
||||
Tile tile = tiles[x][y];
|
||||
|
||||
if(tile.block().isMultiblock()){
|
||||
multiblocks.add(tile.pos());
|
||||
}
|
||||
for(Tile tile : tiles){
|
||||
if(tile.block().isMultiblock()){
|
||||
multiblocks.add(tile.pos());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -380,9 +398,10 @@ public class World{
|
||||
|
||||
int x = Pos.x(pos);
|
||||
int y = Pos.y(pos);
|
||||
Tile tile = tiles.getn(x, y);
|
||||
|
||||
Block result = tiles[x][y].block();
|
||||
Team team = tiles[x][y].getTeam();
|
||||
Block result = tile.block();
|
||||
Team team = tile.team();
|
||||
|
||||
int offsetx = -(result.size - 1) / 2;
|
||||
int offsety = -(result.size - 1) / 2;
|
||||
@@ -409,17 +428,19 @@ public class World{
|
||||
private class Context implements WorldContext{
|
||||
@Override
|
||||
public Tile tile(int x, int y){
|
||||
return tiles[x][y];
|
||||
return tiles.get(x, y);
|
||||
}
|
||||
|
||||
@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 +475,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();
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package mindustry.ctype;
|
||||
|
||||
import arc.*;
|
||||
import arc.util.ArcAnnotate.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.scene.ui.layout.*;
|
||||
@@ -10,10 +11,10 @@ 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 String description;
|
||||
public @Nullable String description;
|
||||
/** Icons by Cicon ID.*/
|
||||
protected TextureRegion[] cicons = new TextureRegion[mindustry.ui.Cicon.all.length];
|
||||
|
||||
@@ -24,6 +25,10 @@ public abstract class UnlockableContent extends MappableContent{
|
||||
this.description = Core.bundle.getOrNull(getContentType() + "." + this.name + ".description");
|
||||
}
|
||||
|
||||
public String displayDescription(){
|
||||
return minfo.mod == null ? description : description + "\n" + Core.bundle.format("mod.display", minfo.mod.meta.displayName());
|
||||
}
|
||||
|
||||
/** Generate any special icons for this content. Called asynchronously.*/
|
||||
@CallSuper
|
||||
public void createIcons(MultiPacker packer){
|
||||
@@ -49,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;
|
||||
}
|
||||
@@ -65,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(){
|
||||
|
||||
@@ -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,5 +1,6 @@
|
||||
package mindustry.editor;
|
||||
|
||||
import arc.util.ArcAnnotate.*;
|
||||
import mindustry.content.Blocks;
|
||||
import mindustry.core.GameState.State;
|
||||
import mindustry.editor.DrawOperation.OpType;
|
||||
@@ -13,7 +14,6 @@ import mindustry.world.modules.*;
|
||||
import static mindustry.Vars.state;
|
||||
import static mindustry.Vars.ui;
|
||||
|
||||
//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,7 +21,7 @@ public class EditorTile extends Tile{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFloor(Floor type){
|
||||
public void setFloor(@NonNull Floor type){
|
||||
if(state.is(State.playing)){
|
||||
super.setFloor(type);
|
||||
return;
|
||||
@@ -140,10 +140,10 @@ public class EditorTile extends Tile{
|
||||
|
||||
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.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());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -138,7 +138,7 @@ public enum EditorTool{
|
||||
|
||||
//only fill synthetic blocks, it's meaningless otherwise
|
||||
if(tile.link().synthetic()){
|
||||
Team dest = tile.getTeam();
|
||||
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));
|
||||
}
|
||||
@@ -166,33 +166,42 @@ public enum EditorTool{
|
||||
stack.clear();
|
||||
stack.add(Pos.get(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 = Pos.x(popped);
|
||||
y = Pos.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(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++;
|
||||
}
|
||||
}
|
||||
stack.clear();
|
||||
}catch(OutOfMemoryError e){
|
||||
//hack
|
||||
stack = null;
|
||||
System.gc();
|
||||
e.printStackTrace();
|
||||
stack = new IntArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,30 +64,28 @@ 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
|
||||
//TODO remove, may not be necessary with blockpart refactor later
|
||||
public void checkLinkedTiles(){
|
||||
Tile[][] tiles = world.getTiles();
|
||||
Tiles tiles = world.tiles;
|
||||
|
||||
//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);
|
||||
}
|
||||
//clear old parts
|
||||
for(Tile tile : tiles){
|
||||
if(tile.block() instanceof BlockPart){
|
||||
tile.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());
|
||||
}
|
||||
//re-add them
|
||||
for(Tile tile : tiles){
|
||||
if(tile.block().isMultiblock()){
|
||||
tile.set(tile.block(), tile.team());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,11 +97,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 +117,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){
|
||||
@@ -245,20 +243,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));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -314,12 +312,14 @@ public class MapEditor{
|
||||
|
||||
@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
|
||||
@@ -337,4 +337,4 @@ public class MapEditor{
|
||||
world.endMapLoad();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,89 +66,69 @@ public class MapEditorDialog extends Dialog implements Disposable{
|
||||
menu.cont.table(t -> {
|
||||
t.defaults().size(swidth, 60f).padBottom(5).padRight(5).padLeft(5);
|
||||
|
||||
t.addImageTextButton("$editor.savemap", Icon.floppy16Small, this::save);
|
||||
t.addImageTextButton("$editor.savemap", Icon.save, this::save);
|
||||
|
||||
t.addImageTextButton("$editor.mapinfo", Icon.pencilSmall, () -> {
|
||||
t.addImageTextButton("$editor.mapinfo", Icon.pencil, () -> {
|
||||
infoDialog.show();
|
||||
menu.hide();
|
||||
});
|
||||
|
||||
t.row();
|
||||
|
||||
t.addImageTextButton("$editor.generate", Icon.editorSmall, () -> {
|
||||
t.addImageTextButton("$editor.generate", Icon.terrain, () -> {
|
||||
generateDialog.show(generateDialog::applyToEditor);
|
||||
menu.hide();
|
||||
});
|
||||
|
||||
t.addImageTextButton("$editor.resize", Icon.resizeSmall, () -> {
|
||||
t.addImageTextButton("$editor.resize", Icon.resize, () -> {
|
||||
resizeDialog.show();
|
||||
menu.hide();
|
||||
});
|
||||
|
||||
t.row();
|
||||
|
||||
t.addImageTextButton("$editor.import", Icon.loadMapSmall, () ->
|
||||
createDialog("$editor.import",
|
||||
"$editor.importmap", "$editor.importmap.description", Icon.loadMap, (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.saveMapSmall, () -> {
|
||||
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();
|
||||
|
||||
if(steam){
|
||||
menu.cont.addImageTextButton("$editor.publish.workshop", Icon.linkSmall, () -> {
|
||||
menu.cont.addImageTextButton("$editor.publish.workshop", Icon.link, () -> {
|
||||
Map builtin = maps.all().find(m -> m.name().equals(editor.getTags().get("name", "").trim()));
|
||||
|
||||
if(editor.getTags().containsKey("steamid") && builtin != null && !builtin.custom){
|
||||
@@ -176,16 +156,16 @@ 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();
|
||||
}
|
||||
|
||||
menu.cont.addImageTextButton("$editor.ingame", Icon.arrowSmall, this::playtest).padTop(!steam ? -3 : 1).size(swidth * 2f + 10, 60f);
|
||||
menu.cont.addImageTextButton("$editor.ingame", Icon.right, this::playtest).padTop(!steam ? -3 : 1).size(swidth * 2f + 10, 60f);
|
||||
|
||||
menu.cont.row();
|
||||
|
||||
menu.cont.addImageTextButton("$quit", Icon.backSmall, () -> {
|
||||
menu.cont.addImageTextButton("$quit", Icon.exit, () -> {
|
||||
tryExit();
|
||||
menu.hide();
|
||||
}).size(swidth * 2f + 10, 60f);
|
||||
@@ -266,7 +246,7 @@ public class MapEditorDialog extends Dialog implements Disposable{
|
||||
state.teams = new Teams();
|
||||
player.reset();
|
||||
state.rules = Gamemode.editor.apply(lastSavedRules.copy());
|
||||
state.rules.zone = null;
|
||||
state.rules.sector = null;
|
||||
world.setMap(new Map(StringMap.of(
|
||||
"name", "Editor Playtesting",
|
||||
"width", editor.width(),
|
||||
@@ -274,16 +254,14 @@ public class MapEditorDialog extends Dialog implements Disposable{
|
||||
)));
|
||||
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);
|
||||
//TODO figure out how to kill player
|
||||
//player.dead(false);
|
||||
logic.play();
|
||||
});
|
||||
}
|
||||
@@ -295,7 +273,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;
|
||||
|
||||
@@ -427,7 +406,7 @@ public class MapEditorDialog extends Dialog implements Disposable{
|
||||
|
||||
Cons<EditorTool> addTool = tool -> {
|
||||
|
||||
ImageButton button = new ImageButton(Core.atlas.drawable("icon-" + tool.name() + "-small"), Styles.clearTogglei);
|
||||
ImageButton button = new ImageButton(ui.getIcon(tool.name()), Styles.clearTogglei);
|
||||
button.clicked(() -> {
|
||||
view.setTool(tool);
|
||||
if(lastTable[0] != null){
|
||||
@@ -503,16 +482,16 @@ public class MapEditorDialog extends Dialog implements Disposable{
|
||||
|
||||
tools.defaults().size(size, size);
|
||||
|
||||
tools.addImageButton(Icon.menuLargeSmall, Styles.cleari, menu::show);
|
||||
tools.addImageButton(Icon.menu, Styles.cleari, menu::show);
|
||||
|
||||
ImageButton grid = tools.addImageButton(Icon.gridSmall, Styles.clearTogglei, () -> view.setGrid(!view.isGrid())).get();
|
||||
ImageButton grid = tools.addImageButton(Icon.grid, Styles.clearTogglei, () -> view.setGrid(!view.isGrid())).get();
|
||||
|
||||
addTool.get(EditorTool.zoom);
|
||||
|
||||
tools.row();
|
||||
|
||||
ImageButton undo = tools.addImageButton(Icon.undoSmall, Styles.cleari, editor::undo).get();
|
||||
ImageButton redo = tools.addImageButton(Icon.redoSmall, Styles.cleari, editor::redo).get();
|
||||
ImageButton undo = tools.addImageButton(Icon.undo, Styles.cleari, editor::undo).get();
|
||||
ImageButton redo = tools.addImageButton(Icon.redo, Styles.cleari, editor::redo).get();
|
||||
|
||||
addTool.get(EditorTool.pick);
|
||||
|
||||
@@ -534,7 +513,7 @@ public class MapEditorDialog extends Dialog implements Disposable{
|
||||
addTool.get(EditorTool.fill);
|
||||
addTool.get(EditorTool.spray);
|
||||
|
||||
ImageButton rotate = tools.addImageButton(Icon.arrow16Small, Styles.cleari, () -> editor.rotation = (editor.rotation + 1) % 4).get();
|
||||
ImageButton rotate = tools.addImageButton(Icon.right, Styles.cleari, () -> editor.rotation = (editor.rotation + 1) % 4).get();
|
||||
rotate.getImage().update(() -> {
|
||||
rotate.getImage().setRotation(editor.rotation * 90);
|
||||
rotate.getImage().setOrigin(Align.center);
|
||||
|
||||
@@ -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;
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -235,24 +235,24 @@ public class MapGenerateDialog extends FloatingDialog{
|
||||
t.table(b -> {
|
||||
ImageButtonStyle style = Styles.cleari;
|
||||
b.defaults().size(50f);
|
||||
b.addImageButton(Icon.refreshSmall, style, () -> {
|
||||
b.addImageButton(Icon.refresh, style, () -> {
|
||||
filter.randomize();
|
||||
update();
|
||||
});
|
||||
|
||||
b.addImageButton(Icon.arrowUpSmall, style, () -> {
|
||||
b.addImageButton(Icon.upOpen, style, () -> {
|
||||
int idx = filters.indexOf(filter);
|
||||
filters.swap(idx, Math.max(0, idx - 1));
|
||||
rebuildFilters();
|
||||
update();
|
||||
});
|
||||
b.addImageButton(Icon.arrowDownSmall, style, () -> {
|
||||
b.addImageButton(Icon.downOpen, style, () -> {
|
||||
int idx = filters.indexOf(filter);
|
||||
filters.swap(idx, Math.min(filters.size - 1, idx + 1));
|
||||
rebuildFilters();
|
||||
update();
|
||||
});
|
||||
b.addImageButton(Icon.trashSmall, style, () -> {
|
||||
b.addImageButton(Icon.trash, style, () -> {
|
||||
filters.remove(filter);
|
||||
rebuildFilters();
|
||||
update();
|
||||
@@ -263,7 +263,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 +292,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 +360,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 +424,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;
|
||||
}
|
||||
|
||||
|
||||
@@ -109,9 +109,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();
|
||||
|
||||
|
||||
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,7 +79,7 @@ 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) -> {
|
||||
@@ -91,7 +87,7 @@ public class Damage{
|
||||
if(tile != null && !collidedBlocks.contains(tile.pos()) && tile.entity != null && tile.getTeamID() != team.id && tile.entity.collide(hitter)){
|
||||
tile.entity.collision(hitter);
|
||||
collidedBlocks.add(tile.pos());
|
||||
hitter.getBulletType().hit(hitter, tile.worldx(), tile.worldy());
|
||||
hitter.type().hit(hitter, tile.worldx(), tile.worldy());
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
@@ -236,9 +232,9 @@ public class Damage{
|
||||
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){
|
||||
if(tile.entity != null && tile.team() != team){
|
||||
int health = (int)tile.entity.health();
|
||||
if(tile.entity.health() > 0){
|
||||
tile.entity.damage(scaledDamage);
|
||||
scaledDamage -= health;
|
||||
|
||||
@@ -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,91 +1,25 @@
|
||||
package mindustry.entities;
|
||||
|
||||
import arc.Core;
|
||||
import arc.struct.Array;
|
||||
import arc.func.Cons;
|
||||
import arc.graphics.Color;
|
||||
import arc.*;
|
||||
import arc.graphics.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.math.Mathf;
|
||||
import arc.math.geom.Position;
|
||||
import arc.util.pooling.Pools;
|
||||
import mindustry.entities.type.EffectEntity;
|
||||
import mindustry.entities.traits.ScaleTrait;
|
||||
import arc.math.*;
|
||||
import arc.math.geom.*;
|
||||
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){
|
||||
@@ -101,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 ScaleTrait{
|
||||
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.removeValue(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.removeValue(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();
|
||||
}
|
||||
}
|
||||
|
||||
69
core/src/mindustry/entities/Fires.java
Normal file
69
core/src/mindustry/entities/Fires.java
Normal file
@@ -0,0 +1,69 @@
|
||||
package mindustry.entities;
|
||||
|
||||
import arc.*;
|
||||
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(Pos.get(x, y));
|
||||
}
|
||||
|
||||
public static boolean has(int x, int y){
|
||||
if(!Structs.inBounds(x, y, world.width(), world.height()) || !map.containsKey(Pos.get(x, y))){
|
||||
return false;
|
||||
}
|
||||
Firec fire = map.get(Pos.get(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.ltile(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,36 +11,36 @@ 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, Tile tile){
|
||||
return player == null || tile == null || tile.interactable(player.team());
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates a target.
|
||||
* @param target The target to validate
|
||||
* @param team The team of the thing doing tha targeting
|
||||
* @param x The X position of the thing doign the targeting
|
||||
* @param y The Y position of the thing doign the targeting
|
||||
* @param x The X position of the thing doing the targeting
|
||||
* @param y The Y position of the thing doing the targeting
|
||||
* @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<Tile> 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<Tile> 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<Tile> 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,14 +2,13 @@ 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.Content;
|
||||
import mindustry.ctype.ContentType;
|
||||
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.*;
|
||||
@@ -39,7 +38,11 @@ public abstract class BulletType extends Content{
|
||||
public float reloadMultiplier = 1f;
|
||||
/** Recoil from shooter entities. */
|
||||
public float recoil;
|
||||
|
||||
/** Whether to kill the shooter when this is shot. For suicide bombers. */
|
||||
public boolean killShooter;
|
||||
/** Whether to instantly make the bullet disappear. */
|
||||
public boolean instantDisappear;
|
||||
/** Damage dealt in splash. 0 to disable.*/
|
||||
public float splashDamage = 0f;
|
||||
/** Knockback in velocity. */
|
||||
public float knockback;
|
||||
@@ -53,8 +56,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. */
|
||||
@@ -94,20 +97,20 @@ public abstract class BulletType extends Content{
|
||||
return speed * lifetime * (1f - drag);
|
||||
}
|
||||
|
||||
public boolean collides(Bullet bullet, Tile tile){
|
||||
public boolean collides(Bulletc bullet, Tile tile){
|
||||
return true;
|
||||
}
|
||||
|
||||
public void hitTile(Bullet b, Tile tile){
|
||||
public void hitTile(Bulletc b, Tile 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);
|
||||
@@ -116,7 +119,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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -125,12 +128,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){
|
||||
@@ -138,22 +141,28 @@ 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){
|
||||
public void init(Bulletc b){
|
||||
if(killShooter && b.owner() instanceof Healthc){
|
||||
((Healthc)b.owner()).kill();
|
||||
}
|
||||
|
||||
if(instantDisappear){
|
||||
b.time(lifetime);
|
||||
}
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -162,4 +171,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,15 @@ 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 +28,28 @@ 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, Tile tile){
|
||||
return tile.team() != b.team() || tile.entity.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, Tile 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.entity != null && tile.team() == b.team() && !(tile.block() instanceof BuildBlock)){
|
||||
Fx.healBlockFull.at(tile.drawx(), tile.drawy(), tile.block().size, Pal.heal);
|
||||
tile.entity.heal(healPercent / 100f * tile.entity.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();
|
||||
}
|
||||
}
|
||||
}
|
||||
233
core/src/mindustry/entities/def/BuilderComp.java
Normal file
233
core/src/mindustry/entities/def/BuilderComp.java
Normal file
@@ -0,0 +1,233 @@
|
||||
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[] tmptr = 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(!(tile.block() instanceof BuildBlock)){
|
||||
if(!current.initialized && !current.breaking && Build.validPlace(team(), current.x, current.y, current.block, current.rotation)){
|
||||
Build.beginPlace(team(), current.x, current.y, current.block, current.rotation);
|
||||
}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(entity == null){
|
||||
return;
|
||||
}
|
||||
|
||||
if(dst(tile) <= finalPlaceDst){
|
||||
rotation = Mathf.slerpDelta(rotation, angleTo(entity), 0.4f);
|
||||
}
|
||||
|
||||
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, 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 || !request.initialized || core == null) return false;
|
||||
return request.stuck && !core.items().has(request.block.requirements);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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 * tile.block().size / 2f;
|
||||
float ang = angleTo(tile);
|
||||
|
||||
tmptr[0].set(tile.drawx() - sz, tile.drawy() - sz);
|
||||
tmptr[1].set(tile.drawx() + sz, tile.drawy() - sz);
|
||||
tmptr[2].set(tile.drawx() - sz, tile.drawy() + sz);
|
||||
tmptr[3].set(tile.drawx() + sz, tile.drawy() + sz);
|
||||
|
||||
Arrays.sort(tmptr, Structs.comparingFloat(vec -> -Angles.angleDist(angleTo(vec), ang)));
|
||||
|
||||
float x1 = tmptr[0].x, y1 = tmptr[0].y,
|
||||
x3 = tmptr[1].x, y3 = tmptr[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();
|
||||
}
|
||||
}
|
||||
131
core/src/mindustry/entities/def/BulletComp.java
Normal file
131
core/src/mindustry/entities/def/BulletComp.java
Normal file
@@ -0,0 +1,131 @@
|
||||
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 mindustry.world.*;
|
||||
|
||||
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) -> {
|
||||
|
||||
Tile tile = world.ltile(x, y);
|
||||
if(tile == null) return false;
|
||||
|
||||
if(tile.entity != null && tile.entity.collide(this) && type.collides(this, tile) && !tile.entity.dead() && (type.collidesTeam || tile.team() != team())){
|
||||
if(tile.team() != team()){
|
||||
tile.entity.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.link().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.block().getFlammability(tile);
|
||||
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);
|
||||
}
|
||||
}
|
||||
90
core/src/mindustry/entities/def/FlyingComp.java
Normal file
90
core/src/mindustry/entities/def/FlyingComp.java
Normal file
@@ -0,0 +1,90 @@
|
||||
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.*;
|
||||
|
||||
import static mindustry.Vars.net;
|
||||
|
||||
@Component
|
||||
abstract class FlyingComp implements Posc, Velc, Healthc, Hitboxc{
|
||||
@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){
|
||||
moveAt(vector, 1f);
|
||||
}
|
||||
|
||||
void moveAt(Vec2 vector, float acceleration){
|
||||
Vec2 t = Tmp.v3.set(vector).scl(floorSpeedMultiplier()); //target vector
|
||||
Tmp.v1.set(t).sub(vel).limit(acceleration * vector.len()); //delta vector
|
||||
vel.add(Tmp.v1);
|
||||
|
||||
//float mag = Tmp.v3.len() * acceleration;
|
||||
//vel.lerp(t, Tmp.v3.len() * acceleration);
|
||||
//vel.x = Mathf.approach(vel.x, t.x, mag);
|
||||
//vel.y = Mathf.approach(vel.y, t.y, mag);
|
||||
}
|
||||
|
||||
float floorSpeedMultiplier(){
|
||||
Floor on = isFlying() ? Blocks.air.asFloor() : floorOn();
|
||||
return on.speedMultiplier;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(){
|
||||
Floor floor = floorOn();
|
||||
|
||||
if(isFlying() && !net.client()){
|
||||
wobble();
|
||||
}
|
||||
|
||||
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.tile().block().acceptStack(item(), stack().amount, core.tile(), 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.tile().block().acceptStack(item, 1, core.tile(), 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;
|
||||
}
|
||||
207
core/src/mindustry/entities/def/PlayerComp.java
Normal file
207
core/src/mindustry/entities/def/PlayerComp.java
Normal file
@@ -0,0 +1,207 @@
|
||||
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.*;
|
||||
import mindustry.net.Administration.*;
|
||||
import mindustry.net.Packets.*;
|
||||
import mindustry.ui.*;
|
||||
|
||||
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 Tilec 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();
|
||||
}
|
||||
|
||||
if(!dead()){
|
||||
x(unit.x());
|
||||
y(unit.y());
|
||||
unit.team(team);
|
||||
}
|
||||
textFadeTime -= Time.delta() / (60 * 5);
|
||||
}
|
||||
|
||||
public void team(Team team){
|
||||
this.team = team;
|
||||
if(unit != null){
|
||||
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.*;
|
||||
|
||||
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.link().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.*;
|
||||
|
||||
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,15 +149,4 @@ public class Statuses implements Saveable{
|
||||
statuses.add(entry);
|
||||
}
|
||||
}
|
||||
|
||||
public static class StatusEntry{
|
||||
public StatusEffect effect;
|
||||
public float time;
|
||||
|
||||
public StatusEntry set(StatusEffect effect, float time){
|
||||
this.effect = effect;
|
||||
this.time = time;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
20
core/src/mindustry/entities/def/TeamComp.java
Normal file
20
core/src/mindustry/entities/def/TeamComp.java
Normal file
@@ -0,0 +1,20 @@
|
||||
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.sharded;
|
||||
|
||||
public @Nullable
|
||||
Tilec closestCore(){
|
||||
return state.teams.closestCore(x, y, team);
|
||||
}
|
||||
}
|
||||
285
core/src/mindustry/entities/def/TileComp.java
Normal file
285
core/src/mindustry/entities/def/TileComp.java
Normal file
@@ -0,0 +1,285 @@
|
||||
package mindustry.entities.def;
|
||||
|
||||
import arc.*;
|
||||
import arc.math.geom.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.ArcAnnotate.*;
|
||||
import arc.util.*;
|
||||
import arc.util.io.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.game.EventType.*;
|
||||
import mindustry.game.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.consumers.*;
|
||||
import mindustry.world.modules.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
@EntityDef(value = {Tilec.class}, isFinal = false, genio = false, serialize = false)
|
||||
@Component
|
||||
abstract class TileComp implements Posc, Teamc, Healthc, Tilec, Timerc{
|
||||
static final float timeToSleep = 60f * 1;
|
||||
static final ObjectSet<Tile> tmpTiles = new ObjectSet<>();
|
||||
static int sleepingEntities = 0;
|
||||
|
||||
transient Tile tile;
|
||||
transient Block block;
|
||||
transient Array<Tile> proximity = new Array<>(8);
|
||||
|
||||
PowerModule power;
|
||||
ItemModule items;
|
||||
LiquidModule liquids;
|
||||
ConsumeModule cons;
|
||||
|
||||
private transient float timeScale = 1f, timeScaleDuration;
|
||||
|
||||
private transient @Nullable SoundLoop sound;
|
||||
|
||||
private transient boolean sleeping;
|
||||
private transient float sleepTime;
|
||||
|
||||
/** Sets this tile entity data to this tile, and adds it if necessary. */
|
||||
@Override
|
||||
public Tilec init(Tile tile, boolean shouldAdd){
|
||||
this.tile = tile;
|
||||
this.block = tile.block();
|
||||
|
||||
set(tile.drawx(), tile.drawy());
|
||||
if(block.activeSound != Sounds.none){
|
||||
sound = new SoundLoop(block.activeSound, block.activeSoundVolume);
|
||||
}
|
||||
|
||||
health(block.health);
|
||||
maxHealth(block.health);
|
||||
timer(new Interval(block.timers));
|
||||
|
||||
if(shouldAdd){
|
||||
add();
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public final void writeBase(Writes write){
|
||||
write.f(health());
|
||||
write.b(tile.rotation());
|
||||
write.b(tile.getTeamID());
|
||||
if(items != null) items.write(write);
|
||||
if(power != null) power.write(write);
|
||||
if(liquids != null) liquids.write(write);
|
||||
if(cons != null) cons.write(write);
|
||||
}
|
||||
|
||||
public final void readBase(Reads read){
|
||||
health(read.f());
|
||||
tile.rotation(read.b());
|
||||
tile.setTeam(Team.get(read.b()));
|
||||
if(items != null) items.read(read);
|
||||
if(power != null) power.read(read);
|
||||
if(liquids != null) liquids.read(read);
|
||||
if(cons != null) cons.read(read);
|
||||
}
|
||||
|
||||
public void writeAll(Writes write){
|
||||
writeBase(write);
|
||||
write(write);
|
||||
}
|
||||
|
||||
public void readAll(Reads read, byte revision){
|
||||
readBase(read);
|
||||
read(read, revision);
|
||||
}
|
||||
|
||||
@CallSuper
|
||||
@Override
|
||||
public void write(Writes write){
|
||||
//overriden by subclasses!
|
||||
}
|
||||
|
||||
@CallSuper
|
||||
@Override
|
||||
public void read(Reads read, byte revision){
|
||||
//overriden by subclasses!
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyBoost(float intensity, float duration){
|
||||
timeScale = Math.max(timeScale, intensity);
|
||||
timeScaleDuration = Math.max(timeScaleDuration, duration);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float timeScale(){
|
||||
return timeScale;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean consValid(){
|
||||
return cons.valid();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void consume(){
|
||||
cons.trigger();
|
||||
}
|
||||
|
||||
/** Scaled delta. */
|
||||
@Override
|
||||
public float delta(){
|
||||
return Time.delta() * timeScale;
|
||||
}
|
||||
|
||||
/** Base efficiency. If this entity has non-buffered power, returns the power %, otherwise returns 1. */
|
||||
@Override
|
||||
public float efficiency(){
|
||||
return power != null && (block.consumes.has(ConsumeType.power) && !block.consumes.getPower().buffered) ? power.status : 1f;
|
||||
}
|
||||
|
||||
/** Call when nothing is happening to the entity. This increments the internal sleep timer. */
|
||||
@Override
|
||||
public void sleep(){
|
||||
sleepTime += Time.delta();
|
||||
if(!sleeping && sleepTime >= timeToSleep){
|
||||
remove();
|
||||
sleeping = true;
|
||||
sleepingEntities++;
|
||||
}
|
||||
}
|
||||
|
||||
/** Call when this entity is updating. This wakes it up. */
|
||||
@Override
|
||||
public void noSleep(){
|
||||
sleepTime = 0f;
|
||||
if(sleeping){
|
||||
add();
|
||||
sleeping = false;
|
||||
sleepingEntities--;
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns the version of this TileEntity IO code.*/
|
||||
@Override
|
||||
public byte version(){
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean collide(Bulletc other){
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void collision(Bulletc other){
|
||||
block.handleBulletHit(this, other);
|
||||
}
|
||||
|
||||
//TODO Implement damage!
|
||||
|
||||
@Override
|
||||
public void removeFromProximity(){
|
||||
block.onProximityRemoved(tile);
|
||||
|
||||
Point2[] nearby = Edges.getEdges(block.size);
|
||||
for(Point2 point : nearby){
|
||||
Tile other = world.ltile(tile.x + point.x, tile.y + point.y);
|
||||
//remove this tile from all nearby tile's proximities
|
||||
if(other != null){
|
||||
other.block().onProximityUpdate(other);
|
||||
|
||||
if(other.entity != null){
|
||||
other.entity.proximity().remove(tile, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateProximity(){
|
||||
tmpTiles.clear();
|
||||
proximity.clear();
|
||||
|
||||
Point2[] nearby = Edges.getEdges(block.size);
|
||||
for(Point2 point : nearby){
|
||||
Tile other = world.ltile(tile.x + point.x, tile.y + point.y);
|
||||
|
||||
if(other == null) continue;
|
||||
if(other.entity == null || !(other.interactable(tile.team()))) continue;
|
||||
|
||||
//add this tile to proximity of nearby tiles
|
||||
if(!other.entity.proximity().contains(tile, true)){
|
||||
other.entity.proximity().add(tile);
|
||||
}
|
||||
|
||||
tmpTiles.add(other);
|
||||
}
|
||||
|
||||
//using a set to prevent duplicates
|
||||
for(Tile tile : tmpTiles){
|
||||
proximity.add(tile);
|
||||
}
|
||||
|
||||
block.onProximityAdded(tile);
|
||||
block.onProximityUpdate(tile);
|
||||
|
||||
for(Tile other : tmpTiles){
|
||||
other.block().onProximityUpdate(other);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Array<Tile> proximity(){
|
||||
return proximity;
|
||||
}
|
||||
|
||||
/** Tile configuration. Defaults to 0. Used for block rebuilding. */
|
||||
@Override
|
||||
public int config(){
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(){
|
||||
if(sound != null){
|
||||
sound.stop();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void killed(){
|
||||
Events.fire(new BlockDestroyEvent(tile));
|
||||
block.breakSound.at(tile);
|
||||
block.onDestroyed(tile);
|
||||
tile.remove();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(){
|
||||
timeScaleDuration -= Time.delta();
|
||||
if(timeScaleDuration <= 0f || !block.canOverdrive){
|
||||
timeScale = 1f;
|
||||
}
|
||||
|
||||
if(sound != null){
|
||||
sound.update(x(), y(), block.shouldActiveSound(tile));
|
||||
}
|
||||
|
||||
if(block.idleSound != Sounds.none && block.shouldIdleSound(tile)){
|
||||
loops.play(block.idleSound, this, block.idleSoundVolume);
|
||||
}
|
||||
|
||||
block.update(tile);
|
||||
|
||||
if(liquids != null){
|
||||
liquids.update();
|
||||
}
|
||||
|
||||
if(cons != null){
|
||||
cons.update();
|
||||
}
|
||||
|
||||
if(power != null){
|
||||
power.graph.update();
|
||||
}
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
212
core/src/mindustry/entities/def/UnitComp.java
Normal file
212
core/src/mindustry/entities/def/UnitComp.java
Normal file
@@ -0,0 +1,212 @@
|
||||
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.*;
|
||||
|
||||
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;
|
||||
|
||||
@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));
|
||||
}
|
||||
|
||||
@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
|
||||
//TODO move elsewhere
|
||||
if(team() != state.rules.waveTeam){
|
||||
float relativeSize = state.rules.dropZoneRadius + bounds()/2f + 1f;
|
||||
for(Tile spawn : spawner.getGroundSpawns()){
|
||||
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){
|
||||
//unit block update
|
||||
tile.block().unitOn(tile, this);
|
||||
|
||||
//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.*;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
140
core/src/mindustry/entities/def/WeaponsComp.java
Normal file
140
core/src/mindustry/entities/def/WeaponsComp.java
Normal file
@@ -0,0 +1,140 @@
|
||||
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 = {};
|
||||
|
||||
void setupWeapons(UnitType def){
|
||||
mounts = new WeaponMount[def.weapons.size];
|
||||
for(int i = 0; i < mounts.length; i++){
|
||||
mounts[i] = new WeaponMount(def.weapons.get(i));
|
||||
}
|
||||
}
|
||||
|
||||
void controlWeapons(boolean rotate, boolean shoot){
|
||||
for(WeaponMount mount : mounts){
|
||||
mount.rotate = rotate;
|
||||
mount.shoot = 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;
|
||||
}
|
||||
}
|
||||
|
||||
/** 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();
|
||||
}
|
||||
@@ -1,229 +0,0 @@
|
||||
package mindustry.entities.effect;
|
||||
|
||||
import arc.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import arc.struct.*;
|
||||
import arc.math.*;
|
||||
import arc.math.geom.*;
|
||||
import arc.util.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.entities.*;
|
||||
import mindustry.entities.traits.*;
|
||||
import mindustry.entities.type.*;
|
||||
import mindustry.game.EventType.*;
|
||||
import mindustry.game.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.world.*;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class Fire extends TimedEntity implements SaveTrait, SyncTrait{
|
||||
private static final IntMap<Fire> map = new IntMap<>();
|
||||
private static final float baseLifetime = 1000f, spreadChance = 0.05f, fireballChance = 0.07f;
|
||||
|
||||
private int loadedPosition = -1;
|
||||
private Tile tile;
|
||||
private Block block;
|
||||
private float baseFlammability = -1, puddleFlammability;
|
||||
private float lifetime;
|
||||
|
||||
/** Deserialization use only! */
|
||||
public Fire(){
|
||||
}
|
||||
|
||||
@Remote
|
||||
public static void onRemoveFire(int fid){
|
||||
fireGroup.removeByID(fid);
|
||||
}
|
||||
|
||||
/** 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.
|
||||
|
||||
Fire fire = map.get(tile.pos());
|
||||
|
||||
if(fire == null){
|
||||
fire = new Fire();
|
||||
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 boolean has(int x, int y){
|
||||
if(!Structs.inBounds(x, y, world.width(), world.height()) || !map.containsKey(Pos.get(x, y))){
|
||||
return false;
|
||||
}
|
||||
Fire fire = map.get(Pos.get(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())){
|
||||
Fire fire = map.get(tile.pos());
|
||||
fire.time += intensity * Time.delta();
|
||||
if(fire.time >= fire.lifetime()){
|
||||
Events.fire(Trigger.fireExtinguish);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public TypeID getTypeID(){
|
||||
return TypeIDs.fire;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte version(){
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float lifetime(){
|
||||
return lifetime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(){
|
||||
if(Mathf.chance(0.1 * Time.delta())){
|
||||
Effects.effect(Fx.fire, x + Mathf.range(4f), y + Mathf.range(4f));
|
||||
}
|
||||
|
||||
if(Mathf.chance(0.05 * Time.delta())){
|
||||
Effects.effect(Fx.fireSmoke, 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());
|
||||
map.put(tile.pos(), this);
|
||||
|
||||
if(net.client()){
|
||||
return;
|
||||
}
|
||||
|
||||
if(time >= lifetime() || tile == null){
|
||||
remove();
|
||||
return;
|
||||
}
|
||||
|
||||
TileEntity entity = tile.link().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.block().getFlammability(tile);
|
||||
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);
|
||||
create(other);
|
||||
|
||||
if(Mathf.chance(fireballChance * Time.delta() * Mathf.clamp(flammability / 10f))){
|
||||
Call.createBullet(Bullets.fireball, Team.derelict, x, y, Mathf.random(360f), 1, 1);
|
||||
}
|
||||
}
|
||||
|
||||
if(Mathf.chance(0.1 * Time.delta())){
|
||||
Puddle p = Puddle.getPuddle(tile);
|
||||
if(p != null){
|
||||
puddleFlammability = p.getFlammability() / 3f;
|
||||
}else{
|
||||
puddleFlammability = 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.applyEffect(StatusEffects.burning, 60 * 5));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeSave(DataOutput stream) throws IOException{
|
||||
stream.writeInt(tile.pos());
|
||||
stream.writeFloat(lifetime);
|
||||
stream.writeFloat(time);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readSave(DataInput stream, byte version) throws IOException{
|
||||
this.loadedPosition = stream.readInt();
|
||||
this.lifetime = stream.readFloat();
|
||||
this.time = stream.readFloat();
|
||||
add();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(DataOutput data) throws IOException{
|
||||
data.writeInt(tile.pos());
|
||||
data.writeFloat(lifetime);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(DataInput data) throws IOException{
|
||||
int pos = data.readInt();
|
||||
this.lifetime = data.readFloat();
|
||||
|
||||
x = Pos.x(pos) * tilesize;
|
||||
y = Pos.y(pos) * tilesize;
|
||||
tile = world.tile(pos);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset(){
|
||||
loadedPosition = -1;
|
||||
tile = null;
|
||||
baseFlammability = -1;
|
||||
puddleFlammability = 0f;
|
||||
incrementID();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void added(){
|
||||
if(loadedPosition != -1){
|
||||
map.put(loadedPosition, this);
|
||||
tile = world.tile(loadedPosition);
|
||||
set(tile.worldx(), tile.worldy());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removed(){
|
||||
if(tile != null){
|
||||
Call.onRemoveFire(id);
|
||||
map.remove(tile.pos());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityGroup targetGroup(){
|
||||
return fireGroup;
|
||||
}
|
||||
}
|
||||
@@ -1,90 +0,0 @@
|
||||
package mindustry.entities.effect;
|
||||
|
||||
import arc.math.Mathf;
|
||||
import arc.util.Time;
|
||||
import mindustry.Vars;
|
||||
import mindustry.entities.Effects;
|
||||
import mindustry.entities.Effects.Effect;
|
||||
import mindustry.entities.Effects.EffectRenderer;
|
||||
import mindustry.entities.type.EffectEntity;
|
||||
import mindustry.world.Tile;
|
||||
|
||||
/**
|
||||
* A ground effect contains an effect that is rendered on the ground layer as opposed to the top layer.
|
||||
*/
|
||||
public class GroundEffectEntity extends EffectEntity{
|
||||
private boolean once;
|
||||
|
||||
@Override
|
||||
public void update(){
|
||||
GroundEffect effect = (GroundEffect)this.effect;
|
||||
|
||||
if(effect.isStatic){
|
||||
time += Time.delta();
|
||||
|
||||
time = Mathf.clamp(time, 0, effect.staticLife);
|
||||
|
||||
if(!once && time >= lifetime()){
|
||||
once = true;
|
||||
time = 0f;
|
||||
Tile tile = Vars.world.tileWorld(x, y);
|
||||
if(tile != null && tile.floor().isLiquid){
|
||||
remove();
|
||||
}
|
||||
}else if(once && time >= effect.staticLife){
|
||||
remove();
|
||||
}
|
||||
}else{
|
||||
super.update();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(){
|
||||
GroundEffect effect = (GroundEffect)this.effect;
|
||||
|
||||
if(once && effect.isStatic)
|
||||
Effects.renderEffect(id, effect, color, lifetime(), rotation, x, y, data);
|
||||
else
|
||||
Effects.renderEffect(id, effect, color, time, rotation, x, y, data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset(){
|
||||
super.reset();
|
||||
once = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* An effect that is rendered on the ground layer as opposed to the top layer.
|
||||
*/
|
||||
public static class GroundEffect extends Effect{
|
||||
/**
|
||||
* How long this effect stays on the ground when static.
|
||||
*/
|
||||
public final float staticLife;
|
||||
/**
|
||||
* If true, this effect will stop and lie on the ground for a specific duration,
|
||||
* after its initial lifetime is over.
|
||||
*/
|
||||
public final boolean isStatic;
|
||||
|
||||
public GroundEffect(float life, float staticLife, EffectRenderer draw){
|
||||
super(life, draw);
|
||||
this.staticLife = staticLife;
|
||||
this.isStatic = true;
|
||||
}
|
||||
|
||||
public GroundEffect(boolean isStatic, float life, EffectRenderer draw){
|
||||
super(life, draw);
|
||||
this.staticLife = 0f;
|
||||
this.isStatic = isStatic;
|
||||
}
|
||||
|
||||
public GroundEffect(float life, EffectRenderer draw){
|
||||
super(life, draw);
|
||||
this.staticLife = 0f;
|
||||
this.isStatic = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,119 +0,0 @@
|
||||
package mindustry.entities.effect;
|
||||
|
||||
import mindustry.annotations.Annotations.Loc;
|
||||
import mindustry.annotations.Annotations.Remote;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.math.Interpolation;
|
||||
import arc.math.Mathf;
|
||||
import arc.math.geom.Position;
|
||||
import arc.math.geom.Vec2;
|
||||
import arc.util.Time;
|
||||
import arc.util.pooling.Pools;
|
||||
import mindustry.entities.*;
|
||||
import mindustry.entities.type.TimedEntity;
|
||||
import mindustry.entities.traits.DrawTrait;
|
||||
import mindustry.entities.type.Unit;
|
||||
import mindustry.graphics.Pal;
|
||||
import mindustry.type.Item;
|
||||
import mindustry.world.Tile;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class ItemTransfer extends TimedEntity implements DrawTrait{
|
||||
private Vec2 from = new Vec2();
|
||||
private Vec2 current = new Vec2();
|
||||
private Vec2 tovec = new Vec2();
|
||||
private Item item;
|
||||
private float seed;
|
||||
private Position to;
|
||||
private Runnable done;
|
||||
|
||||
public ItemTransfer(){
|
||||
}
|
||||
|
||||
@Remote(called = Loc.server, unreliable = true)
|
||||
public static void transferItemEffect(Item item, float x, float y, Unit to){
|
||||
if(to == null) return;
|
||||
create(item, x, y, to, () -> {
|
||||
});
|
||||
}
|
||||
|
||||
@Remote(called = Loc.server, unreliable = true)
|
||||
public static void transferItemToUnit(Item item, float x, float y, Unit to){
|
||||
if(to == null) return;
|
||||
create(item, x, y, to, () -> to.addItem(item));
|
||||
}
|
||||
|
||||
@Remote(called = Loc.server, unreliable = true)
|
||||
public static void transferItemTo(Item item, int amount, float x, float y, Tile tile){
|
||||
if(tile == null || tile.entity == null || tile.entity.items == null) return;
|
||||
for(int i = 0; i < Mathf.clamp(amount / 3, 1, 8); i++){
|
||||
Time.run(i * 3, () -> create(item, x, y, tile, () -> {}));
|
||||
}
|
||||
tile.entity.items.add(item, amount);
|
||||
}
|
||||
|
||||
public static void create(Item item, float fromx, float fromy, Position to, Runnable done){
|
||||
ItemTransfer tr = Pools.obtain(ItemTransfer.class, ItemTransfer::new);
|
||||
tr.item = item;
|
||||
tr.from.set(fromx, fromy);
|
||||
tr.to = to;
|
||||
tr.done = done;
|
||||
tr.seed = Mathf.range(1f);
|
||||
tr.add();
|
||||
}
|
||||
|
||||
@Override
|
||||
public float lifetime(){
|
||||
return 60;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset(){
|
||||
super.reset();
|
||||
item = null;
|
||||
to = null;
|
||||
done = null;
|
||||
from.setZero();
|
||||
current.setZero();
|
||||
tovec.setZero();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removed(){
|
||||
if(done != null){
|
||||
done.run();
|
||||
}
|
||||
Pools.free(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(){
|
||||
if(to == null){
|
||||
remove();
|
||||
return;
|
||||
}
|
||||
|
||||
super.update();
|
||||
current.set(from).interpolate(tovec.set(to.getX(), to.getY()), fin(), Interpolation.pow3);
|
||||
current.add(tovec.set(to.getX(), to.getY()).sub(from).nor().rotate90(1).scl(seed * fslope() * 10f));
|
||||
set(current.x, current.y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(){
|
||||
Lines.stroke(fslope() * 2f, Pal.accent);
|
||||
|
||||
Lines.circle(x, y, fslope() * 2f);
|
||||
|
||||
Draw.color(item.color);
|
||||
Fill.circle(x, y, fslope() * 1.5f);
|
||||
|
||||
Draw.reset();
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityGroup targetGroup(){
|
||||
return effectGroup;
|
||||
}
|
||||
}
|
||||
@@ -1,160 +0,0 @@
|
||||
package mindustry.entities.effect;
|
||||
|
||||
import mindustry.annotations.Annotations.Loc;
|
||||
import mindustry.annotations.Annotations.Remote;
|
||||
import arc.struct.Array;
|
||||
import arc.struct.IntSet;
|
||||
import arc.graphics.Color;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.math.*;
|
||||
import arc.math.geom.*;
|
||||
import arc.util.pooling.Pools;
|
||||
import mindustry.content.Bullets;
|
||||
import mindustry.entities.EntityGroup;
|
||||
import mindustry.entities.Units;
|
||||
import mindustry.entities.type.Bullet;
|
||||
import mindustry.entities.type.TimedEntity;
|
||||
import mindustry.entities.traits.DrawTrait;
|
||||
import mindustry.entities.traits.TimeTrait;
|
||||
import mindustry.entities.type.Unit;
|
||||
import mindustry.game.Team;
|
||||
import mindustry.gen.Call;
|
||||
import mindustry.graphics.Pal;
|
||||
import mindustry.world.Tile;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class Lightning extends TimedEntity implements DrawTrait, TimeTrait{
|
||||
public static final float lifetime = 10f;
|
||||
|
||||
private static final Rand random = new Rand();
|
||||
private static final Rect rect = new Rect();
|
||||
private static final Array<Unit> entities = new Array<>();
|
||||
private static final IntSet hit = new IntSet();
|
||||
private static final int maxChain = 8;
|
||||
private static final float hitRange = 30f;
|
||||
private static int lastSeed = 0;
|
||||
|
||||
private Array<Vec2> lines = new Array<>();
|
||||
private Color color = Pal.lancerLaser;
|
||||
|
||||
/** For pooling use only. Do not call directly! */
|
||||
public Lightning(){
|
||||
}
|
||||
|
||||
/** 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){
|
||||
Call.createLighting(nextSeed(), team, color, damage, x, y, targetAngle, length);
|
||||
}
|
||||
|
||||
public static int nextSeed(){
|
||||
return lastSeed++;
|
||||
}
|
||||
|
||||
/** Do not invoke! */
|
||||
@Remote(called = Loc.server, unreliable = true)
|
||||
public static void createLighting(int seed, Team team, Color color, float damage, float x, float y, float rotation, int length){
|
||||
|
||||
Lightning l = Pools.obtain(Lightning.class, Lightning::new);
|
||||
Float dmg = damage;
|
||||
|
||||
l.x = x;
|
||||
l.y = y;
|
||||
l.color = color;
|
||||
l.add();
|
||||
|
||||
random.setSeed(seed);
|
||||
hit.clear();
|
||||
|
||||
boolean[] bhit = {false};
|
||||
|
||||
for(int i = 0; i < length / 2; i++){
|
||||
Bullet.create(Bullets.damageLightning, l, team, x, y, 0f, 1f, 1f, dmg);
|
||||
l.lines.add(new Vec2(x + Mathf.range(3f), y + Mathf.range(3f)));
|
||||
|
||||
if(l.lines.size > 1){
|
||||
bhit[0] = false;
|
||||
Position from = l.lines.get(l.lines.size - 2);
|
||||
Position to = l.lines.get(l.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.ltile(wx, wy);
|
||||
if(tile != null && tile.block().insulated){
|
||||
bhit[0] = true;
|
||||
//snap it instead of removing
|
||||
l.lines.get(l.lines.size -1).set(wx * tilesize, wy * tilesize);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
if(bhit[0]) break;
|
||||
}
|
||||
|
||||
rect.setSize(hitRange).setCenter(x, y);
|
||||
entities.clear();
|
||||
if(hit.size < maxChain){
|
||||
Units.nearbyEnemies(team, rect, u -> {
|
||||
if(!hit.contains(u.getID())){
|
||||
entities.add(u);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Unit furthest = Geometry.findFurthest(x, y, entities);
|
||||
|
||||
if(furthest != null){
|
||||
hit.add(furthest.getID());
|
||||
x = furthest.x;
|
||||
y = furthest.y;
|
||||
}else{
|
||||
rotation += random.range(20f);
|
||||
|
||||
x += Angles.trnsx(rotation, hitRange / 2f);
|
||||
y += Angles.trnsy(rotation, hitRange / 2f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public float lifetime(){
|
||||
return lifetime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset(){
|
||||
super.reset();
|
||||
color = Pal.lancerLaser;
|
||||
lines.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removed(){
|
||||
super.removed();
|
||||
Pools.free(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(){
|
||||
Lines.stroke(3f * fout());
|
||||
Draw.color(color, Color.white, fin());
|
||||
Lines.beginLine();
|
||||
|
||||
Lines.linePoint(x, y);
|
||||
for(Position p : lines){
|
||||
Lines.linePoint(p.getX(), p.getY());
|
||||
}
|
||||
Lines.endLine();
|
||||
|
||||
int i = 0;
|
||||
|
||||
for(Position p : lines){
|
||||
Fill.square(p.getX(), p.getY(), (5f - (float)i++ / lines.size * 2f) * fout(), 45);
|
||||
}
|
||||
Draw.reset();
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityGroup targetGroup(){
|
||||
return bulletGroup;
|
||||
}
|
||||
}
|
||||
@@ -1,322 +0,0 @@
|
||||
package mindustry.entities.effect;
|
||||
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import arc.struct.*;
|
||||
import arc.graphics.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.math.*;
|
||||
import arc.math.geom.*;
|
||||
import arc.util.*;
|
||||
import arc.util.pooling.Pool.*;
|
||||
import arc.util.pooling.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.entities.*;
|
||||
import mindustry.entities.traits.*;
|
||||
import mindustry.entities.type.*;
|
||||
import mindustry.game.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.world.*;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class Puddle extends SolidEntity implements SaveTrait, Poolable, DrawTrait, SyncTrait{
|
||||
private static final IntMap<Puddle> map = new IntMap<>();
|
||||
private static final float maxLiquid = 70f;
|
||||
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;
|
||||
|
||||
private int loadedPosition = -1;
|
||||
|
||||
private float updateTime;
|
||||
private float lastRipple;
|
||||
private Tile tile;
|
||||
private Liquid liquid;
|
||||
private float amount, targetAmount;
|
||||
private float accepting;
|
||||
private byte generation;
|
||||
|
||||
/** Deserialization use only! */
|
||||
public Puddle(){
|
||||
}
|
||||
|
||||
/** Deposists a puddle between tile and source. */
|
||||
public static void deposit(Tile tile, Tile source, Liquid liquid, float amount){
|
||||
deposit(tile, source, liquid, amount, 0);
|
||||
}
|
||||
|
||||
/** Deposists a puddle at a tile. */
|
||||
public static void deposit(Tile tile, Liquid liquid, float amount){
|
||||
deposit(tile, tile, liquid, amount, 0);
|
||||
}
|
||||
|
||||
/** Returns the puddle on the specified tile. May return null. */
|
||||
public static Puddle getPuddle(Tile tile){
|
||||
return map.get(tile.pos());
|
||||
}
|
||||
|
||||
private 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);
|
||||
|
||||
Puddle p = map.get(tile.pos());
|
||||
|
||||
if(generation == 0 && p != null && p.lastRipple <= Time.time() - 40f){
|
||||
Effects.effect(Fx.ripple, tile.floor().liquidDrop.color,
|
||||
(tile.worldx() + source.worldx()) / 2f, (tile.worldy() + source.worldy()) / 2f);
|
||||
p.lastRipple = Time.time();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
Puddle p = map.get(tile.pos());
|
||||
if(p == null){
|
||||
if(net.client()) return; //not clientside.
|
||||
|
||||
Puddle puddle = Pools.obtain(Puddle.class, Puddle::new);
|
||||
puddle.tile = tile;
|
||||
puddle.liquid = liquid;
|
||||
puddle.amount = amount;
|
||||
puddle.generation = (byte)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){
|
||||
Effects.effect(Fx.ripple, p.liquid.color, (tile.worldx() + source.worldx()) / 2f, (tile.worldy() + source.worldy()) / 2f);
|
||||
p.lastRipple = Time.time();
|
||||
}
|
||||
}else{
|
||||
p.amount += reactPuddle(p.liquid, liquid, amount, p.tile, p.x, p.y);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/** 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
|
||||
Fire.create(tile);
|
||||
if(Mathf.chance(0.006 * amount)){
|
||||
Call.createBullet(Bullets.fireball, Team.derelict, x, y, Mathf.random(360f), 1f, 1f);
|
||||
}
|
||||
}else if(dest.temperature > 0.7f && liquid.temperature < 0.55f){ //cold liquid poured onto hot puddle
|
||||
if(Mathf.chance(0.5f * amount)){
|
||||
Effects.effect(Fx.steam, x, y);
|
||||
}
|
||||
return -0.1f * amount;
|
||||
}else if(liquid.temperature > 0.7f && dest.temperature < 0.55f){ //hot liquid poured onto cold puddle
|
||||
if(Mathf.chance(0.8f * amount)){
|
||||
Effects.effect(Fx.steam, x, y);
|
||||
}
|
||||
return -0.4f * amount;
|
||||
}
|
||||
return 0f;
|
||||
}
|
||||
|
||||
@Remote(called = Loc.server)
|
||||
public static void onPuddleRemoved(int puddleid){
|
||||
puddleGroup.removeByID(puddleid);
|
||||
}
|
||||
|
||||
public float getFlammability(){
|
||||
return liquid.flammability * amount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TypeID getTypeID(){
|
||||
return TypeIDs.puddle;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte version(){
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hitbox(Rect rect){
|
||||
rect.setCenter(x, y).setSize(tilesize);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hitboxTile(Rect rect){
|
||||
rect.setCenter(x, y).setSize(0f);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(){
|
||||
|
||||
//no updating happens clientside
|
||||
if(net.client()){
|
||||
amount = Mathf.lerpDelta(amount, targetAmount, 0.15f);
|
||||
}else{
|
||||
//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){
|
||||
deposit(other, tile, liquid, deposited, generation + 1);
|
||||
amount -= deposited / 2f; //tweak to speed up/slow down puddle propagation
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
amount = Mathf.clamp(amount, 0, maxLiquid);
|
||||
|
||||
if(amount <= 0f){
|
||||
Call.onPuddleRemoved(getID());
|
||||
}
|
||||
}
|
||||
|
||||
//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.isFlying()) return;
|
||||
|
||||
unit.hitbox(rect2);
|
||||
if(!rect.overlaps(rect2)) return;
|
||||
|
||||
unit.applyEffect(liquid.effect, 60 * 2);
|
||||
|
||||
if(unit.velocity().len() > 0.1){
|
||||
Effects.effect(Fx.ripple, liquid.color, unit.x, unit.y);
|
||||
}
|
||||
});
|
||||
|
||||
if(liquid.temperature > 0.7f && (tile.link().entity != null) && Mathf.chance(0.3 * Time.delta())){
|
||||
Fire.create(tile);
|
||||
}
|
||||
|
||||
updateTime = 20f;
|
||||
}
|
||||
|
||||
updateTime -= Time.delta();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(){
|
||||
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;
|
||||
renderer.lights.add(tile.drawx(), tile.drawy(), 30f * f, color, opacity * 0.8f);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public float drawSize(){
|
||||
return 20;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeSave(DataOutput stream) throws IOException{
|
||||
stream.writeInt(tile.pos());
|
||||
stream.writeFloat(x);
|
||||
stream.writeFloat(y);
|
||||
stream.writeByte(liquid.id);
|
||||
stream.writeFloat(amount);
|
||||
stream.writeByte(generation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readSave(DataInput stream, byte version) throws IOException{
|
||||
this.loadedPosition = stream.readInt();
|
||||
this.x = stream.readFloat();
|
||||
this.y = stream.readFloat();
|
||||
this.liquid = content.liquid(stream.readByte());
|
||||
this.amount = stream.readFloat();
|
||||
this.generation = stream.readByte();
|
||||
add();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset(){
|
||||
loadedPosition = -1;
|
||||
tile = null;
|
||||
liquid = null;
|
||||
amount = 0;
|
||||
generation = 0;
|
||||
accepting = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void added(){
|
||||
if(loadedPosition != -1){
|
||||
map.put(loadedPosition, this);
|
||||
tile = world.tile(loadedPosition);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removed(){
|
||||
if(tile != null){
|
||||
map.remove(tile.pos());
|
||||
}
|
||||
reset();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(DataOutput data) throws IOException{
|
||||
data.writeFloat(x);
|
||||
data.writeFloat(y);
|
||||
data.writeByte(liquid.id);
|
||||
data.writeShort((short)(amount * 4));
|
||||
data.writeInt(tile.pos());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(DataInput data) throws IOException{
|
||||
x = data.readFloat();
|
||||
y = data.readFloat();
|
||||
liquid = content.liquid(data.readByte());
|
||||
targetAmount = data.readShort() / 4f;
|
||||
int pos = data.readInt();
|
||||
tile = world.tile(pos);
|
||||
|
||||
map.put(pos, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityGroup targetGroup(){
|
||||
return puddleGroup;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user