From 420110684330aee3e477bac1a48845bab078bc76 Mon Sep 17 00:00:00 2001 From: Anuken Date: Sun, 9 Oct 2022 17:47:12 -0400 Subject: [PATCH] New WIP achievement definitions --- core/src/mindustry/content/StatusEffects.java | 3 + core/src/mindustry/ctype/Content.java | 5 + core/src/mindustry/entities/Damage.java | 25 ++- core/src/mindustry/entities/GroupDefs.java | 1 + .../mindustry/entities/bullet/BulletType.java | 12 +- .../mindustry/entities/comp/BuildingComp.java | 6 + core/src/mindustry/game/EventType.java | 70 ++++++- core/src/mindustry/game/Schematics.java | 4 + core/src/mindustry/game/Teams.java | 11 + core/src/mindustry/mod/Mods.java | 3 + core/src/mindustry/service/Achievement.java | 58 +++++- core/src/mindustry/service/GameService.java | 191 +++++++++++++++++- core/src/mindustry/service/SStat.java | 2 + core/src/mindustry/type/CellLiquid.java | 11 + .../mindustry/ui/dialogs/PlanetDialog.java | 8 +- core/src/mindustry/world/Block.java | 4 + .../world/blocks/defense/ForceProjector.java | 5 + .../world/blocks/defense/ShockwaveTower.java | 20 +- .../world/blocks/power/ConsumeGenerator.java | 3 + .../world/consumers/ConsumeItemExplode.java | 3 + 20 files changed, 419 insertions(+), 26 deletions(-) diff --git a/core/src/mindustry/content/StatusEffects.java b/core/src/mindustry/content/StatusEffects.java index 8f0a3cd60f..a2a2e47816 100644 --- a/core/src/mindustry/content/StatusEffects.java +++ b/core/src/mindustry/content/StatusEffects.java @@ -45,6 +45,9 @@ public class StatusEffects{ affinity(blasted, (unit, result, time) -> { unit.damagePierce(transitionDamage); + if(unit.team == state.rules.waveTeam){ + Events.fire(Trigger.blastFreeze); + } }); }); }}; diff --git a/core/src/mindustry/ctype/Content.java b/core/src/mindustry/ctype/Content.java index bea224683e..46767ca25a 100644 --- a/core/src/mindustry/ctype/Content.java +++ b/core/src/mindustry/ctype/Content.java @@ -39,6 +39,11 @@ public abstract class Content implements Comparable{ return minfo.error != null; } + /** @return whether this is content from the base game. */ + public boolean isVanilla(){ + return minfo.mod == null; + } + @Override public int compareTo(Content c){ return Integer.compare(id, c.id); diff --git a/core/src/mindustry/entities/Damage.java b/core/src/mindustry/entities/Damage.java index b7285392e5..30511138ad 100644 --- a/core/src/mindustry/entities/Damage.java +++ b/core/src/mindustry/entities/Damage.java @@ -19,6 +19,7 @@ import static mindustry.Vars.*; /** Utility class for damaging in an area. */ public class Damage{ + private static final UnitDamageEvent bulletDamageEvent = new UnitDamageEvent(); private static final Rect rect = new Rect(); private static final Rect hitrect = new Rect(); private static final Vec2 vec = new Vec2(), seg1 = new Vec2(), seg2 = new Vec2(); @@ -467,21 +468,29 @@ public class Damage{ /** Damages all entities and blocks in a radius that are enemies of the team. */ public static void damage(Team team, float x, float y, float radius, float damage, boolean complete, boolean air, boolean ground, boolean scaled, @Nullable Bullet source){ - Cons cons = entity -> { - if(entity.team == team || !entity.checkTarget(air, ground) || !entity.hittable() || !entity.within(x, y, radius + (scaled ? entity.hitSize / 2f : 0f))){ + Cons cons = unit -> { + if(unit.team == team || !unit.checkTarget(air, ground) || !unit.hittable() || !unit.within(x, y, radius + (scaled ? unit.hitSize / 2f : 0f))){ return; } - float amount = calculateDamage(scaled ? Math.max(0, entity.dst(x, y) - entity.type.hitSize/2) : entity.dst(x, y), radius, damage); - entity.damage(amount); + boolean dead = unit.dead; + + float amount = calculateDamage(scaled ? Math.max(0, unit.dst(x, y) - unit.type.hitSize/2) : unit.dst(x, y), radius, damage); + unit.damage(amount); + if(source != null){ - entity.controller().hit(source); + Events.fire(bulletDamageEvent.set(unit, source)); + unit.controller().hit(source); + + if(!dead && unit.dead){ + Events.fire(new UnitBulletDestroyEvent(unit, source)); + } } //TODO better velocity displacement - float dst = vec.set(entity.x - x, entity.y - y).len(); - entity.vel.add(vec.setLength((1f - dst / radius) * 2f / entity.mass())); + float dst = vec.set(unit.x - x, unit.y - y).len(); + unit.vel.add(vec.setLength((1f - dst / radius) * 2f / unit.mass())); - if(complete && damage >= 9999999f && entity.isPlayer()){ + if(complete && damage >= 9999999f && unit.isPlayer()){ Events.fire(Trigger.exclusionDeath); } }; diff --git a/core/src/mindustry/entities/GroupDefs.java b/core/src/mindustry/entities/GroupDefs.java index b995d4c415..465d49a58f 100644 --- a/core/src/mindustry/entities/GroupDefs.java +++ b/core/src/mindustry/entities/GroupDefs.java @@ -15,4 +15,5 @@ class GroupDefs{ @GroupDef(value = Puddlec.class) G puddle; @GroupDef(value = WeatherStatec.class) G weather; @GroupDef(value = WorldLabelc.class, mapping = true) G label; + @GroupDef(value = PowerGraphUpdaterc.class) G powerGraph; } diff --git a/core/src/mindustry/entities/bullet/BulletType.java b/core/src/mindustry/entities/bullet/BulletType.java index 020bd40b75..5c14059cac 100644 --- a/core/src/mindustry/entities/bullet/BulletType.java +++ b/core/src/mindustry/entities/bullet/BulletType.java @@ -22,11 +22,12 @@ import mindustry.graphics.*; import mindustry.type.*; import mindustry.world.*; import mindustry.world.blocks.*; -import mindustry.world.blocks.defense.Wall.*; import static mindustry.Vars.*; public class BulletType extends Content implements Cloneable{ + static final UnitDamageEvent bulletDamageEvent = new UnitDamageEvent(); + /** Lifetime in ticks. */ public float lifetime = 40f; /** Speed in units/tick. */ @@ -354,6 +355,8 @@ public class BulletType extends Content implements Cloneable{ } public void hitEntity(Bullet b, Hitboxc entity, float health){ + boolean wasDead = entity instanceof Unit u && u.dead; + if(entity instanceof Healthc h){ if(pierceArmor){ h.damagePierce(b.damage); @@ -367,11 +370,12 @@ public class BulletType extends Content implements Cloneable{ if(impact) Tmp.v3.setAngle(b.rotation() + (knockback < 0 ? 180f : 0f)); unit.impulse(Tmp.v3); unit.apply(status, statusDuration); + + Events.fire(bulletDamageEvent.set(unit, b)); } - //for achievements - if(b.owner instanceof WallBuild && player != null && b.team == player.team() && entity instanceof Unit unit && unit.dead){ - Events.fire(Trigger.phaseDeflectHit); + if(!wasDead && entity instanceof Unit unit && unit.dead){ + Events.fire(new UnitBulletDestroyEvent(unit, b)); } } diff --git a/core/src/mindustry/entities/comp/BuildingComp.java b/core/src/mindustry/entities/comp/BuildingComp.java index 81396d5f7f..5ede75ac5a 100644 --- a/core/src/mindustry/entities/comp/BuildingComp.java +++ b/core/src/mindustry/entities/comp/BuildingComp.java @@ -1570,9 +1570,15 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc, /** Handle a bullet collision. * @return whether the bullet should be removed. */ public boolean collision(Bullet other){ + boolean wasDead = dead(); + damage(other.team, other.damage() * other.type().buildingDamageMultiplier); Events.fire(bulletDamageEvent.set(self(), other)); + if(dead() && !wasDead){ + Events.fire(new BuildingBulletDestroyEvent(self(), other)); + } + return true; } diff --git a/core/src/mindustry/game/EventType.java b/core/src/mindustry/game/EventType.java index 9699e17393..bca54a47b5 100644 --- a/core/src/mindustry/game/EventType.java +++ b/core/src/mindustry/game/EventType.java @@ -16,9 +16,13 @@ public class EventType{ //events that occur very often public enum Trigger{ shock, - phaseDeflectHit, + blastFreeze, impactPower, + blastGenerator, + shockwaveTowerUse, + forceProjectorBreak, thoriumReactorOverheat, + neoplasmReact, fireExtinguish, acceleratorUse, newGame, @@ -33,6 +37,7 @@ public class EventType{ socketConfigChanged, update, unitCommandChange, + importMod, draw, drawOver, preDraw, @@ -127,6 +132,17 @@ public class EventType{ } } + public static class SectorLaunchLoadoutEvent{ + public final Sector sector, from; + public final Schematic loadout; + + public SectorLaunchLoadoutEvent(Sector sector, Sector from, Schematic loadout){ + this.sector = sector; + this.from = from; + this.loadout = loadout; + } + } + public static class SchematicCreateEvent{ public final Schematic schematic; @@ -448,6 +464,29 @@ public class EventType{ } } + /** Called when a neoplasia (or other pressure-based block, from mods) reactor explodes due to pressure.*/ + public static class GeneratorPressureExplodeEvent{ + public final Building build; + + public GeneratorPressureExplodeEvent(Building build){ + this.build = build; + } + } + + /** Called when a building is directly killed by a bullet. May not fire in all circumstances. */ + public static class BuildingBulletDestroyEvent{ + public Building build; + public Bullet bullet; + + public BuildingBulletDestroyEvent(Building build, Bullet bullet){ + this.build = build; + this.bullet = bullet; + } + + public BuildingBulletDestroyEvent(){ + } + } + public static class UnitDestroyEvent{ public final Unit unit; @@ -456,6 +495,35 @@ public class EventType{ } } + /** Called when a unit is directly killed by a bullet. May not fire in all circumstances. */ + public static class UnitBulletDestroyEvent{ + public Unit unit; + public Bullet bullet; + + public UnitBulletDestroyEvent(Unit unit, Bullet bullet){ + this.unit = unit; + this.bullet = bullet; + } + + public UnitBulletDestroyEvent(){ + } + } + + /** + * Called when a unit is hit by a bullet. + * This event is REUSED, do not nest invocations of it (e.g. damage units in its event handler) + * */ + public static class UnitDamageEvent{ + public Unit unit; + public Bullet bullet; + + public UnitDamageEvent set(Unit unit, Bullet bullet){ + this.unit = unit; + this.bullet = bullet; + return this; + } + } + public static class UnitDrownEvent{ public final Unit unit; diff --git a/core/src/mindustry/game/Schematics.java b/core/src/mindustry/game/Schematics.java index 7ea9b0c655..598967608d 100644 --- a/core/src/mindustry/game/Schematics.java +++ b/core/src/mindustry/game/Schematics.java @@ -292,6 +292,10 @@ public class Schematics implements Loadable{ return defaultLoadouts.get(block); } + public boolean isDefaultLoadout(Schematic schem){ + return defaultLoadouts.containsValue(schem, true); + } + /** Checks a schematic for deployment validity and adds it to the cache. */ private void checkLoadout(Schematic s, boolean customSchem){ Stile core = s.tiles.find(t -> t.block instanceof CoreBlock); diff --git a/core/src/mindustry/game/Teams.java b/core/src/mindustry/game/Teams.java index bd5f794a64..85e03def0a 100644 --- a/core/src/mindustry/game/Teams.java +++ b/core/src/mindustry/game/Teams.java @@ -286,6 +286,11 @@ public class Teams{ return buildingTypes.get(block, () -> new Seq<>(false)); } + public int getCount(Block block){ + var res = buildingTypes.get(block); + return res == null ? 0 : res.size; + } + /** Destroys this team's presence on the map, killing part of its buildings and converting everything to 'derelict'. */ public void destroyToDerelict(){ @@ -355,6 +360,12 @@ public class Teams{ } } + //this is just an alias for consistency + @Nullable + public Seq getUnits(UnitType type){ + return unitCache(type); + } + @Nullable public Seq unitCache(UnitType type){ if(unitsByType == null || unitsByType.length <= type.id || unitsByType[type.id] == null) return null; diff --git a/core/src/mindustry/mod/Mods.java b/core/src/mindustry/mod/Mods.java index 042020f0d8..e59a2c5416 100644 --- a/core/src/mindustry/mod/Mods.java +++ b/core/src/mindustry/mod/Mods.java @@ -110,6 +110,9 @@ public class Mods implements Loadable{ sortMods(); //try to load the mod's icon so it displays on import Core.app.post(() -> loadIcon(loaded)); + + Events.fire(Trigger.importMod); + return loaded; }catch(IOException e){ dest.delete(); diff --git a/core/src/mindustry/service/Achievement.java b/core/src/mindustry/service/Achievement.java index 857baca118..06784edbc8 100644 --- a/core/src/mindustry/service/Achievement.java +++ b/core/src/mindustry/service/Achievement.java @@ -15,7 +15,7 @@ public enum Achievement{ launch30Times(SStat.timesLaunched, 30), captureBackground, survive100Waves(SStat.maxWavesSurvived, 100), - researchAll, + researchAll, //TODO - remake/change? shockWetEnemy, killEnemyPhaseWall, researchRouter, @@ -51,6 +51,7 @@ public enum Achievement{ circleConveyor, becomeRouter, create20Schematics(SStat.schematicsCreated, 20), + create500Schematics(SStat.schematicsCreated, 50), //TODO - Steam survive10WavesNoBlocks, captureNoBlocksBroken, useFlameAmmo, @@ -60,6 +61,61 @@ public enum Achievement{ useAccelerator, unlockAllZones, + //TODO new ones + + allTransportOneMap, //TODO - Steam + buildOverdrive, //TODO - Steam + buildMendProjector, //TODO - Steam + buildWexWater, //TODO - Steam + + have10mItems(SStat.totalCampaignItems, 10_000_000), //TODO - Steam + killEclipseDuo, //TODO - Steam + + allPresetsErekir, //TODO - Steam + + launchCoreSchematic, //TODO - Steam + nucleusGroundZero, //TODO - Steam + + neoplasmWater, //TODO - Steam + blastFrozenUnit, //TODO - Steam + + allBlocksSerpulo, //TODO - Steam + allBlocksErekir, //TODO - Steam + + //TODO are these necessary? + //allTurretsSerpulo, //TODO + //allTurretsErekir, //TODO + //allTechSerpulo, //TODO + //allTechErekir, //TODO + + breakForceProjector, //TODO - Steam + researchLogic, //TODO - Steam + + negative10kPower, //TODO - Steam + positive100kPower, //TODO - Steam + store1milPower, //TODO - Steam + + blastGenerator, //TODO - Steam + neoplasiaExplosion, //TODO - Steam + + installMod, //TODO - Steam + routerLanguage, //TODO - Steam + joinCommunityServer, //TODO - Steam + openConsole, //TODO - Steam + + controlTurret, //TODO - Steam + dropUnitsCoreZone, //TODO - Steam + destroyScatterFlare, //TODO - Steam + boostUnit, //TODO - Steam + boostBuildingFloor, //TODO - Steam + + hoverUnitLiquid, //TODO - Steam + + break100Boulders(SStat.bouldersDeconstructed, 100), //TODO - Steam + break10000Boulders(SStat.bouldersDeconstructed, 10_000), //TODO - Steam + + shockwaveTowerUse, //TODO - Steam + ; private final SStat stat; diff --git a/core/src/mindustry/service/GameService.java b/core/src/mindustry/service/GameService.java index 0356a80d25..b27b89d3ca 100644 --- a/core/src/mindustry/service/GameService.java +++ b/core/src/mindustry/service/GameService.java @@ -11,7 +11,13 @@ import mindustry.game.SectorInfo.*; import mindustry.gen.*; import mindustry.type.*; import mindustry.world.*; +import mindustry.world.blocks.defense.*; +import mindustry.world.blocks.defense.Wall.*; +import mindustry.world.blocks.defense.turrets.Turret.*; import mindustry.world.blocks.distribution.*; +import mindustry.world.blocks.production.AttributeCrafter.*; +import mindustry.world.blocks.production.SolidPump.*; +import mindustry.world.meta.*; import static mindustry.Vars.*; import static mindustry.service.Achievement.*; @@ -21,16 +27,17 @@ import static mindustry.service.Achievement.*; * * This includes: * - Desktop (Steam) - * - iOS (Game Center) - * - Android (Google Play Games) * * The default implementation does nothing. * */ public class GameService{ + private Seq tmpTiles = new Seq<>(); private ObjectSet blocksBuilt = new ObjectSet<>(), unitsBuilt = new ObjectSet<>(); private ObjectSet t5s = new ObjectSet<>(); private IntSet checked = new IntSet(); + private Block[] allTransportSerpulo, allTransportErekir, allErekirBlocks, allSerpuloBlocks; + /** Begin listening for new achievement events, once the game service is activated. This can be called at any time, but only once. */ public void init(){ if(clientLoaded){ @@ -64,11 +71,26 @@ public class GameService{ } + private void checkAllBlocks(Achievement ach, Block[] blocks){ + if(!Structs.contains(blocks, t -> !blocksBuilt.contains(t.name))){ + ach.complete(); + } + } + private void registerEvents(){ + allTransportSerpulo = content.blocks().select(b -> b.category == Category.distribution && b.isVisibleOn(Planets.serpulo) && b.isVanilla() && b.buildVisibility == BuildVisibility.shown).toArray(Block.class); + allTransportErekir = content.blocks().select(b -> b.category == Category.distribution && b.isVisibleOn(Planets.erekir) && b.isVanilla() && b.buildVisibility == BuildVisibility.shown).toArray(Block.class); + + allSerpuloBlocks = content.blocks().select(b -> b.synthetic() && b.isVisibleOn(Planets.serpulo) && b.isVanilla() && b.buildVisibility == BuildVisibility.shown).toArray(Block.class); + allErekirBlocks = content.blocks().select(b -> b.synthetic() && b.isVisibleOn(Planets.erekir) && b.isVanilla() && b.buildVisibility == BuildVisibility.shown).toArray(Block.class); + unitsBuilt = Core.settings.getJson("units-built" , ObjectSet.class, String.class, ObjectSet::new); blocksBuilt = Core.settings.getJson("blocks-built" , ObjectSet.class, String.class, ObjectSet::new); t5s = ObjectSet.with(UnitTypes.omura, UnitTypes.reign, UnitTypes.toxopid, UnitTypes.eclipse, UnitTypes.oct, UnitTypes.corvus); + checkAllBlocks(allBlocksErekir, allErekirBlocks); + checkAllBlocks(allBlocksSerpulo, allSerpuloBlocks); + //periodically check for various conditions float updateInterval = 2f; Timer.schedule(this::checkUpdate, updateInterval, updateInterval); @@ -79,6 +101,14 @@ public class GameService{ unlockAllZones.complete(); } + if(mods.list().size > 0){ + installMod.complete(); + } + + if(Core.bundle.get("yes").equals("router")){ + routerLanguage.complete(); + } + Events.on(UnitDestroyEvent.class, e -> { if(campaign()){ if(e.unit.team != Vars.player.team()){ @@ -106,12 +136,43 @@ public class GameService{ SStat.maxProduction.max(Math.round(total)); }); + Events.run(Trigger.update, () -> { + //extremely lazy timer, I just don't care + if(campaign() && !hoverUnitLiquid.isAchieved() && Core.graphics.getFrameId() % 20 == 0){ + var units = state.rules.defaultTeam.data().getUnits(UnitTypes.elude); + if(units != null){ + for(var unit : units){ + if(unit.floorOn().isLiquid){ + hoverUnitLiquid.complete(); + break; + } + } + } + } + + if(campaign() && player.unit().type.canBoost && player.unit().elevation >= 0.25f){ + boostUnit.complete(); + } + }); + Events.run(Trigger.newGame, () -> Core.app.post(() -> { if(campaign() && player.core() != null && player.core().items.total() >= 10 * 1000){ drop10kitems.complete(); } })); + Events.on(BuildingBulletDestroyEvent.class, e -> { + if(campaign() && e.build.block == Blocks.scatter && e.build.team == state.rules.waveTeam && e.bullet.owner instanceof Unit u && u.type == UnitTypes.flare && u.team == player.team()){ + destroyScatterFlare.complete(); + } + }); + + Events.on(BlockBuildBeginEvent.class, e -> { + if(campaign() && state.rules.sector == SectorPresets.groundZero.sector && e.tile.block() == Blocks.coreNucleus){ + nucleusGroundZero.complete(); + } + }); + Events.on(BlockBuildEndEvent.class, e -> { if(campaign() && e.unit != null && e.unit.isLocal() && !e.breaking){ SStat.blocksBuilt.add(); @@ -124,7 +185,40 @@ public class GameService{ buildGroundFactory.complete(); } + if((e.tile.build instanceof AttributeCrafterBuild a && a.attrsum > 0) || (e.tile.build instanceof SolidPumpBuild sp && sp.boost > 0)){ + boostBuildingFloor.complete(); + } + + if(!allTransportOneMap.isAchieved()){ + Block[] allTransports = state.rules.sector.planet == Planets.erekir ? allTransportErekir : allTransportSerpulo; + boolean all = true; + for(var block : allTransports){ + if(state.rules.defaultTeam.data().getCount(block) == 0){ + all = false; + break; + } + } + if(all){ + allTransportOneMap.complete(); + } + } + + if(e.tile.block() instanceof MendProjector || e.tile.block() instanceof RegenProjector) buildMendProjector.complete(); + if(e.tile.block() instanceof OverdriveProjector) buildOverdrive.complete(); + + if(e.tile.block() == Blocks.waterExtractor){ + if(e.tile.getLinkedTiles(tmpTiles).contains(t -> t.floor().liquidDrop == Liquids.water)){ + buildWexWater.complete(); + } + } + if(blocksBuilt.add(e.tile.block().name)){ + if(state.rules.sector.planet == Planets.erekir){ + checkAllBlocks(allBlocksErekir, allErekirBlocks); + }else{ + checkAllBlocks(allBlocksSerpulo, allSerpuloBlocks); + } + if(blocksBuilt.contains("meltdown") && blocksBuilt.contains("spectre") && blocksBuilt.contains("foreshadow")){ buildMeltdownSpectre.complete(); } @@ -153,6 +247,32 @@ public class GameService{ } } } + + if(campaign() && e.unit != null && e.unit.isLocal() && e.breaking){ + //hacky way of testing for boulders without string contains/endsWith + if(e.tile.block().breakSound == Sounds.rockBreak){ + SStat.bouldersDeconstructed.add(); + } + } + }); + + Events.on(TurnEvent.class, e -> { + int total = 0; + for(var planet : content.planets()){ + for(var sector : planet.sectors){ + if(sector.hasBase()){ + total += sector.items().total; + } + } + } + + SStat.totalCampaignItems.max(total); + }); + + Events.on(SectorLaunchLoadoutEvent.class, e -> { + if(!schematics.isDefaultLoadout(e.loadout)){ + launchCoreSchematic.complete(); + } }); Events.on(UnitCreateEvent.class, e -> { @@ -172,6 +292,10 @@ public class GameService{ if(e.unit instanceof BlockUnitc unit && unit.tile().block == Blocks.router){ becomeRouter.complete(); } + + if(e.unit instanceof BlockUnitc unit && unit.tile() instanceof TurretBuild){ + controlTurret.complete(); + } }); Events.on(SchematicCreateEvent.class, e -> { @@ -198,6 +322,8 @@ public class GameService{ Events.run(Trigger.openWiki, openWiki::complete); + Events.run(Trigger.importMod, installMod::complete); + Events.run(Trigger.exclusionDeath, dieExclusion::complete); Events.on(UnitDrownEvent.class, e -> { @@ -216,6 +342,14 @@ public class GameService{ trigger(Trigger.suicideBomb, suicideBomb); + trigger(Trigger.blastGenerator, blastGenerator); + + trigger(Trigger.forceProjectorBreak, breakForceProjector); + + trigger(Trigger.neoplasmReact, neoplasmWater); + + trigger(Trigger.shockwaveTowerUse, shockwaveTowerUse); + Events.run(Trigger.enablePixelation, enablePixelation::complete); Events.run(Trigger.thoriumReactorOverheat, () -> { @@ -224,9 +358,28 @@ public class GameService{ } }); + Events.on(GeneratorPressureExplodeEvent.class, e -> { + if(campaign() && e.build.block == Blocks.neoplasiaReactor){ + neoplasiaExplosion.complete(); + } + }); + trigger(Trigger.shock, shockWetEnemy); - trigger(Trigger.phaseDeflectHit, killEnemyPhaseWall); + trigger(Trigger.blastFreeze, blastFrozenUnit); + + Events.on(UnitBulletDestroyEvent.class, e -> { + if(state.isCampaign() && player != null && player.team() == e.bullet.team){ + + if(e.bullet.owner instanceof WallBuild){ + killEnemyPhaseWall.complete(); + } + + if(e.unit.type == UnitTypes.eclipse && e.bullet.owner instanceof TurretBuild turret && turret.block == Blocks.duo){ + killEclipseDuo.complete(); + } + } + }); Events.on(LaunchItemEvent.class, e -> { if(campaign()){ @@ -276,6 +429,8 @@ public class GameService{ if(!TechTree.all.contains(t -> t.content.locked())){ researchAll.complete(); } + + if(Blocks.logicProcessor.unlocked()) researchLogic.complete(); }; //check unlocked stuff on load as well @@ -289,6 +444,12 @@ public class GameService{ } }); + Events.on(ClientPreConnectEvent.class, e -> { + if(e.host != null && !e.host.address.startsWith("steam:") && !e.host.address.startsWith("192.")){ + joinCommunityServer.complete(); + } + }); + Events.on(SectorCaptureEvent.class, e -> { if(e.sector.isBeingPlayed() || net.client()){ if(Vars.state.wave <= 5 && state.rules.attackMode){ @@ -312,8 +473,18 @@ public class GameService{ captureAllSectors.complete(); } + if(!e.sector.planet.sectors.contains(s -> s.preset != null && !s.hasBase())){ + allPresetsErekir.complete(); + } + SStat.sectorsControlled.set(e.sector.planet.sectors.count(Sector::hasBase)); }); + + Events.on(PayloadDropEvent.class, e -> { + if(e.unit != null && e.carrier.team == state.rules.defaultTeam && state.rules.waveTeam.cores().contains(c -> c.within(e.unit, state.rules.enemyCoreBuildRadius))){ + dropUnitsCoreZone.complete(); + } + }); } private void checkUpdate(){ @@ -330,6 +501,20 @@ public class GameService{ break; } } + + for(var up : Groups.powerGraph){ + var graph = up.graph(); + if(graph.all.size > 0 && graph.all.first().team == player.team()){ + float balance = graph.getPowerBalance(); + if(balance < 10_000) negative10kPower.complete(); + if(balance > 100_000) positive100kPower.complete(); + if(graph.getBatteryStored() > 1_000_000) store1milPower.complete(); + } + } + } + + if(ui.consolefrag.shown()){ + openConsole.complete(); } } diff --git a/core/src/mindustry/service/SStat.java b/core/src/mindustry/service/SStat.java index d099b83bda..598d96d102 100644 --- a/core/src/mindustry/service/SStat.java +++ b/core/src/mindustry/service/SStat.java @@ -22,6 +22,8 @@ public enum SStat{ maxProduction, sectorsControlled, schematicsCreated, + bouldersDeconstructed, //TODO + totalCampaignItems, //TODO ; public int get(){ diff --git a/core/src/mindustry/type/CellLiquid.java b/core/src/mindustry/type/CellLiquid.java index fff557e1be..957ffc6542 100644 --- a/core/src/mindustry/type/CellLiquid.java +++ b/core/src/mindustry/type/CellLiquid.java @@ -1,12 +1,15 @@ package mindustry.type; +import arc.*; import arc.graphics.*; import arc.graphics.g2d.*; import arc.math.*; import arc.math.geom.*; import arc.util.*; import mindustry.*; +import mindustry.content.*; import mindustry.entities.*; +import mindustry.game.EventType.*; import mindustry.gen.*; import mindustry.graphics.*; import mindustry.world.*; @@ -35,6 +38,7 @@ public class CellLiquid extends Liquid{ if(spreadTarget != null){ float scaling = Mathf.pow(Mathf.clamp(puddle.amount / maxLiquid), 2f); + boolean reacted = false; for(var point : Geometry.d4c){ Tile tile = puddle.tile.nearby(point); @@ -42,11 +46,13 @@ public class CellLiquid extends Liquid{ float amount = Math.min(tile.build.liquids.get(spreadTarget), maxSpread * Time.delta * scaling); tile.build.liquids.remove(spreadTarget, amount * removeScaling); Puddles.deposit(tile, this, amount * spreadConversion); + reacted = true; } } //damage thing it is on if(spreadDamage > 0 && puddle.tile.build != null && puddle.tile.build.liquids != null && puddle.tile.build.liquids.get(spreadTarget) > 0.0001f){ + reacted = true; //spread in 4 adjacent directions around thing it is on float amountSpread = Math.min(puddle.tile.build.liquids.get(spreadTarget) * spreadConversion, maxSpread * Time.delta) / 2f; @@ -70,6 +76,7 @@ public class CellLiquid extends Liquid{ float amount = Math.min(other.amount, Math.max(maxSpread * Time.delta * scaling, other.amount * 0.25f * scaling)); other.amount -= amount; puddle.amount += amount; + reacted = true; if(other.amount <= maxLiquid / 3f){ other.remove(); Puddles.deposit(tile, puddle.tile, this, Math.max(amount, maxLiquid / 3f)); @@ -77,6 +84,10 @@ public class CellLiquid extends Liquid{ } } } + + if(reacted && this == Liquids.neoplasm){ + Events.fire(Trigger.neoplasmReact); + } } } diff --git a/core/src/mindustry/ui/dialogs/PlanetDialog.java b/core/src/mindustry/ui/dialogs/PlanetDialog.java index d1a5f4a4fa..faa45bcb50 100644 --- a/core/src/mindustry/ui/dialogs/PlanetDialog.java +++ b/core/src/mindustry/ui/dialogs/PlanetDialog.java @@ -22,6 +22,7 @@ import mindustry.content.*; import mindustry.content.TechTree.*; import mindustry.core.*; import mindustry.ctype.*; +import mindustry.game.EventType.*; import mindustry.game.Objectives.*; import mindustry.game.SectorInfo.*; import mindustry.game.*; @@ -1215,10 +1216,13 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{ CoreBlock block = sector.allowLaunchSchematics() ? (from.info.bestCoreType instanceof CoreBlock b ? b : (CoreBlock)from.planet.defaultCore) : (CoreBlock)from.planet.defaultCore; loadouts.show(block, from, sector, () -> { - var schemCore = universe.getLastLoadout().findCore(); - from.removeItems(universe.getLastLoadout().requirements()); + var loadout = universe.getLastLoadout(); + var schemCore = loadout.findCore(); + from.removeItems(loadout.requirements()); from.removeItems(universe.getLaunchResources()); + Events.fire(new SectorLaunchLoadoutEvent(sector, from, loadout)); + if(settings.getBool("skipcoreanimation")){ //just... go there control.playSector(from, sector); diff --git a/core/src/mindustry/world/Block.java b/core/src/mindustry/world/Block.java index 92332e8796..67f7535bac 100644 --- a/core/src/mindustry/world/Block.java +++ b/core/src/mindustry/world/Block.java @@ -856,6 +856,10 @@ public class Block extends UnlockableContent implements Senseable{ return !isHidden() && (state.rules.editor || (!state.rules.hideBannedBlocks || !state.rules.isBanned(this))); } + public boolean isVisibleOn(Planet planet){ + return !Structs.contains(requirements, i -> planet.hiddenItems.contains(i.item)); + } + public boolean isPlaceable(){ return isVisible() && (!state.rules.isBanned(this) || state.rules.editor) && supportsEnv(state.rules.env); } diff --git a/core/src/mindustry/world/blocks/defense/ForceProjector.java b/core/src/mindustry/world/blocks/defense/ForceProjector.java index 7c8e7d4bd9..fd9ad80508 100644 --- a/core/src/mindustry/world/blocks/defense/ForceProjector.java +++ b/core/src/mindustry/world/blocks/defense/ForceProjector.java @@ -1,5 +1,6 @@ package mindustry.world.blocks.defense; +import arc.*; import arc.func.*; import arc.graphics.*; import arc.graphics.g2d.*; @@ -10,6 +11,7 @@ import arc.util.io.*; import mindustry.annotations.Annotations.*; import mindustry.content.*; import mindustry.entities.*; +import mindustry.game.EventType.*; import mindustry.game.*; import mindustry.gen.*; import mindustry.graphics.*; @@ -186,6 +188,9 @@ public class ForceProjector extends Block{ broken = true; buildup = shieldHealth; shieldBreakEffect.at(x, y, realRadius(), team.color); + if(team != state.rules.defaultTeam){ + Events.fire(Trigger.forceProjectorBreak); + } } if(hit > 0f){ diff --git a/core/src/mindustry/world/blocks/defense/ShockwaveTower.java b/core/src/mindustry/world/blocks/defense/ShockwaveTower.java index 513086c485..da342c25b1 100644 --- a/core/src/mindustry/world/blocks/defense/ShockwaveTower.java +++ b/core/src/mindustry/world/blocks/defense/ShockwaveTower.java @@ -1,18 +1,20 @@ package mindustry.world.blocks.defense; -import arc.math.*; -import arc.util.*; -import arc.struct.*; +import arc.*; +import arc.audio.*; import arc.graphics.*; import arc.graphics.g2d.*; -import arc.audio.*; +import arc.math.*; +import arc.struct.*; +import arc.util.*; +import mindustry.annotations.Annotations.*; import mindustry.content.*; +import mindustry.entities.*; +import mindustry.game.EventType.*; import mindustry.gen.*; import mindustry.graphics.*; -import mindustry.world.meta.*; import mindustry.world.*; -import mindustry.entities.*; -import mindustry.annotations.Annotations.Load; +import mindustry.world.meta.*; import static mindustry.Vars.*; @@ -89,6 +91,10 @@ public class ShockwaveTower extends Block{ target.remove(); } } + + if(team == state.rules.defaultTeam){ + Events.fire(Trigger.shockwaveTowerUse); + } } } diff --git a/core/src/mindustry/world/blocks/power/ConsumeGenerator.java b/core/src/mindustry/world/blocks/power/ConsumeGenerator.java index e4757fdec2..ede8186e3e 100644 --- a/core/src/mindustry/world/blocks/power/ConsumeGenerator.java +++ b/core/src/mindustry/world/blocks/power/ConsumeGenerator.java @@ -1,10 +1,12 @@ package mindustry.world.blocks.power; +import arc.*; import arc.graphics.*; import arc.math.*; import arc.util.*; import mindustry.content.*; import mindustry.entities.*; +import mindustry.game.EventType.*; import mindustry.graphics.*; import mindustry.type.*; import mindustry.world.consumers.*; @@ -115,6 +117,7 @@ public class ConsumeGenerator extends PowerGenerator{ if(explodeOnFull && liquids.get(outputLiquid.liquid) >= liquidCapacity - 0.0001f){ kill(); + Events.fire(new GeneratorPressureExplodeEvent(this)); } } diff --git a/core/src/mindustry/world/consumers/ConsumeItemExplode.java b/core/src/mindustry/world/consumers/ConsumeItemExplode.java index 64715902eb..f2d3fb78f9 100644 --- a/core/src/mindustry/world/consumers/ConsumeItemExplode.java +++ b/core/src/mindustry/world/consumers/ConsumeItemExplode.java @@ -1,10 +1,12 @@ package mindustry.world.consumers; +import arc.*; import arc.math.*; import arc.scene.ui.layout.*; import mindustry.*; import mindustry.content.*; import mindustry.entities.*; +import mindustry.game.EventType.*; import mindustry.gen.*; import mindustry.world.*; import mindustry.world.meta.*; @@ -34,6 +36,7 @@ public class ConsumeItemExplode extends ConsumeItemFilter{ if(Vars.state.rules.reactorExplosions && Mathf.chance(build.delta() * baseChance * Mathf.clamp(item.explosiveness - threshold))){ build.damage(damage); explodeEffect.at(build.x + Mathf.range(build.block.size * tilesize / 2f), build.y + Mathf.range(build.block.size * tilesize / 2f)); + Events.fire(Trigger.blastGenerator); } } }