Merge branch 'master' of https://github.com/Anuken/Mindustry into messages
# Conflicts: # core/assets/sprites/block_colors.png # core/assets/sprites/sprites.atlas # core/assets/sprites/sprites.png # core/assets/sprites/sprites2.png # core/assets/sprites/sprites5.png
This commit is contained in:
@@ -13,6 +13,7 @@ import io.anuke.mindustry.game.EventType.*;
|
||||
import io.anuke.mindustry.gen.*;
|
||||
import io.anuke.mindustry.graphics.*;
|
||||
import io.anuke.mindustry.maps.*;
|
||||
import io.anuke.mindustry.net.Net;
|
||||
|
||||
import static io.anuke.arc.Core.*;
|
||||
import static io.anuke.mindustry.Vars.*;
|
||||
@@ -39,7 +40,9 @@ public abstract class ClientLauncher extends ApplicationCore implements Platform
|
||||
batch = new SpriteBatch();
|
||||
assets = new AssetManager();
|
||||
assets.setLoader(Texture.class, "." + mapExtension, new MapPreviewLoader());
|
||||
assets.load("sprites/error.png", Texture.class);
|
||||
atlas = TextureAtlas.blankAtlas();
|
||||
Vars.net = new Net(platform.getNet());
|
||||
|
||||
UI.loadSystemCursors();
|
||||
|
||||
@@ -47,7 +50,9 @@ public abstract class ClientLauncher extends ApplicationCore implements Platform
|
||||
|
||||
UI.loadDefaultFont();
|
||||
|
||||
assets.load(new AssetDescriptor<>("sprites/sprites.atlas", TextureAtlas.class)).loaded = t -> atlas = (TextureAtlas)t;
|
||||
assets.load(new AssetDescriptor<>("sprites/sprites.atlas", TextureAtlas.class)).loaded = t -> {
|
||||
atlas = (TextureAtlas)t;
|
||||
};
|
||||
|
||||
assets.loadRun("maps", Map.class, () -> maps.loadPreviews());
|
||||
|
||||
@@ -84,6 +89,8 @@ public abstract class ClientLauncher extends ApplicationCore implements Platform
|
||||
|
||||
@Override
|
||||
public void resize(int width, int height){
|
||||
if(assets == null) return;
|
||||
|
||||
if(!assets.isFinished()){
|
||||
Draw.proj().setOrtho(0, 0, width, height);
|
||||
}else{
|
||||
@@ -150,15 +157,15 @@ public abstract class ClientLauncher extends ApplicationCore implements Platform
|
||||
Core.graphics.clear(Pal.darkerGray);
|
||||
Draw.proj().setOrtho(0, 0, Core.graphics.getWidth(), Core.graphics.getHeight());
|
||||
|
||||
float height = UnitScl.dp.scl(50f);
|
||||
float height = Scl.scl(50f);
|
||||
|
||||
Draw.color(Color.BLACK);
|
||||
Draw.color(Color.black);
|
||||
Fill.poly(graphics.getWidth()/2f, graphics.getHeight()/2f, 6, Mathf.dst(graphics.getWidth()/2f, graphics.getHeight()/2f) * smoothProgress);
|
||||
Draw.reset();
|
||||
|
||||
float w = graphics.getWidth()*0.6f;
|
||||
|
||||
Draw.color(Color.BLACK);
|
||||
Draw.color(Color.black);
|
||||
Fill.rect(graphics.getWidth()/2f, graphics.getHeight()/2f, w, height);
|
||||
|
||||
Draw.color(Pal.accent);
|
||||
@@ -170,13 +177,13 @@ public abstract class ClientLauncher extends ApplicationCore implements Platform
|
||||
|
||||
if(assets.isLoaded("outline")){
|
||||
BitmapFont font = assets.get("outline");
|
||||
font.draw((int)(assets.getProgress() * 100) + "%", graphics.getWidth() / 2f, graphics.getHeight() / 2f + UnitScl.dp.scl(10f), Align.center);
|
||||
font.draw(bundle.get("loading", "").replace("[accent]", ""), graphics.getWidth() / 2f, graphics.getHeight() / 2f + height / 2f + UnitScl.dp.scl(20), Align.center);
|
||||
font.draw((int)(assets.getProgress() * 100) + "%", graphics.getWidth() / 2f, graphics.getHeight() / 2f + Scl.scl(10f), Align.center);
|
||||
font.draw(bundle.get("loading", "").replace("[accent]", ""), graphics.getWidth() / 2f, graphics.getHeight() / 2f + height / 2f + Scl.scl(20), Align.center);
|
||||
|
||||
if(assets.getCurrentLoading() != null){
|
||||
String name = assets.getCurrentLoading().fileName.toLowerCase();
|
||||
String key = name.contains("content") ? "content" : name.contains("msav") || name.contains("maps") ? "map" : name.contains("ogg") || name.contains("mp3") ? "sound" : name.contains("png") ? "image" : "system";
|
||||
font.draw(bundle.get("load." + key, ""), graphics.getWidth() / 2f, graphics.getHeight() / 2f - height / 2f - UnitScl.dp.scl(10f), Align.center);
|
||||
font.draw(bundle.get("load." + key, ""), graphics.getWidth() / 2f, graphics.getHeight() / 2f - height / 2f - Scl.scl(10f), Align.center);
|
||||
}
|
||||
}
|
||||
Draw.flush();
|
||||
|
||||
@@ -3,6 +3,7 @@ package io.anuke.mindustry;
|
||||
import io.anuke.arc.Application.*;
|
||||
import io.anuke.arc.*;
|
||||
import io.anuke.arc.assets.*;
|
||||
import io.anuke.arc.collection.*;
|
||||
import io.anuke.arc.files.*;
|
||||
import io.anuke.arc.graphics.*;
|
||||
import io.anuke.arc.scene.ui.layout.*;
|
||||
@@ -10,9 +11,7 @@ import io.anuke.arc.util.*;
|
||||
import io.anuke.mindustry.ai.*;
|
||||
import io.anuke.mindustry.core.*;
|
||||
import io.anuke.mindustry.entities.*;
|
||||
import io.anuke.mindustry.entities.bullet.*;
|
||||
import io.anuke.mindustry.entities.effect.*;
|
||||
import io.anuke.mindustry.entities.impl.*;
|
||||
import io.anuke.mindustry.entities.traits.*;
|
||||
import io.anuke.mindustry.entities.type.*;
|
||||
import io.anuke.mindustry.game.*;
|
||||
@@ -20,6 +19,7 @@ import io.anuke.mindustry.gen.*;
|
||||
import io.anuke.mindustry.input.*;
|
||||
import io.anuke.mindustry.maps.*;
|
||||
import io.anuke.mindustry.net.Net;
|
||||
import io.anuke.mindustry.plugin.*;
|
||||
import io.anuke.mindustry.world.blocks.defense.ForceProjector.*;
|
||||
|
||||
import java.nio.charset.*;
|
||||
@@ -43,6 +43,8 @@ public class Vars implements Loadable{
|
||||
public static final String discordURL = "https://discord.gg/mindustry";
|
||||
/** URL for sending crash reports to */
|
||||
public static final String crashReportURL = "http://mins.us.to/report";
|
||||
/** list of built-in servers.*/
|
||||
public static final Array<String> defaultServers = Array.with(/*"mins.us.to"*/);
|
||||
/** maximum distance between mine and core that supports automatic transferring */
|
||||
public static final float mineTransferRange = 220f;
|
||||
/** team of the player by default */
|
||||
@@ -59,14 +61,6 @@ public class Vars implements Loadable{
|
||||
public static final float itemSize = 5f;
|
||||
/** extra padding around the world; units outside this bound will begin to self-destruct. */
|
||||
public static final float worldBounds = 100f;
|
||||
/** default size of UI icons.*/
|
||||
public static final int iconsize = 48;
|
||||
/** size of UI icons (small)*/
|
||||
public static final int iconsizesmall = 32;
|
||||
/** size of UI icons (medium)*/
|
||||
public static final int iconsizemed = 30;
|
||||
/** size of UI icons (medium)*/
|
||||
public static final int iconsizetiny = 16;
|
||||
/** units outside of this bound will simply die instantly */
|
||||
public static final float finalWorldBounds = worldBounds + 500;
|
||||
/** ticks spent out of bound until self destruct. */
|
||||
@@ -112,6 +106,8 @@ public class Vars implements Loadable{
|
||||
public static boolean android;
|
||||
/** whether the game is running on a headless server */
|
||||
public static boolean headless;
|
||||
/** whether steam is enabled for this game */
|
||||
public static boolean steam;
|
||||
/** application data directory, equivalent to {@link io.anuke.arc.Settings#getDataDirectory()} */
|
||||
public static FileHandle dataDirectory;
|
||||
/** data subdirectory used for screenshots */
|
||||
@@ -126,8 +122,6 @@ public class Vars implements Loadable{
|
||||
public static FileHandle saveDirectory;
|
||||
/** data subdirectory used for plugins */
|
||||
public static FileHandle pluginDirectory;
|
||||
/** old map file extension, for conversion */
|
||||
public static final String oldMapExtension = "mmap";
|
||||
/** map file extension */
|
||||
public static final String mapExtension = "msav";
|
||||
/** save file extension */
|
||||
@@ -136,13 +130,15 @@ public class Vars implements Loadable{
|
||||
/** list of all locales that can be switched to */
|
||||
public static Locale[] locales;
|
||||
|
||||
public static Net net;
|
||||
public static ContentLoader content;
|
||||
public static GameState state;
|
||||
public static GlobalData data;
|
||||
public static EntityCollisions collisions;
|
||||
public static DefaultWaves defaultWaves;
|
||||
public static LoopControl loops;
|
||||
public static Platform platform;
|
||||
public static Platform platform = new Platform(){};
|
||||
public static Plugins plugins;
|
||||
|
||||
public static World world;
|
||||
public static Maps maps;
|
||||
@@ -168,7 +164,6 @@ public class Vars implements Loadable{
|
||||
public static EntityGroup<Fire> fireGroup;
|
||||
public static EntityGroup<BaseUnit>[] unitGroups;
|
||||
|
||||
/** all local players, currently only has one player. may be used for local co-op in the future */
|
||||
public static Player player;
|
||||
|
||||
@Override
|
||||
@@ -226,7 +221,7 @@ public class Vars implements Loadable{
|
||||
|
||||
for(EntityGroup<?> group : entities.all()){
|
||||
group.setRemoveListener(entity -> {
|
||||
if(entity instanceof SyncTrait && Net.client()){
|
||||
if(entity instanceof SyncTrait && net.client()){
|
||||
netClient.addRemovedEntity((entity).getID());
|
||||
}
|
||||
});
|
||||
@@ -252,11 +247,16 @@ public class Vars implements Loadable{
|
||||
|
||||
public static void loadSettings(){
|
||||
Core.settings.setAppName(appName);
|
||||
|
||||
if(steam){
|
||||
Core.settings.setDataDirectory(Core.files.local("saves/"));
|
||||
}
|
||||
|
||||
Core.settings.defaults("locale", "default");
|
||||
Core.keybinds.setDefaults(Binding.values());
|
||||
Core.settings.load();
|
||||
|
||||
UnitScl.dp.setProduct(settings.getInt("uiscale", 100) / 100f);
|
||||
Scl.setProduct(settings.getInt("uiscale", 100) / 100f);
|
||||
|
||||
if(!loadLocales) return;
|
||||
|
||||
@@ -268,6 +268,7 @@ public class Vars implements Loadable{
|
||||
Core.bundle = I18NBundle.createBundle(handle, locale);
|
||||
|
||||
Log.info("NOTE: external translation bundle has been loaded.");
|
||||
|
||||
if(!headless){
|
||||
Time.run(10f, () -> ui.showInfo("Note: You have successfully loaded an external translation bundle."));
|
||||
}
|
||||
|
||||
@@ -1,165 +1,261 @@
|
||||
package io.anuke.mindustry.ai;
|
||||
|
||||
import io.anuke.arc.Events;
|
||||
import io.anuke.arc.collection.IntArray;
|
||||
import io.anuke.arc.collection.IntQueue;
|
||||
import io.anuke.arc.math.geom.Geometry;
|
||||
import io.anuke.arc.math.geom.Point2;
|
||||
import io.anuke.arc.util.Structs;
|
||||
import io.anuke.arc.util.Time;
|
||||
import io.anuke.mindustry.game.EventType.TileChangeEvent;
|
||||
import io.anuke.mindustry.game.EventType.WorldLoadEvent;
|
||||
import io.anuke.mindustry.game.Team;
|
||||
import io.anuke.mindustry.game.Teams.TeamData;
|
||||
import io.anuke.mindustry.net.Net;
|
||||
import io.anuke.mindustry.world.Pos;
|
||||
import io.anuke.mindustry.world.Tile;
|
||||
import io.anuke.mindustry.world.meta.BlockFlag;
|
||||
import io.anuke.annotations.Annotations.*;
|
||||
import io.anuke.arc.*;
|
||||
import io.anuke.arc.collection.*;
|
||||
import io.anuke.arc.function.*;
|
||||
import io.anuke.arc.math.geom.*;
|
||||
import io.anuke.arc.util.*;
|
||||
import io.anuke.arc.util.async.*;
|
||||
import io.anuke.mindustry.game.EventType.*;
|
||||
import io.anuke.mindustry.game.*;
|
||||
import io.anuke.mindustry.gen.*;
|
||||
import io.anuke.mindustry.world.*;
|
||||
import io.anuke.mindustry.world.meta.*;
|
||||
|
||||
import static io.anuke.mindustry.Vars.*;
|
||||
|
||||
public class Pathfinder{
|
||||
public class Pathfinder implements Runnable{
|
||||
private static final long maxUpdate = Time.millisToNanos(4);
|
||||
private PathData[] paths;
|
||||
private IntArray blocked = new IntArray();
|
||||
private static final int updateFPS = 60;
|
||||
private static final int updateInterval = 1000 / updateFPS;
|
||||
private static final int impassable = -1;
|
||||
|
||||
/** tile data, see PathTileStruct */
|
||||
private int[][] tiles;
|
||||
/** unordered array of path data for iteration only. DO NOT iterate ot access this in the main thread.*/
|
||||
private Array<PathData> list = new Array<>();
|
||||
/** Maps teams + flags to a valid path to get to that flag for that team. */
|
||||
private PathData[][] pathMap = new PathData[Team.all.length][PathTarget.all.length];
|
||||
/** Grid map of created path data that should not be queued again. */
|
||||
private GridBits created = new GridBits(Team.all.length, PathTarget.all.length);
|
||||
/** handles task scheduling on the update thread. */
|
||||
private TaskQueue queue = new TaskQueue();
|
||||
/** current pathfinding thread */
|
||||
private @Nullable Thread thread;
|
||||
|
||||
public Pathfinder(){
|
||||
Events.on(WorldLoadEvent.class, event -> clear());
|
||||
Events.on(TileChangeEvent.class, event -> {
|
||||
if(Net.client()) return;
|
||||
Events.on(WorldLoadEvent.class, event -> {
|
||||
stop();
|
||||
|
||||
for(Team team : Team.all){
|
||||
TeamData data = state.teams.get(team);
|
||||
if(state.teams.isActive(team) && data.team != event.tile.getTeam()){
|
||||
update(event.tile, data.team);
|
||||
//reset and update internal tile array
|
||||
tiles = new int[world.width()][world.height()];
|
||||
pathMap = new PathData[Team.all.length][PathTarget.all.length];
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
update(event.tile, event.tile.getTeam());
|
||||
//special preset which may help speed things up; this is optional
|
||||
preloadPath(waveTeam, PathTarget.enemyCores);
|
||||
|
||||
start();
|
||||
});
|
||||
|
||||
Events.on(ResetEvent.class, event -> stop());
|
||||
|
||||
Events.on(TileChangeEvent.class, event -> updateTile(event.tile));
|
||||
}
|
||||
|
||||
/** Packs a tile into its internal representation. */
|
||||
private int packTile(Tile tile){
|
||||
return PathTile.get(tile.cost, tile.getTeamID(), (byte)0, (!tile.solid() || tile.breakable()) && tile.floor().drownTime <= 0f);
|
||||
}
|
||||
|
||||
/** Starts or restarts the pathfinding thread. */
|
||||
private void start(){
|
||||
stop();
|
||||
thread = Threads.daemon(this);
|
||||
}
|
||||
|
||||
/** Stops the pathfinding thread. */
|
||||
private void stop(){
|
||||
if(thread != null){
|
||||
thread.interrupt();
|
||||
thread = null;
|
||||
}
|
||||
queue.clear();
|
||||
}
|
||||
|
||||
/** Update a tile in the internal pathfinding grid. Causes a completely pathfinding reclaculation. */
|
||||
public void updateTile(Tile tile){
|
||||
if(net.client()) return;
|
||||
|
||||
int packed = packTile(tile);
|
||||
int x = tile.x, y = tile.y;
|
||||
tiles[x][y] = packed;
|
||||
|
||||
//can't iterate through array so use the map, which should not lead to problems
|
||||
for(PathData[] arr : pathMap){
|
||||
for(PathData path : arr){
|
||||
if(path != null){
|
||||
synchronized(path.targets){
|
||||
path.targets.clear();
|
||||
path.target.getTargets(path.team, path.targets);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
queue.post(() -> {
|
||||
for(PathData data : list){
|
||||
updateTargets(data, x, y);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void updateSolid(Tile tile){
|
||||
update(tile, tile.getTeam());
|
||||
}
|
||||
/** Thread implementation. */
|
||||
@Override
|
||||
public void run(){
|
||||
while(true){
|
||||
if(net.client()) return;
|
||||
|
||||
public void update(){
|
||||
if(Net.client() || paths == null) return;
|
||||
queue.run();
|
||||
|
||||
for(Team team : Team.all){
|
||||
if(state.teams.isActive(team)){
|
||||
updateFrontier(team, maxUpdate);
|
||||
//total update time no longer than maxUpdate
|
||||
for(PathData data : list){
|
||||
updateFrontier(data, maxUpdate / list.size);
|
||||
}
|
||||
|
||||
try{
|
||||
Thread.sleep(updateInterval);
|
||||
}catch(InterruptedException e){
|
||||
//stop looping when interrupted externally
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Tile getTargetTile(Team team, Tile tile){
|
||||
float[][] values = paths[team.ordinal()].weights;
|
||||
/** Gets next tile to travel to. Main thread only. */
|
||||
public Tile getTargetTile(Tile tile, Team team, PathTarget target){
|
||||
if(tile == null) return null;
|
||||
|
||||
if(values == null || tile == null) return tile;
|
||||
PathData data = pathMap[team.ordinal()][target.ordinal()];
|
||||
|
||||
float value = values[tile.x][tile.y];
|
||||
if(data == null){
|
||||
//if this combination is not found, create it on request
|
||||
if(!created.get(team.ordinal(), target.ordinal())){
|
||||
created.set(team.ordinal(), target.ordinal());
|
||||
//grab targets since this is run on main thread
|
||||
IntArray targets = target.getTargets(team, new IntArray());
|
||||
queue.post(() -> createPath(team, target, targets));
|
||||
}
|
||||
return tile;
|
||||
}
|
||||
|
||||
Tile target = null;
|
||||
float tl = 0f;
|
||||
int[][] values = data.weights;
|
||||
int value = values[tile.x][tile.y];
|
||||
|
||||
Tile current = null;
|
||||
int tl = 0;
|
||||
for(Point2 point : Geometry.d8){
|
||||
int dx = tile.x + point.x, dy = tile.y + point.y;
|
||||
|
||||
Tile other = world.tile(dx, dy);
|
||||
if(other == null) continue;
|
||||
|
||||
if(values[dx][dy] < value && (target == null || values[dx][dy] < tl) &&
|
||||
!other.solid() && other.floor().drownTime <= 0 &&
|
||||
if(values[dx][dy] < value && (current == null || values[dx][dy] < tl) && !other.solid() && other.floor().drownTime <= 0 &&
|
||||
!(point.x != 0 && point.y != 0 && (world.solid(tile.x + point.x, tile.y) || world.solid(tile.x, tile.y + point.y)))){ //diagonal corner trap
|
||||
target = other;
|
||||
current = other;
|
||||
tl = values[dx][dy];
|
||||
}
|
||||
}
|
||||
|
||||
if(target == null || tl == Float.MAX_VALUE) return tile;
|
||||
if(current == null || tl == impassable) return tile;
|
||||
|
||||
return target;
|
||||
return current;
|
||||
}
|
||||
|
||||
public float getValueforTeam(Team team, int x, int y){
|
||||
return paths == null || paths[team.ordinal()].weights == null || team.ordinal() >= paths.length ? 0 : Structs.inBounds(x, y, paths[team.ordinal()].weights) ? paths[team.ordinal()].weights[x][y] : 0;
|
||||
}
|
||||
|
||||
private boolean passable(Tile tile, Team team){
|
||||
return (!tile.solid()) || (tile.breakable() && (tile.getTeam() != team));
|
||||
/** @return whether a tile can be passed through by this team. Pathfinding thread only.*/
|
||||
private boolean passable(int x, int y, Team team){
|
||||
int tile = tiles[x][y];
|
||||
return PathTile.passable(tile) || (PathTile.team(tile) != team.ordinal() && PathTile.team(tile) != Team.derelict.ordinal());
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the frontier, increments the search and sets up all flow sources.
|
||||
* This only occurs for active teams.
|
||||
*/
|
||||
private void update(Tile tile, Team team){
|
||||
//make sure team exists
|
||||
if(paths != null && paths[team.ordinal()] != null && paths[team.ordinal()].weights != null && Structs.inBounds(tile.x, tile.y, paths[team.ordinal()].weights)){
|
||||
PathData path = paths[team.ordinal()];
|
||||
private void updateTargets(PathData path, int x, int y){
|
||||
if(!Structs.inBounds(x, y, path.weights)) return;
|
||||
|
||||
if(path.weights[tile.x][tile.y] <= 0.1f){
|
||||
//this was a previous target
|
||||
path.frontier.clear();
|
||||
}else if(!path.frontier.isEmpty()){
|
||||
return;
|
||||
}
|
||||
|
||||
//impassable tiles have a weight of float.max
|
||||
if(!passable(tile, team)){
|
||||
path.weights[tile.x][tile.y] = Float.MAX_VALUE;
|
||||
}
|
||||
|
||||
//increment search, clear frontier
|
||||
path.search++;
|
||||
if(path.weights[x][y] == 0){
|
||||
//this was a previous target
|
||||
path.frontier.clear();
|
||||
path.lastSearchTime = Time.millis();
|
||||
}else if(!path.frontier.isEmpty()){
|
||||
//skip if this path is processing
|
||||
return;
|
||||
}
|
||||
|
||||
//add all targets to the frontier
|
||||
for(Tile other : indexer.getEnemy(team, BlockFlag.target)){
|
||||
path.weights[other.x][other.y] = 0;
|
||||
path.searches[other.x][other.y] = (short)path.search;
|
||||
path.frontier.addFirst(other.pos());
|
||||
//assign impassability to the tile
|
||||
if(!passable(x, y, path.team)){
|
||||
path.weights[x][y] = impassable;
|
||||
}
|
||||
|
||||
//increment search, clear frontier
|
||||
path.search++;
|
||||
path.frontier.clear();
|
||||
|
||||
synchronized(path.targets){
|
||||
//add targets
|
||||
for(int i = 0; i < path.targets.size; i++){
|
||||
int pos = path.targets.get(i);
|
||||
int tx = Pos.x(pos), ty = Pos.y(pos);
|
||||
|
||||
path.weights[tx][ty] = 0;
|
||||
path.searches[tx][ty] = (short)path.search;
|
||||
path.frontier.addFirst(pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void createFor(Team team){
|
||||
PathData path = new PathData();
|
||||
path.weights = new float[world.width()][world.height()];
|
||||
path.searches = new short[world.width()][world.height()];
|
||||
path.search++;
|
||||
path.frontier.ensureCapacity((world.width() + world.height()) * 3);
|
||||
private void preloadPath(Team team, PathTarget target){
|
||||
updateFrontier(createPath(team, target, target.getTargets(team, new IntArray())), -1);
|
||||
}
|
||||
|
||||
paths[team.ordinal()] = path;
|
||||
/** Created a new flowfield that aims to get to a certain target for a certain team.
|
||||
* Pathfinding thread only. */
|
||||
private PathData createPath(Team team, PathTarget target, IntArray targets){
|
||||
PathData path = new PathData(team, target, world.width(), world.height());
|
||||
|
||||
list.add(path);
|
||||
pathMap[team.ordinal()][target.ordinal()] = path;
|
||||
|
||||
//grab targets from passed array
|
||||
synchronized(path.targets){
|
||||
path.targets.clear();
|
||||
path.targets.addAll(targets);
|
||||
}
|
||||
|
||||
//fill with impassables by default
|
||||
for(int x = 0; x < world.width(); x++){
|
||||
for(int y = 0; y < world.height(); y++){
|
||||
Tile tile = world.tile(x, y);
|
||||
|
||||
if(state.teams.areEnemies(tile.getTeam(), team)
|
||||
&& tile.block().flags.contains(BlockFlag.target)){
|
||||
path.frontier.addFirst(tile.pos());
|
||||
path.weights[x][y] = 0;
|
||||
path.searches[x][y] = (short)path.search;
|
||||
}else{
|
||||
path.weights[x][y] = Float.MAX_VALUE;
|
||||
}
|
||||
path.weights[x][y] = impassable;
|
||||
}
|
||||
}
|
||||
|
||||
updateFrontier(team, -1);
|
||||
//add targets
|
||||
for(int i = 0; i < path.targets.size; i++){
|
||||
int pos = path.targets.get(i);
|
||||
path.weights[Pos.x(pos)][Pos.y(pos)] = 0;
|
||||
path.frontier.addFirst(pos);
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
private void updateFrontier(Team team, long nsToRun){
|
||||
PathData path = paths[team.ordinal()];
|
||||
|
||||
/** Update the frontier for a path. Pathfinding thread only. */
|
||||
private void updateFrontier(PathData path, long nsToRun){
|
||||
long start = Time.nanos();
|
||||
|
||||
while(path.frontier.size > 0 && (nsToRun < 0 || Time.timeSinceNanos(start) <= nsToRun)){
|
||||
Tile tile = world.tile(path.frontier.removeLast());
|
||||
if(tile == null || path.weights == null) return; //something went horribly wrong, bail
|
||||
float cost = path.weights[tile.x][tile.y];
|
||||
int cost = path.weights[tile.x][tile.y];
|
||||
|
||||
//pathfinding overflowed for some reason, time to bail. the next block update will handle this, hopefully
|
||||
if(path.frontier.size >= world.width() * world.height()){
|
||||
@@ -167,14 +263,13 @@ public class Pathfinder{
|
||||
return;
|
||||
}
|
||||
|
||||
if(cost < Float.MAX_VALUE){
|
||||
if(cost != impassable){
|
||||
for(Point2 point : Geometry.d4){
|
||||
|
||||
int dx = tile.x + point.x, dy = tile.y + point.y;
|
||||
Tile other = world.tile(dx, dy);
|
||||
|
||||
if(other != null && (path.weights[dx][dy] > cost + other.cost || path.searches[dx][dy] < path.search)
|
||||
&& passable(other, team)){
|
||||
if(other != null && (path.weights[dx][dy] > cost + other.cost || path.searches[dx][dy] < path.search) && passable(dx, dy, path.team)){
|
||||
if(other.cost < 0) throw new IllegalArgumentException("Tile cost cannot be negative! " + other);
|
||||
path.frontier.addFirst(Pos.get(dx, dy));
|
||||
path.weights[dx][dy] = cost + other.cost;
|
||||
@@ -185,27 +280,78 @@ public class Pathfinder{
|
||||
}
|
||||
}
|
||||
|
||||
private void clear(){
|
||||
Time.mark();
|
||||
|
||||
paths = new PathData[Team.all.length];
|
||||
blocked.clear();
|
||||
|
||||
for(Team team : Team.all){
|
||||
PathData path = new PathData();
|
||||
paths[team.ordinal()] = path;
|
||||
|
||||
if(state.teams.isActive(team)){
|
||||
createFor(team);
|
||||
/** A path target defines a set of targets for a path.*/
|
||||
public enum PathTarget{
|
||||
enemyCores((team, out) -> {
|
||||
for(Tile other : indexer.getEnemy(team, BlockFlag.core)){
|
||||
out.add(other.pos());
|
||||
}
|
||||
|
||||
//spawn points are also enemies.
|
||||
if(state.rules.waves && team == defaultTeam){
|
||||
for(Tile other : spawner.getGroundSpawns()){
|
||||
out.add(other.pos());
|
||||
}
|
||||
}
|
||||
}),
|
||||
rallyPoints((team, out) -> {
|
||||
for(Tile other : indexer.getAllied(team, BlockFlag.rally)){
|
||||
out.add(other.pos());
|
||||
}
|
||||
});
|
||||
|
||||
public static final PathTarget[] all = values();
|
||||
|
||||
private final BiConsumer<Team, IntArray> targeter;
|
||||
|
||||
PathTarget(BiConsumer<Team, IntArray> targeter){
|
||||
this.targeter = targeter;
|
||||
}
|
||||
|
||||
/** Get targets. This must run on the main thread.*/
|
||||
public IntArray getTargets(Team team, IntArray out){
|
||||
targeter.accept(team, out);
|
||||
return out;
|
||||
}
|
||||
}
|
||||
|
||||
/** Data for a specific flow field to some set of destinations. */
|
||||
class PathData{
|
||||
float[][] weights;
|
||||
short[][] searches;
|
||||
int search = 0;
|
||||
long lastSearchTime;
|
||||
IntQueue frontier = new IntQueue();
|
||||
/** Team this path is for. */
|
||||
final Team team;
|
||||
/** Flag that is being targeted. */
|
||||
final PathTarget target;
|
||||
/** costs of getting to a specific tile */
|
||||
final int[][] weights;
|
||||
/** search IDs of each position - the highest, most recent search is prioritized and overwritten */
|
||||
final short[][] searches;
|
||||
/** search frontier, these are Pos objects */
|
||||
final IntQueue frontier = new IntQueue();
|
||||
/** all target positions; these positions have a cost of 0, and must be synchronized on! */
|
||||
final IntArray targets = new IntArray();
|
||||
/** current search ID */
|
||||
int search = 1;
|
||||
|
||||
PathData(Team team, PathTarget target, int width, int height){
|
||||
this.team = team;
|
||||
this.target = target;
|
||||
|
||||
this.weights = new int[width][height];
|
||||
this.searches = new short[width][height];
|
||||
this.frontier.ensureCapacity((width + height) * 3);
|
||||
}
|
||||
}
|
||||
|
||||
/** Holds a copy of tile data for a specific tile position. */
|
||||
@Struct
|
||||
class PathTileStruct{
|
||||
//traversal cost
|
||||
byte cost;
|
||||
//team of block, if applicable (0 by default)
|
||||
byte team;
|
||||
//type of target; TODO remove
|
||||
byte type;
|
||||
//whether it's viable to pass this block
|
||||
boolean passable;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,6 @@ import io.anuke.mindustry.entities.Effects;
|
||||
import io.anuke.mindustry.entities.type.BaseUnit;
|
||||
import io.anuke.mindustry.game.EventType.WorldLoadEvent;
|
||||
import io.anuke.mindustry.game.SpawnGroup;
|
||||
import io.anuke.mindustry.net.Net;
|
||||
import io.anuke.mindustry.world.Tile;
|
||||
|
||||
import static io.anuke.mindustry.Vars.*;
|
||||
@@ -116,7 +115,7 @@ public class WaveSpawner{
|
||||
}
|
||||
|
||||
public boolean isSpawning(){
|
||||
return spawning && !Net.client();
|
||||
return spawning && !net.client();
|
||||
}
|
||||
|
||||
private void reset(){
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package io.anuke.mindustry.content;
|
||||
|
||||
import io.anuke.arc.*;
|
||||
import io.anuke.arc.collection.*;
|
||||
import io.anuke.arc.graphics.*;
|
||||
import io.anuke.arc.graphics.g2d.*;
|
||||
import io.anuke.arc.math.*;
|
||||
@@ -8,6 +9,7 @@ import io.anuke.arc.util.*;
|
||||
import io.anuke.mindustry.*;
|
||||
import io.anuke.mindustry.entities.*;
|
||||
import io.anuke.mindustry.entities.bullet.*;
|
||||
import io.anuke.mindustry.entities.type.Bullet;
|
||||
import io.anuke.mindustry.game.*;
|
||||
import io.anuke.mindustry.gen.*;
|
||||
import io.anuke.mindustry.graphics.*;
|
||||
@@ -501,6 +503,7 @@ public class Blocks implements ContentList{
|
||||
|
||||
consumes.items(new ItemStack(Items.thorium, 4), new ItemStack(Items.sand, 10));
|
||||
consumes.power(5f);
|
||||
itemCapacity = 20;
|
||||
|
||||
int bottomRegion = reg("-bottom"), weaveRegion = reg("-weave");
|
||||
|
||||
@@ -590,7 +593,7 @@ public class Blocks implements ContentList{
|
||||
|
||||
pyratiteMixer = new GenericSmelter("pyratite-mixer"){{
|
||||
requirements(Category.crafting, ItemStack.with(Items.copper, 50, Items.lead, 25));
|
||||
flameColor = Color.CLEAR;
|
||||
flameColor = Color.clear;
|
||||
hasItems = true;
|
||||
hasPower = true;
|
||||
outputItem = new ItemStack(Items.pyratite, 1);
|
||||
@@ -661,7 +664,7 @@ public class Blocks implements ContentList{
|
||||
|
||||
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());
|
||||
@@ -986,7 +989,6 @@ public class Blocks implements ContentList{
|
||||
pulseConduit = new Conduit("pulse-conduit"){{
|
||||
requirements(Category.liquid, ItemStack.with(Items.titanium, 1, Items.metaglass, 1));
|
||||
liquidCapacity = 16f;
|
||||
liquidFlowFactor = 4.9f;
|
||||
health = 90;
|
||||
}};
|
||||
|
||||
@@ -1401,7 +1403,7 @@ public class Blocks implements ContentList{
|
||||
smokeEffect = Fx.lancerLaserShootSmoke;
|
||||
chargeEffect = Fx.lancerLaserCharge;
|
||||
chargeBeginEffect = Fx.lancerLaserChargeBegin;
|
||||
heatColor = Color.RED;
|
||||
heatColor = Color.red;
|
||||
size = 2;
|
||||
health = 280 * size * size;
|
||||
targetAir = false;
|
||||
@@ -1409,16 +1411,16 @@ public class Blocks implements ContentList{
|
||||
}};
|
||||
|
||||
arc = new PowerTurret("arc"){{
|
||||
requirements(Category.turret, ItemStack.with(Items.copper, 35, Items.lead, 35));
|
||||
requirements(Category.turret, ItemStack.with(Items.copper, 35, Items.lead, 50));
|
||||
shootType = Bullets.arc;
|
||||
reload = 24f;
|
||||
reload = 35f;
|
||||
shootCone = 40f;
|
||||
rotatespeed = 8f;
|
||||
powerUse = 0.9f;
|
||||
powerUse = 1.5f;
|
||||
targetAir = false;
|
||||
range = 95f;
|
||||
range = 90f;
|
||||
shootEffect = Fx.lightningShoot;
|
||||
heatColor = Color.RED;
|
||||
heatColor = Color.red;
|
||||
recoil = 1f;
|
||||
size = 1;
|
||||
health = 260;
|
||||
@@ -1497,7 +1499,7 @@ public class Blocks implements ContentList{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(Bullet b){
|
||||
public void init(io.anuke.mindustry.entities.type.Bullet 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);
|
||||
}
|
||||
@@ -1506,7 +1508,7 @@ public class Blocks implements ContentList{
|
||||
@Override
|
||||
public void draw(Bullet b){
|
||||
super.draw(b);
|
||||
Draw.color(Color.WHITE, Pal.lancerLaser, b.fin());
|
||||
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);
|
||||
@@ -1649,6 +1651,7 @@ public class Blocks implements ContentList{
|
||||
|
||||
commandCenter = new CommandCenter("command-center"){{
|
||||
requirements(Category.units, ItemStack.with(Items.copper, 200, Items.lead, 250, Items.silicon, 250, Items.graphite, 100));
|
||||
flags = EnumSet.of(BlockFlag.rally, BlockFlag.comandCenter);
|
||||
size = 2;
|
||||
health = size * size * 55;
|
||||
}};
|
||||
@@ -1779,4 +1782,4 @@ public class Blocks implements ContentList{
|
||||
|
||||
//endregion
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import io.anuke.mindustry.entities.*;
|
||||
import io.anuke.mindustry.entities.bullet.*;
|
||||
import io.anuke.mindustry.entities.effect.*;
|
||||
import io.anuke.mindustry.entities.type.*;
|
||||
import io.anuke.mindustry.entities.type.Bullet;
|
||||
import io.anuke.mindustry.game.*;
|
||||
import io.anuke.mindustry.graphics.*;
|
||||
import io.anuke.mindustry.world.*;
|
||||
@@ -250,7 +251,7 @@ public class Bullets implements ContentList{
|
||||
splashDamageRadius = 25f;
|
||||
splashDamage = 10f;
|
||||
lifetime = 120f;
|
||||
trailColor = Color.GRAY;
|
||||
trailColor = Color.gray;
|
||||
backColor = Pal.bulletYellowBack;
|
||||
frontColor = Pal.bulletYellow;
|
||||
hitEffect = Fx.blastExplosion;
|
||||
@@ -396,7 +397,7 @@ public class Bullets implements ContentList{
|
||||
Draw.color(Pal.heal);
|
||||
Lines.stroke(2f);
|
||||
Lines.lineAngleCenter(b.x, b.y, b.rot(), 7f);
|
||||
Draw.color(Color.WHITE);
|
||||
Draw.color(Color.white);
|
||||
Lines.lineAngleCenter(b.x, b.y, b.rot(), 3f);
|
||||
Draw.reset();
|
||||
}
|
||||
@@ -430,7 +431,7 @@ public class Bullets implements ContentList{
|
||||
|
||||
@Override
|
||||
public void draw(Bullet b){
|
||||
Draw.color(Pal.lightFlame, Pal.darkFlame, Color.GRAY, b.fin());
|
||||
Draw.color(Pal.lightFlame, Pal.darkFlame, Color.gray, b.fin());
|
||||
Fill.circle(b.x, b.y, 3f * b.fout());
|
||||
Draw.reset();
|
||||
}
|
||||
@@ -498,7 +499,7 @@ public class Bullets implements ContentList{
|
||||
};
|
||||
|
||||
lancerLaser = new BulletType(0.001f, 140){
|
||||
Color[] colors = {Pal.lancerLaser.cpy().mul(1f, 1f, 1f, 0.4f), Pal.lancerLaser, Color.WHITE};
|
||||
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;
|
||||
@@ -540,7 +541,7 @@ public class Bullets implements ContentList{
|
||||
|
||||
meltdownLaser = new BulletType(0.001f, 70){
|
||||
Color tmpColor = new Color();
|
||||
Color[] colors = {Color.valueOf("ec745855"), Color.valueOf("ec7458aa"), Color.valueOf("ff9c5a"), Color.WHITE};
|
||||
Color[] colors = {Color.valueOf("ec745855"), Color.valueOf("ec7458aa"), Color.valueOf("ff9c5a"), Color.white};
|
||||
float[] tscales = {1f, 0.7f, 0.5f, 0.2f};
|
||||
float[] strokes = {2f, 1.5f, 1f, 0.3f};
|
||||
float[] lenscales = {1f, 1.12f, 1.15f, 1.17f};
|
||||
@@ -636,7 +637,7 @@ public class Bullets implements ContentList{
|
||||
}
|
||||
};
|
||||
|
||||
arc = new BulletType(0.001f, 25){
|
||||
arc = new BulletType(0.001f, 21){
|
||||
{
|
||||
lifetime = 1;
|
||||
despawnEffect = Fx.none;
|
||||
@@ -688,7 +689,7 @@ public class Bullets implements ContentList{
|
||||
bulletHeight = 12f;
|
||||
hitEffect = Fx.pulverize;
|
||||
backColor = new Color(0x4f4f4fff);
|
||||
frontColor = Color.GRAY;
|
||||
frontColor = Color.gray;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,19 +1,17 @@
|
||||
package io.anuke.mindustry.content;
|
||||
|
||||
import io.anuke.arc.Core;
|
||||
import io.anuke.arc.graphics.Color;
|
||||
import io.anuke.arc.*;
|
||||
import io.anuke.arc.graphics.*;
|
||||
import io.anuke.arc.graphics.g2d.*;
|
||||
import io.anuke.arc.math.Angles;
|
||||
import io.anuke.arc.math.Mathf;
|
||||
import io.anuke.arc.util.Tmp;
|
||||
import io.anuke.mindustry.entities.Effects.Effect;
|
||||
import io.anuke.mindustry.entities.effect.GroundEffectEntity.GroundEffect;
|
||||
import io.anuke.mindustry.entities.type.BaseUnit;
|
||||
import io.anuke.mindustry.game.ContentList;
|
||||
import io.anuke.mindustry.graphics.Pal;
|
||||
import io.anuke.mindustry.graphics.Drawf;
|
||||
import io.anuke.mindustry.type.Item;
|
||||
import io.anuke.mindustry.type.Item.Icon;
|
||||
import io.anuke.arc.math.*;
|
||||
import io.anuke.arc.util.*;
|
||||
import io.anuke.mindustry.entities.Effects.*;
|
||||
import io.anuke.mindustry.entities.effect.GroundEffectEntity.*;
|
||||
import io.anuke.mindustry.entities.type.*;
|
||||
import io.anuke.mindustry.game.*;
|
||||
import io.anuke.mindustry.graphics.*;
|
||||
import io.anuke.mindustry.type.*;
|
||||
import io.anuke.mindustry.type.Item.*;
|
||||
|
||||
import static io.anuke.mindustry.Vars.tilesize;
|
||||
|
||||
@@ -32,7 +30,7 @@ public class Fx implements ContentList{
|
||||
bigShockwave, nuclearShockwave, explosion, blockExplosion, blockExplosionSmoke, shootSmall, shootHeal, shootSmallSmoke, shootBig, shootBig2, shootBigSmoke,
|
||||
shootBigSmoke2, shootSmallFlame, shootPyraFlame, shootLiquid, shellEjectSmall, shellEjectMedium,
|
||||
shellEjectBig, lancerLaserShoot, lancerLaserShootSmoke, lancerLaserCharge, lancerLaserChargeBegin, lightningCharge, lightningShoot,
|
||||
unitSpawn, spawnShockwave, magmasmoke, impactShockwave, impactcloud, impactsmoke, dynamicExplosion, padlaunch, commandSend;
|
||||
unitSpawn, spawnShockwave, magmasmoke, impactShockwave, impactcloud, impactsmoke, dynamicExplosion, padlaunch, commandSend, coreLand;
|
||||
|
||||
@Override
|
||||
public void load(){
|
||||
@@ -93,14 +91,14 @@ public class Fx implements ContentList{
|
||||
});
|
||||
|
||||
smoke = new Effect(100, e -> {
|
||||
Draw.color(Color.GRAY, Pal.darkishGray, e.fin());
|
||||
Draw.color(Color.gray, Pal.darkishGray, e.fin());
|
||||
float size = 7f - e.fin() * 7f;
|
||||
Draw.rect("circle", e.x, e.y, size, size);
|
||||
Draw.reset();
|
||||
});
|
||||
|
||||
magmasmoke = new Effect(110, e -> {
|
||||
Draw.color(Color.GRAY);
|
||||
Draw.color(Color.gray);
|
||||
Fill.circle(e.x, e.y, e.fslope() * 6f);
|
||||
Draw.reset();
|
||||
});
|
||||
@@ -180,7 +178,7 @@ public class Fx implements ContentList{
|
||||
|
||||
|
||||
hitBulletSmall = new Effect(14, e -> {
|
||||
Draw.color(Color.WHITE, Pal.lightOrange, e.fin());
|
||||
Draw.color(Color.white, Pal.lightOrange, e.fin());
|
||||
|
||||
e.scaled(7f, s -> {
|
||||
Lines.stroke(0.5f + s.fout());
|
||||
@@ -199,7 +197,7 @@ public class Fx implements ContentList{
|
||||
});
|
||||
|
||||
hitFuse = new Effect(14, e -> {
|
||||
Draw.color(Color.WHITE, Pal.surge, e.fin());
|
||||
Draw.color(Color.white, Pal.surge, e.fin());
|
||||
|
||||
e.scaled(7f, s -> {
|
||||
Lines.stroke(0.5f + s.fout());
|
||||
@@ -218,7 +216,7 @@ public class Fx implements ContentList{
|
||||
});
|
||||
|
||||
hitBulletBig = new Effect(13, e -> {
|
||||
Draw.color(Color.WHITE, Pal.lightOrange, e.fin());
|
||||
Draw.color(Color.white, Pal.lightOrange, e.fin());
|
||||
Lines.stroke(0.5f + e.fout() * 1.5f);
|
||||
|
||||
Angles.randLenVectors(e.id, 8, e.finpow() * 30f, e.rotation, 50f, (x, y) -> {
|
||||
@@ -252,7 +250,7 @@ public class Fx implements ContentList{
|
||||
});
|
||||
|
||||
hitLancer = new Effect(12, e -> {
|
||||
Draw.color(Color.WHITE);
|
||||
Draw.color(Color.white);
|
||||
Lines.stroke(e.fout() * 1.5f);
|
||||
|
||||
Angles.randLenVectors(e.id, 8, e.finpow() * 17f, e.rotation, 360f, (x, y) -> {
|
||||
@@ -276,14 +274,14 @@ public class Fx implements ContentList{
|
||||
});
|
||||
|
||||
hitLaser = new Effect(8, e -> {
|
||||
Draw.color(Color.WHITE, Pal.heal, e.fin());
|
||||
Draw.color(Color.white, Pal.heal, e.fin());
|
||||
Lines.stroke(0.5f + e.fout());
|
||||
Lines.circle(e.x, e.y, e.fin() * 5f);
|
||||
Draw.reset();
|
||||
});
|
||||
|
||||
despawn = new Effect(12, e -> {
|
||||
Draw.color(Pal.lighterOrange, Color.GRAY, e.fin());
|
||||
Draw.color(Pal.lighterOrange, Color.gray, e.fin());
|
||||
Lines.stroke(e.fout());
|
||||
|
||||
Angles.randLenVectors(e.id, 7, e.fin() * 7f, e.rotation, 40f, (x, y) -> {
|
||||
@@ -302,7 +300,7 @@ public class Fx implements ContentList{
|
||||
Lines.circle(e.x, e.y, 3f + i.fin() * 10f);
|
||||
});
|
||||
|
||||
Draw.color(Color.GRAY);
|
||||
Draw.color(Color.gray);
|
||||
|
||||
Angles.randLenVectors(e.id, 5, 2f + 23f * e.finpow(), (x, y) -> {
|
||||
Fill.circle(e.x + x, e.y + y, e.fout() * 3f + 0.5f);
|
||||
@@ -326,7 +324,7 @@ public class Fx implements ContentList{
|
||||
Lines.circle(e.x, e.y, 3f + i.fin() * 24f);
|
||||
});
|
||||
|
||||
Draw.color(Color.GRAY);
|
||||
Draw.color(Color.gray);
|
||||
|
||||
Angles.randLenVectors(e.id, 7, 2f + 28f * e.finpow(), (x, y) -> {
|
||||
Fill.circle(e.x + x, e.y + y, e.fout() * 4f + 0.5f);
|
||||
@@ -350,7 +348,7 @@ public class Fx implements ContentList{
|
||||
Lines.circle(e.x, e.y, 3f + i.fin() * 34f);
|
||||
});
|
||||
|
||||
Draw.color(Color.GRAY);
|
||||
Draw.color(Color.gray);
|
||||
|
||||
Angles.randLenVectors(e.id, 7, 2f + 30f * e.finpow(), (x, y) -> {
|
||||
Fill.circle(e.x + x, e.y + y, e.fout() * 4f + 0.5f);
|
||||
@@ -374,7 +372,7 @@ public class Fx implements ContentList{
|
||||
Lines.circle(e.x, e.y, 3f + i.fin() * 15f);
|
||||
});
|
||||
|
||||
Draw.color(Color.GRAY);
|
||||
Draw.color(Color.gray);
|
||||
|
||||
Angles.randLenVectors(e.id, 5, 2f + 23f * e.finpow(), (x, y) -> {
|
||||
Fill.circle(e.x + x, e.y + y, e.fout() * 4f + 0.5f);
|
||||
@@ -423,7 +421,7 @@ public class Fx implements ContentList{
|
||||
Lines.circle(e.x, e.y, 3f + i.fin() * 25f);
|
||||
});
|
||||
|
||||
Draw.color(Color.GRAY);
|
||||
Draw.color(Color.gray);
|
||||
|
||||
Angles.randLenVectors(e.id, 6, 2f + 23f * e.finpow(), (x, y) -> {
|
||||
Fill.circle(e.x + x, e.y + y, e.fout() * 4f + 0.5f);
|
||||
@@ -461,7 +459,7 @@ public class Fx implements ContentList{
|
||||
});
|
||||
|
||||
fireSmoke = new Effect(35f, e -> {
|
||||
Draw.color(Color.GRAY);
|
||||
Draw.color(Color.gray);
|
||||
|
||||
Angles.randLenVectors(e.id, 1, 2f + e.fin() * 7f, (x, y) -> {
|
||||
Fill.circle(e.x + x, e.y + y, 0.2f + e.fslope() * 1.5f);
|
||||
@@ -471,7 +469,7 @@ public class Fx implements ContentList{
|
||||
});
|
||||
|
||||
steam = new Effect(35f, e -> {
|
||||
Draw.color(Color.LIGHT_GRAY);
|
||||
Draw.color(Color.lightGray);
|
||||
|
||||
Angles.randLenVectors(e.id, 2, 2f + e.fin() * 7f, (x, y) -> {
|
||||
Fill.circle(e.x + x, e.y + y, 0.2f + e.fslope() * 1.5f);
|
||||
@@ -481,7 +479,7 @@ public class Fx implements ContentList{
|
||||
});
|
||||
|
||||
fireballsmoke = new Effect(25f, e -> {
|
||||
Draw.color(Color.GRAY);
|
||||
Draw.color(Color.gray);
|
||||
|
||||
Angles.randLenVectors(e.id, 1, 2f + e.fin() * 7f, (x, y) -> {
|
||||
Fill.circle(e.x + x, e.y + y, 0.2f + e.fout() * 1.5f);
|
||||
@@ -511,7 +509,7 @@ public class Fx implements ContentList{
|
||||
});
|
||||
|
||||
melting = new Effect(40f, e -> {
|
||||
Draw.color(Liquids.slag.color, Color.WHITE, e.fout() / 5f + Mathf.randomSeedRange(e.id, 0.12f));
|
||||
Draw.color(Liquids.slag.color, Color.white, e.fout() / 5f + Mathf.randomSeedRange(e.id, 0.12f));
|
||||
|
||||
Angles.randLenVectors(e.id, 2, 1f + e.fin() * 3f, (x, y) -> {
|
||||
Fill.circle(e.x + x, e.y + y, .2f + e.fout() * 1.2f);
|
||||
@@ -559,35 +557,35 @@ public class Fx implements ContentList{
|
||||
|
||||
|
||||
shockwave = new Effect(10f, 80f, e -> {
|
||||
Draw.color(Color.WHITE, Color.LIGHT_GRAY, e.fin());
|
||||
Draw.color(Color.white, Color.lightGray, e.fin());
|
||||
Lines.stroke(e.fout() * 2f + 0.2f);
|
||||
Lines.circle(e.x, e.y, e.fin() * 28f);
|
||||
Draw.reset();
|
||||
});
|
||||
|
||||
bigShockwave = new Effect(10f, 80f, e -> {
|
||||
Draw.color(Color.WHITE, Color.LIGHT_GRAY, e.fin());
|
||||
Draw.color(Color.white, Color.lightGray, e.fin());
|
||||
Lines.stroke(e.fout() * 3f);
|
||||
Lines.circle(e.x, e.y, e.fin() * 50f);
|
||||
Draw.reset();
|
||||
});
|
||||
|
||||
nuclearShockwave = new Effect(10f, 200f, e -> {
|
||||
Draw.color(Color.WHITE, Color.LIGHT_GRAY, e.fin());
|
||||
Draw.color(Color.white, Color.lightGray, e.fin());
|
||||
Lines.stroke(e.fout() * 3f + 0.2f);
|
||||
Lines.circle(e.x, e.y, e.fin() * 140f);
|
||||
Draw.reset();
|
||||
});
|
||||
|
||||
impactShockwave = new Effect(13f, 300f, e -> {
|
||||
Draw.color(Pal.lighterOrange, Color.LIGHT_GRAY, e.fin());
|
||||
Draw.color(Pal.lighterOrange, Color.lightGray, e.fin());
|
||||
Lines.stroke(e.fout() * 4f + 0.2f);
|
||||
Lines.circle(e.x, e.y, e.fin() * 200f);
|
||||
Draw.reset();
|
||||
});
|
||||
|
||||
spawnShockwave = new Effect(20f, 400f, e -> {
|
||||
Draw.color(Color.WHITE, Color.LIGHT_GRAY, e.fin());
|
||||
Draw.color(Color.white, Color.lightGray, e.fin());
|
||||
Lines.stroke(e.fout() * 3f + 0.5f);
|
||||
Lines.circle(e.x, e.y, e.fin() * (e.rotation + 50f));
|
||||
Draw.reset();
|
||||
@@ -599,14 +597,14 @@ public class Fx implements ContentList{
|
||||
Lines.circle(e.x, e.y, 3f + i.fin() * 10f);
|
||||
});
|
||||
|
||||
Draw.color(Color.GRAY);
|
||||
Draw.color(Color.gray);
|
||||
|
||||
Angles.randLenVectors(e.id, 6, 2f + 19f * e.finpow(), (x, y) -> {
|
||||
Fill.circle(e.x + x, e.y + y, e.fout() * 3f + 0.5f);
|
||||
Fill.circle(e.x + x / 2f, e.y + y / 2f, e.fout() * 1f);
|
||||
});
|
||||
|
||||
Draw.color(Pal.lighterOrange, Pal.lightOrange, Color.GRAY, e.fin());
|
||||
Draw.color(Pal.lighterOrange, Pal.lightOrange, Color.gray, e.fin());
|
||||
Lines.stroke(1.5f * e.fout());
|
||||
|
||||
Angles.randLenVectors(e.id + 1, 8, 1f + 23f * e.finpow(), (x, y) -> {
|
||||
@@ -624,14 +622,14 @@ public class Fx implements ContentList{
|
||||
Lines.circle(e.x, e.y, (3f + i.fin() * 14f) * intensity);
|
||||
});
|
||||
|
||||
Draw.color(Color.GRAY);
|
||||
Draw.color(Color.gray);
|
||||
|
||||
Angles.randLenVectors(e.id, e.finpow(), (int)(6 * intensity), 21f * intensity, (x, y, in, out) -> {
|
||||
Fill.circle(e.x + x, e.y + y, out * (2f + intensity) * 3 + 0.5f);
|
||||
Fill.circle(e.x + x / 2f, e.y + y / 2f, out * (intensity) * 3);
|
||||
});
|
||||
|
||||
Draw.color(Pal.lighterOrange, Pal.lightOrange, Color.GRAY, e.fin());
|
||||
Draw.color(Pal.lighterOrange, Pal.lightOrange, Color.gray, e.fin());
|
||||
Lines.stroke((1.7f * e.fout()) * (1f + (intensity - 1f) / 2f));
|
||||
|
||||
Angles.randLenVectors(e.id + 1, e.finpow(), (int)(9 * intensity), 40f * intensity, (x, y, in, out) -> {
|
||||
@@ -647,14 +645,14 @@ public class Fx implements ContentList{
|
||||
Lines.circle(e.x, e.y, 3f + i.fin() * 14f);
|
||||
});
|
||||
|
||||
Draw.color(Color.GRAY);
|
||||
Draw.color(Color.gray);
|
||||
|
||||
Angles.randLenVectors(e.id, 6, 2f + 19f * e.finpow(), (x, y) -> {
|
||||
Fill.circle(e.x + x, e.y + y, e.fout() * 3f + 0.5f);
|
||||
Fill.circle(e.x + x / 2f, e.y + y / 2f, e.fout() * 1f);
|
||||
});
|
||||
|
||||
Draw.color(Pal.lighterOrange, Pal.lightOrange, Color.GRAY, e.fin());
|
||||
Draw.color(Pal.lighterOrange, Pal.lightOrange, Color.gray, e.fin());
|
||||
Lines.stroke(1.7f * e.fout());
|
||||
|
||||
Angles.randLenVectors(e.id + 1, 9, 1f + 23f * e.finpow(), (x, y) -> {
|
||||
@@ -665,7 +663,7 @@ public class Fx implements ContentList{
|
||||
});
|
||||
|
||||
blockExplosionSmoke = new Effect(30, e -> {
|
||||
Draw.color(Color.GRAY);
|
||||
Draw.color(Color.gray);
|
||||
|
||||
Angles.randLenVectors(e.id, 6, 4f + 30f * e.finpow(), (x, y) -> {
|
||||
Fill.circle(e.x + x, e.y + y, e.fout() * 3f);
|
||||
@@ -693,7 +691,7 @@ public class Fx implements ContentList{
|
||||
});
|
||||
|
||||
shootSmallSmoke = new Effect(20f, e -> {
|
||||
Draw.color(Pal.lighterOrange, Color.LIGHT_GRAY, Color.GRAY, e.fin());
|
||||
Draw.color(Pal.lighterOrange, Color.lightGray, Color.gray, e.fin());
|
||||
|
||||
Angles.randLenVectors(e.id, 5, e.finpow() * 6f, e.rotation, 20f, (x, y) -> {
|
||||
Fill.circle(e.x + x, e.y + y, e.fout() * 1.5f);
|
||||
@@ -711,7 +709,7 @@ public class Fx implements ContentList{
|
||||
});
|
||||
|
||||
shootBig2 = new Effect(10, e -> {
|
||||
Draw.color(Pal.lightOrange, Color.GRAY, e.fin());
|
||||
Draw.color(Pal.lightOrange, Color.gray, e.fin());
|
||||
float w = 1.2f + 8 * e.fout();
|
||||
Drawf.tri(e.x, e.y, w, 29f * e.fout(), e.rotation);
|
||||
Drawf.tri(e.x, e.y, w, 5f * e.fout(), e.rotation + 180f);
|
||||
@@ -719,7 +717,7 @@ public class Fx implements ContentList{
|
||||
});
|
||||
|
||||
shootBigSmoke = new Effect(17f, e -> {
|
||||
Draw.color(Pal.lighterOrange, Color.LIGHT_GRAY, Color.GRAY, e.fin());
|
||||
Draw.color(Pal.lighterOrange, Color.lightGray, Color.gray, e.fin());
|
||||
|
||||
Angles.randLenVectors(e.id, 8, e.finpow() * 19f, e.rotation, 10f, (x, y) -> {
|
||||
Fill.circle(e.x + x, e.y + y, e.fout() * 2f + 0.2f);
|
||||
@@ -729,7 +727,7 @@ public class Fx implements ContentList{
|
||||
});
|
||||
|
||||
shootBigSmoke2 = new Effect(18f, e -> {
|
||||
Draw.color(Pal.lightOrange, Color.LIGHT_GRAY, Color.GRAY, e.fin());
|
||||
Draw.color(Pal.lightOrange, Color.lightGray, Color.gray, e.fin());
|
||||
|
||||
Angles.randLenVectors(e.id, 9, e.finpow() * 23f, e.rotation, 20f, (x, y) -> {
|
||||
Fill.circle(e.x + x, e.y + y, e.fout() * 2.4f + 0.2f);
|
||||
@@ -739,7 +737,7 @@ public class Fx implements ContentList{
|
||||
});
|
||||
|
||||
shootSmallFlame = new Effect(32f, e -> {
|
||||
Draw.color(Pal.lightFlame, Pal.darkFlame, Color.GRAY, e.fin());
|
||||
Draw.color(Pal.lightFlame, Pal.darkFlame, Color.gray, e.fin());
|
||||
|
||||
Angles.randLenVectors(e.id, 8, e.finpow() * 60f, e.rotation, 10f, (x, y) -> {
|
||||
Fill.circle(e.x + x, e.y + y, 0.65f + e.fout() * 1.5f);
|
||||
@@ -749,7 +747,7 @@ public class Fx implements ContentList{
|
||||
});
|
||||
|
||||
shootPyraFlame = new Effect(33f, e -> {
|
||||
Draw.color(Pal.lightPyraFlame, Pal.darkPyraFlame, Color.GRAY, e.fin());
|
||||
Draw.color(Pal.lightPyraFlame, Pal.darkPyraFlame, Color.gray, e.fin());
|
||||
|
||||
Angles.randLenVectors(e.id, 10, e.finpow() * 70f, e.rotation, 10f, (x, y) -> {
|
||||
Fill.circle(e.x + x, e.y + y, 0.65f + e.fout() * 1.6f);
|
||||
@@ -759,7 +757,7 @@ public class Fx implements ContentList{
|
||||
});
|
||||
|
||||
shootLiquid = new Effect(40f, e -> {
|
||||
Draw.color(e.color, Color.WHITE, e.fout() / 6f + Mathf.randomSeedRange(e.id, 0.1f));
|
||||
Draw.color(e.color, Color.white, e.fout() / 6f + Mathf.randomSeedRange(e.id, 0.1f));
|
||||
|
||||
Angles.randLenVectors(e.id, 6, e.finpow() * 60f, e.rotation, 11f, (x, y) -> {
|
||||
Fill.circle(e.x + x, e.y + y, 0.5f + e.fout() * 2.5f);
|
||||
@@ -769,7 +767,7 @@ public class Fx implements ContentList{
|
||||
});
|
||||
|
||||
shellEjectSmall = new GroundEffect(30f, 400f, e -> {
|
||||
Draw.color(Pal.lightOrange, Color.LIGHT_GRAY, Pal.lightishGray, e.fin());
|
||||
Draw.color(Pal.lightOrange, Color.lightGray, Pal.lightishGray, e.fin());
|
||||
float rot = Math.abs(e.rotation) + 90f;
|
||||
|
||||
int i = Mathf.sign(e.rotation);
|
||||
@@ -784,7 +782,7 @@ public class Fx implements ContentList{
|
||||
});
|
||||
|
||||
shellEjectMedium = new GroundEffect(34f, 400f, e -> {
|
||||
Draw.color(Pal.lightOrange, Color.LIGHT_GRAY, Pal.lightishGray, e.fin());
|
||||
Draw.color(Pal.lightOrange, Color.lightGray, Pal.lightishGray, e.fin());
|
||||
float rot = e.rotation + 90f;
|
||||
for(int i : Mathf.signs){
|
||||
float len = (2f + e.finpow() * 10f) * i;
|
||||
@@ -795,7 +793,7 @@ public class Fx implements ContentList{
|
||||
2f, 3f, rot);
|
||||
}
|
||||
|
||||
Draw.color(Color.LIGHT_GRAY, Color.GRAY, e.fin());
|
||||
Draw.color(Color.lightGray, Color.gray, e.fin());
|
||||
|
||||
for(int i : Mathf.signs){
|
||||
float ex = e.x, ey = e.y, fout = e.fout();
|
||||
@@ -808,7 +806,7 @@ public class Fx implements ContentList{
|
||||
});
|
||||
|
||||
shellEjectBig = new GroundEffect(22f, 400f, e -> {
|
||||
Draw.color(Pal.lightOrange, Color.LIGHT_GRAY, Pal.lightishGray, e.fin());
|
||||
Draw.color(Pal.lightOrange, Color.lightGray, Pal.lightishGray, e.fin());
|
||||
float rot = e.rotation + 90f;
|
||||
for(int i : Mathf.signs){
|
||||
float len = (4f + e.finpow() * 8f) * i;
|
||||
@@ -820,7 +818,7 @@ public class Fx implements ContentList{
|
||||
rot + e.fin() * 30f * i + Mathf.randomSeedRange(e.id + i + 9, 40f * e.fin()));
|
||||
}
|
||||
|
||||
Draw.color(Color.LIGHT_GRAY);
|
||||
Draw.color(Color.lightGray);
|
||||
|
||||
for(int i : Mathf.signs){
|
||||
float ex = e.x, ey = e.y, fout = e.fout();
|
||||
@@ -881,7 +879,7 @@ public class Fx implements ContentList{
|
||||
});
|
||||
|
||||
lightningShoot = new Effect(12f, e -> {
|
||||
Draw.color(Color.WHITE, Pal.lancerLaser, e.fin());
|
||||
Draw.color(Color.white, Pal.lancerLaser, e.fin());
|
||||
Lines.stroke(e.fout() * 1.2f + 0.5f);
|
||||
|
||||
Angles.randLenVectors(e.id, 7, 25f * e.finpow(), e.rotation, 50f, (x, y) -> {
|
||||
@@ -895,7 +893,7 @@ public class Fx implements ContentList{
|
||||
reactorsmoke = new Effect(17, e -> {
|
||||
Angles.randLenVectors(e.id, 4, e.fin() * 8f, (x, y) -> {
|
||||
float size = 1f + e.fout() * 5f;
|
||||
Draw.color(Color.LIGHT_GRAY, Color.GRAY, e.fin());
|
||||
Draw.color(Color.lightGray, Color.gray, e.fin());
|
||||
Draw.rect("circle", e.x + x, e.y + y, size, size);
|
||||
Draw.reset();
|
||||
});
|
||||
@@ -903,7 +901,7 @@ public class Fx implements ContentList{
|
||||
nuclearsmoke = new Effect(40, e -> {
|
||||
Angles.randLenVectors(e.id, 4, e.fin() * 13f, (x, y) -> {
|
||||
float size = e.fslope() * 4f;
|
||||
Draw.color(Color.LIGHT_GRAY, Color.GRAY, e.fin());
|
||||
Draw.color(Color.lightGray, Color.gray, e.fin());
|
||||
Draw.rect("circle", e.x + x, e.y + y, size, size);
|
||||
Draw.reset();
|
||||
});
|
||||
@@ -911,7 +909,7 @@ public class Fx implements ContentList{
|
||||
nuclearcloud = new Effect(90, 200f, e -> {
|
||||
Angles.randLenVectors(e.id, 10, e.finpow() * 90f, (x, y) -> {
|
||||
float size = e.fout() * 14f;
|
||||
Draw.color(Color.LIME, Color.GRAY, e.fin());
|
||||
Draw.color(Color.lime, Color.gray, e.fin());
|
||||
Draw.rect("circle", e.x + x, e.y + y, size, size);
|
||||
Draw.reset();
|
||||
});
|
||||
@@ -919,7 +917,7 @@ public class Fx implements ContentList{
|
||||
impactsmoke = new Effect(60, e -> {
|
||||
Angles.randLenVectors(e.id, 7, e.fin() * 20f, (x, y) -> {
|
||||
float size = e.fslope() * 4f;
|
||||
Draw.color(Color.LIGHT_GRAY, Color.GRAY, e.fin());
|
||||
Draw.color(Color.lightGray, Color.gray, e.fin());
|
||||
Draw.rect("circle", e.x + x, e.y + y, size, size);
|
||||
Draw.reset();
|
||||
});
|
||||
@@ -927,7 +925,7 @@ public class Fx implements ContentList{
|
||||
impactcloud = new Effect(140, 400f, e -> {
|
||||
Angles.randLenVectors(e.id, 20, e.finpow() * 160f, (x, y) -> {
|
||||
float size = e.fout() * 15f;
|
||||
Draw.color(Pal.lighterOrange, Color.LIGHT_GRAY, e.fin());
|
||||
Draw.color(Pal.lighterOrange, Color.lightGray, e.fin());
|
||||
Draw.rect("circle", e.x + x, e.y + y, size, size);
|
||||
Draw.reset();
|
||||
});
|
||||
@@ -935,7 +933,7 @@ public class Fx implements ContentList{
|
||||
redgeneratespark = new Effect(18, e -> {
|
||||
Angles.randLenVectors(e.id, 5, e.fin() * 8f, (x, y) -> {
|
||||
float len = e.fout() * 4f;
|
||||
Draw.color(Pal.redSpark, Color.GRAY, e.fin());
|
||||
Draw.color(Pal.redSpark, Color.gray, e.fin());
|
||||
Draw.rect("circle", e.x + x, e.y + y, len, len);
|
||||
Draw.reset();
|
||||
});
|
||||
@@ -943,7 +941,7 @@ public class Fx implements ContentList{
|
||||
generatespark = new Effect(18, e -> {
|
||||
Angles.randLenVectors(e.id, 5, e.fin() * 8f, (x, y) -> {
|
||||
float len = e.fout() * 4f;
|
||||
Draw.color(Pal.orangeSpark, Color.GRAY, e.fin());
|
||||
Draw.color(Pal.orangeSpark, Color.gray, e.fin());
|
||||
Draw.rect("circle", e.x + x, e.y + y, len, len);
|
||||
Draw.reset();
|
||||
});
|
||||
@@ -951,14 +949,14 @@ public class Fx implements ContentList{
|
||||
fuelburn = new Effect(23, e -> {
|
||||
Angles.randLenVectors(e.id, 5, e.fin() * 9f, (x, y) -> {
|
||||
float len = e.fout() * 4f;
|
||||
Draw.color(Color.LIGHT_GRAY, Color.GRAY, e.fin());
|
||||
Draw.color(Color.lightGray, Color.gray, e.fin());
|
||||
Draw.rect("circle", e.x + x, e.y + y, len, len);
|
||||
Draw.reset();
|
||||
});
|
||||
});
|
||||
plasticburn = new Effect(40, e -> {
|
||||
Angles.randLenVectors(e.id, 5, 3f + e.fin() * 5f, (x, y) -> {
|
||||
Draw.color(Color.valueOf("e9ead3"), Color.GRAY, e.fin());
|
||||
Draw.color(Color.valueOf("e9ead3"), Color.gray, e.fin());
|
||||
Fill.circle(e.x + x, e.y + y, e.fout() * 1f);
|
||||
Draw.reset();
|
||||
});
|
||||
@@ -1000,21 +998,21 @@ public class Fx implements ContentList{
|
||||
});
|
||||
producesmoke = new Effect(12, e -> {
|
||||
Angles.randLenVectors(e.id, 8, 4f + e.fin() * 18f, (x, y) -> {
|
||||
Draw.color(Color.WHITE, Pal.accent, e.fin());
|
||||
Draw.color(Color.white, Pal.accent, e.fin());
|
||||
Fill.square(e.x + x, e.y + y, 1f + e.fout() * 3f, 45);
|
||||
Draw.reset();
|
||||
});
|
||||
});
|
||||
smeltsmoke = new Effect(15, e -> {
|
||||
Angles.randLenVectors(e.id, 6, 4f + e.fin() * 5f, (x, y) -> {
|
||||
Draw.color(Color.WHITE, e.color, e.fin());
|
||||
Draw.color(Color.white, e.color, e.fin());
|
||||
Fill.square(e.x + x, e.y + y, 0.5f + e.fout() * 2f, 45);
|
||||
Draw.reset();
|
||||
});
|
||||
});
|
||||
formsmoke = new Effect(40, e -> {
|
||||
Angles.randLenVectors(e.id, 6, 5f + e.fin() * 8f, (x, y) -> {
|
||||
Draw.color(Pal.plasticSmoke, Color.LIGHT_GRAY, e.fin());
|
||||
Draw.color(Pal.plasticSmoke, Color.lightGray, e.fin());
|
||||
Fill.square(e.x + x, e.y + y, 0.2f + e.fout() * 2f, 45);
|
||||
Draw.reset();
|
||||
});
|
||||
@@ -1022,7 +1020,7 @@ public class Fx implements ContentList{
|
||||
blastsmoke = new Effect(26, e -> {
|
||||
Angles.randLenVectors(e.id, 12, 1f + e.fin() * 23f, (x, y) -> {
|
||||
float size = 2f + e.fout() * 6f;
|
||||
Draw.color(Color.LIGHT_GRAY, Color.DARK_GRAY, e.fin());
|
||||
Draw.color(Color.lightGray, Color.darkGray, e.fin());
|
||||
Draw.rect("circle", e.x + x, e.y + y, size, size);
|
||||
Draw.reset();
|
||||
});
|
||||
@@ -1030,7 +1028,7 @@ public class Fx implements ContentList{
|
||||
lava = new Effect(18, e -> {
|
||||
Angles.randLenVectors(e.id, 3, 1f + e.fin() * 10f, (x, y) -> {
|
||||
float size = e.fslope() * 4f;
|
||||
Draw.color(Color.ORANGE, Color.GRAY, e.fin());
|
||||
Draw.color(Color.orange, Color.gray, e.fin());
|
||||
Draw.rect("circle", e.x + x, e.y + y, size, size);
|
||||
Draw.reset();
|
||||
});
|
||||
@@ -1056,53 +1054,53 @@ public class Fx implements ContentList{
|
||||
Draw.reset();
|
||||
});
|
||||
purify = new Effect(10, e -> {
|
||||
Draw.color(Color.ROYAL, Color.GRAY, e.fin());
|
||||
Draw.color(Color.royal, Color.gray, e.fin());
|
||||
Lines.stroke(2f);
|
||||
Lines.spikes(e.x, e.y, e.fin() * 4f, 2, 6);
|
||||
Draw.reset();
|
||||
});
|
||||
purifyoil = new Effect(10, e -> {
|
||||
Draw.color(Color.BLACK, Color.GRAY, e.fin());
|
||||
Draw.color(Color.black, Color.gray, e.fin());
|
||||
Lines.stroke(2f);
|
||||
Lines.spikes(e.x, e.y, e.fin() * 4f, 2, 6);
|
||||
Draw.reset();
|
||||
});
|
||||
purifystone = new Effect(10, e -> {
|
||||
Draw.color(Color.ORANGE, Color.GRAY, e.fin());
|
||||
Draw.color(Color.orange, Color.gray, e.fin());
|
||||
Lines.stroke(2f);
|
||||
Lines.spikes(e.x, e.y, e.fin() * 4f, 2, 6);
|
||||
Draw.reset();
|
||||
});
|
||||
generate = new Effect(11, e -> {
|
||||
Draw.color(Color.ORANGE, Color.YELLOW, e.fin());
|
||||
Draw.color(Color.orange, Color.yellow, e.fin());
|
||||
Lines.stroke(1f);
|
||||
Lines.spikes(e.x, e.y, e.fin() * 5f, 2, 8);
|
||||
Draw.reset();
|
||||
});
|
||||
mine = new Effect(20, e -> {
|
||||
Angles.randLenVectors(e.id, 6, 3f + e.fin() * 6f, (x, y) -> {
|
||||
Draw.color(e.color, Color.LIGHT_GRAY, e.fin());
|
||||
Draw.color(e.color, Color.lightGray, e.fin());
|
||||
Fill.square(e.x + x, e.y + y, e.fout() * 2f, 45);
|
||||
Draw.reset();
|
||||
});
|
||||
});
|
||||
mineBig = new Effect(30, e -> {
|
||||
Angles.randLenVectors(e.id, 6, 4f + e.fin() * 8f, (x, y) -> {
|
||||
Draw.color(e.color, Color.LIGHT_GRAY, e.fin());
|
||||
Draw.color(e.color, Color.lightGray, e.fin());
|
||||
Fill.square(e.x + x, e.y + y, e.fout() * 2f + 0.2f, 45);
|
||||
Draw.reset();
|
||||
});
|
||||
});
|
||||
mineHuge = new Effect(40, e -> {
|
||||
Angles.randLenVectors(e.id, 8, 5f + e.fin() * 10f, (x, y) -> {
|
||||
Draw.color(e.color, Color.LIGHT_GRAY, e.fin());
|
||||
Draw.color(e.color, Color.lightGray, e.fin());
|
||||
Fill.square(e.x + x, e.y + y, e.fout() * 2f + 0.5f, 45);
|
||||
Draw.reset();
|
||||
});
|
||||
});
|
||||
smelt = new Effect(20, e -> {
|
||||
Angles.randLenVectors(e.id, 6, 2f + e.fin() * 5f, (x, y) -> {
|
||||
Draw.color(Color.WHITE, e.color, e.fin());
|
||||
Draw.color(Color.white, e.color, e.fin());
|
||||
Fill.square(e.x + x, e.y + y, 0.5f + e.fout() * 2f, 45);
|
||||
Draw.reset();
|
||||
});
|
||||
@@ -1209,5 +1207,8 @@ public class Fx implements ContentList{
|
||||
Lines.poly(e.x, e.y, 6, e.rotation + e.fin(), 90);
|
||||
Draw.reset();
|
||||
});
|
||||
|
||||
coreLand = new Effect(120f, e -> {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ public class Items implements ContentList{
|
||||
}};
|
||||
|
||||
coal = new Item("coal", Color.valueOf("272727")){{
|
||||
explosiveness = 0.4f;
|
||||
explosiveness = 0.2f;
|
||||
flammability = 1f;
|
||||
hardness = 2;
|
||||
}};
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
package io.anuke.mindustry.content;
|
||||
|
||||
import io.anuke.arc.*;
|
||||
import io.anuke.arc.math.Mathf;
|
||||
import io.anuke.mindustry.entities.Effects;
|
||||
import io.anuke.mindustry.game.ContentList;
|
||||
import io.anuke.mindustry.game.EventType.*;
|
||||
import io.anuke.mindustry.type.StatusEffect;
|
||||
|
||||
import static io.anuke.mindustry.Vars.waveTeam;
|
||||
|
||||
public class StatusEffects implements ContentList{
|
||||
public static StatusEffect none, burning, freezing, wet, melting, tarred, overdrive, shielded, shocked, corroded, boss;
|
||||
|
||||
@@ -37,8 +41,14 @@ public class StatusEffects implements ContentList{
|
||||
speedMultiplier = 0.9f;
|
||||
effect = Fx.wet;
|
||||
|
||||
trans(() -> shocked, ((unit, time, newTime, result) -> unit.damage(15f)));
|
||||
opposite(() -> burning, () -> shocked);
|
||||
trans(() -> shocked, ((unit, time, newTime, result) -> {
|
||||
unit.damage(20f);
|
||||
if(unit.getTeam() == waveTeam){
|
||||
Events.fire(Trigger.shock);
|
||||
}
|
||||
result.set(this, time);
|
||||
}));
|
||||
opposite(() -> burning);
|
||||
}};
|
||||
|
||||
melting = new StatusEffect(){{
|
||||
|
||||
@@ -8,10 +8,13 @@ import io.anuke.mindustry.world.Block;
|
||||
import static io.anuke.mindustry.content.Blocks.*;
|
||||
|
||||
public class TechTree implements ContentList{
|
||||
public static Array<TechNode> all;
|
||||
public static TechNode root;
|
||||
|
||||
@Override
|
||||
public void load(){
|
||||
all = new Array<>();
|
||||
|
||||
root = node(coreShard, () -> {
|
||||
|
||||
node(conveyor, () -> {
|
||||
@@ -326,6 +329,7 @@ public class TechTree implements ContentList{
|
||||
context = this;
|
||||
children.run();
|
||||
context = last;
|
||||
all.add(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package io.anuke.mindustry.content;
|
||||
import io.anuke.arc.collection.*;
|
||||
import io.anuke.mindustry.entities.bullet.*;
|
||||
import io.anuke.mindustry.entities.type.*;
|
||||
import io.anuke.mindustry.entities.type.Bullet;
|
||||
import io.anuke.mindustry.entities.type.base.*;
|
||||
import io.anuke.mindustry.game.*;
|
||||
import io.anuke.mindustry.gen.*;
|
||||
|
||||
@@ -2,7 +2,6 @@ package io.anuke.mindustry.core;
|
||||
|
||||
import io.anuke.arc.*;
|
||||
import io.anuke.arc.assets.*;
|
||||
import io.anuke.arc.files.*;
|
||||
import io.anuke.arc.graphics.*;
|
||||
import io.anuke.arc.graphics.g2d.*;
|
||||
import io.anuke.arc.input.*;
|
||||
@@ -18,7 +17,6 @@ import io.anuke.mindustry.game.*;
|
||||
import io.anuke.mindustry.gen.*;
|
||||
import io.anuke.mindustry.input.*;
|
||||
import io.anuke.mindustry.maps.Map;
|
||||
import io.anuke.mindustry.net.Net;
|
||||
import io.anuke.mindustry.type.*;
|
||||
import io.anuke.mindustry.ui.dialogs.*;
|
||||
import io.anuke.mindustry.world.*;
|
||||
@@ -30,6 +28,7 @@ import java.util.*;
|
||||
|
||||
import static io.anuke.arc.Core.*;
|
||||
import static io.anuke.mindustry.Vars.*;
|
||||
import static io.anuke.mindustry.Vars.net;
|
||||
|
||||
/**
|
||||
* Control module.
|
||||
@@ -48,6 +47,10 @@ public class Control implements ApplicationListener, Loadable{
|
||||
private boolean wasPaused = false;
|
||||
|
||||
public Control(){
|
||||
saves = new Saves();
|
||||
tutorial = new Tutorial();
|
||||
music = new MusicControl();
|
||||
|
||||
Events.on(StateChangeEvent.class, event -> {
|
||||
if((event.from == State.playing && event.to == State.menu) || (event.from == State.menu && event.to != State.menu)){
|
||||
Time.runTask(5f, platform::updateRPC);
|
||||
@@ -64,7 +67,7 @@ public class Control implements ApplicationListener, Loadable{
|
||||
|
||||
Events.on(WorldLoadEvent.class, event -> {
|
||||
Core.app.post(() -> Core.app.post(() -> {
|
||||
if(Net.active() && player.getClosestCore() != null){
|
||||
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{
|
||||
@@ -96,7 +99,7 @@ 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);
|
||||
if(state.rules.zone != null && !Net.client()){
|
||||
if(state.rules.zone != null && !net.client()){
|
||||
//remove zone save on game over
|
||||
if(saves.getZoneSlot() != null && !state.rules.tutorial){
|
||||
saves.getZoneSlot().delete();
|
||||
@@ -106,12 +109,12 @@ public class Control implements ApplicationListener, Loadable{
|
||||
|
||||
//autohost for pvp maps
|
||||
Events.on(WorldLoadEvent.class, event -> {
|
||||
if(state.rules.pvp && !Net.active()){
|
||||
if(state.rules.pvp && !net.active()){
|
||||
try{
|
||||
Net.host(port);
|
||||
net.host(port);
|
||||
player.isAdmin = true;
|
||||
}catch(IOException e){
|
||||
ui.showError(Core.bundle.format("server.error", Strings.parseException(e, true)));
|
||||
ui.showException("$server.error", e);
|
||||
Core.app.post(() -> state.set(State.menu));
|
||||
}
|
||||
}
|
||||
@@ -148,14 +151,24 @@ public class Control implements ApplicationListener, Loadable{
|
||||
Events.on(ZoneConfigureCompleteEvent.class, e -> {
|
||||
ui.hudfrag.showToast(Core.bundle.format("zone.config.complete", e.zone.configureWave));
|
||||
});
|
||||
|
||||
Events.on(Trigger.newGame, () -> {
|
||||
TileEntity core = player.getClosestCore();
|
||||
|
||||
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));
|
||||
Time.run(Fx.coreLand.lifetime, () -> {
|
||||
Effects.effect(Fx.launch, core);
|
||||
Effects.shake(5f, 5f, core);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadAsync(){
|
||||
saves = new Saves();
|
||||
tutorial = new Tutorial();
|
||||
music = new MusicControl();
|
||||
|
||||
Draw.scl = 1f / Core.atlas.find("scale_marker").getWidth();
|
||||
|
||||
Core.input.setCatch(KeyCode.BACK, true);
|
||||
@@ -174,35 +187,6 @@ public class Control implements ApplicationListener, Loadable{
|
||||
saves.load();
|
||||
}
|
||||
|
||||
//checks for existing 3.5 app data, android only
|
||||
public void checkClassicData(){
|
||||
try{
|
||||
if(files.local("mindustry-maps").exists() || files.local("mindustry-saves").exists()){
|
||||
settings.getBoolOnce("classic-backup-check", () -> {
|
||||
app.post(() -> app.post(() -> ui.showConfirm("$classic.export", "$classic.export.text", () -> {
|
||||
try{
|
||||
platform.requestExternalPerms(() -> {
|
||||
FileHandle external = files.external("MindustryClassic");
|
||||
if(files.local("mindustry-maps").exists()){
|
||||
files.local("mindustry-maps").copyTo(external);
|
||||
}
|
||||
|
||||
if(files.local("mindustry-saves").exists()){
|
||||
files.local("mindustry-saves").copyTo(external);
|
||||
}
|
||||
});
|
||||
}catch(Exception e){
|
||||
e.printStackTrace();
|
||||
ui.showError(Strings.parseException(e, true));
|
||||
}
|
||||
})));
|
||||
});
|
||||
}
|
||||
}catch(Throwable t){
|
||||
t.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
void createPlayer(){
|
||||
player = new Player();
|
||||
player.name = Core.settings.getString("name");
|
||||
@@ -220,9 +204,18 @@ public class Control implements ApplicationListener, Loadable{
|
||||
player.add();
|
||||
}
|
||||
|
||||
Events.on(ClientLoadEvent.class, e -> {
|
||||
Core.input.addProcessor(input);
|
||||
});
|
||||
Events.on(ClientLoadEvent.class, e -> input.add());
|
||||
}
|
||||
|
||||
public void setInput(InputHandler newInput){
|
||||
Block block = input.block;
|
||||
boolean added = Core.input.getInputProcessors().contains(input);
|
||||
input.remove();
|
||||
this.input = newInput;
|
||||
newInput.block = block;
|
||||
if(added){
|
||||
newInput.add();
|
||||
}
|
||||
}
|
||||
|
||||
public void playMap(Map map, Rules rules){
|
||||
@@ -230,17 +223,20 @@ public class Control implements ApplicationListener, Loadable{
|
||||
logic.reset();
|
||||
world.loadMap(map, rules);
|
||||
state.rules = rules;
|
||||
state.rules.zone = null;
|
||||
state.rules.editor = false;
|
||||
logic.play();
|
||||
if(settings.getBool("savecreate") && !world.isInvalidMap()){
|
||||
control.saves.addSave(map.name() + " " + new SimpleDateFormat("MMM dd h:mm", Locale.getDefault()).format(new Date()));
|
||||
}
|
||||
Events.fire(Trigger.newGame);
|
||||
});
|
||||
}
|
||||
|
||||
public void playZone(Zone zone){
|
||||
ui.loadAnd(() -> {
|
||||
logic.reset();
|
||||
Net.reset();
|
||||
net.reset();
|
||||
world.loadGenerator(zone.generator);
|
||||
zone.rules.accept(state.rules);
|
||||
state.rules.zone = zone;
|
||||
@@ -252,6 +248,7 @@ public class Control implements ApplicationListener, Loadable{
|
||||
state.set(State.playing);
|
||||
control.saves.zoneSave();
|
||||
logic.play();
|
||||
Events.fire(Trigger.newGame);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -259,7 +256,7 @@ public class Control implements ApplicationListener, Loadable{
|
||||
Zone zone = Zones.groundZero;
|
||||
ui.loadAnd(() -> {
|
||||
logic.reset();
|
||||
Net.reset();
|
||||
net.reset();
|
||||
|
||||
world.beginMapLoad();
|
||||
|
||||
@@ -304,6 +301,7 @@ public class Control implements ApplicationListener, Loadable{
|
||||
state.rules.waveSpacing = 60f * 30;
|
||||
state.rules.buildCostMultiplier = 0.3f;
|
||||
state.rules.tutorial = true;
|
||||
Events.fire(Trigger.newGame);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -314,7 +312,7 @@ public class Control implements ApplicationListener, Loadable{
|
||||
@Override
|
||||
public void dispose(){
|
||||
content.dispose();
|
||||
Net.dispose();
|
||||
net.dispose();
|
||||
Musics.dispose();
|
||||
Sounds.dispose();
|
||||
ui.editor.dispose();
|
||||
@@ -379,17 +377,20 @@ public class Control implements ApplicationListener, Loadable{
|
||||
|
||||
if(android){
|
||||
Sounds.empty.loop(0f, 1f, 0f);
|
||||
checkClassicData();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(){
|
||||
//TODO find out why this happens on Android
|
||||
if(assets == null) return;
|
||||
|
||||
saves.update();
|
||||
|
||||
//update and load any requested assets
|
||||
assets.update();
|
||||
|
||||
input.updateController();
|
||||
input.updateState();
|
||||
|
||||
//autosave global data if it's modified
|
||||
data.checkSave();
|
||||
@@ -414,7 +415,7 @@ public class Control implements ApplicationListener, Loadable{
|
||||
if(world.isZone()){
|
||||
for(Tile tile : state.teams.get(player.getTeam()).cores){
|
||||
for(Item item : content.items()){
|
||||
if(tile.entity.items.has(item)){
|
||||
if(tile.entity != null && tile.entity.items.has(item)){
|
||||
data.unlockContent(item);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
package io.anuke.mindustry.core;
|
||||
|
||||
import io.anuke.arc.Events;
|
||||
import io.anuke.mindustry.entities.type.BaseUnit;
|
||||
import io.anuke.mindustry.entities.type.base.BaseDrone;
|
||||
import io.anuke.mindustry.game.EventType.StateChangeEvent;
|
||||
import io.anuke.arc.*;
|
||||
import io.anuke.mindustry.entities.type.*;
|
||||
import io.anuke.mindustry.entities.type.base.*;
|
||||
import io.anuke.mindustry.game.EventType.*;
|
||||
import io.anuke.mindustry.game.*;
|
||||
import io.anuke.mindustry.net.Net;
|
||||
|
||||
import static io.anuke.mindustry.Vars.unitGroups;
|
||||
import static io.anuke.mindustry.Vars.waveTeam;
|
||||
import static io.anuke.mindustry.Vars.*;
|
||||
|
||||
public class GameState{
|
||||
/** Current wave number, can be anything in non-wave modes. */
|
||||
@@ -29,7 +27,7 @@ public class GameState{
|
||||
private State state = State.menu;
|
||||
|
||||
public int enemies(){
|
||||
return Net.client() ? enemies : unitGroups[waveTeam.ordinal()].count(b -> !(b instanceof BaseDrone));
|
||||
return net.client() ? enemies : unitGroups[waveTeam.ordinal()].count(b -> !(b instanceof BaseDrone));
|
||||
}
|
||||
|
||||
public BaseUnit boss(){
|
||||
@@ -46,7 +44,7 @@ public class GameState{
|
||||
}
|
||||
|
||||
public boolean isPaused(){
|
||||
return (is(State.paused) && !Net.active()) || (gameOver && !Net.active());
|
||||
return (is(State.paused) && !net.active()) || (gameOver && !net.active());
|
||||
}
|
||||
|
||||
public boolean is(State astate){
|
||||
|
||||
@@ -1,28 +1,21 @@
|
||||
package io.anuke.mindustry.core;
|
||||
|
||||
import io.anuke.annotations.Annotations.Loc;
|
||||
import io.anuke.annotations.Annotations.Remote;
|
||||
import io.anuke.arc.ApplicationListener;
|
||||
import io.anuke.arc.Events;
|
||||
import io.anuke.arc.collection.ObjectSet.ObjectSetIterator;
|
||||
import io.anuke.annotations.Annotations.*;
|
||||
import io.anuke.arc.*;
|
||||
import io.anuke.arc.collection.ObjectSet.*;
|
||||
import io.anuke.arc.util.*;
|
||||
import io.anuke.mindustry.content.*;
|
||||
import io.anuke.mindustry.core.GameState.State;
|
||||
import io.anuke.mindustry.core.GameState.*;
|
||||
import io.anuke.mindustry.entities.*;
|
||||
import io.anuke.mindustry.entities.type.Player;
|
||||
import io.anuke.mindustry.entities.type.TileEntity;
|
||||
import io.anuke.mindustry.entities.type.*;
|
||||
import io.anuke.mindustry.game.EventType.*;
|
||||
import io.anuke.mindustry.game.*;
|
||||
import io.anuke.mindustry.game.Teams.TeamData;
|
||||
import io.anuke.mindustry.gen.BrokenBlock;
|
||||
import io.anuke.mindustry.gen.Call;
|
||||
import io.anuke.mindustry.net.Net;
|
||||
import io.anuke.mindustry.type.Item;
|
||||
import io.anuke.mindustry.type.ItemStack;
|
||||
import io.anuke.mindustry.world.Block;
|
||||
import io.anuke.mindustry.world.Tile;
|
||||
import io.anuke.mindustry.world.blocks.BuildBlock;
|
||||
import io.anuke.mindustry.world.blocks.BuildBlock.BuildEntity;
|
||||
import io.anuke.mindustry.game.Teams.*;
|
||||
import io.anuke.mindustry.gen.*;
|
||||
import io.anuke.mindustry.type.*;
|
||||
import io.anuke.mindustry.world.*;
|
||||
import io.anuke.mindustry.world.blocks.*;
|
||||
import io.anuke.mindustry.world.blocks.BuildBlock.*;
|
||||
|
||||
import static io.anuke.mindustry.Vars.*;
|
||||
|
||||
@@ -164,6 +157,7 @@ public class Logic implements ApplicationListener{
|
||||
}
|
||||
state.launched = true;
|
||||
state.gameOver = true;
|
||||
Events.fire(new LaunchEvent());
|
||||
//manually fire game over event now
|
||||
Events.fire(new GameOverEvent(defaultTeam));
|
||||
});
|
||||
@@ -190,7 +184,7 @@ public class Logic implements ApplicationListener{
|
||||
}
|
||||
}
|
||||
|
||||
if(!Net.client() && state.wavetime <= 0 && state.rules.waves){
|
||||
if(!net.client() && state.wavetime <= 0 && state.rules.waves){
|
||||
runWave();
|
||||
}
|
||||
|
||||
@@ -233,11 +227,9 @@ public class Logic implements ApplicationListener{
|
||||
|
||||
collisions.collideGroups(bulletGroup, playerGroup);
|
||||
}
|
||||
|
||||
pathfinder.update();
|
||||
}
|
||||
|
||||
if(!Net.client() && !world.isInvalidMap() && !state.isEditor()){
|
||||
if(!net.client() && !world.isInvalidMap() && !state.isEditor()){
|
||||
checkGameOver();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,37 +1,33 @@
|
||||
package io.anuke.mindustry.core;
|
||||
|
||||
import io.anuke.annotations.Annotations.*;
|
||||
import io.anuke.arc.ApplicationListener;
|
||||
import io.anuke.arc.Core;
|
||||
import io.anuke.arc.collection.IntSet;
|
||||
import io.anuke.arc.graphics.Color;
|
||||
import io.anuke.arc.math.RandomXS128;
|
||||
import io.anuke.arc.util.*;
|
||||
import io.anuke.arc.*;
|
||||
import io.anuke.arc.collection.*;
|
||||
import io.anuke.arc.graphics.*;
|
||||
import io.anuke.arc.math.*;
|
||||
import io.anuke.arc.util.CommandHandler.*;
|
||||
import io.anuke.arc.util.io.ReusableByteInStream;
|
||||
import io.anuke.arc.util.serialization.Base64Coder;
|
||||
import io.anuke.mindustry.Vars;
|
||||
import io.anuke.mindustry.core.GameState.State;
|
||||
import io.anuke.mindustry.entities.EntityGroup;
|
||||
import io.anuke.mindustry.entities.traits.BuilderTrait.BuildRequest;
|
||||
import io.anuke.mindustry.entities.traits.SyncTrait;
|
||||
import io.anuke.mindustry.entities.type.Player;
|
||||
import io.anuke.mindustry.entities.type.Unit;
|
||||
import io.anuke.mindustry.game.TypeID;
|
||||
import io.anuke.mindustry.game.Version;
|
||||
import io.anuke.mindustry.gen.Call;
|
||||
import io.anuke.mindustry.gen.RemoteReadClient;
|
||||
import io.anuke.mindustry.net.Administration.TraceInfo;
|
||||
import io.anuke.arc.util.*;
|
||||
import io.anuke.arc.util.io.*;
|
||||
import io.anuke.arc.util.serialization.*;
|
||||
import io.anuke.mindustry.*;
|
||||
import io.anuke.mindustry.core.GameState.*;
|
||||
import io.anuke.mindustry.entities.*;
|
||||
import io.anuke.mindustry.entities.traits.BuilderTrait.*;
|
||||
import io.anuke.mindustry.entities.traits.*;
|
||||
import io.anuke.mindustry.entities.type.*;
|
||||
import io.anuke.mindustry.game.EventType.*;
|
||||
import io.anuke.mindustry.game.*;
|
||||
import io.anuke.mindustry.gen.*;
|
||||
import io.anuke.mindustry.net.Administration.*;
|
||||
import io.anuke.mindustry.net.Net.*;
|
||||
import io.anuke.mindustry.net.*;
|
||||
import io.anuke.mindustry.net.Net.SendMode;
|
||||
import io.anuke.mindustry.net.Packets.*;
|
||||
import io.anuke.mindustry.type.ContentType;
|
||||
import io.anuke.mindustry.world.Tile;
|
||||
import io.anuke.mindustry.world.modules.ItemModule;
|
||||
import io.anuke.mindustry.type.*;
|
||||
import io.anuke.mindustry.world.*;
|
||||
import io.anuke.mindustry.world.modules.*;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.zip.InflaterInputStream;
|
||||
import java.io.*;
|
||||
import java.util.zip.*;
|
||||
|
||||
import static io.anuke.mindustry.Vars.*;
|
||||
|
||||
@@ -40,6 +36,7 @@ public class NetClient implements ApplicationListener{
|
||||
private final static float playerSyncTime = 2;
|
||||
public final static float viewScale = 2f;
|
||||
|
||||
private long ping;
|
||||
private Interval timer = new Interval(5);
|
||||
/** Whether the client is currently connecting. */
|
||||
private boolean connecting = false;
|
||||
@@ -60,7 +57,7 @@ public class NetClient implements ApplicationListener{
|
||||
|
||||
public NetClient(){
|
||||
|
||||
Net.handleClient(Connect.class, packet -> {
|
||||
net.handleClient(Connect.class, packet -> {
|
||||
Log.info("Connecting to server: {0}", packet.addressTCP);
|
||||
|
||||
player.isAdmin = false;
|
||||
@@ -74,7 +71,7 @@ public class NetClient implements ApplicationListener{
|
||||
ui.loadfrag.hide();
|
||||
connecting = false;
|
||||
quiet = true;
|
||||
Net.disconnect();
|
||||
net.disconnect();
|
||||
});
|
||||
|
||||
ConnectPacket c = new ConnectPacket();
|
||||
@@ -86,16 +83,16 @@ public class NetClient implements ApplicationListener{
|
||||
c.uuid = platform.getUUID();
|
||||
|
||||
if(c.uuid == null){
|
||||
ui.showError("$invalidid");
|
||||
ui.showErrorMessage("$invalidid");
|
||||
ui.loadfrag.hide();
|
||||
disconnectQuietly();
|
||||
return;
|
||||
}
|
||||
|
||||
Net.send(c, SendMode.tcp);
|
||||
net.send(c, SendMode.tcp);
|
||||
});
|
||||
|
||||
Net.handleClient(Disconnect.class, packet -> {
|
||||
net.handleClient(Disconnect.class, packet -> {
|
||||
if(quietReset) return;
|
||||
|
||||
connecting = false;
|
||||
@@ -116,18 +113,18 @@ public class NetClient implements ApplicationListener{
|
||||
ui.showSmall("$disconnect", "$disconnect.error");
|
||||
}
|
||||
}else{
|
||||
ui.showError("$disconnect");
|
||||
ui.showErrorMessage("$disconnect");
|
||||
}
|
||||
});
|
||||
|
||||
Net.handleClient(WorldStream.class, data -> {
|
||||
net.handleClient(WorldStream.class, data -> {
|
||||
Log.info("Recieved world data: {0} bytes.", data.stream.available());
|
||||
NetworkIO.loadWorld(new InflaterInputStream(data.stream));
|
||||
|
||||
finishConnecting();
|
||||
});
|
||||
|
||||
Net.handleClient(InvokePacket.class, packet -> {
|
||||
net.handleClient(InvokePacket.class, packet -> {
|
||||
packet.writeBuffer.position(0);
|
||||
RemoteReadClient.readPacket(packet.writeBuffer, packet.type);
|
||||
});
|
||||
@@ -190,6 +187,8 @@ public class NetClient implements ApplicationListener{
|
||||
player.sendMessage(text);
|
||||
}
|
||||
}
|
||||
|
||||
Events.fire(new PlayerChatEvent(player, message));
|
||||
}
|
||||
|
||||
public static String colorizeName(int id, String name){
|
||||
@@ -198,6 +197,16 @@ public class NetClient implements ApplicationListener{
|
||||
return "[#" + player.color.toString().toUpperCase() + "]" + name;
|
||||
}
|
||||
|
||||
@Remote(targets = Loc.client)
|
||||
public static void onPing(Player player, long time){
|
||||
Call.onPingResponse(player.con, time);
|
||||
}
|
||||
|
||||
@Remote(variants = Variant.one)
|
||||
public static void onPingResponse(long time){
|
||||
netClient.ping = Time.timeSinceMillis(time);
|
||||
}
|
||||
|
||||
@Remote(variants = Variant.one)
|
||||
public static void onTraceInfo(Player player, TraceInfo info){
|
||||
if(player != null){
|
||||
@@ -233,7 +242,7 @@ public class NetClient implements ApplicationListener{
|
||||
logic.reset();
|
||||
|
||||
ui.chatfrag.clearMessages();
|
||||
Net.setClientLoaded(false);
|
||||
net.setClientLoaded(false);
|
||||
|
||||
ui.loadfrag.show("$connecting.data");
|
||||
|
||||
@@ -241,7 +250,7 @@ public class NetClient implements ApplicationListener{
|
||||
ui.loadfrag.hide();
|
||||
netClient.connecting = false;
|
||||
netClient.quiet = true;
|
||||
Net.disconnect();
|
||||
net.disconnect();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -259,7 +268,7 @@ public class NetClient implements ApplicationListener{
|
||||
@Remote(variants = Variant.one, priority = PacketPriority.low, unreliable = true)
|
||||
public static void onEntitySnapshot(byte groupID, short amount, short dataLen, byte[] data){
|
||||
try{
|
||||
netClient.byteStream.setBytes(Net.decompressSnapshot(data, dataLen));
|
||||
netClient.byteStream.setBytes(net.decompressSnapshot(data, dataLen));
|
||||
DataInputStream input = netClient.dataStream;
|
||||
|
||||
EntityGroup group = entities.get(groupID);
|
||||
@@ -315,7 +324,7 @@ public class NetClient implements ApplicationListener{
|
||||
state.wave = wave;
|
||||
state.enemies = enemies;
|
||||
|
||||
netClient.byteStream.setBytes(Net.decompressSnapshot(coreData, coreDataLen));
|
||||
netClient.byteStream.setBytes(net.decompressSnapshot(coreData, coreDataLen));
|
||||
DataInputStream input = netClient.dataStream;
|
||||
|
||||
byte cores = input.readByte();
|
||||
@@ -337,20 +346,20 @@ public class NetClient implements ApplicationListener{
|
||||
|
||||
@Override
|
||||
public void update(){
|
||||
if(!Net.client()) return;
|
||||
if(!net.client()) return;
|
||||
|
||||
if(!state.is(State.menu)){
|
||||
if(!connecting) sync();
|
||||
}else if(!connecting){
|
||||
Net.disconnect();
|
||||
net.disconnect();
|
||||
}else{ //...must be connecting
|
||||
timeoutTime += Time.delta();
|
||||
if(timeoutTime > dataTimeout){
|
||||
Log.err("Failed to load data!");
|
||||
ui.loadfrag.hide();
|
||||
quiet = true;
|
||||
ui.showError("$disconnect.data");
|
||||
Net.disconnect();
|
||||
ui.showErrorMessage("$disconnect.data");
|
||||
net.disconnect();
|
||||
timeoutTime = 0f;
|
||||
}
|
||||
}
|
||||
@@ -360,18 +369,22 @@ public class NetClient implements ApplicationListener{
|
||||
return connecting;
|
||||
}
|
||||
|
||||
public int getPing(){
|
||||
return (int)ping;
|
||||
}
|
||||
|
||||
private void finishConnecting(){
|
||||
state.set(State.playing);
|
||||
connecting = false;
|
||||
ui.join.hide();
|
||||
Net.setClientLoaded(true);
|
||||
net.setClientLoaded(true);
|
||||
Core.app.post(Call::connectConfirm);
|
||||
Time.runTask(40f, platform::updateRPC);
|
||||
Core.app.post(() -> ui.loadfrag.hide());
|
||||
}
|
||||
|
||||
private void reset(){
|
||||
Net.setClientLoaded(false);
|
||||
net.setClientLoaded(false);
|
||||
removed.clear();
|
||||
timeoutTime = 0f;
|
||||
connecting = true;
|
||||
@@ -390,13 +403,13 @@ public class NetClient implements ApplicationListener{
|
||||
/** Disconnects, resetting state to the menu. */
|
||||
public void disconnectQuietly(){
|
||||
quiet = true;
|
||||
Net.disconnect();
|
||||
net.disconnect();
|
||||
}
|
||||
|
||||
/** Disconnects, causing no further changes or reset.*/
|
||||
public void disconnectNoReset(){
|
||||
quiet = quietReset = true;
|
||||
Net.disconnect();
|
||||
net.disconnect();
|
||||
}
|
||||
|
||||
/** When set, any disconnects will be ignored and no dialogs will be shown. */
|
||||
@@ -435,7 +448,7 @@ public class NetClient implements ApplicationListener{
|
||||
}
|
||||
|
||||
if(timer.get(1, 60)){
|
||||
Net.updatePing();
|
||||
Call.onPing(Time.millis());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,6 @@ import io.anuke.mindustry.game.EventType.*;
|
||||
import io.anuke.mindustry.game.*;
|
||||
import io.anuke.mindustry.gen.*;
|
||||
import io.anuke.mindustry.net.*;
|
||||
import io.anuke.mindustry.net.Net;
|
||||
import io.anuke.mindustry.net.Administration.*;
|
||||
import io.anuke.mindustry.net.Packets.*;
|
||||
import io.anuke.mindustry.world.*;
|
||||
@@ -41,8 +40,6 @@ public class NetServer implements ApplicationListener{
|
||||
public final Administration admins = new Administration();
|
||||
public final CommandHandler clientCommands = new CommandHandler("/");
|
||||
|
||||
/** Maps connection IDs to players. */
|
||||
private IntMap<Player> connections = new IntMap<>();
|
||||
private boolean closing = false;
|
||||
|
||||
private ByteBuffer writeBuffer = ByteBuffer.allocate(127);
|
||||
@@ -54,57 +51,51 @@ public class NetServer implements ApplicationListener{
|
||||
private DataOutputStream dataStream = new DataOutputStream(syncStream);
|
||||
|
||||
public NetServer(){
|
||||
Events.on(WorldLoadEvent.class, event -> {
|
||||
if(!headless){
|
||||
connections.clear();
|
||||
}
|
||||
});
|
||||
|
||||
Net.handleServer(Connect.class, (id, connect) -> {
|
||||
net.handleServer(Connect.class, (con, connect) -> {
|
||||
if(admins.isIPBanned(connect.addressTCP)){
|
||||
kick(id, KickReason.banned);
|
||||
con.kick(KickReason.banned);
|
||||
}
|
||||
});
|
||||
|
||||
Net.handleServer(Disconnect.class, (id, packet) -> {
|
||||
Player player = connections.get(id);
|
||||
if(player != null){
|
||||
onDisconnect(player, packet.reason);
|
||||
net.handleServer(Disconnect.class, (con, packet) -> {
|
||||
if(con.player != null){
|
||||
onDisconnect(con.player, packet.reason);
|
||||
}
|
||||
connections.remove(id);
|
||||
});
|
||||
|
||||
Net.handleServer(ConnectPacket.class, (id, packet) -> {
|
||||
net.handleServer(ConnectPacket.class, (con, packet) -> {
|
||||
String uuid = packet.uuid;
|
||||
|
||||
NetConnection connection = Net.getConnection(id);
|
||||
if(admins.isIPBanned(con.address)) return;
|
||||
|
||||
if(connection == null ||
|
||||
admins.isIPBanned(connection.address)) return;
|
||||
|
||||
if(connection.hasBegunConnecting){
|
||||
kick(id, KickReason.idInUse);
|
||||
if(con.hasBegunConnecting){
|
||||
con.kick(KickReason.idInUse);
|
||||
return;
|
||||
}
|
||||
|
||||
connection.hasBegunConnecting = true;
|
||||
|
||||
PlayerInfo info = admins.getInfo(uuid);
|
||||
|
||||
connection.mobile = packet.mobile;
|
||||
con.hasBegunConnecting = true;
|
||||
con.mobile = packet.mobile;
|
||||
|
||||
if(admins.isIDBanned(uuid)){
|
||||
kick(id, KickReason.banned);
|
||||
con.kick(KickReason.banned);
|
||||
return;
|
||||
}
|
||||
|
||||
if(Time.millis() - info.lastKicked < kickDuration){
|
||||
kick(id, KickReason.recentKick);
|
||||
con.kick(KickReason.recentKick);
|
||||
return;
|
||||
}
|
||||
|
||||
if(admins.isIDBanned(uuid)){
|
||||
kick(id, KickReason.banned);
|
||||
con.kick(KickReason.banned);
|
||||
return;
|
||||
}
|
||||
|
||||
if(admins.getPlayerLimit() > 0 && playerGroup.size() >= admins.getPlayerLimit()){
|
||||
con.kick(KickReason.playerLimit);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -113,14 +104,14 @@ public class NetServer implements ApplicationListener{
|
||||
info.lastName = packet.name;
|
||||
info.id = packet.uuid;
|
||||
admins.save();
|
||||
Call.onInfoMessage(id, "You are not whitelisted here.");
|
||||
Call.onInfoMessage(con, "You are not whitelisted here.");
|
||||
Log.info("&lcDo &lywhitelist-add {0}&lc to whitelist the player &lb'{1}'", packet.uuid, packet.name);
|
||||
kick(id, KickReason.whitelist);
|
||||
con.kick(KickReason.whitelist);
|
||||
return;
|
||||
}
|
||||
|
||||
if(packet.versionType == null || ((packet.version == -1 || !packet.versionType.equals(Version.type)) && Version.build != -1 && !admins.allowsCustomClients())){
|
||||
kick(id, !Version.type.equals(packet.versionType) ? KickReason.typeMismatch : KickReason.customClient);
|
||||
con.kick(!Version.type.equals(packet.versionType) ? KickReason.typeMismatch : KickReason.customClient);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -129,12 +120,12 @@ public class NetServer implements ApplicationListener{
|
||||
if(preventDuplicates){
|
||||
for(Player player : playerGroup.all()){
|
||||
if(player.name.trim().equalsIgnoreCase(packet.name.trim())){
|
||||
kick(id, KickReason.nameInUse);
|
||||
con.kick(KickReason.nameInUse);
|
||||
return;
|
||||
}
|
||||
|
||||
if(player.uuid.equals(packet.uuid) || player.usid.equals(packet.usid)){
|
||||
kick(id, KickReason.idInUse);
|
||||
con.kick(KickReason.idInUse);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -143,28 +134,26 @@ public class NetServer implements ApplicationListener{
|
||||
packet.name = fixName(packet.name);
|
||||
|
||||
if(packet.name.trim().length() <= 0){
|
||||
kick(id, KickReason.nameEmpty);
|
||||
con.kick(KickReason.nameEmpty);
|
||||
return;
|
||||
}
|
||||
|
||||
Log.debug("Recieved connect packet for player '{0}' / UUID {1} / IP {2}", packet.name, uuid, connection.address);
|
||||
|
||||
String ip = Net.getConnection(id).address;
|
||||
String ip = con.address;
|
||||
|
||||
admins.updatePlayerJoined(uuid, ip, packet.name);
|
||||
|
||||
if(packet.version != Version.build && Version.build != -1 && packet.version != -1){
|
||||
kick(id, packet.version > Version.build ? KickReason.serverOutdated : KickReason.clientOutdated);
|
||||
con.kick(packet.version > Version.build ? KickReason.serverOutdated : KickReason.clientOutdated);
|
||||
return;
|
||||
}
|
||||
|
||||
if(packet.version == -1){
|
||||
connection.modclient = true;
|
||||
con.modclient = true;
|
||||
}
|
||||
|
||||
Player player = new Player();
|
||||
player.isAdmin = admins.isAdmin(uuid, packet.usid);
|
||||
player.con = Net.getConnection(id);
|
||||
player.con = con;
|
||||
player.usid = packet.usid;
|
||||
player.name = packet.name;
|
||||
player.uuid = uuid;
|
||||
@@ -179,29 +168,28 @@ public class NetServer implements ApplicationListener{
|
||||
player.write(outputBuffer);
|
||||
}catch(Throwable t){
|
||||
t.printStackTrace();
|
||||
kick(id, KickReason.nameEmpty);
|
||||
con.kick(KickReason.nameEmpty);
|
||||
return;
|
||||
}
|
||||
|
||||
con.player = player;
|
||||
|
||||
//playing in pvp mode automatically assigns players to teams
|
||||
if(state.rules.pvp){
|
||||
player.setTeam(assignTeam(player, playerGroup.all()));
|
||||
Log.info("Auto-assigned player {0} to team {1}.", player.name, player.getTeam());
|
||||
}
|
||||
|
||||
connections.put(id, player);
|
||||
|
||||
sendWorldData(player, id);
|
||||
sendWorldData(player);
|
||||
|
||||
platform.updateRPC();
|
||||
|
||||
Events.fire(new PlayerJoin(player));
|
||||
Events.fire(new PlayerConnect(player));
|
||||
});
|
||||
|
||||
Net.handleServer(InvokePacket.class, (id, packet) -> {
|
||||
Player player = connections.get(id);
|
||||
if(player == null) return;
|
||||
RemoteReadServer.readPacket(packet.writeBuffer, packet.type, player);
|
||||
net.handleServer(InvokePacket.class, (con, packet) -> {
|
||||
if(con.player == null) return;
|
||||
RemoteReadServer.readPacket(packet.writeBuffer, packet.type, con.player);
|
||||
});
|
||||
|
||||
registerCommands();
|
||||
@@ -273,7 +261,7 @@ public class NetServer implements ApplicationListener{
|
||||
if(votes >= votesRequired() && target.isAdded() && target.con.isConnected()){
|
||||
Call.sendMessage(Strings.format("[orange]Vote passed.[scarlet] {0}[orange] will be kicked from the server.", target.name));
|
||||
admins.getInfo(target.uuid).lastKicked = Time.millis() + kickDuration*1000;
|
||||
kick(target.con.id, KickReason.vote);
|
||||
target.con.kick(KickReason.vote);
|
||||
map[0] = null;
|
||||
task.cancel();
|
||||
return true;
|
||||
@@ -305,14 +293,14 @@ public class NetServer implements ApplicationListener{
|
||||
for(Player p : playerGroup.all()){
|
||||
if(p.isAdmin || p.con == null || p == player) continue;
|
||||
|
||||
builder.append("[lightgray] ").append(p.name).append("[accent] (#").append(p.con.id).append(")\n");
|
||||
builder.append("[lightgray] ").append(p.name).append("[accent] (#").append(p.id).append(")\n");
|
||||
}
|
||||
player.sendMessage(builder.toString());
|
||||
}else{
|
||||
Player 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.con != null && p.con.id == id);
|
||||
found = playerGroup.find(p -> p.id == id);
|
||||
}else{
|
||||
found = playerGroup.find(p -> p.name.equalsIgnoreCase(args[0]));
|
||||
}
|
||||
@@ -368,8 +356,8 @@ public class NetServer implements ApplicationListener{
|
||||
if(player.isLocal){
|
||||
player.sendMessage("[scarlet]Re-synchronizing as the host is pointless.");
|
||||
}else{
|
||||
Call.onWorldDataBegin(player.con.id);
|
||||
netServer.sendWorldData(player, player.con.id);
|
||||
Call.onWorldDataBegin(player.con);
|
||||
netServer.sendWorldData(player);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -394,13 +382,13 @@ public class NetServer implements ApplicationListener{
|
||||
});
|
||||
}
|
||||
|
||||
public void sendWorldData(Player player, int clientID){
|
||||
public void sendWorldData(Player player){
|
||||
ByteArrayOutputStream stream = new ByteArrayOutputStream();
|
||||
DeflaterOutputStream def = new FastDeflaterOutputStream(stream);
|
||||
NetworkIO.writeWorld(player, def);
|
||||
WorldStream data = new WorldStream();
|
||||
data.stream = new ByteArrayInputStream(stream.toByteArray());
|
||||
Net.sendStream(clientID, data);
|
||||
player.con.sendStream(data);
|
||||
|
||||
Log.debug("Packed {0} compressed bytes of world data.", stream.size());
|
||||
}
|
||||
@@ -418,7 +406,6 @@ public class NetServer implements ApplicationListener{
|
||||
Call.onPlayerDisconnect(player.id);
|
||||
}
|
||||
player.remove();
|
||||
netServer.connections.remove(player.con.id);
|
||||
Log.info("&lm[{1}] &lc{0} has disconnected. &lg&fi({2})", player.name, player.uuid, reason);
|
||||
}
|
||||
|
||||
@@ -500,7 +487,7 @@ public class NetServer implements ApplicationListener{
|
||||
newx = x;
|
||||
newy = y;
|
||||
}else if(Mathf.dst(x, y, newx, newy) > correctDist){
|
||||
Call.onPositionSet(player.con.id, newx, newy); //teleport and correct position when necessary
|
||||
Call.onPositionSet(player.con, newx, newy); //teleport and correct position when necessary
|
||||
}
|
||||
|
||||
//reset player to previous synced position so it gets interpolated
|
||||
@@ -535,15 +522,15 @@ public class NetServer implements ApplicationListener{
|
||||
state.wavetime = 0f;
|
||||
}else if(action == AdminAction.ban){
|
||||
netServer.admins.banPlayerIP(other.con.address);
|
||||
netServer.kick(other.con.id, KickReason.banned);
|
||||
other.con.kick(KickReason.banned);
|
||||
Log.info("&lc{0} has banned {1}.", player.name, other.name);
|
||||
}else if(action == AdminAction.kick){
|
||||
netServer.kick(other.con.id, KickReason.kick);
|
||||
other.con.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.id, other, info);
|
||||
Call.onTraceInfo(player.con, other, info);
|
||||
}else{
|
||||
NetClient.onTraceInfo(other, info);
|
||||
}
|
||||
@@ -559,6 +546,8 @@ public class NetServer implements ApplicationListener{
|
||||
player.con.hasConnected = true;
|
||||
Call.sendMessage("[accent]" + player.name + "[accent] has connected.");
|
||||
Log.info("&lm[{1}] &y{0} has connected. ", player.name, player.uuid);
|
||||
|
||||
Events.fire(new PlayerJoin(player));
|
||||
}
|
||||
|
||||
public boolean isWaitingForPlayers(){
|
||||
@@ -576,51 +565,27 @@ public class NetServer implements ApplicationListener{
|
||||
|
||||
public void update(){
|
||||
|
||||
if(!headless && !closing && Net.server() && state.is(State.menu)){
|
||||
if(!headless && !closing && net.server() && state.is(State.menu)){
|
||||
closing = true;
|
||||
ui.loadfrag.show("$server.closing");
|
||||
Time.runTask(5f, () -> {
|
||||
Net.closeServer();
|
||||
net.closeServer();
|
||||
ui.loadfrag.hide();
|
||||
closing = false;
|
||||
});
|
||||
}
|
||||
|
||||
if(!state.is(State.menu) && Net.server()){
|
||||
if(!state.is(State.menu) && net.server()){
|
||||
sync();
|
||||
}
|
||||
}
|
||||
|
||||
public void kickAll(KickReason reason){
|
||||
for(NetConnection con : Net.getConnections()){
|
||||
kick(con.id, reason);
|
||||
for(NetConnection con : net.getConnections()){
|
||||
con.kick(reason);
|
||||
}
|
||||
}
|
||||
|
||||
public void kick(int connection, KickReason reason){
|
||||
NetConnection con = Net.getConnection(connection);
|
||||
if(con == null){
|
||||
Log.err("Cannot kick unknown player!");
|
||||
return;
|
||||
}else{
|
||||
Log.info("Kicking connection #{0} / IP: {1}. Reason: {2}", connection, con.address, reason.name());
|
||||
}
|
||||
|
||||
Player player = connections.get(con.id);
|
||||
|
||||
if(player != null && (reason == KickReason.kick || reason == KickReason.banned || reason == KickReason.vote) && player.uuid != null){
|
||||
PlayerInfo info = admins.getInfo(player.uuid);
|
||||
info.timesKicked++;
|
||||
info.lastKicked = Math.max(Time.millis(), info.lastKicked);
|
||||
}
|
||||
|
||||
Call.onKick(connection, reason);
|
||||
|
||||
Time.runTask(2f, con::close);
|
||||
|
||||
admins.save();
|
||||
}
|
||||
|
||||
public void writeSnapshot(Player player) throws IOException{
|
||||
syncStream.reset();
|
||||
ObjectSet<Tile> cores = state.teams.get(player.getTeam()).cores;
|
||||
@@ -636,7 +601,7 @@ public class NetServer implements ApplicationListener{
|
||||
byte[] stateBytes = syncStream.toByteArray();
|
||||
|
||||
//write basic state data.
|
||||
Call.onStateSnapshot(player.con.id, 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);
|
||||
|
||||
@@ -667,7 +632,7 @@ public class NetServer implements ApplicationListener{
|
||||
if(syncStream.size() > maxSnapshotSize){
|
||||
dataStream.close();
|
||||
byte[] syncBytes = syncStream.toByteArray();
|
||||
Call.onEntitySnapshot(player.con.id, (byte)group.getID(), (short)sent, (short)syncBytes.length, Net.compressSnapshot(syncBytes));
|
||||
Call.onEntitySnapshot(player.con, (byte)group.getID(), (short)sent, (short)syncBytes.length, net.compressSnapshot(syncBytes));
|
||||
sent = 0;
|
||||
syncStream.reset();
|
||||
}
|
||||
@@ -677,7 +642,7 @@ public class NetServer implements ApplicationListener{
|
||||
dataStream.close();
|
||||
|
||||
byte[] syncBytes = syncStream.toByteArray();
|
||||
Call.onEntitySnapshot(player.con.id, (byte)group.getID(), (short)sent, (short)syncBytes.length, Net.compressSnapshot(syncBytes));
|
||||
Call.onEntitySnapshot(player.con, (byte)group.getID(), (short)sent, (short)syncBytes.length, net.compressSnapshot(syncBytes));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -739,16 +704,10 @@ public class NetServer implements ApplicationListener{
|
||||
//iterate through each player
|
||||
for(int i = 0; i < playerGroup.size(); i++){
|
||||
Player player = playerGroup.all().get(i);
|
||||
if(player.isLocal) continue;
|
||||
if(player.isLocal || player.con == null) continue;
|
||||
|
||||
NetConnection connection = player.con;
|
||||
|
||||
if(connection == null || !connection.isConnected() || !connections.containsKey(connection.id)){
|
||||
//player disconnected, call d/c event
|
||||
onDisconnect(player, "disappeared");
|
||||
return;
|
||||
}
|
||||
|
||||
if(!player.timer.get(Player.timerSync, serverSyncTime) || !connection.hasConnected) continue;
|
||||
|
||||
writeSnapshot(player);
|
||||
|
||||
@@ -1,18 +1,47 @@
|
||||
package io.anuke.mindustry.core;
|
||||
|
||||
import io.anuke.arc.Core;
|
||||
import io.anuke.arc.Input.TextInput;
|
||||
import io.anuke.arc.files.FileHandle;
|
||||
import io.anuke.arc.function.Consumer;
|
||||
import io.anuke.arc.function.Predicate;
|
||||
import io.anuke.arc.math.RandomXS128;
|
||||
import io.anuke.arc.scene.ui.TextField;
|
||||
import io.anuke.arc.util.serialization.Base64Coder;
|
||||
import io.anuke.arc.*;
|
||||
import io.anuke.arc.Input.*;
|
||||
import io.anuke.arc.collection.*;
|
||||
import io.anuke.arc.files.*;
|
||||
import io.anuke.arc.function.*;
|
||||
import io.anuke.arc.math.*;
|
||||
import io.anuke.arc.scene.ui.*;
|
||||
import io.anuke.arc.util.serialization.*;
|
||||
import io.anuke.mindustry.maps.*;
|
||||
import io.anuke.mindustry.net.*;
|
||||
import io.anuke.mindustry.net.Net.*;
|
||||
import io.anuke.mindustry.ui.dialogs.*;
|
||||
|
||||
import static io.anuke.mindustry.Vars.mobile;
|
||||
|
||||
public interface Platform{
|
||||
|
||||
/** Steam: Update lobby visibility.*/
|
||||
default void updateLobby(){}
|
||||
|
||||
/** Steam: Show multiplayer friend invite dialog.*/
|
||||
default void inviteFriends(){}
|
||||
|
||||
/** Steam: Share a map on the workshop.*/
|
||||
default void publishMap(Map map){}
|
||||
|
||||
/** Steam: Return external workshop maps to be loaded.*/
|
||||
default Array<FileHandle> getExternalMaps(){
|
||||
return Array.with();
|
||||
}
|
||||
|
||||
/** Steam: View a map listing on the workshop.*/
|
||||
default void viewMapListing(Map map){}
|
||||
|
||||
/** Steam: Open workshop for maps.*/
|
||||
default void openWorkshop(){}
|
||||
|
||||
/** Get the networking implementation.*/
|
||||
default NetProvider getNet(){
|
||||
return new ArcNetImpl();
|
||||
}
|
||||
|
||||
/** Add a text input dialog that should show up after the field is tapped. */
|
||||
default void addDialog(TextField field){
|
||||
addDialog(field, 16);
|
||||
@@ -36,11 +65,6 @@ public interface Platform{
|
||||
});
|
||||
}
|
||||
|
||||
/** Request external read/write perms. Run callback when complete.*/
|
||||
default void requestExternalPerms(Runnable callback){
|
||||
callback.run();
|
||||
}
|
||||
|
||||
/** Update discord RPC. */
|
||||
default void updateRPC(){
|
||||
}
|
||||
@@ -70,24 +94,29 @@ public interface Platform{
|
||||
|
||||
/**
|
||||
* Show a file chooser.
|
||||
* @param text File chooser title text
|
||||
* @param content Description of the type of files to be loaded
|
||||
* @param cons Selection listener
|
||||
* @param open Whether to open or save files
|
||||
* @param filetype File extension to filter
|
||||
* @param extension File extension to filter
|
||||
*/
|
||||
default void showFileChooser(String text, String content, Consumer<FileHandle> cons, boolean open, Predicate<String> filetype){
|
||||
default void showFileChooser(boolean open, String extension, Consumer<FileHandle> cons){
|
||||
new FileChooser(open ? "$open" : "$save", file -> file.extension().toLowerCase().equals(extension), open, file -> {
|
||||
if(!open){
|
||||
cons.accept(file.parent().child(file.nameWithoutExtension() + "." + extension));
|
||||
}else{
|
||||
cons.accept(file);
|
||||
}
|
||||
}).show();
|
||||
}
|
||||
|
||||
/** Hide the app. Android only. */
|
||||
default void hide(){
|
||||
}
|
||||
|
||||
/** Forces the app into landscape mode. Currently Android only. */
|
||||
/** Forces the app into landscape mode.*/
|
||||
default void beginForceLandscape(){
|
||||
}
|
||||
|
||||
/** Stops forcing the app into landscape orientation. Currently Android only. */
|
||||
/** Stops forcing the app into landscape orientation.*/
|
||||
default void endForceLandscape(){
|
||||
}
|
||||
}
|
||||
@@ -16,12 +16,14 @@ import io.anuke.mindustry.core.GameState.*;
|
||||
import io.anuke.mindustry.entities.*;
|
||||
import io.anuke.mindustry.entities.effect.*;
|
||||
import io.anuke.mindustry.entities.effect.GroundEffectEntity.*;
|
||||
import io.anuke.mindustry.entities.impl.*;
|
||||
import io.anuke.mindustry.entities.traits.*;
|
||||
import io.anuke.mindustry.entities.type.*;
|
||||
import io.anuke.mindustry.entities.type.EffectEntity;
|
||||
import io.anuke.mindustry.game.EventType.*;
|
||||
import io.anuke.mindustry.game.*;
|
||||
import io.anuke.mindustry.graphics.*;
|
||||
import io.anuke.mindustry.input.*;
|
||||
import io.anuke.mindustry.world.*;
|
||||
import io.anuke.mindustry.world.blocks.defense.ForceProjector.*;
|
||||
|
||||
import static io.anuke.arc.Core.*;
|
||||
@@ -36,8 +38,10 @@ public class Renderer implements ApplicationListener{
|
||||
public FrameBuffer shieldBuffer = new FrameBuffer(2, 2);
|
||||
private Bloom bloom;
|
||||
private Color clearColor;
|
||||
private float targetscale = UnitScl.dp.scl(4);
|
||||
private float targetscale = Scl.scl(4);
|
||||
private float camerascale = targetscale;
|
||||
private float landscale = 0f, landTime;
|
||||
private float minZoomScl = Scl.scl(0.01f);
|
||||
private Rectangle rect = new Rectangle(), rect2 = new Rectangle();
|
||||
private float shakeIntensity, shaketime;
|
||||
|
||||
@@ -99,15 +103,22 @@ public class Renderer implements ApplicationListener{
|
||||
|
||||
@Override
|
||||
public void update(){
|
||||
//TODO hack, find source of this bug
|
||||
Color.WHITE.set(1f, 1f, 1f, 1f);
|
||||
Color.white.set(1f, 1f, 1f, 1f);
|
||||
|
||||
camerascale = Mathf.lerpDelta(camerascale, targetscale, 0.1f);
|
||||
|
||||
if(landTime > 0){
|
||||
landTime -= Time.delta();
|
||||
landscale = Interpolation.pow5In.apply(minZoomScl, Scl.scl(4f), 1f - landTime / Fx.coreLand.lifetime);
|
||||
camerascale = landscale;
|
||||
}
|
||||
|
||||
camera.width = graphics.getWidth() / camerascale;
|
||||
camera.height = graphics.getHeight() / camerascale;
|
||||
|
||||
if(state.is(State.menu)){
|
||||
graphics.clear(Color.BLACK);
|
||||
landTime = 0f;
|
||||
graphics.clear(Color.black);
|
||||
}else{
|
||||
Vector2 position = Tmp.v3.set(player);
|
||||
|
||||
@@ -118,7 +129,7 @@ public class Renderer implements ApplicationListener{
|
||||
}else{
|
||||
camera.position.lerpDelta(position, 0.08f);
|
||||
}
|
||||
}else if(!mobile || settings.getBool("keyboard")){
|
||||
}else if(control.input instanceof DesktopInput){
|
||||
camera.position.lerpDelta(position, 0.08f);
|
||||
}
|
||||
|
||||
@@ -131,6 +142,10 @@ public class Renderer implements ApplicationListener{
|
||||
}
|
||||
}
|
||||
|
||||
public float landScale(){
|
||||
return landTime > 0 ? landscale : 1f;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose(){
|
||||
minimap.dispose();
|
||||
@@ -162,7 +177,7 @@ public class Renderer implements ApplicationListener{
|
||||
e.printStackTrace();
|
||||
settings.put("bloom", false);
|
||||
settings.save();
|
||||
ui.showError("$error.bloom");
|
||||
ui.showErrorMessage("$error.bloom");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -257,13 +272,13 @@ public class Renderer implements ApplicationListener{
|
||||
}
|
||||
|
||||
overlays.drawBottom();
|
||||
playerGroup.draw(p -> true, Player::drawBuildRequests);
|
||||
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);
|
||||
graphics.clear(Color.clear);
|
||||
shieldGroup.draw();
|
||||
shieldGroup.draw(shield -> true, ShieldEntity::drawOver);
|
||||
Draw.flush();
|
||||
@@ -282,10 +297,37 @@ public class Renderer implements ApplicationListener{
|
||||
|
||||
playerGroup.draw(p -> !p.isDead() && !p.isLocal, Player::drawName);
|
||||
|
||||
drawLanding();
|
||||
|
||||
Draw.color();
|
||||
Draw.flush();
|
||||
}
|
||||
|
||||
private void drawLanding(){
|
||||
if(landTime > 0 && player.getClosestCore() != null){
|
||||
float fract = landTime / Fx.coreLand.lifetime;
|
||||
TileEntity entity = player.getClosestCore();
|
||||
|
||||
TextureRegion reg = entity.block.icon(Block.Icon.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);
|
||||
|
||||
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);
|
||||
});
|
||||
|
||||
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.reset();
|
||||
}
|
||||
}
|
||||
|
||||
private void drawGroundShadows(){
|
||||
Draw.color(0, 0, 0, 0.4f);
|
||||
float rad = 1.6f;
|
||||
@@ -348,7 +390,7 @@ public class Renderer implements ApplicationListener{
|
||||
}
|
||||
|
||||
public void clampScale(){
|
||||
float s = UnitScl.dp.scl(1f);
|
||||
float s = Scl.scl(1f);
|
||||
targetscale = Mathf.clamp(targetscale, s * 1.5f, Math.round(s * 6));
|
||||
}
|
||||
|
||||
@@ -361,6 +403,11 @@ public class Renderer implements ApplicationListener{
|
||||
clampScale();
|
||||
}
|
||||
|
||||
public void zoomIn(float duration){
|
||||
landscale = minZoomScl;
|
||||
landTime = duration;
|
||||
}
|
||||
|
||||
public void takeMapScreenshot(){
|
||||
drawGroundShadows();
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ package io.anuke.mindustry.core;
|
||||
import io.anuke.arc.*;
|
||||
import io.anuke.arc.Graphics.*;
|
||||
import io.anuke.arc.Graphics.Cursor.*;
|
||||
import io.anuke.arc.Input.*;
|
||||
import io.anuke.arc.assets.*;
|
||||
import io.anuke.arc.assets.loaders.*;
|
||||
import io.anuke.arc.assets.loaders.resolvers.*;
|
||||
@@ -13,14 +14,13 @@ import io.anuke.arc.freetype.FreeTypeFontGenerator.*;
|
||||
import io.anuke.arc.freetype.FreetypeFontLoader.*;
|
||||
import io.anuke.arc.function.*;
|
||||
import io.anuke.arc.graphics.*;
|
||||
import io.anuke.arc.graphics.Texture.*;
|
||||
import io.anuke.arc.graphics.g2d.*;
|
||||
import io.anuke.arc.graphics.g2d.TextureAtlas.*;
|
||||
import io.anuke.arc.input.*;
|
||||
import io.anuke.arc.math.*;
|
||||
import io.anuke.arc.scene.*;
|
||||
import io.anuke.arc.scene.actions.*;
|
||||
import io.anuke.arc.scene.event.*;
|
||||
import io.anuke.arc.scene.style.*;
|
||||
import io.anuke.arc.scene.ui.*;
|
||||
import io.anuke.arc.scene.ui.TextField.*;
|
||||
import io.anuke.arc.scene.ui.Tooltip.*;
|
||||
@@ -31,6 +31,7 @@ import io.anuke.mindustry.editor.*;
|
||||
import io.anuke.mindustry.game.EventType.*;
|
||||
import io.anuke.mindustry.gen.*;
|
||||
import io.anuke.mindustry.graphics.*;
|
||||
import io.anuke.mindustry.ui.*;
|
||||
import io.anuke.mindustry.ui.dialogs.*;
|
||||
import io.anuke.mindustry.ui.fragments.*;
|
||||
|
||||
@@ -38,8 +39,6 @@ import static io.anuke.arc.scene.actions.Actions.*;
|
||||
import static io.anuke.mindustry.Vars.*;
|
||||
|
||||
public class UI implements ApplicationListener, Loadable{
|
||||
private Skin skin;
|
||||
|
||||
public MenuFragment menufrag;
|
||||
public HudFragment hudfrag;
|
||||
public ChatFragment chatfrag;
|
||||
@@ -73,7 +72,6 @@ public class UI implements ApplicationListener, Loadable{
|
||||
public Cursor drillCursor, unloadCursor;
|
||||
|
||||
public UI(){
|
||||
skin = new Skin();
|
||||
setupFonts();
|
||||
}
|
||||
|
||||
@@ -84,22 +82,19 @@ public class UI implements ApplicationListener, Loadable{
|
||||
|
||||
@Override
|
||||
public void loadSync(){
|
||||
//TODO type-safe skin files
|
||||
skin.addRegions(Core.atlas);
|
||||
loadExtraStyle(skin);
|
||||
skin.add("outline", Core.assets.get("outline"));
|
||||
skin.getFont("outline").getData().markupEnabled = true;
|
||||
skin.getFont("default").getData().markupEnabled = true;
|
||||
skin.getFont("default").setOwnsTexture(false);
|
||||
skin.load(Core.files.internal("sprites/uiskin.json"));
|
||||
Fonts.outline.getData().markupEnabled = true;
|
||||
Fonts.def.getData().markupEnabled = true;
|
||||
Fonts.def.setOwnsTexture(false);
|
||||
|
||||
for(BitmapFont font : skin.getAll(BitmapFont.class).values()){
|
||||
font.setUseIntegerPositions(true);
|
||||
}
|
||||
|
||||
Core.scene = new Scene(skin);
|
||||
Core.assets.getAll(BitmapFont.class, new Array<>()).each(font -> font.setUseIntegerPositions(true));
|
||||
Core.scene = new Scene();
|
||||
Core.input.addProcessor(Core.scene);
|
||||
|
||||
Tex.load();
|
||||
Icon.load();
|
||||
Styles.load();
|
||||
Tex.loadStyles();
|
||||
|
||||
Dialog.setShowAction(() -> sequence(alpha(0f), fadeIn(0.1f)));
|
||||
Dialog.setHideAction(() -> sequence(fadeOut(0.1f)));
|
||||
|
||||
@@ -107,13 +102,13 @@ public class UI implements ApplicationListener, Loadable{
|
||||
|
||||
Core.settings.setErrorHandler(e -> {
|
||||
e.printStackTrace();
|
||||
Core.app.post(() -> showError("Failed to access local storage.\nSettings will not be saved."));
|
||||
Core.app.post(() -> showErrorMessage("Failed to access local storage.\nSettings will not be saved."));
|
||||
});
|
||||
|
||||
ClickListener.clicked = () -> Sounds.press.play();
|
||||
|
||||
Colors.put("accent", Pal.accent);
|
||||
Colors.put("highlight", Pal.accent.cpy().lerp(Color.WHITE, 0.3f));
|
||||
Colors.put("highlight", Pal.accent.cpy().lerp(Color.white, 0.3f));
|
||||
Colors.put("stat", Pal.stat);
|
||||
loadExtraCursors();
|
||||
}
|
||||
@@ -140,39 +135,22 @@ public class UI implements ApplicationListener, Loadable{
|
||||
@Override
|
||||
public BitmapFont loadSync(AssetManager manager, String fileName, FileHandle file, FreeTypeFontLoaderParameter parameter){
|
||||
if(fileName.equals("outline")){
|
||||
parameter.fontParameters.borderWidth = UnitScl.dp.scl(2f);
|
||||
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.DARK_GRAY;
|
||||
borderColor = Color.darkGray;
|
||||
incremental = true;
|
||||
}};
|
||||
|
||||
Core.assets.load("outline", BitmapFont.class, new FreeTypeFontLoaderParameter("fonts/font.ttf", param));
|
||||
}
|
||||
|
||||
void loadExtraStyle(Skin skin){
|
||||
AtlasRegion region = Core.atlas.find("flat-down-base");
|
||||
int[] splits = region.splits;
|
||||
|
||||
ScaledNinePatchDrawable copy = new ScaledNinePatchDrawable(new NinePatch(region, splits[0], splits[1], splits[2], splits[3])){
|
||||
public float getLeftWidth(){ return 0; }
|
||||
public float getRightWidth(){ return 0; }
|
||||
public float getTopHeight(){ return 0; }
|
||||
public float getBottomHeight(){ return 0; }
|
||||
};
|
||||
copy.setMinWidth(0);
|
||||
copy.setMinHeight(0);
|
||||
copy.setTopHeight(0);
|
||||
copy.setRightWidth(0);
|
||||
copy.setBottomHeight(0);
|
||||
copy.setLeftWidth(0);
|
||||
skin.add("flat-down", copy, Drawable.class);
|
||||
Core.assets.load("outline", BitmapFont.class, new FreeTypeFontLoaderParameter("fonts/font.ttf", param)).loaded = t -> Fonts.outline = (BitmapFont)t;
|
||||
}
|
||||
|
||||
void loadExtraCursors(){
|
||||
@@ -185,14 +163,14 @@ public class UI implements ApplicationListener, Loadable{
|
||||
|
||||
FreeTypeFontParameter param = fontParameter();
|
||||
|
||||
Core.assets.load("default", BitmapFont.class, new FreeTypeFontLoaderParameter(fontName, param)).loaded = f -> skin.add("default", f);
|
||||
Core.assets.load("chat", BitmapFont.class, new FreeTypeFontLoaderParameter(fontName, param)).loaded = f -> skin.add("chat", f);
|
||||
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)(UnitScl.dp.scl(18f));
|
||||
shadowColor = Color.DARK_GRAY;
|
||||
size = (int)(Scl.scl(18f));
|
||||
shadowColor = Color.darkGray;
|
||||
shadowOffsetY = 2;
|
||||
incremental = true;
|
||||
}};
|
||||
@@ -290,28 +268,37 @@ public class UI implements ApplicationListener, Loadable{
|
||||
});
|
||||
}
|
||||
|
||||
public void showTextInput(String titleText, String text, int textLength, String def, TextFieldFilter filter, Consumer<String> confirmed){
|
||||
new Dialog(titleText, "dialog"){{
|
||||
cont.margin(30).add(text).padRight(6f);
|
||||
TextField field = cont.addField(def, t -> {
|
||||
}).size(170f, 50f).get();
|
||||
field.setFilter((f, c) -> field.getText().length() < textLength && filter.acceptChar(f, c));
|
||||
platform.addDialog(field);
|
||||
buttons.defaults().size(120, 54).pad(4);
|
||||
buttons.addButton("$ok", () -> {
|
||||
confirmed.accept(field.getText());
|
||||
hide();
|
||||
}).disabled(b -> field.getText().isEmpty());
|
||||
buttons.addButton("$cancel", this::hide);
|
||||
}}.show();
|
||||
public void showTextInput(String titleText, String dtext, int textLength, String def, boolean inumeric, Consumer<String> confirmed){
|
||||
if(mobile){
|
||||
Core.input.getTextInput(new TextInput(){{
|
||||
this.title = (titleText.startsWith("$") ? Core.bundle.get(titleText.substring(1)) : titleText);
|
||||
this.text = def;
|
||||
this.numeric = inumeric;
|
||||
this.maxLength = textLength;
|
||||
this.accepted = confirmed;
|
||||
}});
|
||||
}else{
|
||||
new Dialog(titleText){{
|
||||
cont.margin(30).add(dtext).padRight(6f);
|
||||
TextFieldFilter filter = inumeric ? TextFieldFilter.digitsOnly : (f, c) -> true;
|
||||
TextField field = cont.addField(def, t -> {}).size(170f, 50f).get();
|
||||
field.setFilter((f, c) -> field.getText().length() < textLength && filter.acceptChar(f, c));
|
||||
buttons.defaults().size(120, 54).pad(4);
|
||||
buttons.addButton("$ok", () -> {
|
||||
confirmed.accept(field.getText());
|
||||
hide();
|
||||
}).disabled(b -> field.getText().isEmpty());
|
||||
buttons.addButton("$cancel", this::hide);
|
||||
}}.show();
|
||||
}
|
||||
}
|
||||
|
||||
public void showTextInput(String title, String text, String def, Consumer<String> confirmed){
|
||||
showTextInput(title, text, 12, def, (field, c) -> true, confirmed);
|
||||
showTextInput(title, text, 24, def, confirmed);
|
||||
}
|
||||
|
||||
public void showTextInput(String title, String text, int textLength, String def, Consumer<String> confirmed){
|
||||
showTextInput(title, text, textLength < 0 ? 12 : textLength, def, (field, c) -> true, confirmed);
|
||||
public void showTextInput(String titleText, String text, int textLength, String def, Consumer<String> confirmed){
|
||||
showTextInput(titleText, text, textLength, def, false, confirmed);
|
||||
}
|
||||
|
||||
public void showInfoFade(String info){
|
||||
@@ -323,48 +310,72 @@ public class UI implements ApplicationListener, Loadable{
|
||||
}
|
||||
|
||||
public void showInfo(String info){
|
||||
new Dialog("", "dialog"){{
|
||||
new Dialog(""){{
|
||||
getCell(cont).growX();
|
||||
cont.margin(15).add(info).width(400f).wrap().get().setAlignment(Align.center, Align.center);
|
||||
buttons.addButton("$ok", this::hide).size(90, 50).pad(4);
|
||||
}}.show();
|
||||
}
|
||||
|
||||
public void showError(String text){
|
||||
new Dialog("", "dialog"){{
|
||||
public void showErrorMessage(String text){
|
||||
new Dialog(""){{
|
||||
setFillParent(true);
|
||||
cont.margin(15f);
|
||||
cont.add("$error.title");
|
||||
cont.row();
|
||||
cont.margin(15).pane(t -> {
|
||||
Label l = t.add(text).pad(14f).get();
|
||||
l.setAlignment(Align.center, Align.left);
|
||||
if(mobile){
|
||||
t.getCell(l).wrap().width(400f);
|
||||
}
|
||||
});
|
||||
buttons.addButton("$ok", this::hide).size(90, 50).pad(4);
|
||||
cont.addImage().width(300f).pad(2).height(4f).color(Color.scarlet);
|
||||
cont.row();
|
||||
cont.add(text).pad(2f).growX().wrap().get().setAlignment(Align.center);
|
||||
cont.row();
|
||||
cont.addButton("$ok", this::hide).size(120, 50).pad(4);
|
||||
}}.show();
|
||||
}
|
||||
|
||||
public void showException(Throwable t){
|
||||
showException("", t);
|
||||
}
|
||||
|
||||
public void showException(String text, Throwable exc){
|
||||
new Dialog(""){{
|
||||
String message = Strings.getFinalMesage(exc);
|
||||
|
||||
setFillParent(true);
|
||||
cont.margin(15);
|
||||
cont.add("$error.title").colspan(2);
|
||||
cont.row();
|
||||
cont.addImage().width(300f).pad(2).colspan(2).height(4f).color(Color.scarlet);
|
||||
cont.row();
|
||||
cont.add((text.startsWith("$") ? Core.bundle.get(text.substring(1)) : text) + (message == null ? "" : "\n[lightgray](" + message + ")")).colspan(2).wrap().growX().center().get().setAlignment(Align.center);
|
||||
cont.row();
|
||||
|
||||
Collapser col = new Collapser(base -> base.pane(t -> t.margin(14f).add(Strings.parseException(exc, true)).color(Color.lightGray).left()), true);
|
||||
|
||||
cont.addButton("$details", Styles.togglet, col::toggle).size(180f, 50f).checked(b -> !col.isCollapsed()).fillX().right();
|
||||
cont.addButton("$ok", this::hide).size(100, 50).fillX().left();
|
||||
cont.row();
|
||||
cont.add(col).colspan(2).pad(2);
|
||||
}}.show();
|
||||
}
|
||||
|
||||
public void showText(String titleText, String text){
|
||||
new Dialog(titleText, "dialog"){{
|
||||
new Dialog(titleText){{
|
||||
cont.margin(15).add(text).width(400f).wrap().get().setAlignment(Align.center, Align.center);
|
||||
buttons.addButton("$ok", this::hide).size(90, 50).pad(4);
|
||||
}}.show();
|
||||
}
|
||||
|
||||
public void showInfoText(String titleText, String text){
|
||||
new Dialog(titleText, "dialog"){{
|
||||
new Dialog(titleText){{
|
||||
cont.margin(15).add(text).width(400f).wrap().left().get().setAlignment(Align.left, Align.left);
|
||||
buttons.addButton("$ok", this::hide).size(90, 50).pad(4);
|
||||
}}.show();
|
||||
}
|
||||
|
||||
public void showSmall(String titleText, String text){
|
||||
new Dialog(titleText, "dialog"){{
|
||||
new Dialog(titleText){{
|
||||
cont.margin(10).add(text);
|
||||
titleTable.row();
|
||||
titleTable.addImage("whiteui").color(Pal.accent).height(3f).growX().pad(2f);
|
||||
titleTable.addImage().color(Pal.accent).height(3f).growX().pad(2f);
|
||||
buttons.addButton("$ok", this::hide).size(90, 50).pad(4);
|
||||
}}.show();
|
||||
}
|
||||
|
||||
@@ -215,7 +215,7 @@ public class World{
|
||||
}catch(Exception e){
|
||||
Log.err(e);
|
||||
if(!headless){
|
||||
ui.showError("$map.invalid");
|
||||
ui.showErrorMessage("$map.invalid");
|
||||
Core.app.post(() -> state.set(State.menu));
|
||||
invalidMap = true;
|
||||
}
|
||||
@@ -229,7 +229,7 @@ public class World{
|
||||
|
||||
if(!headless){
|
||||
if(state.teams.get(defaultTeam).cores.size == 0 && !checkRules.pvp){
|
||||
ui.showError("$map.nospawn");
|
||||
ui.showErrorMessage("$map.nospawn");
|
||||
invalidMap = true;
|
||||
}else if(checkRules.pvp){ //pvp maps need two cores to be valid
|
||||
int teams = 0;
|
||||
@@ -240,12 +240,12 @@ public class World{
|
||||
}
|
||||
if(teams < 2){
|
||||
invalidMap = true;
|
||||
ui.showError("$map.nospawn.pvp");
|
||||
ui.showErrorMessage("$map.nospawn.pvp");
|
||||
}
|
||||
}else if(checkRules.attackMode){ //attack maps need two cores to be valid
|
||||
invalidMap = state.teams.get(waveTeam).cores.isEmpty();
|
||||
if(invalidMap){
|
||||
ui.showError("$map.nospawn.attack");
|
||||
ui.showErrorMessage("$map.nospawn.attack");
|
||||
}
|
||||
}
|
||||
}else{
|
||||
|
||||
@@ -13,19 +13,19 @@ import io.anuke.arc.scene.actions.*;
|
||||
import io.anuke.arc.scene.event.*;
|
||||
import io.anuke.arc.scene.style.*;
|
||||
import io.anuke.arc.scene.ui.*;
|
||||
import io.anuke.arc.scene.ui.TextButton.*;
|
||||
import io.anuke.arc.scene.ui.layout.*;
|
||||
import io.anuke.arc.util.*;
|
||||
import io.anuke.mindustry.*;
|
||||
import io.anuke.mindustry.content.*;
|
||||
import io.anuke.mindustry.core.GameState.*;
|
||||
import io.anuke.mindustry.game.*;
|
||||
import io.anuke.mindustry.gen.*;
|
||||
import io.anuke.mindustry.graphics.*;
|
||||
import io.anuke.mindustry.io.*;
|
||||
import io.anuke.mindustry.maps.*;
|
||||
import io.anuke.mindustry.ui.Styles;
|
||||
import io.anuke.mindustry.ui.dialogs.*;
|
||||
import io.anuke.mindustry.world.*;
|
||||
import io.anuke.mindustry.world.Block.*;
|
||||
import io.anuke.mindustry.world.blocks.*;
|
||||
import io.anuke.mindustry.world.blocks.storage.*;
|
||||
|
||||
@@ -47,9 +47,9 @@ public class MapEditorDialog extends Dialog implements Disposable{
|
||||
private Array<Block> blocksOut = new Array<>();
|
||||
|
||||
public MapEditorDialog(){
|
||||
super("", "dialog");
|
||||
super("");
|
||||
|
||||
background("dark");
|
||||
background(Styles.black);
|
||||
|
||||
editor = new MapEditor();
|
||||
view = new MapView(editor);
|
||||
@@ -59,82 +59,75 @@ public class MapEditorDialog extends Dialog implements Disposable{
|
||||
menu = new FloatingDialog("$menu");
|
||||
menu.addCloseButton();
|
||||
|
||||
float isize = iconsize;
|
||||
float swidth = 180f;
|
||||
|
||||
menu.cont.table(t -> {
|
||||
t.defaults().size(swidth, 60f).padBottom(5).padRight(5).padLeft(5);
|
||||
|
||||
t.addImageTextButton("$editor.savemap", "icon-floppy-16", isize, this::save);
|
||||
t.addImageTextButton("$editor.savemap", Icon.floppy16Small, this::save);
|
||||
|
||||
t.addImageTextButton("$editor.mapinfo", "icon-pencil", isize, () -> {
|
||||
t.addImageTextButton("$editor.mapinfo", Icon.pencilSmall, () -> {
|
||||
infoDialog.show();
|
||||
menu.hide();
|
||||
});
|
||||
|
||||
t.row();
|
||||
|
||||
t.addImageTextButton("$editor.generate", "icon-editor", isize, () -> {
|
||||
t.addImageTextButton("$editor.generate", Icon.editorSmall, () -> {
|
||||
generateDialog.show(generateDialog::applyToEditor);
|
||||
menu.hide();
|
||||
});
|
||||
|
||||
t.addImageTextButton("$editor.resize", "icon-resize", isize, () -> {
|
||||
t.addImageTextButton("$editor.resize", Icon.resizeSmall, () -> {
|
||||
resizeDialog.show();
|
||||
menu.hide();
|
||||
});
|
||||
|
||||
t.row();
|
||||
|
||||
if(!ios){
|
||||
t.addImageTextButton("$editor.import", "icon-load-map", isize, () ->
|
||||
createDialog("$editor.import",
|
||||
"$editor.importmap", "$editor.importmap.description", "icon-load-map", (Runnable)loadDialog::show,
|
||||
"$editor.importfile", "$editor.importfile.description", "icon-file", (Runnable)() ->
|
||||
platform.showFileChooser("$editor.loadmap", "Map Files", file -> ui.loadAnd(() -> {
|
||||
maps.tryCatchMapError(() -> {
|
||||
if(MapIO.isImage(file)){
|
||||
ui.showInfo("$editor.errorimage");
|
||||
}else if(file.extension().equalsIgnoreCase(oldMapExtension)){
|
||||
editor.beginEdit(maps.makeLegacyMap(file));
|
||||
}else{
|
||||
editor.beginEdit(MapIO.createMap(file, true));
|
||||
}
|
||||
});
|
||||
}), true, FileChooser.anyMapFiles),
|
||||
|
||||
"$editor.importimage", "$editor.importimage.description", "icon-file-image", (Runnable)() ->
|
||||
platform.showFileChooser("$loadimage", "Image Files", file ->
|
||||
ui.loadAnd(() -> {
|
||||
try{
|
||||
Pixmap pixmap = new Pixmap(file);
|
||||
editor.beginEdit(pixmap);
|
||||
pixmap.dispose();
|
||||
}catch(Exception e){
|
||||
ui.showError(Core.bundle.format("editor.errorload", Strings.parseException(e, true)));
|
||||
Log.err(e);
|
||||
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));
|
||||
}
|
||||
}), true, FileChooser.pngFiles))
|
||||
);
|
||||
}
|
||||
});
|
||||
})),
|
||||
|
||||
Cell cell = t.addImageTextButton("$editor.export", "icon-save-map", isize, () -> {
|
||||
"$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("$editor.savemap", "Map Files", file -> {
|
||||
file = file.parent().child(file.nameWithoutExtension() + "." + mapExtension);
|
||||
FileHandle result = file;
|
||||
platform.showFileChooser(false, mapExtension, file -> {
|
||||
ui.loadAnd(() -> {
|
||||
try{
|
||||
if(!editor.getTags().containsKey("name")){
|
||||
editor.getTags().put("name", result.nameWithoutExtension());
|
||||
editor.getTags().put("name", file.nameWithoutExtension());
|
||||
}
|
||||
MapIO.writeMap(result, editor.createMap(result));
|
||||
MapIO.writeMap(file, editor.createMap(file));
|
||||
}catch(Exception e){
|
||||
ui.showError(Core.bundle.format("editor.errorsave", Strings.parseException(e, true)));
|
||||
ui.showException("$editor.errorsave", e);
|
||||
Log.err(e);
|
||||
}
|
||||
});
|
||||
}, false, FileChooser.mapFiles);
|
||||
});
|
||||
}else{
|
||||
ui.loadAnd(() -> {
|
||||
try{
|
||||
@@ -142,25 +135,43 @@ public class MapEditorDialog extends Dialog implements Disposable{
|
||||
MapIO.writeMap(result, editor.createMap(result));
|
||||
platform.shareFile(result);
|
||||
}catch(Exception e){
|
||||
ui.showError(Core.bundle.format("editor.errorsave", Strings.parseException(e, true)));
|
||||
ui.showException("$editor.errorsave", e);
|
||||
Log.err(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
if(ios){
|
||||
cell.size(swidth * 2f + 10, 60f).colspan(2);
|
||||
}
|
||||
});
|
||||
|
||||
menu.cont.row();
|
||||
|
||||
menu.cont.addImageTextButton("$editor.ingame", "icon-arrow", isize, this::playtest).padTop(-5).size(swidth * 2f + 10, 60f);
|
||||
if(steam){
|
||||
menu.cont.addImageTextButton("$editor.publish.workshop", Icon.linkSmall, () -> {
|
||||
Map map = save();
|
||||
|
||||
if(map == null) return;
|
||||
|
||||
if(map.tags.get("description", "").length() < 4){
|
||||
ui.showErrorMessage("$editor.nodescription");
|
||||
return;
|
||||
}
|
||||
|
||||
if(!Structs.contains(Gamemode.all, g -> g.valid(map))){
|
||||
ui.showErrorMessage("$map.nospawn");
|
||||
return;
|
||||
}
|
||||
|
||||
platform.publishMap(map);
|
||||
}).padTop(-3).size(swidth * 2f + 10, 60f).update(b -> b.setText(editor.getTags().containsKey("steamid") ? "$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.row();
|
||||
|
||||
menu.cont.addImageTextButton("$quit", "icon-back", isize, () -> {
|
||||
menu.cont.addImageTextButton("$quit", Icon.backSmall, () -> {
|
||||
tryExit();
|
||||
menu.hide();
|
||||
}).size(swidth * 2f + 10, 60f);
|
||||
@@ -177,7 +188,7 @@ public class MapEditorDialog extends Dialog implements Disposable{
|
||||
try{
|
||||
editor.beginEdit(map);
|
||||
}catch(Exception e){
|
||||
ui.showError(Core.bundle.format("editor.errorload", Strings.parseException(e, true)));
|
||||
ui.showException("$editor.errorload", e);
|
||||
Log.err(e);
|
||||
}
|
||||
}));
|
||||
@@ -186,7 +197,6 @@ public class MapEditorDialog extends Dialog implements Disposable{
|
||||
|
||||
clearChildren();
|
||||
margin(0);
|
||||
shown(this::build);
|
||||
|
||||
update(() -> {
|
||||
if(Core.scene.getKeyboardFocus() instanceof Dialog && Core.scene.getKeyboardFocus() != this){
|
||||
@@ -228,11 +238,8 @@ public class MapEditorDialog extends Dialog implements Disposable{
|
||||
platform.updateRPC();
|
||||
if(!Core.settings.getBool("landscape")) platform.endForceLandscape();
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void drawBackground(float x, float y){
|
||||
drawDefaultBackground(x, y);
|
||||
shown(this::build);
|
||||
}
|
||||
|
||||
public void resumeEditing(){
|
||||
@@ -253,6 +260,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;
|
||||
world.setMap(new Map(StringMap.of(
|
||||
"name", "Editor Playtesting",
|
||||
"width", editor.width(),
|
||||
@@ -274,33 +282,39 @@ public class MapEditorDialog extends Dialog implements Disposable{
|
||||
});
|
||||
}
|
||||
|
||||
private void save(){
|
||||
public Map save(){
|
||||
boolean isEditor = state.rules.editor;
|
||||
state.rules.editor = false;
|
||||
String name = editor.getTags().get("name", "").trim();
|
||||
editor.getTags().put("rules", JsonIO.write(state.rules));
|
||||
editor.getTags().remove("width");
|
||||
editor.getTags().remove("height");
|
||||
player.dead = true;
|
||||
|
||||
Map returned = null;
|
||||
|
||||
if(name.isEmpty()){
|
||||
infoDialog.show();
|
||||
Core.app.post(() -> ui.showError("$editor.save.noname"));
|
||||
Core.app.post(() -> ui.showErrorMessage("$editor.save.noname"));
|
||||
}else{
|
||||
Map map = maps.all().find(m -> m.name().equals(name));
|
||||
if(map != null && !map.custom){
|
||||
handleSaveBuiltin(map);
|
||||
}else{
|
||||
maps.saveMap(editor.getTags());
|
||||
returned = maps.saveMap(editor.getTags());
|
||||
ui.showInfoFade("$editor.saved");
|
||||
}
|
||||
}
|
||||
|
||||
menu.hide();
|
||||
saved = true;
|
||||
state.rules.editor = isEditor;
|
||||
return returned;
|
||||
}
|
||||
|
||||
/** Called when a built-in map save is attempted.*/
|
||||
protected void handleSaveBuiltin(Map map){
|
||||
ui.showError("$editor.save.overwrite");
|
||||
ui.showErrorMessage("$editor.save.overwrite");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -320,7 +334,7 @@ public class MapEditorDialog extends Dialog implements Disposable{
|
||||
for(int i = 0; i < arguments.length; i += 4){
|
||||
String name = (String)arguments[i];
|
||||
String description = (String)arguments[i + 1];
|
||||
String iconname = (String)arguments[i + 2];
|
||||
Drawable iconname = (Drawable)arguments[i + 2];
|
||||
Runnable listenable = (Runnable)arguments[i + 3];
|
||||
|
||||
TextButton button = dialog.cont.addButton(name, () -> {
|
||||
@@ -330,11 +344,11 @@ public class MapEditorDialog extends Dialog implements Disposable{
|
||||
}).left().margin(0).get();
|
||||
|
||||
button.clearChildren();
|
||||
button.addImage(iconname).size(iconsize).padLeft(10);
|
||||
button.addImage(iconname).padLeft(10);
|
||||
button.table(t -> {
|
||||
t.add(name).growX().wrap();
|
||||
t.row();
|
||||
t.add(description).color(Color.GRAY).growX().wrap();
|
||||
t.add(description).color(Color.gray).growX().wrap();
|
||||
}).growX().pad(10f).padLeft(5);
|
||||
|
||||
button.row();
|
||||
@@ -369,7 +383,7 @@ public class MapEditorDialog extends Dialog implements Disposable{
|
||||
show();
|
||||
}catch(Exception e){
|
||||
Log.err(e);
|
||||
ui.showError(Core.bundle.format("editor.errorload", Strings.parseException(e, true)));
|
||||
ui.showException("$editor.errorload", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -407,14 +421,13 @@ public class MapEditorDialog extends Dialog implements Disposable{
|
||||
|
||||
Consumer<EditorTool> addTool = tool -> {
|
||||
|
||||
ImageButton button = new ImageButton("icon-" + tool.name() + "-small", "clear-toggle");
|
||||
ImageButton button = new ImageButton(Core.atlas.drawable("icon-" + tool.name() + "-small"), Styles.clearTogglei);
|
||||
button.clicked(() -> {
|
||||
view.setTool(tool);
|
||||
if(lastTable[0] != null){
|
||||
lastTable[0].remove();
|
||||
}
|
||||
});
|
||||
button.resizeImage(iconsizesmall);
|
||||
button.update(() -> button.setChecked(view.getTool() == tool));
|
||||
group.add(button);
|
||||
|
||||
@@ -434,7 +447,7 @@ public class MapEditorDialog extends Dialog implements Disposable{
|
||||
lastTable[0].remove();
|
||||
}
|
||||
|
||||
Table table = new Table("dialogDim");
|
||||
Table table = new Table(Styles.black9);
|
||||
table.defaults().size(300f, 70f);
|
||||
|
||||
for(int i = 0; i < tool.altModes.length; i++){
|
||||
@@ -444,10 +457,10 @@ public class MapEditorDialog extends Dialog implements Disposable{
|
||||
table.addButton(b -> {
|
||||
b.left();
|
||||
b.marginLeft(6);
|
||||
b.setStyle(Core.scene.skin.get("clear-toggle", TextButtonStyle.class));
|
||||
b.setStyle(Styles.clearTogglet);
|
||||
b.add(Core.bundle.get("toolmode." + name)).left();
|
||||
b.row();
|
||||
b.add(Core.bundle.get("toolmode." + name + ".description")).color(Color.LIGHT_GRAY).left();
|
||||
b.add(Core.bundle.get("toolmode." + name + ".description")).color(Color.lightGray).left();
|
||||
}, () -> {
|
||||
tool.mode = (tool.mode == mode ? -1 : mode);
|
||||
table.remove();
|
||||
@@ -484,16 +497,16 @@ public class MapEditorDialog extends Dialog implements Disposable{
|
||||
|
||||
tools.defaults().size(size, size);
|
||||
|
||||
tools.addImageButton("icon-menu-large-small", "clear", iconsizesmall, menu::show);
|
||||
tools.addImageButton(Icon.menuLargeSmall, Styles.cleari, menu::show);
|
||||
|
||||
ImageButton grid = tools.addImageButton("icon-grid-small", "clear-toggle", iconsizesmall, () -> view.setGrid(!view.isGrid())).get();
|
||||
ImageButton grid = tools.addImageButton(Icon.gridSmall, Styles.clearTogglei, () -> view.setGrid(!view.isGrid())).get();
|
||||
|
||||
addTool.accept(EditorTool.zoom);
|
||||
|
||||
tools.row();
|
||||
|
||||
ImageButton undo = tools.addImageButton("icon-undo-small", "clear", iconsizesmall, editor::undo).get();
|
||||
ImageButton redo = tools.addImageButton("icon-redo-small", "clear", iconsizesmall, editor::redo).get();
|
||||
ImageButton undo = tools.addImageButton(Icon.undoSmall, Styles.cleari, editor::undo).get();
|
||||
ImageButton redo = tools.addImageButton(Icon.redoSmall, Styles.cleari, editor::redo).get();
|
||||
|
||||
addTool.accept(EditorTool.pick);
|
||||
|
||||
@@ -502,8 +515,8 @@ public class MapEditorDialog extends Dialog implements Disposable{
|
||||
undo.setDisabled(() -> !editor.canUndo());
|
||||
redo.setDisabled(() -> !editor.canRedo());
|
||||
|
||||
undo.update(() -> undo.getImage().setColor(undo.isDisabled() ? Color.GRAY : Color.WHITE));
|
||||
redo.update(() -> redo.getImage().setColor(redo.isDisabled() ? Color.GRAY : Color.WHITE));
|
||||
undo.update(() -> undo.getImage().setColor(undo.isDisabled() ? Color.gray : Color.white));
|
||||
redo.update(() -> redo.getImage().setColor(redo.isDisabled() ? Color.gray : Color.white));
|
||||
grid.update(() -> grid.setChecked(view.isGrid()));
|
||||
|
||||
addTool.accept(EditorTool.line);
|
||||
@@ -515,7 +528,7 @@ public class MapEditorDialog extends Dialog implements Disposable{
|
||||
addTool.accept(EditorTool.fill);
|
||||
addTool.accept(EditorTool.spray);
|
||||
|
||||
ImageButton rotate = tools.addImageButton("icon-arrow-16-small", "clear", iconsizesmall, () -> editor.rotation = (editor.rotation + 1) % 4).get();
|
||||
ImageButton rotate = tools.addImageButton(Icon.arrow16Small, Styles.cleari, () -> editor.rotation = (editor.rotation + 1) % 4).get();
|
||||
rotate.getImage().update(() -> {
|
||||
rotate.getImage().setRotation(editor.rotation * 90);
|
||||
rotate.getImage().setOrigin(Align.center);
|
||||
@@ -523,7 +536,7 @@ public class MapEditorDialog extends Dialog implements Disposable{
|
||||
|
||||
tools.row();
|
||||
|
||||
tools.table("underline", t -> t.add("$editor.teams"))
|
||||
tools.table(Tex.underline, t -> t.add("$editor.teams"))
|
||||
.colspan(3).height(40).width(size * 3f + 3f).padBottom(3);
|
||||
|
||||
tools.row();
|
||||
@@ -533,7 +546,7 @@ public class MapEditorDialog extends Dialog implements Disposable{
|
||||
int i = 0;
|
||||
|
||||
for(Team team : Team.all){
|
||||
ImageButton button = new ImageButton("whiteui", "clear-toggle-partial");
|
||||
ImageButton button = new ImageButton(Tex.whiteui, Styles.clearTogglePartiali);
|
||||
button.margin(4f);
|
||||
button.getImageCell().grow();
|
||||
button.getStyle().imageUpColor = team.color;
|
||||
@@ -549,7 +562,7 @@ public class MapEditorDialog extends Dialog implements Disposable{
|
||||
|
||||
mid.row();
|
||||
|
||||
mid.table("underline", t -> {
|
||||
mid.table(Tex.underline, t -> {
|
||||
Slider slider = new Slider(0, MapEditor.brushSizes.length - 1, 1, false);
|
||||
slider.moved(f -> editor.brushSize = MapEditor.brushSizes[(int)(float)f]);
|
||||
for(int j = 0; j < MapEditor.brushSizes.length; j++){
|
||||
@@ -578,7 +591,6 @@ public class MapEditorDialog extends Dialog implements Disposable{
|
||||
|
||||
if(Core.input.ctrl()){
|
||||
//alt mode select
|
||||
//TODO these keycode are unusable, tweak later
|
||||
for(int i = 0; i < view.getTool().altModes.length + 1; i++){
|
||||
if(Core.input.keyTap(KeyCode.valueOf("NUM_" + (i + 1)))){
|
||||
view.getTool().mode = i - 1;
|
||||
@@ -681,11 +693,11 @@ public class MapEditorDialog extends Dialog implements Disposable{
|
||||
});
|
||||
|
||||
for(Block block : blocksOut){
|
||||
TextureRegion region = block.icon(Icon.medium);
|
||||
TextureRegion region = block.icon(Block.Icon.medium);
|
||||
|
||||
if(!Core.atlas.isFound(region)) continue;
|
||||
|
||||
ImageButton button = new ImageButton("whiteui", "clear-toggle");
|
||||
ImageButton button = new ImageButton(Tex.whiteui, Styles.clearTogglei);
|
||||
button.getStyle().imageUp = new TextureRegionDrawable(region);
|
||||
button.clicked(() -> editor.drawBlock = block);
|
||||
button.resizeImage(8 * 4f);
|
||||
@@ -700,7 +712,7 @@ public class MapEditorDialog extends Dialog implements Disposable{
|
||||
|
||||
group.getButtons().get(2).setChecked(true);
|
||||
|
||||
table.table("underline", extra -> extra.labelWrap(() -> editor.drawBlock.localizedName).width(200f).center()).growX();
|
||||
table.table(Tex.underline, extra -> extra.labelWrap(() -> editor.drawBlock.localizedName).width(200f).center()).growX();
|
||||
table.row();
|
||||
table.add(pane).growY().fillX();
|
||||
}
|
||||
|
||||
@@ -8,10 +8,12 @@ import io.anuke.arc.graphics.Pixmap.*;
|
||||
import io.anuke.arc.math.*;
|
||||
import io.anuke.arc.math.geom.*;
|
||||
import io.anuke.arc.scene.ui.*;
|
||||
import io.anuke.arc.scene.ui.ImageButton.*;
|
||||
import io.anuke.arc.scene.ui.layout.*;
|
||||
import io.anuke.arc.util.*;
|
||||
import io.anuke.arc.util.async.*;
|
||||
import io.anuke.mindustry.game.*;
|
||||
import io.anuke.mindustry.gen.*;
|
||||
import io.anuke.mindustry.graphics.*;
|
||||
import io.anuke.mindustry.io.*;
|
||||
import io.anuke.mindustry.maps.filters.*;
|
||||
@@ -84,7 +86,7 @@ public class MapGenerateDialog extends FloatingDialog{
|
||||
update();
|
||||
}).size(160f, 64f);
|
||||
|
||||
buttons.addImageTextButton("$add", "icon-add", iconsize, this::showAdd).height(64f).width(140f);
|
||||
buttons.addImageTextButton("$add", Icon.add, this::showAdd).height(64f).width(140f);
|
||||
|
||||
if(!applied){
|
||||
hidden(this::apply);
|
||||
@@ -176,10 +178,8 @@ public class MapGenerateDialog extends FloatingDialog{
|
||||
}
|
||||
}
|
||||
}, new Stack(){{
|
||||
add(new Image("loadDim"));
|
||||
add(new Image("icon-refresh"){{
|
||||
setScaling(Scaling.none);
|
||||
}});
|
||||
add(new Image(Styles.black8));
|
||||
add(new Image(Icon.refresh, Scaling.none));
|
||||
visible(() -> generating && !updateEditorOnChange);
|
||||
}}).grow().padRight(10);
|
||||
t.pane(p -> filterTable = p.marginRight(6)).update(pane -> {
|
||||
@@ -216,7 +216,7 @@ public class MapGenerateDialog extends FloatingDialog{
|
||||
}
|
||||
|
||||
void rebuildFilters(){
|
||||
int cols = Math.max((int)(Math.max(filterTable.getParent().getWidth(), Core.graphics.getWidth()/2f * 0.9f) / UnitScl.dp.scl(290f)), 1);
|
||||
int cols = Math.max((int)(Math.max(filterTable.getParent().getWidth(), Core.graphics.getWidth()/2f * 0.9f) / Scl.scl(290f)), 1);
|
||||
filterTable.clearChildren();
|
||||
filterTable.top().left();
|
||||
int i = 0;
|
||||
@@ -224,7 +224,7 @@ public class MapGenerateDialog extends FloatingDialog{
|
||||
for(GenerateFilter filter : filters){
|
||||
|
||||
//main container
|
||||
filterTable.table("button", c -> {
|
||||
filterTable.table(Tex.button, c -> {
|
||||
//icons to perform actions
|
||||
c.table(t -> {
|
||||
t.top();
|
||||
@@ -233,26 +233,26 @@ public class MapGenerateDialog extends FloatingDialog{
|
||||
t.row();
|
||||
|
||||
t.table(b -> {
|
||||
String style = "clear";
|
||||
ImageButtonStyle style = Styles.cleari;
|
||||
b.defaults().size(50f);
|
||||
b.addImageButton("icon-refresh-small", style, iconsizesmall, () -> {
|
||||
b.addImageButton(Icon.refreshSmall, style, () -> {
|
||||
filter.randomize();
|
||||
update();
|
||||
});
|
||||
|
||||
b.addImageButton("icon-arrow-up-small", style, iconsizesmall, () -> {
|
||||
b.addImageButton(Icon.arrowUpSmall, style, () -> {
|
||||
int idx = filters.indexOf(filter);
|
||||
filters.swap(idx, Math.max(0, idx - 1));
|
||||
rebuildFilters();
|
||||
update();
|
||||
});
|
||||
b.addImageButton("icon-arrow-down-small",style, iconsizesmall, () -> {
|
||||
b.addImageButton(Icon.arrowDownSmall, style, () -> {
|
||||
int idx = filters.indexOf(filter);
|
||||
filters.swap(idx, Math.min(filters.size - 1, idx + 1));
|
||||
rebuildFilters();
|
||||
update();
|
||||
});
|
||||
b.addImageButton("icon-trash-small", style, iconsizesmall, () -> {
|
||||
b.addImageButton(Icon.trashSmall, style, () -> {
|
||||
filters.remove(filter);
|
||||
rebuildFilters();
|
||||
update();
|
||||
|
||||
@@ -6,6 +6,7 @@ import io.anuke.arc.scene.ui.*;
|
||||
import io.anuke.mindustry.*;
|
||||
import io.anuke.mindustry.game.*;
|
||||
import io.anuke.mindustry.io.*;
|
||||
import io.anuke.mindustry.ui.*;
|
||||
import io.anuke.mindustry.ui.dialogs.*;
|
||||
|
||||
public class MapInfoDialog extends FloatingDialog{
|
||||
@@ -42,7 +43,7 @@ public class MapInfoDialog extends FloatingDialog{
|
||||
t.row();
|
||||
t.add("$editor.description").padRight(8).left();
|
||||
|
||||
TextArea description = t.addArea(tags.get("description", ""), "textarea", text -> {
|
||||
TextArea description = t.addArea(tags.get("description", ""), Styles.areaField, text -> {
|
||||
tags.put("description", text);
|
||||
}).size(400f, 140f).get();
|
||||
|
||||
|
||||
@@ -48,13 +48,13 @@ public class MapLoadDialog extends FloatingDialog{
|
||||
table.defaults().size(200f, 90f).pad(4f);
|
||||
table.margin(10f);
|
||||
|
||||
ScrollPane pane = new ScrollPane(table, "horizontal");
|
||||
ScrollPane pane = new ScrollPane(table, Styles.horizontalPane);
|
||||
pane.setFadeScrollBars(false);
|
||||
|
||||
for(Map map : maps.all()){
|
||||
|
||||
TextButton button = new TextButton(map.name(), "toggle");
|
||||
button.add(new BorderImage(map.texture, 2f).setScaling(Scaling.fit)).size(16 * 4f);
|
||||
TextButton button = new TextButton(map.name(), Styles.togglet);
|
||||
button.add(new BorderImage(map.safeTexture(), 2f).setScaling(Scaling.fit)).size(16 * 4f);
|
||||
button.getCells().reverse();
|
||||
button.clicked(() -> selected = map);
|
||||
button.getLabelCell().grow().left().padLeft(5f);
|
||||
|
||||
@@ -155,7 +155,7 @@ public class MapRenderer implements Disposable{
|
||||
}
|
||||
|
||||
mesh.draw(idxDecal, region, wx * tilesize + offsetX, wy * tilesize + offsetY, width, height);
|
||||
mesh.setColor(Color.WHITE);
|
||||
mesh.setColor(Color.white);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
package io.anuke.mindustry.editor;
|
||||
|
||||
import io.anuke.arc.function.IntPositionConsumer;
|
||||
import io.anuke.arc.math.Mathf;
|
||||
import io.anuke.arc.scene.ui.layout.Table;
|
||||
import io.anuke.mindustry.ui.dialogs.FloatingDialog;
|
||||
import io.anuke.arc.function.*;
|
||||
import io.anuke.arc.math.*;
|
||||
import io.anuke.arc.scene.ui.layout.*;
|
||||
import io.anuke.mindustry.gen.*;
|
||||
import io.anuke.mindustry.ui.dialogs.*;
|
||||
|
||||
public class MapResizeDialog extends FloatingDialog{
|
||||
private static final int minSize = 50, maxSize = 500, increment = 50;
|
||||
@@ -28,7 +29,7 @@ public class MapResizeDialog extends FloatingDialog{
|
||||
height = move(height, -1);
|
||||
}).size(60f);
|
||||
|
||||
table.table("button", t -> t.label(() -> (w ? width : height) + "")).width(200);
|
||||
table.table(Tex.button, t -> t.label(() -> (w ? width : height) + "")).width(200);
|
||||
|
||||
table.addButton(">", () -> {
|
||||
if(w)
|
||||
|
||||
@@ -55,7 +55,7 @@ public class MapSaveDialog extends FloatingDialog{
|
||||
if(!invalid()){
|
||||
listener.accept(field.getText());
|
||||
}else{
|
||||
ui.showError("$editor.failoverwrite");
|
||||
ui.showErrorMessage("$editor.failoverwrite");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ import io.anuke.arc.math.geom.*;
|
||||
import io.anuke.arc.scene.Element;
|
||||
import io.anuke.arc.scene.event.*;
|
||||
import io.anuke.arc.scene.ui.TextField;
|
||||
import io.anuke.arc.scene.ui.layout.UnitScl;
|
||||
import io.anuke.arc.scene.ui.layout.Scl;
|
||||
import io.anuke.arc.util.*;
|
||||
import io.anuke.mindustry.graphics.Pal;
|
||||
import io.anuke.mindustry.input.Binding;
|
||||
@@ -241,7 +241,7 @@ public class MapView extends Element implements GestureListener{
|
||||
Draw.reset();
|
||||
|
||||
if(grid){
|
||||
Draw.color(Color.GRAY);
|
||||
Draw.color(Color.gray);
|
||||
image.setBounds(centerx - sclwidth / 2, centery - sclheight / 2, sclwidth, sclheight);
|
||||
image.draw();
|
||||
Draw.color();
|
||||
@@ -258,7 +258,7 @@ public class MapView extends Element implements GestureListener{
|
||||
float scaling = zoom * Math.min(width, height) / editor.width();
|
||||
|
||||
Draw.color(Pal.accent);
|
||||
Lines.stroke(UnitScl.dp.scl(2f));
|
||||
Lines.stroke(Scl.scl(2f));
|
||||
|
||||
if((!editor.drawBlock.isMultiblock() || tool == EditorTool.eraser) && tool != EditorTool.fill){
|
||||
if(tool == EditorTool.line && drawing){
|
||||
@@ -294,7 +294,7 @@ public class MapView extends Element implements GestureListener{
|
||||
}
|
||||
|
||||
Draw.color(Pal.accent);
|
||||
Lines.stroke(UnitScl.dp.scl(3f));
|
||||
Lines.stroke(Scl.scl(3f));
|
||||
Lines.rect(x, y, width, height);
|
||||
Draw.reset();
|
||||
|
||||
@@ -320,7 +320,7 @@ public class MapView extends Element implements GestureListener{
|
||||
public boolean zoom(float initialDistance, float distance){
|
||||
if(!active()) return false;
|
||||
float nzoom = distance - initialDistance;
|
||||
zoom += nzoom / 10000f / UnitScl.dp.scl(1f) * zoom;
|
||||
zoom += nzoom / 10000f / Scl.scl(1f) * zoom;
|
||||
clampZoom();
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1,22 +1,23 @@
|
||||
package io.anuke.mindustry.editor;
|
||||
|
||||
import io.anuke.arc.Core;
|
||||
import io.anuke.arc.collection.Array;
|
||||
import io.anuke.arc.graphics.Color;
|
||||
import io.anuke.arc.input.KeyCode;
|
||||
import io.anuke.arc.math.Mathf;
|
||||
import io.anuke.arc.scene.event.Touchable;
|
||||
import io.anuke.arc.scene.ui.Label;
|
||||
import io.anuke.arc.scene.ui.TextField.TextFieldFilter;
|
||||
import io.anuke.arc.scene.ui.layout.Table;
|
||||
import io.anuke.arc.*;
|
||||
import io.anuke.arc.collection.*;
|
||||
import io.anuke.arc.graphics.*;
|
||||
import io.anuke.arc.input.*;
|
||||
import io.anuke.arc.math.*;
|
||||
import io.anuke.arc.scene.event.*;
|
||||
import io.anuke.arc.scene.ui.*;
|
||||
import io.anuke.arc.scene.ui.TextField.*;
|
||||
import io.anuke.arc.scene.ui.layout.*;
|
||||
import io.anuke.arc.util.*;
|
||||
import io.anuke.mindustry.Vars;
|
||||
import io.anuke.mindustry.*;
|
||||
import io.anuke.mindustry.content.*;
|
||||
import io.anuke.mindustry.game.*;
|
||||
import io.anuke.mindustry.graphics.Pal;
|
||||
import io.anuke.mindustry.io.JsonIO;
|
||||
import io.anuke.mindustry.gen.*;
|
||||
import io.anuke.mindustry.graphics.*;
|
||||
import io.anuke.mindustry.io.*;
|
||||
import io.anuke.mindustry.type.*;
|
||||
import io.anuke.mindustry.ui.dialogs.FloatingDialog;
|
||||
import io.anuke.mindustry.ui.dialogs.*;
|
||||
|
||||
import static io.anuke.mindustry.Vars.*;
|
||||
import static io.anuke.mindustry.game.SpawnGroup.never;
|
||||
@@ -61,7 +62,8 @@ public class WaveInfoDialog extends FloatingDialog{
|
||||
groups = maps.readWaves(Core.app.getClipboardText());
|
||||
buildGroups();
|
||||
}catch(Exception e){
|
||||
ui.showError("$waves.invalid");
|
||||
e.printStackTrace();
|
||||
ui.showErrorMessage("$waves.invalid");
|
||||
}
|
||||
dialog.hide();
|
||||
}).disabled(b -> Core.app.getClipboardText() == null || Core.app.getClipboardText().isEmpty());
|
||||
@@ -79,7 +81,7 @@ public class WaveInfoDialog extends FloatingDialog{
|
||||
groups = JsonIO.copy(state.rules.spawns.isEmpty() ? defaultWaves.get() : state.rules.spawns);
|
||||
|
||||
cont.clear();
|
||||
cont.stack(new Table("clear", main -> {
|
||||
cont.stack(new Table(Tex.clear, main -> {
|
||||
main.pane(t -> table = t).growX().growY().padRight(8f).get().setScrollingDisabled(true, false);
|
||||
main.row();
|
||||
main.addButton("$add", () -> {
|
||||
@@ -94,8 +96,8 @@ public class WaveInfoDialog extends FloatingDialog{
|
||||
setAlignment(Align.center, Align.center);
|
||||
}}).width(390f).growY();
|
||||
|
||||
cont.table("clear", m -> {
|
||||
m.add("$waves.preview").color(Color.LIGHT_GRAY).growX().center().get().setAlignment(Align.center, Align.center);
|
||||
cont.table(Tex.clear, m -> {
|
||||
m.add("$waves.preview").color(Color.lightGray).growX().center().get().setAlignment(Align.center, Align.center);
|
||||
m.row();
|
||||
m.addButton("-", () -> {
|
||||
}).update(t -> {
|
||||
@@ -134,7 +136,7 @@ public class WaveInfoDialog extends FloatingDialog{
|
||||
|
||||
if(groups != null){
|
||||
for(SpawnGroup group : groups){
|
||||
table.table("button", t -> {
|
||||
table.table(Tex.button, t -> {
|
||||
t.margin(0).defaults().pad(3).padLeft(5f).growX().left();
|
||||
t.addButton(b -> {
|
||||
b.left();
|
||||
@@ -238,7 +240,7 @@ public class WaveInfoDialog extends FloatingDialog{
|
||||
|
||||
for(int i = start; i < displayed + start; i++){
|
||||
int wave = i;
|
||||
preview.table("underline", table -> {
|
||||
preview.table(Tex.underline, table -> {
|
||||
table.add((wave + 1) + "").color(Pal.accent).center().colspan(2).get().setAlignment(Align.center, Align.center);
|
||||
table.row();
|
||||
|
||||
@@ -252,7 +254,7 @@ public class WaveInfoDialog extends FloatingDialog{
|
||||
if(spawned[j] > 0){
|
||||
UnitType type = content.getByID(ContentType.unit, j);
|
||||
table.addImage(type.iconRegion).size(30f).padRight(4);
|
||||
table.add(spawned[j] + "x").color(Color.LIGHT_GRAY).padRight(6);
|
||||
table.add(spawned[j] + "x").color(Color.lightGray).padRight(6);
|
||||
table.row();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,20 +1,22 @@
|
||||
package io.anuke.mindustry.entities;
|
||||
|
||||
import io.anuke.annotations.Annotations.Struct;
|
||||
import io.anuke.arc.*;
|
||||
import io.anuke.arc.collection.GridBits;
|
||||
import io.anuke.arc.collection.IntQueue;
|
||||
import io.anuke.arc.function.*;
|
||||
import io.anuke.arc.graphics.Color;
|
||||
import io.anuke.arc.math.Mathf;
|
||||
import io.anuke.arc.math.geom.*;
|
||||
import io.anuke.arc.util.Time;
|
||||
import io.anuke.arc.util.*;
|
||||
import io.anuke.mindustry.content.Bullets;
|
||||
import io.anuke.mindustry.content.Fx;
|
||||
import io.anuke.mindustry.entities.Effects.Effect;
|
||||
import io.anuke.mindustry.entities.bullet.Bullet;
|
||||
import io.anuke.mindustry.entities.type.Bullet;
|
||||
import io.anuke.mindustry.entities.effect.Fire;
|
||||
import io.anuke.mindustry.entities.effect.Lightning;
|
||||
import io.anuke.mindustry.entities.type.Unit;
|
||||
import io.anuke.mindustry.game.EventType.*;
|
||||
import io.anuke.mindustry.game.Team;
|
||||
import io.anuke.mindustry.gen.Call;
|
||||
import io.anuke.mindustry.gen.PropCell;
|
||||
@@ -187,6 +189,10 @@ public class Damage{
|
||||
//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()));
|
||||
|
||||
if(complete && damage >= 9999999f && entity == player){
|
||||
Events.fire(Trigger.exclusionDeath);
|
||||
}
|
||||
};
|
||||
|
||||
rect.setSize(radius * 2).setCenter(x, y);
|
||||
|
||||
@@ -7,7 +7,7 @@ import io.anuke.arc.graphics.Color;
|
||||
import io.anuke.arc.math.Mathf;
|
||||
import io.anuke.arc.math.geom.Position;
|
||||
import io.anuke.arc.util.pooling.Pools;
|
||||
import io.anuke.mindustry.entities.impl.EffectEntity;
|
||||
import io.anuke.mindustry.entities.type.EffectEntity;
|
||||
import io.anuke.mindustry.entities.traits.ScaleTrait;
|
||||
|
||||
public class Effects{
|
||||
@@ -49,7 +49,7 @@ public class Effects{
|
||||
}
|
||||
|
||||
public static void effect(Effect effect, float x, float y, float rotation){
|
||||
provider.createEffect(effect, Color.WHITE, x, y, rotation, null);
|
||||
provider.createEffect(effect, Color.white, x, y, rotation, null);
|
||||
}
|
||||
|
||||
public static void effect(Effect effect, float x, float y){
|
||||
@@ -61,7 +61,7 @@ public class Effects{
|
||||
}
|
||||
|
||||
public static void effect(Effect effect, Position loc){
|
||||
provider.createEffect(effect, Color.WHITE, loc.getX(), loc.getY(), 0f, null);
|
||||
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){
|
||||
@@ -73,7 +73,7 @@ public class Effects{
|
||||
}
|
||||
|
||||
public static void effect(Effect effect, float x, float y, float rotation, Object data){
|
||||
provider.createEffect(effect, Color.WHITE, x, y, rotation, data);
|
||||
provider.createEffect(effect, Color.white, x, y, rotation, data);
|
||||
}
|
||||
|
||||
/** Default value is 1000. Higher numbers mean more powerful shake (less falloff). */
|
||||
|
||||
@@ -18,6 +18,8 @@ public class EntityGroup<T extends Entity>{
|
||||
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);
|
||||
private final Array<T> intersectArray = new Array<>();
|
||||
private final Rectangle intersectRect = new Rectangle();
|
||||
private IntMap<T> map;
|
||||
private QuadTree tree;
|
||||
private Consumer<T> removeListener;
|
||||
@@ -161,6 +163,15 @@ public class EntityGroup<T extends Entity>{
|
||||
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));
|
||||
return intersectArray;
|
||||
}
|
||||
|
||||
public QuadTree tree(){
|
||||
if(!useTree) throw new RuntimeException("This group does not support quadtrees! Enable quadtrees when creating it.");
|
||||
return tree;
|
||||
|
||||
@@ -4,6 +4,7 @@ import io.anuke.arc.graphics.g2d.*;
|
||||
import io.anuke.mindustry.content.*;
|
||||
import io.anuke.mindustry.entities.*;
|
||||
import io.anuke.mindustry.entities.Effects.*;
|
||||
import io.anuke.mindustry.entities.type.Bullet;
|
||||
import io.anuke.mindustry.gen.*;
|
||||
|
||||
//TODO scale velocity depending on fslope()
|
||||
@@ -20,7 +21,7 @@ public class ArtilleryBulletType extends BasicBulletType{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(Bullet b){
|
||||
public void update(io.anuke.mindustry.entities.type.Bullet b){
|
||||
super.update(b);
|
||||
|
||||
if(b.timer.get(0, 3 + b.fslope() * 2f)){
|
||||
|
||||
@@ -4,6 +4,7 @@ import io.anuke.arc.Core;
|
||||
import io.anuke.arc.graphics.Color;
|
||||
import io.anuke.arc.graphics.g2d.Draw;
|
||||
import io.anuke.arc.graphics.g2d.TextureRegion;
|
||||
import io.anuke.mindustry.entities.type.Bullet;
|
||||
import io.anuke.mindustry.graphics.Pal;
|
||||
|
||||
/** An extended BulletType for most ammo-based bullets shot from turrets and units. */
|
||||
|
||||
@@ -7,6 +7,7 @@ import io.anuke.mindustry.entities.*;
|
||||
import io.anuke.mindustry.entities.Effects.*;
|
||||
import io.anuke.mindustry.entities.effect.*;
|
||||
import io.anuke.mindustry.entities.traits.*;
|
||||
import io.anuke.mindustry.entities.type.*;
|
||||
import io.anuke.mindustry.game.*;
|
||||
import io.anuke.mindustry.gen.*;
|
||||
import io.anuke.mindustry.graphics.*;
|
||||
|
||||
@@ -4,6 +4,7 @@ import io.anuke.arc.math.geom.Rectangle;
|
||||
import io.anuke.arc.util.Time;
|
||||
import io.anuke.mindustry.content.Fx;
|
||||
import io.anuke.mindustry.entities.Units;
|
||||
import io.anuke.mindustry.entities.type.Bullet;
|
||||
|
||||
public abstract class FlakBulletType extends BasicBulletType{
|
||||
protected static Rectangle rect = new Rectangle();
|
||||
|
||||
@@ -6,6 +6,7 @@ import io.anuke.arc.math.geom.*;
|
||||
import io.anuke.mindustry.content.*;
|
||||
import io.anuke.mindustry.entities.*;
|
||||
import io.anuke.mindustry.entities.effect.*;
|
||||
import io.anuke.mindustry.entities.type.Bullet;
|
||||
import io.anuke.mindustry.type.*;
|
||||
import io.anuke.mindustry.world.*;
|
||||
|
||||
@@ -35,7 +36,7 @@ public class LiquidBulletType extends BulletType{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(Bullet b){
|
||||
public void update(io.anuke.mindustry.entities.type.Bullet b){
|
||||
super.update(b);
|
||||
|
||||
if(liquid.canExtinguish()){
|
||||
@@ -49,8 +50,8 @@ public class LiquidBulletType extends BulletType{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Bullet b){
|
||||
Draw.color(liquid.color, Color.WHITE, b.fout() / 100f);
|
||||
public void draw(io.anuke.mindustry.entities.type.Bullet b){
|
||||
Draw.color(liquid.color, Color.white, b.fout() / 100f);
|
||||
|
||||
Fill.circle(b.x, b.y, 0.5f + b.fout() * 2.5f);
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import io.anuke.arc.math.Angles;
|
||||
import io.anuke.arc.math.Mathf;
|
||||
import io.anuke.mindustry.content.Fx;
|
||||
import io.anuke.mindustry.entities.Effects;
|
||||
import io.anuke.mindustry.entities.type.Bullet;
|
||||
import io.anuke.mindustry.graphics.Pal;
|
||||
import io.anuke.mindustry.world.blocks.distribution.MassDriver.DriverBulletData;
|
||||
|
||||
@@ -23,7 +24,7 @@ public class MassDriverBolt extends BulletType{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Bullet b){
|
||||
public void draw(io.anuke.mindustry.entities.type.Bullet b){
|
||||
float w = 11f, h = 13f;
|
||||
|
||||
Draw.color(Pal.bulletYellowBack);
|
||||
@@ -36,7 +37,7 @@ public class MassDriverBolt extends BulletType{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(Bullet b){
|
||||
public void update(io.anuke.mindustry.entities.type.Bullet b){
|
||||
//data MUST be an instance of DriverBulletData
|
||||
if(!(b.getData() instanceof DriverBulletData)){
|
||||
hit(b);
|
||||
@@ -82,7 +83,7 @@ public class MassDriverBolt extends BulletType{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void despawned(Bullet b){
|
||||
public void despawned(io.anuke.mindustry.entities.type.Bullet b){
|
||||
super.despawned(b);
|
||||
|
||||
if(!(b.getData() instanceof DriverBulletData)) return;
|
||||
@@ -93,7 +94,7 @@ public class MassDriverBolt extends BulletType{
|
||||
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));
|
||||
Effects.effect(Fx.dropItem, Color.white, b.x, b.y, angle, content.item(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import io.anuke.arc.math.Mathf;
|
||||
import io.anuke.arc.util.Time;
|
||||
import io.anuke.mindustry.content.Fx;
|
||||
import io.anuke.mindustry.entities.Effects;
|
||||
import io.anuke.mindustry.entities.type.Bullet;
|
||||
import io.anuke.mindustry.gen.*;
|
||||
import io.anuke.mindustry.graphics.Pal;
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ package io.anuke.mindustry.entities.effect;
|
||||
import io.anuke.arc.graphics.g2d.Draw;
|
||||
import io.anuke.arc.math.Mathf;
|
||||
import io.anuke.mindustry.entities.EntityGroup;
|
||||
import io.anuke.mindustry.entities.impl.TimedEntity;
|
||||
import io.anuke.mindustry.entities.type.TimedEntity;
|
||||
import io.anuke.mindustry.entities.traits.BelowLiquidTrait;
|
||||
import io.anuke.mindustry.entities.traits.DrawTrait;
|
||||
import io.anuke.mindustry.graphics.Pal;
|
||||
|
||||
@@ -1,18 +1,19 @@
|
||||
package io.anuke.mindustry.entities.effect;
|
||||
|
||||
import io.anuke.annotations.Annotations.*;
|
||||
import io.anuke.arc.*;
|
||||
import io.anuke.arc.collection.*;
|
||||
import io.anuke.arc.math.*;
|
||||
import io.anuke.arc.math.geom.*;
|
||||
import io.anuke.arc.util.*;
|
||||
import io.anuke.mindustry.content.*;
|
||||
import io.anuke.mindustry.entities.*;
|
||||
import io.anuke.mindustry.entities.impl.*;
|
||||
import io.anuke.mindustry.entities.traits.*;
|
||||
import io.anuke.mindustry.entities.type.*;
|
||||
import io.anuke.mindustry.entities.type.TimedEntity;
|
||||
import io.anuke.mindustry.game.EventType.*;
|
||||
import io.anuke.mindustry.game.*;
|
||||
import io.anuke.mindustry.gen.*;
|
||||
import io.anuke.mindustry.net.*;
|
||||
import io.anuke.mindustry.world.*;
|
||||
|
||||
import java.io.*;
|
||||
@@ -40,7 +41,7 @@ public class Fire extends TimedEntity implements SaveTrait, SyncTrait{
|
||||
|
||||
/** 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.
|
||||
if(net.client() || tile == null) return; //not clientside.
|
||||
|
||||
Fire fire = map.get(tile.pos());
|
||||
|
||||
@@ -70,7 +71,11 @@ public class Fire extends TimedEntity implements SaveTrait, SyncTrait{
|
||||
*/
|
||||
public static void extinguish(Tile tile, float intensity){
|
||||
if(tile != null && map.containsKey(tile.pos())){
|
||||
map.get(tile.pos()).time += intensity * Time.delta();
|
||||
Fire fire = map.get(tile.pos());
|
||||
fire.time += intensity * Time.delta();
|
||||
if(fire.time >= fire.lifetime()){
|
||||
Events.fire(Trigger.fireExtinguish);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,7 +111,7 @@ public class Fire extends TimedEntity implements SaveTrait, SyncTrait{
|
||||
time = Mathf.clamp(time + Time.delta(), 0, lifetime());
|
||||
map.put(tile.pos(), this);
|
||||
|
||||
if(Net.client()){
|
||||
if(net.client()){
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ import io.anuke.mindustry.Vars;
|
||||
import io.anuke.mindustry.entities.Effects;
|
||||
import io.anuke.mindustry.entities.Effects.Effect;
|
||||
import io.anuke.mindustry.entities.Effects.EffectRenderer;
|
||||
import io.anuke.mindustry.entities.impl.EffectEntity;
|
||||
import io.anuke.mindustry.entities.type.EffectEntity;
|
||||
import io.anuke.mindustry.world.Tile;
|
||||
|
||||
/**
|
||||
|
||||
@@ -10,14 +10,14 @@ import io.anuke.arc.math.geom.Vector2;
|
||||
import io.anuke.arc.util.Time;
|
||||
import io.anuke.arc.util.pooling.Pools;
|
||||
import io.anuke.mindustry.entities.EntityGroup;
|
||||
import io.anuke.mindustry.entities.impl.TimedEntity;
|
||||
import io.anuke.mindustry.entities.type.TimedEntity;
|
||||
import io.anuke.mindustry.entities.traits.DrawTrait;
|
||||
import io.anuke.mindustry.entities.type.Unit;
|
||||
import io.anuke.mindustry.graphics.Pal;
|
||||
import io.anuke.mindustry.type.Item;
|
||||
import io.anuke.mindustry.world.Tile;
|
||||
|
||||
import static io.anuke.mindustry.Vars.effectGroup;
|
||||
import static io.anuke.mindustry.Vars.*;
|
||||
|
||||
public class ItemTransfer extends TimedEntity implements DrawTrait{
|
||||
private Vector2 from = new Vector2();
|
||||
|
||||
@@ -12,8 +12,8 @@ import io.anuke.arc.util.pooling.Pools;
|
||||
import io.anuke.mindustry.content.Bullets;
|
||||
import io.anuke.mindustry.entities.EntityGroup;
|
||||
import io.anuke.mindustry.entities.Units;
|
||||
import io.anuke.mindustry.entities.bullet.Bullet;
|
||||
import io.anuke.mindustry.entities.impl.TimedEntity;
|
||||
import io.anuke.mindustry.entities.type.Bullet;
|
||||
import io.anuke.mindustry.entities.type.TimedEntity;
|
||||
import io.anuke.mindustry.entities.traits.DrawTrait;
|
||||
import io.anuke.mindustry.entities.traits.TimeTrait;
|
||||
import io.anuke.mindustry.entities.type.Unit;
|
||||
@@ -47,7 +47,7 @@ public class Lightning extends TimedEntity implements DrawTrait, TimeTrait{
|
||||
}
|
||||
|
||||
/** Do not invoke! */
|
||||
@Remote(called = Loc.server)
|
||||
@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);
|
||||
@@ -110,7 +110,7 @@ public class Lightning extends TimedEntity implements DrawTrait, TimeTrait{
|
||||
@Override
|
||||
public void draw(){
|
||||
Lines.stroke(3f * fout());
|
||||
Draw.color(color, Color.WHITE, fin());
|
||||
Draw.color(color, Color.white, fin());
|
||||
Lines.beginLine();
|
||||
|
||||
Lines.linePoint(x, y);
|
||||
|
||||
@@ -14,11 +14,10 @@ import io.anuke.arc.util.pooling.Pool.Poolable;
|
||||
import io.anuke.arc.util.pooling.Pools;
|
||||
import io.anuke.mindustry.content.*;
|
||||
import io.anuke.mindustry.entities.*;
|
||||
import io.anuke.mindustry.entities.impl.SolidEntity;
|
||||
import io.anuke.mindustry.entities.type.SolidEntity;
|
||||
import io.anuke.mindustry.entities.traits.*;
|
||||
import io.anuke.mindustry.game.TypeID;
|
||||
import io.anuke.mindustry.gen.Call;
|
||||
import io.anuke.mindustry.net.Net;
|
||||
import io.anuke.mindustry.type.Liquid;
|
||||
import io.anuke.mindustry.world.Tile;
|
||||
|
||||
@@ -83,7 +82,7 @@ public class Puddle extends SolidEntity implements SaveTrait, Poolable, DrawTrai
|
||||
|
||||
Puddle p = map.get(tile.pos());
|
||||
if(p == null){
|
||||
if(Net.client()) return; //not clientside.
|
||||
if(net.client()) return; //not clientside.
|
||||
|
||||
Puddle puddle = Pools.obtain(Puddle.class, Puddle::new);
|
||||
puddle.tile = tile;
|
||||
@@ -168,7 +167,7 @@ public class Puddle extends SolidEntity implements SaveTrait, Poolable, DrawTrai
|
||||
public void update(){
|
||||
|
||||
//no updating happens clientside
|
||||
if(Net.client()){
|
||||
if(net.client()){
|
||||
amount = Mathf.lerpDelta(amount, targetAmount, 0.15f);
|
||||
}else{
|
||||
//update code
|
||||
|
||||
@@ -88,7 +88,7 @@ public interface MinerTrait extends Entity{
|
||||
float ex = tile.worldx() + Mathf.sin(Time.time() + 48, swingScl, swingMag);
|
||||
float ey = tile.worldy() + Mathf.sin(Time.time() + 48, swingScl + 2f, swingMag);
|
||||
|
||||
Draw.color(Color.LIGHT_GRAY, Color.WHITE, 1f - flashScl + Mathf.absin(Time.time(), 0.5f, flashScl));
|
||||
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);
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package io.anuke.mindustry.entities.traits;
|
||||
|
||||
import io.anuke.arc.math.geom.Position;
|
||||
import io.anuke.mindustry.entities.type.Player;
|
||||
import io.anuke.mindustry.entities.type.*;
|
||||
import io.anuke.mindustry.world.Tile;
|
||||
|
||||
public interface SpawnerTrait extends TargetTrait, Position{
|
||||
@@ -9,6 +9,8 @@ public interface SpawnerTrait extends TargetTrait, Position{
|
||||
|
||||
void updateSpawning(Player unit);
|
||||
|
||||
boolean hasUnit(Unit unit);
|
||||
|
||||
@Override
|
||||
default boolean isValid(){
|
||||
return getTile().entity instanceof SpawnerTrait;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package io.anuke.mindustry.entities.impl;
|
||||
package io.anuke.mindustry.entities.type;
|
||||
|
||||
import io.anuke.mindustry.entities.EntityGroup;
|
||||
import io.anuke.mindustry.entities.traits.Entity;
|
||||
@@ -11,11 +11,13 @@ import io.anuke.mindustry.content.*;
|
||||
import io.anuke.mindustry.entities.*;
|
||||
import io.anuke.mindustry.entities.traits.*;
|
||||
import io.anuke.mindustry.entities.units.*;
|
||||
import io.anuke.mindustry.game.EventType.*;
|
||||
import io.anuke.mindustry.game.*;
|
||||
import io.anuke.mindustry.gen.*;
|
||||
import io.anuke.mindustry.net.Net;
|
||||
import io.anuke.mindustry.type.*;
|
||||
import io.anuke.mindustry.world.*;
|
||||
import io.anuke.mindustry.world.blocks.*;
|
||||
import io.anuke.mindustry.world.blocks.defense.DeflectorWall.*;
|
||||
import io.anuke.mindustry.world.blocks.units.CommandCenter.*;
|
||||
import io.anuke.mindustry.world.blocks.units.UnitFactory.*;
|
||||
import io.anuke.mindustry.world.meta.*;
|
||||
@@ -48,7 +50,7 @@ public abstract class BaseUnit extends Unit implements ShooterTrait{
|
||||
public static void onUnitDeath(BaseUnit unit){
|
||||
if(unit == null) return;
|
||||
|
||||
if(Net.server() || !Net.active()){
|
||||
if(net.server() || !net.active()){
|
||||
UnitDrops.dropItems(unit);
|
||||
}
|
||||
|
||||
@@ -56,7 +58,7 @@ public abstract class BaseUnit extends Unit implements ShooterTrait{
|
||||
unit.type.deathSound.at(unit);
|
||||
|
||||
//visual only.
|
||||
if(Net.client()){
|
||||
if(net.client()){
|
||||
Tile tile = world.tile(unit.spawner);
|
||||
if(tile != null){
|
||||
tile.block().unitRemoved(tile, unit);
|
||||
@@ -79,6 +81,17 @@ public abstract class BaseUnit extends Unit implements ShooterTrait{
|
||||
return type.typeID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onHit(SolidTrait entity){
|
||||
if(entity instanceof Bullet && ((Bullet)entity).getOwner() instanceof DeflectorEntity && player != null && getTeam() != player.getTeam()){
|
||||
Core.app.post(() -> {
|
||||
if(isDead()){
|
||||
Events.fire(Trigger.phaseDeflectHit);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public @Nullable Tile getSpawner(){
|
||||
return world.tile(spawner);
|
||||
}
|
||||
@@ -87,7 +100,7 @@ public abstract class BaseUnit extends Unit implements ShooterTrait{
|
||||
return indexer.getAllied(team, BlockFlag.comandCenter).size != 0 && indexer.getAllied(team, BlockFlag.comandCenter).first().entity instanceof CommandCenterEntity;
|
||||
}
|
||||
|
||||
public UnitCommand getCommand(){
|
||||
public @Nullable UnitCommand getCommand(){
|
||||
if(isCommanded()){
|
||||
return indexer.getAllied(team, BlockFlag.comandCenter).first().<CommandCenterEntity>entity().command;
|
||||
}
|
||||
@@ -161,8 +174,15 @@ public abstract class BaseUnit extends Unit implements ShooterTrait{
|
||||
}
|
||||
}
|
||||
|
||||
public TileEntity getClosestEnemyCore(){
|
||||
public Tile getClosest(BlockFlag flag){
|
||||
return Geometry.findClosest(x, y, indexer.getAllied(team, flag));
|
||||
}
|
||||
|
||||
public Tile getClosestSpawner(){
|
||||
return Geometry.findClosest(x, y, Vars.spawner.getGroundSpawns());
|
||||
}
|
||||
|
||||
public TileEntity getClosestEnemyCore(){
|
||||
for(Team enemy : Vars.state.teams.enemiesOf(team)){
|
||||
Tile tile = Geometry.findClosest(x, y, Vars.state.teams.get(enemy).cores);
|
||||
if(tile != null){
|
||||
@@ -255,13 +275,13 @@ public abstract class BaseUnit extends Unit implements ShooterTrait{
|
||||
|
||||
hitTime -= Time.delta();
|
||||
|
||||
if(Net.client()){
|
||||
if(net.client()){
|
||||
interpolate();
|
||||
status.update(this);
|
||||
return;
|
||||
}
|
||||
|
||||
if(!isFlying() && (world.tileWorld(x, y) != null && world.tileWorld(x, y).solid())){
|
||||
if(!isFlying() && (world.tileWorld(x, y) != null && !(world.tileWorld(x, y).block() instanceof BuildBlock) && world.tileWorld(x, y).solid())){
|
||||
kill();
|
||||
}
|
||||
|
||||
@@ -297,7 +317,7 @@ public abstract class BaseUnit extends Unit implements ShooterTrait{
|
||||
public void removed(){
|
||||
super.removed();
|
||||
Tile tile = world.tile(spawner);
|
||||
if(tile != null && !Net.client()){
|
||||
if(tile != null && !net.client()){
|
||||
tile.block().unitRemoved(tile, this);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,23 +1,19 @@
|
||||
package io.anuke.mindustry.entities.bullet;
|
||||
package io.anuke.mindustry.entities.type;
|
||||
|
||||
import io.anuke.annotations.Annotations.Loc;
|
||||
import io.anuke.annotations.Annotations.Remote;
|
||||
import io.anuke.arc.math.Mathf;
|
||||
import io.anuke.arc.math.geom.Rectangle;
|
||||
import io.anuke.arc.math.geom.Vector2;
|
||||
import io.anuke.annotations.Annotations.*;
|
||||
import io.anuke.arc.math.*;
|
||||
import io.anuke.arc.math.geom.*;
|
||||
import io.anuke.arc.util.*;
|
||||
import io.anuke.arc.util.pooling.Pool.Poolable;
|
||||
import io.anuke.arc.util.pooling.Pools;
|
||||
import io.anuke.mindustry.entities.EntityGroup;
|
||||
import io.anuke.mindustry.entities.effect.Lightning;
|
||||
import io.anuke.mindustry.entities.impl.SolidEntity;
|
||||
import io.anuke.arc.util.pooling.Pool.*;
|
||||
import io.anuke.arc.util.pooling.*;
|
||||
import io.anuke.mindustry.entities.*;
|
||||
import io.anuke.mindustry.entities.bullet.*;
|
||||
import io.anuke.mindustry.entities.effect.*;
|
||||
import io.anuke.mindustry.entities.traits.*;
|
||||
import io.anuke.mindustry.entities.type.Unit;
|
||||
import io.anuke.mindustry.game.Team;
|
||||
import io.anuke.mindustry.world.Tile;
|
||||
import io.anuke.mindustry.game.*;
|
||||
import io.anuke.mindustry.world.*;
|
||||
|
||||
import static io.anuke.mindustry.Vars.bulletGroup;
|
||||
import static io.anuke.mindustry.Vars.world;
|
||||
import static io.anuke.mindustry.Vars.*;
|
||||
|
||||
public class Bullet extends SolidEntity implements DamageTrait, ScaleTrait, Poolable, DrawTrait, VelocityTrait, TimeTrait, TeamTrait, AbsorbTrait{
|
||||
public Interval timer = new Interval(3);
|
||||
@@ -25,7 +21,7 @@ public class Bullet extends SolidEntity implements DamageTrait, ScaleTrait, Pool
|
||||
private float lifeScl;
|
||||
private Team team;
|
||||
private Object data;
|
||||
private boolean supressCollision, supressOnce, initialized;
|
||||
private boolean supressCollision, supressOnce, initialized, deflected;
|
||||
|
||||
protected BulletType type;
|
||||
protected Entity owner;
|
||||
@@ -100,9 +96,14 @@ public class Bullet extends SolidEntity implements DamageTrait, ScaleTrait, Pool
|
||||
return type.collidesTiles;
|
||||
}
|
||||
|
||||
public void supress(){
|
||||
public void deflect(){
|
||||
supressCollision = true;
|
||||
supressOnce = true;
|
||||
deflected = true;
|
||||
}
|
||||
|
||||
public boolean isDeflected(){
|
||||
return deflected;
|
||||
}
|
||||
|
||||
public BulletType getBulletType(){
|
||||
@@ -239,6 +240,7 @@ public class Bullet extends SolidEntity implements DamageTrait, ScaleTrait, Pool
|
||||
data = null;
|
||||
supressCollision = false;
|
||||
supressOnce = false;
|
||||
deflected = false;
|
||||
initialized = false;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package io.anuke.mindustry.entities.impl;
|
||||
package io.anuke.mindustry.entities.type;
|
||||
|
||||
|
||||
import io.anuke.mindustry.entities.traits.*;
|
||||
@@ -1,4 +1,4 @@
|
||||
package io.anuke.mindustry.entities.impl;
|
||||
package io.anuke.mindustry.entities.type;
|
||||
|
||||
import io.anuke.arc.graphics.Color;
|
||||
import io.anuke.arc.util.pooling.Pool.Poolable;
|
||||
@@ -13,7 +13,7 @@ import static io.anuke.mindustry.Vars.effectGroup;
|
||||
|
||||
public class EffectEntity extends TimedEntity implements Poolable, DrawTrait{
|
||||
public Effect effect;
|
||||
public Color color = new Color(Color.WHITE);
|
||||
public Color color = new Color(Color.white);
|
||||
public Object data;
|
||||
public float rotation = 0f;
|
||||
|
||||
@@ -63,7 +63,7 @@ public class EffectEntity extends TimedEntity implements Poolable, DrawTrait{
|
||||
@Override
|
||||
public void reset(){
|
||||
effect = null;
|
||||
color.set(Color.WHITE);
|
||||
color.set(Color.white);
|
||||
rotation = time = poffsetx = poffsety = 0f;
|
||||
parent = null;
|
||||
data = null;
|
||||
@@ -5,11 +5,11 @@ import io.anuke.arc.graphics.g2d.*;
|
||||
import io.anuke.arc.math.*;
|
||||
import io.anuke.arc.math.geom.*;
|
||||
import io.anuke.arc.util.*;
|
||||
import io.anuke.mindustry.*;
|
||||
import io.anuke.mindustry.entities.*;
|
||||
import io.anuke.mindustry.entities.bullet.*;
|
||||
import io.anuke.mindustry.entities.units.*;
|
||||
import io.anuke.mindustry.graphics.*;
|
||||
import io.anuke.mindustry.net.*;
|
||||
import io.anuke.mindustry.world.*;
|
||||
import io.anuke.mindustry.world.meta.*;
|
||||
|
||||
@@ -36,13 +36,15 @@ public abstract class FlyingUnit extends BaseUnit{
|
||||
|
||||
if(target == null) targetClosestEnemyFlag(BlockFlag.producer);
|
||||
if(target == null) targetClosestEnemyFlag(BlockFlag.turret);
|
||||
|
||||
if(target == null){
|
||||
setState(patrol);
|
||||
}
|
||||
}
|
||||
|
||||
if(target != null){
|
||||
if(target == null){
|
||||
target = getSpawner();
|
||||
}
|
||||
|
||||
if(target == getSpawner() && getSpawner() != null){
|
||||
circle(80f + Mathf.randomSeed(id) * 120);
|
||||
}else if(target != null){
|
||||
attack(type.attackLength);
|
||||
|
||||
if((Angles.near(angleTo(target), rotation, type.shootCone) || getWeapon().ignoreRotation) //bombers and such don't care about rotation
|
||||
@@ -65,26 +67,28 @@ public abstract class FlyingUnit extends BaseUnit{
|
||||
getWeapon().update(FlyingUnit.this, to.x, to.y);
|
||||
}
|
||||
}
|
||||
}else{
|
||||
target = getClosestSpawner();
|
||||
moveTo(Vars.state.rules.dropZoneRadius + 120f);
|
||||
}
|
||||
}
|
||||
},
|
||||
patrol = new UnitState(){
|
||||
rally = new UnitState(){
|
||||
public void update(){
|
||||
if(retarget()){
|
||||
targetClosestAllyFlag(BlockFlag.rally);
|
||||
targetClosest();
|
||||
targetClosestEnemyFlag(BlockFlag.target);
|
||||
|
||||
if(target != null && !Units.invalidateTarget(target, team, x, y)){
|
||||
setState(attack);
|
||||
return;
|
||||
}
|
||||
|
||||
target = getSpawner();
|
||||
if(target == null) target = getClosestCore();
|
||||
if(target == null) target = getSpawner();
|
||||
}
|
||||
|
||||
if(target != null){
|
||||
circle(80f + Mathf.randomSeed(id) * 120);
|
||||
circle(65f + Mathf.randomSeed(id) * 100);
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -110,7 +114,7 @@ public abstract class FlyingUnit extends BaseUnit{
|
||||
public void onCommand(UnitCommand command){
|
||||
state.set(command == UnitCommand.retreat ? retreat :
|
||||
command == UnitCommand.attack ? attack :
|
||||
command == UnitCommand.patrol ? patrol :
|
||||
command == UnitCommand.rally ? rally :
|
||||
null);
|
||||
}
|
||||
|
||||
@@ -123,10 +127,10 @@ public abstract class FlyingUnit extends BaseUnit{
|
||||
public void update(){
|
||||
super.update();
|
||||
|
||||
if(!Net.client()){
|
||||
if(!net.client()){
|
||||
updateRotation();
|
||||
wobble();
|
||||
}
|
||||
wobble();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -136,7 +140,7 @@ public abstract class FlyingUnit extends BaseUnit{
|
||||
|
||||
@Override
|
||||
public void draw(){
|
||||
Draw.mixcol(Color.WHITE, hitTime / hitDuration);
|
||||
Draw.mixcol(Color.white, hitTime / hitDuration);
|
||||
Draw.rect(type.region, x, y, rotation - 90);
|
||||
|
||||
drawWeapons();
|
||||
@@ -153,7 +157,7 @@ public abstract class FlyingUnit extends BaseUnit{
|
||||
Fill.circle(x + Angles.trnsx(rotation + 180, type.engineOffset), y + Angles.trnsy(rotation + 180, type.engineOffset),
|
||||
type.engineSize + Mathf.absin(Time.time(), 2f, type.engineSize / 4f));
|
||||
|
||||
Draw.color(Color.WHITE);
|
||||
Draw.color(Color.white);
|
||||
Fill.circle(x + Angles.trnsx(rotation + 180, type.engineOffset - 1f), y + Angles.trnsy(rotation + 180, type.engineOffset - 1f),
|
||||
(type.engineSize + Mathf.absin(Time.time(), 2f, type.engineSize / 4f)) / 2f);
|
||||
Draw.color();
|
||||
@@ -176,7 +180,7 @@ public abstract class FlyingUnit extends BaseUnit{
|
||||
}
|
||||
|
||||
protected void wobble(){
|
||||
if(Net.client()) return;
|
||||
if(net.client()) return;
|
||||
|
||||
x += Mathf.sin(Time.time() + id * 999, 25f, 0.05f) * Time.delta();
|
||||
y += Mathf.cos(Time.time() + id * 999, 25f, 0.05f) * Time.delta();
|
||||
|
||||
@@ -6,6 +6,7 @@ import io.anuke.arc.math.*;
|
||||
import io.anuke.arc.math.geom.*;
|
||||
import io.anuke.arc.util.*;
|
||||
import io.anuke.mindustry.*;
|
||||
import io.anuke.mindustry.ai.Pathfinder.*;
|
||||
import io.anuke.mindustry.entities.*;
|
||||
import io.anuke.mindustry.entities.bullet.*;
|
||||
import io.anuke.mindustry.entities.units.*;
|
||||
@@ -13,6 +14,7 @@ import io.anuke.mindustry.game.*;
|
||||
import io.anuke.mindustry.type.*;
|
||||
import io.anuke.mindustry.world.*;
|
||||
import io.anuke.mindustry.world.blocks.*;
|
||||
import io.anuke.mindustry.world.meta.*;
|
||||
|
||||
import static io.anuke.mindustry.Vars.*;
|
||||
|
||||
@@ -34,31 +36,30 @@ public abstract class GroundUnit extends BaseUnit{
|
||||
TileEntity core = getClosestEnemyCore();
|
||||
|
||||
if(core == null){
|
||||
setState(patrol);
|
||||
return;
|
||||
}
|
||||
Tile closestSpawn = getClosestSpawner();
|
||||
if(closestSpawn == null || !withinDst(closestSpawn, Vars.state.rules.dropZoneRadius + 85f)){
|
||||
moveToCore(PathTarget.enemyCores);
|
||||
}
|
||||
}else{
|
||||
|
||||
float dst = dst(core);
|
||||
float dst = dst(core);
|
||||
|
||||
if(dst < getWeapon().bullet.range() / 1.1f){
|
||||
target = core;
|
||||
}
|
||||
if(dst < getWeapon().bullet.range() / 1.1f){
|
||||
target = core;
|
||||
}
|
||||
|
||||
if(dst > getWeapon().bullet.range() * 0.5f){
|
||||
moveToCore();
|
||||
if(dst > getWeapon().bullet.range() * 0.5f){
|
||||
moveToCore(PathTarget.enemyCores);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
patrol = new UnitState(){
|
||||
rally = new UnitState(){
|
||||
public void update(){
|
||||
TileEntity target = getClosestCore();
|
||||
Tile target = getClosest(BlockFlag.rally);
|
||||
|
||||
if(target != null){
|
||||
if(dst(target) > 400f){
|
||||
moveAwayFromCore();
|
||||
}else if(!(!Units.invalidateTarget(GroundUnit.this.target, GroundUnit.this) && dst(GroundUnit.this.target) < getWeapon().bullet.range())){
|
||||
patrol();
|
||||
}
|
||||
if(target != null && dst(target) > 80f){
|
||||
moveToCore(PathTarget.rallyPoints);
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -76,7 +77,7 @@ public abstract class GroundUnit extends BaseUnit{
|
||||
public void onCommand(UnitCommand command){
|
||||
state.set(command == UnitCommand.retreat ? retreat :
|
||||
command == UnitCommand.attack ? attack :
|
||||
command == UnitCommand.patrol ? patrol :
|
||||
command == UnitCommand.rally ? rally :
|
||||
null);
|
||||
}
|
||||
|
||||
@@ -125,14 +126,14 @@ public abstract class GroundUnit extends BaseUnit{
|
||||
|
||||
@Override
|
||||
public void draw(){
|
||||
Draw.mixcol(Color.WHITE, hitTime / hitDuration);
|
||||
Draw.mixcol(Color.white, hitTime / hitDuration);
|
||||
|
||||
float ft = Mathf.sin(walkTime * type.speed * 5f, 6f, 2f + type.hitsize / 15f);
|
||||
|
||||
Floor floor = getFloorOn();
|
||||
|
||||
if(floor.isLiquid){
|
||||
Draw.color(Color.WHITE, floor.color, 0.5f);
|
||||
Draw.color(Color.white, floor.color, 0.5f);
|
||||
}
|
||||
|
||||
for(int i : Mathf.signs){
|
||||
@@ -143,9 +144,9 @@ public abstract class GroundUnit extends BaseUnit{
|
||||
}
|
||||
|
||||
if(floor.isLiquid){
|
||||
Draw.color(Color.WHITE, floor.color, drownTime * 0.4f);
|
||||
Draw.color(Color.white, floor.color, drownTime * 0.4f);
|
||||
}else{
|
||||
Draw.color(Color.WHITE);
|
||||
Draw.color(Color.white);
|
||||
}
|
||||
|
||||
Draw.rect(type.baseRegion, x, y, baseRotation - 90);
|
||||
@@ -220,10 +221,10 @@ public abstract class GroundUnit extends BaseUnit{
|
||||
velocity.add(vec);
|
||||
}
|
||||
|
||||
protected void moveToCore(){
|
||||
protected void moveToCore(PathTarget path){
|
||||
Tile tile = world.tileWorld(x, y);
|
||||
if(tile == null) return;
|
||||
Tile targetTile = pathfinder.getTargetTile(team, tile);
|
||||
Tile targetTile = pathfinder.getTargetTile(tile, team, path);
|
||||
|
||||
if(tile == targetTile) return;
|
||||
|
||||
@@ -242,11 +243,18 @@ public abstract class GroundUnit extends BaseUnit{
|
||||
}
|
||||
}
|
||||
|
||||
if(enemy == null){
|
||||
for(Team team : Vars.state.teams.enemiesOf(team)){
|
||||
enemy = team;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(enemy == null) return;
|
||||
|
||||
Tile tile = world.tileWorld(x, y);
|
||||
if(tile == null) return;
|
||||
Tile targetTile = pathfinder.getTargetTile(enemy, tile);
|
||||
Tile targetTile = pathfinder.getTargetTile(tile, enemy, PathTarget.enemyCores);
|
||||
TileEntity core = getClosestCore();
|
||||
|
||||
if(tile == targetTile || core == null || dst(core) < 120f) return;
|
||||
|
||||
@@ -1,35 +1,32 @@
|
||||
package io.anuke.mindustry.entities.type;
|
||||
|
||||
import io.anuke.annotations.Annotations.Loc;
|
||||
import io.anuke.annotations.Annotations.Remote;
|
||||
import io.anuke.arc.Core;
|
||||
import io.anuke.arc.collection.Queue;
|
||||
import io.anuke.arc.graphics.Color;
|
||||
import io.anuke.annotations.Annotations.*;
|
||||
import io.anuke.arc.*;
|
||||
import io.anuke.arc.collection.*;
|
||||
import io.anuke.arc.graphics.*;
|
||||
import io.anuke.arc.graphics.g2d.*;
|
||||
import io.anuke.arc.math.Angles;
|
||||
import io.anuke.arc.math.Mathf;
|
||||
import io.anuke.arc.math.*;
|
||||
import io.anuke.arc.math.geom.*;
|
||||
import io.anuke.arc.scene.ui.*;
|
||||
import io.anuke.arc.scene.ui.layout.*;
|
||||
import io.anuke.arc.util.*;
|
||||
import io.anuke.arc.util.pooling.Pools;
|
||||
import io.anuke.mindustry.Vars;
|
||||
import io.anuke.arc.util.pooling.*;
|
||||
import io.anuke.mindustry.*;
|
||||
import io.anuke.mindustry.content.*;
|
||||
import io.anuke.mindustry.core.*;
|
||||
import io.anuke.mindustry.entities.*;
|
||||
import io.anuke.mindustry.entities.traits.*;
|
||||
import io.anuke.mindustry.game.*;
|
||||
import io.anuke.mindustry.gen.*;
|
||||
import io.anuke.mindustry.graphics.Pal;
|
||||
import io.anuke.mindustry.graphics.*;
|
||||
import io.anuke.mindustry.input.*;
|
||||
import io.anuke.mindustry.input.InputHandler.PlaceDraw;
|
||||
import io.anuke.mindustry.io.TypeIO;
|
||||
import io.anuke.mindustry.input.InputHandler.*;
|
||||
import io.anuke.mindustry.io.*;
|
||||
import io.anuke.mindustry.net.Administration.*;
|
||||
import io.anuke.mindustry.net.Net;
|
||||
import io.anuke.mindustry.net.NetConnection;
|
||||
import io.anuke.mindustry.net.*;
|
||||
import io.anuke.mindustry.type.*;
|
||||
import io.anuke.mindustry.world.Block;
|
||||
import io.anuke.mindustry.world.Tile;
|
||||
import io.anuke.mindustry.ui.*;
|
||||
import io.anuke.mindustry.world.*;
|
||||
import io.anuke.mindustry.world.blocks.*;
|
||||
|
||||
import java.io.*;
|
||||
@@ -51,7 +48,7 @@ public class Player extends Unit implements BuilderMinerTrait, ShooterTrait{
|
||||
public float baseRotation;
|
||||
public float pointerX, pointerY;
|
||||
public String name = "name";
|
||||
public String uuid, usid;
|
||||
public @Nullable String uuid, usid;
|
||||
public boolean isAdmin, isTransferring, isShooting, isBoosting, isMobile, isTyping;
|
||||
public float boostHeat, shootHeat, destructTime;
|
||||
public boolean achievedFlight;
|
||||
@@ -60,13 +57,13 @@ public class Player extends Unit implements BuilderMinerTrait, ShooterTrait{
|
||||
public SpawnerTrait spawner, lastSpawner;
|
||||
public int respawns;
|
||||
|
||||
public NetConnection con;
|
||||
public @Nullable NetConnection con;
|
||||
public boolean isLocal = false;
|
||||
public Interval timer = new Interval(6);
|
||||
public TargetTrait target;
|
||||
public TargetTrait moveTarget;
|
||||
|
||||
public String lastText;
|
||||
public @Nullable String lastText;
|
||||
public float textFadeTime;
|
||||
|
||||
private float walktime, itemtime;
|
||||
@@ -230,7 +227,7 @@ public class Player extends Unit implements BuilderMinerTrait, ShooterTrait{
|
||||
@Override
|
||||
public void damage(float amount){
|
||||
hitTime = hitDuration;
|
||||
if(!Net.client()){
|
||||
if(!net.client()){
|
||||
health -= calculateDamage(amount);
|
||||
}
|
||||
|
||||
@@ -299,11 +296,11 @@ public class Player extends Unit implements BuilderMinerTrait, ShooterTrait{
|
||||
Floor floor = getFloorOn();
|
||||
|
||||
Draw.color();
|
||||
Draw.mixcol(Color.WHITE, hitTime / hitDuration);
|
||||
Draw.mixcol(Color.white, hitTime / hitDuration);
|
||||
|
||||
if(!mech.flying){
|
||||
if(floor.isLiquid){
|
||||
Draw.color(Color.WHITE, floor.color, 0.5f);
|
||||
Draw.color(Color.white, floor.color, 0.5f);
|
||||
}
|
||||
|
||||
float boostTrnsY = -boostHeat * 3f;
|
||||
@@ -323,9 +320,9 @@ public class Player extends Unit implements BuilderMinerTrait, ShooterTrait{
|
||||
}
|
||||
|
||||
if(floor.isLiquid){
|
||||
Draw.color(Color.WHITE, floor.color, drownTime);
|
||||
Draw.color(Color.white, floor.color, drownTime);
|
||||
}else{
|
||||
Draw.color(Color.WHITE);
|
||||
Draw.color(Color.white);
|
||||
}
|
||||
|
||||
Draw.rect(mech.region, x, y, rotation - 90);
|
||||
@@ -348,7 +345,7 @@ public class Player extends Unit implements BuilderMinerTrait, ShooterTrait{
|
||||
|
||||
@Override
|
||||
public void drawStats(){
|
||||
Draw.color(Color.BLACK, team.color, healthf() + Mathf.absin(Time.time(), healthf() * 5f, 1f - healthf()));
|
||||
Draw.color(Color.black, team.color, healthf() + Mathf.absin(Time.time(), healthf() * 5f, 1f - healthf()));
|
||||
Draw.rect(getPowerCellRegion(), x + Angles.trnsx(rotation, mech.cellTrnsY, 0f), y + Angles.trnsy(rotation, mech.cellTrnsY, 0f), rotation - 90);
|
||||
Draw.reset();
|
||||
drawBackItems(itemtime, isLocal);
|
||||
@@ -370,21 +367,21 @@ public class Player extends Unit implements BuilderMinerTrait, ShooterTrait{
|
||||
Fill.circle(x + Angles.trnsx(rotation + 180, mech.engineOffset), y + Angles.trnsy(rotation + 180, mech.engineOffset),
|
||||
size + Mathf.absin(Time.time(), 2f, size / 4f));
|
||||
|
||||
Draw.color(Color.WHITE);
|
||||
Draw.color(Color.white);
|
||||
Fill.circle(x + Angles.trnsx(rotation + 180, mech.engineOffset - 1f), y + Angles.trnsy(rotation + 180, mech.engineOffset - 1f),
|
||||
(size + Mathf.absin(Time.time(), 2f, size / 4f)) / 2f);
|
||||
Draw.color();
|
||||
}
|
||||
|
||||
public void drawName(){
|
||||
BitmapFont font = Core.scene.skin.getFont("default");
|
||||
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 / UnitScl.dp.scl(1f));
|
||||
font.getData().setScale(0.25f / Scl.scl(1f));
|
||||
layout.setText(font, name);
|
||||
Draw.color(0f, 0f, 0f, 0.3f);
|
||||
Fill.rect(x, y + nameHeight - layout.height / 2, layout.width + 2, layout.height + 3);
|
||||
@@ -396,9 +393,9 @@ public class Player extends Unit implements BuilderMinerTrait, ShooterTrait{
|
||||
if(isAdmin){
|
||||
float s = 3f;
|
||||
Draw.color(color.r * 0.5f, color.g * 0.5f, color.b * 0.5f, 1f);
|
||||
Draw.rect(Core.atlas.find("icon-admin-small"), x + layout.width / 2f + 2 + 1, y + nameHeight - 1.5f, s, s);
|
||||
Draw.rect(Core.atlas.find("icon-admin-badge"), x + layout.width / 2f + 2 + 1, y + nameHeight - 1.5f, s, s);
|
||||
Draw.color(color);
|
||||
Draw.rect(Core.atlas.find("icon-admin-small"), x + layout.width / 2f + 2 + 1, y + nameHeight - 1f, s, s);
|
||||
Draw.rect(Core.atlas.find("icon-admin-badge"), x + layout.width / 2f + 2 + 1, y + nameHeight - 1f, s, s);
|
||||
}
|
||||
|
||||
if(Core.settings.getBool("playerchat") && ((textFadeTime > 0 && lastText != null) || isTyping)){
|
||||
@@ -407,7 +404,7 @@ public class Player extends Unit implements BuilderMinerTrait, ShooterTrait{
|
||||
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);
|
||||
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(x, y + textHeight + layout.height - layout.height/2f, layout.width + 2, layout.height + 3);
|
||||
@@ -417,7 +414,7 @@ public class Player extends Unit implements BuilderMinerTrait, ShooterTrait{
|
||||
Draw.reset();
|
||||
Pools.free(layout);
|
||||
font.getData().setScale(1f);
|
||||
font.setColor(Color.WHITE);
|
||||
font.setColor(Color.white);
|
||||
font.setUseIntegerPositions(ints);
|
||||
}
|
||||
|
||||
@@ -531,7 +528,7 @@ public class Player extends Unit implements BuilderMinerTrait, ShooterTrait{
|
||||
spawner = null;
|
||||
}
|
||||
|
||||
if(isLocal || Net.server()){
|
||||
if(isLocal || net.server()){
|
||||
avoidOthers();
|
||||
}
|
||||
|
||||
@@ -562,7 +559,7 @@ public class Player extends Unit implements BuilderMinerTrait, ShooterTrait{
|
||||
status.update(this); //status effect updating also happens with non locals for effect purposes
|
||||
updateVelocityStatus(); //velocity too, for visual purposes
|
||||
|
||||
if(Net.server()){
|
||||
if(net.server()){
|
||||
updateShooting(); //server simulates player shooting
|
||||
}
|
||||
return;
|
||||
@@ -571,7 +568,7 @@ public class Player extends Unit implements BuilderMinerTrait, ShooterTrait{
|
||||
data.unlockContent(mech);
|
||||
}
|
||||
|
||||
if(mobile && !Core.settings.getBool("keyboard")){
|
||||
if(control.input instanceof MobileInput){
|
||||
updateTouch();
|
||||
}else{
|
||||
updateKeyboard();
|
||||
@@ -727,50 +724,41 @@ public class Player extends Unit implements BuilderMinerTrait, ShooterTrait{
|
||||
//update shooting if not building, not mining and there's ammo left
|
||||
if(!isBuilding() && getMineTile() == null){
|
||||
|
||||
//autofire: mobile only!
|
||||
if(mobile){
|
||||
if(target == null){
|
||||
isShooting = false;
|
||||
if(Core.settings.getBool("autotarget")){
|
||||
target = Units.closestTarget(team, x, y, getWeapon().bullet.range(), u -> u.getTeam() != Team.derelict, u -> u.getTeam() != Team.derelict);
|
||||
//autofire
|
||||
if(target == null){
|
||||
isShooting = false;
|
||||
if(Core.settings.getBool("autotarget")){
|
||||
target = Units.closestTarget(team, x, y, getWeapon().bullet.range(), u -> u.getTeam() != Team.derelict, u -> u.getTeam() != Team.derelict);
|
||||
|
||||
if(mech.canHeal && target == null){
|
||||
target = Geometry.findClosest(x, y, indexer.getDamaged(Team.sharded));
|
||||
if(target != null && dst(target) > getWeapon().bullet.range()){
|
||||
target = null;
|
||||
}else if(target != null){
|
||||
target = ((Tile)target).entity;
|
||||
}
|
||||
}
|
||||
|
||||
if(target != null){
|
||||
setMineTile(null);
|
||||
if(mech.canHeal && target == null){
|
||||
target = Geometry.findClosest(x, y, indexer.getDamaged(Team.sharded));
|
||||
if(target != null && dst(target) > getWeapon().bullet.range()){
|
||||
target = null;
|
||||
}else if(target != null){
|
||||
target = ((Tile)target).entity;
|
||||
}
|
||||
}
|
||||
}else if(target.isValid() || (target instanceof TileEntity && ((TileEntity)target).damaged() && target.getTeam() == team &&
|
||||
mech.canHeal && dst(target) < getWeapon().bullet.range())){
|
||||
//rotate toward and shoot the target
|
||||
if(mech.turnCursor){
|
||||
rotation = Mathf.slerpDelta(rotation, angleTo(target), 0.2f);
|
||||
|
||||
if(target != null){
|
||||
setMineTile(null);
|
||||
}
|
||||
|
||||
Vector2 intercept = Predict.intercept(this, target, getWeapon().bullet.speed);
|
||||
|
||||
pointerX = intercept.x;
|
||||
pointerY = intercept.y;
|
||||
|
||||
updateShooting();
|
||||
isShooting = true;
|
||||
}
|
||||
}else if(target.isValid() || (target instanceof TileEntity && ((TileEntity)target).damaged() && target.getTeam() == team &&
|
||||
mech.canHeal && dst(target) < getWeapon().bullet.range())){
|
||||
//rotate toward and shoot the target
|
||||
if(mech.turnCursor){
|
||||
rotation = Mathf.slerpDelta(rotation, angleTo(target), 0.2f);
|
||||
}
|
||||
|
||||
}else if(isShooting()){
|
||||
Vector2 vec = Core.input.mouseWorld(control.input.getMouseX(),
|
||||
control.input.getMouseY());
|
||||
pointerX = vec.x;
|
||||
pointerY = vec.y;
|
||||
Vector2 intercept = Predict.intercept(this, target, getWeapon().bullet.speed);
|
||||
|
||||
pointerX = intercept.x;
|
||||
pointerY = intercept.y;
|
||||
|
||||
updateShooting();
|
||||
isShooting = true;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -785,7 +773,7 @@ public class Player extends Unit implements BuilderMinerTrait, ShooterTrait{
|
||||
Vars.ui.chatfrag.addMessage(text, null);
|
||||
}
|
||||
}else{
|
||||
Call.sendMessage(con.id, text, null, null);
|
||||
Call.sendMessage(con, text, null, null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -799,7 +787,7 @@ public class Player extends Unit implements BuilderMinerTrait, ShooterTrait{
|
||||
Vars.ui.chatfrag.addMessage(text, fromName);
|
||||
}
|
||||
}else{
|
||||
Call.sendMessage(con.id, text, fromName, from);
|
||||
Call.sendMessage(con, text, fromName, from);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -848,7 +836,7 @@ public class Player extends Unit implements BuilderMinerTrait, ShooterTrait{
|
||||
}else if(spawner != null && spawner.isValid()){
|
||||
spawner.updateSpawning(this);
|
||||
}else if(!netServer.isWaitingForPlayers()){
|
||||
if(!Net.client()){
|
||||
if(!net.client()){
|
||||
if(lastSpawner != null && lastSpawner.isValid()){
|
||||
this.spawner = lastSpawner;
|
||||
}else if(getClosestCore() != null){
|
||||
@@ -920,7 +908,7 @@ public class Player extends Unit implements BuilderMinerTrait, ShooterTrait{
|
||||
buffer.writeInt(Color.rgba8888(color));
|
||||
buffer.writeByte(mech.id);
|
||||
buffer.writeInt(mining == null ? noSpawner : mining.pos());
|
||||
buffer.writeInt(spawner == null ? noSpawner : spawner.getTile().pos());
|
||||
buffer.writeInt(spawner == null || !spawner.hasUnit(this) ? noSpawner : spawner.getTile().pos());
|
||||
buffer.writeShort((short)(baseRotation * 2));
|
||||
|
||||
writeBuilding(buffer);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package io.anuke.mindustry.entities.impl;
|
||||
package io.anuke.mindustry.entities.type;
|
||||
|
||||
import io.anuke.arc.math.geom.Vector2;
|
||||
import io.anuke.mindustry.entities.traits.SolidTrait;
|
||||
@@ -8,8 +8,6 @@ import io.anuke.arc.math.geom.Point2;
|
||||
import io.anuke.arc.math.geom.Vector2;
|
||||
import io.anuke.arc.util.*;
|
||||
import io.anuke.mindustry.entities.EntityGroup;
|
||||
import io.anuke.mindustry.entities.bullet.Bullet;
|
||||
import io.anuke.mindustry.entities.impl.BaseEntity;
|
||||
import io.anuke.mindustry.entities.traits.HealthTrait;
|
||||
import io.anuke.mindustry.entities.traits.TargetTrait;
|
||||
import io.anuke.mindustry.game.*;
|
||||
@@ -207,14 +205,12 @@ public class TileEntity extends BaseEntity implements TargetTrait, HealthTrait{
|
||||
if(other == null) continue;
|
||||
if(other.entity == null || !(other.interactable(tile.getTeam()))) continue;
|
||||
|
||||
other.block().onProximityUpdate(other);
|
||||
|
||||
tmpTiles.add(other);
|
||||
|
||||
//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
|
||||
@@ -224,6 +220,10 @@ public class TileEntity extends BaseEntity implements TargetTrait, HealthTrait{
|
||||
|
||||
block.onProximityAdded(tile);
|
||||
block.onProximityUpdate(tile);
|
||||
|
||||
for(Tile other : tmpTiles){
|
||||
other.block().onProximityUpdate(other);
|
||||
}
|
||||
}
|
||||
|
||||
public Array<Tile> proximity(){
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package io.anuke.mindustry.entities.impl;
|
||||
package io.anuke.mindustry.entities.type;
|
||||
|
||||
import io.anuke.arc.util.pooling.Pool.Poolable;
|
||||
import io.anuke.mindustry.entities.traits.ScaleTrait;
|
||||
@@ -2,6 +2,7 @@ package io.anuke.mindustry.entities.type;
|
||||
|
||||
import io.anuke.annotations.Annotations.*;
|
||||
import io.anuke.arc.*;
|
||||
import io.anuke.arc.collection.*;
|
||||
import io.anuke.arc.graphics.*;
|
||||
import io.anuke.arc.graphics.g2d.*;
|
||||
import io.anuke.arc.math.*;
|
||||
@@ -11,7 +12,6 @@ import io.anuke.arc.util.*;
|
||||
import io.anuke.mindustry.content.*;
|
||||
import io.anuke.mindustry.entities.*;
|
||||
import io.anuke.mindustry.entities.effect.*;
|
||||
import io.anuke.mindustry.entities.impl.*;
|
||||
import io.anuke.mindustry.entities.traits.*;
|
||||
import io.anuke.mindustry.entities.units.*;
|
||||
import io.anuke.mindustry.game.EventType.*;
|
||||
@@ -19,9 +19,9 @@ import io.anuke.mindustry.game.*;
|
||||
import io.anuke.mindustry.game.Teams.*;
|
||||
import io.anuke.mindustry.gen.*;
|
||||
import io.anuke.mindustry.graphics.*;
|
||||
import io.anuke.mindustry.net.Net;
|
||||
import io.anuke.mindustry.net.*;
|
||||
import io.anuke.mindustry.type.*;
|
||||
import io.anuke.mindustry.ui.*;
|
||||
import io.anuke.mindustry.world.*;
|
||||
import io.anuke.mindustry.world.blocks.*;
|
||||
|
||||
@@ -78,7 +78,7 @@ public abstract class Unit extends DestructibleEntity implements SaveTrait, Targ
|
||||
|
||||
@Override
|
||||
public void damage(float amount){
|
||||
if(!Net.client()){
|
||||
if(!net.client()){
|
||||
super.damage(calculateDamage(amount));
|
||||
}
|
||||
hitTime = hitDuration;
|
||||
@@ -110,6 +110,10 @@ public abstract class Unit extends DestructibleEntity implements SaveTrait, Targ
|
||||
drownTime = 0f;
|
||||
status.clear();
|
||||
Events.fire(new UnitDestroyEvent(this));
|
||||
|
||||
if(explosiveness > 7f && this == player){
|
||||
Events.fire(Trigger.suicideBomb);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -209,15 +213,25 @@ public abstract class Unit extends DestructibleEntity implements SaveTrait, Targ
|
||||
float radScl = 1.5f;
|
||||
float fsize = getSize() / radScl;
|
||||
moveVector.setZero();
|
||||
float cx = x - fsize/2f, cy = y - fsize/2f;
|
||||
|
||||
Units.nearby(x - fsize/2f, y - fsize/2f, fsize, fsize, en -> {
|
||||
if(en == this || en.isFlying() != isFlying()) return;
|
||||
for(Team team : Team.all){
|
||||
avoid(unitGroups[team.ordinal()].intersect(cx, cy, fsize, fsize));
|
||||
}
|
||||
|
||||
avoid(playerGroup.intersect(cx, cy, fsize, fsize));
|
||||
velocity.add(moveVector.x / mass() * Time.delta(), moveVector.y / mass() * Time.delta());
|
||||
}
|
||||
|
||||
private void avoid(Array<? extends Unit> arr){
|
||||
float radScl = 1.5f;
|
||||
|
||||
for(Unit en : arr){
|
||||
if(en.isFlying() != isFlying()) continue;
|
||||
float dst = dst(en);
|
||||
float scl = Mathf.clamp(1f - dst / (getSize()/(radScl*2f) + en.getSize()/(radScl*2f)));
|
||||
moveVector.add(Tmp.v1.set((x - en.x) * scl, (y - en.y) * scl).limit(0.4f));
|
||||
});
|
||||
|
||||
velocity.add(moveVector.x / mass() * Time.delta(), moveVector.y / mass() * Time.delta());
|
||||
}
|
||||
}
|
||||
|
||||
public @Nullable TileEntity getClosestCore(){
|
||||
@@ -308,8 +322,11 @@ public abstract class Unit extends DestructibleEntity implements SaveTrait, Targ
|
||||
|
||||
drownTime = Mathf.clamp(drownTime);
|
||||
|
||||
if(drownTime >= 0.999f && !Net.client()){
|
||||
if(drownTime >= 0.999f && !net.client()){
|
||||
damage(health + 1);
|
||||
if(this == player){
|
||||
Events.fire(Trigger.drown);
|
||||
}
|
||||
}
|
||||
|
||||
float px = x, py = y;
|
||||
@@ -347,7 +364,7 @@ public abstract class Unit extends DestructibleEntity implements SaveTrait, Targ
|
||||
}
|
||||
|
||||
public void applyEffect(StatusEffect effect, float duration){
|
||||
if(dead || Net.client()) return; //effects are synced and thus not applied through clients
|
||||
if(dead || net.client()) return; //effects are synced and thus not applied through clients
|
||||
status.handleApply(this, effect, duration);
|
||||
}
|
||||
|
||||
@@ -372,7 +389,7 @@ public abstract class Unit extends DestructibleEntity implements SaveTrait, Targ
|
||||
}
|
||||
|
||||
public void drawStats(){
|
||||
Draw.color(Color.BLACK, team.color, healthf() + Mathf.absin(Time.time(), Math.max(healthf() * 5f, 1f), 1f - healthf()));
|
||||
Draw.color(Color.black, team.color, healthf() + Mathf.absin(Time.time(), Math.max(healthf() * 5f, 1f), 1f - healthf()));
|
||||
Draw.rect(getPowerCellRegion(), x, y, rotation - 90);
|
||||
Draw.color();
|
||||
|
||||
@@ -400,10 +417,10 @@ public abstract class Unit extends DestructibleEntity implements SaveTrait, Targ
|
||||
(3f + Mathf.absin(Time.time(), 5f, 1f)) * itemtime);
|
||||
|
||||
if(number){
|
||||
Core.scene.skin.getFont("outline").draw(item.amount + "",
|
||||
Fonts.outline.draw(item.amount + "",
|
||||
x + Angles.trnsx(rotation + 180f, backTrns),
|
||||
y + Angles.trnsy(rotation + 180f, backTrns) - 3,
|
||||
Pal.accent, 0.25f * itemtime / UnitScl.dp.scl(1f), false, Align.center
|
||||
Pal.accent, 0.25f * itemtime / Scl.scl(1f), false, Align.center
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,14 @@
|
||||
package io.anuke.mindustry.entities.units;
|
||||
|
||||
import io.anuke.arc.collection.Array;
|
||||
import io.anuke.arc.collection.Bits;
|
||||
import io.anuke.arc.graphics.Color;
|
||||
import io.anuke.arc.util.Time;
|
||||
import io.anuke.arc.util.Tmp;
|
||||
import io.anuke.arc.util.pooling.Pools;
|
||||
import io.anuke.mindustry.content.StatusEffects;
|
||||
import io.anuke.mindustry.entities.traits.Saveable;
|
||||
import io.anuke.mindustry.entities.type.Unit;
|
||||
import io.anuke.mindustry.type.ContentType;
|
||||
import io.anuke.mindustry.type.StatusEffect;
|
||||
import io.anuke.arc.collection.*;
|
||||
import io.anuke.arc.graphics.*;
|
||||
import io.anuke.arc.util.*;
|
||||
import io.anuke.arc.util.pooling.*;
|
||||
import io.anuke.mindustry.content.*;
|
||||
import io.anuke.mindustry.entities.traits.*;
|
||||
import io.anuke.mindustry.entities.type.*;
|
||||
import io.anuke.mindustry.type.*;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
@@ -28,8 +26,8 @@ public class Statuses implements Saveable{
|
||||
private float damageMultiplier;
|
||||
private float armorMultiplier;
|
||||
|
||||
public void handleApply(io.anuke.mindustry.entities.type.Unit unit, StatusEffect effect, float duration){
|
||||
if(effect == StatusEffects.none || unit.isImmune(effect)) return; //don't apply empty or immune effects
|
||||
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
|
||||
|
||||
if(statuses.size > 0){
|
||||
//check for opposite effects
|
||||
@@ -39,6 +37,7 @@ public class Statuses implements Saveable{
|
||||
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;
|
||||
|
||||
@@ -60,7 +59,7 @@ public class Statuses implements Saveable{
|
||||
|
||||
public Color getStatusColor(){
|
||||
if(statuses.size == 0){
|
||||
return Tmp.c1.set(Color.WHITE);
|
||||
return Tmp.c1.set(Color.white);
|
||||
}
|
||||
|
||||
float r = 0f, g = 0f, b = 0f;
|
||||
|
||||
@@ -3,7 +3,7 @@ package io.anuke.mindustry.entities.units;
|
||||
import io.anuke.arc.*;
|
||||
|
||||
public enum UnitCommand{
|
||||
attack, retreat, patrol;
|
||||
attack, retreat, rally;
|
||||
|
||||
private final String localized;
|
||||
public static final UnitCommand[] all = values();
|
||||
|
||||
@@ -1,14 +1,64 @@
|
||||
package io.anuke.mindustry.game;
|
||||
|
||||
import io.anuke.annotations.Annotations.*;
|
||||
import io.anuke.mindustry.core.GameState.State;
|
||||
import io.anuke.mindustry.entities.traits.BuilderTrait;
|
||||
import io.anuke.mindustry.entities.type.Unit;
|
||||
import io.anuke.mindustry.type.Zone;
|
||||
import io.anuke.mindustry.entities.type.*;
|
||||
import io.anuke.mindustry.entities.units.*;
|
||||
import io.anuke.mindustry.type.*;
|
||||
import io.anuke.mindustry.world.Tile;
|
||||
import io.anuke.mindustry.entities.type.Player;
|
||||
|
||||
public class EventType{
|
||||
|
||||
//events that occur very often
|
||||
public enum Trigger{
|
||||
shock,
|
||||
phaseDeflectHit,
|
||||
impactPower,
|
||||
thoriumReactorOverheat,
|
||||
itemLaunch,
|
||||
fireExtinguish,
|
||||
newGame,
|
||||
tutorialComplete,
|
||||
flameAmmo,
|
||||
turretCool,
|
||||
enablePixelation,
|
||||
drown,
|
||||
exclusionDeath,
|
||||
suicideBomb,
|
||||
openWiki
|
||||
}
|
||||
|
||||
public static class WinEvent{}
|
||||
|
||||
public static class LoseEvent{}
|
||||
|
||||
public static class LaunchEvent{}
|
||||
|
||||
public static class MapMakeEvent{}
|
||||
|
||||
public static class MapPublishEvent{}
|
||||
|
||||
public static class CommandIssueEvent{
|
||||
public final Tile tile;
|
||||
public final UnitCommand command;
|
||||
|
||||
public CommandIssueEvent(Tile tile, UnitCommand command){
|
||||
this.tile = tile;
|
||||
this.command = command;
|
||||
}
|
||||
}
|
||||
|
||||
public static class PlayerChatEvent{
|
||||
public final Player player;
|
||||
public final String message;
|
||||
|
||||
public PlayerChatEvent(Player player, String message){
|
||||
this.player = player;
|
||||
this.message = message;
|
||||
}
|
||||
}
|
||||
|
||||
/** Called when a zone's requirements are met. */
|
||||
public static class ZoneRequireCompleteEvent{
|
||||
public final Zone zone, required;
|
||||
@@ -118,6 +168,14 @@ public class EventType{
|
||||
}
|
||||
}
|
||||
|
||||
public static class ResearchEvent{
|
||||
public final UnlockableContent content;
|
||||
|
||||
public ResearchEvent(UnlockableContent content){
|
||||
this.content = content;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when block building begins by placing down the BuildBlock.
|
||||
* The tile's block will nearly always be a BuildBlock.
|
||||
@@ -137,11 +195,13 @@ public class EventType{
|
||||
public static class BlockBuildEndEvent{
|
||||
public final Tile tile;
|
||||
public final Team team;
|
||||
public final @Nullable Player player;
|
||||
public final boolean breaking;
|
||||
|
||||
public BlockBuildEndEvent(Tile tile, Team team, boolean breaking){
|
||||
public BlockBuildEndEvent(Tile tile, @Nullable Player player, Team team, boolean breaking){
|
||||
this.tile = tile;
|
||||
this.team = team;
|
||||
this.player = player;
|
||||
this.breaking = breaking;
|
||||
}
|
||||
}
|
||||
@@ -182,10 +242,29 @@ public class EventType{
|
||||
}
|
||||
}
|
||||
|
||||
public static class UnitCreateEvent{
|
||||
public final BaseUnit unit;
|
||||
|
||||
public UnitCreateEvent(BaseUnit unit){
|
||||
this.unit = unit;
|
||||
}
|
||||
}
|
||||
|
||||
public static class ResizeEvent{
|
||||
|
||||
}
|
||||
|
||||
|
||||
public static class MechChangeEvent{
|
||||
public final Player player;
|
||||
public final Mech mech;
|
||||
|
||||
public MechChangeEvent(Player player, Mech mech){
|
||||
this.player = player;
|
||||
this.mech = mech;
|
||||
}
|
||||
}
|
||||
|
||||
/** Called after connecting; when a player recieves world data and is ready to play.*/
|
||||
public static class PlayerJoin{
|
||||
public final Player player;
|
||||
|
||||
@@ -193,7 +272,16 @@ public class EventType{
|
||||
this.player = player;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** Called when a player connects, but has not joined the game yet.*/
|
||||
public static class PlayerConnect{
|
||||
public final Player player;
|
||||
|
||||
public PlayerConnect(Player player){
|
||||
this.player = player;
|
||||
}
|
||||
}
|
||||
|
||||
public static class PlayerLeave{
|
||||
public final Player player;
|
||||
|
||||
|
||||
@@ -22,7 +22,6 @@ public enum Gamemode{
|
||||
attack(rules -> {
|
||||
rules.unitDrops = true;
|
||||
rules.attackMode = true;
|
||||
rules.waves = true;
|
||||
}, map -> map.teams.contains(waveTeam.ordinal())),
|
||||
pvp(rules -> {
|
||||
rules.pvp = true;
|
||||
@@ -69,6 +68,20 @@ public enum Gamemode{
|
||||
this.validator = validator;
|
||||
}
|
||||
|
||||
public static Gamemode bestFit(Rules rules){
|
||||
if(rules.pvp){
|
||||
return pvp;
|
||||
}else if(rules.editor){
|
||||
return editor;
|
||||
}else if(rules.attackMode){
|
||||
return attack;
|
||||
}else if(rules.infiniteResources){
|
||||
return sandbox;
|
||||
}else{
|
||||
return survival;
|
||||
}
|
||||
}
|
||||
|
||||
/** Applies this preset to this ruleset. */
|
||||
public Rules apply(Rules in){
|
||||
rules.accept(in);
|
||||
|
||||
@@ -52,10 +52,12 @@ public class GlobalData{
|
||||
}
|
||||
|
||||
public void importData(FileHandle file){
|
||||
FileHandle zipped = new ZipFileHandle(file);
|
||||
FileHandle dest = Core.files.local("zipdata.zip");
|
||||
file.copyTo(dest);
|
||||
FileHandle zipped = new ZipFileHandle(dest);
|
||||
|
||||
FileHandle base = Core.settings.getDataDirectory();
|
||||
if(!base.child("settings.bin").exists()){
|
||||
if(!zipped.child("settings.bin").exists()){
|
||||
throw new IllegalArgumentException("Not valid save data.");
|
||||
}
|
||||
|
||||
@@ -63,12 +65,13 @@ public class GlobalData{
|
||||
for(FileHandle f : base.list()){
|
||||
if(f.isDirectory()){
|
||||
f.deleteDirectory();
|
||||
}else{
|
||||
}else if(!f.name().equals("zipdata.zip")){
|
||||
f.delete();
|
||||
}
|
||||
}
|
||||
|
||||
zipped.walk(f -> f.copyTo(base.child(f.path())));
|
||||
dest.delete();
|
||||
}
|
||||
|
||||
public void modified(){
|
||||
|
||||
@@ -133,8 +133,8 @@ public class MusicControl{
|
||||
}
|
||||
|
||||
/** Plays a music track once and only once. If something is already playing, does nothing.*/
|
||||
private void playOnce(@NonNull Music music){
|
||||
if(current != null) return; //do not interrupt already-playing tracks
|
||||
private void playOnce(Music music){
|
||||
if(current != null || music == null) return; //do not interrupt already-playing tracks
|
||||
|
||||
//save last random track played to prevent duplicates
|
||||
lastRandomPlayed = music;
|
||||
|
||||
@@ -74,4 +74,9 @@ public class Rules{
|
||||
public Rules copy(){
|
||||
return JsonIO.copy(this);
|
||||
}
|
||||
|
||||
/** Returns the gamemode that best fits these rules.*/
|
||||
public Gamemode mode(){
|
||||
return Gamemode.bestFit(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -266,6 +266,10 @@ public class Saves{
|
||||
return meta == null || meta.rules == null ? null : meta.rules.zone;
|
||||
}
|
||||
|
||||
public Gamemode mode(){
|
||||
return Gamemode.bestFit(meta.rules);
|
||||
}
|
||||
|
||||
public int getBuild(){
|
||||
return meta.build;
|
||||
}
|
||||
@@ -293,9 +297,6 @@ public class Saves{
|
||||
|
||||
public void exportFile(FileHandle file) throws IOException{
|
||||
try{
|
||||
if(!file.extension().equals(saveExtension)){
|
||||
file = file.parent().child(file.nameWithoutExtension() + "." + saveExtension);
|
||||
}
|
||||
SaveIO.fileFor(index).copyTo(file);
|
||||
}catch(Exception e){
|
||||
throw new IOException(e);
|
||||
|
||||
@@ -10,7 +10,7 @@ public enum Team{
|
||||
crux(Color.valueOf("e82d2d")),
|
||||
green(Color.valueOf("4dd98b")),
|
||||
purple(Color.valueOf("9a4bdf")),
|
||||
blue(Color.ROYAL.cpy());
|
||||
blue(Color.royal.cpy());
|
||||
|
||||
public final static Team[] all = values();
|
||||
public final Color color;
|
||||
|
||||
@@ -115,9 +115,7 @@ public class Tutorial{
|
||||
outline("blockinfo");
|
||||
}
|
||||
},
|
||||
conveyor(
|
||||
line -> Strings.format(line, Math.min(placed(Blocks.conveyor), 2), 2),
|
||||
() -> placed(Blocks.conveyor, 2) && event("lineconfirm") && event("coreitem")){
|
||||
conveyor(() -> placed(Blocks.conveyor, 2) && event("lineconfirm") && event("coreitem")){
|
||||
void draw(){
|
||||
outline("category-distribution");
|
||||
outline("block-conveyor");
|
||||
@@ -179,6 +177,7 @@ public class Tutorial{
|
||||
state.wave = 5;
|
||||
|
||||
//end tutorial, never show it again
|
||||
Events.fire(Trigger.tutorialComplete);
|
||||
Core.settings.put("playedtutorial", true);
|
||||
Core.settings.save();
|
||||
}
|
||||
@@ -188,15 +187,14 @@ public class Tutorial{
|
||||
}
|
||||
},;
|
||||
|
||||
protected final String line = Core.bundle.has("tutorial." + name() + ".mobile") && mobile ? "tutorial." + name() + ".mobile" : "tutorial." + name();
|
||||
protected String line = "";
|
||||
protected final Function<String, String> text;
|
||||
protected final Array<String> sentences;
|
||||
protected Array<String> sentences;
|
||||
protected final BooleanProvider done;
|
||||
|
||||
TutorialStage(Function<String, String> text, BooleanProvider done){
|
||||
this.text = text;
|
||||
this.done = done;
|
||||
this.sentences = Array.select(Core.bundle.get(line).split("\n"), s -> !s.isEmpty());
|
||||
}
|
||||
|
||||
TutorialStage(BooleanProvider done){
|
||||
@@ -205,6 +203,10 @@ public class Tutorial{
|
||||
|
||||
/** displayed tutorial stage text.*/
|
||||
public String text(){
|
||||
if(sentences == null){
|
||||
this.line = Core.bundle.has("tutorial." + name() + ".mobile") && mobile ? "tutorial." + name() + ".mobile" : "tutorial." + name();
|
||||
this.sentences = Array.select(Core.bundle.get(line).split("\n"), s -> !s.isEmpty());
|
||||
}
|
||||
String line = sentences.get(control.tutorial.sentence);
|
||||
return line.contains("{") ? text.get(line) : line;
|
||||
}
|
||||
@@ -273,20 +275,20 @@ public class Tutorial{
|
||||
Element element = Core.scene.findVisible(name);
|
||||
if(element != null && !toggled(name)){
|
||||
element.localToStageCoordinates(Tmp.v1.setZero());
|
||||
float sin = Mathf.sin(11f, UnitScl.dp.scl(4f));
|
||||
Lines.stroke(UnitScl.dp.scl(7f), Pal.place);
|
||||
float sin = Mathf.sin(11f, Scl.scl(4f));
|
||||
Lines.stroke(Scl.scl(7f), Pal.place);
|
||||
Lines.rect(Tmp.v1.x - sin, Tmp.v1.y - sin, element.getWidth() + sin*2, element.getHeight() + sin*2);
|
||||
|
||||
float size = Math.max(element.getWidth(), element.getHeight()) + Mathf.absin(11f/2f, UnitScl.dp.scl(18f));
|
||||
float size = Math.max(element.getWidth(), element.getHeight()) + Mathf.absin(11f/2f, Scl.scl(18f));
|
||||
float angle = Angles.angle(Core.graphics.getWidth()/2f, Core.graphics.getHeight()/2f, Tmp.v1.x + element.getWidth()/2f, Tmp.v1.y + element.getHeight()/2f);
|
||||
Tmp.v2.trns(angle + 180f, size*1.4f);
|
||||
float fs = UnitScl.dp.scl(40f);
|
||||
float fs2 = UnitScl.dp.scl(56f);
|
||||
float fs = Scl.scl(40f);
|
||||
float fs2 = Scl.scl(56f);
|
||||
|
||||
Draw.color(Pal.gray);
|
||||
Drawf.tri(Tmp.v1.x + element.getWidth()/2f + Tmp.v2.x, Tmp.v1.y + element.getHeight()/2f + Tmp.v2.y, fs2, fs2, angle);
|
||||
Draw.color(Pal.place);
|
||||
Tmp.v2.setLength(Tmp.v2.len() - UnitScl.dp.scl(4));
|
||||
Tmp.v2.setLength(Tmp.v2.len() - Scl.scl(4));
|
||||
Drawf.tri(Tmp.v1.x + element.getWidth()/2f + Tmp.v2.x, Tmp.v1.y + element.getHeight()/2f + Tmp.v2.y, fs, fs, angle);
|
||||
Draw.reset();
|
||||
}
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
package io.anuke.mindustry.game;
|
||||
|
||||
import io.anuke.arc.Core;
|
||||
import io.anuke.arc.collection.ObjectMap;
|
||||
import io.anuke.arc.files.FileHandle;
|
||||
import io.anuke.arc.util.Strings;
|
||||
import io.anuke.arc.util.io.PropertiesUtils;
|
||||
import io.anuke.arc.*;
|
||||
import io.anuke.arc.Files.*;
|
||||
import io.anuke.arc.collection.*;
|
||||
import io.anuke.arc.files.*;
|
||||
import io.anuke.arc.util.*;
|
||||
import io.anuke.arc.util.io.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.*;
|
||||
|
||||
public class Version{
|
||||
/** Build type. 'official' for official releases; 'custom' or 'bleeding edge' are also used. */
|
||||
@@ -26,13 +27,13 @@ public class Version{
|
||||
if(!enabled) return;
|
||||
|
||||
try{
|
||||
FileHandle file = Core.files.internal("version.properties");
|
||||
FileHandle file = OS.isAndroid || OS.isIos ? Core.files.internal("version.properties") : new FileHandle("version.properties", FileType.Internal);
|
||||
|
||||
ObjectMap<String, String> map = new ObjectMap<>();
|
||||
PropertiesUtils.load(map, file.reader());
|
||||
|
||||
type = map.get("type");
|
||||
number = Integer.parseInt(map.get("number"));
|
||||
number = Integer.parseInt(map.get("number", "4"));
|
||||
modifier = map.get("modifier");
|
||||
if(map.get("build").contains(".")){
|
||||
String[] split = map.get("build").split("\\.");
|
||||
|
||||
@@ -44,7 +44,7 @@ public class BlockRenderer implements Disposable{
|
||||
shadows.getTexture().setFilter(TextureFilter.Linear, TextureFilter.Linear);
|
||||
shadows.resize(world.width(), world.height());
|
||||
shadows.begin();
|
||||
Core.graphics.clear(Color.WHITE);
|
||||
Core.graphics.clear(Color.white);
|
||||
Draw.proj().setOrtho(0, 0, shadows.getWidth(), shadows.getHeight());
|
||||
|
||||
Draw.color(shadowColor);
|
||||
@@ -65,7 +65,7 @@ public class BlockRenderer implements Disposable{
|
||||
fog.getTexture().setFilter(TextureFilter.Linear, TextureFilter.Linear);
|
||||
fog.resize(world.width(), world.height());
|
||||
fog.begin();
|
||||
Core.graphics.clear(Color.WHITE);
|
||||
Core.graphics.clear(Color.white);
|
||||
Draw.proj().setOrtho(0, 0, fog.getWidth(), fog.getHeight());
|
||||
|
||||
for(int x = 0; x < world.width(); x++){
|
||||
@@ -129,10 +129,10 @@ public class BlockRenderer implements Disposable{
|
||||
|
||||
for(Tile tile : shadowEvents){
|
||||
//clear it first
|
||||
Draw.color(Color.WHITE);
|
||||
Draw.color(Color.white);
|
||||
Fill.rect(tile.x + 0.5f, tile.y + 0.5f, 1, 1);
|
||||
//then draw the shadow
|
||||
Draw.color(!tile.block().hasShadow ? Color.WHITE : shadowColor);
|
||||
Draw.color(!tile.block().hasShadow ? Color.white : shadowColor);
|
||||
Fill.rect(tile.x + 0.5f, tile.y + 0.5f, 1, 1);
|
||||
}
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ public enum CacheLayer{
|
||||
|
||||
renderer.blocks.floor.endc();
|
||||
renderer.shieldBuffer.begin();
|
||||
Core.graphics.clear(Color.CLEAR);
|
||||
Core.graphics.clear(Color.clear);
|
||||
renderer.blocks.floor.beginc();
|
||||
}
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@ public class IndexedRenderer implements Disposable{
|
||||
private Matrix3 projMatrix = new Matrix3();
|
||||
private Matrix3 transMatrix = new Matrix3();
|
||||
private Matrix3 combined = new Matrix3();
|
||||
private float color = Color.WHITE.toFloatBits();
|
||||
private float color = Color.white.toFloatBits();
|
||||
|
||||
public IndexedRenderer(int sprites){
|
||||
resize(sprites);
|
||||
|
||||
@@ -8,7 +8,7 @@ import io.anuke.arc.graphics.Color;
|
||||
import io.anuke.arc.graphics.g2d.*;
|
||||
import io.anuke.arc.graphics.glutils.FrameBuffer;
|
||||
import io.anuke.arc.math.*;
|
||||
import io.anuke.arc.scene.ui.layout.UnitScl;
|
||||
import io.anuke.arc.scene.ui.layout.Scl;
|
||||
import io.anuke.arc.util.*;
|
||||
import io.anuke.arc.util.noise.RidgedPerlin;
|
||||
import io.anuke.arc.util.noise.Simplex;
|
||||
@@ -168,8 +168,8 @@ public class MenuRenderer implements Disposable{
|
||||
|
||||
//draw shadows
|
||||
Draw.proj().setOrtho(0, 0, shadows.getWidth(), shadows.getHeight());
|
||||
shadows.beginDraw(Color.CLEAR);
|
||||
Draw.color(Color.BLACK);
|
||||
shadows.beginDraw(Color.clear);
|
||||
Draw.color(Color.black);
|
||||
for(int x = 0; x < width; x++){
|
||||
for(int y = 0; y < height; y++){
|
||||
if(world.rawTile(x, y).block() != Blocks.air){
|
||||
@@ -220,7 +220,7 @@ public class MenuRenderer implements Disposable{
|
||||
|
||||
public void render(){
|
||||
time += Time.delta();
|
||||
float scaling = Math.max(UnitScl.dp.scl(4f), Math.max(Core.graphics.getWidth() / ((width - 1f) * tilesize), Core.graphics.getHeight() / ((height - 1f) * tilesize)));
|
||||
float scaling = Math.max(Scl.scl(4f), Math.max(Core.graphics.getWidth() / ((width - 1f) * tilesize), Core.graphics.getHeight() / ((height - 1f) * tilesize)));
|
||||
camera.position.set(width * tilesize / 2f, height * tilesize / 2f);
|
||||
camera.resize(Core.graphics.getWidth() / scaling,
|
||||
Core.graphics.getHeight() / scaling);
|
||||
@@ -270,7 +270,7 @@ public class MenuRenderer implements Disposable{
|
||||
Fill.circle(x + Angles.trnsx(rotation + 180, engineOffset), y + Angles.trnsy(rotation + 180, engineOffset),
|
||||
engineSize + Mathf.absin(Time.time(), 2f, engineSize / 4f));
|
||||
|
||||
Draw.color(Color.WHITE);
|
||||
Draw.color(Color.white);
|
||||
Fill.circle(x + Angles.trnsx(rotation + 180, engineOffset - 1f), y + Angles.trnsy(rotation + 180, engineOffset - 1f),
|
||||
(engineSize + Mathf.absin(Time.time(), 2f, engineSize / 4f)) / 2f);
|
||||
Draw.color();
|
||||
|
||||
@@ -82,7 +82,7 @@ public class MinimapRenderer implements Disposable{
|
||||
for(Unit unit : units){
|
||||
float rx = (unit.x - rect.x) / rect.width * w, ry = (unit.y - rect.y) / rect.width * h;
|
||||
Draw.color(unit.getTeam().color);
|
||||
Fill.rect(x + rx, y + ry, UnitScl.dp.scl(baseSize / 2f), UnitScl.dp.scl(baseSize / 2f));
|
||||
Fill.rect(x + rx, y + ry, Scl.scl(baseSize / 2f), Scl.scl(baseSize / 2f));
|
||||
}
|
||||
|
||||
Draw.color();
|
||||
|
||||
@@ -84,7 +84,7 @@ public class OverlayRenderer{
|
||||
for(Tile core : state.teams.get(enemy).cores){
|
||||
float dst = Mathf.dst(player.x, player.y, core.drawx(), core.drawy());
|
||||
if(dst < state.rules.enemyCoreBuildRadius * 1.5f){
|
||||
Draw.color(Color.DARK_GRAY);
|
||||
Draw.color(Color.darkGray);
|
||||
Lines.circle(core.drawx(), core.drawy() - 2, state.rules.enemyCoreBuildRadius);
|
||||
Draw.color(Pal.accent, enemy.color, 0.5f + Mathf.absin(Time.time(), 10f, 0.5f));
|
||||
Lines.circle(core.drawx(), core.drawy(), state.rules.enemyCoreBuildRadius);
|
||||
@@ -94,7 +94,7 @@ public class OverlayRenderer{
|
||||
}
|
||||
|
||||
Lines.stroke(2f);
|
||||
Draw.color(Color.GRAY, Color.LIGHT_GRAY, Mathf.absin(Time.time(), 8f, 1f));
|
||||
Draw.color(Color.gray, Color.lightGray, Mathf.absin(Time.time(), 8f, 1f));
|
||||
|
||||
for(Tile tile : spawner.getGroundSpawns()){
|
||||
if(tile.withinDst(player.x, player.y, state.rules.dropZoneRadius + spawnerMargin)){
|
||||
|
||||
@@ -51,7 +51,7 @@ public class Pal{
|
||||
|
||||
health = Color.valueOf("ff341c"),
|
||||
heal = Color.valueOf("98ffa9"),
|
||||
bar = Color.SLATE,
|
||||
bar = Color.slate,
|
||||
accent = Color.valueOf("ffd37f"),
|
||||
stat = Color.valueOf("ffd37f"),
|
||||
gray = Color.valueOf("454545"),
|
||||
|
||||
@@ -37,8 +37,8 @@ public class Pixelator implements Disposable{
|
||||
float px = Core.camera.position.x, py = Core.camera.position.y;
|
||||
Core.camera.position.set((int)px + ((int)(camera.width) % 2 == 0 ? 0 : 0.5f), (int)py + ((int)(camera.height) % 2 == 0 ? 0 : 0.5f));
|
||||
|
||||
int w = (int)(Core.camera.width);
|
||||
int h = (int)(Core.camera.height);
|
||||
int w = (int)(Core.camera.width * renderer.landScale());
|
||||
int h = (int)(Core.camera.height * renderer.landScale());
|
||||
|
||||
if(!graphics.isHidden() && (buffer.getWidth() != w || buffer.getHeight() != h)){
|
||||
buffer.resize(w, h);
|
||||
|
||||
@@ -5,7 +5,7 @@ import io.anuke.arc.Core;
|
||||
import io.anuke.arc.graphics.Color;
|
||||
import io.anuke.arc.graphics.g2d.TextureRegion;
|
||||
import io.anuke.arc.graphics.glutils.Shader;
|
||||
import io.anuke.arc.scene.ui.layout.UnitScl;
|
||||
import io.anuke.arc.scene.ui.layout.Scl;
|
||||
import io.anuke.arc.util.Time;
|
||||
|
||||
public class Shaders{
|
||||
@@ -48,7 +48,7 @@ public class Shaders{
|
||||
setUniformf("u_resolution", Core.graphics.getWidth(), Core.graphics.getHeight());
|
||||
setUniformi("u_time", (int)(time += Core.graphics.getDeltaTime() * 60f));
|
||||
setUniformf("u_uv", Core.atlas.white().getU(), Core.atlas.white().getV());
|
||||
setUniformf("u_scl", UnitScl.dp.scl(1f));
|
||||
setUniformf("u_scl", Scl.scl(1f));
|
||||
setUniformf("u_uv2", Core.atlas.white().getU2(), Core.atlas.white().getV2());
|
||||
}
|
||||
}
|
||||
@@ -124,8 +124,8 @@ public class Shaders{
|
||||
|
||||
@Override
|
||||
public void apply(){
|
||||
setUniformf("u_dp", UnitScl.dp.scl(1f));
|
||||
setUniformf("u_time", Time.time() / UnitScl.dp.scl(1f));
|
||||
setUniformf("u_dp", Scl.scl(1f));
|
||||
setUniformf("u_time", Time.time() / Scl.scl(1f));
|
||||
setUniformf("u_offset",
|
||||
Core.camera.position.x - Core.camera.width / 2,
|
||||
Core.camera.position.y - Core.camera.height / 2);
|
||||
|
||||
@@ -123,7 +123,7 @@ public class DesktopInput extends InputHandler{
|
||||
|
||||
@Override
|
||||
public void update(){
|
||||
if(Net.active() && Core.input.keyTap(Binding.player_list)){
|
||||
if(net.active() && Core.input.keyTap(Binding.player_list)){
|
||||
ui.listfrag.toggle();
|
||||
}
|
||||
|
||||
@@ -236,6 +236,15 @@ public class DesktopInput extends InputHandler{
|
||||
selectY = tileY(Core.input.mouseY());
|
||||
}
|
||||
|
||||
if (mode == placing && block != null){
|
||||
if (!overrideLineRotation && !Core.input.keyDown(Binding.diagonal_placement) && (selectX != cursorX || selectY != cursorY) && ((int) Core.input.axisTap(Binding.rotate) != 0)){
|
||||
rotation = ((int)((Angles.angle(selectX, selectY, cursorX, cursorY) + 45) / 90f)) % 4;
|
||||
overrideLineRotation = true;
|
||||
}
|
||||
}else{
|
||||
overrideLineRotation = false;
|
||||
}
|
||||
|
||||
if(Core.input.keyRelease(Binding.break_block) || Core.input.keyRelease(Binding.select)){
|
||||
|
||||
if(mode == placing && block != null){ //touch up while placing, place everything in selection
|
||||
@@ -280,7 +289,7 @@ public class DesktopInput extends InputHandler{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateController(){
|
||||
public void updateState(){
|
||||
if(state.is(State.menu)){
|
||||
droppingItem = false;
|
||||
mode = none;
|
||||
|
||||
@@ -19,7 +19,6 @@ import io.anuke.mindustry.entities.type.*;
|
||||
import io.anuke.mindustry.game.EventType.*;
|
||||
import io.anuke.mindustry.gen.*;
|
||||
import io.anuke.mindustry.graphics.*;
|
||||
import io.anuke.mindustry.net.Net;
|
||||
import io.anuke.mindustry.net.*;
|
||||
import io.anuke.mindustry.type.*;
|
||||
import io.anuke.mindustry.ui.fragments.*;
|
||||
@@ -39,6 +38,7 @@ public abstract class InputHandler implements InputProcessor{
|
||||
public final OverlayFragment frag = new OverlayFragment();
|
||||
|
||||
public Block block;
|
||||
public boolean overrideLineRotation;
|
||||
public int rotation;
|
||||
public boolean droppingItem;
|
||||
|
||||
@@ -49,21 +49,22 @@ public abstract class InputHandler implements InputProcessor{
|
||||
|
||||
@Remote(targets = Loc.client, called = Loc.server)
|
||||
public static void dropItem(Player player, float angle){
|
||||
if(Net.server() && player.item().amount <= 0){
|
||||
if(net.server() && player.item().amount <= 0){
|
||||
throw new ValidateException(player, "Player cannot drop an item.");
|
||||
}
|
||||
|
||||
Effects.effect(Fx.dropItem, Color.WHITE, player.x, player.y, angle, player.item().item);
|
||||
Effects.effect(Fx.dropItem, Color.white, player.x, player.y, angle, player.item().item);
|
||||
player.clearItem();
|
||||
}
|
||||
|
||||
@Remote(targets = Loc.both, forward = true, called = Loc.server)
|
||||
public static void transferInventory(Player player, Tile tile){
|
||||
if(Net.server() && (player.item().amount <= 0 || player.isTransferring || !player.timer.get(Player.timerTransfer, 40))){
|
||||
if(player == null || player.timer == null || !player.timer.get(Player.timerTransfer, 40)) return;
|
||||
if(net.server() && (player.item().amount <= 0 || player.isTransferring|| !tile.interactable(player.getTeam()))){
|
||||
throw new ValidateException(player, "Player cannot transfer an item.");
|
||||
}
|
||||
|
||||
if(player == null || tile.entity == null) return;
|
||||
if(tile.entity == null) return;
|
||||
|
||||
player.isTransferring = true;
|
||||
|
||||
@@ -132,7 +133,7 @@ public abstract class InputHandler implements InputProcessor{
|
||||
|
||||
}
|
||||
|
||||
public void updateController(){
|
||||
public void updateState(){
|
||||
|
||||
}
|
||||
|
||||
@@ -273,6 +274,23 @@ public abstract class InputHandler implements InputProcessor{
|
||||
public void remove(){
|
||||
Core.input.removeProcessor(this);
|
||||
frag.remove();
|
||||
if(Core.scene != null){
|
||||
Table table = (Table)Core.scene.find("inputTable");
|
||||
if(table != null){
|
||||
table.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void add(){
|
||||
Core.input.addProcessor(this);
|
||||
if(Core.scene != null){
|
||||
Table table = (Table)Core.scene.find("inputTable");
|
||||
if(table != null){
|
||||
table.clear();
|
||||
buildUI(table);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean canShoot(){
|
||||
@@ -297,7 +315,7 @@ public abstract class InputHandler implements InputProcessor{
|
||||
|
||||
ItemStack stack = player.item();
|
||||
|
||||
if(tile.block().acceptStack(stack.item, stack.amount, tile, player) > 0 && tile.interactable(player.getTeam()) && tile.block().hasItems){
|
||||
if(tile.block().acceptStack(stack.item, stack.amount, tile, player) > 0 && tile.interactable(player.getTeam()) && tile.block().hasItems && player.item().amount > 0 && !player.isTransferring && tile.interactable(player.getTeam())){
|
||||
Call.transferInventory(player, tile);
|
||||
}else{
|
||||
Call.dropItem(player.angleTo(x, y));
|
||||
@@ -363,7 +381,10 @@ public abstract class InputHandler implements InputProcessor{
|
||||
}
|
||||
|
||||
float angle = Angles.angle(startX, startY, endX, endY);
|
||||
int baseRotation = (startX == endX && startY == endY) ? rotation : ((int)((angle + 45) / 90f)) % 4;
|
||||
int baseRotation = rotation;
|
||||
if (!overrideLineRotation || diagonal){
|
||||
baseRotation = (startX == endX && startY == endY) ? rotation : ((int)((angle + 45) / 90f)) % 4;
|
||||
}
|
||||
|
||||
Tmp.r3.set(-1, -1, 0, 0);
|
||||
|
||||
@@ -377,7 +398,11 @@ public abstract class InputHandler implements InputProcessor{
|
||||
Point2 next = i == points.size - 1 ? null : points.get(i + 1);
|
||||
line.x = point.x;
|
||||
line.y = point.y;
|
||||
line.rotation = next != null ? Tile.relativeTo(point.x, point.y, next.x, next.y) : baseRotation;
|
||||
if (!overrideLineRotation || diagonal){
|
||||
line.rotation = next != null ? Tile.relativeTo(point.x, point.y, next.x, next.y) : baseRotation;
|
||||
}else{
|
||||
line.rotation = rotation;
|
||||
}
|
||||
line.last = next == null;
|
||||
cons.accept(line);
|
||||
|
||||
|
||||
@@ -17,8 +17,10 @@ import io.anuke.mindustry.entities.traits.BuilderTrait.*;
|
||||
import io.anuke.mindustry.entities.traits.*;
|
||||
import io.anuke.mindustry.entities.type.*;
|
||||
import io.anuke.mindustry.game.EventType.*;
|
||||
import io.anuke.mindustry.gen.*;
|
||||
import io.anuke.mindustry.graphics.*;
|
||||
import io.anuke.mindustry.input.PlaceUtils.*;
|
||||
import io.anuke.mindustry.ui.*;
|
||||
import io.anuke.mindustry.world.*;
|
||||
|
||||
import static io.anuke.mindustry.Vars.*;
|
||||
@@ -29,11 +31,12 @@ public class MobileInput extends InputHandler implements GestureListener{
|
||||
private static final float maxPanSpeed = 1.3f;
|
||||
private static Rectangle r1 = new Rectangle(), r2 = new Rectangle();
|
||||
/** Distance to edge of screen to start panning. */
|
||||
private final float edgePan = UnitScl.dp.scl(60f);
|
||||
private final float edgePan = Scl.scl(60f);
|
||||
|
||||
//gesture data
|
||||
private Vector2 vector = new Vector2();
|
||||
private float lastZoom = -1;
|
||||
private GestureDetector detector;
|
||||
|
||||
/** Position where the player started dragging a line. */
|
||||
private int lineStartX, lineStartY;
|
||||
@@ -63,12 +66,6 @@ public class MobileInput extends InputHandler implements GestureListener{
|
||||
|
||||
private int prevX, prevY, prevRotation;
|
||||
|
||||
public MobileInput(){
|
||||
Events.on(ClientLoadEvent.class, e -> {
|
||||
Core.input.addProcessor(new GestureDetector(20, 0.5f, 0.4f, 0.15f, this));
|
||||
});
|
||||
}
|
||||
|
||||
//region utility methods
|
||||
|
||||
/** Check and assign targets for a specific position. */
|
||||
@@ -190,7 +187,7 @@ public class MobileInput extends InputHandler implements GestureListener{
|
||||
TextureRegion region = placeDraw.region;
|
||||
|
||||
Draw.mixcol(Pal.accent, Mathf.clamp((1f - request.scale) / 0.5f + 0.12f + Mathf.absin(Time.time(), 8f, 0.35f)));
|
||||
Draw.tint(Color.WHITE, Pal.breakInvalid, request.redness);
|
||||
Draw.tint(Color.white, Pal.breakInvalid, request.redness);
|
||||
|
||||
Draw.rect(region, tile.worldx() + offset, tile.worldy() + offset,
|
||||
region.getWidth() * request.scale * Draw.scl * placeDraw.scalex,
|
||||
@@ -249,27 +246,27 @@ public class MobileInput extends InputHandler implements GestureListener{
|
||||
|
||||
@Override
|
||||
public void buildUI(Table table){
|
||||
table.addImage("whiteui").color(Pal.gray).height(4f).colspan(4).growX();
|
||||
table.addImage().color(Pal.gray).height(4f).colspan(4).growX();
|
||||
table.row();
|
||||
table.left().margin(0f).defaults().size(48f);
|
||||
|
||||
table.addImageButton("icon-break-small", "clear-toggle-partial", iconsizesmall, () -> {
|
||||
table.addImageButton(Icon.breakSmall, Styles.clearTogglePartiali, () -> {
|
||||
mode = mode == breaking ? block == null ? none : placing : breaking;
|
||||
lastBlock = block;
|
||||
}).update(l -> l.setChecked(mode == breaking)).name("breakmode");
|
||||
|
||||
//diagonal swap button
|
||||
table.addImageButton("icon-diagonal-small", "clear-toggle-partial", iconsizesmall, () -> {
|
||||
table.addImageButton(Icon.diagonalSmall, Styles.clearTogglePartiali, () -> {
|
||||
Core.settings.put("swapdiagonal", !Core.settings.getBool("swapdiagonal"));
|
||||
Core.settings.save();
|
||||
}).update(l -> l.setChecked(Core.settings.getBool("swapdiagonal")));
|
||||
|
||||
//rotate button
|
||||
table.addImageButton("icon-arrow-small", "clear-partial", iconsizesmall, () -> rotation = Mathf.mod(rotation + 1, 4))
|
||||
table.addImageButton(Icon.arrowSmall, Styles.clearPartiali,() -> rotation = Mathf.mod(rotation + 1, 4))
|
||||
.update(i -> i.getImage().setRotationOrigin(rotation * 90, Align.center)).visible(() -> block != null && block.rotate);
|
||||
|
||||
//confirm button
|
||||
table.addImageButton("icon-check-small", "clear-partial", iconsizesmall, () -> {
|
||||
table.addImageButton(Icon.checkSmall, Styles.clearPartiali, () -> {
|
||||
for(PlaceRequest request : selection){
|
||||
Tile tile = request.tile();
|
||||
|
||||
@@ -294,8 +291,9 @@ public class MobileInput extends InputHandler implements GestureListener{
|
||||
}).visible(() -> !selection.isEmpty()).name("confirmplace");
|
||||
|
||||
Core.scene.table(t -> {
|
||||
t.setName("cancelMobile");
|
||||
t.bottom().left().visible(() -> (player.isBuilding() || block != null || mode == breaking) && !state.is(State.menu));
|
||||
t.addImageTextButton("$cancel", "icon-cancel", 16*2, () -> {
|
||||
t.addImageTextButton("$cancel", Icon.cancelSmall, () -> {
|
||||
player.clearBuilding();
|
||||
mode = none;
|
||||
block = null;
|
||||
@@ -443,6 +441,24 @@ public class MobileInput extends InputHandler implements GestureListener{
|
||||
//endregion
|
||||
//region input events
|
||||
|
||||
@Override
|
||||
public void add(){
|
||||
Core.input.addProcessor(detector = new GestureDetector(20, 0.5f, 0.4f, 0.15f, this));
|
||||
super.add();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(){
|
||||
super.remove();
|
||||
if(detector != null){
|
||||
Core.input.removeProcessor(detector);
|
||||
}
|
||||
|
||||
if(Core.scene != null && Core.scene.find("cancelMobile") != null){
|
||||
Core.scene.find("cancelMobile").remove();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean touchDown(int screenX, int screenY, int pointer, KeyCode button){
|
||||
if(state.is(State.menu) || player.isDead()) return false;
|
||||
|
||||
@@ -207,7 +207,7 @@ public class LegacyMapIO{
|
||||
if(block.ore != null) tile.setOverlay(block.ore);
|
||||
|
||||
//place core
|
||||
if(color == Color.rgba8888(Color.GREEN)){
|
||||
if(color == Color.rgba8888(Color.green)){
|
||||
for(int dx = 0; dx < 3; dx++){
|
||||
for(int dy = 0; dy < 3; dy++){
|
||||
int worldx = dx - 1 + x;
|
||||
|
||||
@@ -73,7 +73,7 @@ public class MapIO{
|
||||
|
||||
Pixmap floors = new Pixmap(map.width, map.height, Format.RGBA8888);
|
||||
Pixmap walls = new Pixmap(map.width, map.height, Format.RGBA8888);
|
||||
int black = Color.rgba8888(Color.BLACK);
|
||||
int black = Color.rgba8888(Color.black);
|
||||
int shade = Color.rgba8888(0f, 0f, 0f, 0.5f);
|
||||
CachedTile tile = new CachedTile(){
|
||||
@Override
|
||||
|
||||
@@ -5,7 +5,7 @@ import io.anuke.annotations.Annotations.WriteClass;
|
||||
import io.anuke.arc.graphics.Color;
|
||||
import io.anuke.mindustry.entities.Effects;
|
||||
import io.anuke.mindustry.entities.Effects.Effect;
|
||||
import io.anuke.mindustry.entities.bullet.Bullet;
|
||||
import io.anuke.mindustry.entities.type.Bullet;
|
||||
import io.anuke.mindustry.entities.bullet.BulletType;
|
||||
import io.anuke.mindustry.entities.traits.BuilderTrait.BuildRequest;
|
||||
import io.anuke.mindustry.entities.traits.ShooterTrait;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package io.anuke.mindustry.io.versions;
|
||||
|
||||
import io.anuke.arc.function.Supplier;
|
||||
import io.anuke.mindustry.entities.bullet.Bullet;
|
||||
import io.anuke.mindustry.entities.type.Bullet;
|
||||
import io.anuke.mindustry.entities.effect.*;
|
||||
import io.anuke.mindustry.entities.type.Player;
|
||||
import io.anuke.mindustry.entities.type.base.*;
|
||||
|
||||
@@ -20,6 +20,8 @@ public class Map implements Comparable<Map>{
|
||||
public final FileHandle file;
|
||||
/** Format version. */
|
||||
public final int version;
|
||||
/** Whether this map is managed, e.g. downloaded from the Steam workshop.*/
|
||||
public boolean workshop;
|
||||
/** Map width/height, shorts. */
|
||||
public int width, height;
|
||||
/** Preview texture. */
|
||||
@@ -57,8 +59,12 @@ public class Map implements Comparable<Map>{
|
||||
return Core.settings.getInt("hiscore" + file.nameWithoutExtension(), 0);
|
||||
}
|
||||
|
||||
public Texture safeTexture(){
|
||||
return texture == null ? Core.assets.get("sprites/error.png") : texture;
|
||||
}
|
||||
|
||||
public FileHandle previewFile(){
|
||||
return Vars.mapPreviewDirectory.child(file.nameWithoutExtension() + ".png");
|
||||
return Vars.mapPreviewDirectory.child((workshop ? file.parent().name() : file.nameWithoutExtension()) + ".png");
|
||||
}
|
||||
|
||||
public FileHandle cacheFile(){
|
||||
@@ -127,6 +133,8 @@ public class Map implements Comparable<Map>{
|
||||
|
||||
@Override
|
||||
public int compareTo(Map map){
|
||||
int work = -Boolean.compare(workshop, map.workshop);
|
||||
if(work != 0) return work;
|
||||
int type = -Boolean.compare(custom, map.custom);
|
||||
if(type != 0) return type;
|
||||
int modes = Boolean.compare(Gamemode.pvp.valid(this), Gamemode.pvp.valid(map));
|
||||
|
||||
@@ -5,6 +5,8 @@ import io.anuke.arc.assets.loaders.*;
|
||||
import io.anuke.arc.assets.loaders.resolvers.*;
|
||||
import io.anuke.arc.collection.*;
|
||||
import io.anuke.arc.files.*;
|
||||
import io.anuke.arc.graphics.*;
|
||||
import io.anuke.arc.util.*;
|
||||
import io.anuke.mindustry.*;
|
||||
import io.anuke.mindustry.game.*;
|
||||
|
||||
@@ -19,12 +21,27 @@ public class MapPreviewLoader extends TextureLoader{
|
||||
try{
|
||||
super.loadAsync(manager, fileName, file.sibling(file.nameWithoutExtension()), parameter);
|
||||
}catch(Exception e){
|
||||
e.printStackTrace();
|
||||
Log.err(e);
|
||||
MapPreviewParameter param = (MapPreviewParameter)parameter;
|
||||
Vars.maps.queueNewPreview(param.map);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Texture loadSync(AssetManager manager, String fileName, FileHandle file, TextureParameter parameter){
|
||||
try{
|
||||
return super.loadSync(manager, fileName, file, parameter);
|
||||
}catch(Throwable e){
|
||||
Log.err(e);
|
||||
try{
|
||||
return new Texture(file);
|
||||
}catch(Throwable e2){
|
||||
Log.err(e2);
|
||||
return new Texture("sprites/error.png");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Array<AssetDescriptor> getDependencies(String fileName, FileHandle file, TextureParameter parameter){
|
||||
return Array.with(new AssetDescriptor<>("contentcreate", Content.class));
|
||||
|
||||
@@ -81,6 +81,7 @@ public class Maps{
|
||||
|
||||
/** Load all maps. Should be called at application start. */
|
||||
public void load(){
|
||||
//defaults; must work
|
||||
try{
|
||||
for(String name : defaultMapNames){
|
||||
FileHandle file = Core.files.internal("maps/" + name + "." + mapExtension);
|
||||
@@ -90,7 +91,27 @@ public class Maps{
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
loadCustomMaps();
|
||||
//custom
|
||||
for(FileHandle file : customMapDirectory.list()){
|
||||
try{
|
||||
if(file.extension().equalsIgnoreCase(mapExtension)){
|
||||
loadMap(file, true);
|
||||
}
|
||||
}catch(Exception e){
|
||||
Log.err("Failed to load custom map file '{0}'!", file);
|
||||
Log.err(e);
|
||||
}
|
||||
}
|
||||
|
||||
//workshop
|
||||
for(FileHandle file : platform.getExternalMaps()){
|
||||
try{
|
||||
loadMap(file, false).workshop = true;
|
||||
}catch(Exception e){
|
||||
Log.err("Failed to load workshop map file '{0}'!", file);
|
||||
Log.err(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void reload(){
|
||||
@@ -108,7 +129,7 @@ public class Maps{
|
||||
* Save a custom map to the directory. This updates all values and stored data necessary.
|
||||
* The tags are copied to prevent mutation later.
|
||||
*/
|
||||
public void saveMap(ObjectMap<String, String> baseTags){
|
||||
public Map saveMap(ObjectMap<String, String> baseTags){
|
||||
|
||||
try{
|
||||
StringMap tags = new StringMap(baseTags);
|
||||
@@ -166,25 +187,35 @@ public class Maps{
|
||||
}
|
||||
maps.add(map);
|
||||
maps.sort();
|
||||
|
||||
return map;
|
||||
|
||||
}catch(IOException e){
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/** Creates a legacy map by converting it to a non-legacy map and pasting it in a temp directory.
|
||||
* Should be followed up by {@link #importMap(FileHandle)} .*/
|
||||
public Map makeLegacyMap(FileHandle file) throws IOException{
|
||||
FileHandle dst = tmpDirectory.child("conversion_map." + mapExtension);
|
||||
LegacyMapIO.convertMap(file, dst);
|
||||
return MapIO.createMap(dst, true);
|
||||
}
|
||||
|
||||
/** Import a map, then save it. This updates all values and stored data necessary. */
|
||||
public void importMap(FileHandle file) throws IOException{
|
||||
FileHandle dest = findFile();
|
||||
file.copyTo(dest);
|
||||
|
||||
createNewPreview(loadMap(dest, true), true);
|
||||
Map map = loadMap(dest, true);
|
||||
Exception[] error = {null};
|
||||
|
||||
createNewPreview(map, e -> {
|
||||
maps.remove(map);
|
||||
try{
|
||||
map.file.delete();
|
||||
}catch(Throwable ignored){
|
||||
|
||||
}
|
||||
error[0] = e;
|
||||
});
|
||||
|
||||
if(error[0] != null){
|
||||
throw new IOException(error[0]);
|
||||
}
|
||||
}
|
||||
|
||||
/** Attempts to run the following code;
|
||||
@@ -196,11 +227,11 @@ public class Maps{
|
||||
Log.err(e);
|
||||
|
||||
if("Outdated legacy map format".equals(e.getMessage())){
|
||||
ui.showError("$editor.errorlegacy");
|
||||
ui.showErrorMessage("$editor.errornot");
|
||||
}else if(e.getMessage() != null && e.getMessage().contains("Incorrect header!")){
|
||||
ui.showError("$editor.errorheader");
|
||||
ui.showErrorMessage("$editor.errorheader");
|
||||
}else{
|
||||
ui.showError(Core.bundle.format("editor.errorload", Strings.parseException(e, true)));
|
||||
ui.showException("$editor.errorload", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -290,40 +321,12 @@ public class Maps{
|
||||
return str == null ? null : str.equals("[]") ? new Array<>() : Array.with(json.fromJson(SpawnGroup[].class, str));
|
||||
}
|
||||
|
||||
public void loadLegacyMaps(){
|
||||
boolean convertedAny = false;
|
||||
for(FileHandle file : customMapDirectory.list()){
|
||||
if(file.extension().equalsIgnoreCase(oldMapExtension)){
|
||||
try{
|
||||
convertedAny = true;
|
||||
LegacyMapIO.convertMap(file, file.sibling(file.nameWithoutExtension() + "." + mapExtension));
|
||||
//delete old, converted file; it is no longer useful
|
||||
file.delete();
|
||||
Log.info("Converted file {0}", file);
|
||||
}catch(Exception e){
|
||||
//rename the file to a 'mmap_conversion_failed' extension to keep it there just in case
|
||||
//but don't delete it
|
||||
file.copyTo(file.sibling(file.name() + "_conversion_failed"));
|
||||
file.delete();
|
||||
Log.err(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//free up any potential memory that was used up during conversion
|
||||
if(convertedAny){
|
||||
world.createTiles(1, 1);
|
||||
//reload maps to load the converted ones
|
||||
reload();
|
||||
}
|
||||
}
|
||||
|
||||
public void loadPreviews(){
|
||||
|
||||
for(Map map : maps){
|
||||
//try to load preview
|
||||
if(map.previewFile().exists()){
|
||||
//this may fail, but calls createNewPreview
|
||||
//this may fail, but calls queueNewPreview
|
||||
Core.assets.load(new AssetDescriptor<>(map.previewFile().path() + "." + mapExtension, Texture.class, new MapPreviewParameter(map))).loaded = t -> map.texture = (Texture)t;
|
||||
|
||||
try{
|
||||
@@ -341,7 +344,7 @@ public class Maps{
|
||||
private void createAllPreviews(){
|
||||
Core.app.post(() -> {
|
||||
for(Map map : previewList){
|
||||
createNewPreview(map, false);
|
||||
createNewPreview(map, e -> Core.app.post(() -> map.texture = Core.assets.get("sprites/error.png")));
|
||||
}
|
||||
previewList.clear();
|
||||
});
|
||||
@@ -351,16 +354,12 @@ public class Maps{
|
||||
Core.app.post(() -> previewList.add(map));
|
||||
}
|
||||
|
||||
private void createNewPreview(Map map, boolean immediate){
|
||||
private void createNewPreview(Map map, Consumer<Exception> failed){
|
||||
try{
|
||||
//if it's here, then the preview failed to load or doesn't exist, make it
|
||||
//this has to be done synchronously!
|
||||
Pixmap pix = MapIO.generatePreview(map);
|
||||
if(immediate){
|
||||
map.texture = new Texture(pix);
|
||||
}else{
|
||||
Core.app.post(() -> map.texture = new Texture(pix));
|
||||
}
|
||||
map.texture = new Texture(pix);
|
||||
executor.submit(() -> {
|
||||
try{
|
||||
map.previewFile().writePNG(pix);
|
||||
@@ -369,9 +368,9 @@ public class Maps{
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
}catch(IOException e){
|
||||
}catch(Exception e){
|
||||
failed.accept(e);
|
||||
Log.err("Failed to generate preview!", e);
|
||||
Core.app.post(() -> map.texture = new Texture("sprites/error.png"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -420,16 +419,4 @@ public class Maps{
|
||||
return map;
|
||||
}
|
||||
|
||||
private void loadCustomMaps(){
|
||||
for(FileHandle file : customMapDirectory.list()){
|
||||
try{
|
||||
if(file.extension().equalsIgnoreCase(mapExtension)){
|
||||
loadMap(file, true);
|
||||
}
|
||||
}catch(Exception e){
|
||||
Log.err("Failed to load custom map file '{0}'!", file);
|
||||
Log.err(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -59,7 +59,7 @@ public class MirrorFilter extends GenerateFilter{
|
||||
clamper.accept(Tmp.v1.trns(angle - 90, size).add(image.getWidth()/2f + image.getX(), image.getHeight()/2f + image.getY()));
|
||||
clamper.accept(Tmp.v2.set(Tmp.v1).sub(image.getWidth()/2f + image.getX(), image.getHeight()/2f + image.getY()).rotate(180f).add(image.getWidth()/2f + image.getX(), image.getHeight()/2f + image.getY()));
|
||||
|
||||
Lines.stroke(UnitScl.dp.scl(3f), Pal.accent);
|
||||
Lines.stroke(Scl.scl(3f), Pal.accent);
|
||||
Lines.line(Tmp.v1.x, Tmp.v1.y, Tmp.v2.x, Tmp.v2.y);
|
||||
Draw.reset();
|
||||
}
|
||||
|
||||
@@ -21,9 +21,16 @@ public class Administration{
|
||||
load();
|
||||
}
|
||||
|
||||
public int getPlayerLimit(){
|
||||
return Core.settings.getInt("playerlimit", 0);
|
||||
}
|
||||
|
||||
public void setPlayerLimit(int limit){
|
||||
Core.settings.putSave("playerlimit", limit);
|
||||
}
|
||||
|
||||
public void setStrict(boolean on){
|
||||
Core.settings.put("strict", on);
|
||||
Core.settings.save();
|
||||
Core.settings.putSave("strict", on);
|
||||
}
|
||||
|
||||
public boolean getStrict(){
|
||||
|
||||
425
core/src/io/anuke/mindustry/net/ArcNetImpl.java
Normal file
425
core/src/io/anuke/mindustry/net/ArcNetImpl.java
Normal file
@@ -0,0 +1,425 @@
|
||||
package io.anuke.mindustry.net;
|
||||
|
||||
import io.anuke.arc.*;
|
||||
import io.anuke.arc.collection.*;
|
||||
import io.anuke.arc.function.*;
|
||||
import io.anuke.arc.net.*;
|
||||
import io.anuke.arc.net.FrameworkMessage.*;
|
||||
import io.anuke.arc.util.*;
|
||||
import io.anuke.arc.util.async.*;
|
||||
import io.anuke.arc.util.pooling.*;
|
||||
import io.anuke.mindustry.net.Net.*;
|
||||
import io.anuke.mindustry.net.Packets.*;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.*;
|
||||
import java.nio.*;
|
||||
import java.nio.channels.*;
|
||||
import java.util.concurrent.*;
|
||||
|
||||
import static io.anuke.mindustry.Vars.*;
|
||||
|
||||
public class ArcNetImpl implements NetProvider{
|
||||
final Client client;
|
||||
final Supplier<DatagramPacket> packetSupplier = () -> new DatagramPacket(new byte[256], 256);
|
||||
|
||||
final Server server;
|
||||
final CopyOnWriteArrayList<ArcConnection> connections = new CopyOnWriteArrayList<>();
|
||||
Thread serverThread;
|
||||
|
||||
public ArcNetImpl(){
|
||||
client = new Client(8192, 4096, new PacketSerializer());
|
||||
client.setDiscoveryPacket(packetSupplier);
|
||||
client.addListener(new NetListener(){
|
||||
@Override
|
||||
public void connected(Connection connection){
|
||||
Connect c = new Connect();
|
||||
c.addressTCP = connection.getRemoteAddressTCP().getAddress().getHostAddress();
|
||||
if(connection.getRemoteAddressTCP() != null) c.addressTCP = connection.getRemoteAddressTCP().toString();
|
||||
|
||||
Core.app.post(() -> net.handleClientReceived(c));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disconnected(Connection connection, DcReason reason){
|
||||
if(connection.getLastProtocolError() != null){
|
||||
netClient.setQuiet();
|
||||
}
|
||||
|
||||
Disconnect c = new Disconnect();
|
||||
c.reason = reason.toString();
|
||||
Core.app.post(() -> net.handleClientReceived(c));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void received(Connection connection, Object object){
|
||||
if(object instanceof FrameworkMessage) return;
|
||||
|
||||
Core.app.post(() -> {
|
||||
try{
|
||||
net.handleClientReceived(object);
|
||||
}catch(Exception e){
|
||||
handleException(e);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
server = new Server(4096 * 2, 4096, new PacketSerializer());
|
||||
server.setMulticast(multicastGroup, multicastPort);
|
||||
server.setDiscoveryHandler((address, handler) -> {
|
||||
ByteBuffer buffer = NetworkIO.writeServerData();
|
||||
buffer.position(0);
|
||||
handler.respond(buffer);
|
||||
});
|
||||
|
||||
server.addListener(new NetListener(){
|
||||
|
||||
@Override
|
||||
public void connected(Connection connection){
|
||||
String ip = connection.getRemoteAddressTCP().getAddress().getHostAddress();
|
||||
|
||||
ArcConnection kn = new ArcConnection(ip, connection);
|
||||
|
||||
Connect c = new Connect();
|
||||
c.addressTCP = ip;
|
||||
|
||||
Log.debug("&bRecieved connection: {0}", c.addressTCP);
|
||||
|
||||
connections.add(kn);
|
||||
Core.app.post(() -> net.handleServerReceived(kn, c));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disconnected(Connection connection, DcReason reason){
|
||||
ArcConnection k = getByArcID(connection.getID());
|
||||
if(k == null) return;
|
||||
|
||||
Disconnect c = new Disconnect();
|
||||
c.reason = reason.toString();
|
||||
|
||||
Core.app.post(() -> {
|
||||
net.handleServerReceived(k, c);
|
||||
connections.remove(k);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void received(Connection connection, Object object){
|
||||
ArcConnection k = getByArcID(connection.getID());
|
||||
if(object instanceof FrameworkMessage || k == null) return;
|
||||
|
||||
Core.app.post(() -> {
|
||||
try{
|
||||
net.handleServerReceived(k, object);
|
||||
}catch(RuntimeException e){
|
||||
if(e.getCause() instanceof ValidateException){
|
||||
ValidateException v = (ValidateException)e.getCause();
|
||||
Log.err("Validation failed: {0} ({1})", v.player.name, v.getMessage());
|
||||
}else{
|
||||
e.printStackTrace();
|
||||
}
|
||||
}catch(Exception e){
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static boolean isLocal(InetAddress addr){
|
||||
if(addr.isAnyLocalAddress() || addr.isLoopbackAddress()) return true;
|
||||
|
||||
try{
|
||||
return NetworkInterface.getByInetAddress(addr) != null;
|
||||
}catch(Exception e){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void connectClient(String ip, int port, Runnable success){
|
||||
Threads.daemon(() -> {
|
||||
try{
|
||||
//just in case
|
||||
client.stop();
|
||||
|
||||
Threads.daemon("Net Client", () -> {
|
||||
try{
|
||||
client.run();
|
||||
}catch(Exception e){
|
||||
if(!(e instanceof ClosedSelectorException)) handleException(e);
|
||||
}
|
||||
});
|
||||
|
||||
client.connect(5000, ip, port, port);
|
||||
success.run();
|
||||
}catch(Exception e){
|
||||
handleException(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disconnectClient(){
|
||||
client.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendClient(Object object, SendMode mode){
|
||||
try{
|
||||
if(mode == SendMode.tcp){
|
||||
client.sendTCP(object);
|
||||
}else{
|
||||
client.sendUDP(object);
|
||||
}
|
||||
//sending things can cause an under/overflow, catch it and disconnect instead of crashing
|
||||
}catch(BufferOverflowException | BufferUnderflowException e){
|
||||
net.showError(e);
|
||||
}
|
||||
|
||||
Pools.free(object);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pingHost(String address, int port, Consumer<Host> valid, Consumer<Exception> invalid){
|
||||
Threads.daemon(() -> {
|
||||
try{
|
||||
DatagramSocket socket = new DatagramSocket();
|
||||
socket.send(new DatagramPacket(new byte[]{-2, 1}, 2, InetAddress.getByName(address), port));
|
||||
socket.setSoTimeout(2000);
|
||||
|
||||
DatagramPacket packet = packetSupplier.get();
|
||||
socket.receive(packet);
|
||||
|
||||
ByteBuffer buffer = ByteBuffer.wrap(packet.getData());
|
||||
Host host = NetworkIO.readServerData(packet.getAddress().getHostAddress(), buffer);
|
||||
|
||||
Core.app.post(() -> valid.accept(host));
|
||||
}catch(Exception e){
|
||||
Core.app.post(() -> invalid.accept(e));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void discoverServers(Consumer<Host> callback, Runnable done){
|
||||
Array<InetAddress> foundAddresses = new Array<>();
|
||||
client.discoverHosts(port, multicastGroup, multicastPort, 3000, packet -> {
|
||||
Core.app.post(() -> {
|
||||
try{
|
||||
if(foundAddresses.contains(address -> address.equals(packet.getAddress()) || (isLocal(address) && isLocal(packet.getAddress())))){
|
||||
return;
|
||||
}
|
||||
ByteBuffer buffer = ByteBuffer.wrap(packet.getData());
|
||||
Host host = NetworkIO.readServerData(packet.getAddress().getHostAddress(), buffer);
|
||||
callback.accept(host);
|
||||
foundAddresses.add(packet.getAddress());
|
||||
}catch(Exception e){
|
||||
//don't crash when there's an error pinging a a server or parsing data
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
}, () -> Core.app.post(done));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose(){
|
||||
disconnectClient();
|
||||
closeServer();
|
||||
try{
|
||||
client.dispose();
|
||||
}catch(IOException ignored){
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<ArcConnection> getConnections(){
|
||||
return connections;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hostServer(int port) throws IOException{
|
||||
connections.clear();
|
||||
server.bind(port, port);
|
||||
|
||||
serverThread = new Thread(() -> {
|
||||
try{
|
||||
server.run();
|
||||
}catch(Throwable e){
|
||||
if(!(e instanceof ClosedSelectorException)) Threads.throwAppException(e);
|
||||
}
|
||||
}, "Net Server");
|
||||
serverThread.setDaemon(true);
|
||||
serverThread.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closeServer(){
|
||||
connections.clear();
|
||||
Threads.daemon(server::stop);
|
||||
}
|
||||
|
||||
ArcConnection getByArcID(int id){
|
||||
for(int i = 0; i < connections.size(); i++){
|
||||
ArcConnection con = connections.get(i);
|
||||
if(con.connection != null && con.connection.getID() == id){
|
||||
return con;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void handleException(Exception e){
|
||||
if(e instanceof ArcNetException){
|
||||
Core.app.post(() -> net.showError(new IOException("mismatch")));
|
||||
}else if(e instanceof ClosedChannelException){
|
||||
Core.app.post(() -> net.showError(new IOException("alreadyconnected")));
|
||||
}else{
|
||||
Core.app.post(() -> net.showError(e));
|
||||
}
|
||||
}
|
||||
|
||||
class ArcConnection extends NetConnection{
|
||||
public final Connection connection;
|
||||
|
||||
public ArcConnection(String address, Connection connection){
|
||||
super(address);
|
||||
this.connection = connection;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isConnected(){
|
||||
return connection.isConnected();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendStream(Streamable stream){
|
||||
connection.addListener(new InputStreamSender(stream.stream, 512){
|
||||
int id;
|
||||
|
||||
@Override
|
||||
protected void start(){
|
||||
//send an object so the receiving side knows how to handle the following chunks
|
||||
StreamBegin begin = new StreamBegin();
|
||||
begin.total = stream.stream.available();
|
||||
begin.type = Registrator.getID(stream.getClass());
|
||||
connection.sendTCP(begin);
|
||||
id = begin.id;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object next(byte[] bytes){
|
||||
StreamChunk chunk = new StreamChunk();
|
||||
chunk.id = id;
|
||||
chunk.data = bytes;
|
||||
return chunk; //wrap the byte[] with an object so the receiving side knows how to handle it.
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void send(Object object, SendMode mode){
|
||||
try{
|
||||
if(mode == SendMode.tcp){
|
||||
connection.sendTCP(object);
|
||||
}else{
|
||||
connection.sendUDP(object);
|
||||
}
|
||||
}catch(Exception e){
|
||||
Log.err(e);
|
||||
Log.info("Error sending packet. Disconnecting invalid client!");
|
||||
connection.close(DcReason.error);
|
||||
|
||||
ArcConnection k = getByArcID(connection.getID());
|
||||
if(k != null) connections.remove(k);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close(){
|
||||
if(connection.isConnected()) connection.close(DcReason.closed);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static class PacketSerializer implements NetSerializer{
|
||||
|
||||
@Override
|
||||
public void write(ByteBuffer byteBuffer, Object o){
|
||||
if(o instanceof FrameworkMessage){
|
||||
byteBuffer.put((byte)-2); //code for framework message
|
||||
writeFramework(byteBuffer, (FrameworkMessage)o);
|
||||
}else{
|
||||
if(!(o instanceof Packet))
|
||||
throw new RuntimeException("All sent objects must implement be Packets! Class: " + o.getClass());
|
||||
byte id = Registrator.getID(o.getClass());
|
||||
if(id == -1)
|
||||
throw new RuntimeException("Unregistered class: " + o.getClass());
|
||||
byteBuffer.put(id);
|
||||
((Packet)o).write(byteBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object read(ByteBuffer byteBuffer){
|
||||
byte id = byteBuffer.get();
|
||||
if(id == -2){
|
||||
return readFramework(byteBuffer);
|
||||
}else{
|
||||
Packet packet = Pools.obtain((Class<Packet>)Registrator.getByID(id).type, (Supplier<Packet>)Registrator.getByID(id).constructor);
|
||||
packet.read(byteBuffer);
|
||||
return packet;
|
||||
}
|
||||
}
|
||||
|
||||
public void writeFramework(ByteBuffer buffer, FrameworkMessage message){
|
||||
if(message instanceof Ping){
|
||||
Ping p = (Ping)message;
|
||||
buffer.put((byte)0);
|
||||
buffer.putInt(p.id);
|
||||
buffer.put(p.isReply ? 1 : (byte)0);
|
||||
}else if(message instanceof DiscoverHost){
|
||||
buffer.put((byte)1);
|
||||
}else if(message instanceof KeepAlive){
|
||||
buffer.put((byte)2);
|
||||
}else if(message instanceof RegisterUDP){
|
||||
RegisterUDP p = (RegisterUDP)message;
|
||||
buffer.put((byte)3);
|
||||
buffer.putInt(p.connectionID);
|
||||
}else if(message instanceof RegisterTCP){
|
||||
RegisterTCP p = (RegisterTCP)message;
|
||||
buffer.put((byte)4);
|
||||
buffer.putInt(p.connectionID);
|
||||
}
|
||||
}
|
||||
|
||||
public FrameworkMessage readFramework(ByteBuffer buffer){
|
||||
byte id = buffer.get();
|
||||
|
||||
if(id == 0){
|
||||
Ping p = new Ping();
|
||||
p.id = buffer.getInt();
|
||||
p.isReply = buffer.get() == 1;
|
||||
return p;
|
||||
}else if(id == 1){
|
||||
return new DiscoverHost();
|
||||
}else if(id == 2){
|
||||
return new KeepAlive();
|
||||
}else if(id == 3){
|
||||
RegisterUDP p = new RegisterUDP();
|
||||
p.connectionID = buffer.getInt();
|
||||
return p;
|
||||
}else if(id == 4){
|
||||
RegisterTCP p = new RegisterTCP();
|
||||
p.connectionID = buffer.getInt();
|
||||
return p;
|
||||
}else{
|
||||
throw new RuntimeException("Unknown framework message!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -17,6 +17,7 @@ import java.nio.file.Files;
|
||||
import java.nio.file.*;
|
||||
import java.text.*;
|
||||
import java.util.*;
|
||||
import static io.anuke.mindustry.Vars.*;
|
||||
|
||||
public class CrashSender{
|
||||
|
||||
@@ -24,8 +25,8 @@ public class CrashSender{
|
||||
try{
|
||||
exception.printStackTrace();
|
||||
|
||||
//don't create crash logs for me (anuke) or custom builds, as it's expected
|
||||
if(System.getProperty("user.name").equals("anuke") || Version.build == -1) return;
|
||||
//don't create crash logs for custom builds, as it's expected
|
||||
if(Version.build == -1) return;
|
||||
|
||||
//attempt to load version regardless
|
||||
if(Version.number == 0){
|
||||
@@ -78,9 +79,9 @@ public class CrashSender{
|
||||
|
||||
//attempt to close connections, if applicable
|
||||
try{
|
||||
netActive = Net.active();
|
||||
netServer = Net.server();
|
||||
Net.dispose();
|
||||
netActive = net.active();
|
||||
netServer = net.server();
|
||||
net.dispose();
|
||||
}catch(Throwable ignored){
|
||||
}
|
||||
|
||||
|
||||
@@ -1,15 +1,18 @@
|
||||
package io.anuke.mindustry.net;
|
||||
|
||||
import io.anuke.mindustry.game.*;
|
||||
|
||||
public class Host{
|
||||
public final String name;
|
||||
public final String address;
|
||||
public final String mapname;
|
||||
public final int wave;
|
||||
public final int players;
|
||||
public final int players, playerLimit;
|
||||
public final int version;
|
||||
public final String versionType;
|
||||
public final Gamemode mode;
|
||||
|
||||
public Host(String name, String address, String mapname, int wave, int players, int version, String versionType){
|
||||
public Host(String name, String address, String mapname, int wave, int players, int version, String versionType, Gamemode mode, int playerLimit){
|
||||
this.name = name;
|
||||
this.address = address;
|
||||
this.players = players;
|
||||
@@ -17,5 +20,7 @@ public class Host{
|
||||
this.wave = wave;
|
||||
this.version = version;
|
||||
this.versionType = versionType;
|
||||
this.playerLimit = playerLimit;
|
||||
this.mode = mode;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package io.anuke.mindustry.net;
|
||||
|
||||
import io.anuke.annotations.Annotations.*;
|
||||
import io.anuke.arc.*;
|
||||
import io.anuke.arc.collection.*;
|
||||
import io.anuke.arc.function.*;
|
||||
@@ -17,20 +18,26 @@ import static io.anuke.mindustry.Vars.*;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public class Net{
|
||||
private static boolean server;
|
||||
private static boolean active;
|
||||
private static boolean clientLoaded;
|
||||
private static Array<Object> packetQueue = new Array<>();
|
||||
private static ObjectMap<Class<?>, Consumer> clientListeners = new ObjectMap<>();
|
||||
private static ObjectMap<Class<?>, BiConsumer<Integer, Object>> serverListeners = new ObjectMap<>();
|
||||
private static ClientProvider clientProvider;
|
||||
private static ServerProvider serverProvider;
|
||||
private static IntMap<StreamBuilder> streams = new IntMap<>();
|
||||
private static final LZ4FastDecompressor decompressor = LZ4Factory.fastestInstance().fastDecompressor();
|
||||
private static final LZ4Compressor compressor = LZ4Factory.fastestInstance().fastCompressor();
|
||||
private boolean server;
|
||||
private boolean active;
|
||||
private boolean clientLoaded;
|
||||
private @Nullable StreamBuilder currentStream;
|
||||
|
||||
private final Array<Object> packetQueue = new Array<>();
|
||||
private final ObjectMap<Class<?>, Consumer> clientListeners = new ObjectMap<>();
|
||||
private final ObjectMap<Class<?>, BiConsumer<NetConnection, Object>> serverListeners = new ObjectMap<>();
|
||||
private final IntMap<StreamBuilder> streams = new IntMap<>();
|
||||
|
||||
private final NetProvider provider;
|
||||
private final LZ4FastDecompressor decompressor = LZ4Factory.fastestInstance().fastDecompressor();
|
||||
private final LZ4Compressor compressor = LZ4Factory.fastestInstance().fastCompressor();
|
||||
|
||||
public Net(NetProvider provider){
|
||||
this.provider = provider;
|
||||
}
|
||||
|
||||
/** Display a network error. Call on the graphics thread. */
|
||||
public static void showError(Throwable e){
|
||||
public void showError(Throwable e){
|
||||
|
||||
if(!headless){
|
||||
|
||||
@@ -39,7 +46,9 @@ public class Net{
|
||||
t = t.getCause();
|
||||
}
|
||||
|
||||
String error = t.getMessage() == null ? "" : t.getMessage().toLowerCase();
|
||||
String baseError = Strings.getFinalMesage(e);
|
||||
|
||||
String error = baseError == null ? "" : baseError.toLowerCase();
|
||||
String type = t.getClass().toString().toLowerCase();
|
||||
boolean isError = false;
|
||||
|
||||
@@ -56,18 +65,18 @@ public class Net{
|
||||
}else if(error.equals("alreadyconnected") || error.contains("connection is closed")){
|
||||
error = Core.bundle.get("error.alreadyconnected");
|
||||
}else if(!error.isEmpty()){
|
||||
error = Core.bundle.get("error.any") + "\n" + Strings.parseException(e, true);
|
||||
error = Core.bundle.get("error.any");
|
||||
isError = true;
|
||||
}
|
||||
|
||||
if(isError){
|
||||
ui.showError(Core.bundle.format("connectfail", error));
|
||||
ui.showException("$error.any", e);
|
||||
}else{
|
||||
ui.showText("", Core.bundle.format("connectfail", error));
|
||||
}
|
||||
ui.loadfrag.hide();
|
||||
|
||||
if(Net.client()){
|
||||
if(client()){
|
||||
netClient.disconnectQuietly();
|
||||
}
|
||||
}
|
||||
@@ -78,7 +87,7 @@ public class Net{
|
||||
/**
|
||||
* Sets the client loaded status, or whether it will recieve normal packets from the server.
|
||||
*/
|
||||
public static void setClientLoaded(boolean loaded){
|
||||
public void setClientLoaded(boolean loaded){
|
||||
clientLoaded = loaded;
|
||||
|
||||
if(loaded){
|
||||
@@ -91,13 +100,18 @@ public class Net{
|
||||
packetQueue.clear();
|
||||
}
|
||||
|
||||
public void setClientConnected(){
|
||||
active = true;
|
||||
server = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Connect to an address.
|
||||
*/
|
||||
public static void connect(String ip, int port, Runnable success){
|
||||
public void connect(String ip, int port, Runnable success){
|
||||
try{
|
||||
if(!active){
|
||||
clientProvider.connect(ip, port, success);
|
||||
provider.connectClient(ip, port, success);
|
||||
active = true;
|
||||
server = false;
|
||||
}else{
|
||||
@@ -111,8 +125,8 @@ public class Net{
|
||||
/**
|
||||
* Host a server at an address.
|
||||
*/
|
||||
public static void host(int port) throws IOException{
|
||||
serverProvider.host(port);
|
||||
public void host(int port) throws IOException{
|
||||
provider.hostServer(port);
|
||||
active = true;
|
||||
server = true;
|
||||
|
||||
@@ -122,32 +136,32 @@ public class Net{
|
||||
/**
|
||||
* Closes the server.
|
||||
*/
|
||||
public static void closeServer(){
|
||||
public void closeServer(){
|
||||
for(NetConnection con : getConnections()){
|
||||
Call.onKick(con.id, KickReason.serverClose);
|
||||
Call.onKick(con, KickReason.serverClose);
|
||||
}
|
||||
|
||||
serverProvider.close();
|
||||
provider.closeServer();
|
||||
server = false;
|
||||
active = false;
|
||||
}
|
||||
|
||||
public static void reset(){
|
||||
public void reset(){
|
||||
closeServer();
|
||||
netClient.disconnectNoReset();
|
||||
}
|
||||
|
||||
public static void disconnect(){
|
||||
clientProvider.disconnect();
|
||||
public void disconnect(){
|
||||
provider.disconnectClient();
|
||||
server = false;
|
||||
active = false;
|
||||
}
|
||||
|
||||
public static byte[] compressSnapshot(byte[] input){
|
||||
public byte[] compressSnapshot(byte[] input){
|
||||
return compressor.compress(input);
|
||||
}
|
||||
|
||||
public static byte[] decompressSnapshot(byte[] input, int size){
|
||||
public byte[] decompressSnapshot(byte[] input, int size){
|
||||
return decompressor.decompress(input, size);
|
||||
}
|
||||
|
||||
@@ -155,92 +169,64 @@ public class Net{
|
||||
* Starts discovering servers on a different thread.
|
||||
* Callback is run on the main libGDX thread.
|
||||
*/
|
||||
public static void discoverServers(Consumer<Host> cons, Runnable done){
|
||||
clientProvider.discover(cons, done);
|
||||
public void discoverServers(Consumer<Host> cons, Runnable done){
|
||||
provider.discoverServers(cons, done);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of all connections IDs.
|
||||
*/
|
||||
public static Iterable<NetConnection> getConnections(){
|
||||
return (Iterable<NetConnection>)serverProvider.getConnections();
|
||||
public Iterable<NetConnection> getConnections(){
|
||||
return (Iterable<NetConnection>)provider.getConnections();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a connection by ID
|
||||
*/
|
||||
public static NetConnection getConnection(int id){
|
||||
return serverProvider.getByID(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send an object to all connected clients, or to the server if this is a client.
|
||||
*/
|
||||
public static void send(Object object, SendMode mode){
|
||||
/** Send an object to all connected clients, or to the server if this is a client.*/
|
||||
public void send(Object object, SendMode mode){
|
||||
if(server){
|
||||
if(serverProvider != null) serverProvider.send(object, mode);
|
||||
for(NetConnection con : provider.getConnections()){
|
||||
con.send(object, mode);
|
||||
}
|
||||
}else{
|
||||
if(clientProvider != null) clientProvider.send(object, mode);
|
||||
provider.sendClient(object, mode);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send an object to a certain client. Server-side only
|
||||
*/
|
||||
public static void sendTo(int id, Object object, SendMode mode){
|
||||
serverProvider.sendTo(id, object, mode);
|
||||
/** Send an object to everyone EXCEPT a certain client. Server-side only.*/
|
||||
public void sendExcept(NetConnection except, Object object, SendMode mode){
|
||||
for(NetConnection con : getConnections()){
|
||||
if(con != except){
|
||||
con.send(object, mode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send an object to everyone EXCEPT certain client. Server-side only
|
||||
*/
|
||||
public static void sendExcept(int id, Object object, SendMode mode){
|
||||
serverProvider.sendExcept(id, object, mode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a stream to a specific client. Server-side only.
|
||||
*/
|
||||
public static void sendStream(int id, Streamable stream){
|
||||
serverProvider.sendStream(id, stream);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the net clientProvider, e.g. what handles sending, recieving and connecting to a server.
|
||||
*/
|
||||
public static void setClientProvider(ClientProvider provider){
|
||||
Net.clientProvider = provider;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the net serverProvider, e.g. what handles hosting a server.
|
||||
*/
|
||||
public static void setServerProvider(ServerProvider provider){
|
||||
Net.serverProvider = provider;
|
||||
public @Nullable StreamBuilder getCurrentStream(){
|
||||
return currentStream;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a client listener for when an object is recieved.
|
||||
*/
|
||||
public static <T> void handleClient(Class<T> type, Consumer<T> listener){
|
||||
public <T> void handleClient(Class<T> type, Consumer<T> listener){
|
||||
clientListeners.put(type, listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a server listener for when an object is recieved.
|
||||
*/
|
||||
public static <T> void handleServer(Class<T> type, BiConsumer<Integer, T> listener){
|
||||
serverListeners.put(type, (BiConsumer<Integer, Object>)listener);
|
||||
public <T> void handleServer(Class<T> type, BiConsumer<NetConnection, T> listener){
|
||||
serverListeners.put(type, (BiConsumer<NetConnection, Object>)listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Call to handle a packet being recieved for the client.
|
||||
*/
|
||||
public static void handleClientReceived(Object object){
|
||||
public void handleClientReceived(Object object){
|
||||
|
||||
if(object instanceof StreamBegin){
|
||||
StreamBegin b = (StreamBegin)object;
|
||||
streams.put(b.id, new StreamBuilder(b));
|
||||
streams.put(b.id, currentStream = new StreamBuilder(b));
|
||||
|
||||
}else if(object instanceof StreamChunk){
|
||||
StreamChunk c = (StreamChunk)object;
|
||||
StreamBuilder builder = streams.get(c.id);
|
||||
@@ -251,6 +237,7 @@ public class Net{
|
||||
if(builder.isDone()){
|
||||
streams.remove(builder.id);
|
||||
handleClientReceived(builder.build());
|
||||
currentStream = null;
|
||||
}
|
||||
}else if(clientListeners.get(object.getClass()) != null){
|
||||
|
||||
@@ -271,7 +258,7 @@ public class Net{
|
||||
/**
|
||||
* Call to handle a packet being recieved for the server.
|
||||
*/
|
||||
public static void handleServerReceived(int connection, Object object){
|
||||
public void handleServerReceived(NetConnection connection, Object object){
|
||||
|
||||
if(serverListeners.get(object.getClass()) != null){
|
||||
if(serverListeners.get(object.getClass()) != null)
|
||||
@@ -285,50 +272,33 @@ public class Net{
|
||||
/**
|
||||
* Pings a host in an new thread. If an error occured, failed() should be called with the exception.
|
||||
*/
|
||||
public static void pingHost(String address, int port, Consumer<Host> valid, Consumer<Exception> failed){
|
||||
clientProvider.pingHost(address, port, valid, failed);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update client ping.
|
||||
*/
|
||||
public static void updatePing(){
|
||||
clientProvider.updatePing();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the client ping. Only valid after updatePing().
|
||||
*/
|
||||
public static int getPing(){
|
||||
return server() ? 0 : clientProvider.getPing();
|
||||
public void pingHost(String address, int port, Consumer<Host> valid, Consumer<Exception> failed){
|
||||
provider.pingHost(address, port, valid, failed);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the net is active, e.g. whether this is a multiplayer game.
|
||||
*/
|
||||
public static boolean active(){
|
||||
public boolean active(){
|
||||
return active;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this is a server or not.
|
||||
*/
|
||||
public static boolean server(){
|
||||
public boolean server(){
|
||||
return server && active;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this is a client or not.
|
||||
*/
|
||||
public static boolean client(){
|
||||
public boolean client(){
|
||||
return !server && active;
|
||||
}
|
||||
|
||||
public static void dispose(){
|
||||
if(clientProvider != null) clientProvider.dispose();
|
||||
if(serverProvider != null) serverProvider.close();
|
||||
clientProvider = null;
|
||||
serverProvider = null;
|
||||
public void dispose(){
|
||||
provider.dispose();
|
||||
server = false;
|
||||
active = false;
|
||||
}
|
||||
@@ -337,98 +307,40 @@ public class Net{
|
||||
tcp, udp
|
||||
}
|
||||
|
||||
/** Client implementation. */
|
||||
public interface ClientProvider{
|
||||
/** Networking implementation. */
|
||||
public interface NetProvider{
|
||||
/** Connect to a server. */
|
||||
void connect(String ip, int port, Runnable success) throws IOException;
|
||||
void connectClient(String ip, int port, Runnable success) throws IOException;
|
||||
|
||||
/** Send an object to the server. */
|
||||
void send(Object object, SendMode mode);
|
||||
|
||||
/** Update the ping. Should be done every second or so. */
|
||||
void updatePing();
|
||||
|
||||
/** Get ping in milliseconds. Will only be valid after a call to updatePing. */
|
||||
int getPing();
|
||||
void sendClient(Object object, SendMode mode);
|
||||
|
||||
/** Disconnect from the server. */
|
||||
void disconnect();
|
||||
void disconnectClient();
|
||||
|
||||
/**
|
||||
* Discover servers. This should run the callback regardless of whether any servers are found. Should not block.
|
||||
* Callback should be run on libGDX main thread.
|
||||
* Callback should be run on the main thread.
|
||||
* @param done is the callback that should run after discovery.
|
||||
*/
|
||||
void discover(Consumer<Host> callback, Runnable done);
|
||||
void discoverServers(Consumer<Host> callback, Runnable done);
|
||||
|
||||
/** Ping a host. If an error occured, failed() should be called with the exception. */
|
||||
void pingHost(String address, int port, Consumer<Host> valid, Consumer<Exception> failed);
|
||||
|
||||
/** Close all connections. */
|
||||
void dispose();
|
||||
}
|
||||
|
||||
/** Server implementation. */
|
||||
public interface ServerProvider{
|
||||
/** Host a server at specified port. */
|
||||
void host(int port) throws IOException;
|
||||
|
||||
/** Sends a large stream of data to a specific client. */
|
||||
default void sendStream(int id, Streamable stream){
|
||||
NetConnection connection = getByID(id);
|
||||
if(connection == null) return;
|
||||
try{
|
||||
int cid;
|
||||
StreamBegin begin = new StreamBegin();
|
||||
begin.total = stream.stream.available();
|
||||
begin.type = Registrator.getID(stream.getClass());
|
||||
connection.send(begin, SendMode.tcp);
|
||||
cid = begin.id;
|
||||
|
||||
while(stream.stream.available() > 0){
|
||||
byte[] bytes = new byte[Math.min(512, stream.stream.available())];
|
||||
stream.stream.read(bytes);
|
||||
|
||||
StreamChunk chunk = new StreamChunk();
|
||||
chunk.id = cid;
|
||||
chunk.data = bytes;
|
||||
connection.send(chunk, SendMode.tcp);
|
||||
}
|
||||
}catch(IOException e){
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
default void send(Object object, SendMode mode){
|
||||
for(NetConnection con : getConnections()){
|
||||
con.send(object, mode);
|
||||
}
|
||||
}
|
||||
|
||||
default void sendTo(int id, Object object, SendMode mode){
|
||||
NetConnection conn = getByID(id);
|
||||
if(conn == null){
|
||||
Log.err("Failed to find connection with ID {0}.", id);
|
||||
return;
|
||||
}
|
||||
conn.send(object, mode);
|
||||
}
|
||||
|
||||
default void sendExcept(int id, Object object, SendMode mode){
|
||||
for(NetConnection con : getConnections()){
|
||||
if(con.id != id){
|
||||
con.send(object, mode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Close the server connection. */
|
||||
void close();
|
||||
void hostServer(int port) throws IOException;
|
||||
|
||||
/** Return all connected users. */
|
||||
Iterable<? extends NetConnection> getConnections();
|
||||
|
||||
/** Returns a connection by ID. */
|
||||
NetConnection getByID(int id);
|
||||
/** Close the server connection. */
|
||||
void closeServer();
|
||||
|
||||
/** Close all connections. */
|
||||
default void dispose(){
|
||||
disconnectClient();
|
||||
closeServer();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,35 +1,77 @@
|
||||
package io.anuke.mindustry.net;
|
||||
|
||||
import io.anuke.mindustry.net.Net.SendMode;
|
||||
import io.anuke.annotations.Annotations.*;
|
||||
import io.anuke.arc.util.*;
|
||||
import io.anuke.mindustry.entities.type.*;
|
||||
import io.anuke.mindustry.gen.*;
|
||||
import io.anuke.mindustry.net.Administration.*;
|
||||
import io.anuke.mindustry.net.Net.*;
|
||||
import io.anuke.mindustry.net.Packets.*;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
import static io.anuke.mindustry.Vars.netServer;
|
||||
|
||||
public abstract class NetConnection{
|
||||
private static int lastID;
|
||||
|
||||
public final int id;
|
||||
public final String address;
|
||||
|
||||
public boolean modclient;
|
||||
public boolean mobile;
|
||||
public boolean mobile, modclient;
|
||||
public @Nullable Player player;
|
||||
|
||||
/** ID of last recieved client snapshot. */
|
||||
public int lastRecievedClientSnapshot = -1;
|
||||
/** Timestamp of last recieved snapshot. */
|
||||
public long lastRecievedClientTime;
|
||||
|
||||
public boolean hasConnected = false;
|
||||
public boolean hasBegunConnecting = false;
|
||||
public boolean hasConnected, hasBegunConnecting;
|
||||
public float viewWidth, viewHeight, viewX, viewY;
|
||||
|
||||
/** Assigns this connection a unique ID. No two connections will ever have the same ID.*/
|
||||
public NetConnection(String address){
|
||||
this.id = lastID++;
|
||||
this.address = address;
|
||||
}
|
||||
|
||||
public void kick(KickReason reason){
|
||||
Log.info("Kicking connection {0}; Reason: {1}", address, reason.name());
|
||||
|
||||
if(player != null && (reason == KickReason.kick || reason == KickReason.banned || reason == KickReason.vote) && player.uuid != null){
|
||||
PlayerInfo info = netServer.admins.getInfo(player.uuid);
|
||||
info.timesKicked++;
|
||||
info.lastKicked = Math.max(Time.millis(), info.lastKicked);
|
||||
}
|
||||
|
||||
Call.onKick(this, reason);
|
||||
|
||||
Time.runTask(2f, this::close);
|
||||
|
||||
netServer.admins.save();
|
||||
}
|
||||
|
||||
public boolean isConnected(){
|
||||
return true;
|
||||
}
|
||||
|
||||
public void sendStream(Streamable stream){
|
||||
try{
|
||||
int cid;
|
||||
StreamBegin begin = new StreamBegin();
|
||||
begin.total = stream.stream.available();
|
||||
begin.type = Registrator.getID(stream.getClass());
|
||||
send(begin, SendMode.tcp);
|
||||
cid = begin.id;
|
||||
|
||||
while(stream.stream.available() > 0){
|
||||
byte[] bytes = new byte[Math.min(512, stream.stream.available())];
|
||||
stream.stream.read(bytes);
|
||||
|
||||
StreamChunk chunk = new StreamChunk();
|
||||
chunk.id = cid;
|
||||
chunk.data = bytes;
|
||||
send(chunk, SendMode.tcp);
|
||||
}
|
||||
}catch(IOException e){
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public abstract void send(Object object, SendMode mode);
|
||||
|
||||
public abstract void close();
|
||||
|
||||
@@ -1,17 +1,15 @@
|
||||
package io.anuke.mindustry.net;
|
||||
|
||||
import io.anuke.arc.Core;
|
||||
import io.anuke.arc.util.Time;
|
||||
import io.anuke.mindustry.entities.Entities;
|
||||
import io.anuke.mindustry.entities.type.Player;
|
||||
import io.anuke.arc.*;
|
||||
import io.anuke.arc.util.*;
|
||||
import io.anuke.mindustry.entities.type.*;
|
||||
import io.anuke.mindustry.game.*;
|
||||
import io.anuke.mindustry.io.JsonIO;
|
||||
import io.anuke.mindustry.io.SaveIO;
|
||||
import io.anuke.mindustry.io.*;
|
||||
import io.anuke.mindustry.maps.Map;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Arrays;
|
||||
import java.nio.*;
|
||||
import java.util.*;
|
||||
|
||||
import static io.anuke.mindustry.Vars.*;
|
||||
|
||||
@@ -71,8 +69,9 @@ public class NetworkIO{
|
||||
buffer.putInt(state.wave);
|
||||
buffer.putInt(Version.build);
|
||||
writeString(buffer, Version.type);
|
||||
//TODO additional information:
|
||||
// - gamemode ID/name (just pick the closest one?)
|
||||
|
||||
buffer.put((byte)Gamemode.bestFit(state.rules).ordinal());
|
||||
buffer.putInt(netServer.admins.getPlayerLimit());
|
||||
return buffer;
|
||||
}
|
||||
|
||||
@@ -83,13 +82,15 @@ public class NetworkIO{
|
||||
int wave = buffer.getInt();
|
||||
int version = buffer.getInt();
|
||||
String vertype = readString(buffer);
|
||||
Gamemode gamemode = Gamemode.all[buffer.get()];
|
||||
int limit = buffer.getInt();
|
||||
|
||||
return new Host(host, hostAddress, map, wave, players, version, vertype);
|
||||
return new Host(host, hostAddress, map, wave, players, version, vertype, gamemode, limit);
|
||||
}
|
||||
|
||||
private static void writeString(ByteBuffer buffer, String string, int maxlen){
|
||||
byte[] bytes = string.getBytes(charset);
|
||||
//truncating this way may lead to wierd encoding errors at the ends of strings...
|
||||
//todo truncating this way may lead to wierd encoding errors at the ends of strings...
|
||||
if(bytes.length > maxlen){
|
||||
bytes = Arrays.copyOfRange(bytes, 0, maxlen);
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ public class Packets{
|
||||
|
||||
public enum KickReason{
|
||||
kick, clientOutdated, serverOutdated, banned, gameover(true), recentKick,
|
||||
nameInUse, idInUse, nameEmpty, customClient, serverClose, vote, typeMismatch, whitelist;
|
||||
nameInUse, idInUse, nameEmpty, customClient, serverClose, vote, typeMismatch, whitelist, playerLimit;
|
||||
|
||||
public final boolean quiet;
|
||||
|
||||
@@ -41,7 +41,6 @@ public class Packets{
|
||||
}
|
||||
|
||||
public static class Connect implements Packet{
|
||||
public int id;
|
||||
public String addressTCP;
|
||||
|
||||
@Override
|
||||
@@ -51,7 +50,6 @@ public class Packets{
|
||||
}
|
||||
|
||||
public static class Disconnect implements Packet{
|
||||
public int id;
|
||||
public String reason;
|
||||
|
||||
@Override
|
||||
|
||||
@@ -16,13 +16,16 @@ public class Streamable implements Packet{
|
||||
public final int id;
|
||||
public final byte type;
|
||||
public final int total;
|
||||
public final ByteArrayOutputStream stream;
|
||||
public final ByteArrayOutputStream stream = new ByteArrayOutputStream();
|
||||
|
||||
public StreamBuilder(StreamBegin begin){
|
||||
id = begin.id;
|
||||
type = begin.type;
|
||||
total = begin.total;
|
||||
stream = new ByteArrayOutputStream();
|
||||
}
|
||||
|
||||
public float progress(){
|
||||
return (float)stream.size() / total;
|
||||
}
|
||||
|
||||
public void add(byte[] bytes){
|
||||
|
||||
@@ -1,9 +1,16 @@
|
||||
package io.anuke.mindustry.plugin;
|
||||
|
||||
import io.anuke.arc.files.*;
|
||||
import io.anuke.arc.util.*;
|
||||
import io.anuke.mindustry.*;
|
||||
|
||||
public abstract class Plugin{
|
||||
|
||||
/** @return the config file for this plugin, as the file 'plugins/[plugin-name]/config.json'.*/
|
||||
public FileHandle getConfig(){
|
||||
return Vars.plugins.getConfig(this);
|
||||
}
|
||||
|
||||
/** Called after all plugins have been created and commands have been registered.*/
|
||||
public void init(){
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user