Merge branch 'master' into GH-v80

This commit is contained in:
(G_H)
2019-06-23 21:07:06 +08:00
committed by GitHub
1849 changed files with 96454 additions and 39768 deletions

View File

@@ -0,0 +1,83 @@
package io.anuke.mindustry.ui;
import io.anuke.arc.Core;
import io.anuke.arc.function.FloatProvider;
import io.anuke.arc.function.Supplier;
import io.anuke.arc.graphics.Color;
import io.anuke.arc.graphics.g2d.*;
import io.anuke.arc.math.Mathf;
import io.anuke.arc.math.geom.Rectangle;
import io.anuke.arc.scene.Element;
import io.anuke.arc.scene.style.Drawable;
import io.anuke.arc.util.pooling.Pools;
public class Bar extends Element{
private static Rectangle scissor = new Rectangle();
private FloatProvider fraction;
private String name = "";
private float value, lastValue, blink;
private Color blinkColor = new Color();
public Bar(String name, Color color, FloatProvider fraction){
this.fraction = fraction;
this.name = Core.bundle.get(name);
this.blinkColor.set(color);
lastValue = value = fraction.get();
setColor(color);
}
public Bar(Supplier<String> name, Supplier<Color> color, FloatProvider 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 blink(Color color){
blinkColor.set(color);
return this;
}
@Override
public void draw(){
float computed = Mathf.clamp(fraction.get());
if(!Mathf.isEqual(lastValue, computed)){
blink = 1f;
lastValue = computed;
}
blink = Mathf.lerpDelta(blink, 0f, 0.2f);
value = Mathf.lerpDelta(value, computed, 0.15f);
Draw.colorl(0.1f);
Draw.drawable("bar", x, y, width, height);
Draw.color(color, blinkColor, blink);
Drawable top = Core.scene.skin.getDrawable("bar-top");
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 = Core.scene.skin.getFont("default-font");
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);
}
}

View File

@@ -1,44 +1,43 @@
package io.anuke.mindustry.ui;
import com.badlogic.gdx.graphics.Colors;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.Batch;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import io.anuke.ucore.graphics.Draw;
import io.anuke.ucore.graphics.Lines;
import io.anuke.ucore.scene.ui.Image;
import io.anuke.ucore.scene.ui.layout.Unit;
import io.anuke.arc.graphics.Texture;
import io.anuke.arc.graphics.g2d.*;
import io.anuke.arc.scene.ui.Image;
import io.anuke.arc.scene.ui.layout.Unit;
import io.anuke.mindustry.graphics.Pal;
public class BorderImage extends Image{
private float thickness = 3f;
public BorderImage(){}
public BorderImage(Texture texture){
super(texture);
}
public BorderImage(Texture texture, float thick){
super(texture);
thickness = thick;
}
private float thickness = 3f;
public BorderImage(TextureRegion region, float thick){
super(region);
thickness = thick;
}
@Override
public void draw(Batch batch, float alpha){
super.draw(batch, alpha);
float scaleX = getScaleX();
float scaleY = getScaleY();
Draw.color(Colors.get("accent"));
Lines.stroke(Unit.dp.scl(thickness));
Lines.rect(x + imageX, y + imageY, imageWidth * scaleX, imageHeight * scaleY);
Draw.reset();
}
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;
}
@Override
public void draw(){
super.draw();
float scaleX = getScaleX();
float scaleY = getScaleY();
Draw.color(Pal.accent);
Draw.alpha(parentAlpha);
Lines.stroke(Unit.dp.scl(thickness));
Lines.rect(x + imageX, y + imageY, imageWidth * scaleX, imageHeight * scaleY);
Draw.reset();
}
}

View File

@@ -0,0 +1,209 @@
package io.anuke.mindustry.ui;
import io.anuke.arc.Core;
import io.anuke.arc.collection.Array;
import io.anuke.arc.collection.OrderedMap;
import io.anuke.arc.graphics.Color;
import io.anuke.arc.scene.ui.layout.Table;
import io.anuke.arc.util.Strings;
import io.anuke.mindustry.graphics.Pal;
import io.anuke.mindustry.type.*;
import io.anuke.mindustry.world.Block;
import io.anuke.mindustry.world.Block.Icon;
import io.anuke.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(Icon.large)).size(size);
title.add("[accent]" + block.localizedName).padLeft(5);
});
table.row();
table.addImage("white").height(3).color(Color.LIGHT_GRAY).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("white").height(3).color(Color.LIGHT_GRAY).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() + ":[] ");
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.getContentIcon()).size(8 * 6);
title.add("[accent]" + item.localizedName()).padLeft(5);
});
table.row();
table.addImage("white").height(3).color(Color.LIGHT_GRAY).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("white").height(3).color(Color.LIGHT_GRAY).pad(15).padLeft(0).padRight(0).fillX();
table.row();
}
table.left().defaults().fillX();
table.add(Core.bundle.format("item.explosiveness", (int)(item.explosiveness * 100)));
table.row();
table.add(Core.bundle.format("item.flammability", (int)(item.flammability * 100)));
table.row();
table.add(Core.bundle.format("item.radioactivity", (int)(item.radioactivity * 100)));
table.row();
}
public static void displayLiquid(Table table, Liquid liquid){
table.table(title -> {
title.addImage(liquid.getContentIcon()).size(8 * 6);
title.add("[accent]" + liquid.localizedName()).padLeft(5);
});
table.row();
table.addImage("white").height(3).color(Color.LIGHT_GRAY).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("white").height(3).color(Color.LIGHT_GRAY).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.getContentIcon()).size(8 * 6);
title.add("[accent]" + mech.localizedName()).padLeft(5);
});
table.left().defaults().left();
table.row();
table.addImage("white").height(3).color(Color.LIGHT_GRAY).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("white").height(3).color(Color.LIGHT_GRAY).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.getContentIcon()).size(8 * 6);
title.add("[accent]" + unit.localizedName()).padLeft(5);
});
table.row();
table.addImage("white").height(3).color(Color.LIGHT_GRAY).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("white").height(3).color(Color.LIGHT_GRAY).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();
}
}

View File

@@ -1,37 +1,38 @@
package io.anuke.mindustry.ui;
import com.badlogic.gdx.graphics.g2d.Batch;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import io.anuke.ucore.graphics.Draw;
import io.anuke.ucore.scene.Element;
import io.anuke.arc.graphics.g2d.Fill;
import io.anuke.arc.scene.Element;
public class GridImage extends Element{
private int imageWidth, imageHeight;
public GridImage(int w, int h){
this.imageWidth = w;
this.imageHeight = h;
}
private int imageWidth, imageHeight;
public void draw(Batch batch, float alpha){
TextureRegion blank = Draw.region("white");
float xspace = (getWidth() / imageWidth);
float yspace = (getHeight() / imageHeight);
float s = 1f;
for(int x = 0; x <= imageWidth; x ++){
batch.draw(blank, (int)(getX() + xspace * x - s), getY() - s, 2, getHeight()+ (x == imageWidth ? 1: 0));
}
for(int y = 0; y <= imageHeight; y ++){
batch.draw(blank, getX() - s, (int)(getY() + y * yspace - s), getWidth(), 2);
}
}
public void setImageSize(int w, int h){
this.imageWidth = w;
this.imageHeight = h;
}
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;
}
}

View File

@@ -0,0 +1,33 @@
package io.anuke.mindustry.ui;
import io.anuke.arc.Core;
import io.anuke.arc.function.Function;
/**
* 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 Function<Integer, String> converter = String::valueOf;
public IntFormat(String text){
this.text = text;
}
public IntFormat(String text, Function<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;
}
}

View File

@@ -0,0 +1,23 @@
package io.anuke.mindustry.ui;
import io.anuke.arc.scene.ui.layout.Table;
import io.anuke.mindustry.type.Item;
import io.anuke.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){
add(new ItemImage(new ItemStack(item, amount))).size(8 * 4);
add(item.localizedName()).padLeft(4);
this.item = item;
this.amount = amount;
}
}

View File

@@ -0,0 +1,29 @@
package io.anuke.mindustry.ui;
import io.anuke.arc.graphics.g2d.TextureRegion;
import io.anuke.arc.scene.ui.Image;
import io.anuke.arc.scene.ui.layout.Stack;
import io.anuke.arc.scene.ui.layout.Table;
import io.anuke.mindustry.type.Item.Icon;
import io.anuke.mindustry.type.ItemStack;
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(ItemStack stack){
add(new Image(stack.item.icon(Icon.large)));
if(stack.amount != 0){
Table t = new Table().left().bottom();
t.add(stack.amount + "").name("item-label");
add(t);
}
}
}

View File

@@ -0,0 +1,44 @@
package io.anuke.mindustry.ui;
import io.anuke.arc.collection.ObjectIntMap;
import io.anuke.arc.graphics.Color;
import io.anuke.arc.scene.ui.layout.Table;
import io.anuke.mindustry.type.Item;
import io.anuke.mindustry.type.Item.Icon;
import io.anuke.mindustry.type.ItemType;
import java.text.NumberFormat;
import java.util.Locale;
import static io.anuke.mindustry.Vars.content;
import static io.anuke.mindustry.Vars.data;
/** Displays a list of items, e.g. launched items.*/
public class ItemsDisplay extends Table{
private static final NumberFormat format = NumberFormat.getNumberInstance(Locale.getDefault());
public ItemsDisplay(){
rebuild();
}
public void rebuild(){
clear();
top().left();
margin(0);
table("flat", t -> {
t.margin(10).marginLeft(15).marginTop(15f);
t.add("$launcheditems").colspan(3).left().padBottom(5);
t.row();
ObjectIntMap<Item> items = data.items();
for(Item item : content.items()){
if(item.type == ItemType.material && data.isUnlocked(item)){
t.label(() -> format.format(items.get(item, 0))).left();
t.addImage(item.icon(Icon.medium)).size(8 * 3).padLeft(4).padRight(4);
t.add(item.localizedName()).color(Color.LIGHT_GRAY).left();
t.row();
}
}
});
}
}

View File

@@ -1,20 +1,28 @@
package io.anuke.mindustry.ui;
import com.badlogic.gdx.graphics.Color;
import io.anuke.ucore.util.Bundles;
import io.anuke.arc.Core;
import io.anuke.arc.graphics.Color;
public class Links {
private static final LinkEntry[] links = {
new LinkEntry("discord", "https://discord.gg/BKADYds", Color.valueOf("7289da")),
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("trello", "https://trello.com/b/aE2tcUwF", Color.valueOf("026aa7")),
new LinkEntry("wiki", "http://mindustry.wikia.com/wiki/Mindustry_Wiki", Color.valueOf("0f142f")),
new LinkEntry("wiki", "https://mindustrygame.github.io/wiki/", Color.valueOf("0f142f")),
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("github", "https://github.com/Anuken/Mindustry/", Color.valueOf("24292e")),
new LinkEntry("dev-builds", "https://github.com/Anuken/Mindustry/wiki", Color.valueOf("fafbfc")),
};
new LinkEntry("dev-builds", "https://jenkins.hellomouse.net/job/mindustry/", Color.valueOf("fafbfc"))
};
}
public static LinkEntry[] getLinks(){
if(links == null){
createLinks();
}
return links;
}
@@ -22,10 +30,10 @@ public class Links {
public final String name, description, link;
public final Color color;
public LinkEntry(String name, String link, Color color) {
public LinkEntry(String name, String link, Color color){
this.name = name;
this.color = color;
this.description = Bundles.getNotNull("text.link." + name +".description");
this.description = Core.bundle.getNotNull("link." + name + ".description");
this.link = link;
}
}

View File

@@ -0,0 +1,38 @@
package io.anuke.mindustry.ui;
import io.anuke.arc.graphics.Color;
import io.anuke.arc.scene.ui.Image;
import io.anuke.arc.scene.ui.layout.Stack;
import io.anuke.arc.scene.ui.layout.Table;
import io.anuke.arc.util.Strings;
import io.anuke.mindustry.type.Liquid;
import io.anuke.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.getContentIcon()));
if(amount != 0){
Table t = new Table().left().bottom();
t.add(Strings.autoFixed(amount, 1));
add(t);
}
}}).size(8 * 4).padRight(3);
if(perSecond){
add(StatUnit.perSecond.localized()).padLeft(2).padRight(5).color(Color.LIGHT_GRAY);
}
add(liquid.localizedName());
}
}

View File

@@ -1,38 +1,33 @@
package io.anuke.mindustry.ui;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.utils.Align;
import io.anuke.ucore.function.Listenable;
import io.anuke.ucore.scene.ui.TextButton;
import io.anuke.arc.graphics.Color;
import io.anuke.arc.scene.ui.TextButton;
import io.anuke.arc.util.Align;
import static io.anuke.mindustry.Vars.iconsize;
public class MenuButton extends TextButton{
public MenuButton(String icon, String text, Listenable clicked){
this(icon, text, null, clicked);
}
public MenuButton(String icon, String text, String description, Listenable clicked){
super("default");
float s = 70f;
public MenuButton(String icon, String text, Runnable clicked){
this(icon, text, null, clicked);
}
clicked(clicked);
public MenuButton(String icon, String text, String description, Runnable clicked){
super("default");
clearChildren();
clicked(clicked);
clearChildren();
margin(0);
margin(0);
table(t -> {
t.addImage(icon).size(14*3);
t.update(() -> t.setBackground(getClickListener().isOver() || getClickListener().isVisualPressed() ? "button-over" : "button"));
}).size(s - 5, s);
table(t -> {
t.addImage(icon).size(iconsize).padLeft(6);
table(t -> {
t.add(text).wrap().growX().get().setAlignment(Align.center, Align.left);
if(description != null){
t.row();
t.add(description).color(Color.LIGHT_GRAY);
}
}).padLeft(5).growX();
}
t.add(text).wrap().growX().get().setAlignment(Align.center, Align.left);
if(description != null){
t.row();
t.add(description).color(Color.LIGHT_GRAY);
}
}).padLeft(5).growX();
}
}

View File

@@ -0,0 +1,99 @@
package io.anuke.mindustry.ui;
import io.anuke.arc.Core;
import io.anuke.arc.graphics.g2d.Draw;
import io.anuke.arc.input.KeyCode;
import io.anuke.arc.scene.Element;
import io.anuke.arc.scene.event.*;
import io.anuke.arc.scene.ui.layout.*;
import static io.anuke.mindustry.Vars.*;
public class Minimap extends Table{
public Minimap(){
background("pane");
float margin = 5f;
touchable(Touchable.enabled);
add(new Element(){
{
setSize(Unit.dp.scl(140f));
}
@Override
public void act(float delta){
setPosition(margin, 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);
}
}
}).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 = Unit.dp.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)){
Core.scene.setScrollFocus(this);
}else if(Core.scene.getScrollFocus() == this){
Core.scene.setScrollFocus(null);
}
});
}
}

View File

@@ -0,0 +1,15 @@
package io.anuke.mindustry.ui;
import io.anuke.arc.scene.ui.ImageButton;
import io.anuke.arc.util.Align;
public class MobileButton extends ImageButton{
public MobileButton(String icon, float isize, String text, Runnable listener){
super(icon);
resizeImage(isize);
clicked(listener);
row();
add(text).growX().wrap().center().get().setAlignment(Align.center, Align.center);
}
}

View File

@@ -0,0 +1,31 @@
package io.anuke.mindustry.ui;
import io.anuke.arc.collection.Array;
import io.anuke.arc.scene.ui.layout.Stack;
import io.anuke.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{
displays.get((int)(time) % displays.size).visible(true);
}
}
}

View File

@@ -1,16 +0,0 @@
package io.anuke.mindustry.ui;
import com.badlogic.gdx.utils.Array;
import io.anuke.ucore.scene.ui.Button;
public class PressGroup{
private Array<Button> buttons = new Array<>();
private boolean active = true;
public void add(Button button){
//TODO make only one button in the group be clickable, add implementation
buttons.add(button);
}
}

View File

@@ -0,0 +1,40 @@
package io.anuke.mindustry.ui;
import io.anuke.arc.function.BooleanProvider;
import io.anuke.arc.graphics.g2d.*;
import io.anuke.arc.scene.Element;
import io.anuke.arc.scene.ui.Image;
import io.anuke.arc.scene.ui.layout.Stack;
import io.anuke.arc.scene.ui.layout.Unit;
import io.anuke.mindustry.graphics.Pal;
public class ReqImage extends Stack{
private final BooleanProvider valid;
public ReqImage(Element image, BooleanProvider valid){
this.valid = valid;
add(image);
add(new Element(){
{
visible(() -> !valid.get());
}
@Override
public void draw(){
Lines.stroke(Unit.dp.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, BooleanProvider valid){
this(new Image(region), valid);
}
public boolean valid(){
return valid.get();
}
}

View File

@@ -0,0 +1,307 @@
package io.anuke.mindustry.ui;
import io.anuke.arc.collection.FloatArray;
import io.anuke.arc.math.geom.Rectangle;
/**
* Algorithm taken from <a href="https://github.com/abego/treelayout">TreeLayout</a>.
*/
public class 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;
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
}
public static class TreeNode<T extends TreeNode>{
public float width, height, x, y;
//should be initialized by user
public T[] children;
public T parent;
private float mode, prelim, change, shift;
private int number = -1;
private TreeNode thread, ancestor;
boolean isLeaf(){
return children == null || children.length == 0;
}
}
}

View File

@@ -1,39 +1,49 @@
package io.anuke.mindustry.ui.dialogs;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.utils.ObjectSet;
import io.anuke.arc.Core;
import io.anuke.arc.collection.*;
import io.anuke.arc.graphics.Color;
import io.anuke.arc.scene.ui.ScrollPane;
import io.anuke.arc.scene.ui.layout.*;
import io.anuke.arc.util.*;
import io.anuke.mindustry.graphics.Pal;
import io.anuke.mindustry.ui.Links;
import io.anuke.mindustry.ui.Links.LinkEntry;
import io.anuke.ucore.core.Core;
import io.anuke.ucore.core.Timers;
import io.anuke.ucore.scene.ui.ScrollPane;
import io.anuke.ucore.scene.ui.layout.Table;
import io.anuke.ucore.util.OS;
import static io.anuke.mindustry.Vars.ios;
import static io.anuke.mindustry.Vars.ui;
import static io.anuke.mindustry.Vars.*;
public class AboutDialog extends FloatingDialog {
public class AboutDialog extends FloatingDialog{
private Array<String> contributors = new Array<>();
private static ObjectSet<String> bannedItems = ObjectSet.with("google-play", "itch.io", "dev-builds", "trello");
public AboutDialog(){
super("$text.about.button");
super("$about.button");
addCloseButton();
shown(() -> {
contributors = Array.with(Core.files.internal("contributors").readString().split("\n"));
Core.app.post(this::setup);
});
float h = 80f;
float w = 600f;
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, "clear");
ScrollPane pane = new ScrollPane(in);
for(LinkEntry link : Links.getLinks()){
if((ios || OS.isMac) && bannedItems.contains(link.name)){ //because Apple doesn't like me mentioning things
continue;
}
Table table = new Table("button");
Table table = new Table("underline");
table.margin(0);
table.table(img -> {
img.addImage("white").height(h - 5).width(40f).color(link.color);
@@ -42,42 +52,67 @@ public class AboutDialog extends FloatingDialog {
}).expandY();
table.table(i -> {
i.background("button");
i.addImage("icon-" + link.name).size(14*3f);
}).size(h-5, h);
i.background("button-edge-3");
i.addImage("icon-" + link.name).size(iconsize);
}).size(h - 5, h);
table.table(inset -> {
inset.add("[accent]"+link.name.replace("-", " ")).growX().left();
inset.add("[accent]" + Strings.capitalize(link.name.replace("-", " "))).growX().left();
inset.row();
inset.labelWrap(link.description).width(w - 100f).color(Color.LIGHT_GRAY).growX();
}).padLeft(8);
table.addImageButton("icon-link", 14*3, () -> {
if(!Gdx.net.openURI(link.link)){
ui.showError("$text.linkfail");
Gdx.app.getClipboard().setContents(link.link);
table.addImageButton("icon-link", iconsize, () -> {
if(!Core.net.openURI(link.link)){
ui.showError("$linkfail");
Core.app.getClipboard().setContents(link.link);
}
}).size(h-5, h);
}).size(h - 5, h);
in.add(table).size(w, h).padTop(5).row();
}
shown(() -> Timers.run(1f, () -> Core.scene.setScrollFocus(pane)));
shown(() -> Time.run(1f, () -> Core.scene.setScrollFocus(pane)));
content().add(pane).growX();
cont.add(pane).growX();
buttons().addButton("$text.credits", this::showCredits).size(200f, 64f);
addCloseButton();
buttons.addButton("$credits", this::showCredits).size(200f, 64f);
if(!ios && !OS.isMac){
buttons().addButton("$text.changelog.title", ui.changelog::show).size(200f, 64f);
buttons.addButton("$changelog.title", ui.changelog::show).size(200f, 64f);
}
if(Core.graphics.isPortrait()){
for(Cell<?> cell : buttons.getCells()){
cell.width(140f);
}
}
}
public void showCredits(){
FloatingDialog dialog = new FloatingDialog("$text.credits");
FloatingDialog dialog = new FloatingDialog("$credits");
dialog.addCloseButton();
dialog.content().add("$text.about");
dialog.cont.add("$credits.text");
dialog.cont.row();
if(!contributors.isEmpty()){
dialog.cont.addImage("blank").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();
}
}

View File

@@ -1,19 +1,15 @@
package io.anuke.mindustry.ui.dialogs;
import io.anuke.mindustry.entities.Player;
import io.anuke.arc.scene.ui.ScrollPane;
import io.anuke.arc.scene.ui.layout.Table;
import io.anuke.mindustry.net.Administration.PlayerInfo;
import io.anuke.mindustry.net.Net;
import io.anuke.mindustry.net.NetConnection;
import io.anuke.mindustry.net.NetEvents;
import io.anuke.ucore.scene.ui.ScrollPane;
import io.anuke.ucore.scene.ui.layout.Table;
import static io.anuke.mindustry.Vars.*;
public class AdminsDialog extends FloatingDialog {
public class AdminsDialog extends FloatingDialog{
public AdminsDialog(){
super("$text.server.admins");
super("$server.admins");
addCloseButton();
@@ -22,19 +18,17 @@ public class AdminsDialog extends FloatingDialog {
}
private void setup(){
content().clear();
if(gwt) return;
cont.clear();
float w = 400f, h = 80f;
Table table = new Table();
ScrollPane pane = new ScrollPane(table, "clear");
ScrollPane pane = new ScrollPane(table);
pane.setFadeScrollBars(false);
if(netServer.admins.getAdmins().size == 0){
table.add("$text.server.admins.none");
table.add("$server.admins.none");
}
for(PlayerInfo info : netServer.admins.getAdmins()){
@@ -43,16 +37,14 @@ public class AdminsDialog extends FloatingDialog {
res.labelWrap("[LIGHT_GRAY]" + info.lastName).width(w - h - 24f);
res.add().growX();
res.addImageButton("icon-cancel", 14*3, () -> {
ui.showConfirm("$text.confirm", "$text.confirmunadmin", () -> {
res.addImageButton("icon-cancel", iconsize, () -> {
ui.showConfirm("$confirm", "$confirmunadmin", () -> {
netServer.admins.unAdminPlayer(info.id);
for(Player player : playerGroup.all()){
NetConnection c = Net.getConnection(player.clientid);
if(c != null){
NetEvents.handleAdminSet(player, false);
break;
playerGroup.all().each(player -> {
if(player != null && player.uuid != null && player.uuid.equals(info.id)){
player.isAdmin = false;
}
}
});
setup();
});
}).size(h).pad(-14f);
@@ -61,6 +53,6 @@ public class AdminsDialog extends FloatingDialog {
table.row();
}
content().add(pane);
cont.add(pane);
}
}

View File

@@ -1,15 +1,15 @@
package io.anuke.mindustry.ui.dialogs;
import io.anuke.arc.scene.ui.ScrollPane;
import io.anuke.arc.scene.ui.layout.Table;
import io.anuke.mindustry.net.Administration.PlayerInfo;
import io.anuke.ucore.scene.ui.ScrollPane;
import io.anuke.ucore.scene.ui.layout.Table;
import static io.anuke.mindustry.Vars.*;
public class BansDialog extends FloatingDialog {
public class BansDialog extends FloatingDialog{
public BansDialog(){
super("$text.server.bans");
super("$server.bans");
addCloseButton();
@@ -19,19 +19,17 @@ public class BansDialog extends FloatingDialog {
}
private void setup(){
content().clear();
if(gwt) return;
cont.clear();
float w = 400f, h = 80f;
Table table = new Table();
ScrollPane pane = new ScrollPane(table, "clear");
ScrollPane pane = new ScrollPane(table);
pane.setFadeScrollBars(false);
if(netServer.admins.getBanned().size == 0){
table.add("$text.server.bans.none");
table.add("$server.bans.none");
}
for(PlayerInfo info : netServer.admins.getBanned()){
@@ -40,8 +38,8 @@ public class BansDialog extends FloatingDialog {
res.labelWrap("IP: [LIGHT_GRAY]" + info.lastIP + "\n[]Name: [LIGHT_GRAY]" + info.lastName).width(w - h - 24f);
res.add().growX();
res.addImageButton("icon-cancel", 14*3, () -> {
ui.showConfirm("$text.confirm", "$text.confirmunban", () -> {
res.addImageButton("icon-cancel", iconsize, () -> {
ui.showConfirm("$confirm", "$confirmunban", () -> {
netServer.admins.unbanPlayerID(info.id);
setup();
});
@@ -51,6 +49,6 @@ public class BansDialog extends FloatingDialog {
table.row();
}
content().add(pane);
cont.add(pane);
}
}

View File

@@ -1,16 +1,15 @@
package io.anuke.mindustry.ui.dialogs;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.utils.Array;
import io.anuke.arc.Core;
import io.anuke.arc.collection.Array;
import io.anuke.arc.scene.ui.ScrollPane;
import io.anuke.arc.scene.ui.layout.Table;
import io.anuke.arc.util.Log;
import io.anuke.arc.util.OS;
import io.anuke.mindustry.Vars;
import io.anuke.mindustry.game.Version;
import io.anuke.mindustry.io.Changelogs;
import io.anuke.mindustry.io.Changelogs.VersionInfo;
import io.anuke.mindustry.io.Version;
import io.anuke.ucore.core.Settings;
import io.anuke.ucore.scene.ui.ScrollPane;
import io.anuke.ucore.scene.ui.layout.Table;
import io.anuke.ucore.util.Log;
import io.anuke.ucore.util.OS;
import static io.anuke.mindustry.Vars.ios;
@@ -19,40 +18,42 @@ public class ChangelogDialog extends FloatingDialog{
private Array<VersionInfo> versions;
public ChangelogDialog(){
super("$text.changelog.title");
super("$changelog.title");
addCloseButton();
content().add("$text.changelog.loading");
cont.add("$changelog.loading");
if(!ios && !OS.isMac) {
Changelogs.getChangelog(result -> {
versions = result;
Gdx.app.postRunnable(this::setup);
}, t -> {
Log.err(t);
Gdx.app.postRunnable(this::setup);
});
}
shown(() -> {
if(!ios && !OS.isMac){
Changelogs.getChangelog(result -> {
versions = result;
Core.app.post(this::setup);
}, t -> {
Log.err(t);
Core.app.post(this::setup);
});
}
});
}
void setup(){
Table table = new Table();
ScrollPane pane = new ScrollPane(table, "clear");
ScrollPane pane = new ScrollPane(table);
content().clear();
content().add(pane).grow();
cont.clear();
cont.add(pane).grow();
if(versions == null){
table.add("$text.changelog.error");
table.add("$changelog.error");
if(Vars.android){
table.row();
table.add("$text.changelog.error.android").padTop(8);
table.add("$changelog.error.android").padTop(8);
}
if(ios){
table.row();
table.add("$text.changelog.error.ios").padTop(8);
table.add("$changelog.error.ios").padTop(8);
}
}else{
for(VersionInfo info : versions){
@@ -60,16 +61,16 @@ public class ChangelogDialog extends FloatingDialog{
desc = desc.replace("Android", "Mobile");
Table in = new Table("clear");
Table in = new Table("underline");
in.top().left().margin(10);
in.add("[accent]" + info.name);
in.add("[accent]" + info.name + "[LIGHT_GRAY] | " + info.date);
if(info.build == Version.build){
in.row();
in.add("$text.changelog.current");
in.add("$changelog.current");
}else if(info == versions.first()){
in.row();
in.add("$text.changelog.latest");
in.add("$changelog.latest");
}
in.row();
in.labelWrap("[lightgray]" + desc).width(vw - 20).padTop(12);
@@ -77,10 +78,10 @@ public class ChangelogDialog extends FloatingDialog{
table.add(in).width(vw).pad(8).row();
}
int lastid = Settings.getInt("lastBuild");
int lastid = Core.settings.getInt("lastBuild");
if(lastid != 0 && versions.peek().build > lastid){
Settings.putInt("lastBuild", versions.peek().build);
Settings.save();
Core.settings.put("lastBuild", versions.peek().build);
Core.settings.save();
show();
}
}

View File

@@ -1,11 +1,11 @@
package io.anuke.mindustry.ui.dialogs;
import com.badlogic.gdx.Input.Keys;
import com.badlogic.gdx.graphics.Color;
import io.anuke.ucore.function.Consumer;
import io.anuke.ucore.scene.ui.Dialog;
import io.anuke.ucore.scene.ui.ImageButton;
import io.anuke.ucore.scene.ui.layout.Table;
import io.anuke.arc.function.Consumer;
import io.anuke.arc.graphics.Color;
import io.anuke.arc.input.KeyCode;
import io.anuke.arc.scene.ui.Dialog;
import io.anuke.arc.scene.ui.ImageButton;
import io.anuke.arc.scene.ui.layout.Table;
import static io.anuke.mindustry.Vars.player;
import static io.anuke.mindustry.Vars.playerColors;
@@ -20,25 +20,25 @@ public class ColorPickDialog extends Dialog{
private void build(){
Table table = new Table();
content().add(table);
cont.add(table);
for(int i = 0; i < playerColors.length; i ++){
for(int i = 0; i < playerColors.length; i++){
Color color = playerColors[i];
ImageButton button = table.addImageButton("white", "toggle", 34, () -> {
ImageButton button = table.addImageButton("white", "clear-toggle", 34, () -> {
cons.accept(color);
hide();
}).size(44, 48).pad(0).padBottom(-5.1f).get();
button.setChecked(player.getColor().equals(color));
}).size(48).get();
button.setChecked(player.color.equals(color));
button.getStyle().imageUpColor = color;
if(i%4 == 3){
if(i % 4 == 3){
table.row();
}
}
keyDown(key->{
if(key == Keys.ESCAPE || key == Keys.BACK)
keyDown(key -> {
if(key == KeyCode.ESCAPE || key == KeyCode.BACK)
hide();
});

View File

@@ -0,0 +1,28 @@
package io.anuke.mindustry.ui.dialogs;
import io.anuke.arc.scene.ui.ScrollPane;
import io.anuke.arc.scene.ui.layout.Table;
import io.anuke.mindustry.game.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();
}
}

View File

@@ -1,31 +1,31 @@
package io.anuke.mindustry.ui.dialogs;
import com.badlogic.gdx.Input.Keys;
import com.badlogic.gdx.graphics.Colors;
import com.badlogic.gdx.utils.Align;
import io.anuke.ucore.scene.ui.Image;
import io.anuke.ucore.scene.ui.KeybindDialog;
import io.anuke.arc.Core;
import io.anuke.arc.input.KeyCode;
import io.anuke.arc.scene.ui.Image;
import io.anuke.arc.scene.ui.KeybindDialog;
import io.anuke.arc.util.Align;
import io.anuke.mindustry.graphics.Pal;
public class ControlsDialog extends KeybindDialog{
public ControlsDialog(){
setDialog();
setFillParent(true);
title().setAlignment(Align.center);
getTitleTable().row();
getTitleTable().add(new Image("white"))
.growX().height(3f).pad(4f).get().setColor(Colors.get("accent"));
}
@Override
public void addCloseButton(){
buttons().addImageTextButton("$text.back", "icon-arrow-left", 30f, this::hide).size(230f, 64f);
keyDown(key->{
if(key == Keys.ESCAPE || key == Keys.BACK)
hide();
});
}
public ControlsDialog(){
setStyle(Core.scene.skin.get("dialog", WindowStyle.class));
setFillParent(true);
title.setAlignment(Align.center);
titleTable.row();
titleTable.add(new Image("white"))
.growX().height(3f).pad(4f).get().setColor(Pal.accent);
}
@Override
public void addCloseButton(){
buttons.addImageTextButton("$back", "icon-arrow-left", 30f, this::hide).size(230f, 64f);
keyDown(key -> {
if(key == KeyCode.ESCAPE || key == KeyCode.BACK)
hide();
});
}
}

View File

@@ -0,0 +1,76 @@
package io.anuke.mindustry.ui.dialogs;
import io.anuke.arc.Core;
import io.anuke.arc.graphics.g2d.TextureRegion;
import io.anuke.arc.scene.ui.ImageButton;
import io.anuke.arc.scene.ui.ScrollPane;
import io.anuke.arc.scene.ui.layout.Table;
import io.anuke.arc.util.Align;
import io.anuke.arc.util.Scaling;
import io.anuke.mindustry.maps.Map;
import io.anuke.mindustry.ui.BorderImage;
import static io.anuke.mindustry.Vars.world;
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);
ScrollPane pane = new ScrollPane(maps);
pane.setFadeScrollBars(false);
int maxwidth = (Core.graphics.isPortrait() ? 2 : 4);
float images = 146f;
int i = 0;
maps.defaults().width(170).fillY().top().pad(4f);
for(Map map : world.maps.all()){
if(i % maxwidth == 0){
maps.row();
}
ImageButton image = new ImageButton(new TextureRegion(map.texture), "clear");
image.margin(5);
image.getImageCell().size(images);
image.top();
image.row();
image.add("[accent]" + map.name()).pad(3f).growX().wrap().get().setAlignment(Align.center, Align.center);
image.row();
image.label((() -> Core.bundle.format("level.highscore", map.getHightScore()))).pad(3f);
BorderImage border = new BorderImage(map.texture, 3f);
border.setScaling(Scaling.fit);
image.replaceImage(border);
image.clicked(() -> dialog.show(map));
maps.add(image);
i++;
}
if(world.maps.all().size == 0){
maps.add("$maps.none").pad(50);
}
cont.add(pane).uniformX();
}
}

View File

@@ -0,0 +1,106 @@
package io.anuke.mindustry.ui.dialogs;
import io.anuke.arc.function.*;
import io.anuke.arc.graphics.Color;
import io.anuke.arc.scene.ui.layout.Table;
import io.anuke.arc.util.Strings;
import io.anuke.mindustry.core.Platform;
import io.anuke.mindustry.game.Rules;
import io.anuke.mindustry.graphics.Pal;
import static io.anuke.mindustry.Vars.tilesize;
public class CustomRulesDialog extends FloatingDialog{
private Table main;
private Rules rules;
private Supplier<Rules> resetter;
public CustomRulesDialog(){
super("$mode.custom");
setFillParent(true);
shown(this::setup);
addCloseButton();
}
public void show(Rules rules, Supplier<Rules> resetter){
this.rules = rules;
this.resetter = resetter;
show();
}
void setup(){
cont.clear();
cont.pane(m -> main = m);
main.margin(10f);
main.addButton("$settings.reset", () -> {
rules = resetter.get();
setup();
}).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");
check("$rules.limitedRespawns", b -> rules.limitedRespawns = b, () -> rules.limitedRespawns);
number("$rules.respawns", true, f -> rules.respawns = (int)f, () -> rules.respawns, () -> rules.limitedRespawns);
number("$rules.respawntime", f -> rules.respawnTime = f * 60f, () -> rules.respawnTime / 60f);
title("$rules.title.resourcesbuilding");
check("$rules.infiniteresources", b -> rules.infiniteResources = b, () -> rules.infiniteResources);
number("$rules.buildcostmultiplier", false, f -> rules.buildCostMultiplier = f, () -> rules.buildCostMultiplier, () -> !rules.infiniteResources);
number("$rules.buildspeedmultiplier", f -> rules.buildSpeedMultiplier = f, () -> rules.buildSpeedMultiplier);
title("$rules.title.player");
number("$rules.playerdamagemultiplier", f -> rules.playerDamageMultiplier = f, () -> rules.playerDamageMultiplier);
number("$rules.playerhealthmultiplier", f -> rules.playerHealthMultiplier = f, () -> rules.playerHealthMultiplier);
title("$rules.title.unit");
check("$rules.unitdrops", b -> rules.unitDrops = b, () -> rules.unitDrops, () -> true);
number("$rules.unitbuildspeedmultiplier", f -> rules.unitBuildSpeedMultiplier = f, () -> rules.unitBuildSpeedMultiplier);
number("$rules.unithealthmultiplier", f -> rules.unitHealthMultiplier = f, () -> rules.unitHealthMultiplier);
number("$rules.unitdamagemultiplier", f -> rules.unitDamageMultiplier = f, () -> rules.unitDamageMultiplier);
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));
}
void number(String text, FloatConsumer cons, FloatProvider prov){
number(text, false, cons, prov, () -> true);
}
void number(String text, boolean integer, FloatConsumer cons, FloatProvider prov, BooleanProvider condition){
main.table(t -> {
t.left();
t.add(text).left().padRight(5)
.update(a -> a.setColor(condition.get() ? Color.WHITE : Color.GRAY));
Platform.instance.addDialog(t.addField((integer ? (int)prov.get() : prov.get()) + "", s -> cons.accept(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, BooleanConsumer cons, BooleanProvider prov){
check(text, cons, prov, () -> true);
}
void check(String text, BooleanConsumer cons, BooleanProvider prov, BooleanProvider 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).padBottom(20).padRight(100f);
main.row();
}
}

View File

@@ -0,0 +1,79 @@
package io.anuke.mindustry.ui.dialogs;
import io.anuke.arc.Core;
import io.anuke.arc.collection.Array;
import io.anuke.arc.scene.event.HandCursorListener;
import io.anuke.arc.scene.ui.*;
import io.anuke.arc.scene.ui.layout.Table;
import io.anuke.mindustry.Vars;
import io.anuke.mindustry.core.GameState.State;
import io.anuke.mindustry.game.Content;
import io.anuke.mindustry.game.UnlockableContent;
import io.anuke.mindustry.graphics.Pal;
import io.anuke.mindustry.type.ContentType;
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("white").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;
float size = Vars.iconsize;
int count = 0;
for(int i = 0; i < array.size; i++){
UnlockableContent unlock = (UnlockableContent)array.get(i);
Image image = unlocked(unlock) ? new Image(unlock.getContentIcon()) : new Image("icon-locked");
image.addListener(new HandCursorListener());
list.add(image).size(size).pad(3);
if(unlocked(unlock)){
image.clicked(() -> Vars.ui.content.show(unlock));
image.addListener(new Tooltip(t -> t.background("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();
}
}

View File

@@ -0,0 +1,202 @@
package io.anuke.mindustry.ui.dialogs;
import io.anuke.arc.Core;
import io.anuke.arc.collection.Array;
import io.anuke.arc.collection.ObjectSet;
import io.anuke.arc.collection.ObjectSet.ObjectSetIterator;
import io.anuke.arc.graphics.g2d.Draw;
import io.anuke.arc.graphics.g2d.Lines;
import io.anuke.arc.scene.Group;
import io.anuke.arc.scene.ui.TextButton;
import io.anuke.arc.scene.ui.layout.Table;
import io.anuke.arc.scene.ui.layout.Unit;
import io.anuke.arc.util.Align;
import io.anuke.arc.util.Structs;
import io.anuke.mindustry.content.Zones;
import io.anuke.mindustry.core.GameState.State;
import io.anuke.mindustry.game.Saves.SaveSlot;
import io.anuke.mindustry.graphics.Pal;
import io.anuke.mindustry.io.SaveIO.SaveException;
import io.anuke.mindustry.type.Zone;
import io.anuke.mindustry.type.Zone.ZoneRequirement;
import io.anuke.mindustry.ui.ItemsDisplay;
import io.anuke.mindustry.ui.TreeLayout;
import io.anuke.mindustry.ui.TreeLayout.TreeNode;
import static io.anuke.mindustry.Vars.*;
public class DeployDialog extends FloatingDialog{
private final float nodeSize = Unit.dp.scl(210f);
private ObjectSet<ZoneNode> nodes = new ObjectSet<>();
private ZoneInfoDialog info = new ZoneInfoDialog();
public DeployDialog(){
super("");
ZoneNode root = new ZoneNode(Zones.groundZero, null);
TreeLayout layout = new TreeLayout();
layout.gapBetweenLevels = layout.gapBetweenNodes = Unit.dp.scl(50f);
layout.layout(root);
addCloseButton();
buttons.addImageTextButton("$techtree", "icon-tree", iconsize, () -> ui.tech.show()).size(230f, 64f);
shown(this::setup);
}
public void setup(){
cont.clear();
titleTable.remove();
margin(0f).marginBottom(8);
if(!Core.settings.getBool("zone-info", false)){
Core.app.post(() -> ui.showInfoText("TEMPORARY GUIDE ON HOW TO PLAY ZONES", "- deploy to zones by selecting them here\n- most zones require items to deploy\n- once you survive a set amount of waves, you can launch all the resources in your core\n- use these items to research in the tech tree or uncover new zones"));
Core.settings.put("zone-info", true);
Core.settings.save();
}
cont.stack(control.saves.getZoneSlot() == null ? new View() : new Table(){{
SaveSlot slot = control.saves.getZoneSlot();
TextButton[] b = {null};
TextButton button = addButton(Core.bundle.format("resume", slot.getZone().localizedName()), () -> {
if(b[0].childrenPressed()) return;
hide();
ui.loadAnd(() -> {
try{
control.saves.getZoneSlot().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();
}
});
}).size(230f).get();
b[0] = button;
String color = "[lightgray]";
button.defaults().colspan(2);
button.row();
button.add(Core.bundle.format("save.wave", color + slot.getWave()));
button.row();
button.label(() -> Core.bundle.format("save.playtime", color + slot.getPlayTime()));
button.row();
button.add().grow();
button.row();
button.addButton("$abandon", () -> {
ui.showConfirm("$warning", "$abandon.text", () -> {
slot.delete();
setup();
});
}).growX().height(50f).pad(-12).padTop(10);
}}, new ItemsDisplay()).grow();
//set up direct and indirect children
for(ZoneNode node : nodes){
node.allChildren.clear();
node.allChildren.addAll(node.children);
for(ZoneNode other : new ObjectSetIterator<>(nodes)){
if(Structs.contains(other.zone.zoneRequirements, req -> req.zone == node.zone)){
node.allChildren.add(other);
}
}
}
}
boolean hidden(Zone zone){
for(ZoneRequirement other : zone.zoneRequirements){
if(!data.isUnlocked(other.zone)){
return true;
}
}
return false;
}
@Override
protected void drawBackground(float x, float y){
drawDefaultBackground(x, y);
}
void buildButton(Zone zone, TextButton button){
button.setDisabled(() -> hidden(zone));
button.clicked(() -> info.show(zone));
if(zone.unlocked()){
button.addImage("icon-terrain").size(iconsize).padRight(3);
button.labelWrap(zone.localizedName()).width(140).growX();
}else{
button.addImage("icon-locked");
button.row();
button.add("$locked");
}
}
//should be static variables of View, but that's impossible
static float panX = 0, panY = -200;
class View extends Group{
{
for(ZoneNode node : nodes){
TextButton button = new TextButton("", "node");
button.setSize(node.width, node.height);
button.update(() -> {
button.setPosition(node.x + panX + width / 2f, node.y + panY + height / 2f, Align.center);
});
button.clearChildren();
buildButton(node.zone, button);
addChild(button);
}
dragged((x, y) -> {
panX += x;
panY += y;
});
}
@Override
public void draw(){
float offsetX = panX + width / 2f + x, offsetY = panY + height / 2f + y;
for(ZoneNode node : nodes){
for(ZoneNode child : node.allChildren){
Lines.stroke(Unit.dp.scl(3f), node.zone.locked() || child.zone.locked() ? Pal.locked : Pal.accent);
Lines.line(node.x + offsetX, node.y + offsetY, child.x + offsetX, child.y + offsetY);
}
}
Draw.reset();
super.draw();
}
}
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.zoneRequirements.length > 0 && other.zoneRequirements[0].zone == zone);
children = new ZoneNode[arr.size];
for(int i = 0; i < children.length; i++){
children[i] = new ZoneNode(arr.get(i), this);
}
}
}
}

View File

@@ -1,25 +1,24 @@
package io.anuke.mindustry.ui.dialogs;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.Colors;
import io.anuke.ucore.scene.ui.Dialog;
import io.anuke.arc.Core;
import io.anuke.arc.graphics.Color;
import io.anuke.arc.scene.ui.Dialog;
import io.anuke.mindustry.graphics.Pal;
import static io.anuke.mindustry.Vars.discordURL;
import static io.anuke.mindustry.Vars.ui;
import static io.anuke.mindustry.Vars.*;
public class DiscordDialog extends Dialog {
public class DiscordDialog extends Dialog{
public DiscordDialog(){
super("", "dialog");
float h = 70f;
content().margin(12f);
cont.margin(12f);
Color color = Color.valueOf("7289da");
content().table(t -> {
cont.table(t -> {
t.background("button").margin(0);
t.table(img -> {
@@ -30,22 +29,22 @@ public class DiscordDialog extends Dialog {
t.table(i -> {
i.background("button");
i.addImage("icon-discord").size(14 * 3);
i.addImage("icon-discord").size(iconsize);
}).size(h).left();
t.add("$text.discord").color(Colors.get("accent")).growX().padLeft(10f);
}).size(470f, h).pad(10f);
t.add("$discord").color(Pal.accent).growX().padLeft(10f);
}).size(440f, h).pad(10f);
buttons().defaults().size(170f, 50);
buttons.defaults().size(150f, 50);
buttons().addButton("$text.back", this::hide);
buttons().addButton("$text.copylink", () ->{
Gdx.app.getClipboard().setContents(discordURL);
buttons.addButton("$back", this::hide);
buttons.addButton("$copylink", () -> {
Core.app.getClipboard().setContents(discordURL);
});
buttons().addButton("$text.openlink", () ->{
if(!Gdx.net.openURI(discordURL)){
ui.showError("$text.linkfail");
Gdx.app.getClipboard().setContents(discordURL);
buttons.addButton("$openlink", () -> {
if(!Core.net.openURI(discordURL)){
ui.showError("$linkfail");
Core.app.getClipboard().setContents(discordURL);
}
});
}

View File

@@ -1,337 +1,340 @@
package io.anuke.mindustry.ui.dialogs;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.files.FileHandle;
import com.badlogic.gdx.graphics.g2d.GlyphLayout;
import com.badlogic.gdx.utils.Align;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.Pools;
import io.anuke.arc.Core;
import io.anuke.arc.collection.Array;
import io.anuke.arc.files.FileHandle;
import io.anuke.arc.function.Consumer;
import io.anuke.arc.function.Predicate;
import io.anuke.arc.graphics.g2d.GlyphLayout;
import io.anuke.arc.scene.event.Touchable;
import io.anuke.arc.scene.ui.*;
import io.anuke.arc.scene.ui.layout.Table;
import io.anuke.arc.scene.ui.layout.Unit;
import io.anuke.arc.util.*;
import io.anuke.arc.util.pooling.Pools;
import io.anuke.mindustry.Vars;
import io.anuke.mindustry.core.Platform;
import io.anuke.ucore.UCore;
import io.anuke.ucore.core.Core;
import io.anuke.ucore.core.Timers;
import io.anuke.ucore.function.Consumer;
import io.anuke.ucore.function.Predicate;
import io.anuke.ucore.scene.event.Touchable;
import io.anuke.ucore.scene.ui.*;
import io.anuke.ucore.scene.ui.layout.Table;
import io.anuke.ucore.scene.ui.layout.Unit;
import io.anuke.ucore.util.OS;
import java.util.Arrays;
public class FileChooser extends FloatingDialog {
private Table files;
private FileHandle homeDirectory = Gdx.files.absolute(OS.isMac ? UCore.getProperty("user.home") + "/Downloads/" :
Gdx.files.getExternalStoragePath());
private FileHandle directory = homeDirectory;
private ScrollPane pane;
private TextField navigation, filefield;
private TextButton ok;
private FileHistory stack = new FileHistory();
private Predicate<FileHandle> filter;
private Consumer<FileHandle> selectListener;
private boolean open;
public FileChooser(String title, boolean open, Consumer<FileHandle> result){
this(title, defaultFilter, open, result);
}
import static io.anuke.mindustry.Vars.iconsize;
public FileChooser(String title, Predicate<FileHandle> filter, boolean open, Consumer<FileHandle> result){
super(title);
this.open = open;
this.filter = filter;
this.selectListener = result;
}
public class FileChooser extends FloatingDialog{
private static final FileHandle homeDirectory = Core.files.absolute(OS.isMac ? OS.getProperty("user.home") + "/Downloads/" : Core.files.getExternalStoragePath());
private static FileHandle lastDirectory = homeDirectory;
private void setupWidgets(){
getCell(content()).maxWidth(Gdx.graphics.getWidth()/Unit.dp.scl(2f));
content().margin(-10);
Table content = new Table();
filefield = new TextField();
filefield.setOnlyFontChars(false);
if(!open) Platform.instance.addDialog(filefield);
filefield.setDisabled(open);
private Table files;
private FileHandle directory = lastDirectory;
private ScrollPane pane;
private TextField navigation, filefield;
private TextButton ok;
private FileHistory stack = new FileHistory();
private Predicate<FileHandle> filter;
private Consumer<FileHandle> selectListener;
private boolean open;
private int lastWidth = Core.graphics.getWidth(), lastHeight = Core.graphics.getHeight();
ok = new TextButton(open ? "$text.load" : "$text.save");
ok.clicked(() -> {
if(ok.isDisabled()) return;
if(selectListener != null)
selectListener.accept(directory.child(filefield.getText()));
hide();
});
filefield.changed(() -> {
ok.setDisabled(filefield.getText().replace(" ", "").isEmpty());
});
filefield.change();
TextButton cancel = new TextButton("$text.cancel");
cancel.clicked(this::hide);
public static final Predicate<String> pngFiles = str -> str.equals("png");
public static final Predicate<String> anyMapFiles = str -> str.equals(Vars.oldMapExtension) || str.equals(Vars.mapExtension);
public static final Predicate<String> mapFiles = str -> str.equals(Vars.mapExtension);
public static final Predicate<String> saveFiles = str -> str.equals(Vars.saveExtension);
navigation = new TextField("");
navigation.setTouchable(Touchable.disabled);
public FileChooser(String title, Predicate<FileHandle> filter, boolean open, Consumer<FileHandle> result){
super(title);
this.open = open;
this.filter = filter;
this.selectListener = result;
files = new Table();
update(() -> {
if(Core.graphics.getWidth() != lastWidth || Core.graphics.getHeight() != lastHeight){
updateFiles(false);
lastHeight = Core.graphics.getHeight();
lastWidth = Core.graphics.getWidth();
}
});
}
pane = new ScrollPane(files){
public float getPrefHeight(){
return Gdx.graphics.getHeight();
}
};
pane.setOverscroll(false, false);
pane.setFadeScrollBars(false);
private void setupWidgets(){
cont.margin(-10);
updateFiles(true);
Table content = new Table();
Table icontable = new Table();
float isize = 14*2;
filefield = new TextField();
filefield.setOnlyFontChars(false);
if(!open) Platform.instance.addDialog(filefield);
filefield.setDisabled(open);
ImageButton up = new ImageButton("icon-folder-parent");
up.resizeImage(isize);
up.clicked(()->{
directory = directory.parent();
updateFiles(true);
});
ok = new TextButton(open ? "$load" : "$save");
//Macs are confined to the Downloads/ directory
if(OS.isMac){
up.setDisabled(true);
}
ok.clicked(() -> {
if(ok.isDisabled()) return;
if(selectListener != null)
selectListener.accept(directory.child(filefield.getText()));
hide();
});
ImageButton back = new ImageButton("icon-arrow-left");
back.resizeImage(isize);
ImageButton forward = new ImageButton("icon-arrow-right");
forward.resizeImage(isize);
forward.clicked(()-> stack.forward());
back.clicked(()-> stack.back());
ImageButton home = new ImageButton("icon-home");
home.resizeImage(isize);
home.clicked(()->{
directory = homeDirectory;
updateFiles(true);
});
icontable.defaults().height(50).growX().uniform();
icontable.add(home);
icontable.add(back);
icontable.add(forward);
icontable.add(up);
Table fieldcontent = new Table();
fieldcontent.bottom().left().add(new Label("File Name:"));
fieldcontent.add(filefield).height(40f).fillX().expandX().padLeft(10f);
Table buttons = new Table();
buttons.defaults().growX().height(50);
buttons.add(cancel);
buttons.add(ok);
content.top().left();
content.add(icontable).expandX().fillX();
content.row();
filefield.changed(() -> {
ok.setDisabled(filefield.getText().replace(" ", "").isEmpty());
});
content.center().add(pane).width(Gdx.graphics.getWidth()/Unit.dp.scl(2)).colspan(3).grow();
content.row();
if(!open){
content.bottom().left().add(fieldcontent).colspan(3).grow().padTop(-2).padBottom(2);
content.row();
}
filefield.change();
content.add(buttons).growX();
content().add(content);
}
private void updateFileFieldStatus(){
if(!open){
ok.setDisabled(filefield.getText().replace(" ", "").isEmpty());
}else{
ok.setDisabled(!directory.child(filefield.getText()).exists() || directory.child(filefield.getText()).isDirectory());
}
}
TextButton cancel = new TextButton("$cancel");
cancel.clicked(this::hide);
private FileHandle[] getFileNames(){
FileHandle[] handles = directory.list(file -> !file.getName().startsWith("."));
navigation = new TextField("");
navigation.touchable(Touchable.disabled);
Arrays.sort(handles, (a, b) ->{
if(a.isDirectory() && !b.isDirectory()) return -1;
if( !a.isDirectory() && b.isDirectory()) return 1;
return a.name().toUpperCase().compareTo(b.name().toUpperCase());
});
return handles;
}
files = new Table();
files.marginRight(10);
files.marginLeft(3);
private void updateFiles(boolean push){
if(push) stack.push(directory);
//if is mac, don't display extra info since you can only ever go to downloads
navigation.setText(OS.isMac ? directory.name() : directory.toString());
GlyphLayout layout = Pools.obtain(GlyphLayout.class);
layout.setText(Core.font, navigation.getText());
if(layout.width < navigation.getWidth()){
navigation.setCursorPosition(0);
}else{
navigation.setCursorPosition(navigation.getText().length());
}
Pools.free(layout);
pane = new ScrollPane(files){
public float getPrefHeight(){
return Core.graphics.getHeight();
}
};
pane.setOverscroll(false, false);
pane.setFadeScrollBars(false);
files.clearChildren();
files.top().left();
FileHandle[] names = getFileNames();
updateFiles(true);
//macs are confined to the Downloads/ directory
if(!OS.isMac) {
Image upimage = new Image("icon-folder-parent");
TextButton upbutton = new TextButton(".." + directory.toString());
upbutton.clicked(() -> {
directory = directory.parent();
updateFiles(true);
});
Table icontable = new Table();
upbutton.left().add(upimage).padRight(4f).size(14 * 2);
upbutton.getLabel().setAlignment(Align.left);
upbutton.getCells().reverse();
float isize = iconsize;
files.add(upbutton).align(Align.topLeft).fillX().expandX().height(50).pad(2).colspan(2);
files.row();
}
ButtonGroup<TextButton> group = new ButtonGroup<TextButton>();
group.setMinCheckCount(0);
ImageButton up = new ImageButton("icon-folder-parent");
up.resizeImage(isize);
up.clicked(() -> {
directory = directory.parent();
updateFiles(true);
});
for(FileHandle file : names){
if( !file.isDirectory() && !filter.test(file)) continue; //skip non-filtered files
//Macs are confined to the Downloads/ directory
if(OS.isMac){
up.setDisabled(true);
}
String filename = file.name();
ImageButton back = new ImageButton("icon-arrow-left");
back.resizeImage(isize);
TextButton button = new TextButton(shorten(filename), "toggle");
group.add(button);
button.clicked(()->{
if( !file.isDirectory()){
filefield.setText(filename);
updateFileFieldStatus();
}else{
directory = directory.child(filename);
updateFiles(true);
}
});
filefield.changed(()->{
button.setChecked(filename.equals(filefield.getText()));
});
Image image = new Image(file.isDirectory() ? "icon-folder" : "icon-file-text");
button.add(image).padRight(4f).size(14*2f);
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();
}
ImageButton forward = new ImageButton("icon-arrow-right");
forward.resizeImage(isize);
pane.setScrollY(0f);
updateFileFieldStatus();
if(open) filefield.clearText();
}
forward.clicked(() -> stack.forward());
private String shorten(String string){
int max = 30;
if(string.length() <= max){
return string;
}else{
return string.substring(0, max - 3).concat("...");
}
}
back.clicked(() -> stack.back());
@Override
public Dialog show(){
Platform.instance.requestWritePerms();
Timers.runTask(2f, () -> {
content().clear();
setupWidgets();
super.show();
Core.scene.setScrollFocus(pane);
});
return this;
}
ImageButton home = new ImageButton("icon-home");
home.resizeImage(isize);
home.clicked(() -> {
directory = homeDirectory;
lastDirectory = directory;
updateFiles(true);
});
public void fileSelected(Consumer<FileHandle> listener){
this.selectListener = listener;
}
icontable.defaults().height(60).growX().padTop(5).uniform();
icontable.add(home);
icontable.add(back);
icontable.add(forward);
icontable.add(up);
public class FileHistory{
private Array<FileHandle> history = new Array<FileHandle>();
private int index;
Table fieldcontent = new Table();
fieldcontent.bottom().left().add(new Label("$filename"));
fieldcontent.add(filefield).height(40f).fillX().expandX().padLeft(10f);
public FileHistory(){
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();
public void push(FileHandle file){
if(index != history.size) history.truncate(index);
history.add(file);
index ++;
}
content.center().add(pane).width(Core.graphics.isPortrait() ? Core.graphics.getWidth() / Unit.dp.scl(1) : Core.graphics.getWidth() / Unit.dp.scl(2)).colspan(3).grow();
content.row();
public void back(){
if( !canBack()) return;
index --;
directory = history.get(index - 1);
updateFiles(false);
}
if(!open){
content.bottom().left().add(fieldcontent).colspan(3).grow().padTop(-2).padBottom(2);
content.row();
}
public void forward(){
if( !canForward()) return;
directory = history.get(index);
index ++;
updateFiles(false);
}
content.add(buttons).growX();
public boolean canForward(){
return !(index >= history.size);
}
cont.add(content);
}
public boolean canBack(){
return !(index == 1) && index > 0;
}
private void updateFileFieldStatus(){
if(!open){
ok.setDisabled(filefield.getText().replace(" ", "").isEmpty());
}else{
ok.setDisabled(!directory.child(filefield.getText()).exists() || directory.child(filefield.getText()).isDirectory());
}
}
void print(){
private FileHandle[] getFileNames(){
FileHandle[] handles = directory.list(file -> !file.getName().startsWith("."));
System.out.println("\n\n\n\n\n\n");
int i = 0;
for(FileHandle file : history){
i ++;
if(index == i){
System.out.println("[[" + file.toString() + "]]");
}else{
System.out.println("--" + file.toString() + "--");
}
}
}
}
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;
}
public interface FileHandleFilter{
boolean accept(FileHandle file);
}
private void updateFiles(boolean push){
if(push) stack.push(directory);
//if is mac, don't display extra info since you can only ever go to downloads
navigation.setText(OS.isMac ? directory.name() : directory.toString());
public static Predicate<FileHandle> pngFilter = file -> file.extension().equalsIgnoreCase("png");
public static Predicate<FileHandle> jpegFilter = file -> file.extension().equalsIgnoreCase("png") || file.extension().equalsIgnoreCase("jpg") || file.extension().equalsIgnoreCase("jpeg");
public static Predicate<FileHandle> defaultFilter = file -> true;
GlyphLayout layout = Pools.obtain(GlyphLayout.class, GlyphLayout::new);
layout.setText(Core.scene.skin.getFont("default-font"), navigation.getText());
if(layout.width < navigation.getWidth()){
navigation.setCursorPosition(0);
}else{
navigation.setCursorPosition(navigation.getText().length());
}
Pools.free(layout);
files.clearChildren();
files.top().left();
FileHandle[] names = getFileNames();
//macs are confined to the Downloads/ directory
if(!OS.isMac){
Image upimage = new Image("icon-folder-parent");
TextButton upbutton = new TextButton(".." + directory.toString(), "clear-toggle");
upbutton.clicked(() -> {
directory = directory.parent();
lastDirectory = directory;
updateFiles(true);
});
upbutton.left().add(upimage).padRight(4f).size(iconsize);
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(FileHandle file : names){
if(!file.isDirectory() && !filter.test(file)) continue; //skip non-filtered files
String filename = file.name();
TextButton button = new TextButton(shorten(filename), "clear-toggle");
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-folder" : "icon-file-text");
button.add(image).padRight(4f).size(iconsize);
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("...");
}
}
@Override
public Dialog show(){
Time.runTask(2f, () -> {
cont.clear();
setupWidgets();
super.show();
Core.scene.setScrollFocus(pane);
});
return this;
}
public class FileHistory{
private Array<FileHandle> history = new Array<>();
private int index;
public FileHistory(){
}
public void push(FileHandle 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(FileHandle file : history){
i++;
if(index == i){
System.out.println("[[" + file.toString() + "]]");
}else{
System.out.println("--" + file.toString() + "--");
}
}
}
}
}

View File

@@ -1,28 +1,75 @@
package io.anuke.mindustry.ui.dialogs;
import com.badlogic.gdx.Input.Keys;
import com.badlogic.gdx.graphics.Colors;
import com.badlogic.gdx.utils.Align;
import io.anuke.ucore.scene.ui.Dialog;
import io.anuke.arc.Core;
import io.anuke.arc.Events;
import io.anuke.arc.input.KeyCode;
import io.anuke.arc.scene.ui.Dialog;
import io.anuke.arc.scene.ui.ScrollPane;
import io.anuke.arc.util.Align;
import io.anuke.mindustry.core.GameState.State;
import io.anuke.mindustry.game.EventType.ResizeEvent;
import io.anuke.mindustry.graphics.Pal;
import io.anuke.mindustry.net.Net;
import static io.anuke.mindustry.Vars.iconsize;
import static io.anuke.mindustry.Vars.state;
public class FloatingDialog extends Dialog{
public FloatingDialog(String title){
super(title, "dialog");
setFillParent(true);
title().setAlignment(Align.center);
getTitleTable().row();
getTitleTable().addImage("white", Colors.get("accent"))
.growX().height(3f).pad(4f);
}
@Override
public void addCloseButton(){
buttons().addImageTextButton("$text.back", "icon-arrow-left", 30f, this::hide).size(230f, 64f);
keyDown(key->{
if(key == Keys.ESCAPE || key == Keys.BACK)
hide();
});
}
private boolean wasPaused;
protected boolean shouldPause;
public FloatingDialog(String title){
super(title, "dialog");
setFillParent(true);
this.title.setAlignment(Align.center);
titleTable.row();
titleTable.addImage("white", Pal.accent)
.growX().height(3f).pad(4f);
hidden(() -> {
if(shouldPause && !state.is(State.menu)){
if(!wasPaused || Net.active()){
state.set(State.playing);
}
}
});
shown(() -> {
if(shouldPause && !state.is(State.menu)){
wasPaused = state.is(State.paused);
state.set(State.paused);
}
});
boolean[] done = {false};
shown(() -> Core.app.post(() ->
forEach(child -> {
if(done[0]) return;
if(child instanceof ScrollPane){
Core.scene.setScrollFocus(child);
done[0] = true;
}
})));
}
protected void onResize(Runnable run){
Events.on(ResizeEvent.class, event -> {
if(isShown()){
run.run();
}
});
}
@Override
public void addCloseButton(){
buttons.addImageTextButton("$back", "icon-arrow-left", iconsize, this::hide).size(210f, 64f);
keyDown(key -> {
if(key == KeyCode.ESCAPE || key == KeyCode.BACK){
Core.app.post(this::hide);
}
});
}
}

View File

@@ -0,0 +1,96 @@
package io.anuke.mindustry.ui.dialogs;
import io.anuke.arc.Core;
import io.anuke.mindustry.core.GameState.State;
import io.anuke.mindustry.game.Stats.RankResult;
import io.anuke.mindustry.game.Team;
import io.anuke.mindustry.type.Item;
import io.anuke.mindustry.type.Item.Icon;
import static io.anuke.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();
}
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(Icon.medium)).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);
}
}
}
}

View File

@@ -1,13 +1,12 @@
package io.anuke.mindustry.ui.dialogs;
import com.badlogic.gdx.graphics.Color;
import io.anuke.arc.Core;
import io.anuke.arc.graphics.Color;
import io.anuke.arc.scene.ui.ImageButton;
import io.anuke.arc.util.Strings;
import io.anuke.arc.util.Time;
import io.anuke.mindustry.Vars;
import io.anuke.mindustry.net.Net;
import io.anuke.ucore.core.Settings;
import io.anuke.ucore.core.Timers;
import io.anuke.ucore.scene.ui.ImageButton;
import io.anuke.ucore.util.Bundles;
import io.anuke.ucore.util.Strings;
import java.io.IOException;
@@ -18,48 +17,52 @@ public class HostDialog extends FloatingDialog{
float w = 300;
public HostDialog(){
super("$text.hostserver");
super("$hostserver");
addCloseButton();
content().table(t -> {
t.add("$text.name").padRight(10);
t.addField(Settings.getString("name"), text -> {
if(text.isEmpty()) return;
Vars.player.name = text;
Settings.put("name", text);
Settings.save();
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("white", 40, () -> {
ImageButton button = t.addImageButton("white", "clear-full", 40, () -> {
new ColorPickDialog().show(color -> {
player.color.set(color);
Settings.putInt("color", Color.rgba8888(color));
Settings.save();
Core.settings.put("color-0", Color.rgba8888(color));
Core.settings.save();
});
}).size(50f, 54f).get();
button.update(() -> button.getStyle().imageUpColor = player.getColor());
}).size(54f).get();
button.update(() -> button.getStyle().imageUpColor = player.color);
}).width(w).height(70f).pad(4).colspan(3);
content().row();
cont.row();
content().add().width(65f);
cont.add().width(65f);
content().addButton("$text.host", () -> {
ui.loadfrag.show("$text.hosting");
Timers.runTask(5f, () -> {
cont.addButton("$host", () -> {
if(Core.settings.getString("name").trim().isEmpty()){
ui.showInfo("$noname");
return;
}
ui.loadfrag.show("$hosting");
Time.runTask(5f, () -> {
try{
Net.host(Vars.port);
player.isAdmin = true;
}catch (IOException e){
ui.showError(Bundles.format("text.server.error", Strings.parseException(e, false)));
}catch(IOException e){
ui.showError(Core.bundle.format("server.error", Strings.parseException(e, true)));
}
ui.loadfrag.hide();
hide();
});
}).width(w).height(70f);
content().addButton("?", () -> ui.showInfo("$text.host.info")).size(65f, 70f).padLeft(6f);
cont.addButton("?", () -> ui.showInfo("$host.info")).size(65f, 70f).padLeft(6f);
}
}

View File

@@ -1,99 +1,103 @@
package io.anuke.mindustry.ui.dialogs;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.utils.Array;
import io.anuke.annotations.Annotations.Serialize;
import io.anuke.arc.Core;
import io.anuke.arc.collection.Array;
import io.anuke.arc.graphics.Color;
import io.anuke.arc.math.Mathf;
import io.anuke.arc.scene.style.Drawable;
import io.anuke.arc.scene.ui.*;
import io.anuke.arc.scene.ui.layout.Cell;
import io.anuke.arc.scene.ui.layout.Table;
import io.anuke.arc.util.Strings;
import io.anuke.arc.util.Time;
import io.anuke.mindustry.Vars;
import io.anuke.mindustry.core.Platform;
import io.anuke.mindustry.io.Version;
import io.anuke.mindustry.game.Version;
import io.anuke.mindustry.net.Host;
import io.anuke.mindustry.net.Net;
import io.anuke.ucore.core.Settings;
import io.anuke.ucore.core.Timers;
import io.anuke.ucore.scene.style.Drawable;
import io.anuke.ucore.scene.ui.Dialog;
import io.anuke.ucore.scene.ui.ImageButton;
import io.anuke.ucore.scene.ui.ScrollPane;
import io.anuke.ucore.scene.ui.TextButton;
import io.anuke.ucore.scene.ui.layout.Cell;
import io.anuke.ucore.scene.ui.layout.Table;
import io.anuke.ucore.util.Bundles;
import io.anuke.ucore.util.Log;
import io.anuke.ucore.util.Strings;
import static io.anuke.mindustry.Vars.player;
import static io.anuke.mindustry.Vars.ui;
import static io.anuke.mindustry.Vars.*;
public class JoinDialog extends FloatingDialog {
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();
float w = 500;
int totalHosts;
public JoinDialog(){
super("$text.joingame");
super("$joingame");
loadServers();
buttons().add().width(60f);
buttons().add().growX();
buttons.add().width(60f);
buttons.add().growX();
addCloseButton();
buttons().add().growX();
buttons().addButton("?", () -> ui.showInfo("$text.join.info")).size(60f, 64f);
buttons.add().growX();
buttons.addButton("?", () -> ui.showInfo("$join.info")).size(60f, 64f);
add = new FloatingDialog("$text.joingame.title");
add.content().add("$text.joingame.ip").padRight(5f).left();
add = new FloatingDialog("$joingame.title");
add.cont.add("$joingame.ip").padRight(5f).left();
Platform.instance.addDialog(add.content().addField(Settings.getString("ip"), text ->{
Settings.putString("ip", text);
Settings.save();
}).size(320f, 54f).get(), 100);
TextField field = add.cont.addField(Core.settings.getString("ip"), text -> {
Core.settings.put("ip", text);
Core.settings.save();
}).size(320f, 54f).get();
add.content().row();
add.buttons().defaults().size(140f, 60f).pad(4f);
add.buttons().addButton("$text.cancel", add::hide);
add.buttons().addButton("$text.ok", () -> {
if(renaming == null) {
Server server = new Server(Settings.getString("ip"), Strings.parseInt(Settings.getString("port")));
Platform.instance.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.ip = Settings.getString("ip");
renaming.setIP(Core.settings.getString("ip"));
saveServers();
setupRemote();
refreshRemote();
}
add.hide();
}).disabled(b -> Settings.getString("ip").isEmpty() || Net.active());
}).disabled(b -> Core.settings.getString("ip").isEmpty() || Net.active());
add.shown(() -> {
add.getTitleLabel().setText(renaming != null ? "$text.server.edit" : "$text.server.add");
add.title.setText(renaming != null ? "$server.edit" : "$server.add");
if(renaming != null){
field.setText(renaming.displayIP());
}
});
setup();
shown(() -> {
setup();
refreshLocal();
refreshRemote();
});
onResize(this::setup);
}
void setupRemote(){
remote.clear();
for (Server server : servers) {
for(Server server : servers){
//why are java lambdas this bad
TextButton[] buttons = {null};
TextButton button = buttons[0] = remote.addButton("[accent]"+server.ip, "clear", () -> {
if(!buttons[0].childrenPressed()) connect(server.ip, Vars.port);
}).width(w).height(150f).pad(4f).get();
TextButton button = buttons[0] = remote.addButton("[accent]" + server.displayIP(), "clear", () -> {
if(!buttons[0].childrenPressed()){
connect(server.ip, server.port);
}
}).width(targetWidth()).height(155f).pad(4f).get();
button.getLabel().setWrap(true);
@@ -103,17 +107,36 @@ public class JoinDialog extends FloatingDialog {
inner.add(button.getLabel()).growX();
inner.addImageButton("icon-loading", "empty", 16*2, () -> {
inner.addImageButton("icon-arrow-up-small", "empty", iconsizesmall, () -> {
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-loading-small", "empty", iconsizesmall, () -> {
refreshServer(server);
}).margin(3f).padTop(6f).top().right();
inner.addImageButton("icon-pencil", "empty", 16*2, () -> {
inner.addImageButton("icon-pencil-small", "empty", iconsizesmall, () -> {
renaming = server;
add.show();
}).margin(3f).padTop(6f).top().right();
inner.addImageButton("icon-trash-16", "empty", 16*2, () -> {
ui.showConfirm("$text.confirm", "$text.server.delete", () -> {
inner.addImageButton("icon-trash-16-small", "empty", iconsizesmall, () -> {
ui.showConfirm("$confirm", "$server.delete", () -> {
servers.removeValue(server, true);
saveServers();
setupRemote();
@@ -123,7 +146,8 @@ public class JoinDialog extends FloatingDialog {
button.row();
server.content = button.table(t -> {}).grow().get();
server.content = button.table(t -> {
}).grow().get();
remote.row();
}
@@ -137,92 +161,84 @@ public class JoinDialog extends FloatingDialog {
void refreshServer(Server server){
server.content.clear();
server.content.label(() -> Bundles.get("text.server.refreshing") + Strings.animated(4, 11, "."));
Net.pingHost(server.ip, server.port, host -> {
String versionString;
if(host.version == -1) {
versionString = Bundles.format("text.server.version", Bundles.get("text.server.custombuild"));
}else if(host.version == 0){
versionString = Bundles.get("text.server.outdated");
}else if(host.version < Version.build && Version.build != -1){
versionString = Bundles.get("text.server.outdated") + "\n" +
Bundles.format("text.server.version", host.version);
}else if(host.version > Version.build && Version.build != -1){
versionString = Bundles.get("text.server.outdated.client") + "\n" +
Bundles.format("text.server.version", host.version);
}else{
versionString = Bundles.format("text.server.version", host.version);
}
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.table(t -> {
t.add(versionString).left();
t.row();
t.add("[lightgray]" + Bundles.format("text.server.hostname", host.name)).left();
t.row();
t.add("[lightgray]" + (host.players != 1 ? Bundles.format("text.players", host.players) :
Bundles.format("text.players.single", host.players))).left();
t.row();
t.add("[lightgray]" + Bundles.format("text.save.map", host.mapname) + " / " + Bundles.format("text.save.wave", host.wave)).left();
}).expand().left().bottom().padLeft(12f).padBottom(8);
//server.content.add(versionString).top().expandY().top().expandX();
}, e -> {
server.content.clear();
server.content.add("$text.host.invalid");
server.content.add("$host.invalid");
});
}
void refreshLocal(){
if(!Vars.gwt) {
local.clear();
local.background("button");
local.label(() -> "[accent]" + Bundles.get("text.hosts.discovering") + Strings.animated(4, 10f, ".")).pad(10f);
Net.discoverServers(this::addLocalHosts);
void setupServer(Server server, Host host){
server.lastHost = host;
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{
versionString = Core.bundle.format("server.version", host.version, host.versionType);
}
server.content.clear();
server.content.table(t -> {
t.add("[lightgray]" + host.name).width(targetWidth() - 10f).left().get().setEllipsis(true);
t.row();
t.add(versionString).left();
t.row();
t.add("[lightgray]" + (host.players != 1 ? Core.bundle.format("players", host.players) :
Core.bundle.format("players.single", host.players))).left();
t.row();
t.add("[lightgray]" + Core.bundle.format("save.map", host.mapname) + "[] / " + Core.bundle.format("save.wave", host.wave)).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, "clear");
ScrollPane pane = new ScrollPane(hosts);
pane.setFadeScrollBars(false);
pane.setScrollingDisabled(true, false);
setupRemote();
refreshRemote();
content().clear();
content().table(t -> {
t.add("$text.name").padRight(10);
t.addField(Settings.getString("name"), text -> {
if(text.isEmpty()) return;
Vars.player.name = text;
Settings.put("name", text);
Settings.save();
}).grow().pad(8).get().setMaxLength(40);
cont.clear();
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();
}).grow().pad(8).get().setMaxLength(maxNameLength);
ImageButton button = t.addImageButton("white", 40, () -> {
ImageButton button = t.addImageButton("white", "clear-full", 40, () -> {
new ColorPickDialog().show(color -> {
player.color.set(color);
Settings.putInt("color", Color.rgba8888(color));
Settings.save();
Core.settings.put("color-0", Color.rgba8888(color));
Core.settings.save();
});
}).size(50f, 54f).get();
button.update(() -> button.getStyle().imageUpColor = player.getColor());
}).size(54f).get();
button.update(() -> button.getStyle().imageUpColor = player.color);
}).width(w).height(70f).pad(4);
content().row();
content().add(pane).width(w + 34).pad(0);
content().row();
content().addCenteredImageTextButton("$text.server.add", "icon-add", "clear", 14*3, () -> {
cont.row();
cont.add(pane).width(w + 38).pad(0);
cont.row();
cont.addCenteredImageTextButton("$server.add", "icon-add", iconsize, () -> {
renaming = null;
add.show();
}).marginLeft(6).width(w).height(80f).update(button -> {
@@ -233,9 +249,9 @@ public class JoinDialog extends FloatingDialog {
pad = 6;
}
Cell<TextButton> cell = ((Table)pane.getParent()).getCell(button);
Cell cell = ((Table)pane.getParent()).getCell(button);
if(!MathUtils.isEqual(cell.getMinWidth(), pw)){
if(!Mathf.isEqual(cell.minWidth(), pw)){
cell.width(pw);
cell.padLeft(pad);
pane.getParent().invalidateHierarchy();
@@ -243,100 +259,113 @@ public class JoinDialog extends FloatingDialog {
});
}
void addLocalHosts(Array<Host> array){
void refreshLocal(){
totalHosts = 0;
local.clear();
local.background((Drawable)null);
local.table("button", t -> t.label(() -> "[accent]" + Core.bundle.get("hosts.discovering") + Strings.animated(Time.time(), 4, 10f, ".")).pad(10f)).growX();
Net.discoverServers(this::addLocalHost, this::finishLocalHosts);
}
if(array.size == 0){
local.add("$text.hosts.none").pad(10f);
void finishLocalHosts(){
if(totalHosts == 0){
local.clear();
local.background("button");
local.add("$hosts.none").pad(10f);
local.add().growX();
local.addImageButton("icon-loading", 16*2f, this::refreshLocal).pad(-10f).padLeft(0).padTop(-6).size(70f, 74f);
}else {
for (Host a : array) {
TextButton button = local.addButton("[accent]"+a.name, "clear", () -> {
connect(a.address, Vars.port);
}).width(w).height(80f).pad(4f).get();
button.left();
button.row();
button.add("[lightgray]" + (a.players != 1 ? Bundles.format("text.players", a.players) :
Bundles.format("text.players.single", a.players)));
button.row();
button.add("[lightgray]" + a.address).pad(4).left();
local.row();
local.background((Drawable) null);
}
local.addImageButton("icon-loading", iconsize, this::refreshLocal).pad(-12f).padLeft(0).size(70f);
}else{
local.background((Drawable)null);
}
}
void addLocalHost(Host host){
if(totalHosts == 0){
local.clear();
}
local.background((Drawable)null);
totalHosts++;
float w = targetWidth();
local.row();
TextButton button = local.addButton("[accent]" + host.name, "clear", () -> connect(host.address, port))
.width(w).height(80f).pad(4f).get();
button.left();
button.row();
button.add("[lightgray]" + (host.players != 1 ? Core.bundle.format("players", host.players) :
Core.bundle.format("players.single", host.players))).padBottom(5);
}
void connect(String ip, int port){
ui.loadfrag.show("$text.connecting");
if(Core.settings.getString("name").trim().isEmpty()){
ui.showInfo("$noname");
return;
}
Timers.runTask(2f, () -> {
try{
Vars.netClient.beginConnecting();
Net.connect(ip, port);
ui.loadfrag.show("$connecting");
ui.loadfrag.setButton(() -> {
ui.loadfrag.hide();
netClient.disconnectQuietly();
});
Time.runTask(2f, () -> {
logic.reset();
Vars.netClient.beginConnecting();
Net.connect(ip, port, () -> {
hide();
add.hide();
}catch (Exception e) {
Throwable t = e;
while(t.getCause() != null){
t = t.getCause();
}
//TODO localize
String error = t.getMessage() == null ? "" : t.getMessage().toLowerCase();
if(error.contains("connection refused")) {
error = "connection refused";
}else if(error.contains("port out of range")){
error = "invalid port!";
}else if(error.contains("invalid argument")) {
error = "invalid IP or port!";
}else if(t.getClass().toString().toLowerCase().contains("sockettimeout")){
error = "timed out!\nmake sure the host has port forwarding set up,\nand that the address is correct!";
}else{
error = Strings.parseException(e, false);
}
ui.showError(Bundles.format("text.connectfail", error));
ui.loadfrag.hide();
Log.err(e);
}
});
});
}
private void loadServers(){
String h = Settings.getString("servers");
String[] list = h.split("\\|\\|\\|");
for(String fname : list){
if(fname.isEmpty()) continue;
String[] split = fname.split(":");
String host = split[0];
int port = Strings.parseInt(split[1]);
float targetWidth(){
return Core.graphics.isPortrait() ? 350f : 500f;
}
if(port != Integer.MIN_VALUE) servers.add(new Server(host, port));
}
@SuppressWarnings("unchecked")
private void loadServers(){
servers = Core.settings.getObject("server-list", Array.class, Array::new);
}
private void saveServers(){
StringBuilder out = new StringBuilder();
for(Server server : servers){
out.append(server.ip);
out.append(":");
out.append(server.port);
out.append("|||");
}
Settings.putString("servers", out.toString());
Settings.save();
Core.settings.putObject("server-list", servers);
Core.settings.save();
}
private class Server{
@Serialize
public static class Server{
public String ip;
public int port;
public Host host;
public Table content;
public Server(String ip, int port){
this.ip = ip;
this.port = 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(){
}
}
}

View File

@@ -1,12 +1,10 @@
package io.anuke.mindustry.ui.dialogs;
import io.anuke.mindustry.core.Platform;
import io.anuke.ucore.core.Settings;
import io.anuke.ucore.scene.ui.ButtonGroup;
import io.anuke.ucore.scene.ui.ScrollPane;
import io.anuke.ucore.scene.ui.TextButton;
import io.anuke.ucore.scene.ui.layout.Table;
import io.anuke.ucore.util.Log;
import io.anuke.arc.Core;
import io.anuke.arc.scene.ui.*;
import io.anuke.arc.scene.ui.layout.Table;
import io.anuke.arc.util.Log;
import io.anuke.arc.util.Strings;
import java.util.Locale;
@@ -14,9 +12,10 @@ import static io.anuke.mindustry.Vars.locales;
import static io.anuke.mindustry.Vars.ui;
public class LanguageDialog extends FloatingDialog{
private Locale lastLocale;
public LanguageDialog(){
super("$text.settings.language");
super("$settings.language");
addCloseButton();
setup();
}
@@ -24,26 +23,62 @@ public class LanguageDialog extends FloatingDialog{
private void setup(){
Table langs = new Table();
langs.marginRight(24f).marginLeft(24f);
ScrollPane pane = new ScrollPane(langs, "clear");
ScrollPane pane = new ScrollPane(langs);
pane.setFadeScrollBars(false);
ButtonGroup<TextButton> group = new ButtonGroup<>();
for(Locale loc : locales){
TextButton button = new TextButton(Platform.instance.getLocaleName(loc), "toggle");
button.setChecked(ui.getLocale().equals(loc));
TextButton button = new TextButton(Strings.capitalize(loc.getDisplayName(loc)), "toggle");
button.clicked(() -> {
if(ui.getLocale().equals(loc)) return;
Settings.putString("locale", loc.toString());
Settings.save();
if(getLocale().equals(loc)) return;
Core.settings.put("locale", loc.toString());
Core.settings.save();
Log.info("Setting locale: {0}", loc.toString());
ui.showInfo("$text.language.restart");
ui.showInfo("$language.restart");
});
langs.add(button).group(group).update(t -> {
t.setChecked(loc.equals(ui.getLocale()));
}).size(400f, 60f).row();
langs.add(button).group(group).update(t -> t.setChecked(loc.equals(getLocale()))).size(400f, 50f).pad(2).row();
}
content().add(pane);
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());
}
}

View File

@@ -1,188 +0,0 @@
package io.anuke.mindustry.ui.dialogs;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.utils.Align;
import io.anuke.mindustry.game.Difficulty;
import io.anuke.mindustry.game.GameMode;
import io.anuke.mindustry.world.Map;
import io.anuke.ucore.core.Core;
import io.anuke.ucore.core.Settings;
import io.anuke.ucore.core.Timers;
import io.anuke.ucore.scene.event.ClickListener;
import io.anuke.ucore.scene.event.InputEvent;
import io.anuke.ucore.scene.event.Touchable;
import io.anuke.ucore.scene.ui.*;
import io.anuke.ucore.scene.ui.layout.Stack;
import io.anuke.ucore.scene.ui.layout.Table;
import io.anuke.ucore.scene.utils.Elements;
import io.anuke.ucore.util.Bundles;
import io.anuke.ucore.util.Mathf;
import static io.anuke.mindustry.Vars.*;
public class LevelDialog extends FloatingDialog{
private Map selectedMap = world.maps().getMap(0);
private ScrollPane pane;
public LevelDialog(){
super("$text.level.select");
getTitleTable().getCell(title()).growX().center();
getTitleTable().center();
addCloseButton();
setup();
}
public void reload(){
content().clear();
setup();
}
void setup(){
Table maps = new Table();
pane = new ScrollPane(maps);
pane.setFadeScrollBars(false);
int maxwidth = 4;
Table selmode = new Table();
ButtonGroup<TextButton> group = new ButtonGroup<>();
selmode.add("$text.level.mode").padRight(15f);
for(GameMode mode : GameMode.values()){
TextButton[] b = {null};
b[0] = Elements.newButton("$mode." + mode.name() + ".name", "toggle", () -> state.mode = mode);
b[0].update(() -> b[0].setChecked(state.mode == mode));
b[0].getLabelCell().wrap().growX().get().setAlignment(Align.center, Align.left);
group.add(b[0]);
selmode.add(b[0]).size(130f, 54f);
}
selmode.addButton("?", this::displayGameModeHelp).size(50f, 54f).padLeft(18f);
content().add(selmode);
content().row();
Difficulty[] ds = Difficulty.values();
float s = 50f;
Table sdif = new Table();
sdif.add("$setting.difficulty.name").padRight(15f);
sdif.defaults().height(s+4);
sdif.addImageButton("icon-arrow-left", 10*3, () -> {
state.difficulty = (ds[Mathf.mod(state.difficulty.ordinal() - 1, ds.length)]);
}).width(s);
sdif.addButton("", () -> {
}).update(t -> {
t.setText(state.difficulty.toString());
t.setTouchable(Touchable.disabled);
}).width(180f);
sdif.addImageButton("icon-arrow-right", 10*3, () -> {
state.difficulty = (ds[Mathf.mod(state.difficulty.ordinal() + 1, ds.length)]);
}).width(s);
content().add(sdif);
content().row();
int i = 0;
for(Map map : world.maps().list()){
if(!map.visible && !debug) continue;
if(i % maxwidth == 0){
maps.row();
}
Table inset = new Table("pane-button");
inset.add("[accent]" + Bundles.get("map."+map.name+".name", map.name)).pad(3f);
inset.row();
inset.label((() -> Bundles.format("text.level.highscore", Settings.getInt("hiscore" + map.name, 0)))).pad(3f).padLeft(-2).padRight(-2)
.wrap().growX().get().setAlignment(Align.center, Align.left);
inset.pack();
float images = 154f;
Stack stack = new Stack();
Image back = new Image("white");
back.setColor(map.backgroundColor);
ImageButton image = new ImageButton(new TextureRegion(map.texture), "togglemap");
image.row();
image.add(inset).width(images+6);
TextButton[] delete = new TextButton[1];
if(map.custom){
image.row();
delete[0] = image.addButton("Delete", () -> {
Timers.run(1f, () -> {
ui.showConfirm("$text.level.delete.title", Bundles.format("text.level.delete", Bundles.get("map."+map.name+".name", map.name)), () -> {
world.maps().removeMap(map);
reload();
Core.scene.setScrollFocus(pane);
});
});
}).width(images+16).padBottom(-10f).grow().get();
}
Vector2 hit = new Vector2();
image.addListener(new ClickListener(){
public void clicked(InputEvent event, float x, float y){
image.localToStageCoordinates(hit.set(x, y));
if(delete[0] != null && (delete[0].getClickListener().isOver() || delete[0].getClickListener().isPressed()
|| (Core.scene.hit(hit.x, hit.y, true) != null &&
Core.scene.hit(hit.x, hit.y, true).isDescendantOf(delete[0])))){
return;
}
selectedMap = map;
hide();
control.playMap(selectedMap);
}
});
image.getImageCell().size(images);
stack.add(back);
stack.add(image);
maps.add(stack).width(170).top().pad(4f);
maps.marginRight(26);
i ++;
}
content().add(pane).uniformX();
shown(()->{
//this is necessary for some reason?
Timers.run(2f, ()->{
Core.scene.setScrollFocus(pane);
});
});
}
private void displayGameModeHelp() {
FloatingDialog d = new FloatingDialog(Bundles.get("mode.text.help.title"));
d.setFillParent(false);
Table table = new Table();
table.defaults().pad(1f);
ScrollPane pane = new ScrollPane(table, "clear");
pane.setFadeScrollBars(false);
table.row();
for(GameMode mode : GameMode.values()){
table.labelWrap("[accent]" + mode.toString() + ":[] [lightgray]" + mode.description()).width(600f);
table.row();
}
d.content().add(pane);
d.buttons().addButton("$text.ok", d::hide).size(110, 50).pad(10f);
d.show();
}
}

View File

@@ -1,195 +1,192 @@
package io.anuke.mindustry.ui.dialogs;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.files.FileHandle;
import com.badlogic.gdx.utils.Array;
import io.anuke.arc.Core;
import io.anuke.arc.collection.Array;
import io.anuke.arc.files.FileHandle;
import io.anuke.arc.scene.ui.ScrollPane;
import io.anuke.arc.scene.ui.TextButton;
import io.anuke.arc.scene.ui.layout.Table;
import io.anuke.arc.util.*;
import io.anuke.mindustry.Vars;
import io.anuke.mindustry.core.GameState.State;
import io.anuke.mindustry.core.Platform;
import io.anuke.mindustry.game.Saves.SaveSlot;
import io.anuke.mindustry.io.SaveIO;
import io.anuke.mindustry.io.Saves.SaveSlot;
import io.anuke.ucore.core.Core;
import io.anuke.ucore.core.Timers;
import io.anuke.ucore.scene.ui.ScrollPane;
import io.anuke.ucore.scene.ui.TextButton;
import io.anuke.ucore.scene.ui.layout.Table;
import io.anuke.ucore.util.Bundles;
import io.anuke.ucore.util.Log;
import io.anuke.ucore.util.Strings;
import io.anuke.mindustry.io.SaveIO.SaveException;
import java.io.IOException;
import static io.anuke.mindustry.Vars.*;
public class LoadDialog extends FloatingDialog{
ScrollPane pane;
Table slots;
ScrollPane pane;
Table slots;
public LoadDialog() {
this("$text.loadgame");
}
public LoadDialog(){
this("$loadgame");
}
public LoadDialog(String title) {
super(title);
setup();
public LoadDialog(String title){
super(title);
setup();
shown(() -> {
setup();
Timers.runTask(2f, () -> Core.scene.setScrollFocus(pane));
});
shown(() -> {
setup();
Time.runTask(2f, () -> Core.scene.setScrollFocus(pane));
});
addCloseButton();
}
addCloseButton();
}
protected void setup(){
content().clear();
protected void setup(){
cont.clear();
slots = new Table();
pane = new ScrollPane(slots, "clear-black");
pane.setFadeScrollBars(false);
pane.setScrollingDisabled(true, false);
slots = new Table();
pane = new ScrollPane(slots);
pane.setFadeScrollBars(false);
pane.setScrollingDisabled(true, false);
slots.marginRight(24);
slots.marginRight(24);
Timers.runTask(2f, () -> Core.scene.setScrollFocus(pane));
Time.runTask(2f, () -> Core.scene.setScrollFocus(pane));
Array<SaveSlot> array = control.getSaves().getSaveSlots();
Array<SaveSlot> array = control.saves.getSaveSlots();
for(SaveSlot slot : array){
for(SaveSlot slot : array){
if(slot.isHidden()) continue;
TextButton button = new TextButton("[accent]" + slot.getName(), "clear");
button.getLabelCell().growX().left();
button.getLabelCell().padBottom(8f);
button.getLabelCell().top().left().growX();
TextButton button = new TextButton("[accent]" + slot.getName(), "clear");
button.getLabelCell().growX().left();
button.getLabelCell().padBottom(8f);
button.getLabelCell().top().left().growX();
button.defaults().left();
button.defaults().left();
button.table(t -> {
t.right();
button.table(t -> {
t.right();
t.addImageButton("icon-floppy", "emptytoggle", 14*3, () -> {
slot.setAutosave(!slot.isAutosave());
}).checked(slot.isAutosave()).right();
t.addImageButton("icon-floppy", "emptytoggle", iconsize, () -> {
slot.setAutosave(!slot.isAutosave());
}).checked(slot.isAutosave()).right();
t.addImageButton("icon-trash", "empty", 14*3, () -> {
ui.showConfirm("$text.confirm", "$text.save.delete.confirm", () -> {
slot.delete();
setup();
});
}).size(14*3).right();
t.addImageButton("icon-trash", "empty", iconsize, () -> {
ui.showConfirm("$confirm", "$save.delete.confirm", () -> {
slot.delete();
setup();
});
}).size(iconsize).right();
t.addImageButton("icon-pencil-small", "empty", 14*3, () -> {
ui.showTextInput("$text.save.rename", "$text.save.rename.text", slot.getName(), text -> {
slot.setName(text);
setup();
});
}).size(14*3).right();
t.addImageButton("icon-pencil", "empty", iconsize, () -> {
ui.showTextInput("$save.rename", "$save.rename.text", slot.getName(), text -> {
slot.setName(text);
setup();
});
}).size(iconsize).right();
if(!gwt) {
t.addImageButton("icon-save", "empty", 14 * 3, () -> {
if(!ios) {
Platform.instance.showFileChooser(Bundles.get("text.save.export"), "Mindustry Save", file -> {
try {
slot.exportFile(file);
setup();
} catch (IOException e) {
ui.showError(Bundles.format("text.save.export.fail", Strings.parseException(e, false)));
}
}, false, "mins");
}else{
try {
FileHandle file = Gdx.files.local("save-" + slot.getName() + ".mins");
t.addImageButton("icon-save", "empty", iconsize, () -> {
if(!ios){
Platform.instance.showFileChooser(Core.bundle.get("save.export"), "Mindustry Save", file -> {
try{
slot.exportFile(file);
Platform.instance.shareFile(file);
}catch (Exception e){
ui.showError(Bundles.format("text.save.export.fail", Strings.parseException(e, false)));
setup();
}catch(IOException e){
ui.showError(Core.bundle.format("save.export.fail", Strings.parseException(e, true)));
}
}, false, FileChooser.saveFiles);
}else{
try{
FileHandle file = Core.files.local("save-" + slot.getName() + "." + Vars.saveExtension);
slot.exportFile(file);
Platform.instance.shareFile(file);
}catch(Exception e){
ui.showError(Core.bundle.format("save.export.fail", Strings.parseException(e, true)));
}
}).size(14 * 3).right();
}
}
}).size(iconsize).right();
}).padRight(-10).growX();
String color = "[lightgray]";
}).padRight(-10).growX();
button.defaults().padBottom(3);
button.row();
button.add(Bundles.format("text.save.map", color+slot.getMap().localized()));
button.row();
button.add(Bundles.get("text.level.mode") + " " +color+ slot.getMode());
button.row();
button.add(Bundles.format("text.save.wave", color+slot.getWave()));
button.row();
button.add(Bundles.format("text.save.difficulty", color+slot.getDifficulty()));
button.row();
button.label(() -> Bundles.format("text.save.autosave", color + Bundles.get(slot.isAutosave() ? "text.on" : "text.off")));
button.row();
button.add();
button.add(Bundles.format("text.save.date", color+slot.getDate()));
button.row();
modifyButton(button, slot);
String color = "[lightgray]";
slots.add(button).uniformX().fillX().pad(4).padRight(-4).margin(10f).marginLeft(20f).marginRight(20f);
slots.row();
}
button.defaults().padBottom(3);
button.row();
button.add(Core.bundle.format("save.map", color + (slot.getMap() == null ? Core.bundle.get("unknown") : slot.getMap().name())));
button.row();
button.add(Core.bundle.format("save.wave", color + slot.getWave()));
button.row();
button.label(() -> Core.bundle.format("save.autosave", color + Core.bundle.get(slot.isAutosave() ? "on" : "off")));
button.row();
button.label(() -> Core.bundle.format("save.playtime", color + slot.getPlayTime()));
button.row();
button.add(Core.bundle.format("save.date", color + slot.getDate())).colspan(2).padTop(5).right();
button.row();
modifyButton(button, slot);
content().add(pane);
slots.add(button).uniformX().fillX().pad(4).padRight(-4).margin(10f).marginLeft(20f).marginRight(20f);
slots.row();
}
addSetup();
}
cont.add(pane);
public void addSetup(){
if(control.getSaves().getSaveSlots().size == 0) {
addSetup();
}
slots.row();
slots.addButton("$text.save.none", "clear", () -> {
}).disabled(true).fillX().margin(20f).minWidth(340f).height(80f).pad(4f);
}
public void addSetup(){
boolean valids = false;
for(SaveSlot slot : control.saves.getSaveSlots()) if(!slot.isHidden()) valids = true;
slots.row();
if(!valids){
if(gwt || ios) return;
slots.row();
slots.addButton("$save.none", () -> {
}).disabled(true).fillX().margin(20f).minWidth(340f).height(80f).pad(4f);
}
slots.addImageTextButton("$text.save.import", "icon-add", "clear", 14*3, () -> {
Platform.instance.showFileChooser(Bundles.get("text.save.import"), "Mindustry Save", file -> {
if(SaveIO.isSaveValid(file)){
try{
control.getSaves().importSave(file);
setup();
}catch (IOException e){
ui.showError(Bundles.format("text.save.import.fail", Strings.parseException(e, false)));
}
}else{
ui.showError("$text.save.import.invalid");
}
}, true, "mins");
}).fillX().margin(10f).minWidth(300f).height(70f).pad(4f).padRight(-4);
}
slots.row();
public void runLoadSave(SaveSlot slot){
ui.loadfrag.show();
if(ios) return;
Timers.runTask(3f, () -> {
ui.loadfrag.hide();
hide();
slots.addImageTextButton("$save.import", "icon-add", iconsize, () -> {
Platform.instance.showFileChooser(Core.bundle.get("save.import"), "Mindustry Save", file -> {
if(SaveIO.isSaveValid(file)){
try{
control.saves.importSave(file);
setup();
}catch(IOException e){
e.printStackTrace();
ui.showError(Core.bundle.format("save.import.fail", Strings.parseException(e, true)));
}
}else{
ui.showError("$save.import.invalid");
}
}, true, FileChooser.saveFiles);
}).fillX().margin(10f).minWidth(300f).height(70f).pad(4f).padRight(-4);
}
public void runLoadSave(SaveSlot slot){
hide();
ui.paused.hide();
ui.loadAnd(() -> {
try{
slot.load();
state.set(State.playing);
ui.paused.hide();
}catch(Exception e){
}catch(SaveException e){
Log.err(e);
ui.paused.hide();
state.set(State.menu);
logic.reset();
ui.showError("$text.save.corrupted");
ui.showError("$save.corrupted");
}
});
}
public void modifyButton(TextButton button, SaveSlot slot){
button.clicked(() -> {
if(!button.childrenPressed()){
runLoadSave(slot);
}
});
}
public void modifyButton(TextButton button, SaveSlot slot){
button.clicked(() -> {
if(!button.childrenPressed()){
int build = slot.getBuild();
runLoadSave(slot);
}
});
}
}

View File

@@ -0,0 +1,94 @@
package io.anuke.mindustry.ui.dialogs;
import io.anuke.arc.Core;
import io.anuke.arc.scene.ui.ScrollPane;
import io.anuke.arc.scene.ui.layout.Table;
import io.anuke.arc.util.Scaling;
import io.anuke.mindustry.game.*;
import io.anuke.mindustry.maps.Map;
import io.anuke.mindustry.ui.BorderImage;
import static io.anuke.mindustry.Vars.*;
public class MapPlayDialog extends FloatingDialog{
CustomRulesDialog dialog = new CustomRulesDialog();
Rules rules;
Gamemode selectedGamemode = Gamemode.survival;
public MapPlayDialog(){
super("");
setFillParent(false);
}
public void show(Map map){
title.setText(map.name());
cont.clearChildren();
//reset to a valid mode after switching to attack
if((selectedGamemode == Gamemode.attack && !map.hasEnemyCore()) || (selectedGamemode == Gamemode.pvp && !map.hasOtherCores())){
selectedGamemode = Gamemode.survival;
}
rules = map.rules();
rules = selectedGamemode.apply(map.rules());
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;
if((mode == Gamemode.attack && !map.hasEnemyCore()) || (mode == Gamemode.pvp && !map.hasOtherCores())){
continue;
}
modes.addButton(mode.toString(), "toggle", () -> {
selectedGamemode = mode;
rules = mode.apply(map.rules());
}).update(b -> b.setChecked(selectedGamemode == mode)).size(140f, 54f);
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-tools-small", iconsizesmall, () -> dialog.show(rules, () -> rules = (selectedGamemode == null ? map.rules() : selectedGamemode.apply(map.rules())))).width(230);
cont.row();
cont.add(new BorderImage(map.texture, 3f)).size(mobile && !Core.graphics.isPortrait() ? 150f : 250f).get().setScaling(Scaling.fit);
buttons.clearChildren();
addCloseButton();
buttons.addImageTextButton("$play", "icon-play", 8*3, () -> {
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();
}
}

View File

@@ -0,0 +1,169 @@
package io.anuke.mindustry.ui.dialogs;
import io.anuke.arc.Core;
import io.anuke.arc.graphics.Color;
import io.anuke.arc.scene.event.Touchable;
import io.anuke.arc.scene.ui.*;
import io.anuke.arc.scene.ui.layout.Table;
import io.anuke.arc.util.Scaling;
import io.anuke.mindustry.Vars;
import io.anuke.mindustry.core.Platform;
import io.anuke.mindustry.io.MapIO;
import io.anuke.mindustry.maps.Map;
import io.anuke.mindustry.ui.BorderImage;
import static io.anuke.mindustry.Vars.*;
public class MapsDialog extends FloatingDialog{
private FloatingDialog dialog;
public MapsDialog(){
super("$maps");
addCloseButton();
buttons.addImageTextButton("$editor.importmap", "icon-add", iconsize, () -> {
Platform.instance.showFileChooser("$editor.importmap", "Map File", file -> {
world.maps.tryCatchMapError(() -> {
if(MapIO.isImage(file)){
ui.showError("$editor.errorimage");
return;
}
Map map;
if(file.extension().equalsIgnoreCase(mapExtension)){
map = MapIO.createMap(file, true);
}else{
map = world.maps.makeLegacyMap(file);
}
String name = map.tags.get("name");
if(name == null){
ui.showError("$editor.errorname");
return;
}
Map conflict = world.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", () -> {
world.maps.tryCatchMapError(() -> {
world.maps.importMap(file);
setup();
});
});
}else{
world.maps.importMap(map.file);
setup();
}
});
}, true, FileChooser.anyMapFiles);
}).size(230f, 64f);
shown(this::setup);
onResize(() -> {
if(dialog != null){
dialog.hide();
}
});
}
void setup(){
cont.clear();
Table maps = new Table();
maps.marginRight(24);
ScrollPane pane = new ScrollPane(maps);
pane.setFadeScrollBars(false);
int maxwidth = Core.graphics.isPortrait() ? 2 : 4;
float mapsize = 200f;
int i = 0;
for(Map map : world.maps.all()){
if(i % maxwidth == 0){
maps.row();
}
TextButton button = maps.addButton("", "clear", () -> 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("white").growX().pad(4).color(Color.GRAY);
button.row();
button.stack(new Image(map.texture).setScaling(Scaling.fit), new BorderImage(map.texture).setScaling(Scaling.fit)).size(mapsize - 20f);
button.row();
button.add(map.custom ? "$custom" : "$builtin").color(Color.GRAY).padTop(3);
i++;
}
if(world.maps.all().size == 0){
maps.add("$maps.none");
}
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.texture).setScaling(Scaling.fit), new BorderImage(map.texture).setScaling(Scaling.fit)).size(mapsize);
table.table("flat", 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.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-load-map", iconsize, () -> {
try{
Vars.ui.editor.beginEditMap(map.file);
dialog.hide();
hide();
}catch(Exception e){
e.printStackTrace();
ui.showError("$error.mapnotfound");
}
}).fillX().height(54f).marginLeft(10);
table.addImageTextButton("$delete", "icon-trash-16", iconsize, () -> {
ui.showConfirm("$confirm", Core.bundle.format("map.delete", map.name()), () -> {
world.maps.removeMap(map);
dialog.hide();
setup();
});
}).fillX().height(54f).marginLeft(10).disabled(!map.custom).touchable(map.custom ? Touchable.enabled : Touchable.disabled);
dialog.show();
}
}

View File

@@ -0,0 +1,64 @@
package io.anuke.mindustry.ui.dialogs;
import io.anuke.arc.Core;
import io.anuke.arc.graphics.Color;
import io.anuke.arc.graphics.g2d.Draw;
import io.anuke.arc.input.KeyCode;
import io.anuke.arc.scene.event.InputEvent;
import io.anuke.arc.scene.event.InputListener;
import io.anuke.arc.scene.ui.layout.Unit;
import static io.anuke.mindustry.Vars.*;
public class MinimapDialog extends FloatingDialog{
public MinimapDialog(){
super("$minimap");
setFillParent(false);
shown(this::setup);
addCloseButton();
shouldPause = true;
}
void setup(){
cont.clear();
float size = Math.min(Core.graphics.getWidth(), Core.graphics.getHeight()) / Unit.dp.scl(1f) / 1.3f;
cont.table("pane", t -> {
t.addRect((x, y, width, height) -> {
if(renderer.minimap.getRegion() == null) return;
Draw.color(Color.WHITE);
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(size);
cont.addListener(new InputListener(){
@Override
public boolean scrolled(InputEvent event, float x, float y, float amountx, float amounty){
renderer.minimap.zoomBy(amounty);
return true;
}
@Override
public boolean touchDown(InputEvent event, float x, float y, int pointer, KeyCode button){
return true;
}
@Override
public void touchDragged(InputEvent event, float x, float y, int pointer){
if(mobile){
float max = Math.min(world.width(), world.height()) / 16f / 2f;
renderer.minimap.setZoom(1f + y / cont.getHeight() * (max - 1f));
}
}
});
Core.app.post(() -> Core.scene.setScrollFocus(cont));
}
}

View File

@@ -1,149 +1,122 @@
package io.anuke.mindustry.ui.dialogs;
import com.badlogic.gdx.utils.reflect.ClassReflection;
import io.anuke.arc.Core;
import io.anuke.arc.input.KeyCode;
import io.anuke.mindustry.core.GameState.State;
import io.anuke.mindustry.net.Net;
import io.anuke.mindustry.ui.PressGroup;
import io.anuke.ucore.core.Timers;
import io.anuke.ucore.scene.Element;
import io.anuke.ucore.scene.builders.build;
import io.anuke.ucore.scene.builders.imagebutton;
import io.anuke.ucore.scene.ui.ImageButton;
import io.anuke.ucore.util.Bundles;
import static io.anuke.mindustry.Vars.*;
public class PausedDialog extends FloatingDialog{
private SaveDialog save = new SaveDialog();
private LoadDialog load = new LoadDialog();
public boolean wasPaused = false;
private SaveDialog save = new SaveDialog();
private LoadDialog load = new LoadDialog();
private boolean wasClient = false;
public PausedDialog() {
super("$text.menu");
setup();
}
public PausedDialog(){
super("$menu");
shouldPause = true;
void setup(){
update(() -> {
if(state.is(State.menu) && isShown()){
hide();
}
});
shown(this::rebuild);
shown(() -> {
wasPaused = state.is(State.paused);
if(!Net.active()) state.set(State.paused);
});
if(!mobile){
content().defaults().width(220).height(50);
keyDown(key -> {
if(key == KeyCode.ESCAPE || key == KeyCode.BACK){
hide();
}
});
}
content().addButton("$text.back", () -> {
hide();
if((!wasPaused || Net.active()) && !state.is(State.menu))
state.set(State.playing);
});
void rebuild(){
cont.clear();
content().row();
content().addButton("$text.settings", ui.settings::show);
update(() -> {
if(state.is(State.menu) && isShown()){
hide();
}
});
content().row();
content().addButton("$text.savegame", () -> {
save.show();
}).disabled(b -> world.getMap().id == -1);
if(!mobile){
float dw = 210f;
cont.defaults().width(dw).height(50).pad(5f);
content().row();
content().addButton("$text.loadgame", () -> {
load.show();
}).disabled(b -> Net.active());
cont.addButton("$back", this::hide).colspan(2).width(dw * 2 + 20f);
content().row();
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(!gwt) {
content().addButton("$text.hostserver", () -> {
ui.host.show();
}).disabled(b -> Net.active());
}
if(!world.isZone() && !state.isEditor()){
cont.row();
cont.addButton("$savegame", save::show);
cont.addButton("$loadgame", load::show).disabled(b -> Net.active());
}
content().row();
cont.row();
content().addButton("$text.quit", () -> {
ui.showConfirm("$text.confirm", "$text.quit.confirm", () -> {
if(Net.client()) netClient.disconnectQuietly();
runExitSave();
hide();
});
});
cont.addButton("$hostserver", ui.host::show).disabled(b -> Net.active()).colspan(2).width(dw * 2 + 20f);
cont.row();
}else{
build.begin(content());
PressGroup group = new PressGroup();
content().defaults().size(120f).pad(5);
float isize = 14f*4;
new imagebutton("icon-play-2", isize, () -> {
hide();
if(!wasPaused && !state.is(State.menu))
state.set(State.playing);
}).text("$text.back").padTop(4f);
new imagebutton("icon-tools", isize, ui.settings::show).text("$text.settings").padTop(4f);
imagebutton sa = new imagebutton("icon-save", isize, save::show);
sa.text("$text.save").padTop(4f);
sa.cell.disabled(b -> world.getMap().id == -1);
cont.addButton("$quit", () -> {
ui.showConfirm("$confirm", "$quit.confirm", () -> {
wasClient = Net.client();
if(Net.client()) netClient.disconnectQuietly();
runExitSave();
hide();
});
}).colspan(2).width(dw + 10f);
content().row();
imagebutton lo = new imagebutton("icon-load", isize, load::show);
lo.text("$text.load").padTop(4f);
lo.cell.disabled(b -> Net.active());
}else{
cont.defaults().size(120f).pad(5);
float isize = 14f * 4;
imagebutton ho = new imagebutton("icon-host", isize, () -> {
ui.host.show();
});
ho.text("$text.host").padTop(4f);
ho.cell.disabled(b -> Net.active());
new imagebutton("icon-quit", isize, () -> {
ui.showConfirm("$text.confirm", "$text.quit.confirm", () -> {
if(Net.client()) netClient.disconnectQuietly();
runExitSave();
hide();
});
}).text("Quit").padTop(4f);
for(Element e : content().getChildren()){
if(e instanceof ImageButton){
group.add((ImageButton)e);
}
}
build.end();
}
}
cont.addRowImageTextButton("$back", "icon-play-2", isize, this::hide);
cont.addRowImageTextButton("$settings", "icon-tools", isize, ui.settings::show);
private void runExitSave(){
if(control.getSaves().getCurrent() == null ||
!control.getSaves().getCurrent().isAutosave()){
state.set(State.menu);
control.tutorial().reset();
return;
}
if(!world.isZone() && !state.isEditor()){
cont.addRowImageTextButton("$save", "icon-save", isize, save::show);
ui.loadfrag.show("$text.saveload");
cont.row();
Timers.runTask(5f, () -> {
ui.loadfrag.hide();
try{
control.getSaves().getCurrent().save();
}catch(Throwable e){
e = (e.getCause() == null ? e : e.getCause());
ui.showError("[orange]"+ Bundles.get("text.savefail")+"\n[white]" + ClassReflection.getSimpleName(e.getClass()) + ": " + e.getMessage() + "\n" + "at " + e.getStackTrace()[0].getFileName() + ":" + e.getStackTrace()[0].getLineNumber());
}
state.set(State.menu);
});
}
cont.addRowImageTextButton("$load", "icon-load", isize, load::show).disabled(b -> Net.active());
}else{
cont.row();
}
cont.addRowImageTextButton("$hostserver.mobile", "icon-host", isize, ui.host::show).disabled(b -> Net.active());
cont.addRowImageTextButton("$quit", "icon-quit", isize, () -> {
ui.showConfirm("$confirm", "$quit.confirm", () -> {
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.set(State.menu);
return;
}
ui.loadAnd("$saveload", () -> {
try{
control.saves.getCurrent().save();
}catch(Throwable e){
e.printStackTrace();
ui.showError("[accent]" + Core.bundle.get("savefail"));
}
state.set(State.menu);
});
}
}

View File

@@ -1,30 +0,0 @@
package io.anuke.mindustry.ui.dialogs;
import io.anuke.mindustry.core.GameState.State;
import io.anuke.ucore.scene.ui.Dialog;
import static io.anuke.mindustry.Vars.*;
public class RestartDialog extends Dialog {
public RestartDialog(){
super("$text.gameover", "dialog");
shown(() -> {
content().clearChildren();
if(control.isHighScore()){
content().add("$text.highscore").pad(6);
content().row();
}
content().add("$text.lasted").pad(12).get();
content().add("[accent]" + state.wave);
pack();
});
getButtonTable().addButton("$text.menu", ()-> {
hide();
state.set(State.menu);
logic.reset();
}).size(130f, 60f);
}
}

View File

@@ -1,40 +0,0 @@
package io.anuke.mindustry.ui.dialogs;
import io.anuke.mindustry.net.NetEvents;
import io.anuke.ucore.scene.ui.Label;
import io.anuke.ucore.scene.ui.TextField;
import io.anuke.ucore.scene.ui.layout.Table;
import io.anuke.ucore.util.Strings;
import static io.anuke.mindustry.Vars.*;
public class RollbackDialog extends FloatingDialog {
public RollbackDialog(){
super("$text.server.rollback");
setup();
shown(this::setup);
}
private void setup(){
content().clear();
buttons().clear();
if(gwt) return;
content().row();
content().add("$text.server.rollback.numberfield");
TextField field = content().addField("", t->{}).size(200f, 48f).get();
field.setTextFieldFilter((f, c) -> field.getText().length() < 4);
content().row();
buttons().defaults().size(200f, 50f).left().pad(2f);
buttons().addButton("$text.cancel", this::hide);
buttons().addButton("$text.ok", () -> {
NetEvents.handleRollbackRequest(Integer.valueOf(field.getText()));
hide();
}).disabled(b -> field.getText().isEmpty() || !Strings.canParsePostiveInt(field.getText()));
}
}

View File

@@ -1,204 +1,261 @@
package io.anuke.mindustry.ui.dialogs;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input.Keys;
import com.badlogic.gdx.graphics.Colors;
import com.badlogic.gdx.utils.Align;
import io.anuke.arc.Core;
import io.anuke.arc.collection.ObjectMap;
import io.anuke.arc.files.FileHandle;
import io.anuke.arc.function.Consumer;
import io.anuke.arc.input.KeyCode;
import io.anuke.arc.scene.Element;
import io.anuke.arc.scene.event.InputEvent;
import io.anuke.arc.scene.event.InputListener;
import io.anuke.arc.scene.ui.*;
import io.anuke.arc.scene.ui.SettingsDialog.SettingsTable.Setting;
import io.anuke.arc.scene.ui.layout.Table;
import io.anuke.arc.util.Align;
import io.anuke.mindustry.Vars;
import io.anuke.mindustry.core.GameState.State;
import io.anuke.mindustry.core.Platform;
import io.anuke.mindustry.graphics.Pal;
import io.anuke.mindustry.net.Net;
import io.anuke.ucore.core.Core;
import io.anuke.ucore.core.Graphics;
import io.anuke.ucore.core.Settings;
import io.anuke.ucore.function.Consumer;
import io.anuke.ucore.scene.Element;
import io.anuke.ucore.scene.event.InputEvent;
import io.anuke.ucore.scene.event.InputListener;
import io.anuke.ucore.scene.ui.Image;
import io.anuke.ucore.scene.ui.ScrollPane;
import io.anuke.ucore.scene.ui.SettingsDialog;
import io.anuke.ucore.scene.ui.Slider;
import io.anuke.ucore.scene.ui.layout.Table;
import io.anuke.ucore.util.Bundles;
import io.anuke.ucore.util.Mathf;
import static io.anuke.mindustry.Vars.*;
public class SettingsMenuDialog extends SettingsDialog{
public SettingsTable graphics;
public SettingsTable game;
public SettingsTable sound;
public SettingsTable graphics;
public SettingsTable game;
public SettingsTable sound;
private Table prefs;
private Table menu;
private boolean wasPaused;
public SettingsMenuDialog(){
setStyle(Core.skin.get("dialog", WindowStyle.class));
private Table prefs;
private Table menu;
private boolean wasPaused;
hidden(() -> {
if(!state.is(State.menu)){
if(!wasPaused || Net.active())
state.set(State.playing);
}
});
public SettingsMenuDialog(){
setStyle(Core.scene.skin.get("dialog", WindowStyle.class));
shown(() -> {
if(!state.is(State.menu)){
wasPaused = state.is(State.paused);
if(ui.paused.getScene() != null){
wasPaused = ui.paused.wasPaused;
}
if(!Net.active()) state.set(State.paused);
ui.paused.hide();
}
});
hidden(() -> {
if(!state.is(State.menu)){
if(!wasPaused || Net.active())
state.set(State.playing);
}
});
setFillParent(true);
title().setAlignment(Align.center);
getTitleTable().row();
getTitleTable().add(new Image("white"))
.growX().height(3f).pad(4f).get().setColor(Colors.get("accent"));
shown(() -> {
if(!state.is(State.menu)){
wasPaused = state.is(State.paused);
state.set(State.paused);
}
});
content().clearChildren();
content().remove();
buttons().remove();
setFillParent(true);
title.setAlignment(Align.center);
titleTable.row();
titleTable.add(new Image("white")).growX().height(3f).pad(4f).get().setColor(Pal.accent);
menu = new Table();
cont.clearChildren();
cont.remove();
buttons.remove();
Consumer<SettingsTable> s = table -> {
table.row();
table.addImageTextButton("$text.back", "icon-arrow-left", 10*3, this::back).size(240f, 60f).colspan(2).padTop(15f);
};
menu = new Table();
game = new SettingsTable(s);
graphics = new SettingsTable(s);
sound = new SettingsTable(s);
Consumer<SettingsTable> s = table -> {
table.row();
table.addImageTextButton("$back", "icon-arrow-left", iconsize, this::back).size(240f, 60f).colspan(2).padTop(15f);
};
prefs = new Table();
prefs.top();
prefs.margin(14f);
game = new SettingsTable(s);
graphics = new SettingsTable(s);
sound = new SettingsTable(s);
menu.defaults().size(300f, 60f).pad(3f);
menu.addButton("$text.settings.game", () -> visible(0));
menu.row();
menu.addButton("$text.settings.graphics", () -> visible(1));
menu.row();
menu.addButton("$text.settings.sound", () -> visible(2));
if(!Vars.mobile) {
menu.row();
menu.addButton("$text.settings.controls", ui.controls::show);
}
menu.row();
menu.addButton("$text.settings.language", ui.language::show);
prefs = new Table();
prefs.top();
prefs.margin(14f);
prefs.clearChildren();
prefs.add(menu);
menu.defaults().size(300f, 60f).pad(3f);
menu.addButton("$settings.game", () -> visible(0));
menu.row();
menu.addButton("$settings.graphics", () -> visible(1));
menu.row();
menu.addButton("$settings.sound", () -> visible(2));
if(!Vars.mobile){
menu.row();
menu.addButton("$settings.controls", ui.controls::show);
}
menu.row();
menu.addButton("$settings.language", ui.language::show);
ScrollPane pane = new ScrollPane(prefs, "clear");
pane.addCaptureListener(new InputListener() {
@Override
public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) {
Element actor = pane.hit(x, y, true);
if (actor instanceof Slider) {
pane.setFlickScroll(false);
return true;
}
prefs.clearChildren();
prefs.add(menu);
return super.touchDown(event, x, y, pointer, button);
}
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;
}
@Override
public void touchUp(InputEvent event, float x, float y, int pointer, int button) {
pane.setFlickScroll(true);
super.touchUp(event, x, y, pointer, button);
}
});
pane.setFadeScrollBars(false);
return super.touchDown(event, x, y, pointer, button);
}
row();
add(pane).grow().top();
row();
add(buttons()).fillX();
@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);
hidden(this::back);
row();
add(pane).grow().top();
row();
add(buttons).fillX();
addSettings();
}
hidden(this::back);
void addSettings(){
sound.volumePrefs();
addSettings();
}
game.screenshakePref();
game.checkPref("smoothcam", true);
game.checkPref("effects", true);
game.sliderPref("sensitivity", 100, 10, 300, i -> i + "%");
game.sliderPref("saveinterval", 90, 10, 5*120, i -> Bundles.format("setting.seconds", i));
void addSettings(){
//TODO add when sound works again
//sound.volumePrefs();
sound.add("[LIGHT_GRAY]there is no sound implemented in v4 yet");
if(!gwt){
graphics.checkPref("multithread", false, threads::setEnabled);
game.screenshakePref();
if(mobile){
game.checkPref("autotarget", true);
}
game.sliderPref("saveinterval", 60, 10, 5 * 120, i -> Core.bundle.format("setting.seconds", i));
if(Settings.getBool("multithread")){
threads.setEnabled(true);
}
}
if(!mobile){
game.checkPref("crashreport", true);
}
if(!mobile && !gwt) {
graphics.checkPref("vsync", true, b -> Gdx.graphics.setVSync(b));
graphics.checkPref("fullscreen", false, b -> {
if (b) {
Gdx.graphics.setFullscreenMode(Gdx.graphics.getDisplayMode());
} else {
Gdx.graphics.setWindowedMode(600, 480);
}
});
game.pref(new Setting(){
@Override
public void add(SettingsTable table){
table.addButton("$settings.cleardata", () -> {
FloatingDialog dialog = new FloatingDialog("$settings.cleardata");
dialog.setFillParent(false);
dialog.cont.defaults().size(230f, 60f).pad(3);
dialog.addCloseButton();
dialog.cont.addButton("$settings.clearunlocks", () -> {
ui.showConfirm("$confirm", "$settings.clear.confirm", () -> {
data.reset();
dialog.hide();
});
});
dialog.cont.row();
dialog.cont.addButton("$settings.clearall", () -> {
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();
Gdx.graphics.setVSync(Settings.getBool("vsync"));
if(Settings.getBool("fullscreen")){
Gdx.graphics.setFullscreenMode(Gdx.graphics.getDisplayMode());
}
}
for(FileHandle file : dataDirectory.list()){
file.deleteDirectory();
}
graphics.checkPref("fps", false);
graphics.checkPref("lasers", true);
graphics.sliderPref("previewopacity", 50, 0, 100, i -> i + "%");
graphics.checkPref("indicators", true);
graphics.checkPref("healthbars", true);
graphics.checkPref("pixelate", true, b -> {
if(b){
renderer.pixelSurface.setScale(Core.cameraScale);
renderer.shadowSurface.setScale(Core.cameraScale);
renderer.shieldSurface.setScale(Core.cameraScale);
Graphics.getEffects1().setScale(Core.cameraScale);
Graphics.getEffects2().setScale(Core.cameraScale);
}else{
renderer.shadowSurface.setScale(1);
renderer.shieldSurface.setScale(1);
Graphics.getEffects1().setScale(1);
Graphics.getEffects2().setScale(1);
}
renderer.setPixelate(b);
});
}
Core.app.exit();
});
});
dialog.cont.row();
dialog.show();
}).size(220f, 60f).pad(6).left();
table.add();
table.row();
}
});
private void back(){
prefs.clearChildren();
prefs.add(menu);
}
graphics.sliderPref("fpscap", 125, 5, 125, 5, s -> (s > 120 ? Core.bundle.get("setting.fpscap.none") : Core.bundle.format("setting.fpscap.text", s)));
graphics.sliderPref("chatopacity", 100, 0, 100, 5, s -> s + "%");
private void visible(int index){
prefs.clearChildren();
Table table = Mathf.select(index, game, graphics, sound);
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(600, 480);
}
});
graphics.checkPref("borderlesswindow", false, b -> Core.graphics.setUndecorated(b));
Core.graphics.setVSync(Core.settings.getBool("vsync"));
if(Core.settings.getBool("fullscreen")){
Core.graphics.setFullscreenMode(Core.graphics.getDisplayMode());
}
if(Core.settings.getBool("borderlesswindow")){
Core.graphics.setUndecorated(true);
}
}else{
graphics.checkPref("landscape", false, b -> {
if(b){
Platform.instance.beginForceLandscape();
}else{
Platform.instance.endForceLandscape();
}
});
if(Core.settings.getBool("landscape")){
Platform.instance.beginForceLandscape();
}
}
graphics.checkPref("effects", true);
graphics.checkPref("playerchat", true);
graphics.checkPref("minimap", !mobile);
graphics.checkPref("fps", false);
graphics.checkPref("indicators", true);
graphics.checkPref("animatedwater", false);
graphics.checkPref("animatedshields", !mobile);
graphics.checkPref("lasers", true);
graphics.checkPref("pixelate", false);
//TODO is this necessary?
/*
graphics.checkPref("linear", false, 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);
}
}*/
}
private void back(){
prefs.clearChildren();
prefs.add(menu);
}
private void visible(int index){
prefs.clearChildren();
Table table = new Table[]{game, graphics, sound}[index];
prefs.add(table);
}
@Override
public void addCloseButton(){
buttons().addImageTextButton("$text.menu", "icon-arrow-left", 30f, this::hide).size(230f, 64f);
keyDown(key->{
if(key == Keys.ESCAPE || key == Keys.BACK)
hide();
});
}
}
@Override
public void addCloseButton(){
buttons.addImageTextButton("$menu", "icon-arrow-left", 30f, this::hide).size(230f, 64f);
keyDown(key -> {
if(key == KeyCode.ESCAPE || key == KeyCode.BACK)
hide();
});
}
}

View File

@@ -0,0 +1,317 @@
package io.anuke.mindustry.ui.dialogs;
import io.anuke.arc.Core;
import io.anuke.arc.collection.Array;
import io.anuke.arc.collection.ObjectSet;
import io.anuke.arc.graphics.Color;
import io.anuke.arc.graphics.g2d.Draw;
import io.anuke.arc.graphics.g2d.Lines;
import io.anuke.arc.math.Interpolation;
import io.anuke.arc.scene.Element;
import io.anuke.arc.scene.Group;
import io.anuke.arc.scene.actions.Actions;
import io.anuke.arc.scene.actions.RelativeTemporalAction;
import io.anuke.arc.scene.event.Touchable;
import io.anuke.arc.scene.style.TextureRegionDrawable;
import io.anuke.arc.scene.ui.ImageButton;
import io.anuke.arc.scene.ui.layout.Table;
import io.anuke.arc.scene.ui.layout.Unit;
import io.anuke.arc.util.Align;
import io.anuke.mindustry.content.TechTree;
import io.anuke.mindustry.content.TechTree.TechNode;
import io.anuke.mindustry.graphics.Pal;
import io.anuke.mindustry.type.ItemStack;
import io.anuke.mindustry.ui.ItemsDisplay;
import io.anuke.mindustry.ui.TreeLayout;
import io.anuke.mindustry.ui.TreeLayout.TreeNode;
import io.anuke.mindustry.world.Block.Icon;
import static io.anuke.mindustry.Vars.*;
public class TechTreeDialog extends FloatingDialog{
private final float nodeSize = Unit.dp.scl(60f);
private ObjectSet<TechTreeNode> nodes = new ObjectSet<>();
private TechTreeNode root = new TechTreeNode(TechTree.root, null);
private ItemsDisplay items;
public TechTreeDialog(){
super("");
titleTable.remove();
margin(0f).marginBottom(8);
cont.stack(new View(), items = new ItemsDisplay()).grow();
shown(() -> {
checkNodes(root);
treeLayout();
});
hidden(ui.deploy::setup);
addCloseButton();
buttons.addImageTextButton("$database", "icon-database", iconsize, () -> {
hide();
ui.database.show();
}).size(210f, 64f);
}
@Override
protected void drawBackground(float x, float y){
drawDefaultBackground(x, y);
}
void treeLayout(){
TreeLayout layout = new TreeLayout();
layout.gapBetweenLevels = Unit.dp.scl(60f);
layout.gapBetweenNodes = Unit.dp.scl(40f);
LayoutNode node = new LayoutNode(root, null);
layout.layout(node);
copyInfo(node);
}
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).select(n -> n.visible).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;
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(Icon.medium), "node");
button.visible(() -> node.visible);
button.clicked(() -> {
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.tapped(() -> moved = false);
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 = Core.scene.skin.getDrawable(!locked(node.node) ? "content-background" : !data.hasItems(node.node.requirements) ? "content-background-noitems" : "content-background-locked");
((TextureRegionDrawable)button.getStyle().imageUp)
.setRegion(node.visible ? node.node.block.icon(Icon.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();
}
});
}
dragged((x, y) -> {
moved = true;
panX += x;
panY += y;
});
}
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();
}
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.table("content-background", b -> {
b.margin(0).left().defaults().left();
b.addImageButton("icon-info", "node", iconsize, () -> 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.getContentIcon()).size(8 * 3).padRight(3);
list.add(req.item.localizedName()).color(Color.LIGHT_GRAY);
list.label(() -> " " + Math.min(data.getItem(req.item), req.amount) + " / " + req.amount)
.update(l -> l.setColor(data.has(req.item, req.amount) ? Color.LIGHT_GRAY : Color.SCARLET));
}).fillX().left();
t.row();
}
});
}else{
desc.add("$completed");
}
}).pad(9);
if(mobile && locked(node)){
b.row();
b.addImageTextButton("$research", "icon-check", "node", iconsize, () -> unlock(node))
.disabled(i -> !data.hasItems(node.requirements)).growX().height(44f).colspan(3);
}
});
infoTable.row();
if(node.block.description != null){
infoTable.table("dialogDim", t -> t.margin(3f).left().labelWrap(node.block.description).color(Color.LIGHT_GRAY).growX()).fillX();
}
addChild(infoTable);
infoTable.pack();
infoTable.act(Core.graphics.getDeltaTime());
}
@Override
public void draw(){
float offsetX = panX + width / 2f + x, offsetY = panY + height / 2f + y;
for(TechTreeNode node : nodes){
if(!node.visible) continue;
for(TechTreeNode child : node.children){
if(!child.visible) continue;
Lines.stroke(Unit.dp.scl(3f), locked(node.node) || locked(child.node) ? Pal.locked : Pal.accent);
Lines.line(node.x + offsetX, node.y + offsetY, child.x + offsetX, child.y + offsetY);
}
}
Draw.reset();
super.draw();
}
}
}

View File

@@ -1,56 +1,42 @@
package io.anuke.mindustry.ui.dialogs;
import io.anuke.mindustry.entities.Player;
import io.anuke.mindustry.net.TraceInfo;
import io.anuke.ucore.scene.ui.layout.Table;
import io.anuke.ucore.util.Bundles;
import io.anuke.arc.Core;
import io.anuke.arc.scene.ui.layout.Table;
import io.anuke.mindustry.entities.type.Player;
import io.anuke.mindustry.net.Administration.TraceInfo;
public class TraceDialog extends FloatingDialog {
public class TraceDialog extends FloatingDialog{
public TraceDialog(){
super("$text.trace");
super("$trace");
addCloseButton();
setFillParent(false);
}
public void show(Player player, TraceInfo info){
content().clear();
cont.clear();
Table table = new Table("button");
Table table = new Table("clear");
table.margin(14);
table.defaults().pad(1);
table.defaults().left();
table.add(Bundles.format("text.trace.playername", player.name));
table.add(Core.bundle.format("trace.playername", player.name));
table.row();
table.add(Bundles.format("text.trace.ip", info.ip));
table.add(Core.bundle.format("trace.ip", info.ip));
table.row();
table.add(Bundles.format("text.trace.id", info.uuid));
table.add(Core.bundle.format("trace.id", info.uuid));
table.row();
table.add(Bundles.format("text.trace.modclient", info.modclient));
table.add(Core.bundle.format("trace.modclient", info.modded));
table.row();
table.add(Bundles.format("text.trace.android", info.android));
table.add(Core.bundle.format("trace.mobile", info.mobile));
table.row();
table.add().pad(5);
table.row();
table.add(Bundles.format("text.trace.totalblocksbroken", info.totalBlocksBroken));
table.row();
table.add(Bundles.format("text.trace.structureblocksbroken", info.structureBlocksBroken));
table.row();
table.add(Bundles.format("text.trace.lastblockbroken", info.lastBlockBroken.formalName));
table.row();
table.add().pad(5);
table.row();
table.add(Bundles.format("text.trace.totalblocksplaced", info.totalBlocksPlaced));
table.row();
table.add(Bundles.format("text.trace.lastblockplaced", info.lastBlockPlaced.formalName));
table.row();
content().add(table);
cont.add(table);
show();
}

View File

@@ -0,0 +1,163 @@
package io.anuke.mindustry.ui.dialogs;
import io.anuke.arc.Core;
import io.anuke.arc.graphics.Color;
import io.anuke.arc.scene.ui.Button;
import io.anuke.arc.scene.ui.layout.Table;
import io.anuke.mindustry.graphics.Pal;
import io.anuke.mindustry.type.*;
import io.anuke.mindustry.type.Zone.ZoneRequirement;
import io.anuke.mindustry.world.Block;
import io.anuke.mindustry.world.Block.Icon;
import static io.anuke.mindustry.Vars.*;
public class ZoneInfoDialog extends FloatingDialog{
private ZoneLoadoutDialog loadout = new ZoneLoadoutDialog();
public ZoneInfoDialog(){
super("");
titleTable.remove();
addCloseButton();
}
@Override
protected void drawBackground(float x, float y){
drawDefaultBackground(x, y);
}
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;
ItemStack[] stacks = zone.getLaunchCost();
for(ItemStack stack : stacks){
if(stack.amount == 0) continue;
if(i++ % 2 == 0){
iteminfo.row();
}
iteminfo.addImage(stack.item.icon(Item.Icon.medium)).size(8 * 3).padRight(1);
iteminfo.add(stack.amount + "").color(Color.LIGHT_GRAY).padRight(5);
}
};
rebuildItems.run();
cont.table(cont -> {
if(zone.locked()){
cont.addImage("icon-locked");
cont.row();
cont.add("$locked").padBottom(6);
cont.row();
cont.table(req -> {
req.defaults().left();
if(zone.zoneRequirements.length > 0){
req.table(r -> {
r.add("$complete").colspan(2).left();
r.row();
for(ZoneRequirement other : zone.zoneRequirements){
r.addImage("icon-terrain").padRight(4);
r.add(Core.bundle.format("zone.requirement", other.wave, other.zone.localizedName())).color(Color.LIGHT_GRAY);
r.addImage(other.zone.bestWave() >= other.wave ? "icon-check" : "icon-cancel")
.color(other.zone.bestWave() >= other.wave ? Color.LIGHT_GRAY : Color.SCARLET).padLeft(3);
r.row();
}
});
}
req.row();
if(zone.blockRequirements.length > 0){
req.table(r -> {
r.add("$research.list").colspan(2).left();
r.row();
for(Block block : zone.blockRequirements){
r.addImage(block.icon(Icon.small)).size(8 * 3).padRight(4);
r.add(block.localizedName).color(Color.LIGHT_GRAY);
r.addImage(data.isUnlocked(block) ? "icon-check" : "icon-cancel")
.color(data.isUnlocked(block) ? Color.LIGHT_GRAY : Color.SCARLET).padLeft(3);
r.row();
}
}).padTop(10);
}
}).growX();
}else{
cont.add(zone.localizedName()).color(Pal.accent).growX().center();
cont.row();
cont.addImage("white").color(Pal.accent).height(3).pad(6).growX();
cont.row();
cont.addButton(zone.canConfigure() ? "$configure" : Core.bundle.format("configure.locked", zone.configureWave), () -> loadout.show(zone, rebuildItems)).fillX().pad(3).disabled(b -> !zone.canConfigure());
cont.row();
cont.table(res -> {
res.add("$zone.resources").padRight(6);
if(zone.resources.length > 0){
for(Item item : zone.resources){
res.addImage(item.icon(Item.Icon.medium)).size(8 * 3);
}
}else{
res.add("$none");
}
});
if(zone.bestWave() > 0){
cont.row();
cont.add(Core.bundle.format("bestwave", zone.bestWave()));
}
}
});
cont.row();
Button button = cont.addButton(zone.locked() ? "$uncover" : "$launch", () -> {
if(!data.isUnlocked(zone)){
data.unlockContent(zone);
ui.deploy.setup();
setup(zone);
}else{
ui.deploy.hide();
data.removeItems(zone.getLaunchCost());
hide();
control.playZone(zone);
}
}).minWidth(150f).margin(13f).padTop(5).disabled(b -> zone.locked() ? !canUnlock(zone) : !data.hasItems(zone.getLaunchCost())).uniformY().get();
button.row();
button.add(iteminfo);
}
private boolean canUnlock(Zone zone){
if(data.isUnlocked(zone)){
return true;
}
for(ZoneRequirement other : zone.zoneRequirements){
if(other.zone.bestWave() < other.wave){
return false;
}
}
for(Block other : zone.blockRequirements){
if(!data.isUnlocked(other)){
return false;
}
}
return true;
}
}

View File

@@ -0,0 +1,84 @@
package io.anuke.mindustry.ui.dialogs;
import io.anuke.arc.scene.ui.TextButton;
import io.anuke.mindustry.type.*;
import static io.anuke.mindustry.Vars.content;
import static io.anuke.mindustry.Vars.data;
public class ZoneLoadoutDialog extends FloatingDialog{
private Zone zone;
private Runnable hider;
public ZoneLoadoutDialog(){
super("$configure");
setFillParent(false);
addCloseButton();
shown(this::setup);
hidden(() -> {
if(hider != null){
hider.run();
}
});
buttons.row();
buttons.addButton("$settings.reset", () -> {
zone.resetStartingItems();
zone.updateLaunchCost();
setup();
}).size(210f, 64f);
}
public void show(Zone zone, Runnable hider){
this.hider = hider;
this.zone = zone;
show();
}
void setup(){
cont.clear();
float bsize = 40f;
int step = 50;
for(ItemStack stack : zone.getStartingItems()){
cont.addButton("x", "clear-partial", () -> {
zone.getStartingItems().remove(stack);
zone.updateLaunchCost();
setup();
}).size(bsize);
cont.addButton("-", "clear-partial", () -> {
stack.amount = Math.max(stack.amount - step, 0);
zone.updateLaunchCost();
}).size(bsize);
cont.addButton("+", "clear-partial", () -> {
stack.amount = Math.min(stack.amount + step, zone.loadout.core().itemCapacity);
zone.updateLaunchCost();
}).size(bsize);
cont.addImage(stack.item.icon(Item.Icon.medium)).size(8 * 3).padRight(4);
cont.label(() -> stack.amount + "").left();
cont.row();
}
cont.addButton("$add", () -> {
FloatingDialog dialog = new FloatingDialog("");
dialog.setFillParent(false);
for(Item item : content.items().select(item -> data.getItem(item) > 0 && item.type == ItemType.material && zone.getStartingItems().find(stack -> stack.item == item) == null)){
TextButton button = dialog.cont.addButton("", "clear", () -> {
zone.getStartingItems().add(new ItemStack(item, 0));
zone.updateLaunchCost();
setup();
dialog.hide();
}).size(300f, 36f).get();
button.clearChildren();
button.left();
button.addImage(item.icon(Item.Icon.medium)).size(8 * 3).pad(4);
button.add(item.localizedName);
dialog.cont.row();
}
dialog.show();
}).colspan(4).size(100f, bsize).left().disabled(b -> !content.items().contains(item -> data.getItem(item) > 0 && item.type == ItemType.material && !zone.getStartingItems().contains(stack -> stack.item == item)));
pack();
}
}

View File

@@ -1,37 +1,33 @@
package io.anuke.mindustry.ui.fragments;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import io.anuke.arc.Core;
import io.anuke.arc.graphics.g2d.*;
import io.anuke.arc.scene.Group;
import io.anuke.arc.scene.ui.layout.Unit;
import io.anuke.mindustry.core.GameState.State;
import io.anuke.ucore.core.Core;
import io.anuke.ucore.graphics.Draw;
import io.anuke.ucore.scene.ui.layout.Unit;
import io.anuke.mindustry.graphics.Shaders;
import static io.anuke.mindustry.Vars.state;
public class BackgroundFragment implements Fragment {
public class BackgroundFragment extends Fragment{
@Override
public void build() {
public void build(Group parent){
Core.scene.table().addRect((a, b, w, h) -> {
Draw.color();
Draw.colorl(0.1f);
Fill.crect(0, 0, w, h);
Draw.shader(Shaders.menu);
Fill.crect(0, 0, w, h);
Draw.shader();
TextureRegion back = Draw.region("background");
float backscl = (int)Math.max(Gdx.graphics.getWidth() / (float)back.getRegionWidth() * 1.5f, Unit.dp.scl(5f));
Draw.alpha(0.5f);
Core.batch.draw(back, w/2 - back.getRegionWidth()*backscl/2, h/2 - back.getRegionHeight()*backscl/2,
back.getRegionWidth()*backscl, back.getRegionHeight()*backscl);
boolean portrait = Gdx.graphics.getWidth() < Gdx.graphics.getHeight();
float logoscl = (int)Unit.dp.scl(7) * (portrait ? 5f/7f : 1f);
TextureRegion logo = Core.skin.getRegion("logotext");
float logow = logo.getRegionWidth()*logoscl;
float logoh = logo.getRegionHeight()*logoscl;
boolean portrait = Core.graphics.getWidth() < Core.graphics.getHeight();
float logoscl = (int)Unit.dp.scl(1);
TextureRegion logo = Core.atlas.find("logotext");
float logow = logo.getWidth() * logoscl;
float logoh = logo.getHeight() * logoscl;
Draw.color();
Core.batch.draw(logo, (int)(w/2 - logow/2), (int)(h - logoh + 15 - Unit.dp.scl(portrait ? 30f : 0)), logow, logoh);
Draw.rect(logo, (int)(w / 2), (int)(h - 10 - logoh - Unit.dp.scl(portrait ? 30f : 0)) + logoh / 2, logow, logoh);
}).visible(() -> state.is(State.menu)).grow();
}
}

View File

@@ -1,25 +1,29 @@
package io.anuke.mindustry.ui.fragments;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.math.Interpolation;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.utils.Align;
import io.anuke.arc.Core;
import io.anuke.arc.math.Interpolation;
import io.anuke.arc.math.geom.Vector2;
import io.anuke.arc.scene.Element;
import io.anuke.arc.scene.Group;
import io.anuke.arc.scene.actions.Actions;
import io.anuke.arc.scene.ui.layout.Table;
import io.anuke.arc.util.Align;
import io.anuke.mindustry.content.Blocks;
import io.anuke.mindustry.core.GameState.State;
import io.anuke.mindustry.world.Block;
import io.anuke.mindustry.world.Tile;
import io.anuke.mindustry.world.blocks.Blocks;
import io.anuke.ucore.core.Core;
import io.anuke.ucore.core.Graphics;
import io.anuke.ucore.scene.Element;
import io.anuke.ucore.scene.actions.Actions;
import io.anuke.ucore.scene.ui.layout.Table;
public class BlockConfigFragment implements Fragment {
private Table table;
import static io.anuke.mindustry.Vars.*;
public class BlockConfigFragment extends Fragment{
private Table table = new Table();
private Tile configTile;
private Block configBlock;
@Override
public void build() {
table = new Table();
Core.scene.add(table);
public void build(Group parent){
table.visible(false);
parent.addChild(table);
}
public boolean isShown(){
@@ -32,30 +36,43 @@ public class BlockConfigFragment implements Fragment {
public void showConfig(Tile tile){
configTile = tile;
configBlock = tile.block();
table.visible(true);
table.clear();
tile.block().buildTable(tile, table);
table.pack();
table.setTransform(true);
table.actions(Actions.scaleTo(0f, 1f), Actions.visible(true),
Actions.scaleTo(1f, 1f, 0.07f, Interpolation.pow3Out));
Actions.scaleTo(1f, 1f, 0.07f, Interpolation.pow3Out));
table.update(() -> {
if(state.is(State.menu)){
hideConfig();
return;
}
if(configTile != null && configTile.block().shouldHideConfigure(configTile, player)){
hideConfig();
return;
}
table.update(()->{
table.setOrigin(Align.center);
Vector2 pos = Graphics.screen(tile.drawx(), tile.drawy());
table.setPosition(pos.x, pos.y, Align.center);
if(configTile == null || configTile.block() == Blocks.air){
Vector2 pos = Core.input.mouseScreen(tile.drawx(), tile.drawy() - tile.block().size * tilesize / 2f - 1);
table.setPosition(pos.x, pos.y, Align.top);
if(configTile == null || configTile.block() == Blocks.air || configTile.block() != configBlock){
hideConfig();
}
});
}
public boolean hasConfigMouse(){
Element e = Core.scene.hit(Gdx.input.getX(), Gdx.graphics.getHeight() - Gdx.input.getY(), true);
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));
}
}

View File

@@ -0,0 +1,207 @@
package io.anuke.mindustry.ui.fragments;
import io.anuke.annotations.Annotations.Loc;
import io.anuke.annotations.Annotations.Remote;
import io.anuke.arc.Core;
import io.anuke.arc.collection.IntSet;
import io.anuke.arc.function.BooleanProvider;
import io.anuke.arc.function.Supplier;
import io.anuke.arc.graphics.g2d.TextureRegion;
import io.anuke.arc.input.KeyCode;
import io.anuke.arc.math.Interpolation;
import io.anuke.arc.math.Mathf;
import io.anuke.arc.math.geom.Vector2;
import io.anuke.arc.scene.Element;
import io.anuke.arc.scene.Group;
import io.anuke.arc.scene.actions.Actions;
import io.anuke.arc.scene.event.*;
import io.anuke.arc.scene.ui.Image;
import io.anuke.arc.scene.ui.layout.Stack;
import io.anuke.arc.scene.ui.layout.Table;
import io.anuke.arc.util.*;
import io.anuke.mindustry.core.GameState.State;
import io.anuke.mindustry.entities.type.Player;
import io.anuke.mindustry.gen.Call;
import io.anuke.mindustry.type.Item;
import io.anuke.mindustry.type.Item.Icon;
import io.anuke.mindustry.world.Tile;
import static io.anuke.mindustry.Vars.*;
public class BlockInventoryFragment extends Fragment{
private final static float holdWithdraw = 20f;
private Table 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) return;
int removed = tile.block().removeStack(tile, item, amount);
player.addItem(item, removed);
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 = new Table();
table.visible(() -> !state.is(State.menu));
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(){
table.actions(Actions.scaleTo(0f, 1f, 0.06f, Interpolation.pow3Out), Actions.visible(false), Actions.run(() -> {
table.clear();
table.update(null);
}));
table.touchable(Touchable.disabled);
tile = null;
}
private void rebuild(boolean actions){
IntSet container = new IntSet();
table.clearChildren();
table.background("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;
}
}
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);
BooleanProvider canPick = () -> player.acceptsItem(item) && !state.isPaused();
HandCursorListener l = new HandCursorListener();
l.setEnabled(canPick);
Element image = itemImage(item.icon(Icon.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.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;
}
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();
if(actions){
table.actions(Actions.scaleTo(0f, 1f), Actions.visible(true),
Actions.scaleTo(1f, 1f, 0.07f, Interpolation.pow3Out));
}
}
private String round(float f){
f = (int)f;
if(f >= 1000000){
return (int)(f / 1000000f) + "[gray]mil[]";
}else if(f >= 1000){
return (int)(f / 1000) + "k";
}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, Supplier<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;
}
}

View File

@@ -1,420 +0,0 @@
package io.anuke.mindustry.ui.fragments;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.Colors;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.math.Interpolation;
import com.badlogic.gdx.utils.Array;
import io.anuke.mindustry.core.GameState.State;
import io.anuke.mindustry.input.InputHandler;
import io.anuke.mindustry.net.EditLog;
import io.anuke.mindustry.resource.*;
import io.anuke.mindustry.ui.dialogs.FloatingDialog;
import io.anuke.mindustry.world.Block;
import io.anuke.ucore.graphics.Draw;
import io.anuke.ucore.graphics.Hue;
import io.anuke.ucore.scene.Element;
import io.anuke.ucore.scene.actions.Actions;
import io.anuke.ucore.scene.builders.table;
import io.anuke.ucore.scene.event.ClickListener;
import io.anuke.ucore.scene.event.InputEvent;
import io.anuke.ucore.scene.event.Touchable;
import io.anuke.ucore.scene.ui.*;
import io.anuke.ucore.scene.ui.layout.Stack;
import io.anuke.ucore.scene.ui.layout.Table;
import io.anuke.ucore.util.Bundles;
import io.anuke.ucore.util.Mathf;
import io.anuke.ucore.util.Strings;
import static io.anuke.mindustry.Vars.*;
public class BlocksFragment implements Fragment{
private Table desctable, itemtable, blocks, weapons;
private Stack stack = new Stack();
private Array<String> statlist = new Array<>();
private boolean shown = true;
private Recipe hoveredDescriptionRecipe;
public void build(){
InputHandler input = control.input();
new table(){{
abottom();
aright();
visible(() -> !state.is(State.menu) && shown);
blocks = new table(){{
itemtable = new Table("button");
itemtable.setVisible(() -> input.recipe == null && !state.mode.infiniteResources);
desctable = new Table("button");
desctable.setVisible(() -> hoveredDescriptionRecipe != null || input.recipe != null);
desctable.update(() -> {
// note: This is required because there is no direct connection between
// input.recipe and the description ui. If input.recipe gets set to null
// a proper cleanup of the ui elements is required.
boolean anyRecipeShown = input.recipe != null || hoveredDescriptionRecipe != null;
boolean descriptionTableClean = desctable.getChildren().size == 0;
boolean cleanupRequired = !anyRecipeShown && !descriptionTableClean;
if(cleanupRequired){
desctable.clear();
}
});
stack.add(itemtable);
stack.add(desctable);
add(stack).fillX().uniformX();
row();
new table("pane") {{
touchable(Touchable.enabled);
int rows = 4;
int maxcol = 0;
float size = 48;
Stack stack = new Stack();
ButtonGroup<ImageButton> group = new ButtonGroup<>();
Array<Recipe> recipes = new Array<>();
for (Section sec : Section.values()) {
recipes.clear();
Recipes.getBy(sec, recipes);
maxcol = Math.max((int) ((float) recipes.size / rows + 1), maxcol);
}
for (Section sec : Section.values()) {
recipes.clear();
Recipes.getBy(sec, recipes);
Table table = new Table();
ImageButton button = new ImageButton("icon-" + sec.name(), "toggle");
button.clicked(() -> {
if (!table.isVisible() && input.recipe != null) {
input.recipe = null;
}
});
button.setName("sectionbutton" + sec.name());
add(button).growX().height(54).padLeft(-1).padTop(sec.ordinal() <= 2 ? -10 : -5);
button.getImageCell().size(40).padBottom(4).padTop(2);
group.add(button);
if (sec.ordinal() % 3 == 2 && sec.ordinal() > 0) {
row();
}
table.margin(4);
table.top().left();
int i = 0;
for (Recipe r : recipes) {
TextureRegion region = Draw.hasRegion(r.result.name() + "-icon") ?
Draw.region(r.result.name() + "-icon") : Draw.region(r.result.name());
ImageButton image = new ImageButton(region, "select");
image.addListener(new ClickListener(){
@Override
public void enter(InputEvent event, float x, float y, int pointer, Element fromActor) {
super.enter(event, x, y, pointer, fromActor);
if (hoveredDescriptionRecipe != r) {
hoveredDescriptionRecipe = r;
updateRecipe(r);
}
}
@Override
public void exit(InputEvent event, float x, float y, int pointer, Element toActor) {
super.exit(event, x, y, pointer, toActor);
hoveredDescriptionRecipe = null;
updateRecipe(input.recipe);
}
});
image.clicked(() -> {
// note: input.recipe only gets set here during a click.
// during a hover only the visual description will be updated.
boolean nothingSelectedYet = input.recipe == null;
boolean selectedSomethingElse = !nothingSelectedYet && input.recipe != r;
boolean shouldMakeSelection = nothingSelectedYet || selectedSomethingElse;
if (shouldMakeSelection) {
input.recipe = r;
hoveredDescriptionRecipe = r;
updateRecipe(r);
} else {
input.recipe = null;
hoveredDescriptionRecipe = null;
updateRecipe(null);
}
});
table.add(image).size(size + 8);
image.getImageCell().size(size);
image.update(() -> {
boolean canPlace = !control.tutorial().active() || control.tutorial().canPlace();
boolean has = (state.inventory.hasItems(r.requirements)) && canPlace;
image.setChecked(input.recipe == r);
image.setTouchable(canPlace ? Touchable.enabled : Touchable.disabled);
image.getImage().setColor(has ? Color.WHITE : Hue.lightness(0.33f));
});
if (i % rows == rows - 1)
table.row();
i++;
}
table.setVisible(button::isChecked);
stack.add(table);
}
row();
add(stack).colspan(Section.values().length);
margin(10f);
marginLeft(1f);
marginRight(1f);
end();
}}.right().bottom().uniformX();
row();
if(!mobile) {
weapons = new table("button").margin(0).fillX().end().get();
}
visible(() -> !state.is(State.menu) && shown);
}}.end().get();
}}.end();
updateWeapons();
}
public void updateWeapons(){
if(mobile) return;
weapons.clearChildren();
weapons.left();
ButtonGroup<ImageButton> group = new ButtonGroup<>();
for(int i = 0; i < control.upgrades().getWeapons().size; i ++){
Weapon weapon = control.upgrades().getWeapons().get(i);
weapons.addImageButton(weapon.name, "toggle", 8*3, () -> {
player.weaponLeft = player.weaponRight = weapon;
}).left().size(40f, 45f).padRight(-1).group(group);
}
int idx = control.upgrades().getWeapons().indexOf(player.weaponLeft, true);
if(idx != -1)
group.getButtons().get(idx).setChecked(true);
else if(group.getButtons().size > 0)
group.getButtons().get(0).setChecked(true);
}
public void toggle(boolean show, float t, Interpolation ip){
if(!show){
blocks.actions(Actions.translateBy(0, -blocks.getHeight() - stack.getHeight(), t, ip), Actions.call(() -> shown = false));
}else{
shown = true;
blocks.actions(Actions.translateBy(0, -blocks.getTranslation().y, t, ip));
}
}
void updateRecipe(Recipe recipe){
if (recipe == null) {
desctable.clear();
return;
}
desctable.clear();
desctable.setTouchable(Touchable.enabled);
desctable.defaults().left();
desctable.left();
desctable.margin(12);
Table header = new Table();
desctable.add(header).left();
desctable.row();
TextureRegion region = Draw.hasRegion(recipe.result.name() + "-icon") ?
Draw.region(recipe.result.name() + "-icon") : Draw.region(recipe.result.name());
header.addImage(region).size(8*5).padTop(4);
Label nameLabel = new Label(recipe.result.formalName);
nameLabel.setWrap(true);
header.add(nameLabel).padLeft(2).width(120f);
//extra info
if(recipe.result.fullDescription != null){
header.addButton("?", () -> showBlockInfo(recipe.result)).expandX().padLeft(3).top().right().size(40f, 44f).padTop(-2);
}
desctable.add().pad(2);
Table requirements = new Table();
desctable.row();
desctable.add(requirements);
desctable.left();
for(ItemStack stack : recipe.requirements){
requirements.addImage(Draw.region("icon-"+stack.item.name)).size(8*3);
Label reqlabel = new Label("");
reqlabel.update(()->{
int current = state.inventory.getAmount(stack.item);
String text = Mathf.clamp(current, 0, stack.amount) + "/" + stack.amount;
reqlabel.setColor(current < stack.amount ? Colors.get("missingitems") : Color.WHITE);
reqlabel.setText(text);
});
requirements.add(reqlabel).left();
requirements.row();
}
desctable.row();
Label label = new Label("[health]"+ Bundles.get("text.health")+": " + recipe.result.health);
label.setWrap(true);
desctable.add(label).width(200).padTop(4).padBottom(2);
}
public void showBlockInfo(Block block){
statlist.clear();
block.getStats(statlist);
Label desclabel = new Label(block.fullDescription);
desclabel.setWrap(true);
boolean wasPaused = state.is(State.paused);
state.set(State.paused);
FloatingDialog d = new FloatingDialog("$text.blocks.blockinfo");
Table table = new Table();
table.defaults().pad(1f);
ScrollPane pane = new ScrollPane(table, "clear");
pane.setFadeScrollBars(false);
Table top = new Table();
top.left();
top.add(new Image(Draw.region(block.name))).size(8*5 * block.width);
top.add("[accent]"+block.formalName).padLeft(6f);
table.add(top).fill().left();
table.row();
table.add(desclabel).width(600);
table.row();
d.content().add(pane).grow();
if(statlist.size > 0){
table.add("$text.blocks.extrainfo").padTop(6).padBottom(5).left();
table.row();
}
for(String s : statlist){
if(s.contains(":")) {
String color = s.substring(0, s.indexOf("]")+1);
String first = s.substring(color.length(), s.indexOf(":")).replace("/", "").replace(" ", "").toLowerCase();
String last = s.substring(s.indexOf(":"), s.length());
s = color + Bundles.get("text.blocks." + first) + last;
}
table.add(s).left();
table.row();
}
d.buttons().addButton("$text.ok", ()->{
if(!wasPaused) state.set(State.playing);
d.hide();
}).size(110, 50).pad(10f);
d.show();
}
public void showBlockLogs(int x, int y){
boolean wasPaused = state.is(State.paused);
state.set(State.paused);
FloatingDialog d = new FloatingDialog("$text.blocks.editlogs");
Table table = new Table();
table.defaults().pad(1f);
ScrollPane pane = new ScrollPane(table, "clear");
pane.setFadeScrollBars(false);
Table top = new Table();
top.left();
top.add("[accent]Edit logs for: "+ x + ", " + y);
table.add(top).fill().left();
table.row();
d.content().add(pane).grow();
if(currentEditLogs == null || currentEditLogs.size == 0) {
table.add("$text.block.editlogsnotfound").left();
table.row();
}
else {
for(int i = 0; i < currentEditLogs.size; i++) {
EditLog log = currentEditLogs.get(i);
table.add("[gold]" + (i + 1) + ". [white]" + log.info()).left();
table.row();
}
}
d.buttons().addButton("$text.ok", () -> {
if(!wasPaused)
state.set(State.playing);
d.hide();
}).size(110, 50).pad(10f);
d.show();
}
public void updateItems(){
itemtable.clear();
itemtable.left();
if(state.mode.infiniteResources){
return;
}
for(int i = 0; i < state.inventory.getItems().length; i ++){
int amount = state.inventory.getItems()[i];
if(amount == 0) continue;
String formatted = amount > 99999999 ? "inf" : format(amount);
Image image = new Image(Draw.hasRegion("icon-" + Item.getByID(i).name) ?
Draw.region("icon-" + Item.getByID(i).name) : Draw.region("blank"));
Label label = new Label(formatted);
label.setFontScale(fontscale*1.5f);
itemtable.add(image).size(8*3);
itemtable.add(label).expandX().left();
if(i % 2 == 1 && i > 0) itemtable.row();
}
}
String format(int number){
if(number > 1000000) {
return Strings.toFixed(number/1000000f, 1) + "[gray]mil";
}else if(number > 10000){
return number/1000 + "[gray]k";
}else if(number > 1000){
return Strings.toFixed(number/1000f, 1) + "[gray]k";
}else{
return number + "";
}
}
}

View File

@@ -1,33 +1,31 @@
package io.anuke.mindustry.ui.fragments;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.g2d.Batch;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.graphics.g2d.GlyphLayout;
import com.badlogic.gdx.utils.Align;
import com.badlogic.gdx.utils.Array;
import io.anuke.arc.Core;
import io.anuke.arc.Input.TextInput;
import io.anuke.arc.collection.Array;
import io.anuke.arc.graphics.Color;
import io.anuke.arc.graphics.g2d.*;
import io.anuke.arc.math.Mathf;
import io.anuke.arc.scene.Group;
import io.anuke.arc.scene.ui.Label;
import io.anuke.arc.scene.ui.Label.LabelStyle;
import io.anuke.arc.scene.ui.TextField;
import io.anuke.arc.scene.ui.layout.Table;
import io.anuke.arc.scene.ui.layout.Unit;
import io.anuke.arc.util.Align;
import io.anuke.arc.util.Time;
import io.anuke.mindustry.Vars;
import io.anuke.mindustry.core.GameState.State;
import io.anuke.mindustry.core.Platform;
import io.anuke.mindustry.gen.Call;
import io.anuke.mindustry.input.Binding;
import io.anuke.mindustry.net.Net;
import io.anuke.mindustry.net.NetEvents;
import io.anuke.ucore.core.Core;
import io.anuke.ucore.core.Inputs;
import io.anuke.ucore.core.Timers;
import io.anuke.ucore.scene.ui.Label;
import io.anuke.ucore.scene.ui.Label.LabelStyle;
import io.anuke.ucore.scene.ui.TextField;
import io.anuke.ucore.scene.ui.layout.Table;
import io.anuke.ucore.scene.ui.layout.Unit;
import io.anuke.ucore.util.Mathf;
import static io.anuke.mindustry.Vars.state;
import static io.anuke.ucore.core.Core.scene;
import static io.anuke.ucore.core.Core.skin;
import static io.anuke.arc.Core.input;
import static io.anuke.arc.Core.scene;
import static io.anuke.mindustry.Vars.*;
public class ChatFragment extends Table implements Fragment{
public class ChatFragment extends Table{
private final static int messagesShown = 10;
private final static int maxLength = 150;
private Array<ChatMessage> messages = new Array<>();
private float fadetime;
private boolean chatOpen = false;
@@ -36,42 +34,53 @@ public class ChatFragment extends Table implements Fragment{
private BitmapFont font;
private GlyphLayout layout = new GlyphLayout();
private float offsetx = Unit.dp.scl(4), offsety = Unit.dp.scl(4), fontoffsetx = Unit.dp.scl(2), chatspace = Unit.dp.scl(50);
private float textWidth = Unit.dp.scl(600);
private Color shadowColor = new Color(0, 0, 0, 0.4f);
private float textspacing = Unit.dp.scl(10);
private Array<String> history = new Array<String>();
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 = Core.skin.getFont("default-font");
font = scene.skin.getFont("default-font");
setVisible(() -> !state.is(State.menu) && Net.active());
visible(() -> {
if(!Net.active() && messages.size > 0){
clearMessages();
//TODO put it in input?
update(() -> {
if(!Net.active() && chatOpen){
hide();
if(chatOpen){
hide();
}
}
if(Net.active() && Inputs.keyTap("chat")){
return !state.is(State.menu) && Net.active();
});
update(() -> {
if(Net.active() && input.keyTap(Binding.chat)){
toggle();
}
if (chatOpen) {
if (Inputs.keyTap("chat_history_prev") && historyPos < history.size - 1) {
if (historyPos == 0) history.set(0, chatfield.getText());
if(chatOpen){
if(input.keyTap(Binding.chat_history_prev) && historyPos < history.size - 1){
if(historyPos == 0) history.set(0, chatfield.getText());
historyPos++;
updateChat();
}
if (Inputs.keyTap("chat_history_next") && historyPos > 0) {
if(input.keyTap(Binding.chat_history_next) && historyPos > 0){
historyPos--;
updateChat();
}
scrollPos = (int)Mathf.clamp(scrollPos + Inputs.getAxis("chat_scroll"), 0, Math.max(0, messages.size - messagesShown));
scrollPos = (int)Mathf.clamp(scrollPos + input.axis(Binding.chat_scroll), 0, Math.max(0, messages.size - messagesShown));
}
});
@@ -79,9 +88,8 @@ public class ChatFragment extends Table implements Fragment{
setup();
}
@Override
public void build() {
scene.add(this);
public Fragment container(){
return container;
}
public void clearMessages(){
@@ -95,70 +103,72 @@ public class ChatFragment extends Table implements Fragment{
fieldlabel.getStyle().font = font;
fieldlabel.setStyle(fieldlabel.getStyle());
chatfield = new TextField("", new TextField.TextFieldStyle(skin.get(TextField.TextFieldStyle.class)));
chatfield.setTextFieldFilter((field, c) -> field.getText().length() < maxLength);
chatfield = new TextField("", new TextField.TextFieldStyle(scene.skin.get(TextField.TextFieldStyle.class)));
chatfield.setFilter((field, c) -> field.getText().length() < Vars.maxTextLength);
chatfield.getStyle().background = null;
chatfield.getStyle().font = scene.skin.getFont("default-font-chat");
chatfield.getStyle().fontColor = Color.WHITE;
chatfield.getStyle().font = skin.getFont("default-font-chat");
chatfield.setStyle(chatfield.getStyle());
Platform.instance.addDialog(chatfield, maxLength);
bottom().left().marginBottom(offsety).marginLeft(offsetx*2).add(fieldlabel).padBottom(4f);
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) {
if(Vars.mobile){
marginBottom(105f);
marginRight(240f);
}
if(Vars.mobile) {
addImageButton("icon-arrow-right", 14 * 2, this::toggle).size(46f, 51f).visible(() -> chatOpen).pad(2f);
}
}
@Override
public void draw(Batch batch, float alpha){
public void draw(){
float opacity = Core.settings.getInt("chatopacity") / 100f;
float textWidth = Math.min(Core.graphics.getWidth()/1.5f, Unit.dp.scl(700f));
batch.setColor(shadowColor);
Draw.color(shadowColor);
if(chatOpen)
batch.draw(skin.getRegion("white"), offsetx, chatfield.getY(), chatfield.getWidth() + 15f, chatfield.getHeight()-1);
if(chatOpen){
Fill.crect(offsetx, chatfield.getY(), chatfield.getWidth() + 15f, chatfield.getHeight() - 1);
}
super.draw(batch, alpha);
super.draw();
float spacing = chatspace;
chatfield.setVisible(chatOpen);
fieldlabel.setVisible(chatOpen);
chatfield.visible(chatOpen);
fieldlabel.visible(chatOpen);
batch.setColor(shadowColor);
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 || chatOpen); 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;
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(!chatOpen && fadetime-i < 1f && fadetime-i >= 0f){
font.getCache().setAlphas(fadetime-i);
batch.setColor(0, 0, 0, shadowColor.a*(fadetime-i));
if(!chatOpen && 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);
}
batch.draw(skin.getRegion("white"), offsetx, theight-layout.height-2, textWidth + Unit.dp.scl(4f), layout.height+textspacing);
batch.setColor(shadowColor);
Fill.crect(offsetx, theight - layout.height - 2, textWidth + Unit.dp.scl(4f), layout.height + textspacing);
Draw.color(shadowColor);
Draw.alpha(opacity * shadowColor.a);
font.getCache().draw(batch);
font.getCache().draw();
}
batch.setColor(Color.WHITE);
Draw.color();
if(fadetime > 0 && !chatOpen)
fadetime -= Timers.delta()/180f;
fadetime -= Time.delta() / 180f;
}
private void sendMessage(){
@@ -168,15 +178,29 @@ public class ChatFragment extends Table implements Fragment{
if(message.replaceAll(" ", "").isEmpty()) return;
history.insert(1, message);
NetEvents.handleSendMessage(message);
Call.sendChatMessage(message);
}
public void toggle(){
if(!chatOpen){
scene.setKeyboardFocus(chatfield);
chatfield.fireClick();
chatOpen = !chatOpen;
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);
chatOpen = !chatOpen;
@@ -191,12 +215,12 @@ public class ChatFragment extends Table implements Fragment{
clearChatInput();
}
public void updateChat() {
public void updateChat(){
chatfield.setText(history.get(historyPos));
chatfield.setCursorPosition(chatfield.getText().length());
}
public void clearChatInput() {
public void clearChatInput(){
historyPos = 0;
history.set(0, "");
chatfield.setText("");
@@ -228,7 +252,7 @@ public class ChatFragment extends Table implements Fragment{
if(sender == null){ //no sender, this is a server message?
formattedMessage = message;
}else{
formattedMessage = "[CORAL][["+sender+"[CORAL]]:[WHITE] "+message;
formattedMessage = "[CORAL][[" + sender + "[CORAL]]:[WHITE] " + message;
}
}
}

View File

@@ -1,186 +0,0 @@
package io.anuke.mindustry.ui.fragments;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.files.FileHandle;
import io.anuke.mindustry.entities.Player;
import io.anuke.mindustry.entities.enemies.Enemy;
import io.anuke.mindustry.entities.enemies.EnemyTypes;
import io.anuke.mindustry.net.Net;
import io.anuke.ucore.core.Timers;
import io.anuke.ucore.scene.builders.button;
import io.anuke.ucore.scene.builders.label;
import io.anuke.ucore.scene.builders.table;
import io.anuke.ucore.scene.ui.Label;
import io.anuke.ucore.scene.ui.ScrollPane;
import io.anuke.ucore.scene.ui.layout.Table;
import io.anuke.ucore.util.Log;
import io.anuke.ucore.util.Log.LogHandler;
import static io.anuke.mindustry.Vars.*;
public class DebugFragment implements Fragment {
private static StringBuilder log = new StringBuilder();
static{
Log.setLogger(new LogHandler(){
@Override
public void print(String text, Object... args){
super.print(text, args);
if(log.length() < 1000) {
log.append(Log.format(text, args));
log.append("\n");
}
}
});
}
@Override
public void build(){
new table(){{
visible(() -> debug);
atop().aright();
new table("pane"){{
defaults().fillX();
new label("Debug");
row();
new button("noclip", "toggle", () -> noclip = !noclip);
row();
new button("hideplayer", "toggle", () -> showPlayer = !showPlayer);
row();
new button("blocks", "toggle", () -> showBlockDebug = !showBlockDebug);
row();
new button("paths", "toggle", () -> showPaths = !showPaths);
row();
new button("wave", () -> state.wavetime = 0f);
row();
new button("time 0", () -> Timers.resetTime(0f));
row();
new button("time max", () -> Timers.resetTime(1080000 - 60*10));
row();
new button("clear", () -> {
enemyGroup.clear();
state.enemies = 0;
netClient.clearRecieved();
});
row();
new button("spawn", () -> {
new Enemy(EnemyTypes.healer).set(player.x, player.y).add();
});
row();
}}.end();
row();
}}.end();
new table(){{
visible(() -> console);
atop().aleft();
new table("pane") {{
defaults().fillX();
ScrollPane pane = new ScrollPane(new Label(DebugFragment::debugInfo), "clear");
add(pane);
row();
new button("dump", () -> {
try{
FileHandle file = Gdx.files.local("packet-dump.txt");
file.writeString("--INFO--\n", false);
file.writeString(debugInfo(), true);
file.writeString("--LOG--\n\n", true);
file.writeString(log.toString(), true);
}catch (Exception e){
ui.showError("Error dumping log.");
}
});
}}.end();
}}.end();
new table(){{
visible(() -> console);
atop();
Table table = new Table("pane");
table.label(() -> log.toString());
ScrollPane pane = new ScrollPane(table, "clear");
get().add(pane);
}}.end();
}
public static void printDebugInfo(){
Gdx.app.error("Minudstry Info Dump", debugInfo());
}
public static String debugInfo(){
StringBuilder result = join(
"net.active: " + Net.active(),
"net.server: " + Net.server(),
"net.client: " + Net.client(),
"state: " + state.getState(),
Net.client() ?
"chat.open: " + ui.chatfrag.chatOpen() + "\n" +
"chat.messages: " + ui.chatfrag.getMessagesSize() + "\n" +
"client.connecting: " + netClient.isConnecting() + "\n" : "",
"players: " + playerGroup.size(),
"enemies: " + enemyGroup.size(),
"tiles: " + tileGroup.size(),
"time: " + Timers.time(),
world.getCore() != null && world.getCore().entity != null ? "core.health: " + world.getCore().entity.health : "",
"core: " + world.getCore(),
"state.gameover: " + state.gameOver,
"state: " + state.getState(),
!Net.server() ? clientDebug.getOut() : serverDebug.getOut()
);
result.append("players: ");
for(Player player : playerGroup.all()){
result.append(" name: ");
result.append(player.name);
result.append("\n");
result.append(" id: ");
result.append(player.id);
result.append("\n");
result.append(" cid: ");
result.append(player.clientid);
result.append("\n");
result.append(" dead: ");
result.append(player.isDead());
result.append("\n");
result.append(" pos: ");
result.append(player.x);
result.append(", ");
result.append(player.y);
result.append("\n");
result.append(" android: ");
result.append(player.isAndroid);
result.append("\n");
result.append(" local: ");
result.append(player.isLocal);
result.append("\n");
result.append("\n");
}
return result.toString();
}
private static StringBuilder join(String... strings){
StringBuilder builder = new StringBuilder();
for (String string : strings) {
builder.append(string);
builder.append("\n");
}
return builder;
}
}

View File

@@ -1,5 +1,7 @@
package io.anuke.mindustry.ui.fragments;
public interface Fragment{
public void build();
import io.anuke.arc.scene.Group;
public abstract class Fragment{
public abstract void build(Group parent);
}

View File

@@ -1,243 +1,624 @@
package io.anuke.mindustry.ui.fragments;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.math.Interpolation;
import io.anuke.annotations.Annotations.Loc;
import io.anuke.annotations.Annotations.Remote;
import io.anuke.arc.Core;
import io.anuke.arc.Events;
import io.anuke.arc.collection.Array;
import io.anuke.arc.graphics.Color;
import io.anuke.arc.graphics.g2d.Draw;
import io.anuke.arc.graphics.g2d.Lines;
import io.anuke.arc.input.KeyCode;
import io.anuke.arc.math.Interpolation;
import io.anuke.arc.math.Mathf;
import io.anuke.arc.math.geom.Vector2;
import io.anuke.arc.scene.Element;
import io.anuke.arc.scene.Group;
import io.anuke.arc.scene.actions.Actions;
import io.anuke.arc.scene.event.Touchable;
import io.anuke.arc.scene.style.TextureRegionDrawable;
import io.anuke.arc.scene.ui.*;
import io.anuke.arc.scene.ui.layout.*;
import io.anuke.arc.scene.utils.Elements;
import io.anuke.arc.util.*;
import io.anuke.mindustry.core.GameState.State;
import io.anuke.mindustry.entities.Units;
import io.anuke.mindustry.entities.type.BaseUnit;
import io.anuke.mindustry.entities.type.Player;
import io.anuke.mindustry.game.EventType.StateChangeEvent;
import io.anuke.mindustry.game.Team;
import io.anuke.mindustry.game.UnlockableContent;
import io.anuke.mindustry.gen.Call;
import io.anuke.mindustry.graphics.Pal;
import io.anuke.mindustry.input.Binding;
import io.anuke.mindustry.net.Net;
import io.anuke.ucore.core.Core;
import io.anuke.ucore.core.Inputs;
import io.anuke.ucore.core.Settings;
import io.anuke.ucore.scene.actions.Actions;
import io.anuke.ucore.scene.builders.imagebutton;
import io.anuke.ucore.scene.builders.label;
import io.anuke.ucore.scene.builders.table;
import io.anuke.ucore.scene.event.Touchable;
import io.anuke.ucore.scene.ui.ImageButton;
import io.anuke.ucore.scene.ui.Label;
import io.anuke.ucore.scene.ui.layout.Table;
import io.anuke.ucore.util.Bundles;
import io.anuke.mindustry.net.Packets.AdminAction;
import io.anuke.mindustry.type.ContentType;
import io.anuke.mindustry.type.UnitType;
import io.anuke.mindustry.ui.*;
import io.anuke.mindustry.ui.dialogs.FloatingDialog;
import static io.anuke.mindustry.Vars.*;
public class HudFragment implements Fragment{
public final BlocksFragment blockfrag = new BlocksFragment();
public class HudFragment extends Fragment{
public final PlacementFragment blockfrag = new PlacementFragment();
private ImageButton menu, flip;
private Table respawntable;
private Table wavetable;
private Label infolabel;
private boolean shown = true;
private float dsize = 58;
private float isize = 40;
private ImageButton flip;
private Table lastUnlockTable;
private Table lastUnlockLayout;
private boolean shown = true;
private float dsize = 59;
public void build(){
private float coreAttackTime;
private float lastCoreHP;
private Team lastTeam;
private float coreAttackOpacity = 0f;
//menu at top left
new table(){{
atop();
aleft();
public void build(Group parent){
new table(){{
//menu at top left
parent.fill(cont -> {
cont.top().left().visible(() -> !state.is(State.menu));
new table() {{
left();
defaults().size(dsize).left();
if(mobile){
menu = new imagebutton("icon-menu", isize, ui.paused::show).get();
flip = new imagebutton("icon-arrow-up", isize, () -> toggleMenus()).get();
{
Table select = new Table();
update(t -> {
if(Inputs.keyTap("toggle_menus") && !ui.chatfrag.chatOpen()){
toggleMenus();
}
});
select.visible(() -> !state.is(State.menu));
select.left();
select.defaults().size(dsize).left();
new imagebutton("icon-pause", isize, () -> {
if (Net.active()) {
ui.listfrag.visible = !ui.listfrag.visible;
} else {
state.set(state.is(State.paused) ? State.playing : State.paused);
}
}).update(i -> {
if (Net.active()) {
i.getStyle().imageUp = Core.skin.getDrawable("icon-players");
} else {
i.setDisabled(Net.active());
i.getStyle().imageUp = Core.skin.getDrawable(state.is(State.paused) ? "icon-play" : "icon-pause");
}
}).get();
select.addImageButton("icon-menu-large", "clear", iconsize, ui.paused::show);
flip = select.addImageButton("icon-arrow-up", "clear", iconsize, this::toggleMenus).get();
new imagebutton("icon-settings", isize, () -> {
if (Net.active() && mobile) {
if (ui.chatfrag.chatOpen()) {
ui.chatfrag.hide();
} else {
ui.chatfrag.toggle();
}
} else {
ui.settings.show();
}
}).update(i -> {
if (Net.active() && mobile) {
i.getStyle().imageUp = Core.skin.getDrawable("icon-chat");
} else {
i.getStyle().imageUp = Core.skin.getDrawable("icon-settings");
}
}).get();
select.addImageButton("icon-pause", "clear", iconsize, () -> {
if(Net.active()){
ui.listfrag.toggle();
}else{
state.set(state.is(State.paused) ? State.playing : State.paused);
}
}).update(i -> {
if(Net.active()){
i.getStyle().imageUp = Core.scene.skin.getDrawable("icon-players");
}else{
i.setDisabled(false);
i.getStyle().imageUp = Core.scene.skin.getDrawable(state.is(State.paused) ? "icon-play" : "icon-pause");
}
}).get();
}}.end();
select.addImageButton("icon-settings", "clear", iconsize, () -> {
if(Net.active() && mobile){
if(ui.chatfrag.chatOpen()){
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 = Core.scene.skin.getDrawable("icon-chat");
}else{
i.getStyle().imageUp = Core.scene.skin.getDrawable("icon-database");
}
}).get();
row();
select.addImage("blank").color(Pal.accent).width(3f).fillY();
new table() {{
touchable(Touchable.enabled);
visible(() -> shown);
addWaveTable();
}}.fillX().end();
float size = Unit.dp.scl(dsize);
Array<Element> children = new Array<>(select.getChildren());
row();
//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++;
Core.scene.add(elem);
elem.visible(() -> {
if(fi < 4){
elem.setSize(size);
}else{
elem.setSize(Unit.dp.scl(3f), size);
}
elem.setPosition(fi * size, Core.graphics.getHeight(), Align.topLeft);
return !state.is(State.menu);
});
}
visible(() -> !state.is(State.menu));
cont.add().size(dsize * 4 + 3, dsize).left();
}
infolabel = new Label(() -> (Settings.getBool("fps") ? (Gdx.graphics.getFramesPerSecond() + " FPS") +
(threads.isEnabled() ? " / " + threads.getFPS() + " TPS" : "") + (Net.client() && !gwt ? "\nPing: " + Net.getPing() : "") : ""));
row();
add(infolabel).size(-1);
cont.row();
cont.addImage("blank").height(3f).color(Pal.accent).fillX();
cont.row();
}
}}.end();
cont.update(() -> {
if(!Core.input.keyDown(Binding.gridMode) && Core.input.keyTap(Binding.toggle_menus) && !ui.chatfrag.chatOpen() && !Core.scene.hasDialog() && !(Core.scene.getKeyboardFocus() instanceof TextField)){
toggleMenus();
}
});
Table wavesMain, editorMain;
boolean[] prev = {false};
cont.stack(wavesMain = new Table(), editorMain = new Table()).height(wavesMain.getPrefHeight()).update(s -> {
((Table)s.getParent()).getCell(s).height((wavesMain.isVisible() ? wavesMain.getPrefHeight() : editorMain.getPrefHeight()) / Unit.dp.scl(1f));
if(prev[0] != wavesMain.isVisible()){
s.getParent().pack();
prev[0] = wavesMain.isVisible();
}
});
}}.end();
{
wavesMain.visible(() -> shown && !state.isEditor());
wavesMain.left();
Stack stack = new Stack();
TextButton waves = new TextButton("", "wave");
Table btable = new Table().margin(0);
//tutorial ui table
new table(){{
control.tutorial().buildUI(this);
stack.add(waves);
stack.add(btable);
visible(() -> control.tutorial().active());
}}.end();
addWaveTable(waves);
addPlayButton(btable);
wavesMain.add(stack).width(dsize * 4 + 3f);
wavesMain.row();
wavesMain.table("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();
}
//paused table
new table(){{
visible(() -> state.is(State.paused) && !Net.active());
atop();
{
editorMain.table("button-edge-4", 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("white", "clear-toggle-partial", 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));
new table("pane"){{
new label("[orange]< "+ Bundles.get("text.paused") + " >").scale(0.75f).pad(6);
}}.end();
}}.end();
if(++i % 3 == 0){
teams.row();
}
}
}).left();
//respawn background table
new table("white"){{
respawntable = get();
respawntable.setColor(Color.CLEAR);
update(t -> {
if(state.is(State.menu)){
respawntable.setColor(Color.CLEAR);
}
});
}}.end();
if(enableUnitEditing){
//respawn table
new table(){{
new table("pane"){{
t.row();
t.addImageTextButton("$editor.spawn", "icon-add", iconsize, () -> {
FloatingDialog dialog = new FloatingDialog("$editor.spawn");
int i = 0;
for(UnitType type : content.<UnitType>getBy(ContentType.unit)){
dialog.cont.addImageButton("white", 48, () -> {
Call.spawnUnitEditor(player, type);
dialog.hide();
}).get().getStyle().imageUp = new TextureRegionDrawable(type.iconRegion);
if(++i % 4 == 0) dialog.cont.row();
}
dialog.addCloseButton();
dialog.setFillParent(false);
dialog.show();
}).fillX();
new label(()->"[orange]"+Bundles.get("text.respawn")+" " + (int)(control.getRespawnTime()/60)).scale(0.75f).pad(10);
float[] size = {0};
float[] position = {0, 0};
visible(()->control.getRespawnTime() > 0 && !state.is(State.menu));
t.row();
t.addImageTextButton("$editor.removeunit", "icon-quit", "toggle", iconsize, () -> {
}}.end();
}}.end();
}).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;
}
});
}
}
new table(){{
abottom();
visible(() -> !state.is(State.menu) && control.getSaves().isSaving());
Draw.color(Pal.accent, Color.WHITE, Mathf.absin(Time.time(), 8f, 1f));
Lines.poly(position[0], position[1], 4, size[0] / 2f);
Draw.reset();
new label("$text.saveload");
if(!found[0]){
size[0] = Mathf.lerpDelta(size[0], 0f, 0.2f);
}
});
}
}).width(dsize * 4 + 3f);
editorMain.visible(() -> shown && state.isEditor());
}
}}.end();
//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 : -Unit.dp.scl(dsize * 4 + 3), 0));
IntFormat fps = new IntFormat("fps");
IntFormat ping = new IntFormat("ping");
blockfrag.build();
}
info.label(() -> fps.get(Core.graphics.getFramesPerSecond())).left();
info.row();
info.label(() -> ping.get(Net.getPing())).visible(Net::client).left();
}).top().left();
});
private void toggleMenus(){
if (wavetable.getActions().size != 0) return;
//minimap
parent.fill(t -> t.top().right().add(new Minimap()).visible(() -> !state.is(State.menu) && Core.settings.getBool("minimap")));
float dur = 0.3f;
Interpolation in = Interpolation.pow3Out;
//spawner warning
parent.fill(t -> {
t.touchable(Touchable.disabled);
t.visible(() -> !state.is(State.menu));
t.table("flat", 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(world.spawner.playerNear()), 0.1f)).get().color.a = 0f;
});
flip.getStyle().imageUp = Core.skin.getDrawable(shown ? "icon-arrow-down" : "icon-arrow-up");
parent.fill(t -> {
t.visible(() -> netServer.isWaitingForPlayers() && !state.is(State.menu));
t.table("button", c -> c.add("$waiting.players"));
});
if (shown) {
blockfrag.toggle(false, dur, in);
wavetable.actions(Actions.translateBy(0, wavetable.getHeight() + dsize, dur, in), Actions.call(() -> shown = false));
infolabel.actions(Actions.translateBy(0, wavetable.getHeight(), dur, in), Actions.call(() -> shown = false));
} else {
shown = true;
blockfrag.toggle(true, dur, in);
wavetable.actions(Actions.translateBy(0, -wavetable.getTranslation().y, dur, in));
infolabel.actions(Actions.translateBy(0, -infolabel.getTranslation().y, dur, in));
}
}
//'core is under attack' table
parent.fill(t -> {
t.touchable(Touchable.disabled);
float notifDuration = 240f;
private String getEnemiesRemaining() {
if(state.enemies == 1) {
return Bundles.format("text.enemies.single", state.enemies);
} else return Bundles.format("text.enemies", state.enemies);
}
Events.on(StateChangeEvent.class, event -> {
if(event.to == State.menu || event.from == State.menu){
coreAttackTime = 0f;
lastCoreHP = Float.NaN;
}
});
private void addWaveTable(){
float uheight = 66f;
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 = 0f;
return false;
}
wavetable = new table("button"){{
aleft();
new table(){{
aleft();
float curr = state.teams.get(player.getTeam()).cores.first().entity.health;
new label(() -> Bundles.format("text.wave", state.wave)).scale(fontscale*1.5f).left().padLeft(-6);
if(lastTeam != player.getTeam()){
lastCoreHP = curr;
lastTeam = player.getTeam();
return false;
}
row();
if(!Float.isNaN(lastCoreHP) && curr < lastCoreHP){
coreAttackTime = notifDuration;
}
lastCoreHP = curr;
new label(()-> state.enemies > 0 ?
getEnemiesRemaining() :
(control.tutorial().active() || state.mode.disableWaveTimer) ? "$text.waiting"
: Bundles.format("text.wave.waiting", (int) (state.wavetime / 60f)))
.minWidth(126).padLeft(-6).left();
t.getColor().a = coreAttackOpacity;
if(coreAttackTime > 0){
coreAttackOpacity = Mathf.lerpDelta(coreAttackOpacity, 1f, 0.1f);
}else{
coreAttackOpacity = Mathf.lerpDelta(coreAttackOpacity, 0f, 0.1f);
}
margin(10f);
get().marginLeft(6);
}}.left().end();
coreAttackTime -= Time.delta();
lastTeam = player.getTeam();
add().growX();
return coreAttackOpacity > 0;
});
t.table("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);
});
playButton(uheight);
}}.height(uheight).fillX().expandX().end().get();
wavetable.getParent().getParent().swapActor(wavetable.getParent(), menu.getParent());
}
//launch button
parent.fill(t -> {
t.top().right().visible(() -> !state.is(State.menu));
TextButton[] testb = {null};
TextButton button = Elements.newButton("$launch", () -> {
FloatingDialog dialog = new FloatingDialog("$launch");
dialog.update(() -> {
if(!testb[0].isVisible()){
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 void playButton(float uheight){
new imagebutton("icon-play", 30f, () -> {
state.wavetime = 0f;
}).height(uheight).fillX().right().padTop(-8f).padBottom(-12f).padLeft(-15).padRight(-10).width(40f).update(l->{
boolean vis = state.enemies <= 0 && (Net.server() || !Net.active());
boolean paused = state.is(State.paused) || !vis;
l.setVisible(vis);
l.getStyle().imageUp = Core.skin.getDrawable(vis ? "icon-play" : "clear");
l.setTouchable(!paused ? Touchable.enabled : Touchable.disabled);
});
}
});
public void updateItems(){
blockfrag.updateItems();
}
testb[0] = button;
public void updateWeapons(){
blockfrag.updateWeapons();
}
public void fadeRespawn(boolean in){
respawntable.addAction(Actions.color(in ? new Color(0, 0, 0, 0.3f) : Color.CLEAR, 0.3f));
}
button.getStyle().disabledFontColor = Color.WHITE;
button.margin(16f);
button.visible(() ->
world.isZone() &&
world.getZone().metCondition() &&
!Net.client() &&
state.wave % world.getZone().launchPeriod == 0 && !world.spawner.isSpawning());
button.update(() -> {
if(world.getZone() == null){
button.setText("");
return;
}
button.setText(state.enemies() > 0 ? Core.bundle.format("launch.unable", state.enemies()) : Core.bundle.get("launch") + "\n" +
Core.bundle.format("launch.next", state.wave + world.getZone().launchPeriod));
button.getLabel().setColor(Tmp.c1.set(Color.WHITE).lerp(state.enemies() > 0 ? Color.WHITE : Color.SCARLET,
Mathf.absin(Time.time(), 2f, 1f)));
});
button.setDisabled(() -> state.enemies() > 0);
button.getLabelCell().left().get().setAlignment(Align.left, Align.left);
t.add(button).size(250f, 80f);
});
//paused table
parent.fill(t -> {
t.top().visible(() -> state.isPaused());
t.table("button", top -> top.add("$paused").pad(6f));
});
//'saving' indicator
parent.fill(t -> {
t.bottom().visible(() -> !state.is(State.menu) && control.saves.isSaving());
t.add("$saveload");
});
blockfrag.build(Core.scene.root);
}
@Remote(targets = Loc.both, forward = true, called = Loc.both)
public static void setPlayerTeamEditor(Player player, Team team){
if(state.isEditor()){
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();
}
}
public void showToast(String text){
Table table = new Table("button");
table.update(() -> {
if(state.is(State.menu)){
table.remove();
}
});
table.margin(12);
table.addImage("icon-check").size(iconsize).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(4f),
//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
if(content.getContentIcon() == null) return;
//if there's currently no unlock notification...
if(lastUnlockTable == null){
Table table = new Table("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.getContentIcon());
image.setScaling(Scaling.fit);
in.add(image).size(48f).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(4f),
//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 <= cap; 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.getContentIcon());
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("white");
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);
}
private void toggleMenus(){
if(flip != null){
flip.getStyle().imageUp = Core.scene.skin.getDrawable(shown ? "icon-arrow-down" : "icon-arrow-up");
}
shown = !shown;
if(flip != null){
flip.getParent().act(Core.graphics.getDeltaTime());
}
}
private void addWaveTable(TextButton 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.labelWrap(() -> {
builder.setLength(0);
builder.append(wavef.get(state.wave));
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(true);
table.visible(() -> state.rules.waves);
}
private void addPlayButton(Table table){
table.right().addImageButton("icon-play", "right", 30f, () -> {
if(Net.client() && player.isAdmin){
Call.onAdminRequest(player, AdminAction.wave);
}else{
state.wavetime = 0f;
}
}).growY().fillX().right().width(40f)
.visible(() -> state.rules.waves && ((Net.server() || player.isAdmin) || !Net.active()) && state.enemies() == 0
&& !world.spawner.isSpawning());
}
}

View File

@@ -1,45 +1,54 @@
package io.anuke.mindustry.ui.fragments;
import com.badlogic.gdx.graphics.Colors;
import io.anuke.ucore.scene.builders.label;
import io.anuke.ucore.scene.builders.table;
import io.anuke.ucore.scene.event.Touchable;
import io.anuke.ucore.scene.ui.Label;
import io.anuke.ucore.scene.ui.layout.Table;
import io.anuke.arc.scene.Group;
import io.anuke.arc.scene.event.Touchable;
import io.anuke.arc.scene.ui.Label;
import io.anuke.arc.scene.ui.TextButton;
import io.anuke.arc.scene.ui.layout.Table;
import io.anuke.mindustry.graphics.Pal;
public class LoadingFragment implements Fragment {
public class LoadingFragment extends Fragment{
private Table table;
private TextButton button;
@Override
public void build() {
public void build(Group parent){
parent.fill("loadDim", t -> {
t.visible(false);
t.touchable(Touchable.enabled);
t.add().height(70f).row();
table = new table("loadDim"){{
touchable(Touchable.enabled);
get().addImage("white").growX()
.height(3f).pad(4f).growX().get().setColor(Colors.get("accent"));
row();
new label("$text.loading"){{
get().setName("namelabel");
}}.pad(10);
row();
get().addImage("white").growX()
.height(3f).pad(4f).growX().get().setColor(Colors.get("accent"));
}}.end().get();
t.addImage("white").growX().height(3f).pad(4f).growX().get().setColor(Pal.accent);
t.row();
t.add("$loading").name("namelabel").pad(10f);
t.row();
t.addImage("white").growX().height(3f).pad(4f).growX().get().setColor(Pal.accent);
t.row();
table.setVisible(false);
button = t.addButton("$cancel", () -> {
}).pad(20).size(250f, 70f).visible(false).get();
table = t;
});
}
public void setButton(Runnable listener){
button.visible(true);
button.getListeners().remove(button.getListeners().size - 1);
button.clicked(listener);
}
public void show(){
show("$text.loading");
show("$loading");
}
public void show(String text){
table.<Label>find("namelabel").setText(text);
table.setVisible(true);
table.visible(true);
table.toFront();
}
public void hide(){
table.setVisible(false);
table.visible(false);
button.visible(false);
}
}

View File

@@ -1,152 +1,156 @@
package io.anuke.mindustry.ui.fragments;
import com.badlogic.gdx.Gdx;
import io.anuke.arc.Core;
import io.anuke.arc.Events;
import io.anuke.arc.scene.Group;
import io.anuke.arc.scene.ui.layout.Table;
import io.anuke.arc.util.Strings;
import io.anuke.mindustry.core.GameState.State;
import io.anuke.mindustry.core.Platform;
import io.anuke.mindustry.io.Version;
import io.anuke.mindustry.game.EventType.ResizeEvent;
import io.anuke.mindustry.game.Version;
import io.anuke.mindustry.ui.MenuButton;
import io.anuke.mindustry.ui.MobileButton;
import io.anuke.mindustry.ui.dialogs.FloatingDialog;
import io.anuke.ucore.scene.builders.imagebutton;
import io.anuke.ucore.scene.builders.label;
import io.anuke.ucore.scene.builders.table;
import io.anuke.ucore.util.OS;
import static io.anuke.mindustry.Vars.*;
public class MenuFragment implements Fragment{
public void build(){
new table(){{
visible(() -> state.is(State.menu));
public class MenuFragment extends Fragment{
private Table container;
if(!mobile){
new table(){{
@Override
public void build(Group parent){
parent.fill(c -> {
container = c;
container.visible(() -> state.is(State.menu));
float w = 200f;
float bw = w * 2f + 10f;
if(!mobile){
buildDesktop();
}else{
buildMobile();
Events.on(ResizeEvent.class, event -> buildMobile());
}
});
defaults().size(w, 70f).padTop(5).padRight(5);
//discord icon in top right
parent.fill(c -> c.top().right().addButton("", "discord", ui.discord::show).size(84, 45)
.visible(() -> state.is(State.menu)));
add(new MenuButton("icon-play-2", "$text.play", MenuFragment.this::showPlaySelect)).width(bw).colspan(2);
//info icon
if(mobile){
parent.fill(c -> c.top().left().addButton("", "info", ui.about::show).size(84, 45)
.visible(() -> state.is(State.menu)));
}
row();
//version info
parent.fill(c -> c.bottom().left().add(Strings.format("Mindustry v{0} {1}-{2} {3}{4}", Version.number, Version.modifier, Version.type,
(Version.build == -1 ? "custom build" : "build " + Version.build), Version.revision == 0 ? "" : "." + Version.revision))
.visible(() -> state.is(State.menu)));
}
add(new MenuButton("icon-editor", "$text.editor", () -> {
if(gwt){
ui.showInfo("$text.editor.web");
}else{
ui.editor.show();
}
}));
add(new MenuButton("icon-tools", "$text.settings", ui.settings::show));
private void buildMobile(){
container.clear();
container.setSize(Core.graphics.getWidth(), Core.graphics.getHeight());
row();
float size = 120f;
float isize = iconsize;
container.defaults().size(size).pad(5).padTop(4f);
add(new MenuButton("icon-info", "$text.about.button", ui.about::show));
MobileButton
play = new MobileButton("icon-play-2", isize, "$play", ui.deploy::show),
maps = new MobileButton("icon-map", isize, "$maps", ui.maps::show),
custom = new MobileButton("icon-play-custom", isize, "$customgame", this::showCustomSelect),
join = new MobileButton("icon-add", isize, "$joingame", ui.join::show),
editor = new MobileButton("icon-editor", isize, "$editor", () -> ui.loadAnd(ui.editor::show)),
tools = new MobileButton("icon-tools", isize, "$settings", ui.settings::show),
donate = new MobileButton("icon-donate", isize, "$donate", () -> Core.net.openURI(donationURL)),
exit = new MobileButton("icon-exit", isize, "$quit", () -> Core.app.exit());
add(new MenuButton("icon-menu", OS.isMac ? "$text.credits" : "$text.changelog.title", () -> {
if(OS.isMac){
ui.about.showCredits();
}else {
ui.changelog.show();
}
}));
if(!Core.graphics.isPortrait()){
container.add(play);
container.add(join);
container.add(custom);
container.add(maps);
container.row();
row();
if(!gwt){
add(new MenuButton("icon-exit", "$text.quit", Gdx.app::exit)).width(bw).colspan(2);
}
container.table(table -> {
table.defaults().set(container.defaults());
get().margin(16);
}}.end();
table.add(editor);
table.add(tools);
}else {
new table() {{
float size = 120f;
defaults().size(size).pad(5);
float isize = 14f * 4;
if(Platform.instance.canDonate()) table.add(donate);
table.add(exit);
}).colspan(4);
}else{
container.add(play);
container.add(maps);
container.row();
container.add(custom);
container.add(join);
container.row();
container.add(editor);
container.add(tools);
container.row();
new imagebutton("icon-play-2", isize, ui.levels::show).text("$text.play").padTop(4f);
container.table(table -> {
table.defaults().set(container.defaults());
new imagebutton("icon-tutorial", isize, () -> control.playMap(world.maps().getMap("tutorial"))).text("$text.tutorial").padTop(4f);
if(Platform.instance.canDonate()) table.add(donate);
table.add(exit);
}).colspan(2);
}
}
new imagebutton("icon-load", isize, ui.load::show).text("$text.load").padTop(4f);
private void buildDesktop(){
container.table(out -> {
new imagebutton("icon-add", isize, ui.join::show).text("$text.joingame").padTop(4f);
float w = 200f;
float bw = w * 2f + 10f;
row();
out.margin(16);
out.defaults().size(w, 66f).padTop(5).padRight(5);
new table(){{
out.add(new MenuButton("icon-play-2", "$play", ui.deploy::show)).width(bw).colspan(2);
defaults().size(size).pad(5);
out.row();
new imagebutton("icon-editor", isize, ui.editor::show).text("$text.editor").padTop(4f);
out.add(new MenuButton("icon-add", "$joingame", ui.join::show));
new imagebutton("icon-tools", isize, ui.settings::show).text("$text.settings").padTop(4f);
out.add(new MenuButton("icon-play-custom", "$customgame", this::showCustomSelect));
new imagebutton("icon-info", isize, ui.about::show).text("$text.about.button").padTop(4f);
out.row();
if (!ios) {
new imagebutton("icon-donate", isize, Platform.instance::openDonations).text("$text.donate").padTop(4f);
}
out.add(new MenuButton("icon-editor", "$editor", () -> ui.loadAnd(ui.editor::show)));
}}.colspan(4).end();
}}.end();
}
}}.end();
out.add(new MenuButton("icon-map", "$maps", ui.maps::show));
//discord icon in top right
if(Platform.instance.hasDiscord()) {
new table() {{
abottom().atop().aright();
get().addButton("", "discord", ui.discord::show);
}}.end().visible(() -> state.is(State.menu));
}
out.row();
//version info
new table(){{
visible(() -> state.is(State.menu));
abottom().aleft();
new label("Mindustry " + Version.code + " " + Version.type + " / " + Version.buildName);
}}.end();
}
out.add(new MenuButton("icon-info", "$about.button", ui.about::show));
private void showPlaySelect(){
float w = 200f;
float bw = w * 2f + 10f;
out.add(new MenuButton("icon-tools", "$settings", ui.settings::show));
FloatingDialog dialog = new FloatingDialog("$text.play");
dialog.addCloseButton();
dialog.content().defaults().height(70f).width(w).padRight(5f);
out.row();
dialog.content().add(new MenuButton("icon-play-2", "$text.newgame", () -> {
dialog.hide();
ui.levels.show();
})).width(bw).colspan(2);
dialog.content().row();
out.add(new MenuButton("icon-exit", "$quit", Core.app::exit)).width(bw).colspan(2);
});
}
dialog.content().add(new MenuButton("icon-add", "$text.joingame", () -> {
if(Platform.instance.canJoinGame()){
ui.join.show();
dialog.hide();
}else{
ui.showInfo("$text.multiplayer.web");
}
}));
dialog.content().add(new MenuButton("icon-tutorial", "$text.tutorial", ()-> {
control.playMap(world.maps().getMap("tutorial"));
dialog.hide();
}));
dialog.content().row();
dialog.content().add(new MenuButton("icon-load", "$text.loadgame", () -> {
ui.load.show();
dialog.hide();
})).width(bw).colspan(2);
dialog.show();
}
private void showCustomSelect(){
FloatingDialog dialog = new FloatingDialog("$play");
dialog.setFillParent(false);
dialog.addCloseButton();
dialog.cont.defaults().size(210f, 64f);
dialog.cont.add(new MenuButton("icon-editor", "$newgame", () -> {
dialog.hide();
ui.custom.show();
}));
dialog.cont.row();
dialog.cont.add(new MenuButton("icon-load", "$loadgame", () -> {
ui.load.show();
dialog.hide();
}));
dialog.show();
}
}

View File

@@ -0,0 +1,30 @@
package io.anuke.mindustry.ui.fragments;
import io.anuke.arc.scene.Group;
import io.anuke.mindustry.input.InputHandler;
/** Fragment for displaying overlays such as block inventories. */
public class OverlayFragment extends Fragment{
public final BlockInventoryFragment inv;
public final BlockConfigFragment config;
private Group group = new Group();
public OverlayFragment(InputHandler input){
inv = new BlockInventoryFragment();
config = new BlockConfigFragment();
}
@Override
public void build(Group parent){
group.setFillParent(true);
parent.addChild(group);
inv.build(group);
config.build(group);
}
public void remove(){
group.remove();
}
}

View File

@@ -1,234 +1,349 @@
package io.anuke.mindustry.ui.fragments;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.math.Interpolation;
import com.badlogic.gdx.utils.Align;
import io.anuke.arc.Core;
import io.anuke.arc.Events;
import io.anuke.arc.collection.Array;
import io.anuke.arc.graphics.Color;
import io.anuke.arc.input.KeyCode;
import io.anuke.arc.math.geom.Vector2;
import io.anuke.arc.scene.Group;
import io.anuke.arc.scene.event.Touchable;
import io.anuke.arc.scene.style.TextureRegionDrawable;
import io.anuke.arc.scene.ui.*;
import io.anuke.arc.scene.ui.layout.Table;
import io.anuke.mindustry.core.GameState.State;
import io.anuke.mindustry.input.AndroidInput;
import io.anuke.mindustry.entities.type.TileEntity;
import io.anuke.mindustry.game.EventType.UnlockEvent;
import io.anuke.mindustry.game.EventType.WorldLoadEvent;
import io.anuke.mindustry.graphics.Pal;
import io.anuke.mindustry.input.Binding;
import io.anuke.mindustry.input.InputHandler;
import io.anuke.mindustry.input.PlaceMode;
import io.anuke.ucore.core.Core;
import io.anuke.ucore.scene.actions.Actions;
import io.anuke.ucore.scene.builders.imagebutton;
import io.anuke.ucore.scene.builders.label;
import io.anuke.ucore.scene.builders.table;
import io.anuke.ucore.scene.event.Touchable;
import io.anuke.ucore.scene.ui.ButtonGroup;
import io.anuke.ucore.scene.ui.ImageButton;
import io.anuke.ucore.scene.ui.Label;
import io.anuke.ucore.scene.ui.layout.Table;
import io.anuke.ucore.scene.ui.layout.Unit;
import io.anuke.ucore.util.Bundles;
import io.anuke.ucore.util.Mathf;
import io.anuke.mindustry.type.*;
import io.anuke.mindustry.world.Block;
import io.anuke.mindustry.world.Block.Icon;
import io.anuke.mindustry.world.Tile;
import static io.anuke.mindustry.Vars.*;
public class PlacementFragment implements Fragment{
boolean shown = false, placing = false;
Table breaktable, next, container;
Label modelabel;
public void build(){
if(!mobile) return;
public class PlacementFragment extends Fragment{
final int rowWidth = 4;
InputHandler input = control.input();
Array<Block> returnArray = new Array<>();
Array<Category> returnCatArray = new Array<>();
boolean[] categoryEmpty = new boolean[Category.values().length];
Category currentCategory = Category.distribution;
Block hovered, lastDisplay;
Tile lastHover;
Tile hoverTile;
Table blockTable, toggler, topTable;
boolean lastGround;
float s = 50f;
float translation = Unit.dp.scl(58f);
//not configurable, no plans to make it configurable
final KeyCode[] inputGrid = {
KeyCode.NUM_1, KeyCode.NUM_2, KeyCode.NUM_3, KeyCode.NUM_4,
KeyCode.Q, KeyCode.W, KeyCode.E, KeyCode.R,
KeyCode.A, KeyCode.S, KeyCode.D, KeyCode.F,
KeyCode.Z, KeyCode.X, KeyCode.C, KeyCode.V
}, inputCatGrid = {
KeyCode.NUM_1, KeyCode.NUM_2,
KeyCode.Q, KeyCode.W,
KeyCode.A, KeyCode.S,
KeyCode.Z, KeyCode.X, KeyCode.C, KeyCode.V
};
new table(){{
visible(() -> !state.is(State.menu));
public PlacementFragment(){
Events.on(WorldLoadEvent.class, event -> {
Core.app.post(() -> {
control.input().block = null;
rebuild();
});
});
abottom();
aleft();
Events.on(UnlockEvent.class, event -> {
if(event.content instanceof Block){
rebuild();
}
});
}
ButtonGroup<ImageButton> placeGroup = new ButtonGroup<>();
ButtonGroup<ImageButton> breakGroup = new ButtonGroup<>();
void rebuild(){
currentCategory = Category.turret;
Group group = toggler.getParent();
int index = toggler.getZIndex();
toggler.remove();
build(group);
toggler.setZIndex(index);
}
update(t -> {
if((input.recipe == null) == placing){
float i = 0.1f;
Interpolation n = Interpolation.pow3Out;
if(input.recipe == null){
placing = false;
container.clearActions();
container.actions(Actions.translateBy(0, -(container.getTranslation().y + translation), i, n));
if (!input.lastBreakMode.both) input.placeMode = input.lastBreakMode;
}else{
placing = true;
container.clearActions();
container.actions(Actions.translateBy(0, -(container.getTranslation().y), i, n));
input.placeMode = input.lastPlaceMode;
}
}
boolean gridUpdate(InputHandler input){
if(Core.input.keyDown(Binding.pick)){ //mouse eyedropper select
Tile tile = world.tileWorld(Core.input.mouseWorld().x, Core.input.mouseWorld().y);
if(!input.placeMode.delete){
placeGroup.setMinCheckCount(1);
for(ImageButton button : placeGroup.getButtons()){
if(button.getName().equals(input.placeMode.name())){
button.setChecked(true);
break;
}
}
}else{
placeGroup.setMinCheckCount(0);
for(ImageButton button : placeGroup.getButtons())
button.setChecked(false);
}
if(tile != null){
tile = tile.link();
Block tryRecipe = tile.block();
if(tryRecipe.isVisible() && unlocked(tryRecipe)){
input.block = tryRecipe;
currentCategory = input.block.buildCategory;
return true;
}
}
}
if(input.placeMode.delete || input.breakMode.both){
PlaceMode mode = input.breakMode;
breakGroup.setMinCheckCount(1);
for(ImageButton button : breakGroup.getButtons()){
if(button.getName().equals(mode.name())){
button.setChecked(true);
break;
}
}
}else{
breakGroup.setMinCheckCount(0);
for(ImageButton button : breakGroup.getButtons())
button.setChecked(false);
}
});
if(!Core.input.keyDown(Binding.gridMode) || ui.chatfrag.chatOpen()) return false;
if(Core.input.keyDown(Binding.gridModeShift)){ //select category
int i = 0;
for(KeyCode key : inputCatGrid){
if(Core.input.keyDown(key)){
input.block = getByCategory(Category.values()[i]).first();
currentCategory = input.block.buildCategory;
}
i++;
}
return true;
}else{ //select block
int i = 0;
Array<Block> recipes = getByCategory(currentCategory);
for(KeyCode key : inputGrid){
if(Core.input.keyDown(key))
input.block = (i < recipes.size && unlocked(recipes.get(i))) ? recipes.get(i) : null;
i++;
}
}
return false;
}
container = new table(){{
modelabel = new label("").get();
@Override
public void build(Group parent){
parent.fill(full -> {
toggler = full;
full.bottom().right().visible(() -> !state.is(State.menu) && ui.hudfrag.shown());
row();
full.table(frame -> {
InputHandler input = control.input();
//break menu
new table() {{
abottom();
aleft();
//rebuilds the category table with the correct recipes
Runnable rebuildCategory = () -> {
blockTable.clear();
blockTable.top().margin(5);
height(s + 5 + 4);
int index = 0;
next = new table("pane") {{
margin(5f);
ButtonGroup<ImageButton> group = new ButtonGroup<>();
group.setMinCheckCount(0);
defaults().padBottom(-5.5f);
for(Block block : getByCategory(currentCategory)){
if(index++ % rowWidth == 0){
blockTable.row();
}
new imagebutton("icon-arrow-right", 10 * 3, () -> {
toggle(!shown);
}).update(l -> l.getStyle().imageUp = Core.skin.getDrawable(shown ? "icon-arrow-left" : "icon-" + input.breakMode.name())).size(s, s + 4);
if(!unlocked(block)){
blockTable.add().size(46);
continue;
}
}}.end().get();
ImageButton button = blockTable.addImageButton("icon-locked", "select", 8 * 4, () -> {
if(unlocked(block)){
input.block = input.block == block ? null : block;
}
}).size(46f).group(group).get();
breaktable = new table("pane") {{
visible(() -> shown);
margin(5f);
marginLeft(-(Unit.dp.scl(1f) - 1f) * 2.5f);
touchable(Touchable.enabled);
aleft();
button.getStyle().imageUp = new TextureRegionDrawable(block.icon(Icon.medium));
defaults().size(s, s + 4);
button.update(() -> { //color unplacable things gray
TileEntity core = player.getClosestCore();
Color color = state.rules.infiniteResources || (core != null && (core.items.has(block.buildRequirements, state.rules.buildCostMultiplier) || state.rules.infiniteResources)) ? Color.WHITE : Color.GRAY;
button.forEach(elem -> elem.setColor(color));
button.setChecked(input.block == block);
});
for (PlaceMode mode : PlaceMode.values()) {
if (!mode.shown || !mode.delete) continue;
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);
};
defaults().padBottom(-5.5f);
//top table with hover info
frame.table("button-edge-2", 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;
ImageButton button = new imagebutton("icon-" + mode.name(), "toggle", 10 * 3, () -> {
control.input().resetCursor();
input.breakMode = mode;
input.lastBreakMode = mode;
if (!mode.both){
input.placeMode = mode;
}else{
input.placeMode = input.lastPlaceMode;
}
modeText(Bundles.format("text.mode.break", mode.toString()));
}).group(breakGroup).get();
topTable.clear();
topTable.top().left().margin(5);
button.setName(mode.name());
button.released(() -> {
//TODO hack
if(mode == PlaceMode.areaDelete){
((AndroidInput)input).placing = false;
}
});
}
lastHover = hoverTile;
lastDisplay = getSelected();
lastGround = tileDisplayBlock() != null;
}}.end().get();
if(lastDisplay != null){ //show selected recipe
lastGround = false;
breaktable.getParent().swapActor(breaktable, next);
topTable.table(header -> {
header.left();
header.add(new Image(lastDisplay.icon(Icon.medium))).size(8 * 4);
header.labelWrap(() -> !unlocked(lastDisplay) ? Core.bundle.get("block.unknown") : lastDisplay.localizedName)
.left().width(190f).padLeft(5);
header.add().growX();
if(unlocked(lastDisplay)){
header.addButton("?", "clear-partial", () -> ui.content.show(lastDisplay))
.size(8 * 5).padTop(-5).padRight(-5).right().grow();
}
}).growX().left();
topTable.row();
//add requirement table
topTable.table(req -> {
req.top().left();
breaktable.getTranslation().set(-breaktable.getPrefWidth(), 0);
for(ItemStack stack : lastDisplay.buildRequirements){
req.table(line -> {
line.left();
line.addImage(stack.item.icon(Item.Icon.small)).size(8 * 2);
line.add(stack.item.localizedName()).color(Color.LIGHT_GRAY).padLeft(2).left();
line.labelWrap(() -> {
TileEntity core = player.getClosestCore();
if(core == null || state.rules.infiniteResources) return "*/*";
}}.end().get();
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]");
row();
return color + ui.formatAmount(amount) + "[white]/" + stackamount;
}).padLeft(5);
}).left();
req.row();
}
}).growX().left().margin(3);
//place menu
new table() {{
touchable(Touchable.enabled);
}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("blank").color(Pal.accent).colspan(3).height(3).growX();
frame.row();
frame.table("pane-2", blocksSelect -> {
blocksSelect.margin(4).marginTop(0);
blocksSelect.table(blocks -> blockTable = blocks).grow();
blocksSelect.row();
blocksSelect.table(input::buildUI).growX();
}).fillY().bottom().touchable(Touchable.enabled);
frame.table(categories -> {
categories.defaults().size(50f);
aleft();
ButtonGroup<ImageButton> group = new ButtonGroup<>();
new table("pane") {{
margin(5f);
aleft();
//update category empty values
for(Category cat : Category.values()){
Array<Block> blocks = getByCategory(cat);
categoryEmpty[cat.ordinal()] = blocks.isEmpty() || !unlocked(blocks.first());
}
defaults().size(s, s + 4).padBottom(-5.5f);
int f = 0;
for(Category cat : getCategories()){
if(f++ % 2 == 0) categories.row();
Color color = Color.GRAY;
if(categoryEmpty[cat.ordinal()]){
categories.addImage("flat");
continue;
}
new imagebutton("icon-cancel", 14 * 3, () -> {
input.recipe = null;
}).imageColor(color)
.visible(() -> input.recipe != null);
categories.addImageButton("icon-" + cat.name(), "clear-toggle", iconsize, () -> {
currentCategory = cat;
rebuildCategory.run();
}).group(group).update(i -> i.setChecked(currentCategory == cat));
}
}).touchable(Touchable.enabled);
for (PlaceMode mode : PlaceMode.values()) {
if (!mode.shown || mode.delete) continue;
rebuildCategory.run();
frame.update(() -> {
if(gridUpdate(input)) rebuildCategory.run();
});
});
});
}
new imagebutton("icon-" + mode.name(), "toggle", 10 * 3, () -> {
control.input().resetCursor();
input.placeMode = mode;
input.lastPlaceMode = mode;
modeText(Bundles.format("text.mode.place", mode.toString()));
}).group(placeGroup).get().setName(mode.name());
}
Array<Category> getCategories(){
returnCatArray.clear();
returnCatArray.addAll(Category.values());
returnCatArray.sort((c1, c2) -> Boolean.compare(categoryEmpty[c1.ordinal()], categoryEmpty[c2.ordinal()]));
return returnCatArray;
}
new imagebutton("icon-arrow", 14 * 3, () -> {
input.rotation = Mathf.mod(input.rotation + 1, 4);
}).imageColor(color).visible(() -> input.recipe != null).update(image -> {
image.getImage().setRotation(input.rotation * 90);
image.getImage().setOrigin(Align.center);
});
Array<Block> getByCategory(Category cat){
returnArray.clear();
for(Block block : content.blocks()){
if(block.buildCategory == cat && block.isVisible()){
returnArray.add(block);
}
}
returnArray.sort((b1, b2) -> -Boolean.compare(unlocked(b1), unlocked(b2)));
return returnArray;
}
}}.left().end();
}}.left().end();
boolean unlocked(Block block){
return !world.isZone() || data.isUnlocked(block);
}
}}.end().get();
/** Returns the currently displayed block in the top box. */
Block getSelected(){
Block toDisplay = null;
container.setTranslation(0, -translation);
Vector2 v = topTable.stageToLocalCoordinates(Core.input.mouse());
}}.end();
}
//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;
}
private void modeText(String text){
modelabel.setText(text);
modelabel.clearActions();
modelabel.setColor(Color.WHITE);
modelabel.actions(Actions.fadeOut(5f, Interpolation.fade));
}
//block currently selected
if(control.input().block != null){
toDisplay = control.input().block;
}
private void toggle(boolean show){
float dur = 0.3f;
Interpolation in = Interpolation.pow3Out;
//block hovered on in build menu
if(hovered != null){
toDisplay = hovered;
}
if(breaktable.getActions().size != 0 || shown == show) return;
return toDisplay;
}
breaktable.getParent().swapActor(breaktable, next);
if(!show){
control.input().breakMode = PlaceMode.none;
if(control.input().placeMode.delete) control.input().placeMode = PlaceMode.none;
breaktable.actions(Actions.translateBy(-breaktable.getWidth() - 5, 0, dur, in), Actions.call(() -> shown = false));
}else{
shown = true;
breaktable.actions(Actions.translateBy(-breaktable.getTranslation().x - 5, 0, dur, in));
}
}
}
/** Returns the block currently being hovered over in the world. */
Block tileDisplayBlock(){
return hoverTile == null ? null : hoverTile.block().synthetic() ? hoverTile.block() : hoverTile.overlay().itemDrop != null ? hoverTile.overlay() : null;
}
}

View File

@@ -1,89 +1,63 @@
package io.anuke.mindustry.ui.fragments;
import io.anuke.mindustry.Vars;
import io.anuke.arc.Core;
import io.anuke.arc.graphics.g2d.Draw;
import io.anuke.arc.graphics.g2d.Lines;
import io.anuke.arc.scene.Group;
import io.anuke.arc.scene.event.Touchable;
import io.anuke.arc.scene.ui.Image;
import io.anuke.arc.scene.ui.layout.Table;
import io.anuke.arc.scene.ui.layout.Unit;
import io.anuke.arc.util.Interval;
import io.anuke.mindustry.core.GameState.State;
import io.anuke.mindustry.entities.Player;
import io.anuke.mindustry.gen.Call;
import io.anuke.mindustry.graphics.Pal;
import io.anuke.mindustry.net.Net;
import io.anuke.mindustry.net.NetConnection;
import io.anuke.mindustry.net.NetEvents;
import io.anuke.mindustry.net.Packets.AdminAction;
import io.anuke.mindustry.net.Packets.KickReason;
import io.anuke.mindustry.ui.BorderImage;
import io.anuke.ucore.core.Inputs;
import io.anuke.ucore.graphics.Draw;
import io.anuke.ucore.scene.Element;
import io.anuke.ucore.scene.builders.button;
import io.anuke.ucore.scene.builders.label;
import io.anuke.ucore.scene.builders.table;
import io.anuke.ucore.scene.event.Touchable;
import io.anuke.ucore.scene.ui.ScrollPane;
import io.anuke.ucore.scene.ui.layout.Stack;
import io.anuke.ucore.scene.ui.layout.Table;
import io.anuke.ucore.util.Bundles;
import io.anuke.ucore.util.Mathf;
import static io.anuke.mindustry.Vars.*;
public class PlayerListFragment implements Fragment{
public boolean visible = false;
Table content = new Table();
int last = 0;
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(){
new table(){{
new table("pane"){{
touchable(Touchable.enabled);
margin(14f);
new label(() -> Bundles.format(playerGroup.size() == 1 ? "text.players.single" :
"text.players", playerGroup.size()));
row();
content.marginRight(13f).marginLeft(13f);
ScrollPane pane = new ScrollPane(content, "clear");
pane.setScrollingDisabled(true, false);
pane.setFadeScrollBars(false);
add(pane).grow();
row();
new table("pane"){{
margin(12f);
get().addCheck("$text.server.friendlyfire", b -> {
state.friendlyFire = b;
NetEvents.handleFriendlyFireChange(b);
}).growX().update(i -> i.setChecked(state.friendlyFire)).disabled(b -> Net.client()).padRight(5);
new button("$text.server.bans", () -> {
ui.bans.show();
}).padTop(-12).padBottom(-12).fillY().cell.disabled(b -> Net.client());
new button("$text.server.admins", () -> {
ui.admins.show();
}).padTop(-12).padBottom(-12).padRight(-12).fillY().cell.disabled(b -> Net.client());
new button("$text.server.rollback", () -> {
ui.rollback.show();
}).padTop(-12).padBottom(-12).padRight(-12).fillY().cell.disabled(b -> !player.isAdmin);
}}.pad(10f).growX().end();
}}.end();
update(t -> {
if(!mobile){
if(Inputs.keyTap("player_list")){
visible = !visible;
}
}
public void build(Group parent){
parent.fill(cont -> {
cont.visible(() -> visible);
cont.update(() -> {
if(!(Net.active() && !state.is(State.menu))){
visible = false;
return;
}
if(playerGroup.size() != last){
if(visible && timer.get(20)){
rebuild();
last = playerGroup.size();
content.pack();
content.act(Core.graphics.getDeltaTime());
//TODO hack
Core.scene.act(0f);
}
});
visible(() -> visible);
}}.end();
cont.table("button", 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();
}
@@ -93,100 +67,86 @@ public class PlayerListFragment implements Fragment{
float h = 74f;
for(Player player : playerGroup.all()){
NetConnection connection = gwt ? null : Net.getConnection(player.clientid);
playerGroup.all().sort((p1, p2) -> p1.getTeam().compareTo(p2.getTeam()));
playerGroup.all().each(user -> {
NetConnection connection = user.con;
if(connection == null && Net.server() && !player.isLocal) continue;
if(connection == null && Net.server() && !user.isLocal) return;
Table button = new Table("button");
Table button = new Table();
button.left();
button.margin(5).marginBottom(10);
Stack stack = new Stack();
BorderImage image = new BorderImage(Draw.region(player.isAndroid ? "ship-standard" : "mech-standard-icon"), 3f);
Table table = new Table(){
@Override
public void draw(){
super.draw();
Draw.color(Pal.accent);
Draw.alpha(parentAlpha);
Lines.stroke(Unit.dp.scl(3f));
Lines.rect(x, y, width, height);
Draw.reset();
}
};
table.margin(8);
table.add(new Image(user.mech.iconRegion)).grow();
stack.add(image);
if(!player.isAndroid) {
stack.add(new Element(){
public void draw(){
float s = getWidth() / 12f;
for(int i : Mathf.signs){
Draw.rect((i < 0 ? player.weaponLeft.name : player.weaponRight.name)
+ "-equip", x + s * 6 + i * 3*s, y + s*6 + 2*s, -8*s*i, 8*s);
}
}
});
}
button.add(stack).size(h);
button.labelWrap("[#" + player.getColor().toString().toUpperCase() + "]" + player.name).width(170f).pad(10);
button.add(table).size(h);
button.labelWrap("[#" + user.color.toString().toUpperCase() + "]" + user.name).width(170f).pad(10);
button.add().grow();
button.addImage("icon-admin").size(14*2).visible(() -> player.isAdmin && !(!player.isLocal && Net.server())).padRight(5);
button.addImage("icon-admin").size(iconsize).visible(() -> user.isAdmin && !(!user.isLocal && Net.server())).padRight(5).get().updateVisibility();
if((Net.server() || Vars.player.isAdmin) && !player.isLocal && (!player.isAdmin || Net.server())){
if((Net.server() || player.isAdmin) && !user.isLocal && (!user.isAdmin || Net.server())){
button.add().growY();
float bs = (h + 14)/2f;
float bs = (h) / 2f;
button.table(t -> {
t.defaults().size(bs - 1, bs + 3);
t.defaults().size(bs);
t.addImageButton("icon-ban", 14*2, () -> {
ui.showConfirm("$text.confirm", "$text.confirmban", () -> {
if(Net.server()) {
netServer.admins.banPlayerIP(connection.address);
netServer.kick(player.clientid, KickReason.banned);
}else{
NetEvents.handleAdministerRequest(player, AdminAction.ban);
}
});
}).padBottom(-5.1f);
t.addImageButton("icon-cancel", 14*2, () -> {
if(Net.server()) {
netServer.kick(player.clientid, KickReason.kick);
}else{
NetEvents.handleAdministerRequest(player, AdminAction.kick);
}
}).padBottom(-5.1f);
t.addImageButton("icon-ban", "clear-partial", iconsize,
() -> ui.showConfirm("$confirm", "$confirmban", () -> Call.onAdminRequest(user, AdminAction.ban)));
t.addImageButton("icon-cancel", "clear-partial", iconsize,
() -> ui.showConfirm("$confirm", "$confirmkick", () -> Call.onAdminRequest(user, AdminAction.kick)));
t.row();
t.addImageButton("icon-admin", "toggle", 14*2, () -> {
t.addImageButton("icon-admin", "clear-toggle-partial", iconsize, () -> {
if(Net.client()) return;
String id = netServer.admins.getTrace(connection.address).uuid;
String id = user.uuid;
if(netServer.admins.isAdmin(id, connection.address)){
ui.showConfirm("$text.confirm", "$text.confirmunadmin", () -> {
netServer.admins.unAdminPlayer(id);
NetEvents.handleAdminSet(player, false);
});
ui.showConfirm("$confirm", "$confirmunadmin", () -> netServer.admins.unAdminPlayer(id));
}else{
ui.showConfirm("$text.confirm", "$text.confirmadmin", () -> {
netServer.admins.adminPlayer(id, connection.address);
NetEvents.handleAdminSet(player, true);
});
ui.showConfirm("$confirm", "$confirmadmin", () -> netServer.admins.adminPlayer(id, user.usid));
}
}).update(b ->{
b.setChecked(player.isAdmin);
b.setDisabled(Net.client());
}).get().setTouchable(() -> Net.client() ? Touchable.disabled : Touchable.enabled);
t.addImageButton("icon-zoom-small", 14*2, () -> NetEvents.handleTraceRequest(player));
}).padRight(12).padTop(-5).padLeft(0).padBottom(-10).size(bs + 10f, bs);
})
.update(b -> b.setChecked(user.isAdmin))
.disabled(b -> Net.client())
.touchable(() -> Net.client() ? Touchable.disabled : Touchable.enabled)
.checked(user.isAdmin);
t.addImageButton("icon-zoom", "clear-partial", iconsize, () -> Call.onAdminRequest(user, AdminAction.trace));
}).padRight(12).size(bs + 10f, bs);
}
content.add(button).padBottom(-6).width(350f).maxHeight(h + 14);
content.row();
}
content.addImage("blank").height(3f).color(state.rules.pvp ? user.getTeam().color : Pal.accent).growX();
content.row();
});
content.marginBottom(5);
}
public void toggle(){
visible = !visible;
if(visible){
rebuild();
}
}
}

View File

@@ -1,70 +0,0 @@
package io.anuke.mindustry.ui.fragments;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.utils.Align;
import static io.anuke.mindustry.Vars.*;
import io.anuke.mindustry.core.GameState.State;
import io.anuke.mindustry.input.InputHandler;
import io.anuke.mindustry.input.PlaceMode;
import io.anuke.ucore.core.Core;
import io.anuke.ucore.core.Graphics;
import io.anuke.ucore.scene.ui.layout.Table;
public class ToolFragment implements Fragment{
private Table tools;
public int px, py, px2, py2;
public boolean confirming;
public void build(){
InputHandler input = control.input();
float isize = 14*3;
tools = new Table();
tools.addImageButton("icon-cancel", isize, () -> {
if(input.placeMode == PlaceMode.areaDelete && confirming){
confirming = false;
}else{
input.recipe = null;
}
});
tools.addImageButton("icon-rotate", isize, () -> {
input.rotation++;
input.rotation %= 4;
});
tools.addImageButton("icon-check", isize, () -> {
if(input.placeMode == PlaceMode.areaDelete && confirming){
input.placeMode.released(px, py, px2, py2);
confirming = false;
}else{
input.placeMode.tapped(control.input().getBlockX(), control.input().getBlockY());
}
});
Core.scene.add(tools);
tools.setVisible(() ->
!state.is(State.menu) && mobile && ((input.recipe != null && state.inventory.hasItems(input.recipe.requirements) &&
input.placeMode == PlaceMode.cursor) || confirming)
);
tools.update(() -> {
if(confirming){
Vector2 v = Graphics.screen((px + px2)/2f * tilesize, Math.min(py, py2) * tilesize - tilesize*1.5f);
tools.setPosition(v.x, v.y, Align.top);
}else{
tools.setPosition(control.input().getCursorX(),
Gdx.graphics.getHeight() - control.input().getCursorY() - 15*Core.cameraScale, Align.top);
}
if(input.placeMode != PlaceMode.areaDelete){
confirming = false;
}
});
}
}