Merge branch 'master' into mod-dependencies

This commit is contained in:
MEEPofFaith
2024-08-19 02:09:41 -07:00
195 changed files with 6964 additions and 4228 deletions

View File

@@ -20,27 +20,21 @@ import arc.struct.*;
import arc.util.*;
import mindustry.*;
import mindustry.core.*;
import mindustry.ctype.*;
import mindustry.game.*;
import mindustry.gen.*;
import java.util.*;
import java.io.*;
public class Fonts{
private static final String mainFont = "fonts/font.woff";
private static final ObjectSet<String> unscaled = ObjectSet.with("iconLarge");
private static ObjectIntMap<String> unicodeIcons = new ObjectIntMap<>();
private static IntMap<String> unicodeToName = new IntMap<>();
private static ObjectMap<String, String> stringIcons = new ObjectMap<>();
private static ObjectMap<String, TextureRegion> largeIcons = new ObjectMap<>();
private static TextureRegion[] iconTable;
private static int lastCid;
public static Font def, outline, icon, iconLarge, tech, logic;
public static TextureRegion logicIcon(int id){
return iconTable[id];
}
public static int getUnicode(String content){
return unicodeIcons.get(content, 0);
}
@@ -95,12 +89,16 @@ public class Fonts{
}})).loaded = f -> Fonts.logic = f;
}
public static @Nullable String unicodeToName(int unicode){
return unicodeToName.get(unicode, () -> Iconc.codeToName.get(unicode));
}
public static TextureRegion getLargeIcon(String name){
return largeIcons.get(name, () -> {
var region = new TextureRegion();
int code = Iconc.codes.get(name, '\uF8D4');
var glyph = iconLarge.getData().getGlyph((char)code);
if(glyph == null) return Core.atlas.find("error");
if(glyph == null) return Core.atlas.find(name);
region.set(iconLarge.getRegion().texture);
region.set(glyph.u, glyph.v2, glyph.u2, glyph.v);
return region;
@@ -112,9 +110,9 @@ public class Fonts{
Texture uitex = Core.atlas.find("logo").texture;
int size = (int)(Fonts.def.getData().lineHeight/Fonts.def.getData().scaleY);
try(Scanner scan = new Scanner(Core.files.internal("icons/icons.properties").read(512))){
while(scan.hasNextLine()){
String line = scan.nextLine();
try(var reader = Core.files.internal("icons/icons.properties").reader(Vars.bufferSize)){
String line;
while((line = reader.readLine()) != null){
String[] split = line.split("=");
String[] nametex = split[1].split("\\|");
String character = split[0], texture = nametex[1];
@@ -127,6 +125,7 @@ public class Fonts{
unicodeIcons.put(nametex[0], ch);
stringIcons.put(nametex[0], ((char)ch) + "");
unicodeToName.put(ch, texture);
Vec2 out = Scaling.fit.apply(region.width, region.height, size, size);
@@ -148,32 +147,21 @@ public class Fonts{
glyph.page = 0;
fonts.each(f -> f.getData().setGlyph(ch, glyph));
}
}catch(IOException e){
throw new RuntimeException(e);
}
stringIcons.put("alphachan", stringIcons.get("alphaaaa"));
iconTable = new TextureRegion[512];
iconTable[0] = Core.atlas.find("error");
lastCid = 1;
Vars.content.each(c -> {
if(c instanceof UnlockableContent u){
TextureRegion region = Core.atlas.find(u.name + "-icon-logic");
if(region.found()){
iconTable[u.iconId = lastCid++] = region;
}
}
});
for(Team team : Team.baseTeams){
team.emoji = stringIcons.get(team.name, "");
}
}
public static void loadContentIconsHeadless(){
try(Scanner scan = new Scanner(Core.files.internal("icons/icons.properties").read(512))){
while(scan.hasNextLine()){
String line = scan.nextLine();
try(var reader = Core.files.internal("icons/icons.properties").reader(Vars.bufferSize)){
String line;
while((line = reader.readLine()) != null){
String[] split = line.split("=");
String[] nametex = split[1].split("\\|");
String character = split[0];
@@ -182,6 +170,8 @@ public class Fonts{
unicodeIcons.put(nametex[0], ch);
stringIcons.put(nametex[0], ((char)ch) + "");
}
}catch(IOException e){
throw new RuntimeException(e);
}
stringIcons.put("alphachan", stringIcons.get("alphaaaa"));

View File

@@ -3,9 +3,11 @@ package mindustry.ui;
import arc.*;
import arc.graphics.g2d.*;
import arc.input.*;
import arc.math.*;
import arc.scene.*;
import arc.scene.event.*;
import arc.scene.ui.layout.*;
import arc.util.*;
import mindustry.gen.*;
import static mindustry.Vars.*;
@@ -20,6 +22,22 @@ public class Minimap extends Table{
add(new Element(){
{
setSize(Scl.scl(140f));
addListener(new ClickListener(KeyCode.mouseRight){
@Override
public void clicked(InputEvent event, float cx, float cy){
var region = renderer.minimap.getRegion();
if(region == null) return;
float
sx = (cx - x) / width,
sy = (cy - y) / height,
scaledX = Mathf.lerp(region.u, region.u2, sx) * world.width() * tilesize,
scaledY = Mathf.lerp(1f - region.v2, 1f - region.v, sy) * world.height() * tilesize;
control.input.panCamera(Tmp.v1.set(scaledX, scaledY));
}
});
}
@Override
@@ -93,7 +111,7 @@ public class Minimap extends Table{
update(() -> {
Element e = Core.scene.hit(Core.input.mouseX(), Core.input.mouseY(), true);
Element e = Core.scene.getHoverElement();
if(e != null && e.isDescendantOf(this)){
requestScroll();
}else if(hasScroll()){

View File

@@ -73,6 +73,8 @@ public class Styles{
geni,
/** Gray, toggleable, no background. */
grayi,
/** Gray square background, standard behavior. Equivalent to grayt. */
graySquarei,
/** Flat, square, black background. */
flati,
/** Square border. */
@@ -288,6 +290,14 @@ public class Styles{
imageUpColor = Color.lightGray;
imageDownColor = Color.white;
}};
graySquarei = new ImageButtonStyle(){{
imageUpColor = Color.white;
imageDownColor = Color.lightGray;
over = flatOver;
down = flatOver;
up = grayPanel;
}};
flati = new ImageButtonStyle(){{
down = flatOver;
up = black;

View File

@@ -30,16 +30,24 @@ public class CustomRulesDialog extends BaseDialog{
private Table main;
private Prov<Rules> resetter;
private LoadoutDialog loadoutDialog;
public boolean showRuleEditRule;
public Seq<Table> categories;
public Table current;
public Seq<String> categoryNames;
public String currentName;
public String currentName = "";
public String ruleSearch = "";
public Seq<Runnable> additionalSetup; // for modding to easily add new rules
public CustomRulesDialog(){
this(false);
}
public CustomRulesDialog(boolean showRuleEditRule){
super("@mode.custom");
this.showRuleEditRule = showRuleEditRule;
loadoutDialog = new LoadoutDialog();
setFillParent(true);
@@ -49,8 +57,6 @@ public class CustomRulesDialog extends BaseDialog{
additionalSetup = new Seq<>();
categories = new Seq<>();
categoryNames = new Seq<>();
currentName = "";
ruleSearch = "";
buttons.button("@edit", Icon.pencil, () -> {
BaseDialog dialog = new BaseDialog("@waves.edit");
@@ -209,12 +215,12 @@ public class CustomRulesDialog extends BaseDialog{
main.left().defaults().fillX().left();
main.row();
category("waves");
check("@rules.waves", b -> rules.waves = b, () -> rules.waves);
check("@rules.wavesending", b -> rules.waveSending = b, () -> rules.waveSending, () -> rules.waves);
check("@rules.wavetimer", b -> rules.waveTimer = b, () -> rules.waveTimer, () -> rules.waves);
check("@rules.waitForWaveToEnd", b -> rules.waitEnemies = b, () -> rules.waitEnemies, () -> rules.waves && rules.waveTimer);
check("@rules.airUseSpawns", b -> rules.airUseSpawns = b, () -> rules.airUseSpawns, () -> rules.waves);
numberi("@rules.wavelimit", f -> rules.winWave = f, () -> rules.winWave, () -> rules.waves, 0, Integer.MAX_VALUE);
number("@rules.wavespacing", false, f -> rules.waveSpacing = f * 60f, () -> rules.waveSpacing / 60f, () -> rules.waves && rules.waveTimer, 1, Float.MAX_VALUE);
//this is experimental, because it's not clear that 0 makes it default.
@@ -351,6 +357,10 @@ public class CustomRulesDialog extends BaseDialog{
category("teams");
//not sure where else to put this
if(showRuleEditRule){
check("@rules.allowedit", b -> rules.allowEditRules = b, () -> rules.allowEditRules);
}
team("@rules.playerteam", t -> rules.defaultTeam = t, () -> rules.defaultTeam);
team("@rules.enemyteam", t -> rules.waveTeam = t, () -> rules.waveTeam);

View File

@@ -45,7 +45,7 @@ public class DatabaseDialog extends BaseDialog{
void rebuild(){
all.clear();
var text = search.getText();
var text = search.getText().toLowerCase();
Seq<Content>[] allContent = Vars.content.getContentMap();
@@ -54,7 +54,7 @@ public class DatabaseDialog extends BaseDialog{
Seq<UnlockableContent> array = allContent[j]
.select(c -> c instanceof UnlockableContent u && !u.isHidden() &&
(text.isEmpty() || u.localizedName.toLowerCase().contains(text.toLowerCase()))).as();
(text.isEmpty() || u.localizedName.toLowerCase().contains(text))).as();
if(array.size == 0) continue;
all.add("@content." + type.name() + ".name").growX().left().color(Pal.accent);

View File

@@ -0,0 +1,74 @@
package mindustry.ui.dialogs;
import arc.*;
import arc.func.*;
import arc.scene.style.*;
import arc.scene.ui.*;
import arc.scene.ui.layout.*;
import arc.util.*;
import mindustry.ctype.*;
import mindustry.gen.*;
import mindustry.graphics.*;
import mindustry.ui.*;
import static mindustry.Vars.*;
public class IconSelectDialog extends Dialog{
private Intc consumer = i -> Log.info("you have mere seconds");
public IconSelectDialog(){
closeOnBack();
setFillParent(true);
cont.pane(t -> {
resized(true, () -> {
t.clearChildren();
t.marginRight(19f);
t.defaults().size(48f);
t.button(Icon.none, Styles.flati, () -> {
hide();
consumer.get(0);
});
int cols = (int)Math.min(20, Core.graphics.getWidth() / Scl.scl(52f));
int i = 1;
for(var key : accessibleIcons){
var value = Icon.icons.get(key);
t.button(value, Styles.flati, () -> {
hide();
consumer.get(Iconc.codes.get(key));
});
if(++i % cols == 0) t.row();
}
for(ContentType ctype : defaultContentIcons){
t.row();
t.image().colspan(cols).growX().width(Float.NEGATIVE_INFINITY).height(3f).color(Pal.accent);
t.row();
i = 0;
for(UnlockableContent u : content.getBy(ctype).<UnlockableContent>as()){
if(!u.isHidden() && u.unlocked()){
t.button(new TextureRegionDrawable(u.uiIcon), Styles.flati, iconMed, () -> {
hide();
consumer.get(u.emojiChar());
});
if(++i % cols == 0) t.row();
}
}
}
});
});
buttons.button("@back", Icon.left, this::hide).size(210f, 64f);
}
public void show(Intc listener){
consumer = listener;
super.show();
}
}

View File

@@ -213,7 +213,6 @@ public class KeybindDialog extends Dialog{
@Override
public boolean keyDown(InputEvent event, KeyCode keycode){
rebindDialog.hide();
if(keycode == KeyCode.escape) return false;
rebind(section, name, keycode);
return false;
}

View File

@@ -14,7 +14,7 @@ import static mindustry.Vars.*;
public class MapPlayDialog extends BaseDialog{
public @Nullable Runnable playListener;
CustomRulesDialog dialog = new CustomRulesDialog();
CustomRulesDialog dialog = new CustomRulesDialog(true);
Rules rules;
Gamemode selectedGamemode = Gamemode.survival;
Map lastMap;
@@ -102,7 +102,7 @@ public class MapPlayDialog extends BaseDialog{
ScrollPane pane = new ScrollPane(table);
pane.setFadeScrollBars(false);
table.row();
for(Gamemode mode : Gamemode.values()){
for(Gamemode mode : Gamemode.all){
if(mode.hidden) continue;
table.labelWrap("[accent]" + mode + ":[] [lightgray]" + mode.description()).width(400f);
table.row();

View File

@@ -1,19 +1,40 @@
package mindustry.ui.dialogs;
import arc.*;
import arc.scene.ui.layout.*;
import mindustry.*;
import mindustry.editor.*;
import mindustry.game.*;
import mindustry.gen.*;
import static mindustry.Vars.*;
public class PausedDialog extends BaseDialog{
private MapProcessorsDialog processors = new MapProcessorsDialog();
private SaveDialog save = new SaveDialog();
private LoadDialog load = new LoadDialog();
private boolean wasClient = false;
private CustomRulesDialog rulesDialog = new CustomRulesDialog();
public PausedDialog(){
super("@menu");
shouldPause = true;
clearChildren();
add(titleTable).growX().row();
stack(cont, new Table(t -> {
t.bottom().left();
t.button(Icon.book, () -> {
Rules toEdit = Vars.state.rules.copy();
rulesDialog.show(toEdit, () -> state.rules.copy());
rulesDialog.hidden(() -> {
//apply rule changes only once it is hidden
Vars.state.rules = toEdit;
Call.setRules(toEdit);
});
}).size(70f).tooltip("@customize").visible(() -> state.rules.allowEditRules && (net.server() || !net.active()));
})).grow().row();
shown(this::rebuild);
addCloseListener();
@@ -49,13 +70,22 @@ public class PausedDialog extends BaseDialog{
cont.row();
cont.button("@hostserver", Icon.host, () -> {
//the button runs out of space when the editor button is added, so use the mobile text
cont.button(state.isEditor() ? "@hostserver.mobile" : "@hostserver", Icon.host, () -> {
if(net.server() && steam){
platform.inviteFriends();
}else{
ui.host.show();
}
}).disabled(b -> !((steam && net.server()) || !net.active())).colspan(2).width(dw * 2 + 10f).update(e -> e.setText(net.server() && steam ? "@invitefriends" : "@hostserver"));
}).disabled(b -> !((steam && net.server()) || !net.active())).colspan(state.isEditor() ? 1 : 2).width(state.isEditor() ? dw : dw * 2 + 10f)
.update(e -> e.setText(net.server() && steam ? "@invitefriends" : state.isEditor() ? "@hostserver.mobile" : "@hostserver"));
if(state.isEditor()){
cont.button("@editor.worldprocessors", Icon.logic, () -> {
hide();
processors.show();
});
}
cont.row();
@@ -119,7 +149,7 @@ public class PausedDialog extends BaseDialog{
}
public void runExitSave(){
wasClient = net.client();
boolean wasClient = net.client();
if(net.client()) netClient.disconnectQuietly();
if(state.isEditor() && !wasClient){

View File

@@ -43,13 +43,6 @@ import static mindustry.graphics.g3d.PlanetRenderer.*;
import static mindustry.ui.dialogs.PlanetDialog.Mode.*;
public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
static final String[] defaultIcons = {
"effect", "power", "logic", "units", "liquid", "production", "defense", "turret", "distribution", "crafting",
"settings", "cancel", "zoom", "ok", "star", "home", "pencil", "up", "down", "left", "right",
"hammer", "warning", "tree", "admin", "map", "modePvp", "terrain",
"modeSurvival", "commandRally", "commandAttack",
};
//if true, enables launching anywhere for testing
public static boolean debugSelect = false;
public static float sectorShowDuration = 60f * 2.4f;
@@ -594,7 +587,7 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
@Override
public void act(float delta){
if(scene.getDialog() == PlanetDialog.this && !scene.hit(input.mouseX(), input.mouseY(), true).isDescendantOf(e -> e instanceof ScrollPane)){
if(scene.getDialog() == PlanetDialog.this && (scene.getHoverElement() == null || !scene.getHoverElement().isDescendantOf(e -> e instanceof ScrollPane))){
scene.setScrollFocus(PlanetDialog.this);
}
@@ -998,6 +991,7 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
sector.save = null;
}
updateSelected();
rebuildList();
});
}
@@ -1053,6 +1047,7 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
Icon.icons.get(sector.info.icon + "Small");
title.button(icon == null ? Icon.noneSmall : icon, Styles.clearNonei, iconSmall, () -> {
//TODO use IconSelectDialog
new Dialog(""){{
closeOnBack();
setFillParent(true);
@@ -1079,7 +1074,7 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
int cols = (int)Math.min(20, Core.graphics.getWidth() / Scl.scl(52f));
int i = 1;
for(var key : defaultIcons){
for(var key : accessibleIcons){
var value = Icon.icons.get(key);
t.button(value, Styles.squareTogglei, () -> {

View File

@@ -472,7 +472,7 @@ public class ResearchDialog extends BaseDialog{
if(mobile){
tapped(() -> {
Element e = Core.scene.hit(Core.input.mouseX(), Core.input.mouseY(), true);
Element e = Core.scene.getHoverElement();
if(e == this){
hoverNode = null;
rebuild();

View File

@@ -398,6 +398,7 @@ public class SchematicsDialog extends BaseDialog{
closeOnBack();
setFillParent(true);
//TODO: use IconSelectDialog
cont.pane(t -> {
resized(true, () -> {
t.clearChildren();
@@ -407,7 +408,7 @@ public class SchematicsDialog extends BaseDialog{
int cols = (int)Math.min(20, Core.graphics.getWidth() / Scl.scl(52f));
int i = 0;
for(String icon : PlanetDialog.defaultIcons){
for(String icon : accessibleIcons){
String out = (char)Iconc.codes.get(icon) + "";
if(tags.contains(out)) continue;

View File

@@ -297,6 +297,7 @@ public class SettingsMenuDialog extends BaseDialog{
}
void addSettings(){
sound.checkPref("alwaysmusic", false);
sound.sliderPref("musicvol", 100, 0, 100, 1, i -> i + "%");
sound.sliderPref("sfxvol", 100, 0, 100, 1, i -> i + "%");
sound.sliderPref("ambientvol", 100, 0, 100, 1, i -> i + "%");

View File

@@ -67,7 +67,7 @@ public class BlockConfigFragment{
}
public boolean hasConfigMouse(){
Element e = Core.scene.hit(Core.input.mouseX(), Core.graphics.getHeight() - Core.input.mouseY(), true);
Element e = Core.scene.getHoverElement();
return e != null && (e == table || e.isDescendantOf(table));
}

View File

@@ -152,7 +152,7 @@ public class BlockInventoryFragment{
container.add(i);
Boolp canPick = () -> player.unit().acceptsItem(item) && !state.isPaused() && player.within(build, itemTransferRange);
Boolp canPick = () -> !player.dead() && player.unit().acceptsItem(item) && !state.isPaused() && player.within(build, itemTransferRange);
HandCursorListener l = new HandCursorListener();
l.enabled = canPick;

View File

@@ -167,7 +167,7 @@ public class HintsFragment{
zoom(visibleDesktop, () -> Core.input.axis(KeyCode.scroll) != 0),
breaking(() -> isTutorial.get() && state.rules.defaultTeam.data().getCount(Blocks.conveyor) > 5, () -> ui.hints.events.contains("break")),
desktopShoot(visibleDesktop, () -> isSerpulo() && Vars.state.enemies > 0, () -> player.shooting),
depositItems(() -> player.unit().hasItem(), () -> !player.unit().hasItem()),
depositItems(() -> !player.dead() && player.unit().hasItem(), () -> !player.dead() && !player.unit().hasItem()),
desktopPause(visibleDesktop, () -> isTutorial.get() && !Vars.net.active() && state.wave >= 2, () -> Core.input.keyTap(Binding.pause)),
unitControl(() -> isSerpulo() && state.rules.defaultTeam.data().units.size > 2 && !net.active() && !player.dead(), () -> !player.dead() && !player.unit().spawnedByCore),
unitSelectControl(() -> isSerpulo() && state.rules.defaultTeam.data().units.size > 3 && !net.active() && !player.dead(),
@@ -179,8 +179,8 @@ public class HintsFragment{
boost(visibleDesktop, () -> !player.dead() && player.unit().type.canBoost, () -> Core.input.keyDown(Binding.boost)),
blockInfo(() -> !(state.isCampaign() && state.rules.sector == SectorPresets.groundZero.sector && state.wave < 3), () -> ui.content.isShown()),
derelict(() -> ui.hints.events.contains("derelictmouse") && !isTutorial.get(), () -> ui.hints.events.contains("derelictbreak")),
payloadPickup(() -> isSerpulo() && !player.unit().dead && player.unit() instanceof Payloadc p && p.payloads().isEmpty(), () -> player.unit() instanceof Payloadc p && p.payloads().any()),
payloadDrop(() -> !player.unit().dead && player.unit() instanceof Payloadc p && p.payloads().any(), () -> player.unit() instanceof Payloadc p && p.payloads().isEmpty()),
payloadPickup(() -> isSerpulo() && !player.dead() && player.unit() instanceof Payloadc p && p.payloads().isEmpty(), () -> player.unit() instanceof Payloadc p && p.payloads().any()),
payloadDrop(() -> !player.dead() && player.unit() instanceof Payloadc p && p.payloads().any(), () -> player.unit() instanceof Payloadc p && p.payloads().isEmpty()),
waveFire(() -> Groups.fire.size() > 0 && Blocks.wave.unlockedNow(), () -> indexer.getFlagged(state.rules.defaultTeam, BlockFlag.extinguisher).size > 0),
generator(() -> control.input.block == Blocks.combustionGenerator, () -> ui.hints.placedBlocks.contains(Blocks.combustionGenerator)),
rebuildSelect(() -> state.rules.defaultTeam.data().plans.size >= 10, () -> control.input.isRebuildSelecting()),

View File

@@ -767,7 +767,7 @@ public class HudFragment{
}
});
t.add(new SideBar(() -> player.unit().healthf(), () -> true, true)).width(bw).growY().padRight(pad);
t.add(new SideBar(() -> player.dead() ? 0f : player.unit().healthf(), () -> true, true)).width(bw).growY().padRight(pad);
t.image(() -> player.icon()).scaling(Scaling.bounded).grow().maxWidth(54f);
t.add(new SideBar(() -> player.dead() ? 0f : player.displayAmmo() ? player.unit().ammof() : player.unit().healthf(), () -> !player.displayAmmo(), false)).width(bw).growY().padLeft(pad).update(b -> {
b.color.set(player.displayAmmo() ? player.dead() || player.unit() instanceof BlockUnitc ? Pal.ammo : player.unit().type.ammoType.color() : Pal.health);
@@ -913,7 +913,7 @@ public class HudFragment{
table.table().update(t -> {
t.left();
Bits applied = player.unit().statusBits();
Bits applied = player.dead() ? null : player.unit().statusBits();
if(!statuses.equals(applied)){
t.clear();

View File

@@ -615,7 +615,7 @@ public class PlacementFragment{
blocksSelect.margin(4).marginTop(0);
blockPane = blocksSelect.pane(blocks -> blockTable = blocks).height(194f).update(pane -> {
if(pane.hasScroll()){
Element result = Core.scene.hit(Core.input.mouseX(), Core.input.mouseY(), true);
Element result = Core.scene.getHoverElement();
if(result == null || !result.isDescendantOf(pane)){
Core.scene.setScrollFocus(null);
}