From 26e70fa585317b91ea12222e98835b5e6027d189 Mon Sep 17 00:00:00 2001 From: Anuken Date: Tue, 7 Jul 2020 21:13:03 -0400 Subject: [PATCH] Many various campaign mechanic changes --- core/src/mindustry/Vars.java | 2 + core/src/mindustry/core/Logic.java | 13 +++-- core/src/mindustry/game/EventType.java | 9 ++++ core/src/mindustry/game/Universe.java | 18 +++++++ core/src/mindustry/maps/SectorDamage.java | 49 ++++++++++++++---- .../maps/generators/PlanetGenerator.java | 28 ++++++++++ .../maps/planet/TODOPlanetGenerator.java | 10 ++++ core/src/mindustry/type/Planet.java | 40 +++++++++++++-- core/src/mindustry/type/Sector.java | 51 ++++++++++++++++--- .../mindustry/ui/dialogs/PlanetDialog.java | 16 +++++- .../mindustry/ui/fragments/HudFragment.java | 15 +++++- 11 files changed, 220 insertions(+), 31 deletions(-) diff --git a/core/src/mindustry/Vars.java b/core/src/mindustry/Vars.java index fce8973805..34b4e99e8a 100644 --- a/core/src/mindustry/Vars.java +++ b/core/src/mindustry/Vars.java @@ -80,6 +80,8 @@ public class Vars implements Loadable{ public static final float buildingRange = 220f; /** duration of time between turns in ticks */ public static final float turnDuration = 20 * Time.toMinutes; + /** turns needed to destroy a sector completely */ + public static final float sectorDestructionTurns = 3f; /** min armor fraction damage; e.g. 0.05 = at least 5% damage */ public static final float minArmorDamage = 0.05f; /** launch animation duration */ diff --git a/core/src/mindustry/core/Logic.java b/core/src/mindustry/core/Logic.java index aaf19238f4..5b2075d430 100644 --- a/core/src/mindustry/core/Logic.java +++ b/core/src/mindustry/core/Logic.java @@ -11,6 +11,7 @@ import mindustry.game.EventType.*; import mindustry.game.*; import mindustry.game.Teams.*; import mindustry.gen.*; +import mindustry.maps.*; import mindustry.type.*; import mindustry.type.Weather.*; import mindustry.world.*; @@ -87,14 +88,16 @@ public class Logic implements ApplicationListener{ //when loading a 'damaged' sector, propagate the damage Events.on(WorldLoadEvent.class, e -> { - if(state.isCampaign() && state.rules.sector.getSecondsPassed() > 0){ + if(state.isCampaign() && state.rules.sector.getSecondsPassed() > 0 && state.rules.sector.hasBase()){ long seconds = state.rules.sector.getSecondsPassed(); CoreEntity core = state.rules.defaultTeam.core(); - //TODO figure out how to apply damage properly - // if(state.rules.sector.hasWaves()){ - //SectorDamage.apply(seconds); - //} + //apply fractional damage based on how many turns have passed for this sector + float turnsPassed = seconds / (turnDuration / 60f); + + if(state.rules.sector.hasWaves()){ + SectorDamage.apply(turnsPassed / sectorDestructionTurns); + } //add resources based on turns passed if(state.rules.sector.save != null && core != null){ diff --git a/core/src/mindustry/game/EventType.java b/core/src/mindustry/game/EventType.java index 256fdae6c3..8915a6917b 100644 --- a/core/src/mindustry/game/EventType.java +++ b/core/src/mindustry/game/EventType.java @@ -59,6 +59,15 @@ public class EventType{ /** Called when a game begins and the world is loaded. */ public static class WorldLoadEvent{} + /** Called when a sector is destroyed by waves when you're not there. */ + public static class SectorLoseEvent{ + public final Sector sector; + + public SectorLoseEvent(Sector sector){ + this.sector = sector; + } + } + public static class LaunchItemEvent{ public final ItemStack stack; diff --git a/core/src/mindustry/game/Universe.java b/core/src/mindustry/game/Universe.java index 7712fbabe9..105e2e4da8 100644 --- a/core/src/mindustry/game/Universe.java +++ b/core/src/mindustry/game/Universe.java @@ -23,6 +23,13 @@ public class Universe{ public Universe(){ load(); + + //update base coverage on capture + Events.on(SectorCaptureEvent.class, e -> { + if(state.isCampaign()){ + state.getSector().planet.updateBaseCoverage(); + } + }); } /** Update regardless of whether the player is in the campaign. */ @@ -131,6 +138,17 @@ public class Universe{ //increment seconds passed for this sector by the time that just passed with this turn if(!sector.isBeingPlayed()){ sector.setSecondsPassed(sector.getSecondsPassed() + actuallyPassed); + + //check if the sector has been attacked too many times... + if(sector.hasBase() && sector.getSecondsPassed() * 60f > turnDuration * sectorDestructionTurns){ + //fire event for losing the sector + Events.fire(new SectorLoseEvent(sector)); + + //if so, just delete the save for now. it's lost. + //TODO don't delete it later maybe + sector.save.delete(); + sector.save = null; + } } //reset time spent to 0 diff --git a/core/src/mindustry/maps/SectorDamage.java b/core/src/mindustry/maps/SectorDamage.java index d595705f84..cf0c785d45 100644 --- a/core/src/mindustry/maps/SectorDamage.java +++ b/core/src/mindustry/maps/SectorDamage.java @@ -3,7 +3,10 @@ package mindustry.maps; import arc.math.*; import arc.math.geom.*; import arc.struct.*; +import mindustry.ai.*; import mindustry.content.*; +import mindustry.entities.*; +import mindustry.gen.*; import mindustry.world.*; import mindustry.world.blocks.storage.*; @@ -11,15 +14,14 @@ import static mindustry.Vars.*; public class SectorDamage{ //direct damage is for testing only - private static final boolean direct = false; + private static final boolean direct = false, rubble = true; - //TODO amount of damage could be related to wave spacing - public static void apply(float turns){ + public static void apply(float fraction){ Tiles tiles = world.tiles; Queue frontier = new Queue<>(); float[][] values = new float[tiles.width][tiles.height]; - float damage = turns*50; + float damage = fraction*80; //arbitrary damage value //phase one: find all spawnpoints for(Tile tile : tiles){ @@ -29,6 +31,29 @@ public class SectorDamage{ } } + Building core = state.rules.defaultTeam.core(); + if(core != null && !frontier.isEmpty()){ + for(Tile spawner : frontier){ + //find path from spawn to core + Seq path = Astar.pathfind(spawner, core.tile, t -> t.cost, t -> !(t.block().isStatic() && t.solid())); + int amount = (int)(path.size * fraction); + for(int i = 0; i < amount; i++){ + Tile t = path.get(i); + Geometry.circle(t.x, t.y, tiles.width, tiles.height, 5, (cx, cy) -> { + Tile other = tiles.getn(cx, cy); + //just remove all the buildings in the way - as long as they're not cores! + if(other.build != null && other.team() == state.rules.defaultTeam && !(other.block() instanceof CoreBlock)){ + if(rubble && !other.floor().solid && !other.floor().isLiquid && Mathf.chance(0.4)){ + Effects.rubble(other.build.x, other.build.y, other.block().size); + } + + other.remove(); + } + }); + } + } + } + float falloff = (damage) / (Math.max(tiles.width, tiles.height) * Mathf.sqrt2); int peak = 0; @@ -53,14 +78,16 @@ public class SectorDamage{ if(direct){ other.build.damage(currDamage); }else{ //indirect damage happens at game load time - other.build.health(other.build.health() - currDamage); + other.build.health -= currDamage; + //don't kill the core! + if(other.block() instanceof CoreBlock) other.build.health = Math.max(other.build.health, 1f); //remove the block when destroyed - if(other.build.health() < 0){ - //rubble currently disabled - //if(!other.floor().solid && !other.floor().isLiquid && Mathf.chance(0.4)){ - // Effects.rubble(other.entity.x(), other.entity.y(), other.block().size); - //} + if(other.build.health < 0){ + //rubble + if(rubble && !other.floor().solid && !other.floor().isLiquid && Mathf.chance(0.4)){ + Effects.rubble(other.build.x, other.build.y, other.block().size); + } other.remove(); } @@ -78,5 +105,7 @@ public class SectorDamage{ } } + + } } diff --git a/core/src/mindustry/maps/generators/PlanetGenerator.java b/core/src/mindustry/maps/generators/PlanetGenerator.java index 132eaf9ae0..2ce0bbae27 100644 --- a/core/src/mindustry/maps/generators/PlanetGenerator.java +++ b/core/src/mindustry/maps/generators/PlanetGenerator.java @@ -1,13 +1,41 @@ package mindustry.maps.generators; import arc.math.geom.*; +import arc.util.noise.*; import mindustry.graphics.g3d.*; +import mindustry.graphics.g3d.PlanetGrid.*; import mindustry.type.*; +import mindustry.type.Sector.*; import mindustry.world.*; public abstract class PlanetGenerator extends BasicGenerator implements HexMesher{ protected Sector sector; + /** Should generate sector bases for a planet. */ + public void generateSector(Sector sector){ + Ptile tile = sector.tile; + + boolean any = false; + float noise = Noise.snoise3(tile.v.x, tile.v.y, tile.v.z, 0.001f, 0.5f); + + if(noise > 0.028){ + any = true; + } + + if(noise < 0.15){ + for(Ptile other : tile.tiles){ + if(sector.planet.getSector(other).is(SectorAttribute.base)){ + any = false; + break; + } + } + } + + if(any){ + sector.data.attributes |= (1 << SectorAttribute.base.ordinal()); + } + } + protected void genTile(Vec3 position, TileGen tile){ } diff --git a/core/src/mindustry/maps/planet/TODOPlanetGenerator.java b/core/src/mindustry/maps/planet/TODOPlanetGenerator.java index 8f50cf3ff1..648e113558 100644 --- a/core/src/mindustry/maps/planet/TODOPlanetGenerator.java +++ b/core/src/mindustry/maps/planet/TODOPlanetGenerator.java @@ -288,6 +288,16 @@ public class TODOPlanetGenerator extends PlanetGenerator{ } state.rules.waves = true; + + float difficulty = sector.baseCoverage; + + //scale up the spawning base on difficulty (this is just for testing) + for(SpawnGroup group : state.rules.spawns){ + group.unitAmount *= difficulty; + if(group.unitScaling != SpawnGroup.never){ + group.unitScaling *= difficulty; + } + } } @Override diff --git a/core/src/mindustry/type/Planet.java b/core/src/mindustry/type/Planet.java index e1b47919be..f27fc0aace 100644 --- a/core/src/mindustry/type/Planet.java +++ b/core/src/mindustry/type/Planet.java @@ -10,6 +10,7 @@ import arc.struct.*; import arc.util.ArcAnnotate.*; import arc.util.*; import arc.util.io.*; +import arc.util.noise.*; import mindustry.*; import mindustry.ctype.*; import mindustry.graphics.*; @@ -18,7 +19,7 @@ import mindustry.graphics.g3d.PlanetGrid.*; import mindustry.maps.generators.*; import mindustry.type.Sector.*; -import static mindustry.Vars.universe; +import static mindustry.Vars.*; public class Planet extends UnlockableContent{ /** Default spacing between planet orbits in world units. */ @@ -98,10 +99,6 @@ public class Planet extends UnlockableContent{ t.printStackTrace(); } } - - for(Sector sector : sectors){ - sector.generate(); - } }else{ sectors = new Seq<>(); } @@ -183,6 +180,24 @@ public class Planet extends UnlockableContent{ return in; } + /** Updates wave coverage of bases. */ + public void updateBaseCoverage(){ + for(Sector sector : sectors){ + float sum = 1f; + for(Sector other : sector.inRange(2)){ + if(other.is(SectorAttribute.base)){ + sum += 1f; + } + } + + if(sector.hasEnemyBase()){ + sum += 2f; + } + + sector.baseCoverage = sum; + } + } + /** @return the supplied matrix with transformation applied. */ public Mat3D getTransform(Mat3D mat){ return mat.setToTranslation(position).rotate(Vec3.Y, getRotation()); @@ -193,6 +208,21 @@ public class Planet extends UnlockableContent{ mesh = meshLoader.get(); } + @Override + public void init(){ + + if(generator != null){ + Noise.setSeed(id + 1); + + for(Sector sector : sectors){ + generator.generateSector(sector); + } + + updateBaseCoverage(); + } + + } + @Override public void dispose(){ if(mesh != null){ diff --git a/core/src/mindustry/type/Sector.java b/core/src/mindustry/type/Sector.java index 5d922979a6..d5c72b754d 100644 --- a/core/src/mindustry/type/Sector.java +++ b/core/src/mindustry/type/Sector.java @@ -1,6 +1,7 @@ package mindustry.type; import arc.*; +import arc.func.*; import arc.math.geom.*; import arc.struct.*; import arc.util.ArcAnnotate.*; @@ -16,6 +17,9 @@ import static mindustry.Vars.*; /** A small section of a planet. */ public class Sector{ + private static final Seq tmpSeq1 = new Seq<>(), tmpSeq2 = new Seq<>(), tmpSeq3 = new Seq<>(); + private static final ObjectSet tmpSet = new ObjectSet<>(); + public final SectorRect rect; public final Plane plane; public final Planet planet; @@ -27,8 +31,7 @@ public class Sector{ public @Nullable SaveSlot save; public @Nullable SectorPreset preset; - /** Sector enemy hostility from 0 to 1 */ - //public float hostility; + public float baseCoverage; //TODO implement a dynamic launch period public int launchPeriod = 10; @@ -42,6 +45,45 @@ public class Sector{ this.data = data; } + public Seq inRange(int range){ + //TODO cleanup/remove + if(true){ + tmpSeq1.clear(); + neighbors(tmpSeq1::add); + + return tmpSeq1; + } + + tmpSeq1.clear(); + tmpSeq2.clear(); + tmpSet.clear(); + + tmpSeq1.add(this); + tmpSet.add(this); + for(int i = 0; i < range; i++){ + while(!tmpSeq1.isEmpty()){ + Sector sec = tmpSeq1.pop(); + tmpSet.add(sec); + sec.neighbors(other -> { + if(tmpSet.add(other)){ + tmpSeq2.add(other); + } + }); + } + tmpSeq1.clear(); + tmpSeq1.addAll(tmpSeq2); + } + + tmpSeq3.clear().addAll(tmpSeq2); + return tmpSeq3; + } + + public void neighbors(Cons cons){ + for(Ptile tile : tile.tiles){ + cons.get(planet.getSector(tile)); + } + } + /** @return whether this sector can be landed on at all. * Only sectors adjacent to non-wave sectors can be landed on. * TODO also preset sectors*/ @@ -77,11 +119,6 @@ public class Sector{ return save != null; } - public void generate(){ - //TODO use simplex and a seed - //hostility = Math.max(Noise.snoise3(tile.v.x, tile.v.y, tile.v.z, 0.5f, 0.4f), 0); - } - public boolean locked(){ return !unlocked(); } diff --git a/core/src/mindustry/ui/dialogs/PlanetDialog.java b/core/src/mindustry/ui/dialogs/PlanetDialog.java index 72dd3200bf..ed847a67d3 100644 --- a/core/src/mindustry/ui/dialogs/PlanetDialog.java +++ b/core/src/mindustry/ui/dialogs/PlanetDialog.java @@ -122,7 +122,11 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{ //draw all sector stuff for(Sector sec : planet.sectors){ if(selectAlpha > 0.01f){ - if(canLaunch(sec) || sec.unlocked()){ + if(/*canLaunch(sec) || sec.unlocked()*/true){ + if(sec.baseCoverage > 0){ + planets.fill(sec, Tmp.c1.set(Team.crux.color).a(0.1f * sec.baseCoverage * selectAlpha), -0.002f); + } + Color color = sec.hasBase() ? Team.sharded.color : sec.preset != null ? Team.derelict.color : @@ -220,6 +224,7 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{ @Override public void draw(){ planets.render(PlanetDialog.this); + Core.scene.setScrollFocus(PlanetDialog.this); } }, new Table(t -> { @@ -277,7 +282,7 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{ selectAlpha = Mathf.lerpDelta(selectAlpha, Mathf.num(planets.zoom < 1.9f), 0.1f); } - //TODO add strings to bundle after prototyping is done + //TODO localize private void updateSelected(){ Sector sector = selected; @@ -294,6 +299,13 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{ stable.image().color(Pal.accent).fillX().height(3f).pad(3f).row(); stable.add(sector.save != null ? sector.save.getPlayTime() : "[lightgray]Unexplored").row(); + if(sector.hasBase() && sector.hasWaves()){ + stable.add("[scarlet]Under attack!"); + stable.row(); + stable.add("[accent]" + Mathf.ceil(sectorDestructionTurns - (sector.getSecondsPassed() * 60) / turnDuration) + " turn(s) until destruction"); + stable.row(); + } + stable.add("Resources:").row(); stable.table(t -> { t.left(); diff --git a/core/src/mindustry/ui/fragments/HudFragment.java b/core/src/mindustry/ui/fragments/HudFragment.java index fc43390be6..c4f15f8b0f 100644 --- a/core/src/mindustry/ui/fragments/HudFragment.java +++ b/core/src/mindustry/ui/fragments/HudFragment.java @@ -7,6 +7,7 @@ import arc.math.*; import arc.scene.*; import arc.scene.actions.*; import arc.scene.event.*; +import arc.scene.style.*; import arc.scene.ui.*; import arc.scene.ui.ImageButton.*; import arc.scene.ui.layout.*; @@ -28,13 +29,14 @@ import mindustry.ui.dialogs.*; import static mindustry.Vars.*; public class HudFragment extends Fragment{ + private static final float dsize = 47.2f; + public final PlacementFragment blockfrag = new PlacementFragment(); private ImageButton flip; private Table lastUnlockTable; private Table lastUnlockLayout; private boolean shown = true; - private float dsize = 47.2f; private CoreItemsDisplay coreItems = new CoreItemsDisplay(); private String hudText = ""; @@ -51,6 +53,11 @@ public class HudFragment extends Fragment{ showToast("Sector[accent] captured[]!"); }); + //TODO localize + Events.on(SectorLoseEvent.class, e -> { + showToast(Icon.warning, "Sector " + e.sector.id + " [scarlet]lost!"); + }); + //TODO full implementation Events.on(ResetEvent.class, e -> { coreItems.resetUsed(); @@ -393,6 +400,10 @@ public class HudFragment extends Fragment{ } public void showToast(String text){ + showToast(Icon.ok, text); + } + + public void showToast(Drawable icon, String text){ if(state.isMenu()) return; scheduleToast(() -> { @@ -405,7 +416,7 @@ public class HudFragment extends Fragment{ } }); table.margin(12); - table.image(Icon.ok).pad(3); + table.image(icon).pad(3); table.add(text).wrap().width(280f).get().setAlignment(Align.center, Align.center); table.pack();