diff --git a/.github/workflows/deployment.yml b/.github/workflows/deployment.yml index 1532c48561..bc993bae07 100644 --- a/.github/workflows/deployment.yml +++ b/.github/workflows/deployment.yml @@ -21,6 +21,8 @@ jobs: - name: Update docs run: | cd ../ + git config --global user.email "cli@github.com" + git config --global user.name "Github Actions" git clone --depth=1 https://github.com/MindustryGame/docs.git cp -a Mindustry/core/build/docs/javadoc/. docs/ cd docs @@ -48,12 +50,12 @@ jobs: uses: svenstaro/upload-release-action@v2 with: repo_token: ${{ secrets.GITHUB_TOKEN }} - file: desktop/libs/Mindustry.jar + file: desktop/build/libs/Mindustry.jar tag: ${{ github.ref }} - name: Upload server artifacts uses: svenstaro/upload-release-action@v2 with: repo_token: ${{ secrets.GITHUB_TOKEN }} - file: server/libs/server-release.jar + file: server/build/libs/server-release.jar tag: ${{ github.ref }} diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index 9436adb06c..cf01e4bc68 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -15,6 +15,7 @@ jobs: - name: Run unit tests run: ./gradlew test - name: Trigger BE build + if: ${{ github.repository == 'Anuken/Mindustry' }} run: | git clone --depth=1 --branch=master https://github.com/Anuken/MindustryBuilds ../MindustryBuilds cd ../MindustryBuilds diff --git a/core/assets/bundles/bundle.properties b/core/assets/bundles/bundle.properties index 82654724eb..068068ff94 100644 --- a/core/assets/bundles/bundle.properties +++ b/core/assets/bundles/bundle.properties @@ -22,7 +22,6 @@ gameover.pvp = The[accent] {0}[] team is victorious! gameover.waiting = [accent]Waiting for next map... highscore = [accent]New highscore! copied = Copied. -indev.popup = [accent]v6[] is currently in [accent]beta[].\n[lightgray]This means:[]\n[scarlet]- The campaign is unfinished[]\n- Everything you see is subject to change or removal.\n\nReport bugs or crashes on [accent]Github[]. indev.notready = This part of the game isn't ready yet indev.campaign = [accent]You've reached the end of the campaign![]\n\nThis is as far as the content goes. Interplanetary travel will be added in future updates. @@ -147,6 +146,7 @@ planetmap = Planet Map launchcore = Launch Core filename = File Name: unlocked = New content unlocked! +available = New research available! completed = [accent]Completed techtree = Tech Tree research.list = [lightgray]Research: @@ -230,6 +230,7 @@ disconnect.timeout = Timed out. disconnect.data = Failed to load world data! cantconnect = Unable to join game ([accent]{0}[]). connecting = [accent]Connecting... +reconnecting = [accent]Reconnecting... connecting.data = [accent]Loading world data... server.port = Port: server.addressinuse = Address already in use! @@ -593,6 +594,11 @@ sector.tarFields.description = The outskirts of an oil production zone, between sector.desolateRift.description = An extremely dangerous zone. Plentiful resources, but little space. High risk of destruction. Leave as soon as possible. Do not be fooled by the long spacing between enemy attacks. sector.nuclearComplex.description = A former facility for the production and processing of thorium, reduced to ruins.\n[lightgray]Research the thorium and its many uses.\n\nThe enemy is present here in great numbers, constantly scouting for attackers. sector.fungalPass.description = A transition area between high mountains and lower, spore-ridden lands. A small enemy reconnaissance base is located here.\nDestroy it.\nUse Dagger and Crawler units. Take out the two cores. +sector.biomassFacility.description = The origin of spores. This is the facility in which they were researched and initially produced.\nResearch the technology contained within. Cultivate spores for the production of fuel and plastics.\n\n[lightgray]Upon this facility's demise, the spores were released. Nothing in the local ecosystem could compete with such an invasive organism. +sector.windsweptIslands.description = Further past the shoreline is this remote chain of islands. Records show they once had [accent]Plastanium[]-producing structures.\n\nFend off the enemy's naval units. Establish a base on the islands. Research these factories. +sector.extractionOutpost.description = A remote outpost, constructed by the enemy for the purpose of launching resources to other sectors.\n\nCross-sector transport technology is essential for further conquest. Destroy the base. Research their Launch Pads. +sector.impact0078.description = Here lie remnants of the interstellar transport vessel that first entered this system.\n\nSalvage as much as possible from the wreckage. Research any intact technology. +sector.planetaryTerminal.description = The final target.\n\nThis coastal base contains a structure capable of launching Cores to local planets. It is extremely well guarded.\n\nProduce naval units. Eliminate the enemy as quickly as possible. Research the launch structure. settings.language = Language settings.data = Game Data @@ -1261,7 +1267,7 @@ hint.schematicSelect = Hold [accent][[F][] and drag to select blocks to copy and hint.conveyorPathfind = Hold [accent][[L-Ctrl][] while dragging conveyors to automatically generate a path. hint.conveyorPathfind.mobile = Enable \ue844 [accent]diagonal mode[] and drag conveyors to automatically generate a path. hint.boost = Hold [accent][[L-Shift][] to fly over obstacles with your current unit.\n\nOnly a few ground units have boosters. -hint.command = Press [accent][[G][] to command nearby units into formation. +hint.command = Press [accent][[G][] to command nearby units of [accent]similar type[] into formation.\n\nTo command ground units, you must first control another ground unit. hint.command.mobile = [accent][[Double-tap][] your unit to command nearby units into formation. hint.payloadPickup = Press [accent][[[] to pick up small blocks or units. hint.payloadPickup.mobile = [accent]Tap and hold[] a small block or unit to pick it up. @@ -1269,6 +1275,7 @@ hint.payloadDrop = Press [accent]][] to drop a payload. hint.payloadDrop.mobile = [accent]Tap and hold[] an empty location to drop a payload there. hint.waveFire = [accent]Wave[] turrets with water as ammunition will automatically put out nearby fires. hint.generator = \uf879 [accent]Combustion Generators[] burn coal and transmit power to adjacent blocks.\n\nPower transmission range can be extended with \uf87f [accent]Power Nodes[]. +hint.guardian = [accent]Guardian[] units are armored. Weak ammo such as [accent]Copper[] and [accent]Lead[] is [scarlet]not effective[].\n\nUse higher tier turrets or \uf835 [accent]Graphite[] \uf861Duo/\uf859Salvo ammunition to take Guardians down. item.copper.description = Used in all types of construction and ammunition. item.copper.details = Copper. Abnormally abundant metal on Serpulo. Structurally weak unless reinforced. diff --git a/core/assets/contributors b/core/assets/contributors index 39c29ff93a..cb64660dae 100644 --- a/core/assets/contributors +++ b/core/assets/contributors @@ -100,4 +100,5 @@ MEEP of Faith jalastram (freesound.org) newlocknew (freesound.org) dsmolenaers (freesound.org) -Headphaze (freesound.org) \ No newline at end of file +Headphaze (freesound.org) +VolasYouKnow diff --git a/core/assets/maps/ruinousShores.msav b/core/assets/maps/ruinousShores.msav index 41e957c0ff..48727feae4 100644 Binary files a/core/assets/maps/ruinousShores.msav and b/core/assets/maps/ruinousShores.msav differ diff --git a/core/assets/maps/saltFlats.msav b/core/assets/maps/saltFlats.msav index 556b33700f..f4979da57b 100644 Binary files a/core/assets/maps/saltFlats.msav and b/core/assets/maps/saltFlats.msav differ diff --git a/core/assets/maps/stainedMountains.msav b/core/assets/maps/stainedMountains.msav index af3abb26c2..082f96ad7b 100644 Binary files a/core/assets/maps/stainedMountains.msav and b/core/assets/maps/stainedMountains.msav differ diff --git a/core/assets/maps/windsweptIslands.msav b/core/assets/maps/windsweptIslands.msav index d5e838b60d..2c8547f6f9 100644 Binary files a/core/assets/maps/windsweptIslands.msav and b/core/assets/maps/windsweptIslands.msav differ diff --git a/core/src/mindustry/Vars.java b/core/src/mindustry/Vars.java index e41b9a08b7..21a8b3fb45 100644 --- a/core/src/mindustry/Vars.java +++ b/core/src/mindustry/Vars.java @@ -88,7 +88,7 @@ public class Vars implements Loadable{ /** duration of time between turns in ticks */ public static final float turnDuration = 2 * Time.toMinutes; /** chance of an invasion per turn, 1 = 100% */ - public static final float baseInvasionChance = 1f / 50f; + public static final float baseInvasionChance = 1f / 75f; /** how many turns have to pass before invasions start */ public static final int invasionGracePeriod = 20; /** min armor fraction damage; e.g. 0.05 = at least 5% damage */ diff --git a/core/src/mindustry/ai/BaseAI.java b/core/src/mindustry/ai/BaseAI.java index 7900b6608c..395493d57f 100644 --- a/core/src/mindustry/ai/BaseAI.java +++ b/core/src/mindustry/ai/BaseAI.java @@ -17,6 +17,7 @@ import mindustry.gen.*; import mindustry.type.*; import mindustry.world.*; import mindustry.world.blocks.defense.*; +import mindustry.world.blocks.distribution.*; import mindustry.world.blocks.production.*; import mindustry.world.blocks.storage.*; import mindustry.world.blocks.storage.CoreBlock.*; @@ -271,6 +272,10 @@ public class BaseAI{ } Tile o = world.tile(tile.x + p.x, tile.y + p.y); + if(o != null && (o.block() instanceof PayloadAcceptor || o.block() instanceof PayloadConveyor)){ + break; + } + if(o != null && o.team() == data.team && !(o.block() instanceof Wall)){ any = true; break; diff --git a/core/src/mindustry/ai/types/BuilderAI.java b/core/src/mindustry/ai/types/BuilderAI.java index a82a220070..f900a60955 100644 --- a/core/src/mindustry/ai/types/BuilderAI.java +++ b/core/src/mindustry/ai/types/BuilderAI.java @@ -1,5 +1,6 @@ package mindustry.ai.types; +import arc.math.*; import arc.struct.*; import arc.util.*; import mindustry.entities.*; @@ -83,8 +84,10 @@ public class BuilderAI extends AIController{ }); } + float rebuildTime = (unit.team.rules().ai ? Mathf.lerp(15f, 2f, unit.team.rules().aiTier) : 2f) * 60f; + //find new request - if(!unit.team.data().blocks.isEmpty() && following == null && timer.get(timerTarget3, 60 * 2f)){ + if(!unit.team.data().blocks.isEmpty() && following == null && timer.get(timerTarget3, rebuildTime)){ Queue blocks = unit.team.data().blocks; BlockPlan block = blocks.first(); diff --git a/core/src/mindustry/content/Blocks.java b/core/src/mindustry/content/Blocks.java index b6f8e76b14..8fa2510a57 100644 --- a/core/src/mindustry/content/Blocks.java +++ b/core/src/mindustry/content/Blocks.java @@ -363,7 +363,7 @@ public class Blocks implements ContentList{ sandWall = new StaticWall("sand-wall"){{ variants = 2; - sandWater.asFloor().wall = this; + sandWater.asFloor().wall = water.asFloor().wall = deepwater.asFloor().wall = this; }}; saltWall = new StaticWall("salt-wall"); @@ -1195,7 +1195,7 @@ public class Blocks implements ContentList{ size = 2; ambientSound = Sounds.smelter; - ambientSoundVolume = 0.05f; + ambientSoundVolume = 0.06f; }}; differentialGenerator = new SingleTypeGenerator("differential-generator"){{ @@ -1216,7 +1216,7 @@ public class Blocks implements ContentList{ requirements(Category.power, with(Items.lead, 100, Items.silicon, 75, Items.phaseFabric, 25, Items.plastanium, 75, Items.thorium, 50)); size = 2; powerProduction = 4.5f; - itemDuration = 60 * 15f; + itemDuration = 60 * 14f; }}; solarPanel = new SolarGenerator("solar-panel"){{ @@ -1377,7 +1377,7 @@ public class Blocks implements ContentList{ itemCapacity = 9000; size = 4; - unitCapModifier = 14; + unitCapModifier = 16; researchCostMultiplier = 0.04f; }}; @@ -1389,7 +1389,7 @@ public class Blocks implements ContentList{ itemCapacity = 13000; size = 5; - unitCapModifier = 20; + unitCapModifier = 24; researchCostMultiplier = 0.05f; }}; @@ -1517,12 +1517,12 @@ public class Blocks implements ContentList{ lancer = new ChargeTurret("lancer"){{ requirements(Category.turret, with(Items.copper, 25, Items.lead, 50, Items.silicon, 45)); - range = 155f; - chargeTime = 50f; + range = 165f; + chargeTime = 40f; chargeMaxDelay = 30f; chargeEffects = 7; recoilAmount = 2f; - reloadTime = 90f; + reloadTime = 80f; cooldown = 0.03f; powerUse = 6f; shootShake = 2f; @@ -1544,6 +1544,7 @@ public class Blocks implements ContentList{ lifetime = 16f; drawSize = 400f; collidesAir = false; + length = 173f; }}; }}; @@ -1557,7 +1558,7 @@ public class Blocks implements ContentList{ reloadTime = 35f; shootCone = 40f; rotateSpeed = 8f; - powerUse = 3f; + powerUse = 3.3f; targetAir = false; range = 90f; shootEffect = Fx.lightningShoot; diff --git a/core/src/mindustry/content/Items.java b/core/src/mindustry/content/Items.java index 6d3964fd2c..2c2e5eb60b 100644 --- a/core/src/mindustry/content/Items.java +++ b/core/src/mindustry/content/Items.java @@ -72,6 +72,7 @@ public class Items implements ContentList{ }}; surgeAlloy = new Item("surge-alloy", Color.valueOf("f3e979")){{ + cost = 1.2f; }}; sporePod = new Item("spore-pod", Color.valueOf("7457ce")){{ diff --git a/core/src/mindustry/content/SectorPresets.java b/core/src/mindustry/content/SectorPresets.java index 75583b19bf..dbfddbc142 100644 --- a/core/src/mindustry/content/SectorPresets.java +++ b/core/src/mindustry/content/SectorPresets.java @@ -24,10 +24,11 @@ public class SectorPresets implements ContentList{ saltFlats = new SectorPreset("saltFlats", serpulo, 101){{ difficulty = 5; + useAI = false; }}; frozenForest = new SectorPreset("frozenForest", serpulo, 86){{ - captureWave = 20; + captureWave = 15; difficulty = 2; }}; diff --git a/core/src/mindustry/content/TechTree.java b/core/src/mindustry/content/TechTree.java index d216a665e1..cf77c5819b 100644 --- a/core/src/mindustry/content/TechTree.java +++ b/core/src/mindustry/content/TechTree.java @@ -121,7 +121,7 @@ public class TechTree implements ContentList{ }); - node(waterExtractor, () -> { + node(waterExtractor, Seq.with(new SectorComplete(saltFlats)), () -> { node(oilExtractor, () -> { }); @@ -198,10 +198,10 @@ public class TechTree implements ContentList{ }); }); }); - }); - node(illuminator, () -> { - + node(illuminator, () -> { + + }); }); }); @@ -421,7 +421,7 @@ public class TechTree implements ContentList{ }); node(additiveReconstructor, Seq.with(new SectorComplete(biomassFacility)), () -> { - node(multiplicativeReconstructor, Seq.with(new SectorComplete(overgrowth)), () -> { + node(multiplicativeReconstructor, () -> { node(exponentialReconstructor, () -> { node(tetrativeReconstructor, () -> { @@ -484,6 +484,7 @@ public class TechTree implements ContentList{ new Research(bryde), new Research(spectre), new Research(launchPad), + new Research(massDriver), new Research(impactReactor), new Research(additiveReconstructor), new Research(exponentialReconstructor) @@ -507,7 +508,9 @@ public class TechTree implements ContentList{ node(saltFlats, Seq.with( new SectorComplete(windsweptIslands), + new Research(commandCenter), new Research(groundFactory), + new Research(additiveReconstructor), new Research(airFactory), new Research(door), new Research(waterExtractor) @@ -645,7 +648,7 @@ public class TechTree implements ContentList{ static TechNode node(UnlockableContent content, ItemStack[] requirements, Seq objectives, Runnable children){ TechNode node = new TechNode(context, content, requirements); if(objectives != null){ - node.objectives = objectives; + node.objectives.addAll(objectives); } TechNode prev = context; diff --git a/core/src/mindustry/content/UnitTypes.java b/core/src/mindustry/content/UnitTypes.java index ed54cf324f..023eeb5631 100644 --- a/core/src/mindustry/content/UnitTypes.java +++ b/core/src/mindustry/content/UnitTypes.java @@ -556,7 +556,7 @@ public class UnitTypes implements ContentList{ range = 40f; weapons.add(new Weapon(){{ - reload = 12f; + reload = 24f; shootCone = 180f; ejectEffect = Fx.none; shootSound = Sounds.explosion; @@ -1321,7 +1321,7 @@ public class UnitTypes implements ContentList{ sprite = "large-bomb"; width = height = 120/4f; - range = 30f; + maxRange = 30f; ignoreRotation = true; backColor = Pal.heal; @@ -1413,12 +1413,12 @@ public class UnitTypes implements ContentList{ ejectEffect = Fx.casing1; shootSound = Sounds.missile; bullet = new MissileBulletType(2.7f, 12, "missile"){{ + keepVelocity = true; width = 8f; height = 8f; shrinkY = 0f; drag = -0.003f; homingRange = 60f; - keepVelocity = false; splashDamageRadius = 25f; splashDamage = 10f; lifetime = 80f; diff --git a/core/src/mindustry/core/Control.java b/core/src/mindustry/core/Control.java index cd1768b25c..bc5acc066f 100644 --- a/core/src/mindustry/core/Control.java +++ b/core/src/mindustry/core/Control.java @@ -6,6 +6,7 @@ import arc.audio.*; import arc.graphics.g2d.*; import arc.input.*; import arc.math.*; +import arc.scene.style.*; import arc.scene.ui.*; import arc.struct.*; import arc.util.*; @@ -16,14 +17,17 @@ import mindustry.core.GameState.*; import mindustry.entities.*; import mindustry.game.EventType.*; import mindustry.game.*; +import mindustry.game.Objectives.*; import mindustry.game.Saves.*; import mindustry.gen.*; import mindustry.input.*; import mindustry.io.*; import mindustry.io.SaveIO.*; +import mindustry.maps.*; import mindustry.maps.Map; import mindustry.net.*; import mindustry.type.*; +import mindustry.ui.*; import mindustry.ui.dialogs.*; import mindustry.world.*; @@ -124,10 +128,18 @@ public class Control implements ApplicationListener, Loadable{ } })); - Events.on(UnlockEvent.class, e -> ui.hudfrag.showUnlock(e.content)); - Events.on(UnlockEvent.class, e -> { + ui.hudfrag.showUnlock(e.content); + checkAutoUnlocks(); + + if(e.content instanceof SectorPreset){ + for(TechNode node : TechTree.all){ + if(!node.content.unlocked() && node.objectives.contains(o -> o instanceof SectorComplete sec && sec.preset == e.content) && !node.objectives.contains(o -> !o.complete())){ + ui.hudfrag.showToast(new TextureRegionDrawable(node.content.icon(Cicon.large)), bundle.get("available")); + } + } + } }); Events.on(SectorCaptureEvent.class, e -> { @@ -311,8 +323,17 @@ public class Control implements ApplicationListener, Loadable{ return; } + //set spawn for sector damage to use + Tile spawn = world.tile(sector.info.spawnPosition); + spawn.setBlock(Blocks.coreShard, state.rules.defaultTeam); + + //add extra damage. + SectorDamage.apply(1f); + //reset wave so things are more fair state.wave = 1; + //set up default wave time + state.wavetime = state.rules.waveSpacing * 2f; //reset win wave?? state.rules.winWave = state.rules.attackMode ? -1 : sector.preset != null ? sector.preset.captureWave : 40; @@ -320,8 +341,8 @@ public class Control implements ApplicationListener, Loadable{ //kill all units, since they should be dead anyway Groups.unit.clear(); Groups.fire.clear(); + Groups.puddle.clear(); - Tile spawn = world.tile(sector.info.spawnPosition); Schematics.placeLaunchLoadout(spawn.x, spawn.y); //set up camera/player locations diff --git a/core/src/mindustry/core/GameState.java b/core/src/mindustry/core/GameState.java index 3eed5e0164..de7cf6f60c 100644 --- a/core/src/mindustry/core/GameState.java +++ b/core/src/mindustry/core/GameState.java @@ -33,9 +33,8 @@ public class GameState{ /** Current game state. */ private State state = State.menu; - //TODO optimize public Unit boss(){ - return Groups.unit.find(u -> u.isBoss() && u.team == rules.waveTeam); + return teams.boss; } public void set(State astate){ diff --git a/core/src/mindustry/core/Logic.java b/core/src/mindustry/core/Logic.java index 5f6bd2c1a9..02222bd327 100644 --- a/core/src/mindustry/core/Logic.java +++ b/core/src/mindustry/core/Logic.java @@ -107,7 +107,7 @@ public class Logic implements ApplicationListener{ if(!(state.getSector().preset != null && !state.getSector().preset.useAI)){ state.rules.waveTeam.rules().ai = true; } - state.rules.waveTeam.rules().aiTier = state.getSector().threat; + state.rules.waveTeam.rules().aiTier = state.getSector().threat * 0.8f; state.rules.waveTeam.rules().infiniteResources = true; } diff --git a/core/src/mindustry/core/NetClient.java b/core/src/mindustry/core/NetClient.java index 841e7e42a7..85bbfd6f95 100644 --- a/core/src/mindustry/core/NetClient.java +++ b/core/src/mindustry/core/NetClient.java @@ -257,6 +257,11 @@ public class NetClient implements ApplicationListener{ public static void kick(KickReason reason){ netClient.disconnectQuietly(); logic.reset(); + + if(reason == KickReason.serverRestarting){ + ui.join.reconnect(); + return; + } if(!reason.quiet){ if(reason.extraText() != null){ diff --git a/core/src/mindustry/entities/GroupDefs.java b/core/src/mindustry/entities/GroupDefs.java index 3ddef67d65..8f98a69442 100644 --- a/core/src/mindustry/entities/GroupDefs.java +++ b/core/src/mindustry/entities/GroupDefs.java @@ -12,5 +12,6 @@ class GroupDefs{ @GroupDef(value = Syncc.class, mapping = true) G sync; @GroupDef(value = Drawc.class) G draw; @GroupDef(value = Firec.class) G fire; + @GroupDef(value = Puddlec.class) G puddle; @GroupDef(value = WeatherStatec.class) G weather; } diff --git a/core/src/mindustry/entities/bullet/BulletType.java b/core/src/mindustry/entities/bullet/BulletType.java index f3d09de8ed..7d94602844 100644 --- a/core/src/mindustry/entities/bullet/BulletType.java +++ b/core/src/mindustry/entities/bullet/BulletType.java @@ -78,7 +78,7 @@ public abstract class BulletType extends Content{ * Do not change unless you know what you're doing. */ public boolean backMove = true; /** Bullet range override. */ - public float range = -1f; + public float maxRange = -1f; /** % of block health healed **/ public float healPercent = 0f; /** whether to make fire on impact */ @@ -154,7 +154,7 @@ public abstract class BulletType extends Content{ /** Returns maximum distance the bullet this bullet type has can travel. */ public float range(){ - return Math.max(speed * lifetime * (1f - drag), range); + return Math.max(speed * lifetime * (1f - drag), maxRange); } public boolean collides(Bullet bullet, Building tile){ @@ -317,11 +317,11 @@ public abstract class BulletType extends Content{ } public Bullet create(Bullet parent, float x, float y, float angle){ - return create(parent.owner(), parent.team, x, y, angle); + 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); + return create(parent.owner, parent.team, x, y, angle, velocityScl, lifeScale); } public Bullet create(Bullet parent, float x, float y, float angle, float velocityScl){ diff --git a/core/src/mindustry/entities/bullet/SapBulletType.java b/core/src/mindustry/entities/bullet/SapBulletType.java index 9d05a37e9a..dbd30b77a8 100644 --- a/core/src/mindustry/entities/bullet/SapBulletType.java +++ b/core/src/mindustry/entities/bullet/SapBulletType.java @@ -61,7 +61,7 @@ public class SapBulletType extends BulletType{ b.data = target; if(target != null){ - float result = Math.min(target.health(), damage); + float result = Math.max(Math.min(target.health(), damage), 0); if(b.owner instanceof Healthc h){ h.heal(result * sapStrength); diff --git a/core/src/mindustry/entities/comp/UnitComp.java b/core/src/mindustry/entities/comp/UnitComp.java index aa02f25022..b90afcae56 100644 --- a/core/src/mindustry/entities/comp/UnitComp.java +++ b/core/src/mindustry/entities/comp/UnitComp.java @@ -397,8 +397,8 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I /** Actually destroys the unit, removing it and creating explosions. **/ public void destroy(){ - float explosiveness = 2f + item().explosiveness * stack().amount / 2f; - float flammability = item().flammability * stack().amount / 2f; + float explosiveness = 2f + item().explosiveness * stack().amount / 2.4f; + float flammability = item().flammability * stack().amount / 2.4f; Damage.dynamicExplosion(x, y, flammability, explosiveness, 0f, bounds() / 2f, Pal.darkFlame, state.rules.damageExplosions); float shake = hitSize / 3f; diff --git a/core/src/mindustry/entities/comp/WeaponsComp.java b/core/src/mindustry/entities/comp/WeaponsComp.java index b919f6fee1..21265731fc 100644 --- a/core/src/mindustry/entities/comp/WeaponsComp.java +++ b/core/src/mindustry/entities/comp/WeaponsComp.java @@ -91,6 +91,10 @@ abstract class WeaponsComp implements Teamc, Posc, Rotc, Velc, Statusc{ mount.bullet.time = mount.bullet.lifetime - 10f; mount.bullet = null; } + + if(mount.sound != null){ + mount.sound.stop(); + } } } diff --git a/core/src/mindustry/game/EventType.java b/core/src/mindustry/game/EventType.java index 43ae282d4c..f0aa3278b2 100644 --- a/core/src/mindustry/game/EventType.java +++ b/core/src/mindustry/game/EventType.java @@ -18,6 +18,7 @@ public class EventType{ impactPower, thoriumReactorOverheat, fireExtinguish, + acceleratorUse, newGame, tutorialComplete, flameAmmo, diff --git a/core/src/mindustry/game/Objectives.java b/core/src/mindustry/game/Objectives.java index acf7fad876..bf306cb957 100644 --- a/core/src/mindustry/game/Objectives.java +++ b/core/src/mindustry/game/Objectives.java @@ -48,7 +48,8 @@ public class Objectives{ } } - public static class SectorComplete extends SectorObjective{ + public static class SectorComplete implements Objective{ + public SectorPreset preset; public SectorComplete(SectorPreset zone){ this.preset = zone; @@ -67,11 +68,6 @@ public class Objectives{ } } - //TODO merge - public abstract static class SectorObjective implements Objective{ - public SectorPreset preset; - } - /** Defines a specific objective for a game. */ public interface Objective{ @@ -86,9 +82,5 @@ public class Objectives{ default void build(Table table){ } - - default SectorPreset zone(){ - return this instanceof SectorObjective ? ((SectorObjective)this).preset : null; - } } } diff --git a/core/src/mindustry/game/Schematics.java b/core/src/mindustry/game/Schematics.java index 7cd51ecec4..2d526606c7 100644 --- a/core/src/mindustry/game/Schematics.java +++ b/core/src/mindustry/game/Schematics.java @@ -127,6 +127,9 @@ public class Schematics implements Loadable{ newSchematic.tags.putAll(target.tags); newSchematic.file = target.file; + loadouts.each((block, list) -> list.remove(target)); + checkLoadout(target, true); + try{ write(newSchematic, target.file); }catch(Exception e){ @@ -134,6 +137,8 @@ public class Schematics implements Loadable{ Log.err(e); ui.showException(e); } + + } private @Nullable Schematic loadFile(Fi file){ diff --git a/core/src/mindustry/game/Teams.java b/core/src/mindustry/game/Teams.java index da5edfd0db..d5afac1217 100644 --- a/core/src/mindustry/game/Teams.java +++ b/core/src/mindustry/game/Teams.java @@ -26,6 +26,8 @@ public class Teams{ public Seq active = new Seq<>(); /** Teams with block or unit presence. */ public Seq present = new Seq<>(TeamData.class); + /** Current boss unit. */ + public @Nullable Unit boss; public Teams(){ active.add(get(Team.crux)); @@ -144,6 +146,7 @@ public class Teams{ public void updateTeamStats(){ present.clear(); + boss = null; for(Team team : Team.all){ TeamData data = team.data(); @@ -178,6 +181,10 @@ public class Teams{ data.units.add(unit); data.presentFlag = true; + if(unit.team == state.rules.waveTeam && unit.isBoss()){ + boss = unit; + } + if(data.unitsByType == null || data.unitsByType.length <= unit.type.id){ data.unitsByType = new Seq[content.units().size]; } diff --git a/core/src/mindustry/game/Universe.java b/core/src/mindustry/game/Universe.java index 1b0b1fc10b..b19fcee6da 100644 --- a/core/src/mindustry/game/Universe.java +++ b/core/src/mindustry/game/Universe.java @@ -208,6 +208,8 @@ public class Universe{ //add production, making sure that it's capped sector.info.production.each((item, stat) -> sector.info.items.add(item, Math.min((int)(stat.mean * newSecondsPassed * scl), sector.info.storageCapacity - sector.info.items.get(item)))); + //prevent negative values with unloaders + sector.info.items.checkNegative(); sector.saveInfo(); } @@ -216,7 +218,7 @@ public class Universe{ if(!sector.isAttacked() && turn > invasionGracePeriod && sector.info.hasSpawns){ //invasion chance depends on # of nearby bases if(Mathf.chance(baseInvasionChance * Math.min(sector.near().count(Sector::hasEnemyBase), 1))){ - int waveMax = Math.max(sector.info.winWave, sector.isBeingPlayed() ? state.wave : sector.info.wave + sector.info.wavesPassed) + Mathf.random(2, 5) * 5; + int waveMax = Math.max(sector.info.winWave, sector.isBeingPlayed() ? state.wave : sector.info.wave + sector.info.wavesPassed) + Mathf.random(2, 4) * 5; //assign invasion-related things if(sector.isBeingPlayed()){ diff --git a/core/src/mindustry/game/Waves.java b/core/src/mindustry/game/Waves.java index 586fb285c1..ddcff2cbbf 100644 --- a/core/src/mindustry/game/Waves.java +++ b/core/src/mindustry/game/Waves.java @@ -10,7 +10,7 @@ import mindustry.type.*; import static mindustry.content.UnitTypes.*; public class Waves{ - public static final int waveVersion = 3; + public static final int waveVersion = 4; private Seq spawns; @@ -277,7 +277,7 @@ public class Waves{ int cap = 150; float shieldStart = 30, shieldsPerWave = 20 + difficulty*30f; - float[] scaling = {1, 1, 1.5f, 3f, 4f}; + float[] scaling = {1, 1.5f, 3f, 4f, 5f}; Intc createProgression = start -> { //main sequence @@ -286,7 +286,7 @@ public class Waves{ for(int i = start; i < cap;){ int f = i; - int next = rand.random(8, 16) + (int)Mathf.lerp(4f, 0f, difficulty) + curTier * 4; + int next = rand.random(8, 16) + (int)Mathf.lerp(5f, 0f, difficulty) + curTier * 4; float shieldAmount = Math.max((i - shieldStart) * shieldsPerWave, 0); int space = start == 0 ? 1 : rand.random(1, 2); @@ -298,7 +298,7 @@ public class Waves{ begin = f; end = f + next >= cap ? never : f + next; max = 13; - unitScaling = (difficulty < 0.4f ? rand.random(2.5f, 4f) : rand.random(1f, 4f)) * scaling[ctier]; + unitScaling = (difficulty < 0.4f ? rand.random(2.5f, 5f) : rand.random(1f, 4f)) * scaling[ctier]; shields = shieldAmount; shieldScaling = shieldsPerWave; spacing = space; @@ -310,7 +310,7 @@ public class Waves{ begin = f + next - 1; end = f + next + rand.random(6, 10); max = 6; - unitScaling = rand.random(1f, 2f); + unitScaling = rand.random(2f, 4f); spacing = rand.random(2, 4); shields = shieldAmount/2f; shieldScaling = shieldsPerWave; @@ -340,10 +340,10 @@ public class Waves{ step += (int)(rand.random(15, 30) * Mathf.lerp(1f, 0.5f, difficulty)); } - int bossWave = (int)(rand.random(50, 70) * Mathf.lerp(1f, 0.5f, difficulty)); + int bossWave = (int)(rand.random(50, 70) * Mathf.lerp(1f, 0.7f, difficulty)); int bossSpacing = (int)(rand.random(25, 40) * Mathf.lerp(1f, 0.6f, difficulty)); - int bossTier = difficulty < 0.5 ? 3 : 4; + int bossTier = difficulty < 0.6 ? 3 : 4; //main boss progression out.add(new SpawnGroup(Structs.random(species)[bossTier]){{ @@ -411,7 +411,7 @@ public class Waves{ } //shift back waves on higher difficulty for a harder start - int shift = Math.max((int)(difficulty * 15 - 5), 0); + int shift = Math.max((int)(difficulty * 14 - 5), 0); for(SpawnGroup group : out){ group.begin -= shift; diff --git a/core/src/mindustry/input/InputHandler.java b/core/src/mindustry/input/InputHandler.java index c2ab8423c9..2df91588b6 100644 --- a/core/src/mindustry/input/InputHandler.java +++ b/core/src/mindustry/input/InputHandler.java @@ -47,6 +47,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{ /** Maximum line length. */ final static int maxLength = 100; final static Rect r1 = new Rect(), r2 = new Rect(); + final static Seq tmpPoints = new Seq<>(), tmpPoints2 = new Seq<>(); public final OverlayFragment frag = new OverlayFragment(); @@ -1164,27 +1165,39 @@ public abstract class InputHandler implements InputProcessor, GestureListener{ points = Placement.normalizeLine(startX, startY, endX, endY); } - if(block instanceof PowerNode){ - Seq skip = new Seq<>(); + if(block instanceof PowerNode node){ + var base = tmpPoints2; + var result = tmpPoints.clear(); - for(int i = 1; i < points.size; i++){ - int overlaps = 0; - Point2 point = points.get(i); + base.selectFrom(points, p -> p == points.first() || p == points.peek() || Build.validPlace(block, player.team(), p.x, p.y, rotation, false)); + boolean addedLast = false; - //check with how many powernodes the *next* tile will overlap - for(int j = 0; j < i; j++){ - if(!skip.contains(points.get(j)) && ((PowerNode)block).overlaps(world.tile(point.x, point.y), world.tile(points.get(j).x, points.get(j).y))){ - overlaps++; + outer: + for(int i = 0; i < base.size;){ + var point = base.get(i); + result.add(point); + if(i == base.size - 1) addedLast = true; + + //find the furthest node that overlaps this one + for(int j = base.size - 1; j > i; j--){ + var other = base.get(j); + boolean over = node.overlaps(world.tile(point.x, point.y), world.tile(other.x, other.y)); + + if(over){ + //add node to list and start searching for node that overlaps the next one + i = j; + continue outer; } } - //if it's more than one, it can bridge the gap - if(overlaps > 1){ - skip.add(points.get(i-1)); - } + //if it got here, that means nothing was found. try to proceed to the next node anyway + i ++; } - //remove skipped points - points.removeAll(skip); + + if(!addedLast) result.add(base.peek()); + + points.clear(); + points.addAll(result); } float angle = Angles.angle(startX, startY, endX, endY); diff --git a/core/src/mindustry/input/Placement.java b/core/src/mindustry/input/Placement.java index 1d713f7f75..59af8ac517 100644 --- a/core/src/mindustry/input/Placement.java +++ b/core/src/mindustry/input/Placement.java @@ -241,35 +241,10 @@ public class Placement{ } public static class NormalizeDrawResult{ - float x, y, x2, y2; + public float x, y, x2, y2; } public static class NormalizeResult{ public int x, y, x2, y2, rotation; - - boolean isX(){ - return Math.abs(x2 - x) > Math.abs(y2 - y); - } - - /** - * Returns length of greater edge of the selection. - */ - int getLength(){ - return Math.max(x2 - x, y2 - y); - } - - /** - * Returns the X position of a specific index along this area as a line. - */ - int getScaledX(int i){ - return x + (x2 - x > y2 - y ? i : 0); - } - - /** - * Returns the Y position of a specific index along this area as a line. - */ - int getScaledY(int i){ - return y + (x2 - x > y2 - y ? 0 : i); - } } } diff --git a/core/src/mindustry/io/SaveVersion.java b/core/src/mindustry/io/SaveVersion.java index 76e3b1cc5c..58d9d77d53 100644 --- a/core/src/mindustry/io/SaveVersion.java +++ b/core/src/mindustry/io/SaveVersion.java @@ -286,7 +286,8 @@ public abstract class SaveVersion extends SaveFileReader{ public void writeEntities(DataOutput stream) throws IOException{ //write team data with entities. - Seq data = state.teams.getActive(); + Seq data = state.teams.getActive().copy(); + if(!data.contains(Team.sharded.data())) data.add(Team.sharded.data()); stream.writeInt(data.size); for(TeamData team : data){ stream.writeInt(team.team.id); @@ -313,12 +314,23 @@ public abstract class SaveVersion extends SaveFileReader{ public void readEntities(DataInput stream) throws IOException{ int teamc = stream.readInt(); + for(int i = 0; i < teamc; i++){ Team team = Team.get(stream.readInt()); TeamData data = team.data(); int blocks = stream.readInt(); + data.blocks.clear(); + data.blocks.ensureCapacity(Math.min(blocks, 1000)); + var reads = Reads.get(stream); + var set = new IntSet(); + for(int j = 0; j < blocks; j++){ - data.blocks.addLast(new BlockPlan(stream.readShort(), stream.readShort(), stream.readShort(), content.block(stream.readShort()).id, TypeIO.readObject(Reads.get(stream)))); + short x = stream.readShort(), y = stream.readShort(), rot = stream.readShort(), bid = stream.readShort(); + var obj = TypeIO.readObject(reads); + //cannot have two in the same position + if(set.add(Point2.pack(x, y))){ + data.blocks.addLast(new BlockPlan(x, y, rot, content.block(bid).id, obj)); + } } } diff --git a/core/src/mindustry/maps/SectorDamage.java b/core/src/mindustry/maps/SectorDamage.java index 1fce5590e9..187778e470 100644 --- a/core/src/mindustry/maps/SectorDamage.java +++ b/core/src/mindustry/maps/SectorDamage.java @@ -21,7 +21,7 @@ import mindustry.world.blocks.storage.*; import static mindustry.Vars.*; public class SectorDamage{ - public static final int maxRetWave = 30, maxWavesSimulated = 50; + public static final int maxRetWave = 40, maxWavesSimulated = 50; //direct damage is for testing only private static final boolean direct = false, rubble = true; @@ -111,23 +111,26 @@ public class SectorDamage{ float damage = getDamage(state.rules.sector.info); //scaled damage has a power component to make it seem a little more realistic (as systems fail, enemy capturing gets easier and easier) - float scaled = Mathf.pow(damage, 1.6f); + float scaled = Mathf.pow(damage, 1.2f); - //apply damage to units - float unitDamage = damage * state.rules.sector.info.sumHealth; Tile spawn = spawner.getFirstSpawn(); //damage only units near the spawn point if(spawn != null){ Seq allies = new Seq<>(); + float sumUnitHealth = 0f; for(Unit ally : Groups.unit){ if(ally.team == state.rules.defaultTeam && ally.within(spawn, state.rules.dropZoneRadius * 2.5f)){ allies.add(ally); + sumUnitHealth += ally.health; } } allies.sort(u -> u.dst2(spawn)); + //apply damage to units + float unitDamage = damage * sumUnitHealth; + //damage units one by one, not uniformly for(var u : allies){ if(u.health < unitDamage){ @@ -335,9 +338,9 @@ public class SectorDamage{ info.waveDpsSlope = reg.slope; //enemy units like to aim for a lot of non-essential things, so increase resulting health slightly - info.sumHealth = sumHealth * 1.3f; + info.sumHealth = sumHealth * 1.2f; //players tend to have longer range units/turrets, so assume DPS is higher - info.sumDps = sumDps * 1.3f; + info.sumDps = sumDps * 1.2f; info.sumRps = sumRps; info.wavesSurvived = getWavesSurvived(info); @@ -348,13 +351,12 @@ public class SectorDamage{ Queue frontier = new Queue<>(); float[][] values = new float[tiles.width][tiles.height]; - float damage = fraction*80; //arbitrary damage value //phase one: find all spawnpoints for(Tile tile : tiles){ if((tile.block() instanceof CoreBlock && tile.team() == state.rules.waveTeam) || tile.overlay() == Blocks.spawn){ frontier.add(tile); - values[tile.x][tile.y] = damage; + values[tile.x][tile.y] = fraction * 26; } } @@ -368,14 +370,16 @@ public class SectorDamage{ int radius = 3; //only penetrate a certain % by health, not by distance - float totalHealth = damage >= 1f ? 1f : path.sumf(t -> { + float totalHealth = fraction >= 1f ? 1f : path.sumf(t -> { float s = 0; for(int dx = -radius; dx <= radius; dx++){ for(int dy = -radius; dy <= radius; dy++){ int wx = dx + t.x, wy = dy + t.y; if(wx >= 0 && wy >= 0 && wx < world.width() && wy < world.height() && Mathf.within(dx, dy, radius)){ Tile other = world.rawTile(wx, wy); - s += other.team() == state.rules.defaultTeam ? other.build.health / other.block().size : 0f; + if(!(other.block() instanceof CoreBlock)){ + s += other.team() == state.rules.defaultTeam ? other.build.health / other.block().size : 0f; + } } } } @@ -385,7 +389,7 @@ public class SectorDamage{ float healthCount = 0; out: - for(int i = 0; i < path.size && (healthCount < targetHealth || damage >= 1f); i++){ + for(int i = 0; i < path.size && (healthCount < targetHealth || fraction >= 1f); i++){ Tile t = path.get(i); for(int dx = -radius; dx <= radius; dx++){ @@ -405,7 +409,7 @@ public class SectorDamage{ removal.add(other.build); - if(healthCount >= targetHealth && damage < 0.999f){ + if(healthCount >= targetHealth && fraction < 0.999f){ break out; } } @@ -430,10 +434,10 @@ public class SectorDamage{ } } - float falloff = (damage) / (Math.max(tiles.width, tiles.height) * Mathf.sqrt2); + float falloff = (fraction) / (Math.max(tiles.width, tiles.height) * Mathf.sqrt2); int peak = 0; - if(damage > 0.1f){ + if(fraction > 0.15f){ //phase two: propagate the damage while(!frontier.isEmpty()){ peak = Math.max(peak, frontier.size); diff --git a/core/src/mindustry/maps/generators/BaseGenerator.java b/core/src/mindustry/maps/generators/BaseGenerator.java index 8a5bf215ab..2677f95c22 100644 --- a/core/src/mindustry/maps/generators/BaseGenerator.java +++ b/core/src/mindustry/maps/generators/BaseGenerator.java @@ -55,7 +55,7 @@ public class BaseGenerator{ BasePart coreschem = bases.cores.getFrac(difficulty); int passes = difficulty < 0.4 ? 1 : difficulty < 0.8 ? 2 : 3; - Block wall = wallsSmall.getFrac(difficulty), wallLarge = wallsLarge.getFrac(difficulty); + Block wall = wallsSmall.getFrac(difficulty * 0.91f), wallLarge = wallsLarge.getFrac(difficulty * 0.91f); for(Tile tile : cores){ tile.clearOverlay(); diff --git a/core/src/mindustry/type/Planet.java b/core/src/mindustry/type/Planet.java index 0f872b393d..f12cdb5c53 100644 --- a/core/src/mindustry/type/Planet.java +++ b/core/src/mindustry/type/Planet.java @@ -182,12 +182,12 @@ public class Planet extends UnlockableContent{ float sum = 1f; for(Sector other : sector.near()){ if(other.generateEnemyBase){ - sum += 1f; + sum += 0.9f; } } if(sector.hasEnemyBase()){ - sum += 1.9f; + sum += 0.88f; } sector.threat = sector.preset == null ? Math.min(sum / 5f, 1.2f) : Mathf.clamp(sector.preset.difficulty / 10f); diff --git a/core/src/mindustry/type/UnitType.java b/core/src/mindustry/type/UnitType.java index a83f6532ae..f5a40de645 100644 --- a/core/src/mindustry/type/UnitType.java +++ b/core/src/mindustry/type/UnitType.java @@ -328,7 +328,7 @@ public class UnitType extends UnlockableContent{ //suicide enemy if(weapons.contains(w -> w.bullet.killShooter)){ //scale down DPS to be insignificant - dpsEstimate /= 20f; + dpsEstimate /= 25f; } } } diff --git a/core/src/mindustry/ui/dialogs/JoinDialog.java b/core/src/mindustry/ui/dialogs/JoinDialog.java index f3d200f08a..fe5ea1bdaf 100644 --- a/core/src/mindustry/ui/dialogs/JoinDialog.java +++ b/core/src/mindustry/ui/dialogs/JoinDialog.java @@ -8,6 +8,7 @@ import arc.scene.ui.*; import arc.scene.ui.layout.*; import arc.struct.*; import arc.util.*; +import arc.util.Timer.*; import arc.util.serialization.*; import mindustry.*; import mindustry.core.*; @@ -33,6 +34,10 @@ public class JoinDialog extends BaseDialog{ int refreshes; boolean showHidden; + String lastIp; + int lastPort; + Task ping; + public JoinDialog(){ super("@joingame"); @@ -445,13 +450,34 @@ public class JoinDialog extends BaseDialog{ logic.reset(); net.reset(); Vars.netClient.beginConnecting(); - net.connect(ip, port, () -> { + net.connect(lastIp = ip, lastPort = port, () -> { hide(); add.hide(); }); }); } + public void reconnect(){ + if(lastIp == null || lastIp.isEmpty()) return; + ui.loadfrag.show("@reconnecting"); + + ping = Timer.schedule(() -> { + net.pingHost(lastIp, lastPort, host -> { + if(ping == null) return; + ping.cancel(); + ping = null; + connect(lastIp, lastPort); + }, exception -> {}); + }, 1, 1); + + ui.loadfrag.setButton(() -> { + ui.loadfrag.hide(); + if(ping == null) return; + ping.cancel(); + ping = null; + }); + } + void safeConnect(String ip, int port, int version){ if(version != Version.build && Version.build != -1 && version != -1){ ui.showInfo("[scarlet]" + (version > Version.build ? KickReason.clientOutdated : KickReason.serverOutdated).toString() + "\n[]" + diff --git a/core/src/mindustry/ui/dialogs/PlanetDialog.java b/core/src/mindustry/ui/dialogs/PlanetDialog.java index a7d0c837de..b3dc7aa135 100644 --- a/core/src/mindustry/ui/dialogs/PlanetDialog.java +++ b/core/src/mindustry/ui/dialogs/PlanetDialog.java @@ -140,6 +140,7 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{ return this; } + rebuildButtons(); mode = look; selected = hovered = launchSector = null; launching = false; @@ -167,6 +168,7 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{ } newPresets.reverse(); + updateSelected(); if(planets.planet.getLastSector() != null){ lookAt(planets.planet.getLastSector()); diff --git a/core/src/mindustry/ui/fragments/HintsFragment.java b/core/src/mindustry/ui/fragments/HintsFragment.java index eb029789fe..f967518cb4 100644 --- a/core/src/mindustry/ui/fragments/HintsFragment.java +++ b/core/src/mindustry/ui/fragments/HintsFragment.java @@ -150,7 +150,7 @@ public class HintsFragment extends Fragment{ depositItems(() -> player.unit().hasItem(), () -> !player.unit().hasItem()), desktopPause(visibleDesktop, () -> isTutorial.get() && !Vars.net.active(), () -> Core.input.keyTap(Binding.pause)), research(isTutorial, () -> ui.research.isShown()), - unitControl(() -> state.rules.defaultTeam.data().units.size > 1 && !net.active(), () -> !player.dead() && !player.unit().spawnedByCore), + unitControl(() -> state.rules.defaultTeam.data().units.size > 2 && !net.active() && !player.dead(), () -> !player.dead() && !player.unit().spawnedByCore), respawn(visibleMobile, () -> !player.dead() && !player.unit().spawnedByCore, () -> !player.dead() && player.unit().spawnedByCore), launch(() -> isTutorial.get() && state.rules.sector.isCaptured(), () -> ui.planet.isShown()), schematicSelect(visibleDesktop, () -> ui.hints.placedBlocks.contains(Blocks.router), () -> Core.input.keyRelease(Binding.schematic_select) || Core.input.keyTap(Binding.pick)), @@ -161,6 +161,7 @@ public class HintsFragment extends Fragment{ payloadDrop(() -> !player.unit().dead && player.unit() instanceof Payloadc p && p.payloads().any(), () -> player.unit() instanceof Payloadc p && p.payloads().isEmpty()), waveFire(() -> Groups.fire.size() > 0 && Blocks.wave.unlockedNow(), () -> indexer.getAllied(state.rules.defaultTeam, BlockFlag.extinguisher).size() > 0), generator(() -> control.input.block == Blocks.combustionGenerator, () -> ui.hints.placedBlocks.contains(Blocks.combustionGenerator)), + guardian(() -> state.boss() != null && state.boss().armor >= 4, () -> state.boss() == null), ; @Nullable diff --git a/core/src/mindustry/world/blocks/campaign/Accelerator.java b/core/src/mindustry/world/blocks/campaign/Accelerator.java index 9dd9040f8f..7c8359a6bf 100644 --- a/core/src/mindustry/world/blocks/campaign/Accelerator.java +++ b/core/src/mindustry/world/blocks/campaign/Accelerator.java @@ -1,5 +1,6 @@ package mindustry.world.blocks.campaign; +import arc.*; import arc.Graphics.*; import arc.Graphics.Cursor.*; import arc.graphics.g2d.*; @@ -8,6 +9,7 @@ import arc.scene.ui.layout.*; import arc.util.*; import mindustry.annotations.Annotations.*; import mindustry.content.*; +import mindustry.game.EventType.*; import mindustry.gen.*; import mindustry.graphics.*; import mindustry.type.*; @@ -91,6 +93,7 @@ public class Accelerator extends Block{ if(!state.isCampaign() || !consValid()) return; ui.showInfo("@indev.campaign"); + Events.fire(Trigger.acceleratorUse); } @Override diff --git a/core/src/mindustry/world/blocks/campaign/LaunchPad.java b/core/src/mindustry/world/blocks/campaign/LaunchPad.java index aa259efea6..d72c1f133a 100644 --- a/core/src/mindustry/world/blocks/campaign/LaunchPad.java +++ b/core/src/mindustry/world/blocks/campaign/LaunchPad.java @@ -19,6 +19,7 @@ import mindustry.graphics.*; import mindustry.type.*; import mindustry.ui.*; import mindustry.world.*; +import mindustry.world.consumers.*; import mindustry.world.meta.*; import static mindustry.Vars.*; @@ -38,6 +39,7 @@ public class LaunchPad extends Block{ solid = true; update = true; configurable = true; + drawDisabled = false; } @Override @@ -61,6 +63,12 @@ public class LaunchPad extends Block{ return !state.isCampaign() || net.client() ? SystemCursor.arrow : super.getCursor(); } + //cannot be disabled + @Override + public float efficiency(){ + return power != null && (block.consumes.has(ConsumeType.power) && !block.consumes.getPower().buffered) ? power.status : 1f; + } + @Override public void draw(){ super.draw(); diff --git a/core/src/mindustry/world/blocks/defense/MendProjector.java b/core/src/mindustry/world/blocks/defense/MendProjector.java index cf71928f26..1c171edfdc 100644 --- a/core/src/mindustry/world/blocks/defense/MendProjector.java +++ b/core/src/mindustry/world/blocks/defense/MendProjector.java @@ -60,6 +60,7 @@ public class MendProjector extends Block{ float heat; float charge = Mathf.random(reload); float phaseHeat; + float smoothEfficiency; @Override public float range(){ @@ -68,6 +69,7 @@ public class MendProjector extends Block{ @Override public void updateTile(){ + smoothEfficiency = Mathf.lerpDelta(smoothEfficiency, efficiency(), 0.08f); heat = Mathf.lerpDelta(heat, consValid() || cheating() ? 1f : 0f, 0.08f); charge += heat * delta(); @@ -115,7 +117,7 @@ public class MendProjector extends Block{ @Override public void drawLight(){ - Drawf.light(team, x, y, 50f * efficiency(), baseColor, 0.7f * efficiency()); + Drawf.light(team, x, y, 50f * smoothEfficiency, baseColor, 0.7f * smoothEfficiency); } @Override diff --git a/core/src/mindustry/world/blocks/defense/OverdriveProjector.java b/core/src/mindustry/world/blocks/defense/OverdriveProjector.java index 7cadaede81..7e645e5a2e 100644 --- a/core/src/mindustry/world/blocks/defense/OverdriveProjector.java +++ b/core/src/mindustry/world/blocks/defense/OverdriveProjector.java @@ -65,6 +65,7 @@ public class OverdriveProjector extends Block{ float heat; float charge = Mathf.random(reload); float phaseHeat; + float smoothEfficiency; @Override public float range(){ @@ -73,11 +74,12 @@ public class OverdriveProjector extends Block{ @Override public void drawLight(){ - Drawf.light(team, x, y, 50f * efficiency(), baseColor, 0.7f * efficiency()); + Drawf.light(team, x, y, 50f * smoothEfficiency, baseColor, 0.7f * smoothEfficiency); } @Override public void updateTile(){ + smoothEfficiency = Mathf.lerpDelta(smoothEfficiency, efficiency(), 0.08f); heat = Mathf.lerpDelta(heat, consValid() ? 1f : 0f, 0.08f); charge += heat * Time.delta; diff --git a/core/src/mindustry/world/blocks/distribution/Conveyor.java b/core/src/mindustry/world/blocks/distribution/Conveyor.java index a76542f92b..b3218ea7be 100644 --- a/core/src/mindustry/world/blocks/distribution/Conveyor.java +++ b/core/src/mindustry/world/blocks/distribution/Conveyor.java @@ -10,7 +10,6 @@ import arc.util.io.*; import mindustry.annotations.Annotations.*; import mindustry.content.*; import mindustry.entities.units.*; -import mindustry.game.*; import mindustry.gen.*; import mindustry.graphics.*; import mindustry.type.*; diff --git a/core/src/mindustry/world/blocks/power/ItemLiquidGenerator.java b/core/src/mindustry/world/blocks/power/ItemLiquidGenerator.java index 12b3f6d691..2a0982a069 100644 --- a/core/src/mindustry/world/blocks/power/ItemLiquidGenerator.java +++ b/core/src/mindustry/world/blocks/power/ItemLiquidGenerator.java @@ -92,6 +92,11 @@ public class ItemLiquidGenerator extends PowerGenerator{ return generateTime > 0; } + @Override + public float ambientVolume(){ + return Mathf.clamp(productionEfficiency); + } + @Override public void updateTile(){ //Note: Do not use this delta when calculating the amount of power or the power efficiency, but use it for resource consumption if necessary. diff --git a/core/src/mindustry/world/blocks/storage/CoreBlock.java b/core/src/mindustry/world/blocks/storage/CoreBlock.java index bc706ff73d..995834c977 100644 --- a/core/src/mindustry/world/blocks/storage/CoreBlock.java +++ b/core/src/mindustry/world/blocks/storage/CoreBlock.java @@ -268,6 +268,26 @@ public class CoreBlock extends StorageBlock{ } } + @Override + public void handleStack(Item item, int amount, Teamc source){ + super.handleStack(item, amount, source); + + if(team == state.rules.defaultTeam && state.isCampaign()){ + state.rules.sector.info.handleCoreItem(item, amount); + } + } + + @Override + public int removeStack(Item item, int amount){ + int result = super.removeStack(item, amount); + + if(team == state.rules.defaultTeam && state.isCampaign()){ + state.rules.sector.info.handleCoreItem(item, -result); + } + + return result; + } + @Override public void drawSelect(){ Lines.stroke(1f, Pal.accent); diff --git a/core/src/mindustry/world/blocks/units/UnitFactory.java b/core/src/mindustry/world/blocks/units/UnitFactory.java index 6b2156e566..1a257d9565 100644 --- a/core/src/mindustry/world/blocks/units/UnitFactory.java +++ b/core/src/mindustry/world/blocks/units/UnitFactory.java @@ -53,15 +53,12 @@ public class UnitFactory extends UnitBlock{ @Override public void init(){ capacities = new int[Vars.content.items().size]; - itemCapacity = 0; for(UnitPlan plan : plans){ for(ItemStack stack : plan.requirements){ capacities[stack.item.id] = Math.max(capacities[stack.item.id], stack.amount * 2); + itemCapacity = Math.max(itemCapacity, stack.amount * 2); } } - for(int i : capacities){ - itemCapacity += i; - } super.init(); } diff --git a/desktop/src/mindustry/desktop/steam/SAchievement.java b/desktop/src/mindustry/desktop/steam/SAchievement.java index 38236eabd8..092f486249 100644 --- a/desktop/src/mindustry/desktop/steam/SAchievement.java +++ b/desktop/src/mindustry/desktop/steam/SAchievement.java @@ -61,6 +61,7 @@ public enum SAchievement{ coolTurret, enablePixelation, openWiki, + useAccelerator, ; private final SStat stat; diff --git a/desktop/src/mindustry/desktop/steam/SStats.java b/desktop/src/mindustry/desktop/steam/SStats.java index e9e8258487..822efe33cc 100644 --- a/desktop/src/mindustry/desktop/steam/SStats.java +++ b/desktop/src/mindustry/desktop/steam/SStats.java @@ -215,6 +215,8 @@ public class SStats implements SteamUserStatsCallback{ } }); + trigger(Trigger.acceleratorUse, useAccelerator); + trigger(Trigger.impactPower, powerupImpactReactor); trigger(Trigger.flameAmmo, useFlameAmmo); diff --git a/fastlane/metadata/android/en-US/changelogs/29723.txt b/fastlane/metadata/android/en-US/changelogs/29723.txt new file mode 100644 index 0000000000..c07b6541ec --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/29723.txt @@ -0,0 +1,10 @@ +Only a few days left until stable 6.0 release now. The campaign should now be playable; I've done a playthrough and fixed all the bugs and irregularities I encountered. + +Aside from internal modding API changes and potential bugfixes, 6.0 should not have any more additions. + +- Added basalt boulder decoration block +- Added hint about generator use & power transfer +- Made power node placement smarter +- Buffed Lancer turret + +Campaign: diff --git a/fastlane/metadata/android/en-US/changelogs/29726.txt b/fastlane/metadata/android/en-US/changelogs/29726.txt new file mode 100644 index 0000000000..52dda104eb --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/29726.txt @@ -0,0 +1,2 @@ +- Fixed broken blocks increasing exponentially, leading to runaway memory usage/lag +- Fixed incorrect planet dialog layout on certain devices diff --git a/gradle.properties b/gradle.properties index ba9e4fa2a9..151ff7ea34 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,3 +1,3 @@ org.gradle.daemon=true org.gradle.jvmargs=-Xms256m -Xmx1024m -archash=9446f0f01b2a1b25abf870a32bf839bc486b12e3 +archash=6742c2b110eeecd1934c42b5b1c87b00c911ecc4 diff --git a/server/src/mindustry/server/ServerControl.java b/server/src/mindustry/server/ServerControl.java index 2eaa0a59e1..553fd6af74 100644 --- a/server/src/mindustry/server/ServerControl.java +++ b/server/src/mindustry/server/ServerControl.java @@ -1003,8 +1003,7 @@ public class ServerControl implements ApplicationListener{ private void logToFile(String text){ if(currentLogFile != null && currentLogFile.length() > maxLogLength){ - String date = DateTimeFormatter.ofPattern("MM-dd-yyyy | HH:mm:ss").format(LocalDateTime.now()); - currentLogFile.writeString("[End of log file. Date: " + date + "]\n", true); + currentLogFile.writeString("[End of log file. Date: " + dateTime.format(LocalDateTime.now()) + "]\n", true); currentLogFile = null; }