it is done
This commit is contained in:
103
core/src/mindustry/ui/Bar.java
Normal file
103
core/src/mindustry/ui/Bar.java
Normal file
@@ -0,0 +1,103 @@
|
||||
package mindustry.ui;
|
||||
|
||||
import arc.*;
|
||||
import arc.func.*;
|
||||
import arc.graphics.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.math.*;
|
||||
import arc.math.geom.*;
|
||||
import arc.scene.*;
|
||||
import arc.scene.style.*;
|
||||
import arc.util.pooling.*;
|
||||
import mindustry.gen.*;
|
||||
|
||||
public class Bar extends Element{
|
||||
private static Rectangle scissor = new Rectangle();
|
||||
|
||||
private Floatp fraction;
|
||||
private String name = "";
|
||||
private float value, lastValue, blink;
|
||||
private Color blinkColor = new Color();
|
||||
|
||||
public Bar(String name, Color color, Floatp fraction){
|
||||
this.fraction = fraction;
|
||||
this.name = Core.bundle.get(name);
|
||||
this.blinkColor.set(color);
|
||||
lastValue = value = fraction.get();
|
||||
setColor(color);
|
||||
}
|
||||
|
||||
public Bar(Prov<String> name, Prov<Color> color, Floatp fraction){
|
||||
this.fraction = fraction;
|
||||
lastValue = value = Mathf.clamp(fraction.get());
|
||||
update(() -> {
|
||||
this.name = name.get();
|
||||
this.blinkColor.set(color.get());
|
||||
setColor(color.get());
|
||||
});
|
||||
}
|
||||
|
||||
public Bar(){
|
||||
|
||||
}
|
||||
|
||||
public void reset(float value){
|
||||
this.value = lastValue = blink = value;
|
||||
}
|
||||
|
||||
public void set(Prov<String> name, Floatp fraction, Color color){
|
||||
this.fraction = fraction;
|
||||
this.lastValue = fraction.get();
|
||||
this.blinkColor.set(color);
|
||||
setColor(color);
|
||||
update(() -> this.name = name.get());
|
||||
}
|
||||
|
||||
public Bar blink(Color color){
|
||||
blinkColor.set(color);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(){
|
||||
if(fraction == null) return;
|
||||
|
||||
float computed = Mathf.clamp(fraction.get());
|
||||
if(!Mathf.equal(lastValue, computed)){
|
||||
blink = 1f;
|
||||
lastValue = computed;
|
||||
}
|
||||
|
||||
blink = Mathf.lerpDelta(blink, 0f, 0.2f);
|
||||
value = Mathf.lerpDelta(value, computed, 0.15f);
|
||||
|
||||
Drawable bar = Tex.bar;
|
||||
|
||||
Draw.colorl(0.1f);
|
||||
bar.draw(x, y, width, height);
|
||||
Draw.color(color, blinkColor, blink);
|
||||
|
||||
Drawable top = Tex.barTop;
|
||||
float topWidth = width * value;
|
||||
|
||||
if(topWidth > Core.atlas.find("bar-top").getWidth()){
|
||||
top.draw(x, y, topWidth, height);
|
||||
}else{
|
||||
if(ScissorStack.pushScissors(scissor.set(x, y, topWidth, height))){
|
||||
top.draw(x, y, Core.atlas.find("bar-top").getWidth(), height);
|
||||
ScissorStack.popScissors();
|
||||
}
|
||||
}
|
||||
|
||||
Draw.color();
|
||||
|
||||
BitmapFont font = Fonts.outline;
|
||||
GlyphLayout lay = Pools.obtain(GlyphLayout.class, GlyphLayout::new);
|
||||
lay.setText(font, name);
|
||||
|
||||
font.setColor(Color.white);
|
||||
font.draw(name, x + width / 2f - lay.width / 2f, y + height / 2f + lay.height / 2f + 1);
|
||||
|
||||
Pools.free(lay);
|
||||
}
|
||||
}
|
||||
49
core/src/mindustry/ui/BorderImage.java
Normal file
49
core/src/mindustry/ui/BorderImage.java
Normal file
@@ -0,0 +1,49 @@
|
||||
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;
|
||||
|
||||
public class BorderImage extends Image{
|
||||
public float thickness = 4f;
|
||||
public Color borderColor = Pal.gray;
|
||||
|
||||
public BorderImage(){
|
||||
|
||||
}
|
||||
|
||||
public BorderImage(Texture texture){
|
||||
super(texture);
|
||||
}
|
||||
|
||||
public BorderImage(Texture texture, float thick){
|
||||
super(texture);
|
||||
thickness = thick;
|
||||
}
|
||||
|
||||
public BorderImage(TextureRegion region, float thick){
|
||||
super(region);
|
||||
thickness = thick;
|
||||
}
|
||||
|
||||
public BorderImage border(Color color){
|
||||
this.borderColor = color;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(){
|
||||
super.draw();
|
||||
|
||||
float scaleX = getScaleX();
|
||||
float scaleY = getScaleY();
|
||||
|
||||
Draw.color(borderColor);
|
||||
Draw.alpha(parentAlpha);
|
||||
Lines.stroke(Scl.scl(thickness));
|
||||
Lines.rect(x + imageX, y + imageY, imageWidth * scaleX, imageHeight * scaleY);
|
||||
Draw.reset();
|
||||
}
|
||||
}
|
||||
23
core/src/mindustry/ui/Cicon.java
Normal file
23
core/src/mindustry/ui/Cicon.java
Normal file
@@ -0,0 +1,23 @@
|
||||
package mindustry.ui;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/** Defines sizes of a content's preview icon. */
|
||||
public enum Cicon{
|
||||
/** Full size. */
|
||||
full(0),
|
||||
tiny(8 * 2),
|
||||
small(8 * 3),
|
||||
medium(8 * 4),
|
||||
large(8 * 5),
|
||||
xlarge(8 * 6);
|
||||
|
||||
public static final Cicon[] all = values();
|
||||
public static final Cicon[] scaled = Arrays.copyOfRange(all, 1, all.length);
|
||||
|
||||
public final int size;
|
||||
|
||||
Cicon(int size){
|
||||
this.size = size;
|
||||
}
|
||||
}
|
||||
210
core/src/mindustry/ui/ContentDisplay.java
Normal file
210
core/src/mindustry/ui/ContentDisplay.java
Normal file
@@ -0,0 +1,210 @@
|
||||
package mindustry.ui;
|
||||
|
||||
import arc.*;
|
||||
import arc.struct.*;
|
||||
import arc.graphics.*;
|
||||
import arc.scene.ui.layout.*;
|
||||
import arc.util.*;
|
||||
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.addImage(block.icon(Cicon.xlarge)).size(size);
|
||||
title.add("[accent]" + block.localizedName).padLeft(5);
|
||||
});
|
||||
|
||||
table.row();
|
||||
|
||||
table.addImage().height(3).color(Color.lightGray).pad(8).padLeft(0).padRight(0).fillX();
|
||||
|
||||
table.row();
|
||||
|
||||
if(block.description != null){
|
||||
table.add(block.description).padLeft(5).padRight(5).width(400f).wrap().fillX();
|
||||
table.row();
|
||||
|
||||
table.addImage().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, Array<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("[LIGHT_GRAY]" + stat.localized() + ":[] ").left();
|
||||
Array<StatValue> arr = map.get(stat);
|
||||
for(StatValue value : arr){
|
||||
value.display(inset);
|
||||
inset.add().size(10f);
|
||||
}
|
||||
|
||||
//map.get(stat).display(inset);
|
||||
}).fillX().padLeft(10);
|
||||
table.row();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void displayItem(Table table, Item item){
|
||||
|
||||
table.table(title -> {
|
||||
title.addImage(item.icon(Cicon.xlarge)).size(8 * 6);
|
||||
title.add("[accent]" + item.localizedName).padLeft(5);
|
||||
});
|
||||
|
||||
table.row();
|
||||
|
||||
table.addImage().height(3).color(Color.lightGray).pad(15).padLeft(0).padRight(0).fillX();
|
||||
|
||||
table.row();
|
||||
|
||||
if(item.description != null){
|
||||
table.add(item.description).padLeft(5).padRight(5).width(400f).wrap().fillX();
|
||||
table.row();
|
||||
|
||||
table.addImage().height(3).color(Color.lightGray).pad(15).padLeft(0).padRight(0).fillX();
|
||||
table.row();
|
||||
}
|
||||
|
||||
table.left().defaults().fillX();
|
||||
|
||||
table.add(Core.bundle.format("item.corestorable", item.type == ItemType.material ? Core.bundle.format("yes") : Core.bundle.format("no")));
|
||||
table.row();
|
||||
|
||||
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.addImage(liquid.icon(Cicon.xlarge)).size(8 * 6);
|
||||
title.add("[accent]" + liquid.localizedName).padLeft(5);
|
||||
});
|
||||
|
||||
table.row();
|
||||
|
||||
table.addImage().height(3).color(Color.lightGray).pad(15).padLeft(0).padRight(0).fillX();
|
||||
|
||||
table.row();
|
||||
|
||||
if(liquid.description != null){
|
||||
table.add(liquid.description).padLeft(5).padRight(5).width(400f).wrap().fillX();
|
||||
table.row();
|
||||
|
||||
table.addImage().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 displayMech(Table table, Mech mech){
|
||||
table.table(title -> {
|
||||
title.addImage(mech.icon(Cicon.xlarge)).size(8 * 6);
|
||||
title.add("[accent]" + mech.localizedName).padLeft(5);
|
||||
});
|
||||
table.left().defaults().left();
|
||||
|
||||
table.row();
|
||||
|
||||
table.addImage().height(3).color(Color.lightGray).pad(15).padLeft(0).padRight(0).fillX();
|
||||
|
||||
table.row();
|
||||
|
||||
if(mech.description != null){
|
||||
table.add(mech.description).padLeft(5).padRight(5).width(400f).wrap().fillX();
|
||||
table.row();
|
||||
|
||||
table.addImage().height(3).color(Color.lightGray).pad(15).padLeft(0).padRight(0).fillX();
|
||||
table.row();
|
||||
}
|
||||
|
||||
table.left().defaults().fillX();
|
||||
|
||||
if(Core.bundle.has("mech." + mech.name + ".weapon")){
|
||||
table.add(Core.bundle.format("mech.weapon", Core.bundle.get("mech." + mech.name + ".weapon")));
|
||||
table.row();
|
||||
}
|
||||
if(Core.bundle.has("mech." + mech.name + ".ability")){
|
||||
table.add(Core.bundle.format("mech.ability", Core.bundle.get("mech." + mech.name + ".ability")));
|
||||
table.row();
|
||||
}
|
||||
|
||||
table.add(Core.bundle.format("mech.buildspeed", (int)(mech.buildPower * 100f)));
|
||||
table.row();
|
||||
|
||||
table.add(Core.bundle.format("mech.health", (int)mech.health));
|
||||
table.row();
|
||||
table.add(Core.bundle.format("mech.itemcapacity", mech.itemCapacity));
|
||||
table.row();
|
||||
|
||||
if(mech.drillPower > 0){
|
||||
table.add(Core.bundle.format("mech.minespeed", (int)(mech.mineSpeed * 100f)));
|
||||
table.row();
|
||||
table.add(Core.bundle.format("mech.minepower", mech.drillPower));
|
||||
table.row();
|
||||
}
|
||||
}
|
||||
|
||||
public static void displayUnit(Table table, UnitType unit){
|
||||
table.table(title -> {
|
||||
title.addImage(unit.icon(Cicon.xlarge)).size(8 * 6);
|
||||
title.add("[accent]" + unit.localizedName).padLeft(5);
|
||||
});
|
||||
|
||||
table.row();
|
||||
|
||||
table.addImage().height(3).color(Color.lightGray).pad(15).padLeft(0).padRight(0).fillX();
|
||||
|
||||
table.row();
|
||||
|
||||
if(unit.description != null){
|
||||
table.add(unit.description).padLeft(5).padRight(5).width(400f).wrap().fillX();
|
||||
table.row();
|
||||
|
||||
table.addImage().height(3).color(Color.lightGray).pad(15).padLeft(0).padRight(0).fillX();
|
||||
table.row();
|
||||
}
|
||||
|
||||
table.left().defaults().fillX();
|
||||
|
||||
table.add(Core.bundle.format("unit.health", unit.health));
|
||||
table.row();
|
||||
table.add(Core.bundle.format("unit.speed", Strings.fixed(unit.speed, 1)));
|
||||
table.row();
|
||||
table.row();
|
||||
}
|
||||
}
|
||||
9
core/src/mindustry/ui/Fonts.java
Normal file
9
core/src/mindustry/ui/Fonts.java
Normal file
@@ -0,0 +1,9 @@
|
||||
package mindustry.ui;
|
||||
|
||||
import arc.graphics.g2d.*;
|
||||
|
||||
public class Fonts{
|
||||
public static BitmapFont def;
|
||||
public static BitmapFont outline;
|
||||
public static BitmapFont chat;
|
||||
}
|
||||
38
core/src/mindustry/ui/GridImage.java
Normal file
38
core/src/mindustry/ui/GridImage.java
Normal file
@@ -0,0 +1,38 @@
|
||||
package mindustry.ui;
|
||||
|
||||
import arc.graphics.g2d.Fill;
|
||||
import arc.scene.Element;
|
||||
|
||||
public class GridImage extends Element{
|
||||
private int imageWidth, imageHeight;
|
||||
|
||||
public GridImage(int w, int h){
|
||||
this.imageWidth = w;
|
||||
this.imageHeight = h;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(){
|
||||
float xspace = (getWidth() / imageWidth);
|
||||
float yspace = (getHeight() / imageHeight);
|
||||
float s = 1f;
|
||||
|
||||
int minspace = 10;
|
||||
|
||||
int jumpx = (int)(Math.max(minspace, xspace) / xspace);
|
||||
int jumpy = (int)(Math.max(minspace, yspace) / yspace);
|
||||
|
||||
for(int x = 0; x <= imageWidth; x += jumpx){
|
||||
Fill.crect((int)(getX() + xspace * x - s), getY() - s, 2, getHeight() + (x == imageWidth ? 1 : 0));
|
||||
}
|
||||
|
||||
for(int y = 0; y <= imageHeight; y += jumpy){
|
||||
Fill.crect(getX() - s, (int)(getY() + y * yspace - s), getWidth(), 2);
|
||||
}
|
||||
}
|
||||
|
||||
public void setImageSize(int w, int h){
|
||||
this.imageWidth = w;
|
||||
this.imageHeight = h;
|
||||
}
|
||||
}
|
||||
14
core/src/mindustry/ui/IconSize.java
Normal file
14
core/src/mindustry/ui/IconSize.java
Normal file
@@ -0,0 +1,14 @@
|
||||
package mindustry.ui;
|
||||
|
||||
public enum IconSize{
|
||||
def(48),
|
||||
small(32),
|
||||
smaller(30),
|
||||
tiny(16);
|
||||
|
||||
public final int size;
|
||||
|
||||
IconSize(int size){
|
||||
this.size = size;
|
||||
}
|
||||
}
|
||||
33
core/src/mindustry/ui/IntFormat.java
Normal file
33
core/src/mindustry/ui/IntFormat.java
Normal file
@@ -0,0 +1,33 @@
|
||||
package mindustry.ui;
|
||||
|
||||
|
||||
import arc.Core;
|
||||
import arc.func.Func;
|
||||
|
||||
/**
|
||||
* A low-garbage way to format bundle strings.
|
||||
*/
|
||||
public class IntFormat{
|
||||
private final StringBuilder builder = new StringBuilder();
|
||||
private final String text;
|
||||
private int lastValue = Integer.MIN_VALUE;
|
||||
private Func<Integer, String> converter = String::valueOf;
|
||||
|
||||
public IntFormat(String text){
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
public IntFormat(String text, Func<Integer, String> converter){
|
||||
this.text = text;
|
||||
this.converter = converter;
|
||||
}
|
||||
|
||||
public CharSequence get(int value){
|
||||
if(lastValue != value){
|
||||
builder.setLength(0);
|
||||
builder.append(Core.bundle.format(text, converter.get(value)));
|
||||
}
|
||||
lastValue = value;
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
27
core/src/mindustry/ui/ItemDisplay.java
Normal file
27
core/src/mindustry/ui/ItemDisplay.java
Normal file
@@ -0,0 +1,27 @@
|
||||
package mindustry.ui;
|
||||
|
||||
import arc.scene.ui.layout.Table;
|
||||
import mindustry.type.Item;
|
||||
import mindustry.type.ItemStack;
|
||||
|
||||
/** An item image with text. */
|
||||
public class ItemDisplay extends Table{
|
||||
public final Item item;
|
||||
public final int amount;
|
||||
|
||||
public ItemDisplay(Item item){
|
||||
this(item, 0);
|
||||
}
|
||||
|
||||
public ItemDisplay(Item item, int amount, boolean showName){
|
||||
add(new ItemImage(new ItemStack(item, amount))).size(8 * 4).padRight(amount > 99 ? 12 : 0);
|
||||
if(showName) add(item.localizedName).padLeft(4 + amount > 99 ? 4 : 0);
|
||||
|
||||
this.item = item;
|
||||
this.amount = amount;
|
||||
}
|
||||
|
||||
public ItemDisplay(Item item, int amount){
|
||||
this(item, amount, true);
|
||||
}
|
||||
}
|
||||
34
core/src/mindustry/ui/ItemImage.java
Normal file
34
core/src/mindustry/ui/ItemImage.java
Normal file
@@ -0,0 +1,34 @@
|
||||
package mindustry.ui;
|
||||
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.scene.ui.*;
|
||||
import arc.scene.ui.layout.*;
|
||||
import mindustry.type.*;
|
||||
|
||||
public class ItemImage extends Stack{
|
||||
|
||||
public ItemImage(TextureRegion region, int amount){
|
||||
Table t = new Table().left().bottom();
|
||||
t.add(amount + "").name("item-label");
|
||||
|
||||
add(new Image(region));
|
||||
add(t);
|
||||
}
|
||||
|
||||
public ItemImage(TextureRegion region){
|
||||
Table t = new Table().left().bottom();
|
||||
|
||||
add(new Image(region));
|
||||
add(t);
|
||||
}
|
||||
|
||||
public ItemImage(ItemStack stack){
|
||||
add(new Image(stack.item.icon(Cicon.medium)));
|
||||
|
||||
if(stack.amount != 0){
|
||||
Table t = new Table().left().bottom();
|
||||
t.add(stack.amount + "").name("item-label");
|
||||
add(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
48
core/src/mindustry/ui/ItemsDisplay.java
Normal file
48
core/src/mindustry/ui/ItemsDisplay.java
Normal file
@@ -0,0 +1,48 @@
|
||||
package mindustry.ui;
|
||||
|
||||
import arc.graphics.*;
|
||||
import arc.scene.ui.layout.*;
|
||||
import mindustry.core.GameState.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.type.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
/** Displays a list of items, e.g. launched items.*/
|
||||
public class ItemsDisplay extends Table{
|
||||
private StringBuilder builder = new StringBuilder();
|
||||
|
||||
public ItemsDisplay(){
|
||||
rebuild();
|
||||
}
|
||||
|
||||
public void rebuild(){
|
||||
clear();
|
||||
top().left();
|
||||
margin(0);
|
||||
|
||||
table(Tex.button,t -> {
|
||||
t.margin(10).marginLeft(15).marginTop(15f);
|
||||
t.label(() -> state.is(State.menu) ? "$launcheditems" : "$launchinfo").colspan(3).padBottom(4).left().colspan(3).width(210f).wrap();
|
||||
t.row();
|
||||
for(Item item : content.items()){
|
||||
if(item.type == ItemType.material && data.isUnlocked(item)){
|
||||
t.label(() -> format(item)).left();
|
||||
t.addImage(item.icon(Cicon.small)).size(8 * 3).padLeft(4).padRight(4);
|
||||
t.add(item.localizedName).color(Color.lightGray).left();
|
||||
t.row();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private String format(Item item){
|
||||
builder.setLength(0);
|
||||
builder.append(ui.formatAmount(data.items().get(item, 0)));
|
||||
if(!state.is(State.menu) && !state.teams.get(player.getTeam()).cores.isEmpty() && state.teams.get(player.getTeam()).cores.first().entity != null && state.teams.get(player.getTeam()).cores.first().entity.items.get(item) > 0){
|
||||
builder.append(" [unlaunched]+ ");
|
||||
builder.append(ui.formatAmount(state.teams.get(player.getTeam()).cores.first().entity.items.get(item)));
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
}
|
||||
49
core/src/mindustry/ui/Links.java
Normal file
49
core/src/mindustry/ui/Links.java
Normal file
@@ -0,0 +1,49 @@
|
||||
package mindustry.ui;
|
||||
|
||||
import arc.Core;
|
||||
import arc.util.Strings;
|
||||
import arc.graphics.Color;
|
||||
import mindustry.graphics.Pal;
|
||||
|
||||
public class Links{
|
||||
private static LinkEntry[] links;
|
||||
|
||||
private static void createLinks(){
|
||||
links = new LinkEntry[]{
|
||||
new LinkEntry("discord", "https://discord.gg/mindustry", Color.valueOf("7289da")),
|
||||
new LinkEntry("changelog", "https://github.com/Anuken/Mindustry/releases", Pal.accent.cpy()),
|
||||
new LinkEntry("trello", "https://trello.com/b/aE2tcUwF", Color.valueOf("026aa7")),
|
||||
new LinkEntry("wiki", "https://mindustrygame.github.io/wiki/", Color.valueOf("0f142f")),
|
||||
new LinkEntry("feathub", "https://feathub.com/Anuken/Mindustry/", Color.valueOf("ebebeb")),
|
||||
new LinkEntry("reddit", "https://www.reddit.com/r/Mindustry/", Color.valueOf("ee593b")),
|
||||
new LinkEntry("itch.io", "https://anuke.itch.io/mindustry", Color.valueOf("fa5c5c")),
|
||||
new LinkEntry("google-play", "https://play.google.com/store/apps/details?id=io.anuke.mindustry", Color.valueOf("689f38")),
|
||||
new LinkEntry("f-droid", "https://f-droid.org/packages/io.anuke.mindustry/", Color.valueOf("026aa7")),
|
||||
new LinkEntry("github", "https://github.com/Anuken/Mindustry/", Color.valueOf("24292e")),
|
||||
new LinkEntry("dev-builds", "https://github.com/Anuken/MindustryBuilds", Color.valueOf("fafbfc"))
|
||||
};
|
||||
}
|
||||
|
||||
public static LinkEntry[] getLinks(){
|
||||
if(links == null){
|
||||
createLinks();
|
||||
}
|
||||
|
||||
return links;
|
||||
}
|
||||
|
||||
public static class LinkEntry{
|
||||
public final String name, title, description, link;
|
||||
public final Color color;
|
||||
|
||||
public LinkEntry(String name, String link, Color color){
|
||||
this.name = name;
|
||||
this.color = color;
|
||||
this.description = Core.bundle.getNotNull("link." + name + ".description");
|
||||
this.link = link;
|
||||
|
||||
String title = Core.bundle.getOrNull("link." + name + ".title");
|
||||
this.title = title != null ? title : Strings.capitalize(name.replace("-", " "));
|
||||
}
|
||||
}
|
||||
}
|
||||
38
core/src/mindustry/ui/LiquidDisplay.java
Normal file
38
core/src/mindustry/ui/LiquidDisplay.java
Normal file
@@ -0,0 +1,38 @@
|
||||
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;
|
||||
|
||||
/** An ItemDisplay, but for liquids. */
|
||||
public class LiquidDisplay extends Table{
|
||||
public final Liquid liquid;
|
||||
public final float amount;
|
||||
public final boolean perSecond;
|
||||
|
||||
public LiquidDisplay(Liquid liquid, float amount, boolean perSecond){
|
||||
this.liquid = liquid;
|
||||
this.amount = amount;
|
||||
this.perSecond = perSecond;
|
||||
|
||||
add(new Stack(){{
|
||||
add(new Image(liquid.icon(Cicon.medium)));
|
||||
|
||||
if(amount != 0){
|
||||
Table t = new Table().left().bottom();
|
||||
t.add(Strings.autoFixed(amount, 1));
|
||||
add(t);
|
||||
}
|
||||
}}).size(8 * 4).padRight(3 + (amount != 0 && Strings.autoFixed(amount, 1).length() > 2 ? 8 : 0));
|
||||
|
||||
if(perSecond){
|
||||
add(StatUnit.perSecond.localized()).padLeft(2).padRight(5).color(Color.lightGray);
|
||||
}
|
||||
|
||||
add(liquid.localizedName);
|
||||
}
|
||||
}
|
||||
100
core/src/mindustry/ui/Minimap.java
Normal file
100
core/src/mindustry/ui/Minimap.java
Normal file
@@ -0,0 +1,100 @@
|
||||
package mindustry.ui;
|
||||
|
||||
import arc.Core;
|
||||
import arc.graphics.g2d.Draw;
|
||||
import arc.input.KeyCode;
|
||||
import arc.scene.Element;
|
||||
import arc.scene.event.*;
|
||||
import arc.scene.ui.layout.*;
|
||||
import mindustry.gen.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class Minimap extends Table{
|
||||
|
||||
public Minimap(){
|
||||
background(Tex.pane);
|
||||
float margin = 5f;
|
||||
touchable(Touchable.enabled);
|
||||
|
||||
add(new Element(){
|
||||
{
|
||||
setSize(Scl.scl(140f));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void act(float delta){
|
||||
setPosition(Scl.scl(margin), Scl.scl(margin));
|
||||
|
||||
super.act(delta);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(){
|
||||
if(renderer.minimap.getRegion() == null) return;
|
||||
|
||||
Draw.rect(renderer.minimap.getRegion(), x + width / 2f, y + height / 2f, width, height);
|
||||
|
||||
if(renderer.minimap.getTexture() != null){
|
||||
renderer.minimap.drawEntities(x, y, width, height, false);
|
||||
}
|
||||
}
|
||||
}).size(140f);
|
||||
|
||||
margin(margin);
|
||||
|
||||
addListener(new InputListener(){
|
||||
@Override
|
||||
public boolean scrolled(InputEvent event, float x, float y, float amountx, float amounty){
|
||||
renderer.minimap.zoomBy(amounty);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
addListener(new ClickListener(){
|
||||
{
|
||||
tapSquareSize = Scl.scl(11f);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void touchUp(InputEvent event, float x, float y, int pointer, KeyCode button){
|
||||
if(inTapSquare()){
|
||||
super.touchUp(event, x, y, pointer, button);
|
||||
}else{
|
||||
pressed = false;
|
||||
pressedPointer = -1;
|
||||
pressedButton = null;
|
||||
cancelled = false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void touchDragged(InputEvent event, float x, float y, int pointer){
|
||||
if(!inTapSquare(x, y)){
|
||||
invalidateTapSquare();
|
||||
}
|
||||
super.touchDragged(event, x, y, pointer);
|
||||
|
||||
if(mobile){
|
||||
float max = Math.min(world.width(), world.height()) / 16f / 2f;
|
||||
renderer.minimap.setZoom(1f + y / height * (max - 1f));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clicked(InputEvent event, float x, float y){
|
||||
ui.minimap.show();
|
||||
}
|
||||
});
|
||||
|
||||
update(() -> {
|
||||
|
||||
Element e = Core.scene.hit(Core.input.mouseX(), Core.input.mouseY(), true);
|
||||
if(e != null && e.isDescendantOf(this)){
|
||||
requestScroll();
|
||||
}else if(hasScroll()){
|
||||
Core.scene.setScrollFocus(null);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
15
core/src/mindustry/ui/MobileButton.java
Normal file
15
core/src/mindustry/ui/MobileButton.java
Normal file
@@ -0,0 +1,15 @@
|
||||
package mindustry.ui;
|
||||
|
||||
import arc.scene.style.*;
|
||||
import arc.scene.ui.ImageButton;
|
||||
import arc.util.Align;
|
||||
|
||||
public class MobileButton extends ImageButton{
|
||||
|
||||
public MobileButton(Drawable icon, String text, Runnable listener){
|
||||
super(icon);
|
||||
clicked(listener);
|
||||
row();
|
||||
add(text).growX().wrap().center().get().setAlignment(Align.center, Align.center);
|
||||
}
|
||||
}
|
||||
33
core/src/mindustry/ui/MultiReqImage.java
Normal file
33
core/src/mindustry/ui/MultiReqImage.java
Normal file
@@ -0,0 +1,33 @@
|
||||
package mindustry.ui;
|
||||
|
||||
import arc.struct.Array;
|
||||
import arc.scene.ui.layout.Stack;
|
||||
import arc.util.Time;
|
||||
|
||||
public class MultiReqImage extends Stack{
|
||||
private Array<ReqImage> displays = new Array<>();
|
||||
private float time;
|
||||
|
||||
public void add(ReqImage display){
|
||||
displays.add(display);
|
||||
super.add(display);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void act(float delta){
|
||||
super.act(delta);
|
||||
|
||||
time += Time.delta() / 60f;
|
||||
|
||||
displays.each(req -> req.visible(false));
|
||||
|
||||
ReqImage valid = displays.find(ReqImage::valid);
|
||||
if(valid != null){
|
||||
valid.visible(true);
|
||||
}else{
|
||||
if(displays.size > 0){
|
||||
displays.get((int)(time) % displays.size).visible(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
40
core/src/mindustry/ui/ReqImage.java
Normal file
40
core/src/mindustry/ui/ReqImage.java
Normal file
@@ -0,0 +1,40 @@
|
||||
package mindustry.ui;
|
||||
|
||||
import arc.func.Boolp;
|
||||
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;
|
||||
|
||||
public class ReqImage extends Stack{
|
||||
private final Boolp valid;
|
||||
|
||||
public ReqImage(Element image, Boolp valid){
|
||||
this.valid = valid;
|
||||
add(image);
|
||||
add(new Element(){
|
||||
{
|
||||
visible(() -> !valid.get());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(){
|
||||
Lines.stroke(Scl.scl(2f), Pal.removeBack);
|
||||
Lines.line(x, y - 2f + height, x + width, y - 2f);
|
||||
Draw.color(Pal.remove);
|
||||
Lines.line(x, y + height, x + width, y);
|
||||
Draw.reset();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public ReqImage(TextureRegion region, Boolp valid){
|
||||
this(new Image(region), valid);
|
||||
}
|
||||
|
||||
public boolean valid(){
|
||||
return valid.get();
|
||||
}
|
||||
}
|
||||
339
core/src/mindustry/ui/Styles.java
Normal file
339
core/src/mindustry/ui/Styles.java
Normal file
@@ -0,0 +1,339 @@
|
||||
package mindustry.ui;
|
||||
|
||||
import arc.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import arc.graphics.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.graphics.g2d.TextureAtlas.*;
|
||||
import arc.scene.style.*;
|
||||
import arc.scene.ui.Button.*;
|
||||
import arc.scene.ui.CheckBox.*;
|
||||
import arc.scene.ui.Dialog.*;
|
||||
import arc.scene.ui.ImageButton.*;
|
||||
import arc.scene.ui.KeybindDialog.*;
|
||||
import arc.scene.ui.Label.*;
|
||||
import arc.scene.ui.ScrollPane.*;
|
||||
import arc.scene.ui.Slider.*;
|
||||
import arc.scene.ui.TextButton.*;
|
||||
import arc.scene.ui.TextField.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
|
||||
import static mindustry.gen.Tex.*;
|
||||
|
||||
@StyleDefaults
|
||||
public class Styles{
|
||||
public static Drawable black, black9, black8, black6, black3, none, flatDown, flatOver;
|
||||
public static ButtonStyle defaultb, waveb;
|
||||
public static TextButtonStyle defaultt, squaret, nodet, cleart, discordt, infot, clearPartialt, clearTogglet, clearToggleMenut, togglet, transt;
|
||||
public static ImageButtonStyle defaulti, nodei, righti, emptyi, emptytogglei, selecti, cleari, clearFulli, clearPartiali, clearPartial2i, clearTogglei, clearTransi, clearToggleTransi, clearTogglePartiali;
|
||||
public static ScrollPaneStyle defaultPane, horizontalPane, smallPane;
|
||||
public static KeybindDialogStyle defaultKeybindDialog;
|
||||
public static SliderStyle defaultSlider, vSlider;
|
||||
public static LabelStyle defaultLabel, outlineLabel;
|
||||
public static TextFieldStyle defaultField, areaField;
|
||||
public static CheckBoxStyle defaultCheck;
|
||||
public static DialogStyle defaultDialog, fullDialog;
|
||||
|
||||
public static void load(){
|
||||
black = whiteui.tint(0f, 0f, 0f, 1f);
|
||||
black9 = whiteui.tint(0f, 0f, 0f, 0.9f);
|
||||
black8 = whiteui.tint(0f, 0f, 0f, 0.8f);
|
||||
black6 = whiteui.tint(0f, 0f, 0f, 0.6f);
|
||||
black3 = whiteui.tint(0f, 0f, 0f, 0.3f);
|
||||
none = whiteui.tint(0f, 0f, 0f, 0f);
|
||||
flatDown = createFlatDown();
|
||||
flatOver = whiteui.tint(Color.valueOf("454545"));
|
||||
|
||||
defaultb = new ButtonStyle(){{
|
||||
down = buttonDown;
|
||||
up = button;
|
||||
over = buttonOver;
|
||||
disabled = buttonDisabled;
|
||||
}};
|
||||
|
||||
waveb = new ButtonStyle(){{
|
||||
up = buttonEdge4;
|
||||
over = buttonEdgeOver4;
|
||||
disabled = buttonEdge4;
|
||||
}};
|
||||
|
||||
defaultt = new TextButtonStyle(){{
|
||||
over = buttonOver;
|
||||
disabled = buttonDisabled;
|
||||
font = Fonts.def;
|
||||
fontColor = Color.white;
|
||||
disabledFontColor = Color.gray;
|
||||
down = buttonDown;
|
||||
up = button;
|
||||
}};
|
||||
squaret = new TextButtonStyle(){{
|
||||
font = Fonts.def;
|
||||
fontColor = Color.white;
|
||||
disabledFontColor = Color.gray;
|
||||
over = buttonSquareOver;
|
||||
disabled = buttonDisabled;
|
||||
down = buttonSquareDown;
|
||||
up = buttonSquare;
|
||||
}};
|
||||
nodet = new TextButtonStyle(){{
|
||||
disabled = button;
|
||||
font = Fonts.def;
|
||||
fontColor = Color.white;
|
||||
disabledFontColor = Color.gray;
|
||||
up = buttonOver;
|
||||
over = buttonDown;
|
||||
}};
|
||||
cleart = new TextButtonStyle(){{
|
||||
over = flatOver;
|
||||
font = Fonts.def;
|
||||
fontColor = Color.white;
|
||||
disabledFontColor = Color.gray;
|
||||
down = flatOver;
|
||||
up = black;
|
||||
}};
|
||||
discordt = new TextButtonStyle(){{
|
||||
font = Fonts.def;
|
||||
fontColor = Color.white;
|
||||
up = discordBanner;
|
||||
}};
|
||||
infot = new TextButtonStyle(){{
|
||||
font = Fonts.def;
|
||||
fontColor = Color.white;
|
||||
up = infoBanner;
|
||||
}};
|
||||
clearPartialt = new TextButtonStyle(){{
|
||||
down = whiteui;
|
||||
up = pane;
|
||||
over = flatDown;
|
||||
font = Fonts.def;
|
||||
fontColor = Color.white;
|
||||
disabledFontColor = Color.gray;
|
||||
}};
|
||||
transt = new TextButtonStyle(){{
|
||||
down = flatDown;
|
||||
up = none;
|
||||
over = flatOver;
|
||||
font = Fonts.def;
|
||||
fontColor = Color.white;
|
||||
disabledFontColor = Color.gray;
|
||||
}};
|
||||
clearTogglet = new TextButtonStyle(){{
|
||||
font = Fonts.def;
|
||||
fontColor = Color.white;
|
||||
checked = flatDown;
|
||||
down = flatDown;
|
||||
up = black;
|
||||
over = flatOver;
|
||||
disabled = black;
|
||||
disabledFontColor = Color.gray;
|
||||
}};
|
||||
clearToggleMenut = new TextButtonStyle(){{
|
||||
font = Fonts.def;
|
||||
fontColor = Color.white;
|
||||
checked = flatDown;
|
||||
down = flatDown;
|
||||
up = clear;
|
||||
over = flatOver;
|
||||
disabled = black;
|
||||
disabledFontColor = Color.gray;
|
||||
}};
|
||||
togglet = new TextButtonStyle(){{
|
||||
font = Fonts.def;
|
||||
fontColor = Color.white;
|
||||
checked = buttonDown;
|
||||
down = buttonDown;
|
||||
up = button;
|
||||
over = buttonOver;
|
||||
disabled = buttonDisabled;
|
||||
disabledFontColor = Color.gray;
|
||||
}};
|
||||
|
||||
defaulti = new ImageButtonStyle(){{
|
||||
down = buttonDown;
|
||||
up = button;
|
||||
over = buttonOver;
|
||||
imageDisabledColor = Color.gray;
|
||||
imageUpColor = Color.white;
|
||||
disabled = buttonDisabled;
|
||||
}};
|
||||
nodei = new ImageButtonStyle(){{
|
||||
up = buttonOver;
|
||||
over = buttonDown;
|
||||
}};
|
||||
righti = new ImageButtonStyle(){{
|
||||
over = buttonRightOver;
|
||||
down = buttonRightDown;
|
||||
up = buttonRight;
|
||||
}};
|
||||
emptyi = new ImageButtonStyle(){{
|
||||
imageDownColor = Pal.accent;
|
||||
imageUpColor = Color.white;
|
||||
}};
|
||||
emptytogglei = new ImageButtonStyle(){{
|
||||
imageCheckedColor = Color.white;
|
||||
imageDownColor = Color.white;
|
||||
imageUpColor = Color.gray;
|
||||
}};
|
||||
selecti = new ImageButtonStyle(){{
|
||||
checked = buttonSelect;
|
||||
up = none;
|
||||
}};
|
||||
cleari = new ImageButtonStyle(){{
|
||||
down = flatOver;
|
||||
up = black;
|
||||
over = flatOver;
|
||||
}};
|
||||
clearFulli = new ImageButtonStyle(){{
|
||||
down = whiteui;
|
||||
up = pane;
|
||||
over = flatDown;
|
||||
}};
|
||||
clearPartiali = new ImageButtonStyle(){{
|
||||
down = flatDown;
|
||||
up = none;
|
||||
over = flatOver;
|
||||
}};
|
||||
clearPartial2i = new ImageButtonStyle(){{
|
||||
down = whiteui;
|
||||
up = pane;
|
||||
over = flatDown;
|
||||
}};
|
||||
clearTogglei = new ImageButtonStyle(){{
|
||||
down = flatDown;
|
||||
checked = flatDown;
|
||||
up = black;
|
||||
over = flatOver;
|
||||
}};
|
||||
clearTransi = new ImageButtonStyle(){{
|
||||
down = flatDown;
|
||||
up = black6;
|
||||
over = flatOver;
|
||||
disabled = black8;
|
||||
imageDisabledColor = Color.lightGray;
|
||||
imageUpColor = Color.white;
|
||||
}};
|
||||
clearToggleTransi = new ImageButtonStyle(){{
|
||||
down = flatDown;
|
||||
checked = flatDown;
|
||||
up = black6;
|
||||
over = flatOver;
|
||||
}};
|
||||
clearTogglePartiali = new ImageButtonStyle(){{
|
||||
down = flatDown;
|
||||
checked = flatDown;
|
||||
up = none;
|
||||
over = flatOver;
|
||||
}};
|
||||
|
||||
defaultPane = new ScrollPaneStyle(){{
|
||||
vScroll = scroll;
|
||||
vScrollKnob = scrollKnobVerticalBlack;
|
||||
}};
|
||||
horizontalPane = new ScrollPaneStyle(){{
|
||||
vScroll = scroll;
|
||||
vScrollKnob = scrollKnobVerticalBlack;
|
||||
hScroll = scrollHorizontal;
|
||||
hScrollKnob = scrollKnobHorizontalBlack;
|
||||
}};
|
||||
smallPane = new ScrollPaneStyle(){{
|
||||
vScroll = clear;
|
||||
vScrollKnob = scrollKnobVerticalThin;
|
||||
}};
|
||||
|
||||
defaultKeybindDialog = new KeybindDialogStyle(){{
|
||||
keyColor = Pal.accent;
|
||||
keyNameColor = Color.white;
|
||||
controllerColor = Color.lightGray;
|
||||
}};
|
||||
|
||||
defaultSlider = new SliderStyle(){{
|
||||
background = slider;
|
||||
knob = sliderKnob;
|
||||
knobOver = sliderKnobOver;
|
||||
knobDown = sliderKnobDown;
|
||||
}};
|
||||
vSlider = new SliderStyle(){{
|
||||
background = sliderVertical;
|
||||
knob = sliderKnob;
|
||||
knobOver = sliderKnobOver;
|
||||
knobDown = sliderKnobDown;
|
||||
}};
|
||||
|
||||
defaultLabel = new LabelStyle(){{
|
||||
font = Fonts.def;
|
||||
fontColor = Color.white;
|
||||
}};
|
||||
outlineLabel = new LabelStyle(){{
|
||||
font = Fonts.outline;
|
||||
fontColor = Color.white;
|
||||
}};
|
||||
|
||||
defaultField = new TextFieldStyle(){{
|
||||
font = Fonts.chat;
|
||||
fontColor = Color.white;
|
||||
disabledFontColor = Color.gray;
|
||||
disabledBackground = underlineDisabled;
|
||||
selection = Tex.selection;
|
||||
background = underline;
|
||||
invalidBackground = underlineRed;
|
||||
cursor = Tex.cursor;
|
||||
messageFont = Fonts.def;
|
||||
messageFontColor = Color.gray;
|
||||
}};
|
||||
areaField = new TextFieldStyle(){{
|
||||
font = Fonts.chat;
|
||||
fontColor = Color.white;
|
||||
disabledFontColor = Color.gray;
|
||||
selection = Tex.selection;
|
||||
background = underline;
|
||||
cursor = Tex.cursor;
|
||||
messageFont = Fonts.def;
|
||||
messageFontColor = Color.gray;
|
||||
}};
|
||||
|
||||
defaultCheck = new CheckBoxStyle(){{
|
||||
checkboxOn = checkOn;
|
||||
checkboxOff = checkOff;
|
||||
checkboxOnOver = checkOnOver;
|
||||
checkboxOver = checkOver;
|
||||
checkboxOnDisabled = checkOnDisabled;
|
||||
checkboxOffDisabled = checkDisabled;
|
||||
font = Fonts.def;
|
||||
fontColor = Color.white;
|
||||
disabledFontColor = Color.gray;
|
||||
}};
|
||||
|
||||
defaultDialog = new DialogStyle(){{
|
||||
stageBackground = black9;
|
||||
titleFont = Fonts.def;
|
||||
background = windowEmpty;
|
||||
titleFontColor = Pal.accent;
|
||||
}};
|
||||
fullDialog = new DialogStyle(){{
|
||||
stageBackground = black;
|
||||
titleFont = Fonts.def;
|
||||
background = windowEmpty;
|
||||
titleFontColor = Pal.accent;
|
||||
}};
|
||||
}
|
||||
|
||||
private static Drawable createFlatDown(){
|
||||
AtlasRegion region = Core.atlas.find("flat-down-base");
|
||||
int[] splits = region.splits;
|
||||
|
||||
ScaledNinePatchDrawable copy = new ScaledNinePatchDrawable(new NinePatch(region, splits[0], splits[1], splits[2], splits[3])){
|
||||
public float getLeftWidth(){ return 0; }
|
||||
public float getRightWidth(){ return 0; }
|
||||
public float getTopHeight(){ return 0; }
|
||||
public float getBottomHeight(){ return 0; }
|
||||
};
|
||||
copy.setMinWidth(0);
|
||||
copy.setMinHeight(0);
|
||||
copy.setTopHeight(0);
|
||||
copy.setRightWidth(0);
|
||||
copy.setBottomHeight(0);
|
||||
copy.setLeftWidth(0);
|
||||
return copy;
|
||||
}
|
||||
}
|
||||
118
core/src/mindustry/ui/dialogs/AboutDialog.java
Normal file
118
core/src/mindustry/ui/dialogs/AboutDialog.java
Normal file
@@ -0,0 +1,118 @@
|
||||
package mindustry.ui.dialogs;
|
||||
|
||||
import arc.*;
|
||||
import arc.struct.*;
|
||||
import arc.graphics.*;
|
||||
import arc.scene.ui.*;
|
||||
import arc.scene.ui.layout.*;
|
||||
import arc.util.*;
|
||||
import mindustry.game.EventType.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.ui.*;
|
||||
import mindustry.ui.Links.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class AboutDialog extends FloatingDialog{
|
||||
private Array<String> contributors = new Array<>();
|
||||
private static ObjectSet<String> bannedItems = ObjectSet.with("google-play", "itch.io", "dev-builds", "f-droid");
|
||||
|
||||
public AboutDialog(){
|
||||
super("$about.button");
|
||||
|
||||
shown(() -> {
|
||||
contributors = Array.with(Core.files.internal("contributors").readString("UTF-8").split("\n"));
|
||||
Core.app.post(this::setup);
|
||||
});
|
||||
|
||||
shown(this::setup);
|
||||
onResize(this::setup);
|
||||
}
|
||||
|
||||
void setup(){
|
||||
cont.clear();
|
||||
buttons.clear();
|
||||
|
||||
float h = Core.graphics.isPortrait() ? 90f : 80f;
|
||||
float w = Core.graphics.isPortrait() ? 330f : 600f;
|
||||
|
||||
Table in = new Table();
|
||||
ScrollPane pane = new ScrollPane(in);
|
||||
|
||||
for(LinkEntry link : Links.getLinks()){
|
||||
if((ios || OS.isMac || steam) && bannedItems.contains(link.name)){
|
||||
continue;
|
||||
}
|
||||
|
||||
Table table = new Table(Tex.underline);
|
||||
table.margin(0);
|
||||
table.table(img -> {
|
||||
img.addImage().height(h - 5).width(40f).color(link.color);
|
||||
img.row();
|
||||
img.addImage().height(5).width(40f).color(link.color.cpy().mul(0.8f, 0.8f, 0.8f, 1f));
|
||||
}).expandY();
|
||||
|
||||
table.table(i -> {
|
||||
i.background(Tex.buttonEdge3);
|
||||
i.addImage(Core.atlas.drawable("icon-" + link.name));
|
||||
}).size(h - 5, h);
|
||||
|
||||
table.table(inset -> {
|
||||
inset.add("[accent]" + link.title).growX().left();
|
||||
inset.row();
|
||||
inset.labelWrap(link.description).width(w - 100f).color(Color.lightGray).growX();
|
||||
}).padLeft(8);
|
||||
|
||||
table.addImageButton(Icon.link, () -> {
|
||||
if(link.name.equals("wiki")) Events.fire(Trigger.openWiki);
|
||||
|
||||
if(!Core.net.openURI(link.link)){
|
||||
ui.showErrorMessage("$linkfail");
|
||||
Core.app.setClipboardText(link.link);
|
||||
}
|
||||
}).size(h - 5, h);
|
||||
|
||||
in.add(table).size(w, h).padTop(5).row();
|
||||
}
|
||||
|
||||
shown(() -> Time.run(1f, () -> Core.scene.setScrollFocus(pane)));
|
||||
|
||||
cont.add(pane).growX();
|
||||
|
||||
addCloseButton();
|
||||
|
||||
buttons.addButton("$credits", this::showCredits).size(200f, 64f);
|
||||
|
||||
if(Core.graphics.isPortrait()){
|
||||
for(Cell<?> cell : buttons.getCells()){
|
||||
cell.width(140f);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void showCredits(){
|
||||
FloatingDialog dialog = new FloatingDialog("$credits");
|
||||
dialog.addCloseButton();
|
||||
dialog.cont.add("$credits.text");
|
||||
dialog.cont.row();
|
||||
if(!contributors.isEmpty()){
|
||||
dialog.cont.addImage().color(Pal.accent).fillX().height(3f).pad(3f);
|
||||
dialog.cont.row();
|
||||
dialog.cont.add("$contributors");
|
||||
dialog.cont.row();
|
||||
dialog.cont.pane(new Table(){{
|
||||
int i = 0;
|
||||
left();
|
||||
for(String c : contributors){
|
||||
add("[lightgray]" + c).left().pad(3).padLeft(6).padRight(6);
|
||||
if(++i % 3 == 0){
|
||||
row();
|
||||
}
|
||||
}
|
||||
}});
|
||||
}
|
||||
dialog.show();
|
||||
}
|
||||
}
|
||||
59
core/src/mindustry/ui/dialogs/AdminsDialog.java
Normal file
59
core/src/mindustry/ui/dialogs/AdminsDialog.java
Normal file
@@ -0,0 +1,59 @@
|
||||
package mindustry.ui.dialogs;
|
||||
|
||||
import arc.scene.ui.*;
|
||||
import arc.scene.ui.layout.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.net.Administration.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class AdminsDialog extends FloatingDialog{
|
||||
|
||||
public AdminsDialog(){
|
||||
super("$server.admins");
|
||||
|
||||
addCloseButton();
|
||||
|
||||
setup();
|
||||
shown(this::setup);
|
||||
}
|
||||
|
||||
private void setup(){
|
||||
cont.clear();
|
||||
|
||||
float w = 400f, h = 80f;
|
||||
|
||||
Table table = new Table();
|
||||
|
||||
ScrollPane pane = new ScrollPane(table);
|
||||
pane.setFadeScrollBars(false);
|
||||
|
||||
if(netServer.admins.getAdmins().size == 0){
|
||||
table.add("$server.admins.none");
|
||||
}
|
||||
|
||||
for(PlayerInfo info : netServer.admins.getAdmins()){
|
||||
Table res = new Table(Tex.button);
|
||||
res.margin(14f);
|
||||
|
||||
res.labelWrap("[LIGHT_GRAY]" + info.lastName).width(w - h - 24f);
|
||||
res.add().growX();
|
||||
res.addImageButton(Icon.cancel, () -> {
|
||||
ui.showConfirm("$confirm", "$confirmunadmin", () -> {
|
||||
netServer.admins.unAdminPlayer(info.id);
|
||||
playerGroup.all().each(player -> {
|
||||
if(player != null && player.uuid != null && player.uuid.equals(info.id)){
|
||||
player.isAdmin = false;
|
||||
}
|
||||
});
|
||||
setup();
|
||||
});
|
||||
}).size(h).pad(-14f);
|
||||
|
||||
table.add(res).width(w).height(h);
|
||||
table.row();
|
||||
}
|
||||
|
||||
cont.add(pane);
|
||||
}
|
||||
}
|
||||
55
core/src/mindustry/ui/dialogs/BansDialog.java
Normal file
55
core/src/mindustry/ui/dialogs/BansDialog.java
Normal file
@@ -0,0 +1,55 @@
|
||||
package mindustry.ui.dialogs;
|
||||
|
||||
import arc.scene.ui.*;
|
||||
import arc.scene.ui.layout.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.net.Administration.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class BansDialog extends FloatingDialog{
|
||||
|
||||
public BansDialog(){
|
||||
super("$server.bans");
|
||||
|
||||
addCloseButton();
|
||||
|
||||
setup();
|
||||
|
||||
shown(this::setup);
|
||||
}
|
||||
|
||||
private void setup(){
|
||||
cont.clear();
|
||||
|
||||
float w = 400f, h = 80f;
|
||||
|
||||
Table table = new Table();
|
||||
|
||||
ScrollPane pane = new ScrollPane(table);
|
||||
pane.setFadeScrollBars(false);
|
||||
|
||||
if(netServer.admins.getBanned().size == 0){
|
||||
table.add("$server.bans.none");
|
||||
}
|
||||
|
||||
for(PlayerInfo info : netServer.admins.getBanned()){
|
||||
Table res = new Table(Tex.button);
|
||||
res.margin(14f);
|
||||
|
||||
res.labelWrap("IP: [LIGHT_GRAY]" + info.lastIP + "\n[]Name: [LIGHT_GRAY]" + info.lastName).width(w - h - 24f);
|
||||
res.add().growX();
|
||||
res.addImageButton(Icon.cancel, () -> {
|
||||
ui.showConfirm("$confirm", "$confirmunban", () -> {
|
||||
netServer.admins.unbanPlayerID(info.id);
|
||||
setup();
|
||||
});
|
||||
}).size(h).pad(-14f);
|
||||
|
||||
table.add(res).width(w).height(h);
|
||||
table.row();
|
||||
}
|
||||
|
||||
cont.add(pane);
|
||||
}
|
||||
}
|
||||
63
core/src/mindustry/ui/dialogs/ColorPicker.java
Normal file
63
core/src/mindustry/ui/dialogs/ColorPicker.java
Normal file
@@ -0,0 +1,63 @@
|
||||
package mindustry.ui.dialogs;
|
||||
|
||||
import arc.func.*;
|
||||
import arc.graphics.*;
|
||||
import arc.scene.ui.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
|
||||
public class ColorPicker extends FloatingDialog{
|
||||
private Cons<Color> cons = c -> {};
|
||||
private Color current = new Color();
|
||||
|
||||
public ColorPicker(){
|
||||
super("$pickcolor");
|
||||
}
|
||||
|
||||
public void show(Color color, Cons<Color> consumer){
|
||||
show(color, true, consumer);
|
||||
}
|
||||
|
||||
public void show(Color color, boolean alpha, Cons<Color> consumer){
|
||||
this.current.set(color);
|
||||
this.cons = consumer;
|
||||
show();
|
||||
|
||||
cont.clear();
|
||||
cont.pane(t -> {
|
||||
t.table(Tex.pane, i -> {
|
||||
i.stack(new Image(Tex.alphaBg), new Image(){{
|
||||
setColor(current);
|
||||
update(() -> setColor(current));
|
||||
}}).size(200f);
|
||||
}).colspan(2).padBottom(5);
|
||||
|
||||
float w = 150f;
|
||||
|
||||
t.row();
|
||||
|
||||
t.defaults().padBottom(4);
|
||||
t.add("R").color(Pal.remove);
|
||||
t.addSlider(0f, 1f, 0.01f, current.r, current::r).width(w);
|
||||
t.row();
|
||||
t.add("G").color(Color.lime);
|
||||
t.addSlider(0f, 1f, 0.01f, current.g, current::g).width(w);
|
||||
t.row();
|
||||
t.add("B").color(Color.royal);
|
||||
t.addSlider(0f, 1f, 0.01f, current.b, current::b).width(w);
|
||||
t.row();
|
||||
if(alpha){
|
||||
t.add("A");
|
||||
t.addSlider(0f, 1f, 0.01f, current.a, current::a).width(w);
|
||||
t.row();
|
||||
}
|
||||
});
|
||||
|
||||
buttons.clear();
|
||||
addCloseButton();
|
||||
buttons.addImageTextButton("$ok", Icon.checkSmall, () -> {
|
||||
cons.get(current);
|
||||
hide();
|
||||
});
|
||||
}
|
||||
}
|
||||
28
core/src/mindustry/ui/dialogs/ContentInfoDialog.java
Normal file
28
core/src/mindustry/ui/dialogs/ContentInfoDialog.java
Normal file
@@ -0,0 +1,28 @@
|
||||
package mindustry.ui.dialogs;
|
||||
|
||||
import arc.scene.ui.ScrollPane;
|
||||
import arc.scene.ui.layout.Table;
|
||||
import mindustry.ctype.UnlockableContent;
|
||||
|
||||
public class ContentInfoDialog extends FloatingDialog{
|
||||
|
||||
public ContentInfoDialog(){
|
||||
super("$info.title");
|
||||
|
||||
addCloseButton();
|
||||
}
|
||||
|
||||
public void show(UnlockableContent content){
|
||||
cont.clear();
|
||||
|
||||
Table table = new Table();
|
||||
table.margin(10);
|
||||
|
||||
content.displayInfo(table);
|
||||
|
||||
ScrollPane pane = new ScrollPane(table);
|
||||
cont.add(pane);
|
||||
|
||||
show();
|
||||
}
|
||||
}
|
||||
27
core/src/mindustry/ui/dialogs/ControlsDialog.java
Normal file
27
core/src/mindustry/ui/dialogs/ControlsDialog.java
Normal file
@@ -0,0 +1,27 @@
|
||||
package mindustry.ui.dialogs;
|
||||
|
||||
import arc.input.*;
|
||||
import arc.scene.ui.*;
|
||||
import arc.util.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
|
||||
public class ControlsDialog extends KeybindDialog{
|
||||
|
||||
public ControlsDialog(){
|
||||
setFillParent(true);
|
||||
title.setAlignment(Align.center);
|
||||
titleTable.row();
|
||||
titleTable.add(new Image()).growX().height(3f).pad(4f).get().setColor(Pal.accent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addCloseButton(){
|
||||
buttons.addImageTextButton("$back", Icon.arrowLeftSmall, this::hide).size(230f, 64f);
|
||||
|
||||
keyDown(key -> {
|
||||
if(key == KeyCode.ESCAPE || key == KeyCode.BACK)
|
||||
hide();
|
||||
});
|
||||
}
|
||||
}
|
||||
92
core/src/mindustry/ui/dialogs/CustomGameDialog.java
Normal file
92
core/src/mindustry/ui/dialogs/CustomGameDialog.java
Normal file
@@ -0,0 +1,92 @@
|
||||
package mindustry.ui.dialogs;
|
||||
|
||||
import arc.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.math.*;
|
||||
import arc.scene.ui.*;
|
||||
import arc.scene.ui.layout.*;
|
||||
import arc.util.*;
|
||||
import mindustry.*;
|
||||
import mindustry.game.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.maps.*;
|
||||
import mindustry.ui.*;
|
||||
|
||||
public class CustomGameDialog extends FloatingDialog{
|
||||
private MapPlayDialog dialog = new MapPlayDialog();
|
||||
|
||||
public CustomGameDialog(){
|
||||
super("$customgame");
|
||||
addCloseButton();
|
||||
shown(this::setup);
|
||||
onResize(this::setup);
|
||||
}
|
||||
|
||||
void setup(){
|
||||
clearChildren();
|
||||
add(titleTable);
|
||||
row();
|
||||
stack(cont, buttons).grow();
|
||||
buttons.bottom();
|
||||
cont.clear();
|
||||
|
||||
Table maps = new Table();
|
||||
maps.marginRight(14);
|
||||
maps.marginBottom(55f);
|
||||
ScrollPane pane = new ScrollPane(maps);
|
||||
pane.setFadeScrollBars(false);
|
||||
|
||||
int maxwidth = Mathf.clamp((int)(Core.graphics.getWidth() / Scl.scl(200)), 1, 8);
|
||||
float images = 146f;
|
||||
|
||||
int i = 0;
|
||||
maps.defaults().width(170).fillY().top().pad(4f);
|
||||
for(Map map : Vars.maps.all()){
|
||||
|
||||
if(i % maxwidth == 0){
|
||||
maps.row();
|
||||
}
|
||||
|
||||
ImageButton image = new ImageButton(new TextureRegion(map.safeTexture()), Styles.cleari);
|
||||
image.margin(5);
|
||||
image.top();
|
||||
|
||||
Image img = image.getImage();
|
||||
img.remove();
|
||||
|
||||
image.row();
|
||||
image.table(t -> {
|
||||
t.left();
|
||||
for(Gamemode mode : Gamemode.all){
|
||||
if(mode.valid(map) && Core.atlas.has("icon-mode-" + mode.name())){
|
||||
t.addImage(Core.atlas.drawable("icon-mode-" + mode.name())).size(16f).pad(4f);
|
||||
}
|
||||
}
|
||||
}).left();
|
||||
image.row();
|
||||
image.add(map.name()).pad(1f).growX().wrap().left().get().setEllipsis(true);
|
||||
image.row();
|
||||
image.addImage(Tex.whiteui, Pal.gray).growX().pad(3).height(4f);
|
||||
image.row();
|
||||
image.add(img).size(images);
|
||||
|
||||
|
||||
BorderImage border = new BorderImage(map.safeTexture(), 3f);
|
||||
border.setScaling(Scaling.fit);
|
||||
image.replaceImage(border);
|
||||
|
||||
image.clicked(() -> dialog.show(map));
|
||||
|
||||
maps.add(image);
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
if(Vars.maps.all().size == 0){
|
||||
maps.add("$maps.none").pad(50);
|
||||
}
|
||||
|
||||
cont.add(pane).uniformX();
|
||||
}
|
||||
}
|
||||
220
core/src/mindustry/ui/dialogs/CustomRulesDialog.java
Normal file
220
core/src/mindustry/ui/dialogs/CustomRulesDialog.java
Normal file
@@ -0,0 +1,220 @@
|
||||
package mindustry.ui.dialogs;
|
||||
|
||||
import arc.*;
|
||||
import arc.struct.*;
|
||||
import arc.func.*;
|
||||
import arc.graphics.*;
|
||||
import arc.scene.style.*;
|
||||
import arc.scene.ui.*;
|
||||
import arc.scene.ui.layout.*;
|
||||
import arc.util.*;
|
||||
import mindustry.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.game.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.ui.*;
|
||||
import mindustry.ui.Cicon;
|
||||
import mindustry.world.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class CustomRulesDialog extends FloatingDialog{
|
||||
private Table main;
|
||||
private Rules rules;
|
||||
private Prov<Rules> resetter;
|
||||
private LoadoutDialog loadoutDialog;
|
||||
private FloatingDialog banDialog;
|
||||
|
||||
public CustomRulesDialog(){
|
||||
super("$mode.custom");
|
||||
|
||||
loadoutDialog = new LoadoutDialog();
|
||||
banDialog = new FloatingDialog("$bannedblocks");
|
||||
banDialog.addCloseButton();
|
||||
|
||||
banDialog.shown(this::rebuildBanned);
|
||||
banDialog.buttons.addImageTextButton("$addall", Icon.arrow16Small, () -> {
|
||||
rules.bannedBlocks.addAll(content.blocks().select(Block::isBuildable));
|
||||
rebuildBanned();
|
||||
}).size(180, 64f);
|
||||
|
||||
banDialog.buttons.addImageTextButton("$clear", Icon.trash16Small, () -> {
|
||||
rules.bannedBlocks.clear();
|
||||
rebuildBanned();
|
||||
}).size(180, 64f);
|
||||
|
||||
setFillParent(true);
|
||||
shown(this::setup);
|
||||
addCloseButton();
|
||||
}
|
||||
|
||||
private void rebuildBanned(){
|
||||
float previousScroll = banDialog.cont.getChildren().isEmpty() ? 0f : ((ScrollPane)banDialog.cont.getChildren().first()).getScrollY();
|
||||
banDialog.cont.clear();
|
||||
banDialog.cont.pane(t -> {
|
||||
t.margin(10f);
|
||||
|
||||
if(rules.bannedBlocks.isEmpty()){
|
||||
t.add("$empty");
|
||||
}
|
||||
|
||||
Array<Block> array = Array.with(rules.bannedBlocks);
|
||||
array.sort();
|
||||
|
||||
int cols = mobile && Core.graphics.isPortrait() ? 1 : mobile ? 2 : 3;
|
||||
int i = 0;
|
||||
|
||||
for(Block block : array){
|
||||
t.table(Tex.underline, b -> {
|
||||
b.left().margin(4f);
|
||||
b.addImage(block.icon(Cicon.medium)).size(Cicon.medium.size).padRight(3);
|
||||
b.add(block.localizedName).color(Color.lightGray).padLeft(3).growX().left().wrap();
|
||||
|
||||
b.addImageButton(Icon.cancelSmall, Styles.clearPartiali, () -> {
|
||||
rules.bannedBlocks.remove(block);
|
||||
rebuildBanned();
|
||||
}).size(70f).pad(-4f).padLeft(0f);
|
||||
}).size(300f, 70f).padRight(5);
|
||||
|
||||
if(++i % cols == 0){
|
||||
t.row();
|
||||
}
|
||||
}
|
||||
}).get().setScrollYForce(previousScroll);
|
||||
banDialog.cont.row();
|
||||
banDialog.cont.addImageTextButton("$add", Icon.addSmall, () -> {
|
||||
FloatingDialog dialog = new FloatingDialog("$add");
|
||||
dialog.cont.pane(t -> {
|
||||
t.left().margin(14f);
|
||||
int[] i = {0};
|
||||
content.blocks().each(b -> !rules.bannedBlocks.contains(b) && b.isBuildable(), b -> {
|
||||
int cols = mobile && Core.graphics.isPortrait() ? 4 : 12;
|
||||
t.addImageButton(new TextureRegionDrawable(b.icon(Cicon.medium)), Styles.cleari, () -> {
|
||||
rules.bannedBlocks.add(b);
|
||||
rebuildBanned();
|
||||
dialog.hide();
|
||||
}).size(60f).get().resizeImage(Cicon.medium.size);
|
||||
|
||||
if(++i[0] % cols == 0){
|
||||
t.row();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
dialog.addCloseButton();
|
||||
dialog.show();
|
||||
}).size(300f, 64f);
|
||||
}
|
||||
|
||||
public void show(Rules rules, Prov<Rules> resetter){
|
||||
this.rules = rules;
|
||||
this.resetter = resetter;
|
||||
show();
|
||||
}
|
||||
|
||||
void setup(){
|
||||
cont.clear();
|
||||
cont.pane(m -> main = m).get().setScrollingDisabled(true, false);
|
||||
main.margin(10f);
|
||||
main.addButton("$settings.reset", () -> {
|
||||
rules = resetter.get();
|
||||
setup();
|
||||
requestKeyboard();
|
||||
requestScroll();
|
||||
}).size(300f, 50f);
|
||||
main.left().defaults().fillX().left().pad(5);
|
||||
main.row();
|
||||
|
||||
title("$rules.title.waves");
|
||||
check("$rules.waves", b -> rules.waves = b, () -> rules.waves);
|
||||
check("$rules.wavetimer", b -> rules.waveTimer = b, () -> rules.waveTimer);
|
||||
check("$rules.waitForWaveToEnd", b -> rules.waitForWaveToEnd = b, () -> rules.waitForWaveToEnd);
|
||||
number("$rules.wavespacing", false, f -> rules.waveSpacing = f * 60f, () -> rules.waveSpacing / 60f, () -> true);
|
||||
number("$rules.dropzoneradius", false, f -> rules.dropZoneRadius = f * tilesize, () -> rules.dropZoneRadius / tilesize, () -> true);
|
||||
|
||||
title("$rules.title.respawns");
|
||||
number("$rules.respawntime", f -> rules.respawnTime = f * 60f, () -> rules.respawnTime / 60f);
|
||||
|
||||
title("$rules.title.resourcesbuilding");
|
||||
check("$rules.infiniteresources", b -> rules.infiniteResources = b, () -> rules.infiniteResources);
|
||||
check("$rules.reactorexplosions", b -> rules.reactorExplosions = b, () -> rules.reactorExplosions);
|
||||
number("$rules.buildcostmultiplier", false, f -> rules.buildCostMultiplier = f, () -> rules.buildCostMultiplier, () -> !rules.infiniteResources);
|
||||
number("$rules.buildspeedmultiplier", f -> rules.buildSpeedMultiplier = f, () -> rules.buildSpeedMultiplier);
|
||||
|
||||
main.addButton("$configure",
|
||||
() -> loadoutDialog.show(Blocks.coreShard.itemCapacity, rules.loadout,
|
||||
() -> {
|
||||
rules.loadout.clear();
|
||||
rules.loadout.add(new ItemStack(Items.copper, 100));
|
||||
}, () -> {}, () -> {}
|
||||
)).left().width(300f);
|
||||
main.row();
|
||||
|
||||
main.addButton("$bannedblocks", banDialog::show).left().width(300f);
|
||||
main.row();
|
||||
|
||||
title("$rules.title.player");
|
||||
number("$rules.playerhealthmultiplier", f -> rules.playerHealthMultiplier = f, () -> rules.playerHealthMultiplier);
|
||||
number("$rules.playerdamagemultiplier", f -> rules.playerDamageMultiplier = f, () -> rules.playerDamageMultiplier);
|
||||
|
||||
title("$rules.title.unit");
|
||||
check("$rules.unitdrops", b -> rules.unitDrops = b, () -> rules.unitDrops, () -> true);
|
||||
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);
|
||||
|
||||
title("$rules.title.enemy");
|
||||
check("$rules.attack", b -> rules.attackMode = b, () -> rules.attackMode);
|
||||
check("$rules.enemyCheat", b -> rules.enemyCheat = b, () -> rules.enemyCheat);
|
||||
number("$rules.enemycorebuildradius", f -> rules.enemyCoreBuildRadius = f * tilesize, () -> Math.min(rules.enemyCoreBuildRadius / tilesize, 200));
|
||||
|
||||
title("$rules.title.experimental");
|
||||
check("$rules.lighting", b -> rules.lighting = b, () -> rules.lighting);
|
||||
|
||||
main.addButton(b -> {
|
||||
b.left();
|
||||
b.table(Tex.pane, in -> {
|
||||
in.stack(new Image(Tex.alphaBg), new Image(Tex.whiteui){{
|
||||
update(() -> setColor(rules.ambientLight));
|
||||
}}).grow();
|
||||
}).margin(4).size(50f).padRight(10);
|
||||
b.add("$rules.ambientlight");
|
||||
}, () -> ui.picker.show(rules.ambientLight, rules.ambientLight::set)).left().width(250f);
|
||||
main.row();
|
||||
}
|
||||
|
||||
void number(String text, Floatc cons, Floatp prov){
|
||||
number(text, false, cons, prov, () -> true);
|
||||
}
|
||||
|
||||
void number(String text, boolean integer, Floatc cons, Floatp prov, Boolp condition){
|
||||
main.table(t -> {
|
||||
t.left();
|
||||
t.add(text).left().padRight(5)
|
||||
.update(a -> a.setColor(condition.get() ? Color.white : Color.gray));
|
||||
Vars.platform.addDialog(t.addField((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().get());
|
||||
}).padTop(0);
|
||||
main.row();
|
||||
}
|
||||
|
||||
void check(String text, Boolc cons, Boolp prov){
|
||||
check(text, cons, prov, () -> true);
|
||||
}
|
||||
|
||||
void check(String text, Boolc cons, Boolp prov, Boolp condition){
|
||||
main.addCheck(text, cons).checked(prov.get()).update(a -> a.setDisabled(!condition.get())).padRight(100f).get().left();
|
||||
main.row();
|
||||
}
|
||||
|
||||
void title(String text){
|
||||
main.add(text).color(Pal.accent).padTop(20).padRight(100f).padBottom(-3);
|
||||
main.row();
|
||||
main.addImage().color(Pal.accent).height(3f).padRight(100f).padBottom(20);
|
||||
main.row();
|
||||
}
|
||||
}
|
||||
86
core/src/mindustry/ui/dialogs/DatabaseDialog.java
Normal file
86
core/src/mindustry/ui/dialogs/DatabaseDialog.java
Normal file
@@ -0,0 +1,86 @@
|
||||
package mindustry.ui.dialogs;
|
||||
|
||||
import arc.*;
|
||||
import arc.struct.*;
|
||||
import arc.graphics.*;
|
||||
import arc.scene.event.*;
|
||||
import arc.scene.ui.*;
|
||||
import arc.scene.ui.layout.*;
|
||||
import arc.util.*;
|
||||
import mindustry.*;
|
||||
import mindustry.core.GameState.*;
|
||||
import mindustry.ctype.*;
|
||||
import mindustry.ctype.ContentType;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.ui.*;
|
||||
|
||||
public class DatabaseDialog extends FloatingDialog{
|
||||
|
||||
public DatabaseDialog(){
|
||||
super("$database");
|
||||
|
||||
shouldPause = true;
|
||||
addCloseButton();
|
||||
shown(this::rebuild);
|
||||
onResize(this::rebuild);
|
||||
}
|
||||
|
||||
void rebuild(){
|
||||
cont.clear();
|
||||
|
||||
Table table = new Table();
|
||||
table.margin(20);
|
||||
ScrollPane pane = new ScrollPane(table);
|
||||
|
||||
Array<Content>[] allContent = Vars.content.getContentMap();
|
||||
|
||||
for(int j = 0; j < allContent.length; j++){
|
||||
ContentType type = ContentType.values()[j];
|
||||
|
||||
Array<Content> array = allContent[j].select(c -> c instanceof UnlockableContent && !((UnlockableContent)c).isHidden());
|
||||
if(array.size == 0) continue;
|
||||
|
||||
table.add("$content." + type.name() + ".name").growX().left().color(Pal.accent);
|
||||
table.row();
|
||||
table.addImage().growX().pad(5).padLeft(0).padRight(0).height(3).color(Pal.accent);
|
||||
table.row();
|
||||
table.table(list -> {
|
||||
list.left();
|
||||
|
||||
int maxWidth = Core.graphics.isPortrait() ? 7 : 13;
|
||||
|
||||
int count = 0;
|
||||
|
||||
for(int i = 0; i < array.size; i++){
|
||||
UnlockableContent unlock = (UnlockableContent)array.get(i);
|
||||
|
||||
Image image = unlocked(unlock) ? new Image(unlock.icon(Cicon.medium)) : new Image(Icon.lockedSmall, Pal.gray);
|
||||
list.add(image).size(8*4).pad(3);
|
||||
ClickListener listener = new ClickListener();
|
||||
image.addListener(listener);
|
||||
if(!Vars.mobile && unlocked(unlock)){
|
||||
image.addListener(new HandCursorListener());
|
||||
image.update(() -> image.getColor().lerp(!listener.isOver() ? Color.lightGray : Color.white, 0.4f * Time.delta()));
|
||||
}
|
||||
|
||||
if(unlocked(unlock)){
|
||||
image.clicked(() -> Vars.ui.content.show(unlock));
|
||||
image.addListener(new Tooltip(t -> t.background(Tex.button).add(unlock.localizedName)));
|
||||
}
|
||||
|
||||
if((++count) % maxWidth == 0){
|
||||
list.row();
|
||||
}
|
||||
}
|
||||
}).growX().left().padBottom(10);
|
||||
table.row();
|
||||
}
|
||||
|
||||
cont.add(pane);
|
||||
}
|
||||
|
||||
boolean unlocked(UnlockableContent content){
|
||||
return (!Vars.world.isZone() && !Vars.state.is(State.menu)) || content.unlocked();
|
||||
}
|
||||
}
|
||||
319
core/src/mindustry/ui/dialogs/DeployDialog.java
Normal file
319
core/src/mindustry/ui/dialogs/DeployDialog.java
Normal file
@@ -0,0 +1,319 @@
|
||||
package mindustry.ui.dialogs;
|
||||
|
||||
import arc.*;
|
||||
import arc.struct.*;
|
||||
import arc.func.*;
|
||||
import arc.graphics.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.input.*;
|
||||
import arc.math.*;
|
||||
import arc.math.geom.*;
|
||||
import arc.scene.*;
|
||||
import arc.scene.event.*;
|
||||
import arc.scene.style.*;
|
||||
import arc.scene.ui.*;
|
||||
import arc.scene.ui.layout.*;
|
||||
import arc.scene.utils.*;
|
||||
import arc.util.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.core.GameState.*;
|
||||
import mindustry.game.EventType.*;
|
||||
import mindustry.game.Saves.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.io.SaveIO.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.ui.*;
|
||||
import mindustry.ui.layout.*;
|
||||
import mindustry.ui.layout.TreeLayout.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class DeployDialog extends FloatingDialog{
|
||||
private final float nodeSize = Scl.scl(230f);
|
||||
private ObjectSet<ZoneNode> nodes = new ObjectSet<>();
|
||||
private ZoneInfoDialog info = new ZoneInfoDialog();
|
||||
private Rectangle bounds = new Rectangle();
|
||||
private View view = new View();
|
||||
|
||||
public DeployDialog(){
|
||||
super("", Styles.fullDialog);
|
||||
|
||||
treeLayout();
|
||||
Events.on(ContentReloadEvent.class, e -> treeLayout());
|
||||
|
||||
addCloseButton();
|
||||
buttons.addImageTextButton("$techtree", Icon.tree, () -> ui.tech.show()).size(230f, 64f);
|
||||
|
||||
shown(this::setup);
|
||||
|
||||
//view input.
|
||||
|
||||
addListener(new InputListener(){
|
||||
@Override
|
||||
public boolean scrolled(InputEvent event, float x, float y, float amountX, float amountY){
|
||||
view.setScale(Mathf.clamp(view.getScaleX() - amountY / 40f, 0.25f, 1f));
|
||||
view.setOrigin(Align.center);
|
||||
view.setTransform(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mouseMoved(InputEvent event, float x, float y){
|
||||
view.requestScroll();
|
||||
return super.mouseMoved(event, x, y);
|
||||
}
|
||||
});
|
||||
|
||||
addListener(new ElementGestureListener(){
|
||||
@Override
|
||||
public void zoom(InputEvent event, float initialDistance, float distance){
|
||||
if(view.lastZoom < 0){
|
||||
view.lastZoom = view.getScaleX();
|
||||
}
|
||||
|
||||
view.setScale(Mathf.clamp(distance / initialDistance * view.lastZoom, 0.25f, 1f));
|
||||
view.setOrigin(Align.center);
|
||||
view.setTransform(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void touchUp(InputEvent event, float x, float y, int pointer, KeyCode button){
|
||||
view.lastZoom = view.getScaleX();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pan(InputEvent event, float x, float y, float deltaX, float deltaY){
|
||||
view.panX += deltaX / view.getScaleX();
|
||||
view.panY += deltaY / view.getScaleY();
|
||||
view.moved = true;
|
||||
view.clamp();
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
void treeLayout(){
|
||||
nodes.clear();
|
||||
ZoneNode root = new ZoneNode(Zones.groundZero, null);
|
||||
|
||||
BranchTreeLayout layout = new BranchTreeLayout();
|
||||
layout.gapBetweenLevels = layout.gapBetweenNodes = Scl.scl(60f);
|
||||
layout.gapBetweenNodes = Scl.scl(120f);
|
||||
layout.layout(root);
|
||||
bounds.set(layout.getBounds());
|
||||
bounds.y += nodeSize*0.4f;
|
||||
}
|
||||
|
||||
public void setup(){
|
||||
platform.updateRPC();
|
||||
|
||||
cont.clear();
|
||||
titleTable.remove();
|
||||
margin(0f).marginBottom(8);
|
||||
|
||||
Stack stack = new Stack();
|
||||
|
||||
stack.add(new Image(new Texture("sprites/backgrounds/stars.png"){{
|
||||
setFilter(TextureFilter.Linear);
|
||||
}}).setScaling(Scaling.fill));
|
||||
|
||||
stack.add(new Image(new Texture("sprites/backgrounds/planet-zero.png"){{
|
||||
setFilter(TextureFilter.Linear);
|
||||
}}){{
|
||||
float[] time = {0};
|
||||
setColor(Color.fromGray(0.3f));
|
||||
setScale(1.5f);
|
||||
update(() -> {
|
||||
setOrigin(Align.center);
|
||||
time[0] += Core.graphics.getDeltaTime() * 10f;
|
||||
setTranslation(Mathf.sin(time[0], 60f, 70f) + view.panX / 30f, Mathf.cos(time[0], 140f, 80f) + (view.panY + 200) / 30f);
|
||||
});
|
||||
}}.setScaling(Scaling.fit));
|
||||
|
||||
if(control.saves.getZoneSlot() != null){
|
||||
float size = 250f;
|
||||
|
||||
stack.add(new Table(t -> {
|
||||
SaveSlot slot = control.saves.getZoneSlot();
|
||||
|
||||
Stack sub = new Stack();
|
||||
|
||||
if(slot.getZone() != null){
|
||||
sub.add(new Table(f -> f.margin(4f).add(new Image()).color(Color.fromGray(0.1f)).grow()));
|
||||
|
||||
sub.add(new Table(f -> f.margin(4f).add(new Image(slot.getZone().preview).setScaling(Scaling.fit)).update(img -> {
|
||||
TextureRegionDrawable draw = (TextureRegionDrawable)img.getDrawable();
|
||||
if(draw.getRegion().getTexture().isDisposed()){
|
||||
draw.setRegion(slot.getZone().preview);
|
||||
}
|
||||
|
||||
Texture text = slot.previewTexture();
|
||||
if(draw.getRegion() == slot.getZone().preview && text != null){
|
||||
draw.setRegion(new TextureRegion(text));
|
||||
}
|
||||
}).color(Color.darkGray).grow()));
|
||||
}
|
||||
|
||||
TextButton button = Elements.newButton(Core.bundle.format("resume", slot.getZone().localizedName), Styles.squaret, () -> {
|
||||
control.saves.getZoneSlot().cautiousLoad(() -> {
|
||||
hide();
|
||||
ui.loadAnd(() -> {
|
||||
logic.reset();
|
||||
net.reset();
|
||||
try{
|
||||
slot.load();
|
||||
state.set(State.playing);
|
||||
}catch(SaveException e){ //make sure to handle any save load errors!
|
||||
e.printStackTrace();
|
||||
if(control.saves.getZoneSlot() != null) control.saves.getZoneSlot().delete();
|
||||
Core.app.post(() -> ui.showInfo("$save.corrupted"));
|
||||
show();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
sub.add(button);
|
||||
|
||||
t.add(sub).size(size);
|
||||
|
||||
String color = "[lightgray]";
|
||||
|
||||
button.defaults().colspan(2);
|
||||
button.row();
|
||||
button.add(Core.bundle.format("save", color + slot.getWave()));
|
||||
button.row();
|
||||
button.label(() -> Core.bundle.format("save.playtime", color + slot.getPlayTime()));
|
||||
button.row();
|
||||
|
||||
t.row();
|
||||
|
||||
t.addButton("$abandon", () -> {
|
||||
ui.showConfirm("$warning", "$abandon.text", () -> {
|
||||
slot.delete();
|
||||
setup();
|
||||
});
|
||||
}).width(size).height(50f).padTop(3);
|
||||
}));
|
||||
}else{
|
||||
stack.add(view = new View());
|
||||
}
|
||||
|
||||
stack.add(new ItemsDisplay());
|
||||
|
||||
cont.add(stack).grow();
|
||||
|
||||
//set up direct and indirect children
|
||||
for(ZoneNode node : nodes){
|
||||
node.allChildren.clear();
|
||||
node.allChildren.addAll(node.children);
|
||||
for(ZoneNode other : nodes){
|
||||
if(other.zone.requirements.contains(req -> req.zone() == node.zone)){
|
||||
node.allChildren.add(other);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
view.setOrigin(Align.center);
|
||||
view.setTransform(true);
|
||||
}
|
||||
|
||||
boolean hidden(Zone zone){
|
||||
return zone.requirements.contains(o -> o.zone() != null && o.zone().locked());
|
||||
}
|
||||
|
||||
void buildButton(Zone zone, Button button){
|
||||
button.setDisabled(() -> hidden(zone));
|
||||
button.clicked(() -> {
|
||||
if(!view.moved){
|
||||
info.show(zone);
|
||||
}
|
||||
});
|
||||
|
||||
if(zone.unlocked() && !hidden(zone)){
|
||||
button.labelWrap(zone.localizedName).style(Styles.outlineLabel).width(140).growX().get().setAlignment(Align.center);
|
||||
}else{
|
||||
Cons<Element> flasher = zone.canUnlock() && !hidden(zone) ? e -> e.update(() -> e.getColor().set(Color.white).lerp(Pal.accent, Mathf.absin(3f, 1f))) : e -> {};
|
||||
flasher.get(button.addImage(Icon.locked).get());
|
||||
button.row();
|
||||
flasher.get(button.add("$locked").get());
|
||||
}
|
||||
}
|
||||
|
||||
class View extends Group{
|
||||
float panX = 0, panY = -200, lastZoom = -1;
|
||||
boolean moved = false;
|
||||
|
||||
{
|
||||
for(ZoneNode node : nodes){
|
||||
Stack stack = new Stack();
|
||||
Tmp.v1.set(node.width, node.height);
|
||||
if(node.zone.preview != null){
|
||||
Tmp.v1.set(Scaling.fit.apply(node.zone.preview.getWidth(), node.zone.preview.getHeight(), node.width, node.height));
|
||||
}
|
||||
|
||||
stack.setSize(Tmp.v1.x, Tmp.v1.y);
|
||||
stack.add(new Table(t -> t.margin(4f).add(new Image(node.zone.preview).setScaling(Scaling.stretch)).color(node.zone.unlocked() ? Color.darkGray : Color.fromGray(0.2f)).grow()));
|
||||
stack.update(() -> stack.setPosition(node.x + panX + width / 2f, node.y + panY + height / 2f, Align.center));
|
||||
|
||||
Button button = new Button(Styles.squaret);
|
||||
buildButton(node.zone, button);
|
||||
stack.add(button);
|
||||
addChild(stack);
|
||||
}
|
||||
|
||||
released(() -> moved = false);
|
||||
}
|
||||
|
||||
void clamp(){
|
||||
float pad = nodeSize;
|
||||
|
||||
float ox = width/2f, oy = height/2f;
|
||||
float rx = bounds.x + panX + ox, ry = panY + oy + bounds.y;
|
||||
float rw = bounds.width, rh = bounds.height;
|
||||
rx = Mathf.clamp(rx, -rw + pad, Core.graphics.getWidth() - pad);
|
||||
ry = Mathf.clamp(ry, pad, Core.graphics.getHeight() - rh - pad);
|
||||
panX = rx - bounds.x - ox;
|
||||
panY = ry - bounds.y - oy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawChildren(){
|
||||
clamp();
|
||||
float offsetX = panX + width / 2f, offsetY = panY + height / 2f;
|
||||
|
||||
for(ZoneNode node : nodes){
|
||||
for(ZoneNode child : node.allChildren){
|
||||
Lines.stroke(Scl.scl(4f), node.zone.locked() || child.zone.locked() ? Pal.gray : Pal.gray);
|
||||
Draw.alpha(parentAlpha);
|
||||
Lines.line(node.x + offsetX, node.y + offsetY, child.x + offsetX, child.y + offsetY);
|
||||
}
|
||||
}
|
||||
|
||||
Draw.reset();
|
||||
super.drawChildren();
|
||||
}
|
||||
}
|
||||
|
||||
class ZoneNode extends TreeNode<ZoneNode>{
|
||||
final Array<Zone> arr = new Array<>();
|
||||
final Array<ZoneNode> allChildren = new Array<>();
|
||||
final Zone zone;
|
||||
|
||||
ZoneNode(Zone zone, ZoneNode parent){
|
||||
this.zone = zone;
|
||||
this.parent = parent;
|
||||
this.width = this.height = nodeSize;
|
||||
//this.height /= 2f;
|
||||
nodes.add(this);
|
||||
|
||||
arr.selectFrom(content.zones(), other -> other.requirements.size > 0 && other.requirements.first().zone() == zone);
|
||||
|
||||
children = new ZoneNode[arr.size];
|
||||
for(int i = 0; i < children.length; i++){
|
||||
children[i] = new ZoneNode(arr.get(i), this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
52
core/src/mindustry/ui/dialogs/DiscordDialog.java
Normal file
52
core/src/mindustry/ui/dialogs/DiscordDialog.java
Normal file
@@ -0,0 +1,52 @@
|
||||
package mindustry.ui.dialogs;
|
||||
|
||||
import arc.Core;
|
||||
import arc.graphics.Color;
|
||||
import arc.scene.ui.Dialog;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.Pal;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class DiscordDialog extends Dialog{
|
||||
|
||||
public DiscordDialog(){
|
||||
super("");
|
||||
|
||||
float h = 70f;
|
||||
|
||||
cont.margin(12f);
|
||||
|
||||
Color color = Color.valueOf("7289da");
|
||||
|
||||
cont.table(t -> {
|
||||
t.background(Tex.button).margin(0);
|
||||
|
||||
t.table(img -> {
|
||||
img.addImage().height(h - 5).width(40f).color(color);
|
||||
img.row();
|
||||
img.addImage().height(5).width(40f).color(color.cpy().mul(0.8f, 0.8f, 0.8f, 1f));
|
||||
}).expandY();
|
||||
|
||||
t.table(i -> {
|
||||
i.background(Tex.button);
|
||||
i.addImage(Icon.discord);
|
||||
}).size(h).left();
|
||||
|
||||
t.add("$discord").color(Pal.accent).growX().padLeft(10f);
|
||||
}).size(440f, h).pad(10f);
|
||||
|
||||
buttons.defaults().size(150f, 50);
|
||||
|
||||
buttons.addButton("$back", this::hide);
|
||||
buttons.addButton("$copylink", () -> {
|
||||
Core.app.setClipboardText(discordURL);
|
||||
});
|
||||
buttons.addButton("$openlink", () -> {
|
||||
if(!Core.net.openURI(discordURL)){
|
||||
ui.showErrorMessage("$linkfail");
|
||||
Core.app.setClipboardText(discordURL);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
309
core/src/mindustry/ui/dialogs/FileChooser.java
Normal file
309
core/src/mindustry/ui/dialogs/FileChooser.java
Normal file
@@ -0,0 +1,309 @@
|
||||
package mindustry.ui.dialogs;
|
||||
|
||||
import arc.*;
|
||||
import arc.struct.*;
|
||||
import arc.files.*;
|
||||
import arc.func.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.scene.event.*;
|
||||
import arc.scene.ui.*;
|
||||
import arc.scene.ui.layout.*;
|
||||
import arc.util.*;
|
||||
import arc.util.pooling.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.ui.*;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import static mindustry.Vars.platform;
|
||||
|
||||
public class FileChooser extends FloatingDialog{
|
||||
private static final Fi homeDirectory = Core.files.absolute(Core.files.getExternalStoragePath());
|
||||
private static Fi lastDirectory = homeDirectory;
|
||||
|
||||
private Table files;
|
||||
private Fi directory = lastDirectory;
|
||||
private ScrollPane pane;
|
||||
private TextField navigation, filefield;
|
||||
private TextButton ok;
|
||||
private FileHistory stack = new FileHistory();
|
||||
private Boolf<Fi> filter;
|
||||
private Cons<Fi> selectListener;
|
||||
private boolean open;
|
||||
|
||||
public FileChooser(String title, Boolf<Fi> filter, boolean open, Cons<Fi> result){
|
||||
super(title);
|
||||
setFillParent(true);
|
||||
this.open = open;
|
||||
this.filter = filter;
|
||||
this.selectListener = result;
|
||||
|
||||
onResize(() -> {
|
||||
cont.clear();
|
||||
setupWidgets();
|
||||
});
|
||||
|
||||
shown(() -> {
|
||||
cont.clear();
|
||||
setupWidgets();
|
||||
});
|
||||
}
|
||||
|
||||
private void setupWidgets(){
|
||||
cont.margin(-10);
|
||||
|
||||
Table content = new Table();
|
||||
|
||||
filefield = new TextField();
|
||||
filefield.setOnlyFontChars(false);
|
||||
if(!open) platform.addDialog(filefield);
|
||||
filefield.setDisabled(open);
|
||||
|
||||
ok = new TextButton(open ? "$load" : "$save");
|
||||
|
||||
ok.clicked(() -> {
|
||||
if(ok.isDisabled()) return;
|
||||
if(selectListener != null)
|
||||
selectListener.get(directory.child(filefield.getText()));
|
||||
hide();
|
||||
});
|
||||
|
||||
filefield.changed(() -> {
|
||||
ok.setDisabled(filefield.getText().replace(" ", "").isEmpty());
|
||||
});
|
||||
|
||||
filefield.change();
|
||||
|
||||
TextButton cancel = new TextButton("$cancel");
|
||||
cancel.clicked(this::hide);
|
||||
|
||||
navigation = new TextField("");
|
||||
navigation.touchable(Touchable.disabled);
|
||||
|
||||
files = new Table();
|
||||
files.marginRight(10);
|
||||
files.marginLeft(3);
|
||||
|
||||
pane = new ScrollPane(files);
|
||||
pane.setOverscroll(false, false);
|
||||
pane.setFadeScrollBars(false);
|
||||
|
||||
updateFiles(true);
|
||||
|
||||
Table icontable = new Table();
|
||||
|
||||
ImageButton up = new ImageButton(Icon.folderParent);
|
||||
up.clicked(() -> {
|
||||
directory = directory.parent();
|
||||
updateFiles(true);
|
||||
});
|
||||
|
||||
|
||||
ImageButton back = new ImageButton(Icon.arrowLeft);
|
||||
ImageButton forward = new ImageButton(Icon.arrowRight);
|
||||
|
||||
forward.clicked(() -> stack.forward());
|
||||
back.clicked(() -> stack.back());
|
||||
forward.setDisabled(() -> !stack.canForward());
|
||||
back.setDisabled(() -> !stack.canBack());
|
||||
|
||||
ImageButton home = new ImageButton(Icon.home);
|
||||
home.clicked(() -> {
|
||||
directory = homeDirectory;
|
||||
lastDirectory = directory;
|
||||
updateFiles(true);
|
||||
});
|
||||
|
||||
icontable.defaults().height(60).growX().padTop(5).uniform();
|
||||
icontable.add(home);
|
||||
icontable.add(back);
|
||||
icontable.add(forward);
|
||||
icontable.add(up);
|
||||
|
||||
Table fieldcontent = new Table();
|
||||
fieldcontent.bottom().left().add(new Label("$filename"));
|
||||
fieldcontent.add(filefield).height(40f).fillX().expandX().padLeft(10f);
|
||||
|
||||
Table buttons = new Table();
|
||||
buttons.defaults().growX().height(60);
|
||||
buttons.add(cancel);
|
||||
buttons.add(ok);
|
||||
|
||||
content.top().left();
|
||||
content.add(icontable).expandX().fillX();
|
||||
content.row();
|
||||
|
||||
content.center().add(pane).colspan(3).grow();
|
||||
content.row();
|
||||
|
||||
if(!open){
|
||||
content.bottom().left().add(fieldcontent).colspan(3).grow().padTop(-2).padBottom(2);
|
||||
content.row();
|
||||
}
|
||||
|
||||
content.add(buttons).growX();
|
||||
|
||||
cont.add(content).grow();
|
||||
}
|
||||
|
||||
private void updateFileFieldStatus(){
|
||||
if(!open){
|
||||
ok.setDisabled(filefield.getText().replace(" ", "").isEmpty());
|
||||
}else{
|
||||
ok.setDisabled(!directory.child(filefield.getText()).exists() || directory.child(filefield.getText()).isDirectory());
|
||||
}
|
||||
}
|
||||
|
||||
private Fi[] getFileNames(){
|
||||
Fi[] handles = directory.list(file -> !file.getName().startsWith("."));
|
||||
|
||||
Arrays.sort(handles, (a, b) -> {
|
||||
if(a.isDirectory() && !b.isDirectory()) return -1;
|
||||
if(!a.isDirectory() && b.isDirectory()) return 1;
|
||||
return String.CASE_INSENSITIVE_ORDER.compare(a.name(), b.name());
|
||||
});
|
||||
return handles;
|
||||
}
|
||||
|
||||
private void updateFiles(boolean push){
|
||||
if(push) stack.push(directory);
|
||||
navigation.setText(directory.toString());
|
||||
|
||||
GlyphLayout layout = Pools.obtain(GlyphLayout.class, GlyphLayout::new);
|
||||
|
||||
layout.setText(Fonts.def, navigation.getText());
|
||||
|
||||
if(layout.width < navigation.getWidth()){
|
||||
navigation.setCursorPosition(0);
|
||||
}else{
|
||||
navigation.setCursorPosition(navigation.getText().length());
|
||||
}
|
||||
|
||||
Pools.free(layout);
|
||||
|
||||
files.clearChildren();
|
||||
files.top().left();
|
||||
Fi[] names = getFileNames();
|
||||
|
||||
Image upimage = new Image(Icon.folderParentSmall);
|
||||
TextButton upbutton = new TextButton(".." + directory.toString(), Styles.clearTogglet);
|
||||
upbutton.clicked(() -> {
|
||||
directory = directory.parent();
|
||||
lastDirectory = directory;
|
||||
updateFiles(true);
|
||||
});
|
||||
|
||||
upbutton.left().add(upimage).padRight(4f).padLeft(4);
|
||||
upbutton.getLabel().setAlignment(Align.left);
|
||||
upbutton.getCells().reverse();
|
||||
|
||||
files.add(upbutton).align(Align.topLeft).fillX().expandX().height(50).pad(2).colspan(2);
|
||||
files.row();
|
||||
|
||||
|
||||
ButtonGroup<TextButton> group = new ButtonGroup<>();
|
||||
group.setMinCheckCount(0);
|
||||
|
||||
for(Fi file : names){
|
||||
if(!file.isDirectory() && !filter.get(file)) continue; //skip non-filtered files
|
||||
|
||||
String filename = file.name();
|
||||
|
||||
TextButton button = new TextButton(filename, Styles.clearTogglet);
|
||||
button.getLabel().setWrap(false);
|
||||
button.getLabel().setEllipsis(true);
|
||||
group.add(button);
|
||||
|
||||
button.clicked(() -> {
|
||||
if(!file.isDirectory()){
|
||||
filefield.setText(filename);
|
||||
updateFileFieldStatus();
|
||||
}else{
|
||||
directory = directory.child(filename);
|
||||
lastDirectory = directory;
|
||||
updateFiles(true);
|
||||
}
|
||||
});
|
||||
|
||||
filefield.changed(() -> {
|
||||
button.setChecked(filename.equals(filefield.getText()));
|
||||
});
|
||||
|
||||
Image image = new Image(file.isDirectory() ? Icon.folderSmall : Icon.fileTextSmall);
|
||||
|
||||
button.add(image).padRight(4f).padLeft(4);
|
||||
button.getCells().reverse();
|
||||
files.top().left().add(button).align(Align.topLeft).fillX().expandX()
|
||||
.height(50).pad(2).padTop(0).padBottom(0).colspan(2);
|
||||
button.getLabel().setAlignment(Align.left);
|
||||
files.row();
|
||||
}
|
||||
|
||||
pane.setScrollY(0f);
|
||||
updateFileFieldStatus();
|
||||
|
||||
if(open) filefield.clearText();
|
||||
}
|
||||
|
||||
private String shorten(String string){
|
||||
int max = 30;
|
||||
if(string.length() <= max){
|
||||
return string;
|
||||
}else{
|
||||
return string.substring(0, max - 3).concat("...");
|
||||
}
|
||||
}
|
||||
|
||||
public class FileHistory{
|
||||
private Array<Fi> history = new Array<>();
|
||||
private int index;
|
||||
|
||||
public FileHistory(){
|
||||
|
||||
}
|
||||
|
||||
public void push(Fi file){
|
||||
if(index != history.size) history.truncate(index);
|
||||
history.add(file);
|
||||
index++;
|
||||
}
|
||||
|
||||
public void back(){
|
||||
if(!canBack()) return;
|
||||
index--;
|
||||
directory = history.get(index - 1);
|
||||
lastDirectory = directory;
|
||||
updateFiles(false);
|
||||
}
|
||||
|
||||
public void forward(){
|
||||
if(!canForward()) return;
|
||||
directory = history.get(index);
|
||||
lastDirectory = directory;
|
||||
index++;
|
||||
updateFiles(false);
|
||||
}
|
||||
|
||||
public boolean canForward(){
|
||||
return !(index >= history.size);
|
||||
}
|
||||
|
||||
public boolean canBack(){
|
||||
return !(index == 1) && index > 0;
|
||||
}
|
||||
|
||||
void print(){
|
||||
|
||||
System.out.println("\n\n\n\n\n\n");
|
||||
int i = 0;
|
||||
for(Fi file : history){
|
||||
i++;
|
||||
if(index == i){
|
||||
System.out.println("[[" + file.toString() + "]]");
|
||||
}else{
|
||||
System.out.println("--" + file.toString() + "--");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
67
core/src/mindustry/ui/dialogs/FloatingDialog.java
Normal file
67
core/src/mindustry/ui/dialogs/FloatingDialog.java
Normal file
@@ -0,0 +1,67 @@
|
||||
package mindustry.ui.dialogs;
|
||||
|
||||
import arc.*;
|
||||
import arc.input.*;
|
||||
import arc.scene.ui.*;
|
||||
import arc.util.*;
|
||||
import mindustry.core.GameState.*;
|
||||
import mindustry.game.EventType.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class FloatingDialog extends Dialog{
|
||||
private boolean wasPaused;
|
||||
protected boolean shouldPause;
|
||||
|
||||
public FloatingDialog(String title, DialogStyle style){
|
||||
super(title, style);
|
||||
setFillParent(true);
|
||||
this.title.setAlignment(Align.center);
|
||||
titleTable.row();
|
||||
titleTable.addImage(Tex.whiteui, Pal.accent)
|
||||
.growX().height(3f).pad(4f);
|
||||
|
||||
hidden(() -> {
|
||||
if(shouldPause && !state.is(State.menu)){
|
||||
if(!wasPaused || net.active()){
|
||||
state.set(State.playing);
|
||||
}
|
||||
}
|
||||
Sounds.back.play();
|
||||
});
|
||||
|
||||
shown(() -> {
|
||||
if(shouldPause && !state.is(State.menu)){
|
||||
wasPaused = state.is(State.paused);
|
||||
state.set(State.paused);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public FloatingDialog(String title){
|
||||
this(title, Core.scene.getStyle(DialogStyle.class));
|
||||
}
|
||||
|
||||
protected void onResize(Runnable run){
|
||||
Events.on(ResizeEvent.class, event -> {
|
||||
if(isShown() && Core.scene.getDialog() == this){
|
||||
run.run();
|
||||
updateScrollFocus();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addCloseButton(){
|
||||
buttons.defaults().size(210f, 64f);
|
||||
buttons.addImageTextButton("$back", Icon.arrowLeft, this::hide).size(210f, 64f);
|
||||
|
||||
keyDown(key -> {
|
||||
if(key == KeyCode.ESCAPE || key == KeyCode.BACK){
|
||||
Core.app.post(this::hide);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
102
core/src/mindustry/ui/dialogs/GameOverDialog.java
Normal file
102
core/src/mindustry/ui/dialogs/GameOverDialog.java
Normal file
@@ -0,0 +1,102 @@
|
||||
package mindustry.ui.dialogs;
|
||||
|
||||
import arc.*;
|
||||
import mindustry.core.GameState.*;
|
||||
import mindustry.game.*;
|
||||
import mindustry.game.EventType.*;
|
||||
import mindustry.game.Stats.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.ui.Cicon;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class GameOverDialog extends FloatingDialog{
|
||||
private Team winner;
|
||||
|
||||
public GameOverDialog(){
|
||||
super("$gameover");
|
||||
setFillParent(true);
|
||||
shown(this::rebuild);
|
||||
}
|
||||
|
||||
public void show(Team winner){
|
||||
this.winner = winner;
|
||||
show();
|
||||
if(winner == player.getTeam()){
|
||||
Events.fire(new WinEvent());
|
||||
}else{
|
||||
Events.fire(new LoseEvent());
|
||||
}
|
||||
}
|
||||
|
||||
void rebuild(){
|
||||
title.setText(state.launched ? "$launch.title" : "$gameover");
|
||||
buttons.clear();
|
||||
cont.clear();
|
||||
|
||||
buttons.margin(10);
|
||||
|
||||
if(state.rules.pvp){
|
||||
cont.add(Core.bundle.format("gameover.pvp", winner.localized())).pad(6);
|
||||
buttons.addButton("$menu", () -> {
|
||||
hide();
|
||||
state.set(State.menu);
|
||||
logic.reset();
|
||||
}).size(130f, 60f);
|
||||
}else{
|
||||
if(control.isHighScore()){
|
||||
cont.add("$highscore").pad(6);
|
||||
cont.row();
|
||||
}
|
||||
|
||||
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();
|
||||
if(world.isZone() && !state.stats.itemsDelivered.isEmpty()){
|
||||
t.add("$stat.delivered");
|
||||
t.row();
|
||||
for(Item item : content.items()){
|
||||
if(state.stats.itemsDelivered.get(item, 0) > 0){
|
||||
t.table(items -> {
|
||||
items.add(" [LIGHT_GRAY]" + state.stats.itemsDelivered.get(item, 0));
|
||||
items.addImage(item.icon(Cicon.small)).size(8 * 3).pad(4);
|
||||
}).left();
|
||||
t.row();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(world.isZone()){
|
||||
RankResult result = state.stats.calculateRank(world.getZone(), state.launched);
|
||||
t.add(Core.bundle.format("stat.rank", result.rank + result.modifier));
|
||||
t.row();
|
||||
}
|
||||
}).pad(12);
|
||||
|
||||
if(world.isZone()){
|
||||
buttons.addButton("$continue", () -> {
|
||||
hide();
|
||||
state.set(State.menu);
|
||||
logic.reset();
|
||||
ui.deploy.show();
|
||||
}).size(130f, 60f);
|
||||
}else{
|
||||
buttons.addButton("$menu", () -> {
|
||||
hide();
|
||||
state.set(State.menu);
|
||||
logic.reset();
|
||||
}).size(130f, 60f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
96
core/src/mindustry/ui/dialogs/HostDialog.java
Normal file
96
core/src/mindustry/ui/dialogs/HostDialog.java
Normal file
@@ -0,0 +1,96 @@
|
||||
package mindustry.ui.dialogs;
|
||||
|
||||
import arc.*;
|
||||
import arc.graphics.*;
|
||||
import arc.scene.ui.*;
|
||||
import arc.util.*;
|
||||
import mindustry.*;
|
||||
import mindustry.core.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.ui.*;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class HostDialog extends FloatingDialog{
|
||||
float w = 300;
|
||||
|
||||
public HostDialog(){
|
||||
super("$hostserver");
|
||||
|
||||
addCloseButton();
|
||||
|
||||
cont.table(t -> {
|
||||
t.add("$name").padRight(10);
|
||||
t.addField(Core.settings.getString("name"), text -> {
|
||||
player.name = text;
|
||||
Core.settings.put("name", text);
|
||||
Core.settings.save();
|
||||
ui.listfrag.rebuild();
|
||||
}).grow().pad(8).get().setMaxLength(40);
|
||||
|
||||
ImageButton button = t.addImageButton(Tex.whiteui, Styles.clearFulli, 40, () -> {
|
||||
new PaletteDialog().show(color -> {
|
||||
player.color.set(color);
|
||||
Core.settings.put("color-0", Color.rgba8888(color));
|
||||
Core.settings.save();
|
||||
});
|
||||
}).size(54f).get();
|
||||
button.update(() -> button.getStyle().imageUpColor = player.color);
|
||||
}).width(w).height(70f).pad(4).colspan(3);
|
||||
|
||||
cont.row();
|
||||
|
||||
cont.add().width(65f);
|
||||
|
||||
cont.addButton("$host", () -> {
|
||||
if(Core.settings.getString("name").trim().isEmpty()){
|
||||
ui.showInfo("$noname");
|
||||
return;
|
||||
}
|
||||
|
||||
runHost();
|
||||
}).width(w).height(70f);
|
||||
|
||||
cont.addButton("?", () -> ui.showInfo("$host.info")).size(65f, 70f).padLeft(6f);
|
||||
|
||||
shown(() -> {
|
||||
if(!steam){
|
||||
Core.app.post(() -> Core.settings.getBoolOnce("hostinfo", () -> ui.showInfo("$host.info")));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void runHost(){
|
||||
ui.loadfrag.show("$hosting");
|
||||
Time.runTask(5f, () -> {
|
||||
try{
|
||||
net.host(Vars.port);
|
||||
player.isAdmin = true;
|
||||
|
||||
if(steam){
|
||||
Core.app.post(() -> Core.settings.getBoolOnce("steampublic2", () -> {
|
||||
ui.showCustomConfirm("$setting.publichost.name", "$public.confirm", "$yes", "$no", () -> {
|
||||
Core.settings.putSave("publichost", true);
|
||||
platform.updateLobby();
|
||||
}, () -> {
|
||||
Core.settings.putSave("publichost", false);
|
||||
platform.updateLobby();
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
||||
if(Version.modifier.contains("beta")){
|
||||
Core.settings.putSave("publichost", false);
|
||||
platform.updateLobby();
|
||||
Core.settings.getBoolOnce("betapublic", () -> ui.showInfo("$public.beta"));
|
||||
}
|
||||
}catch(IOException e){
|
||||
ui.showException("$server.error", e);
|
||||
}
|
||||
ui.loadfrag.hide();
|
||||
hide();
|
||||
});
|
||||
}
|
||||
}
|
||||
417
core/src/mindustry/ui/dialogs/JoinDialog.java
Normal file
417
core/src/mindustry/ui/dialogs/JoinDialog.java
Normal file
@@ -0,0 +1,417 @@
|
||||
package mindustry.ui.dialogs;
|
||||
|
||||
import arc.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import arc.struct.*;
|
||||
import arc.graphics.*;
|
||||
import arc.input.*;
|
||||
import arc.math.*;
|
||||
import arc.scene.ui.*;
|
||||
import arc.scene.ui.layout.*;
|
||||
import arc.util.*;
|
||||
import arc.util.serialization.*;
|
||||
import mindustry.*;
|
||||
import mindustry.core.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.net.*;
|
||||
import mindustry.net.Packets.*;
|
||||
import mindustry.ui.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class JoinDialog extends FloatingDialog{
|
||||
Array<Server> servers = new Array<>();
|
||||
Dialog add;
|
||||
Server renaming;
|
||||
Table local = new Table();
|
||||
Table remote = new Table();
|
||||
Table hosts = new Table();
|
||||
int totalHosts;
|
||||
|
||||
public JoinDialog(){
|
||||
super("$joingame");
|
||||
|
||||
loadServers();
|
||||
|
||||
if(!steam) buttons.add().width(60f);
|
||||
buttons.add().growX().width(-1);
|
||||
|
||||
addCloseButton();
|
||||
|
||||
buttons.add().growX().width(-1);
|
||||
if(!steam){
|
||||
buttons.addButton("?", () -> ui.showInfo("$join.info")).size(60f, 64f).width(-1);
|
||||
}
|
||||
|
||||
add = new FloatingDialog("$joingame.title");
|
||||
add.cont.add("$joingame.ip").padRight(5f).left();
|
||||
|
||||
TextField field = add.cont.addField(Core.settings.getString("ip"), text -> {
|
||||
Core.settings.put("ip", text);
|
||||
Core.settings.save();
|
||||
}).size(320f, 54f).get();
|
||||
|
||||
platform.addDialog(field, 100);
|
||||
|
||||
add.cont.row();
|
||||
add.buttons.defaults().size(140f, 60f).pad(4f);
|
||||
add.buttons.addButton("$cancel", add::hide);
|
||||
add.buttons.addButton("$ok", () -> {
|
||||
if(renaming == null){
|
||||
Server server = new Server();
|
||||
server.setIP(Core.settings.getString("ip"));
|
||||
servers.add(server);
|
||||
saveServers();
|
||||
setupRemote();
|
||||
refreshRemote();
|
||||
}else{
|
||||
renaming.setIP(Core.settings.getString("ip"));
|
||||
saveServers();
|
||||
setupRemote();
|
||||
refreshRemote();
|
||||
}
|
||||
add.hide();
|
||||
}).disabled(b -> Core.settings.getString("ip").isEmpty() || net.active());
|
||||
|
||||
add.shown(() -> {
|
||||
add.title.setText(renaming != null ? "$server.edit" : "$server.add");
|
||||
if(renaming != null){
|
||||
field.setText(renaming.displayIP());
|
||||
}
|
||||
});
|
||||
|
||||
keyDown(KeyCode.F5, () -> {
|
||||
refreshLocal();
|
||||
refreshRemote();
|
||||
});
|
||||
|
||||
shown(() -> {
|
||||
setup();
|
||||
refreshLocal();
|
||||
refreshRemote();
|
||||
|
||||
if(!steam){
|
||||
Core.app.post(() -> Core.settings.getBoolOnce("joininfo", () -> ui.showInfo("$join.info")));
|
||||
}
|
||||
});
|
||||
|
||||
onResize(this::setup);
|
||||
}
|
||||
|
||||
void setupRemote(){
|
||||
remote.clear();
|
||||
for(Server server : servers){
|
||||
//why are java lambdas this bad
|
||||
TextButton[] buttons = {null};
|
||||
|
||||
TextButton button = buttons[0] = remote.addButton("[accent]" + server.displayIP(), Styles.cleart, () -> {
|
||||
if(!buttons[0].childrenPressed()){
|
||||
if(server.lastHost != null){
|
||||
safeConnect(server.ip, server.port, server.lastHost.version);
|
||||
}else{
|
||||
connect(server.ip, server.port);
|
||||
}
|
||||
}
|
||||
}).width(targetWidth()).pad(4f).get();
|
||||
|
||||
button.getLabel().setWrap(true);
|
||||
|
||||
Table inner = new Table();
|
||||
button.clearChildren();
|
||||
button.add(inner).growX();
|
||||
|
||||
inner.add(button.getLabel()).growX();
|
||||
|
||||
inner.addImageButton(Icon.arrowUpSmall, Styles.emptyi, () -> {
|
||||
int index = servers.indexOf(server);
|
||||
if(index > 0){
|
||||
servers.remove(index);
|
||||
servers.insert(0, server);
|
||||
|
||||
saveServers();
|
||||
setupRemote();
|
||||
for(Server other : servers){
|
||||
if(other.lastHost != null){
|
||||
setupServer(other, other.lastHost);
|
||||
}else{
|
||||
refreshServer(other);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}).margin(3f).padTop(6f).top().right();
|
||||
|
||||
inner.addImageButton(Icon.loadingSmall, Styles.emptyi, () -> {
|
||||
refreshServer(server);
|
||||
}).margin(3f).padTop(6f).top().right();
|
||||
|
||||
inner.addImageButton(Icon.pencilSmall, Styles.emptyi, () -> {
|
||||
renaming = server;
|
||||
add.show();
|
||||
}).margin(3f).padTop(6f).top().right();
|
||||
|
||||
inner.addImageButton(Icon.trash16Small, Styles.emptyi, () -> {
|
||||
ui.showConfirm("$confirm", "$server.delete", () -> {
|
||||
servers.removeValue(server, true);
|
||||
saveServers();
|
||||
setupRemote();
|
||||
refreshRemote();
|
||||
});
|
||||
}).margin(3f).pad(6).top().right();
|
||||
|
||||
button.row();
|
||||
|
||||
server.content = button.table(t -> {}).grow().get();
|
||||
|
||||
remote.row();
|
||||
}
|
||||
}
|
||||
|
||||
void refreshRemote(){
|
||||
for(Server server : servers){
|
||||
refreshServer(server);
|
||||
}
|
||||
}
|
||||
|
||||
void refreshServer(Server server){
|
||||
server.content.clear();
|
||||
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();
|
||||
server.content.add("$host.invalid");
|
||||
});
|
||||
}
|
||||
|
||||
void setupServer(Server server, Host host){
|
||||
server.lastHost = host;
|
||||
server.content.clear();
|
||||
buildServer(host, server.content);
|
||||
}
|
||||
|
||||
void buildServer(Host host, Table content){
|
||||
String versionString;
|
||||
|
||||
if(host.version == -1){
|
||||
versionString = Core.bundle.format("server.version", Core.bundle.get("server.custombuild"), "");
|
||||
}else if(host.version == 0){
|
||||
versionString = Core.bundle.get("server.outdated");
|
||||
}else if(host.version < Version.build && Version.build != -1){
|
||||
versionString = Core.bundle.get("server.outdated") + "\n" +
|
||||
Core.bundle.format("server.version", host.version, "");
|
||||
}else if(host.version > Version.build && Version.build != -1){
|
||||
versionString = Core.bundle.get("server.outdated.client") + "\n" +
|
||||
Core.bundle.format("server.version", host.version, "");
|
||||
}else if(host.version == Version.build && Version.type.equals(host.versionType)){
|
||||
//not important
|
||||
versionString = "";
|
||||
}else{
|
||||
versionString = Core.bundle.format("server.version", host.version, host.versionType);
|
||||
}
|
||||
|
||||
content.table(t -> {
|
||||
t.add("[lightgray]" + host.name + " " + versionString).width(targetWidth() - 10f).left().get().setEllipsis(true);
|
||||
t.row();
|
||||
t.add("[lightgray]" + (Core.bundle.format("players" + (host.players == 1 && host.playerLimit <= 0 ? ".single" : ""), (host.players == 0 ? "[lightgray]" : "[accent]") + host.players + (host.playerLimit > 0 ? "[lightgray]/[accent]" + host.playerLimit : "")+ "[lightgray]"))).left();
|
||||
t.row();
|
||||
t.add("[lightgray]" + Core.bundle.format("save.map", host.mapname) + "[lightgray] / " + host.mode.toString()).width(targetWidth() - 10f).left().get().setEllipsis(true);
|
||||
}).expand().left().bottom().padLeft(12f).padBottom(8);
|
||||
}
|
||||
|
||||
void setup(){
|
||||
float w = targetWidth();
|
||||
|
||||
hosts.clear();
|
||||
|
||||
hosts.add(remote).growX();
|
||||
hosts.row();
|
||||
hosts.add(local).width(w);
|
||||
|
||||
ScrollPane pane = new ScrollPane(hosts);
|
||||
pane.setFadeScrollBars(false);
|
||||
pane.setScrollingDisabled(true, false);
|
||||
|
||||
setupRemote();
|
||||
refreshRemote();
|
||||
|
||||
cont.clear();
|
||||
cont.table(t -> {
|
||||
t.add("$name").padRight(10);
|
||||
if(!steam){
|
||||
t.addField(Core.settings.getString("name"), text -> {
|
||||
player.name = text;
|
||||
Core.settings.put("name", text);
|
||||
Core.settings.save();
|
||||
}).grow().pad(8).get().setMaxLength(maxNameLength);
|
||||
}else{
|
||||
t.add(player.name).update(l -> l.setColor(player.color)).grow().pad(8);
|
||||
}
|
||||
|
||||
ImageButton button = t.addImageButton(Tex.whiteui, Styles.clearFulli, 40, () -> {
|
||||
new PaletteDialog().show(color -> {
|
||||
player.color.set(color);
|
||||
Core.settings.put("color-0", Color.rgba8888(color));
|
||||
Core.settings.save();
|
||||
});
|
||||
}).size(54f).get();
|
||||
button.update(() -> button.getStyle().imageUpColor = player.color);
|
||||
}).width(w).height(70f).pad(4);
|
||||
cont.row();
|
||||
cont.add(pane).width(w + 38).pad(0);
|
||||
cont.row();
|
||||
cont.addCenteredImageTextButton("$server.add", Icon.add, () -> {
|
||||
renaming = null;
|
||||
add.show();
|
||||
}).marginLeft(6).width(w).height(80f).update(button -> {
|
||||
float pw = w;
|
||||
float pad = 0f;
|
||||
if(pane.getChildren().first().getPrefHeight() > pane.getHeight()){
|
||||
pw = w + 30;
|
||||
pad = 6;
|
||||
}
|
||||
|
||||
Cell cell = ((Table)pane.getParent()).getCell(button);
|
||||
|
||||
if(!Mathf.equal(cell.minWidth(), pw)){
|
||||
cell.width(pw);
|
||||
cell.padLeft(pad);
|
||||
pane.getParent().invalidateHierarchy();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void refreshLocal(){
|
||||
totalHosts = 0;
|
||||
|
||||
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();
|
||||
net.discoverServers(this::addLocalHost, this::finishLocalHosts);
|
||||
for(String host : defaultServers){
|
||||
net.pingHost(host, port, this::addLocalHost, e -> {});
|
||||
}
|
||||
}
|
||||
|
||||
void finishLocalHosts(){
|
||||
if(totalHosts == 0){
|
||||
local.clear();
|
||||
local.background(Tex.button);
|
||||
local.add("$hosts.none").pad(10f);
|
||||
local.add().growX();
|
||||
local.addImageButton(Icon.loading, this::refreshLocal).pad(-12f).padLeft(0).size(70f);
|
||||
}else{
|
||||
local.background(null);
|
||||
}
|
||||
}
|
||||
|
||||
void addLocalHost(Host host){
|
||||
if(totalHosts == 0){
|
||||
local.clear();
|
||||
}
|
||||
local.background(null);
|
||||
totalHosts++;
|
||||
float w = targetWidth();
|
||||
|
||||
local.row();
|
||||
|
||||
TextButton button = local.addButton("", Styles.cleart, () -> safeConnect(host.address, port, host.version))
|
||||
.width(w).pad(5f).get();
|
||||
button.clearChildren();
|
||||
buildServer(host, button);
|
||||
}
|
||||
|
||||
public void connect(String ip, int port){
|
||||
if(player.name.trim().isEmpty()){
|
||||
ui.showInfo("$noname");
|
||||
return;
|
||||
}
|
||||
|
||||
ui.loadfrag.show("$connecting");
|
||||
|
||||
ui.loadfrag.setButton(() -> {
|
||||
ui.loadfrag.hide();
|
||||
netClient.disconnectQuietly();
|
||||
});
|
||||
|
||||
Time.runTask(2f, () -> {
|
||||
logic.reset();
|
||||
net.reset();
|
||||
Vars.netClient.beginConnecting();
|
||||
net.connect(ip, port, () -> {
|
||||
hide();
|
||||
add.hide();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
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[]" +
|
||||
Core.bundle.format("server.versions", Version.build, version));
|
||||
}else{
|
||||
connect(ip, port);
|
||||
}
|
||||
}
|
||||
|
||||
float targetWidth(){
|
||||
return Core.graphics.isPortrait() ? 350f : 500f;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void loadServers(){
|
||||
servers = Core.settings.getObject("server-list", Array.class, Array::new);
|
||||
|
||||
//get servers
|
||||
Core.net.httpGet(serverJsonURL, result -> {
|
||||
try{
|
||||
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 {0} global servers.", defaultServers.size);
|
||||
}catch(Throwable ignored){}
|
||||
});
|
||||
}catch(Throwable ignored){}
|
||||
}, t -> {});
|
||||
}
|
||||
|
||||
private void saveServers(){
|
||||
Core.settings.putObject("server-list", servers);
|
||||
Core.settings.save();
|
||||
}
|
||||
|
||||
@Serialize
|
||||
public static class Server{
|
||||
public String ip;
|
||||
public int port;
|
||||
|
||||
transient Table content;
|
||||
transient Host lastHost;
|
||||
|
||||
void setIP(String ip){
|
||||
|
||||
//parse ip:port, if unsuccessful, use default values
|
||||
if(ip.lastIndexOf(':') != -1 && ip.lastIndexOf(':') != ip.length() - 1){
|
||||
try{
|
||||
int idx = ip.lastIndexOf(':');
|
||||
this.ip = ip.substring(0, idx);
|
||||
this.port = Integer.parseInt(ip.substring(idx + 1));
|
||||
}catch(Exception e){
|
||||
this.ip = ip;
|
||||
this.port = Vars.port;
|
||||
}
|
||||
}else{
|
||||
this.ip = ip;
|
||||
this.port = Vars.port;
|
||||
}
|
||||
}
|
||||
|
||||
String displayIP(){
|
||||
return ip + (port != Vars.port ? ":" + port : "");
|
||||
}
|
||||
|
||||
public Server(){
|
||||
}
|
||||
}
|
||||
}
|
||||
90
core/src/mindustry/ui/dialogs/LanguageDialog.java
Normal file
90
core/src/mindustry/ui/dialogs/LanguageDialog.java
Normal file
@@ -0,0 +1,90 @@
|
||||
package mindustry.ui.dialogs;
|
||||
|
||||
import arc.Core;
|
||||
import arc.struct.*;
|
||||
import arc.scene.ui.*;
|
||||
import arc.scene.ui.layout.Table;
|
||||
import arc.util.Log;
|
||||
import arc.util.Strings;
|
||||
import mindustry.ui.*;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import static mindustry.Vars.locales;
|
||||
import static mindustry.Vars.ui;
|
||||
|
||||
public class LanguageDialog extends FloatingDialog{
|
||||
private Locale lastLocale;
|
||||
private ObjectMap<Locale, String> displayNames = ObjectMap.of(
|
||||
Locale.TRADITIONAL_CHINESE, "正體中文",
|
||||
Locale.SIMPLIFIED_CHINESE, "简体中文"
|
||||
);
|
||||
|
||||
public LanguageDialog(){
|
||||
super("$settings.language");
|
||||
addCloseButton();
|
||||
setup();
|
||||
}
|
||||
|
||||
private void setup(){
|
||||
Table langs = new Table();
|
||||
langs.marginRight(24f).marginLeft(24f);
|
||||
ScrollPane pane = new ScrollPane(langs);
|
||||
pane.setFadeScrollBars(false);
|
||||
|
||||
ButtonGroup<TextButton> group = new ButtonGroup<>();
|
||||
|
||||
for(Locale loc : locales){
|
||||
TextButton button = new TextButton(Strings.capitalize(displayNames.get(loc, loc.getDisplayName(loc))), Styles.clearTogglet);
|
||||
button.clicked(() -> {
|
||||
if(getLocale().equals(loc)) return;
|
||||
Core.settings.put("locale", loc.toString());
|
||||
Core.settings.save();
|
||||
Log.info("Setting locale: {0}", loc.toString());
|
||||
ui.showInfo("$language.restart");
|
||||
});
|
||||
langs.add(button).group(group).update(t -> t.setChecked(loc.equals(getLocale()))).size(400f, 50f).row();
|
||||
}
|
||||
|
||||
cont.add(pane);
|
||||
}
|
||||
|
||||
public Locale getLocale(){
|
||||
String loc = Core.settings.getString("locale");
|
||||
|
||||
if(loc.equals("default")){
|
||||
findClosestLocale();
|
||||
}
|
||||
|
||||
if(lastLocale == null || !lastLocale.toString().equals(loc)){
|
||||
if(loc.contains("_")){
|
||||
String[] split = loc.split("_");
|
||||
lastLocale = new Locale(split[0], split[1]);
|
||||
}else{
|
||||
lastLocale = new Locale(loc);
|
||||
}
|
||||
}
|
||||
|
||||
return lastLocale;
|
||||
}
|
||||
|
||||
void findClosestLocale(){
|
||||
//check exact locale
|
||||
for(Locale l : locales){
|
||||
if(l.equals(Locale.getDefault())){
|
||||
Core.settings.put("locale", l.toString());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//find by language
|
||||
for(Locale l : locales){
|
||||
if(l.getLanguage().equals(Locale.getDefault().getLanguage())){
|
||||
Core.settings.put("locale", l.toString());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Core.settings.put("locale", new Locale("en").toString());
|
||||
}
|
||||
}
|
||||
216
core/src/mindustry/ui/dialogs/LoadDialog.java
Normal file
216
core/src/mindustry/ui/dialogs/LoadDialog.java
Normal file
@@ -0,0 +1,216 @@
|
||||
package mindustry.ui.dialogs;
|
||||
|
||||
import arc.*;
|
||||
import arc.struct.*;
|
||||
import arc.files.*;
|
||||
import arc.graphics.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.scene.style.*;
|
||||
import arc.scene.ui.*;
|
||||
import arc.scene.ui.layout.*;
|
||||
import arc.util.*;
|
||||
import mindustry.core.GameState.*;
|
||||
import mindustry.game.Saves.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.io.*;
|
||||
import mindustry.io.SaveIO.*;
|
||||
import mindustry.ui.*;
|
||||
import mindustry.ui.Styles;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class LoadDialog extends FloatingDialog{
|
||||
ScrollPane pane;
|
||||
Table slots;
|
||||
|
||||
public LoadDialog(){
|
||||
this("$loadgame");
|
||||
}
|
||||
|
||||
public LoadDialog(String title){
|
||||
super(title);
|
||||
setup();
|
||||
|
||||
shown(() -> {
|
||||
setup();
|
||||
Time.runTask(2f, () -> Core.scene.setScrollFocus(pane));
|
||||
});
|
||||
|
||||
addCloseButton();
|
||||
}
|
||||
|
||||
protected void setup(){
|
||||
cont.clear();
|
||||
|
||||
slots = new Table();
|
||||
pane = new ScrollPane(slots);
|
||||
pane.setFadeScrollBars(false);
|
||||
pane.setScrollingDisabled(true, false);
|
||||
|
||||
slots.marginRight(24);
|
||||
|
||||
Time.runTask(2f, () -> Core.scene.setScrollFocus(pane));
|
||||
|
||||
Array<SaveSlot> array = control.saves.getSaveSlots();
|
||||
array.sort((slot, other) -> -Long.compare(slot.getTimestamp(), other.getTimestamp()));
|
||||
|
||||
for(SaveSlot slot : array){
|
||||
if(slot.isHidden()) continue;
|
||||
|
||||
TextButton button = new TextButton("", Styles.cleart);
|
||||
button.getLabel().remove();
|
||||
button.clearChildren();
|
||||
|
||||
button.defaults().left();
|
||||
|
||||
button.table(title -> {
|
||||
title.add("[accent]" + slot.getName()).left().growX().width(230f).wrap();
|
||||
|
||||
title.table(t -> {
|
||||
t.right();
|
||||
|
||||
t.addImageButton(Icon.floppy, Styles.emptytogglei, () -> {
|
||||
slot.setAutosave(!slot.isAutosave());
|
||||
}).checked(slot.isAutosave()).right();
|
||||
|
||||
t.addImageButton(Icon.trash, Styles.emptyi, () -> {
|
||||
ui.showConfirm("$confirm", "$save.delete.confirm", () -> {
|
||||
slot.delete();
|
||||
setup();
|
||||
});
|
||||
}).right();
|
||||
|
||||
t.addImageButton(Icon.pencil, Styles.emptyi, () -> {
|
||||
ui.showTextInput("$save.rename", "$save.rename.text", slot.getName(), text -> {
|
||||
slot.setName(text);
|
||||
setup();
|
||||
});
|
||||
}).right();
|
||||
|
||||
t.addImageButton(Icon.save, Styles.emptyi, () -> {
|
||||
if(!ios){
|
||||
platform.showFileChooser(false, saveExtension, file -> {
|
||||
try{
|
||||
slot.exportFile(file);
|
||||
setup();
|
||||
}catch(IOException e){
|
||||
ui.showException("save.export.fail", e);
|
||||
}
|
||||
});
|
||||
}else{
|
||||
try{
|
||||
Fi file = Core.files.local("save-" + slot.getName() + "." + saveExtension);
|
||||
slot.exportFile(file);
|
||||
platform.shareFile(file);
|
||||
}catch(Exception e){
|
||||
ui.showException("save.export.fail", e);
|
||||
}
|
||||
}
|
||||
}).right();
|
||||
|
||||
}).padRight(-10).growX();
|
||||
}).growX().colspan(2);
|
||||
button.row();
|
||||
|
||||
String color = "[lightgray]";
|
||||
TextureRegion def = Core.atlas.find("nomap");
|
||||
|
||||
button.left().add(new BorderImage(def, 4f)).update(i -> {
|
||||
TextureRegionDrawable draw = (TextureRegionDrawable)i.getDrawable();
|
||||
if(draw.getRegion().getTexture().isDisposed()){
|
||||
draw.setRegion(def);
|
||||
}
|
||||
|
||||
Texture text = slot.previewTexture();
|
||||
if(draw.getRegion() == def && text != null){
|
||||
draw.setRegion(new TextureRegion(text));
|
||||
}
|
||||
i.setScaling(Scaling.fit);
|
||||
}).left().size(160f).padRight(6);
|
||||
|
||||
button.table(meta -> {
|
||||
meta.left().top();
|
||||
meta.defaults().padBottom(-2).left().width(290f);
|
||||
meta.row();
|
||||
meta.labelWrap(Core.bundle.format("save.map", color + (slot.getMap() == null ? Core.bundle.get("unknown") : slot.getMap().name())));
|
||||
meta.row();
|
||||
meta.labelWrap(slot.mode().toString() + " /" + color + " " + Core.bundle.format("save.wave", color + slot.getWave()));
|
||||
meta.row();
|
||||
meta.labelWrap(() -> Core.bundle.format("save.autosave", color + Core.bundle.get(slot.isAutosave() ? "on" : "off")));
|
||||
meta.row();
|
||||
meta.labelWrap(() -> Core.bundle.format("save.playtime", color + slot.getPlayTime()));
|
||||
meta.row();
|
||||
meta.labelWrap(color + slot.getDate());
|
||||
meta.row();
|
||||
}).left().growX().width(250f);
|
||||
|
||||
modifyButton(button, slot);
|
||||
|
||||
slots.add(button).uniformX().fillX().pad(4).padRight(-4).margin(10f).row();
|
||||
}
|
||||
|
||||
cont.add(pane);
|
||||
|
||||
addSetup();
|
||||
}
|
||||
|
||||
public void addSetup(){
|
||||
boolean valids = false;
|
||||
for(SaveSlot slot : control.saves.getSaveSlots()) if(!slot.isHidden()) valids = true;
|
||||
|
||||
if(!valids){
|
||||
slots.row();
|
||||
slots.addButton("$save.none", () -> {
|
||||
}).disabled(true).fillX().margin(20f).minWidth(340f).height(80f).pad(4f);
|
||||
}
|
||||
|
||||
slots.row();
|
||||
|
||||
slots.addImageTextButton("$save.import", Icon.add, () -> {
|
||||
platform.showFileChooser(true, saveExtension, file -> {
|
||||
if(SaveIO.isSaveValid(file)){
|
||||
try{
|
||||
control.saves.importSave(file);
|
||||
setup();
|
||||
}catch(IOException e){
|
||||
e.printStackTrace();
|
||||
ui.showException("$save.import.fail", e);
|
||||
}
|
||||
}else{
|
||||
ui.showErrorMessage("$save.import.invalid");
|
||||
}
|
||||
});
|
||||
}).fillX().margin(10f).minWidth(300f).height(70f).pad(4f).padRight(-4);
|
||||
}
|
||||
|
||||
public void runLoadSave(SaveSlot slot){
|
||||
slot.cautiousLoad(() -> {
|
||||
ui.loadAnd(() -> {
|
||||
hide();
|
||||
ui.paused.hide();
|
||||
try{
|
||||
net.reset();
|
||||
slot.load();
|
||||
state.rules.editor = false;
|
||||
state.rules.zone = null;
|
||||
state.set(State.playing);
|
||||
}catch(SaveException e){
|
||||
Log.err(e);
|
||||
state.set(State.menu);
|
||||
logic.reset();
|
||||
ui.showErrorMessage("$save.corrupted");
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public void modifyButton(TextButton button, SaveSlot slot){
|
||||
button.clicked(() -> {
|
||||
if(!button.childrenPressed()){
|
||||
runLoadSave(slot);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
127
core/src/mindustry/ui/dialogs/LoadoutDialog.java
Normal file
127
core/src/mindustry/ui/dialogs/LoadoutDialog.java
Normal file
@@ -0,0 +1,127 @@
|
||||
package mindustry.ui.dialogs;
|
||||
|
||||
import arc.*;
|
||||
import arc.struct.*;
|
||||
import arc.input.*;
|
||||
import arc.scene.ui.layout.*;
|
||||
import arc.util.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.ui.*;
|
||||
import mindustry.ui.Cicon;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class LoadoutDialog extends FloatingDialog{
|
||||
private Runnable hider;
|
||||
private Runnable resetter;
|
||||
private Runnable updater;
|
||||
private Array<ItemStack> stacks = new Array<>();
|
||||
private Array<ItemStack> originalStacks = new Array<>();
|
||||
private Table items;
|
||||
private int capacity;
|
||||
|
||||
public LoadoutDialog(){
|
||||
super("$configure");
|
||||
setFillParent(true);
|
||||
|
||||
keyDown(key -> {
|
||||
if(key == KeyCode.ESCAPE || key == KeyCode.BACK){
|
||||
Core.app.post(this::hide);
|
||||
}
|
||||
});
|
||||
|
||||
cont.pane(t -> items = t.margin(10f)).left();
|
||||
|
||||
shown(this::setup);
|
||||
hidden(() -> {
|
||||
originalStacks.selectFrom(stacks, s -> s.amount > 0);
|
||||
updater.run();
|
||||
if(hider != null){
|
||||
hider.run();
|
||||
}
|
||||
});
|
||||
|
||||
buttons.addImageTextButton("$back", Icon.arrowLeft, this::hide).size(210f, 64f);
|
||||
|
||||
buttons.addImageTextButton("$settings.reset", Icon.refreshSmall, () -> {
|
||||
resetter.run();
|
||||
reseed();
|
||||
updater.run();
|
||||
setup();
|
||||
}).size(210f, 64f);
|
||||
}
|
||||
|
||||
public void show(int capacity, Array<ItemStack> stacks, Runnable reseter, Runnable updater, Runnable hider){
|
||||
this.originalStacks = stacks;
|
||||
reseed();
|
||||
this.resetter = reseter;
|
||||
this.updater = updater;
|
||||
this.capacity = capacity;
|
||||
this.hider = hider;
|
||||
//this.filter = filter;
|
||||
show();
|
||||
}
|
||||
|
||||
void setup(){
|
||||
items.clearChildren();
|
||||
items.left();
|
||||
float bsize = 40f;
|
||||
|
||||
int i = 0;
|
||||
|
||||
for(ItemStack stack : stacks){
|
||||
items.table(Tex.pane, t -> {
|
||||
t.margin(4).marginRight(8).left();
|
||||
t.addButton("-", Styles.cleart, () -> {
|
||||
stack.amount = Math.max(stack.amount - step(stack.amount), 0);
|
||||
updater.run();
|
||||
}).size(bsize);
|
||||
|
||||
t.addButton("+", Styles.cleart, () -> {
|
||||
stack.amount = Math.min(stack.amount + step(stack.amount), capacity);
|
||||
updater.run();
|
||||
}).size(bsize);
|
||||
|
||||
t.addImageButton(Icon.pencilSmaller, Styles.cleari, () -> ui.showTextInput("$configure", stack.item.localizedName, 10, stack.amount + "", true, str -> {
|
||||
if(Strings.canParsePostiveInt(str)){
|
||||
int amount = Strings.parseInt(str);
|
||||
if(amount >= 0 && amount <= capacity){
|
||||
stack.amount = amount;
|
||||
updater.run();
|
||||
return;
|
||||
}
|
||||
}
|
||||
ui.showInfo(Core.bundle.format("configure.invalid", capacity));
|
||||
})).size(bsize);
|
||||
|
||||
t.addImage(stack.item.icon(Cicon.small)).size(8 * 3).padRight(4).padLeft(4);
|
||||
t.label(() -> stack.amount + "").left().width(90f);
|
||||
}).pad(2).left().fillX();
|
||||
|
||||
|
||||
if(++i % 2 == 0 || (mobile && Core.graphics.isPortrait())){
|
||||
items.row();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void reseed(){
|
||||
this.stacks = originalStacks.map(ItemStack::copy);
|
||||
this.stacks.addAll(content.items().select(i -> i.type == ItemType.material &&
|
||||
!stacks.contains(stack -> stack.item == i)).map(i -> new ItemStack(i, 0)));
|
||||
this.stacks.sort(Structs.comparingInt(s -> s.item.id));
|
||||
}
|
||||
|
||||
private int step(int amount){
|
||||
if(amount < 1000){
|
||||
return 100;
|
||||
}else if(amount < 2000){
|
||||
return 200;
|
||||
}else if(amount < 5000){
|
||||
return 500;
|
||||
}else{
|
||||
return 1000;
|
||||
}
|
||||
}
|
||||
}
|
||||
110
core/src/mindustry/ui/dialogs/MapPlayDialog.java
Normal file
110
core/src/mindustry/ui/dialogs/MapPlayDialog.java
Normal file
@@ -0,0 +1,110 @@
|
||||
package mindustry.ui.dialogs;
|
||||
|
||||
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.*;
|
||||
import mindustry.ui.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class MapPlayDialog extends FloatingDialog{
|
||||
CustomRulesDialog dialog = new CustomRulesDialog();
|
||||
Rules rules;
|
||||
@NonNull
|
||||
Gamemode selectedGamemode = Gamemode.survival;
|
||||
Map lastMap;
|
||||
|
||||
public MapPlayDialog(){
|
||||
super("");
|
||||
setFillParent(false);
|
||||
|
||||
onResize(() -> {
|
||||
if(lastMap != null){
|
||||
Rules rules = this.rules;
|
||||
show(lastMap);
|
||||
this.rules = rules;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void show(Map map){
|
||||
this.lastMap = map;
|
||||
title.setText(map.name());
|
||||
cont.clearChildren();
|
||||
|
||||
//reset to any valid mode after switching to attack (one must exist)
|
||||
if(!selectedGamemode.valid(map)){
|
||||
selectedGamemode = Structs.find(Gamemode.all, m -> m.valid(map));
|
||||
if(selectedGamemode == null){
|
||||
selectedGamemode = Gamemode.survival;
|
||||
}
|
||||
}
|
||||
|
||||
rules = map.applyRules(selectedGamemode);
|
||||
|
||||
Table selmode = new Table();
|
||||
selmode.add("$level.mode").colspan(4);
|
||||
selmode.row();
|
||||
int i = 0;
|
||||
|
||||
Table modes = new Table();
|
||||
|
||||
for(Gamemode mode : Gamemode.values()){
|
||||
if(mode.hidden) continue;
|
||||
|
||||
modes.addButton(mode.toString(), Styles.togglet, () -> {
|
||||
selectedGamemode = mode;
|
||||
rules = map.applyRules(mode);
|
||||
}).update(b -> b.setChecked(selectedGamemode == mode)).size(140f, 54f).disabled(!mode.valid(map));
|
||||
if(i++ % 2 == 1) modes.row();
|
||||
}
|
||||
selmode.add(modes);
|
||||
selmode.addButton("?", this::displayGameModeHelp).width(50f).fillY().padLeft(18f);
|
||||
|
||||
cont.add(selmode);
|
||||
cont.row();
|
||||
cont.addImageTextButton("$customize", Icon.toolsSmall, () -> dialog.show(rules, () -> rules = map.applyRules(selectedGamemode))).width(230);
|
||||
cont.row();
|
||||
cont.add(new BorderImage(map.safeTexture(), 3f)).size(mobile && !Core.graphics.isPortrait() ? 150f : 250f).get().setScaling(Scaling.fit);
|
||||
//only maps with survival are valid for high scores
|
||||
if(Gamemode.survival.valid(map)){
|
||||
cont.row();
|
||||
cont.label((() -> Core.bundle.format("level.highscore", map.getHightScore()))).pad(3f);
|
||||
}
|
||||
|
||||
buttons.clearChildren();
|
||||
addCloseButton();
|
||||
|
||||
buttons.addImageTextButton("$play", Icon.play, () -> {
|
||||
control.playMap(map, rules);
|
||||
hide();
|
||||
ui.custom.hide();
|
||||
}).size(210f, 64f);
|
||||
|
||||
show();
|
||||
}
|
||||
|
||||
private void displayGameModeHelp(){
|
||||
FloatingDialog d = new FloatingDialog(Core.bundle.get("mode.help.title"));
|
||||
d.setFillParent(false);
|
||||
Table table = new Table();
|
||||
table.defaults().pad(1f);
|
||||
ScrollPane pane = new ScrollPane(table);
|
||||
pane.setFadeScrollBars(false);
|
||||
table.row();
|
||||
for(Gamemode mode : Gamemode.values()){
|
||||
if(mode.hidden) continue;
|
||||
table.labelWrap("[accent]" + mode.toString() + ":[] [lightgray]" + mode.description()).width(400f);
|
||||
table.row();
|
||||
}
|
||||
|
||||
d.cont.add(pane);
|
||||
d.buttons.addButton("$ok", d::hide).size(110, 50).pad(10f);
|
||||
d.show();
|
||||
}
|
||||
}
|
||||
220
core/src/mindustry/ui/dialogs/MapsDialog.java
Normal file
220
core/src/mindustry/ui/dialogs/MapsDialog.java
Normal file
@@ -0,0 +1,220 @@
|
||||
package mindustry.ui.dialogs;
|
||||
|
||||
import arc.*;
|
||||
import arc.graphics.*;
|
||||
import arc.input.*;
|
||||
import arc.math.*;
|
||||
import arc.scene.ui.*;
|
||||
import arc.scene.ui.layout.*;
|
||||
import arc.util.*;
|
||||
import mindustry.*;
|
||||
import mindustry.game.EventType.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.io.*;
|
||||
import mindustry.maps.*;
|
||||
import mindustry.ui.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class MapsDialog extends FloatingDialog{
|
||||
private FloatingDialog dialog;
|
||||
|
||||
public MapsDialog(){
|
||||
super("$maps");
|
||||
|
||||
buttons.remove();
|
||||
|
||||
keyDown(key -> {
|
||||
if(key == KeyCode.ESCAPE || key == KeyCode.BACK){
|
||||
Core.app.post(this::hide);
|
||||
}
|
||||
});
|
||||
|
||||
shown(this::setup);
|
||||
onResize(() -> {
|
||||
if(dialog != null){
|
||||
dialog.hide();
|
||||
}
|
||||
setup();
|
||||
});
|
||||
}
|
||||
|
||||
void setup(){
|
||||
buttons.clearChildren();
|
||||
|
||||
if(Core.graphics.isPortrait()){
|
||||
buttons.addImageTextButton("$back", Icon.arrowLeft, this::hide).size(210f*2f, 64f).colspan(2);
|
||||
buttons.row();
|
||||
}else{
|
||||
buttons.addImageTextButton("$back", Icon.arrowLeft, this::hide).size(210f, 64f);
|
||||
}
|
||||
|
||||
buttons.addImageTextButton("$editor.newmap", Icon.add, () -> {
|
||||
ui.showTextInput("$editor.newmap", "$name", "", text -> {
|
||||
Runnable show = () -> ui.loadAnd(() -> {
|
||||
hide();
|
||||
ui.editor.show();
|
||||
ui.editor.editor.getTags().put("name", text);
|
||||
Events.fire(new MapMakeEvent());
|
||||
});
|
||||
|
||||
if(maps.byName(text) != null){
|
||||
ui.showErrorMessage("$editor.exists");
|
||||
}else{
|
||||
show.run();
|
||||
}
|
||||
});
|
||||
}).size(210f, 64f);
|
||||
|
||||
buttons.addImageTextButton("$editor.importmap", Icon.load, () -> {
|
||||
platform.showFileChooser(true, mapExtension, file -> {
|
||||
ui.loadAnd(() -> {
|
||||
maps.tryCatchMapError(() -> {
|
||||
if(MapIO.isImage(file)){
|
||||
ui.showErrorMessage("$editor.errorimage");
|
||||
return;
|
||||
}
|
||||
|
||||
Map map = MapIO.createMap(file, true);
|
||||
|
||||
|
||||
//when you attempt to import a save, it will have no name, so generate one
|
||||
String name = map.tags.getOr("name", () -> {
|
||||
String result = "unknown";
|
||||
int number = 0;
|
||||
while(maps.byName(result + number++) != null);
|
||||
return result + number;
|
||||
});
|
||||
|
||||
//this will never actually get called, but it remains just in case
|
||||
if(name == null){
|
||||
ui.showErrorMessage("$editor.errorname");
|
||||
return;
|
||||
}
|
||||
|
||||
Map conflict = maps.all().find(m -> m.name().equals(name));
|
||||
|
||||
if(conflict != null && !conflict.custom){
|
||||
ui.showInfo(Core.bundle.format("editor.import.exists", name));
|
||||
}else if(conflict != null){
|
||||
ui.showConfirm("$confirm", "$editor.overwrite.confirm", () -> {
|
||||
maps.tryCatchMapError(() -> {
|
||||
maps.removeMap(conflict);
|
||||
maps.importMap(map.file);
|
||||
setup();
|
||||
});
|
||||
});
|
||||
}else{
|
||||
maps.importMap(map.file);
|
||||
setup();
|
||||
}
|
||||
|
||||
});
|
||||
});
|
||||
});
|
||||
}).size(210f, 64f);
|
||||
|
||||
|
||||
cont.clear();
|
||||
|
||||
Table maps = new Table();
|
||||
maps.marginRight(24);
|
||||
|
||||
ScrollPane pane = new ScrollPane(maps);
|
||||
pane.setFadeScrollBars(false);
|
||||
|
||||
int maxwidth = Mathf.clamp((int)(Core.graphics.getWidth() / Scl.scl(230)), 1, 8);
|
||||
float mapsize = 200f;
|
||||
|
||||
int i = 0;
|
||||
for(Map map : Vars.maps.all()){
|
||||
|
||||
if(i % maxwidth == 0){
|
||||
maps.row();
|
||||
}
|
||||
|
||||
TextButton button = maps.addButton("", Styles.cleart, () -> showMapInfo(map)).width(mapsize).pad(8).get();
|
||||
button.clearChildren();
|
||||
button.margin(9);
|
||||
button.add(map.name()).width(mapsize - 18f).center().get().setEllipsis(true);
|
||||
button.row();
|
||||
button.addImage().growX().pad(4).color(Pal.gray);
|
||||
button.row();
|
||||
button.stack(new Image(map.safeTexture()).setScaling(Scaling.fit), new BorderImage(map.safeTexture()).setScaling(Scaling.fit)).size(mapsize - 20f);
|
||||
button.row();
|
||||
button.add(map.custom ? "$custom" : map.workshop ? "$workshop" : map.mod != null ? "[lightgray]" + map.mod.meta.displayName() : "$builtin").color(Color.gray).padTop(3);
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
if(Vars.maps.all().size == 0){
|
||||
maps.add("$maps.none");
|
||||
}
|
||||
|
||||
cont.add(buttons).growX();
|
||||
cont.row();
|
||||
cont.add(pane).uniformX();
|
||||
}
|
||||
|
||||
void showMapInfo(Map map){
|
||||
dialog = new FloatingDialog("$editor.mapinfo");
|
||||
dialog.addCloseButton();
|
||||
|
||||
float mapsize = Core.graphics.isPortrait() ? 160f : 300f;
|
||||
Table table = dialog.cont;
|
||||
|
||||
table.stack(new Image(map.safeTexture()).setScaling(Scaling.fit), new BorderImage(map.safeTexture()).setScaling(Scaling.fit)).size(mapsize);
|
||||
|
||||
table.table(Styles.black, desc -> {
|
||||
desc.top();
|
||||
Table t = new Table();
|
||||
t.margin(6);
|
||||
|
||||
ScrollPane pane = new ScrollPane(t);
|
||||
desc.add(pane).grow();
|
||||
|
||||
t.top();
|
||||
t.defaults().padTop(10).left();
|
||||
|
||||
t.add("$editor.name").padRight(10).color(Color.gray).padTop(0);
|
||||
t.row();
|
||||
t.add(map.name()).growX().wrap().padTop(2);
|
||||
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.row();
|
||||
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();
|
||||
|
||||
table.addImageTextButton("$editor.openin", Icon.loadMapSmall, () -> {
|
||||
try{
|
||||
Vars.ui.editor.beginEditMap(map.file);
|
||||
dialog.hide();
|
||||
hide();
|
||||
}catch(Exception e){
|
||||
e.printStackTrace();
|
||||
ui.showErrorMessage("$error.mapnotfound");
|
||||
}
|
||||
}).fillX().height(54f).marginLeft(10);
|
||||
|
||||
table.addImageTextButton(map.workshop && steam ? "$view.workshop" : "$delete", map.workshop && steam ? Icon.linkSmall : Icon.trash16Small, () -> {
|
||||
if(map.workshop && steam){
|
||||
platform.viewListing(map);
|
||||
}else{
|
||||
ui.showConfirm("$confirm", Core.bundle.format("map.delete", map.name()), () -> {
|
||||
maps.removeMap(map);
|
||||
dialog.hide();
|
||||
setup();
|
||||
});
|
||||
}
|
||||
}).fillX().height(54f).marginLeft(10).disabled(!map.workshop && !map.custom);
|
||||
|
||||
dialog.show();
|
||||
}
|
||||
}
|
||||
70
core/src/mindustry/ui/dialogs/MinimapDialog.java
Normal file
70
core/src/mindustry/ui/dialogs/MinimapDialog.java
Normal file
@@ -0,0 +1,70 @@
|
||||
package mindustry.ui.dialogs;
|
||||
|
||||
import arc.*;
|
||||
import arc.graphics.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.input.*;
|
||||
import arc.scene.event.*;
|
||||
import arc.scene.ui.layout.*;
|
||||
import mindustry.gen.*;
|
||||
|
||||
import static mindustry.Vars.renderer;
|
||||
|
||||
public class MinimapDialog extends FloatingDialog{
|
||||
|
||||
public MinimapDialog(){
|
||||
super("$minimap");
|
||||
setFillParent(true);
|
||||
|
||||
shown(this::setup);
|
||||
|
||||
addCloseButton();
|
||||
shouldPause = true;
|
||||
titleTable.remove();
|
||||
onResize(this::setup);
|
||||
}
|
||||
|
||||
void setup(){
|
||||
cont.clear();
|
||||
|
||||
cont.table(Tex.pane,t -> {
|
||||
t.addRect((x, y, width, height) -> {
|
||||
if(renderer.minimap.getRegion() == null) return;
|
||||
Draw.color(Color.white);
|
||||
Draw.alpha(parentAlpha);
|
||||
Draw.rect(renderer.minimap.getRegion(), x + width / 2f, y + height / 2f, width, height);
|
||||
|
||||
if(renderer.minimap.getTexture() != null){
|
||||
renderer.minimap.drawEntities(x, y, width, height);
|
||||
}
|
||||
}).grow();
|
||||
}).size(Math.min(Core.graphics.getWidth() / 1.1f, Core.graphics.getHeight() / 1.3f) / Scl.scl(1f)).padTop(-20f);
|
||||
|
||||
cont.addListener(new InputListener(){
|
||||
@Override
|
||||
public boolean scrolled(InputEvent event, float x, float y, float amountx, float amounty){
|
||||
renderer.minimap.zoomBy(amounty);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
cont.addListener(new ElementGestureListener(){
|
||||
float lzoom = -1f;
|
||||
|
||||
@Override
|
||||
public void touchUp(InputEvent event, float x, float y, int pointer, KeyCode button){
|
||||
lzoom = renderer.minimap.getZoom();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void zoom(InputEvent event, float initialDistance, float distance){
|
||||
if(lzoom < 0){
|
||||
lzoom = renderer.minimap.getZoom();
|
||||
}
|
||||
renderer.minimap.setZoom(initialDistance / distance * lzoom);
|
||||
}
|
||||
});
|
||||
|
||||
Core.app.post(() -> Core.scene.setScrollFocus(cont));
|
||||
}
|
||||
}
|
||||
195
core/src/mindustry/ui/dialogs/ModsDialog.java
Normal file
195
core/src/mindustry/ui/dialogs/ModsDialog.java
Normal file
@@ -0,0 +1,195 @@
|
||||
package mindustry.ui.dialogs;
|
||||
|
||||
import arc.*;
|
||||
import arc.Net.*;
|
||||
import arc.files.*;
|
||||
import arc.util.*;
|
||||
import arc.util.io.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.mod.Mods.*;
|
||||
import mindustry.ui.*;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class ModsDialog extends FloatingDialog{
|
||||
|
||||
public ModsDialog(){
|
||||
super("$mods");
|
||||
addCloseButton();
|
||||
|
||||
buttons.addImageTextButton(mobile ? "$mods.report" : "$mods.openfolder", Icon.link,
|
||||
() -> {
|
||||
if(mobile){
|
||||
Core.net.openURI(reportIssueURL);
|
||||
}else{
|
||||
Core.net.openFolder(modDirectory.absolutePath());
|
||||
}
|
||||
})
|
||||
.size(250f, 64f);
|
||||
|
||||
buttons.row();
|
||||
|
||||
buttons.addImageTextButton("$mods.guide", Icon.wiki,
|
||||
() -> Core.net.openURI(modGuideURL))
|
||||
.size(210, 64f);
|
||||
|
||||
buttons.addImageTextButton("$mod.import.github", Icon.github, () -> {
|
||||
ui.showTextInput("$mod.import.github", "", 64, "Anuken/ExampleMod", text -> {
|
||||
ui.loadfrag.show();
|
||||
Core.net.httpGet("http://api.github.com/repos/" + text + "/zipball/master", loc -> {
|
||||
Core.net.httpGet(loc.getHeader("Location"), result -> {
|
||||
if(result.getStatus() != HttpStatus.OK){
|
||||
ui.showErrorMessage(Core.bundle.format("connectfail", result.getStatus()));
|
||||
ui.loadfrag.hide();
|
||||
}else{
|
||||
try{
|
||||
Fi file = tmpDirectory.child(text.replace("/", "") + ".zip");
|
||||
Streams.copyStream(result.getResultAsStream(), file.write(false));
|
||||
mods.importMod(file);
|
||||
file.delete();
|
||||
Core.app.post(() -> {
|
||||
try{
|
||||
mods.reloadContent();
|
||||
setup();
|
||||
ui.loadfrag.hide();
|
||||
}catch(Throwable e){
|
||||
ui.showException(e);
|
||||
}
|
||||
});
|
||||
}catch(Throwable e){
|
||||
modError(e);
|
||||
}
|
||||
}
|
||||
}, t -> Core.app.post(() -> modError(t)));
|
||||
}, t -> Core.app.post(() -> modError(t)));
|
||||
});
|
||||
}).size(250f, 64f);
|
||||
|
||||
|
||||
shown(this::setup);
|
||||
|
||||
hidden(() -> {
|
||||
if(mods.requiresReload()){
|
||||
ui.loadAnd("$reloading", () -> {
|
||||
mods.eachEnabled(mod -> {
|
||||
if(mod.hasUnmetDependencies()){
|
||||
ui.showErrorMessage(Core.bundle.format("mod.nowdisabled", mod.name, mod.missingDependencies.toString(", ")));
|
||||
}
|
||||
});
|
||||
mods.reloadContent();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
shown(() -> Core.app.post(() -> {
|
||||
Core.settings.getBoolOnce("modsalpha", () -> {
|
||||
ui.showText("$mods", "$mods.alphainfo");
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
||||
void modError(Throwable error){
|
||||
ui.loadfrag.hide();
|
||||
|
||||
if(Strings.getCauses(error).contains(t -> t.getMessage() != null && (t.getMessage().contains("SSL") || t.getMessage().contains("protocol")))){
|
||||
ui.showErrorMessage("$feature.unsupported");
|
||||
}else{
|
||||
ui.showException(error);
|
||||
}
|
||||
}
|
||||
|
||||
void setup(){
|
||||
cont.clear();
|
||||
cont.defaults().width(mobile ? 500 : 560f).pad(4);
|
||||
cont.add("$mod.reloadrequired").visible(mods::requiresReload).center().get().setAlignment(Align.center);
|
||||
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;
|
||||
table.row();
|
||||
table.addImage().growX().height(4f).pad(6f).color(Pal.gray);
|
||||
table.row();
|
||||
}
|
||||
|
||||
table.table(Styles.black6, t -> {
|
||||
t.defaults().pad(2).left().top();
|
||||
t.margin(14f).left();
|
||||
t.table(title -> {
|
||||
title.left();
|
||||
title.add("[accent]" + mod.meta.displayName() + "[lightgray] v" + mod.meta.version + (mod.enabled() ? "" : "\n" + Core.bundle.get("mod.disabled") + "")).width(200f).wrap();
|
||||
title.add().growX();
|
||||
|
||||
title.addImageTextButton(mod.enabled() ? "$mod.disable" : "$mod.enable", mod.enabled() ? Icon.arrowDownSmall : Icon.arrowUpSmall, Styles.cleart, () -> {
|
||||
mods.setEnabled(mod, !mod.enabled());
|
||||
setup();
|
||||
}).height(50f).margin(8f).width(130f).disabled(!mod.isSupported());
|
||||
|
||||
if(steam && !mod.hasSteamID()){
|
||||
title.addImageButton(Icon.loadMapSmall, Styles.cleari, () -> {
|
||||
platform.publish(mod);
|
||||
}).size(50f);
|
||||
}
|
||||
|
||||
title.addImageButton(mod.hasSteamID() ? Icon.linkSmall : Icon.trash16Small, Styles.cleari, () -> {
|
||||
if(!mod.hasSteamID()){
|
||||
ui.showConfirm("$confirm", "$mod.remove.confirm", () -> {
|
||||
mods.removeMod(mod);
|
||||
setup();
|
||||
});
|
||||
}else{
|
||||
platform.viewListing(mod);
|
||||
}
|
||||
}).size(50f);
|
||||
}).growX().left().padTop(-14f).padRight(-14f);
|
||||
|
||||
t.row();
|
||||
if(mod.meta.author != null){
|
||||
t.add(Core.bundle.format("mod.author", mod.meta.author));
|
||||
t.row();
|
||||
}
|
||||
if(mod.meta.description != null){
|
||||
t.labelWrap("[lightgray]" + mod.meta.description).growX();
|
||||
t.row();
|
||||
}
|
||||
if(!mod.isSupported()){
|
||||
t.labelWrap(Core.bundle.format("mod.requiresversion", mod.meta.minGameVersion)).growX();
|
||||
t.row();
|
||||
}else if(mod.hasUnmetDependencies()){
|
||||
t.labelWrap(Core.bundle.format("mod.missingdependencies", mod.missingDependencies.toString(", "))).growX();
|
||||
t.row();
|
||||
}else if(mod.hasContentErrors()){
|
||||
t.labelWrap("$mod.erroredcontent").growX();
|
||||
t.row();
|
||||
}
|
||||
}).width(mobile ? 430f : 500f);
|
||||
table.row();
|
||||
}
|
||||
});
|
||||
|
||||
}else{
|
||||
cont.table(Styles.black6, t -> t.add("$mods.none")).height(80f);
|
||||
}
|
||||
|
||||
cont.row();
|
||||
|
||||
cont.addImageTextButton("$mod.import", Icon.add, () -> {
|
||||
platform.showFileChooser(true, "zip", file -> {
|
||||
try{
|
||||
mods.importMod(file);
|
||||
setup();
|
||||
}catch(IOException e){
|
||||
ui.showException(e);
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
}).margin(12f).width(400f);
|
||||
}
|
||||
}
|
||||
51
core/src/mindustry/ui/dialogs/PaletteDialog.java
Normal file
51
core/src/mindustry/ui/dialogs/PaletteDialog.java
Normal file
@@ -0,0 +1,51 @@
|
||||
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.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class PaletteDialog extends Dialog{
|
||||
private Cons<Color> cons;
|
||||
|
||||
public PaletteDialog(){
|
||||
super("");
|
||||
build();
|
||||
}
|
||||
|
||||
private void build(){
|
||||
Table table = new Table();
|
||||
cont.add(table);
|
||||
|
||||
for(int i = 0; i < playerColors.length; i++){
|
||||
Color color = playerColors[i];
|
||||
|
||||
ImageButton button = table.addImageButton(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();
|
||||
}
|
||||
}
|
||||
|
||||
keyDown(key -> {
|
||||
if(key == KeyCode.ESCAPE || key == KeyCode.BACK)
|
||||
hide();
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
public void show(Cons<Color> cons){
|
||||
this.cons = cons;
|
||||
show();
|
||||
}
|
||||
}
|
||||
134
core/src/mindustry/ui/dialogs/PausedDialog.java
Normal file
134
core/src/mindustry/ui/dialogs/PausedDialog.java
Normal file
@@ -0,0 +1,134 @@
|
||||
package mindustry.ui.dialogs;
|
||||
|
||||
import arc.*;
|
||||
import arc.input.*;
|
||||
import mindustry.core.GameState.*;
|
||||
import mindustry.gen.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class PausedDialog extends FloatingDialog{
|
||||
private SaveDialog save = new SaveDialog();
|
||||
private LoadDialog load = new LoadDialog();
|
||||
private boolean wasClient = false;
|
||||
|
||||
public PausedDialog(){
|
||||
super("$menu");
|
||||
shouldPause = true;
|
||||
|
||||
shown(this::rebuild);
|
||||
|
||||
keyDown(key -> {
|
||||
if(key == KeyCode.ESCAPE || key == KeyCode.BACK){
|
||||
hide();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void rebuild(){
|
||||
cont.clear();
|
||||
|
||||
update(() -> {
|
||||
if(state.is(State.menu) && isShown()){
|
||||
hide();
|
||||
}
|
||||
});
|
||||
|
||||
if(!mobile){
|
||||
float dw = 210f;
|
||||
cont.defaults().width(dw).height(50).pad(5f);
|
||||
|
||||
cont.addButton("$back", this::hide).colspan(2).width(dw * 2 + 20f);
|
||||
|
||||
cont.row();
|
||||
if(world.isZone()){
|
||||
cont.addButton("$techtree", ui.tech::show);
|
||||
}else{
|
||||
cont.addButton("$database", ui.database::show);
|
||||
}
|
||||
cont.addButton("$settings", ui.settings::show);
|
||||
|
||||
if(!state.rules.tutorial){
|
||||
if(!world.isZone() && !state.isEditor()){
|
||||
cont.row();
|
||||
cont.addButton("$savegame", save::show);
|
||||
cont.addButton("$loadgame", load::show).disabled(b -> net.active());
|
||||
}
|
||||
|
||||
cont.row();
|
||||
|
||||
cont.addButton("$hostserver", () -> {
|
||||
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.addButton("$quit", this::showQuitConfirm).colspan(2).width(dw + 10f).update(s -> s.setText(control.saves.getCurrent() != null && control.saves.getCurrent().isAutosave() ? "$save.quit" : "$quit"));
|
||||
|
||||
}else{
|
||||
cont.defaults().size(130f).pad(5);
|
||||
cont.addRowImageTextButton("$back", Icon.play2, this::hide);
|
||||
cont.addRowImageTextButton("$settings", Icon.tools, ui.settings::show);
|
||||
|
||||
if(!world.isZone() && !state.isEditor()){
|
||||
cont.addRowImageTextButton("$save", Icon.save, save::show);
|
||||
|
||||
cont.row();
|
||||
|
||||
cont.addRowImageTextButton("$load", Icon.load, load::show).disabled(b -> net.active());
|
||||
}else{
|
||||
cont.row();
|
||||
}
|
||||
|
||||
cont.addRowImageTextButton("$hostserver.mobile", Icon.host, ui.host::show).disabled(b -> net.active());
|
||||
|
||||
cont.addRowImageTextButton("$quit", Icon.quit, this::showQuitConfirm).update(s -> s.setText(control.saves.getCurrent() != null && control.saves.getCurrent().isAutosave() ? "$save.quit" : "$quit"));
|
||||
}
|
||||
}
|
||||
|
||||
void showQuitConfirm(){
|
||||
ui.showConfirm("$confirm", state.rules.tutorial ? "$quit.confirm.tutorial" : "$quit.confirm", () -> {
|
||||
if(state.rules.tutorial){
|
||||
Core.settings.put("playedtutorial", true);
|
||||
Core.settings.save();
|
||||
}
|
||||
wasClient = net.client();
|
||||
if(net.client()) netClient.disconnectQuietly();
|
||||
runExitSave();
|
||||
hide();
|
||||
});
|
||||
}
|
||||
|
||||
public void runExitSave(){
|
||||
if(state.isEditor() && !wasClient){
|
||||
ui.editor.resumeEditing();
|
||||
return;
|
||||
}
|
||||
|
||||
if(control.saves.getCurrent() == null || !control.saves.getCurrent().isAutosave() || state.rules.tutorial || wasClient){
|
||||
state.set(State.menu);
|
||||
logic.reset();
|
||||
return;
|
||||
}
|
||||
|
||||
ui.loadAnd("$saveload", () -> {
|
||||
try{
|
||||
control.saves.getCurrent().save();
|
||||
}catch(Throwable e){
|
||||
e.printStackTrace();
|
||||
ui.showException("[accent]" + Core.bundle.get("savefail"), e);
|
||||
}
|
||||
state.set(State.menu);
|
||||
logic.reset();
|
||||
});
|
||||
}
|
||||
}
|
||||
62
core/src/mindustry/ui/dialogs/SaveDialog.java
Normal file
62
core/src/mindustry/ui/dialogs/SaveDialog.java
Normal file
@@ -0,0 +1,62 @@
|
||||
package mindustry.ui.dialogs;
|
||||
|
||||
import arc.Core;
|
||||
import arc.scene.ui.TextButton;
|
||||
import arc.util.Time;
|
||||
import mindustry.core.GameState.State;
|
||||
import mindustry.game.Saves.SaveSlot;
|
||||
import mindustry.gen.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class SaveDialog extends LoadDialog{
|
||||
|
||||
public SaveDialog(){
|
||||
super("$savegame");
|
||||
|
||||
update(() -> {
|
||||
if(state.is(State.menu) && isShown()){
|
||||
hide();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void addSetup(){
|
||||
slots.row();
|
||||
slots.addImageTextButton("$save.new", Icon.add, () ->
|
||||
ui.showTextInput("$save", "$save.newslot", 30, "", text -> {
|
||||
ui.loadAnd("$saving", () -> {
|
||||
control.saves.addSave(text);
|
||||
Core.app.post(() -> Core.app.post(this::setup));
|
||||
});
|
||||
})
|
||||
).fillX().margin(10f).minWidth(300f).height(70f).pad(4f).padRight(-4);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void modifyButton(TextButton button, SaveSlot slot){
|
||||
button.clicked(() -> {
|
||||
if(button.childrenPressed()) return;
|
||||
|
||||
ui.showConfirm("$overwrite", "$save.overwrite", () -> save(slot));
|
||||
});
|
||||
}
|
||||
|
||||
void save(SaveSlot slot){
|
||||
|
||||
ui.loadfrag.show("$saveload");
|
||||
|
||||
Time.runTask(5f, () -> {
|
||||
hide();
|
||||
ui.loadfrag.hide();
|
||||
try{
|
||||
slot.save();
|
||||
}catch(Throwable e){
|
||||
e.printStackTrace();
|
||||
|
||||
ui.showException("[accent]" + Core.bundle.get("savefail"), e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
325
core/src/mindustry/ui/dialogs/SchematicsDialog.java
Normal file
325
core/src/mindustry/ui/dialogs/SchematicsDialog.java
Normal file
@@ -0,0 +1,325 @@
|
||||
package mindustry.ui.dialogs;
|
||||
|
||||
import arc.*;
|
||||
import arc.struct.*;
|
||||
import arc.graphics.*;
|
||||
import arc.graphics.Texture.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.scene.style.*;
|
||||
import arc.scene.ui.*;
|
||||
import arc.scene.ui.ImageButton.*;
|
||||
import arc.scene.ui.TextButton.*;
|
||||
import arc.scene.ui.layout.*;
|
||||
import arc.util.*;
|
||||
import mindustry.core.GameState.*;
|
||||
import mindustry.game.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.ui.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class SchematicsDialog extends FloatingDialog{
|
||||
private SchematicInfoDialog info = new SchematicInfoDialog();
|
||||
private String search = "";
|
||||
|
||||
public SchematicsDialog(){
|
||||
super("$schematics");
|
||||
Core.assets.load("sprites/schematic-background.png", Texture.class).loaded = t -> {
|
||||
((Texture)t).setWrap(TextureWrap.Repeat);
|
||||
};
|
||||
|
||||
shouldPause = true;
|
||||
addCloseButton();
|
||||
buttons.addImageTextButton("$schematic.import", Icon.loadMapSmall, this::showImport);
|
||||
shown(this::setup);
|
||||
onResize(this::setup);
|
||||
}
|
||||
|
||||
void setup(){
|
||||
search = "";
|
||||
Runnable[] rebuildPane = {null};
|
||||
|
||||
cont.top();
|
||||
cont.clear();
|
||||
|
||||
cont.table(s -> {
|
||||
s.left();
|
||||
s.addImage(Icon.zoom);
|
||||
s.addField(search, res -> {
|
||||
search = res;
|
||||
rebuildPane[0].run();
|
||||
}).growX();
|
||||
}).fillX().padBottom(4);
|
||||
|
||||
cont.row();
|
||||
|
||||
cont.pane(t -> {
|
||||
t.top();
|
||||
t.margin(20f);
|
||||
rebuildPane[0] = () -> {
|
||||
t.clear();
|
||||
int i = 0;
|
||||
|
||||
if(!schematics.all().contains(s -> search.isEmpty() || s.name().toLowerCase().contains(search.toLowerCase()))){
|
||||
t.add("$none");
|
||||
}
|
||||
|
||||
for(Schematic s : schematics.all()){
|
||||
if(!search.isEmpty() && !s.name().toLowerCase().contains(search.toLowerCase())) continue;
|
||||
|
||||
Button[] sel = {null};
|
||||
sel[0] = t.addButton(b -> {
|
||||
b.top();
|
||||
b.margin(0f);
|
||||
b.table(buttons -> {
|
||||
buttons.left();
|
||||
buttons.defaults().size(50f);
|
||||
|
||||
ImageButtonStyle style = Styles.clearPartiali;
|
||||
|
||||
buttons.addImageButton(Icon.infoSmall, style, () -> {
|
||||
showInfo(s);
|
||||
});
|
||||
|
||||
buttons.addImageButton(Icon.loadMapSmall, style, () -> {
|
||||
showExport(s);
|
||||
});
|
||||
|
||||
buttons.addImageButton(Icon.pencilSmall, style, () -> {
|
||||
ui.showTextInput("$schematic.rename", "$name", s.name(), res -> {
|
||||
s.tags.put("name", res);
|
||||
s.save();
|
||||
rebuildPane[0].run();
|
||||
});
|
||||
});
|
||||
|
||||
if(s.hasSteamID()){
|
||||
buttons.addImageButton(Icon.linkSmall, style, () -> platform.viewListing(s));
|
||||
}else{
|
||||
buttons.addImageButton(Icon.trash16Small, style, () -> {
|
||||
if(s.mod != null){
|
||||
ui.showInfo(Core.bundle.format("mod.item.remove", s.mod.meta.displayName()));
|
||||
}else{
|
||||
ui.showConfirm("$confirm", "$schematic.delete.confirm", () -> {
|
||||
schematics.remove(s);
|
||||
rebuildPane[0].run();
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}).growX().height(50f);
|
||||
b.row();
|
||||
b.stack(new SchematicImage(s).setScaling(Scaling.fit), new Table(n -> {
|
||||
n.top();
|
||||
n.table(Styles.black3, c -> {
|
||||
Label label = c.add(s.name()).style(Styles.outlineLabel).color(Color.white).top().growX().maxWidth(200f - 8f).get();
|
||||
label.setEllipsis(true);
|
||||
label.setAlignment(Align.center);
|
||||
}).growX().margin(1).pad(4).maxWidth(Scl.scl(200f - 8f)).padBottom(0);
|
||||
})).size(200f);
|
||||
}, () -> {
|
||||
if(sel[0].childrenPressed()) return;
|
||||
if(state.is(State.menu)){
|
||||
showInfo(s);
|
||||
}else{
|
||||
control.input.useSchematic(s);
|
||||
hide();
|
||||
}
|
||||
}).pad(4).style(Styles.cleari).get();
|
||||
|
||||
sel[0].getStyle().up = Tex.pane;
|
||||
|
||||
if(++i % (mobile ? Core.graphics.isPortrait() ? 2 : 3 : 4) == 0){
|
||||
t.row();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
rebuildPane[0].run();
|
||||
}).get().setScrollingDisabled(true, false);
|
||||
}
|
||||
|
||||
public void showInfo(Schematic schematic){
|
||||
info.show(schematic);
|
||||
}
|
||||
|
||||
public void showImport(){
|
||||
FloatingDialog dialog = new FloatingDialog("$editor.export");
|
||||
dialog.cont.pane(p -> {
|
||||
p.margin(10f);
|
||||
p.table(Tex.button, t -> {
|
||||
TextButtonStyle style = Styles.cleart;
|
||||
t.defaults().size(280f, 60f).left();
|
||||
t.row();
|
||||
t.addImageTextButton("$schematic.copy.import", Icon.copySmall, style, () -> {
|
||||
dialog.hide();
|
||||
try{
|
||||
Schematic s = Schematics.readBase64(Core.app.getClipboardText());
|
||||
s.removeSteamID();
|
||||
schematics.add(s);
|
||||
setup();
|
||||
ui.showInfoFade("$schematic.saved");
|
||||
showInfo(s);
|
||||
}catch(Exception e){
|
||||
ui.showException(e);
|
||||
}
|
||||
}).marginLeft(12f).disabled(b -> Core.app.getClipboardText() == null || !Core.app.getClipboardText().startsWith(schematicBaseStart));
|
||||
t.row();
|
||||
t.addImageTextButton("$schematic.importfile", Icon.saveMapSmall, style, () -> platform.showFileChooser(true, schematicExtension, file -> {
|
||||
dialog.hide();
|
||||
|
||||
try{
|
||||
Schematic s = Schematics.read(file);
|
||||
s.removeSteamID();
|
||||
schematics.add(s);
|
||||
setup();
|
||||
showInfo(s);
|
||||
}catch(Exception e){
|
||||
ui.showException(e);
|
||||
}
|
||||
})).marginLeft(12f);
|
||||
t.row();
|
||||
if(steam){
|
||||
t.addImageTextButton("$schematic.browseworkshop", Icon.wikiSmall, style, () -> {
|
||||
dialog.hide();
|
||||
platform.openWorkshop();
|
||||
}).marginLeft(12f);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
dialog.addCloseButton();
|
||||
dialog.show();
|
||||
}
|
||||
|
||||
public void showExport(Schematic s){
|
||||
FloatingDialog dialog = new FloatingDialog("$editor.export");
|
||||
dialog.cont.pane(p -> {
|
||||
p.margin(10f);
|
||||
p.table(Tex.button, t -> {
|
||||
TextButtonStyle style = Styles.cleart;
|
||||
t.defaults().size(280f, 60f).left();
|
||||
if(steam && !s.hasSteamID()){
|
||||
t.addImageTextButton("$schematic.shareworkshop", Icon.wikiSmall, style,
|
||||
() -> platform.publish(s)).marginLeft(12f);
|
||||
t.row();
|
||||
dialog.hide();
|
||||
}
|
||||
t.addImageTextButton("$schematic.copy", Icon.copySmall, style, () -> {
|
||||
dialog.hide();
|
||||
ui.showInfoFade("$copied");
|
||||
Core.app.setClipboardText(schematics.writeBase64(s));
|
||||
}).marginLeft(12f);
|
||||
t.row();
|
||||
t.addImageTextButton("$schematic.exportfile", Icon.saveMapSmall, style, () -> platform.showFileChooser(false, schematicExtension, file -> {
|
||||
dialog.hide();
|
||||
try{
|
||||
Schematics.write(s, file);
|
||||
}catch(Exception e){
|
||||
ui.showException(e);
|
||||
}
|
||||
})).marginLeft(12f);
|
||||
});
|
||||
});
|
||||
|
||||
dialog.addCloseButton();
|
||||
dialog.show();
|
||||
}
|
||||
|
||||
public static class SchematicImage extends Image{
|
||||
public float scaling = 16f;
|
||||
public float thickness = 4f;
|
||||
public Color borderColor = Pal.gray;
|
||||
|
||||
private Schematic schematic;
|
||||
boolean set;
|
||||
|
||||
public SchematicImage(Schematic s){
|
||||
super(Tex.clear);
|
||||
setScaling(Scaling.fit);
|
||||
schematic = s;
|
||||
|
||||
if(schematics.hasPreview(s)){
|
||||
setPreview();
|
||||
set = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(){
|
||||
boolean checked = getParent().getParent() instanceof Button
|
||||
&& ((Button)getParent().getParent()).isOver();
|
||||
|
||||
boolean wasSet = set;
|
||||
if(!set){
|
||||
Core.app.post(this::setPreview);
|
||||
set = true;
|
||||
}
|
||||
|
||||
Texture background = Core.assets.get("sprites/schematic-background.png", Texture.class);
|
||||
TextureRegion region = Draw.wrap(background);
|
||||
float xr = width / scaling;
|
||||
float yr = height / scaling;
|
||||
region.setU2(xr);
|
||||
region.setV2(yr);
|
||||
Draw.color();
|
||||
Draw.alpha(parentAlpha);
|
||||
Draw.rect(region, x + width/2f, y + height/2f, width, height);
|
||||
|
||||
if(wasSet){
|
||||
super.draw();
|
||||
}else{
|
||||
Draw.rect(Icon.loading.getRegion(), x + width/2f, y + height/2f, width/4f, height/4f);
|
||||
}
|
||||
|
||||
Draw.color(checked ? Pal.accent : borderColor);
|
||||
Draw.alpha(parentAlpha);
|
||||
Lines.stroke(Scl.scl(thickness));
|
||||
Lines.rect(x, y, width, height);
|
||||
Draw.reset();
|
||||
}
|
||||
|
||||
private void setPreview(){
|
||||
TextureRegionDrawable draw = new TextureRegionDrawable(new TextureRegion(schematics.getPreview(schematic)));
|
||||
setDrawable(draw);
|
||||
setScaling(Scaling.fit);
|
||||
}
|
||||
}
|
||||
|
||||
public static class SchematicInfoDialog extends FloatingDialog{
|
||||
|
||||
SchematicInfoDialog(){
|
||||
super("");
|
||||
setFillParent(true);
|
||||
addCloseButton();
|
||||
}
|
||||
|
||||
public void show(Schematic schem){
|
||||
cont.clear();
|
||||
title.setText("[[" + Core.bundle.get("schematic") + "] " +schem.name());
|
||||
|
||||
cont.add(Core.bundle.format("schematic.info", schem.width, schem.height, schem.tiles.size)).color(Color.lightGray);
|
||||
cont.row();
|
||||
cont.add(new SchematicImage(schem)).maxSize(800f);
|
||||
cont.row();
|
||||
|
||||
Array<ItemStack> arr = schem.requirements();
|
||||
cont.table(r -> {
|
||||
int i = 0;
|
||||
for(ItemStack s : arr){
|
||||
r.addImage(s.item.icon(Cicon.small)).left();
|
||||
r.add(s.amount + "").padLeft(2).left().color(Color.lightGray).padRight(4);
|
||||
|
||||
if(++i % 4 == 0){
|
||||
r.row();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
show();
|
||||
}
|
||||
}
|
||||
}
|
||||
376
core/src/mindustry/ui/dialogs/SettingsMenuDialog.java
Normal file
376
core/src/mindustry/ui/dialogs/SettingsMenuDialog.java
Normal file
@@ -0,0 +1,376 @@
|
||||
package mindustry.ui.dialogs;
|
||||
|
||||
import arc.*;
|
||||
import arc.struct.*;
|
||||
import arc.files.*;
|
||||
import arc.graphics.*;
|
||||
import arc.graphics.Texture.*;
|
||||
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.util.*;
|
||||
import mindustry.core.GameState.*;
|
||||
import mindustry.core.*;
|
||||
import mindustry.game.EventType.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.input.*;
|
||||
import mindustry.ui.*;
|
||||
|
||||
import static arc.Core.bundle;
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class SettingsMenuDialog extends SettingsDialog{
|
||||
private SettingsTable graphics;
|
||||
private SettingsTable game;
|
||||
private SettingsTable sound;
|
||||
|
||||
private Table prefs;
|
||||
private Table menu;
|
||||
private FloatingDialog dataDialog;
|
||||
private boolean wasPaused;
|
||||
|
||||
public SettingsMenuDialog(){
|
||||
hidden(() -> {
|
||||
Sounds.back.play();
|
||||
if(!state.is(State.menu)){
|
||||
if(!wasPaused || net.active())
|
||||
state.set(State.playing);
|
||||
}
|
||||
});
|
||||
|
||||
shown(() -> {
|
||||
back();
|
||||
if(!state.is(State.menu)){
|
||||
wasPaused = state.is(State.paused);
|
||||
state.set(State.paused);
|
||||
}
|
||||
|
||||
rebuildMenu();
|
||||
});
|
||||
|
||||
setFillParent(true);
|
||||
title.setAlignment(Align.center);
|
||||
titleTable.row();
|
||||
titleTable.add(new Image()).growX().height(3f).pad(4f).get().setColor(Pal.accent);
|
||||
|
||||
cont.clearChildren();
|
||||
cont.remove();
|
||||
buttons.remove();
|
||||
|
||||
menu = new Table(Tex.button);
|
||||
|
||||
game = new SettingsTable();
|
||||
graphics = new SettingsTable();
|
||||
sound = new SettingsTable();
|
||||
|
||||
prefs = new Table();
|
||||
prefs.top();
|
||||
prefs.margin(14f);
|
||||
|
||||
rebuildMenu();
|
||||
|
||||
prefs.clearChildren();
|
||||
prefs.add(menu);
|
||||
|
||||
dataDialog = new FloatingDialog("$settings.data");
|
||||
dataDialog.addCloseButton();
|
||||
|
||||
dataDialog.cont.table(Tex.button, t -> {
|
||||
t.defaults().size(240f, 60f).left();
|
||||
TextButtonStyle style = Styles.cleart;
|
||||
|
||||
t.addButton("$settings.cleardata", style, () -> ui.showConfirm("$confirm", "$settings.clearall.confirm", () -> {
|
||||
ObjectMap<String, Object> map = new ObjectMap<>();
|
||||
for(String value : Core.settings.keys()){
|
||||
if(value.contains("usid") || value.contains("uuid")){
|
||||
map.put(value, Core.settings.getString(value));
|
||||
}
|
||||
}
|
||||
Core.settings.clear();
|
||||
Core.settings.putAll(map);
|
||||
Core.settings.save();
|
||||
|
||||
for(Fi file : dataDirectory.list()){
|
||||
file.deleteDirectory();
|
||||
}
|
||||
|
||||
Core.app.exit();
|
||||
}));
|
||||
|
||||
t.row();
|
||||
|
||||
t.addButton("$data.export", style, () -> {
|
||||
if(ios){
|
||||
Fi file = Core.files.local("mindustry-data-export.zip");
|
||||
try{
|
||||
data.exportData(file);
|
||||
}catch(Exception e){
|
||||
ui.showException(e);
|
||||
}
|
||||
platform.shareFile(file);
|
||||
}else{
|
||||
platform.showFileChooser(false, "zip", file -> {
|
||||
try{
|
||||
data.exportData(file);
|
||||
ui.showInfo("$data.exported");
|
||||
}catch(Exception e){
|
||||
e.printStackTrace();
|
||||
ui.showException(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
t.row();
|
||||
|
||||
t.addButton("$data.import", style, () -> ui.showConfirm("$confirm", "$data.import.confirm", () -> platform.showFileChooser(true, "zip", file -> {
|
||||
try{
|
||||
data.importData(file);
|
||||
Core.app.exit();
|
||||
}catch(IllegalArgumentException e){
|
||||
ui.showErrorMessage("$data.invalid");
|
||||
}catch(Exception e){
|
||||
e.printStackTrace();
|
||||
if(e.getMessage() == null || !e.getMessage().contains("too short")){
|
||||
ui.showException(e);
|
||||
}else{
|
||||
ui.showErrorMessage("$data.invalid");
|
||||
}
|
||||
}
|
||||
})));
|
||||
});
|
||||
|
||||
ScrollPane pane = new ScrollPane(prefs);
|
||||
pane.addCaptureListener(new InputListener(){
|
||||
@Override
|
||||
public boolean touchDown(InputEvent event, float x, float y, int pointer, KeyCode button){
|
||||
Element actor = pane.hit(x, y, true);
|
||||
if(actor instanceof Slider){
|
||||
pane.setFlickScroll(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
return super.touchDown(event, x, y, pointer, button);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void touchUp(InputEvent event, float x, float y, int pointer, KeyCode button){
|
||||
pane.setFlickScroll(true);
|
||||
super.touchUp(event, x, y, pointer, button);
|
||||
}
|
||||
});
|
||||
pane.setFadeScrollBars(false);
|
||||
|
||||
row();
|
||||
add(pane).grow().top();
|
||||
row();
|
||||
add(buttons).fillX();
|
||||
|
||||
addSettings();
|
||||
}
|
||||
|
||||
void rebuildMenu(){
|
||||
menu.clearChildren();
|
||||
|
||||
TextButtonStyle style = Styles.cleart;
|
||||
|
||||
menu.defaults().size(300f, 60f);
|
||||
menu.addButton("$settings.game", style, () -> visible(0));
|
||||
menu.row();
|
||||
menu.addButton("$settings.graphics", style, () -> visible(1));
|
||||
menu.row();
|
||||
menu.addButton("$settings.sound", style, () -> visible(2));
|
||||
menu.row();
|
||||
menu.addButton("$settings.language", style, ui.language::show);
|
||||
if(!mobile || Core.settings.getBool("keyboard")){
|
||||
menu.row();
|
||||
menu.addButton("$settings.controls", style, ui.controls::show);
|
||||
}
|
||||
|
||||
menu.row();
|
||||
menu.addButton("$settings.data", style, () -> dataDialog.show());
|
||||
}
|
||||
|
||||
void addSettings(){
|
||||
sound.sliderPref("musicvol", bundle.get("setting.musicvol.name", "Music Volume"), 100, 0, 100, 1, i -> i + "%");
|
||||
sound.sliderPref("sfxvol", bundle.get("setting.sfxvol.name", "SFX Volume"), 100, 0, 100, 1, i -> i + "%");
|
||||
sound.sliderPref("ambientvol", bundle.get("setting.ambientvol.name", "Ambient Volume"), 100, 0, 100, 1, i -> i + "%");
|
||||
|
||||
game.screenshakePref();
|
||||
if(mobile){
|
||||
game.checkPref("autotarget", true);
|
||||
game.checkPref("keyboard", false, val -> control.setInput(val ? new DesktopInput() : new MobileInput()));
|
||||
if(Core.settings.getBool("keyboard")){
|
||||
control.setInput(new DesktopInput());
|
||||
}
|
||||
}
|
||||
//the issue with touchscreen support on desktop is that:
|
||||
//1) I can't test it
|
||||
//2) the SDL backend doesn't support multitouch
|
||||
/*else{
|
||||
game.checkPref("touchscreen", false, val -> control.setInput(!val ? new DesktopInput() : new MobileInput()));
|
||||
if(Core.settings.getBool("touchscreen")){
|
||||
control.setInput(new MobileInput());
|
||||
}
|
||||
}*/
|
||||
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);
|
||||
}
|
||||
|
||||
game.checkPref("savecreate", true);
|
||||
game.checkPref("blockreplace", true);
|
||||
game.checkPref("conveyorpathfinding", true);
|
||||
game.checkPref("hints", true);
|
||||
if(!mobile){
|
||||
game.checkPref("buildautopause", false);
|
||||
}
|
||||
|
||||
if(steam && !Version.modifier.contains("beta")){
|
||||
game.checkPref("publichost", false, i -> {
|
||||
platform.updateLobby();
|
||||
});
|
||||
}
|
||||
|
||||
game.pref(new Setting(){
|
||||
@Override
|
||||
public void add(SettingsTable table){
|
||||
table.addButton("$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);
|
||||
}
|
||||
return s + "%";
|
||||
});
|
||||
graphics.sliderPref("fpscap", 240, 15, 245, 5, s -> (s > 240 ? Core.bundle.get("setting.fpscap.none") : Core.bundle.format("setting.fpscap.text", s)));
|
||||
graphics.sliderPref("chatopacity", 100, 0, 100, 5, s -> s + "%");
|
||||
graphics.sliderPref("lasersopacity", 100, 0, 100, 5, s -> {
|
||||
if(ui.settings != null){
|
||||
Core.settings.put("preferredlaseropacity", s);
|
||||
}
|
||||
return s + "%";
|
||||
});
|
||||
|
||||
if(!mobile){
|
||||
graphics.checkPref("vsync", true, b -> Core.graphics.setVSync(b));
|
||||
graphics.checkPref("fullscreen", false, b -> {
|
||||
if(b){
|
||||
Core.graphics.setFullscreenMode(Core.graphics.getDisplayMode());
|
||||
}else{
|
||||
Core.graphics.setWindowedMode(Core.graphics.getWidth(), Core.graphics.getHeight());
|
||||
}
|
||||
});
|
||||
|
||||
graphics.checkPref("borderlesswindow", false, b -> Core.graphics.setUndecorated(b));
|
||||
|
||||
Core.graphics.setVSync(Core.settings.getBool("vsync"));
|
||||
if(Core.settings.getBool("fullscreen")){
|
||||
Core.app.post(() -> Core.graphics.setFullscreenMode(Core.graphics.getDisplayMode()));
|
||||
}
|
||||
|
||||
if(Core.settings.getBool("borderlesswindow")){
|
||||
Core.app.post(() -> Core.graphics.setUndecorated(true));
|
||||
}
|
||||
}else{
|
||||
graphics.checkPref("landscape", false, b -> {
|
||||
if(b){
|
||||
platform.beginForceLandscape();
|
||||
}else{
|
||||
platform.endForceLandscape();
|
||||
}
|
||||
});
|
||||
|
||||
if(Core.settings.getBool("landscape")){
|
||||
platform.beginForceLandscape();
|
||||
}
|
||||
}
|
||||
|
||||
graphics.checkPref("effects", true);
|
||||
graphics.checkPref("destroyedblocks", true);
|
||||
graphics.checkPref("playerchat", true);
|
||||
graphics.checkPref("minimap", !mobile);
|
||||
graphics.checkPref("position", false);
|
||||
graphics.checkPref("fps", false);
|
||||
if(!mobile){
|
||||
graphics.checkPref("blockselectkeys", true);
|
||||
}
|
||||
graphics.checkPref("indicators", true);
|
||||
graphics.checkPref("animatedwater", !mobile);
|
||||
if(Shaders.shield != null){
|
||||
graphics.checkPref("animatedshields", !mobile);
|
||||
}
|
||||
graphics.checkPref("bloom", !mobile, val -> renderer.toggleBloom(val));
|
||||
graphics.checkPref("pixelate", false, val -> {
|
||||
if(val){
|
||||
Events.fire(Trigger.enablePixelation);
|
||||
}
|
||||
});
|
||||
|
||||
graphics.checkPref("linear", !mobile, b -> {
|
||||
for(Texture tex : Core.atlas.getTextures()){
|
||||
TextureFilter filter = b ? TextureFilter.Linear : TextureFilter.Nearest;
|
||||
tex.setFilter(filter, filter);
|
||||
}
|
||||
});
|
||||
|
||||
if(Core.settings.getBool("linear")){
|
||||
for(Texture tex : Core.atlas.getTextures()){
|
||||
TextureFilter filter = TextureFilter.Linear;
|
||||
tex.setFilter(filter, filter);
|
||||
}
|
||||
}
|
||||
|
||||
if(!mobile){
|
||||
Core.settings.put("swapdiagonal", false);
|
||||
}
|
||||
}
|
||||
|
||||
private void back(){
|
||||
rebuildMenu();
|
||||
prefs.clearChildren();
|
||||
prefs.add(menu);
|
||||
}
|
||||
|
||||
private void visible(int index){
|
||||
prefs.clearChildren();
|
||||
prefs.add(new Table[]{game, graphics, sound}[index]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addCloseButton(){
|
||||
buttons.addImageTextButton("$back", Icon.arrowLeftSmaller, () -> {
|
||||
if(prefs.getChildren().first() != menu){
|
||||
back();
|
||||
}else{
|
||||
hide();
|
||||
}
|
||||
}).size(230f, 64f);
|
||||
|
||||
keyDown(key -> {
|
||||
if(key == KeyCode.ESCAPE || key == KeyCode.BACK){
|
||||
if(prefs.getChildren().first() != menu){
|
||||
back();
|
||||
}else{
|
||||
hide();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
389
core/src/mindustry/ui/dialogs/TechTreeDialog.java
Normal file
389
core/src/mindustry/ui/dialogs/TechTreeDialog.java
Normal file
@@ -0,0 +1,389 @@
|
||||
package mindustry.ui.dialogs;
|
||||
|
||||
import arc.*;
|
||||
import arc.struct.*;
|
||||
import arc.graphics.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.input.*;
|
||||
import arc.math.*;
|
||||
import arc.math.geom.*;
|
||||
import arc.scene.*;
|
||||
import arc.scene.actions.*;
|
||||
import arc.scene.event.*;
|
||||
import arc.scene.style.*;
|
||||
import arc.scene.ui.*;
|
||||
import arc.scene.ui.layout.*;
|
||||
import arc.util.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.content.TechTree.*;
|
||||
import mindustry.game.EventType.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.ui.*;
|
||||
import mindustry.ui.Cicon;
|
||||
import mindustry.ui.layout.*;
|
||||
import mindustry.ui.layout.TreeLayout.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class TechTreeDialog extends FloatingDialog{
|
||||
private final float nodeSize = Scl.scl(60f);
|
||||
private ObjectSet<TechTreeNode> nodes = new ObjectSet<>();
|
||||
private TechTreeNode root = new TechTreeNode(TechTree.root, null);
|
||||
private Rectangle bounds = new Rectangle();
|
||||
private ItemsDisplay items;
|
||||
private View view;
|
||||
|
||||
public TechTreeDialog(){
|
||||
super("");
|
||||
|
||||
titleTable.remove();
|
||||
margin(0f).marginBottom(8);
|
||||
Stack stack = cont.stack(view = new View(), items = new ItemsDisplay()).grow().get();
|
||||
|
||||
Events.on(ContentReloadEvent.class, e -> {
|
||||
nodes.clear();
|
||||
root = new TechTreeNode(TechTree.root, null);
|
||||
checkNodes(root);
|
||||
treeLayout();
|
||||
stack.getChildren().get(0).remove();
|
||||
stack.addChildAt(0, view = new View());
|
||||
});
|
||||
|
||||
shown(() -> {
|
||||
checkNodes(root);
|
||||
treeLayout();
|
||||
});
|
||||
|
||||
hidden(ui.deploy::setup);
|
||||
|
||||
addCloseButton();
|
||||
|
||||
buttons.addImageTextButton("$database", Icon.database, () -> {
|
||||
hide();
|
||||
ui.database.show();
|
||||
}).size(210f, 64f);
|
||||
|
||||
//scaling/drag input
|
||||
addListener(new InputListener(){
|
||||
@Override
|
||||
public boolean scrolled(InputEvent event, float x, float y, float amountX, float amountY){
|
||||
view.setScale(Mathf.clamp(view.getScaleX() - amountY / 40f, 0.25f, 1f));
|
||||
view.setOrigin(Align.center);
|
||||
view.setTransform(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mouseMoved(InputEvent event, float x, float y){
|
||||
view.requestScroll();
|
||||
return super.mouseMoved(event, x, y);
|
||||
}
|
||||
});
|
||||
|
||||
addListener(new ElementGestureListener(){
|
||||
@Override
|
||||
public void zoom(InputEvent event, float initialDistance, float distance){
|
||||
if(view.lastZoom < 0){
|
||||
view.lastZoom = view.getScaleX();
|
||||
}
|
||||
|
||||
view.setScale(Mathf.clamp(distance / initialDistance * view.lastZoom, 0.25f, 1f));
|
||||
view.setOrigin(Align.center);
|
||||
view.setTransform(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void touchUp(InputEvent event, float x, float y, int pointer, KeyCode button){
|
||||
view.lastZoom = view.getScaleX();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pan(InputEvent event, float x, float y, float deltaX, float deltaY){
|
||||
view.panX += deltaX / view.getScaleX();
|
||||
view.panY += deltaY / view.getScaleY();
|
||||
view.moved = true;
|
||||
view.clamp();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void treeLayout(){
|
||||
RadialTreeLayout layout = new RadialTreeLayout();
|
||||
LayoutNode node = new LayoutNode(root, null);
|
||||
layout.layout(node);
|
||||
float minx = 0f, miny = 0f, maxx = 0f, maxy = 0f;
|
||||
copyInfo(node);
|
||||
|
||||
for(TechTreeNode n : nodes){
|
||||
if(!n.visible) continue;
|
||||
minx = Math.min(n.x - n.width/2f, minx);
|
||||
maxx = Math.max(n.x + n.width/2f, maxx);
|
||||
miny = Math.min(n.y - n.height/2f, miny);
|
||||
maxy = Math.max(n.y + n.height/2f, maxy);
|
||||
}
|
||||
bounds = new Rectangle(minx, miny, maxx - minx, maxy - miny);
|
||||
bounds.y += nodeSize*1.5f;
|
||||
}
|
||||
|
||||
void copyInfo(LayoutNode node){
|
||||
node.node.x = node.x;
|
||||
node.node.y = node.y;
|
||||
if(node.children != null){
|
||||
for(LayoutNode child : node.children){
|
||||
copyInfo(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void checkNodes(TechTreeNode node){
|
||||
boolean locked = locked(node.node);
|
||||
if(!locked) node.visible = true;
|
||||
for(TechTreeNode l : node.children){
|
||||
l.visible = !locked;
|
||||
checkNodes(l);
|
||||
}
|
||||
|
||||
items.rebuild();
|
||||
}
|
||||
|
||||
void showToast(String info){
|
||||
Table table = new Table();
|
||||
table.actions(Actions.fadeOut(0.5f, Interpolation.fade), Actions.remove());
|
||||
table.top().add(info);
|
||||
table.setName("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.block.locked();
|
||||
}
|
||||
|
||||
class LayoutNode extends TreeNode<LayoutNode>{
|
||||
final TechTreeNode node;
|
||||
|
||||
LayoutNode(TechTreeNode node, LayoutNode parent){
|
||||
this.node = node;
|
||||
this.parent = parent;
|
||||
this.width = this.height = nodeSize;
|
||||
if(node.children != null){
|
||||
children = Array.with(node.children).map(t -> new LayoutNode(t, this)).toArray(LayoutNode.class);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class TechTreeNode extends TreeNode<TechTreeNode>{
|
||||
final TechNode node;
|
||||
boolean visible = true;
|
||||
|
||||
TechTreeNode(TechNode node, TechTreeNode parent){
|
||||
this.node = node;
|
||||
this.parent = parent;
|
||||
this.width = this.height = nodeSize;
|
||||
nodes.add(this);
|
||||
if(node.children != null){
|
||||
children = new TechTreeNode[node.children.size];
|
||||
for(int i = 0; i < children.length; i++){
|
||||
children[i] = new TechTreeNode(node.children.get(i), this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class View extends Group{
|
||||
float panX = 0, panY = -200, lastZoom = -1;
|
||||
boolean moved = false;
|
||||
ImageButton hoverNode;
|
||||
Table infoTable = new Table();
|
||||
|
||||
{
|
||||
infoTable.touchable(Touchable.enabled);
|
||||
|
||||
for(TechTreeNode node : nodes){
|
||||
ImageButton button = new ImageButton(node.node.block.icon(Cicon.medium), Styles.nodei);
|
||||
button.visible(() -> node.visible);
|
||||
button.clicked(() -> {
|
||||
if(moved) return;
|
||||
|
||||
if(mobile){
|
||||
hoverNode = button;
|
||||
rebuild();
|
||||
float right = infoTable.getRight();
|
||||
if(right > Core.graphics.getWidth()){
|
||||
float moveBy = right - Core.graphics.getWidth();
|
||||
addAction(new RelativeTemporalAction(){
|
||||
{
|
||||
setDuration(0.1f);
|
||||
setInterpolation(Interpolation.fade);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateRelative(float percentDelta){
|
||||
panX -= moveBy * percentDelta;
|
||||
}
|
||||
});
|
||||
}
|
||||
}else if(data.hasItems(node.node.requirements) && locked(node.node)){
|
||||
unlock(node.node);
|
||||
}
|
||||
});
|
||||
button.hovered(() -> {
|
||||
if(!mobile && hoverNode != button && node.visible){
|
||||
hoverNode = button;
|
||||
rebuild();
|
||||
}
|
||||
});
|
||||
button.exited(() -> {
|
||||
if(!mobile && hoverNode == button && !infoTable.hasMouse() && !hoverNode.hasMouse()){
|
||||
hoverNode = null;
|
||||
rebuild();
|
||||
}
|
||||
});
|
||||
button.touchable(() -> !node.visible ? Touchable.disabled : Touchable.enabled);
|
||||
button.setUserObject(node.node);
|
||||
button.setSize(nodeSize);
|
||||
button.update(() -> {
|
||||
float offset = (Core.graphics.getHeight() % 2) / 2f;
|
||||
button.setPosition(node.x + panX + width / 2f, node.y + panY + height / 2f + offset, Align.center);
|
||||
button.getStyle().up = !locked(node.node) ? Tex.buttonOver : !data.hasItems(node.node.requirements) ? Tex.buttonRed : Tex.button;
|
||||
((TextureRegionDrawable)button.getStyle().imageUp)
|
||||
.setRegion(node.visible ? node.node.block.icon(Cicon.medium) : Core.atlas.find("icon-locked"));
|
||||
button.getImage().setColor(!locked(node.node) ? Color.white : Color.gray);
|
||||
});
|
||||
addChild(button);
|
||||
}
|
||||
|
||||
if(mobile){
|
||||
tapped(() -> {
|
||||
Element e = Core.scene.hit(Core.input.mouseX(), Core.input.mouseY(), true);
|
||||
if(e == this){
|
||||
hoverNode = null;
|
||||
rebuild();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
setOrigin(Align.center);
|
||||
setTransform(true);
|
||||
released(() -> moved = false);
|
||||
}
|
||||
|
||||
void clamp(){
|
||||
float pad = nodeSize;
|
||||
|
||||
float ox = width/2f, oy = height/2f;
|
||||
float rx = bounds.x + panX + ox, ry = panY + oy + bounds.y;
|
||||
float rw = bounds.width, rh = bounds.height;
|
||||
rx = Mathf.clamp(rx, -rw + pad, Core.graphics.getWidth() - pad);
|
||||
ry = Mathf.clamp(ry, -rh + pad, Core.graphics.getHeight() - pad);
|
||||
panX = rx - bounds.x - ox;
|
||||
panY = ry - bounds.y - oy;
|
||||
}
|
||||
|
||||
void unlock(TechNode node){
|
||||
data.unlockContent(node.block);
|
||||
data.removeItems(node.requirements);
|
||||
showToast(Core.bundle.format("researched", node.block.localizedName));
|
||||
checkNodes(root);
|
||||
hoverNode = null;
|
||||
treeLayout();
|
||||
rebuild();
|
||||
Core.scene.act();
|
||||
Sounds.unlock.play();
|
||||
Events.fire(new ResearchEvent(node.block));
|
||||
}
|
||||
|
||||
void rebuild(){
|
||||
ImageButton button = hoverNode;
|
||||
|
||||
infoTable.remove();
|
||||
infoTable.clear();
|
||||
infoTable.update(null);
|
||||
|
||||
if(button == null) return;
|
||||
|
||||
TechNode node = (TechNode)button.getUserObject();
|
||||
|
||||
infoTable.exited(() -> {
|
||||
if(hoverNode == button && !infoTable.hasMouse() && !hoverNode.hasMouse()){
|
||||
hoverNode = null;
|
||||
rebuild();
|
||||
}
|
||||
});
|
||||
|
||||
infoTable.update(() -> infoTable.setPosition(button.getX() + button.getWidth(), button.getY() + button.getHeight(), Align.topLeft));
|
||||
|
||||
infoTable.left();
|
||||
infoTable.background(Tex.button).margin(8f);
|
||||
|
||||
infoTable.table(b -> {
|
||||
b.margin(0).left().defaults().left();
|
||||
|
||||
b.addImageButton(Icon.infoSmall, Styles.cleari, () -> ui.content.show(node.block)).growY().width(50f);
|
||||
b.add().grow();
|
||||
b.table(desc -> {
|
||||
desc.left().defaults().left();
|
||||
desc.add(node.block.localizedName);
|
||||
desc.row();
|
||||
if(locked(node)){
|
||||
desc.table(t -> {
|
||||
t.left();
|
||||
for(ItemStack req : node.requirements){
|
||||
t.table(list -> {
|
||||
list.left();
|
||||
list.addImage(req.item.icon(Cicon.small)).size(8 * 3).padRight(3);
|
||||
list.add(req.item.localizedName).color(Color.lightGray);
|
||||
list.label(() -> " " + Math.min(data.getItem(req.item), req.amount) + " / " + req.amount)
|
||||
.update(l -> l.setColor(data.has(req.item, req.amount) ? Color.lightGray : Color.scarlet));
|
||||
}).fillX().left();
|
||||
t.row();
|
||||
}
|
||||
});
|
||||
}else{
|
||||
desc.add("$completed");
|
||||
}
|
||||
}).pad(9);
|
||||
|
||||
if(mobile && locked(node)){
|
||||
b.row();
|
||||
b.addImageTextButton("$research", Icon.checkSmall, Styles.nodet, () -> unlock(node))
|
||||
.disabled(i -> !data.hasItems(node.requirements)).growX().height(44f).colspan(3);
|
||||
}
|
||||
});
|
||||
|
||||
infoTable.row();
|
||||
if(node.block.description != null){
|
||||
infoTable.table(t -> t.margin(3f).left().labelWrap(node.block.description).color(Color.lightGray).growX()).fillX();
|
||||
}
|
||||
|
||||
addChild(infoTable);
|
||||
infoTable.pack();
|
||||
infoTable.act(Core.graphics.getDeltaTime());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawChildren(){
|
||||
clamp();
|
||||
float offsetX = panX + width / 2f, offsetY = panY + height / 2f;
|
||||
|
||||
for(TechTreeNode node : nodes){
|
||||
if(!node.visible) continue;
|
||||
for(TechTreeNode child : node.children){
|
||||
if(!child.visible) continue;
|
||||
|
||||
Lines.stroke(Scl.scl(4f), locked(node.node) || locked(child.node) ? Pal.gray : Pal.accent);
|
||||
Draw.alpha(parentAlpha);
|
||||
Lines.line(node.x + offsetX, node.y + offsetY, child.x + offsetX, child.y + offsetY);
|
||||
}
|
||||
}
|
||||
|
||||
Draw.reset();
|
||||
super.drawChildren();
|
||||
}
|
||||
}
|
||||
}
|
||||
44
core/src/mindustry/ui/dialogs/TraceDialog.java
Normal file
44
core/src/mindustry/ui/dialogs/TraceDialog.java
Normal file
@@ -0,0 +1,44 @@
|
||||
package mindustry.ui.dialogs;
|
||||
|
||||
import arc.Core;
|
||||
import arc.scene.ui.layout.Table;
|
||||
import mindustry.entities.type.Player;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.net.Administration.TraceInfo;
|
||||
|
||||
public class TraceDialog extends FloatingDialog{
|
||||
|
||||
public TraceDialog(){
|
||||
super("$trace");
|
||||
|
||||
addCloseButton();
|
||||
setFillParent(false);
|
||||
}
|
||||
|
||||
public void show(Player player, TraceInfo info){
|
||||
cont.clear();
|
||||
|
||||
Table table = new Table(Tex.clear);
|
||||
table.margin(14);
|
||||
table.defaults().pad(1);
|
||||
|
||||
table.defaults().left();
|
||||
table.add(Core.bundle.format("trace.playername", player.name));
|
||||
table.row();
|
||||
table.add(Core.bundle.format("trace.ip", info.ip));
|
||||
table.row();
|
||||
table.add(Core.bundle.format("trace.id", info.uuid));
|
||||
table.row();
|
||||
table.add(Core.bundle.format("trace.modclient", info.modded));
|
||||
table.row();
|
||||
table.add(Core.bundle.format("trace.mobile", info.mobile));
|
||||
table.row();
|
||||
|
||||
table.add().pad(5);
|
||||
table.row();
|
||||
|
||||
cont.add(table);
|
||||
|
||||
show();
|
||||
}
|
||||
}
|
||||
168
core/src/mindustry/ui/dialogs/ZoneInfoDialog.java
Normal file
168
core/src/mindustry/ui/dialogs/ZoneInfoDialog.java
Normal file
@@ -0,0 +1,168 @@
|
||||
package mindustry.ui.dialogs;
|
||||
|
||||
import arc.*;
|
||||
import arc.struct.*;
|
||||
import arc.graphics.*;
|
||||
import arc.scene.ui.*;
|
||||
import arc.scene.ui.layout.*;
|
||||
import mindustry.game.*;
|
||||
import mindustry.game.Objectives.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.ui.Cicon;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class ZoneInfoDialog extends FloatingDialog{
|
||||
private LoadoutDialog loadout = new LoadoutDialog();
|
||||
|
||||
public ZoneInfoDialog(){
|
||||
super("");
|
||||
|
||||
titleTable.remove();
|
||||
addCloseButton();
|
||||
}
|
||||
|
||||
public void show(Zone zone){
|
||||
setup(zone);
|
||||
show();
|
||||
}
|
||||
|
||||
private void setup(Zone zone){
|
||||
cont.clear();
|
||||
|
||||
Table iteminfo = new Table();
|
||||
Runnable rebuildItems = () -> {
|
||||
int i = 0;
|
||||
iteminfo.clear();
|
||||
|
||||
if(!zone.unlocked()) return;
|
||||
|
||||
for(ItemStack stack : zone.getLaunchCost()){
|
||||
if(stack.amount == 0) continue;
|
||||
|
||||
if(i++ % 2 == 0){
|
||||
iteminfo.row();
|
||||
}
|
||||
iteminfo.addImage(stack.item.icon(mindustry.ui.Cicon.small)).size(8 * 3).padRight(1);
|
||||
iteminfo.add(stack.amount + "").color(Color.lightGray).padRight(5);
|
||||
}
|
||||
};
|
||||
|
||||
rebuildItems.run();
|
||||
|
||||
cont.pane(cont -> {
|
||||
if(zone.locked()){
|
||||
cont.addImage(Icon.locked);
|
||||
cont.row();
|
||||
cont.add("$locked").padBottom(6);
|
||||
cont.row();
|
||||
|
||||
cont.table(req -> {
|
||||
req.defaults().left();
|
||||
|
||||
Array<Objective> zones = zone.requirements.select(o -> !(o instanceof Unlock));
|
||||
|
||||
if(!zones.isEmpty()){
|
||||
req.table(r -> {
|
||||
r.add("$complete").colspan(2).left();
|
||||
r.row();
|
||||
for(Objective o : zones){
|
||||
r.addImage(Icon.terrain).padRight(4);
|
||||
r.add(o.display()).color(Color.lightGray);
|
||||
r.addImage(o.complete() ? Icon.checkSmall : Icon.cancelSmall, o.complete() ? Color.lightGray : Color.scarlet).padLeft(3);
|
||||
r.row();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
req.row();
|
||||
Array<Unlock> blocks = zone.requirements.select(o -> o instanceof Unlock).as(Unlock.class);
|
||||
|
||||
if(!blocks.isEmpty()){
|
||||
req.table(r -> {
|
||||
r.add("$research.list").colspan(2).left();
|
||||
r.row();
|
||||
for(Unlock blocko : blocks){
|
||||
r.addImage(blocko.block.icon(mindustry.ui.Cicon.small)).size(8 * 3).padRight(5);
|
||||
r.add(blocko.block.localizedName).color(Color.lightGray).left();
|
||||
r.addImage(blocko.block.unlocked() ? Icon.checkSmall : Icon.cancelSmall, blocko.block.unlocked() ? Color.lightGray : Color.scarlet).padLeft(3);
|
||||
r.row();
|
||||
}
|
||||
|
||||
}).padTop(10);
|
||||
}
|
||||
}).growX();
|
||||
|
||||
}else{
|
||||
cont.add(zone.localizedName).color(Pal.accent).growX().center();
|
||||
cont.row();
|
||||
cont.addImage().color(Pal.accent).height(3).pad(6).growX();
|
||||
cont.row();
|
||||
cont.table(desc -> {
|
||||
desc.left().defaults().left().width(Core.graphics.isPortrait() ? 350f : 500f);
|
||||
desc.pane(t -> t.marginRight(12f).add(zone.description).wrap().growX()).fillX().maxHeight(mobile ? 300f : 450f).pad(2).padBottom(8f).get().setScrollingDisabled(true, false);
|
||||
desc.row();
|
||||
|
||||
desc.table(t -> {
|
||||
t.left();
|
||||
t.add("$zone.resources").padRight(6);
|
||||
|
||||
if(zone.resources.size > 0){
|
||||
t.table(r -> {
|
||||
t.left();
|
||||
int i = 0;
|
||||
for(Item item : zone.resources){
|
||||
r.addImage(item.icon(Cicon.small)).size(8 * 3);
|
||||
if(++i % 4 == 0){
|
||||
r.row();
|
||||
}
|
||||
}
|
||||
});
|
||||
}else{
|
||||
t.add("$none");
|
||||
}
|
||||
});
|
||||
|
||||
Rules rules = zone.getRules();
|
||||
|
||||
desc.row();
|
||||
desc.add(Core.bundle.format("zone.objective", Core.bundle.get(!rules.attackMode ? "zone.objective.survival" : "zone.objective.attack")));
|
||||
|
||||
if(zone.bestWave() > 0){
|
||||
desc.row();
|
||||
desc.add(Core.bundle.format("bestwave", zone.bestWave()));
|
||||
}
|
||||
});
|
||||
|
||||
cont.row();
|
||||
}
|
||||
cont.marginRight(12f);
|
||||
});
|
||||
cont.row();
|
||||
|
||||
cont.addButton(zone.canConfigure() ? "$configure" : Core.bundle.format("configure.locked", zone.configureObjective.display()),
|
||||
() -> loadout.show(zone.loadout.findCore().itemCapacity, zone.getStartingItems(), zone::resetStartingItems, zone::updateLaunchCost, rebuildItems)
|
||||
).fillX().pad(3).disabled(b -> !zone.canConfigure());
|
||||
|
||||
cont.row();
|
||||
|
||||
Button button = cont.addButton(zone.locked() ? "$uncover" : "$launch", () -> {
|
||||
if(!data.isUnlocked(zone)){
|
||||
Sounds.unlock.play();
|
||||
data.unlockContent(zone);
|
||||
ui.deploy.setup();
|
||||
setup(zone);
|
||||
}else{
|
||||
ui.deploy.hide();
|
||||
data.removeItems(zone.getLaunchCost());
|
||||
hide();
|
||||
control.playZone(zone);
|
||||
}
|
||||
}).minWidth(200f).margin(13f).padTop(5).disabled(b -> zone.locked() ? !zone.canUnlock() : !data.hasItems(zone.getLaunchCost())).uniformY().get();
|
||||
|
||||
button.row();
|
||||
button.add(iteminfo);
|
||||
}
|
||||
}
|
||||
83
core/src/mindustry/ui/fragments/BlockConfigFragment.java
Normal file
83
core/src/mindustry/ui/fragments/BlockConfigFragment.java
Normal file
@@ -0,0 +1,83 @@
|
||||
package mindustry.ui.fragments;
|
||||
|
||||
import arc.*;
|
||||
import arc.math.*;
|
||||
import arc.scene.*;
|
||||
import arc.scene.actions.*;
|
||||
import arc.scene.ui.layout.*;
|
||||
import arc.util.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.core.GameState.*;
|
||||
import mindustry.world.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class BlockConfigFragment extends Fragment{
|
||||
private Table table = new Table();
|
||||
private Tile configTile;
|
||||
private Block configBlock;
|
||||
|
||||
@Override
|
||||
public void build(Group parent){
|
||||
table.visible(false);
|
||||
parent.addChild(table);
|
||||
|
||||
//hacky way to hide block config when in menu
|
||||
//TODO remove?
|
||||
Core.scene.add(new Element(){
|
||||
@Override
|
||||
public void act(float delta){
|
||||
super.act(delta);
|
||||
if(state.is(State.menu)){
|
||||
table.visible(false);
|
||||
configTile = null;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public boolean isShown(){
|
||||
return table.isVisible() && configTile != null;
|
||||
}
|
||||
|
||||
public Tile getSelectedTile(){
|
||||
return configTile;
|
||||
}
|
||||
|
||||
public void showConfig(Tile tile){
|
||||
configTile = tile;
|
||||
configBlock = tile.block();
|
||||
|
||||
table.visible(true);
|
||||
table.clear();
|
||||
tile.block().buildConfiguration(tile, table);
|
||||
table.pack();
|
||||
table.setTransform(true);
|
||||
table.actions(Actions.scaleTo(0f, 1f), Actions.visible(true),
|
||||
Actions.scaleTo(1f, 1f, 0.07f, Interpolation.pow3Out));
|
||||
|
||||
table.update(() -> {
|
||||
if(configTile != null && configTile.block().shouldHideConfigure(configTile, player)){
|
||||
hideConfig();
|
||||
return;
|
||||
}
|
||||
|
||||
table.setOrigin(Align.center);
|
||||
if(configTile == null || configTile.block() == Blocks.air || configTile.block() != configBlock){
|
||||
hideConfig();
|
||||
}else{
|
||||
configTile.block().updateTableAlign(tile, table);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public boolean hasConfigMouse(){
|
||||
Element e = Core.scene.hit(Core.input.mouseX(), Core.graphics.getHeight() - Core.input.mouseY(), true);
|
||||
return e != null && (e == table || e.isDescendantOf(table));
|
||||
}
|
||||
|
||||
public void hideConfig(){
|
||||
configTile = null;
|
||||
table.actions(Actions.scaleTo(0f, 1f, 0.06f, Interpolation.pow3Out), Actions.visible(false));
|
||||
}
|
||||
}
|
||||
217
core/src/mindustry/ui/fragments/BlockInventoryFragment.java
Normal file
217
core/src/mindustry/ui/fragments/BlockInventoryFragment.java
Normal file
@@ -0,0 +1,217 @@
|
||||
package mindustry.ui.fragments;
|
||||
|
||||
import arc.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import arc.struct.*;
|
||||
import arc.func.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.input.*;
|
||||
import arc.math.*;
|
||||
import arc.math.geom.*;
|
||||
import arc.scene.*;
|
||||
import arc.scene.actions.*;
|
||||
import arc.scene.event.*;
|
||||
import arc.scene.ui.*;
|
||||
import arc.scene.ui.layout.*;
|
||||
import arc.util.*;
|
||||
import mindustry.core.GameState.*;
|
||||
import mindustry.entities.*;
|
||||
import mindustry.entities.type.*;
|
||||
import mindustry.game.EventType.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.ui.Cicon;
|
||||
import mindustry.world.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class BlockInventoryFragment extends Fragment{
|
||||
private final static float holdWithdraw = 20f;
|
||||
|
||||
private Table table = new Table();
|
||||
private Tile tile;
|
||||
private float holdTime = 0f;
|
||||
private boolean holding;
|
||||
private Item lastItem;
|
||||
|
||||
@Remote(called = Loc.server, targets = Loc.both, forward = true)
|
||||
public static void requestItem(Player player, Tile tile, Item item, int amount){
|
||||
if(player == null || tile == null || !player.timer.get(Player.timerTransfer, 20) || !tile.interactable(player.getTeam())) return;
|
||||
if(!Units.canInteract(player, tile)) return;
|
||||
|
||||
int removed = tile.block().removeStack(tile, item, amount);
|
||||
|
||||
player.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.drawx(), tile.drawy(), player));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void build(Group parent){
|
||||
table.setName("inventory");
|
||||
table.setTransform(true);
|
||||
parent.setTransform(true);
|
||||
parent.addChild(table);
|
||||
}
|
||||
|
||||
public void showFor(Tile t){
|
||||
if(this.tile == t){
|
||||
hide();
|
||||
return;
|
||||
}
|
||||
this.tile = t;
|
||||
if(tile == null || tile.entity == null || !tile.block().isAccessible() || tile.entity.items.total() == 0)
|
||||
return;
|
||||
rebuild(true);
|
||||
}
|
||||
|
||||
public void hide(){
|
||||
if(table == null) return;
|
||||
|
||||
table.actions(Actions.scaleTo(0f, 1f, 0.06f, Interpolation.pow3Out), Actions.run(() -> {
|
||||
table.clearChildren();
|
||||
table.clearListeners();
|
||||
table.update(null);
|
||||
}), Actions.visible(false));
|
||||
table.touchable(Touchable.disabled);
|
||||
tile = null;
|
||||
}
|
||||
|
||||
private void rebuild(boolean actions){
|
||||
|
||||
IntSet container = new IntSet();
|
||||
|
||||
table.clearChildren();
|
||||
table.clearActions();
|
||||
table.background(Tex.inventory);
|
||||
table.touchable(Touchable.enabled);
|
||||
table.update(() -> {
|
||||
|
||||
if(state.is(State.menu) || tile == null || tile.entity == null || !tile.block().isAccessible() || tile.entity.items.total() == 0){
|
||||
hide();
|
||||
}else{
|
||||
if(holding && lastItem != null){
|
||||
holdTime += Time.delta();
|
||||
|
||||
if(holdTime >= holdWithdraw){
|
||||
int amount = Math.min(tile.entity.items.get(lastItem), player.maxAccepted(lastItem));
|
||||
Call.requestItem(player, tile, lastItem, amount);
|
||||
holding = false;
|
||||
holdTime = 0f;
|
||||
|
||||
if(net.client()) Events.fire(new WithdrawEvent(tile, player, lastItem, amount));
|
||||
}
|
||||
}
|
||||
|
||||
updateTablePosition();
|
||||
if(tile.block().hasItems){
|
||||
for(int i = 0; i < content.items().size; i++){
|
||||
boolean has = tile.entity.items.has(content.item(i));
|
||||
if(has != container.contains(i)){
|
||||
rebuild(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
int cols = 3;
|
||||
int row = 0;
|
||||
|
||||
table.margin(4f);
|
||||
table.defaults().size(8 * 5).pad(4f);
|
||||
|
||||
if(tile.block().hasItems){
|
||||
|
||||
for(int i = 0; i < content.items().size; i++){
|
||||
Item item = content.item(i);
|
||||
if(!tile.entity.items.has(item)) continue;
|
||||
|
||||
container.add(i);
|
||||
|
||||
Boolp canPick = () -> player.acceptsItem(item) && !state.isPaused();
|
||||
|
||||
HandCursorListener l = new HandCursorListener();
|
||||
l.setEnabled(canPick);
|
||||
|
||||
Element image = itemImage(item.icon(Cicon.xlarge), () -> {
|
||||
if(tile == null || tile.entity == null){
|
||||
return "";
|
||||
}
|
||||
return round(tile.entity.items.get(item));
|
||||
});
|
||||
image.addListener(l);
|
||||
|
||||
image.addListener(new InputListener(){
|
||||
@Override
|
||||
public boolean touchDown(InputEvent event, float x, float y, int pointer, KeyCode button){
|
||||
if(!canPick.get() || tile == null || tile.entity == null || tile.entity.items == null || !tile.entity.items.has(item)) return false;
|
||||
int amount = Math.min(1, player.maxAccepted(item));
|
||||
if(amount > 0){
|
||||
Call.requestItem(player, tile, item, amount);
|
||||
lastItem = item;
|
||||
holding = true;
|
||||
holdTime = 0f;
|
||||
if(net.client()) Events.fire(new WithdrawEvent(tile, player, item, amount));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void touchUp(InputEvent event, float x, float y, int pointer, KeyCode button){
|
||||
holding = false;
|
||||
lastItem = null;
|
||||
}
|
||||
});
|
||||
table.add(image);
|
||||
|
||||
if(row++ % cols == cols - 1) table.row();
|
||||
}
|
||||
}
|
||||
|
||||
if(row == 0){
|
||||
table.setSize(0f, 0f);
|
||||
}
|
||||
|
||||
updateTablePosition();
|
||||
|
||||
table.visible(true);
|
||||
|
||||
if(actions){
|
||||
table.setScale(0f, 1f);
|
||||
table.actions(Actions.scaleTo(1f, 1f, 0.07f, Interpolation.pow3Out));
|
||||
}else{
|
||||
table.setScale(1f, 1f);
|
||||
}
|
||||
}
|
||||
|
||||
private String round(float f){
|
||||
f = (int)f;
|
||||
if(f >= 1000000){
|
||||
return (int)(f / 1000000f) + "[gray]" + Core.bundle.getOrNull("unit.millions") + "[]";
|
||||
}else if(f >= 1000){
|
||||
return (int)(f / 1000) + Core.bundle.getOrNull("unit.thousands");
|
||||
}else{
|
||||
return (int)f + "";
|
||||
}
|
||||
}
|
||||
|
||||
private void updateTablePosition(){
|
||||
Vector2 v = Core.input.mouseScreen(tile.drawx() + tile.block().size * tilesize / 2f, tile.drawy() + tile.block().size * tilesize / 2f);
|
||||
table.pack();
|
||||
table.setPosition(v.x, v.y, Align.topLeft);
|
||||
}
|
||||
|
||||
private Element itemImage(TextureRegion region, Prov<CharSequence> text){
|
||||
Stack stack = new Stack();
|
||||
|
||||
Table t = new Table().left().bottom();
|
||||
t.label(text);
|
||||
|
||||
stack.add(new Image(region));
|
||||
stack.add(t);
|
||||
return stack;
|
||||
}
|
||||
}
|
||||
252
core/src/mindustry/ui/fragments/ChatFragment.java
Normal file
252
core/src/mindustry/ui/fragments/ChatFragment.java
Normal file
@@ -0,0 +1,252 @@
|
||||
package mindustry.ui.fragments;
|
||||
|
||||
import arc.*;
|
||||
import arc.Input.*;
|
||||
import arc.struct.*;
|
||||
import arc.graphics.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.math.*;
|
||||
import arc.scene.*;
|
||||
import arc.scene.ui.*;
|
||||
import arc.scene.ui.Label.*;
|
||||
import arc.scene.ui.layout.*;
|
||||
import arc.util.*;
|
||||
import mindustry.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.input.*;
|
||||
import mindustry.ui.*;
|
||||
|
||||
import static arc.Core.*;
|
||||
import static mindustry.Vars.net;
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class ChatFragment extends Table{
|
||||
private final static int messagesShown = 10;
|
||||
private Array<ChatMessage> messages = new Array<>();
|
||||
private float fadetime;
|
||||
private boolean shown = false;
|
||||
private TextField chatfield;
|
||||
private Label fieldlabel = new Label(">");
|
||||
private BitmapFont font;
|
||||
private GlyphLayout layout = new GlyphLayout();
|
||||
private float offsetx = Scl.scl(4), offsety = Scl.scl(4), fontoffsetx = Scl.scl(2), chatspace = Scl.scl(50);
|
||||
private Color shadowColor = new Color(0, 0, 0, 0.4f);
|
||||
private float textspacing = Scl.scl(10);
|
||||
private Array<String> history = new Array<>();
|
||||
private int historyPos = 0;
|
||||
private int scrollPos = 0;
|
||||
private Fragment container = new Fragment(){
|
||||
@Override
|
||||
public void build(Group parent){
|
||||
scene.add(ChatFragment.this);
|
||||
}
|
||||
};
|
||||
|
||||
public ChatFragment(){
|
||||
super();
|
||||
|
||||
setFillParent(true);
|
||||
font = Fonts.def;
|
||||
|
||||
visible(() -> {
|
||||
if(!net.active() && messages.size > 0){
|
||||
clearMessages();
|
||||
|
||||
if(shown){
|
||||
hide();
|
||||
}
|
||||
}
|
||||
|
||||
return net.active();
|
||||
});
|
||||
|
||||
update(() -> {
|
||||
|
||||
if(net.active() && input.keyTap(Binding.chat) && (scene.getKeyboardFocus() == chatfield || scene.getKeyboardFocus() == null)){
|
||||
toggle();
|
||||
}
|
||||
|
||||
if(shown){
|
||||
if(input.keyTap(Binding.chat_history_prev) && historyPos < history.size - 1){
|
||||
if(historyPos == 0) history.set(0, chatfield.getText());
|
||||
historyPos++;
|
||||
updateChat();
|
||||
}
|
||||
if(input.keyTap(Binding.chat_history_next) && historyPos > 0){
|
||||
historyPos--;
|
||||
updateChat();
|
||||
}
|
||||
scrollPos = (int)Mathf.clamp(scrollPos + input.axis(Binding.chat_scroll), 0, Math.max(0, messages.size - messagesShown));
|
||||
}
|
||||
});
|
||||
|
||||
history.insert(0, "");
|
||||
setup();
|
||||
}
|
||||
|
||||
public Fragment container(){
|
||||
return container;
|
||||
}
|
||||
|
||||
public void clearMessages(){
|
||||
messages.clear();
|
||||
history.clear();
|
||||
history.insert(0, "");
|
||||
}
|
||||
|
||||
private void setup(){
|
||||
fieldlabel.setStyle(new LabelStyle(fieldlabel.getStyle()));
|
||||
fieldlabel.getStyle().font = font;
|
||||
fieldlabel.setStyle(fieldlabel.getStyle());
|
||||
|
||||
chatfield = new TextField("", new TextField.TextFieldStyle(scene.getStyle(TextField.TextFieldStyle.class)));
|
||||
chatfield.setMaxLength(Vars.maxTextLength);
|
||||
chatfield.getStyle().background = null;
|
||||
chatfield.getStyle().font = Fonts.chat;
|
||||
chatfield.getStyle().fontColor = Color.white;
|
||||
chatfield.setStyle(chatfield.getStyle());
|
||||
|
||||
bottom().left().marginBottom(offsety).marginLeft(offsetx * 2).add(fieldlabel).padBottom(6f);
|
||||
|
||||
add(chatfield).padBottom(offsety).padLeft(offsetx).growX().padRight(offsetx).height(28);
|
||||
|
||||
if(Vars.mobile){
|
||||
marginBottom(105f);
|
||||
marginRight(240f);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(){
|
||||
float opacity = Core.settings.getInt("chatopacity") / 100f;
|
||||
float textWidth = Math.min(Core.graphics.getWidth()/1.5f, Scl.scl(700f));
|
||||
|
||||
Draw.color(shadowColor);
|
||||
|
||||
if(shown){
|
||||
Fill.crect(offsetx, chatfield.getY(), chatfield.getWidth() + 15f, chatfield.getHeight() - 1);
|
||||
}
|
||||
|
||||
super.draw();
|
||||
|
||||
float spacing = chatspace;
|
||||
|
||||
chatfield.visible(shown);
|
||||
fieldlabel.visible(shown);
|
||||
|
||||
Draw.color(shadowColor);
|
||||
Draw.alpha(shadowColor.a * opacity);
|
||||
|
||||
float theight = offsety + spacing + getMarginBottom();
|
||||
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);
|
||||
theight += layout.height + textspacing;
|
||||
if(i - scrollPos == 0) theight -= textspacing + 1;
|
||||
|
||||
font.getCache().clear();
|
||||
font.getCache().addText(messages.get(i).formattedMessage, fontoffsetx + offsetx, offsety + theight, textWidth, Align.bottomLeft, true);
|
||||
|
||||
if(!shown && fadetime - i < 1f && fadetime - i >= 0f){
|
||||
font.getCache().setAlphas((fadetime - i) * opacity);
|
||||
Draw.color(0, 0, 0, shadowColor.a * (fadetime - i) * opacity);
|
||||
}else{
|
||||
font.getCache().setAlphas(opacity);
|
||||
}
|
||||
|
||||
Fill.crect(offsetx, theight - layout.height - 2, textWidth + Scl.scl(4f), layout.height + textspacing);
|
||||
Draw.color(shadowColor);
|
||||
Draw.alpha(opacity * shadowColor.a);
|
||||
|
||||
font.getCache().draw();
|
||||
}
|
||||
|
||||
Draw.color();
|
||||
|
||||
if(fadetime > 0 && !shown)
|
||||
fadetime -= Time.delta() / 180f;
|
||||
}
|
||||
|
||||
private void sendMessage(){
|
||||
String message = chatfield.getText();
|
||||
clearChatInput();
|
||||
|
||||
if(message.replaceAll(" ", "").isEmpty()) return;
|
||||
|
||||
history.insert(1, message);
|
||||
|
||||
Call.sendChatMessage(message);
|
||||
}
|
||||
|
||||
public void toggle(){
|
||||
|
||||
if(!shown){
|
||||
scene.setKeyboardFocus(chatfield);
|
||||
shown = !shown;
|
||||
if(mobile){
|
||||
TextInput input = new TextInput();
|
||||
input.maxLength = maxTextLength;
|
||||
input.accepted = text -> {
|
||||
chatfield.setText(text);
|
||||
sendMessage();
|
||||
hide();
|
||||
Core.input.setOnscreenKeyboardVisible(false);
|
||||
};
|
||||
input.canceled = this::hide;
|
||||
Core.input.getTextInput(input);
|
||||
}else{
|
||||
chatfield.fireClick();
|
||||
}
|
||||
}else{
|
||||
scene.setKeyboardFocus(null);
|
||||
shown = !shown;
|
||||
scrollPos = 0;
|
||||
sendMessage();
|
||||
}
|
||||
}
|
||||
|
||||
public void hide(){
|
||||
scene.setKeyboardFocus(null);
|
||||
shown = false;
|
||||
clearChatInput();
|
||||
}
|
||||
|
||||
public void updateChat(){
|
||||
chatfield.setText(history.get(historyPos));
|
||||
chatfield.setCursorPosition(chatfield.getText().length());
|
||||
}
|
||||
|
||||
public void clearChatInput(){
|
||||
historyPos = 0;
|
||||
history.set(0, "");
|
||||
chatfield.setText("");
|
||||
}
|
||||
|
||||
public boolean shown(){
|
||||
return shown;
|
||||
}
|
||||
|
||||
public void addMessage(String message, String sender){
|
||||
messages.insert(0, new ChatMessage(message, sender));
|
||||
|
||||
fadetime += 1f;
|
||||
fadetime = Math.min(fadetime, messagesShown) + 1f;
|
||||
}
|
||||
|
||||
private static class ChatMessage{
|
||||
public final String sender;
|
||||
public final String message;
|
||||
public final String formattedMessage;
|
||||
|
||||
public ChatMessage(String message, String sender){
|
||||
this.message = message;
|
||||
this.sender = sender;
|
||||
if(sender == null){ //no sender, this is a server message?
|
||||
formattedMessage = message;
|
||||
}else{
|
||||
formattedMessage = "[CORAL][[" + sender + "[CORAL]]:[WHITE] " + message;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
39
core/src/mindustry/ui/fragments/FadeInFragment.java
Normal file
39
core/src/mindustry/ui/fragments/FadeInFragment.java
Normal file
@@ -0,0 +1,39 @@
|
||||
package mindustry.ui.fragments;
|
||||
|
||||
import arc.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.math.*;
|
||||
import arc.scene.*;
|
||||
import arc.scene.event.*;
|
||||
|
||||
/** Fades in a black overlay.*/
|
||||
public class FadeInFragment extends Fragment{
|
||||
private static final float duration = 40f;
|
||||
float time = 0f;
|
||||
|
||||
@Override
|
||||
public void build(Group parent){
|
||||
parent.addChild(new Element(){
|
||||
{
|
||||
setFillParent(true);
|
||||
touchable(Touchable.disabled);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(){
|
||||
Draw.color(0f, 0f, 0f, Mathf.clamp(1f - time));
|
||||
Fill.crect(0, 0, Core.graphics.getWidth(), Core.graphics.getHeight());
|
||||
Draw.color();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void act(float delta){
|
||||
super.act(delta);
|
||||
time += 1f / duration;
|
||||
if(time > 1){
|
||||
remove();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
7
core/src/mindustry/ui/fragments/Fragment.java
Normal file
7
core/src/mindustry/ui/fragments/Fragment.java
Normal file
@@ -0,0 +1,7 @@
|
||||
package mindustry.ui.fragments;
|
||||
|
||||
import arc.scene.Group;
|
||||
|
||||
public abstract class Fragment{
|
||||
public abstract void build(Group parent);
|
||||
}
|
||||
664
core/src/mindustry/ui/fragments/HudFragment.java
Normal file
664
core/src/mindustry/ui/fragments/HudFragment.java
Normal file
@@ -0,0 +1,664 @@
|
||||
package mindustry.ui.fragments;
|
||||
|
||||
import arc.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import arc.struct.*;
|
||||
import arc.graphics.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.input.*;
|
||||
import arc.math.*;
|
||||
import arc.math.geom.*;
|
||||
import arc.scene.*;
|
||||
import arc.scene.actions.*;
|
||||
import arc.scene.event.*;
|
||||
import arc.scene.style.*;
|
||||
import arc.scene.ui.*;
|
||||
import arc.scene.ui.ImageButton.*;
|
||||
import arc.scene.ui.layout.*;
|
||||
import arc.util.*;
|
||||
import mindustry.core.GameState.*;
|
||||
import mindustry.ctype.ContentType;
|
||||
import mindustry.ctype.UnlockableContent;
|
||||
import mindustry.entities.*;
|
||||
import mindustry.entities.type.*;
|
||||
import mindustry.game.*;
|
||||
import mindustry.game.EventType.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.input.*;
|
||||
import mindustry.net.Packets.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.ui.*;
|
||||
import mindustry.ui.Cicon;
|
||||
import mindustry.ui.dialogs.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class HudFragment extends Fragment{
|
||||
public final PlacementFragment blockfrag = new PlacementFragment();
|
||||
|
||||
private ImageButton flip;
|
||||
private Table lastUnlockTable;
|
||||
private Table lastUnlockLayout;
|
||||
private boolean shown = true;
|
||||
private float dsize = 47.2f;
|
||||
|
||||
private long lastToast;
|
||||
|
||||
public void build(Group parent){
|
||||
|
||||
//menu at top left
|
||||
parent.fill(cont -> {
|
||||
cont.setName("overlaymarker");
|
||||
cont.top().left();
|
||||
|
||||
if(mobile){
|
||||
|
||||
{
|
||||
Table select = new Table();
|
||||
|
||||
select.left();
|
||||
select.defaults().size(dsize).left();
|
||||
|
||||
ImageButtonStyle style = Styles.clearTransi;
|
||||
|
||||
select.addImageButton(Icon.menuLargeSmall, style, ui.paused::show);
|
||||
flip = select.addImageButton(Icon.arrowUpSmall, style, this::toggleMenus).get();
|
||||
|
||||
select.addImageButton(Icon.pasteSmall, style, ui.schematics::show);
|
||||
|
||||
select.addImageButton(Icon.pauseSmall, style, () -> {
|
||||
if(net.active()){
|
||||
ui.listfrag.toggle();
|
||||
}else{
|
||||
state.set(state.is(State.paused) ? State.playing : State.paused);
|
||||
}
|
||||
}).name("pause").update(i -> {
|
||||
if(net.active()){
|
||||
i.getStyle().imageUp = Icon.playersSmall;
|
||||
}else{
|
||||
i.setDisabled(false);
|
||||
i.getStyle().imageUp = state.is(State.paused) ? Icon.playSmall : Icon.pauseSmall;
|
||||
}
|
||||
});
|
||||
|
||||
select.addImageButton(Icon.chatSmall, style,() -> {
|
||||
if(net.active() && mobile){
|
||||
if(ui.chatfrag.shown()){
|
||||
ui.chatfrag.hide();
|
||||
}else{
|
||||
ui.chatfrag.toggle();
|
||||
}
|
||||
}else if(world.isZone()){
|
||||
ui.tech.show();
|
||||
}else{
|
||||
ui.database.show();
|
||||
}
|
||||
}).update(i -> {
|
||||
if(net.active() && mobile){
|
||||
i.getStyle().imageUp = Icon.chatSmall;
|
||||
}else{
|
||||
i.getStyle().imageUp = Icon.databaseSmall;
|
||||
}
|
||||
});
|
||||
|
||||
select.addImage().color(Pal.gray).width(4f).fillY();
|
||||
|
||||
float size = Scl.scl(dsize);
|
||||
Array<Element> children = new Array<>(select.getChildren());
|
||||
|
||||
//now, you may be wondering, why is this necessary? the answer is, I don't know, but it fixes layout issues somehow
|
||||
int index = 0;
|
||||
for(Element elem : children){
|
||||
int fi = index++;
|
||||
parent.addChild(elem);
|
||||
elem.visible(() -> {
|
||||
if(fi < 5){
|
||||
elem.setSize(size);
|
||||
}else{
|
||||
elem.setSize(Scl.scl(4f), size);
|
||||
}
|
||||
elem.setPosition(fi * size, Core.graphics.getHeight(), Align.topLeft);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
cont.add().size(dsize * 5 + 3, dsize).left();
|
||||
}
|
||||
|
||||
cont.row();
|
||||
cont.addImage().height(4f).color(Pal.gray).fillX();
|
||||
cont.row();
|
||||
}
|
||||
|
||||
cont.update(() -> {
|
||||
if(Core.input.keyTap(Binding.toggle_menus) && !ui.chatfrag.shown() && !Core.scene.hasDialog() && !(Core.scene.getKeyboardFocus() instanceof TextField)){
|
||||
toggleMenus();
|
||||
}
|
||||
});
|
||||
|
||||
Table wavesMain, editorMain;
|
||||
|
||||
cont.stack(wavesMain = new Table(), editorMain = new Table()).height(wavesMain.getPrefHeight());
|
||||
|
||||
{
|
||||
wavesMain.visible(() -> shown && !state.isEditor());
|
||||
wavesMain.top().left();
|
||||
Stack stack = new Stack();
|
||||
Button waves = new Button(Styles.waveb);
|
||||
Table btable = new Table().margin(0);
|
||||
|
||||
stack.add(waves);
|
||||
stack.add(btable);
|
||||
|
||||
addWaveTable(waves);
|
||||
addPlayButton(btable);
|
||||
wavesMain.add(stack).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();
|
||||
wavesMain.row();
|
||||
}
|
||||
|
||||
{
|
||||
editorMain.table(Tex.buttonEdge4, t -> {
|
||||
//t.margin(0f);
|
||||
t.add("$editor.teams").growX().left();
|
||||
t.row();
|
||||
t.table(teams -> {
|
||||
teams.left();
|
||||
int i = 0;
|
||||
for(Team team : Team.all){
|
||||
ImageButton button = teams.addImageButton(Tex.whiteui, Styles.clearTogglePartiali, 40f, () -> Call.setPlayerTeamEditor(player, team))
|
||||
.size(50f).margin(6f).get();
|
||||
button.getImageCell().grow();
|
||||
button.getStyle().imageUpColor = team.color;
|
||||
button.update(() -> button.setChecked(player.getTeam() == team));
|
||||
|
||||
if(++i % 3 == 0){
|
||||
teams.row();
|
||||
}
|
||||
}
|
||||
}).left();
|
||||
|
||||
if(enableUnitEditing){
|
||||
|
||||
t.row();
|
||||
t.addImageTextButton("$editor.spawn", Icon.add, () -> {
|
||||
FloatingDialog dialog = new FloatingDialog("$editor.spawn");
|
||||
int i = 0;
|
||||
for(UnitType type : content.<UnitType>getBy(ContentType.unit)){
|
||||
dialog.cont.addImageButton(Tex.whiteui, 8 * 6f, () -> {
|
||||
Call.spawnUnitEditor(player, type);
|
||||
dialog.hide();
|
||||
}).get().getStyle().imageUp = new TextureRegionDrawable(type.icon(Cicon.xlarge));
|
||||
if(++i % 4 == 0) dialog.cont.row();
|
||||
}
|
||||
dialog.addCloseButton();
|
||||
dialog.setFillParent(false);
|
||||
dialog.show();
|
||||
}).fillX();
|
||||
|
||||
float[] size = {0};
|
||||
float[] position = {0, 0};
|
||||
|
||||
t.row();
|
||||
t.addImageTextButton("$editor.removeunit", Icon.quit, Styles.togglet, () -> {}).fillX().update(b -> {
|
||||
boolean[] found = {false};
|
||||
if(b.isChecked()){
|
||||
Element e = Core.scene.hit(Core.input.mouseX(), Core.input.mouseY(), true);
|
||||
if(e == null){
|
||||
Vector2 world = Core.input.mouseWorld();
|
||||
Units.nearby(world.x, world.y, 1f, 1f, unit -> {
|
||||
if(!found[0] && unit instanceof BaseUnit){
|
||||
if(Core.input.keyTap(KeyCode.MOUSE_LEFT)){
|
||||
Call.removeUnitEditor(player, (BaseUnit)unit);
|
||||
}
|
||||
found[0] = true;
|
||||
unit.hitbox(Tmp.r1);
|
||||
size[0] = Mathf.lerpDelta(size[0], Tmp.r1.width * 2f + Mathf.absin(Time.time(), 10f, 5f), 0.1f);
|
||||
position[0] = unit.x;
|
||||
position[1] = unit.y;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Draw.color(Pal.accent, Color.white, Mathf.absin(Time.time(), 8f, 1f));
|
||||
Lines.poly(position[0], position[1], 4, size[0] / 2f);
|
||||
Draw.reset();
|
||||
|
||||
if(!found[0]){
|
||||
size[0] = Mathf.lerpDelta(size[0], 0f, 0.2f);
|
||||
}
|
||||
});
|
||||
}
|
||||
}).width(dsize * 5 + 4f);
|
||||
editorMain.visible(() -> shown && state.isEditor());
|
||||
}
|
||||
|
||||
//fps display
|
||||
cont.table(info -> {
|
||||
info.top().left().margin(4).visible(() -> Core.settings.getBool("fps"));
|
||||
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");
|
||||
|
||||
info.label(() -> fps.get(Core.graphics.getFramesPerSecond())).left().style(Styles.outlineLabel);
|
||||
info.row();
|
||||
info.label(() -> ping.get(netClient.getPing())).visible(net::client).left().style(Styles.outlineLabel);
|
||||
}).top().left();
|
||||
});
|
||||
|
||||
parent.fill(t -> {
|
||||
t.visible(() -> Core.settings.getBool("minimap") && !state.rules.tutorial);
|
||||
//minimap
|
||||
t.add(new Minimap());
|
||||
t.row();
|
||||
//position
|
||||
t.label(() -> world.toTile(player.x) + "," + world.toTile(player.y))
|
||||
.visible(() -> Core.settings.getBool("position") && !state.rules.tutorial);
|
||||
t.top().right();
|
||||
});
|
||||
|
||||
//spawner warning
|
||||
parent.fill(t -> {
|
||||
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))))
|
||||
.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.visible(() -> netServer.isWaitingForPlayers());
|
||||
t.table(Tex.button, c -> c.add("$waiting.players"));
|
||||
});
|
||||
|
||||
//'core is under attack' table
|
||||
parent.fill(t -> {
|
||||
t.touchable(Touchable.disabled);
|
||||
float notifDuration = 240f;
|
||||
float[] coreAttackTime = {0};
|
||||
float[] coreAttackOpacity = {0};
|
||||
|
||||
Events.on(Trigger.teamCoreDamage, () -> {
|
||||
coreAttackTime[0] = notifDuration;
|
||||
});
|
||||
|
||||
t.top().visible(() -> {
|
||||
if(state.is(State.menu) || state.teams.get(player.getTeam()).cores.size == 0 || state.teams.get(player.getTeam()).cores.first().entity == null){
|
||||
coreAttackTime[0] = 0f;
|
||||
return false;
|
||||
}
|
||||
|
||||
t.getColor().a = coreAttackOpacity[0];
|
||||
if(coreAttackTime[0] > 0){
|
||||
coreAttackOpacity[0] = Mathf.lerpDelta(coreAttackOpacity[0], 1f, 0.1f);
|
||||
}else{
|
||||
coreAttackOpacity[0] = Mathf.lerpDelta(coreAttackOpacity[0], 0f, 0.1f);
|
||||
}
|
||||
|
||||
coreAttackTime[0] -= Time.delta();
|
||||
|
||||
return coreAttackOpacity[0] > 0;
|
||||
});
|
||||
t.table(Tex.button, top -> top.add("$coreattack").pad(2)
|
||||
.update(label -> label.getColor().set(Color.orange).lerp(Color.scarlet, Mathf.absin(Time.time(), 2f, 1f)))).touchable(Touchable.disabled);
|
||||
});
|
||||
|
||||
//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().addImageButton(Icon.arrowLeftSmall, Styles.emptyi, () -> {
|
||||
control.tutorial.prevSentence();
|
||||
}).width(44f).growY().visible(() -> control.tutorial.canPrev());
|
||||
}));
|
||||
};
|
||||
|
||||
resize.run();
|
||||
Events.on(ResizeEvent.class, e -> resize.run());
|
||||
});
|
||||
|
||||
//paused table
|
||||
parent.fill(t -> {
|
||||
t.top().visible(() -> state.isPaused()).touchable(Touchable.disabled);
|
||||
t.table(Tex.buttonTrans, top -> top.add("$paused").pad(5f));
|
||||
});
|
||||
|
||||
//'saving' indicator
|
||||
parent.fill(t -> {
|
||||
t.bottom().visible(() -> control.saves.isSaving());
|
||||
t.add("$saveload").style(Styles.outlineLabel);
|
||||
});
|
||||
|
||||
blockfrag.build(parent);
|
||||
}
|
||||
|
||||
@Remote(targets = Loc.both, forward = true, called = Loc.both)
|
||||
public static void setPlayerTeamEditor(Player player, Team team){
|
||||
if(state.isEditor() && player != null){
|
||||
player.setTeam(team);
|
||||
}
|
||||
}
|
||||
|
||||
@Remote(targets = Loc.both, called = Loc.server)
|
||||
public static void spawnUnitEditor(Player player, UnitType type){
|
||||
if(state.isEditor()){
|
||||
BaseUnit unit = type.create(player.getTeam());
|
||||
unit.set(player.x, player.y);
|
||||
unit.rotation = player.rotation;
|
||||
unit.add();
|
||||
}
|
||||
}
|
||||
|
||||
@Remote(targets = Loc.both, called = Loc.server, forward = true)
|
||||
public static void removeUnitEditor(Player player, BaseUnit unit){
|
||||
if(state.isEditor() && unit != null){
|
||||
unit.remove();
|
||||
}
|
||||
}
|
||||
|
||||
private void scheduleToast(Runnable run){
|
||||
long duration = (int)(3.5 * 1000);
|
||||
long since = Time.timeSinceMillis(lastToast);
|
||||
if(since > duration){
|
||||
lastToast = Time.millis();
|
||||
run.run();
|
||||
}else{
|
||||
Time.runTask((duration - since) / 1000f * 60f, run);
|
||||
lastToast += duration;
|
||||
}
|
||||
}
|
||||
|
||||
public void showToast(String text){
|
||||
if(state.is(State.menu)) return;
|
||||
|
||||
scheduleToast(() -> {
|
||||
Sounds.message.play();
|
||||
|
||||
Table table = new Table(Tex.button);
|
||||
table.update(() -> {
|
||||
if(state.is(State.menu)){
|
||||
table.remove();
|
||||
}
|
||||
});
|
||||
table.margin(12);
|
||||
table.addImage(Icon.check).pad(3);
|
||||
table.add(text).wrap().width(280f).get().setAlignment(Align.center, Align.center);
|
||||
table.pack();
|
||||
|
||||
//create container table which will align and move
|
||||
Table container = Core.scene.table();
|
||||
container.top().add(table);
|
||||
container.setTranslation(0, table.getPrefHeight());
|
||||
container.actions(Actions.translateBy(0, -table.getPrefHeight(), 1f, Interpolation.fade), Actions.delay(2.5f),
|
||||
//nesting actions() calls is necessary so the right prefHeight() is used
|
||||
Actions.run(() -> container.actions(Actions.translateBy(0, table.getPrefHeight(), 1f, Interpolation.fade), Actions.remove())));
|
||||
});
|
||||
}
|
||||
|
||||
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.is(State.menu) || state.rules.tutorial) return;
|
||||
|
||||
Sounds.message.play();
|
||||
|
||||
//if there's currently no unlock notification...
|
||||
if(lastUnlockTable == null){
|
||||
scheduleToast(() -> {
|
||||
Table table = new Table(Tex.button);
|
||||
table.update(() -> {
|
||||
if(state.is(State.menu)){
|
||||
table.remove();
|
||||
lastUnlockLayout = null;
|
||||
lastUnlockTable = null;
|
||||
}
|
||||
});
|
||||
table.margin(12);
|
||||
|
||||
Table in = new Table();
|
||||
|
||||
//create texture stack for displaying
|
||||
Image image = new Image(content.icon(Cicon.xlarge));
|
||||
image.setScaling(Scaling.fit);
|
||||
|
||||
in.add(image).size(8 * 6).pad(2);
|
||||
|
||||
//add to table
|
||||
table.add(in).padRight(8);
|
||||
table.add("$unlocked");
|
||||
table.pack();
|
||||
|
||||
//create container table which will align and move
|
||||
Table container = Core.scene.table();
|
||||
container.top().add(table);
|
||||
container.setTranslation(0, table.getPrefHeight());
|
||||
container.actions(Actions.translateBy(0, -table.getPrefHeight(), 1f, Interpolation.fade), Actions.delay(2.5f),
|
||||
//nesting actions() calls is necessary so the right prefHeight() is used
|
||||
Actions.run(() -> container.actions(Actions.translateBy(0, table.getPrefHeight(), 1f, Interpolation.fade), Actions.run(() -> {
|
||||
lastUnlockTable = null;
|
||||
lastUnlockLayout = null;
|
||||
}), Actions.remove())));
|
||||
|
||||
lastUnlockTable = container;
|
||||
lastUnlockLayout = in;
|
||||
});
|
||||
}else{
|
||||
//max column size
|
||||
int col = 3;
|
||||
//max amount of elements minus extra 'plus'
|
||||
int cap = col * col - 1;
|
||||
|
||||
//get old elements
|
||||
Array<Element> elements = new Array<>(lastUnlockLayout.getChildren());
|
||||
int esize = elements.size;
|
||||
|
||||
//...if it's already reached the cap, ignore everything
|
||||
if(esize > cap) return;
|
||||
|
||||
//get size of each element
|
||||
float size = 48f / Math.min(elements.size + 1, col);
|
||||
|
||||
lastUnlockLayout.clearChildren();
|
||||
lastUnlockLayout.defaults().size(size).pad(2);
|
||||
|
||||
for(int i = 0; i < esize; i++){
|
||||
lastUnlockLayout.add(elements.get(i));
|
||||
|
||||
if(i % col == col - 1){
|
||||
lastUnlockLayout.row();
|
||||
}
|
||||
}
|
||||
|
||||
//if there's space, add it
|
||||
if(esize < cap){
|
||||
|
||||
Image image = new Image(content.icon(Cicon.medium));
|
||||
image.setScaling(Scaling.fit);
|
||||
|
||||
lastUnlockLayout.add(image);
|
||||
}else{ //else, add a specific icon to denote no more space
|
||||
lastUnlockLayout.addImage(Icon.add);
|
||||
}
|
||||
|
||||
lastUnlockLayout.pack();
|
||||
}
|
||||
}
|
||||
|
||||
public void showLaunch(){
|
||||
Image image = new Image();
|
||||
image.getColor().a = 0f;
|
||||
image.setFillParent(true);
|
||||
image.actions(Actions.fadeIn(40f / 60f));
|
||||
image.update(() -> {
|
||||
if(state.is(State.menu)){
|
||||
image.remove();
|
||||
}
|
||||
});
|
||||
Core.scene.add(image);
|
||||
}
|
||||
|
||||
public void showLand(){
|
||||
Image image = new Image();
|
||||
image.getColor().a = 1f;
|
||||
image.touchable(Touchable.disabled);
|
||||
image.setFillParent(true);
|
||||
image.actions(Actions.fadeOut(0.8f), Actions.remove());
|
||||
image.update(() -> {
|
||||
image.toFront();
|
||||
if(state.is(State.menu)){
|
||||
image.remove();
|
||||
}
|
||||
});
|
||||
Core.scene.add(image);
|
||||
}
|
||||
|
||||
private void showLaunchConfirm(){
|
||||
FloatingDialog dialog = new FloatingDialog("$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.addButton("$cancel", dialog::hide);
|
||||
dialog.buttons.addButton("$ok", () -> {
|
||||
dialog.hide();
|
||||
Call.launchZone();
|
||||
});
|
||||
dialog.keyDown(KeyCode.ESCAPE, dialog::hide);
|
||||
dialog.keyDown(KeyCode.BACK, dialog::hide);
|
||||
dialog.show();
|
||||
}
|
||||
|
||||
private boolean inLaunchWave(){
|
||||
return world.isZone() &&
|
||||
world.getZone().metCondition() &&
|
||||
!net.client() &&
|
||||
state.wave % world.getZone().launchPeriod == 0 && !spawner.isSpawning();
|
||||
}
|
||||
|
||||
private boolean canLaunch(){
|
||||
return inLaunchWave() && state.enemies() <= 0;
|
||||
}
|
||||
|
||||
private void toggleMenus(){
|
||||
if(flip != null){
|
||||
flip.getStyle().imageUp = shown ? Icon.arrowDownSmall : Icon.arrowUpSmall;
|
||||
}
|
||||
|
||||
shown = !shown;
|
||||
}
|
||||
|
||||
private void addWaveTable(Button table){
|
||||
StringBuilder ibuild = new StringBuilder();
|
||||
|
||||
IntFormat wavef = new IntFormat("wave");
|
||||
IntFormat enemyf = new IntFormat("wave.enemy");
|
||||
IntFormat enemiesf = new IntFormat("wave.enemies");
|
||||
IntFormat waitingf = new IntFormat("wave.waiting", i -> {
|
||||
ibuild.setLength(0);
|
||||
int m = i/60;
|
||||
int s = i % 60;
|
||||
if(m <= 0){
|
||||
ibuild.append(s);
|
||||
}else{
|
||||
ibuild.append(m);
|
||||
ibuild.append(":");
|
||||
if(s < 10){
|
||||
ibuild.append("0");
|
||||
}
|
||||
ibuild.append(s);
|
||||
}
|
||||
return ibuild.toString();
|
||||
});
|
||||
|
||||
table.clearChildren();
|
||||
table.touchable(Touchable.enabled);
|
||||
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
table.setName("waves");
|
||||
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 + world.getZone().launchPeriod));
|
||||
builder.append("\n");
|
||||
}
|
||||
builder.append("[]\n");
|
||||
}
|
||||
|
||||
if(state.enemies() > 0){
|
||||
if(state.enemies() == 1){
|
||||
builder.append(enemyf.get(state.enemies()));
|
||||
}else{
|
||||
builder.append(enemiesf.get(state.enemies()));
|
||||
}
|
||||
builder.append("\n");
|
||||
}
|
||||
|
||||
if(state.rules.waveTimer){
|
||||
builder.append((state.rules.waitForWaveToEnd && unitGroups[waveTeam.ordinal()].size() > 0) ? Core.bundle.get("wave.waveInProgress") : ( waitingf.get((int)(state.wavetime/60))));
|
||||
}else if(state.enemies() == 0){
|
||||
builder.append(Core.bundle.get("waiting"));
|
||||
}
|
||||
|
||||
return builder;
|
||||
}).growX().pad(8f);
|
||||
|
||||
table.setDisabled(() -> !canLaunch());
|
||||
table.visible(() -> state.rules.waves);
|
||||
table.clicked(() -> {
|
||||
if(canLaunch()){
|
||||
showLaunchConfirm();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private boolean canSkipWave(){
|
||||
return state.rules.waves && ((net.server() || player.isAdmin) || !net.active()) && state.enemies() == 0 && !spawner.isSpawning() && !state.rules.tutorial;
|
||||
}
|
||||
|
||||
private void addPlayButton(Table table){
|
||||
table.right().addImageButton(Icon.playSmaller, Styles.righti, 30f, () -> {
|
||||
if(net.client() && player.isAdmin){
|
||||
Call.onAdminRequest(player, AdminAction.wave);
|
||||
}else if(inLaunchWave()){
|
||||
ui.showConfirm("$confirm", "$launch.skip.confirm", () -> !canSkipWave(), () -> state.wavetime = 0f);
|
||||
}else{
|
||||
state.wavetime = 0f;
|
||||
}
|
||||
}).growY().fillX().right().width(40f)
|
||||
.visible(this::canSkipWave);
|
||||
}
|
||||
}
|
||||
77
core/src/mindustry/ui/fragments/LoadingFragment.java
Normal file
77
core/src/mindustry/ui/fragments/LoadingFragment.java
Normal file
@@ -0,0 +1,77 @@
|
||||
package mindustry.ui.fragments;
|
||||
|
||||
import arc.func.*;
|
||||
import arc.graphics.*;
|
||||
import arc.scene.Group;
|
||||
import arc.scene.actions.*;
|
||||
import arc.scene.event.Touchable;
|
||||
import arc.scene.ui.Label;
|
||||
import arc.scene.ui.TextButton;
|
||||
import arc.scene.ui.layout.Table;
|
||||
import mindustry.graphics.Pal;
|
||||
import mindustry.ui.*;
|
||||
|
||||
public class LoadingFragment extends Fragment{
|
||||
private Table table;
|
||||
private TextButton button;
|
||||
private Bar bar;
|
||||
|
||||
@Override
|
||||
public void build(Group parent){
|
||||
parent.fill(Styles.black8, t -> {
|
||||
t.visible(false);
|
||||
t.touchable(Touchable.enabled);
|
||||
t.add().height(133f).row();
|
||||
t.addImage().growX().height(3f).pad(4f).growX().get().setColor(Pal.accent);
|
||||
t.row();
|
||||
t.add("$loading").name("namelabel").pad(10f);
|
||||
t.row();
|
||||
t.addImage().growX().height(3f).pad(4f).growX().get().setColor(Pal.accent);
|
||||
t.row();
|
||||
|
||||
bar = t.add(new Bar()).pad(3).size(500f, 40f).visible(false).get();
|
||||
t.row();
|
||||
button = t.addButton("$cancel", () -> {}).pad(20).size(250f, 70f).visible(false).get();
|
||||
table = t;
|
||||
});
|
||||
}
|
||||
|
||||
public void setProgress(Floatp progress){
|
||||
bar.reset(0f);
|
||||
bar.visible(true);
|
||||
bar.set(() -> ((int)(progress.get() * 100) + "%"), progress, Pal.accent);
|
||||
}
|
||||
|
||||
public void setButton(Runnable listener){
|
||||
button.visible(true);
|
||||
button.getListeners().remove(button.getListeners().size - 1);
|
||||
button.clicked(listener);
|
||||
}
|
||||
|
||||
public void setText(String text){
|
||||
table.<Label>find("namelabel").setText(text);
|
||||
table.<Label>find("namelabel").setColor(Pal.accent);
|
||||
}
|
||||
|
||||
public void show(){
|
||||
show("$loading");
|
||||
}
|
||||
|
||||
public void show(String text){
|
||||
table.<Label>find("namelabel").setColor(Color.white);
|
||||
bar.visible(false);
|
||||
table.clearActions();
|
||||
table.touchable(Touchable.enabled);
|
||||
table.<Label>find("namelabel").setText(text);
|
||||
table.visible(true);
|
||||
table.getColor().a = 1f;
|
||||
table.toFront();
|
||||
}
|
||||
|
||||
public void hide(){
|
||||
table.clearActions();
|
||||
table.toFront();
|
||||
table.touchable(Touchable.disabled);
|
||||
table.actions(Actions.fadeOut(0.5f), Actions.visible(false));
|
||||
}
|
||||
}
|
||||
255
core/src/mindustry/ui/fragments/MenuFragment.java
Normal file
255
core/src/mindustry/ui/fragments/MenuFragment.java
Normal file
@@ -0,0 +1,255 @@
|
||||
package mindustry.ui.fragments;
|
||||
|
||||
import arc.*;
|
||||
import arc.graphics.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.math.*;
|
||||
import arc.scene.*;
|
||||
import arc.scene.actions.*;
|
||||
import arc.scene.event.*;
|
||||
import arc.scene.style.*;
|
||||
import arc.scene.ui.*;
|
||||
import arc.scene.ui.layout.*;
|
||||
import arc.util.*;
|
||||
import mindustry.core.*;
|
||||
import mindustry.game.EventType.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.ui.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class MenuFragment extends Fragment{
|
||||
private Table container, submenu;
|
||||
private Button currentMenu;
|
||||
private MenuRenderer renderer;
|
||||
|
||||
public MenuFragment(){
|
||||
Events.on(DisposeEvent.class, event -> {
|
||||
renderer.dispose();
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void build(Group parent){
|
||||
renderer = new MenuRenderer();
|
||||
|
||||
Group group = new WidgetGroup();
|
||||
group.setFillParent(true);
|
||||
group.visible(() -> !ui.editor.isShown());
|
||||
parent.addChild(group);
|
||||
|
||||
parent = group;
|
||||
|
||||
parent.fill((x, y, w, h) -> renderer.render());
|
||||
|
||||
parent.fill(c -> {
|
||||
container = c;
|
||||
|
||||
if(!mobile){
|
||||
buildDesktop();
|
||||
Events.on(ResizeEvent.class, event -> buildDesktop());
|
||||
}else{
|
||||
buildMobile();
|
||||
Events.on(ResizeEvent.class, event -> buildMobile());
|
||||
}
|
||||
});
|
||||
|
||||
//info icon
|
||||
if(mobile){
|
||||
parent.fill(c -> c.bottom().left().addButton("", Styles.infot, ui.about::show).size(84, 45));
|
||||
parent.fill(c -> c.bottom().right().addButton("", Styles.discordt, ui.discord::show).size(84, 45));
|
||||
}
|
||||
|
||||
String versionText = "[#ffffffba]" + ((Version.build == -1) ? "[#fc8140aa]custom build" : (Version.type.equals("official") ? Version.modifier : Version.type) + " build " + Version.build + (Version.revision == 0 ? "" : "." + Version.revision));
|
||||
|
||||
parent.fill((x, y, w, h) -> {
|
||||
TextureRegion logo = Core.atlas.find("logo");
|
||||
float logoscl = Scl.scl(1);
|
||||
float logow = Math.min(logo.getWidth() * logoscl, Core.graphics.getWidth() - Scl.scl(20));
|
||||
float logoh = logow * (float)logo.getHeight() / logo.getWidth();
|
||||
|
||||
float fx = (int)(Core.graphics.getWidth() / 2f);
|
||||
float fy = (int)(Core.graphics.getHeight() - 6 - logoh) + logoh / 2 - (Core.graphics.isPortrait() ? Scl.scl(30f) : 0f);
|
||||
|
||||
Draw.color();
|
||||
Draw.rect(logo, fx, fy, logow, logoh);
|
||||
|
||||
Fonts.def.setColor(Color.white);
|
||||
Fonts.def.draw(versionText, fx, fy - logoh/2f, Align.center);
|
||||
}).touchable(Touchable.disabled);
|
||||
}
|
||||
|
||||
private void buildMobile(){
|
||||
container.clear();
|
||||
container.setSize(Core.graphics.getWidth(), Core.graphics.getHeight());
|
||||
|
||||
float size = 120f;
|
||||
container.defaults().size(size).pad(5).padTop(4f);
|
||||
|
||||
MobileButton
|
||||
play = new MobileButton(Icon.play2, "$campaign", () -> checkPlay(ui.deploy::show)),
|
||||
custom = new MobileButton(Icon.playCustom, "$customgame", () -> checkPlay(ui.custom::show)),
|
||||
maps = new MobileButton(Icon.load, "$loadgame", () -> checkPlay(ui.load::show)),
|
||||
join = new MobileButton(Icon.add, "$joingame", () -> checkPlay(ui.join::show)),
|
||||
editor = new MobileButton(Icon.editor, "$editor", () -> checkPlay(ui.maps::show)),
|
||||
tools = new MobileButton(Icon.tools, "$settings", ui.settings::show),
|
||||
mods = new MobileButton(Icon.wiki, "$mods", ui.mods::show),
|
||||
donate = new MobileButton(Icon.link, "$website", () -> Core.net.openURI("https://anuke.itch.io/mindustry")),
|
||||
exit = new MobileButton(Icon.exit, "$quit", () -> Core.app.exit());
|
||||
|
||||
if(!Core.graphics.isPortrait()){
|
||||
container.marginTop(60f);
|
||||
container.add(play);
|
||||
container.add(join);
|
||||
container.add(custom);
|
||||
container.add(maps);
|
||||
container.row();
|
||||
|
||||
container.table(table -> {
|
||||
table.defaults().set(container.defaults());
|
||||
|
||||
table.add(editor);
|
||||
table.add(tools);
|
||||
|
||||
table.add(mods);
|
||||
//if(platform.canDonate()) table.add(donate);
|
||||
if(!ios) table.add(exit);
|
||||
}).colspan(4);
|
||||
}else{
|
||||
container.marginTop(0f);
|
||||
container.add(play);
|
||||
container.add(maps);
|
||||
container.row();
|
||||
container.add(custom);
|
||||
container.add(join);
|
||||
container.row();
|
||||
container.add(editor);
|
||||
container.add(tools);
|
||||
container.row();
|
||||
|
||||
container.table(table -> {
|
||||
table.defaults().set(container.defaults());
|
||||
|
||||
table.add(mods);
|
||||
//if(platform.canDonate()) table.add(donate);
|
||||
if(!ios) table.add(exit);
|
||||
}).colspan(2);
|
||||
}
|
||||
}
|
||||
|
||||
private void buildDesktop(){
|
||||
container.clear();
|
||||
container.setSize(Core.graphics.getWidth(), Core.graphics.getHeight());
|
||||
|
||||
|
||||
float width = 230f;
|
||||
Drawable background = Styles.black6;
|
||||
|
||||
container.left();
|
||||
container.add().width(Core.graphics.getWidth()/10f);
|
||||
container.table(background, t -> {
|
||||
t.defaults().width(width).height(70f);
|
||||
|
||||
buttons(t,
|
||||
new Buttoni("$play", Icon.play2Small,
|
||||
new Buttoni("$campaign", Icon.play2Small, () -> checkPlay(ui.deploy::show)),
|
||||
new Buttoni("$joingame", Icon.addSmall, () -> checkPlay(ui.join::show)),
|
||||
new Buttoni("$customgame", Icon.editorSmall, () -> checkPlay(ui.custom::show)),
|
||||
new Buttoni("$loadgame", Icon.loadSmall, () -> checkPlay(ui.load::show)),
|
||||
new Buttoni("$tutorial", Icon.infoSmall, () -> checkPlay(control::playTutorial))
|
||||
),
|
||||
new Buttoni("$editor", Icon.editorSmall, () -> checkPlay(ui.maps::show)), steam ? new Buttoni("$workshop", Icon.saveSmall, platform::openWorkshop) : null,
|
||||
new Buttoni(Core.bundle.get("mods") + "\n" + Core.bundle.get("mods.alpha"), Icon.wikiSmall, ui.mods::show),
|
||||
//not enough space for this button
|
||||
//new Buttoni("$schematics", Icon.pasteSmall, ui.schematics::show),
|
||||
new Buttoni("$settings", Icon.toolsSmall, ui.settings::show),
|
||||
new Buttoni("$about.button", Icon.infoSmall, ui.about::show),
|
||||
new Buttoni("$quit", Icon.exitSmall, Core.app::exit)
|
||||
);
|
||||
|
||||
}).width(width).growY();
|
||||
|
||||
container.table(background, t -> {
|
||||
submenu = t;
|
||||
t.getColor().a = 0f;
|
||||
t.top();
|
||||
t.defaults().width(width).height(70f);
|
||||
t.visible(() -> !t.getChildren().isEmpty());
|
||||
|
||||
}).width(width).growY();
|
||||
}
|
||||
|
||||
private void checkPlay(Runnable run){
|
||||
if(!mods.hasContentErrors()){
|
||||
run.run();
|
||||
}else{
|
||||
ui.showInfo("$mod.noerrorplay");
|
||||
}
|
||||
}
|
||||
|
||||
private void fadeInMenu(){
|
||||
submenu.clearActions();
|
||||
submenu.actions(Actions.alpha(1f, 0.15f, Interpolation.fade));
|
||||
}
|
||||
|
||||
private void fadeOutMenu(){
|
||||
//nothing to fade out
|
||||
if(submenu.getChildren().isEmpty()){
|
||||
return;
|
||||
}
|
||||
|
||||
submenu.clearActions();
|
||||
submenu.actions(Actions.alpha(1f), Actions.alpha(0f, 0.2f, Interpolation.fade), Actions.run(() -> submenu.clearChildren()));
|
||||
}
|
||||
|
||||
private void buttons(Table t, Buttoni... buttons){
|
||||
for(Buttoni b : buttons){
|
||||
if(b == null) continue;
|
||||
Button[] out = {null};
|
||||
out[0] = t.addImageTextButton(b.text, b.icon, Styles.clearToggleMenut, () -> {
|
||||
if(currentMenu == out[0]){
|
||||
currentMenu = null;
|
||||
fadeOutMenu();
|
||||
}else{
|
||||
if(b.submenu != null){
|
||||
currentMenu = out[0];
|
||||
submenu.clearChildren();
|
||||
fadeInMenu();
|
||||
//correctly offset the button
|
||||
submenu.add().height((Core.graphics.getHeight() - out[0].getY(Align.topLeft)) / Scl.scl(1f));
|
||||
submenu.row();
|
||||
buttons(submenu, b.submenu);
|
||||
}else{
|
||||
currentMenu = null;
|
||||
fadeOutMenu();
|
||||
b.runnable.run();
|
||||
}
|
||||
}
|
||||
}).marginLeft(11f).get();
|
||||
out[0].update(() -> out[0].setChecked(currentMenu == out[0]));
|
||||
t.row();
|
||||
}
|
||||
}
|
||||
|
||||
private class Buttoni{
|
||||
final Drawable icon;
|
||||
final String text;
|
||||
final Runnable runnable;
|
||||
final Buttoni[] submenu;
|
||||
|
||||
public Buttoni(String text, Drawable icon, Runnable runnable){
|
||||
this.icon = icon;
|
||||
this.text = text;
|
||||
this.runnable = runnable;
|
||||
this.submenu = null;
|
||||
}
|
||||
|
||||
public Buttoni(String text, Drawable icon, Buttoni... buttons){
|
||||
this.icon = icon;
|
||||
this.text = text;
|
||||
this.runnable = () -> {};
|
||||
this.submenu = buttons;
|
||||
}
|
||||
}
|
||||
}
|
||||
32
core/src/mindustry/ui/fragments/OverlayFragment.java
Normal file
32
core/src/mindustry/ui/fragments/OverlayFragment.java
Normal file
@@ -0,0 +1,32 @@
|
||||
package mindustry.ui.fragments;
|
||||
|
||||
import arc.*;
|
||||
import arc.scene.event.*;
|
||||
import arc.scene.ui.layout.*;
|
||||
import mindustry.*;
|
||||
|
||||
/** Fragment for displaying overlays such as block inventories. */
|
||||
public class OverlayFragment{
|
||||
public final BlockInventoryFragment inv;
|
||||
public final BlockConfigFragment config;
|
||||
|
||||
private WidgetGroup group = new WidgetGroup();
|
||||
|
||||
public OverlayFragment(){
|
||||
group.touchable(Touchable.childrenOnly);
|
||||
inv = new BlockInventoryFragment();
|
||||
config = new BlockConfigFragment();
|
||||
}
|
||||
|
||||
public void add(){
|
||||
group.setFillParent(true);
|
||||
Vars.ui.hudGroup.addChildBefore(Core.scene.find("overlaymarker"), group);
|
||||
|
||||
inv.build(group);
|
||||
config.build(group);
|
||||
}
|
||||
|
||||
public void remove(){
|
||||
group.remove();
|
||||
}
|
||||
}
|
||||
467
core/src/mindustry/ui/fragments/PlacementFragment.java
Normal file
467
core/src/mindustry/ui/fragments/PlacementFragment.java
Normal file
@@ -0,0 +1,467 @@
|
||||
package mindustry.ui.fragments;
|
||||
|
||||
import arc.*;
|
||||
import arc.struct.*;
|
||||
import arc.graphics.*;
|
||||
import arc.math.geom.*;
|
||||
import arc.scene.*;
|
||||
import arc.scene.event.*;
|
||||
import arc.scene.style.*;
|
||||
import arc.scene.ui.*;
|
||||
import arc.scene.ui.layout.*;
|
||||
import arc.util.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.entities.traits.BuilderTrait.*;
|
||||
import mindustry.entities.type.*;
|
||||
import mindustry.game.EventType.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.input.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.ui.*;
|
||||
import mindustry.ui.Cicon;
|
||||
import mindustry.world.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class PlacementFragment extends Fragment{
|
||||
final int rowWidth = 4;
|
||||
|
||||
public Category currentCategory = Category.distribution;
|
||||
Array<Block> returnArray = new Array<>();
|
||||
Array<Category> returnCatArray = new Array<>();
|
||||
boolean[] categoryEmpty = new boolean[Category.all.length];
|
||||
ObjectMap<Category,Block> selectedBlocks = new ObjectMap<Category,Block>();
|
||||
Block hovered, lastDisplay;
|
||||
Tile lastHover;
|
||||
Tile hoverTile;
|
||||
Table blockTable, toggler, topTable;
|
||||
boolean lastGround;
|
||||
boolean blockSelectEnd;
|
||||
int blockSelectSeq;
|
||||
long blockSelectSeqMillis;
|
||||
Binding[] blockSelect = {
|
||||
Binding.block_select_01,
|
||||
Binding.block_select_02,
|
||||
Binding.block_select_03,
|
||||
Binding.block_select_04,
|
||||
Binding.block_select_05,
|
||||
Binding.block_select_06,
|
||||
Binding.block_select_07,
|
||||
Binding.block_select_08,
|
||||
Binding.block_select_09,
|
||||
Binding.block_select_10,
|
||||
Binding.block_select_left,
|
||||
Binding.block_select_right,
|
||||
Binding.block_select_up,
|
||||
Binding.block_select_down
|
||||
};
|
||||
|
||||
public PlacementFragment(){
|
||||
Events.on(WorldLoadEvent.class, event -> {
|
||||
Core.app.post(() -> {
|
||||
control.input.block = null;
|
||||
rebuild();
|
||||
});
|
||||
});
|
||||
|
||||
Events.on(UnlockEvent.class, event -> {
|
||||
if(event.content instanceof Block){
|
||||
rebuild();
|
||||
}
|
||||
});
|
||||
|
||||
Events.on(ResetEvent.class, event -> {
|
||||
selectedBlocks.clear();
|
||||
});
|
||||
}
|
||||
|
||||
void rebuild(){
|
||||
currentCategory = Category.turret;
|
||||
Group group = toggler.getParent();
|
||||
int index = toggler.getZIndex();
|
||||
toggler.remove();
|
||||
build(group);
|
||||
toggler.setZIndex(index);
|
||||
}
|
||||
|
||||
boolean gridUpdate(InputHandler input){
|
||||
if(Core.input.keyDown(Binding.pick)){ //mouse eyedropper select
|
||||
Tile tile = world.ltileWorld(Core.input.mouseWorld().x, Core.input.mouseWorld().y);
|
||||
Block tryRecipe = tile == null ? null : tile.block();
|
||||
|
||||
for(BuildRequest req : player.buildQueue()){
|
||||
if(!req.breaking && req.block.bounds(req.x, req.y, Tmp.r1).contains(Core.input.mouseWorld())){
|
||||
tryRecipe = req.block;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(tryRecipe != null && tryRecipe.isVisible() && unlocked(tryRecipe)){
|
||||
input.block = tryRecipe;
|
||||
currentCategory = input.block.category;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if(ui.chatfrag.shown() || Core.scene.hasKeyboard()) return false;
|
||||
|
||||
for(int i = 0; i < blockSelect.length; i++){
|
||||
if(Core.input.keyTap(blockSelect[i])){
|
||||
if(i > 9) { //select block directionally
|
||||
Array<Block> blocks = getByCategory(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
|
||||
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);
|
||||
}
|
||||
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
|
||||
//select only visible categories
|
||||
if(!getByCategory(Category.all[i]).isEmpty()){
|
||||
currentCategory = Category.all[i];
|
||||
if(input.block != null){
|
||||
input.block = getSelectedBlock(currentCategory);
|
||||
}
|
||||
blockSelectEnd = false;
|
||||
blockSelectSeq = 0;
|
||||
blockSelectSeqMillis = Time.millis();
|
||||
}
|
||||
}else{ //select block
|
||||
if(blockSelectSeq == 0){ //2nd number of combo
|
||||
blockSelectSeq = i + 1;
|
||||
}else{ //3rd number of combo
|
||||
//entering "X,1,0" selects the same block as "X,0"
|
||||
i += (blockSelectSeq - (i != 9 ? 0 : 1)) * 10;
|
||||
blockSelectEnd = true;
|
||||
}
|
||||
Array<Block> blocks = getByCategory(currentCategory);
|
||||
input.block = (i < blocks.size) ? blocks.get(i) : null;
|
||||
selectedBlocks.put(currentCategory, input.block);
|
||||
blockSelectSeqMillis = Time.millis();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if(Core.input.keyTap(Binding.category_prev)){
|
||||
do{
|
||||
currentCategory = currentCategory.prev();
|
||||
}while(categoryEmpty[currentCategory.ordinal()]);
|
||||
input.block = getSelectedBlock(currentCategory);
|
||||
return true;
|
||||
}
|
||||
|
||||
if(Core.input.keyTap(Binding.category_next)){
|
||||
do{
|
||||
currentCategory = currentCategory.next();
|
||||
}while(categoryEmpty[currentCategory.ordinal()]);
|
||||
input.block = getSelectedBlock(currentCategory);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void build(Group parent){
|
||||
parent.fill(full -> {
|
||||
toggler = full;
|
||||
full.bottom().right().visible(() -> ui.hudfrag.shown());
|
||||
|
||||
full.table(frame -> {
|
||||
|
||||
//rebuilds the category table with the correct recipes
|
||||
Runnable rebuildCategory = () -> {
|
||||
blockTable.clear();
|
||||
blockTable.top().margin(5);
|
||||
|
||||
int index = 0;
|
||||
|
||||
ButtonGroup<ImageButton> group = new ButtonGroup<>();
|
||||
group.setMinCheckCount(0);
|
||||
|
||||
for(Block block : getByCategory(currentCategory)){
|
||||
if(index++ % rowWidth == 0){
|
||||
blockTable.row();
|
||||
}
|
||||
|
||||
ImageButton button = blockTable.addImageButton(Icon.lockedSmall, Styles.selecti, () -> {
|
||||
if(unlocked(block)){
|
||||
control.input.block = control.input.block == block ? null : block;
|
||||
selectedBlocks.put(currentCategory, control.input.block);
|
||||
}
|
||||
}).size(46f).group(group).name("block-" + block.name).get();
|
||||
|
||||
button.getStyle().imageUp = new TextureRegionDrawable(block.icon(Cicon.medium));
|
||||
|
||||
button.update(() -> { //color unplacable things gray
|
||||
TileEntity core = player.getClosestCore();
|
||||
Color color = state.rules.infiniteResources || (core != null && (core.items.has(block.requirements, state.rules.buildCostMultiplier) || state.rules.infiniteResources)) ? Color.white : Color.gray;
|
||||
button.forEach(elem -> elem.setColor(color));
|
||||
button.setChecked(control.input.block == block);
|
||||
|
||||
if(state.rules.bannedBlocks.contains(block)){
|
||||
button.forEach(elem -> elem.setColor(Color.darkGray));
|
||||
}
|
||||
});
|
||||
|
||||
button.hovered(() -> hovered = block);
|
||||
button.exited(() -> {
|
||||
if(hovered == block){
|
||||
hovered = null;
|
||||
}
|
||||
});
|
||||
}
|
||||
//add missing elements to even out table size
|
||||
if(index < 4){
|
||||
for(int i = 0; i < 4-index; i++){
|
||||
blockTable.add().size(46f);
|
||||
}
|
||||
}
|
||||
blockTable.act(0f);
|
||||
};
|
||||
|
||||
//top table with hover info
|
||||
frame.table(Tex.buttonEdge2,top -> {
|
||||
topTable = top;
|
||||
top.add(new Table()).growX().update(topTable -> {
|
||||
//don't refresh unnecessarily
|
||||
if((tileDisplayBlock() == null && lastDisplay == getSelected() && !lastGround)
|
||||
|| (tileDisplayBlock() != null && lastHover == hoverTile && lastDisplay == tileDisplayBlock() && lastGround))
|
||||
return;
|
||||
|
||||
topTable.clear();
|
||||
topTable.top().left().margin(5);
|
||||
|
||||
lastHover = hoverTile;
|
||||
lastDisplay = getSelected();
|
||||
lastGround = tileDisplayBlock() != null;
|
||||
|
||||
if(lastDisplay != null){ //show selected recipe
|
||||
lastGround = false;
|
||||
|
||||
topTable.table(header -> {
|
||||
String keyCombo = "";
|
||||
if(!mobile && Core.settings.getBool("blockselectkeys")){
|
||||
Array<Block> blocks = getByCategory(currentCategory);
|
||||
for(int i = 0; i < blocks.size; i++){
|
||||
if(blocks.get(i) == lastDisplay && (i + 1) / 10 - 1 < blockSelect.length){
|
||||
keyCombo = Core.bundle.format("placement.blockselectkeys", Core.keybinds.get(blockSelect[currentCategory.ordinal()]).key.toString())
|
||||
+ (i < 10 ? "" : Core.keybinds.get(blockSelect[(i + 1) / 10 - 1]).key.toString() + ",")
|
||||
+ Core.keybinds.get(blockSelect[i % 10]).key.toString() + "]";
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
final String keyComboFinal = keyCombo;
|
||||
header.left();
|
||||
header.add(new Image(lastDisplay.icon(Cicon.medium))).size(8 * 4);
|
||||
header.labelWrap(() -> !unlocked(lastDisplay) ? Core.bundle.get("block.unknown") : lastDisplay.localizedName + keyComboFinal)
|
||||
.left().width(190f).padLeft(5);
|
||||
header.add().growX();
|
||||
if(unlocked(lastDisplay)){
|
||||
header.addButton("?", Styles.clearPartialt, () -> {
|
||||
ui.content.show(lastDisplay);
|
||||
Events.fire(new BlockInfoEvent());
|
||||
}).size(8 * 5).padTop(-5).padRight(-5).right().grow().name("blockinfo");
|
||||
}
|
||||
}).growX().left();
|
||||
topTable.row();
|
||||
//add requirement table
|
||||
topTable.table(req -> {
|
||||
req.top().left();
|
||||
|
||||
for(ItemStack stack : lastDisplay.requirements){
|
||||
req.table(line -> {
|
||||
line.left();
|
||||
line.addImage(stack.item.icon(Cicon.small)).size(8 * 2);
|
||||
line.add(stack.item.localizedName).maxWidth(140f).fillX().color(Color.lightGray).padLeft(2).left().get().setEllipsis(true);
|
||||
line.labelWrap(() -> {
|
||||
TileEntity core = player.getClosestCore();
|
||||
if(core == null || state.rules.infiniteResources) return "*/*";
|
||||
|
||||
int amount = core.items.get(stack.item);
|
||||
int stackamount = Math.round(stack.amount * state.rules.buildCostMultiplier);
|
||||
String color = (amount < stackamount / 2f ? "[red]" : amount < stackamount ? "[accent]" : "[white]");
|
||||
|
||||
return color + ui.formatAmount(amount) + "[white]/" + stackamount;
|
||||
}).padLeft(5);
|
||||
}).left();
|
||||
req.row();
|
||||
}
|
||||
}).growX().left().margin(3);
|
||||
|
||||
if(state.rules.bannedBlocks.contains(lastDisplay)){
|
||||
topTable.row();
|
||||
topTable.table(b -> {
|
||||
b.addImage(Icon.cancelSmall).padRight(2).color(Color.scarlet);
|
||||
b.add("$banned");
|
||||
b.left();
|
||||
}).padTop(2).left();
|
||||
}
|
||||
|
||||
}else if(tileDisplayBlock() != null){ //show selected tile
|
||||
lastDisplay = tileDisplayBlock();
|
||||
topTable.table(t -> {
|
||||
t.left();
|
||||
t.add(new Image(lastDisplay.getDisplayIcon(hoverTile))).size(8 * 4);
|
||||
t.labelWrap(lastDisplay.getDisplayName(hoverTile)).left().width(190f).padLeft(5);
|
||||
}).growX().left();
|
||||
if(hoverTile.getTeam() == player.getTeam()){
|
||||
topTable.row();
|
||||
topTable.table(t -> {
|
||||
t.left().defaults().left();
|
||||
lastDisplay.display(hoverTile, t);
|
||||
}).left().growX();
|
||||
}
|
||||
}
|
||||
});
|
||||
}).colspan(3).fillX().visible(() -> getSelected() != null || tileDisplayBlock() != null).touchable(Touchable.enabled);
|
||||
frame.row();
|
||||
frame.addImage().color(Pal.gray).colspan(3).height(4).growX();
|
||||
frame.row();
|
||||
frame.table(Tex.pane2, blocksSelect -> {
|
||||
blocksSelect.margin(4).marginTop(0);
|
||||
blocksSelect.pane(blocks -> blockTable = blocks).height(194f).update(pane -> {
|
||||
if(pane.hasScroll()){
|
||||
Element result = Core.scene.hit(Core.input.mouseX(), Core.input.mouseY(), true);
|
||||
if(result == null || !result.isDescendantOf(pane)){
|
||||
Core.scene.setScrollFocus(null);
|
||||
}
|
||||
}
|
||||
}).grow().get().setStyle(Styles.smallPane);
|
||||
blocksSelect.row();
|
||||
blocksSelect.table(control.input::buildPlacementUI).name("inputTable").growX();
|
||||
}).fillY().bottom().touchable(Touchable.enabled);
|
||||
frame.table(categories -> {
|
||||
categories.bottom();
|
||||
categories.add(new Image(Styles.black6){
|
||||
@Override
|
||||
public void draw(){
|
||||
if(height <= Scl.scl(3f)) return;
|
||||
getDrawable().draw(x, y, width, height - Scl.scl(3f));
|
||||
}
|
||||
}).colspan(2).growX().growY().padTop(-3f).row();
|
||||
categories.defaults().size(50f);
|
||||
|
||||
ButtonGroup<ImageButton> group = new ButtonGroup<>();
|
||||
|
||||
//update category empty values
|
||||
for(Category cat : Category.all){
|
||||
Array<Block> blocks = getByCategory(cat);
|
||||
categoryEmpty[cat.ordinal()] = blocks.isEmpty();
|
||||
}
|
||||
|
||||
int f = 0;
|
||||
for(Category cat : getCategories()){
|
||||
if(f++ % 2 == 0) categories.row();
|
||||
|
||||
if(categoryEmpty[cat.ordinal()]){
|
||||
categories.addImage(Styles.black6);
|
||||
continue;
|
||||
}
|
||||
|
||||
categories.addImageButton(Core.atlas.drawable("icon-" + cat.name() + "-smaller"), Styles.clearToggleTransi, () -> {
|
||||
currentCategory = cat;
|
||||
if(control.input.block != null){
|
||||
control.input.block = getSelectedBlock(currentCategory);
|
||||
}
|
||||
rebuildCategory.run();
|
||||
}).group(group).update(i -> i.setChecked(currentCategory == cat)).name("category-" + cat.name());
|
||||
}
|
||||
}).fillY().bottom().touchable(Touchable.enabled);
|
||||
|
||||
rebuildCategory.run();
|
||||
frame.update(() -> {
|
||||
if(gridUpdate(control.input)) rebuildCategory.run();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Array<Category> getCategories(){
|
||||
returnCatArray.clear();
|
||||
returnCatArray.addAll(Category.all);
|
||||
returnCatArray.sort((c1, c2) -> Boolean.compare(categoryEmpty[c1.ordinal()], categoryEmpty[c2.ordinal()]));
|
||||
return returnCatArray;
|
||||
}
|
||||
|
||||
Array<Block> getByCategory(Category cat){
|
||||
returnArray.clear();
|
||||
for(Block block : content.blocks()){
|
||||
if(block.category == cat && block.isVisible() && unlocked(block)){
|
||||
returnArray.add(block);
|
||||
}
|
||||
}
|
||||
returnArray.sort((b1, b2) -> {
|
||||
int locked = -Boolean.compare(unlocked(b1), unlocked(b2));
|
||||
if(locked != 0) return locked;
|
||||
return Boolean.compare(state.rules.bannedBlocks.contains(b1), state.rules.bannedBlocks.contains(b2));
|
||||
});
|
||||
return returnArray;
|
||||
}
|
||||
|
||||
Block getSelectedBlock(Category cat){
|
||||
if(selectedBlocks.get(cat) == null){
|
||||
selectedBlocks.put(cat, getByCategory(cat).find(this::unlocked));
|
||||
}
|
||||
return selectedBlocks.get(cat);
|
||||
}
|
||||
|
||||
boolean unlocked(Block block){
|
||||
return !world.isZone() || data.isUnlocked(block);
|
||||
}
|
||||
|
||||
/** Returns the currently displayed block in the top box. */
|
||||
Block getSelected(){
|
||||
Block toDisplay = null;
|
||||
|
||||
Vector2 v = topTable.stageToLocalCoordinates(Core.input.mouse());
|
||||
|
||||
//setup hovering tile
|
||||
if(!Core.scene.hasMouse() && topTable.hit(v.x, v.y, false) == null){
|
||||
Tile tile = world.tileWorld(Core.input.mouseWorld().x, Core.input.mouseWorld().y);
|
||||
if(tile != null){
|
||||
hoverTile = tile.link();
|
||||
}else{
|
||||
hoverTile = null;
|
||||
}
|
||||
}else{
|
||||
hoverTile = null;
|
||||
}
|
||||
|
||||
//block currently selected
|
||||
if(control.input.block != null){
|
||||
toDisplay = control.input.block;
|
||||
}
|
||||
|
||||
//block hovered on in build menu
|
||||
if(hovered != null){
|
||||
toDisplay = hovered;
|
||||
}
|
||||
|
||||
return toDisplay;
|
||||
}
|
||||
|
||||
/** Returns the block currently being hovered over in the world. */
|
||||
Block tileDisplayBlock(){
|
||||
return hoverTile == null ? null : hoverTile.block().synthetic() ? hoverTile.block() : hoverTile.drop() != null && hoverTile.block() == Blocks.air ? hoverTile.overlay().itemDrop != null ? hoverTile.overlay() : hoverTile.floor() : null;
|
||||
}
|
||||
}
|
||||
155
core/src/mindustry/ui/fragments/PlayerListFragment.java
Normal file
155
core/src/mindustry/ui/fragments/PlayerListFragment.java
Normal file
@@ -0,0 +1,155 @@
|
||||
package mindustry.ui.fragments;
|
||||
|
||||
import arc.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.scene.*;
|
||||
import arc.scene.event.*;
|
||||
import arc.scene.ui.*;
|
||||
import arc.scene.ui.layout.*;
|
||||
import arc.util.*;
|
||||
import mindustry.core.GameState.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.net.*;
|
||||
import mindustry.net.Packets.*;
|
||||
import mindustry.ui.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class PlayerListFragment extends Fragment{
|
||||
private boolean visible = false;
|
||||
private Table content = new Table().marginRight(13f).marginLeft(13f);
|
||||
private Interval timer = new Interval();
|
||||
|
||||
@Override
|
||||
public void build(Group parent){
|
||||
parent.fill(cont -> {
|
||||
cont.visible(() -> visible);
|
||||
cont.update(() -> {
|
||||
if(!(net.active() && !state.is(State.menu))){
|
||||
visible = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if(visible && timer.get(20)){
|
||||
rebuild();
|
||||
content.pack();
|
||||
content.act(Core.graphics.getDeltaTime());
|
||||
//TODO hack
|
||||
Core.scene.act(0f);
|
||||
}
|
||||
});
|
||||
|
||||
cont.table(Tex.buttonTrans, pane -> {
|
||||
pane.label(() -> Core.bundle.format(playerGroup.size() == 1 ? "players.single" : "players", playerGroup.size()));
|
||||
pane.row();
|
||||
pane.pane(content).grow().get().setScrollingDisabled(true, false);
|
||||
pane.row();
|
||||
|
||||
pane.table(menu -> {
|
||||
menu.defaults().growX().height(50f).fillY();
|
||||
|
||||
menu.addButton("$server.bans", ui.bans::show).disabled(b -> net.client());
|
||||
menu.addButton("$server.admins", ui.admins::show).disabled(b -> net.client());
|
||||
menu.addButton("$close", this::toggle);
|
||||
}).margin(0f).pad(10f).growX();
|
||||
|
||||
}).touchable(Touchable.enabled).margin(14f);
|
||||
});
|
||||
|
||||
rebuild();
|
||||
}
|
||||
|
||||
public void rebuild(){
|
||||
content.clear();
|
||||
|
||||
float h = 74f;
|
||||
|
||||
playerGroup.all().sort((p1, p2) -> p1.getTeam().compareTo(p2.getTeam()));
|
||||
playerGroup.all().each(user -> {
|
||||
NetConnection connection = user.con;
|
||||
|
||||
if(connection == null && net.server() && !user.isLocal) return;
|
||||
|
||||
Table button = new Table();
|
||||
button.left();
|
||||
button.margin(5).marginBottom(10);
|
||||
|
||||
Table table = new Table(){
|
||||
@Override
|
||||
public void draw(){
|
||||
super.draw();
|
||||
Draw.color(Pal.gray);
|
||||
Draw.alpha(parentAlpha);
|
||||
Lines.stroke(Scl.scl(4f));
|
||||
Lines.rect(x, y, width, height);
|
||||
Draw.reset();
|
||||
}
|
||||
};
|
||||
table.margin(8);
|
||||
table.add(new Image(user.getIconRegion()).setScaling(Scaling.none)).grow();
|
||||
|
||||
button.add(table).size(h);
|
||||
button.labelWrap("[#" + user.color.toString().toUpperCase() + "]" + user.name).width(170f).pad(10);
|
||||
button.add().grow();
|
||||
|
||||
button.addImage(Icon.admin).visible(() -> user.isAdmin && !(!user.isLocal && net.server())).padRight(5).get().updateVisibility();
|
||||
|
||||
if((net.server() || player.isAdmin) && !user.isLocal && (!user.isAdmin || net.server())){
|
||||
button.add().growY();
|
||||
|
||||
float bs = (h) / 2f;
|
||||
|
||||
button.table(t -> {
|
||||
t.defaults().size(bs);
|
||||
|
||||
t.addImageButton(Icon.banSmall, Styles.clearPartiali,
|
||||
() -> ui.showConfirm("$confirm", "$confirmban", () -> Call.onAdminRequest(user, AdminAction.ban)));
|
||||
t.addImageButton(Icon.cancelSmall, Styles.clearPartiali,
|
||||
() -> ui.showConfirm("$confirm", "$confirmkick", () -> Call.onAdminRequest(user, AdminAction.kick)));
|
||||
|
||||
t.row();
|
||||
|
||||
t.addImageButton(Icon.adminSmall, Styles.clearTogglePartiali, () -> {
|
||||
if(net.client()) return;
|
||||
|
||||
String id = user.uuid;
|
||||
|
||||
if(netServer.admins.isAdmin(id, connection.address)){
|
||||
ui.showConfirm("$confirm", "$confirmunadmin", () -> netServer.admins.unAdminPlayer(id));
|
||||
}else{
|
||||
ui.showConfirm("$confirm", "$confirmadmin", () -> netServer.admins.adminPlayer(id, user.usid));
|
||||
}
|
||||
})
|
||||
.update(b -> b.setChecked(user.isAdmin))
|
||||
.disabled(b -> net.client())
|
||||
.touchable(() -> net.client() ? Touchable.disabled : Touchable.enabled)
|
||||
.checked(user.isAdmin);
|
||||
|
||||
t.addImageButton(Icon.zoomSmall, Styles.clearPartiali, () -> Call.onAdminRequest(user, AdminAction.trace));
|
||||
|
||||
}).padRight(12).size(bs + 10f, bs);
|
||||
}else if((!user.isLocal && !user.isAdmin) && net.client() && playerGroup.size() >= 3){ //votekick
|
||||
button.add().growY();
|
||||
|
||||
button.addImageButton(Icon.banSmall, Styles.clearPartiali,
|
||||
() -> ui.showConfirm("$confirm", "$confirmvotekick", () -> Call.sendChatMessage("/votekick " + user.name))).size(h);
|
||||
}
|
||||
|
||||
content.add(button).padBottom(-6).width(350f).maxHeight(h + 14);
|
||||
content.row();
|
||||
content.addImage().height(4f).color(state.rules.pvp ? user.getTeam().color : Pal.gray).growX();
|
||||
content.row();
|
||||
});
|
||||
|
||||
content.marginBottom(5);
|
||||
}
|
||||
|
||||
public void toggle(){
|
||||
visible = !visible;
|
||||
if(visible){
|
||||
rebuild();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
221
core/src/mindustry/ui/fragments/ScriptConsoleFragment.java
Normal file
221
core/src/mindustry/ui/fragments/ScriptConsoleFragment.java
Normal file
@@ -0,0 +1,221 @@
|
||||
package mindustry.ui.fragments;
|
||||
|
||||
import arc.*;
|
||||
import arc.Input.*;
|
||||
import arc.struct.*;
|
||||
import arc.graphics.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.math.*;
|
||||
import arc.scene.*;
|
||||
import arc.scene.ui.*;
|
||||
import arc.scene.ui.Label.*;
|
||||
import arc.scene.ui.layout.*;
|
||||
import arc.util.*;
|
||||
import mindustry.*;
|
||||
import mindustry.input.*;
|
||||
import mindustry.ui.*;
|
||||
|
||||
import static arc.Core.*;
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class ScriptConsoleFragment extends Table{
|
||||
private final static int messagesShown = 30;
|
||||
private Array<String> messages = new Array<>();
|
||||
private boolean open = false, shown;
|
||||
private TextField chatfield;
|
||||
private Label fieldlabel = new Label(">");
|
||||
private BitmapFont font;
|
||||
private GlyphLayout layout = new GlyphLayout();
|
||||
private float offsetx = Scl.scl(4), offsety = Scl.scl(4), fontoffsetx = Scl.scl(2), chatspace = Scl.scl(50);
|
||||
private Color shadowColor = new Color(0, 0, 0, 0.4f);
|
||||
private float textspacing = Scl.scl(10);
|
||||
private Array<String> history = new Array<>();
|
||||
private int historyPos = 0;
|
||||
private int scrollPos = 0;
|
||||
private Fragment container = new Fragment(){
|
||||
@Override
|
||||
public void build(Group parent){
|
||||
scene.add(ScriptConsoleFragment.this);
|
||||
}
|
||||
};
|
||||
|
||||
public ScriptConsoleFragment(){
|
||||
|
||||
setFillParent(true);
|
||||
font = Fonts.def;
|
||||
|
||||
visible(() -> {
|
||||
if(input.keyTap(Binding.console) && !Vars.net.client() && (scene.getKeyboardFocus() == chatfield || scene.getKeyboardFocus() == null)){
|
||||
shown = !shown;
|
||||
if(shown && !open && enableConsole){
|
||||
toggle();
|
||||
}
|
||||
clearChatInput();
|
||||
}
|
||||
|
||||
return shown && !Vars.net.active();
|
||||
});
|
||||
|
||||
update(() -> {
|
||||
if(input.keyTap(Binding.chat) && enableConsole && (scene.getKeyboardFocus() == chatfield || scene.getKeyboardFocus() == null)){
|
||||
toggle();
|
||||
}
|
||||
|
||||
if(open){
|
||||
if(input.keyTap(Binding.chat_history_prev) && historyPos < history.size - 1){
|
||||
if(historyPos == 0) history.set(0, chatfield.getText());
|
||||
historyPos++;
|
||||
updateChat();
|
||||
}
|
||||
if(input.keyTap(Binding.chat_history_next) && historyPos > 0){
|
||||
historyPos--;
|
||||
updateChat();
|
||||
}
|
||||
}
|
||||
|
||||
scrollPos = (int)Mathf.clamp(scrollPos + input.axis(Binding.chat_scroll), 0, Math.max(0, messages.size - messagesShown));
|
||||
});
|
||||
|
||||
history.insert(0, "");
|
||||
setup();
|
||||
}
|
||||
|
||||
public Fragment container(){
|
||||
return container;
|
||||
}
|
||||
|
||||
public void clearMessages(){
|
||||
messages.clear();
|
||||
history.clear();
|
||||
history.insert(0, "");
|
||||
}
|
||||
|
||||
private void setup(){
|
||||
fieldlabel.setStyle(new LabelStyle(fieldlabel.getStyle()));
|
||||
fieldlabel.getStyle().font = font;
|
||||
fieldlabel.setStyle(fieldlabel.getStyle());
|
||||
|
||||
chatfield = new TextField("", new TextField.TextFieldStyle(scene.getStyle(TextField.TextFieldStyle.class)));
|
||||
chatfield.setMaxLength(Vars.maxTextLength);
|
||||
chatfield.getStyle().background = null;
|
||||
chatfield.getStyle().font = Fonts.chat;
|
||||
chatfield.getStyle().fontColor = Color.white;
|
||||
chatfield.setStyle(chatfield.getStyle());
|
||||
|
||||
bottom().left().marginBottom(offsety).marginLeft(offsetx * 2).add(fieldlabel).padBottom(6f);
|
||||
|
||||
add(chatfield).padBottom(offsety).padLeft(offsetx).growX().padRight(offsetx).height(28);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(){
|
||||
float opacity = 1f;
|
||||
float textWidth = graphics.getWidth() - offsetx*2f;
|
||||
|
||||
Draw.color(shadowColor);
|
||||
|
||||
if(open){
|
||||
Fill.crect(offsetx, chatfield.getY(), chatfield.getWidth() + 15f, chatfield.getHeight() - 1);
|
||||
}
|
||||
|
||||
super.draw();
|
||||
|
||||
float spacing = chatspace;
|
||||
|
||||
chatfield.visible(open);
|
||||
fieldlabel.visible(open);
|
||||
|
||||
Draw.color(shadowColor);
|
||||
Draw.alpha(shadowColor.a * opacity);
|
||||
|
||||
float theight = offsety + spacing + getMarginBottom();
|
||||
for(int i = scrollPos; i < messages.size && i < messagesShown + scrollPos; i++){
|
||||
|
||||
layout.setText(font, messages.get(i), Color.white, textWidth, Align.bottomLeft, true);
|
||||
theight += layout.height + textspacing;
|
||||
if(i - scrollPos == 0) theight -= textspacing + 1;
|
||||
|
||||
font.getCache().clear();
|
||||
font.getCache().addText(messages.get(i), fontoffsetx + offsetx, offsety + theight, textWidth, Align.bottomLeft, true);
|
||||
|
||||
if(!open){
|
||||
font.getCache().setAlphas(opacity);
|
||||
Draw.color(0, 0, 0, shadowColor.a * opacity);
|
||||
}else{
|
||||
font.getCache().setAlphas(opacity);
|
||||
}
|
||||
|
||||
Fill.crect(offsetx, theight - layout.height - 2, textWidth + Scl.scl(4f), layout.height + textspacing);
|
||||
Draw.color(shadowColor);
|
||||
Draw.alpha(opacity * shadowColor.a);
|
||||
|
||||
font.getCache().draw();
|
||||
}
|
||||
|
||||
Draw.color();
|
||||
}
|
||||
|
||||
private void sendMessage(){
|
||||
String message = chatfield.getText();
|
||||
clearChatInput();
|
||||
|
||||
if(message.replaceAll(" ", "").isEmpty()) return;
|
||||
|
||||
history.insert(1, message);
|
||||
|
||||
addMessage("[lightgray]> " + message.replace("[", "[["));
|
||||
addMessage(mods.getScripts().runConsole(message).replace("[", "[["));
|
||||
}
|
||||
|
||||
public void toggle(){
|
||||
|
||||
if(!open){
|
||||
scene.setKeyboardFocus(chatfield);
|
||||
open = !open;
|
||||
if(mobile){
|
||||
TextInput input = new TextInput();
|
||||
input.maxLength = maxTextLength;
|
||||
input.accepted = text -> {
|
||||
chatfield.setText(text);
|
||||
sendMessage();
|
||||
hide();
|
||||
Core.input.setOnscreenKeyboardVisible(false);
|
||||
};
|
||||
input.canceled = this::hide;
|
||||
Core.input.getTextInput(input);
|
||||
}else{
|
||||
chatfield.fireClick();
|
||||
}
|
||||
}else{
|
||||
scene.setKeyboardFocus(null);
|
||||
open = !open;
|
||||
scrollPos = 0;
|
||||
sendMessage();
|
||||
}
|
||||
}
|
||||
|
||||
public void hide(){
|
||||
scene.setKeyboardFocus(null);
|
||||
open = false;
|
||||
clearChatInput();
|
||||
}
|
||||
|
||||
public void updateChat(){
|
||||
chatfield.setText(history.get(historyPos));
|
||||
chatfield.setCursorPosition(chatfield.getText().length());
|
||||
}
|
||||
|
||||
public void clearChatInput(){
|
||||
historyPos = 0;
|
||||
history.set(0, "");
|
||||
chatfield.setText("");
|
||||
}
|
||||
|
||||
public boolean open(){
|
||||
return open;
|
||||
}
|
||||
|
||||
public void addMessage(String message){
|
||||
messages.insert(0, message);
|
||||
}
|
||||
}
|
||||
292
core/src/mindustry/ui/layout/BranchTreeLayout.java
Normal file
292
core/src/mindustry/ui/layout/BranchTreeLayout.java
Normal file
@@ -0,0 +1,292 @@
|
||||
package mindustry.ui.layout;
|
||||
|
||||
import arc.struct.*;
|
||||
import arc.math.geom.*;
|
||||
|
||||
/**
|
||||
* Algorithm taken from <a href="https://github.com/abego/treelayout">TreeLayout</a>.
|
||||
*/
|
||||
public class BranchTreeLayout implements TreeLayout{
|
||||
public TreeLocation rootLocation = TreeLocation.top;
|
||||
public TreeAlignment alignment = TreeAlignment.awayFromRoot;
|
||||
public float gapBetweenLevels = 10;
|
||||
public float gapBetweenNodes = 10f;
|
||||
|
||||
private final FloatArray sizeOfLevel = new FloatArray();
|
||||
private float boundsLeft = Float.MAX_VALUE;
|
||||
private float boundsRight = Float.MIN_VALUE;
|
||||
private float boundsTop = Float.MAX_VALUE;
|
||||
private float boundsBottom = Float.MIN_VALUE;
|
||||
|
||||
@Override
|
||||
public void layout(TreeNode root){
|
||||
firstWalk(root, null);
|
||||
calcSizeOfLevels(root, 0);
|
||||
secondWalk(root, -root.prelim, 0, 0);
|
||||
}
|
||||
|
||||
private float getWidthOrHeightOfNode(TreeNode treeNode, boolean returnWidth){
|
||||
return returnWidth ? treeNode.width : treeNode.height;
|
||||
}
|
||||
|
||||
private float getNodeThickness(TreeNode treeNode){
|
||||
return getWidthOrHeightOfNode(treeNode, !isLevelChangeInYAxis());
|
||||
}
|
||||
|
||||
private float getNodeSize(TreeNode treeNode){
|
||||
return getWidthOrHeightOfNode(treeNode, isLevelChangeInYAxis());
|
||||
}
|
||||
|
||||
private boolean isLevelChangeInYAxis(){
|
||||
return rootLocation == TreeLocation.top || rootLocation == TreeLocation.bottom;
|
||||
}
|
||||
|
||||
private int getLevelChangeSign(){
|
||||
return rootLocation == TreeLocation.bottom || rootLocation == TreeLocation.right ? -1 : 1;
|
||||
}
|
||||
|
||||
private void updateBounds(TreeNode node, float centerX, float centerY){
|
||||
float width = node.width;
|
||||
float height = node.height;
|
||||
float left = centerX - width / 2;
|
||||
float right = centerX + width / 2;
|
||||
float top = centerY - height / 2;
|
||||
float bottom = centerY + height / 2;
|
||||
if(boundsLeft > left){
|
||||
boundsLeft = left;
|
||||
}
|
||||
if(boundsRight < right){
|
||||
boundsRight = right;
|
||||
}
|
||||
if(boundsTop > top){
|
||||
boundsTop = top;
|
||||
}
|
||||
if(boundsBottom < bottom){
|
||||
boundsBottom = bottom;
|
||||
}
|
||||
}
|
||||
|
||||
public Rectangle getBounds(){
|
||||
return new Rectangle(boundsLeft, boundsBottom, boundsRight - boundsLeft, boundsTop - boundsBottom);
|
||||
}
|
||||
|
||||
private void calcSizeOfLevels(TreeNode node, int level){
|
||||
float oldSize;
|
||||
if(sizeOfLevel.size <= level){
|
||||
sizeOfLevel.add(0);
|
||||
oldSize = 0;
|
||||
}else{
|
||||
oldSize = sizeOfLevel.get(level);
|
||||
}
|
||||
|
||||
float size = getNodeThickness(node);
|
||||
if(oldSize < size){
|
||||
sizeOfLevel.set(level, size);
|
||||
}
|
||||
|
||||
if(!node.isLeaf()){
|
||||
for(TreeNode child : node.children){
|
||||
calcSizeOfLevels(child, level + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int getLevelCount(){
|
||||
return sizeOfLevel.size;
|
||||
}
|
||||
|
||||
public float getGapBetweenNodes(TreeNode a, TreeNode b){
|
||||
return gapBetweenNodes;
|
||||
}
|
||||
|
||||
public float getSizeOfLevel(int level){
|
||||
if(!(level >= 0)) throw new IllegalArgumentException("level must be >= 0");
|
||||
if(!(level < getLevelCount())) throw new IllegalArgumentException("level must be < levelCount");
|
||||
|
||||
return sizeOfLevel.get(level);
|
||||
}
|
||||
|
||||
private TreeNode getAncestor(TreeNode node){
|
||||
return node.ancestor != null ? node.ancestor : node;
|
||||
}
|
||||
|
||||
private float getDistance(TreeNode v, TreeNode w){
|
||||
float sizeOfNodes = getNodeSize(v) + getNodeSize(w);
|
||||
|
||||
return sizeOfNodes / 2 + getGapBetweenNodes(v, w);
|
||||
}
|
||||
|
||||
private TreeNode nextLeft(TreeNode v){
|
||||
return v.isLeaf() ? v.thread : v.children[0];
|
||||
}
|
||||
|
||||
private TreeNode nextRight(TreeNode v){
|
||||
return v.isLeaf() ? v.thread : v.children[v.children.length - 1];
|
||||
}
|
||||
|
||||
private int getNumber(TreeNode node, TreeNode parentNode){
|
||||
if(node.number == -1){
|
||||
int number = 1;
|
||||
for(TreeNode child : parentNode.children){
|
||||
child.number = number++;
|
||||
}
|
||||
}
|
||||
return node.number;
|
||||
}
|
||||
|
||||
private TreeNode ancestor(TreeNode vIMinus, TreeNode parentOfV, TreeNode defaultAncestor){
|
||||
TreeNode ancestor = getAncestor(vIMinus);
|
||||
return ancestor.parent == parentOfV ? ancestor : defaultAncestor;
|
||||
}
|
||||
|
||||
private void moveSubtree(TreeNode wMinus, TreeNode wPlus, TreeNode parent, float shift){
|
||||
int subtrees = getNumber(wPlus, parent) - getNumber(wMinus, parent);
|
||||
wPlus.change = wPlus.change - shift / subtrees;
|
||||
wPlus.shift = wPlus.shift + shift;
|
||||
wMinus.change = wMinus.change + shift / subtrees;
|
||||
wPlus.prelim = wPlus.prelim + shift;
|
||||
wPlus.mode = wPlus.mode + shift;
|
||||
}
|
||||
|
||||
private TreeNode apportion(TreeNode v, TreeNode defaultAncestor,
|
||||
TreeNode leftSibling, TreeNode parentOfV){
|
||||
if(leftSibling == null){
|
||||
return defaultAncestor;
|
||||
}
|
||||
|
||||
TreeNode vOPlus = v;
|
||||
TreeNode vIPlus = v;
|
||||
TreeNode vIMinus = leftSibling;
|
||||
|
||||
TreeNode vOMinus = parentOfV.children[0];
|
||||
|
||||
float sIPlus = (vIPlus).mode;
|
||||
float sOPlus = (vOPlus).mode;
|
||||
float sIMinus = (vIMinus).mode;
|
||||
float sOMinus = (vOMinus).mode;
|
||||
|
||||
TreeNode nextRightVIMinus = nextRight(vIMinus);
|
||||
TreeNode nextLeftVIPlus = nextLeft(vIPlus);
|
||||
|
||||
while(nextRightVIMinus != null && nextLeftVIPlus != null){
|
||||
vIMinus = nextRightVIMinus;
|
||||
vIPlus = nextLeftVIPlus;
|
||||
vOMinus = nextLeft(vOMinus);
|
||||
vOPlus = nextRight(vOPlus);
|
||||
vOPlus.ancestor = v;
|
||||
float shift = (vIMinus.prelim + sIMinus)
|
||||
- (vIPlus.prelim + sIPlus)
|
||||
+ getDistance(vIMinus, vIPlus);
|
||||
|
||||
if(shift > 0){
|
||||
moveSubtree(ancestor(vIMinus, parentOfV, defaultAncestor),
|
||||
v, parentOfV, shift);
|
||||
sIPlus = sIPlus + shift;
|
||||
sOPlus = sOPlus + shift;
|
||||
}
|
||||
sIMinus += vIMinus.mode;
|
||||
sIPlus += vIPlus.mode;
|
||||
sOMinus += vOMinus.mode;
|
||||
sOPlus += vOPlus.mode;
|
||||
|
||||
nextRightVIMinus = nextRight(vIMinus);
|
||||
nextLeftVIPlus = nextLeft(vIPlus);
|
||||
}
|
||||
|
||||
if(nextRightVIMinus != null && nextRight(vOPlus) == null){
|
||||
vOPlus.thread = nextRightVIMinus;
|
||||
vOPlus.mode += sIMinus - sOPlus;
|
||||
}
|
||||
|
||||
if(nextLeftVIPlus != null && nextLeft(vOMinus) == null){
|
||||
vOMinus.thread = nextLeftVIPlus;
|
||||
vOMinus.mode += sIPlus - sOMinus;
|
||||
defaultAncestor = v;
|
||||
}
|
||||
return defaultAncestor;
|
||||
}
|
||||
|
||||
private void executeShifts(TreeNode v){
|
||||
float shift = 0;
|
||||
float change = 0;
|
||||
|
||||
for(int i = v.children.length - 1; i >= 0; i--){
|
||||
TreeNode w = v.children[i];
|
||||
change = change + w.change;
|
||||
w.prelim += shift;
|
||||
w.mode += shift;
|
||||
shift += w.shift + change;
|
||||
}
|
||||
}
|
||||
|
||||
private void firstWalk(TreeNode v, TreeNode leftSibling){
|
||||
if(v.isLeaf()){
|
||||
if(leftSibling != null){
|
||||
v.prelim = leftSibling.prelim + getDistance(v, leftSibling);
|
||||
}
|
||||
|
||||
}else{
|
||||
TreeNode defaultAncestor = v.children[0];
|
||||
TreeNode previousChild = null;
|
||||
for(TreeNode w : v.children){
|
||||
firstWalk(w, previousChild);
|
||||
defaultAncestor = apportion(w, defaultAncestor, previousChild,
|
||||
v);
|
||||
previousChild = w;
|
||||
}
|
||||
executeShifts(v);
|
||||
float midpoint = (v.children[0].prelim + v.children[v.children.length - 1].prelim) / 2f;
|
||||
if(leftSibling != null){
|
||||
v.prelim = leftSibling.prelim + getDistance(v, leftSibling);
|
||||
v.mode = v.prelim - midpoint;
|
||||
}else{
|
||||
v.prelim = midpoint;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void secondWalk(TreeNode v, float m, int level, float levelStart){
|
||||
float levelChangeSign = getLevelChangeSign();
|
||||
boolean levelChangeOnYAxis = isLevelChangeInYAxis();
|
||||
float levelSize = getSizeOfLevel(level);
|
||||
|
||||
float x = v.prelim + m;
|
||||
|
||||
float y;
|
||||
if(alignment == TreeAlignment.center){
|
||||
y = levelStart + levelChangeSign * (levelSize / 2);
|
||||
}else if(alignment == TreeAlignment.towardsRoot){
|
||||
y = levelStart + levelChangeSign * (getNodeThickness(v) / 2);
|
||||
}else{
|
||||
y = levelStart + levelSize - levelChangeSign
|
||||
* (getNodeThickness(v) / 2);
|
||||
}
|
||||
|
||||
if(!levelChangeOnYAxis){
|
||||
float t = x;
|
||||
x = y;
|
||||
y = t;
|
||||
}
|
||||
|
||||
v.x = x;
|
||||
v.y = y;
|
||||
updateBounds(v, x, y);
|
||||
|
||||
if(!v.isLeaf()){
|
||||
float nextLevelStart = levelStart
|
||||
+ (levelSize + gapBetweenLevels)
|
||||
* levelChangeSign;
|
||||
for(TreeNode w : v.children){
|
||||
secondWalk(w, m + v.mode, level + 1, nextLevelStart);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public enum TreeLocation{
|
||||
top, left, bottom, right
|
||||
}
|
||||
|
||||
public enum TreeAlignment{
|
||||
center, towardsRoot, awayFromRoot
|
||||
}
|
||||
}
|
||||
67
core/src/mindustry/ui/layout/RadialTreeLayout.java
Normal file
67
core/src/mindustry/ui/layout/RadialTreeLayout.java
Normal file
@@ -0,0 +1,67 @@
|
||||
package mindustry.ui.layout;
|
||||
|
||||
import arc.struct.*;
|
||||
import arc.math.*;
|
||||
|
||||
public class RadialTreeLayout implements TreeLayout{
|
||||
private static ObjectSet<TreeNode> visited = new ObjectSet<>();
|
||||
private static Queue<TreeNode> queue = new Queue<>();
|
||||
|
||||
public float startRadius, delta;
|
||||
|
||||
@Override
|
||||
public void layout(TreeNode root){
|
||||
startRadius = root.height * 2.4f;
|
||||
delta = root.height * 2.4f;
|
||||
|
||||
bfs(root, true);
|
||||
|
||||
ObjectSet<TreeNode> all = new ObjectSet<>(visited);
|
||||
for(TreeNode node : all){
|
||||
node.leaves = bfs(node, false);
|
||||
}
|
||||
|
||||
radialize(root, startRadius, 0, 360);
|
||||
}
|
||||
|
||||
void radialize(TreeNode root, float radius, float from, float to){
|
||||
float angle = from;
|
||||
|
||||
for(TreeNode child : root.children){
|
||||
float nextAngle = angle + ((float)child.leaves / root.leaves * (to - from));
|
||||
|
||||
float x = radius * Mathf.cos((angle + nextAngle) / 2f * Mathf.degRad);
|
||||
float y = radius * Mathf.sin((angle + nextAngle) / 2f * Mathf.degRad);
|
||||
|
||||
child.x = x;
|
||||
child.y = y;
|
||||
|
||||
if(child.children.length > 0) radialize(child, radius + delta, angle, nextAngle);
|
||||
angle = nextAngle;
|
||||
}
|
||||
}
|
||||
|
||||
int bfs(TreeNode node, boolean assign){
|
||||
visited.clear();
|
||||
queue.clear();
|
||||
if(assign) node.number = 0;
|
||||
int leaves = 0;
|
||||
|
||||
visited.add(node);
|
||||
queue.addFirst(node);
|
||||
|
||||
while(!queue.isEmpty()){
|
||||
TreeNode current = queue.removeFirst();
|
||||
if(current.children.length == 0) leaves++;
|
||||
|
||||
for(TreeNode child : current.children){
|
||||
if(assign) child.number = current.number + 1;
|
||||
if(visited.add(child)){
|
||||
queue.addLast(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return leaves;
|
||||
}
|
||||
}
|
||||
22
core/src/mindustry/ui/layout/TreeLayout.java
Normal file
22
core/src/mindustry/ui/layout/TreeLayout.java
Normal file
@@ -0,0 +1,22 @@
|
||||
package mindustry.ui.layout;
|
||||
|
||||
public interface TreeLayout{
|
||||
void layout(TreeNode root);
|
||||
|
||||
class TreeNode<T extends TreeNode>{
|
||||
public float width, height, x, y;
|
||||
|
||||
//should be initialized by user
|
||||
public T[] children;
|
||||
public T parent;
|
||||
|
||||
//internal stuff
|
||||
public float mode, prelim, change, shift;
|
||||
public int number = -1, leaves;
|
||||
public TreeNode thread, ancestor;
|
||||
|
||||
public boolean isLeaf(){
|
||||
return children == null || children.length == 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user