Merge branch 'master' of https://github.com/Anuken/Mindustry into lights
# Conflicts: # core/src/io/anuke/mindustry/world/blocks/power/ItemLiquidGenerator.java
This commit is contained in:
@@ -9,8 +9,9 @@ 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.arc.util.async.*;
|
||||
import io.anuke.mindustry.core.*;
|
||||
import io.anuke.mindustry.game.*;
|
||||
import io.anuke.mindustry.ctype.Content;
|
||||
import io.anuke.mindustry.game.EventType.*;
|
||||
import io.anuke.mindustry.gen.*;
|
||||
import io.anuke.mindustry.graphics.*;
|
||||
@@ -81,6 +82,7 @@ public abstract class ClientLauncher extends ApplicationCore implements Platform
|
||||
add(netClient = new NetClient());
|
||||
|
||||
assets.load(mods);
|
||||
assets.load(schematics);
|
||||
|
||||
assets.loadRun("contentinit", ContentLoader.class, () -> {
|
||||
content.init();
|
||||
@@ -118,10 +120,11 @@ public abstract class ClientLauncher extends ApplicationCore implements Platform
|
||||
for(ApplicationListener listener : modules){
|
||||
listener.init();
|
||||
}
|
||||
super.resize(graphics.getWidth(), graphics.getHeight());
|
||||
mods.each(Mod::init);
|
||||
finished = true;
|
||||
Events.fire(new ClientLoadEvent());
|
||||
super.resize(graphics.getWidth(), graphics.getHeight());
|
||||
app.post(() -> app.post(() -> app.post(() -> app.post(() -> super.resize(graphics.getWidth(), graphics.getHeight())))));
|
||||
}
|
||||
}else{
|
||||
super.update();
|
||||
@@ -133,11 +136,7 @@ public abstract class ClientLauncher extends ApplicationCore implements Platform
|
||||
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
|
||||
}
|
||||
Threads.sleep((target - elapsed) / 1000000, (int)((target - elapsed) % 1000000));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -33,6 +33,10 @@ public class Vars implements Loadable{
|
||||
public static boolean loadLocales = true;
|
||||
/** Maximum number of broken blocks. TODO implement or remove.*/
|
||||
public static final int maxBrokenBlocks = 256;
|
||||
/** Maximum schematic size.*/
|
||||
public static final int maxSchematicSize = 32;
|
||||
/** All schematic base64 starts with this string.*/
|
||||
public static final String schematicBaseStart ="bXNjaAB";
|
||||
/** IO buffer size. */
|
||||
public static final int bufferSize = 8192;
|
||||
/** global charset, since Android doesn't support the Charsets class */
|
||||
@@ -128,10 +132,14 @@ public class Vars implements Loadable{
|
||||
public static FileHandle saveDirectory;
|
||||
/** data subdirectory used for mods */
|
||||
public static FileHandle modDirectory;
|
||||
/** data subdirectory used for schematics */
|
||||
public static FileHandle schematicDirectory;
|
||||
/** map file extension */
|
||||
public static final String mapExtension = "msav";
|
||||
/** save file extension */
|
||||
public static final String saveExtension = "msav";
|
||||
/** schematic file extension */
|
||||
public static final String schematicExtension = "msch";
|
||||
|
||||
/** list of all locales that can be switched to */
|
||||
public static Locale[] locales;
|
||||
@@ -146,6 +154,7 @@ public class Vars implements Loadable{
|
||||
public static LoopControl loops;
|
||||
public static Platform platform = new Platform(){};
|
||||
public static Mods mods;
|
||||
public static Schematics schematics = new Schematics();
|
||||
|
||||
public static World world;
|
||||
public static Maps maps;
|
||||
@@ -251,6 +260,7 @@ public class Vars implements Loadable{
|
||||
saveDirectory = dataDirectory.child("saves/");
|
||||
tmpDirectory = dataDirectory.child("tmp/");
|
||||
modDirectory = dataDirectory.child("mods/");
|
||||
schematicDirectory = dataDirectory.child("schematics/");
|
||||
|
||||
modDirectory.mkdirs();
|
||||
|
||||
@@ -261,7 +271,7 @@ public class Vars implements Loadable{
|
||||
public static void loadSettings(){
|
||||
Core.settings.setAppName(appName);
|
||||
|
||||
if(steam || "steam".equals(Version.modifier)){
|
||||
if(steam || (Version.modifier != null && Version.modifier.contains("steam"))){
|
||||
Core.settings.setDataDirectory(Core.files.local("saves/"));
|
||||
}
|
||||
|
||||
@@ -289,7 +299,6 @@ public class Vars implements Loadable{
|
||||
//no external bundle found
|
||||
|
||||
FileHandle handle = Core.files.internal("bundles/bundle");
|
||||
|
||||
Locale locale;
|
||||
String loc = Core.settings.getString("locale");
|
||||
if(loc.equals("default")){
|
||||
|
||||
@@ -2,7 +2,7 @@ package io.anuke.mindustry.ai;
|
||||
|
||||
import io.anuke.arc.*;
|
||||
import io.anuke.arc.collection.*;
|
||||
import io.anuke.arc.function.*;
|
||||
import io.anuke.arc.func.*;
|
||||
import io.anuke.arc.math.*;
|
||||
import io.anuke.arc.math.geom.*;
|
||||
import io.anuke.mindustry.content.*;
|
||||
@@ -27,7 +27,7 @@ public class BlockIndexer{
|
||||
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;
|
||||
private ObjectMap<Item, ObjectSet<Tile>> ores = new ObjectMap<>();
|
||||
/** Tags all quadrants. */
|
||||
private GridBits[] structQuadrants;
|
||||
/** Stores all damaged tile entities by team. */
|
||||
@@ -163,7 +163,11 @@ public class BlockIndexer{
|
||||
set.add(entity.tile);
|
||||
}
|
||||
|
||||
public TileEntity findTile(Team team, float x, float y, float range, Predicate<Tile> pred){
|
||||
public TileEntity findTile(Team team, float x, float y, float range, Boolf<Tile> pred){
|
||||
return findTile(team, x, y, range, pred, false);
|
||||
}
|
||||
|
||||
public TileEntity findTile(Team team, float x, float y, float range, Boolf<Tile> pred, boolean usePriority){
|
||||
TileEntity closest = null;
|
||||
float dst = 0;
|
||||
|
||||
@@ -178,13 +182,13 @@ public class BlockIndexer{
|
||||
|
||||
if(other == null) continue;
|
||||
|
||||
if(other.entity == null || other.getTeam() != team || !pred.test(other) || !other.block().targetable)
|
||||
if(other.entity == null || other.getTeam() != team || !pred.get(other) || !other.block().targetable)
|
||||
continue;
|
||||
|
||||
TileEntity e = other.entity;
|
||||
|
||||
float ndst = Mathf.dst(x, y, e.x, e.y);
|
||||
if(ndst < range && (closest == null || ndst < dst)){
|
||||
if(ndst < range && (closest == null || ndst < dst || (usePriority && closest.block.priority.ordinal() < e.block.priority.ordinal()))){
|
||||
dst = ndst;
|
||||
closest = e;
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ package io.anuke.mindustry.ai;
|
||||
import io.anuke.annotations.Annotations.*;
|
||||
import io.anuke.arc.*;
|
||||
import io.anuke.arc.collection.*;
|
||||
import io.anuke.arc.function.*;
|
||||
import io.anuke.arc.func.*;
|
||||
import io.anuke.arc.math.geom.*;
|
||||
import io.anuke.arc.util.*;
|
||||
import io.anuke.arc.util.ArcAnnotate.*;
|
||||
@@ -317,15 +317,15 @@ public class Pathfinder implements Runnable{
|
||||
|
||||
public static final PathTarget[] all = values();
|
||||
|
||||
private final BiConsumer<Team, IntArray> targeter;
|
||||
private final Cons2<Team, IntArray> targeter;
|
||||
|
||||
PathTarget(BiConsumer<Team, IntArray> targeter){
|
||||
PathTarget(Cons2<Team, IntArray> targeter){
|
||||
this.targeter = targeter;
|
||||
}
|
||||
|
||||
/** Get targets. This must run on the main thread.*/
|
||||
public IntArray getTargets(Team team, IntArray out){
|
||||
targeter.accept(team, out);
|
||||
targeter.get(team, out);
|
||||
return out;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ package io.anuke.mindustry.ai;
|
||||
|
||||
import io.anuke.arc.Events;
|
||||
import io.anuke.arc.collection.Array;
|
||||
import io.anuke.arc.function.PositionConsumer;
|
||||
import io.anuke.arc.func.Floatc2;
|
||||
import io.anuke.arc.math.Angles;
|
||||
import io.anuke.arc.math.Mathf;
|
||||
import io.anuke.arc.util.Time;
|
||||
@@ -99,17 +99,17 @@ public class WaveSpawner{
|
||||
}
|
||||
}
|
||||
|
||||
private void eachFlyerSpawn(PositionConsumer cons){
|
||||
private void eachFlyerSpawn(Floatc2 cons){
|
||||
for(FlyerSpawn spawn : flySpawns){
|
||||
float trns = (world.width() + world.height()) * tilesize;
|
||||
float spawnX = Mathf.clamp(world.width() * tilesize / 2f + Angles.trnsx(spawn.angle, trns), -margin, world.width() * tilesize + margin);
|
||||
float spawnY = Mathf.clamp(world.height() * tilesize / 2f + Angles.trnsy(spawn.angle, trns), -margin, world.height() * tilesize + margin);
|
||||
cons.accept(spawnX, spawnY);
|
||||
cons.get(spawnX, spawnY);
|
||||
}
|
||||
|
||||
if(state.rules.attackMode && state.teams.isActive(waveTeam)){
|
||||
for(Tile core : state.teams.get(waveTeam).cores){
|
||||
cons.accept(core.worldx(), core.worldy());
|
||||
cons.get(core.worldx(), core.worldy());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,10 +7,10 @@ import io.anuke.arc.graphics.g2d.*;
|
||||
import io.anuke.arc.math.*;
|
||||
import io.anuke.arc.util.*;
|
||||
import io.anuke.mindustry.*;
|
||||
import io.anuke.mindustry.ctype.ContentList;
|
||||
import io.anuke.mindustry.entities.*;
|
||||
import io.anuke.mindustry.entities.bullet.*;
|
||||
import io.anuke.mindustry.entities.type.Bullet;
|
||||
import io.anuke.mindustry.game.*;
|
||||
import io.anuke.mindustry.entities.type.*;
|
||||
import io.anuke.mindustry.gen.*;
|
||||
import io.anuke.mindustry.graphics.*;
|
||||
import io.anuke.mindustry.type.*;
|
||||
@@ -29,8 +29,6 @@ import io.anuke.mindustry.world.consumers.*;
|
||||
import io.anuke.mindustry.world.meta.*;
|
||||
import io.anuke.mindustry.world.modules.*;
|
||||
|
||||
import static io.anuke.mindustry.Vars.*;
|
||||
|
||||
public class Blocks implements ContentList{
|
||||
public static Block
|
||||
|
||||
@@ -54,11 +52,11 @@ public class Blocks implements ContentList{
|
||||
|
||||
//defense
|
||||
scrapWall, scrapWallLarge, scrapWallHuge, scrapWallGigantic, thruster, //ok, these names are getting ridiculous, but at least I don't have humongous walls yet
|
||||
copperWall, copperWallLarge, titaniumWall, titaniumWallLarge, thoriumWall, thoriumWallLarge, door, doorLarge,
|
||||
copperWall, copperWallLarge, titaniumWall, titaniumWallLarge, plastaniumWall, plastaniumWallLarge, thoriumWall, thoriumWallLarge, door, doorLarge,
|
||||
phaseWall, phaseWallLarge, surgeWall, surgeWallLarge, mender, mendProjector, overdriveProjector, forceProjector, shockMine,
|
||||
|
||||
//transport
|
||||
conveyor, titaniumConveyor, armoredConveyor, distributor, junction, itemBridge, phaseConveyor, sorter, router, overflowGate, massDriver,
|
||||
conveyor, titaniumConveyor, armoredConveyor, distributor, junction, itemBridge, phaseConveyor, sorter, invertedSorter, router, overflowGate, massDriver,
|
||||
|
||||
//liquids
|
||||
mechanicalPump, rotaryPump, thermalPump, conduit, pulseConduit, liquidRouter, liquidTank, liquidJunction, bridgeConduit, phaseConduit,
|
||||
@@ -716,23 +714,23 @@ public class Blocks implements ContentList{
|
||||
//region sandbox
|
||||
|
||||
powerVoid = new PowerVoid("power-void"){{
|
||||
requirements(Category.power, () -> state.rules.infiniteResources, ItemStack.with());
|
||||
requirements(Category.power, BuildVisibility.sandboxOnly, ItemStack.with());
|
||||
alwaysUnlocked = true;
|
||||
}};
|
||||
powerSource = new PowerSource("power-source"){{
|
||||
requirements(Category.power, () -> state.rules.infiniteResources, ItemStack.with());
|
||||
requirements(Category.power, BuildVisibility.sandboxOnly, ItemStack.with());
|
||||
alwaysUnlocked = true;
|
||||
}};
|
||||
itemSource = new ItemSource("item-source"){{
|
||||
requirements(Category.distribution, () -> state.rules.infiniteResources, ItemStack.with());
|
||||
requirements(Category.distribution, BuildVisibility.sandboxOnly, ItemStack.with());
|
||||
alwaysUnlocked = true;
|
||||
}};
|
||||
itemVoid = new ItemVoid("item-void"){{
|
||||
requirements(Category.distribution, () -> state.rules.infiniteResources, ItemStack.with());
|
||||
requirements(Category.distribution, BuildVisibility.sandboxOnly, ItemStack.with());
|
||||
alwaysUnlocked = true;
|
||||
}};
|
||||
liquidSource = new LiquidSource("liquid-source"){{
|
||||
requirements(Category.liquid, () -> state.rules.infiniteResources, ItemStack.with());
|
||||
requirements(Category.liquid, BuildVisibility.sandboxOnly, ItemStack.with());
|
||||
alwaysUnlocked = true;
|
||||
}};
|
||||
message = new MessageBlock("message"){{
|
||||
@@ -745,27 +743,27 @@ public class Blocks implements ContentList{
|
||||
int wallHealthMultiplier = 4;
|
||||
|
||||
scrapWall = new Wall("scrap-wall"){{
|
||||
requirements(Category.defense, () -> state.rules.infiniteResources, ItemStack.with());
|
||||
requirements(Category.defense, BuildVisibility.sandboxOnly, ItemStack.with());
|
||||
health = 60 * wallHealthMultiplier;
|
||||
variants = 5;
|
||||
}};
|
||||
|
||||
scrapWallLarge = new Wall("scrap-wall-large"){{
|
||||
requirements(Category.defense, () -> state.rules.infiniteResources, ItemStack.with());
|
||||
requirements(Category.defense, BuildVisibility.sandboxOnly, ItemStack.with());
|
||||
health = 60 * 4 * wallHealthMultiplier;
|
||||
size = 2;
|
||||
variants = 4;
|
||||
}};
|
||||
|
||||
scrapWallHuge = new Wall("scrap-wall-huge"){{
|
||||
requirements(Category.defense, () -> state.rules.infiniteResources, ItemStack.with());
|
||||
requirements(Category.defense, BuildVisibility.sandboxOnly, ItemStack.with());
|
||||
health = 60 * 9 * wallHealthMultiplier;
|
||||
size = 3;
|
||||
variants = 3;
|
||||
}};
|
||||
|
||||
scrapWallGigantic = new Wall("scrap-wall-gigantic"){{
|
||||
requirements(Category.defense, () -> state.rules.infiniteResources, ItemStack.with());
|
||||
requirements(Category.defense, BuildVisibility.sandboxOnly, ItemStack.with());
|
||||
health = 60 * 16 * wallHealthMultiplier;
|
||||
size = 4;
|
||||
}};
|
||||
@@ -797,6 +795,19 @@ public class Blocks implements ContentList{
|
||||
size = 2;
|
||||
}};
|
||||
|
||||
plastaniumWall = new Wall("plastanium-wall"){{
|
||||
requirements(Category.defense, ItemStack.with(Items.plastanium, 5, Items.metaglass, 2));
|
||||
health = 190 * wallHealthMultiplier;
|
||||
insulated = true;
|
||||
}};
|
||||
|
||||
plastaniumWallLarge = new Wall("plastanium-wall-large"){{
|
||||
requirements(Category.defense, ItemStack.mult(plastaniumWall.requirements, 4));
|
||||
health = 190 * wallHealthMultiplier * 4;
|
||||
size = 2;
|
||||
insulated = true;
|
||||
}};
|
||||
|
||||
thoriumWall = new Wall("thorium-wall"){{
|
||||
requirements(Category.defense, ItemStack.with(Items.thorium, 6));
|
||||
health = 200 * wallHealthMultiplier;
|
||||
@@ -935,7 +946,11 @@ public class Blocks implements ContentList{
|
||||
|
||||
sorter = new Sorter("sorter"){{
|
||||
requirements(Category.distribution, ItemStack.with(Items.lead, 2, Items.copper, 2));
|
||||
}};
|
||||
|
||||
invertedSorter = new Sorter("inverted-sorter"){{
|
||||
requirements(Category.distribution, ItemStack.with(Items.lead, 2, Items.copper, 2));
|
||||
invert = true;
|
||||
}};
|
||||
|
||||
router = new Router("router"){{
|
||||
@@ -965,12 +980,12 @@ public class Blocks implements ContentList{
|
||||
//region liquid
|
||||
|
||||
mechanicalPump = new Pump("mechanical-pump"){{
|
||||
requirements(Category.liquid, ItemStack.with(Items.copper, 15, Items.lead, 10));
|
||||
requirements(Category.liquid, ItemStack.with(Items.copper, 15, Items.metaglass, 10));
|
||||
pumpAmount = 0.1f;
|
||||
}};
|
||||
|
||||
rotaryPump = new Pump("rotary-pump"){{
|
||||
requirements(Category.liquid, ItemStack.with(Items.copper, 70, Items.lead, 50, Items.silicon, 20, Items.titanium, 35));
|
||||
requirements(Category.liquid, ItemStack.with(Items.copper, 70, Items.metaglass, 50, Items.silicon, 20, Items.titanium, 35));
|
||||
pumpAmount = 0.8f;
|
||||
consumes.power(0.15f);
|
||||
liquidCapacity = 30f;
|
||||
@@ -979,7 +994,7 @@ public class Blocks implements ContentList{
|
||||
}};
|
||||
|
||||
thermalPump = new Pump("thermal-pump"){{
|
||||
requirements(Category.liquid, ItemStack.with(Items.copper, 80, Items.lead, 65, Items.silicon, 30, Items.titanium, 40, Items.thorium, 35));
|
||||
requirements(Category.liquid, ItemStack.with(Items.copper, 80, Items.metaglass, 70, Items.silicon, 30, Items.titanium, 40, Items.thorium, 35));
|
||||
pumpAmount = 1.5f;
|
||||
consumes.power(0.30f);
|
||||
liquidCapacity = 40f;
|
||||
@@ -993,13 +1008,13 @@ public class Blocks implements ContentList{
|
||||
}};
|
||||
|
||||
pulseConduit = new Conduit("pulse-conduit"){{
|
||||
requirements(Category.liquid, ItemStack.with(Items.titanium, 1, Items.metaglass, 1));
|
||||
requirements(Category.liquid, ItemStack.with(Items.titanium, 2, Items.metaglass, 1));
|
||||
liquidCapacity = 16f;
|
||||
health = 90;
|
||||
}};
|
||||
|
||||
liquidRouter = new LiquidRouter("liquid-router"){{
|
||||
requirements(Category.liquid, ItemStack.with(Items.titanium, 2, Items.metaglass, 2));
|
||||
requirements(Category.liquid, ItemStack.with(Items.graphite, 4, Items.metaglass, 2));
|
||||
liquidCapacity = 20f;
|
||||
}};
|
||||
|
||||
@@ -1011,11 +1026,11 @@ public class Blocks implements ContentList{
|
||||
}};
|
||||
|
||||
liquidJunction = new LiquidJunction("liquid-junction"){{
|
||||
requirements(Category.liquid, ItemStack.with(Items.titanium, 2, Items.metaglass, 2));
|
||||
requirements(Category.liquid, ItemStack.with(Items.graphite, 2, Items.metaglass, 2));
|
||||
}};
|
||||
|
||||
bridgeConduit = new LiquidExtendingBridge("bridge-conduit"){{
|
||||
requirements(Category.liquid, ItemStack.with(Items.titanium, 4, Items.metaglass, 4));
|
||||
requirements(Category.liquid, ItemStack.with(Items.graphite, 4, Items.metaglass, 8));
|
||||
range = 4;
|
||||
hasPower = false;
|
||||
}};
|
||||
@@ -1083,11 +1098,12 @@ public class Blocks implements ContentList{
|
||||
size = 2;
|
||||
}};
|
||||
|
||||
differentialGenerator = new SingleTypeGenerator(true, false, "differential-generator"){{
|
||||
differentialGenerator = new SingleTypeGenerator("differential-generator"){{
|
||||
requirements(Category.power, ItemStack.with(Items.copper, 70, Items.titanium, 50, Items.lead, 100, Items.silicon, 65, Items.metaglass, 50));
|
||||
powerProduction = 16f;
|
||||
itemDuration = 120f;
|
||||
hasLiquids = true;
|
||||
hasItems = true;
|
||||
size = 3;
|
||||
|
||||
consumes.item(Items.pyratite).optional(true, false);
|
||||
@@ -1230,7 +1246,7 @@ public class Blocks implements ContentList{
|
||||
//region storage
|
||||
|
||||
coreShard = new CoreBlock("core-shard"){{
|
||||
requirements(Category.effect, () -> false, ItemStack.with(Items.titanium, 1000));
|
||||
requirements(Category.effect, BuildVisibility.debugOnly, ItemStack.with(Items.titanium, 4000));
|
||||
alwaysUnlocked = true;
|
||||
|
||||
health = 1100;
|
||||
@@ -1239,7 +1255,7 @@ public class Blocks implements ContentList{
|
||||
}};
|
||||
|
||||
coreFoundation = new CoreBlock("core-foundation"){{
|
||||
requirements(Category.effect, () -> false, ItemStack.with(Items.titanium, 1500, Items.silicon, 1000));
|
||||
requirements(Category.effect, BuildVisibility.debugOnly, ItemStack.with(Items.titanium, 400, Items.silicon, 3000));
|
||||
|
||||
health = 2000;
|
||||
itemCapacity = 9000;
|
||||
@@ -1247,7 +1263,7 @@ public class Blocks implements ContentList{
|
||||
}};
|
||||
|
||||
coreNucleus = new CoreBlock("core-nucleus"){{
|
||||
requirements(Category.effect, () -> false, ItemStack.with(Items.titanium, 4000, Items.silicon, 2000, Items.surgealloy, 1000));
|
||||
requirements(Category.effect, BuildVisibility.debugOnly, ItemStack.with(Items.titanium, 4000, Items.silicon, 2000, Items.surgealloy, 3000));
|
||||
|
||||
health = 4000;
|
||||
itemCapacity = 13000;
|
||||
@@ -1272,7 +1288,7 @@ public class Blocks implements ContentList{
|
||||
}};
|
||||
|
||||
launchPad = new LaunchPad("launch-pad"){{
|
||||
requirements(Category.effect, () -> world.isZone(), ItemStack.with(Items.copper, 250, Items.silicon, 75, Items.lead, 100));
|
||||
requirements(Category.effect, BuildVisibility.campaignOnly, ItemStack.with(Items.copper, 250, Items.silicon, 75, Items.lead, 100));
|
||||
size = 3;
|
||||
itemCapacity = 100;
|
||||
launchTime = 60f * 16;
|
||||
@@ -1281,7 +1297,7 @@ public class Blocks implements ContentList{
|
||||
}};
|
||||
|
||||
launchPadLarge = new LaunchPad("launch-pad-large"){{
|
||||
requirements(Category.effect, () -> world.isZone(), ItemStack.with(Items.titanium, 200, Items.silicon, 150, Items.lead, 250, Items.plastanium, 75));
|
||||
requirements(Category.effect, BuildVisibility.campaignOnly, ItemStack.with(Items.titanium, 200, Items.silicon, 150, Items.lead, 250, Items.plastanium, 75));
|
||||
size = 4;
|
||||
itemCapacity = 250;
|
||||
launchTime = 60f * 14;
|
||||
@@ -1341,7 +1357,8 @@ public class Blocks implements ContentList{
|
||||
Items.pyratite, Bullets.pyraFlame
|
||||
);
|
||||
recoil = 0f;
|
||||
reload = 4f;
|
||||
reload = 5f;
|
||||
coolantMultiplier = 2f;
|
||||
range = 60f;
|
||||
shootCone = 50f;
|
||||
targetAir = false;
|
||||
@@ -1612,7 +1629,7 @@ public class Blocks implements ContentList{
|
||||
size = 4;
|
||||
shootShake = 2f;
|
||||
range = 190f;
|
||||
reload = 50f;
|
||||
reload = 80f;
|
||||
firingMoveFract = 0.5f;
|
||||
shootDuration = 220f;
|
||||
powerUse = 14f;
|
||||
@@ -1629,7 +1646,7 @@ public class Blocks implements ContentList{
|
||||
|
||||
draugFactory = new UnitFactory("draug-factory"){{
|
||||
requirements(Category.units, ItemStack.with(Items.copper, 30, Items.lead, 70));
|
||||
type = UnitTypes.draug;
|
||||
unitType = UnitTypes.draug;
|
||||
produceTime = 2500;
|
||||
size = 2;
|
||||
maxSpawn = 1;
|
||||
@@ -1639,7 +1656,7 @@ public class Blocks implements ContentList{
|
||||
|
||||
spiritFactory = new UnitFactory("spirit-factory"){{
|
||||
requirements(Category.units, ItemStack.with(Items.metaglass, 45, Items.lead, 55, Items.silicon, 45));
|
||||
type = UnitTypes.spirit;
|
||||
unitType = UnitTypes.spirit;
|
||||
produceTime = 4000;
|
||||
size = 2;
|
||||
maxSpawn = 1;
|
||||
@@ -1649,7 +1666,7 @@ public class Blocks implements ContentList{
|
||||
|
||||
phantomFactory = new UnitFactory("phantom-factory"){{
|
||||
requirements(Category.units, ItemStack.with(Items.titanium, 50, Items.thorium, 60, Items.lead, 65, Items.silicon, 105));
|
||||
type = UnitTypes.phantom;
|
||||
unitType = UnitTypes.phantom;
|
||||
produceTime = 4400;
|
||||
size = 2;
|
||||
maxSpawn = 1;
|
||||
@@ -1666,7 +1683,7 @@ public class Blocks implements ContentList{
|
||||
|
||||
wraithFactory = new UnitFactory("wraith-factory"){{
|
||||
requirements(Category.units, ItemStack.with(Items.titanium, 30, Items.lead, 40, Items.silicon, 45));
|
||||
type = UnitTypes.wraith;
|
||||
unitType = UnitTypes.wraith;
|
||||
produceTime = 700;
|
||||
size = 2;
|
||||
consumes.power(0.5f);
|
||||
@@ -1675,7 +1692,7 @@ public class Blocks implements ContentList{
|
||||
|
||||
ghoulFactory = new UnitFactory("ghoul-factory"){{
|
||||
requirements(Category.units, ItemStack.with(Items.titanium, 75, Items.lead, 65, Items.silicon, 110));
|
||||
type = UnitTypes.ghoul;
|
||||
unitType = UnitTypes.ghoul;
|
||||
produceTime = 1150;
|
||||
size = 3;
|
||||
consumes.power(1.2f);
|
||||
@@ -1684,7 +1701,7 @@ public class Blocks implements ContentList{
|
||||
|
||||
revenantFactory = new UnitFactory("revenant-factory"){{
|
||||
requirements(Category.units, ItemStack.with(Items.plastanium, 50, Items.titanium, 150, Items.lead, 150, Items.silicon, 200));
|
||||
type = UnitTypes.revenant;
|
||||
unitType = UnitTypes.revenant;
|
||||
produceTime = 2000;
|
||||
size = 4;
|
||||
consumes.power(3f);
|
||||
@@ -1693,7 +1710,7 @@ public class Blocks implements ContentList{
|
||||
|
||||
daggerFactory = new UnitFactory("dagger-factory"){{
|
||||
requirements(Category.units, ItemStack.with(Items.lead, 55, Items.silicon, 35));
|
||||
type = UnitTypes.dagger;
|
||||
unitType = UnitTypes.dagger;
|
||||
produceTime = 850;
|
||||
size = 2;
|
||||
consumes.power(0.5f);
|
||||
@@ -1702,7 +1719,7 @@ public class Blocks implements ContentList{
|
||||
|
||||
crawlerFactory = new UnitFactory("crawler-factory"){{
|
||||
requirements(Category.units, ItemStack.with(Items.lead, 45, Items.silicon, 30));
|
||||
type = UnitTypes.crawler;
|
||||
unitType = UnitTypes.crawler;
|
||||
produceTime = 300;
|
||||
size = 2;
|
||||
maxSpawn = 6;
|
||||
@@ -1712,7 +1729,7 @@ public class Blocks implements ContentList{
|
||||
|
||||
titanFactory = new UnitFactory("titan-factory"){{
|
||||
requirements(Category.units, ItemStack.with(Items.graphite, 50, Items.lead, 50, Items.silicon, 45));
|
||||
type = UnitTypes.titan;
|
||||
unitType = UnitTypes.titan;
|
||||
produceTime = 1050;
|
||||
size = 3;
|
||||
consumes.power(0.60f);
|
||||
@@ -1721,7 +1738,7 @@ public class Blocks implements ContentList{
|
||||
|
||||
fortressFactory = new UnitFactory("fortress-factory"){{
|
||||
requirements(Category.units, ItemStack.with(Items.thorium, 40, Items.lead, 110, Items.silicon, 75));
|
||||
type = UnitTypes.fortress;
|
||||
unitType = UnitTypes.fortress;
|
||||
produceTime = 2000;
|
||||
size = 3;
|
||||
maxSpawn = 3;
|
||||
|
||||
@@ -4,11 +4,11 @@ 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.ctype.ContentList;
|
||||
import io.anuke.mindustry.entities.*;
|
||||
import io.anuke.mindustry.entities.bullet.*;
|
||||
import io.anuke.mindustry.entities.effect.*;
|
||||
import io.anuke.mindustry.entities.type.*;
|
||||
import io.anuke.mindustry.game.*;
|
||||
import io.anuke.mindustry.graphics.*;
|
||||
import io.anuke.mindustry.world.*;
|
||||
|
||||
@@ -99,8 +99,7 @@ public class Bullets implements ContentList{
|
||||
collidesTiles = false;
|
||||
splashDamageRadius = 25f;
|
||||
splashDamage = 30f;
|
||||
incendAmount = 4;
|
||||
incendSpread = 11f;
|
||||
status = StatusEffects.burning;
|
||||
frontColor = Pal.lightishOrange;
|
||||
backColor = Pal.lightOrange;
|
||||
trailEffect = Fx.incendTrail;
|
||||
@@ -228,8 +227,7 @@ public class Bullets implements ContentList{
|
||||
splashDamage = 10f;
|
||||
lifetime = 160f;
|
||||
hitEffect = Fx.blastExplosion;
|
||||
incendSpread = 10f;
|
||||
incendAmount = 3;
|
||||
status = StatusEffects.burning;
|
||||
}};
|
||||
|
||||
missileSurge = new MissileBulletType(4.4f, 15, "bullet"){{
|
||||
@@ -342,9 +340,7 @@ public class Bullets implements ContentList{
|
||||
bulletHeight = 12f;
|
||||
frontColor = Pal.lightishOrange;
|
||||
backColor = Pal.lightOrange;
|
||||
incendSpread = 3f;
|
||||
incendAmount = 1;
|
||||
incendChance = 0.3f;
|
||||
status = StatusEffects.burning;
|
||||
inaccuracy = 3f;
|
||||
lifetime = 60f;
|
||||
}};
|
||||
@@ -354,9 +350,7 @@ public class Bullets implements ContentList{
|
||||
bulletHeight = 12f;
|
||||
frontColor = Color.valueOf("feb380");
|
||||
backColor = Color.valueOf("ea8878");
|
||||
incendSpread = 3f;
|
||||
incendAmount = 1;
|
||||
incendChance = 0.3f;
|
||||
status = StatusEffects.burning;
|
||||
lifetime = 60f;
|
||||
}};
|
||||
|
||||
@@ -385,9 +379,7 @@ public class Bullets implements ContentList{
|
||||
bulletHeight = 21f;
|
||||
frontColor = Pal.lightishOrange;
|
||||
backColor = Pal.lightOrange;
|
||||
incendSpread = 3f;
|
||||
incendAmount = 2;
|
||||
incendChance = 0.3f;
|
||||
status = StatusEffects.burning;
|
||||
shootEffect = Fx.shootBig;
|
||||
}};
|
||||
|
||||
|
||||
@@ -5,12 +5,13 @@ 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.ctype.ContentList;
|
||||
import io.anuke.mindustry.entities.Effects.*;
|
||||
import io.anuke.mindustry.entities.effect.GroundEffectEntity.*;
|
||||
import io.anuke.mindustry.entities.type.*;
|
||||
import io.anuke.mindustry.game.*;
|
||||
import io.anuke.mindustry.graphics.*;
|
||||
import io.anuke.mindustry.type.*;
|
||||
import io.anuke.mindustry.ui.Cicon;
|
||||
|
||||
import static io.anuke.mindustry.Vars.tilesize;
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package io.anuke.mindustry.content;
|
||||
|
||||
import io.anuke.arc.graphics.Color;
|
||||
import io.anuke.mindustry.game.ContentList;
|
||||
import io.anuke.mindustry.ctype.ContentList;
|
||||
import io.anuke.mindustry.type.Item;
|
||||
import io.anuke.mindustry.type.ItemType;
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package io.anuke.mindustry.content;
|
||||
|
||||
import io.anuke.arc.graphics.Color;
|
||||
import io.anuke.mindustry.game.ContentList;
|
||||
import io.anuke.mindustry.ctype.ContentList;
|
||||
import io.anuke.mindustry.type.Liquid;
|
||||
|
||||
public class Liquids implements ContentList{
|
||||
@@ -26,6 +26,7 @@ public class Liquids implements ContentList{
|
||||
flammability = 1.2f;
|
||||
explosiveness = 1.2f;
|
||||
heatCapacity = 0.7f;
|
||||
barColor = Color.valueOf("6b675f");
|
||||
effect = StatusEffects.tarred;
|
||||
}};
|
||||
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
package io.anuke.mindustry.content;
|
||||
|
||||
import io.anuke.mindustry.game.ContentList;
|
||||
import io.anuke.mindustry.type.Loadout;
|
||||
import io.anuke.mindustry.ctype.*;
|
||||
import io.anuke.mindustry.game.*;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
public class Loadouts implements ContentList{
|
||||
public static Loadout
|
||||
public static Schematic
|
||||
basicShard,
|
||||
advancedShard,
|
||||
basicFoundation,
|
||||
@@ -12,43 +14,13 @@ public class Loadouts implements ContentList{
|
||||
|
||||
@Override
|
||||
public void load(){
|
||||
basicShard = new Loadout(
|
||||
" ### ",
|
||||
" #1# ",
|
||||
" ### ",
|
||||
" ^ ^ ",
|
||||
" ## ## ",
|
||||
" C# C# "
|
||||
);
|
||||
|
||||
advancedShard = new Loadout(
|
||||
" ### ",
|
||||
" #1# ",
|
||||
"#######",
|
||||
"C#^ ^C#",
|
||||
" ## ## ",
|
||||
" C# C# "
|
||||
);
|
||||
|
||||
basicFoundation = new Loadout(
|
||||
" #### ",
|
||||
" #### ",
|
||||
" #2## ",
|
||||
" #### ",
|
||||
" ^^^^ ",
|
||||
" ###### ",
|
||||
" C#C#C# "
|
||||
);
|
||||
|
||||
basicNucleus = new Loadout(
|
||||
" ##### ",
|
||||
" ##### ",
|
||||
" ##3## ",
|
||||
" ##### ",
|
||||
" >#####< ",
|
||||
" ^ ^ ^ ^ ",
|
||||
"#### ####",
|
||||
"C#C# C#C#"
|
||||
);
|
||||
try{
|
||||
basicShard = Schematics.readBase64("bXNjaAB4nD2K2wqAIBiD5ymibnoRn6YnEP1BwUMoBL19FuJ2sbFvUFgYZDaJsLeQrkinN9UJHImsNzlYE7WrIUastuSbnlKx2VJJt+8IQGGKdfO/8J5yrGJSMegLg+YUIA==");
|
||||
advancedShard = Schematics.readBase64("bXNjaAB4nD2LjQqAIAyET7OMIOhFfJqeYMxBgSkYCL199gu33fFtB4tOwUTaBCP5QpHFzwtl32DahBeKK1NwPq8hoOcUixwpY+CUxe3XIwBbB/pa6tadVCUP02hgHvp5vZq/0b7pBHPYFOQ=");
|
||||
basicFoundation = Schematics.readBase64("bXNjaAB4nD1OSQ6DMBBzFhVu8BG+0X8MQyoiJTNSukj8nlCi2Adbtg/GA4OBF8oB00rvyE/9ykafqOIw58A7SWRKy1ZiShhZ5RcOLZhYS1hefQ1gRIeptH9jq/qW2lvc1d2tgWsOfVX/tOwE86AYBA==");
|
||||
basicNucleus = Schematics.readBase64("bXNjaAB4nD2MUQqAIBBEJy0s6qOLdJXuYNtCgikYBd2+LNmdj308hkGHtkId7M4YFns4mk/yfB4a48602eDI+mlNznu0FMPFd0wYKCaewl8F0EOueqM+yKSLVfJrNKWnSw/FZGzEGXFG9sy/px4gEBW1");
|
||||
}catch(IOException e){
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,11 +6,11 @@ import io.anuke.arc.graphics.g2d.*;
|
||||
import io.anuke.arc.math.*;
|
||||
import io.anuke.arc.util.*;
|
||||
import io.anuke.mindustry.*;
|
||||
import io.anuke.mindustry.ctype.ContentList;
|
||||
import io.anuke.mindustry.entities.*;
|
||||
import io.anuke.mindustry.entities.bullet.*;
|
||||
import io.anuke.mindustry.entities.effect.*;
|
||||
import io.anuke.mindustry.entities.type.*;
|
||||
import io.anuke.mindustry.game.*;
|
||||
import io.anuke.mindustry.gen.*;
|
||||
import io.anuke.mindustry.graphics.*;
|
||||
import io.anuke.mindustry.type.*;
|
||||
@@ -38,7 +38,7 @@ public class Mechs implements ContentList{
|
||||
weapon = new Weapon("blaster"){{
|
||||
length = 1.5f;
|
||||
reload = 14f;
|
||||
roundrobin = true;
|
||||
alternate = true;
|
||||
ejectEffect = Fx.shellEjectSmall;
|
||||
bullet = Bullets.standardMechSmall;
|
||||
}};
|
||||
@@ -71,7 +71,7 @@ public class Mechs implements ContentList{
|
||||
length = 1f;
|
||||
reload = 55f;
|
||||
shotDelay = 3f;
|
||||
roundrobin = true;
|
||||
alternate = true;
|
||||
shots = 2;
|
||||
inaccuracy = 0f;
|
||||
ejectEffect = Fx.none;
|
||||
@@ -116,7 +116,7 @@ public class Mechs implements ContentList{
|
||||
weapon = new Weapon("heal-blaster"){{
|
||||
length = 1.5f;
|
||||
reload = 24f;
|
||||
roundrobin = false;
|
||||
alternate = false;
|
||||
ejectEffect = Fx.none;
|
||||
recoil = 2f;
|
||||
bullet = Bullets.healBullet;
|
||||
@@ -168,7 +168,7 @@ public class Mechs implements ContentList{
|
||||
shots = 4;
|
||||
spacing = 8f;
|
||||
inaccuracy = 8f;
|
||||
roundrobin = true;
|
||||
alternate = true;
|
||||
ejectEffect = Fx.none;
|
||||
shake = 3f;
|
||||
bullet = Bullets.missileSwarm;
|
||||
@@ -194,7 +194,7 @@ public class Mechs implements ContentList{
|
||||
|
||||
@Override
|
||||
public void updateAlt(Player player){
|
||||
float scl = 1f - player.shootHeat / 2f;
|
||||
float scl = 1f - player.shootHeat / 2f*Time.delta();
|
||||
player.velocity().scl(scl);
|
||||
}
|
||||
|
||||
@@ -232,7 +232,7 @@ public class Mechs implements ContentList{
|
||||
weapon = new Weapon("blaster"){{
|
||||
length = 1.5f;
|
||||
reload = 15f;
|
||||
roundrobin = true;
|
||||
alternate = true;
|
||||
ejectEffect = Fx.shellEjectSmall;
|
||||
bullet = Bullets.standardCopper;
|
||||
}};
|
||||
@@ -262,7 +262,7 @@ public class Mechs implements ContentList{
|
||||
reload = 70f;
|
||||
shots = 4;
|
||||
inaccuracy = 2f;
|
||||
roundrobin = true;
|
||||
alternate = true;
|
||||
ejectEffect = Fx.none;
|
||||
velocityRnd = 0.2f;
|
||||
spacing = 1f;
|
||||
@@ -327,7 +327,7 @@ public class Mechs implements ContentList{
|
||||
shots = 2;
|
||||
shotDelay = 1f;
|
||||
shots = 8;
|
||||
roundrobin = true;
|
||||
alternate = true;
|
||||
ejectEffect = Fx.none;
|
||||
velocityRnd = 1f;
|
||||
inaccuracy = 20f;
|
||||
@@ -365,7 +365,7 @@ public class Mechs implements ContentList{
|
||||
weapon = new Weapon("bomber"){{
|
||||
length = 1.5f;
|
||||
reload = 13f;
|
||||
roundrobin = true;
|
||||
alternate = true;
|
||||
ejectEffect = Fx.shellEjectSmall;
|
||||
bullet = Bullets.standardGlaive;
|
||||
shootSound = Sounds.shootSnap;
|
||||
|
||||
@@ -3,7 +3,7 @@ package io.anuke.mindustry.content;
|
||||
import io.anuke.arc.*;
|
||||
import io.anuke.arc.math.Mathf;
|
||||
import io.anuke.mindustry.entities.Effects;
|
||||
import io.anuke.mindustry.game.ContentList;
|
||||
import io.anuke.mindustry.ctype.ContentList;
|
||||
import io.anuke.mindustry.game.EventType.*;
|
||||
import io.anuke.mindustry.type.StatusEffect;
|
||||
|
||||
@@ -18,7 +18,7 @@ public class StatusEffects implements ContentList{
|
||||
none = new StatusEffect();
|
||||
|
||||
burning = new StatusEffect(){{
|
||||
damage = 0.04f;
|
||||
damage = 0.06f;
|
||||
effect = Fx.burning;
|
||||
|
||||
opposite(() -> wet, () -> freezing);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package io.anuke.mindustry.content;
|
||||
|
||||
import io.anuke.arc.collection.Array;
|
||||
import io.anuke.mindustry.game.ContentList;
|
||||
import io.anuke.mindustry.ctype.ContentList;
|
||||
import io.anuke.mindustry.type.ItemStack;
|
||||
import io.anuke.mindustry.world.Block;
|
||||
|
||||
@@ -31,6 +31,7 @@ public class TechTree implements ContentList{
|
||||
|
||||
node(distributor);
|
||||
node(sorter, () -> {
|
||||
node(invertedSorter);
|
||||
node(message);
|
||||
node(overflowGate);
|
||||
});
|
||||
@@ -103,6 +104,11 @@ public class TechTree implements ContentList{
|
||||
node(door, () -> {
|
||||
node(doorLarge);
|
||||
});
|
||||
node(plastaniumWall, () -> {
|
||||
node(plastaniumWallLarge, () -> {
|
||||
|
||||
});
|
||||
});
|
||||
node(titaniumWallLarge);
|
||||
node(thoriumWall, () -> {
|
||||
node(thoriumWallLarge);
|
||||
@@ -316,9 +322,9 @@ public class TechTree implements ContentList{
|
||||
return node(block, () -> {});
|
||||
}
|
||||
|
||||
public static void create(Block parent, Block block){
|
||||
public static TechNode create(Block parent, Block block){
|
||||
TechNode.context = all.find(t -> t.block == parent);
|
||||
node(block, () -> {});
|
||||
return node(block, () -> {});
|
||||
}
|
||||
|
||||
public static class TechNode{
|
||||
|
||||
@@ -3,8 +3,8 @@ package io.anuke.mindustry.content;
|
||||
import io.anuke.mindustry.entities.effect.Fire;
|
||||
import io.anuke.mindustry.entities.effect.Puddle;
|
||||
import io.anuke.mindustry.entities.type.Player;
|
||||
import io.anuke.mindustry.game.ContentList;
|
||||
import io.anuke.mindustry.game.TypeID;
|
||||
import io.anuke.mindustry.ctype.ContentList;
|
||||
import io.anuke.mindustry.type.TypeID;
|
||||
|
||||
public class TypeIDs implements ContentList{
|
||||
public static TypeID fire, puddle, player;
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
package io.anuke.mindustry.content;
|
||||
|
||||
import io.anuke.arc.collection.*;
|
||||
import io.anuke.mindustry.ctype.ContentList;
|
||||
import io.anuke.mindustry.entities.bullet.*;
|
||||
import io.anuke.mindustry.entities.type.*;
|
||||
import io.anuke.mindustry.entities.type.Bullet;
|
||||
import io.anuke.mindustry.entities.type.base.*;
|
||||
import io.anuke.mindustry.game.*;
|
||||
import io.anuke.mindustry.gen.*;
|
||||
import io.anuke.mindustry.type.*;
|
||||
|
||||
@@ -41,11 +41,11 @@ public class UnitTypes implements ContentList{
|
||||
health = 100;
|
||||
engineSize = 1.8f;
|
||||
engineOffset = 5.7f;
|
||||
weapon = new Weapon("heal-blaster"){{
|
||||
weapon = new Weapon(){{
|
||||
length = 1.5f;
|
||||
reload = 40f;
|
||||
width = 0.5f;
|
||||
roundrobin = true;
|
||||
alternate = true;
|
||||
ejectEffect = Fx.none;
|
||||
recoil = 2f;
|
||||
bullet = Bullets.healBulletBig;
|
||||
@@ -62,14 +62,14 @@ public class UnitTypes implements ContentList{
|
||||
range = 70f;
|
||||
itemCapacity = 70;
|
||||
health = 400;
|
||||
buildPower = 1f;
|
||||
buildPower = 0.4f;
|
||||
engineOffset = 6.5f;
|
||||
toMine = ObjectSet.with(Items.lead, Items.copper, Items.titanium);
|
||||
weapon = new Weapon("heal-blaster"){{
|
||||
weapon = new Weapon(){{
|
||||
length = 1.5f;
|
||||
reload = 20f;
|
||||
width = 0.5f;
|
||||
roundrobin = true;
|
||||
alternate = true;
|
||||
ejectEffect = Fx.none;
|
||||
recoil = 2f;
|
||||
bullet = Bullets.healBullet;
|
||||
@@ -86,7 +86,7 @@ public class UnitTypes implements ContentList{
|
||||
weapon = new Weapon("chain-blaster"){{
|
||||
length = 1.5f;
|
||||
reload = 28f;
|
||||
roundrobin = true;
|
||||
alternate = true;
|
||||
ejectEffect = Fx.shellEjectSmall;
|
||||
bullet = Bullets.standardCopper;
|
||||
}};
|
||||
@@ -99,7 +99,7 @@ public class UnitTypes implements ContentList{
|
||||
hitsize = 8f;
|
||||
mass = 1.75f;
|
||||
health = 120;
|
||||
weapon = new Weapon("bomber"){{
|
||||
weapon = new Weapon(){{
|
||||
reload = 12f;
|
||||
ejectEffect = Fx.none;
|
||||
shootSound = Sounds.explosion;
|
||||
@@ -138,7 +138,7 @@ public class UnitTypes implements ContentList{
|
||||
length = 1f;
|
||||
reload = 14f;
|
||||
range = 30f;
|
||||
roundrobin = true;
|
||||
alternate = true;
|
||||
recoil = 1f;
|
||||
ejectEffect = Fx.none;
|
||||
bullet = Bullets.basicFlame;
|
||||
@@ -158,7 +158,7 @@ public class UnitTypes implements ContentList{
|
||||
length = 1f;
|
||||
reload = 60f;
|
||||
width = 10f;
|
||||
roundrobin = true;
|
||||
alternate = true;
|
||||
recoil = 4f;
|
||||
shake = 2f;
|
||||
ejectEffect = Fx.shellEjectMedium;
|
||||
@@ -180,7 +180,7 @@ public class UnitTypes implements ContentList{
|
||||
weapon = new Weapon("eruption"){{
|
||||
length = 3f;
|
||||
reload = 10f;
|
||||
roundrobin = true;
|
||||
alternate = true;
|
||||
ejectEffect = Fx.none;
|
||||
bullet = Bullets.eruptorShot;
|
||||
recoil = 1f;
|
||||
@@ -201,7 +201,7 @@ public class UnitTypes implements ContentList{
|
||||
length = 8f;
|
||||
reload = 50f;
|
||||
width = 17f;
|
||||
roundrobin = true;
|
||||
alternate = true;
|
||||
recoil = 3f;
|
||||
shake = 2f;
|
||||
shots = 4;
|
||||
@@ -225,7 +225,7 @@ public class UnitTypes implements ContentList{
|
||||
length = 13f;
|
||||
reload = 30f;
|
||||
width = 22f;
|
||||
roundrobin = true;
|
||||
alternate = true;
|
||||
recoil = 3f;
|
||||
shake = 2f;
|
||||
inaccuracy = 3f;
|
||||
@@ -247,10 +247,10 @@ public class UnitTypes implements ContentList{
|
||||
health = 75;
|
||||
engineOffset = 5.5f;
|
||||
range = 140f;
|
||||
weapon = new Weapon("chain-blaster"){{
|
||||
weapon = new Weapon(){{
|
||||
length = 1.5f;
|
||||
reload = 28f;
|
||||
roundrobin = true;
|
||||
alternate = true;
|
||||
ejectEffect = Fx.shellEjectSmall;
|
||||
bullet = Bullets.standardCopper;
|
||||
shootSound = Sounds.shoot;
|
||||
@@ -267,11 +267,11 @@ public class UnitTypes implements ContentList{
|
||||
targetAir = false;
|
||||
engineOffset = 7.8f;
|
||||
range = 140f;
|
||||
weapon = new Weapon("bomber"){{
|
||||
weapon = new Weapon(){{
|
||||
length = 0f;
|
||||
width = 2f;
|
||||
reload = 12f;
|
||||
roundrobin = true;
|
||||
alternate = true;
|
||||
ejectEffect = Fx.none;
|
||||
velocityRnd = 1f;
|
||||
inaccuracy = 40f;
|
||||
@@ -303,7 +303,7 @@ public class UnitTypes implements ContentList{
|
||||
width = 10f;
|
||||
shots = 2;
|
||||
inaccuracy = 2f;
|
||||
roundrobin = true;
|
||||
alternate = true;
|
||||
ejectEffect = Fx.none;
|
||||
velocityRnd = 0.2f;
|
||||
spacing = 1f;
|
||||
@@ -336,7 +336,7 @@ public class UnitTypes implements ContentList{
|
||||
shootCone = 100f;
|
||||
shotDelay = 2;
|
||||
inaccuracy = 10f;
|
||||
roundrobin = true;
|
||||
alternate = true;
|
||||
ejectEffect = Fx.none;
|
||||
velocityRnd = 0.2f;
|
||||
spacing = 1f;
|
||||
@@ -369,7 +369,7 @@ public class UnitTypes implements ContentList{
|
||||
|
||||
shake = 1f;
|
||||
inaccuracy = 3f;
|
||||
roundrobin = true;
|
||||
alternate = true;
|
||||
ejectEffect = Fx.none;
|
||||
bullet = new BasicBulletType(7f, 42, "bullet"){
|
||||
{
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
package io.anuke.mindustry.content;
|
||||
|
||||
import io.anuke.arc.collection.Array;
|
||||
import io.anuke.mindustry.game.ContentList;
|
||||
import io.anuke.mindustry.game.SpawnGroup;
|
||||
import io.anuke.mindustry.maps.generators.MapGenerator;
|
||||
import io.anuke.mindustry.maps.generators.MapGenerator.Decoration;
|
||||
import io.anuke.mindustry.maps.zonegen.DesertWastesGenerator;
|
||||
import io.anuke.mindustry.ctype.ContentList;
|
||||
import io.anuke.mindustry.game.*;
|
||||
import io.anuke.mindustry.game.Objectives.*;
|
||||
import io.anuke.mindustry.maps.generators.*;
|
||||
import io.anuke.mindustry.maps.generators.MapGenerator.*;
|
||||
import io.anuke.mindustry.maps.zonegen.*;
|
||||
import io.anuke.mindustry.type.*;
|
||||
import io.anuke.mindustry.world.Block;
|
||||
|
||||
import static io.anuke.arc.collection.Array.with;
|
||||
import static io.anuke.mindustry.content.Items.*;
|
||||
import static io.anuke.mindustry.type.ItemStack.list;
|
||||
|
||||
public class Zones implements ContentList{
|
||||
public static Zone
|
||||
@@ -20,28 +23,26 @@ public class Zones implements ContentList{
|
||||
public void load(){
|
||||
|
||||
groundZero = new Zone("groundZero", new MapGenerator("groundZero", 1)){{
|
||||
baseLaunchCost = ItemStack.with(Items.copper, -60);
|
||||
startingItems = ItemStack.list(Items.copper, 60);
|
||||
baseLaunchCost = list(copper, -60);
|
||||
startingItems = list(copper, 60);
|
||||
alwaysUnlocked = true;
|
||||
conditionWave = 5;
|
||||
launchPeriod = 5;
|
||||
resources = new Item[]{Items.copper, Items.scrap, Items.lead};
|
||||
resources = with(copper, scrap, lead);
|
||||
}};
|
||||
|
||||
desertWastes = new Zone("desertWastes", new DesertWastesGenerator(260, 260)){{
|
||||
startingItems = ItemStack.list(Items.copper, 120);
|
||||
startingItems = list(copper, 120);
|
||||
conditionWave = 20;
|
||||
launchPeriod = 10;
|
||||
loadout = Loadouts.advancedShard;
|
||||
zoneRequirements = ZoneRequirement.with(groundZero, 20);
|
||||
blockRequirements = new Block[]{Blocks.combustionGenerator};
|
||||
resources = new Item[]{Items.copper, Items.lead, Items.coal, Items.sand};
|
||||
resources = with(copper, lead, coal, sand);
|
||||
rules = r -> {
|
||||
r.waves = true;
|
||||
r.waveTimer = true;
|
||||
r.launchWaveMultiplier = 3f;
|
||||
r.waveSpacing = 60 * 50f;
|
||||
r.spawns = Array.with(
|
||||
r.spawns = with(
|
||||
new SpawnGroup(UnitTypes.crawler){{
|
||||
unitScaling = 3f;
|
||||
}},
|
||||
@@ -75,96 +76,140 @@ public class Zones implements ContentList{
|
||||
}}
|
||||
);
|
||||
};
|
||||
requirements = with(
|
||||
new ZoneWave(groundZero, 20),
|
||||
new Unlock(Blocks.combustionGenerator)
|
||||
);
|
||||
}};
|
||||
|
||||
saltFlats = new Zone("saltFlats", new MapGenerator("saltFlats")){{
|
||||
startingItems = ItemStack.list(Items.copper, 200, Items.silicon, 200, Items.lead, 200);
|
||||
startingItems = list(copper, 200, Items.silicon, 200, lead, 200);
|
||||
loadout = Loadouts.basicFoundation;
|
||||
conditionWave = 10;
|
||||
launchPeriod = 5;
|
||||
zoneRequirements = ZoneRequirement.with(desertWastes, 60);
|
||||
blockRequirements = new Block[]{Blocks.daggerFactory, Blocks.draugFactory, Blocks.door, Blocks.waterExtractor};
|
||||
resources = new Item[]{Items.copper, Items.scrap, Items.lead, Items.coal, Items.sand, Items.titanium};
|
||||
configureObjective = new Launched(this);
|
||||
resources = with(copper, scrap, lead, coal, sand, titanium);
|
||||
requirements = with(
|
||||
new ZoneWave(desertWastes, 60),
|
||||
new Unlock(Blocks.daggerFactory),
|
||||
new Unlock(Blocks.draugFactory),
|
||||
new Unlock(Blocks.door),
|
||||
new Unlock(Blocks.waterExtractor)
|
||||
);
|
||||
}};
|
||||
|
||||
frozenForest = new Zone("frozenForest", new MapGenerator("frozenForest", 1)
|
||||
.decor(new Decoration(Blocks.snow, Blocks.sporeCluster, 0.02))){{
|
||||
loadout = Loadouts.basicFoundation;
|
||||
baseLaunchCost = ItemStack.with();
|
||||
startingItems = ItemStack.list(Items.copper, 250);
|
||||
startingItems = list(copper, 250);
|
||||
conditionWave = 10;
|
||||
blockRequirements = new Block[]{Blocks.junction, Blocks.router};
|
||||
zoneRequirements = ZoneRequirement.with(groundZero, 10);
|
||||
resources = new Item[]{Items.copper, Items.lead, Items.coal};
|
||||
resources = with(copper, lead, coal);
|
||||
requirements = with(
|
||||
new ZoneWave(groundZero, 10),
|
||||
new Unlock(Blocks.junction),
|
||||
new Unlock(Blocks.router)
|
||||
);
|
||||
}};
|
||||
|
||||
craters = new Zone("craters", new MapGenerator("craters", 1).decor(new Decoration(Blocks.snow, Blocks.sporeCluster, 0.004))){{
|
||||
startingItems = ItemStack.list(Items.copper, 100);
|
||||
startingItems = list(copper, 100);
|
||||
conditionWave = 10;
|
||||
zoneRequirements = ZoneRequirement.with(frozenForest, 10);
|
||||
blockRequirements = new Block[]{Blocks.mender, Blocks.combustionGenerator};
|
||||
resources = new Item[]{Items.copper, Items.lead, Items.coal, Items.sand, Items.scrap};
|
||||
resources = with(copper, lead, coal, sand, scrap);
|
||||
requirements = with(
|
||||
new ZoneWave(frozenForest, 10),
|
||||
new Unlock(Blocks.mender),
|
||||
new Unlock(Blocks.combustionGenerator)
|
||||
);
|
||||
}};
|
||||
|
||||
ruinousShores = new Zone("ruinousShores", new MapGenerator("ruinousShores", 1)){{
|
||||
loadout = Loadouts.basicFoundation;
|
||||
baseLaunchCost = ItemStack.with();
|
||||
startingItems = ItemStack.list(Items.copper, 140, Items.lead, 50);
|
||||
startingItems = list(copper, 140, lead, 50);
|
||||
conditionWave = 20;
|
||||
launchPeriod = 20;
|
||||
zoneRequirements = ZoneRequirement.with(desertWastes, 20, craters, 15);
|
||||
blockRequirements = new Block[]{Blocks.graphitePress, Blocks.combustionGenerator, Blocks.kiln, Blocks.mechanicalPump};
|
||||
resources = new Item[]{Items.copper, Items.scrap, Items.lead, Items.coal, Items.sand};
|
||||
resources = with(copper, scrap, lead, coal, sand);
|
||||
requirements = with(
|
||||
new ZoneWave(desertWastes, 20),
|
||||
new ZoneWave(craters, 15),
|
||||
new Unlock(Blocks.graphitePress),
|
||||
new Unlock(Blocks.combustionGenerator),
|
||||
new Unlock(Blocks.kiln),
|
||||
new Unlock(Blocks.mechanicalPump)
|
||||
);
|
||||
}};
|
||||
|
||||
stainedMountains = new Zone("stainedMountains", new MapGenerator("stainedMountains", 2)
|
||||
.decor(new Decoration(Blocks.shale, Blocks.shaleBoulder, 0.02))){{
|
||||
loadout = Loadouts.basicFoundation;
|
||||
startingItems = ItemStack.list(Items.copper, 200, Items.lead, 50);
|
||||
startingItems = list(copper, 200, lead, 50);
|
||||
conditionWave = 10;
|
||||
launchPeriod = 10;
|
||||
zoneRequirements = ZoneRequirement.with(frozenForest, 15);
|
||||
blockRequirements = new Block[]{Blocks.pneumaticDrill, Blocks.powerNode, Blocks.turbineGenerator};
|
||||
resources = new Item[]{Items.copper, Items.scrap, Items.lead, Items.coal, Items.titanium, Items.sand};
|
||||
resources = with(copper, scrap, lead, coal, titanium, sand);
|
||||
requirements = with(
|
||||
new ZoneWave(frozenForest, 15),
|
||||
new Unlock(Blocks.pneumaticDrill),
|
||||
new Unlock(Blocks.powerNode),
|
||||
new Unlock(Blocks.turbineGenerator)
|
||||
);
|
||||
}};
|
||||
|
||||
fungalPass = new Zone("fungalPass", new MapGenerator("fungalPass")){{
|
||||
startingItems = ItemStack.list(Items.copper, 250, Items.lead, 250, Items.metaglass, 100, Items.graphite, 100);
|
||||
zoneRequirements = ZoneRequirement.with(stainedMountains, 15);
|
||||
blockRequirements = new Block[]{Blocks.daggerFactory, Blocks.crawlerFactory, Blocks.door, Blocks.siliconSmelter};
|
||||
resources = new Item[]{Items.copper, Items.lead, Items.coal, Items.titanium, Items.sand};
|
||||
startingItems = list(copper, 250, lead, 250, Items.metaglass, 100, Items.graphite, 100);
|
||||
resources = with(copper, lead, coal, titanium, sand);
|
||||
configureObjective = new Launched(this);
|
||||
requirements = with(
|
||||
new ZoneWave(stainedMountains, 15),
|
||||
new Unlock(Blocks.daggerFactory),
|
||||
new Unlock(Blocks.crawlerFactory),
|
||||
new Unlock(Blocks.door),
|
||||
new Unlock(Blocks.siliconSmelter)
|
||||
);
|
||||
}};
|
||||
|
||||
overgrowth = new Zone("overgrowth", new MapGenerator("overgrowth")){{
|
||||
startingItems = ItemStack.list(Items.copper, 1500, Items.lead, 1000, Items.silicon, 500, Items.metaglass, 250);
|
||||
startingItems = list(copper, 1500, lead, 1000, Items.silicon, 500, Items.metaglass, 250);
|
||||
conditionWave = 12;
|
||||
launchPeriod = 4;
|
||||
loadout = Loadouts.basicNucleus;
|
||||
zoneRequirements = ZoneRequirement.with(craters, 40, fungalPass, 10);
|
||||
blockRequirements = new Block[]{Blocks.cultivator, Blocks.sporePress, Blocks.titanFactory, Blocks.wraithFactory};
|
||||
resources = new Item[]{Items.copper, Items.lead, Items.coal, Items.titanium, Items.sand, Items.thorium, Items.scrap};
|
||||
configureObjective = new Launched(this);
|
||||
resources = with(copper, lead, coal, titanium, sand, thorium, scrap);
|
||||
requirements = with(
|
||||
new ZoneWave(craters, 40),
|
||||
new Launched(fungalPass),
|
||||
new Unlock(Blocks.cultivator),
|
||||
new Unlock(Blocks.sporePress),
|
||||
new Unlock(Blocks.titanFactory),
|
||||
new Unlock(Blocks.wraithFactory)
|
||||
);
|
||||
}};
|
||||
|
||||
tarFields = new Zone("tarFields", new MapGenerator("tarFields")
|
||||
.decor(new Decoration(Blocks.shale, Blocks.shaleBoulder, 0.02))){{
|
||||
loadout = Loadouts.basicFoundation;
|
||||
startingItems = ItemStack.list(Items.copper, 250, Items.lead, 100);
|
||||
startingItems = list(copper, 250, lead, 100);
|
||||
conditionWave = 15;
|
||||
launchPeriod = 10;
|
||||
zoneRequirements = ZoneRequirement.with(ruinousShores, 20);
|
||||
blockRequirements = new Block[]{Blocks.coalCentrifuge, Blocks.conduit, Blocks.wave};
|
||||
resources = new Item[]{Items.copper, Items.scrap, Items.lead, Items.coal, Items.titanium, Items.thorium, Items.sand};
|
||||
requirements = with(new ZoneWave(ruinousShores, 20));
|
||||
resources = with(copper, scrap, lead, coal, titanium, thorium, sand);
|
||||
requirements = with(
|
||||
new ZoneWave(ruinousShores, 20),
|
||||
new Unlock(Blocks.coalCentrifuge),
|
||||
new Unlock(Blocks.conduit),
|
||||
new Unlock(Blocks.wave)
|
||||
);
|
||||
}};
|
||||
|
||||
desolateRift = new Zone("desolateRift", new MapGenerator("desolateRift")){{
|
||||
loadout = Loadouts.basicNucleus;
|
||||
baseLaunchCost = ItemStack.with();
|
||||
startingItems = ItemStack.list(Items.copper, 1000, Items.lead, 1000, Items.graphite, 250, Items.titanium, 250, Items.silicon, 250);
|
||||
startingItems = list(copper, 1000, lead, 1000, Items.graphite, 250, titanium, 250, Items.silicon, 250);
|
||||
conditionWave = 3;
|
||||
launchPeriod = 2;
|
||||
zoneRequirements = ZoneRequirement.with(tarFields, 20);
|
||||
blockRequirements = new Block[]{Blocks.thermalGenerator, Blocks.thoriumReactor};
|
||||
resources = new Item[]{Items.copper, Items.scrap, Items.lead, Items.coal, Items.titanium, Items.sand, Items.thorium};
|
||||
resources = with(copper, scrap, lead, coal, titanium, sand, thorium);
|
||||
requirements = with(
|
||||
new ZoneWave(tarFields, 20),
|
||||
new Unlock(Blocks.thermalGenerator),
|
||||
new Unlock(Blocks.thoriumReactor)
|
||||
);
|
||||
}};
|
||||
|
||||
/*
|
||||
@@ -174,21 +219,23 @@ public class Zones implements ContentList{
|
||||
startingItems = ItemStack.list(Items.copper, 2000, Items.lead, 2000, Items.graphite, 500, Items.titanium, 500, Items.silicon, 500);
|
||||
conditionWave = 3;
|
||||
launchPeriod = 2;
|
||||
zoneRequirements = ZoneRequirement.with(stainedMountains, 40);
|
||||
requirements = with(stainedMountains, 40);
|
||||
blockRequirements = new Block[]{Blocks.thermalGenerator};
|
||||
resources = new Item[]{Items.copper, Items.scrap, Items.lead, Items.coal, Items.sand};
|
||||
resources = Array.with(Items.copper, Items.scrap, Items.lead, Items.coal, Items.sand};
|
||||
}};*/
|
||||
|
||||
nuclearComplex = new Zone("nuclearComplex", new MapGenerator("nuclearProductionComplex", 1)
|
||||
.decor(new Decoration(Blocks.snow, Blocks.sporeCluster, 0.01))){{
|
||||
loadout = Loadouts.basicNucleus;
|
||||
baseLaunchCost = ItemStack.with();
|
||||
startingItems = ItemStack.list(Items.copper, 1250, Items.lead, 1500, Items.silicon, 400, Items.metaglass, 250);
|
||||
startingItems = list(copper, 1250, lead, 1500, Items.silicon, 400, Items.metaglass, 250);
|
||||
conditionWave = 30;
|
||||
launchPeriod = 15;
|
||||
zoneRequirements = ZoneRequirement.with(fungalPass, 8);
|
||||
blockRequirements = new Block[]{Blocks.thermalGenerator, Blocks.laserDrill};
|
||||
resources = new Item[]{Items.copper, Items.scrap, Items.lead, Items.coal, Items.titanium, Items.thorium, Items.sand};
|
||||
resources = with(copper, scrap, lead, coal, titanium, thorium, sand);
|
||||
requirements = with(
|
||||
new Launched(fungalPass),
|
||||
new Unlock(Blocks.thermalGenerator),
|
||||
new Unlock(Blocks.laserDrill)
|
||||
);
|
||||
}};
|
||||
|
||||
/*
|
||||
@@ -198,9 +245,9 @@ public class Zones implements ContentList{
|
||||
startingItems = ItemStack.list(Items.copper, 2000, Items.lead, 2000, Items.graphite, 500, Items.titanium, 500, Items.silicon, 500);
|
||||
conditionWave = 3;
|
||||
launchPeriod = 2;
|
||||
zoneRequirements = ZoneRequirement.with(nuclearComplex, 40);
|
||||
requirements = with(nuclearComplex, 40);
|
||||
blockRequirements = new Block[]{Blocks.thermalGenerator};
|
||||
resources = new Item[]{Items.copper, Items.scrap, Items.lead, Items.coal, Items.titanium, Items.thorium};
|
||||
resources = Array.with(Items.copper, Items.scrap, Items.lead, Items.coal, Items.titanium, Items.thorium};
|
||||
}};*/
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
package io.anuke.mindustry.core;
|
||||
|
||||
import io.anuke.arc.collection.*;
|
||||
import io.anuke.arc.function.*;
|
||||
import io.anuke.arc.func.*;
|
||||
import io.anuke.arc.graphics.*;
|
||||
import io.anuke.arc.util.*;
|
||||
import io.anuke.mindustry.content.*;
|
||||
import io.anuke.mindustry.ctype.*;
|
||||
import io.anuke.mindustry.entities.bullet.*;
|
||||
import io.anuke.mindustry.game.*;
|
||||
import io.anuke.mindustry.mod.Mods.*;
|
||||
import io.anuke.mindustry.type.*;
|
||||
import io.anuke.mindustry.world.*;
|
||||
|
||||
@@ -23,7 +24,7 @@ public class ContentLoader{
|
||||
private ObjectMap<String, MappableContent>[] contentNameMap = new ObjectMap[ContentType.values().length];
|
||||
private Array<Content>[] contentMap = new Array[ContentType.values().length];
|
||||
private MappableContent[][] temporaryMapper;
|
||||
private ObjectSet<Consumer<Content>> initialization = new ObjectSet<>();
|
||||
private ObjectSet<Cons<Content>> initialization = new ObjectSet<>();
|
||||
private ContentList[] content = {
|
||||
new Fx(),
|
||||
new Items(),
|
||||
@@ -104,13 +105,20 @@ public class ContentLoader{
|
||||
}
|
||||
|
||||
/** Initializes all content with the specified function. */
|
||||
private void initialize(Consumer<Content> callable){
|
||||
private void initialize(Cons<Content> callable){
|
||||
if(initialization.contains(callable)) return;
|
||||
|
||||
for(ContentType type : ContentType.values()){
|
||||
for(Content content : contentMap[type.ordinal()]){
|
||||
//TODO catch error and display it per mod
|
||||
callable.accept(content);
|
||||
try{
|
||||
callable.get(content);
|
||||
}catch(Throwable e){
|
||||
if(content.mod != null){
|
||||
mods.handleError(new ModLoadException(content, e), content.mod);
|
||||
}else{
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,8 @@ package io.anuke.mindustry.core;
|
||||
|
||||
import io.anuke.arc.*;
|
||||
import io.anuke.arc.assets.*;
|
||||
import io.anuke.arc.audio.*;
|
||||
import io.anuke.arc.collection.*;
|
||||
import io.anuke.arc.graphics.*;
|
||||
import io.anuke.arc.graphics.g2d.*;
|
||||
import io.anuke.arc.input.*;
|
||||
@@ -27,8 +29,8 @@ import java.text.*;
|
||||
import java.util.*;
|
||||
|
||||
import static io.anuke.arc.Core.*;
|
||||
import static io.anuke.mindustry.Vars.*;
|
||||
import static io.anuke.mindustry.Vars.net;
|
||||
import static io.anuke.mindustry.Vars.*;
|
||||
|
||||
/**
|
||||
* Control module.
|
||||
@@ -54,6 +56,9 @@ public class Control implements ApplicationListener, Loadable{
|
||||
Events.on(StateChangeEvent.class, event -> {
|
||||
if((event.from == State.playing && event.to == State.menu) || (event.from == State.menu && event.to != State.menu)){
|
||||
Time.runTask(5f, platform::updateRPC);
|
||||
for(Sound sound : assets.getAll(Sound.class, new Array<>())){
|
||||
sound.stop();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -146,11 +151,15 @@ public class Control implements ApplicationListener, Loadable{
|
||||
});
|
||||
|
||||
Events.on(ZoneRequireCompleteEvent.class, e -> {
|
||||
ui.hudfrag.showToast(Core.bundle.format("zone.requirement.complete", state.wave, e.zone.localizedName));
|
||||
if(e.objective.display() != null){
|
||||
ui.hudfrag.showToast(Core.bundle.format("zone.requirement.complete", e.zoneForMet.localizedName, e.objective.display()));
|
||||
}
|
||||
});
|
||||
|
||||
Events.on(ZoneConfigureCompleteEvent.class, e -> {
|
||||
ui.hudfrag.showToast(Core.bundle.format("zone.config.complete", e.zone.configureWave));
|
||||
if(e.zone.configureObjective.display() != null){
|
||||
ui.hudfrag.showToast(Core.bundle.format("zone.config.unlocked", e.zone.configureObjective.display()));
|
||||
}
|
||||
});
|
||||
|
||||
Events.on(Trigger.newGame, () -> {
|
||||
@@ -166,6 +175,12 @@ public class Control implements ApplicationListener, Loadable{
|
||||
Effects.shake(5f, 5f, core);
|
||||
});
|
||||
});
|
||||
|
||||
Events.on(UnitDestroyEvent.class, e -> {
|
||||
if(e.unit instanceof BaseUnit && world.isZone()){
|
||||
data.unlockContent(((BaseUnit)e.unit).getType());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -239,7 +254,7 @@ public class Control implements ApplicationListener, Loadable{
|
||||
logic.reset();
|
||||
net.reset();
|
||||
world.loadGenerator(zone.generator);
|
||||
zone.rules.accept(state.rules);
|
||||
zone.rules.get(state.rules);
|
||||
state.rules.zone = zone;
|
||||
for(Tile core : state.teams.get(defaultTeam).cores){
|
||||
for(ItemStack stack : zone.getStartingItems()){
|
||||
@@ -287,7 +302,7 @@ public class Control implements ApplicationListener, Loadable{
|
||||
|
||||
world.endMapLoad();
|
||||
|
||||
zone.rules.accept(state.rules);
|
||||
zone.rules.get(state.rules);
|
||||
state.rules.zone = zone;
|
||||
for(Tile core : state.teams.get(defaultTeam).cores){
|
||||
for(ItemStack stack : zone.getStartingItems()){
|
||||
@@ -401,6 +416,7 @@ public class Control implements ApplicationListener, Loadable{
|
||||
|
||||
music.update();
|
||||
loops.update();
|
||||
Time.updateGlobal();
|
||||
|
||||
if(Core.input.keyTap(Binding.fullscreen)){
|
||||
boolean full = settings.getBool("fullscreen");
|
||||
|
||||
@@ -2,10 +2,10 @@ package io.anuke.mindustry.core;
|
||||
|
||||
import io.anuke.annotations.Annotations.*;
|
||||
import io.anuke.arc.*;
|
||||
import io.anuke.arc.collection.ObjectSet.*;
|
||||
import io.anuke.arc.util.*;
|
||||
import io.anuke.mindustry.content.*;
|
||||
import io.anuke.mindustry.core.GameState.*;
|
||||
import io.anuke.mindustry.ctype.UnlockableContent;
|
||||
import io.anuke.mindustry.entities.*;
|
||||
import io.anuke.mindustry.entities.type.*;
|
||||
import io.anuke.mindustry.game.EventType.*;
|
||||
@@ -18,6 +18,8 @@ import io.anuke.mindustry.world.blocks.*;
|
||||
import io.anuke.mindustry.world.blocks.BuildBlock.*;
|
||||
import io.anuke.mindustry.world.blocks.power.*;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import static io.anuke.mindustry.Vars.*;
|
||||
|
||||
/**
|
||||
@@ -79,13 +81,12 @@ public class Logic implements ApplicationListener{
|
||||
Events.on(BlockBuildEndEvent.class, event -> {
|
||||
if(!event.breaking){
|
||||
TeamData data = state.teams.get(event.team);
|
||||
|
||||
//painful O(n) iteration + copy
|
||||
for(int i = 0; i < data.brokenBlocks.size; i++){
|
||||
BrokenBlock b = data.brokenBlocks.get(i);
|
||||
if(b.x == event.tile.x && b.y == event.tile.y){
|
||||
data.brokenBlocks.removeIndex(i);
|
||||
break;
|
||||
Iterator<BrokenBlock> it = data.brokenBlocks.iterator();
|
||||
while(it.hasNext()){
|
||||
BrokenBlock b = it.next();
|
||||
Block block = content.block(b.block);
|
||||
if(event.tile.block().bounds(event.tile.x, event.tile.y, Tmp.r1).overlaps(block.bounds(b.x, b.y, Tmp.r2))){
|
||||
it.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -136,8 +137,7 @@ public class Logic implements ApplicationListener{
|
||||
public void runWave(){
|
||||
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;
|
||||
state.wavetime = world.isZone() && world.getZone().isLaunchWave(state.wave) ? state.rules.waveSpacing * state.rules.launchWaveMultiplier : state.rules.waveSpacing;
|
||||
|
||||
Events.fire(new WaveEvent());
|
||||
}
|
||||
@@ -176,12 +176,16 @@ public class Logic implements ApplicationListener{
|
||||
ui.hudfrag.showLaunch();
|
||||
}
|
||||
|
||||
for(Tile tile : new ObjectSetIterator<>(state.teams.get(defaultTeam).cores)){
|
||||
for(Tile tile : state.teams.get(defaultTeam).cores){
|
||||
Effects.effect(Fx.launch, tile);
|
||||
}
|
||||
|
||||
if(world.getZone() != null){
|
||||
world.getZone().setLaunched();
|
||||
}
|
||||
|
||||
Time.runTask(30f, () -> {
|
||||
for(Tile tile : new ObjectSetIterator<>(state.teams.get(defaultTeam).cores)){
|
||||
for(Tile tile : state.teams.get(defaultTeam).cores){
|
||||
for(Item item : content.items()){
|
||||
if(tile == null || tile.entity == null || tile.entity.items == null) continue;
|
||||
data.addItem(item, tile.entity.items.get(item));
|
||||
|
||||
@@ -15,14 +15,15 @@ import io.anuke.mindustry.entities.*;
|
||||
import io.anuke.mindustry.entities.traits.BuilderTrait.*;
|
||||
import io.anuke.mindustry.entities.traits.*;
|
||||
import io.anuke.mindustry.entities.type.*;
|
||||
import io.anuke.mindustry.game.EventType.*;
|
||||
import io.anuke.mindustry.game.*;
|
||||
import io.anuke.mindustry.game.EventType.*;
|
||||
import io.anuke.mindustry.gen.*;
|
||||
import io.anuke.mindustry.net.Administration.*;
|
||||
import io.anuke.mindustry.net.Net.*;
|
||||
import io.anuke.mindustry.net.*;
|
||||
import io.anuke.mindustry.net.Packets.*;
|
||||
import io.anuke.mindustry.type.*;
|
||||
import io.anuke.mindustry.type.TypeID;
|
||||
import io.anuke.mindustry.world.*;
|
||||
import io.anuke.mindustry.world.modules.*;
|
||||
|
||||
@@ -198,6 +199,15 @@ public class NetClient implements ApplicationListener{
|
||||
return "[#" + player.color.toString().toUpperCase() + "]" + name;
|
||||
}
|
||||
|
||||
@Remote(called = Loc.client, variants = Variant.one)
|
||||
public static void onConnect(String ip, int port){
|
||||
netClient.disconnectQuietly();
|
||||
state.set(State.menu);
|
||||
logic.reset();
|
||||
|
||||
ui.join.connect(ip, port);
|
||||
}
|
||||
|
||||
@Remote(targets = Loc.client)
|
||||
public static void onPing(Player player, long time){
|
||||
Call.onPingResponse(player.con, time);
|
||||
@@ -245,6 +255,11 @@ public class NetClient implements ApplicationListener{
|
||||
ui.showText("", message);
|
||||
}
|
||||
|
||||
@Remote(variants = Variant.both)
|
||||
public static void onSetRules(Rules rules){
|
||||
state.rules = rules;
|
||||
}
|
||||
|
||||
@Remote(variants = Variant.both)
|
||||
public static void onWorldDataBegin(){
|
||||
entities.clear();
|
||||
@@ -456,7 +471,7 @@ public class NetClient implements ApplicationListener{
|
||||
player.pointerX, player.pointerY, player.rotation, player.baseRotation,
|
||||
player.velocity().x, player.velocity().y,
|
||||
player.getMineTile(),
|
||||
player.isBoosting, player.isShooting, ui.chatfrag.chatOpen(),
|
||||
player.isBoosting, player.isShooting, ui.chatfrag.chatOpen(), player.isBuilding,
|
||||
requests,
|
||||
Core.camera.position.x, Core.camera.position.y,
|
||||
Core.camera.width * viewScale, Core.camera.height * viewScale);
|
||||
@@ -479,4 +494,4 @@ public class NetClient implements ApplicationListener{
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,11 +94,6 @@ public class NetServer implements ApplicationListener{
|
||||
return;
|
||||
}
|
||||
|
||||
if(admins.isIDBanned(uuid)){
|
||||
con.kick(KickReason.banned);
|
||||
return;
|
||||
}
|
||||
|
||||
if(admins.getPlayerLimit() > 0 && playerGroup.size() >= admins.getPlayerLimit()){
|
||||
con.kick(KickReason.playerLimit);
|
||||
return;
|
||||
@@ -336,6 +331,8 @@ public class NetServer implements ApplicationListener{
|
||||
player.sendMessage("[scarlet]Did you really expect to be able to kick an admin?");
|
||||
}else if(found.isLocal){
|
||||
player.sendMessage("[scarlet]Local players cannot be kicked.");
|
||||
}else if(found.getTeam() != player.getTeam()){
|
||||
player.sendMessage("[scarlet]Only players on your team can be kicked.");
|
||||
}else{
|
||||
if(!vtime.get()){
|
||||
player.sendMessage("[scarlet]You must wait " + voteTime/60 + " minutes between votekicks.");
|
||||
@@ -357,6 +354,11 @@ public class NetServer implements ApplicationListener{
|
||||
if(currentlyKicking[0] == null){
|
||||
player.sendMessage("[scarlet]Nobody is being voted on.");
|
||||
}else{
|
||||
if(player.isLocal){
|
||||
player.sendMessage("Local players can't vote. Kick the player yourself instead.");
|
||||
return;
|
||||
}
|
||||
|
||||
//hosts can vote all they want
|
||||
if(player.uuid != null && (currentlyKicking[0].voted.contains(player.uuid) || currentlyKicking[0].voted.contains(admins.getInfo(player.uuid).lastIP))){
|
||||
player.sendMessage("[scarlet]You've already voted. Sit down.");
|
||||
@@ -450,7 +452,7 @@ public class NetServer implements ApplicationListener{
|
||||
float rotation, float baseRotation,
|
||||
float xVelocity, float yVelocity,
|
||||
Tile mining,
|
||||
boolean boosting, boolean shooting, boolean chatting,
|
||||
boolean boosting, boolean shooting, boolean chatting, boolean building,
|
||||
BuildRequest[] requests,
|
||||
float viewX, float viewY, float viewWidth, float viewHeight
|
||||
){
|
||||
@@ -477,6 +479,7 @@ public class NetServer implements ApplicationListener{
|
||||
player.isTyping = chatting;
|
||||
player.isBoosting = boosting;
|
||||
player.isShooting = shooting;
|
||||
player.isBuilding = building;
|
||||
player.buildQueue().clear();
|
||||
for(BuildRequest req : requests){
|
||||
if(req == null) continue;
|
||||
|
||||
@@ -4,13 +4,13 @@ import io.anuke.arc.*;
|
||||
import io.anuke.arc.Input.*;
|
||||
import io.anuke.arc.collection.*;
|
||||
import io.anuke.arc.files.*;
|
||||
import io.anuke.arc.function.*;
|
||||
import io.anuke.arc.func.*;
|
||||
import io.anuke.arc.math.*;
|
||||
import io.anuke.arc.scene.ui.*;
|
||||
import io.anuke.arc.util.serialization.*;
|
||||
import io.anuke.mindustry.maps.*;
|
||||
import io.anuke.mindustry.net.*;
|
||||
import io.anuke.mindustry.net.Net.*;
|
||||
import io.anuke.mindustry.type.*;
|
||||
import io.anuke.mindustry.ui.dialogs.*;
|
||||
|
||||
import static io.anuke.mindustry.Vars.mobile;
|
||||
@@ -24,23 +24,19 @@ public interface Platform{
|
||||
default void inviteFriends(){}
|
||||
|
||||
/** Steam: Share a map on the workshop.*/
|
||||
default void publishMap(Map map){}
|
||||
default void publish(Publishable pub){}
|
||||
|
||||
/** Steam: View a listing on the workshop.*/
|
||||
default void viewListing(Publishable pub){}
|
||||
|
||||
/** Steam: View a listing on the workshop by an ID.*/
|
||||
default void viewListingID(String mapid){}
|
||||
|
||||
/** Steam: Return external workshop maps to be loaded.*/
|
||||
default Array<FileHandle> getExternalMaps(){
|
||||
return Array.with();
|
||||
default Array<FileHandle> getWorkshopContent(Class<? extends Publishable> type){
|
||||
return new Array<>(0);
|
||||
}
|
||||
|
||||
/** Steam: View a map listing on the workshop.*/
|
||||
default void viewMapListing(Map map){}
|
||||
|
||||
/** Steam: View a map listing on the workshop.*/
|
||||
default void viewMapListing(String mapid){}
|
||||
|
||||
/** Steam: View map workshop info, removing the map ID tag if its listing is deleted.
|
||||
* Also presents the option to update the map. */
|
||||
default void viewMapListingInfo(Map map){}
|
||||
|
||||
/** Steam: Open workshop for maps.*/
|
||||
default void openWorkshop(){}
|
||||
|
||||
@@ -76,11 +72,6 @@ public interface Platform{
|
||||
default void updateRPC(){
|
||||
}
|
||||
|
||||
/** Whether donating is supported. */
|
||||
default boolean canDonate(){
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Must be a base64 string 8 bytes in length. */
|
||||
default String getUUID(){
|
||||
String uuid = Core.settings.getString("uuid", "");
|
||||
@@ -105,12 +96,12 @@ public interface Platform{
|
||||
* @param open Whether to open or save files
|
||||
* @param extension File extension to filter
|
||||
*/
|
||||
default void showFileChooser(boolean open, String extension, Consumer<FileHandle> cons){
|
||||
default void showFileChooser(boolean open, String extension, Cons<FileHandle> cons){
|
||||
new FileChooser(open ? "$open" : "$save", file -> file.extension().toLowerCase().equals(extension), open, file -> {
|
||||
if(!open){
|
||||
cons.accept(file.parent().child(file.nameWithoutExtension() + "." + extension));
|
||||
cons.get(file.parent().child(file.nameWithoutExtension() + "." + extension));
|
||||
}else{
|
||||
cons.accept(file);
|
||||
cons.get(file);
|
||||
}
|
||||
}).show();
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ package io.anuke.mindustry.core;
|
||||
|
||||
import io.anuke.arc.*;
|
||||
import io.anuke.arc.files.*;
|
||||
import io.anuke.arc.function.*;
|
||||
import io.anuke.arc.func.*;
|
||||
import io.anuke.arc.graphics.*;
|
||||
import io.anuke.arc.graphics.g2d.*;
|
||||
import io.anuke.arc.graphics.glutils.*;
|
||||
@@ -22,6 +22,7 @@ import io.anuke.mindustry.game.*;
|
||||
import io.anuke.mindustry.game.EventType.*;
|
||||
import io.anuke.mindustry.graphics.*;
|
||||
import io.anuke.mindustry.input.*;
|
||||
import io.anuke.mindustry.ui.Cicon;
|
||||
import io.anuke.mindustry.world.blocks.defense.ForceProjector.*;
|
||||
|
||||
import static io.anuke.arc.Core.*;
|
||||
@@ -239,7 +240,7 @@ public class Renderer implements ApplicationListener{
|
||||
blocks.drawBlocks(Layer.block);
|
||||
blocks.drawFog();
|
||||
|
||||
blocks.drawBroken();
|
||||
blocks.drawDestroyed();
|
||||
|
||||
Draw.shader(Shaders.blockbuild, true);
|
||||
blocks.drawBlocks(Layer.placement);
|
||||
@@ -335,19 +336,19 @@ public class Renderer implements ApplicationListener{
|
||||
Draw.color(0, 0, 0, 0.4f);
|
||||
float rad = 1.6f;
|
||||
|
||||
Consumer<Unit> draw = u -> {
|
||||
Cons<Unit> draw = u -> {
|
||||
float size = Math.max(u.getIconRegion().getWidth(), u.getIconRegion().getHeight()) * Draw.scl;
|
||||
Draw.rect("circle-shadow", u.x, u.y, size * rad, size * rad);
|
||||
};
|
||||
|
||||
for(EntityGroup<? extends BaseUnit> group : unitGroups){
|
||||
if(!group.isEmpty()){
|
||||
group.draw(unit -> !unit.isDead(), draw::accept);
|
||||
group.draw(unit -> !unit.isDead(), draw::get);
|
||||
}
|
||||
}
|
||||
|
||||
if(!playerGroup.isEmpty()){
|
||||
playerGroup.draw(unit -> !unit.isDead(), draw::accept);
|
||||
playerGroup.draw(unit -> !unit.isDead(), draw::get);
|
||||
}
|
||||
|
||||
Draw.color();
|
||||
|
||||
@@ -12,7 +12,7 @@ import io.anuke.arc.files.*;
|
||||
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.func.*;
|
||||
import io.anuke.arc.graphics.*;
|
||||
import io.anuke.arc.graphics.Texture.*;
|
||||
import io.anuke.arc.graphics.g2d.*;
|
||||
@@ -68,6 +68,7 @@ public class UI implements ApplicationListener, Loadable{
|
||||
public DeployDialog deploy;
|
||||
public TechTreeDialog tech;
|
||||
public MinimapDialog minimap;
|
||||
public SchematicsDialog schematics;
|
||||
public ModsDialog mods;
|
||||
|
||||
public Cursor drillCursor, unloadCursor;
|
||||
@@ -185,6 +186,13 @@ public class UI implements ApplicationListener, Loadable{
|
||||
Core.scene.act();
|
||||
Core.scene.draw();
|
||||
|
||||
if(Core.input.keyTap(KeyCode.MOUSE_LEFT) && Core.scene.getKeyboardFocus() instanceof TextField){
|
||||
Element e = Core.scene.hit(Core.input.mouseX(), Core.input.mouseY(), true);
|
||||
if(!(e instanceof TextField)){
|
||||
Core.scene.setKeyboardFocus(null);
|
||||
}
|
||||
}
|
||||
|
||||
//draw overlay for buttons
|
||||
if(state.rules.tutorial){
|
||||
control.tutorial.draw();
|
||||
@@ -225,6 +233,7 @@ public class UI implements ApplicationListener, Loadable{
|
||||
tech = new TechTreeDialog();
|
||||
minimap = new MinimapDialog();
|
||||
mods = new ModsDialog();
|
||||
schematics = new SchematicsDialog();
|
||||
|
||||
Group group = Core.scene.root;
|
||||
|
||||
@@ -238,7 +247,6 @@ public class UI implements ApplicationListener, Loadable{
|
||||
Core.scene.add(menuGroup);
|
||||
Core.scene.add(hudGroup);
|
||||
|
||||
control.input.getFrag().build(hudGroup);
|
||||
hudfrag.build(hudGroup);
|
||||
menufrag.build(menuGroup);
|
||||
chatfrag.container().build(hudGroup);
|
||||
@@ -271,7 +279,7 @@ public class UI implements ApplicationListener, Loadable{
|
||||
});
|
||||
}
|
||||
|
||||
public void showTextInput(String titleText, String dtext, int textLength, String def, boolean inumeric, Consumer<String> confirmed){
|
||||
public void showTextInput(String titleText, String dtext, int textLength, String def, boolean inumeric, Cons<String> confirmed){
|
||||
if(mobile){
|
||||
Core.input.getTextInput(new TextInput(){{
|
||||
this.title = (titleText.startsWith("$") ? Core.bundle.get(titleText.substring(1)) : titleText);
|
||||
@@ -288,7 +296,7 @@ public class UI implements ApplicationListener, Loadable{
|
||||
field.setFilter((f, c) -> field.getText().length() < textLength && filter.acceptChar(f, c));
|
||||
buttons.defaults().size(120, 54).pad(4);
|
||||
buttons.addButton("$ok", () -> {
|
||||
confirmed.accept(field.getText());
|
||||
confirmed.get(field.getText());
|
||||
hide();
|
||||
}).disabled(b -> field.getText().isEmpty());
|
||||
buttons.addButton("$cancel", this::hide);
|
||||
@@ -296,11 +304,11 @@ public class UI implements ApplicationListener, Loadable{
|
||||
}
|
||||
}
|
||||
|
||||
public void showTextInput(String title, String text, String def, Consumer<String> confirmed){
|
||||
showTextInput(title, text, 24, def, confirmed);
|
||||
public void showTextInput(String title, String text, String def, Cons<String> confirmed){
|
||||
showTextInput(title, text, 32, def, confirmed);
|
||||
}
|
||||
|
||||
public void showTextInput(String titleText, String text, int textLength, String def, Consumer<String> confirmed){
|
||||
public void showTextInput(String titleText, String text, int textLength, String def, Cons<String> confirmed){
|
||||
showTextInput(titleText, text, textLength, def, false, confirmed);
|
||||
}
|
||||
|
||||
@@ -308,7 +316,7 @@ public class UI implements ApplicationListener, Loadable{
|
||||
Table table = new Table();
|
||||
table.setFillParent(true);
|
||||
table.actions(Actions.fadeOut(7f, Interpolation.fade), Actions.remove());
|
||||
table.top().add(info).padTop(10);
|
||||
table.top().add(info).style(Styles.outlineLabel).padTop(10);
|
||||
Core.scene.add(table);
|
||||
}
|
||||
|
||||
@@ -339,6 +347,7 @@ public class UI implements ApplicationListener, Loadable{
|
||||
}
|
||||
|
||||
public void showException(String text, Throwable exc){
|
||||
loadfrag.hide();
|
||||
new Dialog(""){{
|
||||
String message = Strings.getFinalMesage(exc);
|
||||
|
||||
@@ -395,9 +404,9 @@ public class UI implements ApplicationListener, Loadable{
|
||||
showConfirm(title, text, null, confirmed);
|
||||
}
|
||||
|
||||
public void showConfirm(String title, String text, BooleanProvider hide, Runnable confirmed){
|
||||
public void showConfirm(String title, String text, Boolp hide, Runnable confirmed){
|
||||
FloatingDialog dialog = new FloatingDialog(title);
|
||||
dialog.cont.add(text).width(500f).wrap().pad(4f).get().setAlignment(Align.center, Align.center);
|
||||
dialog.cont.add(text).width(mobile ? 400f : 500f).wrap().pad(4f).get().setAlignment(Align.center, Align.center);
|
||||
dialog.buttons.defaults().size(200f, 54f).pad(2f);
|
||||
dialog.setFillParent(false);
|
||||
dialog.buttons.addButton("$cancel", dialog::hide);
|
||||
@@ -420,7 +429,7 @@ public class UI implements ApplicationListener, Loadable{
|
||||
|
||||
public void showCustomConfirm(String title, String text, String yes, String no, Runnable confirmed){
|
||||
FloatingDialog dialog = new FloatingDialog(title);
|
||||
dialog.cont.add(text).width(500f).wrap().pad(4f).get().setAlignment(Align.center, Align.center);
|
||||
dialog.cont.add(text).width(mobile ? 400f : 500f).wrap().pad(4f).get().setAlignment(Align.center, Align.center);
|
||||
dialog.buttons.defaults().size(200f, 54f).pad(2f);
|
||||
dialog.setFillParent(false);
|
||||
dialog.buttons.addButton(no, dialog::hide);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package io.anuke.mindustry.game;
|
||||
package io.anuke.mindustry.core;
|
||||
|
||||
import io.anuke.arc.*;
|
||||
import io.anuke.arc.Files.*;
|
||||
@@ -185,6 +185,10 @@ public class World{
|
||||
Events.fire(new WorldLoadEvent());
|
||||
}
|
||||
|
||||
public void setGenerating(boolean gen){
|
||||
this.generating = gen;
|
||||
}
|
||||
|
||||
public boolean isGenerating(){
|
||||
return generating;
|
||||
}
|
||||
@@ -272,6 +276,7 @@ public class World{
|
||||
}
|
||||
|
||||
public void removeBlock(Tile tile){
|
||||
if(tile == null) return;
|
||||
tile.link().getLinkedTiles(other -> other.setBlock(Blocks.air));
|
||||
}
|
||||
|
||||
|
||||
@@ -1,16 +1,19 @@
|
||||
package io.anuke.mindustry.game;
|
||||
package io.anuke.mindustry.ctype;
|
||||
|
||||
import io.anuke.arc.files.*;
|
||||
import io.anuke.arc.util.ArcAnnotate.*;
|
||||
import io.anuke.mindustry.Vars;
|
||||
import io.anuke.mindustry.*;
|
||||
import io.anuke.mindustry.mod.Mods.*;
|
||||
import io.anuke.mindustry.type.ContentType;
|
||||
import io.anuke.mindustry.type.*;
|
||||
|
||||
|
||||
/** Base class for a content type that is loaded in {@link io.anuke.mindustry.core.ContentLoader}. */
|
||||
public abstract class Content{
|
||||
public abstract class Content implements Comparable<Content>{
|
||||
public final short id;
|
||||
/** The mod that loaded this piece of content. */
|
||||
public @Nullable LoadedMod mod;
|
||||
/** File that this content was loaded from. */
|
||||
public @Nullable FileHandle sourceFile;
|
||||
|
||||
public Content(){
|
||||
this.id = (short)Vars.content.getBy(getContentType()).size;
|
||||
@@ -34,6 +37,11 @@ public abstract class Content{
|
||||
public void load(){
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(Content c){
|
||||
return Integer.compare(id, c.id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(){
|
||||
return getContentType().name() + "#" + id;
|
||||
@@ -1,4 +1,4 @@
|
||||
package io.anuke.mindustry.game;
|
||||
package io.anuke.mindustry.ctype;
|
||||
|
||||
/** Interface for a list of content to be loaded in {@link io.anuke.mindustry.core.ContentLoader}. */
|
||||
public interface ContentList{
|
||||
@@ -1,4 +1,4 @@
|
||||
package io.anuke.mindustry.game;
|
||||
package io.anuke.mindustry.ctype;
|
||||
|
||||
import io.anuke.mindustry.*;
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
package io.anuke.mindustry.game;
|
||||
package io.anuke.mindustry.ctype;
|
||||
|
||||
import io.anuke.annotations.Annotations.*;
|
||||
import io.anuke.arc.*;
|
||||
import io.anuke.arc.graphics.g2d.*;
|
||||
import io.anuke.arc.scene.ui.layout.*;
|
||||
import io.anuke.mindustry.*;
|
||||
import io.anuke.mindustry.ui.Cicon;
|
||||
|
||||
/** Base interface for an unlockable content type. */
|
||||
public abstract class UnlockableContent extends MappableContent{
|
||||
@@ -13,7 +14,7 @@ public abstract class UnlockableContent extends MappableContent{
|
||||
/** Localized description. May be null. */
|
||||
public String description;
|
||||
/** Icons by Cicon ID.*/
|
||||
protected TextureRegion[] cicons = new TextureRegion[Cicon.all.length];
|
||||
protected TextureRegion[] cicons = new TextureRegion[io.anuke.mindustry.ui.Cicon.all.length];
|
||||
|
||||
public UnlockableContent(String name){
|
||||
super(name);
|
||||
@@ -31,7 +32,11 @@ public abstract class UnlockableContent extends MappableContent{
|
||||
/** Returns a specific content icon, or the region {contentType}-{name} if not found.*/
|
||||
public TextureRegion icon(Cicon icon){
|
||||
if(cicons[icon.ordinal()] == null){
|
||||
cicons[icon.ordinal()] = Core.atlas.find(getContentType().name() + "-" + name + "-" + icon.name(), Core.atlas.find(getContentType().name() + "-" + name + "-full", Core.atlas.find(getContentType().name() + "-" + name, Core.atlas.find(name))));
|
||||
cicons[icon.ordinal()] = Core.atlas.find(getContentType().name() + "-" + name + "-" + icon.name(),
|
||||
Core.atlas.find(getContentType().name() + "-" + name + "-full",
|
||||
Core.atlas.find(getContentType().name() + "-" + name,
|
||||
Core.atlas.find(name,
|
||||
Core.atlas.find(name + "1")))));
|
||||
}
|
||||
return cicons[icon.ordinal()];
|
||||
}
|
||||
@@ -62,6 +67,11 @@ public abstract class UnlockableContent extends MappableContent{
|
||||
return Vars.data.isUnlocked(this);
|
||||
}
|
||||
|
||||
/** @return whether this content is unlocked, or the player is in a custom game. */
|
||||
public final boolean unlockedCur(){
|
||||
return Vars.data.isUnlocked(this) || !Vars.world.isZone();
|
||||
}
|
||||
|
||||
public final boolean locked(){
|
||||
return !unlocked();
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
package io.anuke.mindustry.editor;
|
||||
|
||||
import io.anuke.arc.collection.IntArray;
|
||||
import io.anuke.arc.function.*;
|
||||
import io.anuke.arc.func.*;
|
||||
import io.anuke.arc.math.Mathf;
|
||||
import io.anuke.arc.math.geom.Bresenham2;
|
||||
import io.anuke.arc.util.Structs;
|
||||
@@ -113,8 +113,8 @@ public enum EditorTool{
|
||||
return;
|
||||
}
|
||||
|
||||
Predicate<Tile> tester;
|
||||
Consumer<Tile> setter;
|
||||
Boolf<Tile> tester;
|
||||
Cons<Tile> setter;
|
||||
|
||||
if(editor.drawBlock.isOverlay()){
|
||||
Block dest = tile.overlay();
|
||||
@@ -146,7 +146,7 @@ public enum EditorTool{
|
||||
}
|
||||
}
|
||||
|
||||
void fill(MapEditor editor, int x, int y, boolean replace, Predicate<Tile> tester, Consumer<Tile> filler){
|
||||
void fill(MapEditor editor, int x, int y, boolean replace, Boolf<Tile> tester, Cons<Tile> filler){
|
||||
int width = editor.width(), height = editor.height();
|
||||
|
||||
if(replace){
|
||||
@@ -154,8 +154,8 @@ public enum EditorTool{
|
||||
for(int cx = 0; cx < width; cx++){
|
||||
for(int cy = 0; cy < height; cy++){
|
||||
Tile tile = editor.tile(cx, cy);
|
||||
if(tester.test(tile)){
|
||||
filler.accept(tile);
|
||||
if(tester.get(tile)){
|
||||
filler.get(tile);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -173,23 +173,23 @@ public enum EditorTool{
|
||||
y = Pos.y(popped);
|
||||
|
||||
x1 = x;
|
||||
while(x1 >= 0 && tester.test(editor.tile(x1, y))) x1--;
|
||||
while(x1 >= 0 && tester.get(editor.tile(x1, y))) x1--;
|
||||
x1++;
|
||||
boolean spanAbove = false, spanBelow = false;
|
||||
while(x1 < width && tester.test(editor.tile(x1, y))){
|
||||
filler.accept(editor.tile(x1, y));
|
||||
while(x1 < width && tester.get(editor.tile(x1, y))){
|
||||
filler.get(editor.tile(x1, y));
|
||||
|
||||
if(!spanAbove && y > 0 && tester.test(editor.tile(x1, y - 1))){
|
||||
if(!spanAbove && y > 0 && tester.get(editor.tile(x1, y - 1))){
|
||||
stack.add(Pos.get(x1, y - 1));
|
||||
spanAbove = true;
|
||||
}else if(spanAbove && !tester.test(editor.tile(x1, y - 1))){
|
||||
}else if(spanAbove && !tester.get(editor.tile(x1, y - 1))){
|
||||
spanAbove = false;
|
||||
}
|
||||
|
||||
if(!spanBelow && y < height - 1 && tester.test(editor.tile(x1, y + 1))){
|
||||
if(!spanBelow && y < height - 1 && tester.get(editor.tile(x1, y + 1))){
|
||||
stack.add(Pos.get(x1, y + 1));
|
||||
spanBelow = true;
|
||||
}else if(spanBelow && y < height - 1 && !tester.test(editor.tile(x1, y + 1))){
|
||||
}else if(spanBelow && y < height - 1 && !tester.get(editor.tile(x1, y + 1))){
|
||||
spanBelow = false;
|
||||
}
|
||||
x1++;
|
||||
|
||||
@@ -2,8 +2,8 @@ package io.anuke.mindustry.editor;
|
||||
|
||||
import io.anuke.arc.collection.StringMap;
|
||||
import io.anuke.arc.files.FileHandle;
|
||||
import io.anuke.arc.function.Consumer;
|
||||
import io.anuke.arc.function.Predicate;
|
||||
import io.anuke.arc.func.Cons;
|
||||
import io.anuke.arc.func.Boolf;
|
||||
import io.anuke.arc.graphics.Pixmap;
|
||||
import io.anuke.arc.math.Mathf;
|
||||
import io.anuke.arc.util.Structs;
|
||||
@@ -144,11 +144,11 @@ public class MapEditor{
|
||||
drawBlocks(x, y, false, tile -> true);
|
||||
}
|
||||
|
||||
public void drawBlocks(int x, int y, Predicate<Tile> tester){
|
||||
public void drawBlocks(int x, int y, Boolf<Tile> tester){
|
||||
drawBlocks(x, y, false, tester);
|
||||
}
|
||||
|
||||
public void drawBlocks(int x, int y, boolean square, Predicate<Tile> tester){
|
||||
public void drawBlocks(int x, int y, boolean square, Boolf<Tile> tester){
|
||||
if(drawBlock.isMultiblock()){
|
||||
x = Mathf.clamp(x, (drawBlock.size - 1) / 2, width() - drawBlock.size / 2 - 1);
|
||||
y = Mathf.clamp(y, (drawBlock.size - 1) / 2, height() - drawBlock.size / 2 - 1);
|
||||
@@ -180,8 +180,8 @@ public class MapEditor{
|
||||
}else{
|
||||
boolean isFloor = drawBlock.isFloor() && drawBlock != Blocks.air;
|
||||
|
||||
Consumer<Tile> drawer = tile -> {
|
||||
if(!tester.test(tile)) return;
|
||||
Cons<Tile> drawer = tile -> {
|
||||
if(!tester.get(tile)) return;
|
||||
|
||||
//remove linked tiles blocking the way
|
||||
if(!isFloor && (tile.isLinked() || tile.block().isMultiblock())){
|
||||
@@ -209,7 +209,7 @@ public class MapEditor{
|
||||
}
|
||||
}
|
||||
|
||||
public void drawCircle(int x, int y, Consumer<Tile> drawer){
|
||||
public void drawCircle(int x, int y, Cons<Tile> drawer){
|
||||
for(int rx = -brushSize; rx <= brushSize; rx++){
|
||||
for(int ry = -brushSize; ry <= brushSize; ry++){
|
||||
if(Mathf.dst2(rx, ry) <= (brushSize - 0.5f) * (brushSize - 0.5f)){
|
||||
@@ -219,13 +219,13 @@ public class MapEditor{
|
||||
continue;
|
||||
}
|
||||
|
||||
drawer.accept(tile(wx, wy));
|
||||
drawer.get(tile(wx, wy));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void drawSquare(int x, int y, Consumer<Tile> drawer){
|
||||
public void drawSquare(int x, int y, Cons<Tile> drawer){
|
||||
for(int rx = -brushSize; rx <= brushSize; rx++){
|
||||
for(int ry = -brushSize; ry <= brushSize; ry++){
|
||||
int wx = x + rx, wy = y + ry;
|
||||
@@ -234,7 +234,7 @@ public class MapEditor{
|
||||
continue;
|
||||
}
|
||||
|
||||
drawer.accept(tile(wx, wy));
|
||||
drawer.get(tile(wx, wy));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ package io.anuke.mindustry.editor;
|
||||
import io.anuke.arc.*;
|
||||
import io.anuke.arc.collection.*;
|
||||
import io.anuke.arc.files.*;
|
||||
import io.anuke.arc.function.*;
|
||||
import io.anuke.arc.func.*;
|
||||
import io.anuke.arc.graphics.*;
|
||||
import io.anuke.arc.graphics.g2d.*;
|
||||
import io.anuke.arc.input.*;
|
||||
@@ -25,6 +25,7 @@ import io.anuke.mindustry.graphics.*;
|
||||
import io.anuke.mindustry.io.*;
|
||||
import io.anuke.mindustry.maps.*;
|
||||
import io.anuke.mindustry.ui.*;
|
||||
import io.anuke.mindustry.ui.Cicon;
|
||||
import io.anuke.mindustry.ui.dialogs.*;
|
||||
import io.anuke.mindustry.world.*;
|
||||
import io.anuke.mindustry.world.blocks.*;
|
||||
@@ -149,15 +150,16 @@ public class MapEditorDialog extends Dialog implements Disposable{
|
||||
if(steam){
|
||||
menu.cont.addImageTextButton("$editor.publish.workshop", Icon.linkSmall, () -> {
|
||||
Map builtin = maps.all().find(m -> m.name().equals(editor.getTags().get("name", "").trim()));
|
||||
|
||||
if(editor.getTags().containsKey("steamid") && builtin != null && !builtin.custom){
|
||||
platform.viewMapListing(editor.getTags().get("steamid"));
|
||||
platform.viewListingID(editor.getTags().get("steamid"));
|
||||
return;
|
||||
}
|
||||
|
||||
Map map = save();
|
||||
|
||||
if(editor.getTags().containsKey("steamid") && map != null){
|
||||
platform.viewMapListingInfo(map);
|
||||
platform.viewListing(map);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -173,7 +175,7 @@ public class MapEditorDialog extends Dialog implements Disposable{
|
||||
return;
|
||||
}
|
||||
|
||||
platform.publishMap(map);
|
||||
platform.publish(map);
|
||||
}).padTop(-3).size(swidth * 2f + 10, 60f).update(b -> b.setText(editor.getTags().containsKey("steamid") ? editor.getTags().get("author").equals(player.name) ? "$workshop.listing" : "$view.workshop" : "$editor.publish.workshop"));
|
||||
|
||||
menu.cont.row();
|
||||
@@ -423,7 +425,7 @@ public class MapEditorDialog extends Dialog implements Disposable{
|
||||
ButtonGroup<ImageButton> group = new ButtonGroup<>();
|
||||
Table[] lastTable = {null};
|
||||
|
||||
Consumer<EditorTool> addTool = tool -> {
|
||||
Cons<EditorTool> addTool = tool -> {
|
||||
|
||||
ImageButton button = new ImageButton(Core.atlas.drawable("icon-" + tool.name() + "-small"), Styles.clearTogglei);
|
||||
button.clicked(() -> {
|
||||
@@ -505,14 +507,14 @@ public class MapEditorDialog extends Dialog implements Disposable{
|
||||
|
||||
ImageButton grid = tools.addImageButton(Icon.gridSmall, Styles.clearTogglei, () -> view.setGrid(!view.isGrid())).get();
|
||||
|
||||
addTool.accept(EditorTool.zoom);
|
||||
addTool.get(EditorTool.zoom);
|
||||
|
||||
tools.row();
|
||||
|
||||
ImageButton undo = tools.addImageButton(Icon.undoSmall, Styles.cleari, editor::undo).get();
|
||||
ImageButton redo = tools.addImageButton(Icon.redoSmall, Styles.cleari, editor::redo).get();
|
||||
|
||||
addTool.accept(EditorTool.pick);
|
||||
addTool.get(EditorTool.pick);
|
||||
|
||||
tools.row();
|
||||
|
||||
@@ -523,14 +525,14 @@ public class MapEditorDialog extends Dialog implements Disposable{
|
||||
redo.update(() -> redo.getImage().setColor(redo.isDisabled() ? Color.gray : Color.white));
|
||||
grid.update(() -> grid.setChecked(view.isGrid()));
|
||||
|
||||
addTool.accept(EditorTool.line);
|
||||
addTool.accept(EditorTool.pencil);
|
||||
addTool.accept(EditorTool.eraser);
|
||||
addTool.get(EditorTool.line);
|
||||
addTool.get(EditorTool.pencil);
|
||||
addTool.get(EditorTool.eraser);
|
||||
|
||||
tools.row();
|
||||
|
||||
addTool.accept(EditorTool.fill);
|
||||
addTool.accept(EditorTool.spray);
|
||||
addTool.get(EditorTool.fill);
|
||||
addTool.get(EditorTool.spray);
|
||||
|
||||
ImageButton rotate = tools.addImageButton(Icon.arrow16Small, Styles.cleari, () -> editor.rotation = (editor.rotation + 1) % 4).get();
|
||||
rotate.getImage().update(() -> {
|
||||
|
||||
@@ -2,7 +2,7 @@ package io.anuke.mindustry.editor;
|
||||
|
||||
import io.anuke.arc.*;
|
||||
import io.anuke.arc.collection.*;
|
||||
import io.anuke.arc.function.*;
|
||||
import io.anuke.arc.func.*;
|
||||
import io.anuke.arc.graphics.*;
|
||||
import io.anuke.arc.graphics.Pixmap.*;
|
||||
import io.anuke.arc.math.*;
|
||||
@@ -27,7 +27,7 @@ import static io.anuke.mindustry.Vars.*;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public class MapGenerateDialog extends FloatingDialog{
|
||||
private final Supplier<GenerateFilter>[] filterTypes = new Supplier[]{
|
||||
private final Prov<GenerateFilter>[] filterTypes = new Prov[]{
|
||||
NoiseFilter::new, ScatterFilter::new, TerrainFilter::new, DistortFilter::new,
|
||||
RiverNoiseFilter::new, OreFilter::new, OreMedianFilter::new, MedianFilter::new,
|
||||
BlendFilter::new, MirrorFilter::new, ClearFilter::new
|
||||
@@ -48,7 +48,7 @@ public class MapGenerateDialog extends FloatingDialog{
|
||||
private GenTile returnTile = new GenTile();
|
||||
|
||||
private GenTile[][] buffer1, buffer2;
|
||||
private Consumer<Array<GenerateFilter>> applier;
|
||||
private Cons<Array<GenerateFilter>> applier;
|
||||
private CachedTile ctile = new CachedTile(){
|
||||
//nothing.
|
||||
@Override
|
||||
@@ -95,13 +95,13 @@ public class MapGenerateDialog extends FloatingDialog{
|
||||
onResize(this::rebuildFilters);
|
||||
}
|
||||
|
||||
public void show(Array<GenerateFilter> filters, Consumer<Array<GenerateFilter>> applier){
|
||||
public void show(Array<GenerateFilter> filters, Cons<Array<GenerateFilter>> applier){
|
||||
this.filters = filters;
|
||||
this.applier = applier;
|
||||
show();
|
||||
}
|
||||
|
||||
public void show(Consumer<Array<GenerateFilter>> applier){
|
||||
public void show(Cons<Array<GenerateFilter>> applier){
|
||||
show(this.filters, applier);
|
||||
}
|
||||
|
||||
@@ -289,7 +289,7 @@ public class MapGenerateDialog extends FloatingDialog{
|
||||
selection.setFillParent(false);
|
||||
selection.cont.defaults().size(210f, 60f);
|
||||
int i = 0;
|
||||
for(Supplier<GenerateFilter> gen : filterTypes){
|
||||
for(Prov<GenerateFilter> gen : filterTypes){
|
||||
GenerateFilter filter = gen.get();
|
||||
|
||||
if(!applied && filter.buffered) continue;
|
||||
@@ -334,7 +334,7 @@ public class MapGenerateDialog extends FloatingDialog{
|
||||
texture = null;
|
||||
}
|
||||
|
||||
applier.accept(filters);
|
||||
applier.get(filters);
|
||||
}
|
||||
|
||||
void update(){
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package io.anuke.mindustry.editor;
|
||||
|
||||
import io.anuke.arc.function.*;
|
||||
import io.anuke.arc.func.*;
|
||||
import io.anuke.arc.scene.ui.*;
|
||||
import io.anuke.arc.scene.ui.layout.*;
|
||||
import io.anuke.arc.util.*;
|
||||
@@ -13,7 +13,7 @@ import static io.anuke.mindustry.Vars.maps;
|
||||
public class MapLoadDialog extends FloatingDialog{
|
||||
private Map selected = null;
|
||||
|
||||
public MapLoadDialog(Consumer<Map> loader){
|
||||
public MapLoadDialog(Cons<Map> loader){
|
||||
super("$editor.loadmap");
|
||||
|
||||
shown(this::rebuild);
|
||||
@@ -22,7 +22,7 @@ public class MapLoadDialog extends FloatingDialog{
|
||||
button.setDisabled(() -> selected == null);
|
||||
button.clicked(() -> {
|
||||
if(selected != null){
|
||||
loader.accept(selected);
|
||||
loader.get(selected);
|
||||
hide();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package io.anuke.mindustry.editor;
|
||||
|
||||
import io.anuke.arc.Core;
|
||||
import io.anuke.arc.*;
|
||||
import io.anuke.arc.collection.IntSet;
|
||||
import io.anuke.arc.collection.IntSet.IntSetIterator;
|
||||
import io.anuke.arc.graphics.Color;
|
||||
@@ -10,6 +10,7 @@ import io.anuke.arc.graphics.g2d.TextureRegion;
|
||||
import io.anuke.arc.math.Mathf;
|
||||
import io.anuke.arc.util.*;
|
||||
import io.anuke.mindustry.content.Blocks;
|
||||
import io.anuke.mindustry.game.EventType.*;
|
||||
import io.anuke.mindustry.game.Team;
|
||||
import io.anuke.mindustry.graphics.IndexedRenderer;
|
||||
import io.anuke.mindustry.world.Block;
|
||||
@@ -29,7 +30,11 @@ public class MapRenderer implements Disposable{
|
||||
|
||||
public MapRenderer(MapEditor editor){
|
||||
this.editor = editor;
|
||||
texture = Core.atlas.find("clear-editor").getTexture();
|
||||
this.texture = Core.atlas.find("clear-editor").getTexture();
|
||||
|
||||
Events.on(ContentReloadEvent.class, e -> {
|
||||
texture = Core.atlas.find("clear-editor").getTexture();
|
||||
});
|
||||
}
|
||||
|
||||
public void resize(int width, int height){
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package io.anuke.mindustry.editor;
|
||||
|
||||
import io.anuke.arc.function.*;
|
||||
import io.anuke.arc.func.*;
|
||||
import io.anuke.arc.math.*;
|
||||
import io.anuke.arc.scene.ui.layout.*;
|
||||
import io.anuke.mindustry.gen.*;
|
||||
@@ -10,7 +10,7 @@ public class MapResizeDialog extends FloatingDialog{
|
||||
private static final int minSize = 50, maxSize = 500, increment = 50;
|
||||
int width, height;
|
||||
|
||||
public MapResizeDialog(MapEditor editor, IntPositionConsumer cons){
|
||||
public MapResizeDialog(MapEditor editor, Intc2 cons){
|
||||
super("$editor.resizemap");
|
||||
shown(() -> {
|
||||
cont.clear();
|
||||
@@ -46,8 +46,8 @@ public class MapResizeDialog extends FloatingDialog{
|
||||
|
||||
buttons.defaults().size(200f, 50f);
|
||||
buttons.addButton("$cancel", this::hide);
|
||||
buttons.addButton("$editor.resize", () -> {
|
||||
cons.accept(width, height);
|
||||
buttons.addButton("$ok", () -> {
|
||||
cons.get(width, height);
|
||||
hide();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package io.anuke.mindustry.editor;
|
||||
|
||||
import io.anuke.arc.function.*;
|
||||
import io.anuke.arc.func.*;
|
||||
import io.anuke.arc.scene.ui.*;
|
||||
import io.anuke.mindustry.*;
|
||||
import io.anuke.mindustry.maps.*;
|
||||
@@ -10,9 +10,9 @@ import static io.anuke.mindustry.Vars.ui;
|
||||
|
||||
public class MapSaveDialog extends FloatingDialog{
|
||||
private TextField field;
|
||||
private Consumer<String> listener;
|
||||
private Cons<String> listener;
|
||||
|
||||
public MapSaveDialog(Consumer<String> cons){
|
||||
public MapSaveDialog(Cons<String> cons){
|
||||
super("$editor.savemap");
|
||||
field = new TextField();
|
||||
listener = cons;
|
||||
@@ -43,7 +43,7 @@ public class MapSaveDialog extends FloatingDialog{
|
||||
TextButton button = new TextButton("$save");
|
||||
button.clicked(() -> {
|
||||
if(!invalid()){
|
||||
cons.accept(field.getText());
|
||||
cons.get(field.getText());
|
||||
hide();
|
||||
}
|
||||
});
|
||||
@@ -53,7 +53,7 @@ public class MapSaveDialog extends FloatingDialog{
|
||||
|
||||
public void save(){
|
||||
if(!invalid()){
|
||||
listener.accept(field.getText());
|
||||
listener.get(field.getText());
|
||||
}else{
|
||||
ui.showErrorMessage("$editor.failoverwrite");
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ import io.anuke.mindustry.gen.*;
|
||||
import io.anuke.mindustry.graphics.*;
|
||||
import io.anuke.mindustry.io.*;
|
||||
import io.anuke.mindustry.type.*;
|
||||
import io.anuke.mindustry.ui.Cicon;
|
||||
import io.anuke.mindustry.ui.dialogs.*;
|
||||
|
||||
import static io.anuke.mindustry.Vars.*;
|
||||
@@ -140,7 +141,7 @@ public class WaveInfoDialog extends FloatingDialog{
|
||||
t.margin(0).defaults().pad(3).padLeft(5f).growX().left();
|
||||
t.addButton(b -> {
|
||||
b.left();
|
||||
b.addImage(group.type.icon(Cicon.medium)).size(32f).padRight(3);
|
||||
b.addImage(group.type.icon(io.anuke.mindustry.ui.Cicon.medium)).size(32f).padRight(3);
|
||||
b.add(group.type.localizedName).color(Pal.accent);
|
||||
}, () -> showUpdate(group)).pad(-6f).padBottom(0f);
|
||||
|
||||
@@ -221,7 +222,7 @@ public class WaveInfoDialog extends FloatingDialog{
|
||||
for(UnitType type : content.units()){
|
||||
dialog.cont.addButton(t -> {
|
||||
t.left();
|
||||
t.addImage(type.icon(Cicon.medium)).size(40f).padRight(2f);
|
||||
t.addImage(type.icon(io.anuke.mindustry.ui.Cicon.medium)).size(40f).padRight(2f);
|
||||
t.add(type.localizedName);
|
||||
}, () -> {
|
||||
lastType = type;
|
||||
|
||||
@@ -1,27 +1,22 @@
|
||||
package io.anuke.mindustry.entities;
|
||||
|
||||
import io.anuke.annotations.Annotations.Struct;
|
||||
import io.anuke.annotations.Annotations.*;
|
||||
import io.anuke.arc.*;
|
||||
import io.anuke.arc.collection.GridBits;
|
||||
import io.anuke.arc.collection.IntQueue;
|
||||
import io.anuke.arc.function.*;
|
||||
import io.anuke.arc.graphics.Color;
|
||||
import io.anuke.arc.math.Mathf;
|
||||
import io.anuke.arc.collection.*;
|
||||
import io.anuke.arc.func.*;
|
||||
import io.anuke.arc.graphics.*;
|
||||
import io.anuke.arc.math.*;
|
||||
import io.anuke.arc.math.geom.*;
|
||||
import io.anuke.arc.util.*;
|
||||
import io.anuke.mindustry.content.Bullets;
|
||||
import io.anuke.mindustry.content.Fx;
|
||||
import io.anuke.mindustry.entities.Effects.Effect;
|
||||
import io.anuke.mindustry.entities.type.Bullet;
|
||||
import io.anuke.mindustry.entities.effect.Fire;
|
||||
import io.anuke.mindustry.entities.effect.Lightning;
|
||||
import io.anuke.mindustry.entities.type.Unit;
|
||||
import io.anuke.mindustry.content.*;
|
||||
import io.anuke.mindustry.entities.Effects.*;
|
||||
import io.anuke.mindustry.entities.effect.*;
|
||||
import io.anuke.mindustry.entities.type.*;
|
||||
import io.anuke.mindustry.game.EventType.*;
|
||||
import io.anuke.mindustry.game.Team;
|
||||
import io.anuke.mindustry.gen.Call;
|
||||
import io.anuke.mindustry.gen.PropCell;
|
||||
import io.anuke.mindustry.graphics.Pal;
|
||||
import io.anuke.mindustry.world.Tile;
|
||||
import io.anuke.mindustry.game.*;
|
||||
import io.anuke.mindustry.gen.*;
|
||||
import io.anuke.mindustry.graphics.*;
|
||||
import io.anuke.mindustry.world.*;
|
||||
|
||||
import static io.anuke.mindustry.Vars.*;
|
||||
|
||||
@@ -32,6 +27,7 @@ public class Damage{
|
||||
private static Vector2 tr = new Vector2();
|
||||
private static GridBits bits = new GridBits(30, 30);
|
||||
private static IntQueue propagation = new IntQueue();
|
||||
private static IntSet collidedBlocks = new IntSet();
|
||||
|
||||
/** Creates a dynamic explosion based on specified parameters. */
|
||||
public static void dynamicExplosion(float x, float y, float flammability, float explosiveness, float power, float radius, Color color){
|
||||
@@ -88,20 +84,22 @@ public class Damage{
|
||||
* Only enemies of the specified team are damaged.
|
||||
*/
|
||||
public static void collideLine(Bullet hitter, Team team, Effect effect, float x, float y, float angle, float length, boolean large){
|
||||
collidedBlocks.clear();
|
||||
tr.trns(angle, length);
|
||||
IntPositionConsumer collider = (cx, cy) -> {
|
||||
Intc2 collider = (cx, cy) -> {
|
||||
Tile tile = world.ltile(cx, cy);
|
||||
if(tile != null && tile.entity != null && tile.getTeamID() != team.ordinal() && tile.entity.collide(hitter)){
|
||||
if(tile != null && !collidedBlocks.contains(tile.pos()) && tile.entity != null && tile.getTeamID() != team.ordinal() && tile.entity.collide(hitter)){
|
||||
tile.entity.collision(hitter);
|
||||
collidedBlocks.add(tile.pos());
|
||||
hitter.getBulletType().hit(hitter, tile.worldx(), tile.worldy());
|
||||
}
|
||||
};
|
||||
|
||||
world.raycastEachWorld(x, y, x + tr.x, y + tr.y, (cx, cy) -> {
|
||||
collider.accept(cx, cy);
|
||||
collider.get(cx, cy);
|
||||
if(large){
|
||||
for(Point2 p : Geometry.d4){
|
||||
collider.accept(cx + p.x, cy + p.y);
|
||||
collider.get(cx + p.x, cy + p.y);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
@@ -127,7 +125,7 @@ public class Damage{
|
||||
rect.width += expand * 2;
|
||||
rect.height += expand * 2;
|
||||
|
||||
Consumer<Unit> cons = e -> {
|
||||
Cons<Unit> cons = e -> {
|
||||
e.hitbox(hitrect);
|
||||
Rectangle other = hitrect;
|
||||
other.y -= expand;
|
||||
@@ -148,16 +146,16 @@ public class Damage{
|
||||
}
|
||||
|
||||
/** Damages all entities and blocks in a radius that are enemies of the team. */
|
||||
public static void damageUnits(Team team, float x, float y, float size, float damage, Predicate<Unit> predicate, Consumer<Unit> acceptor){
|
||||
Consumer<Unit> cons = entity -> {
|
||||
if(!predicate.test(entity)) return;
|
||||
public static void damageUnits(Team team, float x, float y, float size, float damage, Boolf<Unit> predicate, Cons<Unit> acceptor){
|
||||
Cons<Unit> cons = entity -> {
|
||||
if(!predicate.get(entity)) return;
|
||||
|
||||
entity.hitbox(hitrect);
|
||||
if(!hitrect.overlaps(rect)){
|
||||
return;
|
||||
}
|
||||
entity.damage(damage);
|
||||
acceptor.accept(entity);
|
||||
acceptor.get(entity);
|
||||
};
|
||||
|
||||
rect.setSize(size * 2).setCenter(x, y);
|
||||
@@ -180,7 +178,7 @@ public class Damage{
|
||||
|
||||
/** Damages all entities and blocks in a radius that are enemies of the team. */
|
||||
public static void damage(Team team, float x, float y, float radius, float damage, boolean complete){
|
||||
Consumer<Unit> cons = entity -> {
|
||||
Cons<Unit> cons = entity -> {
|
||||
if(entity.getTeam() == team || entity.dst(x, y) > radius){
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ package io.anuke.mindustry.entities;
|
||||
|
||||
import io.anuke.arc.Core;
|
||||
import io.anuke.arc.collection.Array;
|
||||
import io.anuke.arc.function.Consumer;
|
||||
import io.anuke.arc.func.Cons;
|
||||
import io.anuke.arc.graphics.Color;
|
||||
import io.anuke.arc.math.Mathf;
|
||||
import io.anuke.arc.math.geom.Position;
|
||||
@@ -142,11 +142,11 @@ public class Effects{
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public void scaled(float lifetime, Consumer<EffectContainer> cons){
|
||||
public void scaled(float lifetime, Cons<EffectContainer> cons){
|
||||
if(innerContainer == null) innerContainer = new EffectContainer();
|
||||
if(time <= lifetime){
|
||||
innerContainer.set(id, color, time, lifetime, rotation, x, y, data);
|
||||
cons.accept(innerContainer);
|
||||
cons.get(innerContainer);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ package io.anuke.mindustry.entities;
|
||||
|
||||
import io.anuke.arc.*;
|
||||
import io.anuke.arc.collection.*;
|
||||
import io.anuke.arc.function.*;
|
||||
import io.anuke.arc.func.*;
|
||||
import io.anuke.arc.graphics.*;
|
||||
import io.anuke.arc.math.geom.*;
|
||||
import io.anuke.mindustry.entities.traits.*;
|
||||
@@ -22,8 +22,8 @@ public class EntityGroup<T extends Entity>{
|
||||
private final Rectangle intersectRect = new Rectangle();
|
||||
private IntMap<T> map;
|
||||
private QuadTree tree;
|
||||
private Consumer<T> removeListener;
|
||||
private Consumer<T> addListener;
|
||||
private Cons<T> removeListener;
|
||||
private Cons<T> addListener;
|
||||
|
||||
private final Rectangle viewport = new Rectangle();
|
||||
private int count = 0;
|
||||
@@ -60,20 +60,20 @@ public class EntityGroup<T extends Entity>{
|
||||
draw(e -> true);
|
||||
}
|
||||
|
||||
public void draw(Predicate<T> toDraw){
|
||||
public void draw(Boolf<T> toDraw){
|
||||
draw(toDraw, t -> ((DrawTrait)t).draw());
|
||||
}
|
||||
|
||||
public void draw(Predicate<T> toDraw, Consumer<T> cons){
|
||||
public void draw(Boolf<T> toDraw, Cons<T> cons){
|
||||
Camera cam = Core.camera;
|
||||
viewport.set(cam.position.x - cam.width / 2, cam.position.y - cam.height / 2, cam.width, cam.height);
|
||||
|
||||
for(Entity e : all()){
|
||||
if(!(e instanceof DrawTrait) || !toDraw.test((T)e) || !e.isAdded()) continue;
|
||||
if(!(e instanceof DrawTrait) || !toDraw.get((T)e) || !e.isAdded()) continue;
|
||||
DrawTrait draw = (DrawTrait)e;
|
||||
|
||||
if(viewport.overlaps(draw.getX() - draw.drawSize()/2f, draw.getY() - draw.drawSize()/2f, draw.drawSize(), draw.drawSize())){
|
||||
cons.accept((T)e);
|
||||
cons.get((T)e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -82,11 +82,11 @@ public class EntityGroup<T extends Entity>{
|
||||
return useTree;
|
||||
}
|
||||
|
||||
public void setRemoveListener(Consumer<T> removeListener){
|
||||
public void setRemoveListener(Cons<T> removeListener){
|
||||
this.removeListener = removeListener;
|
||||
}
|
||||
|
||||
public void setAddListener(Consumer<T> addListener){
|
||||
public void setAddListener(Cons<T> addListener){
|
||||
this.addListener = addListener;
|
||||
}
|
||||
|
||||
@@ -148,7 +148,7 @@ public class EntityGroup<T extends Entity>{
|
||||
if(check.getID() == id){ //if it is indeed queued, remove it
|
||||
entitiesToAdd.removeValue(check, true);
|
||||
if(removeListener != null){
|
||||
removeListener.accept(check);
|
||||
removeListener.get(check);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -157,7 +157,7 @@ public class EntityGroup<T extends Entity>{
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void intersect(float x, float y, float width, float height, Consumer<? super T> out){
|
||||
public void intersect(float x, float y, float width, float height, Cons<? super T> out){
|
||||
//don't waste time for empty groups
|
||||
if(isEmpty()) return;
|
||||
tree().getIntersect(out, x, y, width, height);
|
||||
@@ -192,10 +192,10 @@ public class EntityGroup<T extends Entity>{
|
||||
return entityArray.size;
|
||||
}
|
||||
|
||||
public int count(Predicate<T> pred){
|
||||
public int count(Boolf<T> pred){
|
||||
int count = 0;
|
||||
for(int i = 0; i < entityArray.size; i++){
|
||||
if(pred.test(entityArray.get(i))) count++;
|
||||
if(pred.get(entityArray.get(i))) count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
@@ -211,7 +211,7 @@ public class EntityGroup<T extends Entity>{
|
||||
}
|
||||
|
||||
if(addListener != null){
|
||||
addListener.accept(type);
|
||||
addListener.get(type);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -221,7 +221,7 @@ public class EntityGroup<T extends Entity>{
|
||||
entitiesToRemove.add(type);
|
||||
|
||||
if(removeListener != null){
|
||||
removeListener.accept(type);
|
||||
removeListener.get(type);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -244,10 +244,10 @@ public class EntityGroup<T extends Entity>{
|
||||
map.clear();
|
||||
}
|
||||
|
||||
public T find(Predicate<T> pred){
|
||||
public T find(Boolf<T> pred){
|
||||
|
||||
for(int i = 0; i < entityArray.size; i++){
|
||||
if(pred.test(entityArray.get(i))) return entityArray.get(i);
|
||||
if(pred.get(entityArray.get(i))) return entityArray.get(i);
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
6
core/src/io/anuke/mindustry/entities/TargetPriority.java
Normal file
6
core/src/io/anuke/mindustry/entities/TargetPriority.java
Normal file
@@ -0,0 +1,6 @@
|
||||
package io.anuke.mindustry.entities;
|
||||
|
||||
public enum TargetPriority{
|
||||
base,
|
||||
turret
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
package io.anuke.mindustry.entities;
|
||||
|
||||
import io.anuke.arc.collection.EnumSet;
|
||||
import io.anuke.arc.function.Consumer;
|
||||
import io.anuke.arc.function.Predicate;
|
||||
import io.anuke.arc.func.Cons;
|
||||
import io.anuke.arc.func.Boolf;
|
||||
import io.anuke.arc.math.Mathf;
|
||||
import io.anuke.arc.math.geom.Geometry;
|
||||
import io.anuke.arc.math.geom.Rectangle;
|
||||
@@ -78,16 +78,16 @@ public class Units{
|
||||
}
|
||||
|
||||
/** Returns the neareset ally tile in a range. */
|
||||
public static TileEntity findAllyTile(Team team, float x, float y, float range, Predicate<Tile> pred){
|
||||
public static TileEntity findAllyTile(Team team, float x, float y, float range, Boolf<Tile> pred){
|
||||
return indexer.findTile(team, x, y, range, pred);
|
||||
}
|
||||
|
||||
/** Returns the neareset enemy tile in a range. */
|
||||
public static TileEntity findEnemyTile(Team team, float x, float y, float range, Predicate<Tile> pred){
|
||||
public static TileEntity findEnemyTile(Team team, float x, float y, float range, Boolf<Tile> pred){
|
||||
if(team == Team.derelict) return null;
|
||||
|
||||
for(Team enemy : state.teams.enemiesOf(team)){
|
||||
TileEntity entity = indexer.findTile(enemy, x, y, range, pred);
|
||||
TileEntity entity = indexer.findTile(enemy, x, y, range, pred, true);
|
||||
if(entity != null){
|
||||
return entity;
|
||||
}
|
||||
@@ -101,12 +101,12 @@ public class Units{
|
||||
}
|
||||
|
||||
/** Returns the closest target enemy. First, units are checked, then tile entities. */
|
||||
public static TargetTrait closestTarget(Team team, float x, float y, float range, Predicate<Unit> unitPred){
|
||||
public static TargetTrait closestTarget(Team team, float x, float y, float range, Boolf<Unit> unitPred){
|
||||
return closestTarget(team, x, y, range, unitPred, t -> true);
|
||||
}
|
||||
|
||||
/** Returns the closest target enemy. First, units are checked, then tile entities. */
|
||||
public static TargetTrait closestTarget(Team team, float x, float y, float range, Predicate<Unit> unitPred, Predicate<Tile> tilePred){
|
||||
public static TargetTrait closestTarget(Team team, float x, float y, float range, Boolf<Unit> unitPred, Boolf<Tile> tilePred){
|
||||
if(team == Team.derelict) return null;
|
||||
|
||||
Unit unit = closestEnemy(team, x, y, range, unitPred);
|
||||
@@ -118,14 +118,14 @@ public class Units{
|
||||
}
|
||||
|
||||
/** Returns the closest enemy of this team. Filter by predicate. */
|
||||
public static Unit closestEnemy(Team team, float x, float y, float range, Predicate<Unit> predicate){
|
||||
public static Unit closestEnemy(Team team, float x, float y, float range, Boolf<Unit> predicate){
|
||||
if(team == Team.derelict) return null;
|
||||
|
||||
result = null;
|
||||
cdist = 0f;
|
||||
|
||||
nearbyEnemies(team, x - range, y - range, range*2f, range*2f, e -> {
|
||||
if(e.isDead() || !predicate.test(e)) return;
|
||||
if(e.isDead() || !predicate.get(e)) return;
|
||||
|
||||
float dst2 = Mathf.dst2(e.x, e.y, x, y);
|
||||
if(dst2 < range*range && (result == null || dst2 < cdist)){
|
||||
@@ -138,12 +138,12 @@ public class Units{
|
||||
}
|
||||
|
||||
/** Returns the closest ally of this team. Filter by predicate. */
|
||||
public static Unit closest(Team team, float x, float y, float range, Predicate<Unit> predicate){
|
||||
public static Unit closest(Team team, float x, float y, float range, Boolf<Unit> predicate){
|
||||
result = null;
|
||||
cdist = 0f;
|
||||
|
||||
nearby(team, x, y, range, e -> {
|
||||
if(!predicate.test(e)) return;
|
||||
if(!predicate.get(e)) return;
|
||||
|
||||
float dist = Mathf.dst2(e.x, e.y, x, y);
|
||||
if(result == null || dist < cdist){
|
||||
@@ -156,32 +156,32 @@ public class Units{
|
||||
}
|
||||
|
||||
/** Iterates over all units in a rectangle. */
|
||||
public static void nearby(Team team, float x, float y, float width, float height, Consumer<Unit> cons){
|
||||
public static void nearby(Team team, float x, float y, float width, float height, Cons<Unit> cons){
|
||||
unitGroups[team.ordinal()].intersect(x, y, width, height, cons);
|
||||
playerGroup.intersect(x, y, width, height, player -> {
|
||||
if(player.getTeam() == team){
|
||||
cons.accept(player);
|
||||
cons.get(player);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/** Iterates over all units in a circle around this position. */
|
||||
public static void nearby(Team team, float x, float y, float radius, Consumer<Unit> cons){
|
||||
public static void nearby(Team team, float x, float y, float radius, Cons<Unit> cons){
|
||||
unitGroups[team.ordinal()].intersect(x - radius, y - radius, radius*2f, radius*2f, unit -> {
|
||||
if(unit.withinDst(x, y, radius)){
|
||||
cons.accept(unit);
|
||||
cons.get(unit);
|
||||
}
|
||||
});
|
||||
|
||||
playerGroup.intersect(x - radius, y - radius, radius*2f, radius*2f, unit -> {
|
||||
if(unit.getTeam() == team && unit.withinDst(x, y, radius)){
|
||||
cons.accept(unit);
|
||||
cons.get(unit);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/** Iterates over all units in a rectangle. */
|
||||
public static void nearby(float x, float y, float width, float height, Consumer<Unit> cons){
|
||||
public static void nearby(float x, float y, float width, float height, Cons<Unit> cons){
|
||||
for(Team team : Team.all){
|
||||
unitGroups[team.ordinal()].intersect(x, y, width, height, cons);
|
||||
}
|
||||
@@ -190,12 +190,12 @@ public class Units{
|
||||
}
|
||||
|
||||
/** Iterates over all units in a rectangle. */
|
||||
public static void nearby(Rectangle rect, Consumer<Unit> cons){
|
||||
public static void nearby(Rectangle rect, Cons<Unit> cons){
|
||||
nearby(rect.x, rect.y, rect.width, rect.height, cons);
|
||||
}
|
||||
|
||||
/** Iterates over all units that are enemies of this team. */
|
||||
public static void nearbyEnemies(Team team, float x, float y, float width, float height, Consumer<Unit> cons){
|
||||
public static void nearbyEnemies(Team team, float x, float y, float width, float height, Cons<Unit> cons){
|
||||
EnumSet<Team> targets = state.teams.enemiesOf(team);
|
||||
|
||||
for(Team other : targets){
|
||||
@@ -204,18 +204,18 @@ public class Units{
|
||||
|
||||
playerGroup.intersect(x, y, width, height, player -> {
|
||||
if(targets.contains(player.getTeam())){
|
||||
cons.accept(player);
|
||||
cons.get(player);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/** Iterates over all units that are enemies of this team. */
|
||||
public static void nearbyEnemies(Team team, Rectangle rect, Consumer<Unit> cons){
|
||||
public static void nearbyEnemies(Team team, Rectangle rect, Cons<Unit> cons){
|
||||
nearbyEnemies(team, rect.x, rect.y, rect.width, rect.height, cons);
|
||||
}
|
||||
|
||||
/** Iterates over all units. */
|
||||
public static void all(Consumer<Unit> cons){
|
||||
public static void all(Cons<Unit> cons){
|
||||
for(Team team : Team.all){
|
||||
unitGroups[team.ordinal()].all().each(cons);
|
||||
}
|
||||
|
||||
@@ -20,6 +20,10 @@ public class ArtilleryBulletType extends BasicBulletType{
|
||||
hitSound = Sounds.explosion;
|
||||
}
|
||||
|
||||
public ArtilleryBulletType(){
|
||||
this(1f, 1f, "shell");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(io.anuke.mindustry.entities.type.Bullet b){
|
||||
super.update(b);
|
||||
|
||||
@@ -22,6 +22,11 @@ public class BasicBulletType extends BulletType{
|
||||
this.bulletSprite = bulletSprite;
|
||||
}
|
||||
|
||||
/** For mods. */
|
||||
public BasicBulletType(){
|
||||
this(1f, 1f, "bullet");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load(){
|
||||
backRegion = Core.atlas.find(bulletSprite + "-back");
|
||||
|
||||
@@ -17,4 +17,8 @@ public class BombBulletType extends BasicBulletType{
|
||||
collidesAir = false;
|
||||
hitSound = Sounds.explosion;
|
||||
}
|
||||
|
||||
public BombBulletType(){
|
||||
this(1f, 1f, "shell");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,12 +3,12 @@ package io.anuke.mindustry.entities.bullet;
|
||||
import io.anuke.arc.audio.*;
|
||||
import io.anuke.arc.math.*;
|
||||
import io.anuke.mindustry.content.*;
|
||||
import io.anuke.mindustry.ctype.Content;
|
||||
import io.anuke.mindustry.entities.*;
|
||||
import io.anuke.mindustry.entities.Effects.*;
|
||||
import io.anuke.mindustry.entities.effect.*;
|
||||
import io.anuke.mindustry.entities.traits.*;
|
||||
import io.anuke.mindustry.entities.type.*;
|
||||
import io.anuke.mindustry.game.*;
|
||||
import io.anuke.mindustry.gen.*;
|
||||
import io.anuke.mindustry.graphics.*;
|
||||
import io.anuke.mindustry.type.*;
|
||||
@@ -47,7 +47,7 @@ public abstract class BulletType extends Content{
|
||||
/** Status effect applied on hit. */
|
||||
public StatusEffect status = StatusEffects.none;
|
||||
/** Intensity of applied status effect in terms of duration. */
|
||||
public float statusDuration = 60 * 1f;
|
||||
public float statusDuration = 60 * 10f;
|
||||
/** Whether this bullet type collides with tiles. */
|
||||
public boolean collidesTiles = true;
|
||||
/** Whether this bullet type collides with tiles that are of the same team. */
|
||||
|
||||
@@ -6,7 +6,7 @@ import io.anuke.mindustry.content.Fx;
|
||||
import io.anuke.mindustry.entities.Units;
|
||||
import io.anuke.mindustry.entities.type.Bullet;
|
||||
|
||||
public abstract class FlakBulletType extends BasicBulletType{
|
||||
public class FlakBulletType extends BasicBulletType{
|
||||
protected static Rectangle rect = new Rectangle();
|
||||
protected float explodeRange = 30f;
|
||||
|
||||
@@ -19,6 +19,10 @@ public abstract class FlakBulletType extends BasicBulletType{
|
||||
bulletHeight = 10f;
|
||||
}
|
||||
|
||||
public FlakBulletType(){
|
||||
this(1f, 1f);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(Bullet b){
|
||||
super.update(b);
|
||||
|
||||
@@ -22,6 +22,10 @@ public class HealBulletType extends BulletType{
|
||||
collidesTeam = true;
|
||||
}
|
||||
|
||||
public HealBulletType(){
|
||||
this(1f, 1f);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean collides(Bullet b, Tile tile){
|
||||
return tile.getTeam() != b.getTeam() || tile.entity.healthf() < 1f;
|
||||
|
||||
@@ -3,6 +3,7 @@ package io.anuke.mindustry.entities.bullet;
|
||||
import io.anuke.arc.graphics.*;
|
||||
import io.anuke.arc.graphics.g2d.*;
|
||||
import io.anuke.arc.math.geom.*;
|
||||
import io.anuke.arc.util.ArcAnnotate.*;
|
||||
import io.anuke.mindustry.content.*;
|
||||
import io.anuke.mindustry.entities.*;
|
||||
import io.anuke.mindustry.entities.effect.*;
|
||||
@@ -13,14 +14,17 @@ import io.anuke.mindustry.world.*;
|
||||
import static io.anuke.mindustry.Vars.*;
|
||||
|
||||
public class LiquidBulletType extends BulletType{
|
||||
Liquid liquid;
|
||||
@NonNull Liquid liquid;
|
||||
|
||||
public LiquidBulletType(Liquid liquid){
|
||||
public LiquidBulletType(@Nullable Liquid liquid){
|
||||
super(3.5f, 0);
|
||||
this.liquid = liquid;
|
||||
|
||||
if(liquid != null){
|
||||
this.liquid = liquid;
|
||||
this.status = liquid.effect;
|
||||
}
|
||||
|
||||
lifetime = 74f;
|
||||
status = liquid.effect;
|
||||
statusDuration = 90f;
|
||||
despawnEffect = Fx.none;
|
||||
hitEffect = Fx.hitLiquid;
|
||||
@@ -30,13 +34,17 @@ public class LiquidBulletType extends BulletType{
|
||||
knockback = 0.55f;
|
||||
}
|
||||
|
||||
public LiquidBulletType(){
|
||||
this(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float range(){
|
||||
return speed * lifetime / 2f;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(io.anuke.mindustry.entities.type.Bullet b){
|
||||
public void update(Bullet b){
|
||||
super.update(b);
|
||||
|
||||
if(liquid.canExtinguish()){
|
||||
@@ -50,7 +58,7 @@ public class LiquidBulletType extends BulletType{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(io.anuke.mindustry.entities.type.Bullet b){
|
||||
public void draw(Bullet b){
|
||||
Draw.color(liquid.color, Color.white, b.fout() / 100f);
|
||||
|
||||
Fill.circle(b.x, b.y, 0.5f + b.fout() * 2.5f);
|
||||
|
||||
@@ -23,6 +23,10 @@ public class MissileBulletType extends BasicBulletType{
|
||||
hitSound = Sounds.explosion;
|
||||
}
|
||||
|
||||
public MissileBulletType(){
|
||||
this(1f, 1f, "missile");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(Bullet b){
|
||||
super.update(b);
|
||||
@@ -32,7 +36,7 @@ public class MissileBulletType extends BasicBulletType{
|
||||
}
|
||||
|
||||
if(weaveMag > 0){
|
||||
b.velocity().rotate(Mathf.sin(Time.time() + b.id * 4422, weaveScale, weaveMag));
|
||||
b.velocity().rotate(Mathf.sin(Time.time() + b.id * 4422, weaveScale, weaveMag) * Time.delta());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,8 +12,8 @@ import io.anuke.mindustry.entities.traits.*;
|
||||
import io.anuke.mindustry.entities.type.*;
|
||||
import io.anuke.mindustry.entities.type.TimedEntity;
|
||||
import io.anuke.mindustry.game.EventType.*;
|
||||
import io.anuke.mindustry.game.*;
|
||||
import io.anuke.mindustry.gen.*;
|
||||
import io.anuke.mindustry.type.TypeID;
|
||||
import io.anuke.mindustry.world.*;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
@@ -20,8 +20,9 @@ import io.anuke.mindustry.entities.type.Unit;
|
||||
import io.anuke.mindustry.game.Team;
|
||||
import io.anuke.mindustry.gen.Call;
|
||||
import io.anuke.mindustry.graphics.Pal;
|
||||
import io.anuke.mindustry.world.Tile;
|
||||
|
||||
import static io.anuke.mindustry.Vars.bulletGroup;
|
||||
import static io.anuke.mindustry.Vars.*;
|
||||
|
||||
public class Lightning extends TimedEntity implements DrawTrait, TimeTrait{
|
||||
public static final float lifetime = 10f;
|
||||
@@ -34,7 +35,7 @@ public class Lightning extends TimedEntity implements DrawTrait, TimeTrait{
|
||||
private static final float hitRange = 30f;
|
||||
private static int lastSeed = 0;
|
||||
|
||||
private Array<Position> lines = new Array<>();
|
||||
private Array<Vector2> lines = new Array<>();
|
||||
private Color color = Pal.lancerLaser;
|
||||
|
||||
/** For pooling use only. Do not call directly! */
|
||||
@@ -61,10 +62,30 @@ public class Lightning extends TimedEntity implements DrawTrait, TimeTrait{
|
||||
random.setSeed(seed);
|
||||
hit.clear();
|
||||
|
||||
boolean[] bhit = {false};
|
||||
|
||||
for(int i = 0; i < length / 2; i++){
|
||||
Bullet.create(Bullets.damageLightning, l, team, x, y, 0f, 1f, 1f, dmg);
|
||||
l.lines.add(new Vector2(x + Mathf.range(3f), y + Mathf.range(3f)));
|
||||
|
||||
if(l.lines.size > 1){
|
||||
bhit[0] = false;
|
||||
Position from = l.lines.get(l.lines.size - 2);
|
||||
Position to = l.lines.get(l.lines.size - 1);
|
||||
world.raycastEach(world.toTile(from.getX()), world.toTile(from.getY()), world.toTile(to.getX()), world.toTile(to.getY()), (wx, wy) -> {
|
||||
|
||||
Tile tile = world.ltile(wx, wy);
|
||||
if(tile != null && tile.block().insulated){
|
||||
bhit[0] = true;
|
||||
//snap it instead of removing
|
||||
l.lines.get(l.lines.size -1).set(wx * tilesize, wy * tilesize);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
if(bhit[0]) break;
|
||||
}
|
||||
|
||||
rect.setSize(hitRange).setCenter(x, y);
|
||||
entities.clear();
|
||||
if(hit.size < maxChain){
|
||||
@@ -83,6 +104,7 @@ public class Lightning extends TimedEntity implements DrawTrait, TimeTrait{
|
||||
y = furthest.y;
|
||||
}else{
|
||||
rotation += random.range(20f);
|
||||
|
||||
x += Angles.trnsx(rotation, hitRange / 2f);
|
||||
y += Angles.trnsy(rotation, hitRange / 2f);
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ import io.anuke.mindustry.content.*;
|
||||
import io.anuke.mindustry.entities.*;
|
||||
import io.anuke.mindustry.entities.type.SolidEntity;
|
||||
import io.anuke.mindustry.entities.traits.*;
|
||||
import io.anuke.mindustry.game.TypeID;
|
||||
import io.anuke.mindustry.type.TypeID;
|
||||
import io.anuke.mindustry.gen.Call;
|
||||
import io.anuke.mindustry.type.Liquid;
|
||||
import io.anuke.mindustry.world.Tile;
|
||||
|
||||
@@ -6,8 +6,8 @@ import io.anuke.arc.collection.*;
|
||||
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.arc.util.ArcAnnotate.*;
|
||||
import io.anuke.arc.util.*;
|
||||
import io.anuke.mindustry.*;
|
||||
import io.anuke.mindustry.content.*;
|
||||
import io.anuke.mindustry.entities.type.*;
|
||||
@@ -104,7 +104,7 @@ public interface BuilderTrait extends Entity, TeamTrait{
|
||||
if(current.breaking){
|
||||
entity.deconstruct(unit, core, 1f / entity.buildCost * Time.delta() * getBuildPower(tile) * state.rules.buildSpeedMultiplier);
|
||||
}else{
|
||||
if(entity.construct(unit, core, 1f / entity.buildCost * Time.delta() * getBuildPower(tile) * state.rules.buildSpeedMultiplier)){
|
||||
if(entity.construct(unit, core, 1f / entity.buildCost * Time.delta() * getBuildPower(tile) * state.rules.buildSpeedMultiplier, current.hasConfig)){
|
||||
if(current.hasConfig){
|
||||
Call.onTileConfig(null, tile, current.config);
|
||||
}
|
||||
@@ -188,16 +188,30 @@ public interface BuilderTrait extends Entity, TeamTrait{
|
||||
|
||||
/** Add another build requests to the tail of the queue, if it doesn't exist there yet. */
|
||||
default void addBuildRequest(BuildRequest place){
|
||||
addBuildRequest(place, true);
|
||||
}
|
||||
|
||||
/** Add another build requests to the queue, if it doesn't exist there yet. */
|
||||
default void addBuildRequest(BuildRequest place, boolean tail){
|
||||
BuildRequest replace = null;
|
||||
for(BuildRequest request : buildQueue()){
|
||||
if(request.x == place.x && request.y == place.y){
|
||||
return;
|
||||
replace = request;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(replace != null){
|
||||
buildQueue().remove(replace);
|
||||
}
|
||||
Tile tile = world.tile(place.x, place.y);
|
||||
if(tile != null && tile.entity instanceof BuildEntity){
|
||||
place.progress = tile.<BuildEntity>entity().progress;
|
||||
}
|
||||
buildQueue().addLast(place);
|
||||
if(tail){
|
||||
buildQueue().addLast(place);
|
||||
}else{
|
||||
buildQueue().addFirst(place);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -258,14 +272,26 @@ public interface BuilderTrait extends Entity, TeamTrait{
|
||||
|
||||
/** Class for storing build requests. Can be either a place or remove request. */
|
||||
class BuildRequest{
|
||||
public final int x, y, rotation;
|
||||
public final Block block;
|
||||
public final boolean breaking;
|
||||
/** Position and rotation of this request. */
|
||||
public int x, y, rotation;
|
||||
/** Block being placed. If null, this is a breaking request.*/
|
||||
public @Nullable Block block;
|
||||
/** Whether this is a break request.*/
|
||||
public boolean breaking;
|
||||
/** Whether this request comes with a config int. If yes, any blocks placed with this request will not call playerPlaced.*/
|
||||
public boolean hasConfig;
|
||||
/** Config int. Not used unless hasConfig is true.*/
|
||||
public int config;
|
||||
/** Original position, only used in schematics.*/
|
||||
public int originalX, originalY, originalWidth, originalHeight;
|
||||
|
||||
/** Last progress.*/
|
||||
public float progress;
|
||||
public boolean initialized;
|
||||
/** Whether construction has started for this request.*/
|
||||
public boolean initialized, worldContext = true;
|
||||
|
||||
/** Visual scale. Used only for rendering.*/
|
||||
public float animScale = 0f;
|
||||
|
||||
/** This creates a build request. */
|
||||
public BuildRequest(int x, int y, int rotation, Block block){
|
||||
@@ -285,13 +311,67 @@ public interface BuilderTrait extends Entity, TeamTrait{
|
||||
this.breaking = true;
|
||||
}
|
||||
|
||||
public BuildRequest(){
|
||||
|
||||
}
|
||||
|
||||
public BuildRequest copy(){
|
||||
BuildRequest copy = new BuildRequest();
|
||||
copy.x = x;
|
||||
copy.y = y;
|
||||
copy.rotation = rotation;
|
||||
copy.block = block;
|
||||
copy.breaking = breaking;
|
||||
copy.hasConfig = hasConfig;
|
||||
copy.config = config;
|
||||
copy.originalX = originalX;
|
||||
copy.originalY = originalY;
|
||||
copy.progress = progress;
|
||||
copy.initialized = initialized;
|
||||
copy.animScale = animScale;
|
||||
return copy;
|
||||
}
|
||||
|
||||
public BuildRequest original(int x, int y, int originalWidth, int originalHeight){
|
||||
originalX = x;
|
||||
originalY = y;
|
||||
this.originalWidth = originalWidth;
|
||||
this.originalHeight = originalHeight;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Rectangle bounds(Rectangle rect){
|
||||
if(breaking){
|
||||
return rect.set(-100f, -100f, 0f, 0f);
|
||||
}else{
|
||||
return block.bounds(x, y, rect);
|
||||
}
|
||||
}
|
||||
|
||||
public BuildRequest set(int x, int y, int rotation, Block block){
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.rotation = rotation;
|
||||
this.block = block;
|
||||
this.breaking = false;
|
||||
return this;
|
||||
}
|
||||
|
||||
public float drawx(){
|
||||
return x*tilesize + block.offset();
|
||||
}
|
||||
|
||||
public float drawy(){
|
||||
return y*tilesize + block.offset();
|
||||
}
|
||||
|
||||
public BuildRequest configure(int config){
|
||||
this.config = config;
|
||||
this.hasConfig = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Tile tile(){
|
||||
public @Nullable Tile tile(){
|
||||
return world.tile(x, y);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package io.anuke.mindustry.entities.traits;
|
||||
|
||||
import io.anuke.mindustry.game.TypeID;
|
||||
import io.anuke.mindustry.type.TypeID;
|
||||
|
||||
public interface TypeTrait{
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package io.anuke.mindustry.entities.type;
|
||||
|
||||
import io.anuke.mindustry.*;
|
||||
import io.anuke.mindustry.entities.EntityGroup;
|
||||
import io.anuke.mindustry.entities.traits.Entity;
|
||||
|
||||
@@ -14,6 +15,14 @@ public abstract class BaseEntity implements Entity{
|
||||
id = lastid++;
|
||||
}
|
||||
|
||||
public int tileX(){
|
||||
return Vars.world.toTile(x);
|
||||
}
|
||||
|
||||
public int tileY(){
|
||||
return Vars.world.toTile(y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getID(){
|
||||
return id;
|
||||
|
||||
@@ -16,6 +16,8 @@ import io.anuke.mindustry.game.EventType.*;
|
||||
import io.anuke.mindustry.game.*;
|
||||
import io.anuke.mindustry.gen.*;
|
||||
import io.anuke.mindustry.type.*;
|
||||
import io.anuke.mindustry.type.TypeID;
|
||||
import io.anuke.mindustry.ui.Cicon;
|
||||
import io.anuke.mindustry.world.*;
|
||||
import io.anuke.mindustry.world.blocks.*;
|
||||
import io.anuke.mindustry.world.blocks.defense.DeflectorWall.*;
|
||||
|
||||
@@ -21,7 +21,6 @@ import io.anuke.mindustry.game.*;
|
||||
import io.anuke.mindustry.gen.*;
|
||||
import io.anuke.mindustry.graphics.*;
|
||||
import io.anuke.mindustry.input.*;
|
||||
import io.anuke.mindustry.input.InputHandler.*;
|
||||
import io.anuke.mindustry.io.*;
|
||||
import io.anuke.mindustry.net.Administration.*;
|
||||
import io.anuke.mindustry.net.*;
|
||||
@@ -51,7 +50,8 @@ public class Player extends Unit implements BuilderMinerTrait, ShooterTrait{
|
||||
public String name = "noname";
|
||||
public @Nullable
|
||||
String uuid, usid;
|
||||
public boolean isAdmin, isTransferring, isShooting, isBoosting, isMobile, isTyping;
|
||||
public boolean isAdmin, isTransferring, isShooting, isBoosting, isMobile, isTyping, isBuilding = true;
|
||||
public boolean buildWasAutoPaused = false;
|
||||
public float boostHeat, shootHeat, destructTime;
|
||||
public boolean achievedFlight;
|
||||
public Color color = new Color();
|
||||
@@ -359,7 +359,13 @@ public class Player extends Unit implements BuilderMinerTrait, ShooterTrait{
|
||||
public void drawOver(){
|
||||
if(dead) return;
|
||||
|
||||
drawMechanics();
|
||||
if(isBuilding() && isBuilding){
|
||||
if(!state.isPaused()){
|
||||
drawBuilding();
|
||||
}
|
||||
}else{
|
||||
drawMining();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -426,57 +432,17 @@ public class Player extends Unit implements BuilderMinerTrait, ShooterTrait{
|
||||
|
||||
/** Draw all current build requests. Does not draw the beam effect, only the positions. */
|
||||
public void drawBuildRequests(){
|
||||
BuildRequest last = null;
|
||||
if(!isLocal) return;
|
||||
|
||||
for(BuildRequest request : buildQueue()){
|
||||
if(request.progress > 0.01f || (buildRequest() == request && request.initialized && (dst(request.x * tilesize, request.y * tilesize) <= placeDistance || state.isEditor()))) continue;
|
||||
|
||||
request.animScale = 1f;
|
||||
if(request.breaking){
|
||||
Block block = world.ltile(request.x, request.y).block();
|
||||
|
||||
//draw removal request
|
||||
Lines.stroke(2f, Pal.removeBack);
|
||||
|
||||
float rad = Mathf.absin(Time.time(), 7f, 1f) + block.size * tilesize / 2f - 1;
|
||||
Lines.square(
|
||||
request.x * tilesize + block.offset(),
|
||||
request.y * tilesize + block.offset() - 1,
|
||||
rad);
|
||||
|
||||
Draw.color(Pal.remove);
|
||||
|
||||
Lines.square(
|
||||
request.x * tilesize + block.offset(),
|
||||
request.y * tilesize + block.offset(), rad);
|
||||
control.input.drawBreaking(request);
|
||||
}else{
|
||||
Draw.color();
|
||||
PlaceDraw draw = PlaceDraw.instance;
|
||||
|
||||
draw.scalex = 1;
|
||||
draw.scaley = 1;
|
||||
draw.rotation = request.rotation;
|
||||
|
||||
if(last == null){
|
||||
request.block.getPlaceDraw(draw, request.rotation, request.x, request.y, request.rotation);
|
||||
}else{
|
||||
request.block.getPlaceDraw(draw, request.rotation, last.x - request.x, last.y - request.y, last.rotation);
|
||||
}
|
||||
|
||||
TextureRegion region = draw.region;
|
||||
|
||||
Draw.rect(region,
|
||||
request.x * tilesize + request.block.offset(), request.y * tilesize + request.block.offset(),
|
||||
region.getWidth() * 1f * Draw.scl * draw.scalex,
|
||||
region.getHeight() * 1f * Draw.scl * draw.scaley, request.block.rotate ? draw.rotation * 90 : 0);
|
||||
|
||||
Draw.color(Pal.accent);
|
||||
for(int i = 0; i < 4; i++){
|
||||
Point2 p = Geometry.d8edge[i];
|
||||
float offset = -Math.max(request.block.size - 1, 0) / 2f * tilesize;
|
||||
Draw.rect("block-select", request.x * tilesize + request.block.offset() + offset * p.x, request.y * tilesize + request.block.offset() + offset * p.y, i * 90);
|
||||
}
|
||||
Draw.color();
|
||||
|
||||
last = request;
|
||||
request.block.drawRequest(request, control.input.allRequests(),
|
||||
Build.validPlace(getTeam(), request.x, request.y, request.block, request.rotation) || control.input.requestMatches(request));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -487,6 +453,18 @@ public class Player extends Unit implements BuilderMinerTrait, ShooterTrait{
|
||||
|
||||
//region update methods
|
||||
|
||||
@Override
|
||||
public void updateMechanics(){
|
||||
if(isBuilding){
|
||||
updateBuilding();
|
||||
}
|
||||
|
||||
//mine only when not building
|
||||
if(buildRequest() == null || !isBuilding){
|
||||
updateMining();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(){
|
||||
hitTime -= Time.delta();
|
||||
@@ -519,7 +497,7 @@ public class Player extends Unit implements BuilderMinerTrait, ShooterTrait{
|
||||
}
|
||||
|
||||
BuildRequest request = buildRequest();
|
||||
if(isBuilding() && request.tile() != null && (request.tile().withinDst(x, y, placeDistance) || state.isEditor())){
|
||||
if(isBuilding() && isBuilding && request.tile() != null && (request.tile().withinDst(x, y, placeDistance) || state.isEditor())){
|
||||
loops.play(Sounds.build, request.tile(), 0.75f);
|
||||
}
|
||||
|
||||
@@ -611,7 +589,7 @@ public class Player extends Unit implements BuilderMinerTrait, ShooterTrait{
|
||||
|
||||
float xa = Core.input.axis(Binding.move_x);
|
||||
float ya = Core.input.axis(Binding.move_y);
|
||||
if(!Core.input.keyDown(Binding.gridMode) && !(Core.scene.getKeyboardFocus() instanceof TextField)){
|
||||
if(!(Core.scene.getKeyboardFocus() instanceof TextField)){
|
||||
movement.y += ya * speed;
|
||||
movement.x += xa * speed;
|
||||
}
|
||||
@@ -819,9 +797,11 @@ public class Player extends Unit implements BuilderMinerTrait, ShooterTrait{
|
||||
placeQueue.clear();
|
||||
dead = true;
|
||||
lastText = null;
|
||||
isBuilding = true;
|
||||
textFadeTime = 0f;
|
||||
target = null;
|
||||
moveTarget = null;
|
||||
isShooting = isBoosting = isTransferring = isTyping = false;
|
||||
spawner = lastSpawner = null;
|
||||
health = maxHealth();
|
||||
mining = null;
|
||||
@@ -912,7 +892,7 @@ public class Player extends Unit implements BuilderMinerTrait, ShooterTrait{
|
||||
public void write(DataOutput buffer) throws IOException{
|
||||
super.writeSave(buffer, !isLocal);
|
||||
TypeIO.writeStringData(buffer, name);
|
||||
buffer.writeByte(Pack.byteValue(isAdmin) | (Pack.byteValue(dead) << 1) | (Pack.byteValue(isBoosting) << 2) | (Pack.byteValue(isTyping) << 3));
|
||||
buffer.writeByte(Pack.byteValue(isAdmin) | (Pack.byteValue(dead) << 1) | (Pack.byteValue(isBoosting) << 2) | (Pack.byteValue(isTyping) << 3)| (Pack.byteValue(isBuilding) << 4));
|
||||
buffer.writeInt(Color.rgba8888(color));
|
||||
buffer.writeByte(mech.id);
|
||||
buffer.writeInt(mining == null ? noSpawner : mining.pos());
|
||||
@@ -934,6 +914,7 @@ public class Player extends Unit implements BuilderMinerTrait, ShooterTrait{
|
||||
dead = (bools & 2) != 0;
|
||||
boolean boosting = (bools & 4) != 0;
|
||||
isTyping = (bools & 8) != 0;
|
||||
boolean building = (bools & 16) != 0;
|
||||
color.set(buffer.readInt());
|
||||
mech = content.getByID(ContentType.mech, buffer.readByte());
|
||||
int mine = buffer.readInt();
|
||||
@@ -952,6 +933,7 @@ public class Player extends Unit implements BuilderMinerTrait, ShooterTrait{
|
||||
velocity.y = lastvy;
|
||||
}else{
|
||||
mining = world.tile(mine);
|
||||
isBuilding = building;
|
||||
isBoosting = boosting;
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ import io.anuke.mindustry.graphics.*;
|
||||
import io.anuke.mindustry.net.*;
|
||||
import io.anuke.mindustry.type.*;
|
||||
import io.anuke.mindustry.ui.*;
|
||||
import io.anuke.mindustry.ui.Cicon;
|
||||
import io.anuke.mindustry.world.*;
|
||||
import io.anuke.mindustry.world.blocks.*;
|
||||
|
||||
|
||||
@@ -151,7 +151,13 @@ public class FlyingUnit extends BaseUnit{
|
||||
}
|
||||
|
||||
public void drawWeapons(){
|
||||
|
||||
for(int i : Mathf.signs){
|
||||
float tra = rotation - 90, trY = -type.weapon.getRecoil(this, i > 0) + type.weaponOffsetY;
|
||||
float w = -i * type.weapon.region.getWidth() * Draw.scl;
|
||||
Draw.rect(type.weapon.region,
|
||||
x + Angles.trnsx(tra, getWeapon().width * i, trY),
|
||||
y + Angles.trnsy(tra, getWeapon().width * i, trY), w, type.weapon.region.getHeight() * Draw.scl, rotation - 90);
|
||||
}
|
||||
}
|
||||
|
||||
public void drawEngine(){
|
||||
|
||||
@@ -2,6 +2,7 @@ package io.anuke.mindustry.game;
|
||||
|
||||
import io.anuke.arc.util.ArcAnnotate.*;
|
||||
import io.anuke.mindustry.core.GameState.State;
|
||||
import io.anuke.mindustry.ctype.UnlockableContent;
|
||||
import io.anuke.mindustry.entities.traits.BuilderTrait;
|
||||
import io.anuke.mindustry.entities.type.*;
|
||||
import io.anuke.mindustry.entities.units.*;
|
||||
@@ -61,11 +62,13 @@ public class EventType{
|
||||
|
||||
/** Called when a zone's requirements are met. */
|
||||
public static class ZoneRequireCompleteEvent{
|
||||
public final Zone zone, required;
|
||||
public final Zone zoneMet, zoneForMet;
|
||||
public final Objective objective;
|
||||
|
||||
public ZoneRequireCompleteEvent(Zone zone, Zone required){
|
||||
this.zone = zone;
|
||||
this.required = required;
|
||||
public ZoneRequireCompleteEvent(Zone zoneMet, Zone zoneForMet, Objective objective){
|
||||
this.zoneMet = zoneMet;
|
||||
this.zoneForMet = zoneForMet;
|
||||
this.objective = objective;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -130,7 +133,41 @@ public class EventType{
|
||||
|
||||
/** Called when a player deposits items to a block.*/
|
||||
public static class DepositEvent{
|
||||
public final Tile tile;
|
||||
public final Player player;
|
||||
public final Item item;
|
||||
public final int amount;
|
||||
|
||||
public DepositEvent(Tile tile, Player player, Item item, int amount){
|
||||
this.tile = tile;
|
||||
this.player = player;
|
||||
this.item = item;
|
||||
this.amount = amount;
|
||||
}
|
||||
}
|
||||
|
||||
/** Called when the player taps a block. */
|
||||
public static class TapEvent{
|
||||
public final Tile tile;
|
||||
public final Player player;
|
||||
|
||||
public TapEvent(Tile tile, Player player){
|
||||
this.tile = tile;
|
||||
this.player = player;
|
||||
}
|
||||
}
|
||||
|
||||
/** Called when the player sets a specific block. */
|
||||
public static class TapConfigEvent{
|
||||
public final Tile tile;
|
||||
public final Player player;
|
||||
public final int value;
|
||||
|
||||
public TapConfigEvent(Tile tile, Player player, int value){
|
||||
this.tile = tile;
|
||||
this.player = player;
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
|
||||
public static class GameOverEvent{
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package io.anuke.mindustry.game;
|
||||
|
||||
import io.anuke.arc.*;
|
||||
import io.anuke.arc.function.*;
|
||||
import io.anuke.arc.func.*;
|
||||
import io.anuke.mindustry.maps.*;
|
||||
|
||||
import static io.anuke.mindustry.Vars.waveTeam;
|
||||
@@ -44,25 +44,25 @@ public enum Gamemode{
|
||||
rules.respawnTime = 0f;
|
||||
});
|
||||
|
||||
private final Consumer<Rules> rules;
|
||||
private final Predicate<Map> validator;
|
||||
private final Cons<Rules> rules;
|
||||
private final Boolf<Map> validator;
|
||||
|
||||
public final boolean hidden;
|
||||
public final static Gamemode[] all = values();
|
||||
|
||||
Gamemode(Consumer<Rules> rules){
|
||||
Gamemode(Cons<Rules> rules){
|
||||
this(false, rules);
|
||||
}
|
||||
|
||||
Gamemode(boolean hidden, Consumer<Rules> rules){
|
||||
Gamemode(boolean hidden, Cons<Rules> rules){
|
||||
this(hidden, rules, m -> true);
|
||||
}
|
||||
|
||||
Gamemode(Consumer<Rules> rules, Predicate<Map> validator){
|
||||
Gamemode(Cons<Rules> rules, Boolf<Map> validator){
|
||||
this(false, rules, validator);
|
||||
}
|
||||
|
||||
Gamemode(boolean hidden, Consumer<Rules> rules, Predicate<Map> validator){
|
||||
Gamemode(boolean hidden, Cons<Rules> rules, Boolf<Map> validator){
|
||||
this.rules = rules;
|
||||
this.hidden = hidden;
|
||||
this.validator = validator;
|
||||
@@ -84,13 +84,13 @@ public enum Gamemode{
|
||||
|
||||
/** Applies this preset to this ruleset. */
|
||||
public Rules apply(Rules in){
|
||||
rules.accept(in);
|
||||
rules.get(in);
|
||||
return in;
|
||||
}
|
||||
|
||||
/** @return whether this mode can be played on the specified map. */
|
||||
public boolean valid(Map map){
|
||||
return validator.test(map);
|
||||
return validator.get(map);
|
||||
}
|
||||
|
||||
public String description(){
|
||||
|
||||
@@ -6,6 +6,7 @@ import io.anuke.arc.files.*;
|
||||
import io.anuke.arc.util.io.*;
|
||||
import io.anuke.mindustry.*;
|
||||
import io.anuke.mindustry.content.*;
|
||||
import io.anuke.mindustry.ctype.UnlockableContent;
|
||||
import io.anuke.mindustry.game.EventType.*;
|
||||
import io.anuke.mindustry.type.*;
|
||||
|
||||
@@ -39,10 +40,14 @@ public class GlobalData{
|
||||
files.add(Core.settings.getSettingsFile());
|
||||
files.addAll(customMapDirectory.list());
|
||||
files.addAll(saveDirectory.list());
|
||||
files.addAll(screenshotDirectory.list());
|
||||
files.addAll(modDirectory.list());
|
||||
files.addAll(schematicDirectory.list());
|
||||
String base = Core.settings.getDataDirectory().path();
|
||||
|
||||
try(OutputStream fos = file.write(false, 2048); ZipOutputStream zos = new ZipOutputStream(fos)){
|
||||
for(FileHandle add : files){
|
||||
if(add.isDirectory()) continue;
|
||||
zos.putNextEntry(new ZipEntry(add.path().substring(base.length())));
|
||||
Streams.copyStream(add.read(), zos);
|
||||
zos.closeEntry();
|
||||
@@ -61,14 +66,8 @@ public class GlobalData{
|
||||
throw new IllegalArgumentException("Not valid save data.");
|
||||
}
|
||||
|
||||
//purge existing data
|
||||
for(FileHandle f : base.list()){
|
||||
if(f.isDirectory()){
|
||||
f.deleteDirectory();
|
||||
}else if(!f.name().equals("zipdata.zip")){
|
||||
f.delete();
|
||||
}
|
||||
}
|
||||
//purge existing tmp data, keep everything else
|
||||
tmpDirectory.deleteDirectory();
|
||||
|
||||
zipped.walk(f -> f.copyTo(base.child(f.path())));
|
||||
dest.delete();
|
||||
@@ -91,12 +90,17 @@ public class GlobalData{
|
||||
state.stats.itemsDelivered.getAndIncrement(item, 0, amount);
|
||||
}
|
||||
|
||||
public boolean hasItems(Array<ItemStack> stacks){
|
||||
return !stacks.contains(s -> items.get(s.item, 0) < s.amount);
|
||||
}
|
||||
|
||||
public boolean hasItems(ItemStack[] stacks){
|
||||
for(ItemStack stack : stacks){
|
||||
if(items.get(stack.item, 0) < stack.amount){
|
||||
if(!has(stack.item, stack.amount)){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -107,6 +111,13 @@ public class GlobalData{
|
||||
modified = true;
|
||||
}
|
||||
|
||||
public void removeItems(Array<ItemStack> stacks){
|
||||
for(ItemStack stack : stacks){
|
||||
items.getAndIncrement(stack.item, 0, -stack.amount);
|
||||
}
|
||||
modified = true;
|
||||
}
|
||||
|
||||
public boolean has(Item item, int amount){
|
||||
return items.get(item, 0) >= amount;
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ public class LoopControl{
|
||||
}
|
||||
}else{
|
||||
if(data.curVolume <= 0.01f){
|
||||
sound.stop(data.soundID);
|
||||
sound.stop();
|
||||
data.soundID = -1;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -22,16 +22,12 @@ public class MusicControl{
|
||||
public Array<Music> darkMusic = Array.with();
|
||||
private Music lastRandomPlayed;
|
||||
private Interval timer = new Interval();
|
||||
private @Nullable
|
||||
Music current;
|
||||
private @Nullable Music current;
|
||||
private float fade;
|
||||
private boolean silenced;
|
||||
|
||||
public MusicControl(){
|
||||
Events.on(ClientLoadEvent.class, e -> {
|
||||
ambientMusic = Array.with(Musics.game1, Musics.game3, Musics.game4, Musics.game6);
|
||||
darkMusic = Array.with(Musics.game2, Musics.game5, Musics.game7);
|
||||
});
|
||||
Events.on(ClientLoadEvent.class, e -> reload());
|
||||
|
||||
//only run music 10 seconds after a wave spawns
|
||||
Events.on(WaveEvent.class, e -> Time.run(60f * 10f, () -> {
|
||||
@@ -41,6 +37,13 @@ public class MusicControl{
|
||||
}));
|
||||
}
|
||||
|
||||
private void reload(){
|
||||
current = null;
|
||||
fade = 0f;
|
||||
ambientMusic = Array.with(Musics.game1, Musics.game3, Musics.game4, Musics.game6);
|
||||
darkMusic = Array.with(Musics.game2, Musics.game5, Musics.game7);
|
||||
}
|
||||
|
||||
/** Update and play the right music track.*/
|
||||
public void update(){
|
||||
if(state.is(State.menu)){
|
||||
|
||||
27
core/src/io/anuke/mindustry/game/Objective.java
Normal file
27
core/src/io/anuke/mindustry/game/Objective.java
Normal file
@@ -0,0 +1,27 @@
|
||||
package io.anuke.mindustry.game;
|
||||
|
||||
import io.anuke.arc.scene.ui.layout.*;
|
||||
import io.anuke.arc.util.ArcAnnotate.*;
|
||||
import io.anuke.mindustry.game.Objectives.*;
|
||||
import io.anuke.mindustry.type.*;
|
||||
|
||||
/** Defines a specific objective for a game. */
|
||||
public interface Objective{
|
||||
|
||||
/** @return whether this objective is met. */
|
||||
boolean complete();
|
||||
|
||||
/** @return the string displayed when this objective is completed, in imperative form.
|
||||
* e.g. when the objective is 'complete 10 waves', this would display "complete 10 waves".
|
||||
* If this objective should not be displayed, should return null.*/
|
||||
@Nullable String display();
|
||||
|
||||
/** Build a display for this zone requirement.*/
|
||||
default void build(Table table){
|
||||
|
||||
}
|
||||
|
||||
default Zone zone(){
|
||||
return this instanceof ZoneObjective ? ((ZoneObjective)this).zone : null;
|
||||
}
|
||||
}
|
||||
96
core/src/io/anuke/mindustry/game/Objectives.java
Normal file
96
core/src/io/anuke/mindustry/game/Objectives.java
Normal file
@@ -0,0 +1,96 @@
|
||||
package io.anuke.mindustry.game;
|
||||
|
||||
import io.anuke.arc.*;
|
||||
import io.anuke.arc.util.ArcAnnotate.*;
|
||||
import io.anuke.mindustry.type.*;
|
||||
import io.anuke.mindustry.world.*;
|
||||
|
||||
/** Holds objective classes. */
|
||||
public class Objectives{
|
||||
|
||||
//TODO
|
||||
public static class Wave implements Objective{
|
||||
public int wave;
|
||||
|
||||
public Wave(int wave){
|
||||
this.wave = wave;
|
||||
}
|
||||
|
||||
protected Wave(){}
|
||||
|
||||
@Override
|
||||
public boolean complete(){
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String display(){
|
||||
//TODO
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Unlock implements Objective{
|
||||
public @NonNull Block block;
|
||||
|
||||
public Unlock(Block block){
|
||||
this.block = block;
|
||||
}
|
||||
|
||||
protected Unlock(){}
|
||||
|
||||
@Override
|
||||
public boolean complete(){
|
||||
return block.unlocked();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String display(){
|
||||
return Core.bundle.format("requirement.unlock", block.localizedName);
|
||||
}
|
||||
}
|
||||
|
||||
public static class ZoneWave extends ZoneObjective{
|
||||
public int wave;
|
||||
|
||||
public ZoneWave(Zone zone, int wave){
|
||||
this.zone = zone;
|
||||
this.wave = wave;
|
||||
}
|
||||
|
||||
protected ZoneWave(){}
|
||||
|
||||
@Override
|
||||
public boolean complete(){
|
||||
return zone.bestWave() >= wave;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String display(){
|
||||
return Core.bundle.format("requirement.wave", wave, zone.localizedName);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Launched extends ZoneObjective{
|
||||
|
||||
public Launched(Zone zone){
|
||||
this.zone = zone;
|
||||
}
|
||||
|
||||
protected Launched(){}
|
||||
|
||||
@Override
|
||||
public boolean complete(){
|
||||
return zone.hasLaunched();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String display(){
|
||||
return Core.bundle.format("requirement.core", zone.localizedName);
|
||||
}
|
||||
}
|
||||
|
||||
public abstract static class ZoneObjective implements Objective{
|
||||
public @NonNull Zone zone;
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
package io.anuke.mindustry.game;
|
||||
|
||||
import io.anuke.annotations.Annotations.Serialize;
|
||||
import io.anuke.arc.collection.Array;
|
||||
import io.anuke.mindustry.content.Items;
|
||||
import io.anuke.mindustry.io.JsonIO;
|
||||
import io.anuke.mindustry.type.ItemStack;
|
||||
import io.anuke.mindustry.type.Zone;
|
||||
import io.anuke.annotations.Annotations.*;
|
||||
import io.anuke.arc.collection.*;
|
||||
import io.anuke.mindustry.content.*;
|
||||
import io.anuke.mindustry.io.*;
|
||||
import io.anuke.mindustry.type.*;
|
||||
import io.anuke.mindustry.world.*;
|
||||
|
||||
/**
|
||||
* Defines current rules on how the game should function.
|
||||
@@ -69,6 +69,8 @@ public class Rules{
|
||||
public boolean tutorial = false;
|
||||
/** Starting items put in cores */
|
||||
public Array<ItemStack> loadout = Array.with(ItemStack.with(Items.copper, 100));
|
||||
/** Blocks that cannot be placed. */
|
||||
public ObjectSet<Block> bannedBlocks = new ObjectSet<>();
|
||||
|
||||
/** Copies this ruleset exactly. Not very efficient at all, do not use often. */
|
||||
public Rules copy(){
|
||||
|
||||
@@ -22,13 +22,12 @@ import java.util.*;
|
||||
import static io.anuke.mindustry.Vars.*;
|
||||
|
||||
public class Saves{
|
||||
private int nextSlot;
|
||||
private Array<SaveSlot> saves = new Array<>();
|
||||
private IntMap<SaveSlot> saveMap = new IntMap<>();
|
||||
private SaveSlot current;
|
||||
private AsyncExecutor previewExecutor = new AsyncExecutor(1);
|
||||
private boolean saving;
|
||||
private float time;
|
||||
private FileHandle zoneFile;
|
||||
|
||||
private long totalPlaytime;
|
||||
private long lastTimestamp;
|
||||
@@ -47,16 +46,13 @@ public class Saves{
|
||||
|
||||
public void load(){
|
||||
saves.clear();
|
||||
IntArray slots = Core.settings.getObject("save-slots", IntArray.class, IntArray::new);
|
||||
zoneFile = saveDirectory.child("-1.msav");
|
||||
|
||||
for(int i = 0; i < slots.size; i++){
|
||||
int index = slots.get(i);
|
||||
if(SaveIO.isSaveValid(index)){
|
||||
SaveSlot slot = new SaveSlot(index);
|
||||
for(FileHandle file : saveDirectory.list()){
|
||||
if(!file.name().contains("backup") && SaveIO.isSaveValid(file)){
|
||||
SaveSlot slot = new SaveSlot(file);
|
||||
saves.add(slot);
|
||||
saveMap.put(slot.index, slot);
|
||||
slot.meta = SaveIO.getMeta(index);
|
||||
nextSlot = Math.max(index + 1, nextSlot);
|
||||
slot.meta = SaveIO.getMeta(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -110,73 +106,63 @@ public class Saves{
|
||||
}
|
||||
|
||||
public void zoneSave(){
|
||||
SaveSlot slot = new SaveSlot(-1);
|
||||
SaveSlot slot = new SaveSlot(zoneFile);
|
||||
slot.setName("zone");
|
||||
saves.remove(s -> s.index == -1);
|
||||
saves.remove(s -> s.file.equals(zoneFile));
|
||||
saves.add(slot);
|
||||
saveMap.put(slot.index, slot);
|
||||
slot.save();
|
||||
saveSlots();
|
||||
}
|
||||
|
||||
public SaveSlot addSave(String name){
|
||||
SaveSlot slot = new SaveSlot(nextSlot);
|
||||
nextSlot++;
|
||||
SaveSlot slot = new SaveSlot(getNextSlotFile());
|
||||
slot.setName(name);
|
||||
saves.add(slot);
|
||||
saveMap.put(slot.index, slot);
|
||||
slot.save();
|
||||
saveSlots();
|
||||
return slot;
|
||||
}
|
||||
|
||||
public SaveSlot importSave(FileHandle file) throws IOException{
|
||||
SaveSlot slot = new SaveSlot(nextSlot);
|
||||
SaveSlot slot = new SaveSlot(getNextSlotFile());
|
||||
slot.importFile(file);
|
||||
nextSlot++;
|
||||
slot.setName(file.nameWithoutExtension());
|
||||
saves.add(slot);
|
||||
saveMap.put(slot.index, slot);
|
||||
slot.meta = SaveIO.getMeta(slot.index);
|
||||
slot.meta = SaveIO.getMeta(slot.file);
|
||||
current = slot;
|
||||
saveSlots();
|
||||
return slot;
|
||||
}
|
||||
|
||||
public SaveSlot getZoneSlot(){
|
||||
SaveSlot slot = getByID(-1);
|
||||
SaveSlot slot = getSaveSlots().find(s -> s.file.equals(zoneFile));
|
||||
return slot == null || slot.getZone() == null ? null : slot;
|
||||
}
|
||||
|
||||
public SaveSlot getByID(int id){
|
||||
return saveMap.get(id);
|
||||
public FileHandle getNextSlotFile(){
|
||||
int i = 0;
|
||||
FileHandle file;
|
||||
while((file = saveDirectory.child(i + "." + saveExtension)).exists()){
|
||||
i ++;
|
||||
}
|
||||
return file;
|
||||
}
|
||||
|
||||
public Array<SaveSlot> getSaveSlots(){
|
||||
return saves;
|
||||
}
|
||||
|
||||
private void saveSlots(){
|
||||
IntArray result = new IntArray(saves.size);
|
||||
for(int i = 0; i < saves.size; i++) result.add(saves.get(i).index);
|
||||
|
||||
Core.settings.putObject("save-slots", result);
|
||||
Core.settings.save();
|
||||
}
|
||||
|
||||
public class SaveSlot{
|
||||
public final int index;
|
||||
//public final int index;
|
||||
public final FileHandle file;
|
||||
boolean requestedPreview;
|
||||
SaveMeta meta;
|
||||
|
||||
public SaveSlot(int index){
|
||||
this.index = index;
|
||||
public SaveSlot(FileHandle file){
|
||||
this.file = file;
|
||||
}
|
||||
|
||||
public void load() throws SaveException{
|
||||
try{
|
||||
SaveIO.loadFromSlot(index);
|
||||
meta = SaveIO.getMeta(index);
|
||||
SaveIO.load(file);
|
||||
meta = SaveIO.getMeta(file);
|
||||
current = this;
|
||||
totalPlaytime = meta.timePlayed;
|
||||
savePreview();
|
||||
@@ -190,8 +176,8 @@ public class Saves{
|
||||
long prev = totalPlaytime;
|
||||
totalPlaytime = time;
|
||||
|
||||
SaveIO.saveToSlot(index);
|
||||
meta = SaveIO.getMeta(index);
|
||||
SaveIO.save(file);
|
||||
meta = SaveIO.getMeta(file);
|
||||
if(!state.is(State.menu)){
|
||||
current = this;
|
||||
}
|
||||
@@ -226,8 +212,12 @@ public class Saves{
|
||||
return null;
|
||||
}
|
||||
|
||||
private String index(){
|
||||
return file.nameWithoutExtension();
|
||||
}
|
||||
|
||||
private FileHandle previewFile(){
|
||||
return mapPreviewDirectory.child("save_slot_" + index + ".png");
|
||||
return mapPreviewDirectory.child("save_slot_" + index() + ".png");
|
||||
}
|
||||
|
||||
private FileHandle loadPreviewFile(){
|
||||
@@ -266,11 +256,11 @@ public class Saves{
|
||||
}
|
||||
|
||||
public String getName(){
|
||||
return Core.settings.getString("save-" + index + "-name", "untitled");
|
||||
return Core.settings.getString("save-" + index() + "-name", "untitled");
|
||||
}
|
||||
|
||||
public void setName(String name){
|
||||
Core.settings.put("save-" + index + "-name", name);
|
||||
Core.settings.put("save-" + index() + "-name", name);
|
||||
Core.settings.save();
|
||||
}
|
||||
|
||||
@@ -295,34 +285,33 @@ public class Saves{
|
||||
}
|
||||
|
||||
public boolean isAutosave(){
|
||||
return Core.settings.getBool("save-" + index + "-autosave", true);
|
||||
return Core.settings.getBool("save-" + index() + "-autosave", true);
|
||||
}
|
||||
|
||||
public void setAutosave(boolean save){
|
||||
Core.settings.put("save-" + index + "-autosave", save);
|
||||
Core.settings.put("save-" + index() + "-autosave", save);
|
||||
Core.settings.save();
|
||||
}
|
||||
|
||||
public void importFile(FileHandle file) throws IOException{
|
||||
public void importFile(FileHandle from) throws IOException{
|
||||
try{
|
||||
file.copyTo(SaveIO.fileFor(index));
|
||||
from.copyTo(file);
|
||||
}catch(Exception e){
|
||||
throw new IOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void exportFile(FileHandle file) throws IOException{
|
||||
public void exportFile(FileHandle to) throws IOException{
|
||||
try{
|
||||
SaveIO.fileFor(index).copyTo(file);
|
||||
file.copyTo(to);
|
||||
}catch(Exception e){
|
||||
throw new IOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void delete(){
|
||||
SaveIO.fileFor(index).delete();
|
||||
file.delete();
|
||||
saves.removeValue(this, true);
|
||||
saveMap.remove(index);
|
||||
if(this == current){
|
||||
current = null;
|
||||
}
|
||||
@@ -330,8 +319,6 @@ public class Saves{
|
||||
if(Core.assets.isLoaded(loadPreviewFile().path())){
|
||||
Core.assets.unload(loadPreviewFile().path());
|
||||
}
|
||||
|
||||
saveSlots();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
126
core/src/io/anuke/mindustry/game/Schematic.java
Normal file
126
core/src/io/anuke/mindustry/game/Schematic.java
Normal file
@@ -0,0 +1,126 @@
|
||||
package io.anuke.mindustry.game;
|
||||
|
||||
import io.anuke.arc.collection.*;
|
||||
import io.anuke.arc.collection.IntIntMap.*;
|
||||
import io.anuke.arc.files.*;
|
||||
import io.anuke.arc.util.ArcAnnotate.*;
|
||||
import io.anuke.mindustry.*;
|
||||
import io.anuke.mindustry.type.*;
|
||||
import io.anuke.mindustry.world.*;
|
||||
import io.anuke.mindustry.world.blocks.storage.*;
|
||||
|
||||
import static io.anuke.mindustry.Vars.*;
|
||||
|
||||
public class Schematic implements Publishable, Comparable<Schematic>{
|
||||
public final Array<Stile> tiles;
|
||||
public StringMap tags;
|
||||
public int width, height;
|
||||
public @Nullable FileHandle file;
|
||||
|
||||
public Schematic(Array<Stile> tiles, StringMap tags, int width, int height){
|
||||
this.tiles = tiles;
|
||||
this.tags = tags;
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
}
|
||||
|
||||
public Array<ItemStack> requirements(){
|
||||
IntIntMap amounts = new IntIntMap();
|
||||
|
||||
tiles.each(t -> {
|
||||
for(ItemStack stack : t.block.requirements){
|
||||
amounts.getAndIncrement(stack.item.id, 0, stack.amount);
|
||||
}
|
||||
});
|
||||
Array<ItemStack> stacks = new Array<>();
|
||||
for(Entry ent : amounts.entries()){
|
||||
stacks.add(new ItemStack(Vars.content.item(ent.key), ent.value));
|
||||
}
|
||||
stacks.sort();
|
||||
return stacks;
|
||||
}
|
||||
|
||||
public boolean hasCore(){
|
||||
return tiles.contains(s -> s.block instanceof CoreBlock);
|
||||
}
|
||||
|
||||
public @NonNull CoreBlock findCore(){
|
||||
CoreBlock block = (CoreBlock)tiles.find(s -> s.block instanceof CoreBlock).block;
|
||||
if(block == null) throw new IllegalArgumentException("Schematic is missing a core!");
|
||||
return block;
|
||||
}
|
||||
|
||||
public String name(){
|
||||
return tags.get("name", "unknown");
|
||||
}
|
||||
|
||||
public void save(){
|
||||
schematics.saveChanges(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSteamID(){
|
||||
return tags.get("steamid");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addSteamID(String id){
|
||||
tags.put("steamid", id);
|
||||
save();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeSteamID(){
|
||||
tags.remove("steamid");
|
||||
save();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String steamTitle(){
|
||||
return name();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String steamDescription(){
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String steamTag(){
|
||||
return "schematic";
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileHandle createSteamFolder(String id){
|
||||
FileHandle directory = tmpDirectory.child("schematic_" + id).child("schematic." + schematicExtension);
|
||||
file.copyTo(directory);
|
||||
return directory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileHandle createSteamPreview(String id){
|
||||
FileHandle preview = tmpDirectory.child("schematic_preview_" + id + ".png");
|
||||
schematics.savePreview(this, preview);
|
||||
return preview;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(Schematic schematic){
|
||||
return name().compareTo(schematic.name());
|
||||
}
|
||||
|
||||
public static class Stile{
|
||||
public @NonNull Block block;
|
||||
public short x, y;
|
||||
public int config;
|
||||
public byte rotation;
|
||||
|
||||
public Stile(Block block, int x, int y, int config, byte rotation){
|
||||
this.block = block;
|
||||
this.x = (short)x;
|
||||
this.y = (short)y;
|
||||
this.config = config;
|
||||
this.rotation = rotation;
|
||||
}
|
||||
}
|
||||
}
|
||||
456
core/src/io/anuke/mindustry/game/Schematics.java
Normal file
456
core/src/io/anuke/mindustry/game/Schematics.java
Normal file
@@ -0,0 +1,456 @@
|
||||
package io.anuke.mindustry.game;
|
||||
|
||||
import io.anuke.arc.*;
|
||||
import io.anuke.arc.assets.*;
|
||||
import io.anuke.arc.collection.*;
|
||||
import io.anuke.arc.files.*;
|
||||
import io.anuke.arc.graphics.*;
|
||||
import io.anuke.arc.graphics.g2d.*;
|
||||
import io.anuke.arc.graphics.glutils.*;
|
||||
import io.anuke.arc.util.*;
|
||||
import io.anuke.arc.util.io.Streams.*;
|
||||
import io.anuke.arc.util.serialization.*;
|
||||
import io.anuke.mindustry.*;
|
||||
import io.anuke.mindustry.content.*;
|
||||
import io.anuke.mindustry.entities.traits.BuilderTrait.*;
|
||||
import io.anuke.mindustry.game.EventType.*;
|
||||
import io.anuke.mindustry.game.Schematic.*;
|
||||
import io.anuke.mindustry.input.*;
|
||||
import io.anuke.mindustry.input.Placement.*;
|
||||
import io.anuke.mindustry.type.*;
|
||||
import io.anuke.mindustry.world.*;
|
||||
import io.anuke.mindustry.world.blocks.*;
|
||||
import io.anuke.mindustry.world.blocks.production.*;
|
||||
import io.anuke.mindustry.world.blocks.storage.*;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.zip.*;
|
||||
|
||||
import static io.anuke.mindustry.Vars.*;
|
||||
|
||||
/** Handles schematics.*/
|
||||
public class Schematics implements Loadable{
|
||||
public static final String base64Header = "bXNjaAB";
|
||||
|
||||
private static final byte[] header = {'m', 's', 'c', 'h'};
|
||||
private static final byte version = 0;
|
||||
|
||||
private static final int padding = 2;
|
||||
private static final int maxPreviewsMobile = 32;
|
||||
private static final int resolution = 32;
|
||||
|
||||
private OptimizedByteArrayOutputStream out = new OptimizedByteArrayOutputStream(1024);
|
||||
private Array<Schematic> all = new Array<>();
|
||||
private OrderedMap<Schematic, FrameBuffer> previews = new OrderedMap<>();
|
||||
private FrameBuffer shadowBuffer;
|
||||
private long lastClearTime;
|
||||
|
||||
public Schematics(){
|
||||
Events.on(DisposeEvent.class, e -> {
|
||||
previews.each((schem, m) -> m.dispose());
|
||||
previews.clear();
|
||||
shadowBuffer.dispose();
|
||||
});
|
||||
|
||||
Events.on(ContentReloadEvent.class, event -> {
|
||||
previews.each((schem, m) -> m.dispose());
|
||||
previews.clear();
|
||||
load();
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadSync(){
|
||||
load();
|
||||
}
|
||||
|
||||
/** Load all schematics in the folder immediately.*/
|
||||
public void load(){
|
||||
all.clear();
|
||||
|
||||
for(FileHandle file : schematicDirectory.list()){
|
||||
loadFile(file);
|
||||
}
|
||||
|
||||
platform.getWorkshopContent(Schematic.class).each(this::loadFile);
|
||||
|
||||
all.sort();
|
||||
|
||||
if(shadowBuffer == null){
|
||||
Core.app.post(() -> shadowBuffer = new FrameBuffer(maxSchematicSize + padding + 8, maxSchematicSize + padding + 8));
|
||||
}
|
||||
}
|
||||
|
||||
public void overwrite(Schematic target, Schematic newSchematic){
|
||||
if(previews.containsKey(target)){
|
||||
previews.get(target).dispose();
|
||||
previews.remove(target);
|
||||
}
|
||||
|
||||
target.tiles.clear();
|
||||
target.tiles.addAll(newSchematic.tiles);
|
||||
target.width = newSchematic.width;
|
||||
target.height = newSchematic.height;
|
||||
newSchematic.tags.putAll(target.tags);
|
||||
newSchematic.file = target.file;
|
||||
|
||||
try{
|
||||
write(newSchematic, target.file);
|
||||
}catch(Exception e){
|
||||
Log.err(e);
|
||||
ui.showException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void loadFile(FileHandle file){
|
||||
if(!file.extension().equals(schematicExtension)) return;
|
||||
|
||||
try{
|
||||
Schematic s = read(file);
|
||||
all.add(s);
|
||||
|
||||
//external file from workshop
|
||||
if(!s.file.parent().equals(schematicDirectory)){
|
||||
s.tags.put("steamid", s.file.parent().name());
|
||||
}
|
||||
}catch(IOException e){
|
||||
Log.err(e);
|
||||
}
|
||||
}
|
||||
|
||||
public Array<Schematic> all(){
|
||||
return all;
|
||||
}
|
||||
|
||||
public void saveChanges(Schematic s){
|
||||
if(s.file != null){
|
||||
try{
|
||||
write(s, s.file);
|
||||
}catch(Exception e){
|
||||
ui.showException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void savePreview(Schematic schematic, FileHandle file){
|
||||
FrameBuffer buffer = getBuffer(schematic);
|
||||
Draw.flush();
|
||||
buffer.begin();
|
||||
Pixmap pixmap = ScreenUtils.getFrameBufferPixmap(0, 0, buffer.getWidth(), buffer.getHeight());
|
||||
file.writePNG(pixmap);
|
||||
buffer.end();
|
||||
}
|
||||
|
||||
public Texture getPreview(Schematic schematic){
|
||||
return getBuffer(schematic).getTexture();
|
||||
}
|
||||
|
||||
public boolean hasPreview(Schematic schematic){
|
||||
return previews.containsKey(schematic);
|
||||
}
|
||||
|
||||
public FrameBuffer getBuffer(Schematic schematic){
|
||||
//dispose unneeded previews to prevent memory outage errors.
|
||||
//only runs every 2 seconds
|
||||
if(mobile && Time.timeSinceMillis(lastClearTime) > 1000 * 2 && previews.size > maxPreviewsMobile){
|
||||
Array<Schematic> keys = previews.orderedKeys().copy();
|
||||
for(int i = 0; i < previews.size - maxPreviewsMobile; i++){
|
||||
//dispose and remove unneeded previews
|
||||
previews.get(keys.get(i)).dispose();
|
||||
previews.remove(keys.get(i));
|
||||
}
|
||||
//update last clear time
|
||||
lastClearTime = Time.millis();
|
||||
}
|
||||
|
||||
if(!previews.containsKey(schematic)){
|
||||
Draw.blend();
|
||||
Draw.reset();
|
||||
Tmp.m1.set(Draw.proj());
|
||||
Tmp.m2.set(Draw.trans());
|
||||
FrameBuffer buffer = new FrameBuffer((schematic.width + padding) * resolution, (schematic.height + padding) * resolution);
|
||||
|
||||
shadowBuffer.beginDraw(Color.clear);
|
||||
|
||||
Draw.trans().idt();
|
||||
Draw.proj().setOrtho(0, 0, shadowBuffer.getWidth(), shadowBuffer.getHeight());
|
||||
|
||||
Draw.color();
|
||||
schematic.tiles.each(t -> {
|
||||
int size = t.block.size;
|
||||
int offsetx = -(size - 1) / 2;
|
||||
int offsety = -(size - 1) / 2;
|
||||
for(int dx = 0; dx < size; dx++){
|
||||
for(int dy = 0; dy < size; dy++){
|
||||
int wx = t.x + dx + offsetx;
|
||||
int wy = t.y + dy + offsety;
|
||||
Fill.square(padding/2f + wx + 0.5f, padding/2f + wy + 0.5f, 0.5f);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
shadowBuffer.endDraw();
|
||||
|
||||
buffer.beginDraw(Color.clear);
|
||||
|
||||
Draw.proj().setOrtho(0, buffer.getHeight(), buffer.getWidth(), -buffer.getHeight());
|
||||
|
||||
Tmp.tr1.set(shadowBuffer.getTexture(), 0, 0, schematic.width + padding, schematic.height + padding);
|
||||
Draw.color(0f, 0f, 0f, 1f);
|
||||
Draw.rect(Tmp.tr1, buffer.getWidth()/2f, buffer.getHeight()/2f, buffer.getWidth(), -buffer.getHeight());
|
||||
Draw.color();
|
||||
|
||||
Array<BuildRequest> requests = schematic.tiles.map(t -> new BuildRequest(t.x, t.y, t.rotation, t.block).configure(t.config));
|
||||
|
||||
Draw.flush();
|
||||
//scale each request to fit schematic
|
||||
Draw.trans().scale(resolution / tilesize, resolution / tilesize).translate(tilesize*1.5f, tilesize*1.5f);
|
||||
|
||||
//draw requests
|
||||
requests.each(req -> {
|
||||
req.animScale = 1f;
|
||||
req.worldContext = false;
|
||||
req.block.drawRequestRegion(req, requests::each);
|
||||
});
|
||||
|
||||
requests.each(req -> req.block.drawRequestConfigTop(req, requests::each));
|
||||
|
||||
Draw.flush();
|
||||
Draw.trans().idt();
|
||||
|
||||
buffer.endDraw();
|
||||
|
||||
Draw.proj(Tmp.m1);
|
||||
Draw.trans(Tmp.m2);
|
||||
|
||||
previews.put(schematic, buffer);
|
||||
}
|
||||
|
||||
return previews.get(schematic);
|
||||
}
|
||||
|
||||
/** Creates an array of build requests from a schematic's data, centered on the provided x+y coordinates. */
|
||||
public Array<BuildRequest> toRequests(Schematic schem, int x, int y){
|
||||
return schem.tiles.map(t -> new BuildRequest(t.x + x - schem.width/2, t.y + y - schem.height/2, t.rotation, t.block).original(t.x, t.y, schem.width, schem.height).configure(t.config))
|
||||
.removeAll(s -> !s.block.isVisible() || !s.block.unlockedCur());
|
||||
}
|
||||
|
||||
public void placeLoadout(Schematic schem, int x, int y){
|
||||
Stile coreTile = schem.tiles.find(s -> s.block instanceof CoreBlock);
|
||||
int ox = x - coreTile.x, oy = y - coreTile.y;
|
||||
schem.tiles.each(st -> {
|
||||
Tile tile = world.tile(st.x + ox, st.y + oy);
|
||||
if(tile == null) return;
|
||||
|
||||
world.setBlock(tile, st.block, defaultTeam);
|
||||
tile.rotation(st.rotation);
|
||||
if(st.block.posConfig){
|
||||
tile.configureAny(Pos.get(tile.x - st.x + Pos.x(st.config), tile.y - st.y + Pos.y(st.config)));
|
||||
}else{
|
||||
tile.configureAny(st.config);
|
||||
}
|
||||
|
||||
if(st.block instanceof Drill){
|
||||
tile.getLinkedTiles(t -> t.setOverlay(Blocks.oreCopper));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/** Adds a schematic to the list, also copying it into the files.*/
|
||||
public void add(Schematic schematic){
|
||||
all.add(schematic);
|
||||
try{
|
||||
FileHandle file = schematicDirectory.child(Time.millis() + "." + schematicExtension);
|
||||
write(schematic, file);
|
||||
schematic.file = file;
|
||||
}catch(Exception e){
|
||||
ui.showException(e);
|
||||
Log.err(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void remove(Schematic s){
|
||||
all.remove(s);
|
||||
if(s.file != null){
|
||||
s.file.delete();
|
||||
}
|
||||
|
||||
if(previews.containsKey(s)){
|
||||
previews.get(s).dispose();
|
||||
previews.remove(s);
|
||||
}
|
||||
}
|
||||
|
||||
/** Creates a schematic from a world selection. */
|
||||
public Schematic create(int x, int y, int x2, int y2){
|
||||
NormalizeResult result = Placement.normalizeArea(x, y, x2, y2, 0, false, maxSchematicSize);
|
||||
x = result.x;
|
||||
y = result.y;
|
||||
x2 = result.x2;
|
||||
y2 = result.y2;
|
||||
|
||||
int ox = x, oy = y, ox2 = x2, oy2 = y2;
|
||||
|
||||
Array<Stile> tiles = new Array<>();
|
||||
|
||||
int minx = x2, miny = y2, maxx = x, maxy = y;
|
||||
boolean found = false;
|
||||
for(int cx = x; cx <= x2; cx++){
|
||||
for(int cy = y; cy <= y2; cy++){
|
||||
Tile linked = world.ltile(cx, cy);
|
||||
|
||||
if(linked != null && linked.entity != null && linked.entity.block.isVisible() && !(linked.block() instanceof BuildBlock)){
|
||||
int top = linked.block().size/2;
|
||||
int bot = linked.block().size % 2 == 1 ? -linked.block().size/2 : -(linked.block().size - 1)/2;
|
||||
minx = Math.min(linked.x + bot, minx);
|
||||
miny = Math.min(linked.y + bot, miny);
|
||||
maxx = Math.max(linked.x + top, maxx);
|
||||
maxy = Math.max(linked.y + top, maxy);
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(found){
|
||||
x = minx;
|
||||
y = miny;
|
||||
x2 = maxx;
|
||||
y2 = maxy;
|
||||
}else{
|
||||
return new Schematic(new Array<>(), new StringMap(), 1, 1);
|
||||
}
|
||||
|
||||
int width = x2 - x + 1, height = y2 - y + 1;
|
||||
int offsetX = -x, offsetY = -y;
|
||||
IntSet counted = new IntSet();
|
||||
for(int cx = ox; cx <= ox2; cx++){
|
||||
for(int cy = oy; cy <= oy2; cy++){
|
||||
Tile tile = world.ltile(cx, cy);
|
||||
|
||||
if(tile != null && tile.entity != null && !counted.contains(tile.pos()) && !(tile.block() instanceof BuildBlock) && tile.entity.block.isVisible()){
|
||||
int config = tile.entity.config();
|
||||
if(tile.block().posConfig){
|
||||
config = Pos.get(Pos.x(config) + offsetX, Pos.y(config) + offsetY);
|
||||
}
|
||||
|
||||
tiles.add(new Stile(tile.block(), tile.x + offsetX, tile.y + offsetY, config, tile.rotation()));
|
||||
counted.add(tile.pos());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new Schematic(tiles, new StringMap(), width, height);
|
||||
}
|
||||
|
||||
/** Converts a schematic to base64. Note that the result of this will always start with 'bXNjaAB'.*/
|
||||
public String writeBase64(Schematic schematic){
|
||||
try{
|
||||
out.reset();
|
||||
write(schematic, out);
|
||||
return new String(Base64Coder.encode(out.getBuffer(), out.size()));
|
||||
}catch(IOException e){
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
//region IO methods
|
||||
|
||||
/** Loads a schematic from base64. May throw an exception. */
|
||||
public static Schematic readBase64(String schematic) throws IOException{
|
||||
return read(new ByteArrayInputStream(Base64Coder.decode(schematic)));
|
||||
}
|
||||
|
||||
public static Schematic read(FileHandle file) throws IOException{
|
||||
Schematic s = read(new DataInputStream(file.read(1024)));
|
||||
if(!s.tags.containsKey("name")){
|
||||
s.tags.put("name", file.nameWithoutExtension());
|
||||
}
|
||||
s.file = file;
|
||||
return s;
|
||||
}
|
||||
|
||||
public static Schematic read(InputStream input) throws IOException{
|
||||
for(byte b : header){
|
||||
if(input.read() != b){
|
||||
throw new IOException("Not a schematic file (missing header).");
|
||||
}
|
||||
}
|
||||
|
||||
int ver;
|
||||
if((ver = input.read()) != version){
|
||||
throw new IOException("Unknown version: " + ver);
|
||||
}
|
||||
|
||||
try(DataInputStream stream = new DataInputStream(new InflaterInputStream(input))){
|
||||
short width = stream.readShort(), height = stream.readShort();
|
||||
|
||||
StringMap map = new StringMap();
|
||||
byte tags = stream.readByte();
|
||||
for(int i = 0; i < tags; i++){
|
||||
map.put(stream.readUTF(), stream.readUTF());
|
||||
}
|
||||
|
||||
IntMap<Block> blocks = new IntMap<>();
|
||||
byte length = stream.readByte();
|
||||
for(int i = 0; i < length; i++){
|
||||
Block block = Vars.content.getByName(ContentType.block, stream.readUTF());
|
||||
blocks.put(i, block == null ? Blocks.air : block);
|
||||
}
|
||||
|
||||
int total = stream.readInt();
|
||||
Array<Stile> tiles = new Array<>(total);
|
||||
for(int i = 0; i < total; i++){
|
||||
Block block = blocks.get(stream.readByte());
|
||||
int position = stream.readInt();
|
||||
int config = stream.readInt();
|
||||
byte rotation = stream.readByte();
|
||||
if(block != Blocks.air){
|
||||
tiles.add(new Stile(block, Pos.x(position), Pos.y(position), config, rotation));
|
||||
}
|
||||
}
|
||||
|
||||
return new Schematic(tiles, map, width, height);
|
||||
}
|
||||
}
|
||||
|
||||
public static void write(Schematic schematic, FileHandle file) throws IOException{
|
||||
write(schematic, file.write(false, 1024));
|
||||
}
|
||||
|
||||
public static void write(Schematic schematic, OutputStream output) throws IOException{
|
||||
output.write(header);
|
||||
output.write(version);
|
||||
|
||||
try(DataOutputStream stream = new DataOutputStream(new DeflaterOutputStream(output))){
|
||||
|
||||
stream.writeShort(schematic.width);
|
||||
stream.writeShort(schematic.height);
|
||||
|
||||
stream.writeByte(schematic.tags.size);
|
||||
for(ObjectMap.Entry<String, String> e : schematic.tags.entries()){
|
||||
stream.writeUTF(e.key);
|
||||
stream.writeUTF(e.value);
|
||||
}
|
||||
|
||||
OrderedSet<Block> blocks = new OrderedSet<>();
|
||||
schematic.tiles.each(t -> blocks.add(t.block));
|
||||
|
||||
//create dictionary
|
||||
stream.writeByte(blocks.size);
|
||||
for(int i = 0; i < blocks.size; i++){
|
||||
stream.writeUTF(blocks.orderedItems().get(i).name);
|
||||
}
|
||||
|
||||
stream.writeInt(schematic.tiles.size);
|
||||
//write each tile
|
||||
for(Stile tile : schematic.tiles){
|
||||
stream.writeByte(blocks.orderedItems().indexOf(tile.block));
|
||||
stream.writeInt(Pos.get(tile.x, tile.y));
|
||||
stream.writeInt(tile.config);
|
||||
stream.writeByte(tile.rotation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//endregion
|
||||
}
|
||||
@@ -3,6 +3,7 @@ package io.anuke.mindustry.game;
|
||||
import io.anuke.arc.util.serialization.Json;
|
||||
import io.anuke.arc.util.serialization.Json.Serializable;
|
||||
import io.anuke.arc.util.serialization.JsonValue;
|
||||
import io.anuke.mindustry.content.*;
|
||||
import io.anuke.mindustry.entities.type.BaseUnit;
|
||||
import io.anuke.mindustry.type.*;
|
||||
|
||||
@@ -84,6 +85,7 @@ public class SpawnGroup implements Serializable{
|
||||
@Override
|
||||
public void read(Json json, JsonValue data){
|
||||
type = content.getByName(ContentType.unit, data.getString("type", "dagger"));
|
||||
if(type == null) type = UnitTypes.dagger;
|
||||
begin = data.getInt("begin", 0);
|
||||
end = data.getInt("end", never);
|
||||
spacing = data.getInt("spacing", 1);
|
||||
|
||||
@@ -33,7 +33,7 @@ public class Stats{
|
||||
score += (float)((wavesLasted - zone.conditionWave) / zone.launchPeriod + 1) * 1.2f;
|
||||
}
|
||||
|
||||
int capacity = zone.loadout.core().itemCapacity;
|
||||
int capacity = zone.loadout.findCore().itemCapacity;
|
||||
|
||||
//weigh used fractions
|
||||
float frac = 0f;
|
||||
|
||||
@@ -2,7 +2,7 @@ package io.anuke.mindustry.game;
|
||||
|
||||
import io.anuke.arc.*;
|
||||
import io.anuke.arc.collection.*;
|
||||
import io.anuke.arc.function.*;
|
||||
import io.anuke.arc.func.*;
|
||||
import io.anuke.arc.graphics.g2d.*;
|
||||
import io.anuke.arc.math.*;
|
||||
import io.anuke.arc.scene.*;
|
||||
@@ -40,6 +40,12 @@ public class Tutorial{
|
||||
Events.on(BlockInfoEvent.class, event -> events.add("blockinfo"));
|
||||
Events.on(DepositEvent.class, event -> events.add("deposit"));
|
||||
Events.on(WithdrawEvent.class, event -> events.add("withdraw"));
|
||||
|
||||
Events.on(ClientLoadEvent.class, e -> {
|
||||
for(TutorialStage stage : TutorialStage.values()){
|
||||
stage.load();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/** update tutorial state, transition if needed */
|
||||
@@ -188,29 +194,33 @@ public class Tutorial{
|
||||
},;
|
||||
|
||||
protected String line = "";
|
||||
protected final Function<String, String> text;
|
||||
protected final Func<String, String> text;
|
||||
protected Array<String> sentences;
|
||||
protected final BooleanProvider done;
|
||||
protected final Boolp done;
|
||||
|
||||
TutorialStage(Function<String, String> text, BooleanProvider done){
|
||||
TutorialStage(Func<String, String> text, Boolp done){
|
||||
this.text = text;
|
||||
this.done = done;
|
||||
}
|
||||
|
||||
TutorialStage(BooleanProvider done){
|
||||
TutorialStage(Boolp done){
|
||||
this(line -> line, done);
|
||||
}
|
||||
|
||||
/** displayed tutorial stage text.*/
|
||||
public String text(){
|
||||
if(sentences == null){
|
||||
this.line = Core.bundle.has("tutorial." + name() + ".mobile") && mobile ? "tutorial." + name() + ".mobile" : "tutorial." + name();
|
||||
this.sentences = Array.select(Core.bundle.get(line).split("\n"), s -> !s.isEmpty());
|
||||
load();
|
||||
}
|
||||
String line = sentences.get(control.tutorial.sentence);
|
||||
return line.contains("{") ? text.get(line) : line;
|
||||
}
|
||||
|
||||
void load(){
|
||||
this.line = Core.bundle.has("tutorial." + name() + ".mobile") && mobile ? "tutorial." + name() + ".mobile" : "tutorial." + name();
|
||||
this.sentences = Array.select(Core.bundle.get(line).split("\n"), s -> !s.isEmpty());
|
||||
}
|
||||
|
||||
/** called every frame when this stage is active.*/
|
||||
void update(){
|
||||
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
package io.anuke.mindustry.game;
|
||||
|
||||
import io.anuke.arc.function.Supplier;
|
||||
import io.anuke.mindustry.entities.traits.TypeTrait;
|
||||
import io.anuke.mindustry.type.ContentType;
|
||||
|
||||
public class TypeID extends MappableContent{
|
||||
public final Supplier<? extends TypeTrait> constructor;
|
||||
|
||||
public TypeID(String name, Supplier<? extends TypeTrait> constructor){
|
||||
super(name);
|
||||
this.constructor = constructor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ContentType getContentType(){
|
||||
return ContentType.typeid;
|
||||
}
|
||||
}
|
||||
@@ -9,12 +9,11 @@ import io.anuke.arc.graphics.glutils.*;
|
||||
import io.anuke.arc.math.*;
|
||||
import io.anuke.arc.util.*;
|
||||
import io.anuke.mindustry.content.*;
|
||||
import io.anuke.mindustry.entities.type.base.*;
|
||||
import io.anuke.mindustry.game.EventType.*;
|
||||
import io.anuke.mindustry.game.*;
|
||||
import io.anuke.mindustry.game.EventType.*;
|
||||
import io.anuke.mindustry.game.Teams.*;
|
||||
import io.anuke.mindustry.ui.Cicon;
|
||||
import io.anuke.mindustry.world.*;
|
||||
import io.anuke.mindustry.world.Block.*;
|
||||
|
||||
import static io.anuke.arc.Core.camera;
|
||||
import static io.anuke.mindustry.Vars.*;
|
||||
@@ -30,6 +29,7 @@ public class BlockRenderer implements Disposable{
|
||||
private int lastCamX, lastCamY, lastRangeX, lastRangeY;
|
||||
private int requestidx = 0;
|
||||
private int iterateidx = 0;
|
||||
private float brokenFade = 0f;
|
||||
private FrameBuffer shadows = new FrameBuffer(2, 2);
|
||||
private FrameBuffer fog = new FrameBuffer(2, 2);
|
||||
private Array<Tile> outArray = new Array<>();
|
||||
@@ -124,14 +124,22 @@ public class BlockRenderer implements Disposable{
|
||||
Draw.shader();
|
||||
}
|
||||
|
||||
public void drawBroken(){
|
||||
if(unitGroups[player.getTeam().ordinal()].all().contains(p -> p instanceof BuilderDrone)){
|
||||
public void drawDestroyed(){
|
||||
if(!Core.settings.getBool("destroyedblocks")) return;
|
||||
|
||||
if(control.input.isPlacing() || control.input.isBreaking()){
|
||||
brokenFade = Mathf.lerpDelta(brokenFade, 1f, 0.1f);
|
||||
}else{
|
||||
brokenFade = Mathf.lerpDelta(brokenFade, 0f, 0.1f);
|
||||
}
|
||||
|
||||
if(brokenFade > 0.001f){
|
||||
for(BrokenBlock block : state.teams.get(player.getTeam()).brokenBlocks){
|
||||
Block b = content.block(block.block);
|
||||
if(!camera.bounds(Tmp.r1).grow(tilesize * 2f).overlaps(Tmp.r2.setSize(b.size * tilesize).setCenter(block.x * tilesize + b.offset(), block.y * tilesize + b.offset()))) continue;
|
||||
|
||||
Draw.alpha(0.5f);
|
||||
Draw.mixcol(Pal.accent, 0.2f + Mathf.absin(5f, 0.2f));
|
||||
Draw.alpha(0.53f * brokenFade);
|
||||
Draw.mixcol(Color.white, 0.2f + Mathf.absin(Time.globalTime(), 6f, 0.2f));
|
||||
Draw.rect(b.icon(Cicon.full), block.x * tilesize + b.offset(), block.y * tilesize + b.offset(), b.rotate ? block.rotation * 90 : 0f);
|
||||
}
|
||||
Draw.reset();
|
||||
@@ -242,28 +250,34 @@ public class BlockRenderer implements Disposable{
|
||||
}
|
||||
|
||||
public void drawBlocks(Layer stopAt){
|
||||
|
||||
int startIdx = iterateidx;
|
||||
for(; iterateidx < requestidx; iterateidx++){
|
||||
BlockRequest request = requests.get(iterateidx);
|
||||
|
||||
if(iterateidx < requests.size && requests.get(iterateidx).layer.ordinal() > stopAt.ordinal()){
|
||||
if(request.layer.ordinal() > stopAt.ordinal()){
|
||||
break;
|
||||
}
|
||||
|
||||
BlockRequest req = requests.get(iterateidx);
|
||||
Block block = req.tile.block();
|
||||
if(request.layer == Layer.power){
|
||||
if(iterateidx - startIdx > 0 && request.tile.pos() == requests.get(iterateidx - 1).tile.pos()){
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if(req.layer == Layer.block){
|
||||
block.draw(req.tile);
|
||||
if(req.tile.entity != null && req.tile.entity.damaged()){
|
||||
block.drawCracks(req.tile);
|
||||
Block block = request.tile.block();
|
||||
|
||||
if(request.layer == Layer.block){
|
||||
block.draw(request.tile);
|
||||
if(request.tile.entity != null && request.tile.entity.damaged()){
|
||||
block.drawCracks(request.tile);
|
||||
}
|
||||
if(block.synthetic() && req.tile.getTeam() != player.getTeam()){
|
||||
block.drawTeam(req.tile);
|
||||
if(block.synthetic() && request.tile.getTeam() != player.getTeam()){
|
||||
block.drawTeam(request.tile);
|
||||
}
|
||||
}else if(req.layer == block.layer){
|
||||
block.drawLayer(req.tile);
|
||||
}else if(req.layer == block.layer2){
|
||||
block.drawLayer2(req.tile);
|
||||
}else if(request.layer == block.layer){
|
||||
block.drawLayer(request.tile);
|
||||
}else if(request.layer == block.layer2){
|
||||
block.drawLayer2(request.tile);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -328,7 +342,9 @@ public class BlockRenderer implements Disposable{
|
||||
|
||||
@Override
|
||||
public int compareTo(BlockRequest other){
|
||||
return layer.compareTo(other.layer);
|
||||
int compare = layer.compareTo(other.layer);
|
||||
|
||||
return (compare != 0) ? compare : Integer.compare(tile.pos(), other.tile.pos());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -2,7 +2,7 @@ package io.anuke.mindustry.graphics;
|
||||
|
||||
import io.anuke.arc.Core;
|
||||
import io.anuke.arc.collection.Array;
|
||||
import io.anuke.arc.function.PositionConsumer;
|
||||
import io.anuke.arc.func.Floatc2;
|
||||
import io.anuke.arc.graphics.Camera;
|
||||
import io.anuke.arc.graphics.Color;
|
||||
import io.anuke.arc.graphics.g2d.*;
|
||||
@@ -14,8 +14,8 @@ import io.anuke.arc.util.noise.RidgedPerlin;
|
||||
import io.anuke.arc.util.noise.Simplex;
|
||||
import io.anuke.mindustry.content.Blocks;
|
||||
import io.anuke.mindustry.content.UnitTypes;
|
||||
import io.anuke.mindustry.game.*;
|
||||
import io.anuke.mindustry.type.UnitType;
|
||||
import io.anuke.mindustry.ui.Cicon;
|
||||
import io.anuke.mindustry.world.*;
|
||||
import io.anuke.mindustry.world.blocks.Floor;
|
||||
import io.anuke.mindustry.world.blocks.OreBlock;
|
||||
@@ -282,7 +282,7 @@ public class MenuRenderer implements Disposable{
|
||||
});
|
||||
}
|
||||
|
||||
private void flyers(PositionConsumer cons){
|
||||
private void flyers(Floatc2 cons){
|
||||
float tw = width * tilesize * 1f + tilesize;
|
||||
float th = height * tilesize * 1f + tilesize;
|
||||
float range = 500f;
|
||||
@@ -291,7 +291,7 @@ public class MenuRenderer implements Disposable{
|
||||
for(int i = 0; i < flyers; i++){
|
||||
Tmp.v1.trns(flyerRot, time * (2f + flyerType.speed));
|
||||
|
||||
cons.accept((Mathf.randomSeedRange(i, range) + Tmp.v1.x + Mathf.absin(time + Mathf.randomSeedRange(i + 2, 500), 10f, 3.4f) + offset) % (tw + Mathf.randomSeed(i + 5, 0, 500)),
|
||||
cons.get((Mathf.randomSeedRange(i, range) + Tmp.v1.x + Mathf.absin(time + Mathf.randomSeedRange(i + 2, 500), 10f, 3.4f) + offset) % (tw + Mathf.randomSeed(i + 5, 0, 500)),
|
||||
(Mathf.randomSeedRange(i + 1, range) + Tmp.v1.y + Mathf.absin(time + Mathf.randomSeedRange(i + 3, 500), 10f, 3.4f) + offset) % th);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,10 +9,12 @@ import io.anuke.arc.math.*;
|
||||
import io.anuke.arc.math.geom.*;
|
||||
import io.anuke.arc.scene.ui.layout.*;
|
||||
import io.anuke.arc.util.*;
|
||||
import io.anuke.arc.util.pooling.*;
|
||||
import io.anuke.mindustry.entities.*;
|
||||
import io.anuke.mindustry.entities.type.*;
|
||||
import io.anuke.mindustry.game.EventType.*;
|
||||
import io.anuke.mindustry.io.*;
|
||||
import io.anuke.mindustry.ui.*;
|
||||
import io.anuke.mindustry.world.*;
|
||||
|
||||
import static io.anuke.mindustry.Vars.*;
|
||||
@@ -68,7 +70,7 @@ public class MinimapRenderer implements Disposable{
|
||||
region = new TextureRegion(texture);
|
||||
}
|
||||
|
||||
public void drawEntities(float x, float y, float w, float h){
|
||||
public void drawEntities(float x, float y, float w, float h, boolean withLabels){
|
||||
updateUnitArray();
|
||||
|
||||
float sz = baseSize * zoom;
|
||||
@@ -80,7 +82,17 @@ public class MinimapRenderer implements Disposable{
|
||||
rect.set((dx - sz) * tilesize, (dy - sz) * tilesize, sz * 2 * tilesize, sz * 2 * tilesize);
|
||||
|
||||
for(Unit unit : units){
|
||||
float rx = (unit.x - rect.x) / rect.width * w, ry = (unit.y - rect.y) / rect.width * h;
|
||||
float rx = (unit.x - rect.x) / rect.width * w;
|
||||
float ry = (unit.y - rect.y) / rect.width * h;
|
||||
|
||||
if(withLabels && unit instanceof Player){
|
||||
Player pl = (Player) unit;
|
||||
if(!pl.isLocal){
|
||||
// Only display names for other players.
|
||||
drawLabel(x + rx, y + ry, pl.name, unit.getTeam().color);
|
||||
}
|
||||
}
|
||||
|
||||
Draw.color(unit.getTeam().color);
|
||||
Fill.rect(x + rx, y + ry, Scl.scl(baseSize / 2f), Scl.scl(baseSize / 2f));
|
||||
}
|
||||
@@ -88,6 +100,10 @@ public class MinimapRenderer implements Disposable{
|
||||
Draw.color();
|
||||
}
|
||||
|
||||
public void drawEntities(float x, float y, float w, float h){
|
||||
drawEntities(x, y, w, h, true);
|
||||
}
|
||||
|
||||
public TextureRegion getRegion(){
|
||||
if(texture == null) return null;
|
||||
|
||||
@@ -133,6 +149,10 @@ public class MinimapRenderer implements Disposable{
|
||||
private int colorFor(Tile tile){
|
||||
if(tile == null) return 0;
|
||||
tile = tile.link();
|
||||
int bc = tile.block().minimapColor(tile);
|
||||
if(bc != 0){
|
||||
return bc;
|
||||
}
|
||||
return Tmp.c1.set(MapIO.colorFor(tile.floor(), tile.block(), tile.overlay(), tile.getTeam())).mul(tile.block().cacheLayer == CacheLayer.walls ? 1f - tile.rotation() / 4f : 1f).rgba();
|
||||
}
|
||||
|
||||
@@ -145,4 +165,27 @@ public class MinimapRenderer implements Disposable{
|
||||
pixmap = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void drawLabel(float x, float y, String text, Color color){
|
||||
BitmapFont font = Fonts.outline;
|
||||
GlyphLayout l = Pools.obtain(GlyphLayout.class, GlyphLayout::new);
|
||||
boolean ints = font.usesIntegerPositions();
|
||||
font.getData().setScale(1 / 1.5f / Scl.scl(1f));
|
||||
font.setUseIntegerPositions(false);
|
||||
|
||||
l.setText(font, text, color, 90f, Align.left, true);
|
||||
float yOffset = 20f;
|
||||
float margin = 3f;
|
||||
|
||||
Draw.color(0f, 0f, 0f, 0.2f);
|
||||
Fill.rect(x, y + yOffset - l.height/2f, l.width + margin, l.height + margin);
|
||||
Draw.color();
|
||||
font.setColor(color);
|
||||
font.draw(text, x - l.width/2f, y + yOffset, 90f, Align.left, true);
|
||||
font.setUseIntegerPositions(ints);
|
||||
|
||||
font.getData().setScale(1f);
|
||||
|
||||
Pools.free(l);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,11 @@ import io.anuke.mindustry.entities.*;
|
||||
import io.anuke.mindustry.entities.type.*;
|
||||
import io.anuke.mindustry.game.*;
|
||||
import io.anuke.mindustry.input.*;
|
||||
import io.anuke.mindustry.type.Category;
|
||||
import io.anuke.mindustry.ui.Cicon;
|
||||
import io.anuke.mindustry.world.*;
|
||||
import io.anuke.mindustry.world.blocks.units.MechPad;
|
||||
import io.anuke.mindustry.world.meta.BlockFlag;
|
||||
|
||||
import static io.anuke.mindustry.Vars.*;
|
||||
|
||||
@@ -25,9 +29,9 @@ public class OverlayRenderer{
|
||||
public void drawBottom(){
|
||||
InputHandler input = control.input;
|
||||
|
||||
if(!input.isDrawing() || player.isDead()) return;
|
||||
if(player.isDead()) return;
|
||||
|
||||
input.drawOutlined();
|
||||
input.drawBottom();
|
||||
}
|
||||
|
||||
public void drawTop(){
|
||||
@@ -56,6 +60,21 @@ public class OverlayRenderer{
|
||||
Draw.reset();
|
||||
}
|
||||
});
|
||||
|
||||
if(ui.hudfrag.blockfrag.currentCategory == Category.upgrade){
|
||||
for(Tile mechpad : indexer.getAllied(player.getTeam(), BlockFlag.mechPad)){
|
||||
if(!(mechpad.block() instanceof MechPad)) continue;
|
||||
if(!rect.setSize(Core.camera.width * 0.9f, Core.camera.height * 0.9f)
|
||||
.setCenter(Core.camera.position.x, Core.camera.position.y).contains(mechpad.x, mechpad.y)){
|
||||
|
||||
Tmp.v1.set(mechpad.worldx(), mechpad.worldy()).sub(Core.camera.position.x, Core.camera.position.y).setLength(indicatorLength);
|
||||
|
||||
Lines.stroke(2f, ((MechPad) mechpad.block()).mech.engineColor);
|
||||
Lines.lineAngle(Core.camera.position.x + Tmp.v1.x, Core.camera.position.y + Tmp.v1.y, Tmp.v1.angle(), 0.5f);
|
||||
Draw.reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(player.isDead()) return; //dead players don't draw
|
||||
|
||||
@@ -75,6 +75,8 @@ public class Pal{
|
||||
|
||||
surge = Color.valueOf("f3e979"),
|
||||
|
||||
plastanium = Color.valueOf("a1b46e"),
|
||||
|
||||
redSpark = Color.valueOf("fbb97f"),
|
||||
orangeSpark = Color.valueOf("d2b29c"),
|
||||
|
||||
|
||||
@@ -12,13 +12,17 @@ public enum Binding implements KeyBind{
|
||||
select(KeyCode.MOUSE_LEFT),
|
||||
deselect(KeyCode.MOUSE_RIGHT),
|
||||
break_block(KeyCode.MOUSE_RIGHT),
|
||||
clear_building(KeyCode.Q),
|
||||
pause_building(KeyCode.E),
|
||||
rotate(new Axis(KeyCode.SCROLL)),
|
||||
rotateplaced(KeyCode.R),
|
||||
diagonal_placement(KeyCode.CONTROL_LEFT),
|
||||
pick(KeyCode.MOUSE_MIDDLE),
|
||||
schematic_select(KeyCode.F),
|
||||
schematic_flip_x(KeyCode.Z),
|
||||
schematic_flip_y(KeyCode.X),
|
||||
schematic_menu(KeyCode.T),
|
||||
dash(KeyCode.SHIFT_LEFT),
|
||||
gridMode(KeyCode.BACKTICK),
|
||||
gridModeShift(KeyCode.ALT_LEFT),
|
||||
zoom_hold(KeyCode.CONTROL_LEFT, "view"),
|
||||
zoom(new Axis(KeyCode.SCROLL)),
|
||||
menu(Core.app.getType() == ApplicationType.Android ? KeyCode.BACK : KeyCode.ESCAPE),
|
||||
@@ -27,12 +31,12 @@ public enum Binding implements KeyBind{
|
||||
minimap(KeyCode.M),
|
||||
toggle_menus(KeyCode.C),
|
||||
screenshot(KeyCode.P),
|
||||
toggle_power_lines(KeyCode.F7),
|
||||
player_list(KeyCode.TAB, "multiplayer"),
|
||||
chat(KeyCode.ENTER),
|
||||
chat_history_prev(KeyCode.UP),
|
||||
chat_history_next(KeyCode.DOWN),
|
||||
chat_scroll(new Axis(KeyCode.SCROLL)),
|
||||
|
||||
;
|
||||
|
||||
private final KeybindValue defaultValue;
|
||||
|
||||
@@ -5,15 +5,19 @@ import io.anuke.arc.Graphics.*;
|
||||
import io.anuke.arc.Graphics.Cursor.*;
|
||||
import io.anuke.arc.graphics.g2d.*;
|
||||
import io.anuke.arc.math.*;
|
||||
import io.anuke.arc.math.geom.*;
|
||||
import io.anuke.arc.scene.*;
|
||||
import io.anuke.arc.scene.event.*;
|
||||
import io.anuke.arc.scene.ui.*;
|
||||
import io.anuke.arc.util.*;
|
||||
import io.anuke.mindustry.content.*;
|
||||
import io.anuke.arc.scene.ui.layout.*;
|
||||
import io.anuke.arc.util.ArcAnnotate.*;
|
||||
import io.anuke.mindustry.*;
|
||||
import io.anuke.mindustry.core.GameState.*;
|
||||
import io.anuke.mindustry.entities.traits.BuilderTrait.*;
|
||||
import io.anuke.mindustry.game.EventType.*;
|
||||
import io.anuke.mindustry.game.*;
|
||||
import io.anuke.mindustry.gen.*;
|
||||
import io.anuke.mindustry.graphics.*;
|
||||
import io.anuke.mindustry.input.PlaceUtils.*;
|
||||
import io.anuke.mindustry.ui.*;
|
||||
import io.anuke.mindustry.world.*;
|
||||
|
||||
import static io.anuke.arc.Core.scene;
|
||||
@@ -23,101 +27,105 @@ import static io.anuke.mindustry.input.PlaceMode.*;
|
||||
public class DesktopInput extends InputHandler{
|
||||
/** Current cursor type. */
|
||||
private Cursor cursorType = SystemCursor.arrow;
|
||||
|
||||
/** Position where the player started dragging a line. */
|
||||
private int selectX, selectY;
|
||||
private int selectX, selectY, schemX, schemY;
|
||||
/** Last known line positions.*/
|
||||
private int lastLineX, lastLineY, schematicX, schematicY;
|
||||
/** Whether selecting mode is active. */
|
||||
private PlaceMode mode;
|
||||
/** Animation scale for line. */
|
||||
private float selectScale;
|
||||
/** Selected build request for movement. */
|
||||
private @Nullable BuildRequest sreq;
|
||||
/** Whether player is currently deleting removal requests. */
|
||||
private boolean deleting = false;
|
||||
|
||||
private int prevX, prevY, prevRotation;
|
||||
@Override
|
||||
public void buildUI(Group group){
|
||||
group.fill(t -> {
|
||||
t.bottom().update(() -> t.getColor().a = Mathf.lerpDelta(t.getColor().a, player.isBuilding() ? 1f : 0f, 0.15f));
|
||||
t.visible(() -> Core.settings.getBool("hints") && selectRequests.isEmpty());
|
||||
t.touchable(() -> t.getColor().a < 0.1f ? Touchable.disabled : Touchable.childrenOnly);
|
||||
t.table(Styles.black6, b -> {
|
||||
b.defaults().left();
|
||||
b.label(() -> Core.bundle.format(!player.isBuilding ? "resumebuilding" : "pausebuilding", Core.keybinds.get(Binding.pause_building).key.toString())).style(Styles.outlineLabel);
|
||||
b.row();
|
||||
b.add(Core.bundle.format("cancelbuilding", Core.keybinds.get(Binding.clear_building).key.toString())).style(Styles.outlineLabel);
|
||||
b.row();
|
||||
b.add(Core.bundle.format("selectschematic", Core.keybinds.get(Binding.schematic_select).key.toString())).style(Styles.outlineLabel);
|
||||
}).margin(10f);
|
||||
});
|
||||
|
||||
/** Draws a placement icon for a specific block. */
|
||||
void drawPlace(int x, int y, Block block, int rotation, int prevX, int prevY, int prevRotation){
|
||||
if(validPlace(x, y, block, rotation)){
|
||||
block.getPlaceDraw(placeDraw, rotation, prevX, prevY, prevRotation);
|
||||
|
||||
Draw.color();
|
||||
Draw.mixcol(Pal.accent, 0.12f + Mathf.absin(Time.time(), 8f, 0.35f));
|
||||
Draw.rect(placeDraw.region, x * tilesize + block.offset(), y * tilesize + block.offset(),
|
||||
placeDraw.region.getWidth() * selectScale * Draw.scl * placeDraw.scalex,
|
||||
placeDraw.region.getHeight() * selectScale * Draw.scl * placeDraw.scaley,
|
||||
block.rotate ? placeDraw.rotation * 90 : 0);
|
||||
|
||||
Draw.color(Pal.accent);
|
||||
for(int i = 0; i < 4; i++){
|
||||
Point2 p = Geometry.d8edge[i];
|
||||
float offset = -Math.max(block.size - 1, 0) / 2f * tilesize;
|
||||
if(i % 2 == 0)
|
||||
Draw.rect("block-select", x * tilesize + block.offset() + offset * p.x, y * tilesize + block.offset() + offset * p.y, i * 90);
|
||||
}
|
||||
Draw.color();
|
||||
Draw.mixcol();
|
||||
}else{
|
||||
Draw.color(Pal.removeBack);
|
||||
Lines.square(x * tilesize + block.offset(), y * tilesize + block.offset() - 1, block.size * tilesize / 2f - 1);
|
||||
Draw.color(Pal.remove);
|
||||
Lines.square(x * tilesize + block.offset(), y * tilesize + block.offset(), block.size * tilesize / 2f - 1);
|
||||
}
|
||||
group.fill(t -> {
|
||||
t.visible(() -> lastSchematic != null && !selectRequests.isEmpty());
|
||||
t.bottom();
|
||||
t.table(Styles.black6, b -> {
|
||||
b.defaults().left();
|
||||
b.add(Core.bundle.format("schematic.flip",
|
||||
Core.keybinds.get(Binding.schematic_flip_x).key.toString(),
|
||||
Core.keybinds.get(Binding.schematic_flip_y).key.toString())).style(Styles.outlineLabel);
|
||||
b.row();
|
||||
b.table(a -> {
|
||||
a.addImageTextButton("$schematic.add", Icon.saveSmall, this::showSchematicSave).colspan(2).size(250f, 50f).disabled(f -> lastSchematic == null || lastSchematic.file != null);
|
||||
});
|
||||
}).margin(6f);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDrawing(){
|
||||
return mode != none || block != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawOutlined(){
|
||||
public void drawTop(){
|
||||
Lines.stroke(1f);
|
||||
int cursorX = tileX(Core.input.mouseX());
|
||||
int cursorY = tileY(Core.input.mouseY());
|
||||
|
||||
//draw selection(s)
|
||||
if(mode == placing && block != null){
|
||||
prevX = selectX;
|
||||
prevY = selectY;
|
||||
prevRotation = rotation;
|
||||
|
||||
iterateLine(selectX, selectY, cursorX, cursorY, l -> {
|
||||
if(l.last && block.rotate){
|
||||
drawArrow(block, l.x, l.y, l.rotation);
|
||||
}
|
||||
drawPlace(l.x, l.y, block, l.rotation, prevX - l.x, prevY - l.y, prevRotation);
|
||||
|
||||
prevX = l.x;
|
||||
prevY = l.y;
|
||||
prevRotation = l.rotation;
|
||||
});
|
||||
|
||||
}else if(mode == breaking){
|
||||
NormalizeDrawResult result = PlaceUtils.normalizeDrawArea(Blocks.air, selectX, selectY, cursorX, cursorY, false, maxLength, 1f);
|
||||
NormalizeResult dresult = PlaceUtils.normalizeArea(selectX, selectY, cursorX, cursorY, rotation, false, maxLength);
|
||||
|
||||
for(int x = dresult.x; x <= dresult.x2; x++){
|
||||
for(int y = dresult.y; y <= dresult.y2; y++){
|
||||
Tile tile = world.ltile(x, y);
|
||||
if(tile == null || !validBreak(tile.x, tile.y)) continue;
|
||||
|
||||
Draw.color(Pal.removeBack);
|
||||
Lines.square(tile.drawx(), tile.drawy() - 1, tile.block().size * tilesize / 2f - 1);
|
||||
Draw.color(Pal.remove);
|
||||
Lines.square(tile.drawx(), tile.drawy(), tile.block().size * tilesize / 2f - 1);
|
||||
for(int i = 0; i < lineRequests.size; i++){
|
||||
BuildRequest req = lineRequests.get(i);
|
||||
if(i == lineRequests.size - 1 && req.block.rotate){
|
||||
drawArrow(block, req.x, req.y, req.rotation);
|
||||
}
|
||||
drawRequest(lineRequests.get(i));
|
||||
}
|
||||
|
||||
Draw.color(Pal.removeBack);
|
||||
Lines.rect(result.x, result.y - 1, result.x2 - result.x, result.y2 - result.y);
|
||||
Draw.color(Pal.remove);
|
||||
Lines.rect(result.x, result.y, result.x2 - result.x, result.y2 - result.y);
|
||||
}else if(mode == breaking){
|
||||
drawBreakSelection(selectX, selectY, cursorX, cursorY);
|
||||
}else if(isPlacing()){
|
||||
if(block.rotate){
|
||||
drawArrow(block, cursorX, cursorY, rotation);
|
||||
}
|
||||
drawPlace(cursorX, cursorY, block, rotation, cursorX, cursorY, rotation);
|
||||
Draw.color();
|
||||
drawRequest(cursorX, cursorY, block, rotation);
|
||||
block.drawPlace(cursorX, cursorY, rotation, validPlace(cursorX, cursorY, block, rotation));
|
||||
}
|
||||
|
||||
if(mode == none && !isPlacing()){
|
||||
BuildRequest req = getRequest(cursorX, cursorY);
|
||||
if(req != null){
|
||||
drawSelected(req.x, req.y, req.breaking ? req.tile().block() : req.block, Pal.accent);
|
||||
}
|
||||
}
|
||||
|
||||
//draw schematic requests
|
||||
for(BuildRequest request : selectRequests){
|
||||
request.animScale = 1f;
|
||||
drawRequest(request);
|
||||
}
|
||||
|
||||
if(sreq != null){
|
||||
boolean valid = validPlace(sreq.x, sreq.y, sreq.block, sreq.rotation, sreq);
|
||||
if(sreq.block.rotate){
|
||||
drawArrow(sreq.block, sreq.x, sreq.y, sreq.rotation, valid);
|
||||
}
|
||||
|
||||
sreq.block.drawRequest(sreq, allRequests(), valid);
|
||||
|
||||
drawSelected(sreq.x, sreq.y, sreq.block, getRequest(sreq.x, sreq.y, sreq.block.size, sreq) != null ? Pal.remove : Pal.accent);
|
||||
}
|
||||
|
||||
if(Core.input.keyDown(Binding.schematic_select) && !ui.chatfrag.chatOpen()){
|
||||
drawSelection(schemX, schemY, cursorX, cursorY, Vars.maxSchematicSize);
|
||||
}
|
||||
|
||||
Draw.reset();
|
||||
}
|
||||
|
||||
@@ -142,7 +150,7 @@ public class DesktopInput extends InputHandler{
|
||||
if(state.is(State.menu) || Core.scene.hasDialog()) return;
|
||||
|
||||
//zoom things
|
||||
if(Math.abs(Core.input.axisTap(Binding.zoom)) > 0 && (Core.input.keyDown(Binding.zoom_hold))){
|
||||
if(Math.abs(Core.input.axisTap(Binding.zoom)) > 0 && Core.input.keyDown(Binding.zoom_hold)){
|
||||
renderer.scaleCamera(Core.input.axisTap(Binding.zoom));
|
||||
}
|
||||
|
||||
@@ -158,6 +166,11 @@ public class DesktopInput extends InputHandler{
|
||||
mode = none;
|
||||
}
|
||||
|
||||
if(mode != none || isPlacing()){
|
||||
selectRequests.clear();
|
||||
lastSchematic = null;
|
||||
}
|
||||
|
||||
if(player.isShooting && !canShoot()){
|
||||
player.isShooting = false;
|
||||
}
|
||||
@@ -169,7 +182,20 @@ public class DesktopInput extends InputHandler{
|
||||
selectScale = 0f;
|
||||
}
|
||||
|
||||
rotation = Mathf.mod(rotation + (int)Core.input.axisTap(Binding.rotate), 4);
|
||||
if(!Core.input.keyDown(Binding.zoom_hold) && Math.abs((int)Core.input.axisTap(Binding.rotate)) > 0){
|
||||
|
||||
rotation = Mathf.mod(rotation + (int)Core.input.axisTap(Binding.rotate), 4);
|
||||
|
||||
if(sreq != null){
|
||||
sreq.rotation = Mathf.mod(sreq.rotation + (int)Core.input.axisTap(Binding.rotate), 4);
|
||||
}
|
||||
|
||||
if(isPlacing() && mode == placing){
|
||||
updateLine(selectX, selectY);
|
||||
}else if(!selectRequests.isEmpty()){
|
||||
rotateRequests(selectRequests, (int)Core.input.axisTap(Binding.rotate));
|
||||
}
|
||||
}
|
||||
|
||||
Tile cursor = tileAt(Core.input.mouseX(), Core.input.mouseY());
|
||||
|
||||
@@ -178,7 +204,7 @@ public class DesktopInput extends InputHandler{
|
||||
|
||||
cursorType = cursor.block().getCursor(cursor);
|
||||
|
||||
if(isPlacing()){
|
||||
if(isPlacing() || !selectRequests.isEmpty()){
|
||||
cursorType = SystemCursor.hand;
|
||||
}
|
||||
|
||||
@@ -186,6 +212,10 @@ public class DesktopInput extends InputHandler{
|
||||
cursorType = ui.drillCursor;
|
||||
}
|
||||
|
||||
if(getRequest(cursor.x, cursor.y) != null && mode == none){
|
||||
cursorType = SystemCursor.hand;
|
||||
}
|
||||
|
||||
if(canTapPlayer(Core.input.mouseWorld().x, Core.input.mouseWorld().y)){
|
||||
cursorType = ui.unloadCursor;
|
||||
}
|
||||
@@ -202,46 +232,173 @@ public class DesktopInput extends InputHandler{
|
||||
cursorType = SystemCursor.arrow;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void useSchematic(Schematic schem){
|
||||
block = null;
|
||||
schematicX = tileX(getMouseX());
|
||||
schematicY = tileY(getMouseY());
|
||||
|
||||
selectRequests.clear();
|
||||
selectRequests.addAll(schematics.toRequests(schem, schematicX, schematicY));
|
||||
mode = none;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBreaking(){
|
||||
return mode == breaking;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void buildPlacementUI(Table table){
|
||||
table.addImage().color(Pal.gray).height(4f).colspan(4).growX();
|
||||
table.row();
|
||||
table.left().margin(0f).defaults().size(48f).left();
|
||||
|
||||
table.addImageButton(Icon.pasteSmall, Styles.clearPartiali, () -> {
|
||||
ui.schematics.show();
|
||||
});
|
||||
}
|
||||
|
||||
void pollInput(){
|
||||
if(scene.getKeyboardFocus() instanceof TextField) return;
|
||||
|
||||
Tile selected = tileAt(Core.input.mouseX(), Core.input.mouseY());
|
||||
int cursorX = tileX(Core.input.mouseX());
|
||||
int cursorY = tileY(Core.input.mouseY());
|
||||
int rawCursorX = world.toTile(Core.input.mouseWorld().x), rawCursorY = world.toTile(Core.input.mouseWorld().y);
|
||||
|
||||
// automatically pause building if the current build queue is empty
|
||||
if(Core.settings.getBool("buildautopause") && player.isBuilding && !player.isBuilding()){
|
||||
player.isBuilding = false;
|
||||
player.buildWasAutoPaused = true;
|
||||
}
|
||||
|
||||
if(!selectRequests.isEmpty()){
|
||||
int shiftX = rawCursorX - schematicX, shiftY = rawCursorY - schematicY;
|
||||
|
||||
selectRequests.each(s -> {
|
||||
s.x += shiftX;
|
||||
s.y += shiftY;
|
||||
});
|
||||
|
||||
schematicX += shiftX;
|
||||
schematicY += shiftY;
|
||||
}
|
||||
|
||||
if(Core.input.keyTap(Binding.deselect)){
|
||||
player.setMineTile(null);
|
||||
}
|
||||
|
||||
if(Core.input.keyTap(Binding.clear_building)){
|
||||
player.clearBuilding();
|
||||
}
|
||||
|
||||
if(Core.input.keyTap(Binding.schematic_select) && !ui.chatfrag.chatOpen()){
|
||||
schemX = rawCursorX;
|
||||
schemY = rawCursorY;
|
||||
}
|
||||
|
||||
if(Core.input.keyTap(Binding.schematic_menu) && !ui.chatfrag.chatOpen()){
|
||||
if(ui.schematics.isShown()){
|
||||
ui.schematics.hide();
|
||||
}else{
|
||||
ui.schematics.show();
|
||||
}
|
||||
}
|
||||
|
||||
if(Core.input.keyTap(Binding.clear_building)){
|
||||
lastSchematic = null;
|
||||
selectRequests.clear();
|
||||
}
|
||||
|
||||
if(Core.input.keyRelease(Binding.schematic_select) && !ui.chatfrag.chatOpen()){
|
||||
lastSchematic = schematics.create(schemX, schemY, rawCursorX, rawCursorY);
|
||||
useSchematic(lastSchematic);
|
||||
if(selectRequests.isEmpty()){
|
||||
lastSchematic = null;
|
||||
}
|
||||
}
|
||||
|
||||
if(!selectRequests.isEmpty()){
|
||||
if(Core.input.keyTap(Binding.schematic_flip_x)){
|
||||
flipRequests(selectRequests, true);
|
||||
}
|
||||
|
||||
if(Core.input.keyTap(Binding.schematic_flip_y)){
|
||||
flipRequests(selectRequests, false);
|
||||
}
|
||||
}
|
||||
|
||||
if(sreq != null){
|
||||
float offset = ((sreq.block.size + 2) % 2) * tilesize / 2f;
|
||||
float x = Core.input.mouseWorld().x + offset;
|
||||
float y = Core.input.mouseWorld().y + offset;
|
||||
sreq.x = (int)(x / tilesize);
|
||||
sreq.y = (int)(y / tilesize);
|
||||
}
|
||||
|
||||
if(block == null || mode != placing){
|
||||
lineRequests.clear();
|
||||
}
|
||||
|
||||
if(Core.input.keyTap(Binding.pause_building)){
|
||||
player.isBuilding = !player.isBuilding;
|
||||
player.buildWasAutoPaused = false;
|
||||
}
|
||||
|
||||
if((cursorX != lastLineX || cursorY != lastLineY) && isPlacing() && mode == placing){
|
||||
updateLine(selectX, selectY);
|
||||
lastLineX = cursorX;
|
||||
lastLineY = cursorY;
|
||||
}
|
||||
|
||||
if(Core.input.keyTap(Binding.select) && !Core.scene.hasMouse()){
|
||||
if(isPlacing()){
|
||||
BuildRequest req = getRequest(cursorX, cursorY);
|
||||
|
||||
if(!selectRequests.isEmpty()){
|
||||
flushRequests(selectRequests);
|
||||
}else if(isPlacing()){
|
||||
selectX = cursorX;
|
||||
selectY = cursorY;
|
||||
lastLineX = cursorX;
|
||||
lastLineY = cursorY;
|
||||
mode = placing;
|
||||
updateLine(selectX, selectY);
|
||||
}else if(req != null && !req.breaking && mode == none && !req.initialized){
|
||||
sreq = req;
|
||||
}else if(req != null && req.breaking){
|
||||
deleting = true;
|
||||
}else if(selected != null){
|
||||
//only begin shooting if there's no cursor event
|
||||
if(!tileTapped(selected) && !tryTapPlayer(Core.input.mouseWorld().x, Core.input.mouseWorld().y) && player.buildQueue().size == 0 && !droppingItem &&
|
||||
if(!tileTapped(selected) && !tryTapPlayer(Core.input.mouseWorld().x, Core.input.mouseWorld().y) && (player.buildQueue().size == 0 || !player.isBuilding) && !droppingItem &&
|
||||
!tryBeginMine(selected) && player.getMineTile() == null && !ui.chatfrag.chatOpen()){
|
||||
player.isShooting = true;
|
||||
}
|
||||
}else if(!ui.chatfrag.chatOpen()){ //if it's out of bounds, shooting is just fine
|
||||
player.isShooting = true;
|
||||
}
|
||||
}else if(Core.input.keyTap(Binding.deselect) && (block != null || mode != none || player.isBuilding()) &&
|
||||
!(player.buildRequest() != null && player.buildRequest().breaking && Core.keybinds.get(Binding.deselect) == Core.keybinds.get(Binding.break_block))){
|
||||
if(block == null){
|
||||
player.clearBuilding();
|
||||
}
|
||||
|
||||
}else if(Core.input.keyTap(Binding.deselect) && block != null){
|
||||
block = null;
|
||||
mode = none;
|
||||
}else if(Core.input.keyTap(Binding.break_block) && !Core.scene.hasMouse()){
|
||||
//is recalculated because setting the mode to breaking removes potential multiblock cursor offset
|
||||
deleting = false;
|
||||
mode = breaking;
|
||||
selectX = tileX(Core.input.mouseX());
|
||||
selectY = tileY(Core.input.mouseY());
|
||||
}
|
||||
|
||||
if (mode == placing && block != null){
|
||||
if (!overrideLineRotation && !Core.input.keyDown(Binding.diagonal_placement) && (selectX != cursorX || selectY != cursorY) && ((int) Core.input.axisTap(Binding.rotate) != 0)){
|
||||
if(Core.input.keyDown(Binding.select) && mode == none && !isPlacing() && deleting){
|
||||
BuildRequest req = getRequest(cursorX, cursorY);
|
||||
if(req != null && req.breaking){
|
||||
player.buildQueue().remove(req);
|
||||
}
|
||||
}else{
|
||||
deleting = false;
|
||||
}
|
||||
|
||||
if(mode == placing && block != null){
|
||||
if(!overrideLineRotation && !Core.input.keyDown(Binding.diagonal_placement) && (selectX != cursorX || selectY != cursorY) && ((int) Core.input.axisTap(Binding.rotate) != 0)){
|
||||
rotation = ((int)((Angles.angle(selectX, selectY, cursorX, cursorY) + 45) / 90f)) % 4;
|
||||
overrideLineRotation = true;
|
||||
}
|
||||
@@ -252,29 +409,35 @@ public class DesktopInput extends InputHandler{
|
||||
if(Core.input.keyRelease(Binding.break_block) || Core.input.keyRelease(Binding.select)){
|
||||
|
||||
if(mode == placing && block != null){ //touch up while placing, place everything in selection
|
||||
iterateLine(selectX, selectY, cursorX, cursorY, l -> {
|
||||
rotation = l.rotation;
|
||||
tryPlaceBlock(l.x, l.y);
|
||||
});
|
||||
flushRequests(lineRequests);
|
||||
lineRequests.clear();
|
||||
Events.fire(new LineConfirmEvent());
|
||||
}else if(mode == breaking){ //touch up while breaking, break everything in selection
|
||||
NormalizeResult result = PlaceUtils.normalizeArea(selectX, selectY, cursorX, cursorY, rotation, false, maxLength);
|
||||
for(int x = 0; x <= Math.abs(result.x2 - result.x); x++){
|
||||
for(int y = 0; y <= Math.abs(result.y2 - result.y); y++){
|
||||
int wx = selectX + x * Mathf.sign(cursorX - selectX);
|
||||
int wy = selectY + y * Mathf.sign(cursorY - selectY);
|
||||
|
||||
tryBreakBlock(wx, wy);
|
||||
}
|
||||
}
|
||||
removeSelection(selectX, selectY, cursorX, cursorY);
|
||||
}
|
||||
|
||||
if(selected != null){
|
||||
tryDropItems(selected.link(), Core.input.mouseWorld().x, Core.input.mouseWorld().y);
|
||||
}
|
||||
|
||||
if(sreq != null){
|
||||
if(getRequest(sreq.x, sreq.y, sreq.block.size, sreq) != null){
|
||||
player.buildQueue().remove(sreq, true);
|
||||
}
|
||||
sreq = null;
|
||||
}
|
||||
|
||||
mode = none;
|
||||
}
|
||||
|
||||
if(Core.input.keyTap(Binding.toggle_power_lines)){
|
||||
if(Core.settings.getInt("lasersopacity") == 0){
|
||||
Core.settings.put("lasersopacity", Core.settings.getInt("preferredlaseropacity", 100));
|
||||
}else{
|
||||
Core.settings.put("preferredlaseropacity", Core.settings.getInt("lasersopacity"));
|
||||
Core.settings.put("lasersopacity", 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -298,6 +461,8 @@ public class DesktopInput extends InputHandler{
|
||||
droppingItem = false;
|
||||
mode = none;
|
||||
block = null;
|
||||
sreq = null;
|
||||
selectRequests.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,13 +3,17 @@ package io.anuke.mindustry.input;
|
||||
import io.anuke.annotations.Annotations.*;
|
||||
import io.anuke.arc.*;
|
||||
import io.anuke.arc.collection.*;
|
||||
import io.anuke.arc.function.*;
|
||||
import io.anuke.arc.func.*;
|
||||
import io.anuke.arc.graphics.*;
|
||||
import io.anuke.arc.graphics.g2d.*;
|
||||
import io.anuke.arc.input.*;
|
||||
import io.anuke.arc.input.GestureDetector.*;
|
||||
import io.anuke.arc.math.*;
|
||||
import io.anuke.arc.math.geom.*;
|
||||
import io.anuke.arc.scene.*;
|
||||
import io.anuke.arc.scene.event.*;
|
||||
import io.anuke.arc.scene.ui.layout.*;
|
||||
import io.anuke.arc.util.ArcAnnotate.*;
|
||||
import io.anuke.arc.util.*;
|
||||
import io.anuke.mindustry.content.*;
|
||||
import io.anuke.mindustry.entities.*;
|
||||
@@ -17,21 +21,30 @@ import io.anuke.mindustry.entities.effect.*;
|
||||
import io.anuke.mindustry.entities.traits.BuilderTrait.*;
|
||||
import io.anuke.mindustry.entities.type.*;
|
||||
import io.anuke.mindustry.game.EventType.*;
|
||||
import io.anuke.mindustry.game.*;
|
||||
import io.anuke.mindustry.game.Teams.*;
|
||||
import io.anuke.mindustry.gen.*;
|
||||
import io.anuke.mindustry.graphics.*;
|
||||
import io.anuke.mindustry.input.Placement.*;
|
||||
import io.anuke.mindustry.net.*;
|
||||
import io.anuke.mindustry.type.*;
|
||||
import io.anuke.mindustry.ui.fragments.*;
|
||||
import io.anuke.mindustry.world.*;
|
||||
import io.anuke.mindustry.world.blocks.*;
|
||||
import io.anuke.mindustry.world.blocks.BuildBlock.*;
|
||||
import io.anuke.mindustry.world.blocks.power.PowerNode;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import static io.anuke.mindustry.Vars.*;
|
||||
|
||||
public abstract class InputHandler implements InputProcessor{
|
||||
public abstract class InputHandler implements InputProcessor, GestureListener{
|
||||
/** Used for dropping items. */
|
||||
final static float playerSelectRange = mobile ? 17f : 11f;
|
||||
/** Maximum line length. */
|
||||
final static int maxLength = 100;
|
||||
final static Vector2 stackTrns = new Vector2();
|
||||
final static Rectangle r1 = new Rectangle(), r2 = new Rectangle();
|
||||
/** Distance on the back from where items originate. */
|
||||
final static float backTrns = 3f;
|
||||
|
||||
@@ -41,9 +54,15 @@ public abstract class InputHandler implements InputProcessor{
|
||||
public boolean overrideLineRotation;
|
||||
public int rotation;
|
||||
public boolean droppingItem;
|
||||
public Group uiGroup;
|
||||
|
||||
protected PlaceDraw placeDraw = new PlaceDraw();
|
||||
private PlaceLine line = new PlaceLine();
|
||||
protected @Nullable Schematic lastSchematic;
|
||||
protected GestureDetector detector;
|
||||
protected PlaceLine line = new PlaceLine();
|
||||
protected BuildRequest resultreq;
|
||||
protected BuildRequest brequest = new BuildRequest();
|
||||
protected Array<BuildRequest> lineRequests = new Array<>();
|
||||
protected Array<BuildRequest> selectRequests = new Array<>();
|
||||
|
||||
//methods to override
|
||||
|
||||
@@ -92,7 +111,7 @@ public abstract class InputHandler implements InputProcessor{
|
||||
int[] remaining = {accepted, accepted};
|
||||
Block block = tile.block();
|
||||
|
||||
Events.fire(new DepositEvent());
|
||||
Core.app.post(() -> Events.fire(new DepositEvent(tile, player, item, accepted)));
|
||||
|
||||
for(int i = 0; i < sent; i++){
|
||||
boolean end = i == sent - 1;
|
||||
@@ -126,12 +145,22 @@ public abstract class InputHandler implements InputProcessor{
|
||||
if(tile == null || player == null) return;
|
||||
if(!Units.canInteract(player, tile)) return;
|
||||
tile.block().tapped(tile, player);
|
||||
Core.app.post(() -> Events.fire(new TapEvent(tile, player)));
|
||||
}
|
||||
|
||||
@Remote(targets = Loc.both, called = Loc.both, forward = true)
|
||||
public static void onTileConfig(Player player, Tile tile, int value){
|
||||
if(tile == null || !Units.canInteract(player, tile)) return;
|
||||
tile.block().configured(tile, player, value);
|
||||
Core.app.post(() -> Events.fire(new TapConfigEvent(tile, player, value)));
|
||||
}
|
||||
|
||||
public Eachable<BuildRequest> allRequests(){
|
||||
return cons -> {
|
||||
for(BuildRequest request : player.buildQueue()) cons.get(request);
|
||||
for(BuildRequest request : selectRequests) cons.get(request);
|
||||
for(BuildRequest request : lineRequests) cons.get(request);
|
||||
};
|
||||
}
|
||||
|
||||
public OverlayFragment getFrag(){
|
||||
@@ -150,7 +179,11 @@ public abstract class InputHandler implements InputProcessor{
|
||||
return Core.input.mouseY();
|
||||
}
|
||||
|
||||
public void buildUI(Table table){
|
||||
public void buildPlacementUI(Table table){
|
||||
|
||||
}
|
||||
|
||||
public void buildUI(Group group){
|
||||
|
||||
}
|
||||
|
||||
@@ -158,7 +191,7 @@ public abstract class InputHandler implements InputProcessor{
|
||||
|
||||
}
|
||||
|
||||
public void drawOutlined(){
|
||||
public void drawBottom(){
|
||||
|
||||
}
|
||||
|
||||
@@ -166,8 +199,348 @@ public abstract class InputHandler implements InputProcessor{
|
||||
|
||||
}
|
||||
|
||||
public boolean isDrawing(){
|
||||
return false;
|
||||
public void drawSelected(int x, int y, Block block, Color color){
|
||||
Draw.color(color);
|
||||
for(int i = 0; i < 4; i++){
|
||||
Point2 p = Geometry.d8edge[i];
|
||||
float offset = -Math.max(block.size - 1, 0) / 2f * tilesize;
|
||||
Draw.rect("block-select",
|
||||
x*tilesize + block.offset() + offset * p.x,
|
||||
y*tilesize + block.offset() + offset * p.y, i * 90);
|
||||
}
|
||||
Draw.reset();
|
||||
}
|
||||
|
||||
public void drawBreaking(BuildRequest request){
|
||||
if(request.breaking){
|
||||
drawBreaking(request.x, request.y);
|
||||
}else{
|
||||
drawSelected(request.x, request.y, request.block, Pal.remove);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean requestMatches(BuildRequest request){
|
||||
Tile tile = world.tile(request.x, request.y);
|
||||
return tile != null && tile.block() instanceof BuildBlock && tile.<BuildEntity>entity().cblock == request.block;
|
||||
}
|
||||
|
||||
public void drawBreaking(int x, int y){
|
||||
Tile tile = world.ltile(x, y);
|
||||
if(tile == null) return;
|
||||
Block block = tile.block();
|
||||
|
||||
drawSelected(x, y, block, Pal.remove);
|
||||
}
|
||||
|
||||
public void useSchematic(Schematic schem){
|
||||
selectRequests.addAll(schematics.toRequests(schem, world.toTile(player.x), world.toTile(player.y)));
|
||||
}
|
||||
|
||||
protected void showSchematicSave(){
|
||||
if(lastSchematic == null) return;
|
||||
|
||||
ui.showTextInput("$schematic.add", "$name", "", text -> {
|
||||
Schematic replacement = schematics.all().find(s -> s.name().equals(text));
|
||||
if(replacement != null){
|
||||
ui.showConfirm("$confirm", "$schematic.replace", () -> {
|
||||
schematics.overwrite(replacement, lastSchematic);
|
||||
ui.showInfoFade("$schematic.saved");
|
||||
ui.schematics.showInfo(replacement);
|
||||
});
|
||||
}else{
|
||||
lastSchematic.tags.put("name", text);
|
||||
schematics.add(lastSchematic);
|
||||
ui.showInfoFade("$schematic.saved");
|
||||
ui.schematics.showInfo(lastSchematic);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void rotateRequests(Array<BuildRequest> requests, int direction){
|
||||
int ox = schemOriginX(), oy = schemOriginY();
|
||||
|
||||
requests.each(req -> {
|
||||
//rotate config position
|
||||
if(req.block.posConfig){
|
||||
int cx = Pos.x(req.config) - req.originalX, cy = Pos.y(req.config) - req.originalY;
|
||||
int lx = cx;
|
||||
|
||||
if(direction >= 0){
|
||||
cx = -cy;
|
||||
cy = lx;
|
||||
}else{
|
||||
cx = cy;
|
||||
cy = -lx;
|
||||
}
|
||||
req.config = Pos.get(cx + req.originalX, cy + req.originalY);
|
||||
}
|
||||
|
||||
//rotate actual request, centered on its multiblock position
|
||||
float wx = (req.x - ox) * tilesize + req.block.offset(), wy = (req.y - oy) * tilesize + req.block.offset();
|
||||
float x = wx;
|
||||
if(direction >= 0){
|
||||
wx = -wy;
|
||||
wy = x;
|
||||
}else{
|
||||
wx = wy;
|
||||
wy = -x;
|
||||
}
|
||||
req.x = world.toTile(wx - req.block.offset()) + ox;
|
||||
req.y = world.toTile(wy - req.block.offset()) + oy;
|
||||
req.rotation = Mathf.mod(req.rotation + direction, 4);
|
||||
});
|
||||
}
|
||||
|
||||
public void flipRequests(Array<BuildRequest> requests, boolean x){
|
||||
int origin = (x ? schemOriginX() : schemOriginY()) * tilesize;
|
||||
|
||||
requests.each(req -> {
|
||||
float value = -((x ? req.x : req.y) * tilesize - origin + req.block.offset()) + origin;
|
||||
|
||||
if(x){
|
||||
req.x = (int)((value - req.block.offset()) / tilesize);
|
||||
}else{
|
||||
req.y = (int)((value - req.block.offset()) / tilesize);
|
||||
}
|
||||
|
||||
if(req.block.posConfig){
|
||||
int corigin = x ? req.originalWidth/2 : req.originalHeight/2;
|
||||
int nvalue = -((x ? Pos.x(req.config) : Pos.y(req.config)) - corigin) + corigin;
|
||||
if(x){
|
||||
req.originalX = -(req.originalX - corigin) + corigin;
|
||||
req.config = Pos.get(nvalue, Pos.y(req.config));
|
||||
}else{
|
||||
req.originalY = -(req.originalY - corigin) + corigin;
|
||||
req.config = Pos.get(Pos.x(req.config), nvalue);
|
||||
}
|
||||
}
|
||||
|
||||
//flip rotation
|
||||
if(x == (req.rotation % 2 == 0)){
|
||||
req.rotation = Mathf.mod(req.rotation + 2, 4);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected int schemOriginX(){
|
||||
return rawTileX();
|
||||
}
|
||||
|
||||
protected int schemOriginY(){
|
||||
return rawTileY();
|
||||
}
|
||||
|
||||
/** Returns the selection request that overlaps this position, or null. */
|
||||
protected BuildRequest getRequest(int x, int y){
|
||||
return getRequest(x, y, 1, null);
|
||||
}
|
||||
|
||||
/** Returns the selection request that overlaps this position, or null. */
|
||||
protected BuildRequest getRequest(int x, int y, int size, BuildRequest skip){
|
||||
float offset = ((size + 1) % 2) * tilesize / 2f;
|
||||
r2.setSize(tilesize * size);
|
||||
r2.setCenter(x * tilesize + offset, y * tilesize + offset);
|
||||
resultreq = null;
|
||||
|
||||
Boolf<BuildRequest> test = req -> {
|
||||
if(req == skip) return false;
|
||||
Tile other = req.tile();
|
||||
|
||||
if(other == null) return false;
|
||||
|
||||
if(!req.breaking){
|
||||
r1.setSize(req.block.size * tilesize);
|
||||
r1.setCenter(other.worldx() + req.block.offset(), other.worldy() + req.block.offset());
|
||||
}else{
|
||||
r1.setSize(other.block().size * tilesize);
|
||||
r1.setCenter(other.worldx() + other.block().offset(), other.worldy() + other.block().offset());
|
||||
}
|
||||
|
||||
return r2.overlaps(r1);
|
||||
};
|
||||
|
||||
for(BuildRequest req : player.buildQueue()){
|
||||
if(test.get(req)) return req;
|
||||
}
|
||||
|
||||
for(BuildRequest req : selectRequests){
|
||||
if(test.get(req)) return req;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
protected void drawBreakSelection(int x1, int y1, int x2, int y2){
|
||||
NormalizeDrawResult result = Placement.normalizeDrawArea(Blocks.air, x1, y1, x2, y2, false, maxLength, 1f);
|
||||
NormalizeResult dresult = Placement.normalizeArea(x1, y1, x2, y2, rotation, false, maxLength);
|
||||
|
||||
for(int x = dresult.x; x <= dresult.x2; x++){
|
||||
for(int y = dresult.y; y <= dresult.y2; y++){
|
||||
Tile tile = world.ltile(x, y);
|
||||
if(tile == null || !validBreak(tile.x, tile.y)) continue;
|
||||
|
||||
drawBreaking(tile.x, tile.y);
|
||||
}
|
||||
}
|
||||
|
||||
Tmp.r1.set(result.x, result.y, result.x2 - result.x, result.y2 - result.y);
|
||||
|
||||
Draw.color(Pal.remove);
|
||||
Lines.stroke(1f);
|
||||
|
||||
for(BuildRequest req : player.buildQueue()){
|
||||
if(req.breaking) continue;
|
||||
if(req.bounds(Tmp.r2).overlaps(Tmp.r1)){
|
||||
drawBreaking(req);
|
||||
}
|
||||
}
|
||||
|
||||
for(BuildRequest req : selectRequests){
|
||||
if(req.breaking) continue;
|
||||
if(req.bounds(Tmp.r2).overlaps(Tmp.r1)){
|
||||
drawBreaking(req);
|
||||
}
|
||||
}
|
||||
|
||||
for(BrokenBlock req : state.teams.get(player.getTeam()).brokenBlocks){
|
||||
Block block = content.block(req.block);
|
||||
if(block.bounds(req.x, req.y, Tmp.r2).overlaps(Tmp.r1)){
|
||||
drawSelected(req.x, req.y, content.block(req.block), Pal.remove);
|
||||
}
|
||||
}
|
||||
|
||||
Lines.stroke(2f);
|
||||
|
||||
Draw.color(Pal.removeBack);
|
||||
Lines.rect(result.x, result.y - 1, result.x2 - result.x, result.y2 - result.y);
|
||||
Draw.color(Pal.remove);
|
||||
Lines.rect(result.x, result.y, result.x2 - result.x, result.y2 - result.y);
|
||||
}
|
||||
|
||||
protected void drawSelection(int x1, int y1, int x2, int y2, int maxLength){
|
||||
NormalizeDrawResult result = Placement.normalizeDrawArea(Blocks.air, x1, y1, x2, y2, false, maxLength, 1f);
|
||||
|
||||
Lines.stroke(2f);
|
||||
|
||||
Draw.color(Pal.accentBack);
|
||||
Lines.rect(result.x, result.y - 1, result.x2 - result.x, result.y2 - result.y);
|
||||
Draw.color(Pal.accent);
|
||||
Lines.rect(result.x, result.y, result.x2 - result.x, result.y2 - result.y);
|
||||
}
|
||||
|
||||
protected void flushSelectRequests(Array<BuildRequest> requests){
|
||||
for(BuildRequest req : requests){
|
||||
if(req.block != null && validPlace(req.x, req.y, req.block, req.rotation)){
|
||||
BuildRequest other = getRequest(req.x, req.y, req.block.size, null);
|
||||
if(other == null){
|
||||
selectRequests.add(req.copy());
|
||||
}else if(!other.breaking && other.x == req.x && other.y == req.y && other.block.size == req.block.size){
|
||||
selectRequests.remove(other);
|
||||
selectRequests.add(req.copy());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void flushRequests(Array<BuildRequest> requests){
|
||||
for(BuildRequest req : requests){
|
||||
if(req.block != null && validPlace(req.x, req.y, req.block, req.rotation)){
|
||||
BuildRequest copy = req.copy();
|
||||
if(copy.hasConfig && copy.block.posConfig){
|
||||
copy.config = Pos.get(Pos.x(copy.config) + copy.x - copy.originalX, Pos.y(copy.config) + copy.y - copy.originalY);
|
||||
}
|
||||
player.addBuildRequest(copy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void drawRequest(BuildRequest request){
|
||||
drawRequest(request.x, request.y, request.block, request.rotation);
|
||||
}
|
||||
|
||||
/** Draws a placement icon for a specific block. */
|
||||
protected void drawRequest(int x, int y, Block block, int rotation){
|
||||
brequest.set(x, y, rotation, block);
|
||||
brequest.animScale = 1f;
|
||||
block.drawRequest(brequest, allRequests(), validPlace(x, y, block, rotation));
|
||||
}
|
||||
|
||||
/** Remove everything from the queue in a selection. */
|
||||
protected void removeSelection(int x1, int y1, int x2, int y2){
|
||||
removeSelection(x1, y1, x2, y2, false);
|
||||
}
|
||||
|
||||
/** Remove everything from the queue in a selection. */
|
||||
protected void removeSelection(int x1, int y1, int x2, int y2, boolean flush){
|
||||
NormalizeResult result = Placement.normalizeArea(x1, y1, x2, y2, rotation, false, maxLength);
|
||||
for(int x = 0; x <= Math.abs(result.x2 - result.x); x++){
|
||||
for(int y = 0; y <= Math.abs(result.y2 - result.y); y++){
|
||||
int wx = x1 + x * Mathf.sign(x2 - x1);
|
||||
int wy = y1 + y * Mathf.sign(y2 - y1);
|
||||
|
||||
Tile tile = world.ltile(wx, wy);
|
||||
|
||||
if(tile == null) continue;
|
||||
|
||||
if(!flush){
|
||||
tryBreakBlock(wx, wy);
|
||||
}else if(validBreak(tile.x, tile.y) && !selectRequests.contains(r -> r.tile() != null && r.tile().link() == tile)){
|
||||
selectRequests.add(new BuildRequest(tile.x, tile.y));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//remove build requests
|
||||
Tmp.r1.set(result.x * tilesize, result.y * tilesize, (result.x2 - result.x) * tilesize, (result.y2 - result.y) * tilesize);
|
||||
|
||||
Iterator<BuildRequest> it = player.buildQueue().iterator();
|
||||
while(it.hasNext()){
|
||||
BuildRequest req = it.next();
|
||||
if(!req.breaking && req.bounds(Tmp.r2).overlaps(Tmp.r1)){
|
||||
it.remove();
|
||||
}
|
||||
}
|
||||
|
||||
it = selectRequests.iterator();
|
||||
while(it.hasNext()){
|
||||
BuildRequest req = it.next();
|
||||
if(!req.breaking && req.bounds(Tmp.r2).overlaps(Tmp.r1)){
|
||||
it.remove();
|
||||
}
|
||||
}
|
||||
|
||||
//remove blocks to rebuild
|
||||
Iterator<BrokenBlock> broken = state.teams.get(player.getTeam()).brokenBlocks.iterator();
|
||||
while(broken.hasNext()){
|
||||
BrokenBlock req = broken.next();
|
||||
Block block = content.block(req.block);
|
||||
if(block.bounds(req.x, req.y, Tmp.r2).overlaps(Tmp.r1)){
|
||||
broken.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void updateLine(int x1, int y1, int x2, int y2){
|
||||
lineRequests.clear();
|
||||
iterateLine(x1, y1, x2, y2, l -> {
|
||||
rotation = l.rotation;
|
||||
BuildRequest req = new BuildRequest(l.x, l.y, l.rotation, block);
|
||||
req.animScale = 1f;
|
||||
lineRequests.add(req);
|
||||
});
|
||||
|
||||
if(Core.settings.getBool("blockreplace")){
|
||||
lineRequests.each(req -> {
|
||||
Block replace = req.block.getReplacement(req, lineRequests);
|
||||
if(replace.unlockedCur()){
|
||||
req.block = replace;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
protected void updateLine(int x1, int y1){
|
||||
updateLine(x1, y1, tileX(getMouseX()), tileY(getMouseY()));
|
||||
}
|
||||
|
||||
/** Handles tile tap events that are not platform specific. */
|
||||
@@ -214,13 +587,6 @@ public abstract class InputHandler implements InputProcessor{
|
||||
}
|
||||
}
|
||||
|
||||
//clear when the player taps on something else
|
||||
if(!consumed && !mobile && player.isBuilding() && block == null){
|
||||
player.clearBuilding();
|
||||
block = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
if(!showedInventory){
|
||||
frag.inv.hide();
|
||||
}
|
||||
@@ -264,6 +630,14 @@ public abstract class InputHandler implements InputProcessor{
|
||||
return world.tile(tileX(x), tileY(y));
|
||||
}
|
||||
|
||||
int rawTileX(){
|
||||
return world.toTile(Core.input.mouseWorld().x);
|
||||
}
|
||||
|
||||
int rawTileY(){
|
||||
return world.toTile(Core.input.mouseWorld().y);
|
||||
}
|
||||
|
||||
int tileX(float cursorX){
|
||||
Vector2 vec = Core.input.mouseWorld(cursorX, 0);
|
||||
if(selectedBlock()){
|
||||
@@ -288,6 +662,10 @@ public abstract class InputHandler implements InputProcessor{
|
||||
return block != null;
|
||||
}
|
||||
|
||||
public boolean isBreaking(){
|
||||
return false;
|
||||
}
|
||||
|
||||
public float mouseAngle(float x, float y){
|
||||
return Core.input.mouseWorld(getMouseX(), getMouseY()).sub(x, y).angle();
|
||||
}
|
||||
@@ -301,16 +679,36 @@ public abstract class InputHandler implements InputProcessor{
|
||||
table.clear();
|
||||
}
|
||||
}
|
||||
if(detector != null){
|
||||
Core.input.removeProcessor(detector);
|
||||
}
|
||||
if(uiGroup != null){
|
||||
uiGroup.remove();
|
||||
uiGroup = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void add(){
|
||||
Core.input.addProcessor(detector = new GestureDetector(20, 0.5f, 0.4f, 0.15f, this));
|
||||
Core.input.addProcessor(this);
|
||||
if(Core.scene != null){
|
||||
Table table = (Table)Core.scene.find("inputTable");
|
||||
if(table != null){
|
||||
table.clear();
|
||||
buildUI(table);
|
||||
buildPlacementUI(table);
|
||||
}
|
||||
|
||||
uiGroup = new WidgetGroup();
|
||||
uiGroup.touchable(Touchable.childrenOnly);
|
||||
uiGroup.setFillParent(true);
|
||||
ui.hudGroup.addChild(uiGroup);
|
||||
buildUI(uiGroup);
|
||||
|
||||
frag.add();
|
||||
}
|
||||
|
||||
if(player != null){
|
||||
player.isBuilding = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -356,6 +754,18 @@ public abstract class InputHandler implements InputProcessor{
|
||||
}
|
||||
|
||||
public boolean validPlace(int x, int y, Block type, int rotation){
|
||||
return validPlace(x, y, type, rotation, null);
|
||||
}
|
||||
|
||||
public boolean validPlace(int x, int y, Block type, int rotation, BuildRequest ignore){
|
||||
for(BuildRequest req : player.buildQueue()){
|
||||
if(req != ignore
|
||||
&& !req.breaking
|
||||
&& req.block.bounds(req.x, req.y, Tmp.r1).overlaps(type.bounds(x, y, Tmp.r2))
|
||||
&& !(type.canReplace(req.block) && Tmp.r1.equals(Tmp.r2))){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return Build.validPlace(player.getTeam(), x, y, type, rotation);
|
||||
}
|
||||
|
||||
@@ -364,6 +774,10 @@ public abstract class InputHandler implements InputProcessor{
|
||||
}
|
||||
|
||||
public void placeBlock(int x, int y, Block block, int rotation){
|
||||
BuildRequest req = getRequest(x, y);
|
||||
if(req != null){
|
||||
player.buildQueue().remove(req);
|
||||
}
|
||||
player.addBuildRequest(new BuildRequest(x, y, rotation, block));
|
||||
}
|
||||
|
||||
@@ -392,23 +806,51 @@ public abstract class InputHandler implements InputProcessor{
|
||||
Core.atlas.find("place-arrow").getHeight() * Draw.scl, rotation * 90 - 90);
|
||||
}
|
||||
|
||||
void iterateLine(int startX, int startY, int endX, int endY, Consumer<PlaceLine> cons){
|
||||
void iterateLine(int startX, int startY, int endX, int endY, Cons<PlaceLine> cons){
|
||||
Array<Point2> points;
|
||||
boolean diagonal = Core.input.keyDown(Binding.diagonal_placement);
|
||||
if(Core.settings.getBool("swapdiagonal")){
|
||||
|
||||
if(Core.settings.getBool("swapdiagonal") && mobile){
|
||||
diagonal = !diagonal;
|
||||
}
|
||||
|
||||
if(block instanceof PowerNode){
|
||||
diagonal = !diagonal;
|
||||
}
|
||||
|
||||
if(diagonal){
|
||||
points = PlaceUtils.normalizeDiagonal(startX, startY, endX, endY);
|
||||
points = Placement.pathfindLine(block != null && block.conveyorPlacement, startX, startY, endX, endY);
|
||||
}else{
|
||||
points = PlaceUtils.normalizeLine(startX, startY, endX, endY);
|
||||
points = Placement.normalizeLine(startX, startY, endX, endY);
|
||||
}
|
||||
|
||||
if(block instanceof PowerNode){
|
||||
Array<Point2> skip = new Array<>();
|
||||
|
||||
for(int i = 1; i < points.size; i++){
|
||||
int overlaps = 0;
|
||||
Point2 point = points.get(i);
|
||||
|
||||
//check with how many powernodes the *next* tile will overlap
|
||||
for(int j = 0; j < i; j++){
|
||||
if(!skip.contains(points.get(j)) && ((PowerNode)block).overlaps(world.ltile(point.x, point.y), world.ltile(points.get(j).x, points.get(j).y))){
|
||||
overlaps++;
|
||||
}
|
||||
}
|
||||
|
||||
//if it's more than one, it can bridge the gap
|
||||
if(overlaps > 1){
|
||||
skip.add(points.get(i-1));
|
||||
}
|
||||
}
|
||||
//remove skipped points
|
||||
points.removeAll(skip);
|
||||
}
|
||||
|
||||
float angle = Angles.angle(startX, startY, endX, endY);
|
||||
int baseRotation = rotation;
|
||||
if (!overrideLineRotation || diagonal){
|
||||
baseRotation = (startX == endX && startY == endY) ? rotation : ((int)((angle + 45) / 90f)) % 4;
|
||||
if(!overrideLineRotation || diagonal){
|
||||
baseRotation = (startX == endX && startY == endY) ? rotation : ((int)((angle + 45) / 90f)) % 4;
|
||||
}
|
||||
|
||||
Tmp.r3.set(-1, -1, 0, 0);
|
||||
@@ -423,25 +865,18 @@ public abstract class InputHandler implements InputProcessor{
|
||||
Point2 next = i == points.size - 1 ? null : points.get(i + 1);
|
||||
line.x = point.x;
|
||||
line.y = point.y;
|
||||
if (!overrideLineRotation || diagonal){
|
||||
if(!overrideLineRotation || diagonal){
|
||||
line.rotation = next != null ? Tile.relativeTo(point.x, point.y, next.x, next.y) : baseRotation;
|
||||
}else{
|
||||
line.rotation = rotation;
|
||||
}
|
||||
line.last = next == null;
|
||||
cons.accept(line);
|
||||
cons.get(line);
|
||||
|
||||
Tmp.r3.setSize(block.size * tilesize).setCenter(point.x * tilesize + block.offset(), point.y * tilesize + block.offset());
|
||||
}
|
||||
}
|
||||
|
||||
public static class PlaceDraw{
|
||||
public int rotation, scalex, scaley;
|
||||
public TextureRegion region;
|
||||
|
||||
public static final PlaceDraw instance = new PlaceDraw();
|
||||
}
|
||||
|
||||
class PlaceLine{
|
||||
public int x, y, rotation;
|
||||
public boolean last;
|
||||
|
||||
@@ -2,14 +2,17 @@ package io.anuke.mindustry.input;
|
||||
|
||||
import io.anuke.arc.*;
|
||||
import io.anuke.arc.collection.*;
|
||||
import io.anuke.arc.graphics.*;
|
||||
import io.anuke.arc.func.*;
|
||||
import io.anuke.arc.graphics.g2d.*;
|
||||
import io.anuke.arc.input.*;
|
||||
import io.anuke.arc.input.GestureDetector.*;
|
||||
import io.anuke.arc.input.*;
|
||||
import io.anuke.arc.math.*;
|
||||
import io.anuke.arc.math.geom.*;
|
||||
import io.anuke.arc.scene.*;
|
||||
import io.anuke.arc.scene.ui.ImageButton.*;
|
||||
import io.anuke.arc.scene.ui.layout.*;
|
||||
import io.anuke.arc.util.*;
|
||||
import io.anuke.mindustry.*;
|
||||
import io.anuke.mindustry.content.*;
|
||||
import io.anuke.mindustry.core.GameState.*;
|
||||
import io.anuke.mindustry.entities.*;
|
||||
@@ -17,9 +20,9 @@ import io.anuke.mindustry.entities.traits.BuilderTrait.*;
|
||||
import io.anuke.mindustry.entities.traits.*;
|
||||
import io.anuke.mindustry.entities.type.*;
|
||||
import io.anuke.mindustry.game.EventType.*;
|
||||
import io.anuke.mindustry.game.*;
|
||||
import io.anuke.mindustry.gen.*;
|
||||
import io.anuke.mindustry.graphics.*;
|
||||
import io.anuke.mindustry.input.PlaceUtils.*;
|
||||
import io.anuke.mindustry.ui.*;
|
||||
import io.anuke.mindustry.world.*;
|
||||
|
||||
@@ -29,17 +32,15 @@ import static io.anuke.mindustry.input.PlaceMode.*;
|
||||
public class MobileInput extends InputHandler implements GestureListener{
|
||||
/** Maximum speed the player can pan. */
|
||||
private static final float maxPanSpeed = 1.3f;
|
||||
private static Rectangle r1 = new Rectangle(), r2 = new Rectangle();
|
||||
/** Distance to edge of screen to start panning. */
|
||||
private final float edgePan = Scl.scl(60f);
|
||||
|
||||
//gesture data
|
||||
private Vector2 vector = new Vector2();
|
||||
private float lastZoom = -1;
|
||||
private GestureDetector detector;
|
||||
|
||||
/** Position where the player started dragging a line. */
|
||||
private int lineStartX, lineStartY;
|
||||
private int lineStartX, lineStartY, lastLineX, lastLineY;
|
||||
|
||||
/** Animation scale for line. */
|
||||
private float lineScale;
|
||||
@@ -49,22 +50,20 @@ public class MobileInput extends InputHandler implements GestureListener{
|
||||
/** Used for shifting build requests. */
|
||||
private float shiftDeltaX, shiftDeltaY;
|
||||
|
||||
/** List of currently selected tiles to place. */
|
||||
private Array<PlaceRequest> selection = new Array<>();
|
||||
/** Place requests to be removed. */
|
||||
private Array<PlaceRequest> removals = new Array<>();
|
||||
private Array<BuildRequest> removals = new Array<>();
|
||||
/** Whether or not the player is currently shifting all placed tiles. */
|
||||
private boolean selecting;
|
||||
/** Whether the player is currently in line-place mode. */
|
||||
private boolean lineMode;
|
||||
private boolean lineMode, schematicMode;
|
||||
/** Current place mode. */
|
||||
private PlaceMode mode = none;
|
||||
/** Whether no recipe was available when switching to break mode. */
|
||||
private Block lastBlock;
|
||||
/** Last placed request. Used for drawing block overlay. */
|
||||
private PlaceRequest lastPlaced;
|
||||
|
||||
private int prevX, prevY, prevRotation;
|
||||
private BuildRequest lastPlaced;
|
||||
/** Down tracking for panning.*/
|
||||
private boolean down = false;
|
||||
|
||||
//region utility methods
|
||||
|
||||
@@ -99,10 +98,10 @@ public class MobileInput extends InputHandler implements GestureListener{
|
||||
r2.setSize(block.size * tilesize);
|
||||
r2.setCenter(x * tilesize + block.offset(), y * tilesize + block.offset());
|
||||
|
||||
for(PlaceRequest req : selection){
|
||||
for(BuildRequest req : selectRequests){
|
||||
Tile other = req.tile();
|
||||
|
||||
if(other == null || req.remove) continue;
|
||||
if(other == null || req.breaking) continue;
|
||||
|
||||
r1.setSize(req.block.size * tilesize);
|
||||
r1.setCenter(other.worldx() + req.block.offset(), other.worldy() + req.block.offset());
|
||||
@@ -128,16 +127,16 @@ public class MobileInput extends InputHandler implements GestureListener{
|
||||
}
|
||||
|
||||
/** Returns the selection request that overlaps this tile, or null. */
|
||||
PlaceRequest getRequest(Tile tile){
|
||||
BuildRequest getRequest(Tile tile){
|
||||
r2.setSize(tilesize);
|
||||
r2.setCenter(tile.worldx(), tile.worldy());
|
||||
|
||||
for(PlaceRequest req : selection){
|
||||
for(BuildRequest req : selectRequests){
|
||||
Tile other = req.tile();
|
||||
|
||||
if(other == null) continue;
|
||||
|
||||
if(!req.remove){
|
||||
if(!req.breaking){
|
||||
r1.setSize(req.block.size * tilesize);
|
||||
r1.setCenter(other.worldx() + req.block.offset(), other.worldy() + req.block.offset());
|
||||
|
||||
@@ -156,9 +155,11 @@ public class MobileInput extends InputHandler implements GestureListener{
|
||||
return null;
|
||||
}
|
||||
|
||||
void removeRequest(PlaceRequest request){
|
||||
selection.removeValue(request, true);
|
||||
removals.add(request);
|
||||
void removeRequest(BuildRequest request){
|
||||
selectRequests.removeValue(request, true);
|
||||
if(!request.breaking){
|
||||
removals.add(request);
|
||||
}
|
||||
}
|
||||
|
||||
boolean isLinePlacing(){
|
||||
@@ -172,80 +173,8 @@ public class MobileInput extends InputHandler implements GestureListener{
|
||||
//endregion
|
||||
//region UI and drawing
|
||||
|
||||
void drawRequest(PlaceRequest request, PlaceRequest prev){
|
||||
Tile tile = request.tile();
|
||||
|
||||
if(!request.remove){
|
||||
if(prev != null){
|
||||
request.block.getPlaceDraw(placeDraw, request.rotation, prev.x - request.x, prev.y - request.y, prev.rotation);
|
||||
}else{
|
||||
request.block.getPlaceDraw(placeDraw, request.rotation, 0, 0, request.rotation);
|
||||
}
|
||||
|
||||
//draw placing request
|
||||
float offset = request.block.offset();
|
||||
TextureRegion region = placeDraw.region;
|
||||
|
||||
Draw.mixcol(Pal.accent, Mathf.clamp((1f - request.scale) / 0.5f + 0.12f + Mathf.absin(Time.time(), 8f, 0.35f)));
|
||||
Draw.tint(Color.white, Pal.breakInvalid, request.redness);
|
||||
|
||||
Draw.rect(region, tile.worldx() + offset, tile.worldy() + offset,
|
||||
region.getWidth() * request.scale * Draw.scl * placeDraw.scalex,
|
||||
region.getHeight() * request.scale * Draw.scl * placeDraw.scaley,
|
||||
request.block.rotate ? placeDraw.rotation * 90 : 0);
|
||||
|
||||
Draw.mixcol(Pal.accent, 1f);
|
||||
for(int i = 0; i < 4; i++){
|
||||
Point2 p = Geometry.d8edge[i];
|
||||
float poffset = -Math.max(request.block.size - 1, 0) / 2f * tilesize;
|
||||
TextureRegion find = Core.atlas.find("block-select");
|
||||
if(i % 2 == 0)
|
||||
Draw.rect("block-select", request.tile().x * tilesize + request.block.offset() + poffset * p.x, request.tile().y * tilesize + request.block.offset() + poffset * p.y,
|
||||
find.getWidth() * Draw.scl * request.scale, find.getHeight() * Draw.scl * request.scale, i * 90);
|
||||
}
|
||||
Draw.color();
|
||||
}else{
|
||||
float rad = Math.max((tile.block().size * tilesize / 2f - 1) * request.scale, 1f);
|
||||
|
||||
if(rad <= 1.01f) return;
|
||||
Draw.mixcol();
|
||||
//draw removing request
|
||||
Draw.tint(Pal.removeBack);
|
||||
Lines.square(tile.drawx(), tile.drawy() - 1, rad);
|
||||
Draw.tint(Pal.remove);
|
||||
Lines.square(tile.drawx(), tile.drawy(), rad);
|
||||
}
|
||||
}
|
||||
|
||||
/** Draws a placement icon for a specific block. */
|
||||
void drawPlace(int x, int y, Block block, int rotation, int prevX, int prevY, int prevRotation){
|
||||
if(validPlace(x, y, block, rotation) && !checkOverlapPlacement(x, y, block)){
|
||||
block.getPlaceDraw(placeDraw, rotation, prevX, prevY, prevRotation);
|
||||
|
||||
Draw.color();
|
||||
Draw.rect(placeDraw.region, x * tilesize + block.offset(), y * tilesize + block.offset(),
|
||||
placeDraw.region.getWidth() * Draw.scl * placeDraw.scalex,
|
||||
placeDraw.region.getHeight() * Draw.scl * placeDraw.scaley,
|
||||
block.rotate ? placeDraw.rotation * 90 : 0);
|
||||
|
||||
Draw.color(Pal.accent);
|
||||
for(int i = 0; i < 4; i++){
|
||||
Point2 p = Geometry.d8edge[i];
|
||||
float offset = -Math.max(block.size - 1, 0) / 2f * tilesize;
|
||||
if(i % 2 == 0)
|
||||
Draw.rect("block-select", x * tilesize + block.offset() + offset * p.x, y * tilesize + block.offset() + offset * p.y, i * 90);
|
||||
}
|
||||
Draw.color();
|
||||
}else{
|
||||
Draw.color(Pal.removeBack);
|
||||
Lines.square(x * tilesize + block.offset(), y * tilesize + block.offset() - 1, block.size * tilesize / 2f - 1);
|
||||
Draw.color(Pal.remove);
|
||||
Lines.square(x * tilesize + block.offset(), y * tilesize + block.offset(), block.size * tilesize / 2f - 1);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void buildUI(Table table){
|
||||
public void buildPlacementUI(Table table){
|
||||
table.addImage().color(Pal.gray).height(4f).colspan(4).growX();
|
||||
table.row();
|
||||
table.left().margin(0f).defaults().size(48f);
|
||||
@@ -262,22 +191,49 @@ public class MobileInput extends InputHandler implements GestureListener{
|
||||
}).update(l -> l.setChecked(Core.settings.getBool("swapdiagonal")));
|
||||
|
||||
//rotate button
|
||||
table.addImageButton(Icon.arrowSmall, Styles.clearPartiali,() -> rotation = Mathf.mod(rotation + 1, 4))
|
||||
.update(i -> i.getImage().setRotationOrigin(rotation * 90, Align.center)).visible(() -> block != null && block.rotate);
|
||||
table.addImageButton(Icon.arrowSmall, Styles.clearTogglePartiali, () -> {
|
||||
if(block != null && block.rotate){
|
||||
rotation = Mathf.mod(rotation + 1, 4);
|
||||
}else{
|
||||
schematicMode = !schematicMode;
|
||||
if(schematicMode){
|
||||
block = null;
|
||||
mode = none;
|
||||
}
|
||||
}
|
||||
}).update(i -> {
|
||||
boolean arrow = block != null && block.rotate;
|
||||
|
||||
i.getImage().setRotationOrigin(!arrow ? 0 : rotation * 90, Align.center);
|
||||
i.getStyle().imageUp = arrow ? Icon.arrowSmall : Icon.pasteSmall;
|
||||
i.setChecked(!arrow && schematicMode);
|
||||
});
|
||||
|
||||
//confirm button
|
||||
table.addImageButton(Icon.checkSmall, Styles.clearPartiali, () -> {
|
||||
for(PlaceRequest request : selection){
|
||||
for(BuildRequest request : selectRequests){
|
||||
Tile tile = request.tile();
|
||||
|
||||
//actually place/break all selected blocks
|
||||
if(tile != null){
|
||||
if(!request.remove){
|
||||
if(!request.breaking){
|
||||
if(validPlace(request.x, request.y, request.block, request.rotation)){
|
||||
BuildRequest other = getRequest(request.x, request.y, request.block.size, null);
|
||||
BuildRequest copy = request.copy();
|
||||
|
||||
if(copy.hasConfig && copy.block.posConfig){
|
||||
copy.config = Pos.get(Pos.x(copy.config) + copy.x - copy.originalX, Pos.y(copy.config) + copy.y - copy.originalY);
|
||||
}
|
||||
|
||||
if(other == null){
|
||||
player.addBuildRequest(copy);
|
||||
}else if(!other.breaking && other.x == request.x && other.y == request.y && other.block.size == request.block.size){
|
||||
player.buildQueue().remove(other);
|
||||
player.addBuildRequest(copy);
|
||||
}
|
||||
}
|
||||
|
||||
rotation = request.rotation;
|
||||
Block before = block;
|
||||
block = request.block;
|
||||
tryPlaceBlock(tile.x, tile.y);
|
||||
block = before;
|
||||
}else{
|
||||
tryBreakBlock(tile.x, tile.y);
|
||||
}
|
||||
@@ -285,25 +241,60 @@ public class MobileInput extends InputHandler implements GestureListener{
|
||||
}
|
||||
|
||||
//move all current requests to removal array so they fade out
|
||||
removals.addAll(selection);
|
||||
selection.clear();
|
||||
removals.addAll(selectRequests.select(r -> !r.breaking));
|
||||
selectRequests.clear();
|
||||
selecting = false;
|
||||
}).visible(() -> !selection.isEmpty()).name("confirmplace");
|
||||
}).visible(() -> !selectRequests.isEmpty()).name("confirmplace");
|
||||
}
|
||||
|
||||
Core.scene.table(t -> {
|
||||
t.setName("cancelMobile");
|
||||
t.bottom().left().visible(() -> (player.isBuilding() || block != null || mode == breaking) && !state.is(State.menu));
|
||||
t.addImageTextButton("$cancel", Icon.cancelSmall, () -> {
|
||||
player.clearBuilding();
|
||||
mode = none;
|
||||
block = null;
|
||||
}).width(155f);
|
||||
@Override
|
||||
public void buildUI(Group group){
|
||||
Boolp schem = () -> lastSchematic != null && !selectRequests.isEmpty();
|
||||
|
||||
group.fill(t -> {
|
||||
t.bottom().left().visible(() -> (player.isBuilding() || block != null || mode == breaking || !selectRequests.isEmpty()) && !schem.get());
|
||||
t.addImageTextButton("$cancel", Icon.cancelSmall, () -> {
|
||||
player.clearBuilding();
|
||||
selectRequests.clear();
|
||||
mode = none;
|
||||
block = null;
|
||||
}).width(155f);
|
||||
});
|
||||
|
||||
group.fill(t -> {
|
||||
t.visible(schem);
|
||||
t.bottom().left();
|
||||
t.table(Tex.pane, b -> {
|
||||
b.defaults().size(50f);
|
||||
|
||||
ImageButtonStyle style = Styles.clearPartiali;
|
||||
|
||||
b.addImageButton(Icon.floppySmall, style, this::showSchematicSave).disabled(f -> lastSchematic == null || lastSchematic.file != null);
|
||||
b.addImageButton(Icon.cancelSmall, style, () -> {
|
||||
selectRequests.clear();
|
||||
});
|
||||
b.row();
|
||||
b.addImageButton(Icon.flipSmall, style, () -> flipRequests(selectRequests, true));
|
||||
b.addImageButton(Icon.flipSmall, style, () -> flipRequests(selectRequests, false)).update(i -> i.getImage().setRotationOrigin(90f, Align.center));
|
||||
b.row();
|
||||
b.addImageButton(Icon.rotateSmall, style, () -> rotateRequests(selectRequests, 1));
|
||||
|
||||
}).margin(4f);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDrawing(){
|
||||
return selection.size > 0 || removals.size > 0 || lineMode || player.target != null || mode != PlaceMode.none;
|
||||
protected int schemOriginX(){
|
||||
Tmp.v1.setZero();
|
||||
selectRequests.each(r -> Tmp.v1.add(r.drawx(), r.drawy()));
|
||||
return world.toTile(Tmp.v1.scl(1f / selectRequests.size).x);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int schemOriginY(){
|
||||
Tmp.v1.setZero();
|
||||
selectRequests.each(r -> Tmp.v1.add(r.drawx(), r.drawy()));
|
||||
return world.toTile(Tmp.v1.scl(1f / selectRequests.size).y);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -312,55 +303,53 @@ public class MobileInput extends InputHandler implements GestureListener{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawOutlined(){
|
||||
public void drawBottom(){
|
||||
Lines.stroke(1f);
|
||||
|
||||
//draw removals
|
||||
for(PlaceRequest request : removals){
|
||||
for(BuildRequest request : removals){
|
||||
Tile tile = request.tile();
|
||||
|
||||
if(tile == null) continue;
|
||||
|
||||
request.scale = Mathf.lerpDelta(request.scale, 0f, 0.2f);
|
||||
request.redness = Mathf.lerpDelta(request.redness, 0f, 0.2f);
|
||||
request.animScale = Mathf.lerpDelta(request.animScale, 0f, 0.2f);
|
||||
|
||||
drawRequest(request, null);
|
||||
if(request.breaking){
|
||||
drawSelected(request.x, request.y, tile.block(), Pal.remove);
|
||||
}else{
|
||||
request.block.drawRequest(request, allRequests(), true);
|
||||
}
|
||||
}
|
||||
|
||||
PlaceRequest last = null;
|
||||
|
||||
//draw list of requests
|
||||
for(PlaceRequest request : selection){
|
||||
for(BuildRequest request : selectRequests){
|
||||
Tile tile = request.tile();
|
||||
|
||||
if(tile == null) continue;
|
||||
|
||||
if((!request.remove && validPlace(tile.x, tile.y, request.block, request.rotation))
|
||||
|| (request.remove && validBreak(tile.x, tile.y))){
|
||||
request.scale = Mathf.lerpDelta(request.scale, 1f, 0.2f);
|
||||
request.redness = Mathf.lerpDelta(request.redness, 0f, 0.2f);
|
||||
if((!request.breaking && validPlace(tile.x, tile.y, request.block, request.rotation))
|
||||
|| (request.breaking && validBreak(tile.x, tile.y))){
|
||||
request.animScale = Mathf.lerpDelta(request.animScale, 1f, 0.2f);
|
||||
}else{
|
||||
request.scale = Mathf.lerpDelta(request.scale, 0.6f, 0.1f);
|
||||
request.redness = Mathf.lerpDelta(request.redness, 0.9f, 0.2f);
|
||||
request.animScale = Mathf.lerpDelta(request.animScale, 0.6f, 0.1f);
|
||||
}
|
||||
|
||||
Tmp.c1.set(Draw.getMixColor());
|
||||
|
||||
if(!request.remove && request == lastPlaced && request.block != null){
|
||||
if(!request.breaking && request == lastPlaced && request.block != null){
|
||||
Draw.mixcol();
|
||||
if(request.block.rotate) drawArrow(request.block, tile.x, tile.y, request.rotation);
|
||||
}
|
||||
|
||||
Draw.mixcol(Tmp.c1, 1f);
|
||||
drawRequest(request, last);
|
||||
//Draw.mixcol(Tmp.c1, 1f);
|
||||
Draw.reset();
|
||||
drawRequest(request);
|
||||
|
||||
//draw last placed request
|
||||
if(!request.remove && request == lastPlaced && request.block != null){
|
||||
if(!request.breaking && request == lastPlaced && request.block != null){
|
||||
Draw.mixcol();
|
||||
request.block.drawPlace(tile.x, tile.y, rotation, validPlace(tile.x, tile.y, request.block, rotation));
|
||||
}
|
||||
|
||||
last = request;
|
||||
}
|
||||
|
||||
Draw.mixcol();
|
||||
@@ -373,46 +362,19 @@ public class MobileInput extends InputHandler implements GestureListener{
|
||||
|
||||
if(mode == placing && block != null){
|
||||
//draw placing
|
||||
|
||||
prevX = lineStartX;
|
||||
prevY = lineStartY;
|
||||
prevRotation = rotation;
|
||||
|
||||
iterateLine(lineStartX, lineStartY, tileX, tileY, l -> {
|
||||
if(l.last && block.rotate){
|
||||
drawArrow(block, l.x, l.y, l.rotation);
|
||||
for(int i = 0; i < lineRequests.size; i++){
|
||||
BuildRequest req = lineRequests.get(i);
|
||||
if(i == lineRequests.size - 1 && req.block.rotate){
|
||||
drawArrow(block, req.x, req.y, req.rotation);
|
||||
}
|
||||
drawPlace(l.x, l.y, block, l.rotation, prevX - l.x, prevY - l.y, prevRotation);
|
||||
|
||||
rotation = l.rotation;
|
||||
prevX = l.x;
|
||||
prevY = l.y;
|
||||
prevRotation = l.rotation;
|
||||
});
|
||||
}else if(mode == breaking){
|
||||
//draw breaking
|
||||
NormalizeDrawResult result = PlaceUtils.normalizeDrawArea(Blocks.air, lineStartX, lineStartY, tileX, tileY, false, maxLength, 1f);
|
||||
NormalizeResult dresult = PlaceUtils.normalizeArea(lineStartX, lineStartY, tileX, tileY, rotation, false, maxLength);
|
||||
|
||||
for(int x = dresult.x; x <= dresult.x2; x++){
|
||||
for(int y = dresult.y; y <= dresult.y2; y++){
|
||||
Tile other = world.ltile(x, y);
|
||||
if(other == null || !validBreak(other.x, other.y)) continue;
|
||||
|
||||
Draw.color(Pal.removeBack);
|
||||
Lines.square(other.drawx(), other.drawy() - 1, other.block().size * tilesize / 2f - 1);
|
||||
Draw.color(Pal.remove);
|
||||
Lines.square(other.drawx(), other.drawy(), other.block().size * tilesize / 2f - 1);
|
||||
}
|
||||
BuildRequest request = lineRequests.get(i);
|
||||
request.block.drawRequest(request, allRequests(), validPlace(request.x, request.y, request.block, request.rotation) && getRequest(req.x, request.y, request.block.size, null) == null);
|
||||
drawSelected(request.x, request.y, request.block, Pal.accent);
|
||||
}
|
||||
|
||||
Draw.color(Pal.removeBack);
|
||||
Lines.rect(result.x, result.y - 1, result.x2 - result.x, result.y2 - result.y);
|
||||
Draw.color(Pal.remove);
|
||||
Lines.rect(result.x, result.y, result.x2 - result.x, result.y2 - result.y);
|
||||
|
||||
}else if(mode == breaking){
|
||||
drawBreakSelection(lineStartX, lineStartY, tileX, tileY);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
TargetTrait target = player.target;
|
||||
@@ -438,31 +400,49 @@ public class MobileInput extends InputHandler implements GestureListener{
|
||||
Draw.reset();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawTop(){
|
||||
|
||||
//draw schematic selection
|
||||
if(mode == schematicSelect){
|
||||
drawSelection(lineStartX, lineStartY, lastLineX, lastLineY, Vars.maxSchematicSize);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void drawRequest(BuildRequest request){
|
||||
if(request.tile() == null) return;
|
||||
brequest.animScale = request.animScale = Mathf.lerpDelta(request.animScale, 1f, 0.1f);
|
||||
|
||||
if(request.breaking){
|
||||
drawSelected(request.x, request.y, request.tile().block(), Pal.remove);
|
||||
}else{
|
||||
request.block.drawRequest(request, allRequests(), validPlace(request.x, request.y, request.block, request.rotation));
|
||||
drawSelected(request.x, request.y, request.block, Pal.accent);
|
||||
}
|
||||
}
|
||||
|
||||
//endregion
|
||||
//region input events
|
||||
|
||||
@Override
|
||||
public void add(){
|
||||
Core.input.addProcessor(detector = new GestureDetector(20, 0.5f, 0.4f, 0.15f, this));
|
||||
super.add();
|
||||
public boolean isBreaking(){
|
||||
return mode == breaking;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(){
|
||||
super.remove();
|
||||
if(detector != null){
|
||||
Core.input.removeProcessor(detector);
|
||||
}
|
||||
|
||||
if(Core.scene != null && Core.scene.find("cancelMobile") != null){
|
||||
Core.scene.find("cancelMobile").remove();
|
||||
}
|
||||
public void useSchematic(Schematic schem){
|
||||
selectRequests.clear();
|
||||
selectRequests.addAll(schematics.toRequests(schem, world.toTile(player.x), world.toTile(player.y)));
|
||||
lastSchematic = schem;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean touchDown(int screenX, int screenY, int pointer, KeyCode button){
|
||||
if(state.is(State.menu) || player.isDead()) return false;
|
||||
|
||||
down = true;
|
||||
|
||||
//get tile on cursor
|
||||
Tile cursor = tileAt(screenX, screenY);
|
||||
|
||||
@@ -472,11 +452,20 @@ public class MobileInput extends InputHandler implements GestureListener{
|
||||
if(cursor == null || Core.scene.hasMouse(screenX, screenY)) return false;
|
||||
|
||||
//only begin selecting if the tapped block is a request
|
||||
selecting = hasRequest(cursor) && isPlacing() && mode == placing;
|
||||
selecting = hasRequest(cursor);
|
||||
|
||||
//call tap events
|
||||
if(pointer == 0 && !selecting){
|
||||
if(!tryTapPlayer(worldx, worldy) && Core.settings.getBool("keyboard")){
|
||||
if(schematicMode && block == null){
|
||||
mode = schematicSelect;
|
||||
//engage schematic selection mode
|
||||
int tileX = tileX(screenX);
|
||||
int tileY = tileY(screenY);
|
||||
lineStartX = tileX;
|
||||
lineStartY = tileY;
|
||||
lastLineX = tileX;
|
||||
lastLineY = tileY;
|
||||
}else if(!tryTapPlayer(worldx, worldy) && Core.settings.getBool("keyboard")){
|
||||
//shoot on touch down when in keyboard mode
|
||||
player.isShooting = true;
|
||||
}
|
||||
@@ -489,47 +478,34 @@ public class MobileInput extends InputHandler implements GestureListener{
|
||||
public boolean touchUp(int screenX, int screenY, int pointer, KeyCode button){
|
||||
lastZoom = renderer.getScale();
|
||||
|
||||
if(!Core.input.isTouched()){
|
||||
down = false;
|
||||
}
|
||||
|
||||
selecting = false;
|
||||
|
||||
//place down a line if in line mode
|
||||
if(lineMode){
|
||||
int tileX = tileX(screenX);
|
||||
int tileY = tileY(screenY);
|
||||
|
||||
if(mode == placing && isPlacing()){
|
||||
iterateLine(lineStartX, lineStartY, tileX, tileY, l -> {
|
||||
Tile tile = world.tile(l.x, l.y);
|
||||
if(tile != null && checkOverlapPlacement(tile.x, tile.y, block)){
|
||||
return;
|
||||
}
|
||||
|
||||
PlaceRequest request = new PlaceRequest(l.x, l.y, block, l.rotation);
|
||||
request.scale = 1f;
|
||||
selection.add(request);
|
||||
});
|
||||
flushSelectRequests(lineRequests);
|
||||
Events.fire(new LineConfirmEvent());
|
||||
}else if(mode == breaking){
|
||||
//normalize area
|
||||
NormalizeResult result = PlaceUtils.normalizeArea(lineStartX, lineStartY, tileX, tileY, rotation, false, maxLength);
|
||||
|
||||
//break everything in area
|
||||
for(int x = 0; x <= Math.abs(result.x2 - result.x); x++){
|
||||
for(int y = 0; y <= Math.abs(result.y2 - result.y); y++){
|
||||
int wx = lineStartX + x * Mathf.sign(tileX - lineStartX);
|
||||
int wy = lineStartY + y * Mathf.sign(tileY - lineStartY);
|
||||
|
||||
Tile tar = world.ltile(wx, wy);
|
||||
|
||||
if(tar == null) continue;
|
||||
|
||||
if(!hasRequest(world.tile(tar.x, tar.y)) && validBreak(tar.x, tar.y)){
|
||||
PlaceRequest request = new PlaceRequest(tar.x, tar.y);
|
||||
request.scale = 1f;
|
||||
selection.add(request);
|
||||
}
|
||||
}
|
||||
}
|
||||
removeSelection(lineStartX, lineStartY, tileX, tileY, true);
|
||||
}
|
||||
|
||||
lineMode = false;
|
||||
}else if(mode == schematicSelect){
|
||||
selectRequests.clear();
|
||||
lastSchematic = schematics.create(lineStartX, lineStartY, lastLineX, lastLineY);
|
||||
useSchematic(lastSchematic);
|
||||
if(selectRequests.isEmpty()){
|
||||
lastSchematic = null;
|
||||
}
|
||||
schematicMode = false;
|
||||
mode = none;
|
||||
}else{
|
||||
Tile tile = tileAt(screenX, screenY);
|
||||
|
||||
@@ -548,17 +524,20 @@ public class MobileInput extends InputHandler implements GestureListener{
|
||||
Tile cursor = tileAt(x, y);
|
||||
|
||||
//ignore off-screen taps
|
||||
if(cursor == null || Core.scene.hasMouse(x, y)) return false;
|
||||
if(cursor == null || Core.scene.hasMouse(x, y) || schematicMode) return false;
|
||||
|
||||
//remove request if it's there
|
||||
//long pressing enables line mode otherwise
|
||||
lineStartX = cursor.x;
|
||||
lineStartY = cursor.y;
|
||||
lastLineX = cursor.x;
|
||||
lastLineY = cursor.y;
|
||||
lineMode = true;
|
||||
|
||||
if(mode == breaking){
|
||||
Effects.effect(Fx.tapBlock, cursor.worldx(), cursor.worldy(), 1f);
|
||||
}else if(block != null){
|
||||
updateLine(lineStartX, lineStartY, cursor.x, cursor.y);
|
||||
Effects.effect(Fx.tapBlock, cursor.worldx() + block.offset(), cursor.worldy() + block.offset(), block.size);
|
||||
}
|
||||
|
||||
@@ -584,11 +563,11 @@ public class MobileInput extends InputHandler implements GestureListener{
|
||||
removeRequest(getRequest(cursor));
|
||||
}else if(mode == placing && isPlacing() && validPlace(cursor.x, cursor.y, block, rotation) && !checkOverlapPlacement(cursor.x, cursor.y, block)){
|
||||
//add to selection queue if it's a valid place position
|
||||
selection.add(lastPlaced = new PlaceRequest(cursor.x, cursor.y, block, rotation));
|
||||
selectRequests.add(lastPlaced = new BuildRequest(cursor.x, cursor.y, rotation, block));
|
||||
}else if(mode == breaking && validBreak(cursor.link().x, cursor.link().y) && !hasRequest(cursor.link())){
|
||||
//add to selection queue if it's a valid BREAK position
|
||||
cursor = cursor.link();
|
||||
selection.add(new PlaceRequest(cursor.x, cursor.y));
|
||||
selectRequests.add(new BuildRequest(cursor.x, cursor.y));
|
||||
}else if(!canTapPlayer(worldx, worldy) && !tileTapped(cursor.link())){
|
||||
tryBeginMine(cursor);
|
||||
}
|
||||
@@ -598,12 +577,16 @@ public class MobileInput extends InputHandler implements GestureListener{
|
||||
|
||||
@Override
|
||||
public void update(){
|
||||
if(state.is(State.menu) || player.isDead()){
|
||||
selection.clear();
|
||||
if(state.is(State.menu) ){
|
||||
selectRequests.clear();
|
||||
removals.clear();
|
||||
mode = none;
|
||||
}
|
||||
|
||||
if(player.isDead()){
|
||||
mode = none;
|
||||
}
|
||||
|
||||
//zoom things
|
||||
if(Math.abs(Core.input.axisTap(Binding.zoom)) > 0 && (Core.input.keyDown(Binding.zoom_hold))){
|
||||
renderer.scaleCamera(Core.input.axisTap(Binding.zoom));
|
||||
@@ -627,10 +610,7 @@ public class MobileInput extends InputHandler implements GestureListener{
|
||||
|
||||
//reset state when not placing
|
||||
if(mode == none){
|
||||
selecting = false;
|
||||
lineMode = false;
|
||||
removals.addAll(selection);
|
||||
selection.clear();
|
||||
}
|
||||
|
||||
if(lineMode && mode == placing && block == null){
|
||||
@@ -646,6 +626,22 @@ public class MobileInput extends InputHandler implements GestureListener{
|
||||
mode = none;
|
||||
}
|
||||
|
||||
//stop schematic when in block mode
|
||||
if(block != null){
|
||||
schematicMode = false;
|
||||
}
|
||||
|
||||
//stop select when not in schematic mode
|
||||
if(!schematicMode && mode == schematicSelect){
|
||||
mode = none;
|
||||
}
|
||||
|
||||
if(mode == schematicSelect){
|
||||
lastLineX = rawTileX();
|
||||
lastLineY = rawTileY();
|
||||
autoPan();
|
||||
}
|
||||
|
||||
//automatically switch to placing after a new recipe is selected
|
||||
if(lastBlock != block && mode == breaking && block != null){
|
||||
mode = placing;
|
||||
@@ -656,49 +652,62 @@ public class MobileInput extends InputHandler implements GestureListener{
|
||||
lineScale = Mathf.lerpDelta(lineScale, 1f, 0.1f);
|
||||
|
||||
//When in line mode, pan when near screen edges automatically
|
||||
if(Core.input.isTouched(0) && lineMode){
|
||||
float screenX = Core.input.mouseX(), screenY = Core.input.mouseY();
|
||||
if(Core.input.isTouched(0)){
|
||||
autoPan();
|
||||
}
|
||||
|
||||
float panX = 0, panY = 0;
|
||||
int lx = tileX(Core.input.mouseX()), ly = tileY(Core.input.mouseY());
|
||||
|
||||
if(screenX <= edgePan){
|
||||
panX = -(edgePan - screenX);
|
||||
}
|
||||
|
||||
if(screenX >= Core.graphics.getWidth() - edgePan){
|
||||
panX = (screenX - Core.graphics.getWidth()) + edgePan;
|
||||
}
|
||||
|
||||
if(screenY <= edgePan){
|
||||
panY = -(edgePan - screenY);
|
||||
}
|
||||
|
||||
if(screenY >= Core.graphics.getHeight() - edgePan){
|
||||
panY = (screenY - Core.graphics.getHeight()) + edgePan;
|
||||
}
|
||||
|
||||
vector.set(panX, panY).scl((Core.camera.width) / Core.graphics.getWidth());
|
||||
vector.limit(maxPanSpeed);
|
||||
|
||||
//pan view
|
||||
Core.camera.position.x += vector.x;
|
||||
Core.camera.position.y += vector.y;
|
||||
if((lastLineX != lx || lastLineY != ly) && isPlacing()){
|
||||
lastLineX = lx;
|
||||
lastLineY = ly;
|
||||
updateLine(lineStartX, lineStartY, lx, ly);
|
||||
}
|
||||
}else{
|
||||
lineRequests.clear();
|
||||
lineScale = 0f;
|
||||
}
|
||||
|
||||
//remove place requests that have disappeared
|
||||
for(int i = removals.size - 1; i >= 0; i--){
|
||||
PlaceRequest request = removals.get(i);
|
||||
BuildRequest request = removals.get(i);
|
||||
|
||||
if(request.scale <= 0.0001f){
|
||||
if(request.animScale <= 0.0001f){
|
||||
removals.remove(i);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void autoPan(){
|
||||
float screenX = Core.input.mouseX(), screenY = Core.input.mouseY();
|
||||
|
||||
float panX = 0, panY = 0;
|
||||
|
||||
if(screenX <= edgePan){
|
||||
panX = -(edgePan - screenX);
|
||||
}
|
||||
|
||||
if(screenX >= Core.graphics.getWidth() - edgePan){
|
||||
panX = (screenX - Core.graphics.getWidth()) + edgePan;
|
||||
}
|
||||
|
||||
if(screenY <= edgePan){
|
||||
panY = -(edgePan - screenY);
|
||||
}
|
||||
|
||||
if(screenY >= Core.graphics.getHeight() - edgePan){
|
||||
panY = (screenY - Core.graphics.getHeight()) + edgePan;
|
||||
}
|
||||
|
||||
vector.set(panX, panY).scl((Core.camera.width) / Core.graphics.getWidth());
|
||||
vector.limit(maxPanSpeed);
|
||||
|
||||
//pan view
|
||||
Core.camera.position.x += vector.x;
|
||||
Core.camera.position.y += vector.y;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean pan(float x, float y, float deltaX, float deltaY){
|
||||
if(Core.scene.hasDialog() || Core.settings.getBool("keyboard")) return false;
|
||||
@@ -708,10 +717,12 @@ public class MobileInput extends InputHandler implements GestureListener{
|
||||
deltaY *= scale;
|
||||
|
||||
//can't pan in line mode with one finger or while dropping items!
|
||||
if((lineMode && !Core.input.isTouched(1)) || droppingItem){
|
||||
if((lineMode && !Core.input.isTouched(1)) || droppingItem || schematicMode){
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!down) return false;
|
||||
|
||||
if(selecting){ //pan all requests
|
||||
shiftDeltaX += deltaX;
|
||||
shiftDeltaY += deltaY;
|
||||
@@ -720,8 +731,8 @@ public class MobileInput extends InputHandler implements GestureListener{
|
||||
int shiftedY = (int)(shiftDeltaY / tilesize);
|
||||
|
||||
if(Math.abs(shiftedX) > 0 || Math.abs(shiftedY) > 0){
|
||||
for(PlaceRequest req : selection){
|
||||
if(req.remove) continue; //don't shift removal requests
|
||||
for(BuildRequest req : selectRequests){
|
||||
if(req.breaking) continue; //don't shift removal requests
|
||||
req.x += shiftedX;
|
||||
req.y += shiftedY;
|
||||
}
|
||||
@@ -756,33 +767,4 @@ public class MobileInput extends InputHandler implements GestureListener{
|
||||
}
|
||||
|
||||
//endregion
|
||||
|
||||
private class PlaceRequest{
|
||||
int x, y;
|
||||
Block block;
|
||||
int rotation;
|
||||
boolean remove;
|
||||
|
||||
//animation variables
|
||||
float scale;
|
||||
float redness;
|
||||
|
||||
PlaceRequest(int x, int y, Block block, int rotation){
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.block = block;
|
||||
this.rotation = rotation;
|
||||
this.remove = false;
|
||||
}
|
||||
|
||||
PlaceRequest(int x, int y){
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.remove = true;
|
||||
}
|
||||
|
||||
Tile tile(){
|
||||
return world.tile(x, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
package io.anuke.mindustry.input;
|
||||
|
||||
enum PlaceMode{
|
||||
none, breaking, placing
|
||||
none, breaking, placing, schematicSelect
|
||||
}
|
||||
|
||||
@@ -1,25 +1,41 @@
|
||||
package io.anuke.mindustry.input;
|
||||
|
||||
import io.anuke.arc.collection.Array;
|
||||
import io.anuke.arc.math.Mathf;
|
||||
import io.anuke.arc.math.geom.Bresenham2;
|
||||
import io.anuke.arc.math.geom.Point2;
|
||||
import io.anuke.arc.util.pooling.Pools;
|
||||
import io.anuke.mindustry.world.Block;
|
||||
import io.anuke.arc.*;
|
||||
import io.anuke.arc.collection.*;
|
||||
import io.anuke.arc.math.*;
|
||||
import io.anuke.arc.math.geom.*;
|
||||
import io.anuke.arc.util.pooling.*;
|
||||
import io.anuke.mindustry.world.*;
|
||||
|
||||
import static io.anuke.mindustry.Vars.tilesize;
|
||||
import java.util.*;
|
||||
|
||||
public class PlaceUtils{
|
||||
import static io.anuke.mindustry.Vars.*;
|
||||
|
||||
public class Placement{
|
||||
private static final NormalizeResult result = new NormalizeResult();
|
||||
private static final NormalizeDrawResult drawResult = new NormalizeDrawResult();
|
||||
private static Bresenham2 bres = new Bresenham2();
|
||||
private static Array<Point2> points = new Array<>();
|
||||
|
||||
//for pathfinding
|
||||
private static IntFloatMap costs = new IntFloatMap();
|
||||
private static IntIntMap parents = new IntIntMap();
|
||||
private static IntSet closed = new IntSet();
|
||||
|
||||
/** Normalize a diagonal line into points. */
|
||||
public static Array<Point2> normalizeDiagonal(int startX, int startY, int endX, int endY){
|
||||
public static Array<Point2> pathfindLine(boolean conveyors, int startX, int startY, int endX, int endY){
|
||||
Pools.freeAll(points);
|
||||
|
||||
points.clear();
|
||||
return bres.lineNoDiagonal(startX, startY, endX, endY, Pools.get(Point2.class, Point2::new), points);
|
||||
if(conveyors && Core.settings.getBool("conveyorpathfinding")){
|
||||
if(astar(startX, startY, endX, endY)){
|
||||
return points;
|
||||
}else{
|
||||
return normalizeLine(startX, startY, endX, endY);
|
||||
}
|
||||
}else{
|
||||
return bres.lineNoDiagonal(startX, startY, endX, endY, Pools.get(Point2.class, Point2::new), points);
|
||||
}
|
||||
}
|
||||
|
||||
/** Normalize two points into one straight line, no diagonals. */
|
||||
@@ -40,6 +56,92 @@ public class PlaceUtils{
|
||||
return points;
|
||||
}
|
||||
|
||||
private static float tileHeuristic(Tile tile, Tile other){
|
||||
Block block = control.input.block;
|
||||
|
||||
if((!other.block().alwaysReplace && !(block != null && block.canReplace(other.block()))) || other.floor().isDeep()){
|
||||
return 20;
|
||||
}else{
|
||||
if(parents.containsKey(tile.pos())){
|
||||
Tile prev = world.tile(parents.get(tile.pos(), 0));
|
||||
if(tile.relativeTo(prev) != other.relativeTo(tile)){
|
||||
return 8;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static float distanceHeuristic(int x1, int y1, int x2, int y2){
|
||||
return Math.abs(x1 - x2) + Math.abs(y1 - y2);
|
||||
}
|
||||
|
||||
private static boolean validNode(Tile tile, Tile other){
|
||||
Block block = control.input.block;
|
||||
if(block != null && block.canReplace(other.block())){
|
||||
return true;
|
||||
}else{
|
||||
return other.block().alwaysReplace;
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean astar(int startX, int startY, int endX, int endY){
|
||||
Tile start = world.tile(startX, startY);
|
||||
Tile end = world.tile(endX, endY);
|
||||
if(start == end || start == null || end == null) return false;
|
||||
|
||||
costs.clear();
|
||||
closed.clear();
|
||||
parents.clear();
|
||||
|
||||
int nodeLimit = 1000;
|
||||
int totalNodes = 0;
|
||||
|
||||
PriorityQueue<Tile> queue = new PriorityQueue<>(10, (a, b) -> Float.compare(costs.get(a.pos(), 0f) + distanceHeuristic(a.x, a.y, end.x, end.y), costs.get(b.pos(), 0f) + distanceHeuristic(b.x, b.y, end.x, end.y)));
|
||||
queue.add(start);
|
||||
boolean found = false;
|
||||
while(!queue.isEmpty() && totalNodes++ < nodeLimit){
|
||||
Tile next = queue.poll();
|
||||
float baseCost = costs.get(next.pos(), 0f);
|
||||
if(next == end){
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
closed.add(Pos.get(next.x, next.y));
|
||||
for(Point2 point : Geometry.d4){
|
||||
int newx = next.x + point.x, newy = next.y + point.y;
|
||||
Tile child = world.tile(newx, newy);
|
||||
if(child != null && validNode(next, child)){
|
||||
if(closed.add(child.pos())){
|
||||
parents.put(child.pos(), next.pos());
|
||||
costs.put(child.pos(), tileHeuristic(next, child) + baseCost);
|
||||
queue.add(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!found) return false;
|
||||
int total = 0;
|
||||
|
||||
points.add(Pools.obtain(Point2.class, Point2::new).set(endX, endY));
|
||||
|
||||
Tile current = end;
|
||||
while(current != start && total++ < nodeLimit){
|
||||
if(current == null) return false;
|
||||
int newPos = parents.get(current.pos(), Pos.invalid);
|
||||
|
||||
if(newPos == Pos.invalid) return false;
|
||||
|
||||
points.add(Pools.obtain(Point2.class, Point2::new).set(Pos.x(newPos), Pos.y(newPos)));
|
||||
current = world.tile(newPos);
|
||||
}
|
||||
|
||||
points.reverse();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes a placement area and returns the result, ready to be used for drawing a rectangle.
|
||||
* Returned x2 and y2 will <i>always</i> be greater than x and y.
|
||||
@@ -95,14 +197,14 @@ public class PlaceUtils{
|
||||
}else{
|
||||
endx = tilex;
|
||||
}
|
||||
}
|
||||
|
||||
if(Math.abs(endx - tilex) > maxLength){
|
||||
endx = Mathf.sign(endx - tilex) * maxLength + tilex;
|
||||
}
|
||||
if(Math.abs(endx - tilex) > maxLength){
|
||||
endx = Mathf.sign(endx - tilex) * maxLength + tilex;
|
||||
}
|
||||
|
||||
if(Math.abs(endy - tiley) > maxLength){
|
||||
endy = Mathf.sign(endy - tiley) * maxLength + tiley;
|
||||
}
|
||||
if(Math.abs(endy - tiley) > maxLength){
|
||||
endy = Mathf.sign(endy - tiley) * maxLength + tiley;
|
||||
}
|
||||
|
||||
int dx = endx - tilex, dy = endy - tiley;
|
||||
@@ -141,12 +243,12 @@ public class PlaceUtils{
|
||||
return result;
|
||||
}
|
||||
|
||||
static class NormalizeDrawResult{
|
||||
public static class NormalizeDrawResult{
|
||||
float x, y, x2, y2;
|
||||
}
|
||||
|
||||
static class NormalizeResult{
|
||||
int x, y, x2, y2, rotation;
|
||||
public static class NormalizeResult{
|
||||
public int x, y, x2, y2, rotation;
|
||||
|
||||
boolean isX(){
|
||||
return Math.abs(x2 - x) > Math.abs(y2 - y);
|
||||
@@ -173,4 +275,12 @@ public class PlaceUtils{
|
||||
return y + (x2 - x > y2 - y ? 0 : i);
|
||||
}
|
||||
}
|
||||
|
||||
public interface DistanceHeuristic{
|
||||
float cost(int x1, int y1, int x2, int y2);
|
||||
}
|
||||
|
||||
public interface TileHueristic{
|
||||
float cost(Tile tile, Tile other);
|
||||
}
|
||||
}
|
||||
@@ -4,16 +4,32 @@ import io.anuke.arc.util.serialization.*;
|
||||
import io.anuke.arc.util.serialization.Json.*;
|
||||
import io.anuke.mindustry.*;
|
||||
import io.anuke.mindustry.content.*;
|
||||
import io.anuke.mindustry.ctype.MappableContent;
|
||||
import io.anuke.mindustry.game.*;
|
||||
import io.anuke.mindustry.type.*;
|
||||
import io.anuke.mindustry.world.*;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public class JsonIO{
|
||||
private static CustomJson jsonBase = new CustomJson();
|
||||
private static Json json = new Json(){{
|
||||
apply(this);
|
||||
}};
|
||||
private static Json json = new Json(){
|
||||
{ apply(this); }
|
||||
|
||||
@Override
|
||||
public void writeValue(Object value, Class knownType, Class elementType){
|
||||
if(value instanceof io.anuke.mindustry.ctype.MappableContent){
|
||||
try{
|
||||
getWriter().value(((MappableContent)value).name);
|
||||
}catch(IOException e){
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}else{
|
||||
super.writeValue(value, knownType, elementType);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
public static String write(Object object){
|
||||
return json.toJson(object, object.getClass());
|
||||
@@ -66,24 +82,6 @@ public class JsonIO{
|
||||
}
|
||||
});
|
||||
|
||||
//TODO extremely hacky and disgusting
|
||||
for(Block block : Vars.content.blocks()){
|
||||
Class type = block.getClass();
|
||||
if(type.isAnonymousClass()) type = type.getSuperclass();
|
||||
|
||||
json.setSerializer(type, new Serializer<Block>(){
|
||||
@Override
|
||||
public void write(Json json, Block object, Class knownType){
|
||||
json.writeValue(object.name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Block read(Json json, JsonValue jsonData, Class type){
|
||||
return Vars.content.getByName(ContentType.block, jsonData.asString());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
json.setSerializer(Block.class, new Serializer<Block>(){
|
||||
@Override
|
||||
public void write(Json json, Block object, Class knownType){
|
||||
@@ -96,26 +94,6 @@ public class JsonIO{
|
||||
}
|
||||
});
|
||||
|
||||
/*
|
||||
json.setSerializer(TeamData.class, new Serializer<TeamData>(){
|
||||
@Override
|
||||
public void write(Json json, TeamData object, Class knownType){
|
||||
json.writeObjectStart();
|
||||
json.writeValue("brokenBlocks", object.brokenBlocks.toArray());
|
||||
json.writeValue("team", object.team.ordinal());
|
||||
json.writeObjectEnd();
|
||||
}
|
||||
|
||||
@Override
|
||||
public TeamData read(Json json, JsonValue jsonData, Class type){
|
||||
long[] blocks = jsonData.get("brokenBlocks").asLongArray();
|
||||
Team team = Team.all[jsonData.getInt("team", 0)];
|
||||
TeamData out = new TeamData(team, EnumSet.of(new Team[]{}));
|
||||
out.brokenBlocks = new LongQueue(blocks);
|
||||
return out;
|
||||
}
|
||||
});*/
|
||||
|
||||
json.setSerializer(ItemStack.class, new Serializer<ItemStack>(){
|
||||
@Override
|
||||
public void write(Json json, ItemStack object, Class knownType){
|
||||
|
||||
@@ -1,25 +1,21 @@
|
||||
package io.anuke.mindustry.io;
|
||||
|
||||
import io.anuke.arc.collection.*;
|
||||
import io.anuke.arc.files.FileHandle;
|
||||
import io.anuke.arc.graphics.Color;
|
||||
import io.anuke.arc.graphics.Pixmap;
|
||||
import io.anuke.arc.util.Pack;
|
||||
import io.anuke.arc.util.Structs;
|
||||
import io.anuke.arc.util.serialization.Json;
|
||||
import io.anuke.mindustry.content.Blocks;
|
||||
import io.anuke.mindustry.game.SpawnGroup;
|
||||
import io.anuke.mindustry.game.Team;
|
||||
import io.anuke.mindustry.io.MapIO.TileProvider;
|
||||
import io.anuke.mindustry.maps.Map;
|
||||
import io.anuke.mindustry.type.ContentType;
|
||||
import io.anuke.arc.files.*;
|
||||
import io.anuke.arc.graphics.*;
|
||||
import io.anuke.arc.util.*;
|
||||
import io.anuke.arc.util.serialization.*;
|
||||
import io.anuke.mindustry.content.*;
|
||||
import io.anuke.mindustry.game.*;
|
||||
import io.anuke.mindustry.io.MapIO.*;
|
||||
import io.anuke.mindustry.maps.*;
|
||||
import io.anuke.mindustry.type.*;
|
||||
import io.anuke.mindustry.world.*;
|
||||
import io.anuke.mindustry.world.LegacyColorMapper.LegacyBlock;
|
||||
import io.anuke.mindustry.world.blocks.BlockPart;
|
||||
import io.anuke.mindustry.world.blocks.Floor;
|
||||
import io.anuke.mindustry.world.LegacyColorMapper.*;
|
||||
import io.anuke.mindustry.world.blocks.*;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.zip.InflaterInputStream;
|
||||
import java.util.zip.*;
|
||||
|
||||
import static io.anuke.mindustry.Vars.*;
|
||||
|
||||
@@ -208,20 +204,6 @@ public class LegacyMapIO{
|
||||
|
||||
//place core
|
||||
if(color == Color.rgba8888(Color.green)){
|
||||
for(int dx = 0; dx < 3; dx++){
|
||||
for(int dy = 0; dy < 3; dy++){
|
||||
int worldx = dx - 1 + x;
|
||||
int worldy = dy - 1 + y;
|
||||
|
||||
//multiblock parts
|
||||
if(Structs.inBounds(worldx, worldy, pixmap.getWidth(), pixmap.getHeight())){
|
||||
Tile write = tiles[worldx][worldy];
|
||||
write.setBlock(BlockPart.get(dx - 1, dy - 1));
|
||||
write.setTeam(Team.sharded);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//actual core parts
|
||||
tile.setBlock(Blocks.coreShard);
|
||||
tile.setTeam(Team.sharded);
|
||||
|
||||
@@ -6,6 +6,7 @@ import io.anuke.arc.graphics.*;
|
||||
import io.anuke.arc.graphics.Pixmap.*;
|
||||
import io.anuke.arc.util.io.*;
|
||||
import io.anuke.mindustry.content.*;
|
||||
import io.anuke.mindustry.core.*;
|
||||
import io.anuke.mindustry.game.*;
|
||||
import io.anuke.mindustry.maps.*;
|
||||
import io.anuke.mindustry.world.*;
|
||||
|
||||
@@ -34,37 +34,23 @@ public class SaveIO{
|
||||
return versions.get(version);
|
||||
}
|
||||
|
||||
public static void saveToSlot(int slot){
|
||||
FileHandle file = fileFor(slot);
|
||||
public static void save(FileHandle file){
|
||||
boolean exists = file.exists();
|
||||
if(exists) file.moveTo(backupFileFor(file));
|
||||
try{
|
||||
write(fileFor(slot));
|
||||
write(file);
|
||||
}catch(Exception e){
|
||||
if(exists) backupFileFor(file).moveTo(file);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static void loadFromSlot(int slot) throws SaveException{
|
||||
load(fileFor(slot));
|
||||
public static DataInputStream getStream(FileHandle file){
|
||||
return new DataInputStream(new InflaterInputStream(file.read(bufferSize)));
|
||||
}
|
||||
|
||||
public static DataInputStream getSlotStream(int slot){
|
||||
return new DataInputStream(new InflaterInputStream(fileFor(slot).read(bufferSize)));
|
||||
}
|
||||
|
||||
public static DataInputStream getBackupSlotStream(int slot){
|
||||
return new DataInputStream(new InflaterInputStream(backupFileFor(fileFor(slot)).read(bufferSize)));
|
||||
}
|
||||
|
||||
public static boolean isSaveValid(int slot){
|
||||
try{
|
||||
getMeta(slot);
|
||||
return true;
|
||||
}catch(Exception e){
|
||||
return false;
|
||||
}
|
||||
public static DataInputStream getBackupStream(FileHandle file){
|
||||
return new DataInputStream(new InflaterInputStream(backupFileFor(file).read(bufferSize)));
|
||||
}
|
||||
|
||||
public static boolean isSaveValid(FileHandle file){
|
||||
@@ -85,11 +71,11 @@ public class SaveIO{
|
||||
}
|
||||
}
|
||||
|
||||
public static SaveMeta getMeta(int slot){
|
||||
public static SaveMeta getMeta(FileHandle file){
|
||||
try{
|
||||
return getMeta(getSlotStream(slot));
|
||||
return getMeta(getStream(file));
|
||||
}catch(Exception e){
|
||||
return getMeta(getBackupSlotStream(slot));
|
||||
return getMeta(getBackupStream(file));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -167,6 +153,7 @@ public class SaveIO{
|
||||
}catch(Exception e){
|
||||
throw new SaveException(e);
|
||||
}finally{
|
||||
world.setGenerating(false);
|
||||
content.setTemporaryMapper(null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,9 @@ package io.anuke.mindustry.io;
|
||||
import io.anuke.arc.collection.*;
|
||||
import io.anuke.arc.util.*;
|
||||
import io.anuke.arc.util.io.*;
|
||||
import io.anuke.mindustry.content.*;
|
||||
import io.anuke.mindustry.core.*;
|
||||
import io.anuke.mindustry.ctype.*;
|
||||
import io.anuke.mindustry.entities.*;
|
||||
import io.anuke.mindustry.entities.traits.*;
|
||||
import io.anuke.mindustry.game.*;
|
||||
@@ -164,6 +167,7 @@ public abstract class SaveVersion extends SaveFileReader{
|
||||
short floorid = stream.readShort();
|
||||
short oreid = stream.readShort();
|
||||
int consecutives = stream.readUnsignedByte();
|
||||
if(content.block(floorid) == Blocks.air) floorid = Blocks.stone.id;
|
||||
|
||||
context.create(x, y, floorid, oreid, (short)0);
|
||||
|
||||
@@ -180,6 +184,7 @@ public abstract class SaveVersion extends SaveFileReader{
|
||||
int x = i % width, y = i / width;
|
||||
Block block = content.block(stream.readShort());
|
||||
Tile tile = context.tile(x, y);
|
||||
if(block == null) block = Blocks.air;
|
||||
tile.setBlock(block);
|
||||
|
||||
if(tile.entity != null){
|
||||
@@ -257,7 +262,7 @@ public abstract class SaveVersion extends SaveFileReader{
|
||||
TeamData data = state.teams.get(team);
|
||||
int blocks = stream.readInt();
|
||||
for(int j = 0; j < blocks; j++){
|
||||
data.brokenBlocks.addLast(new BrokenBlock(stream.readShort(), stream.readShort(), stream.readShort(), stream.readShort(), stream.readInt()));
|
||||
data.brokenBlocks.addLast(new BrokenBlock(stream.readShort(), stream.readShort(), stream.readShort(), content.block(stream.readShort()).id, stream.readInt()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ 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.game.*;
|
||||
import io.anuke.mindustry.net.Administration.TraceInfo;
|
||||
import io.anuke.mindustry.net.Packets.AdminAction;
|
||||
import io.anuke.mindustry.net.Packets.KickReason;
|
||||
@@ -126,6 +126,8 @@ public class TypeIO{
|
||||
if(!request.breaking){
|
||||
buffer.putShort(request.block.id);
|
||||
buffer.put((byte)request.rotation);
|
||||
buffer.put(request.hasConfig ? (byte)1 : 0);
|
||||
buffer.putInt(request.config);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -148,7 +150,12 @@ public class TypeIO{
|
||||
}else{ //place
|
||||
short block = buffer.getShort();
|
||||
byte rotation = buffer.get();
|
||||
boolean hasConfig = buffer.get() == 1;
|
||||
int config = buffer.getInt();
|
||||
currentRequest = new BuildRequest(Pos.x(position), Pos.y(position), rotation, content.block(block));
|
||||
if(hasConfig){
|
||||
currentRequest.configure(config);
|
||||
}
|
||||
}
|
||||
|
||||
reqs[i] = (currentRequest);
|
||||
@@ -167,6 +174,23 @@ public class TypeIO{
|
||||
return KickReason.values()[buffer.get()];
|
||||
}
|
||||
|
||||
@WriteClass(Rules.class)
|
||||
public static void writeRules(ByteBuffer buffer, Rules rules){
|
||||
String string = JsonIO.write(rules);
|
||||
byte[] bytes = string.getBytes(charset);
|
||||
buffer.putInt(bytes.length);
|
||||
buffer.put(bytes);
|
||||
}
|
||||
|
||||
@ReadClass(Rules.class)
|
||||
public static Rules readRules(ByteBuffer buffer){
|
||||
int length = buffer.getInt();
|
||||
byte[] bytes = new byte[length];
|
||||
buffer.get(bytes);
|
||||
String string = new String(bytes, charset);
|
||||
return JsonIO.read(Rules.class, string);
|
||||
}
|
||||
|
||||
@WriteClass(Team.class)
|
||||
public static void writeTeam(ByteBuffer buffer, Team reason){
|
||||
buffer.put((byte)reason.ordinal());
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package io.anuke.mindustry.io.versions;
|
||||
|
||||
import io.anuke.arc.function.Supplier;
|
||||
import io.anuke.arc.func.Prov;
|
||||
import io.anuke.mindustry.entities.type.Bullet;
|
||||
import io.anuke.mindustry.entities.effect.*;
|
||||
import io.anuke.mindustry.entities.type.Player;
|
||||
@@ -62,7 +62,7 @@ Before addition of new units: [build 79 and below]
|
||||
14 = Revenant
|
||||
*/
|
||||
public class LegacyTypeTable{
|
||||
private static final Supplier[] build81Table = {
|
||||
private static final Prov[] build81Table = {
|
||||
Player::new,
|
||||
Fire::new,
|
||||
Puddle::new,
|
||||
@@ -79,7 +79,7 @@ public class LegacyTypeTable{
|
||||
Revenant::new
|
||||
};
|
||||
|
||||
private static final Supplier[] build80Table = {
|
||||
private static final Prov[] build80Table = {
|
||||
Player::new,
|
||||
Fire::new,
|
||||
Puddle::new,
|
||||
@@ -98,7 +98,7 @@ public class LegacyTypeTable{
|
||||
Revenant::new
|
||||
};
|
||||
|
||||
private static final Supplier[] build79Table = {
|
||||
private static final Prov[] build79Table = {
|
||||
Player::new,
|
||||
Fire::new,
|
||||
Puddle::new,
|
||||
@@ -116,7 +116,7 @@ public class LegacyTypeTable{
|
||||
Revenant::new
|
||||
};
|
||||
|
||||
public static Supplier[] getTable(int build){
|
||||
public static Prov[] getTable(int build){
|
||||
if(build == -1 || build == 81){
|
||||
//return most recent one since that's probably it; not guaranteed
|
||||
return build81Table;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package io.anuke.mindustry.io.versions;
|
||||
|
||||
import io.anuke.arc.function.*;
|
||||
import io.anuke.arc.func.*;
|
||||
import io.anuke.mindustry.entities.traits.*;
|
||||
|
||||
import java.io.*;
|
||||
@@ -13,7 +13,7 @@ public class Save1 extends Save2{
|
||||
|
||||
@Override
|
||||
public void readEntities(DataInput stream) throws IOException{
|
||||
Supplier[] table = LegacyTypeTable.getTable(lastReadBuild);
|
||||
Prov[] table = LegacyTypeTable.getTable(lastReadBuild);
|
||||
|
||||
byte groups = stream.readByte();
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package io.anuke.mindustry.io.versions;
|
||||
|
||||
import io.anuke.mindustry.entities.traits.*;
|
||||
import io.anuke.mindustry.game.*;
|
||||
import io.anuke.mindustry.io.*;
|
||||
import io.anuke.mindustry.type.*;
|
||||
import io.anuke.mindustry.type.TypeID;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
|
||||
@@ -4,14 +4,17 @@ import io.anuke.arc.*;
|
||||
import io.anuke.arc.collection.*;
|
||||
import io.anuke.arc.files.*;
|
||||
import io.anuke.arc.graphics.*;
|
||||
import io.anuke.arc.util.*;
|
||||
import io.anuke.mindustry.*;
|
||||
import io.anuke.mindustry.game.EventType.*;
|
||||
import io.anuke.mindustry.game.*;
|
||||
import io.anuke.mindustry.io.*;
|
||||
import io.anuke.mindustry.maps.filters.*;
|
||||
import io.anuke.mindustry.type.*;
|
||||
|
||||
import static io.anuke.mindustry.Vars.maps;
|
||||
import static io.anuke.mindustry.Vars.*;
|
||||
|
||||
public class Map implements Comparable<Map>{
|
||||
public class Map implements Comparable<Map>, Publishable{
|
||||
/** Whether this is a custom map. */
|
||||
public final boolean custom;
|
||||
/** Metadata. Author description, display name, etc. */
|
||||
@@ -68,7 +71,7 @@ public class Map implements Comparable<Map>{
|
||||
}
|
||||
|
||||
public FileHandle cacheFile(){
|
||||
return Vars.mapPreviewDirectory.child(file.nameWithoutExtension() + "-cache.dat");
|
||||
return Vars.mapPreviewDirectory.child(workshop ? file.parent().name() + "-workshop-cache.dat" : file.nameWithoutExtension() + "-cache.dat");
|
||||
}
|
||||
|
||||
public void setHighScore(int score){
|
||||
@@ -131,6 +134,78 @@ public class Map implements Comparable<Map>{
|
||||
return tags.containsKey(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSteamID(){
|
||||
return tags.get("steamid");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addSteamID(String id){
|
||||
tags.put("steamid", id);
|
||||
|
||||
ui.editor.editor.getTags().put("steamid", id);
|
||||
try{
|
||||
ui.editor.save();
|
||||
}catch(Exception e){
|
||||
Log.err(e);
|
||||
}
|
||||
Events.fire(new MapPublishEvent());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeSteamID(){
|
||||
tags.remove("steamid");
|
||||
|
||||
ui.editor.editor.getTags().remove("steamid");
|
||||
try{
|
||||
ui.editor.save();
|
||||
}catch(Exception e){
|
||||
Log.err(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String steamTitle(){
|
||||
return name();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String steamDescription(){
|
||||
return description();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String steamTag(){
|
||||
return "map";
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileHandle createSteamFolder(String id){
|
||||
FileHandle mapFile = tmpDirectory.child("map_" + id).child("map.msav");
|
||||
file.copyTo(mapFile);
|
||||
return mapFile.parent();
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileHandle createSteamPreview(String id){
|
||||
return previewFile();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Array<String> extraTags(){
|
||||
Gamemode mode = Gamemode.attack.valid(this) ? Gamemode.attack : Gamemode.survival;
|
||||
return Array.with(mode.name());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean prePublish(){
|
||||
tags.put("author", player.name);
|
||||
ui.editor.editor.getTags().put("author", tags.get("author"));
|
||||
ui.editor.save();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(Map map){
|
||||
int work = -Boolean.compare(workshop, map.workshop);
|
||||
|
||||
@@ -8,7 +8,7 @@ import io.anuke.arc.files.*;
|
||||
import io.anuke.arc.graphics.*;
|
||||
import io.anuke.arc.util.*;
|
||||
import io.anuke.mindustry.*;
|
||||
import io.anuke.mindustry.game.*;
|
||||
import io.anuke.mindustry.ctype.Content;
|
||||
|
||||
public class MapPreviewLoader extends TextureLoader{
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user