Merge branch 'master' into mod-dependencies

This commit is contained in:
MEEPofFaith
2025-02-08 20:48:39 -08:00
424 changed files with 17880 additions and 8956 deletions

View File

@@ -41,7 +41,7 @@ public class AboutDialog extends BaseDialog{
ScrollPane pane = new ScrollPane(in);
for(LinkEntry link : Links.getLinks()){
if((ios || OS.isMac || steam) && bannedItems.contains(link.name)){
if((ios || steam) && bannedItems.contains(link.name)){
continue;
}
@@ -50,7 +50,7 @@ public class AboutDialog extends BaseDialog{
table.table(img -> {
img.image().height(h - 5).width(40f).color(link.color);
img.row();
img.image().height(5).width(40f).color(link.color.cpy().mul(0.8f, 0.8f, 0.8f, 1f));
img.image().height(5).width(40f).color(link.color.cpy().mul(0.6f, 0.6f, 0.8f, 1f));
}).expandY();
table.table(i -> {
@@ -64,7 +64,7 @@ public class AboutDialog extends BaseDialog{
inset.labelWrap(link.description).width(w - 100f - h).color(Color.lightGray).growX();
}).padLeft(8);
table.button(Icon.link, Styles.clearNoneTogglei, () -> {
table.button(Icon.link, Styles.clearNonei, () -> {
if(link.name.equals("wiki")) Events.fire(Trigger.openWiki);
if(!Core.app.openURI(link.link)){

View File

@@ -0,0 +1,100 @@
package mindustry.ui.dialogs;
import arc.*;
import arc.func.*;
import arc.scene.ui.*;
import arc.scene.ui.layout.*;
import mindustry.*;
import mindustry.game.*;
import mindustry.gen.*;
import mindustry.type.*;
import mindustry.ui.*;
public class CampaignRulesDialog extends BaseDialog{
Planet planet;
Table current;
public CampaignRulesDialog(){
super("@campaign.difficulty");
addCloseButton();
hidden(() -> {
if(planet != null){
planet.saveRules();
if(Vars.state.isGame() && Vars.state.isCampaign() && Vars.state.getPlanet() == planet){
planet.campaignRules.apply(planet, Vars.state.rules);
Call.setRules(Vars.state.rules);
}
}
});
onResize(() -> {
rebuild();
});
}
void rebuild(){
CampaignRules rules = planet.campaignRules;
cont.clear();
cont.top().pane(inner -> {
inner.top().left().defaults().fillX().left().pad(5);
current = inner;
current.table(Tex.button, t -> {
t.margin(10f);
var group = new ButtonGroup<>();
var style = Styles.flatTogglet;
t.defaults().size(140f, 50f);
for(Difficulty diff : Difficulty.all){
t.button(diff.localized(), style, () -> {
rules.difficulty = diff;
}).group(group).checked(b -> rules.difficulty == diff);
if(Core.graphics.isPortrait() && diff.ordinal() % 2 == 1){
t.row();
}
}
}).left().fill(false).expand(false, false).row();
if(planet.allowSectorInvasion){
check("@rules.invasions", b -> rules.sectorInvasion = b, () -> rules.sectorInvasion);
}
check("@rules.fog", b -> rules.fog = b, () -> rules.fog);
check("@rules.showspawns", b -> rules.showSpawns = b, () -> rules.showSpawns);
check("@rules.randomwaveai", b -> rules.randomWaveAI = b, () -> rules.randomWaveAI);
//TODO: this is intentionally hidden until the new mechanics have been well-tested. I don't want people immediately switching to the old mechanics
if(planet.allowLegacyLaunchPads){
// check("@rules.legacylaunchpads", b -> rules.legacyLaunchPads = b, () -> rules.legacyLaunchPads);
}
}).growY();
}
public void show(Planet planet){
this.planet = planet;
rebuild();
show();
}
void check(String text, Boolc cons, Boolp prov){
check(text, cons, prov, () -> true);
}
void check(String text, Boolc cons, Boolp prov, Boolp condition){
String infoText = text.substring(1) + ".info";
var cell = current.check(text, cons).checked(prov.get()).update(a -> a.setDisabled(!condition.get()));
if(Core.bundle.has(infoText)){
cell.tooltip(text + ".info");
}
cell.get().left();
current.row();
}
}

View File

@@ -132,7 +132,7 @@ public class ColorPicker extends BaseDialog{
hexField = t.field(current.toString(), value -> {
try{
current.set(Color.valueOf(value).a(a));
Color.valueOf(current, value);
current.toHsv(values);
h = values[0];
s = values[1];

View File

@@ -1,6 +1,7 @@
package mindustry.ui.dialogs;
import arc.*;
import arc.scene.actions.*;
import arc.scene.ui.*;
import arc.scene.ui.layout.*;
import arc.struct.*;
@@ -82,7 +83,6 @@ public class ContentInfoDialog extends BaseDialog{
value.display(inset);
inset.add().size(10f);
}
}).fillX().padLeft(10);
table.row();
}
@@ -96,9 +96,16 @@ public class ContentInfoDialog extends BaseDialog{
content.displayExtra(table);
ScrollPane pane = new ScrollPane(table);
table.marginRight(30f);
//TODO: some things (e.g. reconstructor requirements) are too long and screw up the layout
//pane.setScrollingDisabled(true, false);
cont.add(pane);
show();
if(isShown()){
show(scene, Actions.fadeIn(0f));
}else{
show();
}
}
}

View File

@@ -3,7 +3,6 @@ package mindustry.ui.dialogs;
import arc.*;
import arc.func.*;
import arc.graphics.*;
import arc.scene.style.*;
import arc.scene.ui.*;
import arc.scene.ui.ImageButton.*;
import arc.scene.ui.layout.*;
@@ -12,6 +11,7 @@ import arc.util.*;
import mindustry.*;
import mindustry.content.*;
import mindustry.ctype.*;
import mindustry.editor.BannedContentDialog;
import mindustry.game.*;
import mindustry.game.Rules.*;
import mindustry.gen.*;
@@ -30,7 +30,8 @@ public class CustomRulesDialog extends BaseDialog{
private Table main;
private Prov<Rules> resetter;
private LoadoutDialog loadoutDialog;
private BannedContentDialog<Block> bannedBlocks = new BannedContentDialog<>("@bannedblocks", ContentType.block, Block::canBeBuilt);
private BannedContentDialog<UnitType> bannedUnits = new BannedContentDialog<>("@bannedunits", ContentType.unit, u -> !u.isHidden());
public boolean showRuleEditRule;
public Seq<Table> categories;
public Table current;
@@ -109,84 +110,6 @@ public class CustomRulesDialog extends BaseDialog{
requestScroll();
}
private <T extends UnlockableContent> void showBanned(String title, ContentType type, ObjectSet<T> set, Boolf<T> pred){
BaseDialog bd = new BaseDialog(title);
bd.addCloseButton();
Runnable[] rebuild = {null};
rebuild[0] = () -> {
float previousScroll = bd.cont.getChildren().isEmpty() ? 0f : ((ScrollPane)bd.cont.getChildren().first()).getScrollY();
bd.cont.clear();
bd.cont.pane(t -> {
t.margin(10f);
if(set.isEmpty()){
t.add("@empty");
}
Seq<T> array = set.toSeq();
array.sort();
int cols = mobile && Core.graphics.isPortrait() ? 1 : mobile ? 2 : 3;
int i = 0;
for(T con : array){
t.table(Tex.underline, b -> {
b.left().margin(4f);
b.image(con.uiIcon).size(iconMed).padRight(3);
b.add(con.localizedName).color(Color.lightGray).padLeft(3).growX().left().wrap();
b.button(Icon.cancel, Styles.emptyi, () -> {
set.remove(con);
rebuild[0].run();
}).size(70f).pad(-4f).padLeft(0f);
}).size(300f, 70f).padRight(5);
if(++i % cols == 0){
t.row();
}
}
}).get().setScrollYForce(previousScroll);
bd.cont.row();
bd.cont.button("@add", Icon.add, () -> {
BaseDialog dialog = new BaseDialog("@add");
dialog.cont.pane(t -> {
t.left().margin(14f);
int[] i = {0};
content.<T>getBy(type).each(b -> !set.contains(b) && pred.get(b), b -> {
int cols = mobile && Core.graphics.isPortrait() ? 4 : 12;
t.button(new TextureRegionDrawable(b.uiIcon), Styles.flati, iconMed, () -> {
set.add(b);
rebuild[0].run();
dialog.hide();
}).size(60f).tooltip(b.localizedName);
if(++i[0] % cols == 0){
t.row();
}
});
});
dialog.addCloseButton();
dialog.show();
}).size(300f, 64f).disabled(b -> set.size == content.<T>getBy(type).count(pred));
};
bd.shown(rebuild[0]);
bd.buttons.button("@addall", Icon.add, () -> {
set.addAll(content.<T>getBy(type).select(pred));
rebuild[0].run();
}).size(180, 64f);
bd.buttons.button("@clear", Icon.trash, () -> {
set.clear();
rebuild[0].run();
}).size(180, 64f);
bd.show();
}
public void show(Rules rules, Prov<Rules> resetter){
this.rules = rules;
this.resetter = resetter;
@@ -220,28 +143,16 @@ public class CustomRulesDialog extends BaseDialog{
check("@rules.wavesending", b -> rules.waveSending = b, () -> rules.waveSending, () -> 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.randomwaveai", b -> rules.randomWaveAI = b, () -> rules.randomWaveAI, () -> 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);
//this is experimental, because it's not clear that 0 makes it default.
if(experimental){
number("@rules.initialwavespacing", false, f -> rules.initialWaveSpacing = f * 60f, () -> rules.initialWaveSpacing / 60f, () -> rules.waves && rules.waveTimer, 0, Float.MAX_VALUE);
}
number("@rules.initialwavespacing", false, f -> rules.initialWaveSpacing = f * 60f, () -> rules.initialWaveSpacing / 60f, () -> rules.waves && rules.waveTimer, 0, Float.MAX_VALUE);
number("@rules.dropzoneradius", false, f -> rules.dropZoneRadius = f * tilesize, () -> rules.dropZoneRadius / tilesize, () -> rules.waves);
category("resourcesbuilding");
check("@rules.infiniteresources", b -> {
rules.infiniteResources = b;
//reset to serpulo if any env was enabled
if(!b && rules.hiddenBuildItems.isEmpty()){
rules.env = Planets.serpulo.defaultEnv;
rules.hiddenBuildItems.clear();
rules.hiddenBuildItems.addAll(Planets.serpulo.hiddenItems);
setup();
}
}, () -> rules.infiniteResources);
check("@rules.alloweditworldprocessors", b -> rules.allowEditWorldProcessors = b, () -> rules.allowEditWorldProcessors);
check("@rules.infiniteresources", b -> rules.infiniteResources = b, () -> rules.infiniteResources);
check("@rules.onlydepositcore", b -> rules.onlyDepositCore = b, () -> rules.onlyDepositCore);
check("@rules.derelictrepair", b -> rules.derelictRepair = b, () -> rules.derelictRepair);
check("@rules.reactorexplosions", b -> rules.reactorExplosions = b, () -> rules.reactorExplosions);
@@ -265,7 +176,7 @@ public class CustomRulesDialog extends BaseDialog{
}
if(Core.bundle.get("bannedblocks").toLowerCase().contains(ruleSearch)){
current.button("@bannedblocks", () -> showBanned("@bannedblocks", ContentType.block, rules.bannedBlocks, Block::canBeBuilt)).left().width(300f).row();
current.button("@bannedblocks", () -> bannedBlocks.show(rules.bannedBlocks)).left().width(300f).row();
}
check("@rules.hidebannedblocks", b -> rules.hideBannedBlocks = b, () -> rules.hideBannedBlocks);
check("@bannedblocks.whitelist", b -> rules.blockWhitelist = b, () -> rules.blockWhitelist);
@@ -277,12 +188,13 @@ public class CustomRulesDialog extends BaseDialog{
numberi("@rules.unitcap", f -> rules.unitCap = f, () -> rules.unitCap, -999, 999);
number("@rules.unitdamagemultiplier", f -> rules.unitDamageMultiplier = f, () -> rules.unitDamageMultiplier);
number("@rules.unitcrashdamagemultiplier", f -> rules.unitCrashDamageMultiplier = f, () -> rules.unitCrashDamageMultiplier);
number("@rules.unitminespeedmultiplier", f -> rules.unitMineSpeedMultiplier = f, () -> rules.unitMineSpeedMultiplier);
number("@rules.unitbuildspeedmultiplier", f -> rules.unitBuildSpeedMultiplier = f, () -> rules.unitBuildSpeedMultiplier, 0f, 50f);
number("@rules.unitcostmultiplier", f -> rules.unitCostMultiplier = f, () -> rules.unitCostMultiplier);
number("@rules.unithealthmultiplier", f -> rules.unitHealthMultiplier = f, () -> rules.unitHealthMultiplier);
if(Core.bundle.get("bannedunits").toLowerCase().contains(ruleSearch)){
current.button("@bannedunits", () -> showBanned("@bannedunits", ContentType.unit, rules.bannedUnits, u -> !u.isHidden())).left().width(300f).row();
current.button("@bannedunits", () -> bannedUnits.show(rules.bannedUnits)).left().width(300f).row();
}
check("@bannedunits.whitelist", b -> rules.unitWhitelist = b, () -> rules.unitWhitelist);
@@ -301,13 +213,11 @@ public class CustomRulesDialog extends BaseDialog{
check("@rules.fog", b -> rules.fog = b, () -> rules.fog);
check("@rules.lighting", b -> rules.lighting = b, () -> rules.lighting);
if(experimental){
check("@rules.limitarea", b -> rules.limitMapArea = b, () -> rules.limitMapArea);
numberi("x", x -> rules.limitX = x, () -> rules.limitX, () -> rules.limitMapArea, 0, 10000);
numberi("y", y -> rules.limitY = y, () -> rules.limitY, () -> rules.limitMapArea, 0, 10000);
numberi("w", w -> rules.limitWidth = w, () -> rules.limitWidth, () -> rules.limitMapArea, 0, 10000);
numberi("h", h -> rules.limitHeight = h, () -> rules.limitHeight, () -> rules.limitMapArea, 0, 10000);
}
check("@rules.limitarea", b -> rules.limitMapArea = b, () -> rules.limitMapArea);
numberi("x", x -> rules.limitX = x, () -> rules.limitX, () -> rules.limitMapArea, 0, 10000);
numberi("y", y -> rules.limitY = y, () -> rules.limitY, () -> rules.limitMapArea, 0, 10000);
numberi("w", w -> rules.limitWidth = w, () -> rules.limitWidth, () -> rules.limitMapArea, 0, 10000);
numberi("h", h -> rules.limitHeight = h, () -> rules.limitHeight, () -> rules.limitMapArea, 0, 10000);
number("@rules.solarmultiplier", f -> rules.solarMultiplier = f, () -> rules.solarMultiplier);
@@ -339,7 +249,7 @@ public class CustomRulesDialog extends BaseDialog{
for(Planet planet : content.planets().select(p -> p.accessible && p.visible && p.isLandable())){
t.button(planet.localizedName, style, () -> {
planet.applyRules(rules);
planet.applyRules(rules, true);
}).group(group).checked(b -> rules.planet == planet);
if(t.getChildren().size % 3 == 0){
@@ -349,7 +259,6 @@ public class CustomRulesDialog extends BaseDialog{
t.button("@rules.anyenv", style, () -> {
rules.env = Vars.defaultEnv;
rules.hiddenBuildItems.clear();
rules.planet = Planets.sun;
}).group(group).checked(b -> rules.planet == Planets.sun);
}).left().fill(false).expand(false, false).row();
@@ -393,11 +302,14 @@ public class CustomRulesDialog extends BaseDialog{
check("@rules.buildai", b -> teams.buildAi = b, () -> teams.buildAi, () -> team != rules.defaultTeam && rules.env != Planets.erekir.defaultEnv && !rules.pvp);
number("@rules.buildaitier", false, f -> teams.buildAiTier = f, () -> teams.buildAiTier, () -> teams.buildAi && rules.env != Planets.erekir.defaultEnv && !rules.pvp, 0, 1);
number("@rules.extracorebuildradius", f -> teams.extraCoreBuildRadius = f * tilesize, () -> Math.min(teams.extraCoreBuildRadius / tilesize, 200), () -> !rules.polygonCoreProtection);
check("@rules.infiniteresources", b -> teams.infiniteResources = b, () -> teams.infiniteResources);
number("@rules.buildspeedmultiplier", f -> teams.buildSpeedMultiplier = f, () -> teams.buildSpeedMultiplier, 0.001f, 50f);
number("@rules.unitdamagemultiplier", f -> teams.unitDamageMultiplier = f, () -> teams.unitDamageMultiplier);
number("@rules.unitcrashdamagemultiplier", f -> teams.unitCrashDamageMultiplier = f, () -> teams.unitCrashDamageMultiplier);
number("@rules.unitminespeedmultiplier", f -> teams.unitMineSpeedMultiplier = f, () -> teams.unitMineSpeedMultiplier);
number("@rules.unitbuildspeedmultiplier", f -> teams.unitBuildSpeedMultiplier = f, () -> teams.unitBuildSpeedMultiplier, 0.001f, 50f);
number("@rules.unitcostmultiplier", f -> teams.unitCostMultiplier = f, () -> teams.unitCostMultiplier);
number("@rules.unithealthmultiplier", f -> teams.unitHealthMultiplier = f, () -> teams.unitHealthMultiplier);
@@ -471,7 +383,7 @@ public class CustomRulesDialog extends BaseDialog{
public void numberi(String text, Intc cons, Intp prov, Boolp condition, int min, int max){
if(!Core.bundle.get(text.substring(1)).toLowerCase().contains(ruleSearch)) return;
current.table(t -> {
var cell = current.table(t -> {
t.left();
t.add(text).left().padRight(5)
.update(a -> a.setColor(condition.get() ? Color.white : Color.gray));
@@ -479,12 +391,14 @@ public class CustomRulesDialog extends BaseDialog{
.update(a -> a.setDisabled(!condition.get()))
.padRight(100f)
.valid(f -> Strings.parseInt(f) >= min && Strings.parseInt(f) <= max).width(120f).left();
}).padTop(0).row();
}).padTop(0);
ruleInfo(cell, text);
current.row();
}
public void number(String text, boolean integer, Floatc cons, Floatp prov, Boolp condition, float min, float max){
if(!Core.bundle.get(text.substring(1)).toLowerCase().contains(ruleSearch)) return;
current.table(t -> {
var cell = current.table(t -> {
t.left();
t.add(text).left().padRight(5)
.update(a -> a.setColor(condition.get() ? Color.white : Color.gray));
@@ -493,6 +407,7 @@ public class CustomRulesDialog extends BaseDialog{
.update(a -> a.setDisabled(!condition.get()))
.valid(f -> Strings.canParsePositiveFloat(f) && Strings.parseFloat(f) >= min && Strings.parseFloat(f) <= max).width(120f).left();
}).padTop(0);
ruleInfo(cell, text);
current.row();
}
@@ -502,15 +417,26 @@ public class CustomRulesDialog extends BaseDialog{
public void check(String text, Boolc cons, Boolp prov, Boolp condition){
if(!Core.bundle.get(text.substring(1)).toLowerCase().contains(ruleSearch)) return;
String infoText = text.substring(1) + ".info";
var cell = current.check(text, cons).checked(prov.get()).update(a -> a.setDisabled(!condition.get())).padRight(100f);
if(Core.bundle.has(infoText)){
cell.tooltip(text + ".info");
}
var cell = current.check(text, cons).checked(prov.get()).update(a -> a.setDisabled(!condition.get()));
cell.get().left();
ruleInfo(cell, text);
current.row();
}
public void ruleInfo(Cell<?> cell, String text){
if(Core.bundle.has(text.substring(1) + ".info")){
if(mobile){
Table table = new Table();
table.add(cell.get()).left().expandX().fillX();
cell.clearElement();
table.button(Icon.infoSmall, () -> ui.showInfo(text + ".info")).size(32f).padRight(24f).right();
cell.setElement(table).left().expandX().fillX();
}else{
cell.tooltip(text + ".info");
}
}
}
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)

View File

@@ -5,11 +5,13 @@ import arc.graphics.*;
import arc.input.*;
import arc.math.*;
import arc.scene.event.*;
import arc.scene.style.*;
import arc.scene.ui.*;
import arc.scene.ui.layout.*;
import arc.struct.*;
import arc.util.*;
import mindustry.*;
import mindustry.content.*;
import mindustry.ctype.*;
import mindustry.gen.*;
import mindustry.graphics.*;
@@ -24,16 +26,30 @@ public class DatabaseDialog extends BaseDialog{
private TextField search;
private Table all = new Table();
private @Nullable Seq<UnlockableContent> allTabs;
//sun means "all content"
private UnlockableContent tab = Planets.sun;
public DatabaseDialog(){
super("@database");
shouldPause = true;
addCloseButton();
shown(this::rebuild);
shown(() -> {
checkTabList();
if(state.isCampaign() && allTabs.contains(state.getPlanet())){
tab = state.getPlanet();
}else if(state.isGame() && state.rules.planet != null && allTabs.contains(state.rules.planet)){
tab = state.rules.planet;
}
rebuild();
});
onResize(this::rebuild);
all.margin(20).marginTop(0f);
all.margin(20).marginTop(0f).marginRight(30f);
cont.top();
cont.table(s -> {
s.image(Icon.zoom).padRight(8);
search = s.field(null, text -> rebuild()).growX().get();
@@ -43,18 +59,51 @@ public class DatabaseDialog extends BaseDialog{
cont.pane(all).scrollX(false);
}
void checkTabList(){
if(allTabs == null){
Seq<Content>[] allContent = Vars.content.getContentMap();
ObjectSet<UnlockableContent> all = new ObjectSet<>();
for(var contents : allContent){
for(var content : contents){
if(content instanceof UnlockableContent u){
all.addAll(u.databaseTabs);
}
}
}
allTabs = all.toSeq().sort();
allTabs.insert(0, Planets.sun);
}
}
void rebuild(){
checkTabList();
all.clear();
var text = search.getText().toLowerCase();
Seq<Content>[] allContent = Vars.content.getContentMap();
all.table(t -> {
int i = 0;
for(var content : allTabs){
t.button(content == Planets.sun ? Icon.eyeSmall : content instanceof Planet p ? Icon.icons.get(p.icon, Icon.commandRally) : new TextureRegionDrawable(content.uiIcon), Styles.clearNoneTogglei, iconMed, () -> {
tab = content;
rebuild();
}).size(50f).checked(b -> tab == content).tooltip(content == Planets.sun ? "@all" : content.localizedName).with(but -> {
but.getStyle().imageUpColor = content instanceof Planet p ? p.iconColor : Color.white.cpy();
});
if(++i % 10 == 0) t.row();
}
}).row();;
for(int j = 0; j < allContent.length; j++){
ContentType type = ContentType.all[j];
Seq<UnlockableContent> array = allContent[j]
.select(c -> c instanceof UnlockableContent u && !u.isHidden() &&
.select(c -> c instanceof UnlockableContent u && !u.isHidden() && !u.hideDatabase && (tab == Planets.sun || u.allDatabaseTabs || u.databaseTabs.contains(tab)) &&
(text.isEmpty() || u.localizedName.toLowerCase().contains(text))).as();
if(array.size == 0) continue;
all.add("@content." + type.name() + ".name").growX().left().color(Pal.accent);

View File

@@ -3,13 +3,12 @@ package mindustry.ui.dialogs;
import arc.*;
import arc.scene.ui.*;
import arc.util.*;
import mindustry.*;
import mindustry.core.*;
import mindustry.game.EventType.*;
import mindustry.gen.*;
import mindustry.ui.*;
import java.io.*;
import java.util.*;
import static mindustry.Vars.*;
@@ -49,6 +48,19 @@ public class HostDialog extends BaseDialog{
cont.row();
TextField[] portField = {null};
cont.table(t -> {
t.add("@server.port").padRight(10);
portField[0] = t.field(String.valueOf(Core.settings.getInt("port", port)), text -> Core.settings.put("port", Strings.parseInt(text, 6567)))
.pad(8).grow().maxTextLength(5).valid(text -> {
int port = Strings.parseInt(text);
return port >= 1 && port <= 65535;
}).get();
}).width(w).height(70f).pad(4).colspan(3);
cont.row();
cont.add().width(65f);
cont.button("@host", () -> {
@@ -58,7 +70,7 @@ public class HostDialog extends BaseDialog{
}
runHost();
}).width(w).height(70f);
}).width(w).height(70f).disabled(b -> !portField[0].isValid());
if(!steam){
cont.button("?", () -> ui.showInfo("@host.info")).size(65f, 70f).padLeft(6f);
@@ -77,7 +89,7 @@ public class HostDialog extends BaseDialog{
ui.loadfrag.show("@hosting");
Time.runTask(5f, () -> {
try{
net.host(Vars.port);
net.host(Core.settings.getInt("port", port));
player.admin = true;
Events.fire(new HostEvent());
@@ -90,8 +102,8 @@ public class HostDialog extends BaseDialog{
}
}catch(IOException e){
ui.showException("@server.error", e);
}catch(Exception e){
ui.showException(e.getMessage() != null && e.getMessage().toLowerCase(Locale.ROOT).contains("address already in use") ? "@server.error.addressinuse" : "@server.error", e);
}
ui.loadfrag.hide();
hide();

View File

@@ -15,8 +15,13 @@ import static mindustry.Vars.*;
public class IconSelectDialog extends Dialog{
private Intc consumer = i -> Log.info("you have mere seconds");
private boolean allowLocked;
public IconSelectDialog(){
this(true);
}
public IconSelectDialog(boolean allowLocked){
closeOnBack();
setFillParent(true);
@@ -52,7 +57,7 @@ public class IconSelectDialog extends Dialog{
i = 0;
for(UnlockableContent u : content.getBy(ctype).<UnlockableContent>as()){
if(!u.isHidden() && u.unlocked()){
if(!u.isHidden() && (allowLocked || u.unlocked())){
t.button(new TextureRegionDrawable(u.uiIcon), Styles.flati, iconMed, () -> {
hide();
consumer.get(u.emojiChar());

View File

@@ -138,7 +138,9 @@ public class JoinDialog extends BaseDialog{
refreshLocal();
refreshRemote();
refreshCommunity();
if(Core.settings.getBool("communityservers", true)){
refreshCommunity();
}
}
void setupRemote(){
@@ -317,7 +319,9 @@ public class JoinDialog extends BaseDialog{
section(steam ? "@servers.local.steam" : "@servers.local", local, false);
section("@servers.remote", remote, false);
section("@servers.global", global, true);
if(Core.settings.getBool("communityservers", true)){
section("@servers.global", global, true);
}
ScrollPane pane = new ScrollPane(hosts);
pane.setFadeScrollBars(false);
@@ -599,7 +603,7 @@ public class JoinDialog extends BaseDialog{
connect(lastIp, lastPort);
}, exception -> {});
}, 1, 1);
ui.loadfrag.setButton(() -> {
ui.loadfrag.hide();
if(ping == null) return;
@@ -631,12 +635,30 @@ public class JoinDialog extends BaseDialog{
Core.settings.remove("server-list");
}
var url = becontrol.active() ? serverJsonBeURL : serverJsonURL;
Log.info("Fetching community servers at @", url);
fetchServers();
}
public static void fetchServers(){
var urls = Version.type.equals("bleeding-edge") || Vars.forceBeServers ? serverJsonBeURLs : serverJsonURLs;
if(Core.settings.getBool("communityservers", true)){
fetchServers(urls, 0);
}
}
private static void fetchServers(String[] urls, int index){
if(index >= urls.length) return;
//get servers
Http.get(url)
.error(t -> Log.err("Failed to fetch community servers", t))
Http.get(urls[index])
.error(t -> {
if(index < urls.length - 1){
//attempt fetching from the next URL upon failure
fetchServers(urls, index + 1);
}else{
Log.err("Failed to fetch community servers", t);
}
})
.submit(result -> {
Jval val = Jval.read(result.getResultAsString());
Seq<ServerGroup> servers = new Seq<>();
@@ -655,7 +677,7 @@ public class JoinDialog extends BaseDialog{
Core.app.post(() -> {
servers.sort(s -> s.name == null ? Integer.MAX_VALUE : s.name.hashCode());
defaultServers.addAll(servers);
Log.info("Fetched @ community servers.", defaultServers.size);
Log.info("Fetched @ community servers.", defaultServers.sum(s -> s.addresses.length));
});
});
}

View File

@@ -48,7 +48,7 @@ public class LaunchLoadoutDialog extends BaseDialog{
ItemSeq launch = universe.getLaunchResources();
if(sector.planet.allowLaunchLoadout){
for(var item : content.items()){
if(sector.planet.hiddenItems.contains(item)){
if(!item.isOnPlanet(sector.planet)){
launch.set(item, 0);
}
}
@@ -72,7 +72,7 @@ public class LaunchLoadoutDialog extends BaseDialog{
if(destination.preset != null){
var rules = destination.preset.generator.map.rules();
for(var stack : rules.loadout){
if(!sector.planet.hiddenItems.contains(stack.item)){
if(stack.item.isOnPlanet(sector.planet)){
resources.add(stack.item, stack.amount);
}
}
@@ -89,7 +89,7 @@ public class LaunchLoadoutDialog extends BaseDialog{
total.clear();
selected.requirements().each(total::add);
universe.getLaunchResources().each(total::add);
valid = sitems.has(total);
valid = sitems.has(total) || PlanetDialog.debugSelect;
};
Cons<Table> rebuild = table -> {
@@ -136,7 +136,7 @@ public class LaunchLoadoutDialog extends BaseDialog{
ItemSeq realItems = sitems.copy();
selected.requirements().each(realItems::remove);
loadout.show(lastCapacity, realItems, out, i -> i.unlocked() && !sector.planet.hiddenItems.contains(i), out::clear, () -> {}, () -> {
loadout.show(lastCapacity, realItems, out, i -> i.unlocked() && i.isOnPlanet(sector.planet), out::clear, () -> {}, () -> {
universe.updateLaunchResources(new ItemSeq(out));
update.run();
rebuildItems.run();
@@ -172,7 +172,7 @@ public class LaunchLoadoutDialog extends BaseDialog{
Cons<Schematic> handler = s -> {
if(s.tiles.contains(tile -> !tile.block.supportsEnv(sector.planet.defaultEnv) ||
//make sure block can be built here.
(!sector.planet.hiddenItems.isEmpty() && Structs.contains(tile.block.requirements, stack -> sector.planet.hiddenItems.contains(stack.item))))){
!tile.block.isOnPlanet(sector.planet))){
return;
}

View File

@@ -72,6 +72,7 @@ public class ModsDialog extends BaseDialog{
browserTable = tablebrow;
}).scrollX(false);
browser.addCloseButton();
browser.makeButtonOverlay();
browser.onResize(this::rebuildBrowser);
@@ -106,40 +107,56 @@ public class ModsDialog extends BaseDialog{
ui.showErrorMessage("@feature.unsupported");
}else if(error instanceof HttpStatusException st){
ui.showErrorMessage(Core.bundle.format("connectfail", Strings.capitalize(st.status.toString().toLowerCase())));
}else if(error.getMessage() != null && error.getMessage().toLowerCase(Locale.ROOT).contains("writable dex")){
ui.showException("@error.moddex", error);
}else{
ui.showException(error);
}
}
void getModList(Cons<Seq<ModListing>> listener){
if(modList == null){
Http.get("https://raw.githubusercontent.com/Anuken/MindustryMods/master/mods.json", response -> {
String strResult = response.getResultAsString();
void getModList(int index, Cons<Seq<ModListing>> listener){
if(index >= modJsonURLs.length) return;
if(modList != null){
listener.get(modList);
return;
}
Http.get(modJsonURLs[index], response -> {
String strResult = response.getResultAsString();
Core.app.post(() -> {
try{
modList = JsonIO.json.fromJson(Seq.class, ModListing.class, strResult);
var d = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
Func<String, Date> parser = text -> {
try{
return d.parse(text);
}catch(Exception e){
return new Date();
}
};
modList.sortComparing(m -> parser.get(m.lastUpdated)).reverse();
listener.get(modList);
}catch(Exception e){
Log.err(e);
ui.showException(e);
}
});
}, error -> {
if(index < modJsonURLs.length - 1){
getModList(index + 1, listener);
}else{
Core.app.post(() -> {
try{
modList = JsonIO.json.fromJson(Seq.class, ModListing.class, strResult);
var d = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
Func<String, Date> parser = text -> {
try{
return d.parse(text);
}catch(Exception e){
return new Date();
}
};
modList.sortComparing(m -> parser.get(m.lastUpdated)).reverse();
listener.get(modList);
}catch(Exception e){
e.printStackTrace();
ui.showException(e);
modError(error);
if(browser != null){
browser.hide();
}
});
}, error -> Core.app.post(() -> modError(error)));
}else{
listener.get(modList);
}
}
});
}
void setup(){
@@ -174,7 +191,7 @@ public class ModsDialog extends BaseDialog{
mods.importMod(file);
setup();
}catch(Exception e){
ui.showException(e);
ui.showException(e.getMessage() != null && e.getMessage().toLowerCase(Locale.ROOT).contains("writable dex") ? "@error.moddex" : "", e);
Log.err(e);
}
}, "zip", "jar");
@@ -391,6 +408,12 @@ public class ModsDialog extends BaseDialog{
desc.add(mod.meta.author).growX().wrap().padTop(2);
desc.row();
}
if(mod.meta.version != null){
desc.add("@mod.version").padRight(10).color(Color.gray).top();
desc.row();
desc.add(mod.meta.version).growX().wrap().padTop(2);
desc.row();
}
if(mod.meta.description != null){
desc.add("@editor.description").padRight(10).color(Color.gray).top();
desc.row();
@@ -448,7 +471,7 @@ public class ModsDialog extends BaseDialog{
int cols = (int)Math.max(Core.graphics.getWidth() / Scl.scl(480), 1);
getModList(rlistings -> {
getModList(0, rlistings -> {
browserTable.clear();
int i = 0;

View File

@@ -159,7 +159,7 @@ public class PausedDialog extends BaseDialog{
return;
}
if(control.saves.getCurrent() == null || !control.saves.getCurrent().isAutosave() || wasClient || state.gameOver){
if(control.saves.getCurrent() == null || !control.saves.getCurrent().isAutosave() || wasClient || state.gameOver || disableSave){
logic.reset();
return;
}

View File

@@ -54,6 +54,8 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
public PlanetParams state = new PlanetParams();
public float zoom = 1f;
public @Nullable Sector selected, hovered, launchSector;
/** Must not be null in planet launch mode. */
public @Nullable Seq<Planet> launchCandidates;
public Mode mode = look;
public boolean launching;
public Cons<Sector> listener = s -> {};
@@ -67,10 +69,11 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
public Label hoverLabel = new Label("");
private Texture[] planetTextures;
private CampaignRulesDialog campaignRules = new CampaignRulesDialog();
public PlanetDialog(){
super("", Styles.fullDialog);
state.renderer = this;
state.drawUi = true;
@@ -293,7 +296,7 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
}
void addTech(){
buttons.button("@techtree", Icon.tree, () -> ui.research.show()).size(200f, 54f).pad(2).bottom();
buttons.button("@techtree", Icon.tree, () -> ui.research.show()).size(200f, 54f).visible(() -> mode == look).pad(2).bottom();
}
public void showOverview(){
@@ -311,16 +314,17 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
}
//TODO not fully implemented, cutscene needed
public void showPlanetLaunch(Sector sector, Cons<Sector> listener){
public void showPlanetLaunch(Sector sector, Seq<Planet> launchCandidates, Cons<Sector> listener){
selected = null;
hovered = null;
launching = false;
this.listener = listener;
this.launchCandidates = (launchCandidates == null ? sector.planet.launchCandidates : launchCandidates);
launchSector = sector;
//automatically select next planets;
if(sector.planet.launchCandidates.size == 1){
state.planet = sector.planet.launchCandidates.first();
if(this.launchCandidates.size == 1){
state.planet = this.launchCandidates.first();
state.otherCamPos = sector.planet.position;
state.otherCamAlpha = 0f;
@@ -331,8 +335,6 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
preset.unlock();
}
selected = destSec;
updateSelected();
rebuildExpand();
}
//TODO pan over to correct planet
@@ -344,6 +346,13 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
mode = planetLaunch;
updateSelected();
rebuildExpand();
if(sectorTop != null){
sectorTop.color.a = 0f;
}
super.show();
}
@@ -381,22 +390,29 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
boolean canSelect(Sector sector){
if(mode == select) return sector.hasBase() && launchSector != null && sector.planet == launchSector.planet;
//cannot launch to existing sector w/ accelerator TODO test
if(mode == planetLaunch) return sector.id == sector.planet.startSector;
if(mode == planetLaunch && sector.hasBase()){
return false;
}
if(sector.hasBase() || sector.id == sector.planet.startSector) return true;
//preset sectors can only be selected once unlocked
if(sector.preset != null){
TechNode node = sector.preset.techNode;
return node == null || node.parent == null || (node.parent.content.unlocked() && (!(node.parent.content instanceof SectorPreset preset) || preset.sector.hasBase()));
return sector.preset.unlocked() || node == null || node.parent == null || (node.parent.content.unlocked() && (!(node.parent.content instanceof SectorPreset preset) || preset.sector.hasBase()));
}
return sector.planet.generator != null ?
//use planet impl when possible
sector.planet.generator.allowLanding(sector) :
sector.hasBase() || sector.near().contains(Sector::hasBase); //near an occupied sector
(mode == planetLaunch ? sector.planet.generator.allowAcceleratorLanding(sector) : sector.planet.generator.allowLanding(sector)) :
mode == planetLaunch || sector.hasBase() || sector.near().contains(Sector::hasBase); //near an occupied sector
}
Sector findLauncher(Sector to){
if(mode == planetLaunch){
return launchSector;
}
Sector launchSector = this.launchSector != null && this.launchSector.planet == to.planet && this.launchSector.hasBase() ? this.launchSector : null;
//directly nearby.
if(to.near().contains(launchSector)) return launchSector;
@@ -471,10 +487,14 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
}
}
if(mode == planetLaunch && launchSector != null && selected != null && hovered == null){
planets.drawArc(planet, launchSector.tile.v, selected.tile.v);
}
if(state.uiAlpha > 0.001f){
for(Sector sec : planet.sectors){
if(sec.hasBase()){
if(planet.allowSectorInvasion){
if(planet.campaignRules.sectorInvasion){
for(Sector enemy : sec.near()){
if(enemy.hasEnemyBase()){
planets.drawArc(planet, enemy.tile.v, sec.tile.v, Team.crux.color.write(Tmp.c2).a(state.uiAlpha), Color.clear, 0.24f, 110f, 25);
@@ -484,11 +504,11 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
if(selected != null && selected != sec && selected.hasBase()){
//imports
if(sec.info.getRealDestination() == selected && sec.info.anyExports()){
if(sec.info.destination == selected && sec.info.anyExports()){
planets.drawArc(planet, sec.tile.v, selected.tile.v, Color.gray.write(Tmp.c2).a(state.uiAlpha), Pal.accent.write(Tmp.c3).a(state.uiAlpha), 0.4f, 90f, 25);
}
//exports
if(selected.info.getRealDestination() == sec && selected.info.anyExports()){
if(selected.info.destination == sec && selected.info.anyExports()){
planets.drawArc(planet, selected.tile.v, sec.tile.v, Pal.place.write(Tmp.c2).a(state.uiAlpha), Pal.accent.write(Tmp.c3).a(state.uiAlpha), 0.4f, 90f, 25);
}
}
@@ -547,7 +567,7 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
//TODO what if any sector is selectable?
//TODO launch criteria - which planets can be launched to? Where should this be defined? Should planets even be selectable?
if(mode == select) return planet == state.planet;
if(mode == planetLaunch) return launchSector != null && planet != launchSector.planet && launchSector.planet.launchCandidates.contains(planet);
if(mode == planetLaunch) return launchSector != null && (launchCandidates.contains(planet) || (planet == launchSector.planet && planet.allowSelfSectorLaunch));
return (planet.alwaysUnlocked && planet.isLandable()) || planet.sectors.contains(Sector::hasBase) || debugSelect;
}
@@ -603,7 +623,11 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
new Table(t -> {
t.touchable = Touchable.disabled;
t.top();
t.label(() -> mode == select ? "@sectors.select" : "").style(Styles.outlineLabel).color(Pal.accent);
t.label(() ->
mode == select ? "@sectors.select" :
mode == planetLaunch ? "@sectors.launchselect" :
""
).style(Styles.outlineLabel).color(Pal.accent);
}),
buttons,
@@ -612,6 +636,10 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
t.top().left();
ScrollPane pane = new ScrollPane(null, Styles.smallPane);
t.add(pane).colspan(2).row();
t.button("@campaign.difficulty", Icon.bookSmall, () -> {
campaignRules.show(state.planet);
}).margin(12f).size(208f, 40f).padTop(12f).visible(() -> state.planet.allowCampaignRules && mode != planetLaunch).row();
t.add().height(64f); //padding for close button
Table starsTable = new Table(Styles.black);
pane.setWidget(starsTable);
pane.setScrollingDisabled(true, false);
@@ -629,7 +657,6 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
if(planet.solarSystem == star && selectable(planet)){
Button planetButton = planetTable.button(planet.localizedName, Icon.icons.get(planet.icon + "Small", Icon.icons.get(planet.icon, Icon.commandRallySmall)), Styles.flatTogglet, () -> {
selected = null;
launchSector = null;
if(state.planet != planet){
newPresets.clear();
state.planet = planet;
@@ -655,7 +682,7 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
void rebuildExpand(){
Table c = expandTable;
c.clear();
c.visible(() -> !(graphics.isPortrait() && mobile));
c.visible(() -> !(graphics.isPortrait() && mobile) && mode != planetLaunch);
if(state.planet.sectors.contains(Sector::hasBase)){
int attacked = state.planet.sectors.count(Sector::isAttacked);
@@ -778,15 +805,22 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
if(Mathf.equal(state.otherCamAlpha, 1f, 0.01f)){
//TODO change zoom too
state.camPos.set(Tmp.v31.set(state.otherCamPos).lerp(state.planet.position, state.otherCamAlpha).add(state.camPos).sub(state.planet.position));
state.camPos.set(Tmp.v31.set(state.otherCamPos).slerp(state.planet.position, state.otherCamAlpha).add(state.camPos).sub(state.planet.position));
state.otherCamPos = null;
//announce new sector
newPresets.add(state.planet.sectors.get(state.planet.startSector));
if(!state.planet.sectors.isEmpty()){
newPresets.add(state.planet.sectors.get(state.planet.startSector));
}
}
}
//fade in sector dialog after panning
if(sectorTop != null && state.otherCamPos == null){
sectorTop.color.a = Mathf.lerpDelta(sectorTop.color.a, 1f, 0.1f);
}
if(hovered != null && !mobile && state.planet.hasGrid()){
addChild(hoverLabel);
hoverLabel.toFront();
@@ -930,7 +964,7 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
//import
if(sector.hasBase()){
displayItems(c, 1f, sector.info.importStats(sector.planet), "@sectors.import", t -> {
displayItems(c, 1f, sector.info.imports, "@sectors.import", t -> {
sector.info.eachImport(sector.planet, other -> {
String ic = other.iconChar();
t.add(Iconc.rightOpen + " " + (ic == null || ic.isEmpty() ? "" : ic + " ") + other.name()).padLeft(10f).row();
@@ -1133,7 +1167,7 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
if(sector.isAttacked()){
addSurvivedInfo(sector, stable, false);
}else if(sector.hasBase() && sector.planet.allowSectorInvasion && sector.near().contains(Sector::hasEnemyBase)){
}else if(sector.hasBase() && sector.planet.campaignRules.sectorInvasion && sector.near().contains(Sector::hasEnemyBase)){
stable.add("@sectors.vulnerable");
stable.row();
}else if(!sector.hasBase() && sector.hasEnemyBase()){
@@ -1187,24 +1221,24 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
return;
}
//make sure there are no under-attack sectors (other than this one)
for(Planet planet : content.planets()){
if(!planet.allowWaveSimulation && !debugSelect && planet.allowWaveSimulation == sector.planet.allowWaveSimulation){
//if there are two or more attacked sectors... something went wrong, don't show the dialog to prevent softlock
Sector attacked = planet.sectors.find(s -> s.isAttacked() && s != sector);
if(attacked != null && planet.sectors.count(s -> s.isAttacked()) < 2){
BaseDialog dialog = new BaseDialog("@sector.noswitch.title");
dialog.cont.add(bundle.format("sector.noswitch", attacked.name(), attacked.planet.localizedName)).width(400f).labelAlign(Align.center).center().wrap();
dialog.addCloseButton();
dialog.buttons.button("@sector.view", Icon.eyeSmall, () -> {
dialog.hide();
lookAt(attacked);
selectSector(attacked);
});
dialog.show();
Planet planet = sector.planet;
return;
}
//make sure there are no under-attack sectors (other than this one)
if(!planet.allowWaveSimulation && !debugSelect){
//if there are two or more attacked sectors... something went wrong, don't show the dialog to prevent softlock
Sector attacked = planet.sectors.find(s -> s.isAttacked() && s != sector);
if(attacked != null && planet.sectors.count(s -> s.isAttacked()) < 2){
BaseDialog dialog = new BaseDialog("@sector.noswitch.title");
dialog.cont.add(bundle.format("sector.noswitch", attacked.name(), attacked.planet.localizedName)).width(400f).labelAlign(Align.center).center().wrap();
dialog.addCloseButton();
dialog.buttons.button("@sector.view", Icon.eyeSmall, () -> {
dialog.hide();
lookAt(attacked);
selectSector(attacked);
});
dialog.show();
return;
}
}
@@ -1253,9 +1287,9 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
//allow planet dialog to finish hiding before actually launching
Time.runTask(5f, () -> {
Runnable doLaunch = () -> {
renderer.showLaunch(core, schemCore);
renderer.showLaunch(core);
//run with less delay, as the loading animation is delayed by several frames
Time.runTask(core.landDuration() - 8f, () -> control.playSector(from, sector));
Time.runTask(core.launchDuration() - 8f, () -> control.playSector(from, sector));
};
//load launchFrom sector right before launching so animation is correct
@@ -1270,15 +1304,8 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
}
});
}
}else if(mode == select){
}else if(mode == select || mode == planetLaunch){
listener.get(sector);
}else if(mode == planetLaunch){ //TODO make sure it doesn't have a base already.
//TODO animation
//schematic selection and cost handled by listener
listener.get(sector);
//unlock right before launch
sector.planet.unlockedOnLand.each(UnlockableContent::unlock);
control.playSector(sector);
}else{
//sector should have base here
control.playSector(sector);

View File

@@ -47,10 +47,30 @@ public class ResearchDialog extends BaseDialog{
public ItemSeq items;
private boolean showTechSelect;
private boolean needsRebuild;
public ResearchDialog(){
super("");
Events.on(ResetEvent.class, e -> {
hide();
});
Events.on(UnlockEvent.class, e -> {
if(net.client() && !needsRebuild){
needsRebuild = true;
Core.app.post(() -> {
needsRebuild = false;
checkNodes(root);
view.hoverNode = null;
treeLayout();
view.rebuild();
Core.scene.act();
});
}
});
titleTable.remove();
titleTable.clear();
titleTable.top();
@@ -67,7 +87,7 @@ public class ResearchDialog extends BaseDialog{
t.table(Tex.button, in -> {
in.defaults().width(300f).height(60f);
for(TechNode node : TechTree.roots){
if(node.requiresUnlock && !node.content.unlocked() && node != getPrefRoot()) continue;
if(node.requiresUnlock && !node.content.unlockedHost() && node != getPrefRoot()) continue;
//TODO toggle
in.button(node.localizedName(), node.icon(), Styles.flatTogglet, iconMed, () -> {
@@ -84,10 +104,11 @@ public class ResearchDialog extends BaseDialog{
addCloseButton();
}}.show();
}).visible(() -> showTechSelect = TechTree.roots.count(node -> !(node.requiresUnlock && !node.content.unlocked())) > 1).minWidth(300f);
}).visible(() -> showTechSelect = TechTree.roots.count(node -> !(node.requiresUnlock && !node.content.unlockedHost())) > 1).minWidth(300f);
margin(0f).marginBottom(8);
cont.stack(titleTable, view = new View(), itemDisplay = new ItemsDisplay()).grow();
itemDisplay.visible(() -> !net.client());
titleTable.toFront();
@@ -177,15 +198,6 @@ public class ResearchDialog extends BaseDialog{
});
}
@Override
public Dialog show(){
if(net.client()){
ui.showInfo("@research.multiplayer");
return this;
}
return show(Core.scene);
}
void checkMargin(){
if(Core.graphics.isPortrait() && showTechSelect){
itemDisplay.marginTop(60f);
@@ -361,11 +373,12 @@ public class ResearchDialog extends BaseDialog{
}
boolean selectable(TechNode node){
return node.content.unlocked() || !node.objectives.contains(i -> !i.complete());
//there's a desync here as far as sectors go, since the client doesn't know about that, but I'm not too concerned
return node.content.unlockedHost() || !node.objectives.contains(i -> !i.complete());
}
boolean locked(TechNode node){
return node.content.locked();
return !node.content.unlockedHost();
}
class LayoutNode extends TreeNode<LayoutNode>{
@@ -418,31 +431,34 @@ public class ResearchDialog extends BaseDialog{
button.resizeImage(32f);
button.getImage().setScaling(Scaling.fit);
button.visible(() -> node.visible);
button.clicked(() -> {
if(moved) return;
if(!net.client()){
button.clicked(() -> {
if(moved) return;
if(mobile){
hoverNode = button;
rebuild();
float right = infoTable.getRight();
if(right > Core.graphics.getWidth()){
float moveBy = right - Core.graphics.getWidth();
addAction(new RelativeTemporalAction(){
{
setDuration(0.1f);
setInterpolation(Interp.fade);
}
if(mobile){
hoverNode = button;
rebuild();
float right = infoTable.getRight();
if(right > Core.graphics.getWidth()){
float moveBy = right - Core.graphics.getWidth();
addAction(new RelativeTemporalAction(){
{
setDuration(0.1f);
setInterpolation(Interp.fade);
}
@Override
protected void updateRelative(float percentDelta){
panX -= moveBy * percentDelta;
}
});
@Override
protected void updateRelative(float percentDelta){
panX -= moveBy * percentDelta;
}
});
}
}else if(canSpend(node.node) && locked(node.node)){
spend(node.node);
}
}else if(canSpend(node.node) && locked(node.node)){
spend(node.node);
}
});
});
}
button.hovered(() -> {
if(!mobile && hoverNode != button && node.visible){
hoverNode = button;
@@ -459,9 +475,10 @@ public class ResearchDialog extends BaseDialog{
button.userObject = node.node;
button.setSize(nodeSize);
button.update(() -> {
button.setDisabled(net.client() && !mobile);
float offset = (Core.graphics.getHeight() % 2) / 2f;
button.setPosition(node.x + panX + width / 2f, node.y + panY + height / 2f + offset, Align.center);
button.getStyle().up = !locked(node.node) ? Tex.buttonOver : !selectable(node.node) || !canSpend(node.node) ? Tex.buttonRed : Tex.button;
button.getStyle().up = !locked(node.node) ? Tex.buttonOver : !selectable(node.node) || (!canSpend(node.node) && !net.client()) ? Tex.buttonRed : Tex.button;
((TextureRegionDrawable)button.getStyle().imageUp).setRegion(node.selectable ? node.node.content.uiIcon : Icon.lock.getRegion());
button.getImage().setColor(!locked(node.node) ? Color.white : node.selectable ? Color.gray : Pal.gray);
@@ -498,7 +515,7 @@ public class ResearchDialog extends BaseDialog{
}
boolean canSpend(TechNode node){
if(!selectable(node)) return false;
if(!selectable(node) || net.client()) return false;
if(node.requirements.length == 0) return true;
@@ -514,6 +531,8 @@ public class ResearchDialog extends BaseDialog{
}
void spend(TechNode node){
if(net.client()) return;
boolean complete = true;
boolean[] shine = new boolean[node.requirements.length];
@@ -549,6 +568,7 @@ public class ResearchDialog extends BaseDialog{
Core.scene.act();
rebuild(shine);
itemDisplay.rebuild(items, usedShine);
checkMargin();
}
void unlock(TechNode node){
@@ -611,86 +631,90 @@ public class ResearchDialog extends BaseDialog{
desc.left().defaults().left();
desc.add(selectable ? node.content.localizedName : "[accent]???");
desc.row();
if(locked(node) || debugShowRequirements){
if(locked(node) || (debugShowRequirements && !net.client())){
desc.table(t -> {
t.left();
if(selectable){
if(net.client()){
desc.add("@locked").color(Pal.remove);
}else{
desc.table(t -> {
t.left();
if(selectable){
//check if there is any progress, add research progress text
if(Structs.contains(node.finishedRequirements, s -> s.amount > 0)){
float sum = 0f, used = 0f;
boolean shiny = false;
//check if there is any progress, add research progress text
if(Structs.contains(node.finishedRequirements, s -> s.amount > 0)){
float sum = 0f, used = 0f;
boolean shiny = false;
for(int i = 0; i < node.requirements.length; i++){
sum += node.requirements[i].item.cost * node.requirements[i].amount;
used += node.finishedRequirements[i].item.cost * node.finishedRequirements[i].amount;
if(shine != null) shiny |= shine[i];
}
for(int i = 0; i < node.requirements.length; i++){
sum += node.requirements[i].item.cost * node.requirements[i].amount;
used += node.finishedRequirements[i].item.cost * node.finishedRequirements[i].amount;
if(shine != null) shiny |= shine[i];
}
Label label = t.add(Core.bundle.format("research.progress", Math.min((int)(used / sum * 100), 99))).left().get();
if(shiny){
label.setColor(Pal.accent);
label.actions(Actions.color(Color.lightGray, 0.75f, Interp.fade));
}else{
label.setColor(Color.lightGray);
}
t.row();
}
for(int i = 0; i < node.requirements.length; i++){
ItemStack req = node.requirements[i];
ItemStack completed = node.finishedRequirements[i];
//skip finished stacks
if(req.amount <= completed.amount && !debugShowRequirements) continue;
boolean shiny = shine != null && shine[i];
t.table(list -> {
int reqAmount = debugShowRequirements ? req.amount : req.amount - completed.amount;
list.left();
list.image(req.item.uiIcon).size(8 * 3).padRight(3);
list.add(req.item.localizedName).color(Color.lightGray);
Label label = list.label(() -> " " +
UI.formatAmount(Math.min(items.get(req.item), reqAmount)) + " / "
+ UI.formatAmount(reqAmount)).get();
Color targetColor = items.has(req.item) ? Color.lightGray : Color.scarlet;
Label label = t.add(Core.bundle.format("research.progress", Math.min((int)(used / sum * 100), 99))).left().get();
if(shiny){
label.setColor(Pal.accent);
label.actions(Actions.color(targetColor, 0.75f, Interp.fade));
label.actions(Actions.color(Color.lightGray, 0.75f, Interp.fade));
}else{
label.setColor(targetColor);
label.setColor(Color.lightGray);
}
}).fillX().left();
t.row();
}
for(int i = 0; i < node.requirements.length; i++){
ItemStack req = node.requirements[i];
ItemStack completed = node.finishedRequirements[i];
//skip finished stacks
if(req.amount <= completed.amount && !debugShowRequirements) continue;
boolean shiny = shine != null && shine[i];
t.table(list -> {
int reqAmount = debugShowRequirements ? req.amount : req.amount - completed.amount;
list.left();
list.image(req.item.uiIcon).size(8 * 3).padRight(3);
list.add(req.item.localizedName).color(Color.lightGray);
Label label = list.label(() -> " " +
UI.formatAmount(Math.min(items.get(req.item), reqAmount)) + " / "
+ UI.formatAmount(reqAmount)).get();
Color targetColor = items.has(req.item) ? Color.lightGray : Color.scarlet;
if(shiny){
label.setColor(Pal.accent);
label.actions(Actions.color(targetColor, 0.75f, Interp.fade));
}else{
label.setColor(targetColor);
}
}).fillX().left();
t.row();
}
}else if(node.objectives.size > 0){
t.table(r -> {
r.add("@complete").colspan(2).left();
r.row();
for(Objective o : node.objectives){
if(o.complete()) continue;
r.add("> " + o.display()).color(Color.lightGray).left();
r.image(o.complete() ? Icon.ok : Icon.cancel, o.complete() ? Color.lightGray : Color.scarlet).padLeft(3);
r.row();
}
});
t.row();
}
}else if(node.objectives.size > 0){
t.table(r -> {
r.add("@complete").colspan(2).left();
r.row();
for(Objective o : node.objectives){
if(o.complete()) continue;
r.add("> " + o.display()).color(Color.lightGray).left();
r.image(o.complete() ? Icon.ok : Icon.cancel, o.complete() ? Color.lightGray : Color.scarlet).padLeft(3);
r.row();
}
});
t.row();
}
});
});
}
}else{
desc.add("@completed");
}
}).pad(9);
if(mobile && locked(node)){
if(mobile && locked(node) && !net.client()){
b.row();
b.button("@research", Icon.ok, new TextButtonStyle(){{
disabled = Tex.button;

View File

@@ -71,6 +71,13 @@ public class SchematicsDialog extends BaseDialog{
rebuildPane.run();
}).growX().get();
searchField.setMessageText("@schematic.search");
searchField.clicked(KeyCode.mouseRight, () -> {
if(!search.isEmpty()){
search = "";
searchField.clearText();
rebuildPane.run();
}
});
}).fillX().padBottom(4);
cont.row();

View File

@@ -333,6 +333,13 @@ public class SettingsMenuDialog extends BaseDialog{
game.checkPref("crashreport", true);
}
game.checkPref("communityservers", true, val -> {
defaultServers.clear();
if(val){
JoinDialog.fetchServers();
}
});
game.checkPref("savecreate", true);
game.checkPref("blockreplace", true);
game.checkPref("conveyorpathfinding", true);
@@ -390,6 +397,7 @@ public class SettingsMenuDialog extends BaseDialog{
}
return s + "%";
});
graphics.sliderPref("unitlaseropacity", 100, 0, 100, 5, s -> s + "%");
graphics.sliderPref("bridgeopacity", 100, 0, 100, 5, s -> s + "%");
if(!mobile){
@@ -445,6 +453,7 @@ public class SettingsMenuDialog extends BaseDialog{
graphics.checkPref("drawlight", true);
graphics.checkPref("destroyedblocks", true);
graphics.checkPref("blockstatus", false);
graphics.checkPref("displayselection", true);
graphics.checkPref("playerchat", true);
if(!mobile){
graphics.checkPref("coreitems", true);

View File

@@ -34,6 +34,8 @@ public class TraceDialog extends BaseDialog{
c.add(Core.bundle.format("trace.playername", player.name)).row();
c.button(Icon.copySmall, style, () -> copy(info.ip)).size(s).padRight(4f);
c.add(Core.bundle.format("trace.ip", info.ip)).row();
c.button(Icon.copySmall, style, () -> copy(info.locale)).size(s).padRight(4f);
c.add(Core.bundle.format("trace.language", info.locale)).row();
c.button(Icon.copySmall, style, () -> copy(info.uuid)).size(s).padRight(4f);
c.add(Core.bundle.format("trace.id", info.uuid)).row();
}).row();