diff --git a/README.md b/README.md index 5c9c797297..209775b7f6 100644 --- a/README.md +++ b/README.md @@ -9,34 +9,34 @@ _[Trello Board](https://trello.com/b/aE2tcUwF/mindustry-40-plans)_ _[Wiki](https://mindustrygame.github.io/wiki)_ _[Javadoc](https://mindustrygame.github.io/docs/)_ -### Contributing +## Contributing See [CONTRIBUTING](CONTRIBUTING.md). -### Building +## Building Bleeding-edge builds are generated automatically for every commit. You can see them [here](https://github.com/Anuken/MindustryBuilds/releases). If you'd rather compile on your own, follow these instructions. First, make sure you have [JDK 14](https://adoptopenjdk.net/) installed. Open a terminal in the root directory, `cd` to the Mindustry folder and run the following commands: -#### Windows +### Windows _Running:_ `gradlew desktop:run` _Building:_ `gradlew desktop:dist` _Sprite Packing:_ `gradlew tools:pack` -#### Linux/Mac OS +### Linux/Mac OS _Running:_ `./gradlew desktop:run` _Building:_ `./gradlew desktop:dist` _Sprite Packing:_ `./gradlew tools:pack` -#### Server +### Server Server builds are bundled with each released build (in Releases). If you'd rather compile on your own, replace 'desktop' with 'server', e.g. `gradlew server:dist`. -#### Android +### Android 1. Install the Android SDK [here.](https://developer.android.com/studio#downloads) Make sure you're downloading the "Command line tools only", as Android Studio is not required. 2. Set the `ANDROID_HOME` environment variable to point to your unzipped Android SDK directory. @@ -44,20 +44,29 @@ Server builds are bundled with each released build (in Releases). If you'd rathe To debug the application on a connected phone, run `gradlew android:installDebug android:run`. -##### Troubleshooting +### Troubleshooting + +#### Permission Denied If the terminal returns `Permission denied` or `Command not found` on Mac/Linux, run `chmod +x ./gradlew` before running `./gradlew`. *This is a one-time procedure.* +#### Can not attach to current VM / Error during attachment using [...] + +This is a problem related to [Jabel](https://github.com/bsideup/jabel), which I use to compile Java 8-compatible bytecode while using Java 9+ language features. +I don't know of a complete fix to this issue. However, if you're getting the error when running through IntelliJ, a workaround is to launch through Gradle *once*, which starts a daemon and avoids the problem in future runs through IntelliJ. + + + --- Gradle may take up to several minutes to download files. Be patient.
After building, the output .JAR file should be in `/desktop/build/libs/Mindustry.jar` for desktop builds, and in `/server/build/libs/server-release.jar` for server builds. -### Feature Requests +## Feature Requests Post feature requests and feedback [here](https://github.com/Anuken/Mindustry-Suggestions/issues/new/choose). -### Downloads +## Downloads [Get it on Itch.io u.team() == data.team && u.type() == block.unitType)){ + if(!state.isEditor() && !Groups.unit.contains(u -> u.team() == data.team && u.type == block.unitType)){ Unit unit = block.unitType.create(data.team); unit.set(data.core()); unit.add(); diff --git a/core/src/mindustry/ai/WaveSpawner.java b/core/src/mindustry/ai/WaveSpawner.java index 99b52d40e5..91c7f61467 100644 --- a/core/src/mindustry/ai/WaveSpawner.java +++ b/core/src/mindustry/ai/WaveSpawner.java @@ -175,7 +175,7 @@ public class WaveSpawner{ } private void spawnEffect(Unit unit){ - Call.spawnEffect(unit.x, unit.y, unit.type()); + Call.spawnEffect(unit.x, unit.y, unit.type); Time.run(30f, unit::add); } diff --git a/core/src/mindustry/ai/types/BuilderAI.java b/core/src/mindustry/ai/types/BuilderAI.java index 8e8c74f986..c3558ca58a 100644 --- a/core/src/mindustry/ai/types/BuilderAI.java +++ b/core/src/mindustry/ai/types/BuilderAI.java @@ -79,7 +79,7 @@ public class BuilderAI extends AIController{ float dist = Math.min(cons.dst(unit) - buildingRange, 0); //make sure you can reach the request in time - if(dist / unit.type().speed < cons.buildCost * 0.9f){ + if(dist / unit.type.speed < cons.buildCost * 0.9f){ following = b; found = true; } @@ -112,7 +112,7 @@ public class BuilderAI extends AIController{ @Override public AIController fallback(){ - return unit.type().flying ? new FlyingAI() : new GroundAI(); + return unit.type.flying ? new FlyingAI() : new GroundAI(); } @Override diff --git a/core/src/mindustry/ai/types/FlyingAI.java b/core/src/mindustry/ai/types/FlyingAI.java index 0758f9016a..9d4c2d38f9 100644 --- a/core/src/mindustry/ai/types/FlyingAI.java +++ b/core/src/mindustry/ai/types/FlyingAI.java @@ -12,7 +12,7 @@ public class FlyingAI extends AIController{ @Override public void updateMovement(){ if(target != null && unit.hasWeapons() && command() == UnitCommand.attack){ - if(unit.type().weapons.first().rotate){ + if(unit.type.weapons.first().rotate){ moveTo(target, unit.range() * 0.8f); unit.lookAt(target); }else{ @@ -57,7 +57,7 @@ public class FlyingAI extends AIController{ vec.setAngle(Mathf.slerpDelta(unit.vel().angle(), vec.angle(), 0.6f)); } - vec.setLength(unit.type().speed); + vec.setLength(unit.type.speed); unit.moveAt(vec); } diff --git a/core/src/mindustry/ai/types/FormationAI.java b/core/src/mindustry/ai/types/FormationAI.java index d774719cab..ed175524d8 100644 --- a/core/src/mindustry/ai/types/FormationAI.java +++ b/core/src/mindustry/ai/types/FormationAI.java @@ -27,14 +27,14 @@ public class FormationAI extends AIController implements FormationMember{ @Override public void updateUnit(){ - UnitType type = unit.type(); + UnitType type = unit.type; if(leader.dead){ unit.resetController(); return; } - if(unit.type().canBoost && unit.canPassOn()){ + if(unit.type.canBoost && unit.canPassOn()){ unit.elevation = Mathf.approachDelta(unit.elevation, 0f, 0.08f); } @@ -43,7 +43,7 @@ public class FormationAI extends AIController implements FormationMember{ unit.aim(leader.aimX(), leader.aimY()); - if(unit.type().rotateShooting){ + if(unit.type.rotateShooting){ unit.lookAt(leader.aimX(), leader.aimY()); }else if(unit.moving()){ unit.lookAt(unit.vel.angle()); @@ -65,7 +65,7 @@ public class FormationAI extends AIController implements FormationMember{ CoreBuild core = unit.team.core(); - if(core != null && com.mineTile().drop() != null && unit.within(core, unit.type().range) && !unit.acceptsItem(com.mineTile().drop())){ + if(core != null && com.mineTile().drop() != null && unit.within(core, unit.type.range) && !unit.acceptsItem(com.mineTile().drop())){ if(core.acceptStack(unit.stack.item, unit.stack.amount, unit) > 0){ Call.transferItemTo(unit.stack.item, unit.stack.amount, unit.x, unit.y, core); diff --git a/core/src/mindustry/ai/types/GroundAI.java b/core/src/mindustry/ai/types/GroundAI.java index 157ed2eb9d..0134a424fc 100644 --- a/core/src/mindustry/ai/types/GroundAI.java +++ b/core/src/mindustry/ai/types/GroundAI.java @@ -45,13 +45,13 @@ public class GroundAI extends AIController{ } } - if(unit.type().canBoost && !unit.onSolid()){ + if(unit.type.canBoost && !unit.onSolid()){ unit.elevation = Mathf.approachDelta(unit.elevation, 0f, 0.08f); } - if(!Units.invalidateTarget(target, unit, unit.range()) && unit.type().rotateShooting){ - if(unit.type().hasWeapons()){ - unit.lookAt(Predict.intercept(unit, target, unit.type().weapons.first().bullet.speed)); + if(!Units.invalidateTarget(target, unit, unit.range()) && unit.type.rotateShooting){ + if(unit.type.hasWeapons()){ + unit.lookAt(Predict.intercept(unit, target, unit.type.weapons.first().bullet.speed)); } }else if(unit.moving()){ unit.lookAt(unit.vel().angle()); diff --git a/core/src/mindustry/ai/types/LogicAI.java b/core/src/mindustry/ai/types/LogicAI.java index 76b81794a2..1166c2ff96 100644 --- a/core/src/mindustry/ai/types/LogicAI.java +++ b/core/src/mindustry/ai/types/LogicAI.java @@ -98,7 +98,7 @@ public class LogicAI extends AIController{ } } - if(unit.type().canBoost && !unit.type().flying){ + if(unit.type.canBoost && !unit.type.flying){ unit.elevation = Mathf.approachDelta(unit.elevation, Mathf.num(boost || unit.onSolid()), 0.08f); } @@ -129,7 +129,7 @@ public class LogicAI extends AIController{ @Override protected boolean shouldShoot(){ - return shoot && !(unit.type().canBoost && boost); + return shoot && !(unit.type.canBoost && boost); } //always aim for the main target diff --git a/core/src/mindustry/ai/types/MinerAI.java b/core/src/mindustry/ai/types/MinerAI.java index d19b8e2c0a..aa7d1403c5 100644 --- a/core/src/mindustry/ai/types/MinerAI.java +++ b/core/src/mindustry/ai/types/MinerAI.java @@ -19,7 +19,7 @@ public class MinerAI extends AIController{ if(!(unit instanceof Minerc miner) || core == null) return; - if(miner.mineTile() != null && !miner.mineTile().within(unit, unit.type().range)){ + if(miner.mineTile() != null && !miner.mineTile().within(unit, unit.type.range)){ miner.mineTile(null); } @@ -36,7 +36,7 @@ public class MinerAI extends AIController{ } //if inventory is full, drop it off. - if(unit.stack.amount >= unit.type().itemCapacity || (targetItem != null && !unit.acceptsItem(targetItem))){ + if(unit.stack.amount >= unit.type.itemCapacity || (targetItem != null && !unit.acceptsItem(targetItem))){ mining = false; }else{ if(retarget() && targetItem != null){ @@ -44,9 +44,9 @@ public class MinerAI extends AIController{ } if(ore != null){ - moveTo(ore, unit.type().range / 2f); + moveTo(ore, unit.type.range / 2f); - if(unit.within(ore, unit.type().range)){ + if(unit.within(ore, unit.type.range)){ miner.mineTile(ore); } @@ -63,7 +63,7 @@ public class MinerAI extends AIController{ return; } - if(unit.within(core, unit.type().range)){ + if(unit.within(core, unit.type.range)){ if(core.acceptStack(unit.stack.item, unit.stack.amount, unit) > 0){ Call.transferItemTo(unit.stack.item, unit.stack.amount, unit.x, unit.y, core); } @@ -72,7 +72,7 @@ public class MinerAI extends AIController{ mining = true; } - circle(core, unit.type().range / 1.8f); + circle(core, unit.type.range / 1.8f); } } diff --git a/core/src/mindustry/ai/types/RepairAI.java b/core/src/mindustry/ai/types/RepairAI.java index cb60a3bfc9..36504f908d 100644 --- a/core/src/mindustry/ai/types/RepairAI.java +++ b/core/src/mindustry/ai/types/RepairAI.java @@ -12,7 +12,7 @@ public class RepairAI extends AIController{ if(target instanceof Building){ boolean shoot = false; - if(target.within(unit, unit.type().range)){ + if(target.within(unit, unit.type.range)){ unit.aim(target); shoot = true; } @@ -23,8 +23,8 @@ public class RepairAI extends AIController{ } if(target != null){ - if(!target.within(unit, unit.type().range * 0.65f) && target instanceof Building){ - moveTo(target, unit.type().range * 0.65f); + if(!target.within(unit, unit.type.range * 0.65f) && target instanceof Building){ + moveTo(target, unit.type.range * 0.65f); } unit.lookAt(target); diff --git a/core/src/mindustry/ai/types/SuicideAI.java b/core/src/mindustry/ai/types/SuicideAI.java index 2eae6e409f..0ef3ece228 100644 --- a/core/src/mindustry/ai/types/SuicideAI.java +++ b/core/src/mindustry/ai/types/SuicideAI.java @@ -21,7 +21,7 @@ public class SuicideAI extends GroundAI{ } if(retarget()){ - target = target(unit.x, unit.y, unit.range(), unit.type().targetAir, unit.type().targetGround); + target = target(unit.x, unit.y, unit.range(), unit.type.targetAir, unit.type.targetGround); } Building core = unit.closestEnemyCore(); @@ -30,11 +30,11 @@ public class SuicideAI extends GroundAI{ if(!Units.invalidateTarget(target, unit, unit.range()) && unit.hasWeapons()){ rotate = true; - shoot = unit.within(target, unit.type().weapons.first().bullet.range() + + shoot = unit.within(target, unit.type.weapons.first().bullet.range() + (target instanceof Building ? ((Building)target).block.size * Vars.tilesize / 2f : ((Hitboxc)target).hitSize() / 2f)); - if(unit.type().hasWeapons()){ - unit.aimLook(Predict.intercept(unit, target, unit.type().weapons.first().bullet.speed)); + if(unit.type.hasWeapons()){ + unit.aimLook(Predict.intercept(unit, target, unit.type.weapons.first().bullet.speed)); } //do not move toward walls or transport blocks @@ -65,7 +65,7 @@ public class SuicideAI extends GroundAI{ if(!blocked){ moveToTarget = true; //move towards target directly - unit.moveAt(vec.set(target).sub(unit).limit(unit.type().speed)); + unit.moveAt(vec.set(target).sub(unit).limit(unit.type.speed)); } } diff --git a/core/src/mindustry/async/PhysicsProcess.java b/core/src/mindustry/async/PhysicsProcess.java index 07d44d4717..a60dab7f68 100644 --- a/core/src/mindustry/async/PhysicsProcess.java +++ b/core/src/mindustry/async/PhysicsProcess.java @@ -57,7 +57,7 @@ public class PhysicsProcess implements AsyncProcess{ PhysicRef ref = entity.physref(); ref.body.layer = - entity.type().allowLegStep ? layerLegs : + entity.type.allowLegStep ? layerLegs : entity.isGrounded() ? layerGround : layerFlying; ref.x = entity.x(); ref.y = entity.y(); diff --git a/core/src/mindustry/content/Fx.java b/core/src/mindustry/content/Fx.java index 6ca6ccb24d..ca5bf9a0d0 100644 --- a/core/src/mindustry/content/Fx.java +++ b/core/src/mindustry/content/Fx.java @@ -56,7 +56,7 @@ public class Fx{ mixcol(Pal.accent, 1f); alpha(e.fout()); - rect(block ? ((BlockUnitc)select).tile().block.icon(Cicon.full) : select.type().icon(Cicon.full), select.x, select.y, block ? 0f : 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); @@ -66,7 +66,7 @@ public class Fx{ }), unitDespawn = new Effect(100f, e -> { - if(!(e.data instanceof Unit) || e.data().type() == null) return; + if(!(e.data instanceof Unit) || e.data().type == null) return; Unit select = e.data(); float scl = e.fout(Interp.pow2Out); @@ -74,7 +74,7 @@ public class Fx{ Draw.scl *= scl; mixcol(Pal.accent, 1f); - rect(select.type().icon(Cicon.full), select.x, select.y, select.rotation - 90f); + rect(select.type.icon(Cicon.full), select.x, select.y, select.rotation - 90f); reset(); Draw.scl = p; diff --git a/core/src/mindustry/content/UnitTypes.java b/core/src/mindustry/content/UnitTypes.java index 9a1b851118..b9189ec64e 100644 --- a/core/src/mindustry/content/UnitTypes.java +++ b/core/src/mindustry/content/UnitTypes.java @@ -872,7 +872,6 @@ public class UnitTypes implements ContentList{ drag = 0.01f; flying = true; health = 75; - faceTarget = false; engineOffset = 5.5f; range = 140f; @@ -1449,13 +1448,13 @@ public class UnitTypes implements ContentList{ trailMult = 0.8f; hitEffect = Fx.massiveExplosion; knockback = 1.5f; - lifetime = 140f; + lifetime = 100f; height = 15.5f; width = 15f; collidesTiles = false; ammoMultiplier = 4f; splashDamageRadius = 60f; - splashDamage = 85f; + splashDamage = 80f; backColor = Pal.missileYellowBack; frontColor = Pal.missileYellow; trailEffect = Fx.artilleryTrail; diff --git a/core/src/mindustry/core/Control.java b/core/src/mindustry/core/Control.java index 1d5e2de6fc..0b789d14fd 100644 --- a/core/src/mindustry/core/Control.java +++ b/core/src/mindustry/core/Control.java @@ -301,7 +301,7 @@ public class Control implements ApplicationListener, Loadable{ Groups.unit.clear(); Tile spawn = world.tile(sector.info.spawnPosition); - Schematics.placeLoadout(universe.getLastLoadout(), spawn.x, spawn.y); + Schematics.placeLaunchLoadout(spawn.x, spawn.y); //set up camera/player locations player.set(spawn.x * tilesize, spawn.y * tilesize); diff --git a/core/src/mindustry/core/Logic.java b/core/src/mindustry/core/Logic.java index 00787d9e43..a96ea101a9 100644 --- a/core/src/mindustry/core/Logic.java +++ b/core/src/mindustry/core/Logic.java @@ -14,8 +14,6 @@ import mindustry.maps.*; import mindustry.type.*; import mindustry.type.Weather.*; import mindustry.world.*; -import mindustry.world.blocks.*; -import mindustry.world.blocks.ConstructBlock.*; import java.util.*; @@ -40,32 +38,7 @@ public class Logic implements ApplicationListener{ //skip null entities or un-rebuildables, for obvious reasons; also skip client since they can't modify these requests if(tile.build == null || !tile.block().rebuildable || net.client()) return; - if(block instanceof ConstructBlock){ - - ConstructBuild entity = tile.bc(); - - //update block to reflect the fact that something was being constructed - if(entity.cblock != null && entity.cblock.synthetic()){ - block = entity.cblock; - }else{ - //otherwise this was a deconstruction that was interrupted, don't want to rebuild that - return; - } - } - - TeamData data = state.teams.get(tile.team()); - - //remove existing blocks that have been placed here. - //painful O(n) iteration + copy - for(int i = 0; i < data.blocks.size; i++){ - BlockPlan b = data.blocks.get(i); - if(b.x == tile.x && b.y == tile.y){ - data.blocks.removeIndex(i); - break; - } - } - - data.blocks.addFirst(new BlockPlan(tile.x, tile.y, (short)tile.build.rotation, block.id, tile.build.config())); + tile.build.addPlan(true); }); Events.on(BlockBuildEndEvent.class, event -> { @@ -123,6 +96,11 @@ public class Logic implements ApplicationListener{ Events.on(WorldLoadEvent.class, e -> { //enable infinite ammo for wave team by default state.rules.waveTeam.rules().infiniteAmmo = true; + if(state.isCampaign()){ + //enable building AI + state.rules.waveTeam.rules().ai = true; + state.rules.waveTeam.rules().infiniteResources = true; + } //save settings Core.settings.manualSave(); @@ -201,9 +179,12 @@ public class Logic implements ApplicationListener{ } //if there's a "win" wave and no enemies are present, win automatically - if(state.rules.waves && state.enemies == 0 && state.rules.winWave > 0 && state.wave >= state.rules.winWave && !spawner.isSpawning()){ + if(state.rules.waves && (state.enemies == 0 && state.rules.winWave > 0 && state.wave >= state.rules.winWave && !spawner.isSpawning()) || + (state.rules.attackMode && state.rules.waveTeam.cores().isEmpty())){ //the sector has been conquered - waves get disabled state.rules.waves = false; + //disable attack mode + state.rules.attackMode = false; //fire capture event Events.fire(new SectorCaptureEvent(state.rules.sector)); @@ -291,7 +272,7 @@ public class Logic implements ApplicationListener{ if(state.isGame()){ if(!net.client()){ - state.enemies = Groups.unit.count(u -> u.team() == state.rules.waveTeam && u.type().isCounted); + state.enemies = Groups.unit.count(u -> u.team() == state.rules.waveTeam && u.type.isCounted); } if(!state.isPaused()){ diff --git a/core/src/mindustry/core/NetServer.java b/core/src/mindustry/core/NetServer.java index 77ab732cf6..d9f944c1aa 100644 --- a/core/src/mindustry/core/NetServer.java +++ b/core/src/mindustry/core/NetServer.java @@ -575,7 +575,7 @@ public class NetServer implements ApplicationListener{ shooting = false; } - if(!player.dead() && (player.unit().type().flying || !player.unit().type().canBoost)){ + if(!player.dead() && (player.unit().type.flying || !player.unit().type.canBoost)){ boosting = false; } @@ -629,7 +629,7 @@ public class NetServer implements ApplicationListener{ Unit unit = player.unit(); long elapsed = Time.timeSinceMillis(con.lastReceivedClientTime); - float maxSpeed = ((player.unit().type().canBoost && player.unit().isFlying()) ? 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(); } diff --git a/core/src/mindustry/editor/MapEditor.java b/core/src/mindustry/editor/MapEditor.java index 49d174779b..1c33964e08 100644 --- a/core/src/mindustry/editor/MapEditor.java +++ b/core/src/mindustry/editor/MapEditor.java @@ -4,6 +4,7 @@ import arc.files.*; import arc.func.*; import arc.graphics.*; import arc.math.*; +import arc.math.geom.*; import arc.struct.*; import mindustry.content.*; import mindustry.editor.DrawOperation.*; @@ -180,6 +181,52 @@ public class MapEditor{ return false; } + public void addCliffs(){ + for(Tile tile : world.tiles){ + if(!tile.block().isStatic() || tile.block() == Blocks.cliff) continue; + + int rotation = 0; + for(int i = 0; i < 8; i++){ + Tile other = world.tiles.get(tile.x + Geometry.d8[i].x, tile.y + Geometry.d8[i].y); + if(other != null && !other.block().isStatic()){ + rotation |= (1 << i); + } + } + + if(rotation != 0){ + tile.setBlock(Blocks.cliff); + } + + tile.data = (byte)rotation; + } + + for(Tile tile : world.tiles){ + if(tile.block() != Blocks.cliff && tile.block().isStatic()){ + tile.setBlock(Blocks.air); + } + } + } + + public void addFloorCliffs(){ + for(Tile tile : world.tiles){ + if(!tile.floor().hasSurface() || tile.block() == Blocks.cliff) continue; + + int rotation = 0; + for(int i = 0; i < 8; i++){ + Tile other = world.tiles.get(tile.x + Geometry.d8[i].x, tile.y + Geometry.d8[i].y); + if(other != null && !other.floor().hasSurface()){ + rotation |= (1 << i); + } + } + + if(rotation != 0){ + tile.setBlock(Blocks.cliff); + } + + tile.data = (byte)rotation; + } + } + public void drawCircle(int x, int y, Cons drawer){ for(int rx = -brushSize; rx <= brushSize; rx++){ for(int ry = -brushSize; ry <= brushSize; ry++){ diff --git a/core/src/mindustry/editor/MapEditorDialog.java b/core/src/mindustry/editor/MapEditorDialog.java index 4f7eb008fd..dfad7c03ab 100644 --- a/core/src/mindustry/editor/MapEditorDialog.java +++ b/core/src/mindustry/editor/MapEditorDialog.java @@ -385,7 +385,7 @@ public class MapEditorDialog extends Dialog implements Disposable{ } public void build(){ - float size = 60f; + float size = 58f; clearChildren(); table(cont -> { @@ -559,10 +559,19 @@ public class MapEditorDialog extends Dialog implements Disposable{ mid.row(); - mid.table(t -> { - t.button("@editor.center", Icon.move, Styles.cleart, () -> view.center()).growX().margin(9f); - }).growX().top(); + if(!mobile){ + mid.table(t -> { + t.button("@editor.center", Icon.move, Styles.cleart, view::center).growX().margin(9f); + }).growX().top(); + } + if(addCliffButton){ + mid.row(); + + mid.table(t -> { + t.button("Cliffs", Icon.terrain, Styles.cleart, editor::addCliffs).growX().margin(9f); + }).growX().top(); + } }).margin(0).left().growY(); diff --git a/core/src/mindustry/entities/Units.java b/core/src/mindustry/entities/Units.java index 1babb75e33..e4591a0ed0 100644 --- a/core/src/mindustry/entities/Units.java +++ b/core/src/mindustry/entities/Units.java @@ -126,7 +126,7 @@ public class Units{ nearby(x, y, width, height, unit -> { if(boolResult) return; - if((unit.isGrounded() && !unit.type().hovering) == ground){ + if((unit.isGrounded() && !unit.type.hovering) == ground){ unit.hitbox(hitrect); if(hitrect.overlaps(x, y, width, height)){ diff --git a/core/src/mindustry/entities/bullet/LaserBulletType.java b/core/src/mindustry/entities/bullet/LaserBulletType.java index ad5965f3b8..6d43baf95c 100644 --- a/core/src/mindustry/entities/bullet/LaserBulletType.java +++ b/core/src/mindustry/entities/bullet/LaserBulletType.java @@ -39,6 +39,11 @@ public class LaserBulletType extends BulletType{ this(1f); } + @Override + public float estimateDPS(){ + return super.estimateDPS() * 2f; + } + @Override public void init(){ super.init(); diff --git a/core/src/mindustry/entities/bullet/LiquidBulletType.java b/core/src/mindustry/entities/bullet/LiquidBulletType.java index 2d3de79977..5da33f95e0 100644 --- a/core/src/mindustry/entities/bullet/LiquidBulletType.java +++ b/core/src/mindustry/entities/bullet/LiquidBulletType.java @@ -22,6 +22,8 @@ public class LiquidBulletType extends BulletType{ if(liquid != null){ this.liquid = liquid; this.status = liquid.effect; + lightColor = liquid.lightColor; + lightOpacity = liquid.lightColor.a; } ammoMultiplier = 1f; diff --git a/core/src/mindustry/entities/comp/BuildingComp.java b/core/src/mindustry/entities/comp/BuildingComp.java index a3514cf76a..6891744fce 100644 --- a/core/src/mindustry/entities/comp/BuildingComp.java +++ b/core/src/mindustry/entities/comp/BuildingComp.java @@ -21,12 +21,14 @@ import mindustry.ctype.*; import mindustry.entities.*; import mindustry.game.EventType.*; import mindustry.game.*; +import mindustry.game.Teams.*; import mindustry.gen.*; import mindustry.graphics.*; import mindustry.logic.*; import mindustry.type.*; import mindustry.ui.*; import mindustry.world.*; +import mindustry.world.blocks.ConstructBlock.*; import mindustry.world.blocks.environment.*; import mindustry.world.blocks.payloads.*; import mindustry.world.blocks.power.*; @@ -191,6 +193,36 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc, //endregion //region utility methods + public void addPlan(boolean checkPrevious){ + if(!block.rebuildable) return; + + if(self() instanceof ConstructBuild entity){ + //update block to reflect the fact that something was being constructed + if(entity.cblock != null && entity.cblock.synthetic()){ + block = entity.cblock; + }else{ + //otherwise this was a deconstruction that was interrupted, don't want to rebuild that + return; + } + } + + TeamData data = state.teams.get(team); + + if(checkPrevious){ + //remove existing blocks that have been placed here. + //painful O(n) iteration + copy + for(int i = 0; i < data.blocks.size; i++){ + BlockPlan b = data.blocks.get(i); + if(b.x == tile.x && b.y == tile.y){ + data.blocks.removeIndex(i); + break; + } + } + } + + data.blocks.addFirst(new BlockPlan(tile.x, tile.y, (short)rotation, block.id, config())); + } + /** Configure with the current, local player. */ public void configure(Object value){ //save last used config @@ -1263,7 +1295,7 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc, case type -> block; case firstItem -> items == null ? null : items.first(); case config -> block.configurations.containsKey(Item.class) || block.configurations.containsKey(Liquid.class) ? config() : null; - case payloadType -> getPayload() instanceof UnitPayload p1 ? p1.unit.type() : getPayload() instanceof BuildPayload p2 ? p2.block() : null; + case payloadType -> getPayload() instanceof UnitPayload p1 ? p1.unit.type : getPayload() instanceof BuildPayload p2 ? p2.block() : null; default -> noSensed; }; diff --git a/core/src/mindustry/entities/comp/CommanderComp.java b/core/src/mindustry/entities/comp/CommanderComp.java index b0fbddcf85..e03147d63c 100644 --- a/core/src/mindustry/entities/comp/CommanderComp.java +++ b/core/src/mindustry/entities/comp/CommanderComp.java @@ -59,7 +59,7 @@ abstract class CommanderComp implements Entityc, Posc{ units.clear(); Units.nearby(team, x, y, 150f, u -> { - if(u.isAI() && include.get(u) && u != self() && u.type().flying == type.flying && u.hitSize <= hitSize * 1.1f){ + if(u.isAI() && include.get(u) && u != self() && u.type.flying == type.flying && u.hitSize <= hitSize * 1.1f){ units.add(u); } }); @@ -82,7 +82,7 @@ abstract class CommanderComp implements Entityc, Posc{ FormationAI ai; unit.controller(ai = new FormationAI(self(), formation)); spacing = Math.max(spacing, ai.formationSize()); - minFormationSpeed = Math.min(minFormationSpeed, unit.type().speed); + minFormationSpeed = Math.min(minFormationSpeed, unit.type.speed); } this.formation = formation; @@ -106,7 +106,7 @@ abstract class CommanderComp implements Entityc, Posc{ //reset controlled units for(Unit unit : controlling){ if(unit.controller().isBeingControlled(self())){ - unit.controller(unit.type().createController()); + unit.controller(unit.type.createController()); } } diff --git a/core/src/mindustry/entities/comp/PlayerComp.java b/core/src/mindustry/entities/comp/PlayerComp.java index 4ee481b032..736a1b68e5 100644 --- a/core/src/mindustry/entities/comp/PlayerComp.java +++ b/core/src/mindustry/entities/comp/PlayerComp.java @@ -79,7 +79,7 @@ abstract class PlayerComp implements UnitController, Entityc, Syncc, Timerc, Dra admin = typing = false; textFadeTime = 0f; if(!dead()){ - unit.controller(unit.type().createController()); + unit.controller(unit.type.createController()); unit = Nulls.unit; } } @@ -91,7 +91,7 @@ abstract class PlayerComp implements UnitController, Entityc, Syncc, Timerc, Dra @Replace public float clipSize(){ - return unit.isNull() ? 20 : unit.type().hitSize * 2f; + return unit.isNull() ? 20 : unit.type.hitSize * 2f; } @Override @@ -123,7 +123,7 @@ abstract class PlayerComp implements UnitController, Entityc, Syncc, Timerc, Dra deathTimer = 0; //update some basic state to sync things - if(unit.type().canBoost){ + if(unit.type.canBoost){ Tile tile = unit.tileOn(); unit.elevation = Mathf.approachDelta(unit.elevation, (tile != null && tile.solid()) || boosting ? 1f : 0f, 0.08f); } @@ -177,7 +177,7 @@ abstract class PlayerComp implements UnitController, Entityc, Syncc, Timerc, Dra if(this.unit != Nulls.unit){ //un-control the old unit - this.unit.controller(this.unit.type().createController()); + this.unit.controller(this.unit.type.createController()); } this.unit = unit; if(unit != Nulls.unit){ diff --git a/core/src/mindustry/entities/comp/PuddleComp.java b/core/src/mindustry/entities/comp/PuddleComp.java index 669ee7e1f2..d83408c734 100644 --- a/core/src/mindustry/entities/comp/PuddleComp.java +++ b/core/src/mindustry/entities/comp/PuddleComp.java @@ -74,7 +74,7 @@ abstract class PuddleComp implements Posc, Puddlec, Drawc{ unit.apply(liquid.effect, 60 * 2); if(unit.vel.len() > 0.1){ - Fx.ripple.at(unit.x, unit.y, unit.type().rippleScale, liquid.color); + Fx.ripple.at(unit.x, unit.y, unit.type.rippleScale, liquid.color); } } } diff --git a/core/src/mindustry/entities/comp/UnitComp.java b/core/src/mindustry/entities/comp/UnitComp.java index 72a8d115f0..897305e87a 100644 --- a/core/src/mindustry/entities/comp/UnitComp.java +++ b/core/src/mindustry/entities/comp/UnitComp.java @@ -36,7 +36,7 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I @Import int id; private UnitController controller; - private UnitType type; + UnitType type; boolean spawnedByCore; double flag; @@ -110,7 +110,7 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I case firstItem -> stack().amount == 0 ? null : item(); case payloadType -> self() instanceof Payloadc pay ? (pay.payloads().isEmpty() ? null : - pay.payloads().peek() instanceof UnitPayload p1 ? p1.unit.type() : + pay.payloads().peek() instanceof UnitPayload p1 ? p1.unit.type : pay.payloads().peek() instanceof BuildPayload p2 ? p2.block() : null) : null; default -> noSensed; }; @@ -163,22 +163,12 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I @Override public void set(UnitType def, UnitController controller){ - type(type); + if(this.type != def){ + setType(def); + } controller(controller); } - @Override - public void type(UnitType type){ - if(this.type == type) return; - - setStats(type); - } - - @Override - public UnitType type(){ - return type; - } - /** @return pathfinder path type for calculating costs */ public int pathType(){ return Pathfinder.costGround; @@ -208,7 +198,7 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I return Units.getCap(team); } - public void setStats(UnitType type){ + public void setType(UnitType type){ this.type = type; this.maxHealth = type.health; this.drag = type.drag; @@ -226,7 +216,7 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I @Override public void afterSync(){ //set up type info after reading - setStats(this.type); + setType(this.type); controller.unit(self()); } diff --git a/core/src/mindustry/entities/units/AIController.java b/core/src/mindustry/entities/units/AIController.java index b02fa5dc00..ca005fe25d 100644 --- a/core/src/mindustry/entities/units/AIController.java +++ b/core/src/mindustry/entities/units/AIController.java @@ -95,7 +95,7 @@ public class AIController implements UnitController{ if(tile == targetTile || (costType == Pathfinder.costWater && !targetTile.floor().isLiquid)) return; - unit.moveAt(vec.trns(unit.angleTo(targetTile), unit.type().speed)); + unit.moveAt(vec.trns(unit.angleTo(targetTile), unit.type.speed)); } protected void updateWeapons(){ @@ -105,7 +105,7 @@ public class AIController implements UnitController{ boolean ret = retarget(); if(ret){ - target = findTarget(unit.x, unit.y, unit.range(), unit.type().targetAir, unit.type().targetGround); + target = findTarget(unit.x, unit.y, unit.range(), unit.type.targetAir, unit.type.targetGround); } if(invalid(target)){ @@ -119,7 +119,7 @@ public class AIController implements UnitController{ float mountX = unit.x + Angles.trnsx(rotation, weapon.x, weapon.y), mountY = unit.y + Angles.trnsy(rotation, weapon.x, weapon.y); - if(unit.type().singleTarget){ + if(unit.type.singleTarget){ targets[i] = target; }else{ if(ret){ @@ -176,7 +176,7 @@ public class AIController implements UnitController{ } protected void circle(Position target, float circleLength){ - circle(target, circleLength, unit.type().speed); + circle(target, circleLength, unit.type.speed); } protected void circle(Position target, float circleLength, float speed){ diff --git a/core/src/mindustry/game/DefaultWaves.java b/core/src/mindustry/game/DefaultWaves.java index 12d1d28dfe..f3c98f9fb4 100644 --- a/core/src/mindustry/game/DefaultWaves.java +++ b/core/src/mindustry/game/DefaultWaves.java @@ -81,7 +81,7 @@ public class DefaultWaves{ effect = StatusEffects.overdrive; }}, - new SpawnGroup(mace){{ + new SpawnGroup(pulsar){{ begin = 120; spacing = 2; unitScaling = 3; @@ -122,7 +122,7 @@ public class DefaultWaves{ shieldScaling = 30; }}, - new SpawnGroup(dagger){{ + new SpawnGroup(nova){{ begin = 35; spacing = 3; unitAmount = 4; @@ -233,7 +233,7 @@ public class DefaultWaves{ shieldScaling = 20f; }}, - new SpawnGroup(atrax){{ + new SpawnGroup(toxopid){{ begin = 210; unitAmount = 1; unitScaling = 1; @@ -258,7 +258,7 @@ public class DefaultWaves{ {nova, pulsar, quasar, vela, corvus}, {crawler, atrax, spiroct, arkyid, toxopid}, //{risso, minke, bryde, sei, omura}, //questionable choices - //{mono, poly, mega, quad, oct}, //do not attack + {poly, poly, mega, quad, quad}, {flare, horizon, zenith, antumbra, eclipse} }; diff --git a/core/src/mindustry/game/Rules.java b/core/src/mindustry/game/Rules.java index 329e69c92a..2a927cc59b 100644 --- a/core/src/mindustry/game/Rules.java +++ b/core/src/mindustry/game/Rules.java @@ -106,6 +106,8 @@ public class Rules{ public boolean ai; /** TODO Tier of blocks/designs that the AI uses for building. [0, 1]*/ public float aiTier = 0f; + /** Whether, when AI is enabled, ships should be spawned from the core. */ + public boolean aiCoreSpawn = true; /** If true, blocks don't require power or resources. */ public boolean cheat; /** If true, resources are not consumed when building. */ diff --git a/core/src/mindustry/game/SectorInfo.java b/core/src/mindustry/game/SectorInfo.java index c6c5d3ee51..f9e30f7d02 100644 --- a/core/src/mindustry/game/SectorInfo.java +++ b/core/src/mindustry/game/SectorInfo.java @@ -41,6 +41,8 @@ public class SectorInfo{ public Seq resources = new Seq<>(); /** Whether waves are enabled here. */ public boolean waves = true; + /** Whether attack mode is enabled here. */ + public boolean attack = false; /** Wave # from state */ public int wave = 1, winWave = -1; /** Time between waves. */ @@ -99,20 +101,30 @@ public class SectorInfo{ /** Write contents of meta into main storage. */ public void write(){ + //enable attack mode when there's a core. + if(state.rules.waveTeam.core() != null){ + attack = true; + winWave = 0; + } + + //if there are infinite waves and no win wave, add a win wave. + if(waves && winWave <= 0 && !attack){ + winWave = 30; + } + state.wave = wave; state.rules.waves = waves; state.rules.waveSpacing = waveSpacing; state.rules.winWave = winWave; + state.rules.attackMode = attack; CoreBuild entity = state.rules.defaultTeam.core(); if(entity != null){ entity.items.clear(); entity.items.add(items); //ensure capacity. - entity.items.each((i, a) -> entity.items.set(i, Math.min(a, entity.block.itemCapacity))); + entity.items.each((i, a) -> entity.items.set(i, Math.min(a, entity.storageCapacity))); } - - //TODO write items. } /** Prepare data for writing to a save. */ @@ -135,6 +147,7 @@ public class SectorInfo{ wave = state.wave; winWave = state.rules.winWave; waves = state.rules.waves; + attack = state.rules.attackMode; hasCore = entity != null; bestCoreType = !hasCore ? Blocks.air : state.rules.defaultTeam.cores().max(e -> e.block.size).block; storageCapacity = entity != null ? entity.storageCapacity : 0; diff --git a/core/src/mindustry/game/SpawnGroup.java b/core/src/mindustry/game/SpawnGroup.java index 92ba6811ea..86ba7b6324 100644 --- a/core/src/mindustry/game/SpawnGroup.java +++ b/core/src/mindustry/game/SpawnGroup.java @@ -9,6 +9,8 @@ import mindustry.gen.*; import mindustry.io.legacy.*; import mindustry.type.*; +import java.util.*; + import static mindustry.Vars.*; /** @@ -138,4 +140,20 @@ public class SpawnGroup implements Serializable{ ", items=" + items + '}'; } + + @Override + public boolean equals(Object o){ + if(this == o) return true; + if(o == null || getClass() != o.getClass()) return false; + SpawnGroup group = (SpawnGroup)o; + return end == group.end && begin == group.begin && spacing == group.spacing && max == group.max + && Float.compare(group.unitScaling, unitScaling) == 0 && Float.compare(group.shields, shields) == 0 + && Float.compare(group.shieldScaling, shieldScaling) == 0 && unitAmount == group.unitAmount && + type == group.type && effect == group.effect && Structs.eq(items, group.items); + } + + @Override + public int hashCode(){ + return Arrays.hashCode(new Object[]{type, end, begin, spacing, max, unitScaling, shields, shieldScaling, unitAmount, effect, items}); + } } diff --git a/core/src/mindustry/game/Teams.java b/core/src/mindustry/game/Teams.java index aa390c5d26..6a2972f074 100644 --- a/core/src/mindustry/game/Teams.java +++ b/core/src/mindustry/game/Teams.java @@ -131,7 +131,7 @@ public class Teams{ } private void count(Unit unit){ - unit.team.data().updateCount(unit.type(), 1); + unit.team.data().updateCount(unit.type, 1); if(unit instanceof Payloadc){ ((Payloadc)unit).payloads().each(p -> { @@ -178,15 +178,15 @@ public class Teams{ data.units.add(unit); data.presentFlag = true; - if(data.unitsByType == null || data.unitsByType.length <= unit.type().id){ + if(data.unitsByType == null || data.unitsByType.length <= unit.type.id){ data.unitsByType = new Seq[content.units().size]; } - if(data.unitsByType[unit.type().id] == null){ - data.unitsByType[unit.type().id] = new Seq<>(); + if(data.unitsByType[unit.type.id] == null){ + data.unitsByType[unit.type.id] = new Seq<>(); } - data.unitsByType[unit.type().id].add(unit); + data.unitsByType[unit.type.id].add(unit); count(unit); } diff --git a/core/src/mindustry/game/Universe.java b/core/src/mindustry/game/Universe.java index 5827f61168..30f0100b2d 100644 --- a/core/src/mindustry/game/Universe.java +++ b/core/src/mindustry/game/Universe.java @@ -194,8 +194,8 @@ public class Universe{ //queue random invasions if(!sector.isAttacked() && turn > invasionGracePeriod){ - //TODO use factors like difficulty for better invasion chance - if(sector.near().contains(Sector::hasEnemyBase) && Mathf.chance(baseInvasionChance)){ + //invasion chance depends on # of nearby bases + if(Mathf.chance(baseInvasionChance * sector.near().count(Sector::hasEnemyBase))){ int waveMax = Math.max(sector.info.winWave, sector.isBeingPlayed() ? state.wave : 0) + Mathf.random(2, 5) * 5; //assign invasion-related things diff --git a/core/src/mindustry/graphics/BlockRenderer.java b/core/src/mindustry/graphics/BlockRenderer.java index 223540e601..afe2e3182b 100644 --- a/core/src/mindustry/graphics/BlockRenderer.java +++ b/core/src/mindustry/graphics/BlockRenderer.java @@ -38,7 +38,7 @@ public class BlockRenderer implements Disposable{ private FrameBuffer dark = new FrameBuffer(); private Seq outArray2 = new Seq<>(); private Seq shadowEvents = new Seq<>(); - private IntSet processedEntities = new IntSet(), processedLinks = new IntSet(); + private IntSet procEntities = new IntSet(), procLinks = new IntSet(), procLights = new IntSet(); private boolean displayStatus = false; public BlockRenderer(){ @@ -191,8 +191,9 @@ public class BlockRenderer implements Disposable{ tileview.clear(); lightview.clear(); - processedEntities.clear(); - processedLinks.clear(); + procEntities.clear(); + procLinks.clear(); + procLights.clear(); int minx = Math.max(avgx - rangex - expandr, 0); int miny = Math.max(avgy - rangey - expandr, 0); @@ -209,25 +210,25 @@ public class BlockRenderer implements Disposable{ tile = tile.build.tile; } - if(block != Blocks.air && block.cacheLayer == CacheLayer.normal && (tile.build == null || !processedEntities.contains(tile.build.id))){ + if(block != Blocks.air && block.cacheLayer == CacheLayer.normal && (tile.build == null || !procEntities.contains(tile.build.id))){ if(block.expanded || !expanded){ - if(tile.build == null || processedLinks.add(tile.build.id)){ + if(tile.build == null || procLinks.add(tile.build.id)){ tileview.add(tile); if(tile.build != null){ - processedEntities.add(tile.build.id); - processedLinks.add(tile.build.id); + procEntities.add(tile.build.id); + procLinks.add(tile.build.id); } } } //lights are drawn even in the expanded range - if(tile.build != null || tile.block().emitLight){ + if(((tile.build != null && procLights.add(tile.build.pos())) || tile.block().emitLight)){ lightview.add(tile); } if(tile.build != null && tile.build.power != null && tile.build.power.links.size > 0){ for(Building other : tile.build.getPowerConnections(outArray2)){ - if(other.block instanceof PowerNode && processedLinks.add(other.id)){ //TODO need a generic way to render connections! + if(other.block instanceof PowerNode && procLinks.add(other.id)){ //TODO need a generic way to render connections! tileview.add(other.tile); } } @@ -235,7 +236,7 @@ public class BlockRenderer implements Disposable{ } //special case for floors - if(block == Blocks.air && tile.floor().emitLight){ + if((block == Blocks.air && tile.floor().emitLight) && procLights.add(tile.pos())){ lightview.add(tile); } } diff --git a/core/src/mindustry/graphics/LightRenderer.java b/core/src/mindustry/graphics/LightRenderer.java index 10ffa727b6..0a05c7409b 100644 --- a/core/src/mindustry/graphics/LightRenderer.java +++ b/core/src/mindustry/graphics/LightRenderer.java @@ -100,7 +100,6 @@ public class LightRenderer{ Draw.vert(ledge.texture, vertices, 0, vertices.length); - Vec2 v3 = Tmp.v2.trnsExact(rot, stroke); u = ledge.u; diff --git a/core/src/mindustry/graphics/MinimapRenderer.java b/core/src/mindustry/graphics/MinimapRenderer.java index cdb84e9044..1c34b4424e 100644 --- a/core/src/mindustry/graphics/MinimapRenderer.java +++ b/core/src/mindustry/graphics/MinimapRenderer.java @@ -96,7 +96,7 @@ public class MinimapRenderer implements Disposable{ Draw.mixcol(unit.team().color, 1f); float scale = Scl.scl(1f) / 2f * scaling * 32f; - Draw.rect(unit.type().icon(Cicon.full), x + rx, y + ry, scale, scale, unit.rotation() - 90); + Draw.rect(unit.type.icon(Cicon.full), x + rx, y + ry, scale, scale, unit.rotation() - 90); Draw.reset(); //only disable player names in multiplayer diff --git a/core/src/mindustry/graphics/OverlayRenderer.java b/core/src/mindustry/graphics/OverlayRenderer.java index c7c0c697c3..524ee79ea6 100644 --- a/core/src/mindustry/graphics/OverlayRenderer.java +++ b/core/src/mindustry/graphics/OverlayRenderer.java @@ -85,7 +85,7 @@ public class OverlayRenderer{ //special selection for block "units" Fill.square(select.x, select.y, ((BlockUnitc)select).tile().block.size * tilesize/2f); }else{ - Draw.rect(select.type().icon(Cicon.full), select.x(), select.y(), select.rotation() - 90); + Draw.rect(select.type.icon(Cicon.full), select.x(), select.y(), select.rotation() - 90); } Lines.stroke(unitFade); diff --git a/core/src/mindustry/graphics/g3d/PlanetGrid.java b/core/src/mindustry/graphics/g3d/PlanetGrid.java index e4972e3eb7..380f937658 100644 --- a/core/src/mindustry/graphics/g3d/PlanetGrid.java +++ b/core/src/mindustry/graphics/g3d/PlanetGrid.java @@ -22,15 +22,16 @@ public class PlanetGrid{ {5, 3, 10, 1, 4}, {2, 5, 4, 0, 11}, {3, 7, 6, 1, 8}, {7, 2, 9, 0, 6} }; - public final int size; - public final Ptile[] tiles; - public final Corner[] corners; - public final Edge[] edges; + public int size; + public Ptile[] tiles; + public Corner[] corners; + public Edge[] edges; - PlanetGrid(int size){ + //this is protected so if you want to make strange grids you should know what you're doing. + protected PlanetGrid(int size){ this.size = size; - tiles = new Ptile[Buildingount(size)]; + tiles = new Ptile[tileCount(size)]; for(int i = 0; i < tiles.length; i++){ tiles[i] = new Ptile(i, i < 12 ? 5 : 6); } @@ -67,7 +68,7 @@ public class PlanetGrid{ return result; } - static PlanetGrid initialGrid(){ + public static PlanetGrid initialGrid(){ PlanetGrid grid = new PlanetGrid(0); for(Ptile t : grid.tiles){ @@ -111,7 +112,7 @@ public class PlanetGrid{ return grid; } - static PlanetGrid subdividedGrid(PlanetGrid prev){ + public static PlanetGrid subdividedGrid(PlanetGrid prev){ PlanetGrid grid = new PlanetGrid(prev.size + 1); int prevTiles = prev.tiles.length; @@ -207,7 +208,7 @@ public class PlanetGrid{ return -1; } - static int Buildingount(int size){ + static int tileCount(int size){ return 10 * Mathf.pow(3, size) + 2; } @@ -220,12 +221,12 @@ public class PlanetGrid{ } public static class Ptile{ - public final int id; - public final int edgeCount; + public int id; + public int edgeCount; - public final Ptile[] tiles; - public final Corner[] corners; - public final Edge[] edges; + public Ptile[] tiles; + public Corner[] corners; + public Edge[] edges; public Vec3 v = new Vec3(); @@ -240,11 +241,11 @@ public class PlanetGrid{ } public static class Corner{ - public final int id; - public final Ptile[] tiles = new Ptile[3]; - public final Corner[] corners = new Corner[3]; - public final Edge[] edges = new Edge[3]; - public final Vec3 v = new Vec3(); + public int id; + public Ptile[] tiles = new Ptile[3]; + public Corner[] corners = new Corner[3]; + public Edge[] edges = new Edge[3]; + public Vec3 v = new Vec3(); public Corner(int id){ this.id = id; @@ -252,9 +253,9 @@ public class PlanetGrid{ } public static class Edge{ - public final int id; - public final Ptile[] tiles = new Ptile[2]; - public final Corner[] corners = new Corner[2]; + public int id; + public Ptile[] tiles = new Ptile[2]; + public Corner[] corners = new Corner[2]; public Edge(int id){ this.id = id; diff --git a/core/src/mindustry/input/DesktopInput.java b/core/src/mindustry/input/DesktopInput.java index 7099f254dd..e66f119f8f 100644 --- a/core/src/mindustry/input/DesktopInput.java +++ b/core/src/mindustry/input/DesktopInput.java @@ -599,11 +599,11 @@ public class DesktopInput extends InputHandler{ } protected void updateMovement(Unit unit){ - boolean omni = unit.type().omniMovement; + boolean omni = unit.type.omniMovement; boolean ground = unit.isGrounded(); - float strafePenalty = ground ? 1f : Mathf.lerp(1f, unit.type().strafePenalty, Angles.angleDist(unit.vel().angle(), unit.rotation()) / 180f); - float baseSpeed = unit.type().speed; + float strafePenalty = ground ? 1f : Mathf.lerp(1f, unit.type.strafePenalty, Angles.angleDist(unit.vel().angle(), unit.rotation()) / 180f); + float baseSpeed = unit.type.speed; //limit speed to minimum formation speed to preserve formation if(unit.isCommanding()){ @@ -611,7 +611,7 @@ public class DesktopInput extends InputHandler{ baseSpeed = unit.minFormationSpeed * 0.95f; } - float speed = baseSpeed * Mathf.lerp(1f, unit.isCommanding() ? 1f : unit.type().canBoost ? unit.type().boostMultiplier : 1f, unit.elevation) * strafePenalty; + float speed = baseSpeed * Mathf.lerp(1f, unit.isCommanding() ? 1f : unit.type.canBoost ? unit.type.boostMultiplier : 1f, unit.elevation) * strafePenalty; float xa = Core.input.axis(Binding.move_x); float ya = Core.input.axis(Binding.move_y); boolean boosted = (unit instanceof Mechc && unit.isFlying()); @@ -622,7 +622,7 @@ public class DesktopInput extends InputHandler{ } float mouseAngle = Angles.mouseAngle(unit.x, unit.y); - boolean aimCursor = omni && player.shooting && unit.type().hasWeapons() && unit.type().faceTarget && !boosted && unit.type().rotateShooting; + boolean aimCursor = omni && player.shooting && unit.type.hasWeapons() && unit.type.faceTarget && !boosted && unit.type.rotateShooting; if(aimCursor){ unit.lookAt(mouseAngle); @@ -637,11 +637,11 @@ public class DesktopInput extends InputHandler{ }else{ unit.moveAt(Tmp.v2.trns(unit.rotation, movement.len())); if(!movement.isZero() && ground){ - unit.vel.rotateTo(movement.angle(), unit.type().rotateSpeed); + unit.vel.rotateTo(movement.angle(), unit.type.rotateSpeed); } } - unit.aim(unit.type().faceTarget ? Core.input.mouseWorld() : Tmp.v1.trns(unit.rotation, Core.input.mouseWorld().dst(unit)).add(unit.x, unit.y)); + unit.aim(unit.type.faceTarget ? Core.input.mouseWorld() : Tmp.v1.trns(unit.rotation, Core.input.mouseWorld().dst(unit)).add(unit.x, unit.y)); unit.controlWeapons(true, player.shooting && !boosted); player.boosting = Core.input.keyDown(Binding.boost) && !movement.isZero(); diff --git a/core/src/mindustry/input/InputHandler.java b/core/src/mindustry/input/InputHandler.java index eebe40e15b..4a5c26eb9d 100644 --- a/core/src/mindustry/input/InputHandler.java +++ b/core/src/mindustry/input/InputHandler.java @@ -158,7 +158,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{ Payloadc pay = (Payloadc)unit; if(target.isAI() && target.isGrounded() && pay.canPickup(target) - && target.within(unit, unit.type().hitSize * 2f + target.type().hitSize * 2f)){ + && target.within(unit, unit.type.hitSize * 2f + target.type.hitSize * 2f)){ Call.pickedUnitPayload(unit, target); } } @@ -365,7 +365,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{ if(commander.isCommanding()){ commander.clearCommand(); - }else if(player.unit().type().commandLimit > 0){ + }else if(player.unit().type.commandLimit > 0){ //TODO try out some other formations commander.commandNearby(new CircleFormation()); @@ -398,17 +398,17 @@ public abstract class InputHandler implements InputProcessor, GestureListener{ } if(player.shooting && !wasShooting && player.unit().hasWeapons() && state.rules.unitAmmo && player.unit().ammo <= 0){ - player.unit().type().weapons.first().noAmmoSound.at(player.unit()); + player.unit().type.weapons.first().noAmmoSound.at(player.unit()); } wasShooting = player.shooting; if(!player.dead()){ - controlledType = player.unit().type(); + controlledType = player.unit().type; } if(controlledType != null && player.dead()){ - Unit unit = Units.closest(player.team(), player.x, player.y, u -> !u.isPlayer() && u.type() == controlledType && !u.dead); + Unit unit = Units.closest(player.team(), player.x, player.y, u -> !u.isPlayer() && u.type == controlledType && !u.dead); if(unit != null){ Call.unitControl(player, unit); @@ -418,7 +418,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{ public void checkUnit(){ if(controlledType != null){ - Unit unit = Units.closest(player.team(), player.x, player.y, u -> !u.isPlayer() && u.type() == controlledType && !u.dead); + Unit unit = Units.closest(player.team(), player.x, player.y, u -> !u.isPlayer() && u.type == controlledType && !u.dead); if(unit == null && controlledType == UnitTypes.block){ unit = world.buildWorld(player.x, player.y) instanceof ControlBlock ? ((ControlBlock)world.buildWorld(player.x, player.y)).unit() : null; } @@ -437,7 +437,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{ Unit unit = player.unit(); if(!(unit instanceof Payloadc pay)) return; - Unit target = Units.closest(player.team(), pay.x(), pay.y(), unit.type().hitSize * 2.5f, u -> u.isAI() && u.isGrounded() && pay.canPickup(u) && u.within(unit, u.hitSize + unit.hitSize * 1.2f)); + Unit target = Units.closest(player.team(), pay.x(), pay.y(), unit.type.hitSize * 2.5f, u -> u.isAI() && u.isGrounded() && pay.canPickup(u) && u.within(unit, u.hitSize + unit.hitSize * 1.2f)); if(target != null){ Call.requestUnitPayload(player, target); }else{ diff --git a/core/src/mindustry/input/MobileInput.java b/core/src/mindustry/input/MobileInput.java index 19f458ea88..d5d45192b0 100644 --- a/core/src/mindustry/input/MobileInput.java +++ b/core/src/mindustry/input/MobileInput.java @@ -85,7 +85,7 @@ public class MobileInput extends InputHandler implements GestureListener{ 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()){ + }else if(tile != null && player.unit().type.canHeal && tile.team == player.team() && tile.damaged()){ player.miner().mineTile(null); target = tile; } @@ -834,10 +834,10 @@ public class MobileInput extends InputHandler implements GestureListener{ protected void updateMovement(Unit unit){ Rect rect = Tmp.r3; - UnitType type = unit.type(); + UnitType type = unit.type; if(type == null) return; - boolean omni = unit.type().omniMovement; + boolean omni = unit.type.omniMovement; boolean legs = unit.isGrounded(); boolean allowHealing = type.canHeal; boolean validHealTarget = allowHealing && target instanceof Building && ((Building)target).isValid() && target.team() == unit.team && @@ -855,7 +855,7 @@ public class MobileInput extends InputHandler implements GestureListener{ float attractDst = 15f; float strafePenalty = legs ? 1f : Mathf.lerp(1f, type.strafePenalty, Angles.angleDist(unit.vel.angle(), unit.rotation) / 180f); - float baseSpeed = unit.type().speed; + float baseSpeed = unit.type.speed; //limit speed to minimum formation speed to preserve formation if(unit.isCommanding()){ diff --git a/core/src/mindustry/logic/LExecutor.java b/core/src/mindustry/logic/LExecutor.java index d493a7f7fe..124f3e0d50 100644 --- a/core/src/mindustry/logic/LExecutor.java +++ b/core/src/mindustry/logic/LExecutor.java @@ -395,7 +395,7 @@ public class LExecutor{ if(unit instanceof Payloadc pay){ //units if(exec.bool(p1)){ - Unit result = Units.closest(unit.team, unit.x, unit.y, unit.type().hitSize * 2f, u -> u.isAI() && u.isGrounded() && pay.canPickup(u) && u.within(unit, u.hitSize + unit.hitSize * 1.2f)); + Unit result = Units.closest(unit.team, unit.x, unit.y, unit.type.hitSize * 2f, u -> u.isAI() && u.isGrounded() && pay.canPickup(u) && u.within(unit, u.hitSize + unit.hitSize * 1.2f)); if(result != null){ Call.pickedUnitPayload(unit, result); diff --git a/core/src/mindustry/maps/SectorDamage.java b/core/src/mindustry/maps/SectorDamage.java index 1c942d1228..02e2c20016 100644 --- a/core/src/mindustry/maps/SectorDamage.java +++ b/core/src/mindustry/maps/SectorDamage.java @@ -11,6 +11,7 @@ import mindustry.entities.abilities.*; import mindustry.game.*; import mindustry.gen.*; import mindustry.logic.*; +import mindustry.type.*; import mindustry.world.*; import mindustry.world.blocks.defense.*; import mindustry.world.blocks.defense.turrets.*; @@ -252,13 +253,16 @@ public class SectorDamage{ if(unit.isPlayer()) continue; if(unit.team == state.rules.defaultTeam){ - sumHealth += unit.health + unit.shield; - sumDps += unit.type().dpsEstimate; + //scale health based on armor - yes, this is inaccurate, but better than nothing + float healthMult = 1f + Mathf.clamp(unit.armor / 20f); + + sumHealth += unit.health*healthMult + unit.shield; + sumDps += unit.type.dpsEstimate; if(unit.abilities.find(a -> a instanceof HealFieldAbility) instanceof HealFieldAbility h){ sumRps += h.amount / h.reload * 60f; } }else{ - curEnemyDps += unit.type().dpsEstimate; + curEnemyDps += unit.type.dpsEstimate; curEnemyHealth += unit.health; } } @@ -277,10 +281,12 @@ public class SectorDamage{ } for(SpawnGroup group : state.rules.spawns){ + float healthMult = 1f + Mathf.clamp(group.type.armor / 20f); + StatusEffect effect = (group.effect == null ? StatusEffects.none : group.effect); int spawned = group.getSpawned(wave); if(spawned <= 0) continue; - sumWaveHealth += spawned * (group.getShield(wave) + group.type.health); - sumWaveDps += spawned * group.type.dpsEstimate; + sumWaveHealth += spawned * (group.getShield(wave) + group.type.health * effect.healthMultiplier * healthMult); + sumWaveDps += spawned * group.type.dpsEstimate * effect.damageMultiplier; } waveDps.add(new Vec2(wave, sumWaveDps)); waveHealth.add(new Vec2(wave, sumWaveHealth)); @@ -295,7 +301,8 @@ public class SectorDamage{ info.waveDpsBase = reg.intercept; info.waveDpsSlope = reg.slope; - info.sumHealth = sumHealth; + //enemy units like to aim for a lot of non-essential things, so increase resulting health slightly + info.sumHealth = sumHealth * 1.2f; info.sumDps = sumDps; info.sumRps = sumRps; @@ -328,7 +335,7 @@ public class SectorDamage{ int radius = 3; //only penetrate a certain % by health, not by distance - float totalHealth = path.sumf(t -> { + float totalHealth = damage >= 1f ? 1f : path.sumf(t -> { float s = 0; for(int dx = -radius; dx <= radius; dx++){ for(int dy = -radius; dy <= radius; dy++){ @@ -345,7 +352,7 @@ public class SectorDamage{ float healthCount = 0; out: - for(int i = 0; i < path.size && healthCount < targetHealth; i++){ + for(int i = 0; i < path.size && (healthCount < targetHealth || damage >= 1f); i++){ Tile t = path.get(i); for(int dx = -radius; dx <= radius; dx++){ @@ -365,7 +372,7 @@ public class SectorDamage{ removal.add(other.build); - if(healthCount >= targetHealth){ + if(healthCount >= targetHealth && damage < 0.999f){ break out; } } @@ -376,6 +383,7 @@ public class SectorDamage{ for(Building r : removal){ if(r.tile.build == r){ + r.addPlan(false); r.tile.remove(); } } @@ -424,6 +432,7 @@ public class SectorDamage{ Effect.rubble(other.build.x, other.build.y, other.block().size); } + other.build.addPlan(false); other.remove(); } } diff --git a/core/src/mindustry/maps/planet/SerpuloPlanetGenerator.java b/core/src/mindustry/maps/planet/SerpuloPlanetGenerator.java index ee2f6b437c..9065467dff 100644 --- a/core/src/mindustry/maps/planet/SerpuloPlanetGenerator.java +++ b/core/src/mindustry/maps/planet/SerpuloPlanetGenerator.java @@ -412,12 +412,12 @@ public class SerpuloPlanetGenerator extends PlanetGenerator{ if(sector.hasEnemyBase()){ basegen.generate(tiles, enemies.map(r -> tiles.getn(r.x, r.y)), tiles.get(spawn.x, spawn.y), state.rules.waveTeam, sector, difficulty); - state.rules.attackMode = true; + state.rules.attackMode = sector.info.attack = true; }else{ - state.rules.winWave = 15 * (int)Math.max(difficulty * 10, 1); + state.rules.winWave = sector.info.winWave = 15 * (int)Math.max(difficulty * 10, 1); } - state.rules.waves = true; + state.rules.waves = sector.info.waves = true; //TODO better waves state.rules.spawns = DefaultWaves.generate(difficulty); diff --git a/core/src/mindustry/mod/ContentParser.java b/core/src/mindustry/mod/ContentParser.java index b9c119ee32..c8085ae8c8 100644 --- a/core/src/mindustry/mod/ContentParser.java +++ b/core/src/mindustry/mod/ContentParser.java @@ -260,8 +260,8 @@ public class ContentParser{ //TODO test this! read(() -> { //add reconstructor type - if(value.hasChild("requirements")){ - JsonValue rec = value.remove("requirements"); + if(value.has("requirements")){ + JsonValue rec = value.remove("requirements"); //intermediate class for parsing class UnitReq{ @@ -286,6 +286,17 @@ public class ContentParser{ } + //read extra default waves + if(value.has("waves")){ + JsonValue waves = value.remove("waves"); + SpawnGroup[] groups = parser.readValue(SpawnGroup[].class, waves); + for(SpawnGroup group : groups){ + group.type = unit; + } + + Vars.defaultWaves.get().addAll(groups); + } + readFields(unit, value, true); }); diff --git a/core/src/mindustry/type/AmmoTypes.java b/core/src/mindustry/type/AmmoTypes.java index 5b7c885519..534bdbe703 100644 --- a/core/src/mindustry/type/AmmoTypes.java +++ b/core/src/mindustry/type/AmmoTypes.java @@ -48,8 +48,8 @@ public class AmmoTypes implements ContentList{ if(build.block.consumes.hasPower() && build.block.consumes.getPower().buffered){ float amount = closest.build.power.status * build.block.consumes.getPower().capacity; - float powerPerAmmo = totalPower / unit.type().ammoCapacity; - float ammoRequired = unit.type().ammoCapacity - unit.ammo; + float powerPerAmmo = totalPower / unit.type.ammoCapacity; + float ammoRequired = unit.type.ammoCapacity - unit.ammo; float powerRequired = ammoRequired * powerPerAmmo; float powerTaken = Math.min(amount, powerRequired); diff --git a/core/src/mindustry/type/ItemStack.java b/core/src/mindustry/type/ItemStack.java index d5f4097b58..e2e0d2d5f4 100644 --- a/core/src/mindustry/type/ItemStack.java +++ b/core/src/mindustry/type/ItemStack.java @@ -64,6 +64,13 @@ public class ItemStack implements Comparable{ return item.compareTo(itemStack.item); } + @Override + public boolean equals(Object o){ + if(this == o) return true; + if(!(o instanceof ItemStack stack)) return false; + return amount == stack.amount && item == stack.item; + } + @Override public String toString(){ return "ItemStack{" + diff --git a/core/src/mindustry/type/SectorPreset.java b/core/src/mindustry/type/SectorPreset.java index 923d023b25..f08374fa70 100644 --- a/core/src/mindustry/type/SectorPreset.java +++ b/core/src/mindustry/type/SectorPreset.java @@ -23,6 +23,7 @@ public class SectorPreset extends UnlockableContent{ super(name); this.generator = new FileMapGenerator(name); this.planet = planet; + sector %= planet.sectors.size; this.sector = planet.sectors.get(sector); planet.preset(sector, this); diff --git a/core/src/mindustry/type/StatusEffect.java b/core/src/mindustry/type/StatusEffect.java index eb0bedb60c..90488c62ea 100644 --- a/core/src/mindustry/type/StatusEffect.java +++ b/core/src/mindustry/type/StatusEffect.java @@ -56,7 +56,7 @@ public class StatusEffect extends MappableContent{ } if(effect != Fx.none && Mathf.chanceDelta(effectChance)){ - Tmp.v1.rnd(unit.type().hitSize /2f); + Tmp.v1.rnd(unit.type.hitSize /2f); effect.at(unit.x + Tmp.v1.x, unit.y + Tmp.v1.y); } } diff --git a/core/src/mindustry/type/UnitType.java b/core/src/mindustry/type/UnitType.java index 68fdd6837e..7d0c3fd98b 100644 --- a/core/src/mindustry/type/UnitType.java +++ b/core/src/mindustry/type/UnitType.java @@ -117,7 +117,7 @@ public class UnitType extends UnlockableContent{ public Unit create(Team team){ Unit unit = constructor.get(); unit.team = team; - unit.type(this); + unit.setType(this); unit.ammo = ammoCapacity; //fill up on ammo upon creation unit.elevation = flying ? 1f : 0; unit.heal(); diff --git a/core/src/mindustry/ui/dialogs/SettingsMenuDialog.java b/core/src/mindustry/ui/dialogs/SettingsMenuDialog.java index efcc6e68bb..c00076d92b 100644 --- a/core/src/mindustry/ui/dialogs/SettingsMenuDialog.java +++ b/core/src/mindustry/ui/dialogs/SettingsMenuDialog.java @@ -339,9 +339,6 @@ public class SettingsMenuDialog extends SettingsDialog{ graphics.checkPref("smoothcamera", true); graphics.checkPref("position", false); graphics.checkPref("fps", false); - if(!mobile){ - graphics.checkPref("blockselectkeys", true); - } graphics.checkPref("playerindicators", true); graphics.checkPref("indicators", true); graphics.checkPref("animatedwater", true); diff --git a/core/src/mindustry/ui/fragments/HudFragment.java b/core/src/mindustry/ui/fragments/HudFragment.java index 327e65cd13..d51c83881d 100644 --- a/core/src/mindustry/ui/fragments/HudFragment.java +++ b/core/src/mindustry/ui/fragments/HudFragment.java @@ -712,7 +712,7 @@ public class HudFragment extends Fragment{ t.add(new SideBar(() -> player.unit().healthf(), () -> true, true)).width(bw).growY().padRight(pad); t.image(() -> player.icon()).scaling(Scaling.bounded).grow().maxWidth(54f); t.add(new SideBar(() -> player.dead() ? 0f : player.displayAmmo() ? player.unit().ammof() : player.unit().healthf(), () -> !player.displayAmmo(), false)).width(bw).growY().padLeft(pad).update(b -> { - b.color.set(player.displayAmmo() ? player.dead() || player.unit() instanceof BlockUnitc ? Pal.ammo : player.unit().type().ammoType.color : Pal.health); + b.color.set(player.displayAmmo() ? player.dead() || player.unit() instanceof BlockUnitc ? Pal.ammo : player.unit().type.ammoType.color : Pal.health); }); t.getChildren().get(1).toFront(); diff --git a/core/src/mindustry/ui/fragments/PlacementFragment.java b/core/src/mindustry/ui/fragments/PlacementFragment.java index 7b53a24012..25c009ff63 100644 --- a/core/src/mindustry/ui/fragments/PlacementFragment.java +++ b/core/src/mindustry/ui/fragments/PlacementFragment.java @@ -289,7 +289,7 @@ public class PlacementFragment extends Fragment{ topTable.table(header -> { String keyCombo = ""; - if(!mobile && Core.settings.getBool("blockselectkeys")){ + if(!mobile){ Seq blocks = getByCategory(currentCategory); for(int i = 0; i < blocks.size; i++){ if(blocks.get(i) == displayBlock && (i + 1) / 10 - 1 < blockSelect.length){ diff --git a/core/src/mindustry/world/blocks/campaign/LaunchPad.java b/core/src/mindustry/world/blocks/campaign/LaunchPad.java index dd1cc69007..860eb522d2 100644 --- a/core/src/mindustry/world/blocks/campaign/LaunchPad.java +++ b/core/src/mindustry/world/blocks/campaign/LaunchPad.java @@ -115,6 +115,8 @@ public class LaunchPad extends Block{ public void display(Table table){ super.display(table); + if(!state.isCampaign()) return; + table.row(); table.label(() -> { Sector dest = state.secinfo.getRealDestination(); diff --git a/core/src/mindustry/world/blocks/defense/TractorBeamTurret.java b/core/src/mindustry/world/blocks/defense/TractorBeamTurret.java index 1f74360ed5..3808f7c360 100644 --- a/core/src/mindustry/world/blocks/defense/TractorBeamTurret.java +++ b/core/src/mindustry/world/blocks/defense/TractorBeamTurret.java @@ -75,7 +75,7 @@ public class TractorBeamTurret extends Block{ } //look at target - if(target != null && target.within(this, range) && target.team() != team && target.type().flying && efficiency() > 0.01f){ + if(target != null && target.within(this, range) && target.team() != team && target.type.flying && efficiency() > 0.01f){ any = true; float dest = angleTo(target); rotation = Angles.moveToward(rotation, dest, rotateSpeed * edelta()); diff --git a/core/src/mindustry/world/blocks/payloads/UnitPayload.java b/core/src/mindustry/world/blocks/payloads/UnitPayload.java index 724df792fa..2292244864 100644 --- a/core/src/mindustry/world/blocks/payloads/UnitPayload.java +++ b/core/src/mindustry/world/blocks/payloads/UnitPayload.java @@ -43,7 +43,7 @@ public class UnitPayload implements Payload{ @Override public boolean dump(){ - if(!Units.canCreate(unit.team, unit.type())){ + if(!Units.canCreate(unit.team, unit.type)){ deactiveTime = 1f; return false; } @@ -74,7 +74,7 @@ public class UnitPayload implements Payload{ @Override public void draw(){ Drawf.shadow(unit.x, unit.y, 20); - Draw.rect(unit.type().icon(Cicon.full), unit.x, unit.y, unit.rotation - 90); + Draw.rect(unit.type.icon(Cicon.full), unit.x, unit.y, unit.rotation - 90); //draw warning if(deactiveTime > 0){ diff --git a/core/src/mindustry/world/blocks/storage/CoreBlock.java b/core/src/mindustry/world/blocks/storage/CoreBlock.java index b8c240e95d..1acf5d61c3 100644 --- a/core/src/mindustry/world/blocks/storage/CoreBlock.java +++ b/core/src/mindustry/world/blocks/storage/CoreBlock.java @@ -333,7 +333,7 @@ public class CoreBlock extends StorageBlock{ @Override public void itemTaken(Item item){ - if(state.isCampaign()){ + if(state.isCampaign() && team == state.rules.defaultTeam){ //update item taken amount state.secinfo.handleCoreItem(item, -1); } @@ -342,7 +342,9 @@ public class CoreBlock extends StorageBlock{ @Override public void handleItem(Building source, Item item){ if(net.server() || !net.active()){ - state.secinfo.handleCoreItem(item, 1); + if(team == state.rules.defaultTeam){ + state.secinfo.handleCoreItem(item, 1); + } if(items.get(item) >= getMaximumAccepted(item)){ //create item incineration effect at random intervals diff --git a/core/src/mindustry/world/blocks/storage/StorageBlock.java b/core/src/mindustry/world/blocks/storage/StorageBlock.java index a8a5578d95..71045e166b 100644 --- a/core/src/mindustry/world/blocks/storage/StorageBlock.java +++ b/core/src/mindustry/world/blocks/storage/StorageBlock.java @@ -26,7 +26,7 @@ public class StorageBlock extends Block{ } public static void incinerateEffect(Building self, Building source){ - if(Mathf.chance(0.1)){ + if(Mathf.chance(0.3)){ Tile edge = Edges.getFacingEdge(source, self); Tile edge2 = Edges.getFacingEdge(self, source); if(edge != null && edge2 != null){ @@ -46,7 +46,9 @@ public class StorageBlock extends Block{ @Override public void handleItem(Building source, Item item){ if(linkedCore != null){ - incinerateEffect(this, source); + if(linkedCore.items.get(item) >= ((CoreBuild)linkedCore).storageCapacity){ + incinerateEffect(this, source); + } ((CoreBuild)linkedCore).noEffect = true; linkedCore.handleItem(source, item); }else{ diff --git a/core/src/mindustry/world/blocks/units/Reconstructor.java b/core/src/mindustry/world/blocks/units/Reconstructor.java index 5b5696bf4c..326a9f06eb 100644 --- a/core/src/mindustry/world/blocks/units/Reconstructor.java +++ b/core/src/mindustry/world/blocks/units/Reconstructor.java @@ -90,7 +90,7 @@ public class Reconstructor extends UnitBlock{ return this.payload == null && relativeTo(source) != rotation && payload instanceof UnitPayload - && hasUpgrade(((UnitPayload)payload).unit.type()); + && hasUpgrade(((UnitPayload)payload).unit.type); } @Override @@ -114,9 +114,9 @@ public class Reconstructor extends UnitBlock{ if(constructing() && hasArrived()){ Draw.draw(Layer.blockOver, () -> { Draw.alpha(1f - progress/ constructTime); - Draw.rect(payload.unit.type().icon(Cicon.full), x, y, rotdeg() - 90); + Draw.rect(payload.unit.type.icon(Cicon.full), x, y, rotdeg() - 90); Draw.reset(); - Drawf.construct(this, upgrade(payload.unit.type()), rotdeg() - 90f, progress / constructTime, speedScl, time); + Drawf.construct(this, upgrade(payload.unit.type), rotdeg() - 90f, progress / constructTime, speedScl, time); }); }else{ Draw.z(Layer.blockOver); @@ -135,7 +135,7 @@ public class Reconstructor extends UnitBlock{ if(payload != null){ //check if offloading - if(!hasUpgrade(payload.unit.type())){ + if(!hasUpgrade(payload.unit.type)){ moveOutPayload(); }else{ //update progress if(moveInPayload()){ @@ -146,7 +146,7 @@ public class Reconstructor extends UnitBlock{ //upgrade the unit if(progress >= constructTime){ - payload.unit = upgrade(payload.unit.type()).create(payload.unit.team()); + payload.unit = upgrade(payload.unit.type).create(payload.unit.team()); progress = 0; Effect.shake(2f, 3f, this); Fx.producesmoke.at(this); @@ -168,12 +168,12 @@ public class Reconstructor extends UnitBlock{ public UnitType unit(){ if(payload == null) return null; - UnitType t = upgrade(payload.unit.type()); + UnitType t = upgrade(payload.unit.type); return t != null && t.unlockedNow() ? t : null; } public boolean constructing(){ - return payload != null && hasUpgrade(payload.unit.type()); + return payload != null && hasUpgrade(payload.unit.type); } public boolean hasUpgrade(UnitType type){ diff --git a/core/src/mindustry/world/blocks/units/ResupplyPoint.java b/core/src/mindustry/world/blocks/units/ResupplyPoint.java index 2714be2358..6402b20147 100644 --- a/core/src/mindustry/world/blocks/units/ResupplyPoint.java +++ b/core/src/mindustry/world/blocks/units/ResupplyPoint.java @@ -65,10 +65,10 @@ public class ResupplyPoint extends Block{ public static boolean resupply(Team team, float x, float y, float range, float ammoAmount, Color ammoColor, Boolf valid){ if(!state.rules.unitAmmo) return false; - Unit unit = Units.closest(team, x, y, range, u -> u.type().ammoType instanceof ItemAmmoType && u.ammo <= u.type().ammoCapacity - ammoAmount && valid.get(u)); + Unit unit = Units.closest(team, x, y, range, u -> u.type.ammoType instanceof ItemAmmoType && u.ammo <= u.type.ammoCapacity - ammoAmount && valid.get(u)); if(unit != null){ Fx.itemTransfer.at(x, y, ammoAmount / 2f, ammoColor, unit); - unit.ammo = Math.min(unit.ammo + ammoAmount, unit.type().ammoCapacity); + unit.ammo = Math.min(unit.ammo + ammoAmount, unit.type.ammoCapacity); return true; } diff --git a/core/src/mindustry/world/blocks/units/UnitFactory.java b/core/src/mindustry/world/blocks/units/UnitFactory.java index 74248f9932..52a3af85c7 100644 --- a/core/src/mindustry/world/blocks/units/UnitFactory.java +++ b/core/src/mindustry/world/blocks/units/UnitFactory.java @@ -14,6 +14,7 @@ import mindustry.entities.*; import mindustry.entities.units.*; import mindustry.gen.*; import mindustry.graphics.*; +import mindustry.logic.*; import mindustry.type.*; import mindustry.ui.*; import mindustry.world.blocks.*; @@ -122,6 +123,12 @@ public class UnitFactory extends UnitBlock{ return currentPlan == -1 ? 0 : progress / plans.get(currentPlan).time; } + @Override + public Object senseObject(LAccess sensor){ + if(sensor == LAccess.config) return currentPlan == -1 ? null : plans.get(currentPlan).unit; + return super.senseObject(sensor); + } + @Override public void buildConfiguration(Table table){ Seq units = Seq.with(plans).map(u -> u.unit).filter(u -> u.unlockedNow()); diff --git a/desktop/src/mindustry/desktop/steam/SStats.java b/desktop/src/mindustry/desktop/steam/SStats.java index 7e7c9ecef3..925c670223 100644 --- a/desktop/src/mindustry/desktop/steam/SStats.java +++ b/desktop/src/mindustry/desktop/steam/SStats.java @@ -60,7 +60,7 @@ public class SStats implements SteamUserStatsCallback{ // active10Phantoms.complete(); //} - if(Groups.unit.count(u -> u.type() == UnitTypes.crawler && u.team() == player.team()) >= 50){ + if(Groups.unit.count(u -> u.type == UnitTypes.crawler && u.team() == player.team()) >= 50){ active50Crawlers.complete(); }