Sector refactoring, invasions and cleanup

This commit is contained in:
Anuken
2020-10-16 11:02:24 -04:00
parent 5ee4101ba4
commit 2f54edf34f
27 changed files with 319 additions and 376 deletions

View File

@@ -73,6 +73,15 @@ public class EventType{
}
}
/** Called when a sector is destroyed by waves when you're not there. */
public static class SectorInvasionEvent{
public final Sector sector;
public SectorInvasionEvent(Sector sector){
this.sector = sector;
}
}
public static class LaunchItemEvent{
public final ItemStack stack;

View File

@@ -26,7 +26,7 @@ public class SectorInfo{
/** Export statistics. */
public ObjectMap<Item, ExportStat> export = new ObjectMap<>();
/** Items stored in all cores. */
public ItemSeq coreItems = new ItemSeq();
public ItemSeq items = new ItemSeq();
/** The best available core type. */
public Block bestCoreType = Blocks.air;
/** Max storage capacity. */
@@ -39,13 +39,26 @@ public class SectorInfo{
public @Nullable Sector destination;
/** Resources known to occur at this sector. */
public Seq<UnlockableContent> resources = new Seq<>();
/** Whether waves are enabled here. */
public boolean waves = true;
/** Wave # from state */
public int wave = 1, winWave = -1;
/** Time between waves. */
public float waveSpacing = 60 * 60 * 2;
/** Damage dealt to sector. */
public float damage;
/** How many waves have passed while the player was away. */
public int wavesPassed;
/** Packed core spawn position. */
public int spawnPosition;
/** How long the player has been playing elsewhere. */
public float secondsPassed;
/** Display name. */
public @Nullable String name;
/** Special variables for simulation. */
public float sumHealth, sumRps, sumDps, waveHealthBase, waveHealthSlope, waveDpsBase, waveDpsSlope;
/** Time spent at this sector. Do not use unless you know what you're doing. */
public transient float internalTimeSpent;
/** Counter refresh state. */
private transient Interval time = new Interval();
/** Core item storage to prevent spoofing. */
@@ -84,27 +97,55 @@ public class SectorInfo{
return export.get(item, ExportStat::new).mean;
}
/** Write contents of meta into main storage. */
public void write(){
state.wave = wave;
state.rules.waves = waves;
state.rules.waveSpacing = waveSpacing;
state.rules.winWave = winWave;
CoreBuild entity = state.rules.defaultTeam.core();
if(entity != null){
entity.items.clear();
entity.items.add(items);
//ensure capacity.
entity.items.each((i, a) -> entity.items.set(i, Math.min(a, entity.block.itemCapacity)));
}
//TODO write items.
}
/** Prepare data for writing to a save. */
public void prepare(){
//update core items
coreItems.clear();
items.clear();
CoreBuild entity = state.rules.defaultTeam.core();
if(entity != null){
ItemModule items = entity.items;
for(int i = 0; i < items.length(); i++){
coreItems.set(content.item(i), items.get(i));
this.items.set(content.item(i), items.get(i));
}
spawnPosition = entity.pos();
}
waveSpacing = state.rules.waveSpacing;
wave = state.wave;
winWave = state.rules.winWave;
waves = state.rules.waves;
hasCore = entity != null;
bestCoreType = !hasCore ? Blocks.air : state.rules.defaultTeam.cores().max(e -> e.block.size).block;
storageCapacity = entity != null ? entity.storageCapacity : 0;
secondsPassed = 0;
wavesPassed = 0;
damage = 0;
//update sector's internal time spent counter
state.rules.sector.setTimeSpent(internalTimeSpent);
state.rules.sector.setUnderAttack(state.rules.waves);
if(state.rules.sector != null){
state.rules.sector.info = this;
state.rules.sector.saveInfo();
}
SectorDamage.writeParameters(this);
}
@@ -115,14 +156,6 @@ public class SectorInfo{
//updating in multiplayer as a client doesn't make sense
if(net.client()) return;
internalTimeSpent += Time.delta;
//autorun turns
if(internalTimeSpent >= turnDuration){
internalTimeSpent = 0;
universe.runTurn();
}
CoreBuild ent = state.rules.defaultTeam.core();
//refresh throughput

View File

@@ -40,7 +40,7 @@ public class Stats{
//weigh used fractions
float frac = 0f;
Seq<Item> obtainable = zone.save == null ? new Seq<>() : zone.save.meta.secinfo.resources.select(i -> i instanceof Item).as();
Seq<Item> obtainable = zone.save == null ? new Seq<>() : zone.info.resources.select(i -> i instanceof Item).as();
for(Item item : obtainable){
frac += Mathf.clamp((float)itemsDelivered.get(item, 0) / capacity) / (float)obtainable.size;
}

View File

@@ -18,6 +18,7 @@ public class Universe{
private int netSeconds;
private float secondCounter;
private int turn;
private float turnCounter;
private Schematic lastLoadout;
private ItemSeq lastLaunchResources = new ItemSeq();
@@ -54,17 +55,19 @@ public class Universe{
}
}
/** @return sectors attacked on the current planet, minus the ones that are being played on right now. */
public Seq<Sector> getAttacked(Planet planet){
return planet.sectors.select(s -> s.isUnderAttack() && s.hasBase() && !s.isBeingPlayed() && s.getWavesPassed() > 0);
}
/** Update planet rotations, global time and relevant state. */
public void update(){
//only update time when not in multiplayer
if(!net.client()){
secondCounter += Time.delta / 60f;
turnCounter += Time.delta;
//auto-run turns
if(turnCounter >= turnDuration){
turnCounter = 0;
runTurn();
}
if(secondCounter >= 1){
seconds += (int)secondCounter;
@@ -133,59 +136,84 @@ public class Universe{
//update relevant sectors
for(Planet planet : content.planets()){
for(Sector sector : planet.sectors){
if(sector.hasSave()){
int spent = (int)(sector.getTimeSpent() / 60);
int actuallyPassed = Math.max(newSecondsPassed - spent, 0);
if(sector.hasSave() && sector.hasBase()){
//increment seconds passed for this sector by the time that just passed with this turn
if(!sector.isBeingPlayed()){
int secPassed = sector.getSecondsPassed() + actuallyPassed;
//increment time
sector.info.secondsPassed += turnDuration/60f;
sector.setSecondsPassed(secPassed);
boolean attacked = sector.isUnderAttack();
int wavesPassed = (int)(secPassed*60f / sector.save.meta.rules.waveSpacing);
float damage = attacked ? SectorDamage.getDamage(sector.save.meta.secinfo, sector.save.meta.rules.waveSpacing, sector.save.meta.wave, wavesPassed) : 0f;
int wavesPassed = (int)(sector.info.secondsPassed*60f / sector.info.waveSpacing);
boolean attacked = sector.info.waves;
if(attacked){
sector.setWavesPassed(wavesPassed);
sector.info.wavesPassed = wavesPassed;
}
sector.setDamage(damage);
float damage = attacked ? SectorDamage.getDamage(sector.info) : 0f;
//damage never goes down until the player visits the sector, so use max
sector.info.damage = Math.max(sector.info.damage, damage);
//check if the sector has been attacked too many times...
if(attacked && damage >= 0.999f){
//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.setExtraItems(new ItemSeq());
sector.setDamage(1.01f);
}else if(attacked && wavesPassed > 0 && sector.save.meta.wave + wavesPassed >= sector.save.meta.rules.winWave && !sector.hasEnemyBase()){
//sector is dead.
sector.info.items.clear();
sector.info.damage = 1f;
sector.info.hasCore = false;
sector.info.production.clear();
}else if(attacked && wavesPassed > 0 && sector.info.wave + wavesPassed >= sector.info.winWave && !sector.hasEnemyBase()){
//autocapture the sector
sector.setUnderAttack(false);
sector.info.waves = false;
//fire the event
Events.fire(new SectorCaptureEvent(sector));
}
float scl = sector.getProductionScale();
//export to another sector
if(sector.info.destination != null){
Sector to = sector.info.destination;
if(to.hasBase()){
ItemSeq items = new ItemSeq();
//calculated exported items to this sector
sector.info.export.each((item, stat) -> items.add(item, (int)(stat.mean * newSecondsPassed * scl)));
to.addItems(items);
}
}
//add production, making sure that it's capped
sector.info.production.each((item, stat) -> sector.info.items.add(item, Math.min((int)(stat.mean * seconds * scl), sector.info.storageCapacity - sector.info.items.get(item))));
sector.saveInfo();
}
//export to another sector
if(sector.save != null && sector.save.meta != null && sector.save.meta.secinfo != null && sector.save.meta.secinfo.destination != null){
Sector to = sector.save.meta.secinfo.destination;
if(to.save != null){
float scl = Math.max(1f - sector.getDamage(), 0);
ItemSeq items = new ItemSeq();
//calculated exported items to this sector
sector.save.meta.secinfo.export.each((item, stat) -> items.add(item, (int)(stat.mean * newSecondsPassed * scl)));
to.addItems(items);
//queue random invasions
if(!sector.isAttacked() && turn > invasionGracePeriod){
//TODO use factors like difficulty for better invasion chance
if(sector.near().contains(Sector::hasEnemyBase) && Mathf.chance(baseInvasionChance)){
int waveMax = Math.max(sector.info.winWave, sector.isBeingPlayed() ? state.wave : 0) + Mathf.random(2, 4) * 5;
float waveSpace = Math.max(sector.info.waveSpacing - Mathf.random(1, 4) * 5 * 60, 40 * 60);
//assign invasion-related things
if(sector.isBeingPlayed()){
state.rules.winWave = waveMax;
state.rules.waves = true;
state.rules.waveSpacing = waveSpace;
}else{
sector.info.winWave = waveMax;
sector.info.waves = true;
sector.info.waveSpacing = waveSpace;
sector.saveInfo();
}
Events.fire(new SectorInvasionEvent(sector));
}
}
//reset time spent to 0
sector.setTimeSpent(0f);
}
}
}
@@ -202,7 +230,7 @@ public class Universe{
for(Planet planet : content.planets()){
for(Sector sector : planet.sectors){
if(sector.hasSave()){
count.add(sector.calculateItems());
count.add(sector.items());
}
}
}