Allow setting attack mode wave unit spawn position
This commit is contained in:
@@ -1419,6 +1419,8 @@ rules.alloweditworldprocessors = Allow Editing World Processors
|
|||||||
rules.alloweditworldprocessors.info = When enabled, world logic blocks can be placed and edited even outside the editor.
|
rules.alloweditworldprocessors.info = When enabled, world logic blocks can be placed and edited even outside the editor.
|
||||||
rules.waves = Waves
|
rules.waves = Waves
|
||||||
rules.airUseSpawns = Air Units Use Spawn Points
|
rules.airUseSpawns = Air Units Use Spawn Points
|
||||||
|
rules.wavespawnatcores = Waves Spawn At Cores
|
||||||
|
rules.wavespawnatcores.info = When enabled in attack mode, waves spawn near all enemy cores.
|
||||||
rules.attack = Attack Mode
|
rules.attack = Attack Mode
|
||||||
rules.buildai = Base Builder AI
|
rules.buildai = Base Builder AI
|
||||||
rules.buildaitier = Builder AI Tier
|
rules.buildaitier = Builder AI Tier
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import mindustry.game.*;
|
|||||||
import mindustry.gen.*;
|
import mindustry.gen.*;
|
||||||
import mindustry.type.*;
|
import mindustry.type.*;
|
||||||
import mindustry.world.*;
|
import mindustry.world.*;
|
||||||
|
import mindustry.world.blocks.storage.CoreBlock.*;
|
||||||
|
|
||||||
import static mindustry.Vars.*;
|
import static mindustry.Vars.*;
|
||||||
|
|
||||||
@@ -125,39 +126,46 @@ public class WaveSpawner{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(state.rules.attackMode && state.teams.isActive(state.rules.waveTeam) && !state.teams.playerCores().isEmpty()){
|
if(state.rules.wavesSpawnAtCores && 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(CoreBuild core : state.rules.waveTeam.cores()){
|
||||||
if(filterPos != -1 && filterPos != core.pos()) continue;
|
if(filterPos != -1 && filterPos != core.pos()) continue;
|
||||||
|
|
||||||
Tmp.v1.set(firstCore).sub(core).limit(coreMargin + core.block.size * tilesize /2f * Mathf.sqrt2);
|
if(core.commandPos != null){
|
||||||
|
cons.accept(core.commandPos.x, core.commandPos.y, false);
|
||||||
|
}else{
|
||||||
|
boolean valid = false;
|
||||||
|
|
||||||
boolean valid = false;
|
Tmp.v1.set(firstCore).sub(core).limit(coreMargin + core.block.size * tilesize /2f * Mathf.sqrt2);
|
||||||
int steps = 0;
|
|
||||||
|
|
||||||
//keep moving forward until the max step amount is reached
|
int steps = 0;
|
||||||
while(steps++ < maxSteps){
|
|
||||||
int tx = World.toTile(core.x + Tmp.v1.x), ty = World.toTile(core.y + Tmp.v1.y);
|
//keep moving forward until the max step amount is reached
|
||||||
any = false;
|
while(steps++ < maxSteps){
|
||||||
Geometry.circle(tx, ty, world.width(), world.height(), 3, (x, y) -> {
|
int tx = World.toTile(core.x + Tmp.v1.x), ty = World.toTile(core.y + Tmp.v1.y);
|
||||||
if(world.solid(x, y)){
|
any = false;
|
||||||
any = true;
|
Geometry.circle(tx, ty, world.width(), world.height(), 3, (x, y) -> {
|
||||||
|
if(world.solid(x, y)){
|
||||||
|
any = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
//nothing is in the way, spawn it
|
||||||
|
if(!any){
|
||||||
|
valid = true;
|
||||||
|
break;
|
||||||
|
}else{
|
||||||
|
//make the vector longer
|
||||||
|
Tmp.v1.setLength(Tmp.v1.len() + tilesize*1.1f);
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
//nothing is in the way, spawn it
|
if(valid){
|
||||||
if(!any){
|
cons.accept(core.x + Tmp.v1.x, core.y + Tmp.v1.y, false);
|
||||||
valid = true;
|
|
||||||
break;
|
|
||||||
}else{
|
|
||||||
//make the vector longer
|
|
||||||
Tmp.v1.setLength(Tmp.v1.len() + tilesize*1.1f);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(valid){
|
|
||||||
cons.accept(core.x + Tmp.v1.x, core.y + Tmp.v1.y, false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -176,10 +176,67 @@ public class MapEditorDialog extends Dialog implements Disposable{
|
|||||||
menu.hide();
|
menu.hide();
|
||||||
sectorGenDialog.show();
|
sectorGenDialog.show();
|
||||||
}).padTop(!steam ? -3 : 1).size(swidth * 2f + 10, 60f);
|
}).padTop(!steam ? -3 : 1).size(swidth * 2f + 10, 60f);
|
||||||
menu.cont.row();
|
|
||||||
|
|
||||||
menu.cont.row();
|
menu.cont.row();
|
||||||
|
|
||||||
|
//this is gated behind a property, because it's (1) not useful to most people, (2) confusing and (3) may crash or otherwise bug out
|
||||||
|
if(OS.hasProp("mindustry.editor.simulate.button")){
|
||||||
|
|
||||||
|
menu.cont.button("Simulate", Icon.logic, () -> {
|
||||||
|
menu.hide();
|
||||||
|
|
||||||
|
BaseDialog dialog = new BaseDialog("Simulate");
|
||||||
|
|
||||||
|
int[] seconds = {60 * 1};
|
||||||
|
|
||||||
|
dialog.cont.add("Seconds: ");
|
||||||
|
dialog.cont.field(seconds[0] + "", text -> seconds[0] = Strings.parseInt(text, 1)).valid(s -> Strings.parseInt(s, 9999999) < 10f * 60f);
|
||||||
|
|
||||||
|
dialog.addCloseButton();
|
||||||
|
|
||||||
|
dialog.buttons.button("@ok", Icon.ok, () -> {
|
||||||
|
ui.loadAnd(() -> {
|
||||||
|
|
||||||
|
float deltaScl = 2f;
|
||||||
|
int steps = Mathf.ceil(seconds[0] * 60f / deltaScl);
|
||||||
|
float oldDelta = Time.delta;
|
||||||
|
Time.delta = deltaScl;
|
||||||
|
|
||||||
|
Seq<Building> builds = new Seq<>();
|
||||||
|
Time.clear();
|
||||||
|
|
||||||
|
world.tiles.eachTile(t -> {
|
||||||
|
if(t.build != null && t.isCenter() && t.block().update && t.build.allowUpdate()){
|
||||||
|
builds.add(t.build);
|
||||||
|
t.build.updateProximity();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
for(int i = 0; i < steps; i++){
|
||||||
|
Time.update();
|
||||||
|
for(var build : builds){
|
||||||
|
build.update();
|
||||||
|
}
|
||||||
|
Groups.powerGraph.update();
|
||||||
|
}
|
||||||
|
|
||||||
|
//spawned units will cause havoc, so clear them
|
||||||
|
Groups.unit.clear();
|
||||||
|
|
||||||
|
Time.clear();
|
||||||
|
Time.delta = oldDelta;
|
||||||
|
});
|
||||||
|
|
||||||
|
dialog.hide();
|
||||||
|
}).size(210f, 64f);
|
||||||
|
|
||||||
|
dialog.show();
|
||||||
|
|
||||||
|
}).size(swidth * 2f + 10, 60f);
|
||||||
|
|
||||||
|
menu.cont.row();
|
||||||
|
}
|
||||||
|
|
||||||
menu.cont.button("@quit", Icon.exit, () -> {
|
menu.cont.button("@quit", Icon.exit, () -> {
|
||||||
tryExit();
|
tryExit();
|
||||||
menu.hide();
|
menu.hide();
|
||||||
|
|||||||
@@ -1315,6 +1315,10 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isCommandable(){
|
||||||
|
return block.commandable;
|
||||||
|
}
|
||||||
|
|
||||||
/** @return whether this building is in a payload */
|
/** @return whether this building is in a payload */
|
||||||
public boolean isPayload(){
|
public boolean isPayload(){
|
||||||
return tile == emptyTile;
|
return tile == emptyTile;
|
||||||
|
|||||||
@@ -34,6 +34,8 @@ public class Rules{
|
|||||||
public boolean waves;
|
public boolean waves;
|
||||||
/** Whether air units spawn at spawns instead of the edge of the map */
|
/** Whether air units spawn at spawns instead of the edge of the map */
|
||||||
public boolean airUseSpawns = false;
|
public boolean airUseSpawns = false;
|
||||||
|
/** If true, units spawn at enemy cores in attack maps with waves enabled. */
|
||||||
|
public boolean wavesSpawnAtCores = true;
|
||||||
/** Whether the game objective is PvP. Note that this enables automatic hosting. */
|
/** Whether the game objective is PvP. Note that this enables automatic hosting. */
|
||||||
public boolean pvp;
|
public boolean pvp;
|
||||||
/** Whether is waiting for players enabled in PvP. */
|
/** Whether is waiting for players enabled in PvP. */
|
||||||
|
|||||||
@@ -326,7 +326,7 @@ public class DesktopInput extends InputHandler{
|
|||||||
selectedUnits.clear();
|
selectedUnits.clear();
|
||||||
commandBuildings.clear();
|
commandBuildings.clear();
|
||||||
for(var build : player.team().data().buildings){
|
for(var build : player.team().data().buildings){
|
||||||
if(build.block.commandable){
|
if(build.isCommandable()){
|
||||||
commandBuildings.add(build);
|
commandBuildings.add(build);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -155,6 +155,8 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
|
|||||||
|
|
||||||
Events.on(ResetEvent.class, e -> {
|
Events.on(ResetEvent.class, e -> {
|
||||||
logicCutscene = false;
|
logicCutscene = false;
|
||||||
|
commandBuildings.clear();
|
||||||
|
selectedUnits.clear();
|
||||||
itemDepositCooldown = 0f;
|
itemDepositCooldown = 0f;
|
||||||
Arrays.fill(controlGroups, null);
|
Arrays.fill(controlGroups, null);
|
||||||
lastUnit = null;
|
lastUnit = null;
|
||||||
@@ -413,7 +415,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
|
|||||||
for(int pos : buildings){
|
for(int pos : buildings){
|
||||||
var build = world.build(pos);
|
var build = world.build(pos);
|
||||||
|
|
||||||
if(build == null || build.team() != player.team() || !build.block.commandable) continue;
|
if(build == null || build.team() != player.team() || !build.isCommandable()) continue;
|
||||||
|
|
||||||
build.onCommand(target);
|
build.onCommand(target);
|
||||||
build.updateLastAccess(player);
|
build.updateLastAccess(player);
|
||||||
@@ -839,7 +841,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
|
|||||||
|
|
||||||
itemDepositCooldown -= Time.delta / 60f;
|
itemDepositCooldown -= Time.delta / 60f;
|
||||||
|
|
||||||
commandBuildings.removeAll(b -> !b.isValid());
|
commandBuildings.removeAll(b -> !b.isValid() || !b.isCommandable() || b.team != player.team());
|
||||||
|
|
||||||
if(!commandMode){
|
if(!commandMode){
|
||||||
commandRect = false;
|
commandRect = false;
|
||||||
@@ -1025,7 +1027,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
|
|||||||
//deselect
|
//deselect
|
||||||
selectedUnits.clear();
|
selectedUnits.clear();
|
||||||
|
|
||||||
if(build != null && build.team == player.team() && build.block.commandable){
|
if(build != null && build.team == player.team() && build.isCommandable()){
|
||||||
if(commandBuildings.contains(build)){
|
if(commandBuildings.contains(build)){
|
||||||
commandBuildings.remove(build);
|
commandBuildings.remove(build);
|
||||||
}else{
|
}else{
|
||||||
@@ -1742,7 +1744,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
|
|||||||
boolean consumed = false, showedInventory = false;
|
boolean consumed = false, showedInventory = false;
|
||||||
|
|
||||||
//select building for commanding
|
//select building for commanding
|
||||||
if(build.block.commandable && commandMode){
|
if(build.isCommandable() && commandMode){
|
||||||
//TODO handled in tap.
|
//TODO handled in tap.
|
||||||
consumed = true;
|
consumed = true;
|
||||||
}else if(build.block.configurable && build.interactable(player.team())){ //check if tapped block is configurable
|
}else if(build.block.configurable && build.interactable(player.team())){ //check if tapped block is configurable
|
||||||
|
|||||||
@@ -151,6 +151,7 @@ public class CustomRulesDialog extends BaseDialog{
|
|||||||
check("@rules.wavetimer", b -> rules.waveTimer = b, () -> rules.waveTimer, () -> rules.waves);
|
check("@rules.wavetimer", b -> rules.waveTimer = b, () -> rules.waveTimer, () -> rules.waves);
|
||||||
check("@rules.waitForWaveToEnd", b -> rules.waitEnemies = b, () -> rules.waitEnemies, () -> rules.waves && rules.waveTimer);
|
check("@rules.waitForWaveToEnd", b -> rules.waitEnemies = b, () -> rules.waitEnemies, () -> rules.waves && rules.waveTimer);
|
||||||
check("@rules.randomwaveai", b -> rules.randomWaveAI = b, () -> rules.randomWaveAI, () -> rules.waves);
|
check("@rules.randomwaveai", b -> rules.randomWaveAI = b, () -> rules.randomWaveAI, () -> rules.waves);
|
||||||
|
check("@rules.wavespawnatcores", b -> rules.wavesSpawnAtCores = b, () -> rules.wavesSpawnAtCores, () -> rules.waves);
|
||||||
check("@rules.airUseSpawns", b -> rules.airUseSpawns = b, () -> rules.airUseSpawns, () -> rules.waves);
|
check("@rules.airUseSpawns", b -> rules.airUseSpawns = b, () -> rules.airUseSpawns, () -> rules.waves);
|
||||||
numberi("@rules.wavelimit", f -> rules.winWave = f, () -> rules.winWave, () -> rules.waves, 0, Integer.MAX_VALUE);
|
numberi("@rules.wavelimit", f -> rules.winWave = f, () -> rules.winWave, () -> rules.waves, 0, Integer.MAX_VALUE);
|
||||||
number("@rules.wavespacing", false, f -> rules.waveSpacing = f * 60f, () -> rules.waveSpacing / 60f, () -> rules.waves && rules.waveTimer, 1, Float.MAX_VALUE);
|
number("@rules.wavespacing", false, f -> rules.waveSpacing = f * 60f, () -> rules.waveSpacing / 60f, () -> rules.waves && rules.waveTimer, 1, Float.MAX_VALUE);
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import arc.scene.ui.*;
|
|||||||
import arc.scene.ui.layout.*;
|
import arc.scene.ui.layout.*;
|
||||||
import arc.struct.*;
|
import arc.struct.*;
|
||||||
import arc.util.*;
|
import arc.util.*;
|
||||||
|
import arc.util.io.*;
|
||||||
import mindustry.*;
|
import mindustry.*;
|
||||||
import mindustry.annotations.Annotations.*;
|
import mindustry.annotations.Annotations.*;
|
||||||
import mindustry.content.*;
|
import mindustry.content.*;
|
||||||
@@ -23,6 +24,7 @@ import mindustry.game.EventType.*;
|
|||||||
import mindustry.game.*;
|
import mindustry.game.*;
|
||||||
import mindustry.gen.*;
|
import mindustry.gen.*;
|
||||||
import mindustry.graphics.*;
|
import mindustry.graphics.*;
|
||||||
|
import mindustry.io.*;
|
||||||
import mindustry.logic.*;
|
import mindustry.logic.*;
|
||||||
import mindustry.type.*;
|
import mindustry.type.*;
|
||||||
import mindustry.ui.*;
|
import mindustry.ui.*;
|
||||||
@@ -71,6 +73,7 @@ public class CoreBlock extends StorageBlock{
|
|||||||
unitCapModifier = 10;
|
unitCapModifier = 10;
|
||||||
drawDisabled = false;
|
drawDisabled = false;
|
||||||
canOverdrive = false;
|
canOverdrive = false;
|
||||||
|
commandable = true;
|
||||||
envEnabled |= Env.space;
|
envEnabled |= Env.space;
|
||||||
|
|
||||||
//support everything
|
//support everything
|
||||||
@@ -247,9 +250,25 @@ public class CoreBlock extends StorageBlock{
|
|||||||
public Team lastDamage = Team.derelict;
|
public Team lastDamage = Team.derelict;
|
||||||
public float iframes = -1f;
|
public float iframes = -1f;
|
||||||
public float thrusterTime = 0f;
|
public float thrusterTime = 0f;
|
||||||
|
public @Nullable Vec2 commandPos;
|
||||||
|
|
||||||
protected float cloudSeed, landParticleTimer;
|
protected float cloudSeed, landParticleTimer;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCommandable(){
|
||||||
|
return team != state.rules.defaultTeam && state.rules.attackMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Vec2 getCommandPosition(){
|
||||||
|
return commandPos;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCommand(Vec2 target){
|
||||||
|
commandPos = target;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void draw(){
|
public void draw(){
|
||||||
//draw thrusters when just landed
|
//draw thrusters when just landed
|
||||||
@@ -806,5 +825,24 @@ public class CoreBlock extends StorageBlock{
|
|||||||
noEffect = false;
|
noEffect = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte version(){
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(Writes write){
|
||||||
|
super.write(write);
|
||||||
|
TypeIO.writeVecNullable(write, commandPos);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void read(Reads read, byte revision){
|
||||||
|
super.read(read, revision);
|
||||||
|
if(revision >= 1){
|
||||||
|
commandPos = TypeIO.readVecNullable(read);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user