Merge
This commit is contained in:
177
core/src/io/anuke/mindustry/ClientLauncher.java
Normal file
177
core/src/io/anuke/mindustry/ClientLauncher.java
Normal file
@@ -0,0 +1,177 @@
|
||||
package io.anuke.mindustry;
|
||||
|
||||
import io.anuke.arc.*;
|
||||
import io.anuke.arc.assets.*;
|
||||
import io.anuke.arc.graphics.*;
|
||||
import io.anuke.arc.graphics.g2d.*;
|
||||
import io.anuke.arc.math.*;
|
||||
import io.anuke.arc.scene.ui.layout.*;
|
||||
import io.anuke.arc.util.*;
|
||||
import io.anuke.mindustry.core.*;
|
||||
import io.anuke.mindustry.game.*;
|
||||
import io.anuke.mindustry.game.EventType.*;
|
||||
import io.anuke.mindustry.gen.*;
|
||||
import io.anuke.mindustry.graphics.*;
|
||||
import io.anuke.mindustry.maps.*;
|
||||
|
||||
import static io.anuke.arc.Core.*;
|
||||
import static io.anuke.mindustry.Vars.*;
|
||||
|
||||
public class ClientLauncher extends ApplicationCore{
|
||||
private static final int loadingFPS = 20;
|
||||
|
||||
private float smoothProgress;
|
||||
private long lastTime;
|
||||
private long beginTime;
|
||||
private boolean finished = false;
|
||||
|
||||
@Override
|
||||
public void setup(){
|
||||
Log.setUseColors(false);
|
||||
beginTime = Time.millis();
|
||||
|
||||
Time.setDeltaProvider(() -> {
|
||||
float result = Core.graphics.getDeltaTime() * 60f;
|
||||
return (Float.isNaN(result) || Float.isInfinite(result)) ? 1f : Mathf.clamp(result, 0.0001f, 60f / 10f);
|
||||
});
|
||||
|
||||
batch = new SpriteBatch();
|
||||
assets = new AssetManager();
|
||||
assets.setLoader(Texture.class, "." + mapExtension, new MapPreviewLoader());
|
||||
atlas = TextureAtlas.blankAtlas();
|
||||
|
||||
UI.loadDefaultFont();
|
||||
UI.loadSystemCursors();
|
||||
|
||||
//1. bundles
|
||||
//2. rest of vars
|
||||
assets.load(new Vars());
|
||||
assets.load(new AssetDescriptor<>("sprites/sprites.atlas", TextureAtlas.class)).loaded = t -> atlas = (TextureAtlas)t;
|
||||
|
||||
assets.loadRun("maps", Map.class, () -> {
|
||||
maps.loadPreviews();
|
||||
});
|
||||
|
||||
Musics.load();
|
||||
Sounds.load();
|
||||
|
||||
assets.loadRun("contentcreate", Content.class, () -> {
|
||||
content.createContent();
|
||||
content.loadColors();
|
||||
});
|
||||
|
||||
add(logic = new Logic());
|
||||
add(control = new Control());
|
||||
add(renderer = new Renderer());
|
||||
add(ui = new UI());
|
||||
add(netServer = new NetServer());
|
||||
add(netClient = new NetClient());
|
||||
|
||||
assets.loadRun("contentinit", ContentLoader.class, () -> {
|
||||
content.init();
|
||||
content.load();
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(ApplicationListener module){
|
||||
super.add(module);
|
||||
|
||||
//autoload modules when necessary
|
||||
if(module instanceof Loadable){
|
||||
assets.load((Loadable)module);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resize(int width, int height){
|
||||
super.resize(width, height);
|
||||
|
||||
if(!assets.isFinished()){
|
||||
Draw.proj().setOrtho(0, 0, width, height);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(){
|
||||
if(!finished){
|
||||
drawLoading();
|
||||
if(assets.update(1000 / loadingFPS)){
|
||||
Log.info("Total time to load: {0}", Time.timeSinceMillis(beginTime));
|
||||
for(ApplicationListener listener : modules){
|
||||
listener.init();
|
||||
}
|
||||
finished = true;
|
||||
Events.fire(new ClientLoadEvent());
|
||||
}
|
||||
}else{
|
||||
super.update();
|
||||
}
|
||||
|
||||
int targetfps = Core.settings.getInt("fpscap", 120);
|
||||
|
||||
if(targetfps > 0 && targetfps <= 240){
|
||||
long target = (1000 * 1000000) / targetfps; //target in nanos
|
||||
long elapsed = Time.timeSinceNanos(lastTime);
|
||||
if(elapsed < target){
|
||||
try{
|
||||
Thread.sleep((target - elapsed) / 1000000, (int)((target - elapsed) % 1000000));
|
||||
}catch(InterruptedException ignored){
|
||||
//ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lastTime = Time.nanos();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(){
|
||||
setup();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resume(){
|
||||
if(finished){
|
||||
super.resume();
|
||||
}
|
||||
}
|
||||
|
||||
void drawLoading(){
|
||||
smoothProgress = Mathf.lerpDelta(smoothProgress, assets.getProgress(), 0.1f);
|
||||
|
||||
Core.graphics.clear(Pal.darkerGray);
|
||||
Draw.proj().setOrtho(0, 0, Core.graphics.getWidth(), Core.graphics.getHeight());
|
||||
|
||||
float height = UnitScl.dp.scl(50f);
|
||||
|
||||
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);
|
||||
Fill.rect(graphics.getWidth()/2f, graphics.getHeight()/2f, w, height);
|
||||
|
||||
Draw.color(Pal.accent);
|
||||
Fill.crect(graphics.getWidth()/2f-w/2f, graphics.getHeight()/2f - height/2f, w * smoothProgress, height);
|
||||
|
||||
for(int i : Mathf.signs){
|
||||
Fill.tri(graphics.getWidth()/2f + w/2f*i, graphics.getHeight()/2f + height/2f, graphics.getWidth()/2f + w/2f*i, graphics.getHeight()/2f - height/2f, graphics.getWidth()/2f + w/2f*i + height/2f*i, graphics.getHeight()/2f);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
Draw.flush();
|
||||
}
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
package io.anuke.mindustry;
|
||||
|
||||
import io.anuke.arc.*;
|
||||
import io.anuke.arc.math.*;
|
||||
import io.anuke.arc.util.*;
|
||||
import io.anuke.mindustry.core.*;
|
||||
import io.anuke.mindustry.game.EventType.*;
|
||||
import io.anuke.mindustry.gen.*;
|
||||
import io.anuke.mindustry.io.*;
|
||||
|
||||
import static io.anuke.mindustry.Vars.*;
|
||||
|
||||
public class Mindustry extends ApplicationCore{
|
||||
private long lastTime;
|
||||
|
||||
@Override
|
||||
public void setup(){
|
||||
Time.setDeltaProvider(() -> {
|
||||
float result = Core.graphics.getDeltaTime() * 60f;
|
||||
return (Float.isNaN(result) || Float.isInfinite(result)) ? 1f : Mathf.clamp(result, 0.0001f, 60f / 10f);
|
||||
});
|
||||
|
||||
Time.mark();
|
||||
UI.loadSystemCursors();
|
||||
|
||||
Vars.init();
|
||||
Log.setUseColors(false);
|
||||
BundleLoader.load();
|
||||
Musics.load();
|
||||
Sounds.load();
|
||||
|
||||
content.load();
|
||||
content.loadColors();
|
||||
|
||||
add(logic = new Logic());
|
||||
add(world = new World());
|
||||
add(control = new Control());
|
||||
add(renderer = new Renderer());
|
||||
add(ui = new UI());
|
||||
add(netServer = new NetServer());
|
||||
add(netClient = new NetClient());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(){
|
||||
super.update();
|
||||
|
||||
int targetfps = Core.settings.getInt("fpscap", 120);
|
||||
|
||||
if(targetfps > 0 && targetfps <= 240){
|
||||
long target = (1000 * 1000000) / targetfps; //target in nanos
|
||||
long elapsed = Time.timeSinceNanos(lastTime);
|
||||
if(elapsed < target){
|
||||
try{
|
||||
Thread.sleep((target - elapsed) / 1000000, (int)((target - elapsed) % 1000000));
|
||||
}catch(InterruptedException ignored){
|
||||
//ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lastTime = Time.nanos();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(){
|
||||
super.init();
|
||||
|
||||
Log.info("Time to load [total]: {0}", Time.elapsed());
|
||||
Events.fire(new GameLoadEvent());
|
||||
}
|
||||
}
|
||||
@@ -1,35 +1,36 @@
|
||||
package io.anuke.mindustry;
|
||||
|
||||
import io.anuke.arc.Application.ApplicationType;
|
||||
import io.anuke.arc.Core;
|
||||
import io.anuke.arc.files.FileHandle;
|
||||
import io.anuke.arc.graphics.Color;
|
||||
import io.anuke.arc.util.Structs;
|
||||
import io.anuke.arc.Application.*;
|
||||
import io.anuke.arc.*;
|
||||
import io.anuke.arc.assets.*;
|
||||
import io.anuke.arc.files.*;
|
||||
import io.anuke.arc.graphics.*;
|
||||
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.Bullet;
|
||||
import io.anuke.mindustry.entities.effect.Fire;
|
||||
import io.anuke.mindustry.entities.effect.Puddle;
|
||||
import io.anuke.mindustry.entities.impl.EffectEntity;
|
||||
import io.anuke.mindustry.entities.traits.DrawTrait;
|
||||
import io.anuke.mindustry.entities.traits.SyncTrait;
|
||||
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.*;
|
||||
import io.anuke.mindustry.gen.Serialization;
|
||||
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.world.blocks.defense.ForceProjector.ShieldEntity;
|
||||
import io.anuke.mindustry.world.blocks.defense.ForceProjector.*;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Arrays;
|
||||
import java.util.Locale;
|
||||
import java.nio.charset.*;
|
||||
import java.util.*;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public class Vars{
|
||||
public class Vars implements Loadable{
|
||||
/** Whether to load locales.*/
|
||||
public static boolean loadLocales = true;
|
||||
/** IO buffer size. */
|
||||
public static final int bufferSize = 8192;
|
||||
/** global charset */
|
||||
/** global charset, since Android doesn't support the Charsets class */
|
||||
public static final Charset charset = Charset.forName("UTF-8");
|
||||
/** main application name, capitalized */
|
||||
public static final String appName = "Mindustry";
|
||||
@@ -116,10 +117,14 @@ public class Vars{
|
||||
public static FileHandle screenshotDirectory;
|
||||
/** data subdirectory used for custom mmaps */
|
||||
public static FileHandle customMapDirectory;
|
||||
/** data subdirectory used for custom mmaps */
|
||||
public static FileHandle mapPreviewDirectory;
|
||||
/** tmp subdirectory for map conversion */
|
||||
public static FileHandle tmpDirectory;
|
||||
/** data subdirectory used for saves */
|
||||
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 */
|
||||
@@ -137,14 +142,20 @@ public class Vars{
|
||||
public static DefaultWaves defaultWaves;
|
||||
public static LoopControl loops;
|
||||
|
||||
public static World world;
|
||||
public static Maps maps;
|
||||
public static WaveSpawner spawner;
|
||||
public static BlockIndexer indexer;
|
||||
public static Pathfinder pathfinder;
|
||||
|
||||
public static Control control;
|
||||
public static Logic logic;
|
||||
public static Renderer renderer;
|
||||
public static UI ui;
|
||||
public static World world;
|
||||
public static NetServer netServer;
|
||||
public static NetClient netClient;
|
||||
|
||||
public static Entities entities;
|
||||
public static EntityGroup<Player> playerGroup;
|
||||
public static EntityGroup<TileEntity> tileGroup;
|
||||
public static EntityGroup<Bullet> bulletGroup;
|
||||
@@ -158,6 +169,12 @@ public class Vars{
|
||||
/** all local players, currently only has one player. may be used for local co-op in the future */
|
||||
public static Player player;
|
||||
|
||||
@Override
|
||||
public void loadAsync(){
|
||||
loadSettings();
|
||||
init();
|
||||
}
|
||||
|
||||
public static void init(){
|
||||
Serialization.init();
|
||||
|
||||
@@ -180,29 +197,32 @@ public class Vars{
|
||||
Version.init();
|
||||
|
||||
content = new ContentLoader();
|
||||
if(!headless){
|
||||
content.setVerbose();
|
||||
}
|
||||
|
||||
loops = new LoopControl();
|
||||
defaultWaves = new DefaultWaves();
|
||||
collisions = new EntityCollisions();
|
||||
world = new World();
|
||||
|
||||
playerGroup = Entities.addGroup(Player.class).enableMapping();
|
||||
tileGroup = Entities.addGroup(TileEntity.class, false);
|
||||
bulletGroup = Entities.addGroup(Bullet.class).enableMapping();
|
||||
effectGroup = Entities.addGroup(EffectEntity.class, false);
|
||||
groundEffectGroup = Entities.addGroup(DrawTrait.class, false);
|
||||
puddleGroup = Entities.addGroup(Puddle.class).enableMapping();
|
||||
shieldGroup = Entities.addGroup(ShieldEntity.class, false);
|
||||
fireGroup = Entities.addGroup(Fire.class).enableMapping();
|
||||
maps = new Maps();
|
||||
spawner = new WaveSpawner();
|
||||
indexer = new BlockIndexer();
|
||||
pathfinder = new Pathfinder();
|
||||
|
||||
entities = new Entities();
|
||||
playerGroup = entities.add(Player.class).enableMapping();
|
||||
tileGroup = entities.add(TileEntity.class, false);
|
||||
bulletGroup = entities.add(Bullet.class).enableMapping();
|
||||
effectGroup = entities.add(EffectEntity.class, false);
|
||||
groundEffectGroup = entities.add(DrawTrait.class, false);
|
||||
puddleGroup = entities.add(Puddle.class).enableMapping();
|
||||
shieldGroup = entities.add(ShieldEntity.class, false);
|
||||
fireGroup = entities.add(Fire.class).enableMapping();
|
||||
unitGroups = new EntityGroup[Team.all.length];
|
||||
|
||||
for(Team team : Team.all){
|
||||
unitGroups[team.ordinal()] = Entities.addGroup(BaseUnit.class).enableMapping();
|
||||
unitGroups[team.ordinal()] = entities.add(BaseUnit.class).enableMapping();
|
||||
}
|
||||
|
||||
for(EntityGroup<?> group : Entities.getAllGroups()){
|
||||
for(EntityGroup<?> group : entities.all()){
|
||||
group.setRemoveListener(entity -> {
|
||||
if(entity instanceof SyncTrait && Net.client()){
|
||||
netClient.addRemovedEntity((entity).getID());
|
||||
@@ -217,16 +237,64 @@ public class Vars{
|
||||
ios = Core.app.getType() == ApplicationType.iOS;
|
||||
android = Core.app.getType() == ApplicationType.Android;
|
||||
|
||||
dataDirectory = Core.settings.getDataDirectory();
|
||||
screenshotDirectory = dataDirectory.child("screenshots/");
|
||||
customMapDirectory = dataDirectory.child("maps/");
|
||||
mapPreviewDirectory = dataDirectory.child("previews/");
|
||||
saveDirectory = dataDirectory.child("saves/");
|
||||
tmpDirectory = dataDirectory.child("tmp/");
|
||||
pluginDirectory = dataDirectory.child("plugins/");
|
||||
|
||||
maps.load();
|
||||
}
|
||||
|
||||
public static void loadSettings(){
|
||||
Core.settings.setAppName(appName);
|
||||
|
||||
if(steam){
|
||||
Core.settings.setDataDirectory(Core.files.local("saves/"));
|
||||
}
|
||||
|
||||
dataDirectory = Core.settings.getDataDirectory();
|
||||
screenshotDirectory = dataDirectory.child("screenshots/");
|
||||
customMapDirectory = dataDirectory.child("maps/");
|
||||
saveDirectory = dataDirectory.child("saves/");
|
||||
tmpDirectory = dataDirectory.child("tmp/");
|
||||
Core.settings.defaults("locale", "default");
|
||||
Core.keybinds.setDefaults(Binding.values());
|
||||
Core.settings.load();
|
||||
|
||||
if(!loadLocales) return;
|
||||
|
||||
try{
|
||||
//try loading external bundle
|
||||
FileHandle handle = Core.files.local("bundle");
|
||||
|
||||
Locale locale = Locale.ENGLISH;
|
||||
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."));
|
||||
}
|
||||
}catch(Throwable e){
|
||||
//no external bundle found
|
||||
|
||||
FileHandle handle = Core.files.internal("bundles/bundle");
|
||||
|
||||
Locale locale;
|
||||
String loc = Core.settings.getString("locale");
|
||||
if(loc.equals("default")){
|
||||
locale = Locale.getDefault();
|
||||
}else{
|
||||
Locale lastLocale;
|
||||
if(loc.contains("_")){
|
||||
String[] split = loc.split("_");
|
||||
lastLocale = new Locale(split[0], split[1]);
|
||||
}else{
|
||||
lastLocale = new Locale(loc);
|
||||
}
|
||||
|
||||
locale = lastLocale;
|
||||
}
|
||||
|
||||
Locale.setDefault(locale);
|
||||
Core.bundle = I18NBundle.createBundle(handle, locale);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ public class BlockIndexer{
|
||||
private final static int quadrantSize = 16;
|
||||
|
||||
/** Set of all ores that are being scanned. */
|
||||
private final ObjectSet<Item> scanOres = ObjectSet.with(Item.getAllOres().toArray(Item.class));
|
||||
private final ObjectSet<Item> scanOres = new ObjectSet<>();
|
||||
private final ObjectSet<Item> itemSet = new ObjectSet<>();
|
||||
/** Stores all ore quadtrants on the map. */
|
||||
private ObjectMap<Item, ObjectSet<Tile>> ores;
|
||||
@@ -57,6 +57,8 @@ public class BlockIndexer{
|
||||
});
|
||||
|
||||
Events.on(WorldLoadEvent.class, event -> {
|
||||
scanOres.clear();
|
||||
scanOres.addAll(Item.getAllOres());
|
||||
damagedTiles = new ObjectSet[Team.all.length];
|
||||
flagMap = new ObjectSet[Team.all.length][BlockFlag.all.length];
|
||||
|
||||
|
||||
@@ -15,8 +15,7 @@ import io.anuke.mindustry.world.Pos;
|
||||
import io.anuke.mindustry.world.Tile;
|
||||
import io.anuke.mindustry.world.meta.BlockFlag;
|
||||
|
||||
import static io.anuke.mindustry.Vars.state;
|
||||
import static io.anuke.mindustry.Vars.world;
|
||||
import static io.anuke.mindustry.Vars.*;
|
||||
|
||||
public class Pathfinder{
|
||||
private static final long maxUpdate = Time.millisToNanos(4);
|
||||
@@ -116,7 +115,7 @@ public class Pathfinder{
|
||||
path.lastSearchTime = Time.millis();
|
||||
|
||||
//add all targets to the frontier
|
||||
for(Tile other : world.indexer.getEnemy(team, BlockFlag.target)){
|
||||
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());
|
||||
|
||||
@@ -1,16 +1,14 @@
|
||||
package io.anuke.mindustry.content;
|
||||
|
||||
import io.anuke.arc.Core;
|
||||
import io.anuke.arc.function.BooleanProvider;
|
||||
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.Mathf;
|
||||
import io.anuke.arc.util.Tmp;
|
||||
import io.anuke.mindustry.Vars;
|
||||
import io.anuke.mindustry.entities.Damage;
|
||||
import io.anuke.mindustry.entities.bullet.Bullet;
|
||||
import io.anuke.mindustry.entities.bullet.BulletType;
|
||||
import io.anuke.mindustry.game.ContentList;
|
||||
import io.anuke.arc.math.*;
|
||||
import io.anuke.arc.util.*;
|
||||
import io.anuke.mindustry.*;
|
||||
import io.anuke.mindustry.entities.*;
|
||||
import io.anuke.mindustry.entities.bullet.*;
|
||||
import io.anuke.mindustry.game.*;
|
||||
import io.anuke.mindustry.gen.*;
|
||||
import io.anuke.mindustry.graphics.*;
|
||||
import io.anuke.mindustry.type.*;
|
||||
@@ -24,16 +22,13 @@ import io.anuke.mindustry.world.blocks.production.*;
|
||||
import io.anuke.mindustry.world.blocks.sandbox.*;
|
||||
import io.anuke.mindustry.world.blocks.storage.*;
|
||||
import io.anuke.mindustry.world.blocks.units.*;
|
||||
import io.anuke.mindustry.world.consumers.ConsumeLiquidFilter;
|
||||
import io.anuke.mindustry.world.meta.Attribute;
|
||||
import io.anuke.mindustry.world.modules.LiquidModule;
|
||||
import io.anuke.mindustry.world.consumers.*;
|
||||
import io.anuke.mindustry.world.meta.*;
|
||||
import io.anuke.mindustry.world.modules.*;
|
||||
|
||||
import static io.anuke.mindustry.Vars.state;
|
||||
import static io.anuke.mindustry.Vars.world;
|
||||
import static io.anuke.mindustry.Vars.*;
|
||||
|
||||
public class Blocks implements ContentList{
|
||||
public static final BooleanProvider padVisible = () -> state.rules.attackMode || state.rules.pvp || state.isEditor();
|
||||
|
||||
public static Block
|
||||
|
||||
//environment
|
||||
@@ -79,7 +74,7 @@ public class Blocks implements ContentList{
|
||||
duo, scatter, scorch, hail, arc, wave, lancer, swarmer, salvo, fuse, ripple, cyclone, spectre, meltdown,
|
||||
|
||||
//units
|
||||
draugFactory, spiritFactory, phantomFactory, wraithFactory, ghoulFactory, revenantFactory, daggerFactory, crawlerFactory, titanFactory,
|
||||
commandCenter, draugFactory, spiritFactory, phantomFactory, wraithFactory, ghoulFactory, revenantFactory, daggerFactory, crawlerFactory, titanFactory,
|
||||
fortressFactory, repairPoint,
|
||||
|
||||
//upgrades
|
||||
@@ -1648,17 +1643,23 @@ public class Blocks implements ContentList{
|
||||
consumes.items(new ItemStack(Items.silicon, 30), new ItemStack(Items.lead, 20), new ItemStack(Items.titanium, 10));
|
||||
}};
|
||||
|
||||
wraithFactory = new UnitFactory("wraith-factory"){{
|
||||
requirements(Category.units, padVisible, ItemStack.with(Items.titanium, 30, Items.lead, 40, Items.silicon, 45));
|
||||
type = UnitTypes.wraith;
|
||||
produceTime = 750;
|
||||
commandCenter = new CommandCenter("command-center"){{
|
||||
requirements(Category.units, ItemStack.with(Items.copper, 200, Items.lead, 250, Items.silicon, 250, Items.graphite, 100));
|
||||
size = 2;
|
||||
consumes.power(0.6f);
|
||||
health = size * size * 55;
|
||||
}};
|
||||
|
||||
wraithFactory = new UnitFactory("wraith-factory"){{
|
||||
requirements(Category.units, ItemStack.with(Items.titanium, 30, Items.lead, 40, Items.silicon, 45));
|
||||
type = UnitTypes.wraith;
|
||||
produceTime = 700;
|
||||
size = 2;
|
||||
consumes.power(0.5f);
|
||||
consumes.items(new ItemStack(Items.silicon, 10), new ItemStack(Items.titanium, 5));
|
||||
}};
|
||||
|
||||
ghoulFactory = new UnitFactory("ghoul-factory"){{
|
||||
requirements(Category.units, padVisible, ItemStack.with(Items.titanium, 75, Items.lead, 65, Items.silicon, 110));
|
||||
requirements(Category.units, ItemStack.with(Items.titanium, 75, Items.lead, 65, Items.silicon, 110));
|
||||
type = UnitTypes.ghoul;
|
||||
produceTime = 1150;
|
||||
size = 3;
|
||||
@@ -1667,7 +1668,7 @@ public class Blocks implements ContentList{
|
||||
}};
|
||||
|
||||
revenantFactory = new UnitFactory("revenant-factory"){{
|
||||
requirements(Category.units, padVisible, ItemStack.with(Items.plastanium, 50, Items.titanium, 150, Items.lead, 150, Items.silicon, 200));
|
||||
requirements(Category.units, ItemStack.with(Items.plastanium, 50, Items.titanium, 150, Items.lead, 150, Items.silicon, 200));
|
||||
type = UnitTypes.revenant;
|
||||
produceTime = 2000;
|
||||
size = 4;
|
||||
@@ -1676,7 +1677,7 @@ public class Blocks implements ContentList{
|
||||
}};
|
||||
|
||||
daggerFactory = new UnitFactory("dagger-factory"){{
|
||||
requirements(Category.units, padVisible, ItemStack.with(Items.lead, 55, Items.silicon, 35));
|
||||
requirements(Category.units, ItemStack.with(Items.lead, 55, Items.silicon, 35));
|
||||
type = UnitTypes.dagger;
|
||||
produceTime = 850;
|
||||
size = 2;
|
||||
@@ -1685,17 +1686,17 @@ public class Blocks implements ContentList{
|
||||
}};
|
||||
|
||||
crawlerFactory = new UnitFactory("crawler-factory"){{
|
||||
requirements(Category.units, padVisible, ItemStack.with(Items.lead, 25, Items.silicon, 30));
|
||||
requirements(Category.units, ItemStack.with(Items.lead, 45, Items.silicon, 30));
|
||||
type = UnitTypes.crawler;
|
||||
produceTime = 250;
|
||||
produceTime = 300;
|
||||
size = 2;
|
||||
maxSpawn = 8;
|
||||
consumes.power(0.4f);
|
||||
consumes.items(new ItemStack(Items.coal, 5));
|
||||
maxSpawn = 6;
|
||||
consumes.power(0.5f);
|
||||
consumes.items(new ItemStack(Items.coal, 10));
|
||||
}};
|
||||
|
||||
titanFactory = new UnitFactory("titan-factory"){{
|
||||
requirements(Category.units, padVisible, ItemStack.with(Items.graphite, 50, Items.lead, 50, Items.silicon, 45));
|
||||
requirements(Category.units, ItemStack.with(Items.graphite, 50, Items.lead, 50, Items.silicon, 45));
|
||||
type = UnitTypes.titan;
|
||||
produceTime = 1050;
|
||||
size = 3;
|
||||
@@ -1704,7 +1705,7 @@ public class Blocks implements ContentList{
|
||||
}};
|
||||
|
||||
fortressFactory = new UnitFactory("fortress-factory"){{
|
||||
requirements(Category.units, padVisible, ItemStack.with(Items.thorium, 40, Items.lead, 110, Items.silicon, 75));
|
||||
requirements(Category.units, ItemStack.with(Items.thorium, 40, Items.lead, 110, Items.silicon, 75));
|
||||
type = UnitTypes.fortress;
|
||||
produceTime = 2000;
|
||||
size = 3;
|
||||
|
||||
@@ -32,7 +32,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;
|
||||
unitSpawn, spawnShockwave, magmasmoke, impactShockwave, impactcloud, impactsmoke, dynamicExplosion, padlaunch, commandSend;
|
||||
|
||||
@Override
|
||||
public void load(){
|
||||
@@ -53,6 +53,13 @@ public class Fx implements ContentList{
|
||||
Draw.reset();
|
||||
});
|
||||
|
||||
commandSend = new Effect(28, e -> {
|
||||
Draw.color(Pal.command);
|
||||
Lines.stroke(e.fout() * 2f);
|
||||
Lines.circle(e.x, e.y, 4f + e.finpow() * 120f);
|
||||
Draw.color();
|
||||
});
|
||||
|
||||
placeBlock = new Effect(16, e -> {
|
||||
Draw.color(Pal.accent);
|
||||
Lines.stroke(3f - e.fin() * 2f);
|
||||
|
||||
@@ -5,6 +5,7 @@ import io.anuke.arc.graphics.*;
|
||||
import io.anuke.arc.graphics.g2d.*;
|
||||
import io.anuke.arc.math.*;
|
||||
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.effect.*;
|
||||
@@ -85,7 +86,7 @@ public class Mechs implements ContentList{
|
||||
Effects.shake(1f, 1f, player);
|
||||
Effects.effect(Fx.landShock, player);
|
||||
for(int i = 0; i < 8; i++){
|
||||
Time.run(Mathf.random(8f), () -> Lightning.create(player.getTeam(), Pal.lancerLaser, 17f, player.x, player.y, Mathf.random(360f), 14));
|
||||
Time.run(Mathf.random(8f), () -> Lightning.create(player.getTeam(), Pal.lancerLaser, 17f * Vars.state.rules.playerDamageMultiplier, player.x, player.y, Mathf.random(360f), 14));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -286,7 +287,7 @@ public class Mechs implements ContentList{
|
||||
float scl = scld(player);
|
||||
if(Mathf.chance(Time.delta() * (0.15 * scl))){
|
||||
Effects.effect(Fx.hitLancer, Pal.lancerLaser, player.x, player.y);
|
||||
Lightning.create(player.getTeam(), Pal.lancerLaser, 10f,
|
||||
Lightning.create(player.getTeam(), Pal.lancerLaser, 10f * Vars.state.rules.playerDamageMultiplier,
|
||||
player.x + player.velocity().x, player.y + player.velocity().y, player.rotation, 14);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -254,6 +254,7 @@ public class TechTree implements ContentList{
|
||||
});
|
||||
|
||||
node(daggerFactory, () -> {
|
||||
node(commandCenter, () -> {});
|
||||
node(crawlerFactory, () -> {
|
||||
node(titanFactory, () -> {
|
||||
node(fortressFactory, () -> {
|
||||
@@ -296,7 +297,7 @@ public class TechTree implements ContentList{
|
||||
private TechNode node(Block block, Runnable children){
|
||||
ItemStack[] requirements = new ItemStack[block.buildRequirements.length];
|
||||
for(int i = 0; i < requirements.length; i++){
|
||||
requirements[i] = new ItemStack(block.buildRequirements[i].item, 30 + block.buildRequirements[i].amount * 5);
|
||||
requirements[i] = new ItemStack(block.buildRequirements[i].item, 30 + block.buildRequirements[i].amount * 6);
|
||||
}
|
||||
|
||||
return new TechNode(block, requirements, children);
|
||||
|
||||
@@ -1,16 +1,14 @@
|
||||
package io.anuke.mindustry.core;
|
||||
|
||||
import io.anuke.arc.collection.*;
|
||||
import io.anuke.arc.function.Consumer;
|
||||
import io.anuke.arc.graphics.Color;
|
||||
import io.anuke.arc.graphics.Pixmap;
|
||||
import io.anuke.arc.util.Log;
|
||||
import io.anuke.arc.function.*;
|
||||
import io.anuke.arc.graphics.*;
|
||||
import io.anuke.arc.util.*;
|
||||
import io.anuke.mindustry.content.*;
|
||||
import io.anuke.mindustry.entities.bullet.BulletType;
|
||||
import io.anuke.mindustry.entities.bullet.*;
|
||||
import io.anuke.mindustry.game.*;
|
||||
import io.anuke.mindustry.type.*;
|
||||
import io.anuke.mindustry.world.Block;
|
||||
import io.anuke.mindustry.world.LegacyColorMapper;
|
||||
import io.anuke.mindustry.world.*;
|
||||
|
||||
import static io.anuke.arc.Core.files;
|
||||
|
||||
@@ -21,8 +19,6 @@ import static io.anuke.arc.Core.files;
|
||||
@SuppressWarnings("unchecked")
|
||||
public class ContentLoader{
|
||||
private boolean loaded = false;
|
||||
private boolean verbose = false;
|
||||
|
||||
private ObjectMap<String, MappableContent>[] contentNameMap = new ObjectMap[ContentType.values().length];
|
||||
private Array<Content>[] contentMap = new Array[ContentType.values().length];
|
||||
private MappableContent[][] temporaryMapper;
|
||||
@@ -45,12 +41,8 @@ public class ContentLoader{
|
||||
new LegacyColorMapper(),
|
||||
};
|
||||
|
||||
public void setVerbose(){
|
||||
verbose = true;
|
||||
}
|
||||
|
||||
/** Creates all content types. */
|
||||
public void load(){
|
||||
public void createContent(){
|
||||
if(loaded){
|
||||
Log.info("Content already loaded, skipping.");
|
||||
return;
|
||||
@@ -65,8 +57,6 @@ public class ContentLoader{
|
||||
list.load();
|
||||
}
|
||||
|
||||
int total = 0;
|
||||
|
||||
for(ContentType type : ContentType.values()){
|
||||
|
||||
for(Content c : contentMap[type.ordinal()]){
|
||||
@@ -77,7 +67,6 @@ public class ContentLoader{
|
||||
}
|
||||
contentNameMap[type.ordinal()].put(name, (MappableContent)c);
|
||||
}
|
||||
total++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,25 +80,32 @@ public class ContentLoader{
|
||||
}
|
||||
}
|
||||
|
||||
if(verbose){
|
||||
Log.info("--- CONTENT INFO ---");
|
||||
for(int k = 0; k < contentMap.length; k++){
|
||||
Log.info("[{0}]: loaded {1}", ContentType.values()[k].name(), contentMap[k].size);
|
||||
}
|
||||
Log.info("Total content loaded: {0}", total);
|
||||
Log.info("-------------------");
|
||||
}
|
||||
|
||||
loaded = true;
|
||||
}
|
||||
|
||||
public void initialize(Consumer<Content> callable){
|
||||
initialize(callable, false);
|
||||
/** Logs content statistics.*/
|
||||
public void logContent(){
|
||||
Log.info("--- CONTENT INFO ---");
|
||||
for(int k = 0; k < contentMap.length; k++){
|
||||
Log.info("[{0}]: loaded {1}", ContentType.values()[k].name(), contentMap[k].size);
|
||||
}
|
||||
Log.info("Total content loaded: {0}", Array.with(ContentType.values()).mapInt(c -> contentMap[c.ordinal()].size).sum());
|
||||
Log.info("-------------------");
|
||||
}
|
||||
|
||||
/** Calls Content#init() on everything. Use only after all modules have been created.*/
|
||||
public void init(){
|
||||
initialize(Content::init);
|
||||
}
|
||||
|
||||
/** Calls Content#load() on everything. Use only after all modules have been created on the client.*/
|
||||
public void load(){
|
||||
initialize(Content::load);
|
||||
}
|
||||
|
||||
/** Initializes all content with the specified function. */
|
||||
public void initialize(Consumer<Content> callable, boolean override){
|
||||
if(initialization.contains(callable) && !override) return;
|
||||
private void initialize(Consumer<Content> callable){
|
||||
if(initialization.contains(callable)) return;
|
||||
|
||||
for(ContentType type : ContentType.values()){
|
||||
for(Content content : contentMap[type.ordinal()]){
|
||||
@@ -136,12 +132,8 @@ public class ContentLoader{
|
||||
pixmap.dispose();
|
||||
}
|
||||
|
||||
public void verbose(boolean verbose){
|
||||
this.verbose = verbose;
|
||||
}
|
||||
|
||||
public void dispose(){
|
||||
//clear all content, currently not needed
|
||||
//clear all content, currently not used
|
||||
}
|
||||
|
||||
public void handleContent(Content content){
|
||||
@@ -171,7 +163,7 @@ public class ContentLoader{
|
||||
return null;
|
||||
}
|
||||
if(temporaryMapper[type.ordinal()].length <= id || temporaryMapper[type.ordinal()][id] == null){
|
||||
return getByID(type, 0); //default value is always ID 0
|
||||
return (T)contentMap[type.ordinal()].get(0); //default value is always ID 0
|
||||
}
|
||||
return (T)temporaryMapper[type.ordinal()][id];
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
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.*;
|
||||
@@ -13,8 +14,8 @@ import io.anuke.mindustry.content.*;
|
||||
import io.anuke.mindustry.core.GameState.*;
|
||||
import io.anuke.mindustry.entities.*;
|
||||
import io.anuke.mindustry.entities.type.*;
|
||||
import io.anuke.mindustry.game.*;
|
||||
import io.anuke.mindustry.game.EventType.*;
|
||||
import io.anuke.mindustry.game.*;
|
||||
import io.anuke.mindustry.gen.*;
|
||||
import io.anuke.mindustry.input.*;
|
||||
import io.anuke.mindustry.maps.*;
|
||||
@@ -25,6 +26,8 @@ import io.anuke.mindustry.world.*;
|
||||
import io.anuke.mindustry.world.blocks.storage.*;
|
||||
|
||||
import java.io.*;
|
||||
import java.time.*;
|
||||
import java.time.format.*;
|
||||
|
||||
import static io.anuke.arc.Core.*;
|
||||
import static io.anuke.mindustry.Vars.*;
|
||||
@@ -35,10 +38,10 @@ import static io.anuke.mindustry.Vars.*;
|
||||
* Should <i>not</i> handle any logic-critical state.
|
||||
* This class is not created in the headless server.
|
||||
*/
|
||||
public class Control implements ApplicationListener{
|
||||
public final Saves saves;
|
||||
public final MusicControl music;
|
||||
public final Tutorial tutorial;
|
||||
public class Control implements ApplicationListener, Loadable{
|
||||
public Saves saves;
|
||||
public MusicControl music;
|
||||
public Tutorial tutorial;
|
||||
public InputHandler input;
|
||||
|
||||
private Interval timer = new Interval(2);
|
||||
@@ -46,37 +49,6 @@ public class Control implements ApplicationListener{
|
||||
private boolean wasPaused = false;
|
||||
|
||||
public Control(){
|
||||
batch = new SpriteBatch();
|
||||
saves = new Saves();
|
||||
tutorial = new Tutorial();
|
||||
music = new MusicControl();
|
||||
|
||||
UnitScl.dp.setProduct(settings.getInt("uiscale", 100) / 100f);
|
||||
|
||||
Core.input.setCatch(KeyCode.BACK, true);
|
||||
|
||||
content.initialize(Content::init);
|
||||
Core.atlas = new TextureAtlas("sprites/sprites.atlas");
|
||||
Draw.scl = 1f / Core.atlas.find("scale_marker").getWidth();
|
||||
content.initialize(Content::load, true);
|
||||
|
||||
data.load();
|
||||
|
||||
Core.settings.setAppName(appName);
|
||||
Core.settings.defaults(
|
||||
"ip", "localhost",
|
||||
"color-0", Color.rgba8888(playerColors[8]),
|
||||
"color-1", Color.rgba8888(playerColors[11]),
|
||||
"color-2", Color.rgba8888(playerColors[13]),
|
||||
"color-3", Color.rgba8888(playerColors[9]),
|
||||
"name", "",
|
||||
"lastBuild", 0
|
||||
);
|
||||
|
||||
createPlayer();
|
||||
|
||||
saves.load();
|
||||
|
||||
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.instance::updateRPC);
|
||||
@@ -177,12 +149,32 @@ public class Control implements ApplicationListener{
|
||||
Events.on(ZoneConfigureCompleteEvent.class, e -> {
|
||||
ui.hudfrag.showToast(Core.bundle.format("zone.config.complete", e.zone.configureWave));
|
||||
});
|
||||
}
|
||||
|
||||
if(android){
|
||||
Sounds.empty.loop(0f, 1f, 0f);
|
||||
@Override
|
||||
public void loadAsync(){
|
||||
saves = new Saves();
|
||||
tutorial = new Tutorial();
|
||||
music = new MusicControl();
|
||||
|
||||
checkClassicData();
|
||||
}
|
||||
Draw.scl = 1f / Core.atlas.find("scale_marker").getWidth();
|
||||
|
||||
UnitScl.dp.setProduct(settings.getInt("uiscale", 100) / 100f);
|
||||
|
||||
Core.input.setCatch(KeyCode.BACK, true);
|
||||
|
||||
data.load();
|
||||
|
||||
Core.settings.defaults(
|
||||
"ip", "localhost",
|
||||
"color-0", Color.rgba8888(playerColors[8]),
|
||||
"name", "",
|
||||
"lastBuild", 0
|
||||
);
|
||||
|
||||
createPlayer();
|
||||
|
||||
saves.load();
|
||||
}
|
||||
|
||||
//checks for existing 3.5 app data, android only
|
||||
@@ -240,6 +232,9 @@ public class Control implements ApplicationListener{
|
||||
world.loadMap(map, rules);
|
||||
state.rules = rules;
|
||||
logic.play();
|
||||
if(settings.getBool("savecreate")){
|
||||
control.saves.addSave(map.name() + "-" + DateTimeFormatter.ofPattern("MMM dd h:mm").format(LocalDateTime.now()));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -382,6 +377,11 @@ public class Control implements ApplicationListener{
|
||||
dialog.show();
|
||||
}));
|
||||
}
|
||||
|
||||
if(android){
|
||||
Sounds.empty.loop(0f, 1f, 0f);
|
||||
checkClassicData();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -396,6 +396,17 @@ public class Control implements ApplicationListener{
|
||||
music.update();
|
||||
loops.update();
|
||||
|
||||
if(Core.input.keyTap(Binding.fullscreen)){
|
||||
boolean full = settings.getBool("fullscreen");
|
||||
if(full){
|
||||
graphics.setWindowedMode(graphics.getWidth(), graphics.getHeight());
|
||||
}else{
|
||||
graphics.setFullscreenMode(graphics.getDisplayMode());
|
||||
}
|
||||
settings.put("fullscreen", !full);
|
||||
settings.save();
|
||||
}
|
||||
|
||||
if(!state.is(State.menu)){
|
||||
input.update();
|
||||
|
||||
|
||||
@@ -101,15 +101,15 @@ public class Logic implements ApplicationListener{
|
||||
state.rules = new Rules();
|
||||
state.stats = new Stats();
|
||||
|
||||
entities.clear();
|
||||
Time.clear();
|
||||
Entities.clear();
|
||||
TileEntity.sleepingEntities = 0;
|
||||
|
||||
Events.fire(new ResetEvent());
|
||||
}
|
||||
|
||||
public void runWave(){
|
||||
world.spawner.spawnEnemies();
|
||||
spawner.spawnEnemies();
|
||||
state.wave++;
|
||||
state.wavetime = world.isZone() && world.getZone().isBossWave(state.wave) ? state.rules.waveSpacing * state.rules.bossWaveMultiplier :
|
||||
world.isZone() && world.getZone().isLaunchWave(state.wave) ? state.rules.waveSpacing * state.rules.launchWaveMultiplier : state.rules.waveSpacing;
|
||||
@@ -195,20 +195,20 @@ public class Logic implements ApplicationListener{
|
||||
}
|
||||
|
||||
if(!headless){
|
||||
Entities.update(effectGroup);
|
||||
Entities.update(groundEffectGroup);
|
||||
effectGroup.update();
|
||||
groundEffectGroup.update();
|
||||
}
|
||||
|
||||
if(!state.isEditor()){
|
||||
for(EntityGroup group : unitGroups){
|
||||
Entities.update(group);
|
||||
group.update();
|
||||
}
|
||||
|
||||
Entities.update(puddleGroup);
|
||||
Entities.update(shieldGroup);
|
||||
Entities.update(bulletGroup);
|
||||
Entities.update(tileGroup);
|
||||
Entities.update(fireGroup);
|
||||
puddleGroup.update();
|
||||
shieldGroup.update();
|
||||
bulletGroup.update();
|
||||
tileGroup.update();
|
||||
fireGroup.update();
|
||||
}else{
|
||||
for(EntityGroup<?> group : unitGroups){
|
||||
group.updateEvents();
|
||||
@@ -217,11 +217,11 @@ public class Logic implements ApplicationListener{
|
||||
}
|
||||
|
||||
|
||||
Entities.update(playerGroup);
|
||||
playerGroup.update();
|
||||
|
||||
//effect group only contains item transfers in the headless version, update it!
|
||||
if(headless){
|
||||
Entities.update(effectGroup);
|
||||
effectGroup.update();
|
||||
}
|
||||
|
||||
if(!state.isEditor()){
|
||||
@@ -234,7 +234,7 @@ public class Logic implements ApplicationListener{
|
||||
collisions.collideGroups(bulletGroup, playerGroup);
|
||||
}
|
||||
|
||||
world.pathfinder.update();
|
||||
pathfinder.update();
|
||||
}
|
||||
|
||||
if(!Net.client() && !world.isInvalidMap() && !state.isEditor()){
|
||||
|
||||
@@ -7,11 +7,11 @@ 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.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.Entities;
|
||||
import io.anuke.mindustry.entities.EntityGroup;
|
||||
import io.anuke.mindustry.entities.traits.BuilderTrait.BuildRequest;
|
||||
import io.anuke.mindustry.entities.traits.SyncTrait;
|
||||
@@ -151,15 +151,38 @@ public class NetClient implements ApplicationListener{
|
||||
throw new ValidateException(player, "Player has sent a message above the text limit.");
|
||||
}
|
||||
|
||||
//server console logging
|
||||
Log.info("&y{0}: &lb{1}", player.name, message);
|
||||
//check if it's a command
|
||||
CommandResponse response = netServer.clientCommands.handleMessage(message, player);
|
||||
if(response.type == ResponseType.noCommand){ //no command to handle
|
||||
//server console logging
|
||||
Log.info("&y{0}: &lb{1}", player.name, message);
|
||||
|
||||
//invoke event for all clients but also locally
|
||||
//this is required so other clients get the correct name even if they don't know who's sending it yet
|
||||
Call.sendMessage(message, colorizeName(player.id, player.name), player);
|
||||
//invoke event for all clients but also locally
|
||||
//this is required so other clients get the correct name even if they don't know who's sending it yet
|
||||
Call.sendMessage(message, colorizeName(player.id, player.name), player);
|
||||
}else{
|
||||
//log command to console but with brackets
|
||||
Log.info("<&y{0}: &lm{1}&lg>", player.name, message);
|
||||
|
||||
//a command was sent, now get the output
|
||||
if(response.type != ResponseType.valid){
|
||||
String text;
|
||||
|
||||
//send usage
|
||||
if(response.type == ResponseType.manyArguments){
|
||||
text = "[scarlet]Too many arguments. Usage:[lightgray] " + response.command.text + "[gray] " + response.command.paramText;
|
||||
}else if(response.type == ResponseType.fewArguments){
|
||||
text = "[scarlet]Too few arguments. Usage:[lightgray] " + response.command.text + "[gray] " + response.command.paramText;
|
||||
}else{ //unknown command
|
||||
text = "[scarlet]Unknown command. Check [lightgray]/help[scarlet].";
|
||||
}
|
||||
|
||||
player.sendMessage(text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static String colorizeName(int id, String name){
|
||||
public static String colorizeName(int id, String name){
|
||||
Player player = playerGroup.getByID(id);
|
||||
if(name == null || player == null) return null;
|
||||
return "[#" + player.color.toString().toUpperCase() + "]" + name;
|
||||
@@ -195,7 +218,7 @@ public class NetClient implements ApplicationListener{
|
||||
|
||||
@Remote(variants = Variant.both)
|
||||
public static void onWorldDataBegin(){
|
||||
Entities.clear();
|
||||
entities.clear();
|
||||
netClient.removed.clear();
|
||||
logic.reset();
|
||||
|
||||
@@ -229,7 +252,7 @@ public class NetClient implements ApplicationListener{
|
||||
netClient.byteStream.setBytes(Net.decompressSnapshot(data, dataLen));
|
||||
DataInputStream input = netClient.dataStream;
|
||||
|
||||
EntityGroup group = Entities.getGroup(groupID);
|
||||
EntityGroup group = entities.get(groupID);
|
||||
|
||||
//go through each entity
|
||||
for(int j = 0; j < amount; j++){
|
||||
@@ -347,7 +370,7 @@ public class NetClient implements ApplicationListener{
|
||||
quiet = false;
|
||||
lastSent = 0;
|
||||
|
||||
Entities.clear();
|
||||
entities.clear();
|
||||
ui.chatfrag.clearMessages();
|
||||
}
|
||||
|
||||
|
||||
@@ -4,18 +4,17 @@ 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.IntMap;
|
||||
import io.anuke.arc.collection.ObjectSet;
|
||||
import io.anuke.arc.collection.*;
|
||||
import io.anuke.arc.graphics.Color;
|
||||
import io.anuke.arc.graphics.Colors;
|
||||
import io.anuke.arc.math.Mathf;
|
||||
import io.anuke.arc.math.geom.Rectangle;
|
||||
import io.anuke.arc.math.geom.Vector2;
|
||||
import io.anuke.arc.util.*;
|
||||
import io.anuke.arc.util.CommandHandler.*;
|
||||
import io.anuke.arc.util.io.*;
|
||||
import io.anuke.mindustry.content.Blocks;
|
||||
import io.anuke.mindustry.core.GameState.State;
|
||||
import io.anuke.mindustry.entities.Entities;
|
||||
import io.anuke.mindustry.entities.EntityGroup;
|
||||
import io.anuke.mindustry.entities.traits.BuilderTrait.BuildRequest;
|
||||
import io.anuke.mindustry.entities.traits.Entity;
|
||||
@@ -47,6 +46,7 @@ public class NetServer implements ApplicationListener{
|
||||
private final static float correctDist = 16f;
|
||||
|
||||
public final Administration admins = new Administration();
|
||||
public final CommandHandler clientCommands = new CommandHandler("/");
|
||||
|
||||
/** Maps connection IDs to players. */
|
||||
private IntMap<Player> connections = new IntMap<>();
|
||||
@@ -112,7 +112,7 @@ public class NetServer implements ApplicationListener{
|
||||
}
|
||||
|
||||
if(packet.versionType == null || ((packet.version == -1 || !packet.versionType.equals(Version.type)) && Version.build != -1 && !admins.allowsCustomClients())){
|
||||
kick(id, KickReason.customClient);
|
||||
kick(id, !Version.type.equals(packet.versionType) ? KickReason.typeMismatch : KickReason.customClient);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -191,6 +191,138 @@ public class NetServer implements ApplicationListener{
|
||||
if(player == null) return;
|
||||
RemoteReadServer.readPacket(packet.writeBuffer, packet.type, player);
|
||||
});
|
||||
|
||||
registerCommands();
|
||||
}
|
||||
|
||||
private void registerCommands(){
|
||||
clientCommands.<Player>register("help", "[page]", "Lists all commands.", (args, player) -> {
|
||||
if(args.length > 0 && !Strings.canParseInt(args[0])){
|
||||
player.sendMessage("[scarlet]'page' must be a number.");
|
||||
return;
|
||||
}
|
||||
int commandsPerPage = 6;
|
||||
int page = args.length > 0 ? Strings.parseInt(args[0]) : 1;
|
||||
int pages = Mathf.ceil((float)clientCommands.getCommandList().size / commandsPerPage);
|
||||
|
||||
page --;
|
||||
|
||||
if(page > pages || page < 0){
|
||||
player.sendMessage("[scarlet]'page' must be a number between[orange] 1[] and[orange] " + pages + "[scarlet].");
|
||||
return;
|
||||
}
|
||||
|
||||
StringBuilder result = new StringBuilder();
|
||||
result.append(Strings.format("[orange]-- Commands Page[lightgray] {0}[gray]/[lightgray]{1}[orange] --\n\n", (page+1), pages));
|
||||
|
||||
for(int i = commandsPerPage * page; i < Math.min(commandsPerPage * (page + 1), clientCommands.getCommandList().size); i++){
|
||||
Command command = clientCommands.getCommandList().get(i);
|
||||
result.append("[orange] /").append(command.text).append("[white] ").append(command.paramText).append("[lightgray] - ").append(command.description).append("\n");
|
||||
}
|
||||
player.sendMessage(result.toString());
|
||||
});
|
||||
|
||||
clientCommands.<Player>register("t", "<message...>", "Send a message only to your teammates.", (args, player) -> {
|
||||
playerGroup.all().each(p -> p.getTeam() == player.getTeam(), o -> o.sendMessage(args[0], player, "[#" + player.getTeam().color.toString() + "]<T>" + NetClient.colorizeName(player.id, player.name)));
|
||||
});
|
||||
|
||||
//duration of a a kick in seconds
|
||||
int kickDuration = 10 * 60;
|
||||
|
||||
class VoteSession{
|
||||
Player target;
|
||||
ObjectSet<String> voted = new ObjectSet<>();
|
||||
ObjectMap<Player, VoteSession> map;
|
||||
Timer.Task task;
|
||||
int votes;
|
||||
|
||||
public VoteSession(ObjectMap<Player, VoteSession> map, Player target){
|
||||
this.target = target;
|
||||
this.map = map;
|
||||
this.task = Timer.schedule(() -> {
|
||||
if(!checkPass()){
|
||||
Call.sendMessage(Strings.format("[lightgray]Vote failed. Not enough votes to kick[orange] {0}[lightgray].", target.name));
|
||||
}
|
||||
map.remove(target);
|
||||
task.cancel();
|
||||
}, 60 * 1.5f);
|
||||
}
|
||||
|
||||
boolean checkPass(){
|
||||
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);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//cooldown between votes
|
||||
int voteTime = 60 * 10;
|
||||
Timekeeper vtime = new Timekeeper(voteTime);
|
||||
//current kick sessions
|
||||
ObjectMap<Player, VoteSession> currentlyKicking = new ObjectMap<>();
|
||||
|
||||
clientCommands.<Player>register("votekick", "[player...]", "Vote to kick a player, with a cooldown.", (args, player) -> {
|
||||
if(playerGroup.size() < 3){
|
||||
player.sendMessage("[scarlet]At least 3 players are needed to start a votekick.");
|
||||
return;
|
||||
}
|
||||
|
||||
if(currentlyKicking.values().toArray().contains(v -> v.voted.contains(player.uuid) || v.voted.contains(admins.getInfo(player.uuid).lastIP))){
|
||||
player.sendMessage("[scarlet]You've already voted. Sit down.");
|
||||
return;
|
||||
}
|
||||
|
||||
if(args.length == 0){
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append("[orange]Players to kick: \n");
|
||||
for(Player p : playerGroup.all()){
|
||||
if(p.isAdmin || p.con == null || p == player) continue;
|
||||
|
||||
builder.append("[lightgray] ").append(p.name).append("[accent] (#").append(p.con.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);
|
||||
}else{
|
||||
found = playerGroup.find(p -> p.name.equalsIgnoreCase(args[0]));
|
||||
}
|
||||
|
||||
if(found != null){
|
||||
if(player == found){
|
||||
player.sendMessage("[scarlet]If you're interested in kicking yourself, just leave.");
|
||||
}else if(found.isAdmin){
|
||||
player.sendMessage("[scarlet]Did you really expect to be able to kick an admin?");
|
||||
}else{
|
||||
if(!currentlyKicking.containsKey(found) && !vtime.get()){
|
||||
player.sendMessage("[scarlet]You must wait " + voteTime/60 + " minutes between votekicks.");
|
||||
return;
|
||||
}
|
||||
|
||||
VoteSession session = currentlyKicking.getOr(found, () -> new VoteSession(currentlyKicking, found));
|
||||
session.votes ++;
|
||||
session.voted.addAll(player.uuid, admins.getInfo(player.uuid).lastIP);
|
||||
|
||||
Call.sendMessage(Strings.format("[orange]{0}[lightgray] has voted to kick[orange] {1}[].[accent] ({2}/{3})\n[lightgray]Type[orange] /votekick #{4}[] to agree.",
|
||||
player.name, found.name, session.votes, votesRequired(), found.con.id));
|
||||
session.checkPass();
|
||||
vtime.reset();
|
||||
}
|
||||
}else{
|
||||
player.sendMessage("[scarlet]No player[orange]'" + args[0] + "'[scarlet] found.");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public int votesRequired(){
|
||||
return playerGroup.size() * 2 / 3;
|
||||
}
|
||||
|
||||
public Team assignTeam(Player current, Iterable<Player> players){
|
||||
@@ -422,10 +554,10 @@ public class NetServer implements ApplicationListener{
|
||||
|
||||
Player player = connections.get(con.id);
|
||||
|
||||
if(player != null && (reason == KickReason.kick || reason == KickReason.banned) && player.uuid != null){
|
||||
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 = Time.millis();
|
||||
info.lastKicked = Math.max(Time.millis(), info.lastKicked);
|
||||
}
|
||||
|
||||
Call.onKick(connection, reason);
|
||||
@@ -455,7 +587,7 @@ public class NetServer implements ApplicationListener{
|
||||
viewport.setSize(player.con.viewWidth, player.con.viewHeight).setCenter(player.con.viewX, player.con.viewY);
|
||||
|
||||
//check for syncable groups
|
||||
for(EntityGroup<?> group : Entities.getAllGroups()){
|
||||
for(EntityGroup<?> group : entities.all()){
|
||||
if(group.isEmpty() || !(group.all().get(0) instanceof SyncTrait)) continue;
|
||||
|
||||
//make sure mapping is enabled for this group
|
||||
|
||||
@@ -43,9 +43,6 @@ public class Renderer implements ApplicationListener{
|
||||
|
||||
public Renderer(){
|
||||
camera = new Camera();
|
||||
if(settings.getBool("bloom")){
|
||||
setupBloom();
|
||||
}
|
||||
Shaders.init();
|
||||
|
||||
Effects.setScreenShakeProvider((intensity, duration) -> {
|
||||
@@ -93,6 +90,13 @@ public class Renderer implements ApplicationListener{
|
||||
clearColor = new Color(0f, 0f, 0f, 1f);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(){
|
||||
if(settings.getBool("bloom")){
|
||||
setupBloom();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(){
|
||||
//TODO hack, find source of this bug
|
||||
@@ -205,9 +209,9 @@ public class Renderer implements ApplicationListener{
|
||||
|
||||
blocks.floor.drawFloor();
|
||||
|
||||
draw(groundEffectGroup, e -> e instanceof BelowLiquidTrait);
|
||||
draw(puddleGroup);
|
||||
draw(groundEffectGroup, e -> !(e instanceof BelowLiquidTrait));
|
||||
groundEffectGroup.draw(e -> e instanceof BelowLiquidTrait);
|
||||
puddleGroup.draw();
|
||||
groundEffectGroup.draw(e -> !(e instanceof BelowLiquidTrait));
|
||||
|
||||
blocks.processBlocks();
|
||||
|
||||
@@ -244,8 +248,8 @@ public class Renderer implements ApplicationListener{
|
||||
bloom.capture();
|
||||
}
|
||||
|
||||
draw(bulletGroup);
|
||||
draw(effectGroup);
|
||||
bulletGroup.draw();
|
||||
effectGroup.draw();
|
||||
|
||||
Draw.flush();
|
||||
if(bloom != null && !pixelator.enabled()){
|
||||
@@ -253,15 +257,15 @@ public class Renderer implements ApplicationListener{
|
||||
}
|
||||
|
||||
overlays.drawBottom();
|
||||
draw(playerGroup, p -> true, Player::drawBuildRequests);
|
||||
playerGroup.draw(p -> true, Player::drawBuildRequests);
|
||||
|
||||
if(Entities.countInBounds(shieldGroup) > 0){
|
||||
if(settings.getBool("animatedshields")){
|
||||
if(shieldGroup.countInBounds() > 0){
|
||||
if(settings.getBool("animatedshields") && Shaders.shield != null){
|
||||
Draw.flush();
|
||||
shieldBuffer.begin();
|
||||
graphics.clear(Color.CLEAR);
|
||||
Entities.draw(shieldGroup);
|
||||
Entities.draw(shieldGroup, shield -> true, shield -> ((ShieldEntity)shield).drawOver());
|
||||
shieldGroup.draw();
|
||||
shieldGroup.draw(shield -> true, ShieldEntity::drawOver);
|
||||
Draw.flush();
|
||||
shieldBuffer.end();
|
||||
Draw.shader(Shaders.shield);
|
||||
@@ -270,13 +274,13 @@ public class Renderer implements ApplicationListener{
|
||||
Draw.color();
|
||||
Draw.shader();
|
||||
}else{
|
||||
Entities.draw(shieldGroup, shield -> true, shield -> ((ShieldEntity)shield).drawSimple());
|
||||
shieldGroup.draw(shield -> true, ShieldEntity::drawSimple);
|
||||
}
|
||||
}
|
||||
|
||||
overlays.drawTop();
|
||||
|
||||
draw(playerGroup, p -> !p.isDead() && !p.isLocal, Player::drawName);
|
||||
playerGroup.draw(p -> !p.isDead() && !p.isLocal, Player::drawName);
|
||||
|
||||
Draw.color();
|
||||
Draw.flush();
|
||||
@@ -293,12 +297,12 @@ public class Renderer implements ApplicationListener{
|
||||
|
||||
for(EntityGroup<? extends BaseUnit> group : unitGroups){
|
||||
if(!group.isEmpty()){
|
||||
draw(group, unit -> !unit.isDead(), draw::accept);
|
||||
group.draw(unit -> !unit.isDead(), draw::accept);
|
||||
}
|
||||
}
|
||||
|
||||
if(!playerGroup.isEmpty()){
|
||||
draw(playerGroup, unit -> !unit.isDead(), draw::accept);
|
||||
playerGroup.draw(unit -> !unit.isDead(), draw::accept);
|
||||
}
|
||||
|
||||
Draw.color();
|
||||
@@ -310,12 +314,12 @@ public class Renderer implements ApplicationListener{
|
||||
|
||||
for(EntityGroup<? extends BaseUnit> group : unitGroups){
|
||||
if(!group.isEmpty()){
|
||||
draw(group, unit -> unit.isFlying() && !unit.isDead(), baseUnit -> baseUnit.drawShadow(trnsX, trnsY));
|
||||
group.draw(unit -> unit.isFlying() && !unit.isDead(), baseUnit -> baseUnit.drawShadow(trnsX, trnsY));
|
||||
}
|
||||
}
|
||||
|
||||
if(!playerGroup.isEmpty()){
|
||||
draw(playerGroup, unit -> unit.isFlying() && !unit.isDead(), player -> player.drawShadow(trnsX, trnsY));
|
||||
playerGroup.draw(unit -> unit.isFlying() && !unit.isDead(), player -> player.drawShadow(trnsX, trnsY));
|
||||
}
|
||||
|
||||
Draw.color();
|
||||
@@ -327,29 +331,17 @@ public class Renderer implements ApplicationListener{
|
||||
|
||||
if(group.count(p -> p.isFlying() == flying) + playerGroup.count(p -> p.isFlying() == flying && p.getTeam() == team) == 0 && flying) continue;
|
||||
|
||||
draw(unitGroups[team.ordinal()], u -> u.isFlying() == flying && !u.isDead(), Unit::drawUnder);
|
||||
draw(playerGroup, p -> p.isFlying() == flying && p.getTeam() == team && !p.isDead(), Unit::drawUnder);
|
||||
unitGroups[team.ordinal()].draw(u -> u.isFlying() == flying && !u.isDead(), Unit::drawUnder);
|
||||
playerGroup.draw(p -> p.isFlying() == flying && p.getTeam() == team && !p.isDead(), Unit::drawUnder);
|
||||
|
||||
draw(unitGroups[team.ordinal()], u -> u.isFlying() == flying && !u.isDead(), Unit::drawAll);
|
||||
draw(playerGroup, p -> p.isFlying() == flying && p.getTeam() == team, Unit::drawAll);
|
||||
unitGroups[team.ordinal()].draw(u -> u.isFlying() == flying && !u.isDead(), Unit::drawAll);
|
||||
playerGroup.draw(p -> p.isFlying() == flying && p.getTeam() == team, Unit::drawAll);
|
||||
|
||||
draw(unitGroups[team.ordinal()], u -> u.isFlying() == flying && !u.isDead(), Unit::drawOver);
|
||||
draw(playerGroup, p -> p.isFlying() == flying && p.getTeam() == team, Unit::drawOver);
|
||||
unitGroups[team.ordinal()].draw(u -> u.isFlying() == flying && !u.isDead(), Unit::drawOver);
|
||||
playerGroup.draw(p -> p.isFlying() == flying && p.getTeam() == team, Unit::drawOver);
|
||||
}
|
||||
}
|
||||
|
||||
public <T extends DrawTrait> void draw(EntityGroup<T> group){
|
||||
draw(group, t -> true, DrawTrait::draw);
|
||||
}
|
||||
|
||||
public <T extends DrawTrait> void draw(EntityGroup<T> group, Predicate<T> toDraw){
|
||||
draw(group, toDraw, DrawTrait::draw);
|
||||
}
|
||||
|
||||
public <T extends DrawTrait> void draw(EntityGroup<T> group, Predicate<T> toDraw, Consumer<T> drawer){
|
||||
Entities.draw(group, toDraw, drawer);
|
||||
}
|
||||
|
||||
public void scaleCamera(float amount){
|
||||
targetscale += amount;
|
||||
clampScale();
|
||||
|
||||
@@ -3,8 +3,13 @@ package io.anuke.mindustry.core;
|
||||
import io.anuke.arc.*;
|
||||
import io.anuke.arc.Graphics.*;
|
||||
import io.anuke.arc.Graphics.Cursor.*;
|
||||
import io.anuke.arc.assets.*;
|
||||
import io.anuke.arc.assets.loaders.*;
|
||||
import io.anuke.arc.assets.loaders.resolvers.*;
|
||||
import io.anuke.arc.collection.*;
|
||||
import io.anuke.arc.freetype.*;
|
||||
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.g2d.*;
|
||||
@@ -31,8 +36,8 @@ import io.anuke.mindustry.ui.fragments.*;
|
||||
import static io.anuke.arc.scene.actions.Actions.*;
|
||||
import static io.anuke.mindustry.Vars.*;
|
||||
|
||||
public class UI implements ApplicationListener{
|
||||
private FreeTypeFontGenerator generator;
|
||||
public class UI implements ApplicationListener, Loadable{
|
||||
private Skin skin;
|
||||
|
||||
public MenuFragment menufrag;
|
||||
public HudFragment hudfrag;
|
||||
@@ -67,9 +72,23 @@ public class UI implements ApplicationListener{
|
||||
public Cursor drillCursor, unloadCursor;
|
||||
|
||||
public UI(){
|
||||
Skin skin = new Skin(Core.atlas);
|
||||
generateFonts(skin);
|
||||
skin = new Skin();
|
||||
setupFonts();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadAsync(){
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadSync(){
|
||||
//TODO type-safe skin files
|
||||
skin.addRegions(Core.atlas);
|
||||
loadExtraStyle(skin);
|
||||
skin.add("outline", Core.assets.get("outline"));
|
||||
skin.getFont("default").getData().markupEnabled = true;
|
||||
skin.getFont("default").setOwnsTexture(false);
|
||||
skin.load(Core.files.internal("sprites/uiskin.json"));
|
||||
|
||||
for(BitmapFont font : skin.getAll(BitmapFont.class).values()){
|
||||
@@ -97,6 +116,11 @@ public class UI implements ApplicationListener{
|
||||
loadExtraCursors();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Array<AssetDescriptor> getDependencies(){
|
||||
return Array.with(new AssetDescriptor<>(Control.class), new AssetDescriptor<>("outline", BitmapFont.class), new AssetDescriptor<>("default", BitmapFont.class), new AssetDescriptor<>("chat", BitmapFont.class));
|
||||
}
|
||||
|
||||
/** Called from a static context to make the cursor appear immediately upon startup.*/
|
||||
public static void loadSystemCursors(){
|
||||
SystemCursor.arrow.set(Core.graphics.newCursor("cursor"));
|
||||
@@ -106,6 +130,23 @@ public class UI implements ApplicationListener{
|
||||
Core.graphics.restoreCursor();
|
||||
}
|
||||
|
||||
/** Called from a static context for use in the loading screen.*/
|
||||
public static void loadDefaultFont(){
|
||||
FileHandleResolver resolver = new InternalFileHandleResolver();
|
||||
Core.assets.setLoader(FreeTypeFontGenerator.class, new FreeTypeFontGeneratorLoader(resolver));
|
||||
Core.assets.setLoader(BitmapFont.class, null, new FreetypeFontLoader(resolver));
|
||||
|
||||
FreeTypeFontParameter param = new FreeTypeFontParameter(){{
|
||||
size = fontParameter().size;
|
||||
borderColor = Color.DARK_GRAY;
|
||||
borderWidth = UnitScl.dp.scl(2f);
|
||||
spaceX -= borderWidth;
|
||||
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;
|
||||
@@ -130,29 +171,22 @@ public class UI implements ApplicationListener{
|
||||
unloadCursor = Core.graphics.newCursor("unload");
|
||||
}
|
||||
|
||||
void generateFonts(Skin skin){
|
||||
generator = new FreeTypeFontGenerator(Core.files.internal("fonts/font.ttf"));
|
||||
public void setupFonts(){
|
||||
String fontName = "fonts/font.ttf";
|
||||
|
||||
FreeTypeFontParameter param = new FreeTypeFontParameter(){{
|
||||
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);
|
||||
}
|
||||
|
||||
static FreeTypeFontParameter fontParameter(){
|
||||
return new FreeTypeFontParameter(){{
|
||||
size = (int)(UnitScl.dp.scl(18f));
|
||||
shadowColor = Color.DARK_GRAY;
|
||||
shadowOffsetY = 2;
|
||||
incremental = true;
|
||||
}};
|
||||
|
||||
FreeTypeFontParameter outlined = new FreeTypeFontParameter(){{
|
||||
size = param.size;
|
||||
borderColor = Color.DARK_GRAY;
|
||||
borderWidth = UnitScl.dp.scl(2f);
|
||||
spaceX -= borderWidth;
|
||||
incremental = true;
|
||||
}};
|
||||
|
||||
skin.add("outline", generator.generateFont(outlined));
|
||||
skin.add("default", generator.generateFont(param));
|
||||
skin.add("chat", generator.generateFont(param));
|
||||
skin.getFont("default").getData().markupEnabled = true;
|
||||
skin.getFont("default").setOwnsTexture(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -225,13 +259,14 @@ public class UI implements ApplicationListener{
|
||||
|
||||
@Override
|
||||
public void resize(int width, int height){
|
||||
if(Core.scene == null) return;
|
||||
Core.scene.resize(width, height);
|
||||
Events.fire(new ResizeEvent());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose(){
|
||||
generator.dispose();
|
||||
//generator.dispose();
|
||||
}
|
||||
|
||||
public void loadAnd(Runnable call){
|
||||
|
||||
@@ -6,10 +6,8 @@ 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.ai.*;
|
||||
import io.anuke.mindustry.content.*;
|
||||
import io.anuke.mindustry.core.GameState.*;
|
||||
import io.anuke.mindustry.entities.*;
|
||||
import io.anuke.mindustry.game.EventType.*;
|
||||
import io.anuke.mindustry.game.*;
|
||||
import io.anuke.mindustry.io.*;
|
||||
@@ -23,11 +21,7 @@ import io.anuke.mindustry.world.blocks.*;
|
||||
|
||||
import static io.anuke.mindustry.Vars.*;
|
||||
|
||||
public class World implements ApplicationListener{
|
||||
public final Maps maps = new Maps();
|
||||
public final BlockIndexer indexer = new BlockIndexer();
|
||||
public final WaveSpawner spawner = new WaveSpawner();
|
||||
public final Pathfinder pathfinder = new Pathfinder();
|
||||
public class World{
|
||||
public final Context context = new Context();
|
||||
|
||||
private Map currentMap;
|
||||
@@ -36,17 +30,7 @@ public class World implements ApplicationListener{
|
||||
private boolean generating, invalidMap;
|
||||
|
||||
public World(){
|
||||
maps.load();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(){
|
||||
maps.loadLegacyMaps();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose(){
|
||||
maps.dispose();
|
||||
}
|
||||
|
||||
public boolean isInvalidMap(){
|
||||
@@ -190,9 +174,11 @@ public class World implements ApplicationListener{
|
||||
}
|
||||
}
|
||||
|
||||
addDarkness(tiles);
|
||||
if(!headless){
|
||||
addDarkness(tiles);
|
||||
}
|
||||
|
||||
Entities.getAllGroups().each(group -> group.resize(-finalWorldBounds, -finalWorldBounds, tiles.length * tilesize + finalWorldBounds * 2, tiles[0].length * tilesize + finalWorldBounds * 2));
|
||||
entities.all().each(group -> group.resize(-finalWorldBounds, -finalWorldBounds, tiles.length * tilesize + finalWorldBounds * 2, tiles[0].length * tilesize + finalWorldBounds * 2));
|
||||
|
||||
generating = false;
|
||||
Events.fire(new WorldLoadEvent());
|
||||
@@ -354,7 +340,7 @@ public class World implements ApplicationListener{
|
||||
for(int x = 0; x < tiles.length; x++){
|
||||
for(int y = 0; y < tiles[0].length; y++){
|
||||
Tile tile = tiles[x][y];
|
||||
if(tile.block().solid && !tile.block().synthetic() && tile.block().fillsTile){
|
||||
if(tile.isDarkened()){
|
||||
dark[x][y] = darkIterations;
|
||||
}
|
||||
}
|
||||
@@ -383,9 +369,21 @@ public class World implements ApplicationListener{
|
||||
for(int x = 0; x < tiles.length; x++){
|
||||
for(int y = 0; y < tiles[0].length; y++){
|
||||
Tile tile = tiles[x][y];
|
||||
if(tile.block().solid && !tile.block().synthetic()){
|
||||
if(tile.isDarkened()){
|
||||
tiles[x][y].rotation(dark[x][y]);
|
||||
}
|
||||
if(dark[x][y] == 4){
|
||||
boolean full = true;
|
||||
for(Point2 p : Geometry.d4){
|
||||
int px = p.x + x, py = p.y + y;
|
||||
if(Structs.inBounds(px, py, tiles) && !(tiles[px][py].isDarkened() && dark[px][py] == 4)){
|
||||
full = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(full) tiles[x][y].rotation(5);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,11 +93,11 @@ public class MapEditorDialog extends Dialog implements Disposable{
|
||||
"$editor.importmap", "$editor.importmap.description", "icon-load-map", (Runnable)loadDialog::show,
|
||||
"$editor.importfile", "$editor.importfile.description", "icon-file", (Runnable)() ->
|
||||
Platform.instance.showFileChooser("$editor.loadmap", "Map Files", file -> ui.loadAnd(() -> {
|
||||
world.maps.tryCatchMapError(() -> {
|
||||
maps.tryCatchMapError(() -> {
|
||||
if(MapIO.isImage(file)){
|
||||
ui.showInfo("$editor.errorimage");
|
||||
}else if(file.extension().equalsIgnoreCase(oldMapExtension)){
|
||||
editor.beginEdit(world.maps.makeLegacyMap(file));
|
||||
editor.beginEdit(maps.makeLegacyMap(file));
|
||||
}else{
|
||||
editor.beginEdit(MapIO.createMap(file, true));
|
||||
}
|
||||
@@ -286,11 +286,11 @@ public class MapEditorDialog extends Dialog implements Disposable{
|
||||
infoDialog.show();
|
||||
Core.app.post(() -> ui.showError("$editor.save.noname"));
|
||||
}else{
|
||||
Map map = world.maps.all().find(m -> m.name().equals(name));
|
||||
Map map = maps.all().find(m -> m.name().equals(name));
|
||||
if(map != null && !map.custom){
|
||||
handleSaveBuiltin(map);
|
||||
}else{
|
||||
world.maps.saveMap(editor.getTags());
|
||||
maps.saveMap(editor.getTags());
|
||||
ui.showInfoFade("$editor.saved");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,7 +72,7 @@ public class MapGenerateDialog extends FloatingDialog{
|
||||
}).size(160f, 64f);
|
||||
}else{
|
||||
buttons.addButton("$settings.reset", () -> {
|
||||
filters.set(world.maps.readFilters(""));
|
||||
filters.set(maps.readFilters(""));
|
||||
rebuildFilters();
|
||||
update();
|
||||
}).size(160f, 64f);
|
||||
@@ -304,7 +304,7 @@ public class MapGenerateDialog extends FloatingDialog{
|
||||
}
|
||||
|
||||
selection.cont.addButton("$filter.defaultores", () -> {
|
||||
world.maps.addDefaultOres(filters);
|
||||
maps.addDefaultOres(filters);
|
||||
rebuildFilters();
|
||||
update();
|
||||
selection.hide();
|
||||
|
||||
@@ -9,8 +9,6 @@ import io.anuke.mindustry.game.*;
|
||||
import io.anuke.mindustry.io.*;
|
||||
import io.anuke.mindustry.ui.dialogs.*;
|
||||
|
||||
import static io.anuke.mindustry.Vars.world;
|
||||
|
||||
public class MapInfoDialog extends FloatingDialog{
|
||||
private final MapEditor editor;
|
||||
private final WaveInfoDialog waveInfo;
|
||||
@@ -70,7 +68,7 @@ public class MapInfoDialog extends FloatingDialog{
|
||||
t.row();
|
||||
t.add("$editor.generation").padRight(8).left();
|
||||
t.addButton("$edit",
|
||||
() -> generate.show(world.maps.readFilters(editor.getTags().get("genfilters", "")),
|
||||
() -> generate.show(Vars.maps.readFilters(editor.getTags().get("genfilters", "")),
|
||||
filters -> editor.getTags().put("genfilters", JsonIO.write(filters)))
|
||||
).left().width(200f);
|
||||
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
package io.anuke.mindustry.editor;
|
||||
|
||||
import io.anuke.arc.function.Consumer;
|
||||
import io.anuke.arc.function.*;
|
||||
import io.anuke.arc.scene.ui.*;
|
||||
import io.anuke.arc.scene.ui.layout.Table;
|
||||
import io.anuke.arc.util.Scaling;
|
||||
import io.anuke.mindustry.maps.Map;
|
||||
import io.anuke.mindustry.ui.BorderImage;
|
||||
import io.anuke.mindustry.ui.dialogs.FloatingDialog;
|
||||
import io.anuke.arc.scene.ui.layout.*;
|
||||
import io.anuke.arc.util.*;
|
||||
import io.anuke.mindustry.maps.*;
|
||||
import io.anuke.mindustry.ui.*;
|
||||
import io.anuke.mindustry.ui.dialogs.*;
|
||||
|
||||
import static io.anuke.mindustry.Vars.world;
|
||||
import static io.anuke.mindustry.Vars.maps;
|
||||
|
||||
public class MapLoadDialog extends FloatingDialog{
|
||||
private Map selected = null;
|
||||
@@ -17,7 +17,6 @@ public class MapLoadDialog extends FloatingDialog{
|
||||
super("$editor.loadmap");
|
||||
|
||||
shown(this::rebuild);
|
||||
rebuild();
|
||||
|
||||
TextButton button = new TextButton("$load");
|
||||
button.setDisabled(() -> selected == null);
|
||||
@@ -35,8 +34,8 @@ public class MapLoadDialog extends FloatingDialog{
|
||||
|
||||
public void rebuild(){
|
||||
cont.clear();
|
||||
if(world.maps.all().size > 0){
|
||||
selected = world.maps.all().first();
|
||||
if(maps.all().size > 0){
|
||||
selected = maps.all().first();
|
||||
}
|
||||
|
||||
ButtonGroup<TextButton> group = new ButtonGroup<>();
|
||||
@@ -52,7 +51,7 @@ public class MapLoadDialog extends FloatingDialog{
|
||||
ScrollPane pane = new ScrollPane(table, "horizontal");
|
||||
pane.setFadeScrollBars(false);
|
||||
|
||||
for(Map map : world.maps.all()){
|
||||
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);
|
||||
@@ -64,7 +63,7 @@ public class MapLoadDialog extends FloatingDialog{
|
||||
if(++i % maxcol == 0) table.row();
|
||||
}
|
||||
|
||||
if(world.maps.all().size == 0){
|
||||
if(maps.all().size == 0){
|
||||
table.add("$maps.none").center();
|
||||
}else{
|
||||
cont.add("$editor.loadmap");
|
||||
|
||||
@@ -8,7 +8,7 @@ import io.anuke.arc.graphics.Texture;
|
||||
import io.anuke.arc.graphics.g2d.Draw;
|
||||
import io.anuke.arc.graphics.g2d.TextureRegion;
|
||||
import io.anuke.arc.math.Mathf;
|
||||
import io.anuke.arc.util.Disposable;
|
||||
import io.anuke.arc.util.*;
|
||||
import io.anuke.mindustry.content.Blocks;
|
||||
import io.anuke.mindustry.game.Team;
|
||||
import io.anuke.mindustry.graphics.IndexedRenderer;
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
package io.anuke.mindustry.editor;
|
||||
|
||||
import io.anuke.arc.function.Consumer;
|
||||
import io.anuke.arc.scene.ui.TextButton;
|
||||
import io.anuke.arc.scene.ui.TextField;
|
||||
import io.anuke.mindustry.core.Platform;
|
||||
import io.anuke.mindustry.maps.Map;
|
||||
import io.anuke.mindustry.ui.dialogs.FloatingDialog;
|
||||
import io.anuke.arc.function.*;
|
||||
import io.anuke.arc.scene.ui.*;
|
||||
import io.anuke.mindustry.*;
|
||||
import io.anuke.mindustry.core.*;
|
||||
import io.anuke.mindustry.maps.*;
|
||||
import io.anuke.mindustry.ui.dialogs.*;
|
||||
|
||||
import static io.anuke.mindustry.Vars.ui;
|
||||
import static io.anuke.mindustry.Vars.world;
|
||||
|
||||
public class MapSaveDialog extends FloatingDialog{
|
||||
private TextField field;
|
||||
@@ -24,7 +23,7 @@ public class MapSaveDialog extends FloatingDialog{
|
||||
shown(() -> {
|
||||
cont.clear();
|
||||
cont.label(() -> {
|
||||
Map map = world.maps.byName(field.getText());
|
||||
Map map = Vars.maps.byName(field.getText());
|
||||
if(map != null){
|
||||
if(map.custom){
|
||||
return "$editor.overwrite";
|
||||
@@ -69,7 +68,7 @@ public class MapSaveDialog extends FloatingDialog{
|
||||
if(field.getText().isEmpty()){
|
||||
return true;
|
||||
}
|
||||
Map map = world.maps.byName(field.getText());
|
||||
Map map = Vars.maps.byName(field.getText());
|
||||
return map != null && !map.custom;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,13 +52,13 @@ public class WaveInfoDialog extends FloatingDialog{
|
||||
dialog.cont.defaults().size(210f, 64f);
|
||||
dialog.cont.addButton("$waves.copy", () -> {
|
||||
ui.showInfoFade("$waves.copied");
|
||||
Core.app.setClipboardText(world.maps.writeWaves(groups));
|
||||
Core.app.setClipboardText(maps.writeWaves(groups));
|
||||
dialog.hide();
|
||||
}).disabled(b -> groups == null);
|
||||
dialog.cont.row();
|
||||
dialog.cont.addButton("$waves.load", () -> {
|
||||
try{
|
||||
groups = world.maps.readWaves(Core.app.getClipboardText());
|
||||
groups = maps.readWaves(Core.app.getClipboardText());
|
||||
buildGroups();
|
||||
}catch(Exception e){
|
||||
ui.showError("$waves.invalid");
|
||||
|
||||
@@ -1,89 +1,33 @@
|
||||
package io.anuke.mindustry.entities;
|
||||
|
||||
import io.anuke.arc.Core;
|
||||
import io.anuke.arc.collection.Array;
|
||||
import io.anuke.arc.collection.IntMap;
|
||||
import io.anuke.arc.function.Consumer;
|
||||
import io.anuke.arc.function.Predicate;
|
||||
import io.anuke.arc.graphics.Camera;
|
||||
import io.anuke.arc.math.geom.Rectangle;
|
||||
import io.anuke.mindustry.entities.traits.DrawTrait;
|
||||
import io.anuke.mindustry.entities.traits.Entity;
|
||||
|
||||
import static io.anuke.mindustry.Vars.collisions;
|
||||
import io.anuke.arc.collection.*;
|
||||
import io.anuke.mindustry.entities.traits.*;
|
||||
|
||||
/** Simple container for managing entity groups.*/
|
||||
public class Entities{
|
||||
private static final Array<EntityGroup<?>> groupArray = new Array<>();
|
||||
private static final IntMap<EntityGroup<?>> groups = new IntMap<>();
|
||||
private static final Rectangle viewport = new Rectangle();
|
||||
private static final boolean clip = true;
|
||||
private static int count = 0;
|
||||
private final Array<EntityGroup<?>> groupArray = new Array<>();
|
||||
|
||||
public static void clear(){
|
||||
public void clear(){
|
||||
for(EntityGroup group : groupArray){
|
||||
group.clear();
|
||||
}
|
||||
}
|
||||
|
||||
public static EntityGroup<?> getGroup(int id){
|
||||
return groups.get(id);
|
||||
public EntityGroup<?> get(int id){
|
||||
return groupArray.get(id);
|
||||
}
|
||||
|
||||
public static Array<EntityGroup<?>> getAllGroups(){
|
||||
public Array<EntityGroup<?>> all(){
|
||||
return groupArray;
|
||||
}
|
||||
|
||||
public static <T extends Entity> EntityGroup<T> addGroup(Class<T> type){
|
||||
return addGroup(type, true);
|
||||
public <T extends Entity> EntityGroup<T> add(Class<T> type){
|
||||
return add(type, true);
|
||||
}
|
||||
|
||||
public static <T extends Entity> EntityGroup<T> addGroup(Class<T> type, boolean useTree){
|
||||
EntityGroup<T> group = new EntityGroup<>(type, useTree);
|
||||
groups.put(group.getID(), group);
|
||||
public <T extends Entity> EntityGroup<T> add(Class<T> type, boolean useTree){
|
||||
EntityGroup<T> group = new EntityGroup<>(groupArray.size, type, useTree);
|
||||
groupArray.add(group);
|
||||
return group;
|
||||
}
|
||||
|
||||
public static void update(EntityGroup<?> group){
|
||||
group.updateEvents();
|
||||
|
||||
if(group.useTree()){
|
||||
collisions.updatePhysics(group);
|
||||
}
|
||||
|
||||
for(Entity e : group.all()){
|
||||
e.update();
|
||||
}
|
||||
}
|
||||
|
||||
public static int countInBounds(EntityGroup<?> group){
|
||||
count = 0;
|
||||
draw(group, e -> true, e -> count++);
|
||||
return count;
|
||||
}
|
||||
|
||||
public static void draw(EntityGroup<?> group){
|
||||
draw(group, e -> true);
|
||||
}
|
||||
|
||||
public static <T extends DrawTrait> void draw(EntityGroup<?> group, Predicate<T> toDraw){
|
||||
draw(group, toDraw, DrawTrait::draw);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T extends DrawTrait> void draw(EntityGroup<?> group, Predicate<T> toDraw, Consumer<T> cons){
|
||||
if(clip){
|
||||
Camera cam = Core.camera;
|
||||
viewport.set(cam.position.x - cam.width / 2, cam.position.y - cam.height / 2, cam.width, cam.height);
|
||||
}
|
||||
|
||||
for(Entity e : group.all()){
|
||||
if(!(e instanceof DrawTrait) || !toDraw.test((T)e) || !e.isAdded()) continue;
|
||||
DrawTrait draw = (DrawTrait)e;
|
||||
|
||||
if(!clip || viewport.overlaps(draw.getX() - draw.drawSize()/2f, draw.getY() - draw.drawSize()/2f, draw.drawSize(), draw.drawSize())){
|
||||
cons.accept((T)e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,30 +1,34 @@
|
||||
package io.anuke.mindustry.entities;
|
||||
|
||||
import io.anuke.arc.collection.Array;
|
||||
import io.anuke.arc.collection.IntMap;
|
||||
import io.anuke.arc.function.Consumer;
|
||||
import io.anuke.arc.function.Predicate;
|
||||
import io.anuke.arc.math.geom.QuadTree;
|
||||
import io.anuke.arc.math.geom.Rectangle;
|
||||
import io.anuke.arc.util.*;
|
||||
import io.anuke.mindustry.entities.traits.Entity;
|
||||
import io.anuke.arc.*;
|
||||
import io.anuke.arc.collection.*;
|
||||
import io.anuke.arc.function.*;
|
||||
import io.anuke.arc.graphics.*;
|
||||
import io.anuke.arc.math.geom.*;
|
||||
import io.anuke.mindustry.entities.traits.*;
|
||||
|
||||
import static io.anuke.mindustry.Vars.collisions;
|
||||
|
||||
/** Represents a group of a certain type of entity.*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public class EntityGroup<T extends Entity>{
|
||||
private static int lastid;
|
||||
private final boolean useTree;
|
||||
private final int id;
|
||||
private final Class<T> type;
|
||||
private final Array<T> entityArray = new Array<>(false, 16);
|
||||
private final Array<T> entitiesToRemove = new Array<>(false, 16);
|
||||
private final Array<T> entitiesToAdd = new Array<>(false, 16);
|
||||
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 IntMap<T> map;
|
||||
private QuadTree tree;
|
||||
private Consumer<T> removeListener;
|
||||
private Consumer<T> addListener;
|
||||
|
||||
public EntityGroup(Class<T> type, boolean useTree){
|
||||
private final Rectangle viewport = new Rectangle();
|
||||
private int count = 0;
|
||||
|
||||
public EntityGroup(int id, Class<T> type, boolean useTree){
|
||||
this.useTree = useTree;
|
||||
this.id = lastid++;
|
||||
this.id = id;
|
||||
this.type = type;
|
||||
|
||||
if(useTree){
|
||||
@@ -32,6 +36,46 @@ public class EntityGroup<T extends Entity>{
|
||||
}
|
||||
}
|
||||
|
||||
public void update(){
|
||||
updateEvents();
|
||||
|
||||
if(useTree()){
|
||||
collisions.updatePhysics(this);
|
||||
}
|
||||
|
||||
for(Entity e : all()){
|
||||
e.update();
|
||||
}
|
||||
}
|
||||
|
||||
public int countInBounds(){
|
||||
count = 0;
|
||||
draw(e -> true, e -> count++);
|
||||
return count;
|
||||
}
|
||||
|
||||
public void draw(){
|
||||
draw(e -> true);
|
||||
}
|
||||
|
||||
public void draw(Predicate<T> toDraw){
|
||||
draw(toDraw, t -> ((DrawTrait)t).draw());
|
||||
}
|
||||
|
||||
public void draw(Predicate<T> toDraw, Consumer<T> cons){
|
||||
Camera cam = Core.camera;
|
||||
viewport.set(cam.position.x - cam.width / 2, cam.position.y - cam.height / 2, cam.width, cam.height);
|
||||
|
||||
for(Entity e : all()){
|
||||
if(!(e instanceof DrawTrait) || !toDraw.test((T)e) || !e.isAdded()) continue;
|
||||
DrawTrait draw = (DrawTrait)e;
|
||||
|
||||
if(viewport.overlaps(draw.getX() - draw.drawSize()/2f, draw.getY() - draw.drawSize()/2f, draw.drawSize(), draw.drawSize())){
|
||||
cons.accept((T)e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean useTree(){
|
||||
return useTree;
|
||||
}
|
||||
|
||||
@@ -68,13 +68,13 @@ public class Units{
|
||||
|
||||
/** Returns the neareset damaged tile. */
|
||||
public static TileEntity findDamagedTile(Team team, float x, float y){
|
||||
Tile tile = Geometry.findClosest(x, y, world.indexer.getDamaged(team));
|
||||
Tile tile = Geometry.findClosest(x, y, indexer.getDamaged(team));
|
||||
return tile == null ? null : tile.entity;
|
||||
}
|
||||
|
||||
/** Returns the neareset ally tile in a range. */
|
||||
public static TileEntity findAllyTile(Team team, float x, float y, float range, Predicate<Tile> pred){
|
||||
return world.indexer.findTile(team, x, y, range, pred);
|
||||
return indexer.findTile(team, x, y, range, pred);
|
||||
}
|
||||
|
||||
/** Returns the neareset enemy tile in a range. */
|
||||
@@ -82,7 +82,7 @@ public class Units{
|
||||
if(team == Team.derelict) return null;
|
||||
|
||||
for(Team enemy : state.teams.enemiesOf(team)){
|
||||
TileEntity entity = world.indexer.findTile(enemy, x, y, range, pred);
|
||||
TileEntity entity = indexer.findTile(enemy, x, y, range, pred);
|
||||
if(entity != null){
|
||||
return entity;
|
||||
}
|
||||
|
||||
@@ -20,6 +20,10 @@ public interface HealthTrait{
|
||||
default void onDeath(){
|
||||
}
|
||||
|
||||
default boolean damaged(){
|
||||
return health() < maxHealth() - 0.0001f;
|
||||
}
|
||||
|
||||
default void damage(float amount){
|
||||
health(health() - amount);
|
||||
if(health() <= 0 && !isDead()){
|
||||
|
||||
@@ -16,6 +16,7 @@ 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.units.CommandCenter.*;
|
||||
import io.anuke.mindustry.world.blocks.units.UnitFactory.*;
|
||||
import io.anuke.mindustry.world.meta.*;
|
||||
|
||||
@@ -25,7 +26,6 @@ import static io.anuke.mindustry.Vars.*;
|
||||
|
||||
/** Base class for AI units. */
|
||||
public abstract class BaseUnit extends Unit implements ShooterTrait{
|
||||
|
||||
protected static int timerIndex = 0;
|
||||
|
||||
protected static final int timerTarget = timerIndex++;
|
||||
@@ -83,6 +83,22 @@ public abstract class BaseUnit extends Unit implements ShooterTrait{
|
||||
return world.tile(spawner);
|
||||
}
|
||||
|
||||
public boolean isCommanded(){
|
||||
return indexer.getAllied(team, BlockFlag.comandCenter).size != 0 && indexer.getAllied(team, BlockFlag.comandCenter).first().entity instanceof CommandCenterEntity;
|
||||
}
|
||||
|
||||
public UnitCommand getCommand(){
|
||||
if(isCommanded()){
|
||||
return indexer.getAllied(team, BlockFlag.comandCenter).first().<CommandCenterEntity>entity().command;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**Called when a command is recieved from the command center.*/
|
||||
public void onCommand(UnitCommand command){
|
||||
|
||||
}
|
||||
|
||||
/** Initialize the type and team of this unit. Only call once! */
|
||||
public void init(UnitType type, Team team){
|
||||
if(this.type != null) throw new RuntimeException("This unit is already initialized!");
|
||||
@@ -129,12 +145,12 @@ public abstract class BaseUnit extends Unit implements ShooterTrait{
|
||||
}
|
||||
|
||||
public void targetClosestAllyFlag(BlockFlag flag){
|
||||
Tile target = Geometry.findClosest(x, y, world.indexer.getAllied(team, flag));
|
||||
Tile target = Geometry.findClosest(x, y, indexer.getAllied(team, flag));
|
||||
if(target != null) this.target = target.entity;
|
||||
}
|
||||
|
||||
public void targetClosestEnemyFlag(BlockFlag flag){
|
||||
Tile target = Geometry.findClosest(x, y, world.indexer.getEnemy(team, flag));
|
||||
Tile target = Geometry.findClosest(x, y, indexer.getEnemy(team, flag));
|
||||
if(target != null) this.target = target.entity;
|
||||
}
|
||||
|
||||
@@ -303,6 +319,10 @@ public abstract class BaseUnit extends Unit implements ShooterTrait{
|
||||
state.set(getStartState());
|
||||
|
||||
health(maxHealth());
|
||||
|
||||
if(isCommanded()){
|
||||
onCommand(getCommand());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,20 +1,19 @@
|
||||
package io.anuke.mindustry.entities.type;
|
||||
|
||||
import io.anuke.arc.graphics.Color;
|
||||
import io.anuke.arc.graphics.g2d.Draw;
|
||||
import io.anuke.arc.graphics.g2d.Fill;
|
||||
import io.anuke.arc.math.Angles;
|
||||
import io.anuke.arc.math.Mathf;
|
||||
import io.anuke.arc.math.geom.Vector2;
|
||||
import io.anuke.arc.util.Time;
|
||||
import io.anuke.arc.util.Tmp;
|
||||
import io.anuke.mindustry.entities.Predict;
|
||||
import io.anuke.mindustry.entities.Units;
|
||||
import io.anuke.mindustry.entities.bullet.BulletType;
|
||||
import io.anuke.mindustry.entities.units.UnitState;
|
||||
import io.anuke.mindustry.graphics.Pal;
|
||||
import io.anuke.mindustry.net.Net;
|
||||
import io.anuke.mindustry.world.meta.BlockFlag;
|
||||
import io.anuke.arc.graphics.*;
|
||||
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.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.*;
|
||||
|
||||
import static io.anuke.mindustry.Vars.*;
|
||||
|
||||
public abstract class FlyingUnit extends BaseUnit{
|
||||
protected float[] weaponAngles = {0, 0};
|
||||
@@ -80,14 +79,40 @@ public abstract class FlyingUnit extends BaseUnit{
|
||||
return;
|
||||
}
|
||||
|
||||
target = getClosestCore();
|
||||
};
|
||||
target = getSpawner();
|
||||
if(target == null) target = getClosestCore();
|
||||
}
|
||||
|
||||
if(target != null){
|
||||
circle(60f + Mathf.absin(Time.time() + Mathf.randomSeed(id) * 1200f, 70f, 1200f));
|
||||
circle(80f + Mathf.randomSeed(id) * 120);
|
||||
}
|
||||
}
|
||||
};
|
||||
},
|
||||
retreat = new UnitState(){
|
||||
public void entered(){
|
||||
target = null;
|
||||
}
|
||||
|
||||
public void update(){
|
||||
if(retarget()){
|
||||
target = getSpawner();
|
||||
|
||||
Tile repair = Geometry.findClosest(x, y, indexer.getAllied(team, BlockFlag.repair));
|
||||
if(repair != null && damaged()) FlyingUnit.this.target = repair.entity;
|
||||
if(target == null) target = getClosestCore();
|
||||
}
|
||||
|
||||
circle(targetHasFlag(BlockFlag.repair) ? 20f : 60f + Mathf.randomSeed(id) * 50, 0.65f * type.speed);
|
||||
}
|
||||
};;
|
||||
|
||||
@Override
|
||||
public void onCommand(UnitCommand command){
|
||||
state.set(command == UnitCommand.retreat ? retreat :
|
||||
command == UnitCommand.attack ? attack :
|
||||
command == UnitCommand.patrol ? patrol :
|
||||
null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void move(float x, float y){
|
||||
|
||||
@@ -1,22 +1,20 @@
|
||||
package io.anuke.mindustry.entities.type;
|
||||
|
||||
import io.anuke.arc.graphics.Color;
|
||||
import io.anuke.arc.graphics.g2d.Draw;
|
||||
import io.anuke.arc.math.Angles;
|
||||
import io.anuke.arc.math.Mathf;
|
||||
import io.anuke.arc.math.geom.Vector2;
|
||||
import io.anuke.arc.util.Time;
|
||||
import io.anuke.mindustry.Vars;
|
||||
import io.anuke.mindustry.entities.Predict;
|
||||
import io.anuke.mindustry.entities.Units;
|
||||
import io.anuke.mindustry.entities.bullet.BulletType;
|
||||
import io.anuke.mindustry.entities.units.UnitState;
|
||||
import io.anuke.mindustry.game.Team;
|
||||
import io.anuke.mindustry.type.Weapon;
|
||||
import io.anuke.mindustry.world.Tile;
|
||||
import io.anuke.mindustry.world.blocks.Floor;
|
||||
import io.anuke.arc.graphics.*;
|
||||
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.game.*;
|
||||
import io.anuke.mindustry.type.*;
|
||||
import io.anuke.mindustry.world.*;
|
||||
import io.anuke.mindustry.world.blocks.*;
|
||||
|
||||
import static io.anuke.mindustry.Vars.world;
|
||||
import static io.anuke.mindustry.Vars.*;
|
||||
|
||||
public abstract class GroundUnit extends BaseUnit{
|
||||
protected static Vector2 vec = new Vector2();
|
||||
@@ -63,8 +61,25 @@ public abstract class GroundUnit extends BaseUnit{
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
retreat = new UnitState(){
|
||||
public void entered(){
|
||||
target = null;
|
||||
}
|
||||
|
||||
public void update(){
|
||||
moveAwayFromCore();
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public void onCommand(UnitCommand command){
|
||||
state.set(command == UnitCommand.retreat ? retreat :
|
||||
command == UnitCommand.attack ? attack :
|
||||
command == UnitCommand.patrol ? patrol :
|
||||
null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void interpolate(){
|
||||
super.interpolate();
|
||||
@@ -182,9 +197,9 @@ public abstract class GroundUnit extends BaseUnit{
|
||||
protected void patrol(){
|
||||
vec.trns(baseRotation, type.speed * Time.delta());
|
||||
velocity.add(vec.x, vec.y);
|
||||
vec.trns(baseRotation, type.hitsizeTile * 3);
|
||||
vec.trns(baseRotation, type.hitsizeTile * 5);
|
||||
Tile tile = world.tileWorld(x + vec.x, y + vec.y);
|
||||
if((tile == null || tile.solid() || tile.floor().drownTime > 0) || stuckTime > 10f){
|
||||
if((tile == null || tile.solid() || tile.floor().drownTime > 0 || tile.floor().isLiquid) || stuckTime > 10f){
|
||||
baseRotation += Mathf.sign(id % 2 - 0.5f) * Time.delta() * 3f;
|
||||
}
|
||||
|
||||
@@ -208,7 +223,7 @@ public abstract class GroundUnit extends BaseUnit{
|
||||
protected void moveToCore(){
|
||||
Tile tile = world.tileWorld(x, y);
|
||||
if(tile == null) return;
|
||||
Tile targetTile = world.pathfinder.getTargetTile(team, tile);
|
||||
Tile targetTile = pathfinder.getTargetTile(team, tile);
|
||||
|
||||
if(tile == targetTile) return;
|
||||
|
||||
@@ -231,10 +246,10 @@ public abstract class GroundUnit extends BaseUnit{
|
||||
|
||||
Tile tile = world.tileWorld(x, y);
|
||||
if(tile == null) return;
|
||||
Tile targetTile = world.pathfinder.getTargetTile(enemy, tile);
|
||||
Tile targetTile = pathfinder.getTargetTile(enemy, tile);
|
||||
TileEntity core = getClosestCore();
|
||||
|
||||
if(tile == targetTile || core == null || dst(core) < 90f) return;
|
||||
if(tile == targetTile || core == null || dst(core) < 120f) return;
|
||||
|
||||
velocity.add(vec.trns(angleTo(targetTile), type.speed * Time.delta()));
|
||||
rotation = Mathf.slerpDelta(rotation, baseRotation, type.rotatespeed);
|
||||
|
||||
@@ -14,6 +14,7 @@ import io.anuke.arc.util.*;
|
||||
import io.anuke.arc.util.pooling.Pools;
|
||||
import io.anuke.mindustry.Vars;
|
||||
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.*;
|
||||
@@ -731,7 +732,7 @@ public class Player extends Unit implements BuilderMinerTrait, ShooterTrait{
|
||||
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, world.indexer.getDamaged(Team.sharded));
|
||||
target = Geometry.findClosest(x, y, indexer.getDamaged(Team.sharded));
|
||||
if(target != null && dst(target) > getWeapon().bullet.range()){
|
||||
target = null;
|
||||
}else if(target != null){
|
||||
@@ -774,6 +775,31 @@ public class Player extends Unit implements BuilderMinerTrait, ShooterTrait{
|
||||
|
||||
//region utility methods
|
||||
|
||||
public void sendMessage(String text){
|
||||
if(isLocal){
|
||||
if(Vars.ui != null){
|
||||
Log.info("add " + text);
|
||||
Vars.ui.chatfrag.addMessage(text, null);
|
||||
}
|
||||
}else{
|
||||
Call.sendMessage(con.id, text, null, null);
|
||||
}
|
||||
}
|
||||
|
||||
public void sendMessage(String text, Player from){
|
||||
sendMessage(text, from, NetClient.colorizeName(from.id, from.name));
|
||||
}
|
||||
|
||||
public void sendMessage(String text, Player from, String fromName){
|
||||
if(isLocal){
|
||||
if(Vars.ui != null){
|
||||
Vars.ui.chatfrag.addMessage(text, fromName);
|
||||
}
|
||||
}else{
|
||||
Call.sendMessage(con.id, text, fromName, from);
|
||||
}
|
||||
}
|
||||
|
||||
/** Resets all values of the player. */
|
||||
public void reset(){
|
||||
resetNoAdd();
|
||||
|
||||
@@ -52,7 +52,7 @@ public class TileEntity extends BaseEntity implements TargetTrait, HealthTrait{
|
||||
tile.entity.health = health;
|
||||
|
||||
if(tile.entity.damaged()){
|
||||
world.indexer.notifyTileDamaged(tile.entity);
|
||||
indexer.notifyTileDamaged(tile.entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -171,14 +171,10 @@ public class TileEntity extends BaseEntity implements TargetTrait, HealthTrait{
|
||||
}
|
||||
|
||||
if(preHealth >= maxHealth() - 0.00001f && health < maxHealth() && world != null){ //when just damaged
|
||||
world.indexer.notifyTileDamaged(this);
|
||||
indexer.notifyTileDamaged(this);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean damaged(){
|
||||
return health < maxHealth() - 0.00001f;
|
||||
}
|
||||
|
||||
public Tile getTile(){
|
||||
return tile;
|
||||
}
|
||||
|
||||
@@ -256,7 +256,7 @@ public abstract class Unit extends DestructibleEntity implements SaveTrait, Targ
|
||||
//apply knockback based on spawns
|
||||
if(getTeam() != waveTeam){
|
||||
float relativeSize = state.rules.dropZoneRadius + getSize()/2f + 1f;
|
||||
for(Tile spawn : world.spawner.getGroundSpawns()){
|
||||
for(Tile spawn : spawner.getGroundSpawns()){
|
||||
if(withinDst(spawn.worldx(), spawn.worldy(), relativeSize)){
|
||||
velocity.add(Tmp.v1.set(this).sub(spawn.worldx(), spawn.worldy()).setLength(0.1f + 1f - dst(spawn) / relativeSize).scl(0.45f * Time.delta()));
|
||||
}
|
||||
|
||||
@@ -3,11 +3,11 @@ package io.anuke.mindustry.entities.type.base;
|
||||
import io.anuke.arc.math.Mathf;
|
||||
import io.anuke.arc.math.geom.Geometry;
|
||||
import io.anuke.mindustry.entities.type.FlyingUnit;
|
||||
import io.anuke.mindustry.entities.units.UnitState;
|
||||
import io.anuke.mindustry.entities.units.*;
|
||||
import io.anuke.mindustry.world.Tile;
|
||||
import io.anuke.mindustry.world.meta.BlockFlag;
|
||||
|
||||
import static io.anuke.mindustry.Vars.world;
|
||||
import static io.anuke.mindustry.Vars.*;
|
||||
|
||||
public abstract class BaseDrone extends FlyingUnit{
|
||||
public final UnitState retreat = new UnitState(){
|
||||
@@ -20,7 +20,7 @@ public abstract class BaseDrone extends FlyingUnit{
|
||||
state.set(getStartState());
|
||||
}else if(!targetHasFlag(BlockFlag.repair)){
|
||||
if(retarget()){
|
||||
Tile repairPoint = Geometry.findClosest(x, y, world.indexer.getAllied(team, BlockFlag.repair));
|
||||
Tile repairPoint = Geometry.findClosest(x, y, indexer.getAllied(team, BlockFlag.repair));
|
||||
if(repairPoint != null){
|
||||
target = repairPoint;
|
||||
}else{
|
||||
@@ -33,6 +33,11 @@ public abstract class BaseDrone extends FlyingUnit{
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public void onCommand(UnitCommand command){
|
||||
//do nothing, normal commands are not applicable here
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateRotation(){
|
||||
if(target != null && shouldRotate() && target.dst(this) < type.range){
|
||||
@@ -44,7 +49,7 @@ public abstract class BaseDrone extends FlyingUnit{
|
||||
|
||||
@Override
|
||||
public void behavior(){
|
||||
if(health <= maxHealth() * type.retreatPercent && !state.is(retreat) && Geometry.findClosest(x, y, world.indexer.getAllied(team, BlockFlag.repair)) != null){
|
||||
if(health <= maxHealth() * type.retreatPercent && !state.is(retreat) && Geometry.findClosest(x, y, indexer.getAllied(team, BlockFlag.repair)) != null){
|
||||
setState(retreat);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ import io.anuke.mindustry.world.Tile;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
import static io.anuke.mindustry.Vars.world;
|
||||
import static io.anuke.mindustry.Vars.*;
|
||||
|
||||
/** A drone that only mines.*/
|
||||
public class MinerDrone extends BaseDrone implements MinerTrait{
|
||||
@@ -46,7 +46,7 @@ public class MinerDrone extends BaseDrone implements MinerTrait{
|
||||
setState(drop);
|
||||
}else{
|
||||
if(retarget() && targetItem != null){
|
||||
target = world.indexer.findClosestOre(x, y, targetItem);
|
||||
target = indexer.findClosestOre(x, y, targetItem);
|
||||
}
|
||||
|
||||
if(target instanceof Tile){
|
||||
@@ -174,6 +174,6 @@ public class MinerDrone extends BaseDrone implements MinerTrait{
|
||||
if(entity == null){
|
||||
return;
|
||||
}
|
||||
targetItem = Structs.findMin(type.toMine, world.indexer::hasOre, (a, b) -> -Integer.compare(entity.items.get(a), entity.items.get(b)));
|
||||
targetItem = Structs.findMin(type.toMine, indexer::hasOre, (a, b) -> -Integer.compare(entity.items.get(a), entity.items.get(b)));
|
||||
}
|
||||
}
|
||||
|
||||
18
core/src/io/anuke/mindustry/entities/units/UnitCommand.java
Normal file
18
core/src/io/anuke/mindustry/entities/units/UnitCommand.java
Normal file
@@ -0,0 +1,18 @@
|
||||
package io.anuke.mindustry.entities.units;
|
||||
|
||||
import io.anuke.arc.*;
|
||||
|
||||
public enum UnitCommand{
|
||||
attack, retreat, patrol;
|
||||
|
||||
private final String localized;
|
||||
public static final UnitCommand[] all = values();
|
||||
|
||||
UnitCommand(){
|
||||
localized = Core.bundle.get("command." + name());
|
||||
}
|
||||
|
||||
public String localized(){
|
||||
return localized;
|
||||
}
|
||||
}
|
||||
@@ -19,7 +19,7 @@ public abstract class Content{
|
||||
*/
|
||||
public abstract ContentType getContentType();
|
||||
|
||||
/** Called after all content is created. Do not use to load regions or texture data! */
|
||||
/** Called after all content and modules are created. Do not use to load regions or texture data! */
|
||||
public void init(){
|
||||
}
|
||||
|
||||
|
||||
@@ -27,8 +27,8 @@ public class EventType{
|
||||
}
|
||||
}
|
||||
|
||||
/** Called when the game is first loaded. */
|
||||
public static class GameLoadEvent{
|
||||
/** Called when the client game is first loaded. */
|
||||
public static class ClientLoadEvent{
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,6 @@ public enum Gamemode{
|
||||
rules.respawnTime = 0f;
|
||||
}),
|
||||
attack(rules -> {
|
||||
rules.enemyCheat = true;
|
||||
rules.unitDrops = true;
|
||||
rules.attackMode = true;
|
||||
rules.waves = true;
|
||||
|
||||
@@ -2,11 +2,16 @@ package io.anuke.mindustry.game;
|
||||
|
||||
import io.anuke.arc.*;
|
||||
import io.anuke.arc.collection.*;
|
||||
import io.anuke.arc.files.*;
|
||||
import io.anuke.arc.util.io.*;
|
||||
import io.anuke.mindustry.*;
|
||||
import io.anuke.mindustry.content.*;
|
||||
import io.anuke.mindustry.game.EventType.*;
|
||||
import io.anuke.mindustry.type.*;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.zip.*;
|
||||
|
||||
import static io.anuke.mindustry.Vars.*;
|
||||
|
||||
/** Stores player unlocks. Clientside only. */
|
||||
@@ -29,6 +34,43 @@ public class GlobalData{
|
||||
});
|
||||
}
|
||||
|
||||
public void exportData(FileHandle file) throws IOException{
|
||||
Array<FileHandle> files = new Array<>();
|
||||
files.add(Core.settings.getSettingsFile());
|
||||
files.addAll(customMapDirectory.list());
|
||||
files.addAll(saveDirectory.list());
|
||||
String base = Core.settings.getDataDirectory().path();
|
||||
|
||||
try(OutputStream fos = file.write(false, 2048); ZipOutputStream zos = new ZipOutputStream(fos)){
|
||||
for(FileHandle add : files){
|
||||
zos.putNextEntry(new ZipEntry(add.path().substring(base.length())));
|
||||
Streams.copyStream(add.read(), zos);
|
||||
zos.closeEntry();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public void importData(FileHandle file){
|
||||
FileHandle zipped = new ZipFileHandle(file);
|
||||
|
||||
FileHandle base = Core.settings.getDataDirectory();
|
||||
if(!base.child("settings.bin").exists()){
|
||||
throw new IllegalArgumentException("Not valid save data.");
|
||||
}
|
||||
|
||||
//purge existing data
|
||||
for(FileHandle f : base.list()){
|
||||
if(f.isDirectory()){
|
||||
f.deleteDirectory();
|
||||
}else{
|
||||
f.delete();
|
||||
}
|
||||
}
|
||||
|
||||
zipped.walk(f -> f.copyTo(base.child(f.path())));
|
||||
}
|
||||
|
||||
public void modified(){
|
||||
modified = true;
|
||||
}
|
||||
|
||||
@@ -14,15 +14,12 @@ import static io.anuke.mindustry.Vars.*;
|
||||
|
||||
/** Controls playback of multiple music tracks.*/
|
||||
public class MusicControl{
|
||||
private static final float finTime = 120f, foutTime = 120f, musicInterval = 60 * 60 * 3f, musicChance = 0.45f, musicWaveChance = 0.35f;
|
||||
private static final float finTime = 120f, foutTime = 120f, musicInterval = 60 * 60 * 3f, musicChance = 0.5f, musicWaveChance = 0.4f;
|
||||
|
||||
/** normal, ambient music, plays at any time */
|
||||
public final Array<Music> ambientMusic = Array.with(Musics.game1, Musics.game3, Musics.game4, Musics.game6);
|
||||
/** darker music, used in times of conflict */
|
||||
public final Array<Music> darkMusic = Array.with(Musics.game2, Musics.game5, Musics.game7);
|
||||
/** all music, both dark and ambient */
|
||||
public final Array<Music> allMusic = Array.withArrays(ambientMusic, darkMusic);
|
||||
|
||||
private Music lastRandomPlayed;
|
||||
private Interval timer = new Interval();
|
||||
private @Nullable Music current;
|
||||
@@ -88,7 +85,7 @@ public class MusicControl{
|
||||
}
|
||||
|
||||
//dark based on enemies
|
||||
return Mathf.chance(state.enemies() / 70f);
|
||||
return Mathf.chance(state.enemies() / 70f + 0.1f);
|
||||
}
|
||||
|
||||
/** Plays and fades in a music track. This must be called every frame.
|
||||
|
||||
@@ -161,7 +161,7 @@ public class Tutorial{
|
||||
}
|
||||
},
|
||||
deposit(() -> event("deposit")),
|
||||
waves(() -> state.wave > 2 && state.enemies() <= 0 && !world.spawner.isSpawning()){
|
||||
waves(() -> state.wave > 2 && state.enemies() <= 0 && !spawner.isSpawning()){
|
||||
void begin(){
|
||||
state.rules.waveTimer = true;
|
||||
logic.runWave();
|
||||
|
||||
@@ -85,4 +85,6 @@ public class Drawf{
|
||||
float oy = 17f / 63f * length;
|
||||
Draw.rect(Core.atlas.find("shape-3"), x, y - oy + length / 2f, width, length, width / 2f, oy, rotation - 90);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ public class FloorRenderer implements Disposable{
|
||||
private final static int chunksize = 64;
|
||||
|
||||
private Chunk[][] cache;
|
||||
private CacheBatch cbatch;
|
||||
private MultiCacheBatch cbatch;
|
||||
private IntSet drawnLayerSet = new IntSet();
|
||||
private IntArray drawnLayers = new IntArray();
|
||||
private ObjectSet<CacheLayer> used = new ObjectSet<>();
|
||||
@@ -185,7 +185,7 @@ public class FloorRenderer implements Disposable{
|
||||
floor = tile.floor();
|
||||
}
|
||||
|
||||
if(tile.block().cacheLayer == layer && layer == CacheLayer.walls){
|
||||
if(tile.block().cacheLayer == layer && layer == CacheLayer.walls && !(tile.isDarkened() && tile.rotation() >= 5)){
|
||||
tile.block().draw(tile);
|
||||
}else if(floor.cacheLayer == layer && (world.isAccessible(tile.x, tile.y) || tile.block().cacheLayer != CacheLayer.walls || !tile.block().fillsTile)){
|
||||
floor.draw(tile);
|
||||
@@ -204,8 +204,7 @@ public class FloorRenderer implements Disposable{
|
||||
int chunksx = Mathf.ceil((float)(world.width()) / chunksize),
|
||||
chunksy = Mathf.ceil((float)(world.height()) / chunksize);
|
||||
cache = new Chunk[chunksx][chunksy];
|
||||
SpriteCache sprites = new SpriteCache(world.width() * world.height() * 6, (world.width() / chunksize) * (world.height() / chunksize) * 2, false);
|
||||
cbatch = new CacheBatch(sprites);
|
||||
cbatch = new MultiCacheBatch(chunksize * chunksize * 4);
|
||||
|
||||
Time.mark();
|
||||
|
||||
|
||||
@@ -96,7 +96,7 @@ public class OverlayRenderer{
|
||||
Lines.stroke(2f);
|
||||
Draw.color(Color.GRAY, Color.LIGHT_GRAY, Mathf.absin(Time.time(), 8f, 1f));
|
||||
|
||||
for(Tile tile : world.spawner.getGroundSpawns()){
|
||||
for(Tile tile : spawner.getGroundSpawns()){
|
||||
if(tile.withinDst(player.x, player.y, state.rules.dropZoneRadius + spawnerMargin)){
|
||||
Draw.alpha(Mathf.clamp(1f - (player.dst(tile) - state.rules.dropZoneRadius) / spawnerMargin));
|
||||
Lines.dashCircle(tile.worldx(), tile.worldy(), state.rules.dropZoneRadius);
|
||||
|
||||
@@ -55,7 +55,7 @@ public class Pixelator implements Disposable{
|
||||
Draw.rect(Draw.wrap(buffer.getTexture()), Core.camera.position.x, Core.camera.position.y, Core.camera.width, -Core.camera.height);
|
||||
Draw.blend();
|
||||
|
||||
renderer.draw(playerGroup, p -> !p.isDead() && !p.isLocal, Player::drawName);
|
||||
playerGroup.draw(p -> !p.isDead() && !p.isLocal, Player::drawName);
|
||||
|
||||
Core.camera.position.set(px, py);
|
||||
Core.settings.put("animatedwater", hadWater);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package io.anuke.mindustry.graphics;
|
||||
|
||||
import io.anuke.annotations.Annotations.*;
|
||||
import io.anuke.arc.Core;
|
||||
import io.anuke.arc.graphics.Color;
|
||||
import io.anuke.arc.graphics.g2d.TextureRegion;
|
||||
@@ -10,7 +11,7 @@ import io.anuke.arc.util.Time;
|
||||
public class Shaders{
|
||||
public static Shadow shadow;
|
||||
public static BlockBuild blockbuild;
|
||||
public static Shield shield;
|
||||
public static @Nullable Shield shield;
|
||||
public static UnitBuild build;
|
||||
public static FogShader fog;
|
||||
public static MenuShader menu;
|
||||
@@ -19,7 +20,13 @@ public class Shaders{
|
||||
public static void init(){
|
||||
shadow = new Shadow();
|
||||
blockbuild = new BlockBuild();
|
||||
shield = new Shield();
|
||||
try{
|
||||
shield = new Shield();
|
||||
}catch(Throwable t){
|
||||
//don't load shield shader
|
||||
shield = null;
|
||||
t.printStackTrace();
|
||||
}
|
||||
build = new UnitBuild();
|
||||
fog = new FogShader();
|
||||
menu = new MenuShader();
|
||||
|
||||
@@ -21,6 +21,7 @@ public enum Binding implements KeyBind{
|
||||
zoom_hold(KeyCode.CONTROL_LEFT, "view"),
|
||||
zoom(new Axis(KeyCode.SCROLL)),
|
||||
menu(Core.app.getType() == ApplicationType.Android ? KeyCode.BACK : KeyCode.ESCAPE),
|
||||
fullscreen(KeyCode.F11),
|
||||
pause(KeyCode.SPACE),
|
||||
minimap(KeyCode.M),
|
||||
toggle_menus(KeyCode.C),
|
||||
|
||||
@@ -33,7 +33,7 @@ public class MobileInput extends InputHandler implements GestureListener{
|
||||
|
||||
//gesture data
|
||||
private Vector2 vector = new Vector2();
|
||||
private float lastDistance = -1f;
|
||||
private float lastZoom = -1;
|
||||
|
||||
/** Position where the player started dragging a line. */
|
||||
private int lineStartX, lineStartY;
|
||||
@@ -469,6 +469,7 @@ public class MobileInput extends InputHandler implements GestureListener{
|
||||
|
||||
@Override
|
||||
public boolean touchUp(int screenX, int screenY, int pointer, KeyCode button){
|
||||
lastZoom = renderer.getScale();
|
||||
|
||||
//place down a line if in line mode
|
||||
if(lineMode){
|
||||
@@ -728,11 +729,11 @@ public class MobileInput extends InputHandler implements GestureListener{
|
||||
@Override
|
||||
public boolean zoom(float initialDistance, float distance){
|
||||
if(Core.settings.getBool("keyboard")) return false;
|
||||
if(lastDistance == -1) lastDistance = initialDistance;
|
||||
if(lastZoom < 0){
|
||||
lastZoom = renderer.getScale();
|
||||
}
|
||||
|
||||
float amount = (Mathf.sign(distance > lastDistance) * 0.04f) * Time.delta();
|
||||
renderer.scaleCamera(UnitScl.dp.scl(amount));
|
||||
lastDistance = distance;
|
||||
renderer.setScale(distance / initialDistance * lastZoom);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
package io.anuke.mindustry.io;
|
||||
|
||||
import io.anuke.arc.Core;
|
||||
import io.anuke.arc.files.FileHandle;
|
||||
import io.anuke.arc.util.*;
|
||||
import io.anuke.mindustry.Vars;
|
||||
import io.anuke.mindustry.input.Binding;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import static io.anuke.mindustry.Vars.headless;
|
||||
|
||||
public class BundleLoader{
|
||||
|
||||
public static void load(){
|
||||
Core.settings.defaults("locale", "default");
|
||||
Core.keybinds.setDefaults(Binding.values());
|
||||
Core.settings.load();
|
||||
loadBundle();
|
||||
}
|
||||
|
||||
private static Locale getLocale(){
|
||||
String loc = Core.settings.getString("locale");
|
||||
if(loc.equals("default")){
|
||||
return Locale.getDefault();
|
||||
}else{
|
||||
Locale lastLocale;
|
||||
if(loc.contains("_")){
|
||||
String[] split = loc.split("_");
|
||||
lastLocale = new Locale(split[0], split[1]);
|
||||
}else{
|
||||
lastLocale = new Locale(loc);
|
||||
}
|
||||
|
||||
return lastLocale;
|
||||
}
|
||||
}
|
||||
|
||||
private static void loadBundle(){
|
||||
if(headless) return;
|
||||
|
||||
try{
|
||||
//try loading external bundle
|
||||
FileHandle handle = Core.files.local("bundle");
|
||||
|
||||
Locale locale = Locale.ENGLISH;
|
||||
Core.bundle = I18NBundle.createBundle(handle, locale);
|
||||
|
||||
Log.info("NOTE: external translation bundle has been loaded.");
|
||||
if(!headless){
|
||||
Time.run(10f, () -> Vars.ui.showInfo("Note: You have successfully loaded an external translation bundle."));
|
||||
}
|
||||
}catch(Throwable e){
|
||||
//no external bundle found
|
||||
|
||||
FileHandle handle = Core.files.internal("bundles/bundle");
|
||||
|
||||
Locale locale = getLocale();
|
||||
Locale.setDefault(locale);
|
||||
Core.bundle = I18NBundle.createBundle(handle, locale);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
package io.anuke.mindustry.io;
|
||||
|
||||
import io.anuke.arc.collection.*;
|
||||
import io.anuke.mindustry.game.Rules;
|
||||
import io.anuke.mindustry.maps.Map;
|
||||
import io.anuke.mindustry.game.*;
|
||||
import io.anuke.mindustry.maps.*;
|
||||
|
||||
import static io.anuke.mindustry.Vars.world;
|
||||
import static io.anuke.mindustry.Vars.maps;
|
||||
|
||||
public class SaveMeta{
|
||||
public int version;
|
||||
@@ -21,7 +21,7 @@ public class SaveMeta{
|
||||
this.build = build;
|
||||
this.timestamp = timestamp;
|
||||
this.timePlayed = timePlayed;
|
||||
this.map = world.maps.all().find(m -> m.name().equals(map));
|
||||
this.map = maps.all().find(m -> m.name().equals(map));
|
||||
this.wave = wave;
|
||||
this.rules = rules;
|
||||
this.tags = tags;
|
||||
|
||||
@@ -81,7 +81,7 @@ public abstract class SaveVersion extends SaveFileReader{
|
||||
if(state.rules.spawns.isEmpty()) state.rules.spawns = defaultWaves.get();
|
||||
lastReadBuild = map.getInt("build", -1);
|
||||
|
||||
Map worldmap = world.maps.byName(map.get("mapname", "\\\\\\"));
|
||||
Map worldmap = maps.byName(map.get("mapname", "\\\\\\"));
|
||||
world.setMap(worldmap == null ? new Map(StringMap.of(
|
||||
"name", map.get("mapname", "Unknown"),
|
||||
"width", 1,
|
||||
@@ -209,7 +209,7 @@ public abstract class SaveVersion extends SaveFileReader{
|
||||
//write entity chunk
|
||||
int groups = 0;
|
||||
|
||||
for(EntityGroup<?> group : Entities.getAllGroups()){
|
||||
for(EntityGroup<?> group : entities.all()){
|
||||
if(!group.isEmpty() && group.all().get(0) instanceof SaveTrait){
|
||||
groups++;
|
||||
}
|
||||
@@ -217,7 +217,7 @@ public abstract class SaveVersion extends SaveFileReader{
|
||||
|
||||
stream.writeByte(groups);
|
||||
|
||||
for(EntityGroup<?> group : Entities.getAllGroups()){
|
||||
for(EntityGroup<?> group : entities.all()){
|
||||
if(!group.isEmpty() && group.all().get(0) instanceof SaveTrait){
|
||||
stream.writeInt(group.size());
|
||||
for(Entity entity : group.all()){
|
||||
|
||||
@@ -5,12 +5,12 @@ 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.Entities;
|
||||
import io.anuke.mindustry.entities.bullet.Bullet;
|
||||
import io.anuke.mindustry.entities.bullet.BulletType;
|
||||
import io.anuke.mindustry.entities.traits.BuilderTrait.BuildRequest;
|
||||
import io.anuke.mindustry.entities.traits.ShooterTrait;
|
||||
import io.anuke.mindustry.entities.type.*;
|
||||
import io.anuke.mindustry.entities.units.*;
|
||||
import io.anuke.mindustry.game.Team;
|
||||
import io.anuke.mindustry.net.Administration.TraceInfo;
|
||||
import io.anuke.mindustry.net.Packets.AdminAction;
|
||||
@@ -57,7 +57,7 @@ public class TypeIO{
|
||||
byte gid = buffer.get();
|
||||
if(gid == -1) return null;
|
||||
int id = buffer.getInt();
|
||||
return (Unit)Entities.getGroup(gid).getByID(id);
|
||||
return (Unit)entities.get(gid).getByID(id);
|
||||
}
|
||||
|
||||
@WriteClass(ShooterTrait.class)
|
||||
@@ -70,7 +70,7 @@ public class TypeIO{
|
||||
public static ShooterTrait readShooter(ByteBuffer buffer){
|
||||
byte gid = buffer.get();
|
||||
int id = buffer.getInt();
|
||||
return (ShooterTrait)Entities.getGroup(gid).getByID(id);
|
||||
return (ShooterTrait)entities.get(gid).getByID(id);
|
||||
}
|
||||
|
||||
@WriteClass(Bullet.class)
|
||||
@@ -177,6 +177,16 @@ public class TypeIO{
|
||||
return Team.all[buffer.get()];
|
||||
}
|
||||
|
||||
@WriteClass(UnitCommand.class)
|
||||
public static void writeUnitCommand(ByteBuffer buffer, UnitCommand reason){
|
||||
buffer.put((byte)reason.ordinal());
|
||||
}
|
||||
|
||||
@ReadClass(UnitCommand.class)
|
||||
public static UnitCommand readUnitCommand(ByteBuffer buffer){
|
||||
return UnitCommand.all[buffer.get()];
|
||||
}
|
||||
|
||||
@WriteClass(AdminAction.class)
|
||||
public static void writeAction(ByteBuffer buffer, AdminAction reason){
|
||||
buffer.put((byte)reason.ordinal());
|
||||
|
||||
@@ -9,7 +9,7 @@ import io.anuke.mindustry.game.*;
|
||||
import io.anuke.mindustry.io.*;
|
||||
import io.anuke.mindustry.maps.filters.*;
|
||||
|
||||
import static io.anuke.mindustry.Vars.world;
|
||||
import static io.anuke.mindustry.Vars.maps;
|
||||
|
||||
public class Map implements Comparable<Map>{
|
||||
/** Whether this is a custom map. */
|
||||
@@ -57,6 +57,14 @@ public class Map implements Comparable<Map>{
|
||||
return Core.settings.getInt("hiscore" + file.nameWithoutExtension(), 0);
|
||||
}
|
||||
|
||||
public FileHandle previewFile(){
|
||||
return Vars.mapPreviewDirectory.child(file.nameWithoutExtension() + ".png");
|
||||
}
|
||||
|
||||
public FileHandle cacheFile(){
|
||||
return Vars.mapPreviewDirectory.child(file.nameWithoutExtension() + "-cache.dat");
|
||||
}
|
||||
|
||||
public void setHighScore(int score){
|
||||
Core.settings.put("hiscore" + file.nameWithoutExtension(), score);
|
||||
Vars.data.modified();
|
||||
@@ -94,7 +102,7 @@ public class Map implements Comparable<Map>{
|
||||
if(tags.getInt("build", -1) < 83 && tags.getInt("build", -1) != -1 && tags.get("genfilters", "").isEmpty()){
|
||||
return Array.with();
|
||||
}
|
||||
return world.maps.readFilters(tags.get("genfilters", ""));
|
||||
return maps.readFilters(tags.get("genfilters", ""));
|
||||
}
|
||||
|
||||
public String author(){
|
||||
@@ -120,11 +128,11 @@ public class Map implements Comparable<Map>{
|
||||
@Override
|
||||
public int compareTo(Map map){
|
||||
int type = -Boolean.compare(custom, map.custom);
|
||||
if(type != 0){
|
||||
return type;
|
||||
}else{
|
||||
return name().compareTo(map.name());
|
||||
}
|
||||
if(type != 0) return type;
|
||||
int modes = Boolean.compare(Gamemode.pvp.valid(this), Gamemode.pvp.valid(map));
|
||||
if(modes != 0) return modes;
|
||||
|
||||
return name().compareTo(map.name());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
40
core/src/io/anuke/mindustry/maps/MapPreviewLoader.java
Normal file
40
core/src/io/anuke/mindustry/maps/MapPreviewLoader.java
Normal file
@@ -0,0 +1,40 @@
|
||||
package io.anuke.mindustry.maps;
|
||||
|
||||
import io.anuke.arc.assets.*;
|
||||
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.mindustry.*;
|
||||
import io.anuke.mindustry.game.*;
|
||||
|
||||
public class MapPreviewLoader extends TextureLoader{
|
||||
|
||||
public MapPreviewLoader(){
|
||||
super(new AbsoluteFileHandleResolver());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadAsync(AssetManager manager, String fileName, FileHandle file, TextureParameter parameter){
|
||||
try{
|
||||
super.loadAsync(manager, fileName, file.sibling(file.nameWithoutExtension()), parameter);
|
||||
}catch(Exception e){
|
||||
e.printStackTrace();
|
||||
MapPreviewParameter param = (MapPreviewParameter)parameter;
|
||||
Vars.maps.createNewPreview(param.map);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Array<AssetDescriptor> getDependencies(String fileName, FileHandle file, TextureParameter parameter){
|
||||
return Array.with(new AssetDescriptor<>("contentcreate", Content.class));
|
||||
}
|
||||
|
||||
public static class MapPreviewParameter extends TextureParameter{
|
||||
public Map map;
|
||||
|
||||
public MapPreviewParameter(Map map){
|
||||
this.map = map;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,22 @@
|
||||
package io.anuke.mindustry.maps;
|
||||
|
||||
import io.anuke.arc.*;
|
||||
import io.anuke.arc.assets.*;
|
||||
import io.anuke.arc.assets.loaders.*;
|
||||
import io.anuke.arc.collection.*;
|
||||
import io.anuke.arc.collection.IntSet.*;
|
||||
import io.anuke.arc.files.*;
|
||||
import io.anuke.arc.function.*;
|
||||
import io.anuke.arc.graphics.*;
|
||||
import io.anuke.arc.util.*;
|
||||
import io.anuke.arc.util.async.*;
|
||||
import io.anuke.arc.util.io.*;
|
||||
import io.anuke.arc.util.serialization.*;
|
||||
import io.anuke.mindustry.content.*;
|
||||
import io.anuke.mindustry.game.*;
|
||||
import io.anuke.mindustry.game.EventType.*;
|
||||
import io.anuke.mindustry.io.*;
|
||||
import io.anuke.mindustry.maps.MapPreviewLoader.*;
|
||||
import io.anuke.mindustry.maps.filters.*;
|
||||
import io.anuke.mindustry.world.*;
|
||||
import io.anuke.mindustry.world.blocks.storage.*;
|
||||
@@ -18,7 +25,7 @@ import java.io.*;
|
||||
|
||||
import static io.anuke.mindustry.Vars.*;
|
||||
|
||||
public class Maps implements Disposable{
|
||||
public class Maps{
|
||||
/** List of all built-in maps. Filenames only. */
|
||||
private static String[] defaultMapNames = {"maze", "fortress", "labyrinth", "islands", "tendrils", "caldera", "wasteland", "shattered", "fork", "triad", "veins", "glacier"};
|
||||
/** All maps stored in an ordered array. */
|
||||
@@ -26,6 +33,8 @@ public class Maps implements Disposable{
|
||||
/** Serializer for meta. */
|
||||
private Json json = new Json();
|
||||
|
||||
private AsyncExecutor executor = new AsyncExecutor(2);
|
||||
|
||||
/** Returns a list of all maps, including custom ones. */
|
||||
public Array<Map> all(){
|
||||
return maps;
|
||||
@@ -45,6 +54,12 @@ public class Maps implements Disposable{
|
||||
return maps.find(m -> m.name().equals(name));
|
||||
}
|
||||
|
||||
public Maps(){
|
||||
Events.on(ClientLoadEvent.class, event -> {
|
||||
maps.sort();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a map from the map folder and returns it. Should only be used for zone maps.
|
||||
* Does not add this map to the map list.
|
||||
@@ -74,7 +89,13 @@ public class Maps implements Disposable{
|
||||
}
|
||||
|
||||
public void reload(){
|
||||
dispose();
|
||||
for(Map map : maps){
|
||||
if(map.texture != null){
|
||||
map.texture.dispose();
|
||||
map.texture = null;
|
||||
}
|
||||
}
|
||||
maps.clear();
|
||||
load();
|
||||
}
|
||||
|
||||
@@ -128,7 +149,10 @@ public class Maps implements Disposable{
|
||||
}
|
||||
}
|
||||
|
||||
map.texture = new Texture(MapIO.generatePreview(world.getTiles()));
|
||||
Pixmap pix = MapIO.generatePreview(world.getTiles());
|
||||
executor.submit(() -> map.previewFile().writePNG(pix));
|
||||
|
||||
map.texture = new Texture(pix);
|
||||
}
|
||||
maps.add(map);
|
||||
maps.sort();
|
||||
@@ -227,8 +251,8 @@ public class Maps implements Disposable{
|
||||
int index = 0;
|
||||
for(Block block : new Block[]{Blocks.oreCopper, Blocks.oreLead, Blocks.oreCoal, Blocks.oreTitanium, Blocks.oreThorium}){
|
||||
OreFilter filter = new OreFilter();
|
||||
filter.threshold += index ++ * 0.019f;
|
||||
filter.scl += index/2f;
|
||||
filter.threshold += index ++ * 0.018f;
|
||||
filter.scl += index/2.1f;
|
||||
filter.ore = block;
|
||||
filters.add(filter);
|
||||
}
|
||||
@@ -284,6 +308,70 @@ public class Maps implements Disposable{
|
||||
}
|
||||
}
|
||||
|
||||
public void loadPreviews(){
|
||||
Array<Map> createNew = new Array<>();
|
||||
|
||||
for(Map map : maps){
|
||||
//try to load preview
|
||||
if(map.previewFile().exists()){
|
||||
//this may fail, but calls createNewPreview
|
||||
Core.assets.load(new AssetDescriptor<>(map.previewFile().path() + "." + mapExtension, Texture.class, new MapPreviewParameter(map))).loaded = t -> map.texture = (Texture)t;
|
||||
}else{
|
||||
createNew.add(map);
|
||||
}
|
||||
|
||||
try{
|
||||
readCache(map);
|
||||
}catch(Exception ignored){
|
||||
}
|
||||
}
|
||||
|
||||
((CustomLoader)Core.assets.getLoader(Content.class)).loaded = () -> Core.app.post(() -> createNew.each(this::createNewPreview));
|
||||
}
|
||||
|
||||
public void createNewPreview(Map map){
|
||||
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);
|
||||
Core.app.post(() -> map.texture = new Texture(pix));
|
||||
executor.submit(() -> {
|
||||
try{
|
||||
map.previewFile().writePNG(pix);
|
||||
writeCache(map);
|
||||
}catch(Exception e){
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
}catch(IOException e){
|
||||
Log.err("Failed to generate preview!", e);
|
||||
Core.app.post(() -> map.texture = new Texture("sprites/error.png"));
|
||||
}
|
||||
}
|
||||
|
||||
private void writeCache(Map map) throws IOException{
|
||||
try(DataOutputStream stream = new DataOutputStream(map.cacheFile().write(false, Streams.DEFAULT_BUFFER_SIZE))){
|
||||
stream.write(0);
|
||||
stream.writeInt(map.spawns);
|
||||
stream.write(map.teams.size);
|
||||
IntSetIterator iter = map.teams.iterator();
|
||||
while(iter.hasNext){
|
||||
stream.write(iter.next());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void readCache(Map map) throws IOException{
|
||||
try(DataInputStream stream = new DataInputStream(map.cacheFile().read(Streams.DEFAULT_BUFFER_SIZE))){
|
||||
stream.read(); //version
|
||||
map.spawns = stream.readInt();
|
||||
int teamsize = stream.readByte();
|
||||
for(int i = 0; i < teamsize; i++){
|
||||
map.teams.add(stream.read());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Find a new filename to put a map to. */
|
||||
private FileHandle findFile(){
|
||||
//find a map name that isn't used.
|
||||
@@ -301,12 +389,8 @@ public class Maps implements Disposable{
|
||||
throw new IOException("Map name cannot be empty! File: " + file);
|
||||
}
|
||||
|
||||
if(!headless){
|
||||
map.texture = new Texture(MapIO.generatePreview(map));
|
||||
}
|
||||
|
||||
maps.add(map);
|
||||
//maps.sort();
|
||||
maps.sort();
|
||||
}
|
||||
|
||||
private void loadCustomMaps(){
|
||||
@@ -321,15 +405,4 @@ public class Maps implements Disposable{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose(){
|
||||
for(Map map : maps){
|
||||
if(map.texture != null){
|
||||
map.texture.dispose();
|
||||
map.texture = null;
|
||||
}
|
||||
}
|
||||
maps.clear();
|
||||
}
|
||||
}
|
||||
@@ -33,7 +33,9 @@ public class MirrorFilter extends GenerateFilter{
|
||||
mirror(v3, v1.x, v1.y, v2.x, v2.y);
|
||||
Tile tile = in.tile(v3.x, v3.y);
|
||||
in.floor = tile.floor();
|
||||
in.block = tile.block();
|
||||
if(!tile.block().synthetic()){
|
||||
in.block = tile.block();
|
||||
}
|
||||
in.ore = tile.overlay();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import static io.anuke.mindustry.maps.filters.FilterOption.BlockOption;
|
||||
import static io.anuke.mindustry.maps.filters.FilterOption.oresOnly;
|
||||
|
||||
public class OreFilter extends GenerateFilter{
|
||||
public float scl = 23, threshold = 0.811f, octaves = 2f, falloff = 0.3f;
|
||||
public float scl = 23, threshold = 0.81f, octaves = 2f, falloff = 0.3f;
|
||||
public Block ore = Blocks.oreCopper;
|
||||
|
||||
{
|
||||
|
||||
@@ -51,7 +51,7 @@ public class MapGenerator extends Generator{
|
||||
@Override
|
||||
public void init(Loadout loadout){
|
||||
this.loadout = loadout;
|
||||
map = world.maps.loadInternalMap(mapName);
|
||||
map = maps.loadInternalMap(mapName);
|
||||
width = map.width;
|
||||
height = map.height;
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ import io.anuke.arc.collection.*;
|
||||
import static io.anuke.mindustry.Vars.headless;
|
||||
|
||||
public class Administration{
|
||||
|
||||
/** All player info. Maps UUIDs to info. This persists throughout restarts. */
|
||||
private ObjectMap<String, PlayerInfo> playerInfo = new ObjectMap<>();
|
||||
private Array<String> bannedIPs = new Array<>();
|
||||
|
||||
@@ -97,7 +97,7 @@ public class CrashSender{
|
||||
ex(() -> value.addChild("server", new JsonValue(fs)));
|
||||
ex(() -> value.addChild("players", new JsonValue(Vars.playerGroup.size())));
|
||||
ex(() -> value.addChild("state", new JsonValue(Vars.state.getState().name())));
|
||||
ex(() -> value.addChild("os", new JsonValue(System.getProperty("os.name"))));
|
||||
ex(() -> value.addChild("os", new JsonValue(System.getProperty("os.name") + "x" + (OS.is64Bit ? "64" : "32"))));
|
||||
ex(() -> value.addChild("trace", new JsonValue(parseException(exception))));
|
||||
|
||||
boolean[] sent = {false};
|
||||
|
||||
@@ -1,19 +1,18 @@
|
||||
package io.anuke.mindustry.net;
|
||||
|
||||
import io.anuke.arc.Core;
|
||||
import io.anuke.arc.*;
|
||||
import io.anuke.arc.collection.*;
|
||||
import io.anuke.arc.function.BiConsumer;
|
||||
import io.anuke.arc.function.Consumer;
|
||||
import io.anuke.arc.function.*;
|
||||
import io.anuke.arc.util.*;
|
||||
import io.anuke.arc.util.pooling.Pools;
|
||||
import io.anuke.mindustry.core.Platform;
|
||||
import io.anuke.mindustry.gen.Call;
|
||||
import io.anuke.arc.util.pooling.*;
|
||||
import io.anuke.mindustry.core.*;
|
||||
import io.anuke.mindustry.gen.*;
|
||||
import io.anuke.mindustry.net.Packets.*;
|
||||
import io.anuke.mindustry.net.Streamable.StreamBuilder;
|
||||
import io.anuke.mindustry.net.Streamable.*;
|
||||
import net.jpountz.lz4.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.BufferOverflowException;
|
||||
import java.nio.BufferUnderflowException;
|
||||
import java.io.*;
|
||||
import java.nio.*;
|
||||
|
||||
import static io.anuke.mindustry.Vars.*;
|
||||
|
||||
@@ -28,6 +27,8 @@ public class Net{
|
||||
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();
|
||||
|
||||
/** Display a network error. Call on the graphics thread. */
|
||||
public static void showError(Throwable e){
|
||||
@@ -149,11 +150,11 @@ public class Net{
|
||||
}
|
||||
|
||||
public static byte[] compressSnapshot(byte[] input){
|
||||
return serverProvider.compressSnapshot(input);
|
||||
return compressor.compress(input);
|
||||
}
|
||||
|
||||
public static byte[] decompressSnapshot(byte[] input, int size){
|
||||
return clientProvider.decompressSnapshot(input, size);
|
||||
return decompressor.decompress(input, size);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -359,9 +360,6 @@ public class Net{
|
||||
/** Disconnect from the server. */
|
||||
void disconnect();
|
||||
|
||||
/** Decompress an input snapshot byte array. */
|
||||
byte[] decompressSnapshot(byte[] input, int size);
|
||||
|
||||
/**
|
||||
* 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.
|
||||
@@ -435,9 +433,6 @@ public class Net{
|
||||
/** Close the server connection. */
|
||||
void close();
|
||||
|
||||
/** Compress an input snapshot byte array. */
|
||||
byte[] compressSnapshot(byte[] input);
|
||||
|
||||
/** Return all connected users. */
|
||||
Iterable<? extends NetConnection> getConnections();
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ public class NetworkIO{
|
||||
state.wave = stream.readInt();
|
||||
state.wavetime = stream.readFloat();
|
||||
|
||||
Entities.clear();
|
||||
entities.clear();
|
||||
int id = stream.readInt();
|
||||
player.resetNoAdd();
|
||||
player.read(stream);
|
||||
|
||||
@@ -14,7 +14,7 @@ public class Packets{
|
||||
|
||||
public enum KickReason{
|
||||
kick, clientOutdated, serverOutdated, banned, gameover(true), recentKick,
|
||||
nameInUse, idInUse, nameEmpty, customClient, serverClose;
|
||||
nameInUse, idInUse, nameEmpty, customClient, serverClose, vote, typeMismatch;
|
||||
|
||||
public final boolean quiet;
|
||||
|
||||
|
||||
21
core/src/io/anuke/mindustry/plugin/Plugin.java
Normal file
21
core/src/io/anuke/mindustry/plugin/Plugin.java
Normal file
@@ -0,0 +1,21 @@
|
||||
package io.anuke.mindustry.plugin;
|
||||
|
||||
import io.anuke.arc.util.*;
|
||||
|
||||
public abstract class Plugin{
|
||||
|
||||
/** Called after all plugins have been created and commands have been registered.*/
|
||||
public void init(){
|
||||
|
||||
}
|
||||
|
||||
/** Register any commands to be used on the server side, e.g. from the console. */
|
||||
public void registerServerCommands(CommandHandler handler){
|
||||
|
||||
}
|
||||
|
||||
/** Register any commands to be used on the client side, e.g. sent from an in-game player.. */
|
||||
public void registerClientCommands(CommandHandler handler){
|
||||
|
||||
}
|
||||
}
|
||||
82
core/src/io/anuke/mindustry/plugin/Plugins.java
Normal file
82
core/src/io/anuke/mindustry/plugin/Plugins.java
Normal file
@@ -0,0 +1,82 @@
|
||||
package io.anuke.mindustry.plugin;
|
||||
|
||||
import io.anuke.arc.collection.Array;
|
||||
import io.anuke.arc.files.*;
|
||||
import io.anuke.arc.function.*;
|
||||
import io.anuke.arc.util.*;
|
||||
import io.anuke.mindustry.io.*;
|
||||
|
||||
import java.lang.reflect.*;
|
||||
import java.net.*;
|
||||
|
||||
import static io.anuke.mindustry.Vars.pluginDirectory;
|
||||
|
||||
public class Plugins{
|
||||
private Array<LoadedPlugin> loaded = new Array<>();
|
||||
|
||||
/** Loads all plugins from the folder, but does call any methods on them.*/
|
||||
public void load(){
|
||||
for(FileHandle file : pluginDirectory.list()){
|
||||
if(!file.extension().equals("jar")) continue;
|
||||
|
||||
try{
|
||||
loaded.add(loadPlugin(file));
|
||||
}catch(IllegalArgumentException ignored){
|
||||
}catch(Exception e){
|
||||
Log.err("Failed to load plugin file {0}. Skipping.", file);
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** @return all loaded plugins. */
|
||||
public Array<LoadedPlugin> all(){
|
||||
return loaded;
|
||||
}
|
||||
|
||||
/** Iterates through each plugin.*/
|
||||
public void each(Consumer<Plugin> cons){
|
||||
loaded.each(p -> cons.accept(p.plugin));
|
||||
}
|
||||
|
||||
private LoadedPlugin loadPlugin(FileHandle jar) throws Exception{
|
||||
FileHandle zip = new ZipFileHandle(jar);
|
||||
|
||||
FileHandle metaf = zip.child("plugin.json");
|
||||
if(!metaf.exists()){
|
||||
Log.warn("Plugin {0} doesn't have a 'plugin.json' file, skipping.", jar);
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
|
||||
PluginMeta meta = JsonIO.read(PluginMeta.class, metaf.readString());
|
||||
|
||||
URLClassLoader classLoader = (URLClassLoader)ClassLoader.getSystemClassLoader();
|
||||
Method method = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
|
||||
method.setAccessible(true);
|
||||
method.invoke(classLoader, jar.file().toURI().toURL());
|
||||
|
||||
Class<?> main = Class.forName(meta.main);
|
||||
return new LoadedPlugin(jar, zip, (Plugin)main.newInstance(), meta);
|
||||
}
|
||||
|
||||
/** Represents a plugin that has been loaded from a jar file.*/
|
||||
public static class LoadedPlugin{
|
||||
public final FileHandle jarFile;
|
||||
public final FileHandle zipRoot;
|
||||
public final Plugin plugin;
|
||||
public final PluginMeta meta;
|
||||
|
||||
public LoadedPlugin(FileHandle jarFile, FileHandle zipRoot, Plugin plugin, PluginMeta meta){
|
||||
this.zipRoot = zipRoot;
|
||||
this.jarFile = jarFile;
|
||||
this.plugin = plugin;
|
||||
this.meta = meta;
|
||||
}
|
||||
}
|
||||
|
||||
/** Plugin metadata information.*/
|
||||
public static class PluginMeta{
|
||||
public String name, author, main, description;
|
||||
public String version;
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,13 @@
|
||||
package io.anuke.mindustry.type;
|
||||
|
||||
import io.anuke.arc.Core;
|
||||
import io.anuke.arc.collection.Array;
|
||||
import io.anuke.arc.graphics.Color;
|
||||
import io.anuke.arc.graphics.g2d.TextureRegion;
|
||||
import io.anuke.arc.scene.ui.layout.Table;
|
||||
import io.anuke.mindustry.game.UnlockableContent;
|
||||
import io.anuke.mindustry.ui.ContentDisplay;
|
||||
import io.anuke.mindustry.world.blocks.Floor;
|
||||
import io.anuke.mindustry.world.blocks.OreBlock;
|
||||
import io.anuke.arc.*;
|
||||
import io.anuke.arc.collection.*;
|
||||
import io.anuke.arc.graphics.*;
|
||||
import io.anuke.arc.graphics.g2d.*;
|
||||
import io.anuke.arc.scene.ui.layout.*;
|
||||
import io.anuke.mindustry.game.*;
|
||||
import io.anuke.mindustry.ui.*;
|
||||
import io.anuke.mindustry.world.blocks.*;
|
||||
|
||||
import static io.anuke.mindustry.Vars.content;
|
||||
|
||||
@@ -40,6 +39,7 @@ public class Item extends UnlockableContent implements Comparable<Item>{
|
||||
this.description = Core.bundle.getOrNull("item." + this.name + ".description");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load(){
|
||||
regions = new TextureRegion[Icon.values().length];
|
||||
for(int i = 0; i < regions.length; i++){
|
||||
|
||||
@@ -3,7 +3,6 @@ package io.anuke.mindustry.type;
|
||||
import io.anuke.arc.*;
|
||||
import io.anuke.arc.collection.*;
|
||||
import io.anuke.arc.function.*;
|
||||
import io.anuke.arc.graphics.*;
|
||||
import io.anuke.arc.graphics.g2d.*;
|
||||
import io.anuke.arc.scene.ui.layout.*;
|
||||
import io.anuke.arc.util.*;
|
||||
@@ -28,7 +27,7 @@ public class Zone extends UnlockableContent{
|
||||
public int configureWave = 15;
|
||||
public int launchPeriod = 10;
|
||||
public Loadout loadout = Loadouts.basicShard;
|
||||
public Texture preview;
|
||||
public TextureRegion preview;
|
||||
|
||||
protected ItemStack[] baseLaunchCost = {};
|
||||
protected Array<ItemStack> startingItems = new Array<>();
|
||||
@@ -41,6 +40,11 @@ public class Zone extends UnlockableContent{
|
||||
this.generator = generator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load(){
|
||||
preview = Core.atlas.find(name);
|
||||
}
|
||||
|
||||
public Rules getRules(){
|
||||
if(generator instanceof MapGenerator){
|
||||
return ((MapGenerator)generator).getMap().rules();
|
||||
@@ -174,13 +178,6 @@ public class Zone extends UnlockableContent{
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load(){
|
||||
if(Core.files.internal("zones/" + name + ".png").exists() && !headless){
|
||||
preview = new Texture(Core.files.internal("zones/" + name + ".png"));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean alwaysUnlocked(){
|
||||
return alwaysUnlocked;
|
||||
|
||||
@@ -6,13 +6,12 @@ import io.anuke.arc.math.*;
|
||||
import io.anuke.arc.scene.ui.*;
|
||||
import io.anuke.arc.scene.ui.layout.*;
|
||||
import io.anuke.arc.util.*;
|
||||
import io.anuke.mindustry.*;
|
||||
import io.anuke.mindustry.game.*;
|
||||
import io.anuke.mindustry.graphics.*;
|
||||
import io.anuke.mindustry.maps.*;
|
||||
import io.anuke.mindustry.ui.*;
|
||||
|
||||
import static io.anuke.mindustry.Vars.world;
|
||||
|
||||
public class CustomGameDialog extends FloatingDialog{
|
||||
private MapPlayDialog dialog = new MapPlayDialog();
|
||||
|
||||
@@ -42,7 +41,7 @@ public class CustomGameDialog extends FloatingDialog{
|
||||
|
||||
int i = 0;
|
||||
maps.defaults().width(170).fillY().top().pad(4f);
|
||||
for(Map map : world.maps.all()){
|
||||
for(Map map : Vars.maps.all()){
|
||||
|
||||
if(i % maxwidth == 0){
|
||||
maps.row();
|
||||
@@ -83,7 +82,7 @@ public class CustomGameDialog extends FloatingDialog{
|
||||
i++;
|
||||
}
|
||||
|
||||
if(world.maps.all().size == 0){
|
||||
if(Vars.maps.all().size == 0){
|
||||
maps.add("$maps.none").pad(50);
|
||||
}
|
||||
|
||||
|
||||
@@ -14,9 +14,8 @@ import io.anuke.arc.scene.ui.layout.*;
|
||||
import io.anuke.arc.scene.utils.*;
|
||||
import io.anuke.arc.util.*;
|
||||
import io.anuke.mindustry.content.*;
|
||||
import io.anuke.mindustry.core.*;
|
||||
import io.anuke.mindustry.core.GameState.*;
|
||||
import io.anuke.mindustry.game.EventType.*;
|
||||
import io.anuke.mindustry.core.*;
|
||||
import io.anuke.mindustry.game.Saves.*;
|
||||
import io.anuke.mindustry.graphics.*;
|
||||
import io.anuke.mindustry.io.SaveIO.*;
|
||||
@@ -33,13 +32,10 @@ public class DeployDialog extends FloatingDialog{
|
||||
private ObjectSet<ZoneNode> nodes = new ObjectSet<>();
|
||||
private ZoneInfoDialog info = new ZoneInfoDialog();
|
||||
private Rectangle bounds = new Rectangle();
|
||||
private Texture nomap = new Texture("zones/nomap.png");
|
||||
|
||||
public DeployDialog(){
|
||||
super("", "fulldialog");
|
||||
|
||||
Events.on(DisposeEvent.class, e -> nomap.dispose());
|
||||
|
||||
ZoneNode root = new ZoneNode(Zones.groundZero, null);
|
||||
|
||||
TreeLayout layout = new TreeLayout();
|
||||
@@ -90,6 +86,7 @@ public class DeployDialog extends FloatingDialog{
|
||||
Stack sub = new Stack();
|
||||
|
||||
if(control.saves.getZoneSlot().getZone() != null){
|
||||
sub.add(new Table(f -> f.margin(4f).add(new Image("whiteui")).color(Color.fromGray(0.1f)).grow()));
|
||||
sub.add(new Table(f -> f.margin(4f).add(new Image(control.saves.getZoneSlot().getZone().preview).setScaling(Scaling.fit)).color(Color.DARK_GRAY).grow()));
|
||||
}
|
||||
|
||||
@@ -195,7 +192,7 @@ public class DeployDialog extends FloatingDialog{
|
||||
}
|
||||
|
||||
stack.setSize(Tmp.v1.x, Tmp.v1.y);
|
||||
stack.add(new Table(t -> t.margin(4f).add(new Image(node.zone.preview != null ? node.zone.preview : nomap).setScaling(Scaling.stretch)).color(node.zone.unlocked() ? Color.DARK_GRAY : Color.fromGray(0.2f)).grow()));
|
||||
stack.add(new Table(t -> t.margin(4f).add(new Image(node.zone.preview).setScaling(Scaling.stretch)).color(node.zone.unlocked() ? Color.DARK_GRAY : Color.fromGray(0.2f)).grow()));
|
||||
stack.update(() -> stack.setPosition(node.x + panX + width / 2f, node.y + panY + height / 2f, Align.center));
|
||||
|
||||
Button button = new Button("square");
|
||||
|
||||
@@ -51,6 +51,7 @@ public class LoadDialog extends FloatingDialog{
|
||||
Time.runTask(2f, () -> Core.scene.setScrollFocus(pane));
|
||||
|
||||
Array<SaveSlot> array = control.saves.getSaveSlots();
|
||||
array.sort((slot, other) -> -Long.compare(slot.getTimestamp(), other.getTimestamp()));
|
||||
|
||||
for(SaveSlot slot : array){
|
||||
if(slot.isHidden()) continue;
|
||||
|
||||
@@ -63,7 +63,7 @@ public class MapsDialog extends FloatingDialog{
|
||||
if(!ios){
|
||||
buttons.addImageTextButton("$editor.importmap", "icon-load", iconsize, () -> {
|
||||
Platform.instance.showFileChooser("$editor.importmap", "Map File", file -> {
|
||||
world.maps.tryCatchMapError(() -> {
|
||||
maps.tryCatchMapError(() -> {
|
||||
if(MapIO.isImage(file)){
|
||||
ui.showError("$editor.errorimage");
|
||||
return;
|
||||
@@ -73,14 +73,14 @@ public class MapsDialog extends FloatingDialog{
|
||||
if(file.extension().equalsIgnoreCase(mapExtension)){
|
||||
map = MapIO.createMap(file, true);
|
||||
}else{
|
||||
map = world.maps.makeLegacyMap(file);
|
||||
map = maps.makeLegacyMap(file);
|
||||
}
|
||||
|
||||
//when you attempt to import a save, it will have no name, so generate one
|
||||
String name = map.tags.getOr("name", () -> {
|
||||
String result = "unknown";
|
||||
int number = 0;
|
||||
while(world.maps.byName(result + number++) != null) ;
|
||||
while(maps.byName(result + number++) != null) ;
|
||||
return result + number;
|
||||
});
|
||||
|
||||
@@ -90,19 +90,19 @@ public class MapsDialog extends FloatingDialog{
|
||||
return;
|
||||
}
|
||||
|
||||
Map conflict = world.maps.all().find(m -> m.name().equals(name));
|
||||
Map conflict = maps.all().find(m -> m.name().equals(name));
|
||||
|
||||
if(conflict != null && !conflict.custom){
|
||||
ui.showInfo(Core.bundle.format("editor.import.exists", name));
|
||||
}else if(conflict != null){
|
||||
ui.showConfirm("$confirm", "$editor.overwrite.confirm", () -> {
|
||||
world.maps.tryCatchMapError(() -> {
|
||||
world.maps.importMap(file);
|
||||
maps.tryCatchMapError(() -> {
|
||||
maps.importMap(file);
|
||||
setup();
|
||||
});
|
||||
});
|
||||
}else{
|
||||
world.maps.importMap(map.file);
|
||||
maps.importMap(map.file);
|
||||
setup();
|
||||
}
|
||||
|
||||
@@ -123,7 +123,7 @@ public class MapsDialog extends FloatingDialog{
|
||||
float mapsize = 200f;
|
||||
|
||||
int i = 0;
|
||||
for(Map map : world.maps.all()){
|
||||
for(Map map : Vars.maps.all()){
|
||||
|
||||
if(i % maxwidth == 0){
|
||||
maps.row();
|
||||
@@ -143,7 +143,7 @@ public class MapsDialog extends FloatingDialog{
|
||||
i++;
|
||||
}
|
||||
|
||||
if(world.maps.all().size == 0){
|
||||
if(Vars.maps.all().size == 0){
|
||||
maps.add("$maps.none");
|
||||
}
|
||||
|
||||
@@ -200,7 +200,7 @@ public class MapsDialog extends FloatingDialog{
|
||||
|
||||
table.addImageTextButton("$delete", "icon-trash-16-small", iconsizesmall, () -> {
|
||||
ui.showConfirm("$confirm", Core.bundle.format("map.delete", map.name()), () -> {
|
||||
world.maps.removeMap(map);
|
||||
maps.removeMap(map);
|
||||
dialog.hide();
|
||||
setup();
|
||||
});
|
||||
|
||||
@@ -22,12 +22,13 @@ import static io.anuke.arc.Core.bundle;
|
||||
import static io.anuke.mindustry.Vars.*;
|
||||
|
||||
public class SettingsMenuDialog extends SettingsDialog{
|
||||
public SettingsTable graphics;
|
||||
public SettingsTable game;
|
||||
public SettingsTable sound;
|
||||
private SettingsTable graphics;
|
||||
private SettingsTable game;
|
||||
private SettingsTable sound;
|
||||
|
||||
private Table prefs;
|
||||
private Table menu;
|
||||
private FloatingDialog dataDialog;
|
||||
private boolean wasPaused;
|
||||
|
||||
public SettingsMenuDialog(){
|
||||
@@ -75,6 +76,85 @@ public class SettingsMenuDialog extends SettingsDialog{
|
||||
prefs.clearChildren();
|
||||
prefs.add(menu);
|
||||
|
||||
dataDialog = new FloatingDialog("$settings.data");
|
||||
dataDialog.addCloseButton();
|
||||
|
||||
dataDialog.cont.table("button", t -> {
|
||||
t.defaults().size(240f, 60f).left();
|
||||
String style = "clear";
|
||||
|
||||
t.addButton("$settings.cleardata", style, () -> ui.showConfirm("$confirm", "$settings.clearall.confirm", () -> {
|
||||
ObjectMap<String, Object> map = new ObjectMap<>();
|
||||
for(String value : Core.settings.keys()){
|
||||
if(value.contains("usid") || value.contains("uuid")){
|
||||
map.put(value, Core.settings.getString(value));
|
||||
}
|
||||
}
|
||||
Core.settings.clear();
|
||||
Core.settings.putAll(map);
|
||||
Core.settings.save();
|
||||
|
||||
for(FileHandle file : dataDirectory.list()){
|
||||
file.deleteDirectory();
|
||||
}
|
||||
|
||||
Core.app.exit();
|
||||
}));
|
||||
|
||||
t.row();
|
||||
|
||||
if(android && (Core.files.local("mindustry-maps").exists() || Core.files.local("mindustry-saves").exists())){
|
||||
t.addButton("$classic.export", style, () -> {
|
||||
control.checkClassicData();
|
||||
});
|
||||
}
|
||||
|
||||
t.row();
|
||||
|
||||
t.addButton("$data.export", style, () -> {
|
||||
if(ios){
|
||||
FileHandle file = Core.files.local("mindustry-data-export.zip");
|
||||
try{
|
||||
data.exportData(file);
|
||||
}catch(Exception e){
|
||||
ui.showError(Strings.parseException(e, true));
|
||||
}
|
||||
Platform.instance.shareFile(file);
|
||||
}else{
|
||||
Platform.instance.showFileChooser("$data.export", "Zip Files", file -> {
|
||||
FileHandle ff = file;
|
||||
if(!ff.extension().equals("zip")){
|
||||
ff = ff.sibling(ff.nameWithoutExtension() + ".zip");
|
||||
}
|
||||
try{
|
||||
data.exportData(ff);
|
||||
ui.showInfo("$data.exported");
|
||||
}catch(Exception e){
|
||||
e.printStackTrace();
|
||||
ui.showError(Strings.parseException(e, true));
|
||||
}
|
||||
}, false, f -> false);
|
||||
}
|
||||
});
|
||||
|
||||
t.row();
|
||||
|
||||
//iOS doesn't have a file chooser.
|
||||
if(!ios){
|
||||
t.addButton("$data.import", style, () -> ui.showConfirm("$confirm", "$data.import.confirm", () -> Platform.instance.showFileChooser("$data.import", "Zip Files", file -> {
|
||||
try{
|
||||
data.importData(file);
|
||||
Core.app.exit();
|
||||
}catch(IllegalArgumentException e){
|
||||
ui.showError("$data.invalid");
|
||||
}catch(Exception e){
|
||||
e.printStackTrace();
|
||||
ui.showError(Strings.parseException(e, true));
|
||||
}
|
||||
}, true, f -> f.equalsIgnoreCase("zip"))));
|
||||
}
|
||||
});
|
||||
|
||||
ScrollPane pane = new ScrollPane(prefs);
|
||||
pane.addCaptureListener(new InputListener(){
|
||||
@Override
|
||||
@@ -121,6 +201,9 @@ public class SettingsMenuDialog extends SettingsDialog{
|
||||
menu.row();
|
||||
menu.addButton("$settings.controls", style, ui.controls::show);
|
||||
}
|
||||
|
||||
menu.row();
|
||||
menu.addButton("$settings.data", style, () -> dataDialog.show());
|
||||
}
|
||||
|
||||
void addSettings(){
|
||||
@@ -139,30 +222,7 @@ public class SettingsMenuDialog extends SettingsDialog{
|
||||
game.checkPref("crashreport", true);
|
||||
}
|
||||
|
||||
game.pref(new Setting(){
|
||||
@Override
|
||||
public void add(SettingsTable table){
|
||||
table.addButton("$settings.cleardata", () -> ui.showConfirm("$confirm", "$settings.clearall.confirm", () -> {
|
||||
ObjectMap<String, Object> map = new ObjectMap<>();
|
||||
for(String value : Core.settings.keys()){
|
||||
if(value.contains("usid") || value.contains("uuid")){
|
||||
map.put(value, Core.settings.getString(value));
|
||||
}
|
||||
}
|
||||
Core.settings.clear();
|
||||
Core.settings.putAll(map);
|
||||
Core.settings.save();
|
||||
|
||||
for(FileHandle file : dataDirectory.list()){
|
||||
file.deleteDirectory();
|
||||
}
|
||||
|
||||
Core.app.exit();
|
||||
})).size(220f, 60f).pad(6).left();
|
||||
table.add();
|
||||
table.row();
|
||||
}
|
||||
});
|
||||
game.checkPref("savecreate", true);
|
||||
|
||||
game.pref(new Setting(){
|
||||
@Override
|
||||
@@ -177,28 +237,13 @@ public class SettingsMenuDialog extends SettingsDialog{
|
||||
}
|
||||
});
|
||||
|
||||
if(android && (Core.files.local("mindustry-maps").exists() || Core.files.local("mindustry-saves").exists())){
|
||||
game.pref(new Setting(){
|
||||
@Override
|
||||
public void add(SettingsTable table){
|
||||
table.addButton("$classic.export", () -> {
|
||||
control.checkClassicData();
|
||||
}).size(220f, 60f).pad(6).left();
|
||||
table.add();
|
||||
table.row();
|
||||
hide();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
graphics.sliderPref("uiscale", 100, 25, 400, 25, s -> {
|
||||
if(Core.graphics.getFrameId() > 10){
|
||||
Log.info("changed");
|
||||
if(ui.settings != null){
|
||||
Core.settings.put("uiscalechanged", true);
|
||||
}
|
||||
return s + "%";
|
||||
});
|
||||
graphics.sliderPref("fpscap", 241, 5, 241, 5, s -> (s > 240 ? Core.bundle.get("setting.fpscap.none") : Core.bundle.format("setting.fpscap.text", s)));
|
||||
graphics.sliderPref("fpscap", 240, 5, 245, 5, s -> (s > 240 ? Core.bundle.get("setting.fpscap.none") : Core.bundle.format("setting.fpscap.text", s)));
|
||||
graphics.sliderPref("chatopacity", 100, 0, 100, 5, s -> s + "%");
|
||||
|
||||
if(!mobile){
|
||||
@@ -207,7 +252,7 @@ public class SettingsMenuDialog extends SettingsDialog{
|
||||
if(b){
|
||||
Core.graphics.setFullscreenMode(Core.graphics.getDisplayMode());
|
||||
}else{
|
||||
Core.graphics.setWindowedMode(600, 480);
|
||||
Core.graphics.setWindowedMode(Core.graphics.getWidth(), Core.graphics.getHeight());
|
||||
}
|
||||
});
|
||||
|
||||
@@ -241,7 +286,9 @@ public class SettingsMenuDialog extends SettingsDialog{
|
||||
graphics.checkPref("fps", false);
|
||||
graphics.checkPref("indicators", true);
|
||||
graphics.checkPref("animatedwater", false);
|
||||
graphics.checkPref("animatedshields", !mobile);
|
||||
if(Shaders.shield != null){
|
||||
graphics.checkPref("animatedshields", !mobile);
|
||||
}
|
||||
graphics.checkPref("bloom", false, val -> renderer.toggleBloom(val));
|
||||
graphics.checkPref("lasers", true);
|
||||
graphics.checkPref("pixelate", false);
|
||||
|
||||
@@ -257,7 +257,7 @@ public class HudFragment extends Fragment{
|
||||
t.table("flat", c -> c.add("$nearpoint")
|
||||
.update(l -> l.setColor(Tmp.c1.set(Color.WHITE).lerp(Color.SCARLET, Mathf.absin(Time.time(), 10f, 1f))))
|
||||
.get().setAlignment(Align.center, Align.center))
|
||||
.margin(6).update(u -> u.color.a = Mathf.lerpDelta(u.color.a, Mathf.num(world.spawner.playerNear()), 0.1f)).get().color.a = 0f;
|
||||
.margin(6).update(u -> u.color.a = Mathf.lerpDelta(u.color.a, Mathf.num(spawner.playerNear()), 0.1f)).get().color.a = 0f;
|
||||
});
|
||||
|
||||
parent.fill(t -> {
|
||||
@@ -543,7 +543,7 @@ public class HudFragment extends Fragment{
|
||||
return world.isZone() &&
|
||||
world.getZone().metCondition() &&
|
||||
!Net.client() &&
|
||||
state.wave % world.getZone().launchPeriod == 0 && !world.spawner.isSpawning();
|
||||
state.wave % world.getZone().launchPeriod == 0 && !spawner.isSpawning();
|
||||
}
|
||||
|
||||
private boolean canLaunch(){
|
||||
@@ -639,7 +639,7 @@ public class HudFragment extends Fragment{
|
||||
}
|
||||
|
||||
private boolean canSkipWave(){
|
||||
return state.rules.waves && ((Net.server() || player.isAdmin) || !Net.active()) && state.enemies() == 0 && !world.spawner.isSpawning() && !state.rules.tutorial;
|
||||
return state.rules.waves && ((Net.server() || player.isAdmin) || !Net.active()) && state.enemies() == 0 && !spawner.isSpawning() && !state.rules.tutorial;
|
||||
}
|
||||
|
||||
private void addPlayButton(Table table){
|
||||
|
||||
@@ -19,18 +19,19 @@ import io.anuke.mindustry.game.Version;
|
||||
import io.anuke.mindustry.graphics.MenuRenderer;
|
||||
import io.anuke.mindustry.ui.MobileButton;
|
||||
|
||||
import static io.anuke.arc.Core.assets;
|
||||
import static io.anuke.mindustry.Vars.*;
|
||||
|
||||
public class MenuFragment extends Fragment{
|
||||
private Texture logo = new Texture("sprites/logo.png");
|
||||
private Table container, submenu;
|
||||
private Button currentMenu;
|
||||
private MenuRenderer renderer;
|
||||
|
||||
public MenuFragment(){
|
||||
assets.load("sprites/logo.png", Texture.class);
|
||||
assets.finishLoading();
|
||||
Events.on(DisposeEvent.class, event -> {
|
||||
renderer.dispose();
|
||||
logo.dispose();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -59,19 +60,16 @@ public class MenuFragment extends Fragment{
|
||||
}
|
||||
});
|
||||
|
||||
//discord icon in top right
|
||||
//parent.fill(c -> c.top().right().addButton("", "discord", ui.discord::show).size(84, 45)
|
||||
//.visible(() -> state.is(State.menu)));
|
||||
|
||||
//info icon
|
||||
if(mobile){
|
||||
parent.fill(c -> c.bottom().left().addButton("", "info", ui.about::show).size(84, 45));
|
||||
parent.fill(c -> c.bottom().right().addButton("", "discord", ui.discord::show).size(84, 45));
|
||||
}
|
||||
|
||||
String versionText = "[#ffffffba]" + ((Version.build == -1) ? "[#fc8140aa]custom build" : Version.modifier + " build " + Version.build);
|
||||
String versionText = "[#ffffffba]" + ((Version.build == -1) ? "[#fc8140aa]custom build" : (Version.type.equals("official") ? Version.modifier : Version.type) + " build " + Version.build);
|
||||
|
||||
parent.fill((x, y, w, h) -> {
|
||||
Texture logo = Core.assets.get("sprites/logo.png");
|
||||
float logoscl = UnitScl.dp.scl(1);
|
||||
float logow = Math.min(logo.getWidth() * logoscl, Core.graphics.getWidth() - UnitScl.dp.scl(20));
|
||||
float logoh = logow * (float)logo.getHeight() / logo.getWidth();
|
||||
@@ -81,6 +79,7 @@ public class MenuFragment extends Fragment{
|
||||
|
||||
Draw.color();
|
||||
Draw.rect(Draw.wrap(logo), fx, fy, logow, logoh);
|
||||
|
||||
Core.scene.skin.font().setColor(Color.WHITE);
|
||||
Core.scene.skin.font().draw(versionText, fx, fy - logoh/2f, Align.center);
|
||||
}).touchable(Touchable.disabled);
|
||||
|
||||
@@ -1,26 +1,22 @@
|
||||
package io.anuke.mindustry.ui.fragments;
|
||||
|
||||
import io.anuke.arc.Core;
|
||||
import io.anuke.arc.Events;
|
||||
import io.anuke.arc.collection.Array;
|
||||
import io.anuke.arc.graphics.Color;
|
||||
import io.anuke.arc.input.KeyCode;
|
||||
import io.anuke.arc.math.geom.Vector2;
|
||||
import io.anuke.arc.scene.Group;
|
||||
import io.anuke.arc.scene.event.Touchable;
|
||||
import io.anuke.arc.scene.style.TextureRegionDrawable;
|
||||
import io.anuke.arc.*;
|
||||
import io.anuke.arc.collection.*;
|
||||
import io.anuke.arc.graphics.*;
|
||||
import io.anuke.arc.input.*;
|
||||
import io.anuke.arc.math.geom.*;
|
||||
import io.anuke.arc.scene.*;
|
||||
import io.anuke.arc.scene.event.*;
|
||||
import io.anuke.arc.scene.style.*;
|
||||
import io.anuke.arc.scene.ui.*;
|
||||
import io.anuke.arc.scene.ui.layout.Table;
|
||||
import io.anuke.mindustry.content.Blocks;
|
||||
import io.anuke.mindustry.entities.type.TileEntity;
|
||||
import io.anuke.arc.scene.ui.layout.*;
|
||||
import io.anuke.mindustry.entities.type.*;
|
||||
import io.anuke.mindustry.game.EventType.*;
|
||||
import io.anuke.mindustry.graphics.Pal;
|
||||
import io.anuke.mindustry.input.Binding;
|
||||
import io.anuke.mindustry.input.InputHandler;
|
||||
import io.anuke.mindustry.graphics.*;
|
||||
import io.anuke.mindustry.input.*;
|
||||
import io.anuke.mindustry.type.*;
|
||||
import io.anuke.mindustry.world.Block;
|
||||
import io.anuke.mindustry.world.Block.Icon;
|
||||
import io.anuke.mindustry.world.Tile;
|
||||
import io.anuke.mindustry.world.*;
|
||||
import io.anuke.mindustry.world.Block.*;
|
||||
|
||||
import static io.anuke.mindustry.Vars.*;
|
||||
|
||||
@@ -151,8 +147,7 @@ public class PlacementFragment extends Fragment{
|
||||
|
||||
button.update(() -> { //color unplacable things gray
|
||||
TileEntity core = player.getClosestCore();
|
||||
Color color = block.buildVisibility == Blocks.padVisible && !block.buildVisibility.get() ? Pal.noplace :
|
||||
state.rules.infiniteResources || (core != null && (core.items.has(block.buildRequirements, state.rules.buildCostMultiplier) || state.rules.infiniteResources)) ? Color.WHITE : Color.GRAY;
|
||||
Color color = state.rules.infiniteResources || (core != null && (core.items.has(block.buildRequirements, state.rules.buildCostMultiplier) || state.rules.infiniteResources)) ? Color.WHITE : Color.GRAY;
|
||||
button.forEach(elem -> elem.setColor(color));
|
||||
button.setChecked(input.block == block);
|
||||
});
|
||||
@@ -204,10 +199,6 @@ public class PlacementFragment extends Fragment{
|
||||
Events.fire(new BlockInfoEvent());
|
||||
}).size(8 * 5).padTop(-5).padRight(-5).right().grow().name("blockinfo");
|
||||
}
|
||||
if(lastDisplay.buildVisibility == Blocks.padVisible && !lastDisplay.buildVisibility.get()){
|
||||
header.row();
|
||||
header.add("$attackpvponly").width(230f).wrap().colspan(3).left();
|
||||
}
|
||||
}).growX().left();
|
||||
topTable.row();
|
||||
//add requirement table
|
||||
@@ -305,7 +296,7 @@ public class PlacementFragment extends Fragment{
|
||||
Array<Block> getByCategory(Category cat){
|
||||
returnArray.clear();
|
||||
for(Block block : content.blocks()){
|
||||
if(block.buildCategory == cat && (block.isVisible() || block.buildVisibility == Blocks.padVisible)){
|
||||
if(block.buildCategory == cat && block.isVisible()){
|
||||
returnArray.add(block);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,6 +104,10 @@ public class Tile implements Position, TargetTrait{
|
||||
return block().offset() + worldy();
|
||||
}
|
||||
|
||||
public boolean isDarkened(){
|
||||
return block().solid && !block().synthetic() && block().fillsTile;
|
||||
}
|
||||
|
||||
public Floor floor(){
|
||||
return floor;
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import static io.anuke.mindustry.Vars.*;
|
||||
|
||||
public class StaticWall extends Rock{
|
||||
TextureRegion large;
|
||||
TextureRegion[][] split;
|
||||
|
||||
public StaticWall(String name){
|
||||
super(name);
|
||||
@@ -25,9 +26,7 @@ public class StaticWall extends Rock{
|
||||
int ry = tile.y / 2 * 2;
|
||||
|
||||
if(Core.atlas.isFound(large) && eq(rx, ry) && Mathf.randomSeed(Pos.get(rx, ry)) < 0.5){
|
||||
if(rx == tile.x && ry == tile.y){
|
||||
Draw.rect(large, tile.worldx() + tilesize / 2f, tile.worldy() + tilesize / 2f);
|
||||
}
|
||||
Draw.rect(split[tile.x % 2][1 - tile.y % 2], tile.worldx(), tile.worldy());
|
||||
}else if(variants > 0){
|
||||
Draw.rect(variantRegions[Mathf.randomSeed(tile.pos(), 0, Math.max(0, variantRegions.length - 1))], tile.worldx(), tile.worldy());
|
||||
}else{
|
||||
@@ -39,6 +38,7 @@ public class StaticWall extends Rock{
|
||||
public void load(){
|
||||
super.load();
|
||||
large = Core.atlas.find(name + "-large");
|
||||
split = large.split(32, 32);
|
||||
}
|
||||
|
||||
boolean eq(int rx, int ry){
|
||||
|
||||
@@ -15,11 +15,12 @@ import io.anuke.mindustry.world.*;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
import static io.anuke.mindustry.Vars.world;
|
||||
import static io.anuke.mindustry.Vars.*;
|
||||
|
||||
public class Door extends Wall{
|
||||
protected final Rectangle rect = new Rectangle();
|
||||
|
||||
protected int timerToggle = timers++;
|
||||
protected Effect openfx = Fx.dooropen;
|
||||
protected Effect closefx = Fx.doorclose;
|
||||
|
||||
@@ -39,7 +40,7 @@ public class Door extends Wall{
|
||||
entity.open = open;
|
||||
Door door = (Door)tile.block();
|
||||
|
||||
world.pathfinder.updateSolid(tile);
|
||||
pathfinder.updateSolid(tile);
|
||||
if(!entity.open){
|
||||
Effects.effect(door.openfx, tile.drawx(), tile.drawy());
|
||||
}else{
|
||||
@@ -81,7 +82,7 @@ public class Door extends Wall{
|
||||
public void tapped(Tile tile, Player player){
|
||||
DoorEntity entity = tile.entity();
|
||||
|
||||
if(Units.anyEntities(tile) && entity.open){
|
||||
if((Units.anyEntities(tile) && entity.open) || !tile.entity.timer.get(timerToggle, 30f)){
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,23 +1,20 @@
|
||||
package io.anuke.mindustry.world.blocks.defense;
|
||||
|
||||
import io.anuke.arc.Core;
|
||||
import io.anuke.arc.function.Consumer;
|
||||
import io.anuke.arc.graphics.Blending;
|
||||
import io.anuke.arc.graphics.Color;
|
||||
import io.anuke.arc.*;
|
||||
import io.anuke.arc.function.*;
|
||||
import io.anuke.arc.graphics.*;
|
||||
import io.anuke.arc.graphics.g2d.*;
|
||||
import io.anuke.arc.math.Mathf;
|
||||
import io.anuke.arc.util.Time;
|
||||
import io.anuke.mindustry.content.Fx;
|
||||
import io.anuke.arc.math.*;
|
||||
import io.anuke.arc.util.*;
|
||||
import io.anuke.mindustry.content.*;
|
||||
import io.anuke.mindustry.entities.*;
|
||||
import io.anuke.mindustry.entities.impl.BaseEntity;
|
||||
import io.anuke.mindustry.entities.impl.*;
|
||||
import io.anuke.mindustry.entities.traits.*;
|
||||
import io.anuke.mindustry.entities.type.TileEntity;
|
||||
import io.anuke.mindustry.graphics.Pal;
|
||||
import io.anuke.mindustry.world.Block;
|
||||
import io.anuke.mindustry.world.Tile;
|
||||
import io.anuke.mindustry.entities.type.*;
|
||||
import io.anuke.mindustry.graphics.*;
|
||||
import io.anuke.mindustry.world.*;
|
||||
import io.anuke.mindustry.world.consumers.*;
|
||||
import io.anuke.mindustry.world.meta.BlockStat;
|
||||
import io.anuke.mindustry.world.meta.StatUnit;
|
||||
import io.anuke.mindustry.world.meta.*;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
@@ -94,7 +91,6 @@ public class ForceProjector extends Block{
|
||||
@Override
|
||||
public void update(Tile tile){
|
||||
ForceEntity entity = tile.entity();
|
||||
boolean cheat = tile.isEnemyCheat();
|
||||
|
||||
if(entity.shield == null){
|
||||
entity.shield = new ShieldEntity(tile);
|
||||
@@ -109,21 +105,15 @@ public class ForceProjector extends Block{
|
||||
entity.cons.trigger();
|
||||
}
|
||||
|
||||
entity.radscl = Mathf.lerpDelta(entity.radscl, entity.broken ? 0f : 1f, 0.05f);
|
||||
entity.radscl = Mathf.lerpDelta(entity.radscl, entity.broken ? 0f : entity.warmup, 0.05f);
|
||||
|
||||
if(Mathf.chance(Time.delta() * entity.buildup / breakage * 0.1f)){
|
||||
Effects.effect(Fx.reactorsmoke, tile.drawx() + Mathf.range(tilesize / 2f), tile.drawy() + Mathf.range(tilesize / 2f));
|
||||
}
|
||||
|
||||
//use cases:
|
||||
// - There is enough power in the buffer, and there are no shots fired => Draw base power and keep shield up
|
||||
// - There is enough power in the buffer, but not enough power to cope for shots being fired => Draw all power and break shield
|
||||
// - There is enough power in the buffer and enough power to cope for shots being fired => Draw base power + additional power based on shots absorbed
|
||||
// - There is not enough base power in the buffer => Draw all power and break shield
|
||||
// - The generator is in the AI base and uses cheat mode => Only draw power from shots being absorbed
|
||||
|
||||
float relativePowerDraw = cheat ? 0f : 1f;
|
||||
entity.warmup = Mathf.lerpDelta(entity.warmup, entity.power.satisfaction, 0.1f);
|
||||
|
||||
/*
|
||||
if(entity.power.satisfaction < relativePowerDraw){
|
||||
entity.warmup = Mathf.lerpDelta(entity.warmup, 0f, 0.15f);
|
||||
entity.power.satisfaction = 0f;
|
||||
@@ -132,7 +122,7 @@ public class ForceProjector extends Block{
|
||||
}
|
||||
}else{
|
||||
entity.warmup = Mathf.lerpDelta(entity.warmup, 1f, 0.1f);
|
||||
}
|
||||
}*/
|
||||
|
||||
if(entity.buildup > 0){
|
||||
float scale = !entity.broken ? cooldownNormal : cooldownBrokenBase;
|
||||
@@ -145,7 +135,7 @@ public class ForceProjector extends Block{
|
||||
entity.buildup -= Time.delta() * scale;
|
||||
}
|
||||
|
||||
if(entity.broken && entity.buildup <= 0 && entity.warmup >= 0.9f){
|
||||
if(entity.broken && entity.buildup <= 0){
|
||||
entity.broken = false;
|
||||
}
|
||||
|
||||
|
||||
@@ -101,7 +101,7 @@ public class MendProjector extends Block{
|
||||
|
||||
@Override
|
||||
public void drawPlace(int x, int y, int rotation, boolean valid){
|
||||
Drawf.dashCircle(x * tilesize, y * tilesize, range, Pal.accent);
|
||||
Drawf.dashCircle(x * tilesize + offset(), y * tilesize + offset(), range, Pal.accent);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -109,10 +109,7 @@ public class PowerGraph{
|
||||
for(Tile battery : batteries){
|
||||
Consumers consumes = battery.block().consumes;
|
||||
if(consumes.hasPower()){
|
||||
ConsumePower consumePower = consumes.getPower();
|
||||
if(consumePower.capacity > 0f){
|
||||
battery.entity.power.satisfaction = Math.max(0.0f, battery.entity.power.satisfaction - consumedPowerPercentage);
|
||||
}
|
||||
battery.entity.power.satisfaction *= (1f-consumedPowerPercentage);
|
||||
}
|
||||
}
|
||||
return used;
|
||||
|
||||
@@ -122,7 +122,7 @@ public class PowerNode extends PowerBlock{
|
||||
private void getPotentialLinks(Tile tile, Consumer<Tile> others){
|
||||
Predicate<Tile> valid = other -> other != null && other != tile && other.entity != null && other.entity.power != null &&
|
||||
((!other.block().outputsPower && other.block().consumesPower) || (other.block().outputsPower && !other.block().consumesPower) || other.block() instanceof PowerNode) &&
|
||||
overlaps(tile.x * tilesize + offset(), tile.y *tilesize + offset(), other, laserRange * tilesize)
|
||||
overlaps(tile.x * tilesize + offset(), tile.y *tilesize + offset(), other, laserRange * tilesize) && other.getTeam() == player.getTeam()
|
||||
&& !other.entity.proximity().contains(tile) && !graphs.contains(other.entity.power.graph);
|
||||
|
||||
tempTiles.clear();
|
||||
@@ -222,20 +222,7 @@ public class PowerNode extends PowerBlock{
|
||||
Draw.color(Pal.placing);
|
||||
Drawf.circles(x * tilesize + offset(), y * tilesize + offset(), laserRange * tilesize);
|
||||
|
||||
getPotentialLinks(tile, other -> {
|
||||
Drawf.square(other.drawx(), other.drawy(), other.block().size * tilesize / 2f + 2f, Pal.place);
|
||||
});
|
||||
|
||||
/*
|
||||
for(int cx = (int)(x - laserRange - 1); cx <= x + laserRange + 1; cx++){
|
||||
for(int cy = (int)(y - laserRange - 1); cy <= y + laserRange + 1; cy++){
|
||||
Tile link = world.ltile(cx, cy);
|
||||
|
||||
if(link != null && !(link.x == x && link.y == y) && link.block().hasPower && overlaps(x * tilesize + offset(), y *tilesize + offset(), link, laserRange * tilesize)){
|
||||
Drawf.square(link.drawx(), link.drawy(), link.block().size * tilesize / 2f + 2f, link.pos() == lastPlaced ? Pal.place : Pal.accent);
|
||||
}
|
||||
}
|
||||
}*/
|
||||
getPotentialLinks(tile, other -> Drawf.square(other.drawx(), other.drawy(), other.block().size * tilesize / 2f + 2f, Pal.place));
|
||||
|
||||
Draw.reset();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,129 @@
|
||||
package io.anuke.mindustry.world.blocks.units;
|
||||
|
||||
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.scene.ui.*;
|
||||
import io.anuke.arc.scene.ui.layout.*;
|
||||
import io.anuke.arc.util.*;
|
||||
import io.anuke.mindustry.content.*;
|
||||
import io.anuke.mindustry.entities.*;
|
||||
import io.anuke.mindustry.entities.Effects.*;
|
||||
import io.anuke.mindustry.entities.type.*;
|
||||
import io.anuke.mindustry.entities.units.*;
|
||||
import io.anuke.mindustry.game.*;
|
||||
import io.anuke.mindustry.gen.*;
|
||||
import io.anuke.mindustry.graphics.*;
|
||||
import io.anuke.mindustry.world.*;
|
||||
import io.anuke.mindustry.world.meta.*;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
import static io.anuke.mindustry.Vars.*;
|
||||
|
||||
public class CommandCenter extends Block{
|
||||
protected TextureRegion[] commandRegions = new TextureRegion[UnitCommand.all.length];
|
||||
protected Color topColor = Pal.command;
|
||||
protected Color bottomColor = Color.valueOf("5e5e5e");
|
||||
protected Effect effect = Fx.commandSend;
|
||||
|
||||
public CommandCenter(String name){
|
||||
super(name);
|
||||
|
||||
flags = EnumSet.of(BlockFlag.comandCenter);
|
||||
destructible = true;
|
||||
solid = true;
|
||||
configurable = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void placed(Tile tile){
|
||||
super.placed(tile);
|
||||
ObjectSet<Tile> set = indexer.getAllied(tile.getTeam(), BlockFlag.comandCenter);
|
||||
|
||||
if(set.size > 0){
|
||||
CommandCenterEntity entity = tile.entity();
|
||||
CommandCenterEntity oe = set.first().entity();
|
||||
entity.command = oe.command;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load(){
|
||||
super.load();
|
||||
|
||||
for(UnitCommand cmd : UnitCommand.all){
|
||||
commandRegions[cmd.ordinal()] = Core.atlas.find("icon-command-" + cmd.name() + "-small");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Tile tile){
|
||||
CommandCenterEntity entity = tile.entity();
|
||||
super.draw(tile);
|
||||
|
||||
float size = iconsizesmall/4f;
|
||||
|
||||
Draw.color(bottomColor);
|
||||
Draw.rect(commandRegions[entity.command.ordinal()], tile.drawx(), tile.drawy() - 1, size, size);
|
||||
Draw.color(topColor);
|
||||
Draw.rect(commandRegions[entity.command.ordinal()], tile.drawx(), tile.drawy(), size, size);
|
||||
Draw.color();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void buildTable(Tile tile, Table table){
|
||||
CommandCenterEntity entity = tile.entity();
|
||||
ButtonGroup<ImageButton> group = new ButtonGroup<>();
|
||||
Table buttons = new Table();
|
||||
|
||||
for(UnitCommand cmd : UnitCommand.all){
|
||||
buttons.addImageButton("icon-command-" + cmd.name() + "-small", "clear-toggle-trans", iconsizesmall, () -> Call.onCommandCenterSet(player, tile, cmd))
|
||||
.size(44).group(group).update(b -> b.setChecked(entity.command == cmd));
|
||||
}
|
||||
table.add(buttons);
|
||||
table.row();
|
||||
table.label(() -> entity.command.localized()).style("outline").center().growX().get().setAlignment(Align.center);
|
||||
}
|
||||
|
||||
@Remote(called = Loc.server, forward = true, targets = Loc.both)
|
||||
public static void onCommandCenterSet(Player player, Tile tile, UnitCommand command){
|
||||
Effects.effect(((CommandCenter)tile.block()).effect, tile);
|
||||
|
||||
for(Tile center : indexer.getAllied(tile.getTeam(), BlockFlag.comandCenter)){
|
||||
if(center.block() instanceof CommandCenter){
|
||||
CommandCenterEntity entity = center.entity();
|
||||
entity.command = command;
|
||||
}
|
||||
}
|
||||
|
||||
Team team = (player == null ? tile.getTeam() : player.getTeam());
|
||||
|
||||
for(BaseUnit unit : unitGroups[team.ordinal()].all()){
|
||||
unit.onCommand(command);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public TileEntity newEntity(){
|
||||
return new CommandCenterEntity();
|
||||
}
|
||||
|
||||
public class CommandCenterEntity extends TileEntity{
|
||||
public UnitCommand command = UnitCommand.attack;
|
||||
|
||||
@Override
|
||||
public void write(DataOutput stream) throws IOException{
|
||||
super.write(stream);
|
||||
stream.writeByte(command.ordinal());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(DataInput stream, byte version) throws IOException{
|
||||
super.read(stream, version);
|
||||
command = UnitCommand.all[stream.readByte()];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,8 @@ public enum BlockFlag{
|
||||
producer(Float.MAX_VALUE),
|
||||
/** A turret. */
|
||||
turret(Float.MAX_VALUE),
|
||||
/** Only the command center block.*/
|
||||
comandCenter(Float.MAX_VALUE),
|
||||
/** Repair point. */
|
||||
repair(Float.MAX_VALUE);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user