Files
Mindustry/core/src/mindustry/ui/dialogs/CustomRulesDialog.java
2021-06-02 11:08:08 -04:00

370 lines
15 KiB
Java

package mindustry.ui.dialogs;
import arc.*;
import arc.func.*;
import arc.graphics.*;
import arc.math.*;
import arc.scene.style.*;
import arc.scene.ui.*;
import arc.scene.ui.ImageButton.*;
import arc.scene.ui.layout.*;
import arc.struct.*;
import arc.util.*;
import mindustry.content.*;
import mindustry.ctype.*;
import mindustry.game.*;
import mindustry.gen.*;
import mindustry.graphics.*;
import mindustry.type.*;
import mindustry.type.Weather.*;
import mindustry.ui.*;
import mindustry.world.*;
import static arc.util.Time.*;
import static mindustry.Vars.*;
public class CustomRulesDialog extends BaseDialog{
Rules rules;
private Table main;
private Prov<Rules> resetter;
private LoadoutDialog loadoutDialog;
private BaseDialog banDialog;
public CustomRulesDialog(){
super("@mode.custom");
loadoutDialog = new LoadoutDialog();
banDialog = new BaseDialog("@bannedblocks");
banDialog.addCloseButton();
banDialog.shown(this::rebuildBanned);
banDialog.buttons.button("@addall", Icon.add, () -> {
rules.bannedBlocks.addAll(content.blocks().select(Block::canBeBuilt));
rebuildBanned();
}).size(180, 64f);
banDialog.buttons.button("@clear", Icon.trash, () -> {
rules.bannedBlocks.clear();
rebuildBanned();
}).size(180, 64f);
setFillParent(true);
shown(this::setup);
addCloseButton();
}
private void rebuildBanned(){
float previousScroll = banDialog.cont.getChildren().isEmpty() ? 0f : ((ScrollPane)banDialog.cont.getChildren().first()).getScrollY();
banDialog.cont.clear();
banDialog.cont.pane(t -> {
t.margin(10f);
if(rules.bannedBlocks.isEmpty()){
t.add("@empty");
}
Seq<Block> array = Seq.with(rules.bannedBlocks);
array.sort();
int cols = mobile && Core.graphics.isPortrait() ? 1 : mobile ? 2 : 3;
int i = 0;
for(Block block : array){
t.table(Tex.underline, b -> {
b.left().margin(4f);
b.image(block.uiIcon).size(iconMed).padRight(3);
b.add(block.localizedName).color(Color.lightGray).padLeft(3).growX().left().wrap();
b.button(Icon.cancel, Styles.clearPartiali, () -> {
rules.bannedBlocks.remove(block);
rebuildBanned();
}).size(70f).pad(-4f).padLeft(0f);
}).size(300f, 70f).padRight(5);
if(++i % cols == 0){
t.row();
}
}
}).get().setScrollYForce(previousScroll);
banDialog.cont.row();
banDialog.cont.button("@add", Icon.add, () -> {
BaseDialog dialog = new BaseDialog("@add");
dialog.cont.pane(t -> {
t.left().margin(14f);
int[] i = {0};
content.blocks().each(b -> !rules.bannedBlocks.contains(b) && b.canBeBuilt(), b -> {
int cols = mobile && Core.graphics.isPortrait() ? 4 : 12;
t.button(new TextureRegionDrawable(b.uiIcon), Styles.cleari, iconMed, () -> {
rules.bannedBlocks.add(b);
rebuildBanned();
dialog.hide();
}).size(60f);
if(++i[0] % cols == 0){
t.row();
}
});
});
dialog.addCloseButton();
dialog.show();
}).size(300f, 64f);
}
public void show(Rules rules, Prov<Rules> resetter){
this.rules = rules;
this.resetter = resetter;
show();
}
void setup(){
cont.clear();
cont.pane(m -> main = m).get().setScrollingDisabled(true, false);
main.margin(10f);
main.button("@settings.reset", () -> {
rules = resetter.get();
setup();
requestKeyboard();
requestScroll();
}).size(300f, 50f);
main.left().defaults().fillX().left().pad(5);
main.row();
title("@rules.title.waves");
check("@rules.waves", b -> rules.waves = b, () -> rules.waves);
check("@rules.wavetimer", b -> rules.waveTimer = b, () -> rules.waveTimer);
check("@rules.waitForWaveToEnd", b -> rules.waitEnemies = b, () -> rules.waitEnemies);
number("@rules.wavespacing", false, f -> rules.waveSpacing = f * 60f, () -> rules.waveSpacing / 60f, () -> true, 1, Float.MAX_VALUE);
number("@rules.dropzoneradius", false, f -> rules.dropZoneRadius = f * tilesize, () -> rules.dropZoneRadius / tilesize, () -> true);
title("@rules.title.resourcesbuilding");
check("@rules.infiniteresources", b -> rules.infiniteResources = b, () -> rules.infiniteResources);
check("@rules.reactorexplosions", b -> rules.reactorExplosions = b, () -> rules.reactorExplosions);
check("@rules.schematic", b -> rules.schematicsAllowed = b, () -> rules.schematicsAllowed);
check("@rules.coreincinerates", b -> rules.coreIncinerates = b, () -> rules.coreIncinerates);
number("@rules.buildcostmultiplier", false, f -> rules.buildCostMultiplier = f, () -> rules.buildCostMultiplier, () -> !rules.infiniteResources);
number("@rules.buildspeedmultiplier", f -> rules.buildSpeedMultiplier = f, () -> rules.buildSpeedMultiplier, 0.001f, 50f);
number("@rules.deconstructrefundmultiplier", false, f -> rules.deconstructRefundMultiplier = f, () -> rules.deconstructRefundMultiplier, () -> !rules.infiniteResources);
number("@rules.blockhealthmultiplier", f -> rules.blockHealthMultiplier = f, () -> rules.blockHealthMultiplier);
number("@rules.blockdamagemultiplier", f -> rules.blockDamageMultiplier = f, () -> rules.blockDamageMultiplier);
main.button("@configure",
() -> loadoutDialog.show(999999, rules.loadout,
i -> true,
() -> rules.loadout.clear().add(new ItemStack(Items.copper, 100)),
() -> {}, () -> {}
)).left().width(300f);
main.row();
main.button("@bannedblocks", banDialog::show).left().width(300f);
main.row();
title("@rules.title.unit");
check("@rules.unitammo", b -> rules.unitAmmo = b, () -> rules.unitAmmo);
check("@rules.unitcapvariable", b -> rules.unitCapVariable = b, () -> rules.unitCapVariable);
number("@rules.unitcap", true, f -> rules.unitCap = f, () -> rules.unitCap, -999, 999);
number("@rules.unitdamagemultiplier", f -> rules.unitDamageMultiplier = f, () -> rules.unitDamageMultiplier);
number("@rules.unitbuildspeedmultiplier", f -> rules.unitBuildSpeedMultiplier = f, () -> rules.unitBuildSpeedMultiplier, 0.001f, 50f);
title("@rules.title.enemy");
check("@rules.attack", b -> rules.attackMode = b, () -> rules.attackMode);
check("@rules.buildai", b -> rules.teams.get(rules.waveTeam).ai = rules.teams.get(rules.waveTeam).infiniteResources = b, () -> rules.teams.get(rules.waveTeam).ai);
check("@rules.corecapture", b -> rules.coreCapture = b, () -> rules.coreCapture);
number("@rules.enemycorebuildradius", f -> rules.enemyCoreBuildRadius = f * tilesize, () -> Math.min(rules.enemyCoreBuildRadius / tilesize, 200));
title("@rules.title.environment");
check("@rules.explosions", b -> rules.damageExplosions = b, () -> rules.damageExplosions);
check("@rules.fire", b -> rules.fire = b, () -> rules.fire);
check("@rules.lighting", b -> rules.lighting = b, () -> rules.lighting);
check("@rules.enemyLights", b -> rules.enemyLights = b, () -> rules.enemyLights);
main.button(b -> {
b.left();
b.table(Tex.pane, in -> {
in.stack(new Image(Tex.alphaBg), new Image(Tex.whiteui){{
update(() -> setColor(rules.ambientLight));
}}).grow();
}).margin(4).size(50f).padRight(10);
b.add("@rules.ambientlight");
}, () -> ui.picker.show(rules.ambientLight, rules.ambientLight::set)).left().width(250f).row();
main.button("@rules.weather", this::weatherDialog).width(250f).left().row();
}
void number(String text, Floatc cons, Floatp prov){
number(text, false, cons, prov, () -> true, 0, Float.MAX_VALUE);
}
void number(String text, Floatc cons, Floatp prov, float min, float max){
number(text, false, cons, prov, () -> true, min, max);
}
void number(String text, boolean integer, Floatc cons, Floatp prov, Boolp condition){
number(text, integer, cons, prov, condition, 0, Float.MAX_VALUE);
}
void number(String text, Floatc cons, Floatp prov, Boolp condition){
number(text, false, cons, prov, condition, 0, Float.MAX_VALUE);
}
void number(String text, boolean integer, Intc cons, Intp prov, int min, int max){
main.table(t -> {
t.left();
t.add(text).left().padRight(5);
t.field((integer ? (int)prov.get() : prov.get()) + "", s -> cons.get(Strings.parseInt(s)))
.padRight(100f)
.valid(f -> Strings.parseInt(f) >= min && Strings.parseInt(f) <= max).width(120f).left().addInputDialog();
}).padTop(0);
main.row();
}
void number(String text, boolean integer, Floatc cons, Floatp prov, Boolp condition, float min, float max){
main.table(t -> {
t.left();
t.add(text).left().padRight(5)
.update(a -> a.setColor(condition.get() ? Color.white : Color.gray));
t.field((integer ? (int)prov.get() : prov.get()) + "", s -> cons.get(Strings.parseFloat(s)))
.padRight(100f)
.update(a -> a.setDisabled(!condition.get()))
.valid(f -> Strings.canParsePositiveFloat(f) && Strings.parseFloat(f) >= min && Strings.parseFloat(f) <= max).width(120f).left().addInputDialog();
}).padTop(0);
main.row();
}
void check(String text, Boolc cons, Boolp prov){
check(text, cons, prov, () -> true);
}
void check(String text, Boolc cons, Boolp prov, Boolp condition){
main.check(text, cons).checked(prov.get()).update(a -> a.setDisabled(!condition.get())).padRight(100f).get().left();
main.row();
}
void title(String text){
main.add(text).color(Pal.accent).padTop(20).padRight(100f).padBottom(-3);
main.row();
main.image().color(Pal.accent).height(3f).padRight(100f).padBottom(20);
main.row();
}
Cell<TextField> field(Table table, float value, Floatc setter){
return table.field(Strings.autoFixed(value, 2), v -> setter.get(Strings.parseFloat(v)))
.valid(Strings::canParsePositiveFloat)
.size(90f, 40f).pad(2f).addInputDialog();
}
void weatherDialog(){
BaseDialog dialog = new BaseDialog("@rules.weather");
Runnable[] rebuild = {null};
dialog.cont.pane(base -> {
rebuild[0] = () -> {
base.clearChildren();
int cols = Math.max(1, Core.graphics.getWidth() / 460);
int idx = 0;
for(WeatherEntry entry : rules.weather){
base.top();
//main container
base.table(Tex.pane, c -> {
c.margin(0);
//icons to perform actions
c.table(Tex.whiteui, t -> {
t.setColor(Pal.gray);
t.top().left();
t.add(entry.weather.localizedName).left().padLeft(6);
t.add().growX();
ImageButtonStyle style = Styles.geni;
t.defaults().size(42f);
t.button(Icon.cancel, style, () -> {
rules.weather.remove(entry);
rebuild[0].run();
});
}).growX();
c.row();
//all the options
c.table(f -> {
f.marginLeft(4);
f.left().top();
f.defaults().padRight(4).left();
f.add("@rules.weather.duration");
field(f, entry.minDuration / toMinutes, v -> entry.minDuration = v * toMinutes).disabled(v -> entry.always);
f.add("@waves.to");
field(f, entry.maxDuration / toMinutes, v -> entry.maxDuration = v * toMinutes).disabled(v -> entry.always);
f.add("@unit.minutes");
f.row();
f.add("@rules.weather.frequency");
field(f, entry.minFrequency / toMinutes, v -> entry.minFrequency = v * toMinutes).disabled(v -> entry.always);
f.add("@waves.to");
field(f, entry.maxFrequency / toMinutes, v -> entry.maxFrequency = v * toMinutes).disabled(v -> entry.always);
f.add("@unit.minutes");
f.row();
f.check("@rules.weather.always", val -> entry.always = val).checked(cc -> entry.always).padBottom(4);
//intensity can't currently be customized
}).grow().left().pad(6).top();
}).width(410f).pad(3).top().left().fillY();
if(++idx % cols == 0){
base.row();
}
}
};
rebuild[0].run();
}).grow();
dialog.addCloseButton();
dialog.buttons.button("@add", Icon.add, () -> {
BaseDialog add = new BaseDialog("@add");
add.cont.pane(t -> {
t.background(Tex.button);
int i = 0;
for(Weather weather : content.<Weather>getBy(ContentType.weather)){
t.button(weather.localizedName, Styles.cleart, () -> {
rules.weather.add(new WeatherEntry(weather));
rebuild[0].run();
add.hide();
}).size(140f, 50f);
if(++i % 2 == 0) t.row();
}
});
add.addCloseButton();
add.show();
}).width(170f);
//reset cooldown to random number
dialog.hidden(() -> {
float sum = 0;
Seq<WeatherEntry> sh = rules.weather.copy();
sh.shuffle();
for(WeatherEntry w : sh){
//add the previous cooldowns to the sum so weather events are staggered and don't happen all at once.
w.cooldown = sum + Mathf.random(w.minFrequency, w.maxFrequency);
sum += w.cooldown;
}
});
dialog.show();
}
}