Merge branch 'master' into schematic
This commit is contained in:
@@ -63,6 +63,7 @@ public class BaseAI{
|
||||
int range = 150;
|
||||
|
||||
Position pos = randomPosition();
|
||||
|
||||
//when there are no random positions, do nothing.
|
||||
if(pos == null) return;
|
||||
|
||||
@@ -159,7 +160,10 @@ public class BaseAI{
|
||||
|
||||
private void tryWalls(){
|
||||
Block wall = Blocks.copperWall;
|
||||
Tile spawn = state.rules.defaultTeam.core() != null ? state.rules.defaultTeam.core().tile : data.team.core().tile;
|
||||
Building spawnt = state.rules.defaultTeam.core() != null ? state.rules.defaultTeam.core() : data.team.core();
|
||||
Tile spawn = spawnt == null ? null : spawnt.tile;
|
||||
|
||||
if(spawn == null) return;
|
||||
|
||||
for(int wx = lastX; wx <= lastX + lastW; wx++){
|
||||
for(int wy = lastY; wy <= lastY + lastH; wy++){
|
||||
|
||||
@@ -185,7 +185,7 @@ public class BlockIndexer{
|
||||
|
||||
if(other == null) continue;
|
||||
|
||||
if(other.team() == team && pred.get(other) && intSet.add(other.pos())){
|
||||
if(other.team == team && pred.get(other) && intSet.add(other.pos())){
|
||||
cons.get(other);
|
||||
any = true;
|
||||
}
|
||||
@@ -212,11 +212,11 @@ public class BlockIndexer{
|
||||
}
|
||||
|
||||
public void notifyTileDamaged(Building entity){
|
||||
if(damagedTiles[entity.team().id] == null){
|
||||
damagedTiles[entity.team().id] = new BuildingArray();
|
||||
if(damagedTiles[entity.team.id] == null){
|
||||
damagedTiles[entity.team.id] = new BuildingArray();
|
||||
}
|
||||
|
||||
damagedTiles[entity.team().id].add(entity);
|
||||
damagedTiles[entity.team.id].add(entity);
|
||||
}
|
||||
|
||||
public Building findEnemyTile(Team team, float x, float y, float range, Boolf<Building> pred){
|
||||
@@ -251,7 +251,7 @@ public class BlockIndexer{
|
||||
|
||||
if(e == null) continue;
|
||||
|
||||
if(e.team() != team || !pred.get(e) || !e.block().targetable)
|
||||
if(e.team != team || !pred.get(e) || !e.block().targetable)
|
||||
continue;
|
||||
|
||||
float ndst = e.dst2(x, y);
|
||||
@@ -390,7 +390,7 @@ public class BlockIndexer{
|
||||
for(int y = quadrantY * quadrantSize; y < world.height() && y < (quadrantY + 1) * quadrantSize; y++){
|
||||
Building result = world.build(x, y);
|
||||
//when a targetable block is found, mark this quadrant as occupied and stop searching
|
||||
if(result != null && result.team() == team){
|
||||
if(result != null && result.team == team){
|
||||
bits.set(quadrantX, quadrantY);
|
||||
break outer;
|
||||
}
|
||||
|
||||
@@ -99,7 +99,7 @@ public class WaveSpawner{
|
||||
|
||||
private void eachFlyerSpawn(Floatc2 cons){
|
||||
for(Tile tile : spawns){
|
||||
float angle = Angles.angle(tile.x, tile.y, world.width() / 2, world.height() / 2);
|
||||
float angle = Angles.angle(world.width() / 2, world.height() / 2, tile.x, tile.y);
|
||||
|
||||
float trns = Math.max(world.width(), world.height()) * Mathf.sqrt2 * tilesize;
|
||||
float spawnX = Mathf.clamp(world.width() * tilesize / 2f + Angles.trnsx(angle, trns), -margin, world.width() * tilesize + margin);
|
||||
|
||||
@@ -6,6 +6,8 @@ import mindustry.entities.units.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.world.meta.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class FlyingAI extends AIController{
|
||||
|
||||
@Override
|
||||
@@ -18,7 +20,7 @@ public class FlyingAI extends AIController{
|
||||
unit.wobble();
|
||||
}
|
||||
|
||||
if(target != null && unit.hasWeapons()){
|
||||
if(target != null && unit.hasWeapons() && command() == UnitCommand.attack){
|
||||
if(unit.type().weapons.first().rotate){
|
||||
moveTo(target, unit.range() * 0.8f);
|
||||
unit.lookAt(target);
|
||||
@@ -26,6 +28,15 @@ public class FlyingAI extends AIController{
|
||||
attack(80f);
|
||||
}
|
||||
}
|
||||
|
||||
if(target == null && command() == UnitCommand.attack && state.rules.waves && unit.team == state.rules.defaultTeam){
|
||||
moveTo(getClosestSpawner(), state.rules.dropZoneRadius + 120f);
|
||||
}
|
||||
|
||||
if(command() == UnitCommand.rally){
|
||||
target = targetFlag(unit.x, unit.y, BlockFlag.rally, false);
|
||||
moveTo(target, 60f);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,31 +1,55 @@
|
||||
package mindustry.ai.types;
|
||||
|
||||
import arc.math.*;
|
||||
import mindustry.ai.Pathfinder.*;
|
||||
import mindustry.entities.*;
|
||||
import mindustry.entities.units.*;
|
||||
import mindustry.game.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.meta.*;
|
||||
|
||||
import static mindustry.Vars.pathfinder;
|
||||
import java.util.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class GroundAI extends AIController{
|
||||
//static final float commandCooldown = 60f * 10;
|
||||
//float commandTimer = 60*3;
|
||||
|
||||
@Override
|
||||
public void updateMovement(){
|
||||
|
||||
Building core = unit.closestEnemyCore();
|
||||
|
||||
if(core != null){
|
||||
if(unit.within(core,unit.range() / 1.1f)){
|
||||
target = core;
|
||||
if(core != null && unit.within(core, unit.range() / 1.1f + core.block.size * tilesize / 2f)){
|
||||
target = core;
|
||||
Arrays.fill(targets, core);
|
||||
}
|
||||
|
||||
if((core == null || !unit.within(core, unit.range() * 0.5f)) && command() == UnitCommand.attack){
|
||||
boolean move = true;
|
||||
|
||||
if(state.rules.waves && unit.team == state.rules.defaultTeam){
|
||||
Tile spawner = getClosestSpawner();
|
||||
if(spawner != null && unit.within(spawner, state.rules.dropZoneRadius + 120f)) move = false;
|
||||
}
|
||||
|
||||
if(!unit.within(core, unit.range() * 0.5f)){
|
||||
moveToCore(FlagTarget.enemyCores);
|
||||
if(move) moveToCore(FlagTarget.enemyCores);
|
||||
}
|
||||
|
||||
if(command() == UnitCommand.rally){
|
||||
Teamc target = targetFlag(unit.x, unit.y, BlockFlag.rally, false);
|
||||
|
||||
if(target != null && !unit.within(target, 70f)){
|
||||
moveToCore(FlagTarget.rallyPoints);
|
||||
}
|
||||
}
|
||||
|
||||
if(unit.type().canBoost){
|
||||
unit.elevation = Mathf.approachDelta(unit.elevation, 0f, 0.08f);
|
||||
}
|
||||
|
||||
if(!Units.invalidateTarget(target, unit, unit.range())){
|
||||
if(unit.type().hasWeapons()){
|
||||
unit.aimLook(Predict.intercept(unit, target, unit.type().weapons.first().bullet.speed));
|
||||
@@ -33,12 +57,27 @@ public class GroundAI extends AIController{
|
||||
}else if(unit.moving()){
|
||||
unit.lookAt(unit.vel().angle());
|
||||
}
|
||||
|
||||
//auto-command works but it's very buggy
|
||||
/*
|
||||
if(unit instanceof Commanderc){
|
||||
Commanderc c = (Commanderc)unit;
|
||||
//try to command when missing members
|
||||
if(c.controlling().size <= unit.type().commandLimit/2){
|
||||
commandTimer -= Time.delta;
|
||||
|
||||
if(commandTimer <= 0){
|
||||
c.commandNearby(new SquareFormation(), u -> !(u.controller() instanceof FormationAI) && !(u instanceof Commanderc));
|
||||
commandTimer = commandCooldown;
|
||||
}
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
protected void moveToCore(FlagTarget path){
|
||||
Tile tile = unit.tileOn();
|
||||
if(tile == null) return;
|
||||
Tile targetTile = pathfinder.getTargetTile(tile, unit.team(), path);
|
||||
Tile targetTile = pathfinder.getTargetTile(tile, unit.team, path);
|
||||
|
||||
if(tile == targetTile) return;
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ public class SuicideAI extends GroundAI{
|
||||
boolean blocked = Vars.world.raycast(unit.tileX(), unit.tileY(), target.tileX(), target.tileY(), (x, y) -> {
|
||||
Tile tile = Vars.world.tile(x, y);
|
||||
if(tile != null && tile.build == target) return false;
|
||||
if(tile != null && tile.build != null && tile.build.team() != unit.team()){
|
||||
if(tile != null && tile.build != null && tile.build.team != unit.team()){
|
||||
blockedByBlock = true;
|
||||
return true;
|
||||
}else{
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package mindustry.async;
|
||||
|
||||
import arc.*;
|
||||
import arc.box2d.*;
|
||||
import arc.box2d.BodyDef.*;
|
||||
import arc.math.geom.*;
|
||||
@@ -98,7 +97,7 @@ public class PhysicsProcess implements AsyncProcess{
|
||||
ref.lastVelocity.set(ref.velocity);
|
||||
}
|
||||
|
||||
physics.step(Core.graphics.getDeltaTime(), 5, 8);
|
||||
physics.step(1f/45f, 5, 8);
|
||||
|
||||
//get delta vectors
|
||||
for(PhysicRef ref : refs){
|
||||
|
||||
@@ -19,6 +19,7 @@ public class MusicControl{
|
||||
public Seq<Music> ambientMusic = Seq.with();
|
||||
/** darker music, used in times of conflict */
|
||||
public Seq<Music> darkMusic = Seq.with();
|
||||
|
||||
protected Music lastRandomPlayed;
|
||||
protected Interval timer = new Interval();
|
||||
protected @Nullable Music current;
|
||||
|
||||
@@ -19,7 +19,6 @@ import mindustry.world.blocks.experimental.*;
|
||||
import mindustry.world.blocks.legacy.*;
|
||||
import mindustry.world.blocks.liquid.*;
|
||||
import mindustry.world.blocks.logic.*;
|
||||
import mindustry.world.blocks.logic.MessageBlock;
|
||||
import mindustry.world.blocks.power.*;
|
||||
import mindustry.world.blocks.production.*;
|
||||
import mindustry.world.blocks.sandbox.*;
|
||||
@@ -78,12 +77,13 @@ public class Blocks implements ContentList{
|
||||
duo, scatter, scorch, hail, arc, wave, lancer, swarmer, salvo, fuse, ripple, cyclone, spectre, meltdown, segment, parallax,
|
||||
|
||||
//units
|
||||
commandCenter,
|
||||
groundFactory, airFactory, navalFactory,
|
||||
additiveReconstructor, multiplicativeReconstructor, exponentialReconstructor, tetrativeReconstructor,
|
||||
repairPoint, resupplyPoint,
|
||||
|
||||
//logic
|
||||
message, switchBlock, microProcessor, logicProcessor, logicDisplay, memoryCell,
|
||||
message, switchBlock, microProcessor, logicProcessor, hyperProcessor, logicDisplay, memoryCell,
|
||||
|
||||
//campaign
|
||||
launchPad, launchPadLarge,
|
||||
@@ -270,7 +270,7 @@ public class Blocks implements ContentList{
|
||||
}};
|
||||
|
||||
grass = new Floor("grass"){{
|
||||
|
||||
attributes.set(Attribute.water, 0.1f);
|
||||
}};
|
||||
|
||||
salt = new Floor("salt"){{
|
||||
@@ -908,6 +908,7 @@ public class Blocks implements ContentList{
|
||||
health = 45;
|
||||
speed = 0.03f;
|
||||
displayedSpeed = 4.2f;
|
||||
buildCostMultiplier = 2f;
|
||||
}};
|
||||
|
||||
titaniumConveyor = new Conveyor("titanium-conveyor"){{
|
||||
@@ -995,12 +996,14 @@ public class Blocks implements ContentList{
|
||||
consumes.power(1.75f);
|
||||
}};
|
||||
|
||||
payloadConveyor = new PayloadConveyor("mass-conveyor"){{
|
||||
requirements(Category.distribution, with(Items.copper, 1));
|
||||
payloadConveyor = new PayloadConveyor("payload-conveyor"){{
|
||||
requirements(Category.distribution, with(Items.graphite, 10, Items.copper, 20));
|
||||
canOverdrive = false;
|
||||
}};
|
||||
|
||||
payloadRouter = new PayloadRouter("payload-router"){{
|
||||
requirements(Category.distribution, with(Items.copper, 1));
|
||||
requirements(Category.distribution, with(Items.graphite, 15, Items.copper, 20));
|
||||
canOverdrive = false;
|
||||
}};
|
||||
|
||||
//endregion
|
||||
@@ -1338,7 +1341,7 @@ public class Blocks implements ContentList{
|
||||
|
||||
unloader = new Unloader("unloader"){{
|
||||
requirements(Category.effect, with(Items.titanium, 25, Items.silicon, 30));
|
||||
speed = 7f;
|
||||
speed = 6f;
|
||||
}};
|
||||
|
||||
//endregion
|
||||
@@ -1485,7 +1488,7 @@ public class Blocks implements ContentList{
|
||||
reloadTime = 35f;
|
||||
shootCone = 40f;
|
||||
rotatespeed = 8f;
|
||||
powerUse = 5f;
|
||||
powerUse = 4f;
|
||||
targetAir = false;
|
||||
range = 90f;
|
||||
shootEffect = Fx.lightningShoot;
|
||||
@@ -1501,8 +1504,8 @@ public class Blocks implements ContentList{
|
||||
|
||||
hasPower = true;
|
||||
size = 2;
|
||||
force = 2.5f;
|
||||
scaledForce = 5f;
|
||||
force = 3f;
|
||||
scaledForce = 5.5f;
|
||||
range = 170f;
|
||||
damage = 0.08f;
|
||||
health = 160 * size * size;
|
||||
@@ -1555,14 +1558,14 @@ public class Blocks implements ContentList{
|
||||
}};
|
||||
|
||||
segment = new PointDefenseTurret("segment"){{
|
||||
requirements(Category.turret, with(Items.silicon, 130, Items.thorium, 80, Items.phasefabric, 50));
|
||||
requirements(Category.turret, with(Items.silicon, 130, Items.thorium, 80, Items.phasefabric, 25));
|
||||
|
||||
hasPower = true;
|
||||
consumes.power(3f);
|
||||
size = 2;
|
||||
shootLength = 5f;
|
||||
bulletDamage = 12f;
|
||||
reloadTime = 25f;
|
||||
reloadTime = 20f;
|
||||
health = 190 * size * size;
|
||||
}};
|
||||
|
||||
@@ -1698,11 +1701,17 @@ public class Blocks implements ContentList{
|
||||
//endregion
|
||||
//region units
|
||||
|
||||
commandCenter = new CommandCenter("command-center"){{
|
||||
requirements(Category.units, ItemStack.with(Items.copper, 200, Items.lead, 250, Items.silicon, 250, Items.graphite, 100));
|
||||
size = 2;
|
||||
health = size * size * 55;
|
||||
}};
|
||||
|
||||
groundFactory = new UnitFactory("ground-factory"){{
|
||||
requirements(Category.units, with(Items.copper, 50, Items.lead, 120, Items.silicon, 80));
|
||||
plans = new UnitPlan[]{
|
||||
new UnitPlan(UnitTypes.dagger, 60f * 20, with(Items.silicon, 10, Items.lead, 10)),
|
||||
new UnitPlan(UnitTypes.crawler, 60f * 15, with(Items.silicon, 10, Items.coal, 20)),
|
||||
new UnitPlan(UnitTypes.dagger, 60f * 15, with(Items.silicon, 10, Items.lead, 10)),
|
||||
new UnitPlan(UnitTypes.crawler, 60f * 12, with(Items.silicon, 10, Items.coal, 20)),
|
||||
new UnitPlan(UnitTypes.nova, 60f * 40, with(Items.silicon, 30, Items.lead, 20, Items.titanium, 20)),
|
||||
};
|
||||
size = 3;
|
||||
@@ -1710,7 +1719,7 @@ public class Blocks implements ContentList{
|
||||
}};
|
||||
|
||||
airFactory = new UnitFactory("air-factory"){{
|
||||
requirements(Category.units, with(Items.copper, 30, Items.lead, 70));
|
||||
requirements(Category.units, with(Items.copper, 60, Items.lead, 70));
|
||||
plans = new UnitPlan[]{
|
||||
new UnitPlan(UnitTypes.flare, 60f * 15, with(Items.silicon, 15)),
|
||||
new UnitPlan(UnitTypes.mono, 60f * 35, with(Items.silicon, 30, Items.lead, 15)),
|
||||
@@ -1720,7 +1729,7 @@ public class Blocks implements ContentList{
|
||||
}};
|
||||
|
||||
navalFactory = new UnitFactory("naval-factory"){{
|
||||
requirements(Category.units, with(Items.copper, 30, Items.lead, 70));
|
||||
requirements(Category.units, with(Items.copper, 150, Items.lead, 130, Items.metaglass, 120));
|
||||
plans = new UnitPlan[]{
|
||||
new UnitPlan(UnitTypes.risso, 60f * 30f, with(Items.silicon, 20, Items.metaglass, 25)),
|
||||
};
|
||||
@@ -1768,11 +1777,11 @@ public class Blocks implements ContentList{
|
||||
}};
|
||||
|
||||
exponentialReconstructor = new Reconstructor("exponential-reconstructor"){{
|
||||
requirements(Category.units, with(Items.lead, 2000, Items.silicon, 750, Items.titanium, 950, Items.thorium, 450, Items.plastanium, 350, Items.phasefabric, 250));
|
||||
requirements(Category.units, with(Items.lead, 2000, Items.silicon, 750, Items.titanium, 950, Items.thorium, 450, Items.plastanium, 350, Items.phasefabric, 450));
|
||||
|
||||
size = 7;
|
||||
consumes.power(12f);
|
||||
consumes.items(with(Items.silicon, 250, Items.titanium, 500, Items.plastanium, 400));
|
||||
consumes.power(13f);
|
||||
consumes.items(with(Items.silicon, 450, Items.titanium, 550, Items.plastanium, 550));
|
||||
consumes.liquid(Liquids.cryofluid, 1f);
|
||||
|
||||
constructTime = 60f * 60f * 1.5f;
|
||||
@@ -1780,15 +1789,16 @@ public class Blocks implements ContentList{
|
||||
|
||||
upgrades = new UnitType[][]{
|
||||
{UnitTypes.zenith, UnitTypes.antumbra},
|
||||
{UnitTypes.spiroct, UnitTypes.arkyid},
|
||||
};
|
||||
}};
|
||||
|
||||
tetrativeReconstructor = new Reconstructor("tetrative-reconstructor"){{
|
||||
requirements(Category.units, with(Items.lead, 4000, Items.silicon, 1500, Items.thorium, 500, Items.plastanium, 50, Items.phasefabric, 600, Items.surgealloy, 500));
|
||||
requirements(Category.units, with(Items.lead, 4000, Items.silicon, 1500, Items.thorium, 500, Items.plastanium, 450, Items.phasefabric, 600, Items.surgealloy, 500));
|
||||
|
||||
size = 9;
|
||||
consumes.power(25f);
|
||||
consumes.items(with(Items.silicon, 350, Items.plastanium, 450, Items.surgealloy, 400, Items.phasefabric, 150));
|
||||
consumes.items(with(Items.silicon, 350, Items.plastanium, 550, Items.surgealloy, 350, Items.phasefabric, 150));
|
||||
consumes.liquid(Liquids.cryofluid, 3f);
|
||||
|
||||
constructTime = 60f * 60f * 4;
|
||||
@@ -1861,7 +1871,6 @@ public class Blocks implements ContentList{
|
||||
//looked up by name, no ref needed
|
||||
new LegacyMechPad("legacy-mech-pad");
|
||||
new LegacyUnitFactory("legacy-unit-factory");
|
||||
new LegacyCommandCenter("legacy-command-center");
|
||||
|
||||
//endregion
|
||||
//region campaign
|
||||
@@ -1897,7 +1906,7 @@ public class Blocks implements ContentList{
|
||||
}};
|
||||
|
||||
microProcessor = new LogicBlock("micro-processor"){{
|
||||
requirements(Category.logic, with(Items.copper, 80, Items.lead, 50, Items.silicon, 60));
|
||||
requirements(Category.logic, with(Items.copper, 80, Items.lead, 50, Items.silicon, 50));
|
||||
|
||||
instructionsPerTick = 2;
|
||||
|
||||
@@ -1905,15 +1914,28 @@ public class Blocks implements ContentList{
|
||||
}};
|
||||
|
||||
logicProcessor = new LogicBlock("logic-processor"){{
|
||||
requirements(Category.logic, with(Items.lead, 320, Items.silicon, 140, Items.graphite, 80, Items.thorium, 70));
|
||||
requirements(Category.logic, with(Items.lead, 320, Items.silicon, 100, Items.graphite, 60, Items.thorium, 50));
|
||||
|
||||
instructionsPerTick = 5;
|
||||
|
||||
range = 16 * 10;
|
||||
range = 8 * 20;
|
||||
|
||||
size = 2;
|
||||
}};
|
||||
|
||||
hyperProcessor = new LogicBlock("hyper-processor"){{
|
||||
requirements(Category.logic, with(Items.lead, 450, Items.silicon, 150, Items.thorium, 75, Items.surgealloy, 50));
|
||||
|
||||
consumes.liquid(Liquids.cryofluid, 0.08f);
|
||||
hasLiquids = true;
|
||||
|
||||
instructionsPerTick = 25;
|
||||
|
||||
range = 8 * 40;
|
||||
|
||||
size = 3;
|
||||
}};
|
||||
|
||||
logicDisplay = new LogicDisplay("logic-display"){{
|
||||
requirements(Category.logic, with(Items.copper, 200, Items.lead, 120, Items.silicon, 100, Items.metaglass, 50));
|
||||
|
||||
|
||||
@@ -163,7 +163,7 @@ public class Bullets implements ContentList{
|
||||
width = 6f;
|
||||
height = 8f;
|
||||
hitEffect = Fx.flakExplosion;
|
||||
splashDamage = 20f;
|
||||
splashDamage = 22f;
|
||||
splashDamageRadius = 20f;
|
||||
fragBullet = flakGlassFrag;
|
||||
fragBullets = 5;
|
||||
@@ -240,7 +240,7 @@ public class Bullets implements ContentList{
|
||||
explodeRange = 20f;
|
||||
}};
|
||||
|
||||
missileExplosive = new MissileBulletType(3f, 10){{
|
||||
missileExplosive = new MissileBulletType(3.7f, 10){{
|
||||
width = 8f;
|
||||
height = 8f;
|
||||
shrinkY = 0f;
|
||||
@@ -255,7 +255,7 @@ public class Bullets implements ContentList{
|
||||
statusDuration = 60f;
|
||||
}};
|
||||
|
||||
missileIncendiary = new MissileBulletType(3f, 12){{
|
||||
missileIncendiary = new MissileBulletType(3.7f, 12){{
|
||||
frontColor = Pal.lightishOrange;
|
||||
backColor = Pal.lightOrange;
|
||||
width = 7f;
|
||||
@@ -269,7 +269,7 @@ public class Bullets implements ContentList{
|
||||
status = StatusEffects.burning;
|
||||
}};
|
||||
|
||||
missileSurge = new MissileBulletType(3f, 20){{
|
||||
missileSurge = new MissileBulletType(3.7f, 20){{
|
||||
width = 8f;
|
||||
height = 8f;
|
||||
shrinkY = 0f;
|
||||
@@ -279,7 +279,7 @@ public class Bullets implements ContentList{
|
||||
hitEffect = Fx.blastExplosion;
|
||||
despawnEffect = Fx.blastExplosion;
|
||||
lightning = 2;
|
||||
lightningLength = 14;
|
||||
lightningLength = 10;
|
||||
}};
|
||||
|
||||
standardCopper = new BasicBulletType(2.5f, 9){{
|
||||
|
||||
@@ -33,8 +33,10 @@ public class Fx{
|
||||
float scl = 1f + e.fout() * 2f;
|
||||
|
||||
UnitType unit = e.data();
|
||||
rect(unit.region, e.x, e.y,
|
||||
unit.region.getWidth() * Draw.scl * scl, unit.region.getHeight() * Draw.scl * scl, 180f);
|
||||
TextureRegion region = unit.icon(Cicon.full);
|
||||
|
||||
rect(region, e.x, e.y,
|
||||
region.getWidth() * Draw.scl * scl, region.getHeight() * Draw.scl * scl, 180f);
|
||||
|
||||
}),
|
||||
|
||||
@@ -512,6 +514,29 @@ public class Fx{
|
||||
|
||||
}),
|
||||
|
||||
sapExplosion = new Effect(25, e -> {
|
||||
|
||||
color(Pal.sapBullet);
|
||||
e.scaled(6, i -> {
|
||||
stroke(3f * i.fout());
|
||||
Lines.circle(e.x, e.y, 3f + i.fin() * 80f);
|
||||
});
|
||||
|
||||
color(Color.gray);
|
||||
|
||||
randLenVectors(e.id, 9, 2f + 70 * e.finpow(), (x, y) -> {
|
||||
Fill.circle(e.x + x, e.y + y, e.fout() * 4f + 0.5f);
|
||||
});
|
||||
|
||||
color(Pal.sapBulletBack);
|
||||
stroke(1f * e.fout());
|
||||
|
||||
randLenVectors(e.id + 1, 8, 1f + 60f * e.finpow(), (x, y) -> {
|
||||
lineAngle(e.x + x, e.y + y, Mathf.angle(x, y), 1f + e.fout() * 3f);
|
||||
});
|
||||
|
||||
}),
|
||||
|
||||
massiveExplosion = new Effect(30, e -> {
|
||||
|
||||
color(Pal.missileYellow);
|
||||
|
||||
@@ -17,7 +17,7 @@ import static mindustry.content.UnitTypes.*;
|
||||
import static mindustry.type.ItemStack.*;
|
||||
|
||||
public class TechTree implements ContentList{
|
||||
private static ObjectMap<UnlockableContent, TechNode> map = new ObjectMap<>();
|
||||
static ObjectMap<UnlockableContent, TechNode> map = new ObjectMap<>();
|
||||
|
||||
public static Seq<TechNode> all;
|
||||
public static TechNode root;
|
||||
@@ -38,7 +38,6 @@ public class TechTree implements ContentList{
|
||||
node(distributor);
|
||||
node(sorter, () -> {
|
||||
node(invertedSorter);
|
||||
node(message);
|
||||
node(overflowGate, () -> {
|
||||
node(underflowGate);
|
||||
});
|
||||
@@ -204,6 +203,26 @@ public class TechTree implements ContentList{
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
node(microProcessor, () -> {
|
||||
node(switchBlock, () -> {
|
||||
node(message, () -> {
|
||||
node(logicDisplay, () -> {
|
||||
|
||||
});
|
||||
|
||||
node(memoryCell, () -> {
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
node(logicProcessor, () -> {
|
||||
node(hyperProcessor, () -> {
|
||||
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -340,6 +359,10 @@ public class TechTree implements ContentList{
|
||||
});
|
||||
|
||||
node(groundFactory, () -> {
|
||||
node(commandCenter, () -> {
|
||||
|
||||
});
|
||||
|
||||
node(dagger, () -> {
|
||||
node(mace, () -> {
|
||||
node(fortress, () -> {
|
||||
@@ -406,8 +429,6 @@ public class TechTree implements ContentList{
|
||||
});
|
||||
});
|
||||
|
||||
//TODO research sectors
|
||||
|
||||
node(groundZero, () -> {
|
||||
node(frozenForest, Seq.with(
|
||||
new SectorComplete(groundZero),
|
||||
@@ -544,7 +565,7 @@ public class TechTree implements ContentList{
|
||||
}
|
||||
|
||||
public static class TechNode{
|
||||
private static TechNode context;
|
||||
static TechNode context;
|
||||
|
||||
/** Depth in tech tree. */
|
||||
public int depth;
|
||||
@@ -556,25 +577,20 @@ public class TechTree implements ContentList{
|
||||
public ItemStack[] requirements;
|
||||
/** Requirements that have been fulfilled. Always the same length as the requirement array. */
|
||||
public final ItemStack[] finishedRequirements;
|
||||
/** Extra objectives needed to research this. TODO implement */
|
||||
/** Extra objectives needed to research this. */
|
||||
public Seq<Objective> objectives = new Seq<>();
|
||||
/** Time required to research this content, in seconds. */
|
||||
public float time;
|
||||
/** Nodes that depend on this node. */
|
||||
public final Seq<TechNode> children = new Seq<>();
|
||||
/** Research progress, in seconds. */
|
||||
public float progress;
|
||||
|
||||
TechNode(@Nullable TechNode ccontext, UnlockableContent content, ItemStack[] requirements, Runnable children){
|
||||
if(ccontext != null){
|
||||
ccontext.children.add(this);
|
||||
}
|
||||
if(ccontext != null) ccontext.children.add(this);
|
||||
|
||||
this.parent = ccontext;
|
||||
this.content = content;
|
||||
this.requirements = requirements;
|
||||
this.depth = parent == null ? 0 : parent.depth + 1;
|
||||
this.progress = Core.settings == null ? 0 : Core.settings.getFloat("research-" + content.name, 0f);
|
||||
this.time = Seq.with(requirements).mapFloat(i -> i.item.cost * i.amount).sum() * 10;
|
||||
this.finishedRequirements = new ItemStack[requirements.length];
|
||||
|
||||
@@ -599,7 +615,6 @@ public class TechTree implements ContentList{
|
||||
|
||||
/** Flushes research progress to settings. */
|
||||
public void save(){
|
||||
Core.settings.put("research-" + content.name, progress);
|
||||
|
||||
//save finished requirements by item type
|
||||
for(ItemStack stack : finishedRequirements){
|
||||
|
||||
@@ -27,7 +27,7 @@ public class UnitTypes implements ContentList{
|
||||
public static @EntityDef({Unitc.class, Legsc.class}) UnitType atrax;
|
||||
|
||||
//legs + building
|
||||
public static @EntityDef({Unitc.class, Legsc.class, Builderc.class}) UnitType spiroct, arkyid;
|
||||
public static @EntityDef({Unitc.class, Legsc.class, Builderc.class}) UnitType spiroct, arkyid, toxopid;
|
||||
|
||||
//air (no special traits)
|
||||
public static @EntityDef({Unitc.class}) UnitType flare, eclipse, horizon, zenith, antumbra;
|
||||
@@ -72,7 +72,6 @@ public class UnitTypes implements ContentList{
|
||||
mace = new UnitType("mace"){{
|
||||
speed = 0.4f;
|
||||
hitsize = 9f;
|
||||
range = 10f;
|
||||
health = 500;
|
||||
armor = 4f;
|
||||
|
||||
@@ -84,12 +83,11 @@ public class UnitTypes implements ContentList{
|
||||
reload = 14f;
|
||||
recoil = 1f;
|
||||
ejectEffect = Fx.none;
|
||||
bullet = new BulletType(3f, 30f){{
|
||||
bullet = new BulletType(3.9f, 30f){{
|
||||
ammoMultiplier = 3f;
|
||||
hitSize = 7f;
|
||||
lifetime = 42f;
|
||||
lifetime = 12f;
|
||||
pierce = true;
|
||||
drag = 0.05f;
|
||||
statusDuration = 60f * 4;
|
||||
shootEffect = Fx.shootSmallFlame;
|
||||
hitEffect = Fx.hitFlameSmall;
|
||||
@@ -165,7 +163,7 @@ public class UnitTypes implements ContentList{
|
||||
boostMultiplier = 1.5f;
|
||||
speed = 0.48f;
|
||||
hitsize = 10f;
|
||||
health = 300f;
|
||||
health = 320f;
|
||||
buildSpeed = 0.9f;
|
||||
armor = 4f;
|
||||
|
||||
@@ -173,7 +171,7 @@ public class UnitTypes implements ContentList{
|
||||
mineSpeed = 5f;
|
||||
commandLimit = 8;
|
||||
|
||||
abilities.add(new ShieldFieldAbility(15f, 30f, 60f * 5, 60f));
|
||||
abilities.add(new ShieldFieldAbility(20f, 40f, 60f * 5, 60f));
|
||||
|
||||
weapons.add(new Weapon("heal-shotgun-weapon"){{
|
||||
x = 5f;
|
||||
@@ -193,7 +191,7 @@ public class UnitTypes implements ContentList{
|
||||
|
||||
bullet = new LightningBulletType(){{
|
||||
lightningColor = hitColor = Pal.heal;
|
||||
damage = 12f;
|
||||
damage = 15f;
|
||||
lightningLength = 7;
|
||||
lightningLengthRand = 7;
|
||||
shootEffect = Fx.shootHeal;
|
||||
@@ -206,7 +204,7 @@ public class UnitTypes implements ContentList{
|
||||
hitsize = 12f;
|
||||
boostMultiplier = 2f;
|
||||
itemCapacity = 80;
|
||||
health = 640f;
|
||||
health = 650f;
|
||||
buildSpeed = 1.7f;
|
||||
canBoost = true;
|
||||
armor = 9f;
|
||||
@@ -219,7 +217,7 @@ public class UnitTypes implements ContentList{
|
||||
mineSpeed = 7f;
|
||||
drawShields = false;
|
||||
|
||||
abilities.add(new ForceFieldAbility(60f, 0.12f, 200f, 60f * 8));
|
||||
abilities.add(new ForceFieldAbility(60f, 0.2f, 300f, 60f * 7));
|
||||
|
||||
weapons.add(new Weapon("beam-weapon"){{
|
||||
shake = 2f;
|
||||
@@ -230,7 +228,7 @@ public class UnitTypes implements ContentList{
|
||||
shootSound = Sounds.laser;
|
||||
|
||||
bullet = new LaserBulletType(){{
|
||||
damage = 27f;
|
||||
damage = 30f;
|
||||
recoil = 1f;
|
||||
sideAngle = 45f;
|
||||
sideWidth = 1f;
|
||||
@@ -316,7 +314,7 @@ public class UnitTypes implements ContentList{
|
||||
drag = 0.4f;
|
||||
hitsize = 12f;
|
||||
rotateSpeed = 3f;
|
||||
health = 600;
|
||||
health = 760;
|
||||
immunities = ObjectSet.with(StatusEffects.burning, StatusEffects.melting);
|
||||
legCount = 6;
|
||||
legLength = 13f;
|
||||
@@ -352,7 +350,7 @@ public class UnitTypes implements ContentList{
|
||||
despawnEffect = Fx.none;
|
||||
width = 0.54f;
|
||||
lifetime = 35f;
|
||||
knockback = -1.2f;
|
||||
knockback = -1.24f;
|
||||
}};
|
||||
}});
|
||||
|
||||
@@ -365,62 +363,109 @@ public class UnitTypes implements ContentList{
|
||||
bullet = new SapBulletType(){{
|
||||
sapStrength = 0.65f;
|
||||
length = 40f;
|
||||
damage = 12;
|
||||
damage = 13;
|
||||
shootEffect = Fx.shootSmall;
|
||||
hitColor = color = Color.valueOf("bf92f9");
|
||||
despawnEffect = Fx.none;
|
||||
width = 0.4f;
|
||||
lifetime = 25f;
|
||||
knockback = -0.6f;
|
||||
knockback = -0.65f;
|
||||
}};
|
||||
}});
|
||||
}};
|
||||
|
||||
//TODO implement
|
||||
arkyid = new UnitType("arkyid"){{
|
||||
drag = 0.1f;
|
||||
speed = 0.5f;
|
||||
hitsize = 9f;
|
||||
health = 140;
|
||||
hitsize = 21f;
|
||||
health = 8000;
|
||||
armor = 6f;
|
||||
|
||||
legCount = 6;
|
||||
legMoveSpace = 1f;
|
||||
legPairOffset = 3;
|
||||
legLength = 34f;
|
||||
rotateShooting = false;
|
||||
legLength = 30f;
|
||||
legExtension = -15;
|
||||
legBaseOffset = 10f;
|
||||
landShake = 2f;
|
||||
landShake = 1f;
|
||||
legSpeed = 0.1f;
|
||||
legLengthScl = 1f;
|
||||
rippleScale = 2f;
|
||||
legSpeed = 0.2f;
|
||||
|
||||
legSplashDamage = 32;
|
||||
legSplashRange = 30;
|
||||
|
||||
hovering = true;
|
||||
allowLegStep = true;
|
||||
visualElevation = 0.4f;
|
||||
groundLayer = Layer.legUnit;
|
||||
|
||||
BulletType sapper = new SapBulletType(){{
|
||||
sapStrength = 0.8f;
|
||||
length = 55f;
|
||||
damage = 34;
|
||||
shootEffect = Fx.shootSmall;
|
||||
hitColor = color = Color.valueOf("bf92f9");
|
||||
despawnEffect = Fx.none;
|
||||
width = 0.55f;
|
||||
lifetime = 30f;
|
||||
knockback = -1f;
|
||||
}};
|
||||
|
||||
weapons.add(
|
||||
new Weapon("missiles-mount"){{
|
||||
reload = 20f;
|
||||
new Weapon("spiroct-weapon"){{
|
||||
reload = 9f;
|
||||
x = 4f;
|
||||
y = 8f;
|
||||
rotate = true;
|
||||
shake = 1f;
|
||||
bullet = new MissileBulletType(2.7f, 12, "missile"){{
|
||||
width = 8f;
|
||||
height = 8f;
|
||||
shrinkY = 0f;
|
||||
drag = -0.003f;
|
||||
homingRange = 60f;
|
||||
keepVelocity = false;
|
||||
splashDamageRadius = 25f;
|
||||
splashDamage = 10f;
|
||||
lifetime = 120f;
|
||||
trailColor = Color.gray;
|
||||
backColor = Pal.bulletYellowBack;
|
||||
frontColor = Pal.bulletYellow;
|
||||
hitEffect = Fx.blastExplosion;
|
||||
despawnEffect = Fx.blastExplosion;
|
||||
weaveScale = 8f;
|
||||
weaveMag = 2f;
|
||||
bullet = sapper;
|
||||
}},
|
||||
new Weapon("spiroct-weapon"){{
|
||||
reload = 15f;
|
||||
x = 9f;
|
||||
y = 6f;
|
||||
rotate = true;
|
||||
bullet = sapper;
|
||||
}},
|
||||
new Weapon("spiroct-weapon"){{
|
||||
reload = 23f;
|
||||
x = 14f;
|
||||
y = 0f;
|
||||
rotate = true;
|
||||
bullet = sapper;
|
||||
}},
|
||||
new Weapon("large-purple-mount"){{
|
||||
y = -7f;
|
||||
x = 9f;
|
||||
shootY = 7f;
|
||||
reload = 45;
|
||||
shake = 3f;
|
||||
rotateSpeed = 2f;
|
||||
ejectEffect = Fx.shellEjectSmall;
|
||||
shootSound = Sounds.shootBig;
|
||||
rotate = true;
|
||||
occlusion = 8f;
|
||||
recoil = 3f;
|
||||
|
||||
bullet = new ArtilleryBulletType(2f, 12){{
|
||||
hitEffect = Fx.sapExplosion;
|
||||
knockback = 0.8f;
|
||||
lifetime = 70f;
|
||||
width = height = 19f;
|
||||
collidesTiles = false;
|
||||
ammoMultiplier = 4f;
|
||||
splashDamageRadius = 95f;
|
||||
splashDamage = 55f;
|
||||
backColor = Pal.sapBulletBack;
|
||||
frontColor = lightningColor = Pal.sapBullet;
|
||||
lightning = 3;
|
||||
lightningLength = 10;
|
||||
smokeEffect = Fx.shootBigSmoke2;
|
||||
shake = 5f;
|
||||
|
||||
status = StatusEffects.sapped;
|
||||
statusDuration = 60f * 10;
|
||||
}};
|
||||
}});
|
||||
}};
|
||||
@@ -448,7 +493,7 @@ public class UnitTypes implements ContentList{
|
||||
}};
|
||||
|
||||
horizon = new UnitType("horizon"){{
|
||||
health = 300;
|
||||
health = 350;
|
||||
speed = 2f;
|
||||
accel = 0.08f;
|
||||
drag = 0.016f;
|
||||
@@ -457,19 +502,19 @@ public class UnitTypes implements ContentList{
|
||||
engineOffset = 7.8f;
|
||||
range = 140f;
|
||||
faceTarget = false;
|
||||
armor = 2f;
|
||||
armor = 4f;
|
||||
|
||||
weapons.add(new Weapon(){{
|
||||
minShootVelocity = 0.75f;
|
||||
x = 3f;
|
||||
shootY = 0f;
|
||||
reload = 12f;
|
||||
reload = 11f;
|
||||
shootCone = 180f;
|
||||
ejectEffect = Fx.none;
|
||||
inaccuracy = 15f;
|
||||
ignoreRotation = true;
|
||||
shootSound = Sounds.none;
|
||||
bullet = new BombBulletType(23f, 25f){{
|
||||
bullet = new BombBulletType(28f, 25f){{
|
||||
width = 10f;
|
||||
height = 14f;
|
||||
hitEffect = Fx.flakExplosion;
|
||||
@@ -483,21 +528,21 @@ public class UnitTypes implements ContentList{
|
||||
}};
|
||||
|
||||
zenith = new UnitType("zenith"){{
|
||||
health = 1000;
|
||||
speed = 1.9f;
|
||||
health = 700;
|
||||
speed = 1.7f;
|
||||
accel = 0.04f;
|
||||
drag = 0.016f;
|
||||
flying = true;
|
||||
range = 140f;
|
||||
hitsize = 18f;
|
||||
lowAltitude = true;
|
||||
armor = 6f;
|
||||
armor = 5f;
|
||||
|
||||
engineOffset = 12f;
|
||||
engineSize = 3f;
|
||||
|
||||
weapons.add(new Weapon("zenith-missiles"){{
|
||||
reload = 32f;
|
||||
reload = 40f;
|
||||
x = 7f;
|
||||
rotate = true;
|
||||
shake = 1f;
|
||||
@@ -533,21 +578,21 @@ public class UnitTypes implements ContentList{
|
||||
rotateSpeed = 1.9f;
|
||||
flying = true;
|
||||
lowAltitude = true;
|
||||
health = 9000;
|
||||
health = 7000;
|
||||
armor = 9f;
|
||||
engineOffset = 21;
|
||||
engineSize = 5.3f;
|
||||
hitsize = 58f;
|
||||
hitsize = 56f;
|
||||
|
||||
BulletType missiles = new MissileBulletType(2.7f, 10){{
|
||||
width = 8f;
|
||||
height = 8f;
|
||||
shrinkY = 0f;
|
||||
drag = -0.01f;
|
||||
splashDamageRadius = 40f;
|
||||
splashDamage = 40f;
|
||||
splashDamageRadius = 20f;
|
||||
splashDamage = 30f;
|
||||
ammoMultiplier = 4f;
|
||||
lifetime = 80f;
|
||||
lifetime = 50f;
|
||||
hitEffect = Fx.blastExplosion;
|
||||
despawnEffect = Fx.blastExplosion;
|
||||
|
||||
@@ -581,17 +626,18 @@ public class UnitTypes implements ContentList{
|
||||
new Weapon("large-bullet-mount"){{
|
||||
y = 2f;
|
||||
x = 10f;
|
||||
shootY = 12f;
|
||||
reload = 10;
|
||||
shootY = 10f;
|
||||
reload = 12;
|
||||
shake = 1f;
|
||||
rotateSpeed = 2f;
|
||||
ejectEffect = Fx.shellEjectSmall;
|
||||
shootSound = Sounds.shootBig;
|
||||
rotate = true;
|
||||
occlusion = 8f;
|
||||
bullet = new BasicBulletType(7f, 60){{
|
||||
bullet = new BasicBulletType(7f, 50){{
|
||||
width = 12f;
|
||||
height = 18f;
|
||||
lifetime = 25f;
|
||||
shootEffect = Fx.shootBig;
|
||||
}};
|
||||
}}
|
||||
@@ -612,6 +658,18 @@ public class UnitTypes implements ContentList{
|
||||
destructibleWreck = false;
|
||||
armor = 13f;
|
||||
|
||||
BulletType fragBullet = new FlakBulletType(4f, 5){{
|
||||
shootEffect = Fx.shootBig;
|
||||
ammoMultiplier = 4f;
|
||||
splashDamage = 42f;
|
||||
splashDamageRadius = 25f;
|
||||
collidesGround = true;
|
||||
lifetime = 38f;
|
||||
|
||||
status = StatusEffects.blasted;
|
||||
statusDuration = 60f;
|
||||
}};
|
||||
|
||||
weapons.add(
|
||||
new Weapon("large-laser-mount"){{
|
||||
shake = 4f;
|
||||
@@ -619,14 +677,14 @@ public class UnitTypes implements ContentList{
|
||||
x = 18f;
|
||||
y = 5f;
|
||||
rotateSpeed = 2f;
|
||||
reload = 50f;
|
||||
reload = 45f;
|
||||
recoil = 4f;
|
||||
shootSound = Sounds.laser;
|
||||
occlusion = 20f;
|
||||
rotate = true;
|
||||
|
||||
bullet = new LaserBulletType(){{
|
||||
damage = 75f;
|
||||
damage = 90f;
|
||||
sideAngle = 20f;
|
||||
sideWidth = 1.5f;
|
||||
sideLength = 80f;
|
||||
@@ -636,50 +694,29 @@ public class UnitTypes implements ContentList{
|
||||
colors = new Color[]{Color.valueOf("ec7458aa"), Color.valueOf("ff9c5a"), Color.white};
|
||||
}};
|
||||
}},
|
||||
new Weapon("missiles-mount"){{
|
||||
new Weapon("large-artillery"){{
|
||||
x = 11f;
|
||||
y = 27f;
|
||||
rotateSpeed = 2f;
|
||||
reload = 4f;
|
||||
reload = 9f;
|
||||
shootSound = Sounds.flame;
|
||||
occlusion = 7f;
|
||||
rotate = true;
|
||||
recoil = 0.5f;
|
||||
|
||||
bullet = Bullets.pyraFlame;
|
||||
bullet = fragBullet;
|
||||
}},
|
||||
new Weapon("large-artillery"){{
|
||||
y = -13f;
|
||||
x = 20f;
|
||||
reload = 18f;
|
||||
reload = 12f;
|
||||
ejectEffect = Fx.shellEjectSmall;
|
||||
rotateSpeed = 7f;
|
||||
shake = 1f;
|
||||
shootSound = Sounds.shoot;
|
||||
rotate = true;
|
||||
occlusion = 12f;
|
||||
bullet = new ArtilleryBulletType(3.2f, 12){{
|
||||
trailMult = 0.8f;
|
||||
hitEffect = Fx.massiveExplosion;
|
||||
knockback = 1.5f;
|
||||
lifetime = 140f;
|
||||
height = 12f;
|
||||
width = 12f;
|
||||
collidesTiles = false;
|
||||
ammoMultiplier = 4f;
|
||||
splashDamageRadius = 60f;
|
||||
splashDamage = 60f;
|
||||
backColor = Pal.missileYellowBack;
|
||||
frontColor = Pal.missileYellow;
|
||||
trailEffect = Fx.artilleryTrail;
|
||||
trailSize = 6f;
|
||||
hitShake = 4f;
|
||||
|
||||
shootEffect = Fx.shootBig2;
|
||||
|
||||
status = StatusEffects.blasted;
|
||||
statusDuration = 60f;
|
||||
}};
|
||||
bullet = fragBullet;
|
||||
}});
|
||||
}};
|
||||
|
||||
@@ -1001,7 +1038,7 @@ public class UnitTypes implements ContentList{
|
||||
lifetime = 60f;
|
||||
shootEffect = Fx.shootSmall;
|
||||
smokeEffect = Fx.shootSmallSmoke;
|
||||
tileDamageMultiplier = 0.95f;
|
||||
tileDamageMultiplier = 0.09f;
|
||||
}};
|
||||
}});
|
||||
}};
|
||||
@@ -1092,6 +1129,7 @@ public class UnitTypes implements ContentList{
|
||||
hitsize = 0f;
|
||||
health = 1;
|
||||
rotateSpeed = 360f;
|
||||
itemCapacity = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -243,11 +243,11 @@ public class ContentLoader{
|
||||
}
|
||||
|
||||
public Block block(int id){
|
||||
return (Block)getByID(ContentType.block, id);
|
||||
return getByID(ContentType.block, id);
|
||||
}
|
||||
|
||||
public Block block(String name){
|
||||
return (Block)getByName(ContentType.block, name);
|
||||
return getByName(ContentType.block, name);
|
||||
}
|
||||
|
||||
public Seq<Item> items(){
|
||||
@@ -255,7 +255,7 @@ public class ContentLoader{
|
||||
}
|
||||
|
||||
public Item item(int id){
|
||||
return (Item)getByID(ContentType.item, id);
|
||||
return getByID(ContentType.item, id);
|
||||
}
|
||||
|
||||
public Seq<Liquid> liquids(){
|
||||
@@ -263,7 +263,7 @@ public class ContentLoader{
|
||||
}
|
||||
|
||||
public Liquid liquid(int id){
|
||||
return (Liquid)getByID(ContentType.liquid, id);
|
||||
return getByID(ContentType.liquid, id);
|
||||
}
|
||||
|
||||
public Seq<BulletType> bullets(){
|
||||
@@ -271,7 +271,7 @@ public class ContentLoader{
|
||||
}
|
||||
|
||||
public BulletType bullet(int id){
|
||||
return (BulletType)getByID(ContentType.bullet, id);
|
||||
return getByID(ContentType.bullet, id);
|
||||
}
|
||||
|
||||
public Seq<SectorPreset> sectors(){
|
||||
|
||||
@@ -155,12 +155,6 @@ public class Control implements ApplicationListener, Loadable{
|
||||
}
|
||||
});
|
||||
|
||||
Events.on(ZoneRequireCompleteEvent.class, e -> {
|
||||
if(e.objective.display() != null){
|
||||
ui.hudfrag.showToast(Core.bundle.format("zone.requirement.complete", e.zoneForMet.localizedName, e.objective.display()));
|
||||
}
|
||||
});
|
||||
|
||||
//delete save on campaign game over
|
||||
Events.on(GameOverEvent.class, e -> {
|
||||
if(state.isCampaign() && !net.client() && !headless){
|
||||
|
||||
@@ -302,6 +302,11 @@ public class Logic implements ApplicationListener{
|
||||
});
|
||||
}
|
||||
|
||||
@Remote(called = Loc.both)
|
||||
public static void updateGameOver(Team winner){
|
||||
state.gameOver = true;
|
||||
}
|
||||
|
||||
@Remote(called = Loc.both)
|
||||
public static void gameOver(Team winner){
|
||||
state.stats.wavesLasted = state.wave;
|
||||
@@ -320,6 +325,10 @@ public class Logic implements ApplicationListener{
|
||||
Events.fire(Trigger.update);
|
||||
universe.updateGlobal();
|
||||
|
||||
if(Core.settings.modified() && !state.isPlaying()){
|
||||
Core.settings.forceSave();
|
||||
}
|
||||
|
||||
if(state.isGame()){
|
||||
if(!net.client()){
|
||||
state.enemies = Groups.unit.count(u -> u.team() == state.rules.waveTeam && u.type().isCounted);
|
||||
|
||||
@@ -366,6 +366,9 @@ public class NetClient implements ApplicationListener{
|
||||
|
||||
@Remote
|
||||
public static void playerDisconnect(int playerid){
|
||||
if(netClient != null){
|
||||
netClient.addRemovedEntity(playerid);
|
||||
}
|
||||
Groups.player.removeByID(playerid);
|
||||
}
|
||||
|
||||
@@ -437,13 +440,14 @@ public class NetClient implements ApplicationListener{
|
||||
}
|
||||
|
||||
@Remote(variants = Variant.one, priority = PacketPriority.low, unreliable = true)
|
||||
public static void stateSnapshot(float waveTime, int wave, int enemies, boolean paused, short coreDataLen, byte[] coreData){
|
||||
public static void stateSnapshot(float waveTime, int wave, int enemies, boolean paused, boolean gameOver, short coreDataLen, byte[] coreData){
|
||||
try{
|
||||
if(wave > state.wave){
|
||||
state.wave = wave;
|
||||
Events.fire(new WaveEvent());
|
||||
}
|
||||
|
||||
state.gameOver = gameOver;
|
||||
state.wavetime = waveTime;
|
||||
state.wave = wave;
|
||||
state.enemies = enemies;
|
||||
@@ -558,6 +562,22 @@ public class NetClient implements ApplicationListener{
|
||||
//limit to 10 to prevent buffer overflows
|
||||
int usedRequests = Math.min(player.builder().plans().size, 10);
|
||||
|
||||
int totalLength = 0;
|
||||
|
||||
//prevent buffer overflow by checking config length
|
||||
for(int i = 0; i < usedRequests; i++){
|
||||
BuildPlan plan = player.builder().plans().get(i);
|
||||
if(plan.config instanceof byte[]){
|
||||
int length = ((byte[])plan.config).length;
|
||||
totalLength += length;
|
||||
}
|
||||
|
||||
if(totalLength > 2048){
|
||||
usedRequests = i + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
requests = new BuildPlan[usedRequests];
|
||||
for(int i = 0; i < usedRequests; i++){
|
||||
requests[i] = player.builder().plans().get(i);
|
||||
|
||||
@@ -34,8 +34,9 @@ import static arc.util.Log.*;
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class NetServer implements ApplicationListener{
|
||||
private static final int maxSnapshotSize = 430, timerBlockSync = 0;
|
||||
private static final float serverSyncTime = 12, blockSyncTime = 60 * 8;
|
||||
/** note that snapshots are compressed, so the max snapshot size here is above the typical UDP safe limit */
|
||||
private static final int maxSnapshotSize = 800, timerBlockSync = 0;
|
||||
private static final float serverSyncTime = 12, blockSyncTime = 60 * 6;
|
||||
private static final FloatBuffer fbuffer = FloatBuffer.allocate(20);
|
||||
private static final Vec2 vector = new Vec2();
|
||||
private static final Rect viewport = new Rect();
|
||||
@@ -370,6 +371,11 @@ public class NetServer implements ApplicationListener{
|
||||
return;
|
||||
}
|
||||
|
||||
if(currentlyKicking[0] != null){
|
||||
player.sendMessage("[scarlet]A vote is already in progress.");
|
||||
return;
|
||||
}
|
||||
|
||||
if(args.length == 0){
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append("[orange]Players to kick: \n");
|
||||
@@ -384,9 +390,7 @@ public class NetServer implements ApplicationListener{
|
||||
int id = Strings.parseInt(args[0].substring(1));
|
||||
found = Groups.player.find(p -> p.id() == id);
|
||||
}else{
|
||||
found = Groups.player.find(p -> {
|
||||
return p.name.equalsIgnoreCase(args[0]);
|
||||
});
|
||||
found = Groups.player.find(p -> p.name.equalsIgnoreCase(args[0]));
|
||||
}
|
||||
|
||||
if(found != null){
|
||||
@@ -526,6 +530,10 @@ public class NetServer implements ApplicationListener{
|
||||
public static void serverPacketUnreliable(Player player, String type, String contents){
|
||||
serverPacketReliable(player, type, contents);
|
||||
}
|
||||
|
||||
private static boolean invalid(float f){
|
||||
return Float.isInfinite(f) || Float.isNaN(f);
|
||||
}
|
||||
|
||||
@Remote(targets = Loc.client, unreliable = true)
|
||||
public static void clientSnapshot(
|
||||
@@ -544,6 +552,16 @@ public class NetServer implements ApplicationListener{
|
||||
NetConnection con = player.con;
|
||||
if(con == null || snapshotID < con.lastReceivedClientSnapshot) return;
|
||||
|
||||
//validate coordinates just in case
|
||||
if(invalid(x)) x = 0f;
|
||||
if(invalid(y)) y = 0f;
|
||||
if(invalid(xVelocity)) xVelocity = 0f;
|
||||
if(invalid(yVelocity)) yVelocity = 0f;
|
||||
if(invalid(pointerX)) pointerX = 0f;
|
||||
if(invalid(pointerY)) pointerY = 0f;
|
||||
if(invalid(rotation)) rotation = 0f;
|
||||
if(invalid(baseRotation)) baseRotation = 0f;
|
||||
|
||||
boolean verifyPosition = !player.dead() && netServer.admins.getStrict() && headless;
|
||||
|
||||
if(con.lastReceivedClientTime == 0) con.lastReceivedClientTime = Time.millis() - 16;
|
||||
@@ -612,12 +630,12 @@ public class NetServer implements ApplicationListener{
|
||||
if(!player.dead()){
|
||||
Unit unit = player.unit();
|
||||
|
||||
unit.vel.set(xVelocity, yVelocity).limit(unit.type().speed);
|
||||
long elapsed = Time.timeSinceMillis(con.lastReceivedClientTime);
|
||||
float maxSpeed = (boosting ? player.unit().type().boostMultiplier : 1f) * player.unit().type().speed;
|
||||
float maxSpeed = ((player.unit().type().canBoost && player.unit().isFlying()) ? player.unit().type().boostMultiplier : 1f) * player.unit().type().speed;
|
||||
if(unit.isGrounded()){
|
||||
maxSpeed *= unit.floorSpeedMultiplier();
|
||||
}
|
||||
unit.vel.set(xVelocity, yVelocity).limit(maxSpeed);
|
||||
float maxMove = elapsed / 1000f * 60f * maxSpeed * 1.1f;
|
||||
|
||||
if(con.lastUnit != unit){
|
||||
@@ -681,8 +699,8 @@ public class NetServer implements ApplicationListener{
|
||||
public static void adminRequest(Player player, Player other, AdminAction action){
|
||||
|
||||
if(!player.admin){
|
||||
Log.warn("ACCESS DENIED: Player @ / @ attempted to perform admin action without proper security access.",
|
||||
player.name, player.con.address);
|
||||
Log.warn("ACCESS DENIED: Player @ / @ attempted to perform admin action '@' on '@' without proper security access.",
|
||||
player.name, player.con.address, action.name(), other == null ? null : other.name);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -735,7 +753,7 @@ public class NetServer implements ApplicationListener{
|
||||
}
|
||||
|
||||
public boolean isWaitingForPlayers(){
|
||||
if(state.rules.pvp){
|
||||
if(state.rules.pvp && !state.gameOver){
|
||||
int used = 0;
|
||||
for(TeamData t : state.teams.getActive()){
|
||||
if(Groups.player.count(p -> p.team() == t.team) > 0){
|
||||
@@ -761,6 +779,10 @@ public class NetServer implements ApplicationListener{
|
||||
}
|
||||
|
||||
if(state.isGame() && net.server()){
|
||||
if(state.rules.pvp){
|
||||
state.serverPaused = isWaitingForPlayers();
|
||||
}
|
||||
|
||||
sync();
|
||||
}
|
||||
}
|
||||
@@ -794,7 +816,7 @@ public class NetServer implements ApplicationListener{
|
||||
if(!entity.block().sync) continue;
|
||||
sent ++;
|
||||
|
||||
dataStream.writeInt(entity.tile().pos());
|
||||
dataStream.writeInt(entity.pos());
|
||||
entity.writeAll(Writes.get(dataStream));
|
||||
|
||||
if(syncStream.size() > maxSnapshotSize){
|
||||
@@ -828,7 +850,7 @@ public class NetServer implements ApplicationListener{
|
||||
byte[] stateBytes = syncStream.toByteArray();
|
||||
|
||||
//write basic state data.
|
||||
Call.stateSnapshot(player.con, state.wavetime, state.wave, state.enemies, state.serverPaused, (short)stateBytes.length, net.compressSnapshot(stateBytes));
|
||||
Call.stateSnapshot(player.con, state.wavetime, state.wave, state.enemies, state.serverPaused, state.gameOver, (short)stateBytes.length, net.compressSnapshot(stateBytes));
|
||||
|
||||
viewport.setSize(player.con.viewWidth, player.con.viewHeight).setCenter(player.con.viewX, player.con.viewY);
|
||||
|
||||
|
||||
@@ -185,6 +185,8 @@ public class Renderer implements ApplicationListener{
|
||||
}
|
||||
|
||||
public void draw(){
|
||||
Events.fire(Trigger.preDraw);
|
||||
|
||||
camera.update();
|
||||
|
||||
if(Float.isNaN(camera.position.x) || Float.isNaN(camera.position.y)){
|
||||
@@ -205,6 +207,8 @@ public class Renderer implements ApplicationListener{
|
||||
|
||||
Draw.sort(true);
|
||||
|
||||
Events.fire(Trigger.draw);
|
||||
|
||||
if(pixelator.enabled()){
|
||||
pixelator.register();
|
||||
}
|
||||
@@ -254,6 +258,8 @@ public class Renderer implements ApplicationListener{
|
||||
Draw.reset();
|
||||
Draw.flush();
|
||||
Draw.sort(false);
|
||||
|
||||
Events.fire(Trigger.postDraw);
|
||||
}
|
||||
|
||||
private void drawBackground(){
|
||||
|
||||
@@ -128,6 +128,8 @@ public class UI implements ApplicationListener, Loadable{
|
||||
public void update(){
|
||||
if(disableUI || Core.scene == null) return;
|
||||
|
||||
Events.fire(Trigger.uiDrawBegin);
|
||||
|
||||
Core.scene.act();
|
||||
Core.scene.draw();
|
||||
|
||||
@@ -143,6 +145,8 @@ public class UI implements ApplicationListener, Loadable{
|
||||
control.tutorial.draw();
|
||||
Draw.flush();
|
||||
}
|
||||
|
||||
Events.fire(Trigger.uiDrawEnd);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -221,12 +225,15 @@ public class UI implements ApplicationListener, Loadable{
|
||||
}
|
||||
|
||||
public TextureRegionDrawable getIcon(String name){
|
||||
if(Icon.icons.containsKey(name)){
|
||||
return Icon.icons.get(name);
|
||||
}
|
||||
if(Icon.icons.containsKey(name)) return Icon.icons.get(name);
|
||||
return Core.atlas.getDrawable("error");
|
||||
}
|
||||
|
||||
public TextureRegionDrawable getIcon(String name, String def){
|
||||
if(Icon.icons.containsKey(name)) return Icon.icons.get(name);
|
||||
return getIcon(def);
|
||||
}
|
||||
|
||||
public void loadAnd(Runnable call){
|
||||
loadAnd("@loading", call);
|
||||
}
|
||||
|
||||
@@ -484,6 +484,9 @@ public class World{
|
||||
|
||||
private class Context implements WorldContext{
|
||||
|
||||
Context(){
|
||||
}
|
||||
|
||||
@Override
|
||||
public Tile tile(int index){
|
||||
return tiles.geti(index);
|
||||
|
||||
@@ -37,18 +37,18 @@ public class MapGenerateDialog extends BaseDialog{
|
||||
private Pixmap pixmap;
|
||||
private Texture texture;
|
||||
private GenerateInput input = new GenerateInput();
|
||||
private Seq<GenerateFilter> filters = new Seq<>();
|
||||
Seq<GenerateFilter> filters = new Seq<>();
|
||||
private int scaling = mobile ? 3 : 1;
|
||||
private Table filterTable;
|
||||
|
||||
private AsyncExecutor executor = new AsyncExecutor(1);
|
||||
private AsyncResult<Void> result;
|
||||
private boolean generating;
|
||||
boolean generating;
|
||||
private GenTile returnTile = new GenTile();
|
||||
|
||||
private GenTile[][] buffer1, buffer2;
|
||||
private Cons<Seq<GenerateFilter>> applier;
|
||||
private CachedTile ctile = new CachedTile(){
|
||||
CachedTile ctile = new CachedTile(){
|
||||
//nothing.
|
||||
@Override
|
||||
protected void changeEntity(Team team, Prov<Building> entityprov, int rotation){
|
||||
@@ -178,7 +178,7 @@ public class MapGenerateDialog extends BaseDialog{
|
||||
add(new Image(Styles.black8));
|
||||
add(new Image(Icon.refresh, Scaling.none));
|
||||
visible(() -> generating && !updateEditorOnChange);
|
||||
}}).grow().padRight(10);
|
||||
}}).uniformX().grow().padRight(10);
|
||||
t.pane(p -> filterTable = p.marginRight(6)).update(pane -> {
|
||||
if(Core.scene.getKeyboardFocus() instanceof Dialog && Core.scene.getKeyboardFocus() != this){
|
||||
return;
|
||||
@@ -191,7 +191,7 @@ public class MapGenerateDialog extends BaseDialog{
|
||||
}else{
|
||||
Core.scene.setScrollFocus(null);
|
||||
}
|
||||
}).grow().get().setScrollingDisabled(true, false);
|
||||
}).grow().uniformX().get().setScrollingDisabled(true, false);
|
||||
}).grow();
|
||||
|
||||
buffer1 = create();
|
||||
@@ -213,7 +213,7 @@ public class MapGenerateDialog extends BaseDialog{
|
||||
}
|
||||
|
||||
void rebuildFilters(){
|
||||
int cols = Math.max((int)(Math.max(filterTable.parent.getWidth(), Core.graphics.getWidth()/2f * 0.9f) / Scl.scl(290f)), 1);
|
||||
int cols = Math.max((int)(Core.graphics.getWidth()/2f / Scl.scl(290f)), 1);
|
||||
filterTable.clearChildren();
|
||||
filterTable.top().left();
|
||||
int i = 0;
|
||||
@@ -229,7 +229,7 @@ public class MapGenerateDialog extends BaseDialog{
|
||||
t.setColor(Pal.gray);
|
||||
|
||||
t.top().left();
|
||||
t.add(filter.name()).left().padLeft(6);
|
||||
t.add(filter.name()).left().padLeft(6).width(100f).wrap();
|
||||
|
||||
t.add().growX();
|
||||
|
||||
@@ -408,6 +408,9 @@ public class MapGenerateDialog extends BaseDialog{
|
||||
public byte team;
|
||||
public short block, floor, ore;
|
||||
|
||||
GenTile(){
|
||||
}
|
||||
|
||||
public void set(Block floor, Block wall, Block ore, Team team){
|
||||
this.floor = floor.id;
|
||||
this.block = wall.id;
|
||||
|
||||
@@ -22,7 +22,7 @@ import static mindustry.Vars.ui;
|
||||
|
||||
public class MapView extends Element implements GestureListener{
|
||||
private MapEditor editor;
|
||||
private EditorTool tool = EditorTool.pencil;
|
||||
EditorTool tool = EditorTool.pencil;
|
||||
private float offsetx, offsety;
|
||||
private float zoom = 1f;
|
||||
private boolean grid = false;
|
||||
@@ -31,11 +31,11 @@ public class MapView extends Element implements GestureListener{
|
||||
private Rect rect = new Rect();
|
||||
private Vec2[][] brushPolygons = new Vec2[MapEditor.brushSizes.length][0];
|
||||
|
||||
private boolean drawing;
|
||||
private int lastx, lasty;
|
||||
private int startx, starty;
|
||||
private float mousex, mousey;
|
||||
private EditorTool lastTool;
|
||||
boolean drawing;
|
||||
int lastx, lasty;
|
||||
int startx, starty;
|
||||
float mousex, mousey;
|
||||
EditorTool lastTool;
|
||||
|
||||
public MapView(MapEditor editor){
|
||||
this.editor = editor;
|
||||
@@ -204,7 +204,7 @@ public class MapView extends Element implements GestureListener{
|
||||
zoom = Mathf.clamp(zoom, 0.2f, 20f);
|
||||
}
|
||||
|
||||
private Point2 project(float x, float y){
|
||||
Point2 project(float x, float y){
|
||||
float ratio = 1f / ((float)editor.width() / editor.height());
|
||||
float size = Math.min(width, height);
|
||||
float sclwidth = size * zoom;
|
||||
|
||||
@@ -23,7 +23,7 @@ import static mindustry.game.SpawnGroup.*;
|
||||
|
||||
public class WaveInfoDialog extends BaseDialog{
|
||||
private int displayed = 20;
|
||||
private Seq<SpawnGroup> groups = new Seq<>();
|
||||
Seq<SpawnGroup> groups = new Seq<>();
|
||||
|
||||
private Table table;
|
||||
private int start = 0;
|
||||
|
||||
@@ -30,24 +30,26 @@ public class Damage{
|
||||
private static Unit tmpUnit;
|
||||
|
||||
/** 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){
|
||||
for(int i = 0; i < Mathf.clamp(power / 20, 0, 6); i++){
|
||||
int branches = 5 + Mathf.clamp((int)(power / 30), 1, 20);
|
||||
Time.run(i * 2f + Mathf.random(4f), () -> Lightning.create(Team.derelict, Pal.power, 3, x, y, Mathf.random(360f), branches + Mathf.range(2)));
|
||||
}
|
||||
public static void dynamicExplosion(float x, float y, float flammability, float explosiveness, float power, float radius, Color color, boolean damage){
|
||||
if(damage){
|
||||
for(int i = 0; i < Mathf.clamp(power / 20, 0, 6); i++){
|
||||
int branches = 5 + Mathf.clamp((int)(power / 30), 1, 20);
|
||||
Time.run(i * 2f + Mathf.random(4f), () -> Lightning.create(Team.derelict, Pal.power, 3, x, y, Mathf.random(360f), branches + Mathf.range(2)));
|
||||
}
|
||||
|
||||
for(int i = 0; i < Mathf.clamp(flammability / 4, 0, 30); i++){
|
||||
Time.run(i / 2f, () -> Call.createBullet(Bullets.fireball, Team.derelict, x, y, Mathf.random(360f), Bullets.fireball.damage, 1, 1));
|
||||
}
|
||||
for(int i = 0; i < Mathf.clamp(flammability / 4, 0, 30); i++){
|
||||
Time.run(i / 2f, () -> Call.createBullet(Bullets.fireball, Team.derelict, x, y, Mathf.random(360f), Bullets.fireball.damage, 1, 1));
|
||||
}
|
||||
|
||||
int waves = Mathf.clamp((int)(explosiveness / 4), 0, 30);
|
||||
int waves = Mathf.clamp((int)(explosiveness / 4), 0, 30);
|
||||
|
||||
for(int i = 0; i < waves; i++){
|
||||
int f = i;
|
||||
Time.run(i * 2f, () -> {
|
||||
Damage.damage(x, y, Mathf.clamp(radius + explosiveness, 0, 50f) * ((f + 1f) / waves), explosiveness / 2f);
|
||||
Fx.blockExplosionSmoke.at(x + Mathf.range(radius), y + Mathf.range(radius));
|
||||
});
|
||||
for(int i = 0; i < waves; i++){
|
||||
int f = i;
|
||||
Time.run(i * 2f, () -> {
|
||||
Damage.damage(x, y, Mathf.clamp(radius + explosiveness, 0, 50f) * ((f + 1f) / waves), explosiveness / 2f);
|
||||
Fx.blockExplosionSmoke.at(x + Mathf.range(radius), y + Mathf.range(radius));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if(explosiveness > 15f){
|
||||
@@ -87,7 +89,7 @@ public class Damage{
|
||||
tr.trns(angle, length);
|
||||
Intc2 collider = (cx, cy) -> {
|
||||
Building tile = world.build(cx, cy);
|
||||
if(tile != null && !collidedBlocks.contains(tile.pos()) && tile.team() != team && tile.collide(hitter)){
|
||||
if(tile != null && !collidedBlocks.contains(tile.pos()) && tile.team != team && tile.collide(hitter)){
|
||||
tile.collision(hitter);
|
||||
collidedBlocks.add(tile.pos());
|
||||
hitter.type.hit(hitter, tile.x, tile.y);
|
||||
|
||||
@@ -101,7 +101,7 @@ public class EntityGroup<T extends Entityc> implements Iterable<T>{
|
||||
if(map == null) throw new RuntimeException("Mapping is not enabled for group " + id + "!");
|
||||
T t = map.get(id);
|
||||
if(t != null){ //remove if present in map already
|
||||
remove(t);
|
||||
t.remove();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ public class Fires{
|
||||
|
||||
/** Start a fire on the tile. If there already is a file there, refreshes its lifetime. */
|
||||
public static void create(Tile tile){
|
||||
if(net.client() || tile == null) return; //not clientside.
|
||||
if(net.client() || tile == null || !state.rules.fire) return; //not clientside.
|
||||
|
||||
Fire fire = map.get(tile.pos());
|
||||
|
||||
|
||||
@@ -19,8 +19,17 @@ public class Units{
|
||||
private static boolean boolResult;
|
||||
|
||||
@Remote(called = Loc.server)
|
||||
public static void unitDeath(Unit unit){
|
||||
unit.killed();
|
||||
public static void unitDeath(int uid){
|
||||
Unit unit = Groups.unit.getByID(uid);
|
||||
|
||||
//if there's no unit don't add it later and get it stuck as a ghost
|
||||
if(netClient != null){
|
||||
netClient.addRemovedEntity(uid);
|
||||
}
|
||||
|
||||
if(unit != null){
|
||||
unit.killed();
|
||||
}
|
||||
}
|
||||
|
||||
@Remote(called = Loc.server)
|
||||
|
||||
@@ -31,6 +31,7 @@ public class ForceFieldAbility implements Ability{
|
||||
//break shield
|
||||
if(paramUnit.shield <= trait.damage()){
|
||||
paramUnit.shield -= cooldown * regen;
|
||||
|
||||
Fx.shieldBreak.at(paramUnit.x, paramUnit.y, radius, paramUnit.team.color);
|
||||
}
|
||||
|
||||
|
||||
@@ -92,6 +92,7 @@ public abstract class BulletType extends Content{
|
||||
public float homingPower = 0f;
|
||||
public float homingRange = 50f;
|
||||
|
||||
public Color lightningColor = Pal.surge;
|
||||
public int lightning;
|
||||
public int lightningLength = 5;
|
||||
/** Use a negative value to use default bullet damage. */
|
||||
@@ -172,7 +173,7 @@ public abstract class BulletType extends Content{
|
||||
}
|
||||
|
||||
for(int i = 0; i < lightning; i++){
|
||||
Lightning.create(b, Pal.surge, lightningDamage < 0 ? damage : lightningDamage, b.x, b.y, Mathf.random(360f), lightningLength);
|
||||
Lightning.create(b, lightningColor, lightningDamage < 0 ? damage : lightningDamage, b.x, b.y, Mathf.random(360f), lightningLength);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -264,7 +265,7 @@ public abstract class BulletType extends Content{
|
||||
bullet.damage = damage < 0 ? this.damage : damage;
|
||||
bullet.add();
|
||||
|
||||
if(keepVelocity && owner instanceof Hitboxc) bullet.vel.add(((Hitboxc)owner).deltaX(), ((Hitboxc)owner).deltaY());
|
||||
if(keepVelocity && owner instanceof Hitboxc) bullet.vel.add(((Hitboxc)owner).deltaX() / Time.delta, ((Hitboxc)owner).deltaY() / Time.delta);
|
||||
return bullet;
|
||||
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ public class HealBulletType extends BulletType{
|
||||
|
||||
@Override
|
||||
public boolean collides(Bullet b, Building tile){
|
||||
return tile.team() != b.team || tile.healthf() < 1f;
|
||||
return tile.team != b.team || tile.healthf() < 1f;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -46,7 +46,7 @@ public class HealBulletType extends BulletType{
|
||||
public void hitTile(Bullet b, Building tile){
|
||||
super.hit(b);
|
||||
|
||||
if(tile.team() == b.team && !(tile.block() instanceof BuildBlock)){
|
||||
if(tile.team == b.team && !(tile.block() instanceof BuildBlock)){
|
||||
Fx.healBlockFull.at(tile.x, tile.y, tile.block().size, Pal.heal);
|
||||
tile.heal(healPercent / 100f * tile.maxHealth());
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ abstract class BlockUnitComp implements Unitc{
|
||||
@Override
|
||||
public void update(){
|
||||
if(tile != null){
|
||||
team = tile.team();
|
||||
team = tile.team;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ abstract class BlockUnitComp implements Unitc{
|
||||
public void team(Team team){
|
||||
if(tile != null && this.team != team){
|
||||
this.team = team;
|
||||
if(tile.team() != team){
|
||||
if(tile.team != team){
|
||||
tile.team(team);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -144,7 +144,6 @@ abstract class BuilderComp implements Unitc{
|
||||
boolean shouldSkip(BuildPlan request, @Nullable Building core){
|
||||
//requests that you have at least *started* are considered
|
||||
if(state.rules.infiniteResources || team().rules().infiniteResources || request.breaking || core == null) return false;
|
||||
//TODO these are bad criteria
|
||||
return (request.stuck && !core.items.has(request.block.requirements)) || (Structs.contains(request.block.requirements, i -> !core.items.has(i.item)) && !request.initialized);
|
||||
}
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ import static mindustry.Vars.*;
|
||||
@Component(base = true)
|
||||
abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc, QuadTreeObject, Displayable, Senseable, Controllable{
|
||||
//region vars and initialization
|
||||
static final float timeToSleep = 60f * 1;
|
||||
static final float timeToSleep = 60f * 1, timeToUncontrol = 60f * 6;
|
||||
static final ObjectSet<Building> tmpTiles = new ObjectSet<>();
|
||||
static final Seq<Building> tempTileEnts = new Seq<>();
|
||||
static final Seq<Tile> tempTiles = new Seq<>();
|
||||
@@ -57,6 +57,7 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc,
|
||||
transient byte dump;
|
||||
transient int rotation;
|
||||
transient boolean enabled = true;
|
||||
transient float enabledControlTime;
|
||||
|
||||
PowerModule power;
|
||||
ItemModule items;
|
||||
@@ -134,8 +135,9 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc,
|
||||
|
||||
public final void writeBase(Writes write){
|
||||
write.f(health);
|
||||
write.b(rotation);
|
||||
write.b(rotation | 0b10000000);
|
||||
write.b(team.id);
|
||||
write.b(0); //extra padding for later use
|
||||
if(items != null) items.write(write);
|
||||
if(power != null) power.write(write);
|
||||
if(liquids != null) liquids.write(write);
|
||||
@@ -144,12 +146,20 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc,
|
||||
|
||||
public final void readBase(Reads read){
|
||||
health = read.f();
|
||||
rotation = read.b();
|
||||
byte rot = read.b();
|
||||
team = Team.get(read.b());
|
||||
if(items != null) items.read(read);
|
||||
if(power != null) power.read(read);
|
||||
if(liquids != null) liquids.read(read);
|
||||
if(cons != null) cons.read(read);
|
||||
|
||||
rotation = rot & 0b01111111;
|
||||
boolean legacy = true;
|
||||
if((rot & 0b10000000) != 0){
|
||||
read.b(); //padding
|
||||
legacy = false;
|
||||
}
|
||||
|
||||
if(items != null) items.read(read, legacy);
|
||||
if(power != null) power.read(read, legacy);
|
||||
if(liquids != null) liquids.read(read, legacy);
|
||||
if(cons != null) cons.read(read, legacy);
|
||||
}
|
||||
|
||||
public void writeAll(Writes write){
|
||||
@@ -330,15 +340,20 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc,
|
||||
//endregion
|
||||
//region handler methods
|
||||
|
||||
/** Called when this block is dropped as a payload. */
|
||||
public void dropped(){
|
||||
|
||||
}
|
||||
|
||||
/** This is for logic blocks. */
|
||||
public void handleString(Object value){
|
||||
|
||||
}
|
||||
|
||||
public void created(){}
|
||||
|
||||
|
||||
public boolean shouldConsume(){
|
||||
return true;
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public boolean productionValid(){
|
||||
@@ -404,7 +419,7 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc,
|
||||
int trns = block.size/2 + 1;
|
||||
Tile next = tile.getNearby(Geometry.d4(rotation).x * trns, Geometry.d4(rotation).y * trns);
|
||||
|
||||
if(next != null && next.build != null && next.build.team() == team && next.build.acceptPayload(base(), todump)){
|
||||
if(next != null && next.build != null && next.build.team == team && next.build.acceptPayload(base(), todump)){
|
||||
next.build.handlePayload(base(), todump);
|
||||
return true;
|
||||
}
|
||||
@@ -425,7 +440,7 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc,
|
||||
for(int i = 0; i < proximity.size; i++){
|
||||
Building other = proximity.get((i + dump) % proximity.size);
|
||||
|
||||
if(other.team() == team && other.acceptPayload(base(), todump)){
|
||||
if(other.team == team && other.acceptPayload(base(), todump)){
|
||||
other.handlePayload(base(), todump);
|
||||
incrementDump(proximity.size);
|
||||
return true;
|
||||
@@ -504,34 +519,31 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc,
|
||||
|
||||
next = next.getLiquidDestination(base(), liquid);
|
||||
|
||||
if(next.team() == team && next.block.hasLiquids && liquids.get(liquid) > 0f){
|
||||
if(next.team == team && next.block.hasLiquids && liquids.get(liquid) > 0f){
|
||||
float ofract = next.liquids.get(liquid) / next.block.liquidCapacity;
|
||||
float fract = liquids.get(liquid) / block.liquidCapacity * block.liquidPressure;
|
||||
float flow = Math.min(Mathf.clamp((fract - ofract) * (1f)) * (block.liquidCapacity), liquids.get(liquid));
|
||||
flow = Math.min(flow, next.block.liquidCapacity - next.liquids.get(liquid) - 0.001f);
|
||||
|
||||
if(next.acceptLiquid(base(), liquid, 0f)){
|
||||
float ofract = next.liquids().get(liquid) / next.block.liquidCapacity;
|
||||
float fract = liquids.get(liquid) / block.liquidCapacity * block.liquidPressure;
|
||||
float flow = Math.min(Mathf.clamp((fract - ofract) * (1f)) * (block.liquidCapacity), liquids.get(liquid));
|
||||
flow = Math.min(flow, next.block.liquidCapacity - next.liquids().get(liquid) - 0.001f);
|
||||
if(flow > 0f && ofract <= fract && next.acceptLiquid(base(), liquid, flow)){
|
||||
next.handleLiquid(base(), liquid, flow);
|
||||
liquids.remove(liquid, flow);
|
||||
return flow;
|
||||
}else if(next.liquids.currentAmount() / next.block.liquidCapacity > 0.1f && fract > 0.1f){
|
||||
//TODO these are incorrect effect positions
|
||||
float fx = (x + next.x) / 2f, fy = (y + next.y) / 2f;
|
||||
|
||||
if(flow > 0f && ofract <= fract && next.acceptLiquid(base(), liquid, flow)){
|
||||
next.handleLiquid(base(), liquid, flow);
|
||||
liquids.remove(liquid, flow);
|
||||
return flow;
|
||||
}else if(ofract > 0.1f && fract > 0.1f){
|
||||
//TODO these are incorrect effect positions
|
||||
float fx = (x + next.x) / 2f, fy = (y + next.y) / 2f;
|
||||
|
||||
Liquid other = next.liquids().current();
|
||||
if((other.flammability > 0.3f && liquid.temperature > 0.7f) || (liquid.flammability > 0.3f && other.temperature > 0.7f)){
|
||||
damage(1 * Time.delta);
|
||||
next.damage(1 * Time.delta);
|
||||
if(Mathf.chance(0.1 * Time.delta)){
|
||||
Fx.fire.at(fx, fy);
|
||||
}
|
||||
}else if((liquid.temperature > 0.7f && other.temperature < 0.55f) || (other.temperature > 0.7f && liquid.temperature < 0.55f)){
|
||||
liquids.remove(liquid, Math.min(liquids.get(liquid), 0.7f * Time.delta));
|
||||
if(Mathf.chance(0.2f * Time.delta)){
|
||||
Fx.steam.at(fx, fy);
|
||||
}
|
||||
Liquid other = next.liquids.current();
|
||||
if((other.flammability > 0.3f && liquid.temperature > 0.7f) || (liquid.flammability > 0.3f && other.temperature > 0.7f)){
|
||||
damage(1 * Time.delta);
|
||||
next.damage(1 * Time.delta);
|
||||
if(Mathf.chance(0.1 * Time.delta)){
|
||||
Fx.fire.at(fx, fy);
|
||||
}
|
||||
}else if((liquid.temperature > 0.7f && other.temperature < 0.55f) || (other.temperature > 0.7f && liquid.temperature < 0.55f)){
|
||||
liquids.remove(liquid, Math.min(liquids.get(liquid), 0.7f * Time.delta));
|
||||
if(Mathf.chance(0.2f * Time.delta)){
|
||||
Fx.steam.at(fx, fy);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -562,7 +574,7 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc,
|
||||
for(int i = 0; i < proximity.size; i++){
|
||||
incrementDump(proximity.size);
|
||||
Building other = proximity.get((i + dump) % proximity.size);
|
||||
if(other.team() == team && other.acceptItem(base(), item) && canDump(other, item)){
|
||||
if(other.team == team && other.acceptItem(base(), item) && canDump(other, item)){
|
||||
other.handleItem(base(), item);
|
||||
return;
|
||||
}
|
||||
@@ -580,7 +592,7 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc,
|
||||
for(int i = 0; i < proximity.size; i++){
|
||||
incrementDump(proximity.size);
|
||||
Building other = proximity.get((i + dump) % proximity.size);
|
||||
if(other.team() == team && other.acceptItem(base(), item) && canDump(other, item)){
|
||||
if(other.team == team && other.acceptItem(base(), item) && canDump(other, item)){
|
||||
other.handleItem(base(), item);
|
||||
return true;
|
||||
}
|
||||
@@ -613,7 +625,7 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc,
|
||||
for(int ii = 0; ii < content.items().size; ii++){
|
||||
Item item = content.item(ii);
|
||||
|
||||
if(other.team() == team && items.has(item) && other.acceptItem(base(), item) && canDump(other, item)){
|
||||
if(other.team == team && items.has(item) && other.acceptItem(base(), item) && canDump(other, item)){
|
||||
other.handleItem(base(), item);
|
||||
items.remove(item, 1);
|
||||
incrementDump(proximity.size);
|
||||
@@ -621,7 +633,7 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc,
|
||||
}
|
||||
}
|
||||
}else{
|
||||
if(other.team() == team && other.acceptItem(base(), todump) && canDump(other, todump)){
|
||||
if(other.team == team && other.acceptItem(base(), todump) && canDump(other, todump)){
|
||||
other.handleItem(base(), todump);
|
||||
items.remove(todump, 1);
|
||||
incrementDump(proximity.size);
|
||||
@@ -647,7 +659,7 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc,
|
||||
/** Try offloading an item to a nearby container in its facing direction. Returns true if success. */
|
||||
public boolean moveForward(Item item){
|
||||
Building other = front();
|
||||
if(other != null && other.team() == team && other.acceptItem(base(), item)){
|
||||
if(other != null && other.team == team && other.acceptItem(base(), item)){
|
||||
other.handleItem(base(), item);
|
||||
return true;
|
||||
}
|
||||
@@ -751,6 +763,16 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc,
|
||||
public void drawSelect(){
|
||||
}
|
||||
|
||||
public void drawDisabled(){
|
||||
Draw.color(Color.scarlet);
|
||||
Draw.alpha(0.8f);
|
||||
|
||||
float size = 6f;
|
||||
Draw.rect(Icon.cancel.getRegion(), x, y, size, size);
|
||||
|
||||
Draw.reset();
|
||||
}
|
||||
|
||||
public void draw(){
|
||||
Draw.rect(block.region, x, y, block.rotate ? rotdeg() : 0);
|
||||
|
||||
@@ -871,7 +893,7 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc,
|
||||
power += this.power.status * block.consumes.getPower().capacity;
|
||||
}
|
||||
|
||||
if(block.hasLiquids){
|
||||
if(block.hasLiquids && state.rules.damageExplosions){
|
||||
|
||||
liquids.each((liquid, amount) -> {
|
||||
float splash = Mathf.clamp(amount / 4f, 0f, 10f);
|
||||
@@ -887,7 +909,8 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc,
|
||||
});
|
||||
}
|
||||
|
||||
Damage.dynamicExplosion(x, y, flammability, explosiveness * 3.5f, power, tilesize * block.size / 2f, Pal.darkFlame);
|
||||
Damage.dynamicExplosion(x, y, flammability, explosiveness * 3.5f, power, tilesize * block.size / 2f, Pal.darkFlame, state.rules.damageExplosions);
|
||||
|
||||
if(!floor().solid && !floor().isLiquid){
|
||||
Effect.rubble(x, y, block.size);
|
||||
}
|
||||
@@ -1203,6 +1226,7 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc,
|
||||
public void control(LAccess type, double p1, double p2, double p3, double p4){
|
||||
if(type == LAccess.enabled){
|
||||
enabled = !Mathf.zero((float)p1);
|
||||
enabledControlTime = timeToUncontrol;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1230,6 +1254,14 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc,
|
||||
timeScale = 1f;
|
||||
}
|
||||
|
||||
if(block.autoResetEnabled){
|
||||
enabledControlTime -= Time.delta;
|
||||
|
||||
if(enabledControlTime <= 0){
|
||||
enabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
if(sound != null){
|
||||
sound.update(x, y, shouldActiveSound());
|
||||
}
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
package mindustry.entities.comp;
|
||||
|
||||
import arc.func.*;
|
||||
import arc.math.geom.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.ArcAnnotate.*;
|
||||
import mindustry.ai.formations.*;
|
||||
import mindustry.ai.types.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.entities.*;
|
||||
import mindustry.entities.units.*;
|
||||
import mindustry.gen.*;
|
||||
|
||||
@@ -12,6 +15,7 @@ import mindustry.gen.*;
|
||||
@Component
|
||||
abstract class CommanderComp implements Unitc{
|
||||
private static final Seq<FormationMember> members = new Seq<>();
|
||||
private static final Seq<Unit> units = new Seq<>();
|
||||
|
||||
@Import float x, y, rotation;
|
||||
|
||||
@@ -44,6 +48,28 @@ abstract class CommanderComp implements Unitc{
|
||||
clearCommand();
|
||||
}
|
||||
|
||||
void commandNearby(FormationPattern pattern){
|
||||
commandNearby(pattern, u -> true);
|
||||
}
|
||||
|
||||
void commandNearby(FormationPattern pattern, Boolf<Unit> include){
|
||||
Formation formation = new Formation(new Vec3(x, y, rotation), pattern);
|
||||
formation.slotAssignmentStrategy = new DistanceAssignmentStrategy(pattern);
|
||||
|
||||
units.clear();
|
||||
|
||||
Units.nearby(team(), x, y, 200f, u -> {
|
||||
if(u.isAI() && include.get(u) && u != base()){
|
||||
units.add(u);
|
||||
}
|
||||
});
|
||||
|
||||
units.sort(u -> u.dst2(this));
|
||||
units.truncate(type().commandLimit);
|
||||
|
||||
command(formation, units);
|
||||
}
|
||||
|
||||
void command(Formation formation, Seq<Unit> units){
|
||||
clearCommand();
|
||||
|
||||
|
||||
@@ -15,14 +15,14 @@ import static mindustry.Vars.*;
|
||||
|
||||
@EntityDef(value = {Firec.class}, pooled = true)
|
||||
@Component(base = true)
|
||||
abstract class FireComp implements Timedc, Posc, Firec{
|
||||
abstract class FireComp implements Timedc, Posc, Firec, Syncc{
|
||||
private static final float spreadChance = 0.05f, fireballChance = 0.07f;
|
||||
|
||||
@Import float time, lifetime, x, y;
|
||||
|
||||
Tile tile;
|
||||
private Block block;
|
||||
private float baseFlammability = -1, puddleFlammability;
|
||||
private transient Block block;
|
||||
private transient float baseFlammability = -1, puddleFlammability;
|
||||
|
||||
@Override
|
||||
public void update(){
|
||||
@@ -99,4 +99,9 @@ abstract class FireComp implements Timedc, Posc, Firec{
|
||||
public void afterRead(){
|
||||
Fires.register(base());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterSync(){
|
||||
Fires.register(base());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ import static mindustry.Vars.net;
|
||||
abstract class FlyingComp implements Posc, Velc, Healthc, Hitboxc{
|
||||
private static final Vec2 tmp1 = new Vec2(), tmp2 = new Vec2();
|
||||
|
||||
@Import float x, y;
|
||||
@Import float x, y, speedMultiplier;
|
||||
@Import Vec2 vel;
|
||||
|
||||
@SyncLocal float elevation;
|
||||
@@ -56,7 +56,7 @@ abstract class FlyingComp implements Posc, Velc, Healthc, Hitboxc{
|
||||
|
||||
float floorSpeedMultiplier(){
|
||||
Floor on = isFlying() || hovering ? Blocks.air.asFloor() : floorOn();
|
||||
return on.speedMultiplier;
|
||||
return on.speedMultiplier * speedMultiplier;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -13,7 +13,7 @@ abstract class MechComp implements Posc, Flyingc, Hitboxc, Unitc, Mechc, Elevati
|
||||
@Override
|
||||
public void update(){
|
||||
float len = deltaLen();
|
||||
baseRotation = Angles.moveToward(baseRotation, deltaAngle(), type().baseRotateSpeed * Mathf.clamp(len / type().speed));
|
||||
walkTime += Time.delta *len;
|
||||
baseRotation = Angles.moveToward(baseRotation, deltaAngle(), type().baseRotateSpeed * Mathf.clamp(len / type().speed / Time.delta) * Time.delta);
|
||||
walkTime += len;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,7 +95,7 @@ abstract class PayloadComp implements Posc, Rotc, Hitboxc{
|
||||
Building tile = payload.entity;
|
||||
int tx = Vars.world.toTile(x - tile.block().offset), ty = Vars.world.toTile(y - tile.block().offset);
|
||||
Tile on = Vars.world.tile(tx, ty);
|
||||
if(on != null && Build.validPlace(tile.block(), tile.team(), tx, ty, tile.rotation)){
|
||||
if(on != null && Build.validPlace(tile.block(), tile.team, tx, ty, tile.rotation)){
|
||||
int rot = (int)((rotation + 45f) / 90f) % 4;
|
||||
payload.place(on, rot);
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I
|
||||
@Import float x, y, rotation, elevation, maxHealth, drag, armor, hitSize, health;
|
||||
@Import boolean dead;
|
||||
@Import Team team;
|
||||
@Import int id;
|
||||
|
||||
private UnitController controller;
|
||||
private UnitType type;
|
||||
@@ -223,7 +224,7 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I
|
||||
|
||||
//apply knockback based on spawns
|
||||
if(team != state.rules.waveTeam){
|
||||
float relativeSize = state.rules.dropZoneRadius + bounds()/2f + 1f;
|
||||
float relativeSize = state.rules.dropZoneRadius + hitSize/2f + 1f;
|
||||
for(Tile spawn : spawner.getSpawns()){
|
||||
if(within(spawn.worldx(), spawn.worldy(), relativeSize)){
|
||||
vel().add(Tmp.v1.set(this).sub(spawn.worldx(), spawn.worldy()).setLength(0.1f + 1f - dst(spawn) / relativeSize).scl(0.45f * Time.delta));
|
||||
@@ -275,10 +276,10 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I
|
||||
damageContinuous(floor.damageTaken);
|
||||
}
|
||||
|
||||
if(!net.client() && tile.solid()){
|
||||
if(tile.solid()){
|
||||
if(type.canBoost){
|
||||
elevation = 1f;
|
||||
}else{
|
||||
}else if(!net.client()){
|
||||
kill();
|
||||
}
|
||||
}
|
||||
@@ -314,7 +315,7 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I
|
||||
public void destroy(){
|
||||
float explosiveness = 2f + item().explosiveness * stack().amount;
|
||||
float flammability = item().flammability * stack().amount;
|
||||
Damage.dynamicExplosion(x, y, flammability, explosiveness, 0f, bounds() / 2f, Pal.darkFlame);
|
||||
Damage.dynamicExplosion(x, y, flammability, explosiveness, 0f, bounds() / 2f, Pal.darkFlame, state.rules.damageExplosions);
|
||||
|
||||
float shake = hitSize / 3f;
|
||||
|
||||
@@ -389,6 +390,6 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I
|
||||
if(dead || net.client()) return;
|
||||
|
||||
//deaths are synced; this calls killed()
|
||||
Call.unitDeath(base());
|
||||
Call.unitDeath(id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package mindustry.entities.comp;
|
||||
|
||||
import arc.math.*;
|
||||
import arc.math.geom.*;
|
||||
import arc.util.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
@@ -18,7 +19,7 @@ abstract class VelComp implements Posc{
|
||||
@Override
|
||||
public void update(){
|
||||
move(vel.x * Time.delta, vel.y * Time.delta);
|
||||
vel.scl(1f - drag * Time.delta);
|
||||
vel.scl(Mathf.clamp(1f - drag * Time.delta));
|
||||
}
|
||||
|
||||
boolean moving(){
|
||||
|
||||
@@ -62,8 +62,8 @@ abstract class WaterMoveComp implements Posc, Velc, Hitboxc, Flyingc, Unitc{
|
||||
|
||||
Draw.z(Layer.debris);
|
||||
|
||||
Floor floor = floorOn();
|
||||
Color color = Tmp.c1.set(floor.mapColor).mul(1.5f);
|
||||
Floor floor = tileOn() == null ? Blocks.air.asFloor() : tileOn().floor();
|
||||
Color color = Tmp.c1.set(floor.mapColor.equals(Color.black) ? Blocks.water.mapColor : floor.mapColor).mul(1.5f);
|
||||
trailColor.lerp(color, Mathf.clamp(Time.delta * 0.04f));
|
||||
|
||||
tleft.draw(trailColor, type.trailScl);
|
||||
|
||||
@@ -113,7 +113,8 @@ abstract class WeaponsComp implements Teamc, Posc, Rotc, Velc{
|
||||
can && //must be able to shoot
|
||||
(ammo > 0 || !state.rules.unitAmmo || team().rules().infiniteAmmo) && //check ammo
|
||||
(!weapon.alternate || mount.side == weapon.flipSprite) &&
|
||||
vel.len() >= mount.weapon.minShootVelocity && //check velocity requirements
|
||||
//TODO checking for velocity this way isn't entirely correct
|
||||
(vel.len() >= mount.weapon.minShootVelocity || (net.active() && !isLocal())) && //check velocity requirements
|
||||
mount.reload <= 0.0001f && //reload has to be 0
|
||||
Angles.within(weapon.rotate ? mount.rotation : this.rotation, mount.targetRotation, mount.weapon.shootCone) //has to be within the cone
|
||||
){
|
||||
|
||||
@@ -2,7 +2,9 @@ package mindustry.entities.units;
|
||||
|
||||
import arc.math.*;
|
||||
import arc.math.geom.*;
|
||||
import arc.util.ArcAnnotate.*;
|
||||
import arc.util.*;
|
||||
import mindustry.*;
|
||||
import mindustry.entities.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.type.*;
|
||||
@@ -33,6 +35,10 @@ public class AIController implements UnitController{
|
||||
updateMovement();
|
||||
}
|
||||
|
||||
protected UnitCommand command(){
|
||||
return unit.team.data().command;
|
||||
}
|
||||
|
||||
protected void updateMovement(){
|
||||
|
||||
}
|
||||
@@ -115,6 +121,10 @@ public class AIController implements UnitController{
|
||||
|
||||
}
|
||||
|
||||
protected @Nullable Tile getClosestSpawner(){
|
||||
return Geometry.findClosest(unit.x, unit.y, Vars.spawner.getSpawns());
|
||||
}
|
||||
|
||||
protected void circle(Position target, float circleLength){
|
||||
circle(target, circleLength, unit.type().speed);
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ package mindustry.entities.units;
|
||||
import arc.*;
|
||||
|
||||
public enum UnitCommand{
|
||||
attack, retreat, rally, idle;
|
||||
attack, rally, idle;
|
||||
|
||||
private final String localized;
|
||||
public static final UnitCommand[] all = values();
|
||||
|
||||
@@ -35,6 +35,12 @@ public class DefaultWaves{
|
||||
max = 4;
|
||||
}},
|
||||
|
||||
new SpawnGroup(UnitTypes.pulsar){{
|
||||
begin = 13;
|
||||
spacing = 3;
|
||||
unitScaling = 0.5f;
|
||||
}},
|
||||
|
||||
new SpawnGroup(UnitTypes.mace){{
|
||||
begin = 7;
|
||||
spacing = 3;
|
||||
|
||||
@@ -30,7 +30,12 @@ public class EventType{
|
||||
openWiki,
|
||||
teamCoreDamage,
|
||||
socketConfigChanged,
|
||||
update
|
||||
update,
|
||||
draw,
|
||||
preDraw,
|
||||
postDraw,
|
||||
uiDrawBegin,
|
||||
uiDrawEnd
|
||||
}
|
||||
|
||||
public static class WinEvent{}
|
||||
@@ -77,7 +82,6 @@ public class EventType{
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static class CommandIssueEvent{
|
||||
public final Building tile;
|
||||
public final UnitCommand command;
|
||||
@@ -106,27 +110,6 @@ public class EventType{
|
||||
}
|
||||
}
|
||||
|
||||
/** Called when a zone's requirements are met. */
|
||||
public static class ZoneRequireCompleteEvent{
|
||||
public final SectorPreset zoneMet, zoneForMet;
|
||||
public final Objectives.Objective objective;
|
||||
|
||||
public ZoneRequireCompleteEvent(SectorPreset zoneMet, SectorPreset zoneForMet, Objectives.Objective objective){
|
||||
this.zoneMet = zoneMet;
|
||||
this.zoneForMet = zoneForMet;
|
||||
this.objective = objective;
|
||||
}
|
||||
}
|
||||
|
||||
/** Called when a zone's requirements are met. */
|
||||
public static class ZoneConfigureCompleteEvent{
|
||||
public final SectorPreset zone;
|
||||
|
||||
public ZoneConfigureCompleteEvent(SectorPreset zone){
|
||||
this.zone = zone;
|
||||
}
|
||||
}
|
||||
|
||||
/** Called when a sector is conquered, e.g. a boss or base is defeated. */
|
||||
public static class SectorCaptureEvent{
|
||||
public final Sector sector;
|
||||
|
||||
@@ -42,6 +42,10 @@ public class Rules{
|
||||
public boolean schematicAllowed = true;
|
||||
/** Whether copying is allowed */
|
||||
public boolean copyAllowed = true;
|
||||
/** Whether friendly explosions can occur and set fire/damage other blocks. */
|
||||
public boolean damageExplosions = true;
|
||||
/** Whether fire is enabled. */
|
||||
public boolean fire = true;
|
||||
/** Whether units use and require ammo. */
|
||||
public boolean unitAmmo = false;
|
||||
/** How fast unit pads build units. */
|
||||
|
||||
@@ -23,14 +23,14 @@ import java.util.*;
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class Saves{
|
||||
private Seq<SaveSlot> saves = new Seq<>();
|
||||
private @Nullable SaveSlot current;
|
||||
Seq<SaveSlot> saves = new Seq<>();
|
||||
@Nullable SaveSlot current;
|
||||
private @Nullable SaveSlot lastSectorSave;
|
||||
private AsyncExecutor previewExecutor = new AsyncExecutor(1);
|
||||
AsyncExecutor previewExecutor = new AsyncExecutor(1);
|
||||
private boolean saving;
|
||||
private float time;
|
||||
|
||||
private long totalPlaytime;
|
||||
long totalPlaytime;
|
||||
private long lastTimestamp;
|
||||
|
||||
public Saves(){
|
||||
|
||||
@@ -102,7 +102,7 @@ public class Teams{
|
||||
}
|
||||
|
||||
public void registerCore(CoreBuild core){
|
||||
TeamData data = get(core.team());
|
||||
TeamData data = get(core.team);
|
||||
//add core if not present
|
||||
if(!data.cores.contains(core)){
|
||||
data.cores.add(core);
|
||||
@@ -117,7 +117,7 @@ public class Teams{
|
||||
}
|
||||
|
||||
public void unregisterCore(CoreBuild entity){
|
||||
TeamData data = get(entity.team());
|
||||
TeamData data = get(entity.team);
|
||||
//remove core
|
||||
data.cores.remove(entity);
|
||||
//unregister in active list
|
||||
@@ -181,7 +181,7 @@ public class Teams{
|
||||
|
||||
/** @return whether this team is controlled by the AI and builds bases. */
|
||||
public boolean hasAI(){
|
||||
return state.rules.attackMode && team.rules().ai;
|
||||
return team.rules().ai;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -23,9 +23,9 @@ public class Tutorial{
|
||||
private static final int mineCopper = 18;
|
||||
private static final int blocksToBreak = 3, blockOffset = -6;
|
||||
|
||||
private ObjectSet<String> events = new ObjectSet<>();
|
||||
private ObjectIntMap<Block> blocksPlaced = new ObjectIntMap<>();
|
||||
private int sentence;
|
||||
ObjectSet<String> events = new ObjectSet<>();
|
||||
ObjectIntMap<Block> blocksPlaced = new ObjectIntMap<>();
|
||||
int sentence;
|
||||
public TutorialStage stage = TutorialStage.values()[0];
|
||||
|
||||
public Tutorial(){
|
||||
|
||||
@@ -140,7 +140,7 @@ public class Universe{
|
||||
sector.setSecondsPassed(sector.getSecondsPassed() + actuallyPassed);
|
||||
|
||||
//check if the sector has been attacked too many times...
|
||||
if(sector.hasBase() && sector.getSecondsPassed() * 60f > turnDuration * sectorDestructionTurns){
|
||||
if(sector.hasBase() && sector.hasWaves() && sector.getSecondsPassed() * 60f > turnDuration * sectorDestructionTurns){
|
||||
//fire event for losing the sector
|
||||
Events.fire(new SectorLoseEvent(sector));
|
||||
|
||||
|
||||
@@ -249,7 +249,7 @@ public class BlockRenderer implements Disposable{
|
||||
Draw.z(Layer.block);
|
||||
}
|
||||
|
||||
if(entity.team() != player.team()){
|
||||
if(entity.team != player.team()){
|
||||
entity.drawTeam();
|
||||
Draw.z(Layer.block);
|
||||
}
|
||||
|
||||
@@ -256,5 +256,8 @@ public class FloorRenderer implements Disposable{
|
||||
/** Maps cache layer ID to cache ID in the batch.
|
||||
* -1 means that this cache is unoccupied. */
|
||||
int[] caches = new int[CacheLayer.all.length];
|
||||
|
||||
Chunk(){
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,9 @@ public class Layer{
|
||||
|
||||
public static final float
|
||||
|
||||
//min layer
|
||||
min = -11,
|
||||
|
||||
//background, which may be planets or an image or nothing at all
|
||||
background = -10,
|
||||
|
||||
@@ -81,7 +84,10 @@ public class Layer{
|
||||
end = 200,
|
||||
|
||||
//things after pixelation - used for text
|
||||
endPixeled = 210
|
||||
endPixeled = 210,
|
||||
|
||||
//max layer
|
||||
max = 220
|
||||
|
||||
;
|
||||
}
|
||||
|
||||
@@ -111,7 +111,7 @@ public class OverlayRenderer{
|
||||
if(dst < state.rules.enemyCoreBuildRadius * 2.2f){
|
||||
Draw.color(Color.darkGray);
|
||||
Lines.circle(core.x, core.y - 2, state.rules.enemyCoreBuildRadius);
|
||||
Draw.color(Pal.accent, core.team().color, 0.5f + Mathf.absin(Time.time(), 10f, 0.5f));
|
||||
Draw.color(Pal.accent, core.team.color, 0.5f + Mathf.absin(Time.time(), 10f, 0.5f));
|
||||
Lines.circle(core.x, core.y, state.rules.enemyCoreBuildRadius);
|
||||
}
|
||||
});
|
||||
@@ -134,8 +134,11 @@ public class OverlayRenderer{
|
||||
Vec2 vec = Core.input.mouseWorld(input.getMouseX(), input.getMouseY());
|
||||
Building tile = world.buildWorld(vec.x, vec.y);
|
||||
|
||||
if(tile != null && tile.team() == player.team()){
|
||||
if(tile != null && tile.team == player.team()){
|
||||
tile.drawSelect();
|
||||
if(!tile.enabled && tile.block.drawDisabled){
|
||||
tile.drawDisabled();
|
||||
}
|
||||
|
||||
if(Core.input.keyDown(Binding.rotateplaced) && tile.block().rotate && tile.interactable(player.team())){
|
||||
control.input.drawArrow(tile.block(), tile.tileX(), tile.tileY(), tile.rotation, true);
|
||||
|
||||
@@ -9,6 +9,8 @@ public class Pal{
|
||||
command = Color.valueOf("eab678"),
|
||||
|
||||
sap = Color.valueOf("665c9f"),
|
||||
sapBullet = Color.valueOf("bf92f9"),
|
||||
sapBulletBack = Color.valueOf("6d56bf"),
|
||||
|
||||
spore = Color.valueOf("7457ce"),
|
||||
|
||||
|
||||
@@ -8,7 +8,8 @@ import arc.input.*;
|
||||
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.mouseForward),
|
||||
mouse_move(KeyCode.mouseBack),
|
||||
pan(KeyCode.mouseForward),
|
||||
|
||||
boost(KeyCode.shiftLeft),
|
||||
control(KeyCode.controlLeft),
|
||||
|
||||
@@ -27,24 +27,35 @@ import static mindustry.Vars.*;
|
||||
import static mindustry.input.PlaceMode.*;
|
||||
|
||||
public class DesktopInput extends InputHandler{
|
||||
private Vec2 movement = new Vec2();
|
||||
public Vec2 movement = new Vec2();
|
||||
/** Current cursor type. */
|
||||
private Cursor cursorType = SystemCursor.arrow;
|
||||
public Cursor cursorType = SystemCursor.arrow;
|
||||
/** Position where the player started dragging a line. */
|
||||
private int selectX, selectY, schemX, schemY;
|
||||
public int selectX, selectY, schemX, schemY;
|
||||
/** Last known line positions.*/
|
||||
private int lastLineX, lastLineY, schematicX, schematicY;
|
||||
public int lastLineX, lastLineY, schematicX, schematicY;
|
||||
/** Whether selecting mode is active. */
|
||||
private PlaceMode mode;
|
||||
public PlaceMode mode;
|
||||
/** Animation scale for line. */
|
||||
private float selectScale;
|
||||
public float selectScale;
|
||||
/** Selected build request for movement. */
|
||||
private @Nullable BuildPlan sreq;
|
||||
public @Nullable BuildPlan sreq;
|
||||
/** Whether player is currently deleting removal requests. */
|
||||
private boolean deleting = false, shouldShoot = false;
|
||||
public boolean deleting = false, shouldShoot = false, panning = false;
|
||||
/** Mouse pan speed. */
|
||||
public float panScale = 0.005f, panSpeed = 4.5f, panBoostSpeed = 9f;
|
||||
|
||||
@Override
|
||||
public void buildUI(Group group){
|
||||
group.fill(t -> {
|
||||
t.visible(() -> Core.settings.getBool("hints") && !player.dead() && !player.unit().spawnedByCore() && !(Core.settings.getBool("hints") && lastSchematic != null && !selectRequests.isEmpty()));
|
||||
t.bottom();
|
||||
t.table(Styles.black6, b -> {
|
||||
b.defaults().left();
|
||||
b.label(() -> Core.bundle.format("respawn", Core.keybinds.get(Binding.respawn).key.toString())).style(Styles.outlineLabel);
|
||||
}).margin(6f);
|
||||
});
|
||||
|
||||
group.fill(t -> {
|
||||
t.bottom();
|
||||
t.visible(() -> {
|
||||
@@ -77,15 +88,6 @@ public class DesktopInput extends InputHandler{
|
||||
});
|
||||
}).margin(6f);
|
||||
});
|
||||
|
||||
group.fill(t -> {
|
||||
t.visible(() -> Core.settings.getBool("hints") && !player.dead() && !player.unit().spawnedByCore());
|
||||
t.bottom();
|
||||
t.table(Styles.black6, b -> {
|
||||
b.defaults().left();
|
||||
b.label(() -> Core.bundle.format("respawn", Core.keybinds.get(Binding.respawn).key.toString())).style(Styles.outlineLabel);
|
||||
}).margin(6f);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -132,14 +134,12 @@ public class DesktopInput extends InputHandler{
|
||||
}
|
||||
|
||||
//draw schematic requests
|
||||
for(BuildPlan request : selectRequests){
|
||||
request.animScale = 1f;
|
||||
drawRequest(request);
|
||||
}
|
||||
selectRequests.each(req -> {
|
||||
req.animScale = 1f;
|
||||
drawRequest(req);
|
||||
});
|
||||
|
||||
for(BuildPlan request : selectRequests){
|
||||
drawOverRequest(request);
|
||||
}
|
||||
selectRequests.each(this::drawOverRequest);
|
||||
|
||||
if(player.isBuilder()){
|
||||
//draw things that may be placed soon
|
||||
@@ -180,22 +180,35 @@ public class DesktopInput extends InputHandler{
|
||||
ui.listfrag.toggle();
|
||||
}
|
||||
|
||||
//TODO awful UI state checking code
|
||||
if((player.dead() || state.isPaused()) && !ui.chatfrag.shown()){
|
||||
if(!(scene.getKeyboardFocus() instanceof TextField) && !scene.hasDialog()){
|
||||
//move camera around
|
||||
float camSpeed = !Core.input.keyDown(Binding.boost) ? 3f : 8f;
|
||||
Core.camera.position.add(Tmp.v1.setZero().add(Core.input.axis(Binding.move_x), Core.input.axis(Binding.move_y)).nor().scl(Time.delta * camSpeed));
|
||||
boolean panCam = false;
|
||||
float camSpeed = !Core.input.keyDown(Binding.boost) ? panSpeed : panBoostSpeed;
|
||||
|
||||
if(Core.input.keyDown(Binding.mouse_move)){
|
||||
Core.camera.position.x += Mathf.clamp((Core.input.mouseX() - Core.graphics.getWidth() / 2f) * 0.005f, -1, 1) * camSpeed;
|
||||
Core.camera.position.y += Mathf.clamp((Core.input.mouseY() - Core.graphics.getHeight() / 2f) * 0.005f, -1, 1) * camSpeed;
|
||||
}
|
||||
if(input.keyDown(Binding.pan)){
|
||||
panCam = true;
|
||||
panning = true;
|
||||
}
|
||||
|
||||
if((Math.abs(Core.input.axis(Binding.move_x)) > 0 || Math.abs(Core.input.axis(Binding.move_y)) > 0 || input.keyDown(Binding.mouse_move)) && (!scene.hasField())){
|
||||
panning = false;
|
||||
}
|
||||
|
||||
//TODO awful UI state checking code
|
||||
if(((player.dead() || state.isPaused()) && !ui.chatfrag.shown()) && (!scene.hasField() && !scene.hasDialog())){
|
||||
if(input.keyDown(Binding.mouse_move)){
|
||||
panCam = true;
|
||||
}
|
||||
}else if(!player.dead()){
|
||||
panning = false;
|
||||
|
||||
Core.camera.position.add(Tmp.v1.setZero().add(Core.input.axis(Binding.move_x), Core.input.axis(Binding.move_y)).nor().scl(Time.delta * camSpeed));
|
||||
}else if(!player.dead() && !panning){
|
||||
Core.camera.position.lerpDelta(player, Core.settings.getBool("smoothcamera") ? 0.08f : 1f);
|
||||
}
|
||||
|
||||
if(panCam){
|
||||
Core.camera.position.x += Mathf.clamp((Core.input.mouseX() - Core.graphics.getWidth() / 2f) * panScale, -1, 1) * camSpeed;
|
||||
Core.camera.position.y += Mathf.clamp((Core.input.mouseY() - Core.graphics.getHeight() / 2f) * panScale, -1, 1) * camSpeed;
|
||||
}
|
||||
|
||||
shouldShoot = !scene.hasMouse();
|
||||
|
||||
if(!scene.hasMouse()){
|
||||
@@ -327,22 +340,25 @@ public class DesktopInput extends InputHandler{
|
||||
table.row();
|
||||
table.left().margin(0f).defaults().size(48f).left();
|
||||
|
||||
//TODO localize these
|
||||
|
||||
table.button(Icon.paste, Styles.clearPartiali, () -> {
|
||||
ui.schematics.show();
|
||||
}).tooltip("Schematics").disabled(d -> !state.rules.schematicAllowed);
|
||||
|
||||
}).tooltip("@schematics").disabled(d -> !state.rules.schematicAllowed);
|
||||
|
||||
table.button(Icon.tree, Styles.clearPartiali, () -> {
|
||||
ui.research.show();
|
||||
}).visible(() -> state.isCampaign()).tooltip("Research");
|
||||
}).visible(() -> state.isCampaign()).tooltip("@research");
|
||||
|
||||
table.button(Icon.map, Styles.clearPartiali, () -> {
|
||||
ui.planet.show();
|
||||
}).visible(() -> state.isCampaign()).tooltip("Planet Map");
|
||||
}).visible(() -> state.isCampaign()).tooltip("@planetmap");
|
||||
|
||||
table.button(Icon.up, Styles.clearPartiali, () -> {
|
||||
ui.planet.show(state.getSector(), player.team().core());
|
||||
}).visible(() -> state.isCampaign())
|
||||
.disabled(b -> player.team().core() == null || !player.team().core().items.has(player.team().core().block.requirements)).tooltip("Launch Core");
|
||||
.disabled(b -> player.team().core() == null || !player.team().core().items.has(player.team().core().block.requirements)).tooltip("@launchcore");
|
||||
}
|
||||
|
||||
void pollInput(){
|
||||
@@ -561,6 +577,8 @@ public class DesktopInput extends InputHandler{
|
||||
|
||||
@Override
|
||||
public void updateState(){
|
||||
super.updateState();
|
||||
|
||||
if(state.isMenu()){
|
||||
droppingItem = false;
|
||||
mode = none;
|
||||
@@ -580,7 +598,7 @@ public class DesktopInput extends InputHandler{
|
||||
//limit speed to minimum formation speed to preserve formation
|
||||
if(unit instanceof Commanderc && ((Commanderc)unit).isCommanding()){
|
||||
//add a tiny multiplier to let units catch up just in case
|
||||
baseSpeed = ((Commanderc)unit).minFormationSpeed() * 0.98f;
|
||||
baseSpeed = ((Commanderc)unit).minFormationSpeed() * 0.95f;
|
||||
}
|
||||
|
||||
float speed = baseSpeed * Mathf.lerp(1f, unit.type().canBoost ? unit.type().boostMultiplier : 1f, unit.elevation) * strafePenalty;
|
||||
@@ -599,8 +617,8 @@ public class DesktopInput extends InputHandler{
|
||||
if(aimCursor){
|
||||
unit.lookAt(mouseAngle);
|
||||
}else{
|
||||
if(unit.moving()){
|
||||
unit.lookAt(unit.vel().angle());
|
||||
if(!movement.isZero()){
|
||||
unit.lookAt(unit.vel.isZero() ? movement.angle() : unit.vel.angle());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -622,7 +640,6 @@ public class DesktopInput extends InputHandler{
|
||||
|
||||
//update payload input
|
||||
if(unit instanceof Payloadc){
|
||||
Payloadc pay = (Payloadc)unit;
|
||||
|
||||
if(Core.input.keyTap(Binding.pickupCargo)){
|
||||
tryPickupPayload();
|
||||
|
||||
@@ -14,7 +14,6 @@ import arc.scene.ui.layout.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.ArcAnnotate.*;
|
||||
import arc.util.*;
|
||||
import mindustry.ai.formations.*;
|
||||
import mindustry.ai.formations.patterns.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.content.*;
|
||||
@@ -63,13 +62,13 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
|
||||
public boolean isBuilding = true, buildWasAutoPaused = false;
|
||||
public @Nullable UnitType controlledType;
|
||||
|
||||
protected @Nullable Schematic lastSchematic;
|
||||
protected GestureDetector detector;
|
||||
protected PlaceLine line = new PlaceLine();
|
||||
protected BuildPlan resultreq;
|
||||
protected BuildPlan brequest = new BuildPlan();
|
||||
protected Seq<BuildPlan> lineRequests = new Seq<>();
|
||||
protected Seq<BuildPlan> selectRequests = new Seq<>();
|
||||
public @Nullable Schematic lastSchematic;
|
||||
public GestureDetector detector;
|
||||
public PlaceLine line = new PlaceLine();
|
||||
public BuildPlan resultreq;
|
||||
public BuildPlan brequest = new BuildPlan();
|
||||
public Seq<BuildPlan> lineRequests = new Seq<>();
|
||||
public Seq<BuildPlan> selectRequests = new Seq<>();
|
||||
|
||||
//methods to override
|
||||
|
||||
@@ -108,6 +107,8 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
|
||||
|
||||
@Remote(targets = Loc.both, called = Loc.server, forward = true)
|
||||
public static void pickupUnitPayload(Player player, Unit target){
|
||||
if(player == null) return;
|
||||
|
||||
Unit unit = player.unit();
|
||||
Payloadc pay = (Payloadc)unit;
|
||||
|
||||
@@ -120,10 +121,12 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
|
||||
|
||||
@Remote(targets = Loc.both, called = Loc.server, forward = true)
|
||||
public static void pickupBlockPayload(Player player, Building tile){
|
||||
if(player == null) return;
|
||||
|
||||
Unit unit = player.unit();
|
||||
Payloadc pay = (Payloadc)unit;
|
||||
|
||||
if(tile != null && tile.team() == unit.team && pay.payloads().size < unit.type().payloadCapacity
|
||||
if(tile != null && tile.team == unit.team && pay.payloads().size < unit.type().payloadCapacity
|
||||
&& unit.within(tile, tilesize * tile.block.size * 1.2f)){
|
||||
//pick up block directly
|
||||
if(tile.block().buildVisibility != BuildVisibility.hidden && tile.block().size <= 2 && tile.canPickup()){
|
||||
@@ -143,6 +146,8 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
|
||||
|
||||
@Remote(targets = Loc.both, called = Loc.server, forward = true)
|
||||
public static void dropPayload(Player player, float x, float y){
|
||||
if(player == null) return;
|
||||
|
||||
Payloadc pay = (Payloadc)player.unit();
|
||||
|
||||
//allow a slight margin of error
|
||||
@@ -156,6 +161,8 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
|
||||
|
||||
@Remote(targets = Loc.client, called = Loc.server)
|
||||
public static void dropItem(Player player, float angle){
|
||||
if(player == null) return;
|
||||
|
||||
if(net.server() && player.unit().stack.amount <= 0){
|
||||
throw new ValidateException(player, "Player cannot drop an item.");
|
||||
}
|
||||
@@ -179,6 +186,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
|
||||
@Remote(targets = Loc.both, forward = true, called = Loc.server)
|
||||
public static void transferInventory(Player player, Building tile){
|
||||
if(player == null || tile == null) return;
|
||||
|
||||
if(net.server() && (player.unit().stack.amount <= 0 || !Units.canInteract(player, tile) ||
|
||||
!netServer.admins.allowAction(player, ActionType.depositItem, tile.tile(), action -> {
|
||||
action.itemAmount = player.unit().stack.amount;
|
||||
@@ -217,6 +225,8 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
|
||||
|
||||
@Remote(targets = Loc.both, called = Loc.both, forward = true)
|
||||
public static void unitControl(Player player, @Nullable Unit unit){
|
||||
if(player == null) return;
|
||||
|
||||
//clear player unit when they possess a core
|
||||
if((unit instanceof BlockUnitc && ((BlockUnitc)unit).tile() instanceof CoreBuild)){
|
||||
Fx.spawn.at(player);
|
||||
@@ -226,10 +236,12 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
|
||||
|
||||
player.clearUnit();
|
||||
player.deathTimer = 61f;
|
||||
((CoreBuild)((BlockUnitc)unit).tile()).requestSpawn(player);
|
||||
|
||||
}else if(unit == null){ //just clear the unit (is this used?)
|
||||
player.clearUnit();
|
||||
//make sure it's AI controlled, so players can't overwrite each other
|
||||
}else if(unit.isAI() && unit.team == player.team() && !unit.deactivated()){
|
||||
}else if(unit.isAI() && unit.team == player.team() && !unit.deactivated() && !unit.dead){
|
||||
player.unit(unit);
|
||||
Time.run(Fx.unitSpirit.lifetime, () -> Fx.unitControl.at(unit.x, unit.y, 0f, unit));
|
||||
if(!player.dead()){
|
||||
@@ -257,23 +269,9 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
|
||||
if(commander.isCommanding()){
|
||||
commander.clearCommand();
|
||||
}else{
|
||||
FormationPattern pattern = new SquareFormation();
|
||||
Formation formation = new Formation(new Vec3(player.x, player.y, player.unit().rotation), pattern);
|
||||
formation.slotAssignmentStrategy = new DistanceAssignmentStrategy(pattern);
|
||||
|
||||
units.clear();
|
||||
|
||||
commander.commandNearby(new SquareFormation());
|
||||
Fx.commandSend.at(player);
|
||||
Units.nearby(player.team(), player.x, player.y, 200f, u -> {
|
||||
if(u.isAI()){
|
||||
units.add(u);
|
||||
}
|
||||
});
|
||||
|
||||
units.sort(u -> u.dst2(player.unit()));
|
||||
units.truncate(player.unit().type().commandLimit);
|
||||
|
||||
commander.command(formation, units);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -306,7 +304,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
|
||||
}
|
||||
|
||||
if(controlledType != null && player.dead()){
|
||||
Unit unit = Units.closest(player.team(), player.x, player.y, u -> !u.isPlayer() && u.type() == controlledType && !u.deactivated());
|
||||
Unit unit = Units.closest(player.team(), player.x, player.y, u -> !u.isPlayer() && u.type() == controlledType && !u.deactivated() && !u.dead);
|
||||
|
||||
if(unit != null){
|
||||
Call.unitControl(player, unit);
|
||||
@@ -316,7 +314,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
|
||||
|
||||
public void checkUnit(){
|
||||
if(controlledType != null){
|
||||
Unit unit = Units.closest(player.team(), player.x, player.y, u -> !u.isPlayer() && u.type() == controlledType && !u.deactivated());
|
||||
Unit unit = Units.closest(player.team(), player.x, player.y, u -> !u.isPlayer() && u.type() == controlledType && !u.deactivated() && !u.dead);
|
||||
if(unit == null && controlledType == UnitTypes.block){
|
||||
unit = world.buildWorld(player.x, player.y) instanceof ControlBlock ? ((ControlBlock)world.buildWorld(player.x, player.y)).unit() : null;
|
||||
}
|
||||
@@ -376,7 +374,9 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
|
||||
}
|
||||
|
||||
public void updateState(){
|
||||
|
||||
if(state.isMenu()){
|
||||
controlledType = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void drawBottom(){
|
||||
@@ -542,11 +542,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
|
||||
if(test.get(req)) return req;
|
||||
}
|
||||
|
||||
for(BuildPlan req : selectRequests){
|
||||
if(test.get(req)) return req;
|
||||
}
|
||||
|
||||
return null;
|
||||
return selectRequests.find(test);
|
||||
}
|
||||
|
||||
protected void drawBreakSelection(int x1, int y1, int x2, int y2){
|
||||
@@ -877,7 +873,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
|
||||
}
|
||||
|
||||
Building tile = world.buildWorld(Core.input.mouseWorld().x, Core.input.mouseWorld().y);
|
||||
if(tile instanceof ControlBlock && tile.team() == player.team()){
|
||||
if(tile instanceof ControlBlock && tile.team == player.team()){
|
||||
return ((ControlBlock)tile).unit();
|
||||
}
|
||||
|
||||
|
||||
@@ -31,39 +31,39 @@ public class MobileInput extends InputHandler implements GestureListener{
|
||||
/** Maximum speed the player can pan. */
|
||||
private static final float maxPanSpeed = 1.3f;
|
||||
/** Distance to edge of screen to start panning. */
|
||||
private final float edgePan = Scl.scl(60f);
|
||||
public final float edgePan = Scl.scl(60f);
|
||||
|
||||
//gesture data
|
||||
private Vec2 vector = new Vec2(), movement = new Vec2(), targetPos = new Vec2();
|
||||
private float lastZoom = -1;
|
||||
public Vec2 vector = new Vec2(), movement = new Vec2(), targetPos = new Vec2();
|
||||
public float lastZoom = -1;
|
||||
|
||||
/** Position where the player started dragging a line. */
|
||||
private int lineStartX, lineStartY, lastLineX, lastLineY;
|
||||
public int lineStartX, lineStartY, lastLineX, lastLineY;
|
||||
|
||||
/** Animation scale for line. */
|
||||
private float lineScale;
|
||||
public float lineScale;
|
||||
/** Animation data for crosshair. */
|
||||
private float crosshairScale;
|
||||
private Teamc lastTarget;
|
||||
public float crosshairScale;
|
||||
public Teamc lastTarget;
|
||||
/** Used for shifting build requests. */
|
||||
private float shiftDeltaX, shiftDeltaY;
|
||||
public float shiftDeltaX, shiftDeltaY;
|
||||
|
||||
/** Place requests to be removed. */
|
||||
private Seq<BuildPlan> removals = new Seq<>();
|
||||
public Seq<BuildPlan> removals = new Seq<>();
|
||||
/** Whether or not the player is currently shifting all placed tiles. */
|
||||
private boolean selecting;
|
||||
public boolean selecting;
|
||||
/** Whether the player is currently in line-place mode. */
|
||||
private boolean lineMode, schematicMode;
|
||||
public boolean lineMode, schematicMode;
|
||||
/** Current place mode. */
|
||||
private PlaceMode mode = none;
|
||||
public PlaceMode mode = none;
|
||||
/** Whether no recipe was available when switching to break mode. */
|
||||
private Block lastBlock;
|
||||
public Block lastBlock;
|
||||
/** Last placed request. Used for drawing block overlay. */
|
||||
private BuildPlan lastPlaced;
|
||||
public BuildPlan lastPlaced;
|
||||
/** Down tracking for panning.*/
|
||||
private boolean down = false;
|
||||
public boolean down = false;
|
||||
|
||||
private Teamc target, moveTarget;
|
||||
public Teamc target, moveTarget;
|
||||
|
||||
//region utility methods
|
||||
|
||||
@@ -77,7 +77,7 @@ public class MobileInput extends InputHandler implements GestureListener{
|
||||
}else{
|
||||
Building tile = world.buildWorld(x, y);
|
||||
|
||||
if(tile != null && player.team().isEnemy(tile.team())){
|
||||
if(tile != null && player.team().isEnemy(tile.team)){
|
||||
player.miner().mineTile(null);
|
||||
target = tile;
|
||||
}else if(tile != null && player.unit().type().canHeal && tile.team == player.team() && tile.damaged()){
|
||||
@@ -559,7 +559,9 @@ public class MobileInput extends InputHandler implements GestureListener{
|
||||
if(cursor == null || Core.scene.hasMouse(x, y)) return false;
|
||||
Tile linked = cursor.build == null ? cursor : cursor.build.tile();
|
||||
|
||||
checkTargets(worldx, worldy);
|
||||
if(!player.dead()){
|
||||
checkTargets(worldx, worldy);
|
||||
}
|
||||
|
||||
//remove if request present
|
||||
if(hasRequest(cursor)){
|
||||
@@ -854,19 +856,9 @@ public class MobileInput extends InputHandler implements GestureListener{
|
||||
movement.set(targetPos).sub(player).limit(speed);
|
||||
movement.setAngle(Mathf.slerp(movement.angle(), unit.vel.angle(), 0.05f));
|
||||
|
||||
//pathfind for ground units
|
||||
if(!flying && !type.canBoost && !(unit instanceof WaterMovec)){
|
||||
Tile on = unit.tileOn();
|
||||
if(on != null && !on.solid()){
|
||||
Tile to = pathfinder.getTargetTile(unit.tileOn(), unit.team, targetPos);
|
||||
if(to != null){
|
||||
movement.set(to).sub(unit).setLength(speed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(player.within(targetPos, attractDst)){
|
||||
movement.setZero();
|
||||
unit.vel.approachDelta(Vec2.ZERO, type.speed * type.accel / 2f);
|
||||
}
|
||||
|
||||
float expansion = 3f;
|
||||
|
||||
@@ -68,7 +68,7 @@ public class JsonIO{
|
||||
return json.prettyPrint(in);
|
||||
}
|
||||
|
||||
private static void apply(Json json){
|
||||
static void apply(Json json){
|
||||
json.setIgnoreUnknownFields(true);
|
||||
json.setElementType(Rules.class, "spawns", SpawnGroup.class);
|
||||
json.setElementType(Rules.class, "loadout", ItemStack.class);
|
||||
|
||||
@@ -34,7 +34,7 @@ public abstract class SaveFileReader{
|
||||
"titan-factory", "legacy-unit-factory",
|
||||
"fortress-factory", "legacy-unit-factory",
|
||||
|
||||
"command-center", "legacy-command-center"
|
||||
"mass-conveyor", "payload-conveyor"
|
||||
);
|
||||
|
||||
protected int lastRegionLength;
|
||||
|
||||
@@ -92,6 +92,9 @@ public class TypeIO{
|
||||
write.b((byte)14);
|
||||
write.i(((byte[])object).length);
|
||||
write.b((byte[])object);
|
||||
}else if(object instanceof UnitCommand){
|
||||
write.b((byte)15);
|
||||
write.b(((UnitCommand)object).ordinal());
|
||||
}else{
|
||||
throw new IllegalArgumentException("Unknown object type: " + object.getClass());
|
||||
}
|
||||
@@ -116,6 +119,7 @@ public class TypeIO{
|
||||
case 12: return world.build(read.i());
|
||||
case 13: return LAccess.all[read.s()];
|
||||
case 14: int blen = read.i(); byte[] bytes = new byte[blen]; read.b(bytes); return bytes;
|
||||
case 15: return UnitCommand.all[read.b()];
|
||||
default: throw new IllegalArgumentException("Unknown object type: " + type);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,10 +74,10 @@ public abstract class LegacySaveVersion extends SaveVersion{
|
||||
tile.setTeam(Team.get(team));
|
||||
tile.build.rotation = rotation;
|
||||
|
||||
if(tile.build.items != null) tile.build.items.read(Reads.get(stream));
|
||||
if(tile.build.power != null) tile.build.power.read(Reads.get(stream));
|
||||
if(tile.build.liquids != null) tile.build.liquids.read(Reads.get(stream));
|
||||
if(tile.build.cons != null) tile.build.cons.read(Reads.get(stream));
|
||||
if(tile.build.items != null) tile.build.items.read(Reads.get(stream), true);
|
||||
if(tile.build.power != null) tile.build.power.read(Reads.get(stream), true);
|
||||
if(tile.build.liquids != null) tile.build.liquids.read(Reads.get(stream), true);
|
||||
if(tile.build.cons != null) tile.build.cons.read(Reads.get(stream), true);
|
||||
|
||||
//read only from subclasses!
|
||||
tile.build.read(Reads.get(in), version);
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
package mindustry.logic;
|
||||
|
||||
import arc.math.*;
|
||||
|
||||
public enum BinaryOp{
|
||||
add("+", (a, b) -> a + b),
|
||||
sub("-", (a, b) -> a - b),
|
||||
mul("*", (a, b) -> a * b),
|
||||
div("/", (a, b) -> a / b),
|
||||
mod("%", (a, b) -> a % b),
|
||||
equal("==", (a, b) -> Math.abs(a - b) < 0.000001 ? 1 : 0),
|
||||
notEqual("not", (a, b) -> Math.abs(a - b) < 0.000001 ? 0 : 1),
|
||||
lessThan("<", (a, b) -> a < b ? 1 : 0),
|
||||
lessThanEq("<=", (a, b) -> a <= b ? 1 : 0),
|
||||
greaterThan(">", (a, b) -> a > b ? 1 : 0),
|
||||
greaterThanEq(">=", (a, b) -> a >= b ? 1 : 0),
|
||||
pow("^", Math::pow),
|
||||
shl(">>", (a, b) -> (int)a >> (int)b),
|
||||
shr("<<", (a, b) -> (int)a << (int)b),
|
||||
or("or", (a, b) -> (int)a | (int)b),
|
||||
and("and", (a, b) -> (int)a & (int)b),
|
||||
xor("xor", (a, b) -> (int)a ^ (int)b),
|
||||
max("max", Math::max),
|
||||
min("min", Math::min),
|
||||
atan2("atan2", (x, y) -> Mathf.atan2((float)x, (float)y) * Mathf.radDeg),
|
||||
dst("dst", (x, y) -> Mathf.dst((float)x, (float)y));
|
||||
|
||||
public static final BinaryOp[] all = values();
|
||||
|
||||
public final OpLambda function;
|
||||
public final String symbol;
|
||||
|
||||
BinaryOp(String symbol, OpLambda function){
|
||||
this.symbol = symbol;
|
||||
this.function = function;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(){
|
||||
return symbol;
|
||||
}
|
||||
|
||||
interface OpLambda{
|
||||
double get(double a, double b);
|
||||
}
|
||||
}
|
||||
@@ -96,7 +96,7 @@ public class LAssembler{
|
||||
if(c == '"'){
|
||||
inString = !inString;
|
||||
}else if(c == ' ' && !inString){
|
||||
tokens.add(line.substring(lastIdx, i).replace("\\n", "\n"));
|
||||
tokens.add(line.substring(lastIdx, i));
|
||||
lastIdx = i + 1;
|
||||
}
|
||||
}
|
||||
@@ -106,6 +106,32 @@ public class LAssembler{
|
||||
arr = new String[]{line};
|
||||
}
|
||||
|
||||
String type = arr[0];
|
||||
|
||||
//legacy stuff
|
||||
if(type.equals("bop")){
|
||||
arr[0] = "op";
|
||||
|
||||
//field order for bop used to be op a, b, result, but now it's op result a b
|
||||
String res = arr[4];
|
||||
arr[4] = arr[3];
|
||||
arr[3] = arr[2];
|
||||
arr[2] = res;
|
||||
}else if(type.equals("uop")){
|
||||
arr[0] = "op";
|
||||
|
||||
if(arr[1].equals("negate")){
|
||||
arr = new String[]{
|
||||
"op", "mul", arr[3], arr[2], "-1"
|
||||
};
|
||||
}else{
|
||||
//field order for uop used to be op a, result, but now it's op result a
|
||||
String res = arr[3];
|
||||
arr[3] = arr[2];
|
||||
arr[2] = res;
|
||||
}
|
||||
}
|
||||
|
||||
LStatement st = LogicIO.read(arr);
|
||||
|
||||
if(st != null){
|
||||
@@ -121,6 +147,7 @@ public class LAssembler{
|
||||
}
|
||||
}
|
||||
}catch(Exception parseFailed){
|
||||
parseFailed.printStackTrace();
|
||||
//when parsing fails, add a dummy invalid statement
|
||||
statements.add(new InvalidStatement());
|
||||
}
|
||||
@@ -135,7 +162,7 @@ public class LAssembler{
|
||||
|
||||
//string case
|
||||
if(symbol.startsWith("\"") && symbol.endsWith("\"")){
|
||||
return putConst("___" + symbol, symbol.substring(1, symbol.length() - 1)).id;
|
||||
return putConst("___" + symbol, symbol.substring(1, symbol.length() - 1).replace("\\n", "\n")).id;
|
||||
}
|
||||
|
||||
try{
|
||||
|
||||
@@ -20,9 +20,8 @@ import mindustry.ui.*;
|
||||
import mindustry.world.blocks.logic.*;
|
||||
|
||||
public class LCanvas extends Table{
|
||||
private static final Color backgroundCol = Pal.darkMetal.cpy().mul(0.1f), gridCol = Pal.darkMetal.cpy().mul(0.5f);
|
||||
private static Seq<Runnable> postDraw = new Seq<>();
|
||||
private Vec2 offset = new Vec2();
|
||||
static Seq<Runnable> postDraw = new Seq<>();
|
||||
static Seq<Runnable> postDrawPriority = new Seq<>();
|
||||
|
||||
DragLayout statements;
|
||||
StatementElem dragging;
|
||||
@@ -91,11 +90,27 @@ public class LCanvas extends Table{
|
||||
this.statements.layout();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void act(float delta){
|
||||
super.act(delta);
|
||||
|
||||
if(Core.input.isTouched()){
|
||||
float y = Core.input.mouseY();
|
||||
float dst = Math.min(y - this.y, Core.graphics.getHeight() - y);
|
||||
if(dst < Scl.scl(100f)){ //scroll margin
|
||||
int sign = Mathf.sign(Core.graphics.getHeight()/2f - y);
|
||||
pane.setScrollY(pane.getScrollY() + sign * Scl.scl(15f));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(){
|
||||
postDraw.clear();
|
||||
postDrawPriority.clear();
|
||||
super.draw();
|
||||
postDraw.each(Runnable::run);
|
||||
postDrawPriority.each(Runnable::run);
|
||||
}
|
||||
|
||||
public class DragLayout extends WidgetGroup{
|
||||
@@ -252,7 +267,7 @@ public class LCanvas extends Table{
|
||||
return false;
|
||||
}
|
||||
|
||||
Vec2 v = localToStageCoordinates(Tmp.v1.set(x, y));
|
||||
Vec2 v = localToParentCoordinates(Tmp.v1.set(x, y));
|
||||
lastx = v.x;
|
||||
lasty = v.y;
|
||||
dragging = StatementElem.this;
|
||||
@@ -263,7 +278,7 @@ public class LCanvas extends Table{
|
||||
|
||||
@Override
|
||||
public void touchDragged(InputEvent event, float x, float y, int pointer){
|
||||
Vec2 v = localToStageCoordinates(Tmp.v1.set(x, y));
|
||||
Vec2 v = localToParentCoordinates(Tmp.v1.set(x, y));
|
||||
|
||||
translation.add(v.x - lastx, v.y - lasty);
|
||||
lastx = v.x;
|
||||
@@ -317,16 +332,18 @@ public class LCanvas extends Table{
|
||||
}
|
||||
|
||||
public static class JumpButton extends ImageButton{
|
||||
Color hoverColor = Pal.place;
|
||||
Color defaultColor = Color.white;
|
||||
@NonNull Prov<StatementElem> to;
|
||||
boolean selecting;
|
||||
float mx, my;
|
||||
ClickListener listener;
|
||||
|
||||
public JumpButton(Color color, @NonNull Prov<StatementElem> getter, Cons<StatementElem> setter){
|
||||
public JumpButton(@NonNull Prov<StatementElem> getter, Cons<StatementElem> setter){
|
||||
super(Tex.logicNode, Styles.colori);
|
||||
|
||||
to = getter;
|
||||
|
||||
getStyle().imageUpColor = color;
|
||||
addListener(listener = new ClickListener());
|
||||
|
||||
addListener(new InputListener(){
|
||||
@Override
|
||||
@@ -362,6 +379,9 @@ public class LCanvas extends Table{
|
||||
if(to.get() != null && to.get().parent == null){
|
||||
setter.get(null);
|
||||
}
|
||||
|
||||
setColor(listener.isOver() ? hoverColor : defaultColor);
|
||||
getStyle().imageUpColor = this.color;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -369,7 +389,7 @@ public class LCanvas extends Table{
|
||||
public void draw(){
|
||||
super.draw();
|
||||
|
||||
postDraw.add(() -> {
|
||||
(listener.isOver() ? postDrawPriority : postDraw).add(() -> {
|
||||
Element hover = to.get() == null && selecting ? hovered() : to.get();
|
||||
float tx = 0, ty = 0;
|
||||
boolean draw = false;
|
||||
@@ -402,10 +422,12 @@ public class LCanvas extends Table{
|
||||
}
|
||||
|
||||
if(draw){
|
||||
drawCurve(rx + width/2f, ry + height/2f, tx, ty, color);
|
||||
drawCurve(rx + width/2f, ry + height/2f, tx, ty);
|
||||
|
||||
float s = width;
|
||||
Draw.color(color);
|
||||
Tex.logicNode.draw(tx + s*0.75f, ty - s/2f, -s, s);
|
||||
Draw.reset();
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -421,7 +443,7 @@ public class LCanvas extends Table{
|
||||
return (StatementElem)e;
|
||||
}
|
||||
|
||||
void drawCurve(float x, float y, float x2, float y2, Color color){
|
||||
void drawCurve(float x, float y, float x2, float y2){
|
||||
Lines.stroke(4f, color);
|
||||
Draw.alpha(parentAlpha);
|
||||
|
||||
@@ -434,8 +456,6 @@ public class LCanvas extends Table{
|
||||
x2, y2,
|
||||
Math.max(20, (int)(Mathf.dst(x, y, x2, y2) / 5))
|
||||
);
|
||||
|
||||
Draw.reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ public enum LCategory{
|
||||
blocks(Pal.accentBack),
|
||||
control(Color.cyan.cpy().shiftSaturation(-0.6f).mul(0.7f)),
|
||||
operations(Pal.place.cpy().shiftSaturation(-0.5f).mul(0.7f)),
|
||||
io(Pal.remove.cpy().shiftSaturation(-0.5f).mul(0.7f));;
|
||||
io(Pal.remove.cpy().shiftSaturation(-0.5f).mul(0.7f));
|
||||
|
||||
public final Color color;
|
||||
|
||||
|
||||
@@ -21,7 +21,8 @@ public class LExecutor{
|
||||
varTime = 1;
|
||||
|
||||
public static final int
|
||||
maxGraphicsBuffer = 512,
|
||||
maxGraphicsBuffer = 256,
|
||||
maxDisplayBuffer = 512,
|
||||
maxTextBuffer = 256;
|
||||
|
||||
public LInstruction[] instructions = {};
|
||||
@@ -177,9 +178,7 @@ public class LExecutor{
|
||||
public void run(LExecutor exec){
|
||||
int address = exec.numi(index);
|
||||
|
||||
if(address >= 0 && address < exec.links.length){
|
||||
exec.setobj(output, exec.links[address]);
|
||||
}
|
||||
exec.setobj(output, address >= 0 && address < exec.links.length ? exec.links[address] : null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -379,40 +378,26 @@ public class LExecutor{
|
||||
}
|
||||
}
|
||||
|
||||
public static class BinaryOpI implements LInstruction{
|
||||
public BinaryOp op = BinaryOp.add;
|
||||
public static class OpI implements LInstruction{
|
||||
public LogicOp op = LogicOp.add;
|
||||
public int a, b, dest;
|
||||
|
||||
public BinaryOpI(BinaryOp op, int a, int b, int dest){
|
||||
public OpI(LogicOp op, int a, int b, int dest){
|
||||
this.op = op;
|
||||
this.a = a;
|
||||
this.b = b;
|
||||
this.dest = dest;
|
||||
}
|
||||
|
||||
BinaryOpI(){}
|
||||
OpI(){}
|
||||
|
||||
@Override
|
||||
public void run(LExecutor exec){
|
||||
exec.setnum(dest, op.function.get(exec.num(a), exec.num(b)));
|
||||
}
|
||||
}
|
||||
|
||||
public static class UnaryOpI implements LInstruction{
|
||||
public UnaryOp op = UnaryOp.negate;
|
||||
public int value, dest;
|
||||
|
||||
public UnaryOpI(UnaryOp op, int value, int dest){
|
||||
this.op = op;
|
||||
this.value = value;
|
||||
this.dest = dest;
|
||||
}
|
||||
|
||||
UnaryOpI(){}
|
||||
|
||||
@Override
|
||||
public void run(LExecutor exec){
|
||||
exec.setnum(dest, op.function.get(exec.num(value)));
|
||||
if(op.unary){
|
||||
exec.setnum(dest, op.function1.get(exec.num(a)));
|
||||
}else{
|
||||
exec.setnum(dest, op.function2.get(exec.num(a), exec.num(b)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -478,8 +463,10 @@ public class LExecutor{
|
||||
Building build = exec.building(target);
|
||||
if(build instanceof LogicDisplayBuild){
|
||||
LogicDisplayBuild d = (LogicDisplayBuild)build;
|
||||
for(int i = 0; i < exec.graphicsBuffer.size; i++){
|
||||
d.commands.addLast(exec.graphicsBuffer.items[i]);
|
||||
if(d.commands.size + exec.graphicsBuffer.size < maxDisplayBuffer){
|
||||
for(int i = 0; i < exec.graphicsBuffer.size; i++){
|
||||
d.commands.addLast(exec.graphicsBuffer.items[i]);
|
||||
}
|
||||
}
|
||||
exec.graphicsBuffer.clear();
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package mindustry.logic;
|
||||
|
||||
import arc.func.*;
|
||||
import arc.graphics.*;
|
||||
import arc.scene.style.*;
|
||||
import arc.scene.ui.*;
|
||||
import arc.scene.ui.layout.*;
|
||||
@@ -56,15 +55,23 @@ public class LStatements{
|
||||
}
|
||||
}
|
||||
|
||||
@RegisterStatement("getlink")
|
||||
public static class GetLinkStatement extends LStatement{
|
||||
public String output = "result", address = "0";
|
||||
@RegisterStatement("read")
|
||||
public static class ReadStatement extends LStatement{
|
||||
public String output = "result", target = "cell1", address = "0";
|
||||
|
||||
@Override
|
||||
public void build(Table table){
|
||||
table.add(" read ");
|
||||
|
||||
field(table, output, str -> output = str);
|
||||
|
||||
table.add(" = link# ");
|
||||
table.add(" = ");
|
||||
|
||||
fields(table, target, str -> target = str);
|
||||
|
||||
row(table);
|
||||
|
||||
table.add(" at ");
|
||||
|
||||
field(table, address, str -> address = str);
|
||||
}
|
||||
@@ -76,7 +83,7 @@ public class LStatements{
|
||||
|
||||
@Override
|
||||
public LInstruction build(LAssembler builder){
|
||||
return new GetLinkI(builder.var(output), builder.var(address));
|
||||
return new ReadI(builder.var(target), builder.var(address), builder.var(output));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,38 +119,6 @@ public class LStatements{
|
||||
}
|
||||
}
|
||||
|
||||
@RegisterStatement("read")
|
||||
public static class ReadStatement extends LStatement{
|
||||
public String output = "result", target = "cell1", address = "0";
|
||||
|
||||
@Override
|
||||
public void build(Table table){
|
||||
table.add(" read ");
|
||||
|
||||
field(table, output, str -> output = str);
|
||||
|
||||
table.add(" = ");
|
||||
|
||||
fields(table, target, str -> target = str);
|
||||
|
||||
row(table);
|
||||
|
||||
table.add(" at ");
|
||||
|
||||
field(table, address, str -> address = str);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LCategory category(){
|
||||
return LCategory.io;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LInstruction build(LAssembler builder){
|
||||
return new ReadI(builder.var(target), builder.var(address), builder.var(output));
|
||||
}
|
||||
}
|
||||
|
||||
@RegisterStatement("draw")
|
||||
public static class DrawStatement extends LStatement{
|
||||
public GraphicsType type = GraphicsType.clear;
|
||||
@@ -253,6 +228,26 @@ public class LStatements{
|
||||
}
|
||||
}
|
||||
|
||||
@RegisterStatement("print")
|
||||
public static class PrintStatement extends LStatement{
|
||||
public String value = "\"frog\"";
|
||||
|
||||
@Override
|
||||
public void build(Table table){
|
||||
field(table, value, str -> value = str).width(0f).growX().padRight(3);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LInstruction build(LAssembler builder){
|
||||
return new PrintI(builder.var(value));
|
||||
}
|
||||
|
||||
@Override
|
||||
public LCategory category(){
|
||||
return LCategory.io;
|
||||
}
|
||||
}
|
||||
|
||||
@RegisterStatement("drawflush")
|
||||
public static class DrawFlushStatement extends LStatement{
|
||||
public String target = "display1";
|
||||
@@ -274,26 +269,6 @@ public class LStatements{
|
||||
}
|
||||
}
|
||||
|
||||
@RegisterStatement("print")
|
||||
public static class PrintStatement extends LStatement{
|
||||
public String value = "\"frog\"";
|
||||
|
||||
@Override
|
||||
public void build(Table table){
|
||||
field(table, value, str -> value = str).width(0f).growX().padRight(3);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LInstruction build(LAssembler builder){
|
||||
return new PrintI(builder.var(value));
|
||||
}
|
||||
|
||||
@Override
|
||||
public LCategory category(){
|
||||
return LCategory.control;
|
||||
}
|
||||
}
|
||||
|
||||
@RegisterStatement("printflush")
|
||||
public static class PrintFlushStatement extends LStatement{
|
||||
public String target = "message1";
|
||||
@@ -315,6 +290,30 @@ public class LStatements{
|
||||
}
|
||||
}
|
||||
|
||||
@RegisterStatement("getlink")
|
||||
public static class GetLinkStatement extends LStatement{
|
||||
public String output = "result", address = "0";
|
||||
|
||||
@Override
|
||||
public void build(Table table){
|
||||
field(table, output, str -> output = str);
|
||||
|
||||
table.add(" = link# ");
|
||||
|
||||
field(table, address, str -> address = str);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LCategory category(){
|
||||
return LCategory.blocks;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LInstruction build(LAssembler builder){
|
||||
return new GetLinkI(builder.var(output), builder.var(address));
|
||||
}
|
||||
}
|
||||
|
||||
@RegisterStatement("control")
|
||||
public static class ControlStatement extends LStatement{
|
||||
public LAccess type = LAccess.enabled;
|
||||
@@ -372,7 +371,7 @@ public class LStatements{
|
||||
public static class RadarStatement extends LStatement{
|
||||
public RadarTarget target1 = RadarTarget.enemy, target2 = RadarTarget.any, target3 = RadarTarget.any;
|
||||
public RadarSort sort = RadarSort.distance;
|
||||
public String radar = "turret1", sortOrder = "0", output = "result";
|
||||
public String radar = "turret1", sortOrder = "1", output = "result";
|
||||
|
||||
@Override
|
||||
public void build(Table table){
|
||||
@@ -561,62 +560,51 @@ public class LStatements{
|
||||
}
|
||||
}
|
||||
|
||||
@RegisterStatement("bop")
|
||||
public static class BinaryOpStatement extends LStatement{
|
||||
public BinaryOp op = BinaryOp.add;
|
||||
public String a = "a", b = "b", dest = "result";
|
||||
@RegisterStatement("op")
|
||||
public static class OperationStatement extends LStatement{
|
||||
public LogicOp op = LogicOp.add;
|
||||
public String dest = "result", a = "a", b = "b";
|
||||
|
||||
@Override
|
||||
public void build(Table table){
|
||||
rebuild(table);
|
||||
}
|
||||
|
||||
void rebuild(Table table){
|
||||
table.clearChildren();
|
||||
|
||||
field(table, dest, str -> dest = str);
|
||||
|
||||
table.add(" = ");
|
||||
|
||||
row(table);
|
||||
if(op.unary){
|
||||
opButton(table);
|
||||
|
||||
field(table, a, str -> a = str);
|
||||
field(table, a, str -> a = str);
|
||||
}else{
|
||||
row(table);
|
||||
|
||||
field(table, a, str -> a = str);
|
||||
|
||||
opButton(table);
|
||||
|
||||
field(table, b, str -> b = str);
|
||||
}
|
||||
}
|
||||
|
||||
void opButton(Table table){
|
||||
table.button(b -> {
|
||||
b.label(() -> op.symbol);
|
||||
b.clicked(() -> showSelect(b, BinaryOp.all, op, o -> op = o));
|
||||
b.clicked(() -> showSelect(b, LogicOp.all, op, o -> {
|
||||
op = o;
|
||||
rebuild(table);
|
||||
}));
|
||||
}, Styles.logict, () -> {}).size(60f, 40f).pad(4f).color(table.color);
|
||||
|
||||
field(table, b, str -> b = str);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LInstruction build(LAssembler builder){
|
||||
return new BinaryOpI(op,builder.var(a), builder.var(b), builder.var(dest));
|
||||
}
|
||||
|
||||
@Override
|
||||
public LCategory category(){
|
||||
return LCategory.operations;
|
||||
}
|
||||
}
|
||||
|
||||
@RegisterStatement("uop")
|
||||
public static class UnaryOpStatement extends LStatement{
|
||||
public UnaryOp op = UnaryOp.negate;
|
||||
public String value = "b", dest = "result";
|
||||
|
||||
@Override
|
||||
public void build(Table table){
|
||||
field(table, dest, str -> dest = str);
|
||||
|
||||
table.add(" = ");
|
||||
|
||||
table.button(b -> {
|
||||
b.label(() -> op.symbol);
|
||||
b.clicked(() -> showSelect(b, UnaryOp.all, op, o -> op = o));
|
||||
}, Styles.logict, () -> {}).size(50f, 40f).pad(3f).color(table.color);
|
||||
|
||||
field(table, value, str -> value = str);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LInstruction build(LAssembler builder){
|
||||
return new UnaryOpI(op, builder.var(value), builder.var(dest));
|
||||
return new OpI(op,builder.var(a), builder.var(b), builder.var(dest));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -666,7 +654,7 @@ public class LStatements{
|
||||
field(table, compare, str -> compare = str);
|
||||
|
||||
table.add().growX();
|
||||
table.add(new JumpButton(Color.white, () -> dest, s -> dest = s)).size(30).right().padLeft(-8);
|
||||
table.add(new JumpButton(() -> dest, s -> dest = s)).size(30).right().padLeft(-8);
|
||||
}
|
||||
|
||||
//elements need separate conversion logic
|
||||
|
||||
@@ -36,7 +36,7 @@ public class LogicDialog extends BaseDialog{
|
||||
t.button("@schematic.copy.import", Icon.download, style, () -> {
|
||||
dialog.hide();
|
||||
try{
|
||||
canvas.load(Core.app.getClipboardText());
|
||||
canvas.load(Core.app.getClipboardText().replace("\r\n", "\n"));
|
||||
}catch(Throwable e){
|
||||
ui.showException(e);
|
||||
}
|
||||
@@ -61,7 +61,12 @@ public class LogicDialog extends BaseDialog{
|
||||
for(Prov<LStatement> prov : LogicIO.allStatements){
|
||||
LStatement example = prov.get();
|
||||
if(example instanceof InvalidStatement) continue;
|
||||
t.button(example.name(), Styles.cleart, () -> {
|
||||
|
||||
TextButtonStyle style = new TextButtonStyle(Styles.cleart);
|
||||
style.fontColor = example.category().color;
|
||||
style.font = Fonts.outline;
|
||||
|
||||
t.button(example.name(), style, () -> {
|
||||
canvas.add(prov.get());
|
||||
dialog.hide();
|
||||
}).size(140f, 50f);
|
||||
|
||||
75
core/src/mindustry/logic/LogicOp.java
Normal file
75
core/src/mindustry/logic/LogicOp.java
Normal file
@@ -0,0 +1,75 @@
|
||||
package mindustry.logic;
|
||||
|
||||
import arc.math.*;
|
||||
|
||||
public enum LogicOp{
|
||||
add("+", (a, b) -> a + b),
|
||||
sub("-", (a, b) -> a - b),
|
||||
mul("*", (a, b) -> a * b),
|
||||
div("/", (a, b) -> a / b),
|
||||
mod("%", (a, b) -> a % b),
|
||||
equal("==", (a, b) -> Math.abs(a - b) < 0.000001 ? 1 : 0),
|
||||
notEqual("not", (a, b) -> Math.abs(a - b) < 0.000001 ? 0 : 1),
|
||||
lessThan("<", (a, b) -> a < b ? 1 : 0),
|
||||
lessThanEq("<=", (a, b) -> a <= b ? 1 : 0),
|
||||
greaterThan(">", (a, b) -> a > b ? 1 : 0),
|
||||
greaterThanEq(">=", (a, b) -> a >= b ? 1 : 0),
|
||||
pow("^", Math::pow),
|
||||
shl(">>", (a, b) -> (int)a >> (int)b),
|
||||
shr("<<", (a, b) -> (int)a << (int)b),
|
||||
or("or", (a, b) -> (int)a | (int)b),
|
||||
and("and", (a, b) -> (int)a & (int)b),
|
||||
xor("xor", (a, b) -> (int)a ^ (int)b),
|
||||
max("max", Math::max),
|
||||
min("min", Math::min),
|
||||
atan2("atan2", (x, y) -> Mathf.atan2((float)x, (float)y) * Mathf.radDeg),
|
||||
dst("dst", (x, y) -> Mathf.dst((float)x, (float)y)),
|
||||
|
||||
not("not", a -> ~(int)(a)),
|
||||
abs("abs", a -> Math.abs(a)),
|
||||
log("log", Math::log),
|
||||
log10("log10", Math::log10),
|
||||
sin("sin", d -> Math.sin(d * 0.017453292519943295D)),
|
||||
cos("cos", d -> Math.cos(d * 0.017453292519943295D)),
|
||||
tan("tan", d -> Math.tan(d * 0.017453292519943295D)),
|
||||
floor("floor", Math::floor),
|
||||
ceil("ceil", Math::ceil),
|
||||
sqrt("sqrt", Math::sqrt),
|
||||
rand("rand", d -> Mathf.rand.nextDouble() * d),
|
||||
|
||||
;
|
||||
|
||||
public static final LogicOp[] all = values();
|
||||
|
||||
public final OpLambda2 function2;
|
||||
public final OpLambda1 function1;
|
||||
public final boolean unary;
|
||||
public final String symbol;
|
||||
|
||||
LogicOp(String symbol, OpLambda2 function){
|
||||
this.symbol = symbol;
|
||||
this.function2 = function;
|
||||
this.function1 = null;
|
||||
this.unary = false;
|
||||
}
|
||||
|
||||
LogicOp(String symbol, OpLambda1 function){
|
||||
this.symbol = symbol;
|
||||
this.function1 = function;
|
||||
this.function2 = null;
|
||||
this.unary = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(){
|
||||
return symbol;
|
||||
}
|
||||
|
||||
interface OpLambda2{
|
||||
double get(double a, double b);
|
||||
}
|
||||
|
||||
interface OpLambda1{
|
||||
double get(double a);
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
package mindustry.logic;
|
||||
|
||||
import arc.math.*;
|
||||
|
||||
public enum UnaryOp{
|
||||
negate("-", a -> -a),
|
||||
not("not", a -> ~(int)(a)),
|
||||
abs("abs", Math::abs),
|
||||
log("log", Math::log),
|
||||
log10("log10", Math::log10),
|
||||
sin("sin", d -> Math.sin(d * 0.017453292519943295D)),
|
||||
cos("cos", d -> Math.cos(d * 0.017453292519943295D)),
|
||||
tan("tan", d -> Math.tan(d * 0.017453292519943295D)),
|
||||
floor("floor", Math::floor),
|
||||
ceil("ceil", Math::ceil),
|
||||
sqrt("sqrt", Math::sqrt),
|
||||
rand("rand", d -> Mathf.rand.nextDouble() * d),
|
||||
;
|
||||
|
||||
public static final UnaryOp[] all = values();
|
||||
|
||||
public final UnaryOpLambda function;
|
||||
public final String symbol;
|
||||
|
||||
UnaryOp(String symbol, UnaryOpLambda function){
|
||||
this.symbol = symbol;
|
||||
this.function = function;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(){
|
||||
return symbol;
|
||||
}
|
||||
|
||||
interface UnaryOpLambda{
|
||||
double get(double a);
|
||||
}
|
||||
}
|
||||
@@ -32,7 +32,7 @@ public class Maps{
|
||||
/** List of all built-in maps. Filenames only. */
|
||||
private static String[] defaultMapNames = {"maze", "fortress", "labyrinth", "islands", "tendrils", "caldera", "wasteland", "shattered", "fork", "triad", "veins", "glacier"};
|
||||
/** Maps tagged as PvP */
|
||||
private static final String[] pvpMaps = {"veins", "glacier"};
|
||||
static final String[] pvpMaps = {"veins", "glacier"};
|
||||
/** All maps stored in an ordered array. */
|
||||
private Seq<Map> maps = new Seq<>();
|
||||
/** Serializer for meta. */
|
||||
|
||||
@@ -35,9 +35,9 @@ import java.lang.reflect.*;
|
||||
@SuppressWarnings("unchecked")
|
||||
public class ContentParser{
|
||||
private static final boolean ignoreUnknownFields = true;
|
||||
private ObjectMap<Class<?>, ContentType> contentTypes = new ObjectMap<>();
|
||||
ObjectMap<Class<?>, ContentType> contentTypes = new ObjectMap<>();
|
||||
|
||||
private ObjectMap<Class<?>, FieldParser> classParsers = new ObjectMap<Class<?>, FieldParser>(){{
|
||||
ObjectMap<Class<?>, FieldParser> classParsers = new ObjectMap<Class<?>, FieldParser>(){{
|
||||
put(Effect.class, (type, data) -> field(Fx.class, data));
|
||||
put(Schematic.class, (type, data) -> {
|
||||
Object result = fieldOpt(Loadouts.class, data);
|
||||
@@ -105,7 +105,7 @@ public class ContentParser{
|
||||
private Seq<Runnable> reads = new Seq<>();
|
||||
private Seq<Runnable> postreads = new Seq<>();
|
||||
private ObjectSet<Object> toBeParsed = new ObjectSet<>();
|
||||
private LoadedMod currentMod;
|
||||
LoadedMod currentMod;
|
||||
private Content currentContent;
|
||||
|
||||
private Json parser = new Json(){
|
||||
@@ -480,7 +480,7 @@ public class ContentParser{
|
||||
return first != null ? first : Vars.content.getByName(type, currentMod.name + "-" + name);
|
||||
}
|
||||
|
||||
private <T> T make(Class<T> type){
|
||||
<T> T make(Class<T> type){
|
||||
try{
|
||||
Constructor<T> cons = type.getDeclaredConstructor();
|
||||
cons.setAccessible(true);
|
||||
@@ -515,7 +515,7 @@ public class ContentParser{
|
||||
}
|
||||
}
|
||||
|
||||
private Object field(Class<?> type, JsonValue value){
|
||||
Object field(Class<?> type, JsonValue value){
|
||||
return field(type, value.asString());
|
||||
}
|
||||
|
||||
@@ -530,7 +530,7 @@ public class ContentParser{
|
||||
}
|
||||
}
|
||||
|
||||
private Object fieldOpt(Class<?> type, JsonValue value){
|
||||
Object fieldOpt(Class<?> type, JsonValue value){
|
||||
try{
|
||||
return type.getField(value.asString()).get(null);
|
||||
}catch(Exception e){
|
||||
@@ -538,7 +538,7 @@ public class ContentParser{
|
||||
}
|
||||
}
|
||||
|
||||
private void checkNullFields(Object object){
|
||||
void checkNullFields(Object object){
|
||||
if(object instanceof Number || object instanceof String || toBeParsed.contains(object)) return;
|
||||
|
||||
parser.getFields(object.getClass()).values().toSeq().each(field -> {
|
||||
@@ -559,7 +559,7 @@ public class ContentParser{
|
||||
readFields(object, jsonMap);
|
||||
}
|
||||
|
||||
private void readFields(Object object, JsonValue jsonMap){
|
||||
void readFields(Object object, JsonValue jsonMap){
|
||||
toBeParsed.remove(object);
|
||||
Class type = object.getClass();
|
||||
ObjectMap<String, FieldMetadata> fields = parser.getFields(type);
|
||||
@@ -593,7 +593,7 @@ public class ContentParser{
|
||||
}
|
||||
|
||||
/** Tries to resolve a class from a list of potential class names. */
|
||||
private <T> Class<T> resolve(String base, String... potentials){
|
||||
<T> Class<T> resolve(String base, String... potentials){
|
||||
if(!base.isEmpty() && Character.isLowerCase(base.charAt(0))) base = Strings.capitalize(base);
|
||||
|
||||
for(String type : potentials){
|
||||
|
||||
79
core/src/mindustry/mod/ModLoadingMusic.java
Normal file
79
core/src/mindustry/mod/ModLoadingMusic.java
Normal file
@@ -0,0 +1,79 @@
|
||||
package mindustry.mod;
|
||||
|
||||
import arc.audio.*;
|
||||
import arc.mock.*;
|
||||
import arc.util.ArcAnnotate.*;
|
||||
|
||||
public class ModLoadingMusic implements Music{
|
||||
public @NonNull Music music = new MockMusic();
|
||||
|
||||
@Override
|
||||
public void play(){
|
||||
music.play();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pause(){
|
||||
music.pause();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop(){
|
||||
music.stop();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPlaying(){
|
||||
return music.isPlaying();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLooping(){
|
||||
return music.isLooping();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLooping(boolean isLooping){
|
||||
music.setLooping(isLooping);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getVolume(){
|
||||
return music.getVolume();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setVolume(float volume){
|
||||
music.setVolume(volume);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPan(float pan, float volume){
|
||||
music.setPan(pan, volume);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getPosition(){
|
||||
return music.getPosition();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPosition(float position){
|
||||
music.setPosition(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose(){
|
||||
music.dispose();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCompletionListener(OnCompletionListener listener){
|
||||
music.setCompletionListener(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDisposed(){
|
||||
return music.isDisposed();
|
||||
}
|
||||
}
|
||||
@@ -39,7 +39,7 @@ public class Mods implements Loadable{
|
||||
private int totalSprites;
|
||||
private MultiPacker packer;
|
||||
|
||||
private Seq<LoadedMod> mods = new Seq<>();
|
||||
Seq<LoadedMod> mods = new Seq<>();
|
||||
private ObjectMap<Class<?>, ModMeta> metas = new ObjectMap<>();
|
||||
private boolean requiresReload, createdAtlas;
|
||||
|
||||
@@ -454,7 +454,7 @@ public class Mods implements Loadable{
|
||||
content.setCurrentMod(null);
|
||||
}
|
||||
|
||||
Log.debug("Time to initialize modded scripts: @", Time.elapsed());
|
||||
Log.info("Time to initialize modded scripts: @", Time.elapsed());
|
||||
}
|
||||
|
||||
/** Creates all the content found in mod files. */
|
||||
@@ -586,58 +586,70 @@ public class Mods implements Loadable{
|
||||
/** Loads a mod file+meta, but does not add it to the list.
|
||||
* Note that directories can be loaded as mods.*/
|
||||
private LoadedMod loadMod(Fi sourceFile) throws Exception{
|
||||
Fi zip = sourceFile.isDirectory() ? sourceFile : new ZipFi(sourceFile);
|
||||
if(zip.list().length == 1 && zip.list()[0].isDirectory()){
|
||||
zip = zip.list()[0];
|
||||
}
|
||||
Time.mark();
|
||||
|
||||
Fi metaf = zip.child("mod.json").exists() ? zip.child("mod.json") : zip.child("mod.hjson").exists() ? zip.child("mod.hjson") : zip.child("plugin.json");
|
||||
if(!metaf.exists()){
|
||||
Log.warn("Mod @ doesn't have a 'mod.json'/'mod.hjson'/'plugin.json' file, skipping.", sourceFile);
|
||||
throw new IllegalArgumentException("Invalid file: No mod.json found.");
|
||||
}
|
||||
ZipFi rootZip = null;
|
||||
|
||||
ModMeta meta = json.fromJson(ModMeta.class, Jval.read(metaf.readString()).toString(Jformat.plain));
|
||||
meta.cleanup();
|
||||
String camelized = meta.name.replace(" ", "");
|
||||
String mainClass = meta.main == null ? camelized.toLowerCase() + "." + camelized + "Mod" : meta.main;
|
||||
String baseName = meta.name.toLowerCase().replace(" ", "-");
|
||||
|
||||
if(mods.contains(m -> m.name.equals(baseName))){
|
||||
throw new IllegalArgumentException("A mod with the name '" + baseName + "' is already imported.");
|
||||
}
|
||||
|
||||
Mod mainMod;
|
||||
|
||||
Fi mainFile = zip;
|
||||
String[] path = (mainClass.replace('.', '/') + ".class").split("/");
|
||||
for(String str : path){
|
||||
if(!str.isEmpty()){
|
||||
mainFile = mainFile.child(str);
|
||||
}
|
||||
}
|
||||
|
||||
//make sure the main class exists before loading it; if it doesn't just don't put it there
|
||||
if(mainFile.exists()){
|
||||
//mobile versions don't support class mods
|
||||
if(mobile){
|
||||
throw new IllegalArgumentException("Java class mods are not supported on mobile.");
|
||||
try{
|
||||
Fi zip = sourceFile.isDirectory() ? sourceFile : (rootZip = new ZipFi(sourceFile));
|
||||
if(zip.list().length == 1 && zip.list()[0].isDirectory()){
|
||||
zip = zip.list()[0];
|
||||
}
|
||||
|
||||
URLClassLoader classLoader = new URLClassLoader(new URL[]{sourceFile.file().toURI().toURL()}, ClassLoader.getSystemClassLoader());
|
||||
Class<?> main = classLoader.loadClass(mainClass);
|
||||
metas.put(main, meta);
|
||||
mainMod = (Mod)main.getDeclaredConstructor().newInstance();
|
||||
}else{
|
||||
mainMod = null;
|
||||
}
|
||||
Fi metaf = zip.child("mod.json").exists() ? zip.child("mod.json") : zip.child("mod.hjson").exists() ? zip.child("mod.hjson") : zip.child("plugin.json");
|
||||
if(!metaf.exists()){
|
||||
Log.warn("Mod @ doesn't have a 'mod.json'/'mod.hjson'/'plugin.json' file, skipping.", sourceFile);
|
||||
throw new IllegalArgumentException("Invalid file: No mod.json found.");
|
||||
}
|
||||
|
||||
//all plugins are hidden implicitly
|
||||
if(mainMod instanceof Plugin){
|
||||
meta.hidden = true;
|
||||
}
|
||||
ModMeta meta = json.fromJson(ModMeta.class, Jval.read(metaf.readString()).toString(Jformat.plain));
|
||||
meta.cleanup();
|
||||
String camelized = meta.name.replace(" ", "");
|
||||
String mainClass = meta.main == null ? camelized.toLowerCase() + "." + camelized + "Mod" : meta.main;
|
||||
String baseName = meta.name.toLowerCase().replace(" ", "-");
|
||||
|
||||
return new LoadedMod(sourceFile, zip, mainMod, meta);
|
||||
if(mods.contains(m -> m.name.equals(baseName))){
|
||||
throw new IllegalArgumentException("A mod with the name '" + baseName + "' is already imported.");
|
||||
}
|
||||
|
||||
Mod mainMod;
|
||||
|
||||
Fi mainFile = zip;
|
||||
String[] path = (mainClass.replace('.', '/') + ".class").split("/");
|
||||
for(String str : path){
|
||||
if(!str.isEmpty()){
|
||||
mainFile = mainFile.child(str);
|
||||
}
|
||||
}
|
||||
|
||||
//make sure the main class exists before loading it; if it doesn't just don't put it there
|
||||
if(mainFile.exists()){
|
||||
//mobile versions don't support class mods
|
||||
if(mobile){
|
||||
throw new IllegalArgumentException("Java class mods are not supported on mobile.");
|
||||
}
|
||||
|
||||
URLClassLoader classLoader = new URLClassLoader(new URL[]{sourceFile.file().toURI().toURL()}, ClassLoader.getSystemClassLoader());
|
||||
Class<?> main = classLoader.loadClass(mainClass);
|
||||
metas.put(main, meta);
|
||||
mainMod = (Mod)main.getDeclaredConstructor().newInstance();
|
||||
}else{
|
||||
mainMod = null;
|
||||
}
|
||||
|
||||
//all plugins are hidden implicitly
|
||||
if(mainMod instanceof Plugin){
|
||||
meta.hidden = true;
|
||||
}
|
||||
|
||||
Log.info("Loaded mod '@' in @", meta.name, Time.elapsed());
|
||||
return new LoadedMod(sourceFile, zip, mainMod, meta);
|
||||
|
||||
}catch(Exception e){
|
||||
//delete root zip file so it can be closed on windows
|
||||
if(rootZip != null) rootZip.delete();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
/** Represents a mod's state. May be a jar file, folder or zip. */
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
package mindustry.mod;
|
||||
|
||||
import arc.*;
|
||||
import arc.assets.*;
|
||||
import arc.audio.*;
|
||||
import arc.files.*;
|
||||
import arc.mock.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import arc.util.Log.*;
|
||||
@@ -23,7 +26,7 @@ public class Scripts implements Disposable{
|
||||
private final Context context;
|
||||
private final Scriptable scope;
|
||||
private boolean errored;
|
||||
private LoadedMod currentMod = null;
|
||||
LoadedMod currentMod = null;
|
||||
|
||||
public Scripts(){
|
||||
Time.mark();
|
||||
@@ -73,7 +76,7 @@ public class Scripts implements Disposable{
|
||||
Log.log(level, "[@]: @", source, message);
|
||||
}
|
||||
|
||||
//utility mod functions
|
||||
//region utility mod functions
|
||||
|
||||
public String readString(String path){
|
||||
return Vars.tree.get(path, true).readString();
|
||||
@@ -83,6 +86,38 @@ public class Scripts implements Disposable{
|
||||
return Vars.tree.get(path, true).readBytes();
|
||||
}
|
||||
|
||||
public Sound loadSound(String soundName){
|
||||
if(Vars.headless) return new MockSound();
|
||||
|
||||
String name = "sounds/" + soundName;
|
||||
String path = Vars.tree.get(name + ".ogg").exists() && !Vars.ios ? name + ".ogg" : name + ".mp3";
|
||||
|
||||
if(Core.assets.contains(path, Sound.class)) return Core.assets.get(path, Sound.class);
|
||||
ModLoadingSound sound = new ModLoadingSound();
|
||||
AssetDescriptor<?> desc = Core.assets.load(path, Sound.class);
|
||||
desc.loaded = result -> sound.sound = (Sound)result;
|
||||
desc.errored = Throwable::printStackTrace;
|
||||
|
||||
return sound;
|
||||
}
|
||||
|
||||
public Music loadMusic(String soundName){
|
||||
if(Vars.headless) return new MockMusic();
|
||||
|
||||
String name = "music/" + soundName;
|
||||
String path = Vars.tree.get(name + ".ogg").exists() && !Vars.ios ? name + ".ogg" : name + ".mp3";
|
||||
|
||||
if(Core.assets.contains(path, Music.class)) return Core.assets.get(path, Music.class);
|
||||
ModLoadingMusic sound = new ModLoadingMusic();
|
||||
AssetDescriptor<?> desc = Core.assets.load(path, Music.class);
|
||||
desc.loaded = result -> sound.music = (Music)result;
|
||||
desc.errored = Throwable::printStackTrace;
|
||||
|
||||
return sound;
|
||||
}
|
||||
|
||||
//endregion
|
||||
|
||||
public void run(LoadedMod mod, Fi file){
|
||||
currentMod = mod;
|
||||
run(file.readString(), file.name(), true);
|
||||
|
||||
@@ -5,6 +5,7 @@ import arc.func.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.ArcAnnotate.*;
|
||||
import arc.util.*;
|
||||
import arc.util.Log.*;
|
||||
import arc.util.pooling.Pool.*;
|
||||
import arc.util.pooling.*;
|
||||
import mindustry.*;
|
||||
@@ -18,13 +19,14 @@ import static mindustry.Vars.*;
|
||||
import static mindustry.game.EventType.*;
|
||||
|
||||
public class Administration{
|
||||
/** All player info. Maps UUIDs to info. This persists throughout restarts. */
|
||||
public Seq<String> bannedIPs = new Seq<>();
|
||||
public Seq<String> whitelist = new Seq<>();
|
||||
public Seq<ChatFilter> chatFilters = new Seq<>();
|
||||
public Seq<ActionFilter> actionFilters = new Seq<>();
|
||||
public Seq<String> subnetBans = new Seq<>();
|
||||
|
||||
/** All player info. Maps UUIDs to info. This persists throughout restarts. Do not access directly. */
|
||||
private ObjectMap<String, PlayerInfo> playerInfo = new ObjectMap<>();
|
||||
private Seq<String> bannedIPs = new Seq<>();
|
||||
private Seq<String> whitelist = new Seq<>();
|
||||
private Seq<ChatFilter> chatFilters = new Seq<>();
|
||||
private Seq<ActionFilter> actionFilters = new Seq<>();
|
||||
private Seq<String> subnetBans = new Seq<>();
|
||||
private IntIntMap lastPlaced = new IntIntMap();
|
||||
|
||||
public Administration(){
|
||||
@@ -72,12 +74,17 @@ public class Administration{
|
||||
});
|
||||
|
||||
//block interaction rate limit
|
||||
//TODO when someone disconnects, a different player is mistakenly kicked for spamming actions
|
||||
addActionFilter(action -> {
|
||||
if(action.type != ActionType.breakBlock &&
|
||||
action.type != ActionType.placeBlock &&
|
||||
Config.antiSpam.bool() &&
|
||||
//make sure players can configure their own stuff, e.g. in schematics
|
||||
lastPlaced.get(action.tile.pos(), -1) != action.player.id()){
|
||||
Config.antiSpam.bool()){
|
||||
|
||||
//make sure players can configure their own stuff, e.g. in schematics - but only once.
|
||||
if(lastPlaced.get(action.tile.pos(), -1) == action.player.id()){
|
||||
lastPlaced.remove(action.tile.pos());
|
||||
return true;
|
||||
}
|
||||
|
||||
Ratekeeper rate = action.player.getInfo().rate;
|
||||
if(rate.allow(Config.interactRateWindow.num() * 1000, Config.interactRateLimit.num())){
|
||||
@@ -572,7 +579,8 @@ public class Administration{
|
||||
motd("The message displayed to people on connection.", "off"),
|
||||
autosave("Whether the periodically save the map when playing.", false),
|
||||
autosaveAmount("The maximum amount of autosaves. Older ones get replaced.", 10),
|
||||
autosaveSpacing("Spacing between autosaves in seconds.", 60 * 5);
|
||||
autosaveSpacing("Spacing between autosaves in seconds.", 60 * 5),
|
||||
debug("Enable debug logging", false, () -> Log.setLogLevel(debug() ? LogLevel.debug : LogLevel.info));
|
||||
|
||||
public static final Config[] all = values();
|
||||
|
||||
@@ -631,6 +639,10 @@ public class Administration{
|
||||
Core.settings.put(key, value);
|
||||
changed.run();
|
||||
}
|
||||
|
||||
private static boolean debug(){
|
||||
return Config.debug.bool();
|
||||
}
|
||||
}
|
||||
|
||||
public static class PlayerInfo{
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
package mindustry.net;
|
||||
|
||||
import arc.*;
|
||||
import arc.struct.*;
|
||||
import arc.func.*;
|
||||
import arc.net.*;
|
||||
import arc.net.FrameworkMessage.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import arc.util.async.*;
|
||||
import arc.util.pooling.*;
|
||||
@@ -28,7 +28,9 @@ public class ArcNetProvider implements NetProvider{
|
||||
Thread serverThread;
|
||||
|
||||
public ArcNetProvider(){
|
||||
client = new Client(8192, 4096, new PacketSerializer());
|
||||
ArcNet.errorHandler = e -> Log.debug(Strings.getStackTrace(e));
|
||||
|
||||
client = new Client(8192, 8192, new PacketSerializer());
|
||||
client.setDiscoveryPacket(packetSupplier);
|
||||
client.addListener(new NetListener(){
|
||||
@Override
|
||||
@@ -66,7 +68,7 @@ public class ArcNetProvider implements NetProvider{
|
||||
}
|
||||
});
|
||||
|
||||
server = new Server(4096 * 2, 4096, new PacketSerializer());
|
||||
server = new Server(8192, 8192, new PacketSerializer());
|
||||
server.setMulticast(multicastGroup, multicastPort);
|
||||
server.setDiscoveryHandler((address, handler) -> {
|
||||
ByteBuffer buffer = NetworkIO.writeServerData();
|
||||
@@ -180,6 +182,7 @@ public class ArcNetProvider implements NetProvider{
|
||||
Threads.daemon(() -> {
|
||||
try{
|
||||
DatagramSocket socket = new DatagramSocket();
|
||||
long time = Time.millis();
|
||||
socket.send(new DatagramPacket(new byte[]{-2, 1}, 2, InetAddress.getByName(address), port));
|
||||
socket.setSoTimeout(2000);
|
||||
|
||||
@@ -187,7 +190,7 @@ public class ArcNetProvider implements NetProvider{
|
||||
socket.receive(packet);
|
||||
|
||||
ByteBuffer buffer = ByteBuffer.wrap(packet.getData());
|
||||
Host host = NetworkIO.readServerData(packet.getAddress().getHostAddress(), buffer);
|
||||
Host host = NetworkIO.readServerData((int)Time.timeSinceMillis(time), packet.getAddress().getHostAddress(), buffer);
|
||||
|
||||
Core.app.post(() -> valid.get(host));
|
||||
}catch(Exception e){
|
||||
@@ -199,6 +202,7 @@ public class ArcNetProvider implements NetProvider{
|
||||
@Override
|
||||
public void discoverServers(Cons<Host> callback, Runnable done){
|
||||
Seq<InetAddress> foundAddresses = new Seq<>();
|
||||
long time = Time.millis();
|
||||
client.discoverHosts(port, multicastGroup, multicastPort, 3000, packet -> {
|
||||
Core.app.post(() -> {
|
||||
try{
|
||||
@@ -206,7 +210,7 @@ public class ArcNetProvider implements NetProvider{
|
||||
return;
|
||||
}
|
||||
ByteBuffer buffer = ByteBuffer.wrap(packet.getData());
|
||||
Host host = NetworkIO.readServerData(packet.getAddress().getHostAddress(), buffer);
|
||||
Host host = NetworkIO.readServerData((int)Time.timeSinceMillis(time), packet.getAddress().getHostAddress(), buffer);
|
||||
callback.get(host);
|
||||
foundAddresses.add(packet.getAddress());
|
||||
}catch(Exception e){
|
||||
@@ -265,7 +269,7 @@ public class ArcNetProvider implements NetProvider{
|
||||
return null;
|
||||
}
|
||||
|
||||
private void handleException(Throwable e){
|
||||
void handleException(Throwable e){
|
||||
if(e instanceof ArcNetException){
|
||||
Core.app.post(() -> net.showError(new IOException("mismatch")));
|
||||
}else if(e instanceof ClosedChannelException){
|
||||
|
||||
@@ -89,31 +89,38 @@ public class BeControl{
|
||||
if(!headless){
|
||||
checkUpdates = false;
|
||||
ui.showCustomConfirm(Core.bundle.format("be.update", "") + " " + updateBuild, "@be.update.confirm", "@ok", "@be.ignore", () -> {
|
||||
boolean[] cancel = {false};
|
||||
float[] progress = {0};
|
||||
int[] length = {0};
|
||||
Fi file = bebuildDirectory.child("client-be-" + updateBuild + ".jar");
|
||||
try{
|
||||
boolean[] cancel = {false};
|
||||
float[] progress = {0};
|
||||
int[] length = {0};
|
||||
Fi file = Fi.get(BeControl.class.getProtectionDomain().getCodeSource().getLocation().toURI().getPath());
|
||||
|
||||
BaseDialog dialog = new BaseDialog("@be.updating");
|
||||
download(updateUrl, file, i -> length[0] = i, v -> progress[0] = v, () -> cancel[0], () -> {
|
||||
try{
|
||||
Runtime.getRuntime().exec(new String[]{"java", "-DlastBuild=" + Version.build, "-Dberestart", "-jar", file.absolutePath()});
|
||||
System.exit(0);
|
||||
}catch(IOException e){
|
||||
BaseDialog dialog = new BaseDialog("@be.updating");
|
||||
download(updateUrl, file, i -> length[0] = i, v -> progress[0] = v, () -> cancel[0], () -> {
|
||||
try{
|
||||
Runtime.getRuntime().exec(OS.isMac ?
|
||||
new String[]{"java", "-XstartOnFirstThread", "-DlastBuild=" + Version.build, "-Dberestart", "-jar", file.absolutePath()} :
|
||||
new String[]{"java", "-DlastBuild=" + Version.build, "-Dberestart", "-jar", file.absolutePath()}
|
||||
);
|
||||
System.exit(0);
|
||||
}catch(IOException e){
|
||||
ui.showException(e);
|
||||
}
|
||||
}, e -> {
|
||||
dialog.hide();
|
||||
ui.showException(e);
|
||||
}
|
||||
}, e -> {
|
||||
dialog.hide();
|
||||
ui.showException(e);
|
||||
});
|
||||
});
|
||||
|
||||
dialog.cont.add(new Bar(() -> length[0] == 0 ? Core.bundle.get("be.updating") : (int)(progress[0] * length[0]) / 1024/ 1024 + "/" + length[0]/1024/1024 + " MB", () -> Pal.accent, () -> progress[0])).width(400f).height(70f);
|
||||
dialog.buttons.button("@cancel", Icon.cancel, () -> {
|
||||
cancel[0] = true;
|
||||
dialog.hide();
|
||||
}).size(210f, 64f);
|
||||
dialog.setFillParent(false);
|
||||
dialog.show();
|
||||
dialog.cont.add(new Bar(() -> length[0] == 0 ? Core.bundle.get("be.updating") : (int)(progress[0] * length[0]) / 1024/ 1024 + "/" + length[0]/1024/1024 + " MB", () -> Pal.accent, () -> progress[0])).width(400f).height(70f);
|
||||
dialog.buttons.button("@cancel", Icon.cancel, () -> {
|
||||
cancel[0] = true;
|
||||
dialog.hide();
|
||||
}).size(210f, 64f);
|
||||
dialog.setFillParent(false);
|
||||
dialog.show();
|
||||
}catch(Exception e){
|
||||
ui.showException(e);
|
||||
}
|
||||
}, () -> checkUpdates = false);
|
||||
}else{
|
||||
Log.info("&lcA new update is available: &lyBleeding Edge build @", updateBuild);
|
||||
@@ -132,6 +139,7 @@ public class BeControl{
|
||||
() -> Core.app.post(() -> {
|
||||
Log.info("&lcSaving...");
|
||||
SaveIO.save(saveDirectory.child("autosavebe." + saveExtension));
|
||||
Log.info("&lcAutosaved.");
|
||||
|
||||
netServer.kickAll(KickReason.serverRestarting);
|
||||
Threads.sleep(32);
|
||||
|
||||
@@ -16,7 +16,8 @@ public class Host{
|
||||
public final @Nullable String modeName;
|
||||
public int ping, port = Vars.port;
|
||||
|
||||
public Host(String name, String address, String mapname, int wave, int players, int version, String versionType, Gamemode mode, int playerLimit, String description, String modeName){
|
||||
public Host(int ping, String name, String address, String mapname, int wave, int players, int version, String versionType, Gamemode mode, int playerLimit, String description, String modeName){
|
||||
this.ping = ping;
|
||||
this.name = name;
|
||||
this.address = address;
|
||||
this.players = players;
|
||||
|
||||
@@ -21,6 +21,7 @@ public abstract class NetConnection{
|
||||
public @Nullable Player player;
|
||||
public @Nullable Unitc lastUnit;
|
||||
public Vec2 lastPosition = new Vec2();
|
||||
public boolean kicked = false;
|
||||
|
||||
/** ID of last received client snapshot. */
|
||||
public int lastReceivedClientSnapshot = -1;
|
||||
@@ -38,6 +39,8 @@ public abstract class NetConnection{
|
||||
|
||||
/** Kick with a special, localized reason. Use this if possible. */
|
||||
public void kick(KickReason reason){
|
||||
if(kicked) return;
|
||||
|
||||
Log.info("Kicking connection @; Reason: @", address, reason.name());
|
||||
|
||||
if((reason == KickReason.kick || reason == KickReason.banned || reason == KickReason.vote)){
|
||||
@@ -51,6 +54,7 @@ public abstract class NetConnection{
|
||||
Time.runTask(2f, this::close);
|
||||
|
||||
netServer.admins.save();
|
||||
kicked = true;
|
||||
}
|
||||
|
||||
/** Kick with an arbitrary reason. */
|
||||
@@ -60,6 +64,8 @@ public abstract class NetConnection{
|
||||
|
||||
/** Kick with an arbitrary reason, and a kick duration in milliseconds. */
|
||||
public void kick(String reason, int kickDuration){
|
||||
if(kicked) return;
|
||||
|
||||
Log.info("Kicking connection @; Reason: @", address, reason.replace("\n", " "));
|
||||
|
||||
PlayerInfo info = netServer.admins.getInfo(uuid);
|
||||
@@ -71,6 +77,7 @@ public abstract class NetConnection{
|
||||
Time.runTask(2f, this::close);
|
||||
|
||||
netServer.admins.save();
|
||||
kicked = true;
|
||||
}
|
||||
|
||||
public boolean isConnected(){
|
||||
|
||||
@@ -88,7 +88,7 @@ public class NetworkIO{
|
||||
return buffer;
|
||||
}
|
||||
|
||||
public static Host readServerData(String hostAddress, ByteBuffer buffer){
|
||||
public static Host readServerData(int ping, String hostAddress, ByteBuffer buffer){
|
||||
String host = readString(buffer);
|
||||
String map = readString(buffer);
|
||||
int players = buffer.getInt();
|
||||
@@ -100,7 +100,7 @@ public class NetworkIO{
|
||||
String description = readString(buffer);
|
||||
String modeName = readString(buffer);
|
||||
|
||||
return new Host(host, hostAddress, map, wave, players, version, vertype, gamemode, limit, description, modeName.isEmpty() ? null : modeName);
|
||||
return new Host(ping, host, hostAddress, map, wave, players, version, vertype, gamemode, limit, description, modeName.isEmpty() ? null : modeName);
|
||||
}
|
||||
|
||||
private static void writeString(ByteBuffer buffer, String string, int maxlen){
|
||||
|
||||
@@ -42,7 +42,7 @@ public class UnitType extends UnlockableContent{
|
||||
public float speed = 1.1f, boostMultiplier = 1f, rotateSpeed = 5f, baseRotateSpeed = 5f;
|
||||
public float drag = 0.3f, accel = 0.5f, landShake = 0f, rippleScale = 1f, fallSpeed = 0.018f;
|
||||
public float health = 200f, range = -1, armor = 0f;
|
||||
public float crashDamageMultiplier = 4f;
|
||||
public float crashDamageMultiplier = 3f;
|
||||
public boolean targetAir = true, targetGround = true;
|
||||
public boolean faceTarget = true, rotateShooting = true, isCounted = true, lowAltitude = false;
|
||||
public boolean canBoost = false;
|
||||
@@ -127,7 +127,7 @@ public class UnitType extends UnlockableContent{
|
||||
public void display(Unit unit, Table table){
|
||||
table.table(t -> {
|
||||
t.left();
|
||||
t.add(new Image(icon(Cicon.medium))).size(8 * 4);
|
||||
t.add(new Image(icon(Cicon.medium))).size(8 * 4).scaling(Scaling.fit);
|
||||
t.labelWrap(localizedName).left().width(190f).padLeft(5);
|
||||
}).growX().left();
|
||||
table.row();
|
||||
|
||||
@@ -3,6 +3,7 @@ package mindustry.type;
|
||||
import arc.func.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.math.*;
|
||||
import arc.math.geom.*;
|
||||
import arc.util.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.content.*;
|
||||
@@ -15,7 +16,7 @@ import static mindustry.Vars.*;
|
||||
|
||||
public abstract class Weather extends MappableContent{
|
||||
/** Default duration of this weather event in ticks. */
|
||||
public float duration = 15f * Time.toMinutes;
|
||||
public float duration = 9f * Time.toMinutes;
|
||||
public Attributes attrs = new Attributes();
|
||||
|
||||
//internals
|
||||
@@ -110,7 +111,7 @@ public abstract class Weather extends MappableContent{
|
||||
|
||||
/** Creates a weather entry with some approximate weather values. */
|
||||
public WeatherEntry(Weather weather){
|
||||
this(weather, weather.duration/2f, weather.duration * 1.5f, weather.duration/2f, weather.duration * 1.5f);
|
||||
this(weather, weather.duration * 1f, weather.duration * 3f, weather.duration / 2f, weather.duration * 1.5f);
|
||||
}
|
||||
|
||||
public WeatherEntry(Weather weather, float minFrequency, float maxFrequency, float minDuration, float maxDuration){
|
||||
@@ -136,6 +137,7 @@ public abstract class Weather extends MappableContent{
|
||||
|
||||
Weather weather;
|
||||
float intensity = 1f, opacity = 0f, life, effectTimer;
|
||||
Vec2 windVector = new Vec2();
|
||||
|
||||
void init(Weather weather){
|
||||
this.weather = weather;
|
||||
|
||||
@@ -28,11 +28,8 @@ public class CoreItemsDisplay extends Table{
|
||||
update(() -> {
|
||||
CoreBuild core = Vars.player.team().core();
|
||||
|
||||
for(Item item : content.items()){
|
||||
if(core != null && core.items.get(item) > 0 && usedItems.add(item)){
|
||||
rebuild();
|
||||
break;
|
||||
}
|
||||
if(content.items().contains(item -> core != null && core.items.get(item) > 0 && usedItems.add(item))){
|
||||
rebuild();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -15,8 +15,8 @@ import mindustry.ui.Links.*;
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class AboutDialog extends BaseDialog{
|
||||
private Seq<String> contributors = new Seq<>();
|
||||
private static ObjectSet<String> bannedItems = ObjectSet.with("google-play", "itch.io", "dev-builds", "f-droid");
|
||||
Seq<String> contributors = new Seq<>();
|
||||
static ObjectSet<String> bannedItems = ObjectSet.with("google-play", "itch.io", "dev-builds", "f-droid");
|
||||
|
||||
public AboutDialog(){
|
||||
super("@about.button");
|
||||
|
||||
@@ -8,7 +8,7 @@ import mindustry.graphics.*;
|
||||
|
||||
public class ColorPicker extends BaseDialog{
|
||||
private Cons<Color> cons = c -> {};
|
||||
private Color current = new Color();
|
||||
Color current = new Color();
|
||||
|
||||
public ColorPicker(){
|
||||
super("@pickcolor");
|
||||
|
||||
@@ -20,7 +20,7 @@ import static mindustry.Vars.*;
|
||||
|
||||
public class CustomRulesDialog extends BaseDialog{
|
||||
private Table main;
|
||||
private Rules rules;
|
||||
Rules rules;
|
||||
private Prov<Rules> resetter;
|
||||
private LoadoutDialog loadoutDialog;
|
||||
private BaseDialog banDialog;
|
||||
|
||||
@@ -56,7 +56,7 @@ public class DatabaseDialog extends BaseDialog{
|
||||
for(int i = 0; i < array.size; i++){
|
||||
UnlockableContent unlock = (UnlockableContent)array.get(i);
|
||||
|
||||
Image image = unlocked(unlock) ? new Image(unlock.icon(Cicon.medium)) : new Image(Icon.lockOpen, Pal.gray);
|
||||
Image image = unlocked(unlock) ? new Image(unlock.icon(Cicon.medium)) : new Image(Icon.lock, Pal.gray);
|
||||
list.add(image).size(8*4).pad(3);
|
||||
ClickListener listener = new ClickListener();
|
||||
image.addListener(listener);
|
||||
|
||||
@@ -17,10 +17,10 @@ import java.util.*;
|
||||
|
||||
public class FileChooser extends BaseDialog{
|
||||
private static final Fi homeDirectory = Core.files.absolute(Core.files.getExternalStoragePath());
|
||||
private static Fi lastDirectory = homeDirectory;
|
||||
static Fi lastDirectory = homeDirectory;
|
||||
|
||||
private Table files;
|
||||
private Fi directory = lastDirectory;
|
||||
Fi directory = lastDirectory;
|
||||
private ScrollPane pane;
|
||||
private TextField navigation, filefield;
|
||||
private TextButton ok;
|
||||
@@ -163,7 +163,7 @@ public class FileChooser extends BaseDialog{
|
||||
return handles;
|
||||
}
|
||||
|
||||
private void updateFiles(boolean push){
|
||||
void updateFiles(boolean push){
|
||||
if(push) stack.push(directory);
|
||||
navigation.setText(directory.toString());
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package mindustry.ui.dialogs;
|
||||
|
||||
import arc.*;
|
||||
import arc.graphics.*;
|
||||
import arc.input.*;
|
||||
import arc.math.*;
|
||||
import arc.scene.ui.*;
|
||||
@@ -233,6 +234,10 @@ public class JoinDialog extends BaseDialog{
|
||||
t.add("[lightgray]" + (Core.bundle.format("players" + (host.players == 1 && host.playerLimit <= 0 ? ".single" : ""), (host.players == 0 ? "[lightgray]" : "[accent]") + host.players + (host.playerLimit > 0 ? "[lightgray]/[accent]" + host.playerLimit : "")+ "[lightgray]"))).left();
|
||||
t.row();
|
||||
t.add("[lightgray]" + Core.bundle.format("save.map", host.mapname) + "[lightgray] / " + (host.modeName == null ? host.mode.toString() : host.modeName)).width(targetWidth() - 10f).left().get().setEllipsis(true);
|
||||
if(host.ping > 0){
|
||||
t.row();
|
||||
t.add(Iconc.chartBar + " " + host.ping + "ms").color(Color.gray).left();
|
||||
}
|
||||
}).expand().left().bottom().padLeft(12f).padBottom(8);
|
||||
}
|
||||
|
||||
@@ -434,9 +439,13 @@ public class JoinDialog extends BaseDialog{
|
||||
defaultServers.clear();
|
||||
val.asArray().each(child -> defaultServers.add(child.getString("address", "<invalid>")));
|
||||
Log.info("Fetched @ global servers.", defaultServers.size);
|
||||
}catch(Throwable ignored){}
|
||||
}catch(Throwable ignored){
|
||||
Log.err("Failed to parse community servers.");
|
||||
}
|
||||
});
|
||||
}catch(Throwable ignored){}
|
||||
}catch(Throwable e){
|
||||
Log.err("Failed to fetch community servers.");
|
||||
}
|
||||
}, t -> {});
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ package mindustry.ui.dialogs;
|
||||
import arc.*;
|
||||
import arc.Net.*;
|
||||
import arc.files.*;
|
||||
import arc.func.*;
|
||||
import arc.graphics.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.scene.ui.TextButton.*;
|
||||
@@ -106,31 +107,13 @@ public class ModsDialog extends BaseDialog{
|
||||
Core.settings.put("lastmod", text);
|
||||
|
||||
ui.loadfrag.show();
|
||||
Core.net.httpGet("http://api.github.com/repos/" + text + "/zipball/master", loc -> {
|
||||
Core.net.httpGet(loc.getHeader("Location"), result -> {
|
||||
if(result.getStatus() != HttpStatus.OK){
|
||||
ui.showErrorMessage(Core.bundle.format("connectfail", result.getStatus()));
|
||||
ui.loadfrag.hide();
|
||||
}else{
|
||||
try{
|
||||
Fi file = tmpDirectory.child(text.replace("/", "") + ".zip");
|
||||
Streams.copy(result.getResultAsStream(), file.write(false));
|
||||
mods.importMod(file);
|
||||
file.delete();
|
||||
Core.app.post(() -> {
|
||||
try{
|
||||
setup();
|
||||
ui.loadfrag.hide();
|
||||
}catch(Throwable e){
|
||||
ui.showException(e);
|
||||
}
|
||||
});
|
||||
}catch(Throwable e){
|
||||
modError(e);
|
||||
}
|
||||
}
|
||||
}, t2 -> Core.app.post(() -> modError(t2)));
|
||||
}, t2 -> Core.app.post(() -> modError(t2)));
|
||||
// Try to download the 6.0 branch first, but if it doesnt exist try master.
|
||||
githubImport("6.0", text, e1 -> {
|
||||
githubImport("master", text, e2 -> {
|
||||
ui.showErrorMessage(Core.bundle.format("connectfail", e2));
|
||||
ui.loadfrag.hide();
|
||||
});
|
||||
});
|
||||
});
|
||||
}).margin(12f);
|
||||
});
|
||||
@@ -178,8 +161,22 @@ public class ModsDialog extends BaseDialog{
|
||||
border(Pal.accent);
|
||||
}}).size(h - 8f).padTop(-8f).padLeft(-8f).padRight(8f);
|
||||
|
||||
title.add("" + mod.meta.displayName() + "\n[lightgray]v" + mod.meta.version + (mod.enabled() ? "" : "\n" + Core.bundle.get("mod.disabled") + ""))
|
||||
.wrap().top().width(170f).growX().left();
|
||||
title.table(text -> {
|
||||
text.add("" + mod.meta.displayName() + "\n[lightgray]v" + mod.meta.version + (mod.enabled() ? "" : "\n" + Core.bundle.get("mod.disabled") + ""))
|
||||
.wrap().top().width(300f).growX().left();
|
||||
|
||||
text.row();
|
||||
if(!mod.isSupported()){
|
||||
text.labelWrap(Core.bundle.format("mod.requiresversion", mod.meta.minGameVersion)).growX();
|
||||
text.row();
|
||||
}else if(mod.hasUnmetDependencies()){
|
||||
text.labelWrap(Core.bundle.format("mod.missingdependencies", mod.missingDependencies.toString(", "))).growX();
|
||||
t.row();
|
||||
}else if(mod.hasContentErrors()){
|
||||
text.labelWrap("@mod.erroredcontent").growX();
|
||||
text.row();
|
||||
}
|
||||
}).top().growX();
|
||||
|
||||
title.add().growX();
|
||||
}).growX().growY().left();
|
||||
@@ -210,17 +207,7 @@ public class ModsDialog extends BaseDialog{
|
||||
}
|
||||
}).growX().right().padRight(-8f).padTop(-8f);
|
||||
|
||||
t.row();
|
||||
if(!mod.isSupported()){
|
||||
t.labelWrap(Core.bundle.format("mod.requiresversion", mod.meta.minGameVersion)).growX();
|
||||
t.row();
|
||||
}else if(mod.hasUnmetDependencies()){
|
||||
t.labelWrap(Core.bundle.format("mod.missingdependencies", mod.missingDependencies.toString(", "))).growX();
|
||||
t.row();
|
||||
}else if(mod.hasContentErrors()){
|
||||
t.labelWrap("@mod.erroredcontent").growX();
|
||||
t.row();
|
||||
}
|
||||
|
||||
}, Styles.clearPartialt, () -> showMod(mod)).size(w, h).growX().pad(4f);
|
||||
table.row();
|
||||
}
|
||||
@@ -288,8 +275,33 @@ public class ModsDialog extends BaseDialog{
|
||||
}*/
|
||||
}).width(400f);
|
||||
|
||||
|
||||
|
||||
dialog.show();
|
||||
}
|
||||
|
||||
private void githubImport(String branch, String repo, Cons<HttpStatus> err){
|
||||
Core.net.httpGet("http://api.github.com/repos/" + repo + "/zipball/" + branch, loc -> {
|
||||
Core.net.httpGet(loc.getHeader("Location"), result -> {
|
||||
if(result.getStatus() != HttpStatus.OK){
|
||||
err.get(result.getStatus());
|
||||
}else{
|
||||
try{
|
||||
Fi file = tmpDirectory.child(repo.replace("/", "") + ".zip");
|
||||
Streams.copy(result.getResultAsStream(), file.write(false));
|
||||
mods.importMod(file);
|
||||
file.delete();
|
||||
Core.app.post(() -> {
|
||||
try{
|
||||
setup();
|
||||
ui.loadfrag.hide();
|
||||
}catch(Throwable e){
|
||||
ui.showException(e);
|
||||
}
|
||||
});
|
||||
}catch(Throwable e){
|
||||
modError(e);
|
||||
}
|
||||
}
|
||||
}, t2 -> Core.app.post(() -> modError(t2)));
|
||||
}, t2 -> Core.app.post(() -> modError(t2)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,16 +44,7 @@ public class PausedDialog extends BaseDialog{
|
||||
float dw = 220f;
|
||||
cont.defaults().width(dw).height(55).pad(5f);
|
||||
|
||||
cont.button("@back", Icon.left, this::hide).colspan(2).width(dw * 2 + 20f);
|
||||
|
||||
cont.row();
|
||||
//if(state.isCampaign()){
|
||||
// cont.button("@techtree", Icon.tree, ui.tech::show);
|
||||
//}else{
|
||||
// cont.button("@database", Icon.book, ui.database::show);
|
||||
//}
|
||||
//TODO remove
|
||||
cont.button("nothing", Icon.warning, () -> ui.showInfo("no"));
|
||||
cont.button("@back", Icon.left, this::hide);
|
||||
cont.button("@settings", Icon.settings, ui.settings::show);
|
||||
|
||||
if(!state.rules.tutorial){
|
||||
@@ -93,6 +84,18 @@ public class PausedDialog extends BaseDialog{
|
||||
cont.row();
|
||||
|
||||
cont.buttonRow("@load", Icon.download, load::show).disabled(b -> net.active());
|
||||
}else if(state.isCampaign()){
|
||||
cont.buttonRow("@launchcore", Icon.up, () -> {
|
||||
hide();
|
||||
ui.planet.show(state.getSector(), player.team().core());
|
||||
}).disabled(b -> player.team().core() == null || !player.team().core().items.has(player.team().core().block.requirements));
|
||||
|
||||
cont.row();
|
||||
|
||||
cont.buttonRow("@planetmap", Icon.map, () -> {
|
||||
hide();
|
||||
ui.planet.show();
|
||||
});
|
||||
}else{
|
||||
cont.row();
|
||||
}
|
||||
|
||||
@@ -30,15 +30,15 @@ import static mindustry.ui.dialogs.PlanetDialog.Mode.*;
|
||||
|
||||
public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
|
||||
private final FrameBuffer buffer = new FrameBuffer(2, 2, true);
|
||||
private final PlanetRenderer planets = renderer.planets;
|
||||
final PlanetRenderer planets = renderer.planets;
|
||||
private final LaunchLoadoutDialog loadouts = new LaunchLoadoutDialog();
|
||||
private final Table stable = new Table().background(Styles.black3);
|
||||
|
||||
private int launchRange;
|
||||
private float zoom = 1f, selectAlpha = 1f;
|
||||
private @Nullable Sector selected, hovered, launchSector;
|
||||
@Nullable Sector selected, hovered, launchSector;
|
||||
private CoreBuild launcher;
|
||||
private Mode mode = look;
|
||||
Mode mode = look;
|
||||
private boolean launching;
|
||||
|
||||
public PlanetDialog(){
|
||||
@@ -291,7 +291,7 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
|
||||
}
|
||||
|
||||
//TODO localize
|
||||
private void updateSelected(){
|
||||
void updateSelected(){
|
||||
Sector sector = selected;
|
||||
|
||||
if(sector == null){
|
||||
|
||||
@@ -32,14 +32,14 @@ import java.util.*;
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class ResearchDialog extends BaseDialog{
|
||||
private final float nodeSize = Scl.scl(60f);
|
||||
private ObjectSet<TechTreeNode> nodes = new ObjectSet<>();
|
||||
private TechTreeNode root = new TechTreeNode(TechTree.root, null);
|
||||
private Rect bounds = new Rect();
|
||||
private ItemsDisplay itemDisplay;
|
||||
private View view;
|
||||
final float nodeSize = Scl.scl(60f);
|
||||
ObjectSet<TechTreeNode> nodes = new ObjectSet<>();
|
||||
TechTreeNode root = new TechTreeNode(TechTree.root, null);
|
||||
Rect bounds = new Rect();
|
||||
ItemsDisplay itemDisplay;
|
||||
View view;
|
||||
|
||||
private ItemSeq items;
|
||||
ItemSeq items;
|
||||
|
||||
public ResearchDialog(){
|
||||
super("");
|
||||
@@ -134,7 +134,7 @@ public class ResearchDialog extends BaseDialog{
|
||||
}
|
||||
});
|
||||
|
||||
addListener(new ElementGestureListener(){
|
||||
view.addListener(new ElementGestureListener(){
|
||||
@Override
|
||||
public void zoom(InputEvent event, float initialDistance, float distance){
|
||||
if(view.lastZoom < 0){
|
||||
@@ -233,7 +233,7 @@ public class ResearchDialog extends BaseDialog{
|
||||
}
|
||||
|
||||
boolean selectable(TechNode node){
|
||||
return !node.objectives.contains(i -> !i.complete());
|
||||
return node.content.unlocked() || !node.objectives.contains(i -> !i.complete());
|
||||
}
|
||||
|
||||
void showToast(String info){
|
||||
|
||||
@@ -74,7 +74,7 @@ public class SchematicsDialog extends BaseDialog{
|
||||
|
||||
t.clear();
|
||||
int i = 0;
|
||||
String regex = "[`~!@#$%^&*()-_=+[{]}|;:'\",<.>/?]";
|
||||
String regex = "[`~!@#$%^&*()-_=+{}|;:'\",<.>/?]";
|
||||
String searchString = search.toLowerCase().replaceAll(regex, " ");
|
||||
|
||||
firstSchematic = null;
|
||||
|
||||
@@ -12,8 +12,8 @@ import mindustry.gen.*;
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class BlockConfigFragment extends Fragment{
|
||||
private Table table = new Table();
|
||||
private Building configTile;
|
||||
Table table = new Table();
|
||||
Building configTile;
|
||||
|
||||
@Override
|
||||
public void build(Group parent){
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user