This commit is contained in:
Anuken
2019-11-11 19:09:37 -05:00
301 changed files with 11549 additions and 6997 deletions

View File

@@ -11,7 +11,7 @@ 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.*;

View File

@@ -299,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")){

View File

@@ -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,11 +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, Predicate<Tile> pred, boolean usePriority){
public TileEntity findTile(Team team, float x, float y, float range, Boolf<Tile> pred, boolean usePriority){
TileEntity closest = null;
float dst = 0;
@@ -182,7 +182,7 @@ 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;

View File

@@ -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;
}
}

View File

@@ -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());
}
}
}

View File

@@ -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.*;
import io.anuke.mindustry.game.*;
import io.anuke.mindustry.gen.*;
import io.anuke.mindustry.graphics.*;
import io.anuke.mindustry.type.*;
@@ -52,7 +52,7 @@ 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
@@ -795,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;
@@ -1616,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;
@@ -1633,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;
@@ -1643,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;
@@ -1653,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;
@@ -1670,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);
@@ -1679,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;
maxSpawn = 2;
@@ -1689,7 +1702,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;
maxSpawn = 2;
@@ -1699,7 +1712,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);
@@ -1708,7 +1721,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;
@@ -1718,7 +1731,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);
@@ -1727,7 +1740,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;

View File

@@ -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.*;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;
}};

View File

@@ -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);
}
}
}

View File

@@ -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("vanguard-blaster"){{
length = 1.5f;
reload = 30f;
roundrobin = true;
alternate = true;
inaccuracy = 6f;
velocityRnd = 0.1f;
ejectEffect = Fx.none;
@@ -81,7 +81,7 @@ public class Mechs implements ContentList{
shake = 2f;
length = 1f;
reload = 70f;
roundrobin = true;
alternate = true;
bullet = Bullets.lancerLaser;
recoil = 4f;
shootSound = Sounds.spark;
@@ -120,7 +120,7 @@ public class Mechs implements ContentList{
length = 1.5f;
reload = 10f;
width = 4f;
roundrobin = true;
alternate = true;
bullet = Bullets.basicFlame;
}};
}
@@ -150,7 +150,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;
@@ -202,7 +202,7 @@ public class Mechs implements ContentList{
shots = 4;
spacing = 8f;
inaccuracy = 8f;
roundrobin = true;
alternate = true;
ejectEffect = Fx.none;
shake = 3f;
bullet = Bullets.missileSwarm;
@@ -228,7 +228,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);
}
@@ -266,7 +266,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;
}};
@@ -291,7 +291,7 @@ public class Mechs implements ContentList{
reload = 70f;
shots = 4;
inaccuracy = 2f;
roundrobin = true;
alternate = true;
ejectEffect = Fx.none;
velocityRnd = 0.2f;
spacing = 1f;
@@ -356,7 +356,7 @@ public class Mechs implements ContentList{
shots = 2;
shotDelay = 1f;
shots = 8;
roundrobin = true;
alternate = true;
ejectEffect = Fx.none;
velocityRnd = 1f;
inaccuracy = 20f;
@@ -394,7 +394,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;

View File

@@ -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;

View File

@@ -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;
@@ -104,6 +104,11 @@ public class TechTree implements ContentList{
node(door, () -> {
node(doorLarge);
});
node(plastaniumWall, () -> {
node(plastaniumWallLarge, () -> {
});
});
node(titaniumWallLarge);
node(thoriumWall, () -> {
node(thoriumWallLarge);

View File

@@ -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;

View File

@@ -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;
@@ -65,11 +65,11 @@ public class UnitTypes implements ContentList{
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"){
{

View File

@@ -1,5 +1,6 @@
package io.anuke.mindustry.content;
import io.anuke.mindustry.ctype.ContentList;
import io.anuke.mindustry.game.*;
import io.anuke.mindustry.game.Objectives.*;
import io.anuke.mindustry.maps.generators.*;

View File

@@ -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);
}
}
}
}

View File

@@ -254,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()){
@@ -302,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()){

View File

@@ -5,6 +5,7 @@ import io.anuke.arc.*;
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.*;
@@ -17,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.*;
/**
@@ -78,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();
}
}
}

View File

@@ -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;
}
}
}
}

View File

@@ -331,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.");
@@ -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;

View File

@@ -4,7 +4,7 @@ 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.*;
@@ -72,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", "");
@@ -101,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();
}

View File

@@ -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.*;
@@ -238,7 +239,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);
@@ -332,19 +333,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();

View File

@@ -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.*;
@@ -279,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);
@@ -296,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);
@@ -304,11 +304,11 @@ public class UI implements ApplicationListener, Loadable{
}
}
public void showTextInput(String title, String text, String def, Consumer<String> 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);
}
@@ -404,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);
@@ -429,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);

View File

@@ -1,4 +1,4 @@
package io.anuke.mindustry.game;
package io.anuke.mindustry.core;
import io.anuke.arc.*;
import io.anuke.arc.Files.*;

View File

@@ -185,6 +185,10 @@ public class World{
Events.fire(new WorldLoadEvent());
}
public void setGenerating(boolean gen){
this.generating = gen;
}
public boolean isGenerating(){
return generating;
}

View File

@@ -1,4 +1,4 @@
package io.anuke.mindustry.game;
package io.anuke.mindustry.ctype;
import io.anuke.arc.files.*;
import io.anuke.arc.util.ArcAnnotate.*;

View File

@@ -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{

View File

@@ -1,4 +1,4 @@
package io.anuke.mindustry.game;
package io.anuke.mindustry.ctype;
import io.anuke.mindustry.*;

View File

@@ -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);
@@ -61,6 +62,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();
}

View File

@@ -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++;

View File

@@ -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));
}
}
}

View File

@@ -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.*;
@@ -424,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(() -> {
@@ -506,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();
@@ -524,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(() -> {

View File

@@ -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(){

View File

@@ -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();
}
});

View File

@@ -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){

View File

@@ -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();
@@ -47,7 +47,7 @@ public class MapResizeDialog extends FloatingDialog{
buttons.defaults().size(200f, 50f);
buttons.addButton("$cancel", this::hide);
buttons.addButton("$ok", () -> {
cons.accept(width, height);
cons.get(width, height);
hide();
});
}

View File

@@ -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");
}

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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);
}
}

View File

@@ -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;

View File

@@ -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,12 +78,12 @@ 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)){
@@ -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);
}

View File

@@ -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.*;

View File

@@ -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;

View File

@@ -36,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());
}
}
}

View File

@@ -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.*;

View File

@@ -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);
}

View File

@@ -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;

View File

@@ -193,11 +193,16 @@ public interface BuilderTrait extends Entity, TeamTrait{
/** 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;
@@ -283,7 +288,7 @@ public interface BuilderTrait extends Entity, TeamTrait{
/** Last progress.*/
public float progress;
/** Whether construction has started for this request.*/
public boolean initialized;
public boolean initialized, worldContext = true;
/** Visual scale. Used only for rendering.*/
public float animScale = 0f;

View File

@@ -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{

View File

@@ -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.*;

View File

@@ -24,7 +24,9 @@ import io.anuke.mindustry.io.*;
import io.anuke.mindustry.net.Administration.*;
import io.anuke.mindustry.net.*;
import io.anuke.mindustry.type.*;
import io.anuke.mindustry.type.TypeID;
import io.anuke.mindustry.ui.*;
import io.anuke.mindustry.ui.Cicon;
import io.anuke.mindustry.world.*;
import io.anuke.mindustry.world.blocks.*;
@@ -50,6 +52,7 @@ public class Player extends Unit implements BuilderMinerTrait, ShooterTrait{
public @Nullable
String uuid, usid;
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,8 +362,8 @@ public class Player extends Unit implements BuilderMinerTrait, ShooterTrait{
public void drawOver(){
if(dead) return;
if(isBuilding()){
if(!state.isPaused() && isBuilding){
if(isBuilding() && isBuilding){
if(!state.isPaused()){
drawBuilding();
}
}else{
@@ -442,7 +445,7 @@ public class Player extends Unit implements BuilderMinerTrait, ShooterTrait{
control.input.drawBreaking(request);
}else{
request.block.drawRequest(request, control.input.allRequests(),
Build.validPlace(getTeam(), request.x, request.y, request.block, request.rotation));
Build.validPlace(getTeam(), request.x, request.y, request.block, request.rotation) || control.input.requestMatches(request));
}
}
@@ -460,7 +463,7 @@ public class Player extends Unit implements BuilderMinerTrait, ShooterTrait{
}
//mine only when not building
if(buildRequest() == null){
if(buildRequest() == null || !isBuilding){
updateMining();
}
}
@@ -589,11 +592,16 @@ 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;
}
if(Core.input.keyDown(Binding.mouse_move)){
movement.x += Mathf.clamp((Core.input.mouseX() - Core.graphics.getWidth() / 2) * 0.005f, -1, 1) * speed;
movement.y += Mathf.clamp((Core.input.mouseY() - Core.graphics.getHeight() / 2) * 0.005f, -1, 1) * speed;
}
Vector2 vec = Core.input.mouseWorld(control.input.getMouseX(), control.input.getMouseY());
pointerX = vec.x;
pointerY = vec.y;

View File

@@ -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.*;

View File

@@ -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(){

View File

@@ -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.*;
@@ -134,10 +135,38 @@ public class EventType{
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){
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;
}
}

View File

@@ -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(){

View File

@@ -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.*;
@@ -46,6 +47,7 @@ public class GlobalData{
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();
@@ -64,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();

View File

@@ -37,7 +37,7 @@ public class LoopControl{
}
}else{
if(data.curVolume <= 0.01f){
sound.stop(data.soundID);
sound.stop();
data.soundID = -1;
return;
}

View File

@@ -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)){

View File

@@ -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();
}
}
}

View File

@@ -5,13 +5,13 @@ 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.game.Schematics.*;
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{
public class Schematic implements Publishable, Comparable<Schematic>{
public final Array<Stile> tiles;
public StringMap tags;
public int width, height;
@@ -40,6 +40,16 @@ public class Schematic implements Publishable{
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");
}
@@ -90,10 +100,15 @@ public class Schematic implements Publishable{
@Override
public FileHandle createSteamPreview(String id){
FileHandle preview = tmpDirectory.child("schematic_preview_" + id + ".png");
schematics.savePreview(this, PreviewRes.high, preview);
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;

View File

@@ -16,9 +16,12 @@ 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.PlaceUtils.*;
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.*;
@@ -27,22 +30,33 @@ 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, ObjectMap<PreviewRes, FrameBuffer>> previews = new OrderedMap<>();
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.each((res, buffer) -> buffer.dispose()));
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
@@ -60,9 +74,32 @@ public class Schematics implements Loadable{
platform.getWorkshopContent(Schematic.class).each(this::loadFile);
Core.app.post(() -> {
shadowBuffer = new FrameBuffer(maxSchematicSize + padding + 2, maxSchematicSize + padding + 2);
});
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){
@@ -95,8 +132,8 @@ public class Schematics implements Loadable{
}
}
public void savePreview(Schematic schematic, PreviewRes res, FileHandle file){
FrameBuffer buffer = getBuffer(schematic, res);
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());
@@ -104,16 +141,31 @@ public class Schematics implements Loadable{
buffer.end();
}
public Texture getPreview(Schematic schematic, PreviewRes res){
return getBuffer(schematic, res).getTexture();
public Texture getPreview(Schematic schematic){
return getBuffer(schematic).getTexture();
}
public FrameBuffer getBuffer(Schematic schematic, PreviewRes res){
if(!previews.getOr(schematic, ObjectMap::new).containsKey(res)){
int resolution = res.resolution;
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();
Time.mark();
Tmp.m1.set(Draw.proj());
Tmp.m2.set(Draw.trans());
FrameBuffer buffer = new FrameBuffer((schematic.width + padding) * resolution, (schematic.height + padding) * resolution);
@@ -157,6 +209,7 @@ public class Schematics implements Loadable{
//draw requests
requests.each(req -> {
req.animScale = 1f;
req.worldContext = false;
req.block.drawRequestRegion(req, requests::each);
});
@@ -170,24 +223,48 @@ public class Schematics implements Loadable{
Draw.proj(Tmp.m1);
Draw.trans(Tmp.m2);
previews.getOr(schematic, ObjectMap::new).put(res, buffer);
Log.info("Time taken: {0}", Time.elapsed());
previews.put(schematic, buffer);
}
return previews.get(schematic).get(res);
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());
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{
write(schematic, schematicDirectory.child(Time.millis() + "." + schematicExtension));
}catch(IOException e){
FileHandle file = schematicDirectory.child(Time.millis() + "." + schematicExtension);
write(schematic, file);
schematic.file = file;
}catch(Exception e){
ui.showException(e);
Log.err(e);
}
}
@@ -197,16 +274,23 @@ public class Schematics implements Loadable{
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 = PlaceUtils.normalizeArea(x, y, x2, y2, 0, false, maxSchematicSize);
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;
@@ -215,7 +299,7 @@ public class Schematics implements Loadable{
for(int cy = y; cy <= y2; cy++){
Tile linked = world.ltile(cx, cy);
if(linked != null && linked.entity != null && linked.entity.block.isVisible()){
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);
@@ -238,17 +322,19 @@ public class Schematics implements Loadable{
int width = x2 - x + 1, height = y2 - y + 1;
int offsetX = -x, offsetY = -y;
for(int cx = x; cx <= x2; cx++){
for(int cy = y; cy <= y2; cy++){
Tile tile = world.tile(cx, cy);
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){
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(), cx + offsetX, cy + offsetY, config, tile.rotation()));
tiles.add(new Stile(tile.block(), tile.x + offsetX, tile.y + offsetY, config, tile.rotation()));
counted.add(tile.pos());
}
}
}
@@ -267,13 +353,13 @@ public class Schematics implements Loadable{
}
}
//region IO methods
/** Loads a schematic from base64. May throw an exception. */
public Schematic readBase64(String schematic) throws IOException{
public static Schematic readBase64(String schematic) throws IOException{
return read(new ByteArrayInputStream(Base64Coder.decode(schematic)));
}
//region IO methods
public static Schematic read(FileHandle file) throws IOException{
Schematic s = read(new DataInputStream(file.read(1024)));
if(!s.tags.containsKey("name")){
@@ -367,14 +453,4 @@ public class Schematics implements Loadable{
}
//endregion
public enum PreviewRes{
low(8), med(8), high(32);
public final int resolution;
PreviewRes(int resolution){
this.resolution = resolution;
}
}
}

View File

@@ -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);

View File

@@ -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;

View File

@@ -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.*;
@@ -41,9 +41,11 @@ public class Tutorial{
Events.on(DepositEvent.class, event -> events.add("deposit"));
Events.on(WithdrawEvent.class, event -> events.add("withdraw"));
for(TutorialStage stage : TutorialStage.values()){
stage.load();
}
Events.on(ClientLoadEvent.class, e -> {
for(TutorialStage stage : TutorialStage.values()){
stage.load();
}
});
}
/** update tutorial state, transition if needed */
@@ -192,16 +194,16 @@ 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);
}

View File

@@ -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;
}
}

View File

@@ -12,6 +12,7 @@ import io.anuke.mindustry.content.*;
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 static io.anuke.arc.Core.camera;
@@ -123,7 +124,9 @@ public class BlockRenderer implements Disposable{
Draw.shader();
}
public void drawBroken(){
public void drawDestroyed(){
if(!Core.settings.getBool("destroyedblocks")) return;
if(control.input.isPlacing() || control.input.isBreaking()){
brokenFade = Mathf.lerpDelta(brokenFade, 1f, 0.1f);
}else{

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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.*;
@@ -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

View File

@@ -75,6 +75,8 @@ public class Pal{
surge = Color.valueOf("f3e979"),
plastanium = Color.valueOf("a1b46e"),
redSpark = Color.valueOf("fbb97f"),
orangeSpark = Color.valueOf("d2b29c"),

View File

@@ -9,6 +9,7 @@ import io.anuke.arc.input.KeyCode;
public enum Binding implements KeyBind{
move_x(new Axis(KeyCode.A, KeyCode.D), "general"),
move_y(new Axis(KeyCode.S, KeyCode.W)),
mouse_move(KeyCode.MOUSE_BACK),
select(KeyCode.MOUSE_LEFT),
deselect(KeyCode.MOUSE_RIGHT),
break_block(KeyCode.MOUSE_RIGHT),
@@ -23,8 +24,6 @@ public enum Binding implements KeyBind{
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),
@@ -33,12 +32,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;

View File

@@ -45,13 +45,14 @@ public class DesktopInput extends InputHandler{
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.name())).style(Styles.outlineLabel);
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.name())).style(Styles.outlineLabel);
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.name())).style(Styles.outlineLabel);
b.add(Core.bundle.format("selectschematic", Core.keybinds.get(Binding.schematic_select).key.toString())).style(Styles.outlineLabel);
}).margin(10f);
});
@@ -59,21 +60,13 @@ public class DesktopInput extends InputHandler{
t.visible(() -> lastSchematic != null && !selectRequests.isEmpty());
t.bottom();
t.table(Styles.black6, b -> {
b.touchable(Touchable.enabled);
b.defaults().left();
b.add(Core.bundle.format("schematic.flip",
Core.keybinds.get(Binding.schematic_flip_x).key.name(),
Core.keybinds.get(Binding.schematic_flip_y).key.name())).style(Styles.outlineLabel);
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, () -> {
ui.showTextInput("$schematic.add", "$name", "", text -> {
lastSchematic.tags.put("name", text);
schematics.add(lastSchematic);
ui.showInfoFade("$schematic.saved");
ui.schematics.showInfo(lastSchematic);
});
}).colspan(2).size(250f, 50f);
a.addImageTextButton("$schematic.add", Icon.saveSmall, this::showSchematicSave).colspan(2).size(250f, 50f).disabled(f -> lastSchematic == null || lastSchematic.file != null);
});
}).margin(6f);
});
@@ -129,7 +122,7 @@ public class DesktopInput extends InputHandler{
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)){
if(Core.input.keyDown(Binding.schematic_select) && !ui.chatfrag.chatOpen()){
drawSelection(schemX, schemY, cursorX, cursorY, Vars.maxSchematicSize);
}
@@ -173,8 +166,9 @@ public class DesktopInput extends InputHandler{
mode = none;
}
if(mode != none){
if(mode != none || isPlacing()){
selectRequests.clear();
lastSchematic = null;
}
if(player.isShooting && !canShoot()){
@@ -188,13 +182,14 @@ public class DesktopInput extends InputHandler{
selectScale = 0f;
}
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(!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()){
@@ -243,6 +238,7 @@ public class DesktopInput extends InputHandler{
schematicX = tileX(getMouseX());
schematicY = tileY(getMouseY());
selectRequests.clear();
selectRequests.addAll(schematics.toRequests(schem, schematicX, schematicY));
mode = none;
}
@@ -258,17 +254,25 @@ public class DesktopInput extends InputHandler{
table.row();
table.left().margin(0f).defaults().size(48f).left();
table.addImageButton(Icon.wikiSmall, Styles.clearPartiali, () -> {
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;
@@ -289,13 +293,17 @@ public class DesktopInput extends InputHandler{
player.clearBuilding();
}
if(Core.input.keyTap(Binding.schematic_select)){
if(Core.input.keyTap(Binding.schematic_select) && !ui.chatfrag.chatOpen()){
schemX = rawCursorX;
schemY = rawCursorY;
}
if(Core.input.keyTap(Binding.schematic_menu)){
ui.schematics.show();
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)){
@@ -303,7 +311,7 @@ public class DesktopInput extends InputHandler{
selectRequests.clear();
}
if(Core.input.keyRelease(Binding.schematic_select)){
if(Core.input.keyRelease(Binding.schematic_select) && !ui.chatfrag.chatOpen()){
lastSchematic = schematics.create(schemX, schemY, rawCursorX, rawCursorY);
useSchematic(lastSchematic);
if(selectRequests.isEmpty()){
@@ -335,6 +343,7 @@ public class DesktopInput extends InputHandler{
if(Core.input.keyTap(Binding.pause_building)){
player.isBuilding = !player.isBuilding;
player.buildWasAutoPaused = false;
}
if((cursorX != lastLineX || cursorY != lastLineY) && isPlacing() && mode == placing){
@@ -348,7 +357,6 @@ public class DesktopInput extends InputHandler{
if(!selectRequests.isEmpty()){
flushRequests(selectRequests);
//selectRequests.clear();
}else if(isPlacing()){
selectX = cursorX;
selectY = cursorY;
@@ -362,7 +370,7 @@ public class DesktopInput extends InputHandler{
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;
}
@@ -421,6 +429,15 @@ public class DesktopInput extends InputHandler{
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

View File

@@ -3,7 +3,7 @@ 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.*;
@@ -13,8 +13,8 @@ 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.*;
import io.anuke.arc.util.ArcAnnotate.*;
import io.anuke.arc.util.*;
import io.anuke.mindustry.content.*;
import io.anuke.mindustry.entities.*;
import io.anuke.mindustry.entities.effect.*;
@@ -25,11 +25,14 @@ 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.PlaceUtils.*;
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.*;
@@ -108,7 +111,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
int[] remaining = {accepted, accepted};
Block block = tile.block();
Core.app.post(() -> Events.fire(new DepositEvent(tile, player)));
Core.app.post(() -> Events.fire(new DepositEvent(tile, player, item, accepted)));
for(int i = 0; i < sent; i++){
boolean end = i == sent - 1;
@@ -142,19 +145,21 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
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.accept(request);
for(BuildRequest request : selectRequests) cons.accept(request);
for(BuildRequest request : lineRequests) cons.accept(request);
for(BuildRequest request : player.buildQueue()) cons.get(request);
for(BuildRequest request : selectRequests) cons.get(request);
for(BuildRequest request : lineRequests) cons.get(request);
};
}
@@ -214,6 +219,11 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
}
}
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;
@@ -226,8 +236,28 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
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 = rawTileX(), oy = rawTileY();
int ox = schemOriginX(), oy = schemOriginY();
requests.each(req -> {
//rotate config position
@@ -262,15 +292,15 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
}
public void flipRequests(Array<BuildRequest> requests, boolean x){
int origin = x ? rawTileX() : rawTileY();
int origin = (x ? schemOriginX() : schemOriginY()) * tilesize;
requests.each(req -> {
int value = -((x ? req.x : req.y) - origin) + origin;
float value = -((x ? req.x : req.y) * tilesize - origin + req.block.offset()) + origin;
if(x){
req.x = value;
req.x = (int)((value - req.block.offset()) / tilesize);
}else{
req.y = value;
req.y = (int)((value - req.block.offset()) / tilesize);
}
if(req.block.posConfig){
@@ -292,6 +322,14 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
});
}
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);
@@ -304,7 +342,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
r2.setCenter(x * tilesize + offset, y * tilesize + offset);
resultreq = null;
Predicate<BuildRequest> test = req -> {
Boolf<BuildRequest> test = req -> {
if(req == skip) return false;
Tile other = req.tile();
@@ -322,19 +360,19 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
};
for(BuildRequest req : player.buildQueue()){
if(test.test(req)) return req;
if(test.get(req)) return req;
}
for(BuildRequest req : selectRequests){
if(test.test(req)) return req;
if(test.get(req)) return req;
}
return null;
}
protected void drawBreakSelection(int x1, int y1, int x2, int y2){
NormalizeDrawResult result = PlaceUtils.normalizeDrawArea(Blocks.air, x1, y1, x2, y2, false, maxLength, 1f);
NormalizeResult dresult = PlaceUtils.normalizeArea(x1, y1, x2, y2, rotation, false, maxLength);
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++){
@@ -357,13 +395,12 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
}
}
/*
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);
@@ -381,7 +418,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
}
protected void drawSelection(int x1, int y1, int x2, int y2, int maxLength){
NormalizeDrawResult result = PlaceUtils.normalizeDrawArea(Blocks.air, x1, y1, x2, y2, false, maxLength, 1f);
NormalizeDrawResult result = Placement.normalizeDrawArea(Blocks.air, x1, y1, x2, y2, false, maxLength, 1f);
Lines.stroke(2f);
@@ -394,7 +431,13 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
protected void flushSelectRequests(Array<BuildRequest> requests){
for(BuildRequest req : requests){
if(req.block != null && validPlace(req.x, req.y, req.block, req.rotation)){
selectRequests.add(req.copy());
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());
}
}
}
}
@@ -429,7 +472,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
/** Remove everything from the queue in a selection. */
protected void removeSelection(int x1, int y1, int x2, int y2, boolean flush){
NormalizeResult result = PlaceUtils.normalizeArea(x1, y1, x2, y2, rotation, false, maxLength);
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);
@@ -458,14 +501,13 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
}
}
/*
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();
@@ -486,6 +528,15 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
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){
@@ -708,7 +759,10 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
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))){
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;
}
}
@@ -720,6 +774,10 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
}
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));
}
@@ -748,17 +806,45 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
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);
@@ -785,7 +871,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
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());
}

View File

@@ -2,14 +2,17 @@ package io.anuke.mindustry.input;
import io.anuke.arc.*;
import io.anuke.arc.collection.*;
import io.anuke.arc.func.*;
import io.anuke.arc.graphics.g2d.*;
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,6 +20,7 @@ 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.ui.*;
@@ -51,7 +55,7 @@ public class MobileInput extends InputHandler implements GestureListener{
/** 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. */
@@ -153,7 +157,9 @@ public class MobileInput extends InputHandler implements GestureListener{
void removeRequest(BuildRequest request){
selectRequests.removeValue(request, true);
removals.add(request);
if(!request.breaking){
removals.add(request);
}
}
boolean isLinePlacing(){
@@ -185,8 +191,23 @@ 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, () -> {
@@ -196,11 +217,23 @@ public class MobileInput extends InputHandler implements GestureListener{
//actually place/break all selected blocks
if(tile != null){
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);
}
@@ -216,8 +249,10 @@ public class MobileInput extends InputHandler implements GestureListener{
@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()) && !state.is(State.menu));
t.bottom().left().visible(() -> (player.isBuilding() || block != null || mode == breaking || !selectRequests.isEmpty()) && !schem.get());
t.addImageTextButton("$cancel", Icon.cancelSmall, () -> {
player.clearBuilding();
selectRequests.clear();
@@ -225,6 +260,41 @@ public class MobileInput extends InputHandler implements GestureListener{
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
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
@@ -249,8 +319,6 @@ public class MobileInput extends InputHandler implements GestureListener{
}else{
request.block.drawRequest(request, allRequests(), true);
}
//TODO
//drawRequest(request);
}
//draw list of requests
@@ -299,7 +367,10 @@ public class MobileInput extends InputHandler implements GestureListener{
if(i == lineRequests.size - 1 && req.block.rotate){
drawArrow(block, req.x, req.y, req.rotation);
}
drawRequest(lineRequests.get(i));
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);
}
}else if(mode == breaking){
drawBreakSelection(lineStartX, lineStartY, tileX, tileY);
@@ -329,6 +400,15 @@ 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;
@@ -350,6 +430,13 @@ public class MobileInput extends InputHandler implements GestureListener{
return mode == breaking;
}
@Override
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;
@@ -365,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;
}
@@ -386,6 +482,8 @@ public class MobileInput extends InputHandler implements GestureListener{
down = false;
}
selecting = false;
//place down a line if in line mode
if(lineMode){
int tileX = tileX(screenX);
@@ -399,6 +497,15 @@ public class MobileInput extends InputHandler implements GestureListener{
}
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);
@@ -417,7 +524,7 @@ 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
@@ -503,7 +610,6 @@ public class MobileInput extends InputHandler implements GestureListener{
//reset state when not placing
if(mode == none){
selecting = false;
lineMode = false;
}
@@ -520,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;
@@ -531,32 +653,7 @@ public class MobileInput extends InputHandler implements GestureListener{
//When in line mode, pan when near screen edges automatically
if(Core.input.isTouched(0)){
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;
autoPan();
}
int lx = tileX(Core.input.mouseX()), ly = tileY(Core.input.mouseY());
@@ -582,6 +679,35 @@ public class MobileInput extends InputHandler implements GestureListener{
}
}
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;
@@ -591,7 +717,7 @@ 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;
}

View File

@@ -1,5 +1,5 @@
package io.anuke.mindustry.input;
enum PlaceMode{
none, breaking, placing
none, breaking, placing, schematicSelect
}

View File

@@ -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.
@@ -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);
}
}

View File

@@ -4,6 +4,7 @@ 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.*;
@@ -18,7 +19,7 @@ public class JsonIO{
@Override
public void writeValue(Object value, Class knownType, Class elementType){
if(value instanceof MappableContent){
if(value instanceof io.anuke.mindustry.ctype.MappableContent){
try{
getWriter().value(((MappableContent)value).name);
}catch(IOException e){

View File

@@ -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);

View File

@@ -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.*;

View File

@@ -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);
}
}

View File

@@ -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){

View File

@@ -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());

View File

@@ -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;

View File

@@ -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();

View File

@@ -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.*;

View File

@@ -71,7 +71,7 @@ public class Map implements Comparable<Map>, Publishable{
}
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){
@@ -181,12 +181,14 @@ public class Map implements Comparable<Map>, Publishable{
@Override
public FileHandle createSteamFolder(String id){
return null;
FileHandle mapFile = tmpDirectory.child("map_" + id).child("map.msav");
file.copyTo(mapFile);
return mapFile.parent();
}
@Override
public FileHandle createSteamPreview(String id){
return null;
return previewFile();
}
@Override

View File

@@ -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{

View File

@@ -6,15 +6,16 @@ import io.anuke.arc.assets.loaders.*;
import io.anuke.arc.collection.*;
import io.anuke.arc.collection.IntSet.*;
import io.anuke.arc.files.*;
import io.anuke.arc.function.*;
import io.anuke.arc.func.*;
import io.anuke.arc.graphics.*;
import io.anuke.arc.util.*;
import io.anuke.arc.util.async.*;
import io.anuke.arc.util.io.*;
import io.anuke.arc.util.serialization.*;
import io.anuke.mindustry.content.*;
import io.anuke.mindustry.game.*;
import io.anuke.mindustry.ctype.*;
import io.anuke.mindustry.game.EventType.*;
import io.anuke.mindustry.game.*;
import io.anuke.mindustry.io.*;
import io.anuke.mindustry.maps.MapPreviewLoader.*;
import io.anuke.mindustry.maps.filters.*;
@@ -222,10 +223,10 @@ public class Maps{
/** Attempts to run the following code;
* catches any errors and attempts to display them in a readable way.*/
public void tryCatchMapError(ExceptionRunnable run){
public void tryCatchMapError(UnsafeRunnable run){
try{
run.run();
}catch(Exception e){
}catch(Throwable e){
Log.err(e);
if("Outdated legacy map format".equals(e.getMessage())){
@@ -356,7 +357,7 @@ public class Maps{
Core.app.post(() -> previewList.add(map));
}
private void createNewPreview(Map map, Consumer<Exception> failed){
private void createNewPreview(Map map, Cons<Exception> failed){
try{
//if it's here, then the preview failed to load or doesn't exist, make it
//this has to be done synchronously!
@@ -371,7 +372,7 @@ public class Maps{
}
});
}catch(Exception e){
failed.accept(e);
failed.get(e);
Log.err("Failed to generate preview!", e);
}
}

View File

@@ -2,13 +2,13 @@ package io.anuke.mindustry.maps.filters;
import io.anuke.arc.*;
import io.anuke.arc.function.*;
import io.anuke.arc.func.*;
import io.anuke.arc.scene.style.*;
import io.anuke.arc.scene.ui.*;
import io.anuke.arc.scene.ui.layout.*;
import io.anuke.mindustry.*;
import io.anuke.mindustry.content.*;
import io.anuke.mindustry.game.*;
import io.anuke.mindustry.ui.Cicon;
import io.anuke.mindustry.ui.dialogs.*;
import io.anuke.mindustry.world.*;
import io.anuke.mindustry.world.blocks.*;
@@ -16,13 +16,13 @@ import io.anuke.mindustry.world.blocks.*;
import static io.anuke.mindustry.Vars.updateEditorOnChange;
public abstract class FilterOption{
public static final Predicate<Block> floorsOnly = b -> (b instanceof Floor && !(b instanceof OverlayFloor)) && Core.atlas.isFound(b.icon(Cicon.full));
public static final Predicate<Block> wallsOnly = b -> (!b.synthetic() && !(b instanceof Floor)) && Core.atlas.isFound(b.icon(Cicon.full));
public static final Predicate<Block> floorsOptional = b -> b == Blocks.air || ((b instanceof Floor && !(b instanceof OverlayFloor)) && Core.atlas.isFound(b.icon(Cicon.full)));
public static final Predicate<Block> wallsOptional = b -> b == Blocks.air || ((!b.synthetic() && !(b instanceof Floor)) && Core.atlas.isFound(b.icon(Cicon.full)));
public static final Predicate<Block> wallsOresOptional = b -> b == Blocks.air || (((!b.synthetic() && !(b instanceof Floor)) || (b instanceof OverlayFloor)) && Core.atlas.isFound(b.icon(Cicon.full)));
public static final Predicate<Block> oresOnly = b -> b instanceof OverlayFloor && Core.atlas.isFound(b.icon(Cicon.full));
public static final Predicate<Block> anyOptional = b -> floorsOnly.test(b) || wallsOnly.test(b) || oresOnly.test(b) || b == Blocks.air;
public static final Boolf<Block> floorsOnly = b -> (b instanceof Floor && !(b instanceof OverlayFloor)) && Core.atlas.isFound(b.icon(io.anuke.mindustry.ui.Cicon.full));
public static final Boolf<Block> wallsOnly = b -> (!b.synthetic() && !(b instanceof Floor)) && Core.atlas.isFound(b.icon(io.anuke.mindustry.ui.Cicon.full));
public static final Boolf<Block> floorsOptional = b -> b == Blocks.air || ((b instanceof Floor && !(b instanceof OverlayFloor)) && Core.atlas.isFound(b.icon(io.anuke.mindustry.ui.Cicon.full)));
public static final Boolf<Block> wallsOptional = b -> b == Blocks.air || ((!b.synthetic() && !(b instanceof Floor)) && Core.atlas.isFound(b.icon(io.anuke.mindustry.ui.Cicon.full)));
public static final Boolf<Block> wallsOresOptional = b -> b == Blocks.air || (((!b.synthetic() && !(b instanceof Floor)) || (b instanceof OverlayFloor)) && Core.atlas.isFound(b.icon(io.anuke.mindustry.ui.Cicon.full)));
public static final Boolf<Block> oresOnly = b -> b instanceof OverlayFloor && Core.atlas.isFound(b.icon(io.anuke.mindustry.ui.Cicon.full));
public static final Boolf<Block> anyOptional = b -> floorsOnly.get(b) || wallsOnly.get(b) || oresOnly.get(b) || b == Blocks.air;
public abstract void build(Table table);
@@ -30,15 +30,15 @@ public abstract class FilterOption{
static class SliderOption extends FilterOption{
final String name;
final FloatProvider getter;
final FloatConsumer setter;
final Floatp getter;
final Floatc setter;
final float min, max, step;
SliderOption(String name, FloatProvider getter, FloatConsumer setter, float min, float max){
SliderOption(String name, Floatp getter, Floatc setter, float min, float max){
this(name, getter, setter, min, max, (max - min) / 200);
}
SliderOption(String name, FloatProvider getter, FloatConsumer setter, float min, float max, float step){
SliderOption(String name, Floatp getter, Floatc setter, float min, float max, float step){
this.name = name;
this.getter = getter;
this.setter = setter;
@@ -63,11 +63,11 @@ public abstract class FilterOption{
static class BlockOption extends FilterOption{
final String name;
final Supplier<Block> supplier;
final Consumer<Block> consumer;
final Predicate<Block> filter;
final Prov<Block> supplier;
final Cons<Block> consumer;
final Boolf<Block> filter;
BlockOption(String name, Supplier<Block> supplier, Consumer<Block> consumer, Predicate<Block> filter){
BlockOption(String name, Prov<Block> supplier, Cons<Block> consumer, Boolf<Block> filter){
this.name = name;
this.supplier = supplier;
this.consumer = consumer;
@@ -76,16 +76,16 @@ public abstract class FilterOption{
@Override
public void build(Table table){
table.addButton(b -> b.addImage(supplier.get().icon(Cicon.small)).update(i -> ((TextureRegionDrawable)i.getDrawable())
.setRegion(supplier.get() == Blocks.air ? Core.atlas.find("icon-none") : supplier.get().icon(Cicon.small))).size(8 * 3), () -> {
table.addButton(b -> b.addImage(supplier.get().icon(io.anuke.mindustry.ui.Cicon.small)).update(i -> ((TextureRegionDrawable)i.getDrawable())
.setRegion(supplier.get() == Blocks.air ? Core.atlas.find("icon-none") : supplier.get().icon(io.anuke.mindustry.ui.Cicon.small))).size(8 * 3), () -> {
FloatingDialog dialog = new FloatingDialog("");
dialog.setFillParent(false);
int i = 0;
for(Block block : Vars.content.blocks()){
if(!filter.test(block)) continue;
if(!filter.get(block)) continue;
dialog.cont.addImage(block == Blocks.air ? Core.atlas.find("icon-none-small") : block.icon(Cicon.medium)).size(8 * 4).pad(3).get().clicked(() -> {
consumer.accept(block);
consumer.get(block);
dialog.hide();
changed.run();
});

View File

@@ -1,6 +1,6 @@
package io.anuke.mindustry.maps.filters;
import io.anuke.arc.function.*;
import io.anuke.arc.func.*;
import io.anuke.arc.graphics.g2d.*;
import io.anuke.arc.math.geom.*;
import io.anuke.arc.scene.ui.*;
@@ -49,15 +49,15 @@ public class MirrorFilter extends GenerateFilter{
float imageHeight = Math.max(vsize.y, vsize.x);
float size = Math.max(image.getWidth() *2, image.getHeight()*2);
Consumer<Vector2> clamper = v ->
Cons<Vector2> clamper = v ->
v.clamp(
image.getX() + image.getWidth()/2f - imageWidth/2f,
image.getX() + image.getWidth()/2f + imageWidth/2f,
image.getY() + image.getHeight()/2f - imageHeight/2f,
image.getY() + image.getHeight()/2f + imageHeight/2f);
clamper.accept(Tmp.v1.trns(angle - 90, size).add(image.getWidth()/2f + image.getX(), image.getHeight()/2f + image.getY()));
clamper.accept(Tmp.v2.set(Tmp.v1).sub(image.getWidth()/2f + image.getX(), image.getHeight()/2f + image.getY()).rotate(180f).add(image.getWidth()/2f + image.getX(), image.getHeight()/2f + image.getY()));
clamper.get(Tmp.v1.trns(angle - 90, size).add(image.getWidth()/2f + image.getX(), image.getHeight()/2f + image.getY()));
clamper.get(Tmp.v2.set(Tmp.v1).sub(image.getWidth()/2f + image.getX(), image.getHeight()/2f + image.getY()).rotate(180f).add(image.getWidth()/2f + image.getX(), image.getHeight()/2f + image.getY()));
Lines.stroke(Scl.scl(3f), Pal.accent);
Lines.line(Tmp.v1.x, Tmp.v1.y, Tmp.v2.x, Tmp.v2.y);

View File

@@ -29,7 +29,7 @@ public class NoiseFilter extends GenerateFilter{
if(noise > threshold){
in.floor = floor;
if(wallsOnly.test(in.block)) in.block = block;
if(wallsOnly.get(in.block)) in.block = block;
}
}
}

View File

@@ -1,7 +1,7 @@
package io.anuke.mindustry.maps.generators;
import io.anuke.arc.collection.*;
import io.anuke.arc.function.IntPositionConsumer;
import io.anuke.arc.func.Intc2;
import io.anuke.arc.math.Mathf;
import io.anuke.arc.math.geom.Geometry;
import io.anuke.arc.math.geom.Point2;
@@ -137,10 +137,10 @@ public abstract class BasicGenerator extends RandomGenerator{
});
}
public void each(IntPositionConsumer r){
public void each(Intc2 r){
for(int x = 0; x < width; x++){
for(int y = 0; y < height; y++){
r.accept(x, y);
r.get(x, y);
}
}
}
@@ -149,13 +149,13 @@ public abstract class BasicGenerator extends RandomGenerator{
return (float)sim2.octaveNoise2D(1f, 0f, 1f / scl, x + 0x361266f, y + 0x251259f) * mag;
}
public void pass(Tile[][] tiles, IntPositionConsumer r){
public void pass(Tile[][] tiles, Intc2 r){
for(int x = 0; x < width; x++){
for(int y = 0; y < height; y++){
floor = tiles[x][y].floor();
block = tiles[x][y].block();
ore = tiles[x][y].overlay();
r.accept(x, y);
r.get(x, y);
tiles[x][y] = new Tile(x, y, floor.id, ore.id, block.id);
}
}

View File

@@ -1,11 +1,11 @@
package io.anuke.mindustry.maps.generators;
import io.anuke.mindustry.type.Loadout;
import io.anuke.mindustry.world.Tile;
import io.anuke.mindustry.game.*;
import io.anuke.mindustry.world.*;
public abstract class Generator{
public int width, height;
protected Loadout loadout;
protected Schematic loadout;
public Generator(int width, int height){
this.width = width;
@@ -15,7 +15,7 @@ public abstract class Generator{
public Generator(){
}
public void init(Loadout loadout){
public void init(Schematic loadout){
this.loadout = loadout;
}

View File

@@ -5,6 +5,7 @@ import io.anuke.arc.math.*;
import io.anuke.arc.math.geom.*;
import io.anuke.arc.util.*;
import io.anuke.mindustry.content.*;
import io.anuke.mindustry.game.*;
import io.anuke.mindustry.io.*;
import io.anuke.mindustry.maps.*;
import io.anuke.mindustry.type.*;
@@ -39,6 +40,9 @@ public class MapGenerator extends Generator{
this.decorations.addAll(decor);
return this;
}
public void removePrefix(String name){
this.mapName = this.mapName.substring(name.length() + 1);
}
{
decor(new Decoration(Blocks.snow, Blocks.snowrock, 0.01), new Decoration(Blocks.ignarock, Blocks.pebbles, 0.03f));
@@ -49,7 +53,7 @@ public class MapGenerator extends Generator{
}
@Override
public void init(Loadout loadout){
public void init(Schematic loadout){
this.loadout = loadout;
map = maps.loadInternalMap(mapName);
width = map.width;
@@ -146,7 +150,7 @@ public class MapGenerator extends Generator{
throw new IllegalArgumentException("All zone maps must have a core.");
}
loadout.setup(core.x, core.y);
schematics.placeLoadout(loadout, core.x, core.y);
world.prepareTiles(tiles);
world.setMap(map);

View File

@@ -5,6 +5,8 @@ import io.anuke.mindustry.content.Blocks;
import io.anuke.mindustry.maps.generators.BasicGenerator;
import io.anuke.mindustry.world.Tile;
import static io.anuke.mindustry.Vars.schematics;
public class DesertWastesGenerator extends BasicGenerator{
public DesertWastesGenerator(int width, int height){
@@ -42,6 +44,6 @@ public class DesertWastesGenerator extends BasicGenerator{
//scatter(tiles, Blocks.sandRocks, Blocks.creeptree, 1f);
tiles[endX][endY].setOverlay(Blocks.spawn);
loadout.setup(spawnX, spawnY);
schematics.placeLoadout(loadout, spawnX, spawnY);
}
}

View File

@@ -5,6 +5,8 @@ import io.anuke.mindustry.content.Blocks;
import io.anuke.mindustry.maps.generators.BasicGenerator;
import io.anuke.mindustry.world.Tile;
import static io.anuke.mindustry.Vars.schematics;
public class OvergrowthGenerator extends BasicGenerator{
public OvergrowthGenerator(int width, int height){
@@ -38,6 +40,6 @@ public class OvergrowthGenerator extends BasicGenerator{
//scatter(tiles, Blocks.sporePine, Blocks.whiteTreeDead, 1f);
tiles[endX][endY].setOverlay(Blocks.spawn);
loadout.setup(spawnX, spawnY);
schematics.placeLoadout(loadout, spawnX, spawnY);
}
}

View File

@@ -2,10 +2,11 @@ package io.anuke.mindustry.mod;
import io.anuke.arc.*;
import io.anuke.arc.audio.*;
import io.anuke.arc.audio.mock.*;
import io.anuke.arc.collection.Array;
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.util.ArcAnnotate.*;
import io.anuke.arc.util.*;
@@ -16,6 +17,7 @@ import io.anuke.arc.util.serialization.Json.*;
import io.anuke.mindustry.*;
import io.anuke.mindustry.content.*;
import io.anuke.mindustry.content.TechTree.*;
import io.anuke.mindustry.ctype.*;
import io.anuke.mindustry.entities.Effects.*;
import io.anuke.mindustry.entities.bullet.*;
import io.anuke.mindustry.entities.type.*;
@@ -37,7 +39,19 @@ public class ContentParser{
private ObjectMap<Class<?>, FieldParser> classParsers = new ObjectMap<Class<?>, FieldParser>(){{
put(Effect.class, (type, data) -> field(Fx.class, data));
put(StatusEffect.class, (type, data) -> field(StatusEffects.class, data));
put(Loadout.class, (type, data) -> field(Loadouts.class, data));
put(Schematic.class, (type, data) -> {
Object result = fieldOpt(Loadouts.class, data);
if(result != null){
return result;
}else{
String str = data.asString();
if(str.startsWith(Schematics.base64Header)){
return Schematics.readBase64(str);
}else{
return Schematics.read(Vars.tree.get("schematics/" + str + "." + Vars.schematicExtension));
}
}
});
put(Color.class, (type, data) -> Color.valueOf(data.asString()));
put(BulletType.class, (type, data) -> {
if(data.isString()){
@@ -51,9 +65,11 @@ public class ContentParser{
});
put(Sound.class, (type, data) -> {
if(fieldOpt(Sounds.class, data) != null) return fieldOpt(Sounds.class, data);
if(Vars.headless) return new MockSound();
String path = "sounds/" + data.asString() + (Vars.ios ? ".mp3" : ".ogg");
ProxySound sound = new ProxySound();
String name = "sounds/" + data.asString();
String path = Vars.tree.get(name + ".ogg").exists() && !Vars.ios ? name + ".ogg" : name + ".mp3";
ModLoadingSound sound = new ModLoadingSound();
Core.assets.load(path, Sound.class).loaded = result -> {
sound.sound = (Sound)result;
};
@@ -66,11 +82,18 @@ public class ContentParser{
readFields(obj, data);
return obj;
});
put(Weapon.class, (type, data) -> {
Weapon weapon = new Weapon();
readFields(weapon, data);
weapon.name = currentMod.name + "-" + weapon.name;
return weapon;
});
}};
/** Stores things that need to be parsed fully, e.g. reading fields of content.
* This is done to accomodate binding of content names first.*/
private Array<Runnable> reads = new Array<>();
private Array<Runnable> postreads = new Array<>();
private ObjectSet<Object> toBeParsed = new ObjectSet<>();
private LoadedMod currentMod;
private Content currentContent;
@@ -118,11 +141,11 @@ public class ContentParser{
block = Vars.content.getByName(ContentType.block, name);
if(value.has("type")){
throw new IllegalArgumentException("When overwriting an existing block, you must not re-declared its type. The original type will be used. Block: " + name);
throw new IllegalArgumentException("When overwriting an existing block, you must not re-declare its type. The original type will be used. Block: " + name);
}
}else{
//TODO generate dynamically instead of doing.. this
Class<? extends Block> type = resolve(value.getString("type"),
Class<? extends Block> type = resolve(getType(value),
"io.anuke.mindustry.world",
"io.anuke.mindustry.world.blocks",
"io.anuke.mindustry.world.blocks.defense",
@@ -199,7 +222,7 @@ public class ContentParser{
ContentType.unit, (TypeParser<UnitType>)(mod, name, value) -> {
readBundle(ContentType.unit, name, value);
Class<BaseUnit> type = resolve(value.getString("type"), "io.anuke.mindustry.entities.type.base");
Class<BaseUnit> type = resolve(getType(value), "io.anuke.mindustry.entities.type.base");
UnitType unit = new UnitType(mod + "-" + name, supply(type));
currentContent = unit;
read(() -> readFields(unit, value, true));
@@ -212,6 +235,18 @@ public class ContentParser{
ContentType.zone, parser(ContentType.zone, Zone::new)
);
private String getString(JsonValue value, String key){
if(value.has(key)){
return value.getString(key);
}else{
throw new IllegalArgumentException((currentContent == null ? "" : currentContent.sourceFile + ": ") + "You are missing a \"" + key + "\". It must be added before the file can be parsed.");
}
}
private String getType(JsonValue value){
return getString(value, "type");
}
private <T extends Content> T find(ContentType type, String name){
Content c = Vars.content.getByName(type, name);
if(c == null) c = Vars.content.getByName(type, currentMod.name + "-" + name);
@@ -219,7 +254,7 @@ public class ContentParser{
return (T)c;
}
private <T extends Content> TypeParser<T> parser(ContentType type, Function<String, T> constructor){
private <T extends Content> TypeParser<T> parser(ContentType type, Func<String, T> constructor){
return (mod, name, value) -> {
T item;
if(Vars.content.getByName(type, name) != null){
@@ -291,6 +326,7 @@ public class ContentParser{
}
reads.clear();
postreads.clear();
toBeParsed.clear();
}
/**
@@ -306,6 +342,9 @@ public class ContentParser{
init();
}
//add comments starting with //, but ignore links
json = json.replace("http://", "http:~~").replace("https://", "https:~~").replaceAll("//.*?\n","\n").replace("http:~~", "http://").replace("https:~~", "https://");
JsonValue value = parser.fromJson(null, json);
if(!parsers.containsKey(type)){
throw new SerializationException("No parsers for content type '" + type + "'");
@@ -314,6 +353,7 @@ public class ContentParser{
currentMod = mod;
boolean exists = Vars.content.getByName(type, name) != null;
Content c = parsers.get(type).parse(mod.name, name, value);
toBeParsed.add(c);
if(!exists){
c.sourceFile = file;
c.mod = mod;
@@ -341,7 +381,7 @@ public class ContentParser{
}
}
private <T> Supplier<T> supply(Class<T> type){
private <T> Prov<T> supply(Class<T> type){
try{
java.lang.reflect.Constructor<T> cons = type.getDeclaredConstructor();
return () -> {
@@ -380,7 +420,7 @@ public class ContentParser{
}
private void checkNullFields(Object object){
if(object instanceof Number || object instanceof String) return;
if(object instanceof Number || object instanceof String || toBeParsed.contains(object)) return;
parser.getFields(object.getClass()).values().toArray().each(field -> {
try{
@@ -401,6 +441,7 @@ public class ContentParser{
}
private void readFields(Object object, JsonValue jsonMap){
toBeParsed.remove(object);
Class type = object.getClass();
ObjectMap<String, FieldMetadata> fields = parser.getFields(type);
for(JsonValue child = jsonMap.child; child != null; child = child.next){

Some files were not shown because too many files have changed in this diff Show More