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:
Anuken
2020-09-03 09:18:06 -04:00
186 changed files with 10171 additions and 8983 deletions
+1 -2
View File
@@ -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(){
+5 -1
View File
@@ -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++){
+6 -6
View File
@@ -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;
}
+1 -1
View File
@@ -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);
+12 -1
View File
@@ -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
+26 -5
View File
@@ -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));
+1 -1
View File
@@ -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;
+42 -18
View File
@@ -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));
+6 -6
View File
@@ -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){{
+25 -1
View File
@@ -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);
+26 -11
View File
@@ -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){
+238 -78
View File
@@ -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
+4
View File
@@ -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);
+19
View File
@@ -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);
+27 -6
View File
@@ -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();
}
}
+6
View File
@@ -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(){
+10 -3
View File
@@ -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);
}
+1 -1
View File
@@ -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;
+6 -11
View File
@@ -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)];
+36 -16
View File
@@ -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);
+1 -1
View File
@@ -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();
}
}
+1 -1
View File
@@ -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());
+11 -2
View File
@@ -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();
+6 -2
View File
@@ -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;
+4
View File
@@ -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. */
+3 -3
View File
@@ -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
+1 -1
View File
@@ -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);
}
+7 -1
View File
@@ -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());
+2
View File
@@ -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"),
+2 -1
View File
@@ -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),
+57 -40
View File
@@ -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;
+19 -15
View File
@@ -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();
}
+58 -62
View File
@@ -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"
);
+4
View File
@@ -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);
-46
View File
@@ -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);
}
}
+29 -2
View File
@@ -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{
+32 -12
View File
@@ -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();
}
}
}
+18 -30
View File
@@ -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();
}
+7 -1
View File
@@ -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);
+87 -99
View File
@@ -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
+7 -2
View File
@@ -36,7 +36,7 @@ public class LogicDialog extends BaseDialog{
t.button("@schematic.copy.import", Icon.download, style, () -> {
dialog.hide();
try{
canvas.load(Core.app.getClipboardText());
canvas.load(Core.app.getClipboardText().replace("\r\n", "\n"));
}catch(Throwable e){
ui.showException(e);
}
@@ -61,7 +61,12 @@ public class LogicDialog extends BaseDialog{
for(Prov<LStatement> prov : LogicIO.allStatements){
LStatement example = prov.get();
if(example instanceof InvalidStatement) continue;
t.button(example.name(), Styles.cleart, () -> {
TextButtonStyle style = new TextButtonStyle(Styles.cleart);
style.fontColor = example.category().color;
style.font = Fonts.outline;
t.button(example.name(), style, () -> {
canvas.add(prov.get());
dialog.hide();
}).size(140f, 50f);
+75
View File
@@ -0,0 +1,75 @@
package mindustry.logic;
import arc.math.*;
public enum LogicOp{
add("+", (a, b) -> a + b),
sub("-", (a, b) -> a - b),
mul("*", (a, b) -> a * b),
div("/", (a, b) -> a / b),
mod("%", (a, b) -> a % b),
equal("==", (a, b) -> Math.abs(a - b) < 0.000001 ? 1 : 0),
notEqual("not", (a, b) -> Math.abs(a - b) < 0.000001 ? 0 : 1),
lessThan("<", (a, b) -> a < b ? 1 : 0),
lessThanEq("<=", (a, b) -> a <= b ? 1 : 0),
greaterThan(">", (a, b) -> a > b ? 1 : 0),
greaterThanEq(">=", (a, b) -> a >= b ? 1 : 0),
pow("^", Math::pow),
shl(">>", (a, b) -> (int)a >> (int)b),
shr("<<", (a, b) -> (int)a << (int)b),
or("or", (a, b) -> (int)a | (int)b),
and("and", (a, b) -> (int)a & (int)b),
xor("xor", (a, b) -> (int)a ^ (int)b),
max("max", Math::max),
min("min", Math::min),
atan2("atan2", (x, y) -> Mathf.atan2((float)x, (float)y) * Mathf.radDeg),
dst("dst", (x, y) -> Mathf.dst((float)x, (float)y)),
not("not", a -> ~(int)(a)),
abs("abs", a -> Math.abs(a)),
log("log", Math::log),
log10("log10", Math::log10),
sin("sin", d -> Math.sin(d * 0.017453292519943295D)),
cos("cos", d -> Math.cos(d * 0.017453292519943295D)),
tan("tan", d -> Math.tan(d * 0.017453292519943295D)),
floor("floor", Math::floor),
ceil("ceil", Math::ceil),
sqrt("sqrt", Math::sqrt),
rand("rand", d -> Mathf.rand.nextDouble() * d),
;
public static final LogicOp[] all = values();
public final OpLambda2 function2;
public final OpLambda1 function1;
public final boolean unary;
public final String symbol;
LogicOp(String symbol, OpLambda2 function){
this.symbol = symbol;
this.function2 = function;
this.function1 = null;
this.unary = false;
}
LogicOp(String symbol, OpLambda1 function){
this.symbol = symbol;
this.function1 = function;
this.function2 = null;
this.unary = true;
}
@Override
public String toString(){
return symbol;
}
interface OpLambda2{
double get(double a, double b);
}
interface OpLambda1{
double get(double a);
}
}
-38
View File
@@ -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();
}
}
+62 -49
View File
@@ -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. */
+36 -1
View File
@@ -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);
+22 -10
View File
@@ -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{
+5 -3
View File
@@ -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 -29
View File
@@ -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(){
-2
View File
@@ -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;
+5 -5
View File
@@ -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;
+4 -2
View File
@@ -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;
+4 -7
View File
@@ -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));
}
+17 -13
View File
@@ -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();
}
+13 -10
View File
@@ -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);
}
+2
View File
@@ -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. */
+10 -1
View File
@@ -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);
+1 -1
View File
@@ -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