Experimental positional spawn support / WIP map

This commit is contained in:
Anuken
2021-09-10 13:56:12 -04:00
parent 10c3f9e44a
commit 7c028ffcb8
8 changed files with 59 additions and 14 deletions

Binary file not shown.

Binary file not shown.

View File

@@ -56,7 +56,7 @@ public class WaveSpawner{
public void spawnEnemies(){ public void spawnEnemies(){
spawning = true; spawning = true;
eachGroundSpawn((spawnX, spawnY, doShockwave) -> { eachGroundSpawn(-1, (spawnX, spawnY, doShockwave) -> {
if(doShockwave){ if(doShockwave){
doShockwave(spawnX, spawnY); doShockwave(spawnX, spawnY);
} }
@@ -70,7 +70,7 @@ public class WaveSpawner{
if(group.type.flying){ if(group.type.flying){
float spread = margin / 1.5f; float spread = margin / 1.5f;
eachFlyerSpawn((spawnX, spawnY) -> { eachFlyerSpawn(group.spawn, (spawnX, spawnY) -> {
for(int i = 0; i < spawned; i++){ for(int i = 0; i < spawned; i++){
Unit unit = group.createUnit(state.rules.waveTeam, state.wave - 1); Unit unit = group.createUnit(state.rules.waveTeam, state.wave - 1);
unit.set(spawnX + Mathf.range(spread), spawnY + Mathf.range(spread)); unit.set(spawnX + Mathf.range(spread), spawnY + Mathf.range(spread));
@@ -80,7 +80,7 @@ public class WaveSpawner{
}else{ }else{
float spread = tilesize * 2; float spread = tilesize * 2;
eachGroundSpawn((spawnX, spawnY, doShockwave) -> { eachGroundSpawn(group.spawn, (spawnX, spawnY, doShockwave) -> {
for(int i = 0; i < spawned; i++){ for(int i = 0; i < spawned; i++){
Tmp.v1.rnd(spread); Tmp.v1.rnd(spread);
@@ -102,12 +102,14 @@ public class WaveSpawner{
} }
public void eachGroundSpawn(Intc2 cons){ public void eachGroundSpawn(Intc2 cons){
eachGroundSpawn((x, y, shock) -> cons.get(World.toTile(x), World.toTile(y))); eachGroundSpawn(-1, (x, y, shock) -> cons.get(World.toTile(x), World.toTile(y)));
} }
private void eachGroundSpawn(SpawnConsumer cons){ private void eachGroundSpawn(int filterPos, SpawnConsumer cons){
if(state.hasSpawns()){ if(state.hasSpawns()){
for(Tile spawn : spawns){ for(Tile spawn : spawns){
if(filterPos != -1 && filterPos != spawn.pos()) continue;
cons.accept(spawn.worldx(), spawn.worldy(), true); cons.accept(spawn.worldx(), spawn.worldy(), true);
} }
} }
@@ -115,6 +117,8 @@ public class WaveSpawner{
if(state.rules.attackMode && state.teams.isActive(state.rules.waveTeam) && !state.teams.playerCores().isEmpty()){ if(state.rules.attackMode && state.teams.isActive(state.rules.waveTeam) && !state.teams.playerCores().isEmpty()){
Building firstCore = state.teams.playerCores().first(); Building firstCore = state.teams.playerCores().first();
for(Building core : state.rules.waveTeam.cores()){ for(Building core : state.rules.waveTeam.cores()){
if(filterPos != -1 && filterPos != core.pos()) continue;
Tmp.v1.set(firstCore).sub(core).limit(coreMargin + core.block.size * tilesize /2f * Mathf.sqrt2); Tmp.v1.set(firstCore).sub(core).limit(coreMargin + core.block.size * tilesize /2f * Mathf.sqrt2);
boolean valid = false; boolean valid = false;
@@ -147,8 +151,10 @@ public class WaveSpawner{
} }
} }
private void eachFlyerSpawn(Floatc2 cons){ private void eachFlyerSpawn(int filterPos, Floatc2 cons){
for(Tile tile : spawns){ for(Tile tile : spawns){
if(filterPos != -1 && filterPos != tile.pos()) continue;
float angle = Angles.angle(world.width() / 2f, world.height() / 2f, tile.x, tile.y); float angle = Angles.angle(world.width() / 2f, world.height() / 2f, tile.x, tile.y);
float trns = Math.max(world.width(), world.height()) * Mathf.sqrt2 * tilesize; float trns = Math.max(world.width(), world.height()) * Mathf.sqrt2 * tilesize;
float spawnX = Mathf.clamp(world.width() * tilesize / 2f + Angles.trnsx(angle, trns), -margin, world.width() * tilesize + margin); float spawnX = Mathf.clamp(world.width() * tilesize / 2f + Angles.trnsx(angle, trns), -margin, world.width() * tilesize + margin);
@@ -158,6 +164,8 @@ public class WaveSpawner{
if(state.rules.attackMode && state.teams.isActive(state.rules.waveTeam)){ if(state.rules.attackMode && state.teams.isActive(state.rules.waveTeam)){
for(Building core : state.rules.waveTeam.data().cores){ for(Building core : state.rules.waveTeam.data().cores){
if(filterPos != -1 && filterPos != core.pos()) continue;
cons.get(core.x, core.y); cons.get(core.x, core.y);
} }
} }
@@ -171,7 +179,7 @@ public class WaveSpawner{
public int countFlyerSpawns(){ public int countFlyerSpawns(){
tmpCount = 0; tmpCount = 0;
eachFlyerSpawn((x, y) -> tmpCount ++); eachFlyerSpawn(-1, (x, y) -> tmpCount ++);
return tmpCount; return tmpCount;
} }

View File

@@ -70,9 +70,8 @@ public class SectorPresets implements ContentList{
difficulty = 5; difficulty = 5;
}}; }};
//TODO
if(false)
navalFortress = new SectorPreset("navalFortress", serpulo, 216){{ navalFortress = new SectorPreset("navalFortress", serpulo, 216){{
//TODO AI or not?
useAI = true; useAI = true;
difficulty = 8; difficulty = 8;
}}; }};

View File

@@ -3,6 +3,7 @@ package mindustry.editor;
import arc.*; import arc.*;
import arc.graphics.*; import arc.graphics.*;
import arc.math.*; import arc.math.*;
import arc.math.geom.*;
import arc.scene.event.*; import arc.scene.event.*;
import arc.scene.style.*; import arc.scene.style.*;
import arc.scene.ui.*; import arc.scene.ui.*;
@@ -316,7 +317,30 @@ public class WaveInfoDialog extends BaseDialog{
t.check("@waves.guardian", b -> { t.check("@waves.guardian", b -> {
group.effect = (b ? StatusEffects.boss : null); group.effect = (b ? StatusEffects.boss : null);
buildGroups(); buildGroups();
}).padTop(4).update(b -> b.setChecked(group.effect == StatusEffects.boss)).padBottom(8f); }).padTop(4).update(b -> b.setChecked(group.effect == StatusEffects.boss)).padBottom(8f).row();
//spawn positions are clunky and thus experimental for now
if(experimental){
t.table(a -> {
a.add("spawn at ");
a.field(group.spawn == -1 ? "" : Point2.x(group.spawn) + "", TextFieldFilter.digitsOnly, text -> {
if(Strings.canParsePositiveInt(text)){
group.spawn = Point2.pack(Strings.parseInt(text), Point2.y(group.spawn));
Log.info(group.spawn);
}
}).width(70f);
a.add(",");
a.field(group.spawn == -1 ? "" : Point2.y(group.spawn) + "", TextFieldFilter.digitsOnly, text -> {
if(Strings.canParsePositiveInt(text)){
group.spawn = Point2.pack(Point2.x(group.spawn), Strings.parseInt(text));
Log.info(group.spawn);
}
}).width(70f);
}).padBottom(8f).padTop(-8f).row();
}
} }
}).width(340f).pad(8); }).width(340f).pad(8);

View File

@@ -36,10 +36,12 @@ public class SpawnGroup implements JsonSerializable, Cloneable{
public float unitScaling = never; public float unitScaling = never;
/** Shield points that this unit has. */ /** Shield points that this unit has. */
public float shields = 0f; public float shields = 0f;
/** How much shields get increased per wave. */ /** How much shields get increased by per wave. */
public float shieldScaling = 0f; public float shieldScaling = 0f;
/** Amount of enemies spawned initially, with no scaling */ /** Amount of enemies spawned initially, with no scaling */
public int unitAmount = 1; public int unitAmount = 1;
/** If not -1, the unit will only spawn in spawnpoints with these packed coordinates. */
public int spawn = -1;
/** Seq of payloads that this unit will spawn with. */ /** Seq of payloads that this unit will spawn with. */
public @Nullable Seq<UnitType> payloads; public @Nullable Seq<UnitType> payloads;
/** Status effect applied to the spawned unit. Null to disable. */ /** Status effect applied to the spawned unit. Null to disable. */
@@ -55,6 +57,10 @@ public class SpawnGroup implements JsonSerializable, Cloneable{
//serialization use only //serialization use only
} }
public boolean canSpawn(int position){
return spawn == -1 || spawn == position;
}
/** @return amount of units spawned on a specific wave. */ /** @return amount of units spawned on a specific wave. */
public int getSpawned(int wave){ public int getSpawned(int wave){
if(spacing == 0) spacing = 1; if(spacing == 0) spacing = 1;
@@ -111,6 +117,7 @@ public class SpawnGroup implements JsonSerializable, Cloneable{
if(shieldScaling != 0) json.writeValue("shieldScaling", shieldScaling); if(shieldScaling != 0) json.writeValue("shieldScaling", shieldScaling);
if(unitAmount != 1) json.writeValue("amount", unitAmount); if(unitAmount != 1) json.writeValue("amount", unitAmount);
if(effect != null) json.writeValue("effect", effect.name); if(effect != null) json.writeValue("effect", effect.name);
if(spawn != -1) json.writeValue("spawn", spawn);
if(payloads != null && payloads.size > 0){ if(payloads != null && payloads.size > 0){
json.writeValue("payloads", payloads.map(u -> u.name).toArray(String.class)); json.writeValue("payloads", payloads.map(u -> u.name).toArray(String.class));
} }
@@ -130,6 +137,7 @@ public class SpawnGroup implements JsonSerializable, Cloneable{
shields = data.getFloat("shields", 0); shields = data.getFloat("shields", 0);
shieldScaling = data.getFloat("shieldScaling", 0); shieldScaling = data.getFloat("shieldScaling", 0);
unitAmount = data.getInt("amount", 1); unitAmount = data.getInt("amount", 1);
spawn = data.getInt("spawn", -1);
if(data.has("payloads")){ if(data.has("payloads")){
payloads = Seq.with(json.readValue(String[].class, data.get("payloads"))).map(s -> content.getByName(ContentType.unit, s)); payloads = Seq.with(json.readValue(String[].class, data.get("payloads"))).map(s -> content.getByName(ContentType.unit, s));
} }

View File

@@ -321,14 +321,20 @@ public class SectorDamage{
var reg = new LinearRegression(); var reg = new LinearRegression();
SpawnGroup bossGroup = null; SpawnGroup bossGroup = null;
Seq<Vec2> waveDps = new Seq<>(), waveHealth = new Seq<>(); Seq<Vec2> waveDps = new Seq<>(), waveHealth = new Seq<>();
int groundSpawns = Math.max(spawner.countFlyerSpawns(), 1), airSpawns = Math.max(spawner.countGroundSpawns(), 1);
for(int wave = state.wave; wave < state.wave + 10; wave ++){ for(int wave = state.wave; wave < state.wave + 10; wave ++){
float sumWaveDps = 0f, sumWaveHealth = 0f; float sumWaveDps = 0f, sumWaveHealth = 0f;
for(SpawnGroup group : state.rules.spawns){ for(SpawnGroup group : state.rules.spawns){
//calculate the amount of spawn points used
//if there's a spawn position override, there is only one potential place they spawn
//assume that all overridden positions are valid, should always be true in properly designed campaign maps
int spawnCount = group.spawn != -1 ? 1 : group.type.flying ? airSpawns : groundSpawns;
float healthMult = 1f + Mathf.clamp(group.type.armor / 20f); float healthMult = 1f + Mathf.clamp(group.type.armor / 20f);
StatusEffect effect = (group.effect == null ? StatusEffects.none : group.effect); StatusEffect effect = (group.effect == null ? StatusEffects.none : group.effect);
int spawned = group.getSpawned(wave); int spawned = group.getSpawned(wave) * spawnCount;
//save the boss group //save the boss group
if(group.effect == StatusEffects.boss){ if(group.effect == StatusEffects.boss){
bossGroup = group; bossGroup = group;

View File

@@ -8,7 +8,7 @@ import static mindustry.maps.filters.FilterOption.*;
public class BlendFilter extends GenerateFilter{ public class BlendFilter extends GenerateFilter{
float radius = 2f; float radius = 2f;
Block block = Blocks.stone, floor = Blocks.ice, ignore = Blocks.air; Block block = Blocks.sand, floor = Blocks.sandWater, ignore = Blocks.air;
@Override @Override
public FilterOption[] options(){ public FilterOption[] options(){
@@ -16,7 +16,7 @@ public class BlendFilter extends GenerateFilter{
new SliderOption("radius", () -> radius, f -> radius = f, 1f, 10f), new SliderOption("radius", () -> radius, f -> radius = f, 1f, 10f),
new BlockOption("block", () -> block, b -> block = b, anyOptional), new BlockOption("block", () -> block, b -> block = b, anyOptional),
new BlockOption("floor", () -> floor, b -> floor = b, anyOptional), new BlockOption("floor", () -> floor, b -> floor = b, anyOptional),
new BlockOption("ignore", () -> ignore, b -> ignore = b, floorsOptional) new BlockOption("ignore", () -> ignore, b -> ignore = b, anyOptional)
}; };
} }