Merge branch 'master' of https://github.com/Anuken/Mindustry into refactor-paths
Conflicts: core/src/mindustry/ai/types/GroundAI.java
This commit is contained in:
@@ -199,8 +199,7 @@ public class Vars implements Loadable{
|
||||
public static NetServer netServer;
|
||||
public static NetClient netClient;
|
||||
|
||||
public static
|
||||
Player player;
|
||||
public static Player player;
|
||||
|
||||
@Override
|
||||
public void loadAsync(){
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -5,6 +5,9 @@ import mindustry.entities.*;
|
||||
import mindustry.entities.units.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.meta.*;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
@@ -17,16 +20,34 @@ public class GroundAI extends AIController{
|
||||
|
||||
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)){
|
||||
moveTo(Pathfinder.fieldCore);
|
||||
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.onSolid()){
|
||||
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));
|
||||
|
||||
@@ -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{
|
||||
|
||||
@@ -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,
|
||||
@@ -998,10 +998,12 @@ public class Blocks implements ContentList{
|
||||
|
||||
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.graphite, 15, Items.copper, 20));
|
||||
canOverdrive = false;
|
||||
}};
|
||||
|
||||
//endregion
|
||||
@@ -1339,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
|
||||
@@ -1486,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;
|
||||
@@ -1502,12 +1504,13 @@ public class Blocks implements ContentList{
|
||||
|
||||
hasPower = true;
|
||||
size = 2;
|
||||
force = 3f;
|
||||
force = 4f;
|
||||
scaledForce = 5.5f;
|
||||
range = 170f;
|
||||
damage = 0.08f;
|
||||
damage = 0.1f;
|
||||
health = 160 * size * size;
|
||||
rotateSpeed = 10;
|
||||
range = 85f;
|
||||
|
||||
consumes.powerCond(3f, (TractorBeamEntity e) -> e.target != null);
|
||||
}};
|
||||
@@ -1556,14 +1559,15 @@ 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));
|
||||
|
||||
range = 100f;
|
||||
hasPower = true;
|
||||
consumes.power(3f);
|
||||
size = 2;
|
||||
shootLength = 5f;
|
||||
bulletDamage = 12f;
|
||||
reloadTime = 25f;
|
||||
bulletDamage = 16f;
|
||||
reloadTime = 15f;
|
||||
health = 190 * size * size;
|
||||
}};
|
||||
|
||||
@@ -1699,6 +1703,12 @@ 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[]{
|
||||
@@ -1769,11 +1779,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;
|
||||
@@ -1781,15 +1791,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;
|
||||
@@ -1797,6 +1808,7 @@ public class Blocks implements ContentList{
|
||||
|
||||
upgrades = new UnitType[][]{
|
||||
{UnitTypes.antumbra, UnitTypes.eclipse},
|
||||
{UnitTypes.arkyid, UnitTypes.toxopid},
|
||||
};
|
||||
}};
|
||||
|
||||
@@ -1862,7 +1874,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
|
||||
@@ -1898,7 +1909,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;
|
||||
|
||||
@@ -1906,15 +1917,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,17 +269,17 @@ 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;
|
||||
drag = -0.01f;
|
||||
splashDamageRadius = 28f;
|
||||
splashDamage = 40f;
|
||||
splashDamage = 35f;
|
||||
hitEffect = Fx.blastExplosion;
|
||||
despawnEffect = Fx.blastExplosion;
|
||||
lightning = 2;
|
||||
lightningLength = 14;
|
||||
lightningLength = 10;
|
||||
}};
|
||||
|
||||
standardCopper = new BasicBulletType(2.5f, 9){{
|
||||
|
||||
@@ -52,10 +52,11 @@ public class Fx{
|
||||
if(!(e.data instanceof Unit)) return;
|
||||
|
||||
Unit select = e.data();
|
||||
boolean block = select instanceof BlockUnitc;
|
||||
|
||||
mixcol(Pal.accent, 1f);
|
||||
alpha(e.fout());
|
||||
rect(select.type().icon(Cicon.full), select.x, select.y, select.rotation - 90f);
|
||||
rect(block ? ((BlockUnitc)select).tile().block.icon(Cicon.full) : select.type().icon(Cicon.full), select.x, select.y, block ? 0f : select.rotation - 90f);
|
||||
alpha(1f);
|
||||
Lines.stroke(e.fslope() * 1f);
|
||||
Lines.square(select.x, select.y, e.fout() * select.hitSize * 2f, 45);
|
||||
@@ -514,6 +515,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);
|
||||
|
||||
@@ -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),
|
||||
@@ -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;
|
||||
@@ -376,52 +374,218 @@ public class UnitTypes implements ContentList{
|
||||
}});
|
||||
}};
|
||||
|
||||
//TODO implement
|
||||
arkyid = new UnitType("arkyid"){{
|
||||
drag = 0.1f;
|
||||
speed = 0.5f;
|
||||
hitsize = 9f;
|
||||
health = 140;
|
||||
hitsize = 21f;
|
||||
health = 8000;
|
||||
armor = 6f;
|
||||
|
||||
rotateSpeed = 2.7f;
|
||||
|
||||
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;
|
||||
legLengthScl = 0.96f;
|
||||
rippleScale = 2f;
|
||||
legSpeed = 0.2f;
|
||||
|
||||
legSplashDamage = 32;
|
||||
legSplashRange = 30;
|
||||
|
||||
hovering = true;
|
||||
allowLegStep = true;
|
||||
visualElevation = 0.65f;
|
||||
groundLayer = Layer.legUnit;
|
||||
|
||||
BulletType sapper = new SapBulletType(){{
|
||||
sapStrength = 0.83f;
|
||||
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 = true;
|
||||
ammoMultiplier = 4f;
|
||||
splashDamageRadius = 95f;
|
||||
splashDamage = 65f;
|
||||
backColor = Pal.sapBulletBack;
|
||||
frontColor = lightningColor = Pal.sapBullet;
|
||||
lightning = 3;
|
||||
lightningLength = 10;
|
||||
smokeEffect = Fx.shootBigSmoke2;
|
||||
shake = 5f;
|
||||
|
||||
status = StatusEffects.sapped;
|
||||
statusDuration = 60f * 10;
|
||||
}};
|
||||
}});
|
||||
}};
|
||||
|
||||
toxopid = new UnitType("toxopid"){{
|
||||
drag = 0.1f;
|
||||
speed = 0.5f;
|
||||
hitsize = 21f;
|
||||
health = 23000;
|
||||
armor = 14f;
|
||||
|
||||
rotateSpeed = 1.9f;
|
||||
|
||||
legCount = 8;
|
||||
legMoveSpace = 0.8f;
|
||||
legPairOffset = 3;
|
||||
legLength = 75f;
|
||||
legExtension = -20;
|
||||
legBaseOffset = 8f;
|
||||
landShake = 1f;
|
||||
legSpeed = 0.1f;
|
||||
legLengthScl = 0.93f;
|
||||
rippleScale = 3f;
|
||||
legSpeed = 0.19f;
|
||||
|
||||
legSplashDamage = 80;
|
||||
legSplashRange = 60;
|
||||
|
||||
hovering = true;
|
||||
allowLegStep = true;
|
||||
visualElevation = 0.95f;
|
||||
groundLayer = Layer.legUnit;
|
||||
|
||||
weapons.add(
|
||||
new Weapon("large-purple-mount"){{
|
||||
y = -5f;
|
||||
x = 11f;
|
||||
shootY = 7f;
|
||||
reload = 30;
|
||||
shake = 4f;
|
||||
rotateSpeed = 2f;
|
||||
ejectEffect = Fx.shellEjectSmall;
|
||||
shootSound = Sounds.shootBig;
|
||||
rotate = true;
|
||||
occlusion = 12f;
|
||||
recoil = 3f;
|
||||
shots = 2;
|
||||
spacing = 17f;
|
||||
|
||||
bullet = new ShrapnelBulletType(){{
|
||||
length = 90f;
|
||||
damage = 110f;
|
||||
width = 25f;
|
||||
serrationLenScl = 7f;
|
||||
serrationSpaceOffset = 60f;
|
||||
serrationFadeOffset = 0f;
|
||||
serrations = 10;
|
||||
serrationWidth = 6f;
|
||||
fromColor = Pal.sapBullet;
|
||||
toColor = Pal.sapBulletBack;
|
||||
shootEffect = smokeEffect = Fx.sparkShoot;
|
||||
}};
|
||||
}});
|
||||
|
||||
weapons.add(new Weapon("toxopid-cannon"){{
|
||||
y = -14f;
|
||||
x = 0f;
|
||||
shootY = 22f;
|
||||
mirror = false;
|
||||
reload = 180;
|
||||
shake = 10f;
|
||||
recoil = 10f;
|
||||
rotateSpeed = 1f;
|
||||
ejectEffect = Fx.shellEjectBig;
|
||||
shootSound = Sounds.shootBig;
|
||||
rotate = true;
|
||||
occlusion = 30f;
|
||||
|
||||
bullet = new ArtilleryBulletType(3f, 70){{
|
||||
hitEffect = Fx.sapExplosion;
|
||||
knockback = 0.8f;
|
||||
lifetime = 80f;
|
||||
width = height = 25f;
|
||||
collidesTiles = collides = true;
|
||||
ammoMultiplier = 4f;
|
||||
splashDamageRadius = 95f;
|
||||
splashDamage = 90f;
|
||||
backColor = Pal.sapBulletBack;
|
||||
frontColor = lightningColor = Pal.sapBullet;
|
||||
lightning = 5;
|
||||
lightningLength = 20;
|
||||
smokeEffect = Fx.shootBigSmoke2;
|
||||
hitShake = 10f;
|
||||
|
||||
status = StatusEffects.sapped;
|
||||
statusDuration = 60f * 10;
|
||||
|
||||
fragLifeMin = 0.3f;
|
||||
fragBullets = 12;
|
||||
|
||||
fragBullet = new ArtilleryBulletType(2.3f, 30){{
|
||||
hitEffect = Fx.sapExplosion;
|
||||
knockback = 0.8f;
|
||||
lifetime = 90f;
|
||||
width = height = 20f;
|
||||
collidesTiles = false;
|
||||
splashDamageRadius = 90f;
|
||||
splashDamage = 55f;
|
||||
backColor = Pal.sapBulletBack;
|
||||
frontColor = lightningColor = Pal.sapBullet;
|
||||
lightning = 2;
|
||||
lightningLength = 5;
|
||||
smokeEffect = Fx.shootBigSmoke2;
|
||||
hitShake = 5f;
|
||||
|
||||
status = StatusEffects.sapped;
|
||||
statusDuration = 60f * 10;
|
||||
}};
|
||||
}};
|
||||
}});
|
||||
}};
|
||||
@@ -438,6 +602,8 @@ public class UnitTypes implements ContentList{
|
||||
faceTarget = false;
|
||||
engineOffset = 5.5f;
|
||||
range = 140f;
|
||||
crashDamageMultiplier = 4f;
|
||||
|
||||
weapons.add(new Weapon(){{
|
||||
y = 0f;
|
||||
x = 2f;
|
||||
@@ -449,7 +615,7 @@ public class UnitTypes implements ContentList{
|
||||
}};
|
||||
|
||||
horizon = new UnitType("horizon"){{
|
||||
health = 300;
|
||||
health = 350;
|
||||
speed = 2f;
|
||||
accel = 0.08f;
|
||||
drag = 0.016f;
|
||||
@@ -458,19 +624,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;
|
||||
@@ -484,21 +650,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;
|
||||
@@ -534,21 +700,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;
|
||||
|
||||
@@ -582,18 +748,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 = 30f;
|
||||
lifetime = 25f;
|
||||
shootEffect = Fx.shootBig;
|
||||
}};
|
||||
}}
|
||||
@@ -607,13 +773,25 @@ public class UnitTypes implements ContentList{
|
||||
rotateSpeed = 1f;
|
||||
flying = true;
|
||||
lowAltitude = true;
|
||||
health = 18000;
|
||||
health = 20000;
|
||||
engineOffset = 38;
|
||||
engineSize = 7.3f;
|
||||
hitsize = 58f;
|
||||
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;
|
||||
@@ -621,14 +799,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;
|
||||
@@ -638,50 +816,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;
|
||||
}});
|
||||
}};
|
||||
|
||||
@@ -700,6 +857,7 @@ public class UnitTypes implements ContentList{
|
||||
engineOffset = 5.7f;
|
||||
itemCapacity = 30;
|
||||
range = 50f;
|
||||
isCounted = false;
|
||||
|
||||
mineTier = 1;
|
||||
mineSpeed = 2.5f;
|
||||
@@ -720,6 +878,7 @@ public class UnitTypes implements ContentList{
|
||||
engineOffset = 6.5f;
|
||||
hitsize = 8f;
|
||||
lowAltitude = true;
|
||||
isCounted = false;
|
||||
|
||||
mineTier = 2;
|
||||
mineSpeed = 3.5f;
|
||||
@@ -1094,6 +1253,7 @@ public class UnitTypes implements ContentList{
|
||||
hitsize = 0f;
|
||||
health = 1;
|
||||
rotateSpeed = 360f;
|
||||
itemCapacity = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -325,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);
|
||||
}
|
||||
|
||||
@@ -559,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);
|
||||
|
||||
@@ -371,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");
|
||||
@@ -385,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){
|
||||
@@ -527,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(
|
||||
@@ -545,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;
|
||||
@@ -614,7 +631,7 @@ public class NetServer implements ApplicationListener{
|
||||
Unit unit = player.unit();
|
||||
|
||||
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();
|
||||
}
|
||||
@@ -682,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;
|
||||
}
|
||||
|
||||
@@ -762,6 +779,10 @@ public class NetServer implements ApplicationListener{
|
||||
}
|
||||
|
||||
if(state.isGame() && net.server()){
|
||||
if(state.rules.pvp){
|
||||
state.serverPaused = isWaitingForPlayers();
|
||||
}
|
||||
|
||||
sync();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -126,7 +126,7 @@ public class MapEditor{
|
||||
x = Mathf.clamp(x, (drawBlock.size - 1) / 2, width() - drawBlock.size / 2 - 1);
|
||||
y = Mathf.clamp(y, (drawBlock.size - 1) / 2, height() - drawBlock.size / 2 - 1);
|
||||
if(!hasOverlap(x, y)){
|
||||
tile(x, y).setBlock(drawBlock, drawTeam, 0);
|
||||
tile(x, y).setBlock(drawBlock, drawTeam, rotation);
|
||||
}
|
||||
}else{
|
||||
boolean isFloor = drawBlock.isFloor() && drawBlock != Blocks.air;
|
||||
|
||||
@@ -110,18 +110,13 @@ public class MapRenderer implements Disposable{
|
||||
if(wall != Blocks.air && wall.synthetic()){
|
||||
region = !Core.atlas.isFound(wall.editorIcon()) || !center ? Core.atlas.find("clear-editor") : wall.editorIcon();
|
||||
|
||||
if(wall.rotate){
|
||||
mesh.draw(idxWall, region,
|
||||
wx * tilesize + wall.offset, wy * tilesize + wall.offset,
|
||||
region.getWidth() * Draw.scl, region.getHeight() * Draw.scl, tile.build == null ? 0 : tile.build.rotdeg() - 90);
|
||||
}else{
|
||||
float width = region.getWidth() * Draw.scl, height = region.getHeight() * Draw.scl;
|
||||
float width = region.getWidth() * Draw.scl, height = region.getHeight() * Draw.scl;
|
||||
|
||||
mesh.draw(idxWall, region,
|
||||
wx * tilesize + wall.offset + (tilesize - width) / 2f,
|
||||
wy * tilesize + wall.offset + (tilesize - height) / 2f,
|
||||
width, height);
|
||||
}
|
||||
mesh.draw(idxWall, region,
|
||||
wx * tilesize + wall.offset + (tilesize - width) / 2f,
|
||||
wy * tilesize + wall.offset + (tilesize - height) / 2f,
|
||||
width, height,
|
||||
tile.build == null || !wall.rotate ? 0 : tile.build.rotdeg() - 90);
|
||||
}else{
|
||||
region = floor.editorVariantRegions()[Mathf.randomSeed(idxWall, 0, floor.editorVariantRegions().length - 1)];
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ import static mindustry.Vars.*;
|
||||
|
||||
/** Utility class for damaging in an area. */
|
||||
public class Damage{
|
||||
private static Tile furthest;
|
||||
private static Rect rect = new Rect();
|
||||
private static Rect hitrect = new Rect();
|
||||
private static Vec2 tr = new Vec2();
|
||||
@@ -30,24 +31,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){
|
||||
@@ -74,6 +77,23 @@ public class Damage{
|
||||
}
|
||||
}
|
||||
|
||||
/** Collides a bullet with blocks in a laser, taking into account absorption blocks. Resulting length is stored in the bullet's fdata. */
|
||||
public static float collideLaser(Bullet b, float length){
|
||||
Tmp.v1.trns(b.rotation(), length);
|
||||
|
||||
furthest = null;
|
||||
|
||||
world.raycast(b.tileX(), b.tileY(), world.toTile(b.x + Tmp.v1.x), world.toTile(b.y + Tmp.v1.y),
|
||||
(x, y) -> (furthest = world.tile(x, y)) != null && furthest.team() != b.team && furthest.block().absorbLasers);
|
||||
|
||||
float resultLength = furthest != null ? Math.max(6f, b.dst(furthest.worldx(), furthest.worldy())) : length;
|
||||
|
||||
Damage.collideLine(b, b.team, b.type.hitEffect, b.x, b.y, b.rotation(), resultLength);
|
||||
b.fdata = furthest != null ? resultLength : length;
|
||||
|
||||
return resultLength;
|
||||
}
|
||||
|
||||
public static void collideLine(Bullet hitter, Team team, Effect effect, float x, float y, float angle, float length){
|
||||
collideLine(hitter, team, effect, x, y, angle, length, false);
|
||||
}
|
||||
@@ -87,7 +107,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)
|
||||
|
||||
@@ -23,7 +23,6 @@ public class ForceFieldAbility implements Ability{
|
||||
|
||||
private float realRad;
|
||||
private Unit paramUnit;
|
||||
private boolean hadShield;
|
||||
private final Cons<Shielderc> shieldConsumer = trait -> {
|
||||
if(trait.team() != paramUnit.team && Intersector.isInsideHexagon(paramUnit.x, paramUnit.y, realRad * 2f, trait.x(), trait.y()) && paramUnit.shield > 0){
|
||||
trait.absorb();
|
||||
@@ -32,6 +31,8 @@ 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);
|
||||
}
|
||||
|
||||
paramUnit.shield -= trait.damage();
|
||||
@@ -54,13 +55,6 @@ public class ForceFieldAbility implements Ability{
|
||||
unit.shield += Time.delta * regen;
|
||||
}
|
||||
|
||||
//break effect
|
||||
if(hadShield && unit.shield <= 0){
|
||||
Fx.shieldBreak.at(paramUnit.x, paramUnit.y, radius, paramUnit.team.color);
|
||||
}
|
||||
|
||||
hadShield = unit.shield > 0;
|
||||
|
||||
if(unit.shield > 0){
|
||||
unit.timer2 = Mathf.lerpDelta(unit.timer2, 1f, 0.06f);
|
||||
paramUnit = unit;
|
||||
|
||||
@@ -74,7 +74,7 @@ public abstract class BulletType extends Content{
|
||||
|
||||
public float fragCone = 360f;
|
||||
public int fragBullets = 9;
|
||||
public float fragVelocityMin = 0.2f, fragVelocityMax = 1f;
|
||||
public float fragVelocityMin = 0.2f, fragVelocityMax = 1f, fragLifeMin = 1f, fragLifeMax = 1f;
|
||||
public BulletType fragBullet = null;
|
||||
public Color hitColor = Color.white;
|
||||
|
||||
@@ -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. */
|
||||
@@ -148,7 +149,7 @@ public abstract class BulletType extends Content{
|
||||
for(int i = 0; i < fragBullets; i++){
|
||||
float len = Mathf.random(1f, 7f);
|
||||
float a = b.rotation() + Mathf.range(fragCone/2);
|
||||
fragBullet.create(b, x + Angles.trnsx(a, len), y + Angles.trnsy(a, len), a, Mathf.random(fragVelocityMin, fragVelocityMax));
|
||||
fragBullet.create(b, x + Angles.trnsx(a, len), y + Angles.trnsy(a, len), a, Mathf.random(fragVelocityMin, fragVelocityMax), Mathf.random(fragLifeMin, fragLifeMax));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -246,6 +247,10 @@ public abstract class BulletType extends Content{
|
||||
return create(parent.owner(), parent.team, x, y, angle);
|
||||
}
|
||||
|
||||
public Bullet create(Bullet parent, float x, float y, float angle, float velocityScl, float lifeScale){
|
||||
return create(parent.owner(), parent.team, x, y, angle, velocityScl, lifeScale);
|
||||
}
|
||||
|
||||
public Bullet create(Bullet parent, float x, float y, float angle, float velocityScl){
|
||||
return create(parent.owner(), parent.team, x, y, angle, velocityScl);
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -10,8 +10,6 @@ import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.world.*;
|
||||
|
||||
import static mindustry.Vars.world;
|
||||
|
||||
public class LaserBulletType extends BulletType{
|
||||
protected static Tile furthest;
|
||||
|
||||
@@ -49,24 +47,13 @@ public class LaserBulletType extends BulletType{
|
||||
|
||||
@Override
|
||||
public void init(Bullet b){
|
||||
Tmp.v1.trns(b.rotation(), length);
|
||||
|
||||
furthest = null;
|
||||
|
||||
world.raycast(b.tileX(), b.tileY(), world.toTile(b.x + Tmp.v1.x), world.toTile(b.y + Tmp.v1.y),
|
||||
(x, y) -> (furthest = world.tile(x, y)) != null && furthest.team() != b.team && furthest.block().absorbLasers);
|
||||
|
||||
float resultLength = furthest != null ? Math.max(6f, b.dst(furthest.worldx(), furthest.worldy())) : length;
|
||||
|
||||
Damage.collideLine(b, b.team, hitEffect, b.x, b.y, b.rotation(), resultLength);
|
||||
if(furthest != null) b.data(resultLength);
|
||||
|
||||
float resultLength = Damage.collideLaser(b, length);
|
||||
laserEffect.at(b.x, b.y, b.rotation(), resultLength * 0.75f);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Bullet b){
|
||||
float realLength = b.data() == null ? length : (Float)b.data();
|
||||
float realLength = b.fdata;
|
||||
|
||||
float f = Mathf.curve(b.fin(), 0f, 0.2f);
|
||||
float baseLen = realLength * f;
|
||||
|
||||
@@ -15,7 +15,7 @@ public class ShrapnelBulletType extends BulletType{
|
||||
public Color fromColor = Color.white, toColor = Pal.lancerLaser;
|
||||
|
||||
public int serrations = 7;
|
||||
public float serrationLenScl = 10f, serrationWidth = 4f, serrationSpacing = 8f, serrationSpaceOffset = 80f;
|
||||
public float serrationLenScl = 10f, serrationWidth = 4f, serrationSpacing = 8f, serrationSpaceOffset = 80f, serrationFadeOffset = 0.5f;
|
||||
|
||||
public ShrapnelBulletType(){
|
||||
speed = 0.01f;
|
||||
@@ -24,23 +24,26 @@ public class ShrapnelBulletType extends BulletType{
|
||||
lifetime = 10f;
|
||||
despawnEffect = Fx.none;
|
||||
pierce = true;
|
||||
keepVelocity = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(Bullet b){
|
||||
Damage.collideLine(b, b.team, hitEffect, b.x, b.y, b.rotation(), length);
|
||||
Damage.collideLaser(b, length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Bullet b){
|
||||
float realLength = b.fdata;
|
||||
|
||||
Draw.color(fromColor, toColor, b.fin());
|
||||
for(int i = 0; i < serrations; i++){
|
||||
for(int i = 0; i < (int)(serrations * realLength / length); i++){
|
||||
Tmp.v1.trns(b.rotation(), i * serrationSpacing);
|
||||
float sl = Mathf.clamp(b.fout() - 0.5f) * (serrationSpaceOffset - i * serrationLenScl);
|
||||
float sl = Mathf.clamp(b.fout() - serrationFadeOffset) * (serrationSpaceOffset - i * serrationLenScl);
|
||||
Drawf.tri(b.x + Tmp.v1.x, b.y + Tmp.v1.y, serrationWidth, sl, b.rotation() + 90);
|
||||
Drawf.tri(b.x + Tmp.v1.x, b.y + Tmp.v1.y, serrationWidth, sl, b.rotation() - 90);
|
||||
}
|
||||
Drawf.tri(b.x, b.y, width * b.fout(), (length + 50), b.rotation());
|
||||
Drawf.tri(b.x, b.y, width * b.fout(), (realLength + 50), b.rotation());
|
||||
Drawf.tri(b.x, b.y, width * b.fout(), 10f, b.rotation() + 180f);
|
||||
Draw.reset();
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -135,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);
|
||||
@@ -145,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){
|
||||
@@ -410,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;
|
||||
}
|
||||
@@ -431,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;
|
||||
@@ -510,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -568,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;
|
||||
}
|
||||
@@ -586,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;
|
||||
}
|
||||
@@ -619,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);
|
||||
@@ -627,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);
|
||||
@@ -653,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;
|
||||
}
|
||||
@@ -887,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);
|
||||
@@ -903,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);
|
||||
}
|
||||
@@ -960,7 +967,7 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc,
|
||||
table.row();
|
||||
table.table(this::displayConsumption).growX();
|
||||
|
||||
boolean displayFlow = (block.category == Category.distribution || block.category == Category.liquid) && Core.settings.getBool("flow");
|
||||
boolean displayFlow = (block.category == Category.distribution || block.category == Category.liquid) && Core.settings.getBool("flow") && block.displayFlow;
|
||||
|
||||
if(displayFlow){
|
||||
String ps = " " + StatUnit.perSecond.localized();
|
||||
@@ -998,9 +1005,21 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc,
|
||||
if(liquids != null){
|
||||
table.row();
|
||||
table.table(l -> {
|
||||
l.left();
|
||||
l.image(() -> liquids.current().icon(Cicon.small)).padRight(3f);
|
||||
l.label(() -> liquids.getFlowRate() < 0 ? "..." : Strings.fixed(liquids.getFlowRate(), 2) + ps).color(Color.lightGray);
|
||||
boolean[] had = {false};
|
||||
|
||||
Runnable rebuild = () -> {
|
||||
l.clearChildren();
|
||||
l.left();
|
||||
l.image(() -> liquids.current().icon(Cicon.small)).padRight(3f);
|
||||
l.label(() -> liquids.getFlowRate() < 0 ? "..." : Strings.fixed(liquids.getFlowRate(), 2) + ps).color(Color.lightGray);
|
||||
};
|
||||
|
||||
l.update(() -> {
|
||||
if(!had[0] && liquids.hadFlow()){
|
||||
had[0] = true;
|
||||
rebuild.run();
|
||||
}
|
||||
});
|
||||
}).left();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ abstract class BulletComp implements Timedc, Damagec, Hitboxc, Teamc, Posc, Draw
|
||||
Object data;
|
||||
BulletType type;
|
||||
float damage;
|
||||
float fdata;
|
||||
|
||||
@Override
|
||||
public void getCollisions(Cons<QuadTree> consumer){
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -33,7 +33,7 @@ abstract class LegsComp implements Posc, Rotc, Hitboxc, Flyingc, Unitc{
|
||||
@Override
|
||||
public void update(){
|
||||
if(Mathf.dst(deltaX(), deltaY()) > 0.001f){
|
||||
baseRotation = Mathf.slerpDelta(baseRotation, Mathf.angle(deltaX(), deltaY()), 0.1f);
|
||||
baseRotation = Angles.moveToward(baseRotation, Mathf.angle(deltaX(), deltaY()), type.rotateSpeed);
|
||||
}
|
||||
|
||||
float rot = baseRotation;
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -45,11 +45,16 @@ abstract class PosComp implements Position{
|
||||
return tile == null || tile.block() != Blocks.air ? (Floor)Blocks.air : tile.floor();
|
||||
}
|
||||
|
||||
Block blockOn(){
|
||||
Block blockOn(){
|
||||
Tile tile = tileOn();
|
||||
return tile == null ? Blocks.air : tile.block();
|
||||
}
|
||||
|
||||
boolean onSolid(){
|
||||
Tile tile = tileOn();
|
||||
return tile != null && tile.solid();
|
||||
}
|
||||
|
||||
@Nullable Tile tileOn(){
|
||||
return world.tileWorld(x, y);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -38,6 +38,10 @@ public class Rules{
|
||||
public boolean canGameOver = true;
|
||||
/** Whether reactors can explode and damage other blocks. */
|
||||
public boolean reactorExplosions = 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. */
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
});
|
||||
@@ -132,23 +132,25 @@ public class OverlayRenderer{
|
||||
//draw selected block
|
||||
if(input.block == null && !Core.scene.hasMouse()){
|
||||
Vec2 vec = Core.input.mouseWorld(input.getMouseX(), input.getMouseY());
|
||||
Building tile = world.buildWorld(vec.x, vec.y);
|
||||
Building build = world.buildWorld(vec.x, vec.y);
|
||||
|
||||
if(tile != null && tile.team() == player.team()){
|
||||
tile.drawSelect();
|
||||
if(!tile.enabled && tile.block.drawDisabled){
|
||||
tile.drawDisabled();
|
||||
if(build != null && build.team == player.team()){
|
||||
build.drawSelect();
|
||||
if(!build.enabled && build.block.drawDisabled){
|
||||
build.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);
|
||||
if(Core.input.keyDown(Binding.rotateplaced) && build.block().rotate && build.interactable(player.team())){
|
||||
control.input.drawArrow(build.block(), build.tileX(), build.tileY(), build.rotation, true);
|
||||
Draw.color(Pal.accent, 0.3f + Mathf.absin(4f, 0.2f));
|
||||
Fill.square(tile.x, tile.y, tile.block().size * tilesize/2f);
|
||||
Fill.square(build.x, build.y, build.block().size * tilesize/2f);
|
||||
Draw.color();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
input.drawOverSelect();
|
||||
|
||||
//draw selection overlay when dropping item
|
||||
if(input.isDroppingItem()){
|
||||
Vec2 v = Core.input.mouseWorld(input.getMouseX(), input.getMouseY());
|
||||
|
||||
@@ -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,24 @@ 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");
|
||||
}).tooltip("@schematics");
|
||||
|
||||
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(){
|
||||
@@ -555,6 +570,8 @@ public class DesktopInput extends InputHandler{
|
||||
|
||||
@Override
|
||||
public void updateState(){
|
||||
super.updateState();
|
||||
|
||||
if(state.isMenu()){
|
||||
droppingItem = false;
|
||||
mode = none;
|
||||
@@ -574,7 +591,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;
|
||||
|
||||
@@ -62,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
|
||||
|
||||
@@ -126,7 +126,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
|
||||
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()){
|
||||
@@ -236,6 +236,8 @@ 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
|
||||
@@ -372,7 +374,9 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
|
||||
}
|
||||
|
||||
public void updateState(){
|
||||
|
||||
if(state.isMenu()){
|
||||
controlledType = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void drawBottom(){
|
||||
@@ -383,6 +387,10 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
|
||||
|
||||
}
|
||||
|
||||
public void drawOverSelect(){
|
||||
|
||||
}
|
||||
|
||||
public void drawSelected(int x, int y, Block block, Color color){
|
||||
Drawf.selected(x, y, block, color);
|
||||
}
|
||||
@@ -538,11 +546,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){
|
||||
@@ -873,7 +877,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()){
|
||||
@@ -277,7 +277,7 @@ public class MobileInput extends InputHandler implements GestureListener{
|
||||
public void drawBottom(){
|
||||
Lines.stroke(1f);
|
||||
|
||||
//draw removals
|
||||
//draw requests about to be removed
|
||||
for(BuildPlan request : removals){
|
||||
Tile tile = request.tile();
|
||||
|
||||
@@ -292,6 +292,43 @@ public class MobileInput extends InputHandler implements GestureListener{
|
||||
}
|
||||
}
|
||||
|
||||
Draw.mixcol();
|
||||
Draw.color(Pal.accent);
|
||||
|
||||
//Draw lines
|
||||
if(lineMode){
|
||||
int tileX = tileX(Core.input.mouseX());
|
||||
int tileY = tileY(Core.input.mouseY());
|
||||
|
||||
if(mode == placing && block != null){
|
||||
//draw placing
|
||||
for(int i = 0; i < lineRequests.size; i++){
|
||||
BuildPlan request = lineRequests.get(i);
|
||||
if(i == lineRequests.size - 1 && request.block.rotate){
|
||||
drawArrow(block, request.x, request.y, request.rotation);
|
||||
}
|
||||
request.block.drawRequest(request, allRequests(), validPlace(request.x, request.y, request.block, request.rotation) && getRequest(request.x, request.y, request.block.size, null) == null);
|
||||
drawSelected(request.x, request.y, request.block, Pal.accent);
|
||||
}
|
||||
}else if(mode == breaking){
|
||||
drawBreakSelection(lineStartX, lineStartY, tileX, tileY);
|
||||
}
|
||||
}
|
||||
|
||||
Draw.reset();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawTop(){
|
||||
|
||||
//draw schematic selection
|
||||
if(mode == schematicSelect){
|
||||
drawSelection(lineStartX, lineStartY, lastLineX, lastLineY, Vars.maxSchematicSize);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawOverSelect(){
|
||||
//draw list of requests
|
||||
for(BuildPlan request : selectRequests){
|
||||
Tile tile = request.tile();
|
||||
@@ -322,29 +359,6 @@ public class MobileInput extends InputHandler implements GestureListener{
|
||||
}
|
||||
}
|
||||
|
||||
Draw.mixcol();
|
||||
Draw.color(Pal.accent);
|
||||
|
||||
//Draw lines
|
||||
if(lineMode){
|
||||
int tileX = tileX(Core.input.mouseX());
|
||||
int tileY = tileY(Core.input.mouseY());
|
||||
|
||||
if(mode == placing && block != null){
|
||||
//draw placing
|
||||
for(int i = 0; i < lineRequests.size; i++){
|
||||
BuildPlan request = lineRequests.get(i);
|
||||
if(i == lineRequests.size - 1 && request.block.rotate){
|
||||
drawArrow(block, request.x, request.y, request.rotation);
|
||||
}
|
||||
request.block.drawRequest(request, allRequests(), validPlace(request.x, request.y, request.block, request.rotation) && getRequest(request.x, request.y, request.block.size, null) == null);
|
||||
drawSelected(request.x, request.y, request.block, Pal.accent);
|
||||
}
|
||||
}else if(mode == breaking){
|
||||
drawBreakSelection(lineStartX, lineStartY, tileX, tileY);
|
||||
}
|
||||
}
|
||||
|
||||
//draw targeting crosshair
|
||||
if(target != null && !state.isEditor()){
|
||||
if(target != lastTarget){
|
||||
@@ -366,15 +380,6 @@ public class MobileInput extends InputHandler implements GestureListener{
|
||||
Draw.reset();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawTop(){
|
||||
|
||||
//draw schematic selection
|
||||
if(mode == schematicSelect){
|
||||
drawSelection(lineStartX, lineStartY, lastLineX, lastLineY, Vars.maxSchematicSize);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void drawRequest(BuildPlan request){
|
||||
if(request.tile() == null) return;
|
||||
@@ -555,7 +560,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)){
|
||||
@@ -850,17 +857,6 @@ 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);
|
||||
|
||||
@@ -34,7 +34,6 @@ public abstract class SaveFileReader{
|
||||
"titan-factory", "legacy-unit-factory",
|
||||
"fortress-factory", "legacy-unit-factory",
|
||||
|
||||
"command-center", "legacy-command-center",
|
||||
"mass-conveyor", "payload-conveyor"
|
||||
);
|
||||
|
||||
|
||||
@@ -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);
|
||||
static Seq<Runnable> postDraw = new Seq<>();
|
||||
private Vec2 offset = new Vec2();
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,8 @@ public class LExecutor{
|
||||
varTime = 1;
|
||||
|
||||
public static final int
|
||||
maxGraphicsBuffer = 512,
|
||||
maxGraphicsBuffer = 256,
|
||||
maxDisplayBuffer = 512,
|
||||
maxTextBuffer = 256;
|
||||
|
||||
public LInstruction[] instructions = {};
|
||||
@@ -41,7 +42,8 @@ public class LExecutor{
|
||||
vars[varTime].numval = Time.millis();
|
||||
|
||||
//reset to start
|
||||
if(vars[varCounter].numval >= instructions.length) vars[varCounter].numval = 0;
|
||||
if(vars[varCounter].numval >= instructions.length
|
||||
|| vars[varCounter].numval < 0) vars[varCounter].numval = 0;
|
||||
|
||||
if(vars[varCounter].numval < instructions.length){
|
||||
instructions[(int)(vars[varCounter].numval++)].run(this);
|
||||
@@ -177,9 +179,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 +379,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 +464,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();
|
||||
}
|
||||
|
||||
@@ -93,7 +93,13 @@ public abstract class LStatement{
|
||||
Core.scene.add(t);
|
||||
|
||||
t.update(() -> {
|
||||
if(b.parent == null) return;
|
||||
if(b.parent == null || !b.isDescendantOf(Core.scene.root)){
|
||||
Core.app.post(() -> {
|
||||
hitter.remove();
|
||||
t.remove();
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
b.localToStageCoordinates(Tmp.v1.set(b.getWidth()/2f, b.getHeight()/2f));
|
||||
t.setPosition(Tmp.v1.x, Tmp.v1.y, Align.center);
|
||||
|
||||
@@ -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;
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -425,6 +425,7 @@ public class Mods implements Loadable{
|
||||
/** This must be run on the main thread! */
|
||||
public void loadScripts(){
|
||||
Time.mark();
|
||||
boolean[] any = {false};
|
||||
|
||||
try{
|
||||
eachEnabled(mod -> {
|
||||
@@ -438,6 +439,7 @@ public class Mods implements Loadable{
|
||||
if(scripts == null){
|
||||
scripts = platform.createScripts();
|
||||
}
|
||||
any[0] = true;
|
||||
scripts.run(mod, main);
|
||||
}catch(Throwable e){
|
||||
Core.app.post(() -> {
|
||||
@@ -454,7 +456,9 @@ public class Mods implements Loadable{
|
||||
content.setCurrentMod(null);
|
||||
}
|
||||
|
||||
Log.info("Time to initialize modded scripts: @", Time.elapsed());
|
||||
if(any[0]){
|
||||
Log.info("Time to initialize modded scripts: @", Time.elapsed());
|
||||
}
|
||||
}
|
||||
|
||||
/** Creates all the content found in mod files. */
|
||||
@@ -588,59 +592,68 @@ public class Mods implements Loadable{
|
||||
private LoadedMod loadMod(Fi sourceFile) throws Exception{
|
||||
Time.mark();
|
||||
|
||||
Fi zip = sourceFile.isDirectory() ? sourceFile : new ZipFi(sourceFile);
|
||||
if(zip.list().length == 1 && zip.list()[0].isDirectory()){
|
||||
zip = zip.list()[0];
|
||||
}
|
||||
ZipFi rootZip = 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.");
|
||||
}
|
||||
|
||||
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(" ", "-");
|
||||
|
||||
Log.info("Loaded mod '@' in @", meta.name, Time.elapsed());
|
||||
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.*;
|
||||
@@ -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();
|
||||
|
||||
@@ -44,6 +44,17 @@ public class BeControl{
|
||||
}
|
||||
}, updateInterval, updateInterval);
|
||||
}
|
||||
|
||||
if(System.getProperties().contains("becopy")){
|
||||
try{
|
||||
Fi dest = Fi.get(System.getProperty("becopy"));
|
||||
Fi self = Fi.get(BeControl.class.getProtectionDomain().getCodeSource().getLocation().toURI().getPath());
|
||||
|
||||
self.copyTo(dest);
|
||||
}catch(Throwable e){
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** asynchronously checks for updates. */
|
||||
@@ -68,13 +79,7 @@ public class BeControl{
|
||||
}else{
|
||||
Core.app.post(() -> done.get(false));
|
||||
}
|
||||
}, error -> Core.app.post(() -> {
|
||||
if(!headless){
|
||||
ui.showException(error);
|
||||
}else{
|
||||
error.printStackTrace();
|
||||
}
|
||||
}));
|
||||
}, error -> {}); //ignore errors
|
||||
}
|
||||
|
||||
/** @return whether a new update is available */
|
||||
@@ -89,31 +94,41 @@ 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 = bebuildDirectory.child("client-be-" + updateBuild + ".jar");
|
||||
Fi fileDest = System.getProperties().contains("becopy") ?
|
||||
Fi.get(System.getProperty("becopy")) :
|
||||
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", "-Dbecopy=" + fileDest.absolutePath(), "-jar", file.absolutePath()} :
|
||||
new String[]{"java", "-DlastBuild=" + Version.build, "-Dberestart", "-Dbecopy=" + fileDest.absolutePath(), "-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);
|
||||
|
||||
@@ -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(){
|
||||
|
||||
@@ -11,8 +11,6 @@ import mindustry.world.modules.ItemModule.*;
|
||||
import java.util.*;
|
||||
|
||||
public class ItemSeq implements Iterable<ItemStack>, Serializable{
|
||||
private final static ItemStack tmp = new ItemStack();
|
||||
|
||||
protected final int[] values;
|
||||
public int total;
|
||||
|
||||
|
||||
@@ -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();
|
||||
@@ -261,7 +261,7 @@ public class UnitType extends UnlockableContent{
|
||||
drawControl(unit);
|
||||
}
|
||||
|
||||
if(unit.isFlying()){
|
||||
if(unit.isFlying() || visualElevation > 0){
|
||||
Draw.z(Math.min(Layer.darkness, z - 1f));
|
||||
drawShadow(unit);
|
||||
}
|
||||
@@ -478,7 +478,6 @@ public class UnitType extends UnlockableContent{
|
||||
}
|
||||
|
||||
public <T extends Unit & Legsc> void drawLegs(T unit){
|
||||
//Draw.z(Layer.groundUnit - 0.02f);
|
||||
|
||||
Leg[] legs = unit.legs();
|
||||
|
||||
@@ -494,8 +493,9 @@ public class UnitType extends UnlockableContent{
|
||||
Draw.rect(baseRegion, unit.x, unit.y, rotation);
|
||||
}
|
||||
|
||||
//TODO figure out layering
|
||||
for(int i = 0; i < legs.length; i++){
|
||||
//legs are drawn front first
|
||||
for(int j = legs.length - 1; j >= 0; j--){
|
||||
int i = (j % 2 == 0 ? j/2 : legs.length - 1 - j/2);
|
||||
Leg leg = legs[i];
|
||||
float angle = unit.legAngle(rotation, i);
|
||||
boolean flip = i >= legs.length/2f;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -11,6 +11,7 @@ import static mindustry.Vars.*;
|
||||
|
||||
public class CoreItemsDisplay extends Table{
|
||||
private final ObjectSet<Item> usedItems = new ObjectSet<>();
|
||||
private CoreBuild core;
|
||||
|
||||
public CoreItemsDisplay(){
|
||||
rebuild();
|
||||
@@ -26,19 +27,15 @@ public class CoreItemsDisplay extends Table{
|
||||
margin(4);
|
||||
|
||||
update(() -> {
|
||||
CoreBuild core = Vars.player.team().core();
|
||||
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();
|
||||
}
|
||||
});
|
||||
|
||||
int i = 0;
|
||||
|
||||
CoreBuild core = Vars.player.team().core();
|
||||
for(Item item : content.items()){
|
||||
if(usedItems.contains(item)){
|
||||
image(item.icon(Cicon.small)).padRight(3);
|
||||
|
||||
@@ -143,6 +143,7 @@ public class CustomRulesDialog extends BaseDialog{
|
||||
|
||||
main.button("@configure",
|
||||
() -> loadoutDialog.show(Blocks.coreShard.itemCapacity, rules.loadout,
|
||||
i -> true,
|
||||
() -> rules.loadout.clear().add(new ItemStack(Items.copper, 100)),
|
||||
() -> {}, () -> {}
|
||||
)).left().width(300f);
|
||||
@@ -162,7 +163,8 @@ public class CustomRulesDialog extends BaseDialog{
|
||||
number("@rules.enemycorebuildradius", f -> rules.enemyCoreBuildRadius = f * tilesize, () -> Math.min(rules.enemyCoreBuildRadius / tilesize, 200));
|
||||
|
||||
title("@rules.title.environment");
|
||||
number("@rules.solarpowermultiplier", f -> rules.solarPowerMultiplier = f, () -> rules.solarPowerMultiplier);
|
||||
//various multipliers should be handled elsewhere
|
||||
//number("@rules.solarpowermultiplier", f -> rules.solarPowerMultiplier = f, () -> rules.solarPowerMultiplier);
|
||||
check("@rules.lighting", b -> rules.lighting = b, () -> rules.lighting);
|
||||
|
||||
main.button(b -> {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -5,6 +5,7 @@ import arc.func.*;
|
||||
import arc.scene.ui.*;
|
||||
import arc.scene.ui.layout.*;
|
||||
import arc.struct.*;
|
||||
import mindustry.ctype.*;
|
||||
import mindustry.game.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.type.*;
|
||||
@@ -71,7 +72,7 @@ public class LaunchLoadoutDialog extends BaseDialog{
|
||||
Runnable rebuildItems = () -> rebuild.get(items);
|
||||
|
||||
buttons.button("@resources", Icon.terrain, () -> {
|
||||
loadout.show(core.itemCapacity, stacks, stacks::clear, () -> {}, () -> {
|
||||
loadout.show(core.itemCapacity, stacks, UnlockableContent::unlocked, stacks::clear, () -> {}, () -> {
|
||||
universe.updateLaunchResources(stacks);
|
||||
update.run();
|
||||
rebuildItems.run();
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
package mindustry.ui.dialogs;
|
||||
|
||||
import arc.*;
|
||||
import arc.struct.*;
|
||||
import arc.func.*;
|
||||
import arc.input.*;
|
||||
import arc.scene.ui.layout.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.type.*;
|
||||
@@ -17,6 +18,7 @@ public class LoadoutDialog extends BaseDialog{
|
||||
private Runnable updater;
|
||||
private Seq<ItemStack> stacks = new Seq<>();
|
||||
private Seq<ItemStack> originalStacks = new Seq<>();
|
||||
private Boolf<Item> validator = i -> true;
|
||||
private Table items;
|
||||
private int capacity;
|
||||
|
||||
@@ -51,13 +53,14 @@ public class LoadoutDialog extends BaseDialog{
|
||||
}).size(210f, 64f);
|
||||
}
|
||||
|
||||
public void show(int capacity, Seq<ItemStack> stacks, Runnable reseter, Runnable updater, Runnable hider){
|
||||
public void show(int capacity, Seq<ItemStack> stacks, Boolf<Item> validator, Runnable reseter, Runnable updater, Runnable hider){
|
||||
this.originalStacks = stacks;
|
||||
reseed();
|
||||
this.validator = validator;
|
||||
this.resetter = reseter;
|
||||
this.updater = updater;
|
||||
this.capacity = capacity;
|
||||
this.hider = hider;
|
||||
reseed();
|
||||
show();
|
||||
}
|
||||
|
||||
@@ -106,7 +109,7 @@ public class LoadoutDialog extends BaseDialog{
|
||||
|
||||
private void reseed(){
|
||||
this.stacks = originalStacks.map(ItemStack::copy);
|
||||
this.stacks.addAll(content.items().select(i -> !stacks.contains(stack -> stack.item == i)).map(i -> new ItemStack(i, 0)));
|
||||
this.stacks.addAll(content.items().select(i -> validator.get(i) && !stacks.contains(stack -> stack.item == i)).map(i -> new ItemStack(i, 0)));
|
||||
this.stacks.sort(Structs.comparingInt(s -> s.item.id));
|
||||
}
|
||||
|
||||
|
||||
@@ -161,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();
|
||||
@@ -193,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();
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -134,6 +134,8 @@ public class ResearchDialog extends BaseDialog{
|
||||
}
|
||||
});
|
||||
|
||||
touchable = Touchable.enabled;
|
||||
|
||||
addListener(new ElementGestureListener(){
|
||||
@Override
|
||||
public void zoom(InputEvent event, float initialDistance, float distance){
|
||||
@@ -233,7 +235,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){
|
||||
|
||||
@@ -62,7 +62,7 @@ public class ChatFragment extends Table{
|
||||
|
||||
update(() -> {
|
||||
|
||||
if(net.active() && input.keyTap(Binding.chat) && (scene.getKeyboardFocus() == chatfield || scene.getKeyboardFocus() == null || ui.minimapfrag.shown())){
|
||||
if(net.active() && input.keyTap(Binding.chat) && (scene.getKeyboardFocus() == chatfield || scene.getKeyboardFocus() == null || ui.minimapfrag.shown()) && !ui.scriptfrag.shown()){
|
||||
toggle();
|
||||
}
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ import mindustry.ui.dialogs.*;
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class HudFragment extends Fragment{
|
||||
private static final float dsize = 47.2f;
|
||||
private static final float dsize = 47f;
|
||||
|
||||
public final PlacementFragment blockfrag = new PlacementFragment();
|
||||
|
||||
@@ -122,6 +122,8 @@ public class HudFragment extends Fragment{
|
||||
}).update(i -> {
|
||||
if(net.active() && mobile){
|
||||
i.getStyle().imageUp = Icon.chat;
|
||||
}else if(state.isCampaign()){
|
||||
i.getStyle().imageUp = Icon.tree;
|
||||
}else{
|
||||
i.getStyle().imageUp = Icon.book;
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ package mindustry.ui.fragments;
|
||||
|
||||
import arc.*;
|
||||
import arc.Input.*;
|
||||
import arc.struct.*;
|
||||
import arc.graphics.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.math.*;
|
||||
@@ -10,8 +9,8 @@ import arc.scene.*;
|
||||
import arc.scene.ui.*;
|
||||
import arc.scene.ui.Label.*;
|
||||
import arc.scene.ui.layout.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import mindustry.*;
|
||||
import mindustry.input.*;
|
||||
import mindustry.ui.*;
|
||||
|
||||
@@ -45,7 +44,7 @@ public class ScriptConsoleFragment extends Table{
|
||||
font = Fonts.def;
|
||||
|
||||
visible(() -> {
|
||||
if(input.keyTap(Binding.console) && (scene.getKeyboardFocus() == chatfield || scene.getKeyboardFocus() == null)){
|
||||
if(input.keyTap(Binding.console) && (scene.getKeyboardFocus() == chatfield || scene.getKeyboardFocus() == null) && !ui.chatfrag.shown()){
|
||||
shown = !shown;
|
||||
if(shown && !open && enableConsole){
|
||||
toggle();
|
||||
@@ -96,7 +95,6 @@ public class ScriptConsoleFragment extends Table{
|
||||
fieldlabel.setStyle(fieldlabel.getStyle());
|
||||
|
||||
chatfield = new TextField("", new TextField.TextFieldStyle(scene.getStyle(TextField.TextFieldStyle.class)));
|
||||
chatfield.setMaxLength(Vars.maxTextLength);
|
||||
chatfield.getStyle().background = null;
|
||||
chatfield.getStyle().font = Fonts.chat;
|
||||
chatfield.getStyle().fontColor = Color.white;
|
||||
@@ -180,7 +178,6 @@ public class ScriptConsoleFragment extends Table{
|
||||
open = !open;
|
||||
if(mobile){
|
||||
TextInput input = new TextInput();
|
||||
input.maxLength = maxTextLength;
|
||||
input.accepted = text -> {
|
||||
chatfield.setText(text);
|
||||
sendMessage();
|
||||
@@ -221,6 +218,10 @@ public class ScriptConsoleFragment extends Table{
|
||||
return open;
|
||||
}
|
||||
|
||||
public boolean shown(){
|
||||
return shown;
|
||||
}
|
||||
|
||||
public void addMessage(String message){
|
||||
messages.insert(0, message);
|
||||
}
|
||||
|
||||
@@ -56,6 +56,8 @@ public class Block extends UnlockableContent{
|
||||
public final BlockBars bars = new BlockBars();
|
||||
public final Consumers consumes = new Consumers();
|
||||
|
||||
/** whether to display flow rate */
|
||||
public boolean displayFlow = true;
|
||||
/** whether this block is visible in the editor */
|
||||
public boolean inEditor = true;
|
||||
/** the last configuration value applied to this block. */
|
||||
|
||||
@@ -148,7 +148,7 @@ public class Tile implements Position, QuadTreeObject, Displayable{
|
||||
}
|
||||
|
||||
public Team team(){
|
||||
return build == null ? Team.derelict : build.team();
|
||||
return build == null ? Team.derelict : build.team;
|
||||
}
|
||||
|
||||
public void setTeam(Team team){
|
||||
@@ -180,6 +180,10 @@ public class Tile implements Position, QuadTreeObject, Displayable{
|
||||
public void setBlock(@NonNull Block type, Team team, int rotation, Prov<Building> entityprov){
|
||||
changing = true;
|
||||
|
||||
if(type.isStatic() || this.block.isStatic()){
|
||||
recache();
|
||||
}
|
||||
|
||||
this.block = type;
|
||||
preChanged();
|
||||
changeEntity(team, entityprov, (byte)Mathf.mod(rotation, 4));
|
||||
@@ -286,6 +290,11 @@ public class Tile implements Position, QuadTreeObject, Displayable{
|
||||
Call.removeTile(this);
|
||||
}
|
||||
|
||||
/** set()-s this tile, except it's synced across the network */
|
||||
public void setNet(Block block){
|
||||
Call.setTile(this, block, Team.derelict, 0);
|
||||
}
|
||||
|
||||
/** set()-s this tile, except it's synced across the network */
|
||||
public void setNet(Block block, Team team, int rotation){
|
||||
Call.setTile(this, block, team, rotation);
|
||||
|
||||
@@ -72,7 +72,7 @@ public class Tiles implements Iterable<Tile>{
|
||||
return get(Point2.x(pos), Point2.y(pos));
|
||||
}
|
||||
|
||||
public void each(Cons<Tile> cons){
|
||||
public void eachTile(Cons<Tile> cons){
|
||||
for(Tile tile : array){
|
||||
cons.get(tile);
|
||||
}
|
||||
|
||||
@@ -147,7 +147,7 @@ public interface Autotiler{
|
||||
|
||||
default boolean blends(Tile tile, int rotation, int direction){
|
||||
Building other = tile.getNearbyEntity(Mathf.mod(rotation - direction, 4));
|
||||
return other != null && other.team() == tile.team() && blends(tile, rotation, other.tileX(), other.tileY(), other.rotation, other.block());
|
||||
return other != null && other.team == tile.team() && blends(tile, rotation, other.tileX(), other.tileY(), other.rotation, other.block());
|
||||
}
|
||||
|
||||
default boolean blendsArmored(Tile tile, int rotation, int otherx, int othery, int otherrot, Block otherblock){
|
||||
|
||||
@@ -61,9 +61,9 @@ public class BuildBlock extends Block{
|
||||
if(tile == null) return;
|
||||
float healthf = tile.build == null ? 1f : tile.build.healthf();
|
||||
tile.setBlock(block, team, rotation);
|
||||
tile.build.health = block.health * healthf;
|
||||
if(tile.build != null) tile.build.health = block.health * healthf;
|
||||
//last builder was this local client player, call placed()
|
||||
if(!headless && builderID == player.unit().id()){
|
||||
if(tile.build != null && !headless && builderID == player.unit().id()){
|
||||
if(!skipConfig){
|
||||
tile.build.playerPlaced();
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ public class ShockMine extends Block{
|
||||
|
||||
@Override
|
||||
public void unitOn(Unit unit){
|
||||
if(unit.team() != team && timer(timerDamage, cooldown)){
|
||||
if(enabled && unit.team != team && timer(timerDamage, cooldown)){
|
||||
for(int i = 0; i < tendrils; i++){
|
||||
Lightning.create(team, Pal.lancerLaser, damage, x, y, Mathf.random(360f), length);
|
||||
}
|
||||
|
||||
@@ -91,7 +91,7 @@ public class Wall extends Block{
|
||||
//create lightning if necessary
|
||||
if(lightningChance > 0){
|
||||
if(Mathf.chance(lightningChance)){
|
||||
Lightning.create(team(), Pal.surge, lightningDamage, x, y, bullet.rotation() + 180f, lightningLength);
|
||||
Lightning.create(team, Pal.surge, lightningDamage, x, y, bullet.rotation() + 180f, lightningLength);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -138,13 +138,18 @@ public class ItemTurret extends Turret{
|
||||
return ammoTypes.get(item) != null && totalAmmo + ammoTypes.get(item).ammoMultiplier <= maxAmmo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte version(){
|
||||
return 2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(Writes write){
|
||||
super.write(write);
|
||||
write.b(ammo.size);
|
||||
for(AmmoEntry entry : ammo){
|
||||
ItemEntry i = (ItemEntry)entry;
|
||||
write.b(i.item.id);
|
||||
write.s(i.item.id);
|
||||
write.s(i.amount);
|
||||
}
|
||||
}
|
||||
@@ -152,12 +157,16 @@ public class ItemTurret extends Turret{
|
||||
@Override
|
||||
public void read(Reads read, byte revision){
|
||||
super.read(read, revision);
|
||||
byte amount = read.b();
|
||||
int amount = read.ub();
|
||||
for(int i = 0; i < amount; i++){
|
||||
Item item = Vars.content.item(read.b());
|
||||
Item item = Vars.content.item(revision < 2 ? read.ub() : read.s());
|
||||
short a = read.s();
|
||||
totalAmmo += a;
|
||||
ammo.add(new ItemEntry(item, a));
|
||||
|
||||
//only add ammo if this is a valid ammo type
|
||||
if(ammoTypes.containsKey(item)){
|
||||
ammo.add(new ItemEntry(item, a));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@ public class LaserTurret extends PowerTurret{
|
||||
bullet = null;
|
||||
}
|
||||
}else if(reload > 0){
|
||||
Liquid liquid = liquids().current();
|
||||
Liquid liquid = liquids.current();
|
||||
float maxUsed = consumes.<ConsumeLiquidBase>get(ConsumeType.liquid).amount;
|
||||
|
||||
float used = (cheating() ? maxUsed * Time.delta : Math.min(liquids.get(liquid), maxUsed * Time.delta)) * liquid.heatCapacity * coolantMultiplier;
|
||||
|
||||
@@ -436,7 +436,7 @@ public abstract class Turret extends Block{
|
||||
public void read(Reads read, byte revision){
|
||||
super.read(read, revision);
|
||||
|
||||
if(revision == 1){
|
||||
if(revision >= 1){
|
||||
reload = read.f();
|
||||
rotation = read.f();
|
||||
}
|
||||
|
||||
@@ -163,7 +163,7 @@ public class Conveyor extends Block implements Autotiler{
|
||||
|
||||
if(front() != null && front() != null){
|
||||
next = front();
|
||||
nextc = next instanceof ConveyorBuild && next.team() == team ? (ConveyorBuild)next : null;
|
||||
nextc = next instanceof ConveyorBuild && next.team == team ? (ConveyorBuild)next : null;
|
||||
aligned = nextc != null && rotation == next.rotation;
|
||||
}
|
||||
}
|
||||
@@ -178,7 +178,7 @@ public class Conveyor extends Block implements Autotiler{
|
||||
float mspeed = speed * tilesize * 55f;
|
||||
float centerSpeed = 0.1f;
|
||||
float centerDstScl = 3f;
|
||||
float tx = Geometry.d4x[rotation], ty = Geometry.d4y[rotation];
|
||||
float tx = Geometry.d4x(rotation), ty = Geometry.d4y(rotation);
|
||||
|
||||
float centerx = 0f, centery = 0f;
|
||||
|
||||
|
||||
@@ -42,6 +42,7 @@ public class ItemBridge extends Block{
|
||||
unloadable = false;
|
||||
group = BlockGroup.transportation;
|
||||
canOverdrive = false;
|
||||
noUpdateDisabled = true;
|
||||
|
||||
//point2 config is relative
|
||||
config(Point2.class, (ItemBridgeBuild tile, Point2 i) -> tile.link = Point2.pack(i.x + tile.tileX(), i.y + tile.tileY()));
|
||||
@@ -341,7 +342,7 @@ public class ItemBridge extends Block{
|
||||
|
||||
@Override
|
||||
public boolean acceptLiquid(Building source, Liquid liquid, float amount){
|
||||
if(team != source.team() || !hasLiquids) return false;
|
||||
if(team != source.team || !hasLiquids) return false;
|
||||
|
||||
Tile other = world.tile(link);
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ public class Junction extends Block{
|
||||
solid = true;
|
||||
group = BlockGroup.transportation;
|
||||
unloadable = false;
|
||||
noUpdateDisabled = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -49,7 +50,7 @@ public class Junction extends Block{
|
||||
Building dest = nearby(i);
|
||||
|
||||
//skip blocks that don't want the item, keep waiting until they do
|
||||
if(dest == null || !dest.acceptItem(this, item) || dest.team() != team){
|
||||
if(dest == null || !dest.acceptItem(this, item) || dest.team != team){
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -73,7 +74,7 @@ public class Junction extends Block{
|
||||
|
||||
if(relative == -1 || !buffer.accepts(relative)) return false;
|
||||
Building to = nearby(relative);
|
||||
return to != null && to.team() == team;
|
||||
return to != null && to.team == team;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -223,7 +223,7 @@ public class MassDriver extends Block{
|
||||
if(link == other.pos()){
|
||||
configure(-1);
|
||||
return false;
|
||||
}else if(other.block() instanceof MassDriver && other.dst(tile) <= range && other.team() == team){
|
||||
}else if(other.block() instanceof MassDriver && other.dst(tile) <= range && other.team == team){
|
||||
configure(other.pos());
|
||||
return false;
|
||||
}
|
||||
@@ -254,7 +254,7 @@ public class MassDriver extends Block{
|
||||
|
||||
float angle = tile.angleTo(target);
|
||||
|
||||
Bullets.driverBolt.create(this, team(),
|
||||
Bullets.driverBolt.create(this, team,
|
||||
x + Angles.trnsx(angle, translation), y + Angles.trnsy(angle, translation),
|
||||
angle, -1f, bulletSpeed, bulletLifetime, data);
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user