Merge branch 'master' into port-field
This commit is contained in:
@@ -29,11 +29,19 @@ public class Bar extends Element{
|
||||
|
||||
public Bar(Prov<String> name, Prov<Color> color, Floatp fraction){
|
||||
this.fraction = fraction;
|
||||
lastValue = value = Mathf.clamp(fraction.get());
|
||||
try{
|
||||
lastValue = value = Mathf.clamp(fraction.get());
|
||||
}catch(Exception e){ //getting the fraction may involve referring to invalid data
|
||||
lastValue = value = 0f;
|
||||
}
|
||||
update(() -> {
|
||||
this.name = name.get();
|
||||
this.blinkColor.set(color.get());
|
||||
setColor(color.get());
|
||||
try{
|
||||
this.name = name.get();
|
||||
this.blinkColor.set(color.get());
|
||||
setColor(color.get());
|
||||
}catch(Exception e){ //getting the fraction may involve referring to invalid data
|
||||
this.name = "";
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -62,12 +70,25 @@ public class Bar extends Element{
|
||||
public void draw(){
|
||||
if(fraction == null) return;
|
||||
|
||||
float computed = Mathf.clamp(fraction.get());
|
||||
float computed;
|
||||
try{
|
||||
computed = Mathf.clamp(fraction.get());
|
||||
}catch(Exception e){ //getting the fraction may involve referring to invalid data
|
||||
computed = 0f;
|
||||
}
|
||||
|
||||
if(lastValue > computed){
|
||||
blink = 1f;
|
||||
lastValue = computed;
|
||||
}
|
||||
|
||||
if(Float.isNaN(lastValue)) lastValue = 0;
|
||||
if(Float.isInfinite(lastValue)) lastValue = 1f;
|
||||
if(Float.isNaN(value)) value = 0;
|
||||
if(Float.isInfinite(value)) value = 1f;
|
||||
if(Float.isNaN(computed)) computed = 0;
|
||||
if(Float.isInfinite(computed)) computed = 1f;
|
||||
|
||||
blink = Mathf.lerpDelta(blink, 0f, 0.2f);
|
||||
value = Mathf.lerpDelta(value, computed, 0.15f);
|
||||
|
||||
|
||||
@@ -2,9 +2,9 @@ package mindustry.ui;
|
||||
|
||||
import arc.graphics.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.scene.ui.Image;
|
||||
import arc.scene.ui.layout.Scl;
|
||||
import mindustry.graphics.Pal;
|
||||
import arc.scene.ui.*;
|
||||
import arc.scene.ui.layout.*;
|
||||
import mindustry.graphics.*;
|
||||
|
||||
public class BorderImage extends Image{
|
||||
public float thickness = 4f;
|
||||
|
||||
@@ -1,165 +0,0 @@
|
||||
package mindustry.ui;
|
||||
|
||||
import arc.*;
|
||||
import arc.struct.*;
|
||||
import arc.graphics.*;
|
||||
import arc.scene.ui.layout.*;
|
||||
import arc.util.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.meta.*;
|
||||
|
||||
public class ContentDisplay{
|
||||
|
||||
public static void displayBlock(Table table, Block block){
|
||||
|
||||
table.table(title -> {
|
||||
int size = 8 * 6;
|
||||
|
||||
title.image(block.icon(Cicon.xlarge)).size(size);
|
||||
title.add("[accent]" + block.localizedName).padLeft(5);
|
||||
});
|
||||
|
||||
table.row();
|
||||
|
||||
table.image().height(3).color(Color.lightGray).pad(8).padLeft(0).padRight(0).fillX();
|
||||
|
||||
table.row();
|
||||
|
||||
if(block.description != null){
|
||||
table.add(block.displayDescription()).padLeft(5).padRight(5).width(400f).wrap().fillX();
|
||||
table.row();
|
||||
|
||||
table.image().height(3).color(Color.lightGray).pad(8).padLeft(0).padRight(0).fillX();
|
||||
table.row();
|
||||
}
|
||||
|
||||
BlockStats stats = block.stats;
|
||||
|
||||
for(StatCategory cat : stats.toMap().keys()){
|
||||
OrderedMap<BlockStat, Seq<StatValue>> map = stats.toMap().get(cat);
|
||||
|
||||
if(map.size == 0) continue;
|
||||
|
||||
table.add("@category." + cat.name()).color(Pal.accent).fillX();
|
||||
table.row();
|
||||
|
||||
for(BlockStat stat : map.keys()){
|
||||
table.table(inset -> {
|
||||
inset.left();
|
||||
inset.add("[lightgray]" + stat.localized() + ":[] ").left();
|
||||
Seq<StatValue> arr = map.get(stat);
|
||||
for(StatValue value : arr){
|
||||
value.display(inset);
|
||||
inset.add().size(10f);
|
||||
}
|
||||
|
||||
}).fillX().padLeft(10);
|
||||
table.row();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void displayItem(Table table, Item item){
|
||||
|
||||
table.table(title -> {
|
||||
title.image(item.icon(Cicon.xlarge)).size(8 * 6);
|
||||
title.add("[accent]" + item.localizedName).padLeft(5);
|
||||
});
|
||||
|
||||
table.row();
|
||||
|
||||
table.image().height(3).color(Color.lightGray).pad(15).padLeft(0).padRight(0).fillX();
|
||||
|
||||
table.row();
|
||||
|
||||
if(item.description != null){
|
||||
table.add(item.displayDescription()).padLeft(5).padRight(5).width(400f).wrap().fillX();
|
||||
table.row();
|
||||
|
||||
table.image().height(3).color(Color.lightGray).pad(15).padLeft(0).padRight(0).fillX();
|
||||
table.row();
|
||||
}
|
||||
|
||||
table.left().defaults().fillX();
|
||||
|
||||
table.add(Core.bundle.format("item.explosiveness", (int)(item.explosiveness * 100)));
|
||||
table.row();
|
||||
table.add(Core.bundle.format("item.flammability", (int)(item.flammability * 100)));
|
||||
table.row();
|
||||
table.add(Core.bundle.format("item.radioactivity", (int)(item.radioactivity * 100)));
|
||||
table.row();
|
||||
}
|
||||
|
||||
public static void displayLiquid(Table table, Liquid liquid){
|
||||
|
||||
table.table(title -> {
|
||||
title.image(liquid.icon(Cicon.xlarge)).size(8 * 6);
|
||||
title.add("[accent]" + liquid.localizedName).padLeft(5);
|
||||
});
|
||||
|
||||
table.row();
|
||||
|
||||
table.image().height(3).color(Color.lightGray).pad(15).padLeft(0).padRight(0).fillX();
|
||||
|
||||
table.row();
|
||||
|
||||
if(liquid.description != null){
|
||||
table.add(liquid.displayDescription()).padLeft(5).padRight(5).width(400f).wrap().fillX();
|
||||
table.row();
|
||||
|
||||
table.image().height(3).color(Color.lightGray).pad(15).padLeft(0).padRight(0).fillX();
|
||||
table.row();
|
||||
}
|
||||
|
||||
table.left().defaults().fillX();
|
||||
|
||||
table.add(Core.bundle.format("item.explosiveness", (int)(liquid.explosiveness * 100)));
|
||||
table.row();
|
||||
table.add(Core.bundle.format("item.flammability", (int)(liquid.flammability * 100)));
|
||||
table.row();
|
||||
table.add(Core.bundle.format("liquid.heatcapacity", (int)(liquid.heatCapacity * 100)));
|
||||
table.row();
|
||||
table.add(Core.bundle.format("liquid.temperature", (int)(liquid.temperature * 100)));
|
||||
table.row();
|
||||
table.add(Core.bundle.format("liquid.viscosity", (int)(liquid.viscosity * 100)));
|
||||
table.row();
|
||||
}
|
||||
|
||||
public static void displayUnit(Table table, UnitType unit){
|
||||
table.table(title -> {
|
||||
title.image(unit.icon(Cicon.xlarge)).size(8 * 6).scaling(Scaling.fit);
|
||||
title.add("[accent]" + unit.localizedName).padLeft(5);
|
||||
});
|
||||
|
||||
table.row();
|
||||
|
||||
table.image().height(3).color(Color.lightGray).pad(15).padLeft(0).padRight(0).fillX();
|
||||
|
||||
table.row();
|
||||
|
||||
if(unit.description != null){
|
||||
table.add(unit.displayDescription()).padLeft(5).padRight(5).width(400f).wrap().fillX();
|
||||
table.row();
|
||||
|
||||
table.image().height(3).color(Color.lightGray).pad(15).padLeft(0).padRight(0).fillX();
|
||||
table.row();
|
||||
}
|
||||
|
||||
table.left().defaults().fillX();
|
||||
|
||||
Unit inst = unit.constructor.get();
|
||||
|
||||
//TODO more stats
|
||||
table.add(Core.bundle.format("unit.health", unit.health)).row();
|
||||
table.add(Core.bundle.format("unit.speed", Strings.fixed(unit.speed, 1))).row();
|
||||
table.add(Core.bundle.format("unit.itemcapacity", unit.itemCapacity)).row();
|
||||
|
||||
if(inst instanceof Minerc) table.add(Core.bundle.format("unit.minespeed", (int)(unit.mineSpeed * 100f))).row();
|
||||
if(inst instanceof Builderc) table.add(Core.bundle.format("unit.buildspeed", (int)(unit.buildSpeed * 100f))).row();
|
||||
|
||||
table.row();
|
||||
}
|
||||
}
|
||||
@@ -16,27 +16,38 @@ import arc.graphics.g2d.*;
|
||||
import arc.graphics.g2d.Font.*;
|
||||
import arc.graphics.g2d.PixmapPacker.*;
|
||||
import arc.graphics.g2d.TextureAtlas.*;
|
||||
import arc.math.*;
|
||||
import arc.math.geom.*;
|
||||
import arc.scene.style.*;
|
||||
import arc.scene.ui.layout.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import mindustry.*;
|
||||
import mindustry.core.*;
|
||||
import mindustry.ctype.*;
|
||||
import mindustry.gen.*;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class Fonts{
|
||||
private static final String mainFont = "fonts/font.woff";
|
||||
private static final ObjectSet<String> unscaled = ObjectSet.with("iconLarge");
|
||||
private static ObjectIntMap<String> unicodeIcons = new ObjectIntMap<>();
|
||||
private static ObjectMap<String, String> stringIcons = new ObjectMap<>();
|
||||
private static ObjectMap<String, TextureRegion> largeIcons = new ObjectMap<>();
|
||||
private static TextureRegion[] iconTable;
|
||||
private static int lastCid;
|
||||
|
||||
public static Font def;
|
||||
public static Font outline;
|
||||
public static Font chat;
|
||||
public static Font icon;
|
||||
public static Font iconLarge;
|
||||
public static Font tech;
|
||||
|
||||
public static TextureRegion logicIcon(int id){
|
||||
return iconTable[id];
|
||||
}
|
||||
|
||||
public static int getUnicode(String content){
|
||||
return unicodeIcons.get(content, 0);
|
||||
}
|
||||
@@ -55,10 +66,11 @@ public class Fonts{
|
||||
}
|
||||
|
||||
public static int cursorScale(){
|
||||
return Math.max(1, Mathf.round(Scl.scl(1f)));
|
||||
return 1;
|
||||
}
|
||||
|
||||
public static void loadFonts(){
|
||||
largeIcons.clear();
|
||||
FreeTypeFontParameter param = fontParameter();
|
||||
|
||||
Core.assets.load("default", Font.class, new FreeTypeFontLoaderParameter(mainFont, param)).loaded = f -> Fonts.def = (Font)f;
|
||||
@@ -68,6 +80,25 @@ public class Fonts{
|
||||
incremental = true;
|
||||
characters = "\0";
|
||||
}})).loaded = f -> Fonts.icon = (Font)f;
|
||||
Core.assets.load("iconLarge", Font.class, new FreeTypeFontLoaderParameter("fonts/icon.ttf", new FreeTypeFontParameter(){{
|
||||
size = 48;
|
||||
incremental = false;
|
||||
characters = "\0" + Iconc.all;
|
||||
borderWidth = 5f;
|
||||
borderColor = Color.darkGray;
|
||||
}})).loaded = f -> Fonts.iconLarge = (Font)f;
|
||||
}
|
||||
|
||||
public static TextureRegion getLargeIcon(String name){
|
||||
return largeIcons.get(name, () -> {
|
||||
var region = new TextureRegion();
|
||||
int code = Iconc.codes.get(name, '\uF8D4');
|
||||
var glyph = iconLarge.getData().getGlyph((char)code);
|
||||
if(glyph == null) return Core.atlas.find("error");
|
||||
region.set(iconLarge.getRegion().texture);
|
||||
region.set(glyph.u, glyph.v2, glyph.u2, glyph.v);
|
||||
return region;
|
||||
});
|
||||
}
|
||||
|
||||
public static void loadContentIcons(){
|
||||
@@ -97,7 +128,7 @@ public class Fonts{
|
||||
glyph.srcX = 0;
|
||||
glyph.srcY = 0;
|
||||
glyph.width = size;
|
||||
glyph.height = size;
|
||||
glyph.height = (int)((float)region.height / region.width * size);
|
||||
glyph.u = region.u;
|
||||
glyph.v = region.v2;
|
||||
glyph.u2 = region.u2;
|
||||
@@ -111,6 +142,19 @@ public class Fonts{
|
||||
fonts.each(f -> f.getData().setGlyph(ch, glyph));
|
||||
}
|
||||
}
|
||||
|
||||
iconTable = new TextureRegion[512];
|
||||
iconTable[0] = Core.atlas.find("error");
|
||||
lastCid = 1;
|
||||
|
||||
Vars.content.each(c -> {
|
||||
if(c instanceof UnlockableContent u){
|
||||
TextureRegion region = Core.atlas.find(u.name + "-icon-logic");
|
||||
if(region.found()){
|
||||
iconTable[u.iconId = lastCid++] = region;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/** Called from a static context for use in the loading screen.*/
|
||||
@@ -129,7 +173,8 @@ public class Fonts{
|
||||
parameter.fontParameters.borderWidth = Scl.scl(2f);
|
||||
parameter.fontParameters.spaceX -= parameter.fontParameters.borderWidth;
|
||||
}
|
||||
if(!scaled.contains(parameter.fontParameters)){
|
||||
|
||||
if(!scaled.contains(parameter.fontParameters) && !unscaled.contains(fileName)){
|
||||
parameter.fontParameters.size = (int)(Scl.scl(parameter.fontParameters.size));
|
||||
scaled.add(parameter.fontParameters);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package mindustry.ui;
|
||||
|
||||
import arc.graphics.g2d.Fill;
|
||||
import arc.scene.Element;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.scene.*;
|
||||
|
||||
public class GridImage extends Element{
|
||||
private int imageWidth, imageHeight;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package mindustry.ui;
|
||||
|
||||
|
||||
import arc.Core;
|
||||
import arc.func.Func;
|
||||
import arc.*;
|
||||
import arc.func.*;
|
||||
|
||||
/**
|
||||
* A low-garbage way to format bundle strings.
|
||||
@@ -10,7 +10,7 @@ import arc.func.Func;
|
||||
public class IntFormat{
|
||||
private final StringBuilder builder = new StringBuilder();
|
||||
private final String text;
|
||||
private int lastValue = Integer.MIN_VALUE;
|
||||
private int lastValue = Integer.MIN_VALUE, lastValue2 = Integer.MIN_VALUE;
|
||||
private Func<Integer, String> converter = String::valueOf;
|
||||
|
||||
public IntFormat(String text){
|
||||
@@ -30,4 +30,14 @@ public class IntFormat{
|
||||
lastValue = value;
|
||||
return builder;
|
||||
}
|
||||
|
||||
public CharSequence get(int value1, int value2){
|
||||
if(lastValue != value1 || lastValue2 != value2){
|
||||
builder.setLength(0);
|
||||
builder.append(Core.bundle.format(text, value1, value2));
|
||||
}
|
||||
lastValue = value1;
|
||||
lastValue2 = value2;
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
package mindustry.ui;
|
||||
|
||||
import arc.scene.ui.layout.Table;
|
||||
import mindustry.type.Item;
|
||||
import mindustry.type.ItemStack;
|
||||
import arc.scene.ui.layout.*;
|
||||
import mindustry.type.*;
|
||||
|
||||
/** An item image with text. */
|
||||
public class ItemDisplay extends Table{
|
||||
|
||||
@@ -3,6 +3,7 @@ package mindustry.ui;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.scene.ui.*;
|
||||
import arc.scene.ui.layout.*;
|
||||
import mindustry.core.*;
|
||||
import mindustry.type.*;
|
||||
|
||||
public class ItemImage extends Stack{
|
||||
@@ -16,7 +17,7 @@ public class ItemImage extends Stack{
|
||||
|
||||
add(new Table(t -> {
|
||||
t.left().bottom();
|
||||
t.add(amount + "");
|
||||
t.add(amount > 1000 ? UI.formatAmount(amount) : amount + "");
|
||||
t.pack();
|
||||
}));
|
||||
}
|
||||
@@ -38,7 +39,7 @@ public class ItemImage extends Stack{
|
||||
if(stack.amount != 0){
|
||||
add(new Table(t -> {
|
||||
t.left().bottom();
|
||||
t.add(stack.amount + "").style(Styles.outlineLabel);
|
||||
t.add(stack.amount > 1000 ? UI.formatAmount(stack.amount) : stack.amount + "").style(Styles.outlineLabel);
|
||||
t.pack();
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import arc.math.*;
|
||||
import arc.scene.actions.*;
|
||||
import arc.scene.ui.*;
|
||||
import arc.scene.ui.layout.*;
|
||||
import arc.util.ArcAnnotate.*;
|
||||
import arc.util.*;
|
||||
import mindustry.core.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
|
||||
@@ -1,28 +1,32 @@
|
||||
package mindustry.ui;
|
||||
|
||||
import arc.Core;
|
||||
import arc.*;
|
||||
import arc.graphics.*;
|
||||
import arc.scene.style.*;
|
||||
import arc.util.Strings;
|
||||
import arc.graphics.Color;
|
||||
import arc.util.*;
|
||||
import mindustry.*;
|
||||
import mindustry.core.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.Pal;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.mod.Mods.*;
|
||||
|
||||
public class Links{
|
||||
private static LinkEntry[] links;
|
||||
|
||||
private static void createLinks(){
|
||||
links = new LinkEntry[]{
|
||||
new LinkEntry("discord", "https://discord.gg/mindustry", Icon.discord, Color.valueOf("7289da")),
|
||||
new LinkEntry("changelog", "https://github.com/Anuken/Mindustry/releases", Icon.list, Pal.accent.cpy()),
|
||||
new LinkEntry("trello", "https://trello.com/b/aE2tcUwF", Icon.trello, Color.valueOf("026aa7")),
|
||||
new LinkEntry("wiki", "https://mindustrygame.github.io/wiki/", Icon.book, Color.valueOf("0f142f")),
|
||||
new LinkEntry("suggestions", "https://github.com/Anuken/Mindustry-Suggestions/issues/new/choose/", Icon.add, Color.valueOf("ebebeb")),
|
||||
new LinkEntry("reddit", "https://www.reddit.com/r/Mindustry/", Icon.redditAlien, Color.valueOf("ee593b")),
|
||||
new LinkEntry("itch.io", "https://anuke.itch.io/mindustry", Icon.itchio, Color.valueOf("fa5c5c")),
|
||||
new LinkEntry("google-play", "https://play.google.com/store/apps/details?id=io.anuke.mindustry", Icon.googleplay, Color.valueOf("689f38")),
|
||||
new LinkEntry("f-droid", "https://f-droid.org/packages/io.anuke.mindustry/", Icon.android, Color.valueOf("026aa7")),
|
||||
new LinkEntry("github", "https://github.com/Anuken/Mindustry/", Icon.github, Color.valueOf("24292e")),
|
||||
new LinkEntry("dev-builds", "https://github.com/Anuken/MindustryBuilds", Icon.githubSquare, Color.valueOf("fafbfc"))
|
||||
new LinkEntry("discord", "https://discord.gg/mindustry", Icon.discord, Color.valueOf("7289da")),
|
||||
new LinkEntry("changelog", "https://github.com/Anuken/Mindustry/releases", Icon.list, Pal.accent.cpy()),
|
||||
new LinkEntry("trello", "https://trello.com/b/aE2tcUwF", Icon.trello, Color.valueOf("026aa7")),
|
||||
new LinkEntry("wiki", "https://mindustrygame.github.io/wiki/", Icon.book, Color.valueOf("0f142f")),
|
||||
new LinkEntry("suggestions", "https://github.com/Anuken/Mindustry-Suggestions/issues/new/choose/", Icon.add, Color.valueOf("ebebeb")),
|
||||
new LinkEntry("reddit", "https://www.reddit.com/r/Mindustry/", Icon.redditAlien, Color.valueOf("ee593b")),
|
||||
new LinkEntry("itch.io", "https://anuke.itch.io/mindustry", Icon.itchio, Color.valueOf("fa5c5c")),
|
||||
new LinkEntry("google-play", "https://play.google.com/store/apps/details?id=io.anuke.mindustry", Icon.googleplay, Color.valueOf("689f38")),
|
||||
new LinkEntry("f-droid", "https://f-droid.org/packages/io.anuke.mindustry/", Icon.android, Color.valueOf("026aa7")),
|
||||
new LinkEntry("github", "https://github.com/Anuken/Mindustry/", Icon.github, Color.valueOf("24292e")),
|
||||
new LinkEntry("dev-builds", "https://github.com/Anuken/MindustryBuilds", Icon.githubSquare, Color.valueOf("fafbfc")),
|
||||
new LinkEntry("bug", report(), Icon.wrench, Color.valueOf("cbd97f"))
|
||||
};
|
||||
}
|
||||
|
||||
@@ -48,4 +52,23 @@ public class Links{
|
||||
this.title = Core.bundle.get("link." + name + ".title", Strings.capitalize(name.replace("-", " ")));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static String report(){
|
||||
return "https://github.com/Anuken/Mindustry/issues/new?assignees=&labels=bug&body=" +
|
||||
Strings.encode(Strings.format(
|
||||
"**Platform**: `@`\n" +
|
||||
"\n**Build**: `@`\n" +
|
||||
"\n**Issue**: *Explain your issue in detail.*\n" +
|
||||
"\n**Steps to reproduce**: *How you happened across the issue, and what exactly you did to make the bug happen.*\n" +
|
||||
"\n**Link(s) to mod(s) used**: `@`\n" +
|
||||
"\n**Save file**: *The (zipped) save file you were playing on when the bug happened. THIS IS REQUIRED FOR ANY ISSUE HAPPENING IN-GAME, REGARDLESS OF WHETHER YOU THINK IT HAPPENS EVERYWHERE. DO NOT DELETE OR OMIT THIS LINE UNLESS YOU ARE SURE THAT THE ISSUE DOES NOT HAPPEN IN-GAME.*\n" +
|
||||
"\n**Crash report**: *The contents of relevant crash report files. REQUIRED if you are reporting a crash.*\n" +
|
||||
"\n---\n" +
|
||||
"\n*Place an X (no spaces) between the brackets to confirm that you have read the line below.*" +
|
||||
"\n- [ ] **I have updated to the latest release (https://github.com/Anuken/Mindustry/releases) to make sure my issue has not been fixed.**" +
|
||||
"\n- [ ] **I have searched the closed and open issues to make sure that this problem has not already been reported.**",
|
||||
OS.isAndroid ? "Android " + Core.app.getVersion() : (System.getProperty("os.name") + (OS.is64Bit ? " x64" : " x32")),
|
||||
Version.combined(),
|
||||
Vars.mods.list().any() ? Vars.mods.list().select(LoadedMod::enabled).map(l -> l.meta.author + "/" + l.name + ":" + l.meta.version) : "none"));
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,11 @@
|
||||
package mindustry.ui;
|
||||
|
||||
import arc.graphics.Color;
|
||||
import arc.scene.ui.Image;
|
||||
import arc.scene.ui.layout.Stack;
|
||||
import arc.scene.ui.layout.Table;
|
||||
import arc.util.Strings;
|
||||
import mindustry.type.Liquid;
|
||||
import mindustry.world.meta.StatUnit;
|
||||
import arc.graphics.*;
|
||||
import arc.scene.ui.*;
|
||||
import arc.scene.ui.layout.*;
|
||||
import arc.util.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.world.meta.*;
|
||||
|
||||
/** An ItemDisplay, but for liquids. */
|
||||
public class LiquidDisplay extends Table{
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package mindustry.ui;
|
||||
|
||||
import arc.Core;
|
||||
import arc.graphics.g2d.Draw;
|
||||
import arc.input.KeyCode;
|
||||
import arc.scene.Element;
|
||||
import arc.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.input.*;
|
||||
import arc.scene.*;
|
||||
import arc.scene.event.*;
|
||||
import arc.scene.ui.layout.*;
|
||||
import mindustry.gen.*;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package mindustry.ui;
|
||||
|
||||
import arc.scene.style.*;
|
||||
import arc.scene.ui.ImageButton;
|
||||
import arc.util.Align;
|
||||
import arc.scene.ui.*;
|
||||
import arc.util.*;
|
||||
|
||||
public class MobileButton extends ImageButton{
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package mindustry.ui;
|
||||
|
||||
import arc.struct.Seq;
|
||||
import arc.scene.ui.layout.Stack;
|
||||
import arc.util.Time;
|
||||
import arc.scene.ui.layout.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.*;
|
||||
|
||||
public class MultiReqImage extends Stack{
|
||||
private Seq<ReqImage> displays = new Seq<>();
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
package mindustry.ui;
|
||||
|
||||
import arc.func.Boolp;
|
||||
import arc.func.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.scene.Element;
|
||||
import arc.scene.ui.Image;
|
||||
import arc.scene.ui.layout.Stack;
|
||||
import arc.scene.ui.layout.Scl;
|
||||
import mindustry.graphics.Pal;
|
||||
import arc.scene.*;
|
||||
import arc.scene.ui.*;
|
||||
import arc.scene.ui.layout.*;
|
||||
import mindustry.graphics.*;
|
||||
|
||||
public class ReqImage extends Stack{
|
||||
private final Boolp valid;
|
||||
|
||||
62
core/src/mindustry/ui/SearchBar.java
Normal file
62
core/src/mindustry/ui/SearchBar.java
Normal file
@@ -0,0 +1,62 @@
|
||||
package mindustry.ui;
|
||||
|
||||
import arc.func.*;
|
||||
import arc.graphics.*;
|
||||
import arc.scene.ui.layout.*;
|
||||
import arc.struct.*;
|
||||
import mindustry.gen.*;
|
||||
|
||||
public class SearchBar{
|
||||
|
||||
public static <T> Table add(Table parent, Seq<T> list, Func<String, String> queryf,
|
||||
Func<T, String> namef, Cons2<Table, T> itemc, boolean show){
|
||||
Table[] pane = {null};
|
||||
|
||||
Cons<String> rebuild = str -> {
|
||||
String query = queryf.get(str);
|
||||
|
||||
pane[0].clear();
|
||||
boolean any = false;
|
||||
for(T item : list){
|
||||
if(query.isEmpty() || matches(query, namef.get(item))){
|
||||
any = true;
|
||||
itemc.get(pane[0], item);
|
||||
}
|
||||
}
|
||||
|
||||
if(!any){
|
||||
pane[0].add("@none.found").color(Color.lightGray).pad(4);
|
||||
}
|
||||
};
|
||||
|
||||
if(show){
|
||||
parent.table(search -> {
|
||||
search.image(Icon.zoom).padRight(8f);
|
||||
search.field("", rebuild).growX();
|
||||
}).fillX().padBottom(4);
|
||||
}
|
||||
|
||||
parent.row();
|
||||
parent.pane(table -> {
|
||||
pane[0] = table;
|
||||
rebuild.get("");
|
||||
});
|
||||
return pane[0];
|
||||
}
|
||||
|
||||
public static <T> Table add(Table parent, Seq<T> list, Func<String, String> queryf, Func<T, String> namef, Cons2<Table, T> itemc){
|
||||
return add(parent, list, queryf, namef, itemc, true);
|
||||
}
|
||||
|
||||
public static <T> Table add(Table parent, Seq<T> list, Func<T, String> namef, Cons2<Table, T> itemc, boolean show){
|
||||
return add(parent, list, String::toLowerCase, namef, itemc, show);
|
||||
}
|
||||
|
||||
/** Match a list item with the search query, case insensitive */
|
||||
public static boolean matches(String query, String name){
|
||||
if(name == null || name.isEmpty()){
|
||||
return false;
|
||||
}
|
||||
return name.toLowerCase().contains(query);
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
package mindustry.ui;
|
||||
|
||||
import arc.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import arc.graphics.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.graphics.g2d.TextureAtlas.*;
|
||||
@@ -16,6 +15,7 @@ import arc.scene.ui.ScrollPane.*;
|
||||
import arc.scene.ui.Slider.*;
|
||||
import arc.scene.ui.TextButton.*;
|
||||
import arc.scene.ui.TextField.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
|
||||
@@ -23,10 +23,11 @@ import static mindustry.gen.Tex.*;
|
||||
|
||||
@StyleDefaults
|
||||
public class Styles{
|
||||
//TODO all these names are inconsistent and not descriptive
|
||||
public static Drawable black, black9, black8, black6, black3, black5, none, flatDown, flatOver;
|
||||
public static ButtonStyle defaultb, waveb;
|
||||
public static TextButtonStyle defaultt, squaret, nodet, cleart, discordt, infot, clearPartialt, clearTogglet, clearToggleMenut, togglet, transt, fullTogglet, logict;
|
||||
public static ImageButtonStyle defaulti, nodei, righti, emptyi, emptytogglei, selecti, logici, geni, colori, cleari, clearFulli, clearPartiali, clearPartial2i, clearTogglei, clearTransi, clearToggleTransi, clearTogglePartiali;
|
||||
public static ButtonStyle defaultb, waveb, modsb;
|
||||
public static TextButtonStyle defaultt, squaret, nodet, cleart, discordt, nonet, infot, clearPartialt, clearTogglet, clearToggleMenut, togglet, transt, fullTogglet, logict;
|
||||
public static ImageButtonStyle defaulti, nodei, righti, emptyi, emptytogglei, selecti, logici, geni, colori, accenti, cleari, clearFulli, clearPartiali, clearPartial2i, clearTogglei, clearTransi, clearToggleTransi, clearTogglePartiali;
|
||||
public static ScrollPaneStyle defaultPane, horizontalPane, smallPane;
|
||||
public static KeybindDialogStyle defaultKeybindDialog;
|
||||
public static SliderStyle defaultSlider, vSlider;
|
||||
@@ -52,6 +53,12 @@ public class Styles{
|
||||
over = buttonOver;
|
||||
disabled = buttonDisabled;
|
||||
}};
|
||||
|
||||
modsb = new ButtonStyle(){{
|
||||
down = flatOver;
|
||||
up = underline;
|
||||
over = underlineWhite;
|
||||
}};
|
||||
|
||||
waveb = new ButtonStyle(){{
|
||||
up = wavepane;
|
||||
@@ -85,6 +92,13 @@ public class Styles{
|
||||
up = buttonOver;
|
||||
over = buttonDown;
|
||||
}};
|
||||
nonet = new TextButtonStyle(){{
|
||||
font = Fonts.outline;
|
||||
fontColor = Color.lightGray;
|
||||
overFontColor = Pal.accent;
|
||||
disabledFontColor = Color.gray;
|
||||
up = none;
|
||||
}};
|
||||
cleart = new TextButtonStyle(){{
|
||||
over = flatOver;
|
||||
font = Fonts.def;
|
||||
@@ -212,6 +226,11 @@ public class Styles{
|
||||
//imageDownColor = Pal.accent;
|
||||
imageUpColor = Color.white;
|
||||
}};
|
||||
accenti = new ImageButtonStyle(){{
|
||||
//imageDownColor = Pal.accent;
|
||||
imageUpColor = Color.lightGray;
|
||||
imageDownColor = Color.white;
|
||||
}};
|
||||
cleari = new ImageButtonStyle(){{
|
||||
down = flatOver;
|
||||
up = black;
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
package mindustry.ui.dialogs;
|
||||
|
||||
import arc.*;
|
||||
import arc.struct.*;
|
||||
import arc.graphics.*;
|
||||
import arc.scene.ui.*;
|
||||
import arc.scene.ui.layout.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import mindustry.game.EventType.*;
|
||||
import mindustry.gen.*;
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package mindustry.ui.dialogs;
|
||||
|
||||
import arc.*;
|
||||
import arc.input.*;
|
||||
import arc.scene.ui.*;
|
||||
import arc.util.*;
|
||||
import mindustry.core.GameState.*;
|
||||
@@ -53,15 +52,15 @@ public class BaseDialog extends Dialog{
|
||||
});
|
||||
}
|
||||
|
||||
public void addCloseListener(){
|
||||
closeOnBack();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addCloseButton(){
|
||||
buttons.defaults().size(210f, 64f);
|
||||
buttons.button("@back", Icon.left, this::hide).size(210f, 64f);
|
||||
|
||||
keyDown(key -> {
|
||||
if(key == KeyCode.escape || key == KeyCode.back){
|
||||
Core.app.post(this::hide);
|
||||
}
|
||||
});
|
||||
addCloseListener();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
package mindustry.ui.dialogs;
|
||||
|
||||
import arc.scene.ui.ScrollPane;
|
||||
import arc.scene.ui.layout.Table;
|
||||
import mindustry.ctype.UnlockableContent;
|
||||
import arc.scene.ui.*;
|
||||
import arc.scene.ui.layout.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import mindustry.ctype.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.ui.*;
|
||||
import mindustry.world.meta.*;
|
||||
|
||||
public class ContentInfoDialog extends BaseDialog{
|
||||
|
||||
@@ -18,11 +23,72 @@ public class ContentInfoDialog extends BaseDialog{
|
||||
Table table = new Table();
|
||||
table.margin(10);
|
||||
|
||||
content.displayInfo(table);
|
||||
//initialize stats if they haven't been yet
|
||||
content.checkStats();
|
||||
|
||||
table.table(title1 -> {
|
||||
int size = 8 * 6;
|
||||
|
||||
title1.image(content.icon(Cicon.xlarge)).size(size).scaling(Scaling.fit);
|
||||
title1.add("[accent]" + content.localizedName).padLeft(5);
|
||||
});
|
||||
|
||||
table.row();
|
||||
|
||||
if(content.description != null){
|
||||
var any = content.stats.toMap().size > 0;
|
||||
|
||||
if(any){
|
||||
table.add("@category.purpose").color(Pal.accent).fillX().padTop(10);
|
||||
table.row();
|
||||
}
|
||||
|
||||
table.add("[lightgray]" + content.displayDescription()).wrap().fillX().padLeft(any ? 10 : 0).width(500f).padTop(any ? 0 : 10).left();
|
||||
table.row();
|
||||
|
||||
if(!content.stats.useCategories && any){
|
||||
table.add("@category.general").fillX().color(Pal.accent);
|
||||
table.row();
|
||||
}
|
||||
}
|
||||
|
||||
Stats stats = content.stats;
|
||||
|
||||
for(StatCat cat : stats.toMap().keys()){
|
||||
OrderedMap<Stat, Seq<StatValue>> map = stats.toMap().get(cat);
|
||||
|
||||
if(map.size == 0) continue;
|
||||
|
||||
//TODO check
|
||||
if(stats.useCategories){
|
||||
table.add("@category." + cat.name()).color(Pal.accent).fillX();
|
||||
table.row();
|
||||
}
|
||||
|
||||
for(Stat stat : map.keys()){
|
||||
table.table(inset -> {
|
||||
inset.left();
|
||||
inset.add("[lightgray]" + stat.localized() + ":[] ").left();
|
||||
Seq<StatValue> arr = map.get(stat);
|
||||
for(StatValue value : arr){
|
||||
value.display(inset);
|
||||
inset.add().size(10f);
|
||||
}
|
||||
|
||||
}).fillX().padLeft(10);
|
||||
table.row();
|
||||
}
|
||||
}
|
||||
|
||||
if(content.details != null){
|
||||
table.add("[gray]" + content.details).pad(6).padTop(20).width(400f).wrap().fillX();
|
||||
table.row();
|
||||
}
|
||||
|
||||
ScrollPane pane = new ScrollPane(table);
|
||||
cont.add(pane);
|
||||
|
||||
show();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -24,8 +24,8 @@ import static arc.util.Time.*;
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class CustomRulesDialog extends BaseDialog{
|
||||
private Table main;
|
||||
Rules rules;
|
||||
private Table main;
|
||||
private Prov<Rules> resetter;
|
||||
private LoadoutDialog loadoutDialog;
|
||||
private BaseDialog banDialog;
|
||||
@@ -134,15 +134,15 @@ public class CustomRulesDialog extends BaseDialog{
|
||||
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);
|
||||
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.schematic", b -> rules.schematicsAllowed = b, () -> rules.schematicsAllowed);
|
||||
number("@rules.buildcostmultiplier", false, f -> rules.buildCostMultiplier = f, () -> rules.buildCostMultiplier, () -> !rules.infiniteResources);
|
||||
number("@rules.buildspeedmultiplier", f -> rules.buildSpeedMultiplier = f, () -> rules.buildSpeedMultiplier);
|
||||
number("@rules.buildspeedmultiplier", f -> rules.buildSpeedMultiplier = f, () -> rules.buildSpeedMultiplier, 0.00001f, 10000f);
|
||||
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);
|
||||
@@ -162,17 +162,18 @@ public class CustomRulesDialog extends BaseDialog{
|
||||
check("@rules.unitammo", b -> rules.unitAmmo = b, () -> rules.unitAmmo);
|
||||
number("@rules.unithealthmultiplier", f -> rules.unitHealthMultiplier = f, () -> rules.unitHealthMultiplier);
|
||||
number("@rules.unitdamagemultiplier", f -> rules.unitDamageMultiplier = f, () -> rules.unitDamageMultiplier);
|
||||
number("@rules.unitbuildspeedmultiplier", f -> rules.unitBuildSpeedMultiplier = f, () -> rules.unitBuildSpeedMultiplier);
|
||||
number("@rules.unitbuildspeedmultiplier", f -> rules.unitBuildSpeedMultiplier = f, () -> rules.unitBuildSpeedMultiplier, 0.00001f, 100f);
|
||||
|
||||
title("@rules.title.enemy");
|
||||
check("@rules.attack", b -> rules.attackMode = b, () -> rules.attackMode);
|
||||
check("@rules.buildai", b -> rules.waveTeam.rules().ai = b, () -> rules.waveTeam.rules().ai);
|
||||
check("@rules.buildai", b -> rules.teams.get(rules.waveTeam).ai = rules.teams.get(rules.waveTeam).infiniteResources = b, () -> rules.teams.get(rules.waveTeam).ai);
|
||||
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();
|
||||
@@ -185,15 +186,25 @@ public class CustomRulesDialog extends BaseDialog{
|
||||
}, () -> ui.picker.show(rules.ambientLight, rules.ambientLight::set)).left().width(250f).row();
|
||||
|
||||
main.button("@rules.weather", this::weatherDialog).width(250f).left().row();
|
||||
|
||||
//TODO add weather patterns
|
||||
}
|
||||
|
||||
void number(String text, Floatc cons, Floatp prov){
|
||||
number(text, false, cons, prov, () -> true);
|
||||
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, Floatc cons, Floatp prov, Boolp condition, float min, float max){
|
||||
main.table(t -> {
|
||||
t.left();
|
||||
t.add(text).left().padRight(5)
|
||||
@@ -201,7 +212,7 @@ public class CustomRulesDialog extends BaseDialog{
|
||||
t.field((integer ? (int)prov.get() : prov.get()) + "", s -> cons.get(Strings.parseFloat(s)))
|
||||
.padRight(100f)
|
||||
.update(a -> a.setDisabled(!condition.get()))
|
||||
.valid(Strings::canParsePositiveFloat).width(120f).left().addInputDialog();
|
||||
.valid(f -> Strings.canParsePositiveFloat(f) && Strings.parseFloat(f) >= min && Strings.parseFloat(f) <= max).width(120f).left().addInputDialog();
|
||||
}).padTop(0);
|
||||
main.row();
|
||||
}
|
||||
@@ -273,19 +284,23 @@ public class CustomRulesDialog extends BaseDialog{
|
||||
f.defaults().padRight(4).left();
|
||||
|
||||
f.add("@rules.weather.duration");
|
||||
field(f, entry.minDuration / toMinutes, v -> entry.minDuration = v * toMinutes);
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
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();
|
||||
@@ -303,8 +318,8 @@ public class CustomRulesDialog extends BaseDialog{
|
||||
dialog.addCloseButton();
|
||||
|
||||
dialog.buttons.button("@add", Icon.add, () -> {
|
||||
BaseDialog addd = new BaseDialog("@add");
|
||||
addd.cont.pane(t -> {
|
||||
BaseDialog add = new BaseDialog("@add");
|
||||
add.cont.pane(t -> {
|
||||
t.background(Tex.button);
|
||||
int i = 0;
|
||||
for(Weather weather : content.<Weather>getBy(ContentType.weather)){
|
||||
@@ -313,13 +328,13 @@ public class CustomRulesDialog extends BaseDialog{
|
||||
rules.weather.add(new WeatherEntry(weather));
|
||||
rebuild[0].run();
|
||||
|
||||
addd.hide();
|
||||
add.hide();
|
||||
}).size(140f, 50f);
|
||||
if(++i % 2 == 0) t.row();
|
||||
}
|
||||
});
|
||||
addd.addCloseButton();
|
||||
addd.show();
|
||||
add.addCloseButton();
|
||||
add.show();
|
||||
}).width(170f);
|
||||
|
||||
//reset cooldown to random number
|
||||
|
||||
@@ -40,7 +40,7 @@ public class DatabaseDialog extends BaseDialog{
|
||||
for(int j = 0; j < allContent.length; j++){
|
||||
ContentType type = ContentType.all[j];
|
||||
|
||||
Seq<Content> array = allContent[j].select(c -> c instanceof UnlockableContent && !((UnlockableContent)c).isHidden());
|
||||
Seq<Content> array = allContent[j].select(c -> c instanceof UnlockableContent u && (!u.isHidden() || u.node() != null));
|
||||
if(array.size == 0) continue;
|
||||
|
||||
table.add("@content." + type.name() + ".name").growX().left().color(Pal.accent);
|
||||
@@ -57,7 +57,7 @@ public class DatabaseDialog extends BaseDialog{
|
||||
UnlockableContent unlock = (UnlockableContent)array.get(i);
|
||||
|
||||
Image image = unlocked(unlock) ? new Image(unlock.icon(Cicon.medium)).setScaling(Scaling.fit) : new Image(Icon.lock, Pal.gray);
|
||||
list.add(image).size(8*4).pad(3);
|
||||
list.add(image).size(8 * 4).pad(3);
|
||||
ClickListener listener = new ClickListener();
|
||||
image.addListener(listener);
|
||||
if(!Vars.mobile && unlocked(unlock)){
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
package mindustry.ui.dialogs;
|
||||
|
||||
import arc.Core;
|
||||
import arc.graphics.Color;
|
||||
import arc.scene.ui.Dialog;
|
||||
import arc.*;
|
||||
import arc.graphics.*;
|
||||
import arc.scene.ui.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.Pal;
|
||||
import mindustry.graphics.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import arc.*;
|
||||
import arc.files.*;
|
||||
import arc.func.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.input.*;
|
||||
import arc.scene.event.*;
|
||||
import arc.scene.ui.*;
|
||||
import arc.scene.ui.layout.*;
|
||||
@@ -17,7 +18,7 @@ import java.util.*;
|
||||
|
||||
public class FileChooser extends BaseDialog{
|
||||
private static final Fi homeDirectory = Core.files.absolute(Core.files.getExternalStoragePath());
|
||||
static Fi lastDirectory = homeDirectory;
|
||||
static Fi lastDirectory = Core.files.absolute(Core.settings.getString("lastDirectory", homeDirectory.absolutePath()));
|
||||
|
||||
private Table files;
|
||||
Fi directory = lastDirectory;
|
||||
@@ -45,6 +46,12 @@ public class FileChooser extends BaseDialog{
|
||||
cont.clear();
|
||||
setupWidgets();
|
||||
});
|
||||
|
||||
keyDown(KeyCode.enter, () -> {
|
||||
ok.fireClick();
|
||||
});
|
||||
|
||||
addCloseListener();
|
||||
}
|
||||
|
||||
private void setupWidgets(){
|
||||
@@ -108,7 +115,7 @@ public class FileChooser extends BaseDialog{
|
||||
ImageButton home = new ImageButton(Icon.home);
|
||||
home.clicked(() -> {
|
||||
directory = homeDirectory;
|
||||
lastDirectory = directory;
|
||||
setLastDirectory(directory);
|
||||
updateFiles(true);
|
||||
});
|
||||
|
||||
@@ -187,7 +194,7 @@ public class FileChooser extends BaseDialog{
|
||||
TextButton upbutton = new TextButton(".." + directory.toString(), Styles.clearTogglet);
|
||||
upbutton.clicked(() -> {
|
||||
directory = directory.parent();
|
||||
lastDirectory = directory;
|
||||
setLastDirectory(directory);
|
||||
updateFiles(true);
|
||||
});
|
||||
|
||||
@@ -217,7 +224,7 @@ public class FileChooser extends BaseDialog{
|
||||
updateFileFieldStatus();
|
||||
}else{
|
||||
directory = directory.child(filename);
|
||||
lastDirectory = directory;
|
||||
setLastDirectory(directory);
|
||||
updateFiles(true);
|
||||
}
|
||||
});
|
||||
@@ -242,6 +249,11 @@ public class FileChooser extends BaseDialog{
|
||||
if(open) filefield.clearText();
|
||||
}
|
||||
|
||||
public static void setLastDirectory(Fi directory){
|
||||
lastDirectory = directory;
|
||||
Core.settings.put("lastDirectory", directory.absolutePath());
|
||||
}
|
||||
|
||||
private String shorten(String string){
|
||||
int max = 30;
|
||||
if(string.length() <= max){
|
||||
@@ -269,14 +281,14 @@ public class FileChooser extends BaseDialog{
|
||||
if(!canBack()) return;
|
||||
index--;
|
||||
directory = history.get(index - 1);
|
||||
lastDirectory = directory;
|
||||
setLastDirectory(directory);
|
||||
updateFiles(false);
|
||||
}
|
||||
|
||||
public void forward(){
|
||||
if(!canForward()) return;
|
||||
directory = history.get(index);
|
||||
lastDirectory = directory;
|
||||
setLastDirectory(directory);
|
||||
index++;
|
||||
updateFiles(false);
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package mindustry.ui.dialogs;
|
||||
|
||||
import arc.*;
|
||||
import mindustry.core.GameState.*;
|
||||
import mindustry.game.EventType.*;
|
||||
import mindustry.game.Stats.*;
|
||||
import mindustry.game.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.ui.*;
|
||||
@@ -16,6 +16,8 @@ public class GameOverDialog extends BaseDialog{
|
||||
super("@gameover");
|
||||
setFillParent(true);
|
||||
shown(this::rebuild);
|
||||
|
||||
Events.on(ResetEvent.class, e -> hide());
|
||||
}
|
||||
|
||||
public void show(Team winner){
|
||||
@@ -29,13 +31,13 @@ public class GameOverDialog extends BaseDialog{
|
||||
}
|
||||
|
||||
void rebuild(){
|
||||
title.setText(state.launched ? "@launch.title" : "@gameover");
|
||||
title.setText(state.isCampaign() ? "@sector.curlost" : "@gameover");
|
||||
buttons.clear();
|
||||
cont.clear();
|
||||
|
||||
buttons.margin(10);
|
||||
|
||||
if(state.rules.pvp){
|
||||
if(state.rules.pvp && winner != null){
|
||||
cont.add(Core.bundle.format("gameover.pvp", winner.localized())).pad(6);
|
||||
buttons.button("@menu", () -> {
|
||||
hide();
|
||||
@@ -50,52 +52,51 @@ public class GameOverDialog extends BaseDialog{
|
||||
cont.pane(t -> {
|
||||
t.margin(13f);
|
||||
t.left().defaults().left();
|
||||
t.add(Core.bundle.format("stat.wave", state.stats.wavesLasted));
|
||||
t.row();
|
||||
t.add(Core.bundle.format("stat.enemiesDestroyed", state.stats.enemyUnitsDestroyed));
|
||||
t.row();
|
||||
t.add(Core.bundle.format("stat.built", state.stats.buildingsBuilt));
|
||||
t.row();
|
||||
t.add(Core.bundle.format("stat.destroyed", state.stats.buildingsDestroyed));
|
||||
t.row();
|
||||
t.add(Core.bundle.format("stat.deconstructed", state.stats.buildingsDeconstructed));
|
||||
t.row();
|
||||
t.add(Core.bundle.format("stat.wave", state.stats.wavesLasted)).row();
|
||||
t.add(Core.bundle.format("stat.enemiesDestroyed", state.stats.enemyUnitsDestroyed)).row();
|
||||
t.add(Core.bundle.format("stat.built", state.stats.buildingsBuilt)).row();
|
||||
t.add(Core.bundle.format("stat.destroyed", state.stats.buildingsDestroyed)).row();
|
||||
t.add(Core.bundle.format("stat.deconstructed", state.stats.buildingsDeconstructed)).row();
|
||||
if(control.saves.getCurrent() != null){
|
||||
t.add(Core.bundle.format("stat.playtime", control.saves.getCurrent().getPlayTime()));
|
||||
t.row();
|
||||
t.add(Core.bundle.format("stat.playtime", control.saves.getCurrent().getPlayTime())).row();
|
||||
}
|
||||
if(state.isCampaign() && !state.stats.itemsDelivered.isEmpty()){
|
||||
t.add("@stat.delivered");
|
||||
t.row();
|
||||
t.add("@stat.delivered").row();
|
||||
for(Item item : content.items()){
|
||||
if(state.stats.itemsDelivered.get(item, 0) > 0){
|
||||
t.table(items -> {
|
||||
items.add(" [lightgray]" + state.stats.itemsDelivered.get(item, 0));
|
||||
items.image(item.icon(Cicon.small)).size(8 * 3).pad(4);
|
||||
}).left();
|
||||
t.row();
|
||||
}).left().row();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(state.hasSector()){
|
||||
RankResult result = state.stats.calculateRank(state.getSector(), state.launched);
|
||||
t.add(Core.bundle.format("stat.rank", result.rank + result.modifier));
|
||||
t.row();
|
||||
if(state.isCampaign() && net.client()){
|
||||
t.add("@gameover.waiting").padTop(20f).row();
|
||||
}
|
||||
|
||||
}).pad(12);
|
||||
|
||||
if(state.isCampaign()){
|
||||
buttons.button("@continue", () -> {
|
||||
hide();
|
||||
logic.reset();
|
||||
ui.planet.show();
|
||||
}).size(130f, 60f);
|
||||
if(net.client()){
|
||||
buttons.button("@gameover.disconnect", () -> {
|
||||
logic.reset();
|
||||
net.reset();
|
||||
hide();
|
||||
state.set(State.menu);
|
||||
}).size(170f, 60f);
|
||||
}else{
|
||||
buttons.button("@continue", () -> {
|
||||
hide();
|
||||
ui.planet.show();
|
||||
}).size(170f, 60f);
|
||||
}
|
||||
}else{
|
||||
buttons.button("@menu", () -> {
|
||||
hide();
|
||||
logic.reset();
|
||||
}).size(130f, 60f);
|
||||
}).size(140f, 60f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,22 +85,29 @@ public class HostDialog extends BaseDialog{
|
||||
player.admin(true);
|
||||
|
||||
if(steam){
|
||||
Core.app.post(() -> Core.settings.getBoolOnce("steampublic2", () -> {
|
||||
Core.app.post(() -> Core.settings.getBoolOnce("steampublic3", () -> {
|
||||
ui.showCustomConfirm("@setting.publichost.name", "@public.confirm", "@yes", "@no", () -> {
|
||||
Core.settings.put("publichost", true);
|
||||
platform.updateLobby();
|
||||
ui.showCustomConfirm("@setting.publichost.name", "@public.confirm.really", "@no", "@yes", () -> {
|
||||
Core.settings.put("publichost", true);
|
||||
platform.updateLobby();
|
||||
}, () -> {
|
||||
Core.settings.put("publichost", false);
|
||||
platform.updateLobby();
|
||||
});
|
||||
}, () -> {
|
||||
Core.settings.put("publichost", false);
|
||||
platform.updateLobby();
|
||||
});
|
||||
}));
|
||||
|
||||
if(Version.modifier.contains("beta") || Version.modifier.contains("alpha")){
|
||||
Core.settings.put("publichost", false);
|
||||
platform.updateLobby();
|
||||
Core.settings.getBoolOnce("betapublic", () -> ui.showInfo("@public.beta"));
|
||||
}
|
||||
}
|
||||
|
||||
if(Version.modifier.contains("beta")){
|
||||
Core.settings.put("publichost", false);
|
||||
platform.updateLobby();
|
||||
Core.settings.getBoolOnce("betapublic", () -> ui.showInfo("@public.beta"));
|
||||
}
|
||||
|
||||
}catch(IOException e){
|
||||
ui.showException("@server.error", e);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package mindustry.ui.dialogs;
|
||||
|
||||
import arc.*;
|
||||
import arc.Net.*;
|
||||
import arc.graphics.*;
|
||||
import arc.input.*;
|
||||
import arc.math.*;
|
||||
@@ -8,6 +9,7 @@ import arc.scene.ui.*;
|
||||
import arc.scene.ui.layout.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import arc.util.Timer.*;
|
||||
import arc.util.serialization.*;
|
||||
import mindustry.*;
|
||||
import mindustry.core.*;
|
||||
@@ -31,6 +33,11 @@ public class JoinDialog extends BaseDialog{
|
||||
Table hosts = new Table();
|
||||
int totalHosts;
|
||||
int refreshes;
|
||||
boolean showHidden;
|
||||
|
||||
String lastIp;
|
||||
int lastPort;
|
||||
Task ping;
|
||||
|
||||
public JoinDialog(){
|
||||
super("@joingame");
|
||||
@@ -193,7 +200,7 @@ public class JoinDialog extends BaseDialog{
|
||||
|
||||
void refreshServer(Server server){
|
||||
server.content.clear();
|
||||
server.content.label(() -> Core.bundle.get("server.refreshing") + Strings.animated(Time.time(), 4, 11, "."));
|
||||
server.content.label(() -> Core.bundle.get("server.refreshing") + Strings.animated(Time.time, 4, 11, "."));
|
||||
|
||||
net.pingHost(server.ip, server.port, host -> setupServer(server, host), e -> {
|
||||
server.content.clear();
|
||||
@@ -252,9 +259,9 @@ public class JoinDialog extends BaseDialog{
|
||||
|
||||
hosts.clear();
|
||||
|
||||
section("@servers.local", local);
|
||||
section("@servers.remote", remote);
|
||||
section("@servers.global", global);
|
||||
section("@servers.local", local, false);
|
||||
section("@servers.remote", remote, false);
|
||||
section("@servers.global", global, true);
|
||||
|
||||
ScrollPane pane = new ScrollPane(hosts);
|
||||
pane.setFadeScrollBars(false);
|
||||
@@ -292,7 +299,7 @@ public class JoinDialog extends BaseDialog{
|
||||
pad = 6;
|
||||
}
|
||||
|
||||
Cell cell = ((Table)pane.parent).getCell(button);
|
||||
var cell = ((Table)pane.parent).getCell(button);
|
||||
|
||||
if(!Mathf.equal(cell.minWidth(), pw)){
|
||||
cell.width(pw);
|
||||
@@ -302,12 +309,21 @@ public class JoinDialog extends BaseDialog{
|
||||
});
|
||||
}
|
||||
|
||||
void section(String label, Table servers){
|
||||
void section(String label, Table servers, boolean eye){
|
||||
Collapser coll = new Collapser(servers, Core.settings.getBool("collapsed-" + label, false));
|
||||
coll.setDuration(0.1f);
|
||||
|
||||
hosts.table(name -> {
|
||||
name.add(label).pad(10).growX().left().color(Pal.accent);
|
||||
|
||||
if(eye){
|
||||
name.button(Icon.eyeSmall, Styles.emptyi, () -> {
|
||||
showHidden = !showHidden;
|
||||
refreshGlobal();
|
||||
}).update(i -> i.getStyle().imageUp = (showHidden ? Icon.eyeSmall : Icon.eyeOffSmall))
|
||||
.size(40f).right().padRight(3).tooltip("@servers.showhidden");
|
||||
}
|
||||
|
||||
name.button(Icon.downOpen, Styles.emptyi, () -> {
|
||||
coll.toggle(false);
|
||||
Core.settings.put("collapsed-" + label, coll.isCollapsed());
|
||||
@@ -325,7 +341,7 @@ public class JoinDialog extends BaseDialog{
|
||||
|
||||
local.clear();
|
||||
local.background(null);
|
||||
local.table(Tex.button, t -> t.label(() -> "[accent]" + Core.bundle.get("hosts.discovering.any") + Strings.animated(Time.time(), 4, 10f, ".")).pad(10f)).growX();
|
||||
local.table(Tex.button, t -> t.label(() -> "[accent]" + Core.bundle.get("hosts.discovering.any") + Strings.animated(Time.time, 4, 10f, ".")).pad(10f)).growX();
|
||||
net.discoverServers(this::addLocalHost, this::finishLocalHosts);
|
||||
}
|
||||
|
||||
@@ -334,17 +350,73 @@ public class JoinDialog extends BaseDialog{
|
||||
|
||||
global.clear();
|
||||
global.background(null);
|
||||
for(String host : defaultServers){
|
||||
String resaddress = host.contains(":") ? host.split(":")[0] : host;
|
||||
int resport = host.contains(":") ? Strings.parseInt(host.split(":")[1]) : port;
|
||||
net.pingHost(resaddress, resport, res -> {
|
||||
if(refreshes != cur) return;
|
||||
res.port = resport;
|
||||
addGlobalHost(res);
|
||||
}, e -> {});
|
||||
for(ServerGroup group : defaultServers){
|
||||
boolean hidden = group.hidden();
|
||||
if(hidden && !showHidden){
|
||||
continue;
|
||||
}
|
||||
|
||||
Table[] groupTable = {null};
|
||||
|
||||
//table containing all groups
|
||||
for(String address : group.addresses){
|
||||
String resaddress = address.contains(":") ? address.split(":")[0] : address;
|
||||
int resport = address.contains(":") ? Strings.parseInt(address.split(":")[1]) : port;
|
||||
net.pingHost(resaddress, resport, res -> {
|
||||
if(refreshes != cur) return;
|
||||
res.port = resport;
|
||||
|
||||
//add header
|
||||
if(groupTable[0] == null){
|
||||
global.table(t -> groupTable[0] = t).row();
|
||||
|
||||
groupTable[0].table(head -> {
|
||||
if(!group.name.isEmpty()){
|
||||
head.add(group.name).color(Color.lightGray).padRight(4);
|
||||
}
|
||||
head.image().height(3f).growX().color(Color.lightGray);
|
||||
|
||||
//button for showing/hiding servers
|
||||
ImageButton[] image = {null};
|
||||
image[0] = head.button(hidden ? Icon.eyeOffSmall : Icon.eyeSmall, Styles.accenti, () -> {
|
||||
group.setHidden(!group.hidden());
|
||||
image[0].getStyle().imageUp = group.hidden() ? Icon.eyeOffSmall : Icon.eyeSmall;
|
||||
if(group.hidden() && !showHidden){
|
||||
groupTable[0].remove();
|
||||
}
|
||||
}).size(40f).get();
|
||||
image[0].addListener(new Tooltip(t -> t.background(Styles.black6).margin(4).label(() -> !group.hidden() ? "@server.shown" : "@server.hidden")));
|
||||
}).width(targetWidth()).padBottom(-2).row();
|
||||
}
|
||||
|
||||
addGlobalHost(res, groupTable[0]);
|
||||
|
||||
groupTable[0].margin(5f);
|
||||
groupTable[0].pack();
|
||||
}, e -> {});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void addGlobalHost(Host host, Table container){
|
||||
global.background(null);
|
||||
float w = targetWidth();
|
||||
|
||||
container.button(b -> buildServer(host, b), Styles.cleart, () -> {
|
||||
Events.fire(new ClientPreConnectEvent(host));
|
||||
if(!Core.settings.getBool("server-disclaimer", false)){
|
||||
ui.showCustomConfirm("@warning", "@servers.disclaimer", "@ok", "@back", () -> {
|
||||
Core.settings.put("server-disclaimer", true);
|
||||
safeConnect(host.address, host.port, host.version);
|
||||
}, () -> {
|
||||
Core.settings.put("server-disclaimer", false);
|
||||
});
|
||||
}else{
|
||||
safeConnect(host.address, host.port, host.version);
|
||||
}
|
||||
}).width(w).row();
|
||||
}
|
||||
|
||||
void finishLocalHosts(){
|
||||
if(totalHosts == 0){
|
||||
local.clear();
|
||||
@@ -367,22 +439,10 @@ public class JoinDialog extends BaseDialog{
|
||||
|
||||
local.row();
|
||||
|
||||
TextButton button = local.button("", Styles.cleart, () -> safeConnect(host.address, host.port, host.version))
|
||||
.width(w).pad(5f).get();
|
||||
button.clearChildren();
|
||||
buildServer(host, button);
|
||||
}
|
||||
|
||||
void addGlobalHost(Host host){
|
||||
global.background(null);
|
||||
float w = targetWidth();
|
||||
|
||||
global.row();
|
||||
|
||||
TextButton button = global.button("", Styles.cleart, () -> safeConnect(host.address, host.port, host.version))
|
||||
.width(w).pad(5f).get();
|
||||
button.clearChildren();
|
||||
buildServer(host, button);
|
||||
local.button(b -> buildServer(host, b), Styles.cleart, () -> {
|
||||
Events.fire(new ClientPreConnectEvent(host));
|
||||
safeConnect(host.address, host.port, host.version);
|
||||
}).width(w);
|
||||
}
|
||||
|
||||
public void connect(String ip, int port){
|
||||
@@ -402,13 +462,34 @@ public class JoinDialog extends BaseDialog{
|
||||
logic.reset();
|
||||
net.reset();
|
||||
Vars.netClient.beginConnecting();
|
||||
net.connect(ip, port, () -> {
|
||||
net.connect(lastIp = ip, lastPort = port, () -> {
|
||||
hide();
|
||||
add.hide();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public void reconnect(){
|
||||
if(lastIp == null || lastIp.isEmpty()) return;
|
||||
ui.loadfrag.show("@reconnecting");
|
||||
|
||||
ping = Timer.schedule(() -> {
|
||||
net.pingHost(lastIp, lastPort, host -> {
|
||||
if(ping == null) return;
|
||||
ping.cancel();
|
||||
ping = null;
|
||||
connect(lastIp, lastPort);
|
||||
}, exception -> {});
|
||||
}, 1, 1);
|
||||
|
||||
ui.loadfrag.setButton(() -> {
|
||||
ui.loadfrag.hide();
|
||||
if(ping == null) return;
|
||||
ping.cancel();
|
||||
ping = null;
|
||||
});
|
||||
}
|
||||
|
||||
void safeConnect(String ip, int port, int version){
|
||||
if(version != Version.build && Version.build != -1 && version != -1){
|
||||
ui.showInfo("[scarlet]" + (version > Version.build ? KickReason.clientOutdated : KickReason.serverOutdated).toString() + "\n[]" +
|
||||
@@ -432,23 +513,42 @@ public class JoinDialog extends BaseDialog{
|
||||
Core.settings.remove("server-list");
|
||||
}
|
||||
|
||||
var url = becontrol.active() ? serverJsonBeURL : serverJsonV6URL;
|
||||
Log.info("Fetching community servers at @", url);
|
||||
|
||||
//get servers
|
||||
Core.net.httpGet(becontrol.active() ? serverJsonBeURL : serverJsonV6URL, result -> {
|
||||
Core.net.httpGet(url, result -> {
|
||||
try{
|
||||
if(result.getStatus() != HttpStatus.OK){
|
||||
Log.warn("Failed to fetch community servers: @", result.getStatus());
|
||||
return;
|
||||
}
|
||||
|
||||
Jval val = Jval.read(result.getResultAsString());
|
||||
Core.app.post(() -> {
|
||||
try{
|
||||
defaultServers.clear();
|
||||
val.asArray().each(child -> defaultServers.add(child.getString("address", "<invalid>")));
|
||||
Log.info("Fetched @ global servers.", defaultServers.size);
|
||||
}catch(Throwable ignored){
|
||||
val.asArray().each(child -> {
|
||||
String name = child.getString("name", "");
|
||||
String[] addresses;
|
||||
if(child.has("addresses") || (child.has("address") && child.get("address").isArray())){
|
||||
addresses = (child.has("addresses") ? child.get("addresses") : child.get("address")).asArray().map(Jval::asString).toArray(String.class);
|
||||
}else{
|
||||
addresses = new String[]{child.getString("address", "<invalid>")};
|
||||
}
|
||||
defaultServers.add(new ServerGroup(name, addresses));
|
||||
});
|
||||
Log.info("Fetched @ community servers.", defaultServers.size);
|
||||
}catch(Throwable e){
|
||||
Log.err("Failed to parse community servers.");
|
||||
Log.err(e);
|
||||
}
|
||||
});
|
||||
}catch(Throwable e){
|
||||
Log.err("Failed to fetch community servers.");
|
||||
Log.err(e);
|
||||
}
|
||||
}, t -> {});
|
||||
}, Log::err);
|
||||
}
|
||||
|
||||
private void saveServers(){
|
||||
|
||||
@@ -1,17 +1,15 @@
|
||||
package mindustry.ui.dialogs;
|
||||
|
||||
import arc.Core;
|
||||
import arc.struct.*;
|
||||
import arc.*;
|
||||
import arc.scene.ui.*;
|
||||
import arc.scene.ui.layout.Table;
|
||||
import arc.util.Log;
|
||||
import arc.util.Strings;
|
||||
import arc.scene.ui.layout.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import mindustry.ui.*;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.*;
|
||||
|
||||
import static mindustry.Vars.locales;
|
||||
import static mindustry.Vars.ui;
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class LanguageDialog extends BaseDialog{
|
||||
private Locale lastLocale;
|
||||
|
||||
@@ -29,18 +29,30 @@ public class LaunchLoadoutDialog extends BaseDialog{
|
||||
super("@configure");
|
||||
}
|
||||
|
||||
public void show(CoreBlock core, Building build, Runnable confirm){
|
||||
public void show(CoreBlock core, Sector sector, Runnable confirm){
|
||||
cont.clear();
|
||||
buttons.clear();
|
||||
|
||||
addCloseButton();
|
||||
buttons.defaults().size(160f, 64f);
|
||||
buttons.button("@back", Icon.left, this::hide);
|
||||
|
||||
addCloseListener();
|
||||
|
||||
ItemSeq sitems = sector.items();
|
||||
|
||||
//updates sum requirements
|
||||
Runnable update = () -> {
|
||||
int cap = selected.findCore().itemCapacity;
|
||||
|
||||
//cap resources based on core type
|
||||
ItemSeq resources = universe.getLaunchResources();
|
||||
resources.min(cap);
|
||||
universe.updateLaunchResources(resources);
|
||||
|
||||
total.clear();
|
||||
selected.requirements().each(total::add);
|
||||
universe.getLaunchResources().each(total::add);
|
||||
valid = build.items.has(total);
|
||||
valid = sitems.has(total);
|
||||
};
|
||||
|
||||
Cons<Table> rebuild = table -> {
|
||||
@@ -51,14 +63,14 @@ public class LaunchLoadoutDialog extends BaseDialog{
|
||||
ItemSeq launches = universe.getLaunchResources();
|
||||
|
||||
for(ItemStack s : total){
|
||||
table.image(s.item.icon(Cicon.small)).left();
|
||||
table.image(s.item.icon(Cicon.small)).left().size(Cicon.small.size);
|
||||
int as = schems.get(s.item), al = launches.get(s.item);
|
||||
|
||||
String amountStr = "[lightgray]" + (al + " + [accent]" + as + "[lightgray]");
|
||||
String amountStr = (al + as) + "[gray] (" + (al + " + " + as + ")");
|
||||
|
||||
table.add(
|
||||
build.items.has(s.item, s.amount) ? amountStr :
|
||||
"[scarlet]" + (Math.min(build.items.get(s.item), s.amount) + "[lightgray]/" + amountStr)).padLeft(2).left().padRight(4);
|
||||
sitems.has(s.item, s.amount) ? amountStr :
|
||||
"[scarlet]" + (Math.min(sitems.get(s.item), s.amount) + "[lightgray]/" + amountStr)).padLeft(2).left().padRight(4);
|
||||
|
||||
if(++i % 4 == 0){
|
||||
table.row();
|
||||
@@ -74,12 +86,12 @@ public class LaunchLoadoutDialog extends BaseDialog{
|
||||
ItemSeq stacks = universe.getLaunchResources();
|
||||
Seq<ItemStack> out = stacks.toSeq();
|
||||
|
||||
loadout.show(core.itemCapacity, out, UnlockableContent::unlocked, out::clear, () -> {}, () -> {
|
||||
loadout.show(selected.findCore().itemCapacity, out, UnlockableContent::unlocked, out::clear, () -> {}, () -> {
|
||||
universe.updateLaunchResources(new ItemSeq(out));
|
||||
update.run();
|
||||
rebuildItems.run();
|
||||
});
|
||||
});
|
||||
}).width(204);
|
||||
|
||||
buttons.button("@launch.text", Icon.ok, () -> {
|
||||
universe.updateLoadout(core, selected);
|
||||
@@ -91,25 +103,35 @@ public class LaunchLoadoutDialog extends BaseDialog{
|
||||
ButtonGroup<Button> group = new ButtonGroup<>();
|
||||
selected = universe.getLoadout(core);
|
||||
|
||||
cont.add(Core.bundle.format("launch.from", sector.name())).row();
|
||||
|
||||
cont.pane(t -> {
|
||||
int i = 0;
|
||||
|
||||
for(Schematic s : schematics.getLoadouts(core)){
|
||||
for(var entry : schematics.getLoadouts()){
|
||||
if(entry.key.size <= core.size){
|
||||
for(Schematic s : entry.value){
|
||||
|
||||
t.button(b -> b.add(new SchematicImage(s)), Styles.togglet, () -> {
|
||||
selected = s;
|
||||
update.run();
|
||||
rebuildItems.run();
|
||||
}).group(group).pad(4).disabled(!build.items.has(s.requirements())).checked(s == selected).size(200f);
|
||||
t.button(b -> b.add(new SchematicImage(s)), Styles.togglet, () -> {
|
||||
selected = s;
|
||||
update.run();
|
||||
rebuildItems.run();
|
||||
}).group(group).pad(4).checked(s == selected).size(200f);
|
||||
|
||||
if(++i % cols == 0){
|
||||
t.row();
|
||||
if(++i % cols == 0){
|
||||
t.row();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}).growX().get().setScrollingDisabled(true, false);
|
||||
|
||||
cont.row();
|
||||
cont.add(items);
|
||||
cont.pane(items);
|
||||
cont.row();
|
||||
cont.add("@sector.missingresources").visible(() -> !valid);
|
||||
|
||||
update.run();
|
||||
rebuildItems.run();
|
||||
|
||||
@@ -4,7 +4,6 @@ import arc.*;
|
||||
import arc.scene.ui.*;
|
||||
import arc.scene.ui.layout.*;
|
||||
import arc.util.*;
|
||||
import arc.util.ArcAnnotate.*;
|
||||
import mindustry.game.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.maps.*;
|
||||
@@ -15,7 +14,7 @@ import static mindustry.Vars.*;
|
||||
public class MapPlayDialog extends BaseDialog{
|
||||
CustomRulesDialog dialog = new CustomRulesDialog();
|
||||
Rules rules;
|
||||
@NonNull Gamemode selectedGamemode = Gamemode.survival;
|
||||
Gamemode selectedGamemode = Gamemode.survival;
|
||||
Map lastMap;
|
||||
|
||||
public MapPlayDialog(){
|
||||
|
||||
@@ -2,7 +2,6 @@ package mindustry.ui.dialogs;
|
||||
|
||||
import arc.*;
|
||||
import arc.graphics.*;
|
||||
import arc.input.*;
|
||||
import arc.scene.ui.*;
|
||||
import arc.scene.ui.layout.*;
|
||||
import arc.util.*;
|
||||
@@ -24,11 +23,7 @@ public class MapsDialog extends BaseDialog{
|
||||
|
||||
buttons.remove();
|
||||
|
||||
keyDown(key -> {
|
||||
if(key == KeyCode.escape || key == KeyCode.back){
|
||||
Core.app.post(this::hide);
|
||||
}
|
||||
});
|
||||
addCloseListener();
|
||||
|
||||
shown(this::setup);
|
||||
onResize(() -> {
|
||||
@@ -182,11 +177,14 @@ public class MapsDialog extends BaseDialog{
|
||||
t.row();
|
||||
t.add("@editor.author").padRight(10).color(Color.gray);
|
||||
t.row();
|
||||
t.add(!map.custom && map.author().isEmpty() ? "Anuke" : map.author()).growX().wrap().padTop(2);
|
||||
t.add(!map.custom && map.tags.get("author", "").isEmpty() ? "Anuke" : map.author()).growX().wrap().padTop(2);
|
||||
t.row();
|
||||
t.add("@editor.description").padRight(10).color(Color.gray).top();
|
||||
t.row();
|
||||
t.add(map.description()).growX().wrap().padTop(2);
|
||||
|
||||
if(!map.tags.get("description", "").isEmpty()){
|
||||
t.add("@editor.description").padRight(10).color(Color.gray).top();
|
||||
t.row();
|
||||
t.add(map.description()).growX().wrap().padTop(2);
|
||||
}
|
||||
}).height(mapsize).width(mapsize);
|
||||
|
||||
table.row();
|
||||
|
||||
@@ -8,7 +8,7 @@ import arc.scene.event.*;
|
||||
import arc.scene.ui.layout.*;
|
||||
import mindustry.gen.*;
|
||||
|
||||
import static mindustry.Vars.renderer;
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class MinimapDialog extends BaseDialog{
|
||||
|
||||
|
||||
@@ -6,11 +6,19 @@ import arc.files.*;
|
||||
import arc.func.*;
|
||||
import arc.graphics.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.input.*;
|
||||
import arc.scene.style.*;
|
||||
import arc.scene.ui.TextButton.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import arc.util.io.*;
|
||||
import arc.util.serialization.*;
|
||||
import mindustry.*;
|
||||
import mindustry.core.*;
|
||||
import mindustry.ctype.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.mod.*;
|
||||
import mindustry.mod.Mods.*;
|
||||
import mindustry.ui.*;
|
||||
|
||||
@@ -19,6 +27,8 @@ import java.io.*;
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class ModsDialog extends BaseDialog{
|
||||
private String searchtxt = "";
|
||||
private @Nullable Seq<ModListing> modList;
|
||||
|
||||
public ModsDialog(){
|
||||
super("@mods");
|
||||
@@ -27,6 +37,9 @@ public class ModsDialog extends BaseDialog{
|
||||
buttons.button("@mods.guide", Icon.link, () -> Core.app.openURI(modGuideURL)).size(210, 64f);
|
||||
|
||||
shown(this::setup);
|
||||
if(mobile){
|
||||
onResize(this::setup);
|
||||
}
|
||||
|
||||
hidden(() -> {
|
||||
if(mods.requiresReload()){
|
||||
@@ -51,6 +64,30 @@ public class ModsDialog extends BaseDialog{
|
||||
}
|
||||
}
|
||||
|
||||
void getModList(Cons<Seq<ModListing>> listener){
|
||||
if(modList == null){
|
||||
Core.net.httpGet("https://raw.githubusercontent.com/Anuken/MindustryMods/master/mods.json", response -> {
|
||||
String strResult = response.getResultAsString();
|
||||
var status = response.getStatus();
|
||||
|
||||
Core.app.post(() -> {
|
||||
if(status != HttpStatus.OK){
|
||||
ui.showErrorMessage(Core.bundle.format("connectfail", status));
|
||||
}else{
|
||||
modList = new Json().fromJson(Seq.class, ModListing.class, strResult);
|
||||
|
||||
//potentially sort mods by game version compatibility, or other criteria
|
||||
//modList.sort(Structs.comparingBool(m -> !Version.isAtLeast(m.minGameVersion)));
|
||||
|
||||
listener.get(modList);
|
||||
}
|
||||
});
|
||||
}, error -> Core.app.post(() -> ui.showException(error)));
|
||||
}else{
|
||||
listener.get(modList);
|
||||
}
|
||||
}
|
||||
|
||||
void setup(){
|
||||
float h = 110f;
|
||||
float w = mobile ? 440f : 524f;
|
||||
@@ -103,24 +140,87 @@ public class ModsDialog extends BaseDialog{
|
||||
t.button("@mod.import.github", Icon.github, bstyle, () -> {
|
||||
dialog.hide();
|
||||
|
||||
ui.showTextInput("@mod.import.github", "", 64, Core.settings.getString("lastmod", "Anuken/ExampleMod"), text -> {
|
||||
ui.showTextInput("@mod.import.github", "", 64, Core.settings.getString("lastmod", ""), text -> {
|
||||
Core.settings.put("lastmod", text);
|
||||
|
||||
ui.loadfrag.show();
|
||||
// Try to download the 6.0 branch first, but if it doesnt exist try master.
|
||||
githubImport("6.0", text, e1 -> {
|
||||
githubImport("master", text, e2 -> {
|
||||
ui.showErrorMessage(Core.bundle.format("connectfail", e2));
|
||||
ui.loadfrag.hide();
|
||||
});
|
||||
});
|
||||
githubImportMod(text);
|
||||
});
|
||||
}).margin(12f);
|
||||
});
|
||||
|
||||
t.row();
|
||||
|
||||
t.button("@mod.featured.dialog.title", Icon.star, bstyle, () -> {
|
||||
Runnable[] rebuildBrowser = {null};
|
||||
dialog.hide();
|
||||
BaseDialog browser = new BaseDialog("$mod.featured.dialog.title");
|
||||
browser.cont.table(table -> {
|
||||
table.left();
|
||||
table.image(Icon.zoom);
|
||||
table.field(searchtxt, res -> {
|
||||
searchtxt = res;
|
||||
rebuildBrowser[0].run();
|
||||
}).growX().get();
|
||||
}).fillX().padBottom(4);
|
||||
|
||||
browser.cont.row();
|
||||
|
||||
browser.cont.pane(tablebrow -> {
|
||||
tablebrow.margin(10f).top();
|
||||
rebuildBrowser[0] = () -> {
|
||||
tablebrow.clear();
|
||||
tablebrow.add("@loading");
|
||||
|
||||
getModList(listings -> {
|
||||
tablebrow.clear();
|
||||
|
||||
for(ModListing mod : listings){
|
||||
if(mod.hasJava || !searchtxt.isEmpty() && !mod.repo.contains(searchtxt) || (Vars.ios && mod.hasScripts)) continue;
|
||||
|
||||
tablebrow.button(btn -> {
|
||||
btn.top().left();
|
||||
btn.margin(12f);
|
||||
btn.table(con -> {
|
||||
con.left();
|
||||
con.add("[accent]" + mod.name + "[white]\n[lightgray]Author:[] " + mod.author + "\n[lightgray]\uE809 " + mod.stars +
|
||||
(Version.isAtLeast(mod.minGameVersion) ? "" : "\n" + Core.bundle.format("mod.requiresversion", mod.minGameVersion)))
|
||||
.width(388f).wrap().growX().pad(0f, 6f, 0f, 6f).left().labelAlign(Align.left);
|
||||
con.add().growX().pad(0f, 6f, 0f, 6f);
|
||||
}).fillY().growX().pad(0f, 6f, 0f, 6f);
|
||||
}, Styles.modsb, () -> {
|
||||
var sel = new BaseDialog(mod.name);
|
||||
sel.cont.add(mod.description).width(mobile ? 400f : 500f).wrap().pad(4f).labelAlign(Align.center, Align.left);
|
||||
sel.buttons.defaults().size(150f, 54f).pad(2f);
|
||||
sel.setFillParent(false);
|
||||
sel.buttons.button("@back", Icon.left, () -> {
|
||||
sel.clear();
|
||||
sel.hide();
|
||||
});
|
||||
sel.buttons.button("@mods.browser.add", Icon.download, () -> {
|
||||
sel.hide();
|
||||
githubImportMod(mod.repo);
|
||||
});
|
||||
sel.buttons.button("@mods.github.open", Icon.link, () -> {
|
||||
Core.app.openURI("https://github.com/" + mod.repo);
|
||||
});
|
||||
sel.keyDown(KeyCode.escape, sel::hide);
|
||||
sel.keyDown(KeyCode.back, sel::hide);
|
||||
sel.show();
|
||||
}).width(480f).growX().left().fillY();
|
||||
tablebrow.row();
|
||||
}
|
||||
});
|
||||
};
|
||||
rebuildBrowser[0].run();
|
||||
});
|
||||
browser.addCloseButton();
|
||||
browser.show();
|
||||
}).margin(12f);
|
||||
});
|
||||
dialog.addCloseButton();
|
||||
|
||||
dialog.show();
|
||||
|
||||
}).margin(margin);
|
||||
|
||||
if(!mobile){
|
||||
@@ -131,14 +231,12 @@ public class ModsDialog extends BaseDialog{
|
||||
cont.row();
|
||||
|
||||
if(!mods.list().isEmpty()){
|
||||
cont.pane(table -> {
|
||||
table.margin(10f).top();
|
||||
|
||||
boolean anyDisabled = false;
|
||||
for(LoadedMod mod : mods.list()){
|
||||
|
||||
if(!mod.enabled() && !anyDisabled && mods.list().size > 0){
|
||||
anyDisabled = true;
|
||||
boolean[] anyDisabled = {false};
|
||||
SearchBar.add(cont, mods.list(),
|
||||
mod -> mod.meta.displayName(),
|
||||
(table, mod) -> {
|
||||
if(!mod.enabled() && !anyDisabled[0] && mods.list().size > 0){
|
||||
anyDisabled[0] = true;
|
||||
table.row();
|
||||
table.image().growX().height(4f).pad(6f).color(Pal.gray);
|
||||
table.row();
|
||||
@@ -212,20 +310,14 @@ public class ModsDialog extends BaseDialog{
|
||||
}).size(50f);
|
||||
}
|
||||
}).growX().right().padRight(-8f).padTop(-8f);
|
||||
|
||||
|
||||
}, Styles.clearPartialt, () -> showMod(mod)).size(w, h).growX().pad(4f);
|
||||
table.row();
|
||||
}
|
||||
});
|
||||
|
||||
}, !mobile || Core.graphics.isPortrait()).margin(10f).top();
|
||||
}else{
|
||||
cont.table(Styles.black6, t -> t.add("@mods.none")).height(80f);
|
||||
}
|
||||
|
||||
cont.row();
|
||||
|
||||
|
||||
}
|
||||
|
||||
private void reload(){
|
||||
@@ -260,54 +352,86 @@ public class ModsDialog extends BaseDialog{
|
||||
desc.add("@editor.description").padRight(10).color(Color.gray).top();
|
||||
desc.row();
|
||||
desc.add(mod.meta.description).growX().wrap().padTop(2);
|
||||
desc.row();
|
||||
}
|
||||
|
||||
//TODO add this when mods work properly
|
||||
/*
|
||||
Array<UnlockableContent> all = Array.with(content.getContentMap()).<Content>flatten().select(c -> c.minfo.mod == mod && c instanceof UnlockableContent).as(UnlockableContent.class);
|
||||
}).width(400f);
|
||||
|
||||
//TODO maybe enable later
|
||||
if(false){
|
||||
Seq<UnlockableContent> all = Seq.with(content.getContentMap()).<Content>flatten().select(c -> c.minfo.mod == mod && c instanceof UnlockableContent).as();
|
||||
if(all.any()){
|
||||
desc.add("@mod.content").padRight(10).color(Color.gray).top();
|
||||
desc.row();
|
||||
desc.pane(cs -> {
|
||||
dialog.cont.row();
|
||||
dialog.cont.pane(cs -> {
|
||||
int i = 0;
|
||||
for(UnlockableContent c : all){
|
||||
cs.addImageButton(new TextureRegionDrawable(c.icon(Cicon.medium)), () -> {
|
||||
cs.button(new TextureRegionDrawable(c.icon(Cicon.medium)), Styles.cleari, Cicon.medium.size, () -> {
|
||||
ui.content.show(c);
|
||||
}).size(50f).with(im -> {
|
||||
var click = im.getClickListener();
|
||||
im.update(() -> im.getImage().color.lerp(!click.isOver() ? Color.lightGray : Color.white, 0.4f * Time.delta));
|
||||
});
|
||||
|
||||
if(++i % 8 == 0) cs.row();
|
||||
}
|
||||
}).growX().minHeight(60f);
|
||||
}*/
|
||||
}).width(400f);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
dialog.show();
|
||||
}
|
||||
|
||||
private void githubImport(String branch, String repo, Cons<HttpStatus> err){
|
||||
Core.net.httpGet("http://api.github.com/repos/" + repo + "/zipball/" + branch, loc -> {
|
||||
Core.net.httpGet(loc.getHeader("Location"), result -> {
|
||||
if(result.getStatus() != HttpStatus.OK){
|
||||
err.get(result.getStatus());
|
||||
}else{
|
||||
try{
|
||||
Fi file = tmpDirectory.child(repo.replace("/", "") + ".zip");
|
||||
Streams.copy(result.getResultAsStream(), file.write(false));
|
||||
mods.importMod(file);
|
||||
file.delete();
|
||||
Core.app.post(() -> {
|
||||
try{
|
||||
setup();
|
||||
ui.loadfrag.hide();
|
||||
}catch(Throwable e){
|
||||
ui.showException(e);
|
||||
}
|
||||
});
|
||||
}catch(Throwable e){
|
||||
modError(e);
|
||||
}
|
||||
private void handleMod(String repo, HttpResponse result){
|
||||
try{
|
||||
Fi file = tmpDirectory.child(repo.replace("/", "") + ".zip");
|
||||
Streams.copy(result.getResultAsStream(), file.write(false));
|
||||
mods.importMod(file);
|
||||
file.delete();
|
||||
Core.app.post(() -> {
|
||||
try{
|
||||
setup();
|
||||
ui.loadfrag.hide();
|
||||
}catch(Throwable e){
|
||||
ui.showException(e);
|
||||
}
|
||||
}, t2 -> Core.app.post(() -> modError(t2)));
|
||||
});
|
||||
}catch(Throwable e){
|
||||
modError(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void githubImportMod(String name){
|
||||
//try several branches
|
||||
//TODO use only the main branch as specified in meta
|
||||
githubImportBranch("6.0", name, e1 -> {
|
||||
githubImportBranch("master", name, e2 -> {
|
||||
githubImportBranch("main", name, e3 -> {
|
||||
ui.showErrorMessage(Core.bundle.format("connectfail", e2));
|
||||
ui.loadfrag.hide();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private void githubImportBranch(String branch, String repo, Cons<HttpStatus> err){
|
||||
Core.net.httpGet("https://api.github.com/repos/" + repo + "/zipball/" + branch, loc -> {
|
||||
if(loc.getStatus() == HttpStatus.OK){
|
||||
if(loc.getHeader("Location") != null){
|
||||
Core.net.httpGet(loc.getHeader("Location"), result -> {
|
||||
if(result.getStatus() != HttpStatus.OK){
|
||||
err.get(result.getStatus());
|
||||
}else{
|
||||
handleMod(repo, result);
|
||||
}
|
||||
}, t2 -> Core.app.post(() -> modError(t2)));
|
||||
}else{
|
||||
handleMod(repo, loc);
|
||||
}
|
||||
}else{
|
||||
err.get(loc.getStatus());
|
||||
}
|
||||
}, t2 -> Core.app.post(() -> modError(t2)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,9 +2,7 @@ package mindustry.ui.dialogs;
|
||||
|
||||
import arc.func.*;
|
||||
import arc.graphics.*;
|
||||
import arc.input.*;
|
||||
import arc.scene.ui.*;
|
||||
import arc.scene.ui.layout.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.ui.*;
|
||||
|
||||
@@ -19,29 +17,24 @@ public class PaletteDialog extends Dialog{
|
||||
}
|
||||
|
||||
private void build(){
|
||||
Table table = new Table();
|
||||
cont.add(table);
|
||||
cont.table(table -> {
|
||||
for(int i = 0; i < playerColors.length; i++){
|
||||
Color color = playerColors[i];
|
||||
|
||||
for(int i = 0; i < playerColors.length; i++){
|
||||
Color color = playerColors[i];
|
||||
ImageButton button = table.button(Tex.whiteui, Styles.clearTogglei, 34, () -> {
|
||||
cons.get(color);
|
||||
hide();
|
||||
}).size(48).get();
|
||||
button.setChecked(player.color().equals(color));
|
||||
button.getStyle().imageUpColor = color;
|
||||
|
||||
ImageButton button = table.button(Tex.whiteui, Styles.clearTogglei, 34, () -> {
|
||||
cons.get(color);
|
||||
hide();
|
||||
}).size(48).get();
|
||||
button.setChecked(player.color().equals(color));
|
||||
button.getStyle().imageUpColor = color;
|
||||
|
||||
if(i % 4 == 3){
|
||||
table.row();
|
||||
if(i % 4 == 3){
|
||||
table.row();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
keyDown(key -> {
|
||||
if(key == KeyCode.escape || key == KeyCode.back)
|
||||
hide();
|
||||
});
|
||||
|
||||
closeOnBack();
|
||||
}
|
||||
|
||||
public void show(Cons<Color> cons){
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package mindustry.ui.dialogs;
|
||||
|
||||
import arc.*;
|
||||
import arc.input.*;
|
||||
import mindustry.gen.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
@@ -17,11 +16,7 @@ public class PausedDialog extends BaseDialog{
|
||||
|
||||
shown(this::rebuild);
|
||||
|
||||
keyDown(key -> {
|
||||
if(key == KeyCode.escape || key == KeyCode.back){
|
||||
hide();
|
||||
}
|
||||
});
|
||||
addCloseListener();
|
||||
}
|
||||
|
||||
void rebuild(){
|
||||
@@ -34,43 +29,34 @@ public class PausedDialog extends BaseDialog{
|
||||
});
|
||||
|
||||
if(!mobile){
|
||||
//TODO localize
|
||||
cont.label(() -> state.getSector() == null ? "" :
|
||||
("[lightgray]Next turn in [accent]" + state.getSector().displayTimeRemaining() +
|
||||
(state.rules.winWave > 0 && !state.getSector().isCaptured() ? "\n[lightgray]Reach wave[accent] " + state.rules.winWave + "[] to capture" : "")))
|
||||
.visible(() -> state.getSector() != null).colspan(2);
|
||||
cont.row();
|
||||
|
||||
float dw = 220f;
|
||||
cont.defaults().width(dw).height(55).pad(5f);
|
||||
|
||||
cont.button("@back", Icon.left, this::hide);
|
||||
cont.button("@settings", Icon.settings, ui.settings::show);
|
||||
|
||||
if(!state.rules.tutorial){
|
||||
if(!state.isCampaign() && !state.isEditor()){
|
||||
cont.row();
|
||||
cont.button("@savegame", Icon.save, save::show);
|
||||
cont.button("@loadgame", Icon.upload, load::show).disabled(b -> net.active());
|
||||
}
|
||||
cont.button("@back", Icon.left, this::hide).name("back");
|
||||
cont.button("@settings", Icon.settings, ui.settings::show).name("settings");
|
||||
|
||||
if(!state.isCampaign() && !state.isEditor()){
|
||||
cont.row();
|
||||
|
||||
cont.button("@hostserver", Icon.host, () -> {
|
||||
if(net.server() && steam){
|
||||
platform.inviteFriends();
|
||||
}else{
|
||||
if(steam){
|
||||
ui.host.runHost();
|
||||
}else{
|
||||
ui.host.show();
|
||||
}
|
||||
}
|
||||
}).disabled(b -> !((steam && net.server()) || !net.active())).colspan(2).width(dw * 2 + 20f).update(e -> e.setText(net.server() && steam ? "@invitefriends" : "@hostserver"));
|
||||
cont.button("@savegame", Icon.save, save::show);
|
||||
cont.button("@loadgame", Icon.upload, load::show).disabled(b -> net.active());
|
||||
}
|
||||
|
||||
cont.row();
|
||||
|
||||
cont.button("@hostserver", Icon.host, () -> {
|
||||
if(net.server() && steam){
|
||||
platform.inviteFriends();
|
||||
}else{
|
||||
if(steam){
|
||||
ui.host.runHost();
|
||||
}else{
|
||||
ui.host.show();
|
||||
}
|
||||
}
|
||||
}).disabled(b -> !((steam && net.server()) || !net.active())).colspan(2).width(dw * 2 + 20f).update(e -> e.setText(net.server() && steam ? "@invitefriends" : "@hostserver"));
|
||||
|
||||
cont.row();
|
||||
|
||||
cont.button("@quit", Icon.exit, this::showQuitConfirm).colspan(2).width(dw + 20f).update(s -> s.setText(control.saves.getCurrent() != null && control.saves.getCurrent().isAutosave() ? "@save.quit" : "@quit"));
|
||||
|
||||
}else{
|
||||
@@ -85,10 +71,7 @@ public class PausedDialog extends BaseDialog{
|
||||
|
||||
cont.buttonRow("@load", Icon.download, load::show).disabled(b -> net.active());
|
||||
}else if(state.isCampaign()){
|
||||
cont.buttonRow("@launchcore", Icon.up, () -> {
|
||||
hide();
|
||||
ui.planet.showLaunch(state.getSector(), player.team().core());
|
||||
}).disabled(b -> player.team().core() == null);
|
||||
cont.buttonRow("@research", Icon.tree, ui.research::show);
|
||||
|
||||
cont.row();
|
||||
|
||||
@@ -110,10 +93,7 @@ public class PausedDialog extends BaseDialog{
|
||||
}
|
||||
|
||||
void showQuitConfirm(){
|
||||
ui.showConfirm("@confirm", state.rules.tutorial ? "@quit.confirm.tutorial" : "@quit.confirm", () -> {
|
||||
if(state.rules.tutorial){
|
||||
Core.settings.put("playedtutorial", true);
|
||||
}
|
||||
ui.showConfirm("@confirm", "@quit.confirm", () -> {
|
||||
wasClient = net.client();
|
||||
if(net.client()) netClient.disconnectQuietly();
|
||||
runExitSave();
|
||||
@@ -127,7 +107,7 @@ public class PausedDialog extends BaseDialog{
|
||||
return;
|
||||
}
|
||||
|
||||
if(control.saves.getCurrent() == null || !control.saves.getCurrent().isAutosave() || state.rules.tutorial || wasClient){
|
||||
if(control.saves.getCurrent() == null || !control.saves.getCurrent().isAutosave() || wasClient){
|
||||
logic.reset();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -12,52 +12,85 @@ import arc.scene.*;
|
||||
import arc.scene.event.*;
|
||||
import arc.scene.ui.*;
|
||||
import arc.scene.ui.layout.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import arc.util.ArcAnnotate.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.content.TechTree.*;
|
||||
import mindustry.core.*;
|
||||
import mindustry.ctype.*;
|
||||
import mindustry.game.Objectives.*;
|
||||
import mindustry.game.SectorInfo.*;
|
||||
import mindustry.game.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.graphics.g3d.*;
|
||||
import mindustry.input.*;
|
||||
import mindustry.io.legacy.*;
|
||||
import mindustry.maps.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.ui.*;
|
||||
import mindustry.world.blocks.storage.*;
|
||||
import mindustry.world.blocks.storage.CoreBlock.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
import static mindustry.graphics.g3d.PlanetRenderer.*;
|
||||
import static mindustry.ui.dialogs.PlanetDialog.Mode.*;
|
||||
|
||||
public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
|
||||
final FrameBuffer buffer = new FrameBuffer(2, 2, true);
|
||||
final PlanetRenderer planets = renderer.planets;
|
||||
final LaunchLoadoutDialog loadouts = new LaunchLoadoutDialog();
|
||||
final Table stable = new Table().background(Styles.black3);
|
||||
//if true, enables launching anywhere for testing
|
||||
public static boolean debugSelect = false;
|
||||
public static float sectorShowDuration = 60f * 2.4f;
|
||||
|
||||
int launchRange;
|
||||
float zoom = 1f, selectAlpha = 1f;
|
||||
@Nullable Sector selected, hovered, launchSector;
|
||||
CoreBuild launcher;
|
||||
Mode mode = look;
|
||||
boolean launching;
|
||||
Cons<Sector> listener = s -> {};
|
||||
public final FrameBuffer buffer = new FrameBuffer(2, 2, true);
|
||||
public final PlanetRenderer planets = renderer.planets;
|
||||
public final LaunchLoadoutDialog loadouts = new LaunchLoadoutDialog();
|
||||
|
||||
public float zoom = 1f, selectAlpha = 1f;
|
||||
public @Nullable Sector selected, hovered, launchSector;
|
||||
public Mode mode = look;
|
||||
public boolean launching;
|
||||
public Cons<Sector> listener = s -> {};
|
||||
|
||||
public Seq<Sector> newPresets = new Seq<>();
|
||||
public float presetShow = 0f;
|
||||
public boolean showed = false;
|
||||
|
||||
public Table sectorTop = new Table();
|
||||
public Label hoverLabel = new Label("");
|
||||
|
||||
public PlanetDialog(){
|
||||
super("", Styles.fullDialog);
|
||||
|
||||
shouldPause = true;
|
||||
|
||||
getCell(buttons).padBottom(-4);
|
||||
buttons.background(Styles.black).defaults().growX().height(64f).pad(0);
|
||||
|
||||
keyDown(key -> {
|
||||
if(key == KeyCode.escape || key == KeyCode.back){
|
||||
Core.app.post(this::hide);
|
||||
if(key == KeyCode.escape || key == KeyCode.back || key == Core.keybinds.get(Binding.planet_map).key){
|
||||
if(showing() && newPresets.size > 1){
|
||||
//clear all except first, which is the last sector.
|
||||
newPresets.truncate(1);
|
||||
}else if(selected != null){
|
||||
selected = null;
|
||||
updateSelected();
|
||||
}else{
|
||||
Core.app.post(this::hide);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
hoverLabel.setStyle(Styles.outlineLabel);
|
||||
hoverLabel.setAlignment(Align.center);
|
||||
|
||||
rebuildButtons();
|
||||
|
||||
onResize(this::rebuildButtons);
|
||||
|
||||
dragged((cx, cy) -> {
|
||||
//no multitouch drag
|
||||
if(Core.input.getTouches() > 1) return;
|
||||
|
||||
if(showing()){
|
||||
newPresets.clear();
|
||||
}
|
||||
|
||||
Vec3 pos = planets.camPos;
|
||||
|
||||
float upV = pos.angle(Vec3.Y);
|
||||
@@ -80,21 +113,122 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
|
||||
zoom = Mathf.clamp(zoom + value / 10f, 0.5f, 2f);
|
||||
});
|
||||
|
||||
addCaptureListener(new ElementGestureListener(){
|
||||
float lastZoom = -1f;
|
||||
|
||||
@Override
|
||||
public void zoom(InputEvent event, float initialDistance, float distance){
|
||||
if(lastZoom < 0){
|
||||
lastZoom = zoom;
|
||||
}
|
||||
|
||||
zoom = (Mathf.clamp(initialDistance / distance * lastZoom, 0.5f, 2f));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void touchUp(InputEvent event, float x, float y, int pointer, KeyCode button){
|
||||
lastZoom = zoom;
|
||||
}
|
||||
});
|
||||
|
||||
shown(this::setup);
|
||||
}
|
||||
|
||||
/** show with no limitations, just as a map. */
|
||||
@Override
|
||||
public Dialog show(){
|
||||
if(net.client()){
|
||||
ui.showInfo("@map.multiplayer");
|
||||
return this;
|
||||
}
|
||||
|
||||
//load legacy research
|
||||
if(Core.settings.has("unlocks") && !Core.settings.has("junction-unlocked")){
|
||||
Core.app.post(() -> {
|
||||
ui.showCustomConfirm("@research", "@research.legacy", "@research.load", "@research.discard", () -> {
|
||||
LegacyIO.readResearch();
|
||||
Core.settings.remove("unlocks");
|
||||
}, () -> Core.settings.remove("unlocks"));
|
||||
});
|
||||
}
|
||||
|
||||
rebuildButtons();
|
||||
mode = look;
|
||||
selected = hovered = launchSector = null;
|
||||
launching = false;
|
||||
|
||||
zoom = 1f;
|
||||
planets.zoom = 1f;
|
||||
selectAlpha = 0f;
|
||||
launchSector = state.getSector();
|
||||
presetShow = 0f;
|
||||
showed = false;
|
||||
|
||||
newPresets.clear();
|
||||
|
||||
//announce new presets
|
||||
for(SectorPreset preset : content.sectors()){
|
||||
if(preset.unlocked() && !preset.alwaysUnlocked && !preset.sector.info.shown && !preset.sector.hasBase()){
|
||||
newPresets.add(preset.sector);
|
||||
preset.sector.info.shown = true;
|
||||
preset.sector.saveInfo();
|
||||
}
|
||||
}
|
||||
|
||||
if(newPresets.any()){
|
||||
newPresets.add(planets.planet.getLastSector());
|
||||
}
|
||||
|
||||
newPresets.reverse();
|
||||
updateSelected();
|
||||
|
||||
if(planets.planet.getLastSector() != null){
|
||||
lookAt(planets.planet.getLastSector());
|
||||
}
|
||||
|
||||
return super.show();
|
||||
}
|
||||
|
||||
void rebuildButtons(){
|
||||
buttons.clearChildren();
|
||||
|
||||
buttons.bottom();
|
||||
|
||||
if(Core.graphics.isPortrait()){
|
||||
buttons.add(sectorTop).colspan(2).fillX().row();
|
||||
addBack();
|
||||
addTech();
|
||||
}else{
|
||||
addBack();
|
||||
buttons.add().growX();
|
||||
buttons.add(sectorTop).minWidth(230f);
|
||||
buttons.add().growX();
|
||||
addTech();
|
||||
}
|
||||
}
|
||||
|
||||
void addBack(){
|
||||
buttons.button("@back", Icon.left, this::hide).size(200, 54).pad(2).bottom();
|
||||
}
|
||||
|
||||
void addTech(){
|
||||
buttons.button("@techtree", Icon.tree, () -> ui.research.show()).size(200, 54).pad(2).bottom();
|
||||
}
|
||||
|
||||
public void showOverview(){
|
||||
//TODO implement later if necessary
|
||||
/*
|
||||
sectors.captured = Captured Sectors
|
||||
sectors.explored = Explored Sectors
|
||||
sectors.production.total = Total Production
|
||||
sectors.resources.total = Total Resources
|
||||
*/
|
||||
var dialog = new BaseDialog("@overview");
|
||||
dialog.addCloseButton();
|
||||
|
||||
dialog.add("@sectors.captured");
|
||||
}
|
||||
|
||||
public void showSelect(Sector sector, Cons<Sector> listener){
|
||||
selected = null;
|
||||
hovered = null;
|
||||
@@ -104,7 +238,7 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
|
||||
//update view to sector
|
||||
lookAt(sector);
|
||||
zoom = 1f;
|
||||
planets.zoom = 2f;
|
||||
planets.zoom = 1f;
|
||||
selectAlpha = 0f;
|
||||
launchSector = sector;
|
||||
|
||||
@@ -113,54 +247,58 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
|
||||
super.show();
|
||||
}
|
||||
|
||||
public void showLaunch(Sector sector, CoreBuild launcher){
|
||||
if(launcher == null) return;
|
||||
|
||||
this.launcher = launcher;
|
||||
selected = null;
|
||||
hovered = null;
|
||||
launching = false;
|
||||
|
||||
//update view to sector
|
||||
lookAt(sector);
|
||||
zoom = 1f;
|
||||
planets.zoom = 2f;
|
||||
selectAlpha = 0f;
|
||||
launchRange = ((CoreBlock)launcher.block).launchRange;
|
||||
launchSector = sector;
|
||||
|
||||
mode = launch;
|
||||
|
||||
super.show();
|
||||
}
|
||||
|
||||
private void lookAt(Sector sector){
|
||||
void lookAt(Sector sector){
|
||||
planets.camPos.set(Tmp.v33.set(sector.tile.v).rotate(Vec3.Y, -sector.planet.getRotation()));
|
||||
}
|
||||
|
||||
boolean canSelect(Sector sector){
|
||||
if(mode == select) return sector.hasBase();
|
||||
if(sector.hasBase()) return true;
|
||||
//preset sectors can only be selected once unlocked
|
||||
if(sector.preset != null){
|
||||
TechNode node = sector.preset.node();
|
||||
return node == null || node.parent == null || node.parent.content.unlocked();
|
||||
}
|
||||
|
||||
return mode == launch &&
|
||||
(sector.tile.v.within(launchSector.tile.v, (launchRange + 0.5f) * planets.planet.sectorApproxRadius*2) //within range
|
||||
|| (sector.preset != null && sector.preset.unlocked())); //is an unlocked preset
|
||||
return sector.hasBase() || sector.near().contains(Sector::hasBase); //near an occupied sector
|
||||
}
|
||||
|
||||
Sector findLauncher(Sector to){
|
||||
Sector launchSector = this.launchSector != null && this.launchSector.hasBase() ? this.launchSector : null;
|
||||
//directly nearby.
|
||||
if(to.near().contains(launchSector)) return launchSector;
|
||||
|
||||
Sector launchFrom = launchSector;
|
||||
if(launchFrom == null || (to.preset == null && !to.near().contains(launchSector))){
|
||||
//TODO pick one with the most resources
|
||||
launchFrom = to.near().find(Sector::hasBase);
|
||||
if(launchFrom == null && to.preset != null){
|
||||
if(launchSector != null) return launchSector;
|
||||
launchFrom = planets.planet.sectors.min(s -> !s.hasBase() ? Float.MAX_VALUE : s.tile.v.dst2(to.tile.v));
|
||||
if(!launchFrom.hasBase()) launchFrom = null;
|
||||
}
|
||||
}
|
||||
|
||||
return launchFrom;
|
||||
}
|
||||
|
||||
boolean showing(){
|
||||
return newPresets.any();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderSectors(Planet planet){
|
||||
|
||||
//draw all sector stuff
|
||||
for(Sector sec : planet.sectors){
|
||||
|
||||
if(selectAlpha > 0.01f){
|
||||
if(canSelect(sec) || sec.unlocked()){
|
||||
if(sec.baseCoverage > 0){
|
||||
planets.fill(sec, Tmp.c1.set(Team.crux.color).a(0.5f * sec.baseCoverage * selectAlpha), -0.002f);
|
||||
}
|
||||
if(selectAlpha > 0.01f){
|
||||
for(Sector sec : planet.sectors){
|
||||
if(canSelect(sec) || sec.unlocked() || debugSelect){
|
||||
|
||||
Color color =
|
||||
sec.hasBase() ? Team.sharded.color :
|
||||
sec.preset != null ? Team.derelict.color :
|
||||
sec.hasBase() ? Tmp.c2.set(Team.sharded.color).lerp(Team.crux.color, sec.hasEnemyBase() ? 0.5f : 0f) :
|
||||
sec.preset != null ?
|
||||
sec.preset.unlocked() ? Tmp.c2.set(Team.derelict.color).lerp(Color.white, Mathf.absin(Time.time, 10f, 1f)) :
|
||||
Color.gray :
|
||||
sec.hasEnemyBase() ? Team.crux.color :
|
||||
null;
|
||||
|
||||
@@ -173,8 +311,10 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
|
||||
}
|
||||
}
|
||||
|
||||
if(launchSector != null){
|
||||
planets.fill(launchSector, hoverColor, -0.001f);
|
||||
Sector current = state.getSector() != null && state.getSector().isBeingPlayed() && state.getSector().planet == planets.planet ? state.getSector() : null;
|
||||
|
||||
if(current != null){
|
||||
planets.fill(current, hoverColor, -0.001f);
|
||||
}
|
||||
|
||||
//draw hover border
|
||||
@@ -191,57 +331,93 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
|
||||
|
||||
planets.batch.flush(Gl.triangles);
|
||||
|
||||
if(mode == launch || mode == select){
|
||||
if(hovered != launchSector && hovered != null && canSelect(hovered)){
|
||||
planets.drawArc(planet, launchSector.tile.v, hovered.tile.v);
|
||||
if(hovered != null && !hovered.hasBase()){
|
||||
Sector launchFrom = findLauncher(hovered);
|
||||
if(launchFrom != null && hovered != launchFrom && canSelect(hovered)){
|
||||
planets.drawArc(planet, launchFrom.tile.v, hovered.tile.v);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
//TODO render arcs
|
||||
if(selected != null && selected.preset != null){
|
||||
for(Objective o : selected.preset.requirements){
|
||||
if(o instanceof SectorObjective){
|
||||
SectorPreset preset = ((SectorObjective)o).preset;
|
||||
planets.drawArc(planet, selected.tile.v, preset.sector.tile.v);
|
||||
if(selectAlpha > 0.001f){
|
||||
for(Sector sec : planet.sectors){
|
||||
if(sec.hasBase()){
|
||||
for(Sector enemy : sec.near()){
|
||||
if(enemy.hasEnemyBase()){
|
||||
planets.drawArc(planet, enemy.tile.v, sec.tile.v, Team.crux.color.write(Tmp.c2).a(selectAlpha), Color.clear, 0.24f, 110f, 25);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderProjections(){
|
||||
if(hovered != null){
|
||||
planets.drawPlane(hovered, () -> {
|
||||
Draw.color(Color.white, Pal.accent, Mathf.absin(5f, 1f));
|
||||
public void renderProjections(Planet planet){
|
||||
float iw = 48f/4f;
|
||||
|
||||
TextureRegion icon = hovered.locked() && !canSelect(hovered) ? Icon.lock.getRegion() : null;
|
||||
for(Sector sec : planet.sectors){
|
||||
if(sec != hovered){
|
||||
var preficon = sec.icon();
|
||||
var icon =
|
||||
sec.isAttacked() ? Fonts.getLargeIcon("warning") :
|
||||
!sec.hasBase() && sec.preset != null && sec.preset.unlocked() && preficon == null ?
|
||||
Fonts.getLargeIcon("terrain") :
|
||||
sec.preset != null && sec.preset.locked() && sec.preset.node() != null && !sec.preset.node().parent.content.locked() ? Fonts.getLargeIcon("lock") :
|
||||
preficon;
|
||||
var color = sec.preset != null && !sec.hasBase() ? Team.derelict.color : Team.sharded.color;
|
||||
|
||||
if(icon != null){
|
||||
Draw.rect(icon, 0, 0);
|
||||
planets.drawPlane(sec, () -> {
|
||||
Draw.color(color, selectAlpha);
|
||||
Draw.rect(icon, 0, 0, iw, iw * icon.height / icon.width);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Draw.reset();
|
||||
|
||||
if(hovered != null){
|
||||
planets.drawPlane(hovered, () -> {
|
||||
Draw.color(hovered.isAttacked() ? Pal.remove : Color.white, Pal.accent, Mathf.absin(5f, 1f));
|
||||
|
||||
var icon = hovered.locked() && !canSelect(hovered) ? Fonts.getLargeIcon("lock") : hovered.isAttacked() ? Fonts.getLargeIcon("warning") : hovered.icon();
|
||||
|
||||
if(icon != null){
|
||||
Draw.rect(icon, 0, 0, iw, iw * icon.height / icon.width);
|
||||
}
|
||||
|
||||
Draw.reset();
|
||||
});
|
||||
}
|
||||
|
||||
Draw.reset();
|
||||
}
|
||||
|
||||
void setup(){
|
||||
zoom = planets.zoom = 1f;
|
||||
selectAlpha = 1f;
|
||||
ui.minimapfrag.hide();
|
||||
|
||||
cont.clear();
|
||||
titleTable.remove();
|
||||
clearChildren();
|
||||
|
||||
margin(0f);
|
||||
|
||||
cont.stack(
|
||||
stack(
|
||||
new Element(){
|
||||
{
|
||||
//add listener to the background rect, so it doesn't get unnecessary touch input
|
||||
addListener(new ElementGestureListener(){
|
||||
@Override
|
||||
public void tap(InputEvent event, float x, float y, int count, KeyCode button){
|
||||
if(hovered != null && (mode == launch ? canSelect(hovered) && hovered != launchSector : hovered.unlocked())){
|
||||
if(showing()) return;
|
||||
|
||||
if(hovered != null && selected == hovered && count == 2){
|
||||
playSelected();
|
||||
}
|
||||
|
||||
if(hovered != null && (canSelect(hovered) || debugSelect)){
|
||||
selected = hovered;
|
||||
}
|
||||
|
||||
@@ -254,14 +430,47 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
|
||||
|
||||
@Override
|
||||
public void draw(){
|
||||
planets.orbitAlpha = selectAlpha;
|
||||
planets.render(PlanetDialog.this);
|
||||
Core.scene.setScrollFocus(PlanetDialog.this);
|
||||
if(Core.scene.getDialog() == PlanetDialog.this){
|
||||
Core.scene.setScrollFocus(PlanetDialog.this);
|
||||
}
|
||||
}
|
||||
},
|
||||
//info text
|
||||
new Table(t -> {
|
||||
//TODO localize
|
||||
t.touchable = Touchable.disabled;
|
||||
t.top();
|
||||
t.label(() -> mode == select ? "@sectors.select" : mode == launch ? "Select Launch Sector" : "Turn " + universe.turn()).style(Styles.outlineLabel).color(Pal.accent);
|
||||
t.label(() -> mode == select ? "@sectors.select" : "").style(Styles.outlineLabel).color(Pal.accent);
|
||||
}),
|
||||
buttons,
|
||||
//planet selection
|
||||
new Table(t -> {
|
||||
t.right();
|
||||
if(content.planets().count(p -> p.accessible) > 1){
|
||||
t.table(Styles.black6, pt -> {
|
||||
pt.add("@planets").color(Pal.accent);
|
||||
pt.row();
|
||||
pt.image().growX().height(4f).pad(6f).color(Pal.accent);
|
||||
pt.row();
|
||||
for(int i = 0; i < content.planets().size; i++){
|
||||
Planet planet = content.planets().get(i);
|
||||
if(planet.accessible){
|
||||
pt.button(planet.localizedName, Styles.clearTogglet, () -> {
|
||||
selected = null;
|
||||
launchSector = null;
|
||||
renderer.planets.planet = planet;
|
||||
}).width(200).height(40).growX().update(bb -> bb.setChecked(renderer.planets.planet == planet));
|
||||
pt.row();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}),
|
||||
|
||||
new Table(t -> {
|
||||
t.top();
|
||||
//t.add(sectorTop);
|
||||
})).grow();
|
||||
|
||||
}
|
||||
@@ -286,23 +495,59 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
|
||||
}
|
||||
}
|
||||
|
||||
public void lookAt(Sector sector, float alpha){
|
||||
float len = planets.camPos.len();
|
||||
planets.camPos.slerp(Tmp.v31.set(sector.tile.v).rotate(Vec3.Y, -sector.planet.getRotation()).setLength(len), alpha);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void act(float delta){
|
||||
super.act(delta);
|
||||
|
||||
if(selected != null){
|
||||
addChild(stable);
|
||||
Vec3 pos = planets.cam.project(Tmp.v31.set(selected.tile.v).setLength(PlanetRenderer.outlineRad).rotate(Vec3.Y, -planets.planet.getRotation()).add(planets.planet.position));
|
||||
stable.setPosition(pos.x, pos.y, Align.center);
|
||||
stable.toFront();
|
||||
if(hovered != null && !mobile){
|
||||
addChild(hoverLabel);
|
||||
hoverLabel.toFront();
|
||||
hoverLabel.touchable = Touchable.disabled;
|
||||
|
||||
//smooth camera toward the sector
|
||||
if(mode == launch && launching){
|
||||
float len = planets.camPos.len();
|
||||
planets.camPos.slerp(Tmp.v31.set(selected.tile.v).rotate(Vec3.Y,-selected.planet.getRotation()).setLength(len), 0.1f);
|
||||
Vec3 pos = planets.cam.project(Tmp.v31.set(hovered.tile.v).setLength(PlanetRenderer.outlineRad).rotate(Vec3.Y, -planets.planet.getRotation()).add(planets.planet.position));
|
||||
hoverLabel.setPosition(pos.x - Core.scene.marginLeft, pos.y - Core.scene.marginBottom, Align.center);
|
||||
|
||||
hoverLabel.getText().setLength(0);
|
||||
if(hovered != null){
|
||||
StringBuilder tx = hoverLabel.getText();
|
||||
if(!canSelect(hovered)){
|
||||
tx.append("[gray]").append(Iconc.lock).append(" ").append(Core.bundle.get("locked"));
|
||||
}else{
|
||||
tx.append("[accent][[ [white]").append(hovered.name()).append("[accent] ]");
|
||||
}
|
||||
}
|
||||
hoverLabel.invalidateHierarchy();
|
||||
}else{
|
||||
stable.remove();
|
||||
hoverLabel.remove();
|
||||
}
|
||||
|
||||
if(launching && selected != null){
|
||||
lookAt(selected, 0.1f);
|
||||
}
|
||||
|
||||
if(showing()){
|
||||
Sector to = newPresets.peek();
|
||||
|
||||
presetShow += Time.delta;
|
||||
|
||||
lookAt(to, 0.11f);
|
||||
zoom = 0.75f;
|
||||
|
||||
if(presetShow >= 20f && !showed && newPresets.size > 1){
|
||||
showed = true;
|
||||
ui.announce(Iconc.lockOpen + " [accent]" + to.name(), 2f);
|
||||
}
|
||||
|
||||
if(presetShow > sectorShowDuration){
|
||||
newPresets.pop();
|
||||
showed = false;
|
||||
presetShow = 0f;
|
||||
}
|
||||
}
|
||||
|
||||
if(planets.planet.isLandable()){
|
||||
@@ -315,11 +560,95 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
|
||||
selectAlpha = Mathf.lerpDelta(selectAlpha, Mathf.num(planets.zoom < 1.9f), 0.1f);
|
||||
}
|
||||
|
||||
void showStats(Sector sector){
|
||||
BaseDialog dialog = new BaseDialog(sector.name());
|
||||
|
||||
dialog.cont.pane(c -> {
|
||||
Cons2<ObjectMap<Item, ExportStat>, String> display = (stats, name) -> {
|
||||
Table t = new Table().left();
|
||||
|
||||
float scl = sector.getProductionScale();
|
||||
|
||||
int[] i = {0};
|
||||
|
||||
stats.each((item, stat) -> {
|
||||
int total = (int)(stat.mean * 60 * scl);
|
||||
if(total > 1){
|
||||
t.image(item.icon(Cicon.small)).padRight(3);
|
||||
t.add(UI.formatAmount(total) + " " + Core.bundle.get("unit.perminute")).color(Color.lightGray).padRight(3);
|
||||
if(++i[0] % 3 == 0){
|
||||
t.row();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if(t.getChildren().any()){
|
||||
c.add(name).left().row();
|
||||
c.add(t).padLeft(10f).left().row();
|
||||
}
|
||||
};
|
||||
|
||||
c.defaults().padBottom(5);
|
||||
|
||||
c.add(Core.bundle.get("sectors.time") + " [accent]" + sector.save.getPlayTime()).left().row();
|
||||
|
||||
if(sector.info.waves && sector.hasBase()){
|
||||
c.add(Core.bundle.get("sectors.wave") + " [accent]" + (sector.info.wave + sector.info.wavesPassed)).left().row();
|
||||
}
|
||||
|
||||
if(sector.isAttacked() || !sector.hasBase()){
|
||||
c.add(Core.bundle.get("sectors.threat") + " [accent]" + sector.displayThreat()).left().row();
|
||||
}
|
||||
|
||||
if(sector.save != null && sector.info.resources.any()){
|
||||
c.add("@sectors.resources").left().row();
|
||||
c.table(t -> {
|
||||
for(UnlockableContent uc : sector.info.resources){
|
||||
t.image(uc.icon(Cicon.small)).padRight(3).size(Cicon.small.size);
|
||||
}
|
||||
}).padLeft(10f).left().row();
|
||||
}
|
||||
|
||||
//production
|
||||
display.get(sector.info.production, "@sectors.production");
|
||||
|
||||
//export
|
||||
display.get(sector.info.export, "@sectors.export");
|
||||
|
||||
ItemSeq items = sector.items();
|
||||
|
||||
//stored resources
|
||||
if(sector.hasBase() && items.total > 0){
|
||||
|
||||
c.add("@sectors.stored").left().row();
|
||||
c.table(t -> {
|
||||
t.left();
|
||||
|
||||
t.table(res -> {
|
||||
|
||||
int i = 0;
|
||||
for(ItemStack stack : items){
|
||||
res.image(stack.item.icon(Cicon.small)).padRight(3);
|
||||
res.add(UI.formatAmount(Math.max(stack.amount, 0))).color(Color.lightGray);
|
||||
if(++i % 4 == 0){
|
||||
res.row();
|
||||
}
|
||||
}
|
||||
}).padLeft(10f);
|
||||
}).left().row();
|
||||
}
|
||||
});
|
||||
|
||||
dialog.addCloseButton();
|
||||
dialog.show();
|
||||
}
|
||||
|
||||
void updateSelected(){
|
||||
Sector sector = selected;
|
||||
Table stable = sectorTop;
|
||||
|
||||
if(sector == null){
|
||||
stable.remove();
|
||||
stable.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -327,117 +656,117 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
|
||||
stable.clear();
|
||||
stable.background(Styles.black6);
|
||||
|
||||
stable.add("[accent]" + (sector.preset == null ? sector.id : sector.preset.localizedName)).row();
|
||||
stable.image().color(Pal.accent).fillX().height(3f).pad(3f).row();
|
||||
stable.add(sector.save != null ? sector.save.getPlayTime() : "@sectors.unexplored").row();
|
||||
if(sector.hasWaves() || sector.hasEnemyBase()){
|
||||
stable.add("[accent]Difficulty: " + (int)(sector.baseCoverage * 10)).row();
|
||||
}
|
||||
stable.table(title -> {
|
||||
title.add("[accent]" + sector.name()).padLeft(3);
|
||||
if(sector.preset == null){
|
||||
title.add().growX();
|
||||
|
||||
if(sector.hasBase() && sector.hasWaves()){
|
||||
//TODO localize when finalized
|
||||
//these mechanics are likely to change and as such are not added to the bundle
|
||||
stable.add("[scarlet]Under attack!");
|
||||
stable.row();
|
||||
stable.add("[accent]" + Mathf.ceil(sectorDestructionTurns - (sector.getSecondsPassed() * 60) / turnDuration) + " turn(s)\nuntil destruction");
|
||||
stable.row();
|
||||
}
|
||||
title.button(Icon.pencilSmall, Styles.clearPartiali, () -> {
|
||||
ui.showTextInput("@sectors.rename", "@name", 20, sector.name(), v -> {
|
||||
sector.setName(v);
|
||||
updateSelected();
|
||||
});
|
||||
}).size(40f).padLeft(4);
|
||||
}
|
||||
|
||||
if(sector.save != null){
|
||||
stable.add("@sectors.resources").row();
|
||||
stable.table(t -> {
|
||||
var icon = Icon.icons.get(sector.info.icon + "Small");
|
||||
|
||||
if(sector.save != null && sector.save.meta.secinfo != null && sector.save.meta.secinfo.resources.any()){
|
||||
t.left();
|
||||
int idx = 0;
|
||||
int max = 5;
|
||||
for(UnlockableContent c : sector.save.meta.secinfo.resources){
|
||||
t.image(c.icon(Cicon.small)).padRight(3);
|
||||
if(++idx % max == 0) t.row();
|
||||
}
|
||||
}else{
|
||||
t.add("@unknown").color(Color.lightGray);
|
||||
}
|
||||
title.button(icon == null ? Icon.noneSmall : icon, Styles.clearPartiali, () -> {
|
||||
new Dialog(""){{
|
||||
closeOnBack();
|
||||
setFillParent(true);
|
||||
cont.pane(t -> {
|
||||
t.marginRight(19f);
|
||||
t.defaults().size(48);
|
||||
|
||||
t.button(Icon.none, Styles.clearTogglei, () -> {
|
||||
sector.info.icon = null;
|
||||
sector.saveInfo();
|
||||
hide();
|
||||
updateSelected();
|
||||
}).checked(sector.info.icon == null);
|
||||
|
||||
}).fillX().row();
|
||||
}
|
||||
int i = 1;
|
||||
for(var entry : Icon.icons.entries()){
|
||||
if(entry.key.endsWith("Small") || entry.key.contains("none")) continue;
|
||||
String key = entry.key;
|
||||
|
||||
//production
|
||||
if(sector.hasBase() && sector.save.meta.hasProduction){
|
||||
stable.add("@sectors.production").row();
|
||||
stable.table(t -> {
|
||||
t.left();
|
||||
t.button(entry.value, Styles.cleari, () -> {
|
||||
sector.info.icon = key;
|
||||
sector.saveInfo();
|
||||
hide();
|
||||
updateSelected();
|
||||
}).checked(entry.key.equals(sector.info.icon));
|
||||
|
||||
sector.save.meta.secinfo.production.each((item, stat) -> {
|
||||
int total = (int)(stat.mean * 60);
|
||||
if(total > 1){
|
||||
t.image(item.icon(Cicon.small)).padRight(3);
|
||||
t.add(UI.formatAmount(total) + " " + Core.bundle.get("unit.perminute")).color(Color.lightGray);
|
||||
t.row();
|
||||
}
|
||||
});
|
||||
}).row();
|
||||
}
|
||||
|
||||
//stored resources
|
||||
if(sector.hasBase() && sector.save.meta.secinfo.coreItems.size > 0){
|
||||
stable.add("@sectors.stored").row();
|
||||
stable.table(t -> {
|
||||
t.left();
|
||||
|
||||
t.table(res -> {
|
||||
ItemSeq items = sector.calculateItems();
|
||||
|
||||
int i = 0;
|
||||
for(ItemStack stack : items){
|
||||
res.image(stack.item.icon(Cicon.small)).padRight(3);
|
||||
res.add(UI.formatAmount(stack.amount)).color(Color.lightGray);
|
||||
if(++i % 2 == 0){
|
||||
res.row();
|
||||
if(++i % 8 == 0) t.row();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}}.show();
|
||||
}).size(40f);
|
||||
}).row();
|
||||
|
||||
stable.image().color(Pal.accent).fillX().height(3f).pad(3f).row();
|
||||
|
||||
boolean locked = sector.preset != null && sector.preset.locked() && !sector.hasBase() && sector.preset.node() != null;
|
||||
|
||||
if(locked){
|
||||
stable.table(r -> {
|
||||
r.add("@complete").colspan(2).left();
|
||||
r.row();
|
||||
for(Objective o : sector.preset.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();
|
||||
}
|
||||
}).row();
|
||||
}else if(!sector.hasBase()){
|
||||
stable.add(Core.bundle.get("sectors.threat") + " [accent]" + sector.displayThreat()).row();
|
||||
}
|
||||
|
||||
if(sector.isAttacked()){
|
||||
stable.add(Core.bundle.format("sectors.underattack", (int)(sector.info.damage * 100)));
|
||||
stable.row();
|
||||
|
||||
if(sector.info.wavesSurvived >= 0 && sector.info.wavesSurvived - sector.info.wavesPassed >= 0 && !sector.isBeingPlayed()){
|
||||
int toCapture = sector.info.attack || sector.info.winWave <= 1 ? -1 : sector.info.winWave - (sector.info.wave + sector.info.wavesPassed);
|
||||
boolean plus = (sector.info.wavesSurvived - sector.info.wavesPassed) >= SectorDamage.maxRetWave - 1;
|
||||
stable.add(Core.bundle.format("sectors.survives", Math.min(sector.info.wavesSurvived - sector.info.wavesPassed, toCapture <= 0 ? 200 : toCapture) +
|
||||
(plus ? "+" : "") + (toCapture < 0 ? "" : "/" + toCapture)));
|
||||
stable.row();
|
||||
}
|
||||
}else if(sector.hasBase() && sector.near().contains(Sector::hasEnemyBase)){
|
||||
stable.add("@sectors.vulnerable");
|
||||
stable.row();
|
||||
}else if(!sector.hasBase() && sector.hasEnemyBase()){
|
||||
stable.add("@sectors.enemybase");
|
||||
stable.row();
|
||||
}
|
||||
|
||||
if(sector.save != null && sector.info.resources.any()){
|
||||
stable.table(t -> {
|
||||
t.add("@sectors.resources").padRight(4);
|
||||
for(UnlockableContent c : sector.info.resources){
|
||||
if(c == null) continue; //apparently this is possible.
|
||||
t.image(c.icon(Cicon.small)).padRight(3).size(Cicon.small.size);
|
||||
}
|
||||
}).padLeft(10f).fillX().row();
|
||||
}
|
||||
|
||||
stable.row();
|
||||
|
||||
if((sector.hasBase() && mode == look) || canSelect(sector) || (sector.preset != null && sector.preset.alwaysUnlocked)){
|
||||
stable.button(mode == select ? "@sectors.select" : sector.hasBase() ? "@sectors.resume" : "@sectors.launch", Styles.transt, () -> {
|
||||
if(sector.hasBase()){
|
||||
stable.button("@stats", Icon.info, Styles.transt, () -> showStats(sector)).height(40f).fillX().row();
|
||||
}
|
||||
|
||||
boolean shouldHide = true;
|
||||
|
||||
//save before launch.
|
||||
if(control.saves.getCurrent() != null && state.isGame() && mode != select){
|
||||
try{
|
||||
control.saves.getCurrent().save();
|
||||
}catch(Throwable e){
|
||||
e.printStackTrace();
|
||||
ui.showException("[accent]" + Core.bundle.get("savefail"), e);
|
||||
}
|
||||
}
|
||||
|
||||
if(mode == launch && !sector.hasBase()){
|
||||
Sector current = state.rules.sector;
|
||||
shouldHide = false;
|
||||
loadouts.show((CoreBlock)launcher.block, launcher, () -> {
|
||||
control.handleLaunch(launcher);
|
||||
launching = true;
|
||||
zoom = 0.5f;
|
||||
|
||||
ui.hudfrag.showLaunchDirect();
|
||||
Time.runTask(launchDuration, () -> control.playSector(current, sector));
|
||||
});
|
||||
}else if(mode == select){
|
||||
listener.get(sector);
|
||||
}else{
|
||||
control.playSector(sector);
|
||||
}
|
||||
|
||||
if(shouldHide) hide();
|
||||
}).growX().padTop(2f).height(50f).minWidth(170f).disabled(b -> state.rules.sector == sector && !state.isMenu());
|
||||
if((sector.hasBase() && mode == look) || canSelect(sector) || (sector.preset != null && sector.preset.alwaysUnlocked) || debugSelect){
|
||||
stable.button(
|
||||
mode == select ? "@sectors.select" :
|
||||
sector.isBeingPlayed() ? "@sectors.resume" :
|
||||
sector.hasBase() ? "@sectors.go" :
|
||||
locked ? "@locked" : "@sectors.launch",
|
||||
locked ? Icon.lock : Icon.play, this::playSelected).growX().height(54f).minWidth(170f).padTop(4).disabled(locked);
|
||||
}
|
||||
|
||||
stable.pack();
|
||||
@@ -453,22 +782,77 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
|
||||
float dot = planets.cam.direction.dot(Tmp.v31);
|
||||
stable.color.a = Math.max(dot, 0f)*2f;
|
||||
if(dot*2f <= -0.1f){
|
||||
stable.remove();
|
||||
selected = null;
|
||||
updateSelected();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
stable.act(0f);
|
||||
}
|
||||
|
||||
enum Mode{
|
||||
void playSelected(){
|
||||
if(selected == null) return;
|
||||
|
||||
Sector sector = selected;
|
||||
|
||||
if(sector.isBeingPlayed()){
|
||||
//already at this sector
|
||||
hide();
|
||||
return;
|
||||
}
|
||||
|
||||
if(sector.preset != null && sector.preset.locked() && !sector.hasBase()){
|
||||
return;
|
||||
}
|
||||
|
||||
boolean shouldHide = true;
|
||||
|
||||
//save before launch.
|
||||
if(control.saves.getCurrent() != null && state.isGame() && mode != select){
|
||||
try{
|
||||
control.saves.getCurrent().save();
|
||||
}catch(Throwable e){
|
||||
e.printStackTrace();
|
||||
ui.showException("[accent]" + Core.bundle.get("savefail"), e);
|
||||
}
|
||||
}
|
||||
|
||||
if(mode == look && !sector.hasBase()){
|
||||
shouldHide = false;
|
||||
Sector from = findLauncher(sector);
|
||||
if(from == null){
|
||||
//clear loadout information, so only the basic loadout gets used
|
||||
universe.clearLoadoutInfo();
|
||||
//free launch.
|
||||
control.playSector(sector);
|
||||
}else{
|
||||
CoreBlock block = from.info.bestCoreType instanceof CoreBlock b ? b : (CoreBlock)Blocks.coreShard;
|
||||
|
||||
loadouts.show(block, from, () -> {
|
||||
from.removeItems(universe.getLastLoadout().requirements());
|
||||
from.removeItems(universe.getLaunchResources());
|
||||
|
||||
launching = true;
|
||||
zoom = 0.5f;
|
||||
|
||||
ui.hudfrag.showLaunchDirect();
|
||||
Time.runTask(launchDuration, () -> control.playSector(from, sector));
|
||||
});
|
||||
}
|
||||
}else if(mode == select){
|
||||
listener.get(sector);
|
||||
}else{
|
||||
control.playSector(sector);
|
||||
}
|
||||
|
||||
if(shouldHide) hide();
|
||||
}
|
||||
|
||||
public enum Mode{
|
||||
/** Look around for existing sectors. Can only deploy. */
|
||||
look,
|
||||
/** Launch to a new location. */
|
||||
launch,
|
||||
/** Select a sector for some purpose. */
|
||||
select
|
||||
}
|
||||
|
||||
@@ -14,7 +14,6 @@ import arc.scene.ui.*;
|
||||
import arc.scene.ui.layout.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import arc.util.ArcAnnotate.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.content.TechTree.*;
|
||||
import mindustry.core.*;
|
||||
@@ -22,6 +21,7 @@ import mindustry.game.EventType.*;
|
||||
import mindustry.game.Objectives.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.input.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.ui.*;
|
||||
import mindustry.ui.layout.*;
|
||||
@@ -32,14 +32,14 @@ import java.util.*;
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class ResearchDialog extends BaseDialog{
|
||||
final float nodeSize = Scl.scl(60f);
|
||||
ObjectSet<TechTreeNode> nodes = new ObjectSet<>();
|
||||
TechTreeNode root = new TechTreeNode(TechTree.root, null);
|
||||
Rect bounds = new Rect();
|
||||
ItemsDisplay itemDisplay;
|
||||
View view;
|
||||
public final float nodeSize = Scl.scl(60f);
|
||||
public ObjectSet<TechTreeNode> nodes = new ObjectSet<>();
|
||||
public TechTreeNode root = new TechTreeNode(TechTree.root, null);
|
||||
public Rect bounds = new Rect();
|
||||
public ItemsDisplay itemDisplay;
|
||||
public View view;
|
||||
|
||||
ItemSeq items;
|
||||
public ItemSeq items;
|
||||
|
||||
public ResearchDialog(){
|
||||
super("");
|
||||
@@ -60,10 +60,13 @@ public class ResearchDialog extends BaseDialog{
|
||||
//add global counts of each sector
|
||||
for(Planet planet : content.planets()){
|
||||
for(Sector sector : planet.sectors){
|
||||
if(sector.hasSave()){
|
||||
ItemSeq cached = sector.calculateItems();
|
||||
add(cached);
|
||||
if(sector.hasSave() && sector.hasBase()){
|
||||
ItemSeq cached = sector.items();
|
||||
cache.put(sector, cached);
|
||||
cached.each((item, amount) -> {
|
||||
values[item.id] += Math.max(amount, 0);
|
||||
total += Math.max(amount, 0);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -112,10 +115,16 @@ public class ResearchDialog extends BaseDialog{
|
||||
|
||||
addCloseButton();
|
||||
|
||||
keyDown(key -> {
|
||||
if(key == Core.keybinds.get(Binding.research).key){
|
||||
Core.app.post(this::hide);
|
||||
}
|
||||
});
|
||||
|
||||
buttons.button("@database", Icon.book, () -> {
|
||||
hide();
|
||||
ui.database.show();
|
||||
}).size(210f, 64f);
|
||||
}).size(210f, 64f).name("database");
|
||||
|
||||
//scaling/drag input
|
||||
addListener(new InputListener(){
|
||||
@@ -238,18 +247,6 @@ public class ResearchDialog extends BaseDialog{
|
||||
return node.content.unlocked() || !node.objectives.contains(i -> !i.complete());
|
||||
}
|
||||
|
||||
void showToast(String info){
|
||||
Table table = new Table();
|
||||
table.actions(Actions.fadeOut(0.5f, Interp.fade), Actions.remove());
|
||||
table.top().add(info);
|
||||
table.name = "toast";
|
||||
table.update(() -> {
|
||||
table.toFront();
|
||||
table.setPosition(Core.graphics.getWidth() / 2f, Core.graphics.getHeight() - 21, Align.top);
|
||||
});
|
||||
Core.scene.add(table);
|
||||
}
|
||||
|
||||
boolean locked(TechNode node){
|
||||
return node.content.locked();
|
||||
}
|
||||
@@ -267,11 +264,11 @@ public class ResearchDialog extends BaseDialog{
|
||||
}
|
||||
}
|
||||
|
||||
class TechTreeNode extends TreeNode<TechTreeNode>{
|
||||
final TechNode node;
|
||||
boolean visible = true, selectable = true;
|
||||
public class TechTreeNode extends TreeNode<TechTreeNode>{
|
||||
public final TechNode node;
|
||||
public boolean visible = true, selectable = true;
|
||||
|
||||
TechTreeNode(TechNode node, TechTreeNode parent){
|
||||
public TechTreeNode(TechNode node, TechTreeNode parent){
|
||||
this.node = node;
|
||||
this.parent = parent;
|
||||
this.width = this.height = nodeSize;
|
||||
@@ -285,11 +282,11 @@ public class ResearchDialog extends BaseDialog{
|
||||
}
|
||||
}
|
||||
|
||||
class View extends Group{
|
||||
float panX = 0, panY = -200, lastZoom = -1;
|
||||
boolean moved = false;
|
||||
ImageButton hoverNode;
|
||||
Table infoTable = new Table();
|
||||
public class View extends Group{
|
||||
public float panX = 0, panY = -200, lastZoom = -1;
|
||||
public boolean moved = false;
|
||||
public ImageButton hoverNode;
|
||||
public Table infoTable = new Table();
|
||||
|
||||
{
|
||||
infoTable.touchable = Touchable.enabled;
|
||||
@@ -388,7 +385,8 @@ public class ResearchDialog extends BaseDialog{
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
//can always spend when locked
|
||||
return node.content.locked();
|
||||
}
|
||||
|
||||
void spend(TechNode node){
|
||||
@@ -402,7 +400,7 @@ public class ResearchDialog extends BaseDialog{
|
||||
ItemStack completed = node.finishedRequirements[i];
|
||||
|
||||
//amount actually taken from inventory
|
||||
int used = Math.min(req.amount - completed.amount, items.get(req.item));
|
||||
int used = Math.max(Math.min(req.amount - completed.amount, items.get(req.item)), 0);
|
||||
items.remove(req.item, used);
|
||||
completed.amount += used;
|
||||
|
||||
@@ -431,7 +429,14 @@ public class ResearchDialog extends BaseDialog{
|
||||
|
||||
void unlock(TechNode node){
|
||||
node.content.unlock();
|
||||
showToast(Core.bundle.format("researched", node.content.localizedName));
|
||||
|
||||
//unlock parent nodes in multiplayer.
|
||||
TechNode parent = node.parent;
|
||||
while(parent != null){
|
||||
parent.content.unlock();
|
||||
parent = parent.parent;
|
||||
}
|
||||
|
||||
checkNodes(root);
|
||||
hoverNode = null;
|
||||
treeLayout();
|
||||
@@ -474,7 +479,7 @@ public class ResearchDialog extends BaseDialog{
|
||||
infoTable.table(b -> {
|
||||
b.margin(0).left().defaults().left();
|
||||
|
||||
if(selectable){
|
||||
if(selectable && (node.content.description != null || node.content.stats.toMap().size > 0)){
|
||||
b.button(Icon.info, Styles.cleari, () -> ui.content.show(node.content)).growY().width(50f);
|
||||
}
|
||||
b.add().grow();
|
||||
@@ -499,8 +504,7 @@ public class ResearchDialog extends BaseDialog{
|
||||
if(shine != null) shiny |= shine[i];
|
||||
}
|
||||
|
||||
int percent = (int)(used / sum * 100);
|
||||
Label label = t.add(Core.bundle.format("research.progress", percent)).left().get();
|
||||
Label label = t.add(Core.bundle.format("research.progress", Math.min((int)(used / sum * 100), 99))).left().get();
|
||||
|
||||
if(shiny){
|
||||
label.setColor(Pal.accent);
|
||||
@@ -570,7 +574,7 @@ public class ResearchDialog extends BaseDialog{
|
||||
});
|
||||
|
||||
infoTable.row();
|
||||
if(node.content.description != null && selectable){
|
||||
if(node.content.description != null && node.content.inlineDescription && selectable){
|
||||
infoTable.table(t -> t.margin(3f).left().labelWrap(node.content.displayDescription()).color(Color.lightGray).growX()).fillX();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package mindustry.ui.dialogs;
|
||||
|
||||
import arc.Core;
|
||||
import arc.scene.ui.TextButton;
|
||||
import arc.util.Time;
|
||||
import mindustry.game.Saves.SaveSlot;
|
||||
import arc.*;
|
||||
import arc.scene.ui.*;
|
||||
import arc.util.*;
|
||||
import mindustry.game.Saves.*;
|
||||
import mindustry.gen.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
@@ -4,6 +4,7 @@ import arc.*;
|
||||
import arc.graphics.*;
|
||||
import arc.graphics.Texture.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.input.*;
|
||||
import arc.math.*;
|
||||
import arc.scene.style.*;
|
||||
import arc.scene.ui.*;
|
||||
@@ -11,7 +12,7 @@ import arc.scene.ui.ImageButton.*;
|
||||
import arc.scene.ui.TextButton.*;
|
||||
import arc.scene.ui.layout.*;
|
||||
import arc.util.*;
|
||||
import mindustry.Vars;
|
||||
import mindustry.*;
|
||||
import mindustry.game.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
@@ -29,9 +30,7 @@ public class SchematicsDialog extends BaseDialog{
|
||||
|
||||
public SchematicsDialog(){
|
||||
super("@schematics");
|
||||
Core.assets.load("sprites/schematic-background.png", Texture.class).loaded = t -> {
|
||||
((Texture)t).setWrap(TextureWrap.repeat);
|
||||
};
|
||||
Core.assets.load("sprites/schematic-background.png", Texture.class).loaded = t -> ((Texture)t).setWrap(TextureWrap.repeat);
|
||||
|
||||
shouldPause = true;
|
||||
addCloseButton();
|
||||
@@ -106,18 +105,36 @@ public class SchematicsDialog extends BaseDialog{
|
||||
});
|
||||
|
||||
buttons.button(Icon.pencil, style, () -> {
|
||||
ui.showTextInput("@schematic.rename", "@name", s.name(), res -> {
|
||||
Schematic replacement = schematics.all().find(other -> other.name().equals(res) && other != s);
|
||||
if(replacement != null){
|
||||
//renaming to an existing schematic is not allowed, as it is not clear how the tags would be merged, and which one should be removed
|
||||
ui.showErrorMessage("@schematic.exists");
|
||||
return;
|
||||
}
|
||||
new Dialog("@schematic.rename"){{
|
||||
cont.margin(30).add("@name").padRight(6f);
|
||||
TextField nameField = cont.field(s.name(), null).size(400f, 55f).addInputDialog().get();
|
||||
|
||||
s.tags.put("name", res);
|
||||
s.save();
|
||||
rebuildPane[0].run();
|
||||
});
|
||||
cont.row();
|
||||
|
||||
cont.margin(30).add("@editor.description").padRight(6f);
|
||||
TextField descripionField = cont.area(s.description(), Styles.areaField, t -> {}).size(400f, 140f).addInputDialog().get();
|
||||
|
||||
Runnable accept = () -> {
|
||||
s.tags.put("name", nameField.getText());
|
||||
s.tags.put("description", descripionField.getText());
|
||||
s.save();
|
||||
hide();
|
||||
rebuildPane[0].run();
|
||||
};
|
||||
|
||||
buttons.defaults().size(120, 54).pad(4);
|
||||
buttons.button("@ok", accept).disabled(b -> nameField.getText().isEmpty());
|
||||
buttons.button("@cancel", this::hide);
|
||||
|
||||
keyDown(KeyCode.enter, () -> {
|
||||
if(!nameField.getText().isEmpty() && Core.scene.getKeyboardFocus() != descripionField){
|
||||
accept.run();
|
||||
}
|
||||
});
|
||||
keyDown(KeyCode.escape, this::hide);
|
||||
keyDown(KeyCode.back, this::hide);
|
||||
show();
|
||||
}};
|
||||
});
|
||||
|
||||
if(s.hasSteamID()){
|
||||
@@ -264,6 +281,17 @@ public class SchematicsDialog extends BaseDialog{
|
||||
Core.scene.setKeyboardFocus(searchField);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dialog show(){
|
||||
super.show();
|
||||
|
||||
if(Core.app.isDesktop()){
|
||||
focusSearchField();
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public static class SchematicImage extends Image{
|
||||
public float scaling = 16f;
|
||||
public float thickness = 4f;
|
||||
|
||||
@@ -8,14 +8,16 @@ import arc.input.*;
|
||||
import arc.scene.*;
|
||||
import arc.scene.event.*;
|
||||
import arc.scene.ui.*;
|
||||
import arc.scene.ui.SettingsDialog.SettingsTable.*;
|
||||
import arc.scene.ui.TextButton.*;
|
||||
import arc.scene.ui.layout.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import arc.util.io.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.content.TechTree.*;
|
||||
import mindustry.core.GameState.*;
|
||||
import mindustry.core.*;
|
||||
import mindustry.ctype.*;
|
||||
import mindustry.game.EventType.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
@@ -30,9 +32,9 @@ import static mindustry.Vars.net;
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class SettingsMenuDialog extends SettingsDialog{
|
||||
private SettingsTable graphics;
|
||||
private SettingsTable game;
|
||||
private SettingsTable sound;
|
||||
public SettingsTable graphics;
|
||||
public SettingsTable game;
|
||||
public SettingsTable sound;
|
||||
|
||||
private Table prefs;
|
||||
private Table menu;
|
||||
@@ -116,6 +118,45 @@ public class SettingsMenuDialog extends SettingsDialog{
|
||||
|
||||
t.row();
|
||||
|
||||
t.button("@settings.clearresearch", Icon.trash, style, () -> {
|
||||
ui.showConfirm("@confirm", "@settings.clearresearch.confirm", () -> {
|
||||
universe.clearLoadoutInfo();
|
||||
for(TechNode node : TechTree.all){
|
||||
node.reset();
|
||||
}
|
||||
content.each(c -> {
|
||||
if(c instanceof UnlockableContent u){
|
||||
u.clearUnlock();
|
||||
}
|
||||
});
|
||||
settings.remove("unlocks");
|
||||
});
|
||||
}).marginLeft(4);
|
||||
|
||||
t.row();
|
||||
|
||||
t.button("@settings.clearcampaignsaves", Icon.trash, style, () -> {
|
||||
ui.showConfirm("@confirm", "@settings.clearcampaignsaves.confirm", () -> {
|
||||
for(var planet : content.planets()){
|
||||
for(var sec : planet.sectors){
|
||||
sec.clearInfo();
|
||||
if(sec.save != null){
|
||||
sec.save.delete();
|
||||
sec.save = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(var slot : control.saves.getSaveSlots().copy()){
|
||||
if(slot.isSector()){
|
||||
slot.delete();
|
||||
}
|
||||
}
|
||||
});
|
||||
}).marginLeft(4);
|
||||
|
||||
t.row();
|
||||
|
||||
t.button("@data.export", Icon.upload, style, () -> {
|
||||
if(ios){
|
||||
Fi file = Core.files.local("mindustry-data-export.zip");
|
||||
@@ -160,6 +201,25 @@ public class SettingsMenuDialog extends SettingsDialog{
|
||||
t.row();
|
||||
t.button("@data.openfolder", Icon.folder, style, () -> Core.app.openFolder(Core.settings.getDataDirectory().absolutePath())).marginLeft(4);
|
||||
}
|
||||
|
||||
t.row();
|
||||
|
||||
t.button("@crash.export", Icon.upload, style, () -> {
|
||||
if(settings.getDataDirectory().child("crashes").list().length == 0 && !settings.getDataDirectory().child("last_log.txt").exists()){
|
||||
ui.showInfo("@crash.none");
|
||||
}else{
|
||||
if(ios){
|
||||
Fi logs = tmpDirectory.child("logs.txt");
|
||||
logs.writeString(getLogs());
|
||||
platform.shareFile(logs);
|
||||
}else{
|
||||
platform.showFileChooser(false, "txt", file -> {
|
||||
file.writeString(getLogs());
|
||||
app.post(() -> ui.showInfo("@crash.exported"));
|
||||
});
|
||||
}
|
||||
}
|
||||
}).marginLeft(4);
|
||||
});
|
||||
|
||||
ScrollPane pane = new ScrollPane(prefs);
|
||||
@@ -191,6 +251,21 @@ public class SettingsMenuDialog extends SettingsDialog{
|
||||
addSettings();
|
||||
}
|
||||
|
||||
String getLogs(){
|
||||
Fi log = settings.getDataDirectory().child("last_log.txt");
|
||||
|
||||
StringBuilder out = new StringBuilder();
|
||||
for(Fi fi : settings.getDataDirectory().child("crashes").list()){
|
||||
out.append(fi.name()).append("\n\n").append(fi.readString()).append("\n");
|
||||
}
|
||||
|
||||
if(log.exists()){
|
||||
out.append("\nlast log:\n").append(log.readString());
|
||||
}
|
||||
|
||||
return out.toString();
|
||||
}
|
||||
|
||||
void rebuildMenu(){
|
||||
menu.clearChildren();
|
||||
|
||||
@@ -238,8 +313,6 @@ public class SettingsMenuDialog extends SettingsDialog{
|
||||
game.sliderPref("saveinterval", 60, 10, 5 * 120, 10, i -> Core.bundle.format("setting.seconds", i));
|
||||
|
||||
if(!mobile){
|
||||
game.sliderPref("blockselecttimeout", 750, 0, 2000, 50, i -> Core.bundle.format("setting.milliseconds", i));
|
||||
|
||||
game.checkPref("crashreport", true);
|
||||
}
|
||||
|
||||
@@ -247,10 +320,11 @@ public class SettingsMenuDialog extends SettingsDialog{
|
||||
game.checkPref("blockreplace", true);
|
||||
game.checkPref("conveyorpathfinding", true);
|
||||
game.checkPref("hints", true);
|
||||
|
||||
if(!mobile){
|
||||
game.checkPref("backgroundpause", true);
|
||||
game.checkPref("buildautopause", false);
|
||||
}
|
||||
game.checkPref("mapcenter", true);
|
||||
|
||||
if(steam){
|
||||
game.sliderPref("playerlimit", 16, 2, 32, i -> {
|
||||
@@ -265,19 +339,6 @@ public class SettingsMenuDialog extends SettingsDialog{
|
||||
}
|
||||
}
|
||||
|
||||
game.pref(new Setting(){
|
||||
@Override
|
||||
public void add(SettingsTable table){
|
||||
table.button("@tutorial.retake", () -> {
|
||||
hide();
|
||||
control.playTutorial();
|
||||
}).size(220f, 60f).pad(6).left();
|
||||
table.add();
|
||||
table.row();
|
||||
hide();
|
||||
}
|
||||
});
|
||||
|
||||
graphics.sliderPref("uiscale", 100, 25, 300, 25, s -> {
|
||||
if(ui.settings != null){
|
||||
Core.settings.put("uiscalechanged", true);
|
||||
@@ -292,7 +353,7 @@ public class SettingsMenuDialog extends SettingsDialog{
|
||||
}
|
||||
return s + "%";
|
||||
});
|
||||
graphics.sliderPref("bridgeopacity", 75, 0, 100, 5, s -> s + "%");
|
||||
graphics.sliderPref("bridgeopacity", 100, 0, 100, 5, s -> s + "%");
|
||||
|
||||
if(!mobile){
|
||||
graphics.checkPref("vsync", true, b -> Core.graphics.setVSync(b));
|
||||
@@ -334,29 +395,26 @@ public class SettingsMenuDialog extends SettingsDialog{
|
||||
graphics.checkPref("blockstatus", false);
|
||||
graphics.checkPref("playerchat", true);
|
||||
if(!mobile){
|
||||
graphics.checkPref("coreitems", false);
|
||||
graphics.checkPref("coreitems", true);
|
||||
}
|
||||
graphics.checkPref("minimap", !mobile);
|
||||
graphics.checkPref("smoothcamera", true);
|
||||
graphics.checkPref("position", false);
|
||||
graphics.checkPref("fps", false);
|
||||
if(!mobile){
|
||||
graphics.checkPref("blockselectkeys", true);
|
||||
}
|
||||
graphics.checkPref("playerindicators", true);
|
||||
graphics.checkPref("indicators", true);
|
||||
graphics.checkPref("showweather", true);
|
||||
graphics.checkPref("animatedwater", true);
|
||||
if(Shaders.shield != null){
|
||||
graphics.checkPref("animatedshields", !mobile);
|
||||
}
|
||||
|
||||
if(!ios){
|
||||
graphics.checkPref("bloom", true, val -> renderer.toggleBloom(val));
|
||||
}else{
|
||||
Core.settings.put("bloom", false);
|
||||
}
|
||||
|
||||
|
||||
|
||||
graphics.checkPref("pixelate", false, val -> {
|
||||
if(val){
|
||||
Events.fire(Trigger.enablePixelation);
|
||||
@@ -414,12 +472,17 @@ public class SettingsMenuDialog extends SettingsDialog{
|
||||
throw new IllegalArgumentException("Not valid save data.");
|
||||
}
|
||||
|
||||
//delete old saves so they don't interfere
|
||||
saveDirectory.deleteDirectory();
|
||||
|
||||
//purge existing tmp data, keep everything else
|
||||
tmpDirectory.deleteDirectory();
|
||||
|
||||
zipped.walk(f -> f.copyTo(base.child(f.path())));
|
||||
dest.delete();
|
||||
|
||||
//clear old data
|
||||
settings.clear();
|
||||
//load data so it's saved on exit
|
||||
settings.load();
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package mindustry.ui.dialogs;
|
||||
|
||||
import arc.Core;
|
||||
import arc.scene.ui.layout.Table;
|
||||
import arc.*;
|
||||
import arc.scene.ui.layout.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.net.Administration.TraceInfo;
|
||||
import mindustry.net.Administration.*;
|
||||
|
||||
public class TraceDialog extends BaseDialog{
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import arc.scene.actions.*;
|
||||
import arc.scene.ui.layout.*;
|
||||
import arc.util.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.game.EventType.*;
|
||||
import mindustry.gen.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
@@ -32,6 +33,11 @@ public class BlockConfigFragment extends Fragment{
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Events.on(ResetEvent.class, e -> {
|
||||
table.visible = false;
|
||||
configTile = null;
|
||||
});
|
||||
}
|
||||
|
||||
public boolean isShown(){
|
||||
|
||||
@@ -10,16 +10,12 @@ import arc.scene.*;
|
||||
import arc.scene.actions.*;
|
||||
import arc.scene.event.*;
|
||||
import arc.scene.ui.*;
|
||||
import arc.scene.ui.layout.*;
|
||||
import arc.scene.ui.layout.Stack;
|
||||
import arc.scene.ui.layout.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.entities.*;
|
||||
import mindustry.game.EventType.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.net.Administration.*;
|
||||
import mindustry.net.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.ui.*;
|
||||
|
||||
@@ -42,29 +38,6 @@ public class BlockInventoryFragment extends Fragment{
|
||||
Events.on(WorldLoadEvent.class, e -> hide());
|
||||
}
|
||||
|
||||
@Remote(called = Loc.server, targets = Loc.both, forward = true)
|
||||
public static void requestItem(Player player, Building tile, Item item, int amount){
|
||||
if(player == null || tile == null || !tile.interactable(player.team()) || !player.within(tile, buildingRange)) return;
|
||||
amount = Math.min(player.unit().maxAccepted(item), amount);
|
||||
int fa = amount;
|
||||
|
||||
if(amount == 0) return;
|
||||
|
||||
if(net.server() && (!Units.canInteract(player, tile) ||
|
||||
!netServer.admins.allowAction(player, ActionType.withdrawItem, tile.tile(), action -> {
|
||||
action.item = item;
|
||||
action.itemAmount = fa;
|
||||
}))) throw new ValidateException(player, "Player cannot request items.");
|
||||
|
||||
int removed = tile.removeStack(item, amount);
|
||||
|
||||
player.unit().addItem(item, removed);
|
||||
Events.fire(new WithdrawEvent(tile, player, item, amount));
|
||||
for(int j = 0; j < Mathf.clamp(removed / 3, 1, 8); j++){
|
||||
Time.run(j * 3f, () -> Call.transferItemEffect(item, tile.x, tile.y, player.unit()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void build(Group parent){
|
||||
table.name = "inventory";
|
||||
@@ -148,6 +121,10 @@ public class BlockInventoryFragment extends Fragment{
|
||||
}
|
||||
if(dirty) rebuild(false);
|
||||
}
|
||||
|
||||
if(table.getChildren().isEmpty()){
|
||||
hide();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -168,7 +145,7 @@ public class BlockInventoryFragment extends Fragment{
|
||||
Boolp canPick = () -> player.unit().acceptsItem(item) && !state.isPaused() && player.within(tile, itemTransferRange);
|
||||
|
||||
HandCursorListener l = new HandCursorListener();
|
||||
l.setEnabled(canPick);
|
||||
l.enabled = canPick;
|
||||
|
||||
Element image = itemImage(item.icon(Cicon.xlarge), () -> {
|
||||
if(tile == null || !tile.isValid()){
|
||||
|
||||
@@ -2,7 +2,7 @@ package mindustry.ui.fragments;
|
||||
|
||||
import arc.*;
|
||||
import arc.Input.*;
|
||||
import arc.struct.*;
|
||||
import arc.func.*;
|
||||
import arc.graphics.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.math.*;
|
||||
@@ -10,10 +10,12 @@ import arc.scene.*;
|
||||
import arc.scene.ui.*;
|
||||
import arc.scene.ui.Label.*;
|
||||
import arc.scene.ui.layout.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import mindustry.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.input.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.ui.*;
|
||||
|
||||
import static arc.Core.*;
|
||||
@@ -27,6 +29,7 @@ public class ChatFragment extends Table{
|
||||
private boolean shown = false;
|
||||
private TextField chatfield;
|
||||
private Label fieldlabel = new Label(">");
|
||||
private ChatMode mode = ChatMode.normal;
|
||||
private Font font;
|
||||
private GlyphLayout layout = new GlyphLayout();
|
||||
private float offsetx = Scl.scl(4), offsety = Scl.scl(4), fontoffsetx = Scl.scl(2), chatspace = Scl.scl(50);
|
||||
@@ -57,7 +60,7 @@ public class ChatFragment extends Table{
|
||||
}
|
||||
}
|
||||
|
||||
return net.active() && ui.hudfrag.shown();
|
||||
return net.active() && ui.hudfrag.shown;
|
||||
});
|
||||
|
||||
update(() -> {
|
||||
@@ -76,6 +79,9 @@ public class ChatFragment extends Table{
|
||||
historyPos--;
|
||||
updateChat();
|
||||
}
|
||||
if(input.keyTap(Binding.chat_mode)){
|
||||
nextMode();
|
||||
}
|
||||
scrollPos = (int)Mathf.clamp(scrollPos + input.axis(Binding.chat_scroll), 0, Math.max(0, messages.size - messagesShown));
|
||||
}
|
||||
});
|
||||
@@ -124,7 +130,7 @@ public class ChatFragment extends Table{
|
||||
Draw.color(shadowColor);
|
||||
|
||||
if(shown){
|
||||
Fill.crect(offsetx, chatfield.y, chatfield.getWidth() + 15f, chatfield.getHeight() - 1);
|
||||
Fill.crect(offsetx, chatfield.y + scene.marginBottom, chatfield.getWidth() + 15f, chatfield.getHeight() - 1);
|
||||
}
|
||||
|
||||
super.draw();
|
||||
@@ -137,7 +143,7 @@ public class ChatFragment extends Table{
|
||||
Draw.color(shadowColor);
|
||||
Draw.alpha(shadowColor.a * opacity);
|
||||
|
||||
float theight = offsety + spacing + getMarginBottom();
|
||||
float theight = offsety + spacing + getMarginBottom() + scene.marginBottom;
|
||||
for(int i = scrollPos; i < messages.size && i < messagesShown + scrollPos && (i < fadetime || shown); i++){
|
||||
|
||||
layout.setText(font, messages.get(i).formattedMessage, Color.white, textWidth, Align.bottomLeft, true);
|
||||
@@ -163,15 +169,16 @@ public class ChatFragment extends Table{
|
||||
|
||||
Draw.color();
|
||||
|
||||
if(fadetime > 0 && !shown)
|
||||
if(fadetime > 0 && !shown){
|
||||
fadetime -= Time.delta / 180f;
|
||||
}
|
||||
}
|
||||
|
||||
private void sendMessage(){
|
||||
String message = chatfield.getText();
|
||||
String message = chatfield.getText().trim();
|
||||
clearChatInput();
|
||||
|
||||
if(message.replace(" ", "").isEmpty()) return;
|
||||
if(message.isEmpty()) return;
|
||||
|
||||
history.insert(1, message);
|
||||
|
||||
@@ -182,7 +189,7 @@ public class ChatFragment extends Table{
|
||||
|
||||
if(!shown){
|
||||
scene.setKeyboardFocus(chatfield);
|
||||
shown = !shown;
|
||||
shown = true;
|
||||
if(mobile){
|
||||
TextInput input = new TextInput();
|
||||
input.maxLength = maxTextLength;
|
||||
@@ -198,10 +205,13 @@ public class ChatFragment extends Table{
|
||||
chatfield.fireClick();
|
||||
}
|
||||
}else{
|
||||
scene.setKeyboardFocus(null);
|
||||
shown = !shown;
|
||||
scrollPos = 0;
|
||||
sendMessage();
|
||||
//sending chat has a delay; workaround for issue #1943
|
||||
Time.run(2f, () ->{
|
||||
scene.setKeyboardFocus(null);
|
||||
shown = false;
|
||||
scrollPos = 0;
|
||||
sendMessage();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -212,14 +222,35 @@ public class ChatFragment extends Table{
|
||||
}
|
||||
|
||||
public void updateChat(){
|
||||
chatfield.setText(history.get(historyPos));
|
||||
chatfield.setCursorPosition(chatfield.getText().length());
|
||||
chatfield.setText(mode.normalizedPrefix() + history.get(historyPos));
|
||||
updateCursor();
|
||||
}
|
||||
|
||||
public void nextMode(){
|
||||
ChatMode prev = mode;
|
||||
|
||||
do{
|
||||
mode = mode.next();
|
||||
}while(!mode.isValid());
|
||||
|
||||
if(chatfield.getText().startsWith(prev.normalizedPrefix())){
|
||||
chatfield.setText(mode.normalizedPrefix() + chatfield.getText().substring(prev.normalizedPrefix().length()));
|
||||
}else{
|
||||
chatfield.setText(mode.normalizedPrefix());
|
||||
}
|
||||
|
||||
updateCursor();
|
||||
}
|
||||
|
||||
public void clearChatInput(){
|
||||
historyPos = 0;
|
||||
history.set(0, "");
|
||||
chatfield.setText("");
|
||||
chatfield.setText(mode.normalizedPrefix());
|
||||
updateCursor();
|
||||
}
|
||||
|
||||
public void updateCursor(){
|
||||
chatfield.setCursorPosition(chatfield.getText().length());
|
||||
}
|
||||
|
||||
public boolean shown(){
|
||||
@@ -252,4 +283,36 @@ public class ChatFragment extends Table{
|
||||
}
|
||||
}
|
||||
|
||||
private enum ChatMode{
|
||||
normal(""),
|
||||
team("/t"),
|
||||
admin("/a", player::admin)
|
||||
;
|
||||
|
||||
public String prefix;
|
||||
public Boolp valid;
|
||||
public static final ChatMode[] all = values();
|
||||
|
||||
ChatMode(String prefix){
|
||||
this.prefix = prefix;
|
||||
this.valid = () -> true;
|
||||
}
|
||||
|
||||
ChatMode(String prefix, Boolp valid){
|
||||
this.prefix = prefix;
|
||||
this.valid = valid;
|
||||
}
|
||||
|
||||
public ChatMode next(){
|
||||
return all[(ordinal() + 1) % all.length];
|
||||
}
|
||||
|
||||
public String normalizedPrefix(){
|
||||
return prefix.isEmpty() ? "" : prefix + " ";
|
||||
}
|
||||
|
||||
public boolean isValid(){
|
||||
return valid.get();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package mindustry.ui.fragments;
|
||||
|
||||
import arc.scene.Group;
|
||||
import arc.scene.*;
|
||||
|
||||
public abstract class Fragment{
|
||||
public abstract void build(Group parent);
|
||||
|
||||
277
core/src/mindustry/ui/fragments/HintsFragment.java
Normal file
277
core/src/mindustry/ui/fragments/HintsFragment.java
Normal file
@@ -0,0 +1,277 @@
|
||||
package mindustry.ui.fragments;
|
||||
|
||||
import arc.*;
|
||||
import arc.func.*;
|
||||
import arc.input.*;
|
||||
import arc.math.*;
|
||||
import arc.scene.*;
|
||||
import arc.scene.actions.*;
|
||||
import arc.scene.event.*;
|
||||
import arc.scene.ui.layout.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import mindustry.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.game.EventType.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.input.*;
|
||||
import mindustry.ui.*;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.meta.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class HintsFragment extends Fragment{
|
||||
private static final Boolp isTutorial = () -> Vars.state.rules.sector == SectorPresets.groundZero.sector;
|
||||
private static final float foutTime = 0.6f;
|
||||
|
||||
/** All hints to be displayed in the game. */
|
||||
public Seq<Hint> hints = new Seq<>().and(DefaultHint.values()).as();
|
||||
|
||||
@Nullable Hint current;
|
||||
Group group = new WidgetGroup();
|
||||
ObjectSet<String> events = new ObjectSet<>();
|
||||
ObjectSet<Block> placedBlocks = new ObjectSet<>();
|
||||
Table last;
|
||||
|
||||
@Override
|
||||
public void build(Group parent){
|
||||
group.setFillParent(true);
|
||||
group.touchable = Touchable.childrenOnly;
|
||||
group.visibility = () -> Core.settings.getBool("hints", true) && ui.hudfrag.shown;
|
||||
group.update(() -> {
|
||||
if(current != null){
|
||||
//current got completed
|
||||
if(current.complete()){
|
||||
complete();
|
||||
}else if(!current.show()){ //current became hidden
|
||||
hide();
|
||||
}
|
||||
}else if(hints.size > 0){
|
||||
//check one hint each frame to see if it should be shown.
|
||||
Hint hint = hints.find(Hint::show);
|
||||
if(hint != null && hint.complete()){
|
||||
hints.remove(hint);
|
||||
}else if(hint != null){
|
||||
display(hint);
|
||||
}
|
||||
}
|
||||
});
|
||||
parent.addChild(group);
|
||||
|
||||
checkNext();
|
||||
|
||||
Events.on(BlockBuildEndEvent.class, event -> {
|
||||
if(!event.breaking && event.unit == player.unit()){
|
||||
placedBlocks.add(event.tile.block());
|
||||
}
|
||||
|
||||
if(event.breaking){
|
||||
events.add("break");
|
||||
}
|
||||
});
|
||||
|
||||
Events.on(ResetEvent.class, e -> {
|
||||
placedBlocks.clear();
|
||||
events.clear();
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
void checkNext(){
|
||||
if(current != null) return;
|
||||
|
||||
hints.removeAll(h -> !h.valid() || h.finished() || (h.show() && h.complete()));
|
||||
hints.sort(Hint::order);
|
||||
|
||||
Hint first = hints.find(Hint::show);
|
||||
if(first != null){
|
||||
hints.remove(first);
|
||||
display(first);
|
||||
}
|
||||
}
|
||||
|
||||
void display(Hint hint){
|
||||
if(current != null) return;
|
||||
|
||||
group.fill(t -> {
|
||||
last = t;
|
||||
t.left();
|
||||
t.table(Styles.black5, cont -> {
|
||||
cont.actions(Actions.alpha(0f), Actions.alpha(1f, 1f, Interp.smooth));
|
||||
cont.margin(6f).add(hint.text()).width(Vars.mobile ? 270f : 400f).left().labelAlign(Align.left).wrap();
|
||||
});
|
||||
t.row();
|
||||
t.button("@hint.skip", Styles.nonet, () -> {
|
||||
if(current != null){
|
||||
complete();
|
||||
}
|
||||
}).size(112f, 40f).left();
|
||||
});
|
||||
|
||||
this.current = hint;
|
||||
}
|
||||
|
||||
/** Completes and hides the current hint. */
|
||||
void complete(){
|
||||
if(current == null) return;
|
||||
|
||||
current.finish();
|
||||
hints.remove(current);
|
||||
|
||||
hide();
|
||||
}
|
||||
|
||||
/** Hides the current hint, but does not complete it. */
|
||||
void hide(){
|
||||
//hide previous child if found
|
||||
if(last != null){
|
||||
last.actions(Actions.parallel(Actions.alpha(0f, foutTime, Interp.smooth), Actions.translateBy(0f, Scl.scl(-200f), foutTime, Interp.smooth)), Actions.remove());
|
||||
}
|
||||
//check for next hint to display immediately
|
||||
current = null;
|
||||
last = null;
|
||||
checkNext();
|
||||
}
|
||||
|
||||
public boolean shown(){
|
||||
return current != null;
|
||||
}
|
||||
|
||||
public enum DefaultHint implements Hint{
|
||||
desktopMove(visibleDesktop, () -> Core.input.axis(Binding.move_x) != 0 || Core.input.axis(Binding.move_y) != 0),
|
||||
zoom(visibleDesktop, () -> Core.input.axis(KeyCode.scroll) != 0),
|
||||
mine(() -> player.unit().canMine() && isTutorial.get(), () -> player.unit().mining()),
|
||||
placeDrill(isTutorial, () -> ui.hints.placedBlocks.contains(Blocks.mechanicalDrill)),
|
||||
placeConveyor(isTutorial, () -> ui.hints.placedBlocks.contains(Blocks.conveyor)),
|
||||
placeTurret(isTutorial, () -> ui.hints.placedBlocks.contains(Blocks.duo)),
|
||||
breaking(isTutorial, () -> ui.hints.events.contains("break")),
|
||||
desktopShoot(visibleDesktop, () -> Vars.state.enemies > 0, () -> player.shooting),
|
||||
depositItems(() -> player.unit().hasItem(), () -> !player.unit().hasItem()),
|
||||
desktopPause(visibleDesktop, () -> isTutorial.get() && !Vars.net.active(), () -> Core.input.keyTap(Binding.pause)),
|
||||
research(isTutorial, () -> ui.research.isShown()),
|
||||
unitControl(() -> state.rules.defaultTeam.data().units.size > 2 && !net.active() && !player.dead(), () -> !player.dead() && !player.unit().spawnedByCore),
|
||||
respawn(visibleMobile, () -> !player.dead() && !player.unit().spawnedByCore, () -> !player.dead() && player.unit().spawnedByCore),
|
||||
launch(() -> isTutorial.get() && state.rules.sector.isCaptured(), () -> ui.planet.isShown()),
|
||||
schematicSelect(visibleDesktop, () -> ui.hints.placedBlocks.contains(Blocks.router), () -> Core.input.keyRelease(Binding.schematic_select) || Core.input.keyTap(Binding.pick)),
|
||||
conveyorPathfind(() -> control.input.block == Blocks.titaniumConveyor, () -> Core.input.keyRelease(Binding.diagonal_placement) || (mobile && Core.settings.getBool("swapdiagonal"))),
|
||||
boost(visibleDesktop, () -> !player.dead() && player.unit().type.canBoost, () -> Core.input.keyDown(Binding.boost)),
|
||||
command(() -> state.rules.defaultTeam.data().units.size > 3 && !net.active(), () -> player.unit().isCommanding()),
|
||||
payloadPickup(() -> !player.unit().dead && player.unit() instanceof Payloadc p && p.payloads().isEmpty(), () -> player.unit() instanceof Payloadc p && p.payloads().any()),
|
||||
payloadDrop(() -> !player.unit().dead && player.unit() instanceof Payloadc p && p.payloads().any(), () -> player.unit() instanceof Payloadc p && p.payloads().isEmpty()),
|
||||
waveFire(() -> Groups.fire.size() > 0 && Blocks.wave.unlockedNow(), () -> indexer.getAllied(state.rules.defaultTeam, BlockFlag.extinguisher).size() > 0),
|
||||
generator(() -> control.input.block == Blocks.combustionGenerator, () -> ui.hints.placedBlocks.contains(Blocks.combustionGenerator)),
|
||||
guardian(() -> state.boss() != null && state.boss().armor >= 4, () -> state.boss() == null),
|
||||
coreUpgrade(() -> state.isCampaign() && Blocks.coreFoundation.unlocked()
|
||||
&& state.rules.defaultTeam.core() != null
|
||||
&& state.rules.defaultTeam.core().block == Blocks.coreShard
|
||||
&& state.rules.defaultTeam.core().items.has(Blocks.coreFoundation.requirements),
|
||||
() -> ui.hints.placedBlocks.contains(Blocks.coreFoundation)),
|
||||
presetLaunch(() -> state.isCampaign()
|
||||
&& state.getSector().preset == null
|
||||
&& SectorPresets.frozenForest.unlocked()
|
||||
&& SectorPresets.frozenForest.sector.save == null,
|
||||
() -> state.isCampaign() && state.getSector().preset == SectorPresets.frozenForest),
|
||||
coreIncinerate(() -> state.isCampaign() && state.rules.defaultTeam.core() != null && state.rules.defaultTeam.core().items.get(Items.copper) >= state.rules.defaultTeam.core().storageCapacity - 10, () -> false),
|
||||
coopCampaign(() -> net.client() && state.isCampaign() && SectorPresets.groundZero.sector.hasBase(), () -> false),
|
||||
;
|
||||
|
||||
@Nullable
|
||||
String text;
|
||||
int visibility = visibleAll;
|
||||
Hint[] dependencies = {};
|
||||
boolean finished, cached;
|
||||
Boolp complete, shown = () -> true;
|
||||
|
||||
DefaultHint(Boolp complete){
|
||||
this.complete = complete;
|
||||
}
|
||||
|
||||
DefaultHint(int visiblity, Boolp complete){
|
||||
this(complete);
|
||||
this.visibility = visiblity;
|
||||
}
|
||||
|
||||
DefaultHint(Boolp shown, Boolp complete){
|
||||
this(complete);
|
||||
this.shown = shown;
|
||||
}
|
||||
|
||||
DefaultHint(int visiblity, Boolp shown, Boolp complete){
|
||||
this(complete);
|
||||
this.shown = shown;
|
||||
this.visibility = visiblity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean finished(){
|
||||
if(!cached){
|
||||
cached = true;
|
||||
finished = Core.settings.getBool(name() + "-hint-done", false);
|
||||
}
|
||||
return finished;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void finish(){
|
||||
Core.settings.put(name() + "-hint-done", finished = true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String text(){
|
||||
if(text == null){
|
||||
text = Vars.mobile && Core.bundle.has("hint." + name() + ".mobile") ? Core.bundle.get("hint." + name() + ".mobile") : Core.bundle.get("hint." + name());
|
||||
if(!Vars.mobile) text = text.replace("tap", "click").replace("Tap", "Click");
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean complete(){
|
||||
return complete.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean show(){
|
||||
return shown.get() && (dependencies.length == 0 || !Structs.contains(dependencies, d -> !d.finished()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int order(){
|
||||
return ordinal();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean valid(){
|
||||
return (Vars.mobile && (visibility & visibleMobile) != 0) || (!Vars.mobile && (visibility & visibleDesktop) != 0);
|
||||
}
|
||||
}
|
||||
|
||||
/** Hint interface for defining any sort of message appearing at the left. */
|
||||
public interface Hint{
|
||||
int visibleDesktop = 1, visibleMobile = 2, visibleAll = visibleDesktop | visibleMobile;
|
||||
|
||||
/** Hint name for preference storage. */
|
||||
String name();
|
||||
/** Displayed text. */
|
||||
String text();
|
||||
/** @return true if hint objective is complete */
|
||||
boolean complete();
|
||||
/** @return whether the hint is ready to be shown */
|
||||
boolean show();
|
||||
/** @return order integer, determines priority */
|
||||
int order();
|
||||
/** @return whether this hint should be processed, used for platform splits */
|
||||
boolean valid();
|
||||
|
||||
/** finishes the hint - it should not be shown again */
|
||||
default void finish(){
|
||||
Core.settings.put(name() + "-hint-done", true);
|
||||
}
|
||||
|
||||
/** @return whether the hint is finished - if true, it should not be shown again */
|
||||
default boolean finished(){
|
||||
return Core.settings.getBool(name() + "-hint-done", false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,6 @@ import arc.*;
|
||||
import arc.func.*;
|
||||
import arc.graphics.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.input.*;
|
||||
import arc.math.*;
|
||||
import arc.scene.*;
|
||||
import arc.scene.actions.*;
|
||||
@@ -16,6 +15,7 @@ import arc.scene.ui.layout.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.core.GameState.*;
|
||||
import mindustry.ctype.*;
|
||||
import mindustry.game.EventType.*;
|
||||
@@ -26,7 +26,6 @@ import mindustry.input.*;
|
||||
import mindustry.net.Packets.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.ui.*;
|
||||
import mindustry.ui.dialogs.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
@@ -34,37 +33,54 @@ public class HudFragment extends Fragment{
|
||||
private static final float dsize = 65f;
|
||||
|
||||
public final PlacementFragment blockfrag = new PlacementFragment();
|
||||
|
||||
//TODO localize
|
||||
public String sectorText = "Out of sector time.";
|
||||
public Seq<Sector> attackedSectors = new Seq<>();
|
||||
public boolean shown = true;
|
||||
|
||||
private ImageButton flip;
|
||||
private Table lastUnlockTable;
|
||||
private Table lastUnlockLayout;
|
||||
private boolean shown = true;
|
||||
private CoreItemsDisplay coreItems = new CoreItemsDisplay();
|
||||
|
||||
private String hudText = "";
|
||||
private boolean showHudText;
|
||||
|
||||
private Table lastUnlockTable;
|
||||
private Table lastUnlockLayout;
|
||||
private long lastToast;
|
||||
|
||||
@Override
|
||||
public void build(Group parent){
|
||||
|
||||
//TODO details and stuff
|
||||
Events.on(SectorCaptureEvent.class, e ->{
|
||||
//TODO localize
|
||||
showToast("Sector[accent] captured[]!");
|
||||
//warn about guardian/boss waves
|
||||
Events.on(WaveEvent.class, e -> {
|
||||
int max = 10;
|
||||
int winWave = state.isCampaign() && state.rules.winWave > 0 ? state.rules.winWave : Integer.MAX_VALUE;
|
||||
outer:
|
||||
for(int i = state.wave - 1; i <= Math.min(state.wave + max, winWave - 2); i++){
|
||||
for(SpawnGroup group : state.rules.spawns){
|
||||
if(group.effect == StatusEffects.boss && group.getSpawned(i) > 0){
|
||||
int diff = (i + 2) - state.wave;
|
||||
|
||||
//increments at which to warn about incoming guardian
|
||||
if(diff == 1 || diff == 2 || diff == 5 || diff == 10){
|
||||
showToast(Icon.warning, Core.bundle.format("wave.guardianwarn" + (diff == 1 ? ".one" : ""), diff));
|
||||
}
|
||||
|
||||
break outer;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Events.on(SectorCaptureEvent.class, e -> {
|
||||
showToast(Core.bundle.format("sector.captured", e.sector.isBeingPlayed() ? "" : e.sector.name() + " "));
|
||||
});
|
||||
|
||||
//TODO localize
|
||||
Events.on(SectorLoseEvent.class, e -> {
|
||||
showToast(Icon.warning, "Sector " + e.sector.id + " [scarlet]lost!");
|
||||
showToast(Icon.warning, Core.bundle.format("sector.lost", e.sector.name()));
|
||||
});
|
||||
|
||||
Events.on(SectorInvasionEvent.class, e -> {
|
||||
showToast(Icon.warning, Core.bundle.format("sector.attacked", e.sector.name()));
|
||||
});
|
||||
|
||||
//TODO full implementation
|
||||
Events.on(ResetEvent.class, e -> {
|
||||
coreItems.resetUsed();
|
||||
coreItems.clear();
|
||||
@@ -72,24 +88,28 @@ public class HudFragment extends Fragment{
|
||||
|
||||
//paused table
|
||||
parent.fill(t -> {
|
||||
t.top().visible(() -> state.isPaused() && !state.isOutOfTime()).touchable = Touchable.disabled;
|
||||
t.table(Styles.black5, top -> top.add("@paused").style(Styles.outlineLabel).pad(8f)).growX();
|
||||
t.name = "paused";
|
||||
t.top().visible(() -> state.isPaused() && shown).touchable = Touchable.disabled;
|
||||
t.table(Styles.black5, top -> top.label(() -> state.gameOver && state.isCampaign() ? "@sector.curlost" : "@paused").style(Styles.outlineLabel).pad(8f)).growX();
|
||||
});
|
||||
|
||||
//minimap + position
|
||||
parent.fill(t -> {
|
||||
t.visible(() -> Core.settings.getBool("minimap") && !state.rules.tutorial && shown);
|
||||
t.name = "minimap/position";
|
||||
t.visible(() -> Core.settings.getBool("minimap") && shown);
|
||||
//minimap
|
||||
t.add(new Minimap());
|
||||
t.add(new Minimap()).name("minimap");
|
||||
t.row();
|
||||
//position
|
||||
t.label(() -> player.tileX() + "," + player.tileY())
|
||||
.visible(() -> Core.settings.getBool("position") && !state.rules.tutorial)
|
||||
.touchable(Touchable.disabled);
|
||||
.visible(() -> Core.settings.getBool("position"))
|
||||
.touchable(Touchable.disabled)
|
||||
.name("position");
|
||||
t.top().right();
|
||||
});
|
||||
|
||||
//TODO tear this all down
|
||||
ui.hints.build(parent);
|
||||
|
||||
//menu at top left
|
||||
parent.fill(cont -> {
|
||||
cont.name = "overlaymarker";
|
||||
@@ -97,15 +117,18 @@ public class HudFragment extends Fragment{
|
||||
|
||||
if(mobile){
|
||||
cont.table(select -> {
|
||||
select.name = "mobile buttons";
|
||||
select.left();
|
||||
select.defaults().size(dsize).left();
|
||||
|
||||
ImageButtonStyle style = Styles.clearTransi;
|
||||
|
||||
select.button(Icon.menu, style, ui.paused::show);
|
||||
select.button(Icon.menu, style, ui.paused::show).name("menu");
|
||||
flip = select.button(Icon.upOpen, style, this::toggleMenus).get();
|
||||
flip.name = "flip";
|
||||
|
||||
select.button(Icon.paste, style, ui.schematics::show);
|
||||
select.button(Icon.paste, style, ui.schematics::show)
|
||||
.name("schematics");
|
||||
|
||||
select.button(Icon.pause, style, () -> {
|
||||
if(net.active()){
|
||||
@@ -134,7 +157,7 @@ public class HudFragment extends Fragment{
|
||||
}else{
|
||||
ui.database.show();
|
||||
}
|
||||
}).update(i -> {
|
||||
}).name("chat").update(i -> {
|
||||
if(net.active() && mobile){
|
||||
i.getStyle().imageUp = Icon.chat;
|
||||
}else if(state.isCampaign()){
|
||||
@@ -154,42 +177,46 @@ public class HudFragment extends Fragment{
|
||||
|
||||
cont.update(() -> {
|
||||
if(Core.input.keyTap(Binding.toggle_menus) && !ui.chatfrag.shown() && !Core.scene.hasDialog() && !(Core.scene.getKeyboardFocus() instanceof TextField)){
|
||||
Core.settings.getBoolOnce("ui-hidden", () -> {
|
||||
ui.announce(Core.bundle.format("showui", Core.keybinds.get(Binding.toggle_menus).key.toString(), 11));
|
||||
});
|
||||
toggleMenus();
|
||||
}
|
||||
});
|
||||
|
||||
Table wavesMain, editorMain;
|
||||
|
||||
cont.stack(wavesMain = new Table(), editorMain = new Table()).height(wavesMain.getPrefHeight());
|
||||
cont.stack(wavesMain = new Table(), editorMain = new Table()).height(wavesMain.getPrefHeight())
|
||||
.name("waves/editor");
|
||||
|
||||
wavesMain.visible(() -> shown && !state.isEditor());
|
||||
wavesMain.top().left();
|
||||
wavesMain.top().left().name = "waves";
|
||||
|
||||
wavesMain.table(s -> {
|
||||
//wave info button with text
|
||||
s.add(makeStatusTable()).grow();
|
||||
s.add(makeStatusTable()).grow().name("status");
|
||||
|
||||
//table with button to skip wave
|
||||
s.button(Icon.play, Styles.righti, 30f, () -> {
|
||||
if(net.client() && player.admin){
|
||||
Call.adminRequest(player, AdminAction.wave);
|
||||
}else if(inLaunchWave()){
|
||||
ui.showConfirm("@confirm", "@launch.skip.confirm", () -> !canSkipWave(), () -> logic.skipWave());
|
||||
}else{
|
||||
logic.skipWave();
|
||||
}
|
||||
}).growY().fillX().right().width(40f).disabled(b -> !canSkipWave()).visible(() -> state.rules.waves);
|
||||
}).growY().fillX().right().width(40f).disabled(b -> !canSkipWave()).name("skip");
|
||||
}).width(dsize * 5 + 4f);
|
||||
|
||||
wavesMain.row();
|
||||
|
||||
wavesMain.table(Tex.button, t -> t.margin(10f).add(new Bar("boss.health", Pal.health, () -> state.boss() == null ? 0f : state.boss().healthf()).blink(Color.white))
|
||||
.grow()).fillX().visible(() -> state.rules.waves && state.boss() != null).height(60f).get();
|
||||
.grow()).fillX().visible(() -> state.rules.waves && state.boss() != null).height(60f).name("boss");
|
||||
|
||||
wavesMain.row();
|
||||
|
||||
editorMain.name = "editor";
|
||||
editorMain.table(Tex.buttonEdge4, t -> {
|
||||
//t.margin(0f);
|
||||
t.name = "teams";
|
||||
t.add("@editor.teams").growX().left();
|
||||
t.row();
|
||||
t.table(teams -> {
|
||||
@@ -210,43 +237,57 @@ public class HudFragment extends Fragment{
|
||||
}).width(dsize * 5 + 4f);
|
||||
editorMain.visible(() -> shown && state.isEditor());
|
||||
|
||||
|
||||
//fps display
|
||||
cont.table(info -> {
|
||||
info.name = "fps/ping";
|
||||
info.touchable = Touchable.disabled;
|
||||
info.top().left().margin(4).visible(() -> Core.settings.getBool("fps") && shown);
|
||||
info.update(() -> info.setTranslation(state.rules.waves || state.isEditor() ? 0f : -Scl.scl(dsize * 4 + 3), 0));
|
||||
IntFormat fps = new IntFormat("fps");
|
||||
IntFormat ping = new IntFormat("ping");
|
||||
IntFormat mem = new IntFormat("memory");
|
||||
IntFormat memnative = new IntFormat("memory2");
|
||||
|
||||
info.label(() -> fps.get(Core.graphics.getFramesPerSecond())).left().style(Styles.outlineLabel);
|
||||
info.label(() -> fps.get(Core.graphics.getFramesPerSecond())).left().style(Styles.outlineLabel).name("fps");
|
||||
info.row();
|
||||
info.label(() -> ping.get(netClient.getPing())).visible(net::client).left().style(Styles.outlineLabel);
|
||||
|
||||
if(android){
|
||||
info.label(() -> memnative.get((int)(Core.app.getJavaHeap() / 1024 / 1024), (int)(Core.app.getNativeHeap() / 1024 / 1024))).left().style(Styles.outlineLabel).name("memory2");
|
||||
}else{
|
||||
info.label(() -> mem.get((int)(Core.app.getJavaHeap() / 1024 / 1024))).left().style(Styles.outlineLabel).name("memory");
|
||||
}
|
||||
info.row();
|
||||
|
||||
info.label(() -> ping.get(netClient.getPing())).visible(net::client).left().style(Styles.outlineLabel).name("ping");
|
||||
|
||||
}).top().left();
|
||||
});
|
||||
|
||||
//core items
|
||||
parent.fill(t -> {
|
||||
t.name = "coreitems";
|
||||
t.top().add(coreItems);
|
||||
t.visible(() -> Core.settings.getBool("coreitems") && !mobile && !state.isPaused() && shown);
|
||||
});
|
||||
|
||||
//spawner warning
|
||||
parent.fill(t -> {
|
||||
t.name = "nearpoint";
|
||||
t.touchable = Touchable.disabled;
|
||||
t.table(Styles.black, c -> c.add("@nearpoint")
|
||||
.update(l -> l.setColor(Tmp.c1.set(Color.white).lerp(Color.scarlet, Mathf.absin(Time.time(), 10f, 1f))))
|
||||
.update(l -> l.setColor(Tmp.c1.set(Color.white).lerp(Color.scarlet, Mathf.absin(Time.time, 10f, 1f))))
|
||||
.get().setAlignment(Align.center, Align.center))
|
||||
.margin(6).update(u -> u.color.a = Mathf.lerpDelta(u.color.a, Mathf.num(spawner.playerNear()), 0.1f)).get().color.a = 0f;
|
||||
});
|
||||
|
||||
parent.fill(t -> {
|
||||
t.name = "waiting";
|
||||
t.visible(() -> netServer.isWaitingForPlayers());
|
||||
t.table(Tex.button, c -> c.add("@waiting.players"));
|
||||
});
|
||||
|
||||
//'core is under attack' table
|
||||
parent.fill(t -> {
|
||||
t.name = "coreattack";
|
||||
t.touchable = Touchable.disabled;
|
||||
float notifDuration = 240f;
|
||||
float[] coreAttackTime = {0};
|
||||
@@ -257,6 +298,7 @@ public class HudFragment extends Fragment{
|
||||
});
|
||||
|
||||
t.top().visible(() -> {
|
||||
if(!shown) return false;
|
||||
if(state.isMenu() || !state.teams.get(player.team()).hasCore()){
|
||||
coreAttackTime[0] = 0f;
|
||||
return false;
|
||||
@@ -274,69 +316,18 @@ public class HudFragment extends Fragment{
|
||||
return coreAttackOpacity[0] > 0;
|
||||
});
|
||||
t.table(Tex.button, top -> top.add("@coreattack").pad(2)
|
||||
.update(label -> label.color.set(Color.orange).lerp(Color.scarlet, Mathf.absin(Time.time(), 2f, 1f)))).touchable(Touchable.disabled);
|
||||
});
|
||||
|
||||
//paused table for when the player is out of time
|
||||
parent.fill(t -> {
|
||||
t.top().visible(() -> state.isOutOfTime());
|
||||
t.table(Styles.black5, top -> {
|
||||
//TODO localize
|
||||
top.add(sectorText).style(Styles.outlineLabel).color(Pal.accent).update(l -> {
|
||||
l.color.a = Mathf.absin(Time.globalTime(), 7f, 1f);
|
||||
l.setText(sectorText);
|
||||
}).colspan(2);
|
||||
top.row();
|
||||
|
||||
top.defaults().pad(2).size(150f, 54f);
|
||||
//TODO localize
|
||||
top.button("Skip", () -> {
|
||||
universe.runTurn();
|
||||
state.set(State.playing);
|
||||
|
||||
//announce turn info only when something is skipped.
|
||||
ui.announce("[accent][[ Turn " + universe.turn() + " ]\n[scarlet]" + attackedSectors.size + "[lightgray] sector(s) attacked.");
|
||||
});
|
||||
|
||||
//TODO localize
|
||||
top.button("Switch Sectors", () -> {
|
||||
ui.paused.runExitSave();
|
||||
|
||||
//switch to first attacked sector
|
||||
control.playSector(attackedSectors.first());
|
||||
}).disabled(b -> attackedSectors.isEmpty());
|
||||
}).margin(8).growX();
|
||||
});
|
||||
|
||||
//tutorial text
|
||||
parent.fill(t -> {
|
||||
Runnable resize = () -> {
|
||||
t.clearChildren();
|
||||
t.top().right().visible(() -> state.rules.tutorial);
|
||||
t.stack(new Button(){{
|
||||
marginLeft(48f);
|
||||
labelWrap(() -> control.tutorial.stage.text() + (control.tutorial.canNext() ? "\n\n" + Core.bundle.get("tutorial.next") : "")).width(!Core.graphics.isPortrait() ? 400f : 160f).pad(2f);
|
||||
clicked(() -> control.tutorial.nextSentence());
|
||||
setDisabled(() -> !control.tutorial.canNext());
|
||||
}},
|
||||
new Table(f -> {
|
||||
f.left().button(Icon.left, Styles.emptyi, () -> {
|
||||
control.tutorial.prevSentence();
|
||||
}).width(44f).growY().visible(() -> control.tutorial.canPrev());
|
||||
}));
|
||||
};
|
||||
|
||||
resize.run();
|
||||
Events.on(ResizeEvent.class, e -> resize.run());
|
||||
.update(label -> label.color.set(Color.orange).lerp(Color.scarlet, Mathf.absin(Time.time, 2f, 1f)))).touchable(Touchable.disabled);
|
||||
});
|
||||
|
||||
//'saving' indicator
|
||||
parent.fill(t -> {
|
||||
t.name = "saving";
|
||||
t.bottom().visible(() -> control.saves.isSaving());
|
||||
t.add("@saving").style(Styles.outlineLabel);
|
||||
});
|
||||
|
||||
parent.fill(p -> {
|
||||
p.name = "hudtext";
|
||||
p.top().table(Styles.black3, t -> t.margin(4).label(() -> hudText)
|
||||
.style(Styles.outlineLabel)).padTop(10).visible(p.color.a >= 0.001f);
|
||||
p.update(() -> {
|
||||
@@ -352,6 +343,7 @@ public class HudFragment extends Fragment{
|
||||
//TODO DEBUG: rate table
|
||||
if(false)
|
||||
parent.fill(t -> {
|
||||
t.name = "rates";
|
||||
t.bottom().left();
|
||||
t.table(Styles.black6, c -> {
|
||||
Bits used = new Bits(content.items().size);
|
||||
@@ -360,9 +352,9 @@ public class HudFragment extends Fragment{
|
||||
c.clearChildren();
|
||||
|
||||
for(Item item : content.items()){
|
||||
if(state.secinfo.getExport(item) >= 1){
|
||||
if(state.rules.sector != null && state.rules.sector.info.getExport(item) >= 1){
|
||||
c.image(item.icon(Cicon.small));
|
||||
c.label(() -> (int)state.secinfo.getExport(item) + " /s").color(Color.lightGray);
|
||||
c.label(() -> (int)state.rules.sector.info.getExport(item) + " /s").color(Color.lightGray);
|
||||
c.row();
|
||||
}
|
||||
}
|
||||
@@ -371,7 +363,7 @@ public class HudFragment extends Fragment{
|
||||
c.update(() -> {
|
||||
boolean wrong = false;
|
||||
for(Item item : content.items()){
|
||||
boolean has = state.secinfo.getExport(item) >= 1;
|
||||
boolean has = state.rules.sector != null && state.rules.sector.info.getExport(item) >= 1;
|
||||
if(used.get(item.id) != has){
|
||||
used.set(item.id, has);
|
||||
wrong = true;
|
||||
@@ -381,7 +373,7 @@ public class HudFragment extends Fragment{
|
||||
rebuild.run();
|
||||
}
|
||||
});
|
||||
}).visible(() -> state.isCampaign() && content.items().contains(i -> state.secinfo.getExport(i) > 0));
|
||||
}).visible(() -> state.isCampaign() && content.items().contains(i -> state.rules.sector != null && state.rules.sector.info.getExport(i) > 0));
|
||||
});
|
||||
|
||||
blockfrag.build(parent);
|
||||
@@ -427,7 +419,7 @@ public class HudFragment extends Fragment{
|
||||
|
||||
Table table = new Table(Tex.button);
|
||||
table.update(() -> {
|
||||
if(state.isMenu()){
|
||||
if(state.isMenu() || !ui.hudfrag.shown){
|
||||
table.remove();
|
||||
}
|
||||
});
|
||||
@@ -446,15 +438,11 @@ public class HudFragment extends Fragment{
|
||||
});
|
||||
}
|
||||
|
||||
public boolean shown(){
|
||||
return shown;
|
||||
}
|
||||
|
||||
/** Show unlock notification for a new recipe. */
|
||||
public void showUnlock(UnlockableContent content){
|
||||
//some content may not have icons... yet
|
||||
//also don't play in the tutorial to prevent confusion
|
||||
if(state.isMenu() || state.rules.tutorial) return;
|
||||
if(state.isMenu()) return;
|
||||
|
||||
Sounds.message.play();
|
||||
|
||||
@@ -576,40 +564,6 @@ public class HudFragment extends Fragment{
|
||||
Core.scene.add(image);
|
||||
}
|
||||
|
||||
private void showLaunchConfirm(){
|
||||
BaseDialog dialog = new BaseDialog("@launch");
|
||||
dialog.update(() -> {
|
||||
if(!inLaunchWave()){
|
||||
dialog.hide();
|
||||
}
|
||||
});
|
||||
dialog.cont.add("@launch.confirm").width(500f).wrap().pad(4f).get().setAlignment(Align.center, Align.center);
|
||||
dialog.buttons.defaults().size(200f, 54f).pad(2f);
|
||||
dialog.setFillParent(false);
|
||||
dialog.buttons.button("@cancel", dialog::hide);
|
||||
dialog.buttons.button("@ok", () -> {
|
||||
dialog.hide();
|
||||
Call.launchZone();
|
||||
});
|
||||
dialog.keyDown(KeyCode.escape, dialog::hide);
|
||||
dialog.keyDown(KeyCode.back, dialog::hide);
|
||||
dialog.show();
|
||||
}
|
||||
|
||||
//TODO launching is disabled, possibly forever
|
||||
private boolean inLaunchWave(){
|
||||
return false;
|
||||
/*
|
||||
return state.hasSector() &&
|
||||
state.getSector().metCondition() &&
|
||||
!net.client() &&
|
||||
state.wave % state.getSector().launchPeriod == 0 && !spawner.isSpawning();*/
|
||||
}
|
||||
|
||||
private boolean canLaunch(){
|
||||
return inLaunchWave() && state.enemies <= 0;
|
||||
}
|
||||
|
||||
private void toggleMenus(){
|
||||
if(flip != null){
|
||||
flip.getStyle().imageUp = shown ? Icon.downOpen : Icon.upOpen;
|
||||
@@ -619,13 +573,16 @@ public class HudFragment extends Fragment{
|
||||
}
|
||||
|
||||
private Table makeStatusTable(){
|
||||
Button table = new Button(Styles.waveb);
|
||||
Table table = new Table(Tex.wavepane);
|
||||
|
||||
StringBuilder ibuild = new StringBuilder();
|
||||
|
||||
IntFormat wavef = new IntFormat("wave");
|
||||
IntFormat wavefc = new IntFormat("wave.cap");
|
||||
IntFormat enemyf = new IntFormat("wave.enemy");
|
||||
IntFormat enemiesf = new IntFormat("wave.enemies");
|
||||
IntFormat enemycf = new IntFormat("wave.enemycore");
|
||||
IntFormat enemycsf = new IntFormat("wave.enemycores");
|
||||
IntFormat waitingf = new IntFormat("wave.waiting", i -> {
|
||||
ibuild.setLength(0);
|
||||
int m = i/60;
|
||||
@@ -641,7 +598,6 @@ public class HudFragment extends Fragment{
|
||||
return ibuild.toString();
|
||||
});
|
||||
|
||||
table.clearChildren();
|
||||
table.touchable = Touchable.enabled;
|
||||
|
||||
StringBuilder builder = new StringBuilder();
|
||||
@@ -669,6 +625,8 @@ public class HudFragment extends Fragment{
|
||||
public void draw(){
|
||||
float next = amount.get();
|
||||
|
||||
if(Float.isNaN(next) || Float.isInfinite(next)) next = 1f;
|
||||
|
||||
if(next < last && flash.get()){
|
||||
blink = 1f;
|
||||
}
|
||||
@@ -677,6 +635,8 @@ public class HudFragment extends Fragment{
|
||||
value = Mathf.lerpDelta(value, next, 0.15f);
|
||||
last = next;
|
||||
|
||||
if(Float.isNaN(value) || Float.isInfinite(value)) value = 1f;
|
||||
|
||||
drawInner(Pal.darkishGray);
|
||||
|
||||
Draw.beginStencil();
|
||||
@@ -737,11 +697,17 @@ public class HudFragment extends Fragment{
|
||||
float bw = 40f;
|
||||
float pad = -20;
|
||||
t.margin(0);
|
||||
t.clicked(() -> {
|
||||
if(!player.dead() && mobile){
|
||||
Call.unitClear(player);
|
||||
control.input.controlledType = null;
|
||||
}
|
||||
});
|
||||
|
||||
t.add(new SideBar(() -> player.unit().healthf(), () -> true, true)).width(bw).growY().padRight(pad);
|
||||
t.image(() -> player.icon()).scaling(Scaling.bounded).grow().maxWidth(54f);
|
||||
t.add(new SideBar(() -> player.dead() ? 0f : player.displayAmmo() ? player.unit().ammof() : player.unit().healthf(), () -> !player.displayAmmo(), false)).width(bw).growY().padLeft(pad).update(b -> {
|
||||
b.color.set(player.displayAmmo() ? player.dead() ? Pal.ammo : player.unit().type().ammoType.color : Pal.health);
|
||||
b.color.set(player.displayAmmo() ? player.dead() || player.unit() instanceof BlockUnitc ? Pal.ammo : player.unit().type.ammoType.color : Pal.health);
|
||||
});
|
||||
|
||||
t.getChildren().get(1).toFront();
|
||||
@@ -749,25 +715,28 @@ public class HudFragment extends Fragment{
|
||||
|
||||
table.labelWrap(() -> {
|
||||
builder.setLength(0);
|
||||
builder.append(wavef.get(state.wave));
|
||||
builder.append("\n");
|
||||
|
||||
if(inLaunchWave()){
|
||||
builder.append("[#");
|
||||
Tmp.c1.set(Color.white).lerp(state.enemies > 0 ? Color.white : Color.scarlet, Mathf.absin(Time.time(), 2f, 1f)).toString(builder);
|
||||
builder.append("]");
|
||||
|
||||
if(!canLaunch()){
|
||||
builder.append(Core.bundle.get("launch.unable2"));
|
||||
}else{
|
||||
builder.append(Core.bundle.get("launch"));
|
||||
builder.append("\n");
|
||||
builder.append(Core.bundle.format("launch.next", state.wave + state.getSector().launchPeriod));
|
||||
builder.append("\n");
|
||||
}
|
||||
builder.append("[]\n");
|
||||
if(!state.rules.waves && state.rules.attackMode){
|
||||
int sum = Math.max(state.teams.present.sum(t -> t.team != player.team() ? t.cores.size : 0), 1);
|
||||
builder.append(sum > 1 ? enemycsf.get(sum) : enemycf.get(sum));
|
||||
return builder;
|
||||
}
|
||||
|
||||
if(!state.rules.waves && state.isCampaign()){
|
||||
builder.append("[lightgray]").append(Core.bundle.get("sector.curcapture"));
|
||||
}
|
||||
|
||||
if(!state.rules.waves){
|
||||
return builder;
|
||||
}
|
||||
|
||||
if(state.rules.winWave > 1 && state.rules.winWave >= state.wave && state.isCampaign()){
|
||||
builder.append(wavefc.get(state.wave, state.rules.winWave));
|
||||
}else{
|
||||
builder.append(wavef.get(state.wave));
|
||||
}
|
||||
builder.append("\n");
|
||||
|
||||
if(state.enemies > 0){
|
||||
if(state.enemies == 1){
|
||||
builder.append(enemyf.get(state.enemies));
|
||||
@@ -778,7 +747,7 @@ public class HudFragment extends Fragment{
|
||||
}
|
||||
|
||||
if(state.rules.waveTimer){
|
||||
builder.append((logic.isWaitingWave() ? Core.bundle.get("wave.waveInProgress") : ( waitingf.get((int)(state.wavetime/60)))));
|
||||
builder.append((logic.isWaitingWave() ? Core.bundle.get("wave.waveInProgress") : (waitingf.get((int)(state.wavetime/60)))));
|
||||
}else if(state.enemies == 0){
|
||||
builder.append(Core.bundle.get("waiting"));
|
||||
}
|
||||
@@ -786,19 +755,25 @@ public class HudFragment extends Fragment{
|
||||
return builder;
|
||||
}).growX().pad(8f);
|
||||
|
||||
table.setDisabled(() -> !canLaunch());
|
||||
table.visible(() -> state.rules.waves);
|
||||
table.clicked(() -> {
|
||||
if(canLaunch()){
|
||||
showLaunchConfirm();
|
||||
table.row();
|
||||
|
||||
var count = new float[]{-1};
|
||||
table.table().update(t -> {
|
||||
if(player.unit() instanceof Payloadc payload){
|
||||
if(count[0] != payload.payloadUsed()){
|
||||
payload.contentInfo(t, 8 * 2, 275f);
|
||||
count[0] = payload.payloadUsed();
|
||||
}
|
||||
}else{
|
||||
t.clear();
|
||||
}
|
||||
});
|
||||
}).growX().visible(() -> player.unit() instanceof Payloadc p && p.payloadUsed() > 0).colspan(2);
|
||||
|
||||
return table;
|
||||
}
|
||||
|
||||
private boolean canSkipWave(){
|
||||
return state.rules.waves && ((net.server() || player.admin) || !net.active()) && state.enemies == 0 && !spawner.isSpawning() && !state.rules.tutorial;
|
||||
return state.rules.waves && ((net.server() || player.admin) || !net.active()) && state.enemies == 0 && !spawner.isSpawning();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package mindustry.ui.fragments;
|
||||
|
||||
import arc.*;
|
||||
import arc.func.*;
|
||||
import arc.graphics.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.scene.*;
|
||||
import arc.scene.actions.*;
|
||||
import arc.scene.event.*;
|
||||
@@ -18,7 +20,12 @@ public class LoadingFragment extends Fragment{
|
||||
|
||||
@Override
|
||||
public void build(Group parent){
|
||||
parent.fill(Styles.black8, t -> {
|
||||
parent.fill(t -> {
|
||||
//rect must fill screen completely.
|
||||
t.rect((x, y, w, h) -> {
|
||||
Draw.alpha(t.color.a);
|
||||
Styles.black8.draw(0, 0, Core.graphics.getWidth(), Core.graphics.getHeight());
|
||||
});
|
||||
t.visible = false;
|
||||
t.touchable = Touchable.enabled;
|
||||
t.add().height(133f).row();
|
||||
|
||||
@@ -45,6 +45,7 @@ public class MenuFragment extends Fragment{
|
||||
|
||||
parent.fill(c -> {
|
||||
container = c;
|
||||
c.name = "menu container";
|
||||
|
||||
if(!mobile){
|
||||
buildDesktop();
|
||||
@@ -57,8 +58,8 @@ public class MenuFragment extends Fragment{
|
||||
|
||||
//info icon
|
||||
if(mobile){
|
||||
parent.fill(c -> c.bottom().left().button("", Styles.infot, ui.about::show).size(84, 45));
|
||||
parent.fill(c -> c.bottom().right().button("", Styles.discordt, ui.discord::show).size(84, 45));
|
||||
parent.fill(c -> c.bottom().left().button("", Styles.infot, ui.about::show).size(84, 45).name("info"));
|
||||
parent.fill(c -> c.bottom().right().button("", Styles.discordt, ui.discord::show).size(84, 45).name("discord"));
|
||||
}else if(becontrol.active()){
|
||||
parent.fill(c -> c.bottom().right().button("@be.check", Icon.refresh, () -> {
|
||||
ui.loadfrag.show();
|
||||
@@ -68,7 +69,7 @@ public class MenuFragment extends Fragment{
|
||||
ui.showInfo("@be.noupdates");
|
||||
}
|
||||
});
|
||||
}).size(200, 60).update(t -> {
|
||||
}).size(200, 60).name("becheck").update(t -> {
|
||||
t.getLabel().setColor(becontrol.isUpdateAvailable() ? Tmp.c1.set(Color.white).lerp(Pal.accent, Mathf.absin(5f, 1f)) : Color.white);
|
||||
}));
|
||||
}
|
||||
@@ -76,12 +77,13 @@ public class MenuFragment extends Fragment{
|
||||
String versionText = ((Version.build == -1) ? "[#fc8140aa]" : "[#ffffffba]") + Version.combined();
|
||||
parent.fill((x, y, w, h) -> {
|
||||
TextureRegion logo = Core.atlas.find("logo");
|
||||
float width = Core.graphics.getWidth(), height = Core.graphics.getHeight() - Core.scene.marginTop;
|
||||
float logoscl = Scl.scl(1);
|
||||
float logow = Math.min(logo.width * logoscl, Core.graphics.getWidth() - Scl.scl(20));
|
||||
float logoh = logow * (float)logo.height / logo.width;
|
||||
|
||||
float fx = (int)(Core.graphics.getWidth() / 2f);
|
||||
float fy = (int)(Core.graphics.getHeight() - 6 - logoh) + logoh / 2 - (Core.graphics.isPortrait() ? Scl.scl(30f) : 0f);
|
||||
float fx = (int)(width / 2f);
|
||||
float fy = (int)(height - 6 - logoh) + logoh / 2 - (Core.graphics.isPortrait() ? Scl.scl(30f) : 0f);
|
||||
|
||||
Draw.color();
|
||||
Draw.rect(logo, fx, fy, logow, logoh);
|
||||
@@ -93,6 +95,7 @@ public class MenuFragment extends Fragment{
|
||||
|
||||
private void buildMobile(){
|
||||
container.clear();
|
||||
container.name = "buttons";
|
||||
container.setSize(Core.graphics.getWidth(), Core.graphics.getHeight());
|
||||
|
||||
float size = 120f;
|
||||
@@ -106,7 +109,6 @@ public class MenuFragment extends Fragment{
|
||||
editor = new MobileButton(Icon.terrain, "@editor", () -> checkPlay(ui.maps::show)),
|
||||
tools = new MobileButton(Icon.settings, "@settings", ui.settings::show),
|
||||
mods = new MobileButton(Icon.book, "@mods", ui.mods::show),
|
||||
donate = new MobileButton(Icon.link, "@website", () -> Core.app.openURI("https://anuke.itch.io/mindustry")),
|
||||
exit = new MobileButton(Icon.exit, "@quit", () -> Core.app.exit());
|
||||
|
||||
if(!Core.graphics.isPortrait()){
|
||||
@@ -153,7 +155,6 @@ public class MenuFragment extends Fragment{
|
||||
container.clear();
|
||||
container.setSize(Core.graphics.getWidth(), Core.graphics.getHeight());
|
||||
|
||||
|
||||
float width = 230f;
|
||||
Drawable background = Styles.black6;
|
||||
|
||||
@@ -161,17 +162,17 @@ public class MenuFragment extends Fragment{
|
||||
container.add().width(Core.graphics.getWidth()/10f);
|
||||
container.table(background, t -> {
|
||||
t.defaults().width(width).height(70f);
|
||||
t.name = "buttons";
|
||||
|
||||
buttons(t,
|
||||
new Buttoni("@play", Icon.play,
|
||||
new Buttoni("@campaign", Icon.play, () -> checkPlay(ui.planet::show)),
|
||||
new Buttoni("@joingame", Icon.add, () -> checkPlay(ui.join::show)),
|
||||
new Buttoni("@customgame", Icon.terrain, () -> checkPlay(ui.custom::show)),
|
||||
new Buttoni("@loadgame", Icon.download, () -> checkPlay(ui.load::show)),
|
||||
new Buttoni("@tutorial", Icon.info, () -> checkPlay(control::playTutorial))
|
||||
new Buttoni("@loadgame", Icon.download, () -> checkPlay(ui.load::show))
|
||||
),
|
||||
new Buttoni("@editor", Icon.terrain, () -> checkPlay(ui.maps::show)), steam ? new Buttoni("@workshop", Icon.book, platform::openWorkshop) : null,
|
||||
new Buttoni(Core.bundle.get("mods"), Icon.bookOpen, ui.mods::show),
|
||||
new Buttoni("@mods", Icon.book, ui.mods::show),
|
||||
//not enough space for this button
|
||||
//new Buttoni("@schematics", Icon.paste, ui.schematics::show),
|
||||
new Buttoni("@settings", Icon.settings, ui.settings::show),
|
||||
@@ -183,6 +184,7 @@ public class MenuFragment extends Fragment{
|
||||
|
||||
container.table(background, t -> {
|
||||
submenu = t;
|
||||
t.name = "submenu";
|
||||
t.color.a = 0f;
|
||||
t.top();
|
||||
t.defaults().width(width).height(70f);
|
||||
@@ -192,6 +194,7 @@ public class MenuFragment extends Fragment{
|
||||
}
|
||||
|
||||
private void checkPlay(Runnable run){
|
||||
|
||||
if(!mods.hasContentErrors()){
|
||||
run.run();
|
||||
}else{
|
||||
@@ -228,7 +231,7 @@ public class MenuFragment extends Fragment{
|
||||
submenu.clearChildren();
|
||||
fadeInMenu();
|
||||
//correctly offset the button
|
||||
submenu.add().height((Core.graphics.getHeight() - out[0].getY(Align.topLeft)) / Scl.scl(1f));
|
||||
submenu.add().height((Core.graphics.getHeight() - Core.scene.marginTop - Core.scene.marginBottom - out[0].getY(Align.topLeft)) / Scl.scl(1f));
|
||||
submenu.row();
|
||||
buttons(submenu, b.submenu);
|
||||
}else{
|
||||
|
||||
@@ -18,7 +18,7 @@ public class MinimapFragment extends Fragment{
|
||||
private boolean shown;
|
||||
float panx, pany, zoom = 1f, lastZoom = -1;
|
||||
private float baseSize = Scl.scl(5f);
|
||||
private Element elem;
|
||||
public Element elem;
|
||||
|
||||
@Override
|
||||
public void build(Group parent){
|
||||
@@ -28,11 +28,11 @@ public class MinimapFragment extends Fragment{
|
||||
float size = baseSize * zoom * world.width();
|
||||
|
||||
Draw.color(Color.black);
|
||||
Fill.crect(x, y, w, h);
|
||||
Fill.crect(0, 0, w, h);
|
||||
|
||||
if(renderer.minimap.getTexture() != null){
|
||||
Draw.color();
|
||||
float ratio = (float)renderer.minimap.getTexture().height / renderer.minimap.getTexture().getWidth();
|
||||
float ratio = (float)renderer.minimap.getTexture().height / renderer.minimap.getTexture().width;
|
||||
TextureRegion reg = Draw.wrap(renderer.minimap.getTexture());
|
||||
Draw.rect(reg, w/2f + panx*zoom, h/2f + pany*zoom, size, size * ratio);
|
||||
renderer.minimap.drawEntities(w/2f + panx*zoom - size/2f, h/2f + pany*zoom - size/2f * ratio, size, size * ratio, zoom, true);
|
||||
@@ -110,13 +110,16 @@ public class MinimapFragment extends Fragment{
|
||||
return shown;
|
||||
}
|
||||
|
||||
public void hide(){
|
||||
shown = false;
|
||||
}
|
||||
|
||||
public void toggle(){
|
||||
if(Core.settings.getBool("mapcenter")){
|
||||
float size = baseSize * zoom * world.width();
|
||||
float ratio = (float)renderer.minimap.getTexture().height / renderer.minimap.getTexture().getWidth();
|
||||
panx = (size/2f - player.x() / (world.width() * tilesize) * size) / zoom;
|
||||
pany = (size*ratio/2f - player.y() / (world.height() * tilesize) * size*ratio) / zoom;
|
||||
}
|
||||
float size = baseSize * zoom * world.width();
|
||||
float ratio = (float)renderer.minimap.getTexture().height / renderer.minimap.getTexture().width;
|
||||
float px = player.dead() ? Core.camera.position.x : player.x, py = player.dead() ? Core.camera.position.y : player.y;
|
||||
panx = (size/2f - px / (world.width() * tilesize) * size) / zoom;
|
||||
pany = (size*ratio/2f - py / (world.height() * tilesize) * size*ratio) / zoom;
|
||||
shown = !shown;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@ import arc.scene.style.*;
|
||||
import arc.scene.ui.*;
|
||||
import arc.scene.ui.layout.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.ArcAnnotate.*;
|
||||
import arc.util.*;
|
||||
import mindustry.core.*;
|
||||
import mindustry.entities.*;
|
||||
@@ -31,6 +30,7 @@ public class PlacementFragment extends Fragment{
|
||||
final int rowWidth = 4;
|
||||
|
||||
public Category currentCategory = Category.distribution;
|
||||
|
||||
Seq<Block> returnArray = new Seq<>(), returnArray2 = new Seq<>();
|
||||
Seq<Category> returnCatArray = new Seq<>();
|
||||
boolean[] categoryEmpty = new boolean[Category.all.length];
|
||||
@@ -81,6 +81,10 @@ public class PlacementFragment extends Fragment{
|
||||
});
|
||||
}
|
||||
|
||||
public Displayable hover(){
|
||||
return hover;
|
||||
}
|
||||
|
||||
void rebuild(){
|
||||
currentCategory = Category.turret;
|
||||
Group group = toggler.parent;
|
||||
@@ -93,12 +97,12 @@ public class PlacementFragment extends Fragment{
|
||||
boolean gridUpdate(InputHandler input){
|
||||
scrollPositions.put(currentCategory, blockPane.getScrollY());
|
||||
|
||||
if(Core.input.keyDown(Binding.pick) && player.isBuilder()){ //mouse eyedropper select
|
||||
if(Core.input.keyTap(Binding.pick) && player.isBuilder()){ //mouse eyedropper select
|
||||
Building tile = world.buildWorld(Core.input.mouseWorld().x, Core.input.mouseWorld().y);
|
||||
Block tryRecipe = tile == null ? null : tile.block instanceof ConstructBlock ? ((ConstructBuild)tile).cblock : tile.block;
|
||||
Object tryConfig = tile == null ? null : tile.config();
|
||||
|
||||
for(BuildPlan req : player.builder().plans()){
|
||||
for(BuildPlan req : player.unit().plans()){
|
||||
if(!req.breaking && req.block.bounds(req.x, req.y, Tmp.r1).contains(Core.input.mouseWorld())){
|
||||
tryRecipe = req.block;
|
||||
tryConfig = req.config;
|
||||
@@ -118,31 +122,30 @@ public class PlacementFragment extends Fragment{
|
||||
|
||||
for(int i = 0; i < blockSelect.length; i++){
|
||||
if(Core.input.keyTap(blockSelect[i])){
|
||||
if(i > 9) { //select block directionally
|
||||
if(i > 9){ //select block directionally
|
||||
Seq<Block> blocks = getUnlockedByCategory(currentCategory);
|
||||
Block currentBlock = getSelectedBlock(currentCategory);
|
||||
for(int j = 0; j < blocks.size; j++){
|
||||
if(blocks.get(j) == currentBlock){
|
||||
switch(i){
|
||||
case 10: //left
|
||||
j = (j - 1 + blocks.size) % blocks.size;
|
||||
break;
|
||||
case 11: //right
|
||||
j = (j + 1) % blocks.size;
|
||||
break;
|
||||
case 12: //up
|
||||
//left
|
||||
case 10 -> j = (j - 1 + blocks.size) % blocks.size;
|
||||
//right
|
||||
case 11 -> j = (j + 1) % blocks.size;
|
||||
//up
|
||||
case 12 -> {
|
||||
j = (j > 3 ? j - 4 : blocks.size - blocks.size % 4 + j);
|
||||
j -= (j < blocks.size ? 0 : 4);
|
||||
break;
|
||||
case 13: //down
|
||||
j = (j < blocks.size - 4 ? j + 4 : j % 4);
|
||||
}
|
||||
//down
|
||||
case 13 -> j = (j < blocks.size - 4 ? j + 4 : j % 4);
|
||||
}
|
||||
input.block = blocks.get(j);
|
||||
selectedBlocks.put(currentCategory, input.block);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}else if(blockSelectEnd || Time.timeSinceMillis(blockSelectSeqMillis) > Core.settings.getInt("blockselecttimeout")){ //1st number of combo, select category
|
||||
}else if(blockSelectEnd || Time.timeSinceMillis(blockSelectSeqMillis) > 400){ //1st number of combo, select category
|
||||
//select only visible categories
|
||||
if(!getUnlockedByCategory(Category.all[i]).isEmpty()){
|
||||
currentCategory = Category.all[i];
|
||||
@@ -194,7 +197,7 @@ public class PlacementFragment extends Fragment{
|
||||
public void build(Group parent){
|
||||
parent.fill(full -> {
|
||||
toggler = full;
|
||||
full.bottom().right().visible(() -> ui.hudfrag.shown());
|
||||
full.bottom().right().visible(() -> ui.hudfrag.shown);
|
||||
|
||||
full.table(frame -> {
|
||||
|
||||
@@ -286,7 +289,7 @@ public class PlacementFragment extends Fragment{
|
||||
|
||||
topTable.table(header -> {
|
||||
String keyCombo = "";
|
||||
if(!mobile && Core.settings.getBool("blockselectkeys")){
|
||||
if(!mobile){
|
||||
Seq<Block> blocks = getByCategory(currentCategory);
|
||||
for(int i = 0; i < blocks.size; i++){
|
||||
if(blocks.get(i) == displayBlock && (i + 1) / 10 - 1 < blockSelect.length){
|
||||
@@ -339,7 +342,7 @@ public class PlacementFragment extends Fragment{
|
||||
topTable.row();
|
||||
topTable.table(b -> {
|
||||
b.image(Icon.cancel).padRight(2).color(Color.scarlet);
|
||||
b.add(!player.isBuilder() ? "@unit.nobuild" : displayBlock.unplaceableMessage()).width(190f).wrap();
|
||||
b.add(!player.isBuilder() ? "@unit.nobuild" : "@banned").width(190f).wrap();
|
||||
b.left();
|
||||
}).padTop(2).left();
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import arc.scene.*;
|
||||
import arc.scene.event.*;
|
||||
import arc.scene.ui.*;
|
||||
import arc.scene.ui.layout.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
@@ -20,11 +21,13 @@ public class PlayerListFragment extends Fragment{
|
||||
private boolean visible = false;
|
||||
private Interval timer = new Interval();
|
||||
private TextField sField;
|
||||
private boolean found = false;
|
||||
private Seq<Player> players = new Seq<>();
|
||||
|
||||
@Override
|
||||
public void build(Group parent){
|
||||
content.name = "players";
|
||||
parent.fill(cont -> {
|
||||
cont.name = "playerlist";
|
||||
cont.visible(() -> visible);
|
||||
cont.update(() -> {
|
||||
if(!(net.active() && state.isGame())){
|
||||
@@ -36,7 +39,7 @@ public class PlayerListFragment extends Fragment{
|
||||
rebuild();
|
||||
content.pack();
|
||||
content.act(Core.graphics.getDeltaTime());
|
||||
//TODO hack
|
||||
//hacky
|
||||
Core.scene.act(0f);
|
||||
}
|
||||
});
|
||||
@@ -47,6 +50,7 @@ public class PlayerListFragment extends Fragment{
|
||||
sField = pane.field(null, text -> {
|
||||
rebuild();
|
||||
}).grow().pad(8).get();
|
||||
sField.name = "search";
|
||||
sField.setMaxLength(maxNameLength);
|
||||
sField.setMessageText(Core.bundle.format("players.search"));
|
||||
|
||||
@@ -56,6 +60,7 @@ public class PlayerListFragment extends Fragment{
|
||||
|
||||
pane.table(menu -> {
|
||||
menu.defaults().growX().height(50f).fillY();
|
||||
menu.name = "menu";
|
||||
|
||||
menu.button("@server.bans", ui.bans::show).disabled(b -> net.client());
|
||||
menu.button("@server.admins", ui.admins::show).disabled(b -> net.client());
|
||||
@@ -72,10 +77,14 @@ public class PlayerListFragment extends Fragment{
|
||||
content.clear();
|
||||
|
||||
float h = 74f;
|
||||
found = false;
|
||||
boolean found = false;
|
||||
|
||||
Groups.player.sort(Structs.comparing(Player::team));
|
||||
Groups.player.each(user -> {
|
||||
players.clear();
|
||||
Groups.player.copy(players);
|
||||
|
||||
players.sort(Structs.comps(Structs.comparing(Player::team), Structs.comparingBool(p -> !p.admin)));
|
||||
|
||||
for(var user : players){
|
||||
found = true;
|
||||
NetConnection connection = user.con;
|
||||
|
||||
@@ -99,6 +108,7 @@ public class PlayerListFragment extends Fragment{
|
||||
};
|
||||
table.margin(8);
|
||||
table.add(new Image(user.icon()).setScaling(Scaling.bounded)).grow();
|
||||
table.name = user.name();
|
||||
|
||||
button.add(table).size(h);
|
||||
button.labelWrap("[#" + user.color().toString().toUpperCase() + "]" + user.name()).width(170f).pad(10);
|
||||
@@ -115,13 +125,9 @@ public class PlayerListFragment extends Fragment{
|
||||
t.defaults().size(bs);
|
||||
|
||||
t.button(Icon.hammer, Styles.clearPartiali,
|
||||
() -> {
|
||||
ui.showConfirm("@confirm", Core.bundle.format("confirmban", user.name()), () -> Call.adminRequest(user, AdminAction.ban));
|
||||
});
|
||||
() -> ui.showConfirm("@confirm", Core.bundle.format("confirmban", user.name()), () -> Call.adminRequest(user, AdminAction.ban)));
|
||||
t.button(Icon.cancel, Styles.clearPartiali,
|
||||
() -> {
|
||||
ui.showConfirm("@confirm", Core.bundle.format("confirmkick", user.name()), () -> Call.adminRequest(user, AdminAction.kick));
|
||||
});
|
||||
() -> ui.showConfirm("@confirm", Core.bundle.format("confirmkick", user.name()), () -> Call.adminRequest(user, AdminAction.kick)));
|
||||
|
||||
t.row();
|
||||
|
||||
@@ -158,7 +164,7 @@ public class PlayerListFragment extends Fragment{
|
||||
content.row();
|
||||
content.image().height(4f).color(state.rules.pvp ? user.team().color : Pal.gray).growX();
|
||||
content.row();
|
||||
});
|
||||
}
|
||||
|
||||
if(!found){
|
||||
content.add(Core.bundle.format("players.notfound")).padBottom(6).width(350f).maxHeight(h + 14);
|
||||
|
||||
@@ -39,7 +39,6 @@ public class ScriptConsoleFragment extends Table{
|
||||
};
|
||||
|
||||
public ScriptConsoleFragment(){
|
||||
|
||||
setFillParent(true);
|
||||
font = Fonts.def;
|
||||
|
||||
@@ -113,7 +112,7 @@ public class ScriptConsoleFragment extends Table{
|
||||
Draw.color(shadowColor);
|
||||
|
||||
if(open){
|
||||
Fill.crect(offsetx, chatfield.y, chatfield.getWidth() + 15f, chatfield.getHeight() - 1);
|
||||
Fill.crect(offsetx, chatfield.y + scene.marginBottom, chatfield.getWidth() + 15f, chatfield.getHeight() - 1);
|
||||
}
|
||||
|
||||
super.draw();
|
||||
@@ -126,7 +125,7 @@ public class ScriptConsoleFragment extends Table{
|
||||
Draw.color(shadowColor);
|
||||
Draw.alpha(shadowColor.a * opacity);
|
||||
|
||||
float theight = offsety + spacing + getMarginBottom();
|
||||
float theight = offsety + spacing + getMarginBottom() + scene.marginBottom;
|
||||
for(int i = scrollPos; i < messages.size && i < messagesShown + scrollPos; i++){
|
||||
|
||||
layout.setText(font, messages.get(i), Color.white, textWidth, Align.bottomLeft, true);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package mindustry.ui.layout;
|
||||
|
||||
import arc.struct.*;
|
||||
import arc.math.geom.*;
|
||||
import arc.struct.*;
|
||||
|
||||
/**
|
||||
* Algorithm taken from <a href="https://github.com/abego/treelayout">TreeLayout</a>.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package mindustry.ui.layout;
|
||||
|
||||
import arc.struct.*;
|
||||
import arc.math.*;
|
||||
import arc.struct.*;
|
||||
|
||||
public class RadialTreeLayout implements TreeLayout{
|
||||
private static ObjectSet<TreeNode> visited = new ObjectSet<>();
|
||||
|
||||
Reference in New Issue
Block a user