Experimental positional spawn support / WIP map
This commit is contained in:
Binary file not shown.
BIN
core/assets/maps/navalFortress.msav
Normal file
BIN
core/assets/maps/navalFortress.msav
Normal file
Binary file not shown.
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
}};
|
}};
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
|
|||||||
@@ -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));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user