Allow setting attack mode wave unit spawn position
This commit is contained in:
@@ -15,6 +15,7 @@ import mindustry.game.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.blocks.storage.CoreBlock.*;
|
||||
|
||||
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();
|
||||
for(Building core : state.rules.waveTeam.cores()){
|
||||
for(CoreBuild 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);
|
||||
if(core.commandPos != null){
|
||||
cons.accept(core.commandPos.x, core.commandPos.y, false);
|
||||
}else{
|
||||
boolean valid = false;
|
||||
|
||||
boolean valid = false;
|
||||
int steps = 0;
|
||||
Tmp.v1.set(firstCore).sub(core).limit(coreMargin + core.block.size * tilesize /2f * Mathf.sqrt2);
|
||||
|
||||
//keep moving forward until the max step amount is reached
|
||||
while(steps++ < maxSteps){
|
||||
int tx = World.toTile(core.x + Tmp.v1.x), ty = World.toTile(core.y + Tmp.v1.y);
|
||||
any = false;
|
||||
Geometry.circle(tx, ty, world.width(), world.height(), 3, (x, y) -> {
|
||||
if(world.solid(x, y)){
|
||||
any = true;
|
||||
int steps = 0;
|
||||
|
||||
//keep moving forward until the max step amount is reached
|
||||
while(steps++ < maxSteps){
|
||||
int tx = World.toTile(core.x + Tmp.v1.x), ty = World.toTile(core.y + Tmp.v1.y);
|
||||
any = false;
|
||||
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(!any){
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
sectorGenDialog.show();
|
||||
}).padTop(!steam ? -3 : 1).size(swidth * 2f + 10, 60f);
|
||||
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, () -> {
|
||||
tryExit();
|
||||
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 */
|
||||
public boolean isPayload(){
|
||||
return tile == emptyTile;
|
||||
|
||||
@@ -34,6 +34,8 @@ public class Rules{
|
||||
public boolean waves;
|
||||
/** Whether air units spawn at spawns instead of the edge of the map */
|
||||
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. */
|
||||
public boolean pvp;
|
||||
/** Whether is waiting for players enabled in PvP. */
|
||||
|
||||
@@ -326,7 +326,7 @@ public class DesktopInput extends InputHandler{
|
||||
selectedUnits.clear();
|
||||
commandBuildings.clear();
|
||||
for(var build : player.team().data().buildings){
|
||||
if(build.block.commandable){
|
||||
if(build.isCommandable()){
|
||||
commandBuildings.add(build);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,6 +155,8 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
|
||||
|
||||
Events.on(ResetEvent.class, e -> {
|
||||
logicCutscene = false;
|
||||
commandBuildings.clear();
|
||||
selectedUnits.clear();
|
||||
itemDepositCooldown = 0f;
|
||||
Arrays.fill(controlGroups, null);
|
||||
lastUnit = null;
|
||||
@@ -413,7 +415,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
|
||||
for(int pos : buildings){
|
||||
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.updateLastAccess(player);
|
||||
@@ -839,7 +841,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
|
||||
|
||||
itemDepositCooldown -= Time.delta / 60f;
|
||||
|
||||
commandBuildings.removeAll(b -> !b.isValid());
|
||||
commandBuildings.removeAll(b -> !b.isValid() || !b.isCommandable() || b.team != player.team());
|
||||
|
||||
if(!commandMode){
|
||||
commandRect = false;
|
||||
@@ -1025,7 +1027,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
|
||||
//deselect
|
||||
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)){
|
||||
commandBuildings.remove(build);
|
||||
}else{
|
||||
@@ -1742,7 +1744,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
|
||||
boolean consumed = false, showedInventory = false;
|
||||
|
||||
//select building for commanding
|
||||
if(build.block.commandable && commandMode){
|
||||
if(build.isCommandable() && commandMode){
|
||||
//TODO handled in tap.
|
||||
consumed = true;
|
||||
}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.waitForWaveToEnd", b -> rules.waitEnemies = b, () -> rules.waitEnemies, () -> rules.waves && rules.waveTimer);
|
||||
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);
|
||||
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);
|
||||
|
||||
@@ -13,6 +13,7 @@ import arc.scene.ui.*;
|
||||
import arc.scene.ui.layout.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import arc.util.io.*;
|
||||
import mindustry.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.content.*;
|
||||
@@ -23,6 +24,7 @@ import mindustry.game.EventType.*;
|
||||
import mindustry.game.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.io.*;
|
||||
import mindustry.logic.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.ui.*;
|
||||
@@ -71,6 +73,7 @@ public class CoreBlock extends StorageBlock{
|
||||
unitCapModifier = 10;
|
||||
drawDisabled = false;
|
||||
canOverdrive = false;
|
||||
commandable = true;
|
||||
envEnabled |= Env.space;
|
||||
|
||||
//support everything
|
||||
@@ -247,9 +250,25 @@ public class CoreBlock extends StorageBlock{
|
||||
public Team lastDamage = Team.derelict;
|
||||
public float iframes = -1f;
|
||||
public float thrusterTime = 0f;
|
||||
public @Nullable Vec2 commandPos;
|
||||
|
||||
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
|
||||
public void draw(){
|
||||
//draw thrusters when just landed
|
||||
@@ -806,5 +825,24 @@ public class CoreBlock extends StorageBlock{
|
||||
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