mirror of
https://github.com/Anuken/Mindustry.git
synced 2025-02-21 20:18:06 +07:00
Many various campaign mechanic changes
This commit is contained in:
parent
b95206cf87
commit
26e70fa585
@ -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 */
|
||||
|
@ -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){
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
|
||||
|
@ -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<Tile> 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<Tile> 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{
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -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){
|
||||
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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){
|
||||
|
@ -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<Sector> tmpSeq1 = new Seq<>(), tmpSeq2 = new Seq<>(), tmpSeq3 = new Seq<>();
|
||||
private static final ObjectSet<Sector> 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<Sector> 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<Sector> 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();
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user