Merge remote-tracking branch 'upstream/master'
This commit is contained in:
@@ -146,7 +146,7 @@ public class SoundControl{
|
||||
if(state.isMenu()){
|
||||
silenced = false;
|
||||
if(ui.planet.isShown()){
|
||||
play(Musics.launch);
|
||||
play(ui.planet.state.planet.launchMusic);
|
||||
}else if(ui.editor.isShown()){
|
||||
play(Musics.editor);
|
||||
}else{
|
||||
|
||||
@@ -332,6 +332,13 @@ public class Control implements ApplicationListener, Loadable{
|
||||
void createPlayer(){
|
||||
player = Player.create();
|
||||
player.name = Core.settings.getString("name");
|
||||
|
||||
String locale = Core.settings.getString("locale");
|
||||
if(locale.equals("default")){
|
||||
locale = Locale.getDefault().toString();
|
||||
}
|
||||
player.locale = locale;
|
||||
|
||||
player.color.set(Core.settings.getInt("color-0"));
|
||||
|
||||
if(mobile){
|
||||
|
||||
@@ -36,6 +36,8 @@ public class GameState{
|
||||
public GameStats stats = new GameStats();
|
||||
/** Markers not linked to objectives. Controlled by world processors. */
|
||||
public IntMap<ObjectiveMarker> markers = new IntMap<>();
|
||||
/** Locale-specific string bundles of current map */
|
||||
public MapLocales mapLocales = new MapLocales();
|
||||
/** Global attributes of the environment, calculated by weather. */
|
||||
public Attributes envAttrs = new Attributes();
|
||||
/** Team data. Gets reset every new game. */
|
||||
|
||||
@@ -24,6 +24,7 @@ import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.io.*;
|
||||
import mindustry.maps.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.ui.*;
|
||||
import mindustry.ui.dialogs.*;
|
||||
import mindustry.world.*;
|
||||
|
||||
@@ -7,6 +7,7 @@ import mindustry.game.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.io.*;
|
||||
import mindustry.maps.filters.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.ui.*;
|
||||
import mindustry.ui.dialogs.*;
|
||||
|
||||
@@ -17,6 +18,7 @@ public class MapInfoDialog extends BaseDialog{
|
||||
private final MapGenerateDialog generate;
|
||||
private final CustomRulesDialog ruleInfo = new CustomRulesDialog();
|
||||
private final MapObjectivesDialog objectives = new MapObjectivesDialog();
|
||||
private final MapLocalesDialog locales = new MapLocalesDialog();
|
||||
|
||||
public MapInfoDialog(){
|
||||
super("@editor.mapinfo");
|
||||
@@ -94,6 +96,19 @@ public class MapInfoDialog extends BaseDialog{
|
||||
});
|
||||
hide();
|
||||
}).marginLeft(10f);
|
||||
|
||||
r.row();
|
||||
|
||||
r.button("@editor.locales", Icon.fileText, style, () -> {
|
||||
try{
|
||||
MapLocales res = JsonIO.read(MapLocales.class, editor.tags.get("locales", "{}"));
|
||||
locales.show(res);
|
||||
}catch(Throwable e){
|
||||
locales.show(new MapLocales());
|
||||
ui.showException(e);
|
||||
}
|
||||
hide();
|
||||
}).marginLeft(10f).width(0f).colspan(2).center().growX();
|
||||
}).colspan(2).center();
|
||||
|
||||
name.change();
|
||||
|
||||
758
core/src/mindustry/editor/MapLocalesDialog.java
Normal file
758
core/src/mindustry/editor/MapLocalesDialog.java
Normal file
@@ -0,0 +1,758 @@
|
||||
package mindustry.editor;
|
||||
|
||||
import arc.Core;
|
||||
import arc.func.*;
|
||||
import arc.graphics.*;
|
||||
import arc.scene.style.*;
|
||||
import arc.scene.ui.*;
|
||||
import arc.scene.ui.layout.*;
|
||||
import arc.scene.utils.*;
|
||||
import arc.struct.*;
|
||||
import mindustry.*;
|
||||
import mindustry.ctype.*;
|
||||
import mindustry.game.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.io.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.ui.*;
|
||||
import mindustry.ui.dialogs.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class MapLocalesDialog extends BaseDialog{
|
||||
/** Width of UI property card. */
|
||||
private static final float cardWidth = 400f;
|
||||
/** Icons for use in map locales dialog. */
|
||||
private static final ContentType[] contentIcons = {ContentType.item, ContentType.block, ContentType.liquid, ContentType.status, ContentType.unit};
|
||||
|
||||
private MapLocales locales;
|
||||
private MapLocales lastSaved;
|
||||
private boolean saved = true;
|
||||
private Table langs;
|
||||
private Table main;
|
||||
private Table propView;
|
||||
private String selectedLocale;
|
||||
|
||||
private boolean applytoall = true;
|
||||
private boolean collapsed = false;
|
||||
private String searchString = "";
|
||||
private boolean searchByValue = false;
|
||||
private boolean showCorrect = true;
|
||||
private boolean showMissing = true;
|
||||
private boolean showSame = true;
|
||||
|
||||
public MapLocalesDialog(){
|
||||
super("@editor.locales");
|
||||
|
||||
selectedLocale = MapLocales.currentLocale();
|
||||
|
||||
langs = new Table(Tex.button);
|
||||
main = new Table();
|
||||
propView = new Table();
|
||||
|
||||
buttons.add("").uniform();
|
||||
|
||||
buttons.table(t -> {
|
||||
t.defaults().pad(3).center();
|
||||
|
||||
t.button("@back", Icon.left, () -> {
|
||||
if(!saved) ui.showConfirm("@editor.locales", "@editor.savechanges", () -> {
|
||||
editor.tags.put("locales", JsonIO.write(locales));
|
||||
state.mapLocales = locales;
|
||||
});
|
||||
hide();
|
||||
}).size(210f, 64f);
|
||||
closeOnBack(() -> {
|
||||
if(!saved) ui.showConfirm("@editor.locales", "@editor.savechanges", () -> {
|
||||
editor.tags.put("locales", JsonIO.write(locales));
|
||||
state.mapLocales = locales;
|
||||
});
|
||||
});
|
||||
|
||||
t.button("@editor.apply", Icon.ok, () -> {
|
||||
editor.tags.put("locales", JsonIO.write(locales));
|
||||
state.mapLocales = locales;
|
||||
lastSaved = locales.copy();
|
||||
saved = true;
|
||||
}).size(210f, 64f).disabled(b -> saved);
|
||||
|
||||
t.button("@edit", Icon.edit, this::editDialog).size(210f, 64f);
|
||||
}).growX();
|
||||
|
||||
buttons.button("?", () -> ui.showInfo("@locales.info")).size(60f, 64f).uniform();
|
||||
|
||||
shown(this::setup);
|
||||
}
|
||||
|
||||
public void show(MapLocales locales){
|
||||
this.locales = locales;
|
||||
lastSaved = locales.copy();
|
||||
saved = true;
|
||||
show();
|
||||
}
|
||||
|
||||
private void setup(){
|
||||
cont.clear();
|
||||
|
||||
buildTables();
|
||||
|
||||
cont.add(langs).left();
|
||||
|
||||
cont.table(t -> {
|
||||
// search/collapse all/filter
|
||||
t.table(a -> {
|
||||
a.button(Icon.downOpen, Styles.emptyTogglei, () -> {
|
||||
collapsed = !collapsed;
|
||||
buildMain();
|
||||
}).update(b -> {
|
||||
b.replaceImage(new Image(collapsed ? Icon.upOpen : Icon.downOpen));
|
||||
b.setChecked(collapsed);
|
||||
}).size(35f);
|
||||
|
||||
a.button(Icon.filter, Styles.emptyi, () -> filterDialog(this::buildMain)).padLeft(10f).size(35f);
|
||||
|
||||
var field = a.field("", v -> {
|
||||
searchString = v;
|
||||
buildMain();
|
||||
}).update(f -> f.setText(searchString)).maxTextLength(64).padLeft(10f).width(250f).update(f -> f.setMessageText(searchByValue ? "@locales.searchvalue": "@locales.searchname")).get();
|
||||
|
||||
a.button(Icon.cancel, Styles.emptyi, () -> {
|
||||
searchString = "";
|
||||
field.setText("");
|
||||
buildMain();
|
||||
}).padLeft(10f).size(35f);
|
||||
}).row();
|
||||
|
||||
t.check("@locales.applytoall", applytoall, b -> applytoall = b).pad(10f).row();
|
||||
|
||||
t.add(main).center().grow().row();
|
||||
}).pad(10f).grow();
|
||||
|
||||
// property addition
|
||||
cont.table(Tex.button, t -> {
|
||||
TextField name = t.field("name", s -> {}).maxTextLength(64).fillX().padTop(10f).get();
|
||||
t.row();
|
||||
TextField value = t.area("text", s -> {}).maxTextLength(1000).fillX().height(140f).get();
|
||||
t.row();
|
||||
|
||||
t.button("@add", Icon.add, () -> {
|
||||
if(applytoall){
|
||||
for(var locale : locales.values()){
|
||||
locale.put(name.getText(), value.getText());
|
||||
}
|
||||
}else{
|
||||
locales.get(selectedLocale).put(name.getText(), value.getText());
|
||||
}
|
||||
|
||||
saved = false;
|
||||
buildMain();
|
||||
}).padTop(10f).size(400f, 50f).fillX().row();
|
||||
}).right();
|
||||
}
|
||||
|
||||
private void buildTables(){
|
||||
if(!locales.containsKey(selectedLocale)){
|
||||
locales.put(selectedLocale, new StringMap());
|
||||
}
|
||||
|
||||
buildLocalesTable();
|
||||
buildMain();
|
||||
}
|
||||
|
||||
private void buildLocalesTable(){
|
||||
langs.clear();
|
||||
|
||||
langs.pane(p -> {
|
||||
for(var loc : Vars.locales){
|
||||
String name = loc.toString();
|
||||
|
||||
if(locales.containsKey(name)){
|
||||
p.button(loc.getDisplayName(), Styles.flatTogglet, () -> {
|
||||
if(name.equals(selectedLocale)) return;
|
||||
|
||||
selectedLocale = name;
|
||||
buildTables();
|
||||
}).update(b -> b.setChecked(selectedLocale.equals(name))).size(300f, 50f);
|
||||
p.button(Icon.edit, Styles.flati, () -> localeEditDialog(name)).size(50f);
|
||||
p.button(Icon.trash, Styles.flati, () -> ui.showConfirm("@confirm", "@locales.deletelocale", () -> {
|
||||
locales.remove(name);
|
||||
|
||||
selectedLocale = (locales.size != 0 ? locales.keys().next() : Core.settings.getString("locale"));
|
||||
saved = false;
|
||||
buildTables();
|
||||
})).size(50f).row();
|
||||
}
|
||||
}
|
||||
}).row();
|
||||
langs.button("@add", Icon.add, this::addLocaleDialog).padTop(10f).width(400f);
|
||||
}
|
||||
|
||||
private void buildMain(){
|
||||
main.clear();
|
||||
|
||||
StringMap props = locales.get(selectedLocale);
|
||||
|
||||
main.image().color(Pal.gray).height(3f).growX().expandY().top().row();
|
||||
main.pane(p -> {
|
||||
int cols = (Core.graphics.getWidth() - 380) / ((int)cardWidth + 10);
|
||||
if(props.size == 0 || cols == 0){
|
||||
main.add("@empty").center().row();
|
||||
return;
|
||||
}
|
||||
p.defaults().top();
|
||||
|
||||
Table[] colTables = new Table[cols];
|
||||
for(var i = 0; i < cols; i++){
|
||||
colTables[i] = new Table();
|
||||
}
|
||||
int i = 0;
|
||||
|
||||
// To sort properties in alphabetic order
|
||||
Seq<String> keys = props.keys().toSeq().sort();
|
||||
|
||||
for(var key : keys){
|
||||
var comparsionString = (searchByValue ? props.get(key).toLowerCase() : key.toLowerCase());
|
||||
if(!searchString.isEmpty() && !comparsionString.contains(searchString.toLowerCase())) continue;
|
||||
|
||||
PropertyStatus status = getPropertyStatus(key, props.get(key), selectedLocale, false);
|
||||
if(status == PropertyStatus.correct && !showCorrect) continue;
|
||||
if(status == PropertyStatus.missing && !showMissing) continue;
|
||||
if(status == PropertyStatus.same && !showSame) continue;
|
||||
|
||||
colTables[i].table(Tex.whitePane, t -> {
|
||||
boolean[] shown = {!collapsed};
|
||||
String[] propKey = {key};
|
||||
String[] propValue = {props.get(key)};
|
||||
|
||||
// collapse button
|
||||
t.button(Icon.downOpen, Styles.emptyTogglei, () -> shown[0] = !shown[0]).update(b -> {
|
||||
b.replaceImage(new Image(shown[0] ? Icon.upOpen : Icon.downOpen));
|
||||
b.setChecked(shown[0]);
|
||||
}).size(35f);
|
||||
|
||||
// property name field
|
||||
t.field(propKey[0], (f, c) -> c != '=' && c != ':', v -> {
|
||||
if(props.containsKey(v)){
|
||||
t.setColor(Color.valueOf("f25555"));
|
||||
return;
|
||||
}
|
||||
|
||||
if(applytoall){
|
||||
for(var bundle : locales.values()){
|
||||
if(!bundle.containsKey(v)){
|
||||
String value = bundle.get(propKey[0]);
|
||||
if(value == null) continue;
|
||||
|
||||
bundle.remove(propKey[0]);
|
||||
bundle.put(v, value);
|
||||
}
|
||||
}
|
||||
}else{
|
||||
if(!props.containsKey(v)){
|
||||
props.remove(propKey[0]);
|
||||
props.put(v, propValue[0]);
|
||||
}
|
||||
}
|
||||
|
||||
propKey[0] = v;
|
||||
updateCard(t, v, propValue[0]);
|
||||
saved = false;
|
||||
}).maxTextLength(64).width(cardWidth - 125f);
|
||||
|
||||
// remove button
|
||||
t.button(Icon.trash, Styles.emptyi, () -> {
|
||||
if(applytoall){
|
||||
for(var bundle : locales.values()){
|
||||
bundle.remove(propKey[0]);
|
||||
}
|
||||
}else{
|
||||
props.remove(propKey[0]);
|
||||
}
|
||||
saved = false;
|
||||
buildMain();
|
||||
}).size(35f);
|
||||
|
||||
// more actions
|
||||
t.button(Icon.edit, Styles.emptyi, () -> propEditDialog(t, propKey[0], propValue[0])).size(35f).row();
|
||||
|
||||
// property value area
|
||||
t.collapser(c -> c.area(propValue[0], v -> {
|
||||
props.put(propKey[0], v);
|
||||
updateCard(t, propKey[0], v);
|
||||
saved = false;
|
||||
}).maxTextLength(1000).height(140f).update(a -> {
|
||||
propValue[0] = props.get(propKey[0]);
|
||||
a.setText(props.get(propKey[0]));
|
||||
}).growX(), () -> shown[0]).colspan(4).growX();
|
||||
|
||||
updateCard(t, propKey[0], propValue[0]);
|
||||
}).top().width(cardWidth).pad(5f).row();
|
||||
|
||||
i = ++i % cols;
|
||||
}
|
||||
|
||||
if(!colTables[0].hasChildren()){
|
||||
main.add("@empty").center().row();
|
||||
}else{
|
||||
p.add(colTables);
|
||||
}
|
||||
}).growX().row();
|
||||
main.image().color(Pal.gray).height(3f).growX().expandY().bottom().row();
|
||||
}
|
||||
|
||||
private void updateCard(Table table, String propKey, String propValue){
|
||||
updateCard(table, propKey, propValue, selectedLocale, false);
|
||||
}
|
||||
|
||||
private void updateCard(Table table, String propKey, String propValue, String locale, boolean viewCard){
|
||||
switch(getPropertyStatus(propKey, propValue, locale, viewCard)){
|
||||
case missing -> table.setColor(Pal.accent);
|
||||
case same -> table.setColor(Pal.techBlue);
|
||||
case correct -> table.setColor(Pal.gray);
|
||||
}
|
||||
}
|
||||
|
||||
// Property statuses for main dialog and property view dialog are a bit different
|
||||
private PropertyStatus getPropertyStatus(String propKey, String propValue, String locale, boolean forView){
|
||||
if(forView && propValue == null) return PropertyStatus.missing;
|
||||
|
||||
for(var bundle : locales.entries()){
|
||||
if(!forView && bundle.key.equals(selectedLocale)) continue;
|
||||
if(forView && bundle.key.equals(locale)) continue;
|
||||
|
||||
StringMap props = bundle.value;
|
||||
|
||||
if(!props.containsKey(propKey)){
|
||||
if(!forView) return PropertyStatus.missing;
|
||||
}else{
|
||||
if(props.get(propKey).equals(propValue)){
|
||||
return PropertyStatus.same;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return PropertyStatus.correct;
|
||||
}
|
||||
|
||||
private void addLocaleDialog(){
|
||||
BaseDialog dialog = new BaseDialog("@add");
|
||||
|
||||
dialog.cont.pane(t -> {
|
||||
for(var loc : Vars.locales){
|
||||
String name = loc.toString();
|
||||
|
||||
if(!locales.containsKey(name)){
|
||||
t.button(loc.getDisplayName(), Styles.flatTogglet, () -> {
|
||||
if(name.equals(selectedLocale)) return;
|
||||
|
||||
locales.put(name, new StringMap());
|
||||
|
||||
selectedLocale = name;
|
||||
saved = false;
|
||||
buildTables();
|
||||
dialog.hide();
|
||||
}).update(b -> b.setChecked(selectedLocale.equals(name))).size(400f, 50f).row();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
dialog.addCloseButton();
|
||||
dialog.show();
|
||||
}
|
||||
|
||||
private void propEditDialog(Table card, String key, String value){
|
||||
BaseDialog dialog = new BaseDialog("@edit");
|
||||
|
||||
dialog.cont.pane(p -> {
|
||||
p.margin(10f);
|
||||
p.table(Tex.button, t -> {
|
||||
t.defaults().size(450f, 60f).left();
|
||||
|
||||
t.button("@locales.addtoother", Icon.add, Styles.flatt, () -> {
|
||||
for(var bundle : locales.values()){
|
||||
if(!bundle.containsKey(key)){
|
||||
bundle.put(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
saved = false;
|
||||
updateCard(card, key, value);
|
||||
dialog.hide();
|
||||
}).marginLeft(12f).row();
|
||||
|
||||
t.button("@locales.viewproperty", Icon.zoom, Styles.flatt, () -> {
|
||||
viewPropertyDialog(key);
|
||||
dialog.hide();
|
||||
}).marginLeft(12f).row();
|
||||
|
||||
t.button("@locales.addicon", Icon.image, Styles.flatt, () -> {
|
||||
addIconDialog(res -> {
|
||||
locales.get(selectedLocale).put(key, value + res);
|
||||
saved = false;
|
||||
});
|
||||
dialog.hide();
|
||||
}).marginLeft(12f).row();
|
||||
|
||||
t.button("@locales.rollback", Icon.undo, Styles.flatt, () -> {
|
||||
locales.get(selectedLocale).put(key, lastSaved.get(selectedLocale).get(key));
|
||||
buildTables();
|
||||
dialog.hide();
|
||||
}).disabled(b -> {
|
||||
if(!lastSaved.containsKey(selectedLocale)) return true;
|
||||
StringMap savedMap = lastSaved.get(selectedLocale);
|
||||
return !savedMap.containsKey(key) || savedMap.get(key).equals(locales.get(selectedLocale).get(key));
|
||||
}).marginLeft(12f).row();
|
||||
});
|
||||
});
|
||||
|
||||
dialog.addCloseButton();
|
||||
dialog.show();
|
||||
}
|
||||
|
||||
private void localeEditDialog(String locale){
|
||||
BaseDialog dialog = new BaseDialog("@edit");
|
||||
|
||||
dialog.cont.pane(p -> {
|
||||
p.margin(10f);
|
||||
p.table(Tex.button, t -> {
|
||||
t.defaults().size(350f, 60f).left();
|
||||
|
||||
t.button("@waves.copy", Icon.copy, Styles.flatt, () -> {
|
||||
Core.app.setClipboardText(writeLocale(locale));
|
||||
ui.showInfoFade("@copied");
|
||||
dialog.hide();
|
||||
}).marginLeft(12f).row();
|
||||
t.button("@waves.load", Icon.download, Styles.flatt, () -> {
|
||||
locales.put(locale, readLocale(Core.app.getClipboardText()));
|
||||
buildTables();
|
||||
saved = false;
|
||||
dialog.hide();
|
||||
}).disabled(Core.app.getClipboardText() == null).marginLeft(12f).row();
|
||||
});
|
||||
});
|
||||
|
||||
dialog.addCloseButton();
|
||||
dialog.show();
|
||||
}
|
||||
|
||||
private void editDialog(){
|
||||
BaseDialog dialog = new BaseDialog("@edit");
|
||||
|
||||
dialog.cont.pane(p -> {
|
||||
p.margin(10f);
|
||||
p.table(Tex.button, t -> {
|
||||
t.defaults().size(450f, 60f).left();
|
||||
|
||||
t.button("@waves.copy", Icon.copy, Styles.flatt, () -> {
|
||||
Core.app.setClipboardText(writeBundles());
|
||||
ui.showInfoFade("@copied");
|
||||
dialog.hide();
|
||||
}).marginLeft(12f).row();
|
||||
t.button("@waves.load", Icon.download, Styles.flatt, () -> {
|
||||
locales = readBundles(Core.app.getClipboardText());
|
||||
buildTables();
|
||||
saved = false;
|
||||
dialog.hide();
|
||||
}).disabled(Core.app.getClipboardText() == null).marginLeft(12f).row();
|
||||
t.button("@locales.rollback", Icon.undo, Styles.flatt, () -> {
|
||||
locales = lastSaved.copy();
|
||||
saved = true;
|
||||
buildTables();
|
||||
dialog.hide();
|
||||
}).disabled(b -> saved).marginLeft(12f).row();
|
||||
});
|
||||
});
|
||||
|
||||
dialog.addCloseButton();
|
||||
dialog.show();
|
||||
}
|
||||
|
||||
private void viewPropertyDialog(String key){
|
||||
BaseDialog dialog = new BaseDialog(Core.bundle.format("locales.viewing", key));
|
||||
|
||||
dialog.cont.table(t -> {
|
||||
t.button(Icon.filter, Styles.emptyi, () -> filterDialog(() -> buildPropView(key))).size(35f);
|
||||
|
||||
var field = t.field(searchString, v -> {
|
||||
searchString = v;
|
||||
buildPropView(key);
|
||||
}).update(f -> f.setText(searchString)).maxTextLength(64).padLeft(10f).width(250f).update(f -> f.setMessageText(searchByValue ? "@locales.searchvalue" : "@locales.searchlocale")).get();
|
||||
|
||||
t.button(Icon.cancel, Styles.emptyi, () -> {
|
||||
searchString = "";
|
||||
field.setText("");
|
||||
buildPropView(key);
|
||||
}).padLeft(10f).size(35f);
|
||||
}).row();
|
||||
|
||||
buildPropView(key);
|
||||
dialog.cont.add(propView).grow().center().row();
|
||||
|
||||
dialog.addCloseButton();
|
||||
dialog.closeOnBack();
|
||||
dialog.hidden(this::buildMain);
|
||||
|
||||
dialog.show();
|
||||
}
|
||||
|
||||
private void buildPropView(String key){
|
||||
propView.clear();
|
||||
|
||||
propView.image().color(Pal.gray).height(3f).fillX().top().row();
|
||||
propView.pane(p -> {
|
||||
int cols = (Core.graphics.getWidth() - 100) / ((int)cardWidth + 10);
|
||||
if(cols == 0){
|
||||
propView.add("@empty").center().row();
|
||||
return;
|
||||
}
|
||||
p.defaults().top();
|
||||
|
||||
Table[] colTables = new Table[cols];
|
||||
for(var i = 0; i < cols; i++){
|
||||
colTables[i] = new Table();
|
||||
}
|
||||
int i = 0;
|
||||
|
||||
for(var loc : Vars.locales){
|
||||
String name = loc.toString();
|
||||
if(!locales.containsKey(name)) continue;
|
||||
|
||||
PropertyStatus status = getPropertyStatus(key, locales.get(name).get(key), name, true);
|
||||
if(status == PropertyStatus.correct && !showCorrect) continue;
|
||||
if(status == PropertyStatus.missing && !showMissing) continue;
|
||||
if(status == PropertyStatus.same && !showSame) continue;
|
||||
|
||||
if(status != PropertyStatus.missing){
|
||||
var comparsionString = (searchByValue ? locales.get(name).get(key).toLowerCase() : loc.getDisplayName().toLowerCase());
|
||||
if(!searchString.isEmpty() && !comparsionString.contains(searchString.toLowerCase())) continue;
|
||||
}
|
||||
|
||||
colTables[i].table(Tex.whitePane, t -> {
|
||||
t.add(loc.getDisplayName()).left().color(Pal.accent).row();
|
||||
t.image().color(Pal.accent).fillX().row();
|
||||
|
||||
if(status == PropertyStatus.missing){
|
||||
t.table(b ->
|
||||
b.button("@add", Icon.add, () -> {
|
||||
locales.get(name).put(key, "moai");
|
||||
|
||||
t.getCells().get(2).clearElement();
|
||||
t.getCells().remove(2);
|
||||
|
||||
t.area(locales.get(name).get(key), v -> {
|
||||
locales.get(name).put(key, v);
|
||||
saved = false;
|
||||
}).maxTextLength(1000).height(140f).growX().row();
|
||||
}).size(160f, 50f)).height(140f).growX().row();
|
||||
}else{
|
||||
t.area(locales.get(name).get(key), v -> {
|
||||
locales.get(name).put(key, v);
|
||||
saved = false;
|
||||
}).maxTextLength(1000).height(140f).growX().row();
|
||||
}
|
||||
}).update(t -> updateCard(t, key, locales.get(name).get(key), name, true)).top().width(cardWidth).pad(5f).row();
|
||||
|
||||
i = ++i % cols;
|
||||
}
|
||||
|
||||
if(!colTables[0].hasChildren()){
|
||||
propView.add("@empty").center().row();
|
||||
}else{
|
||||
p.add(colTables);
|
||||
}
|
||||
}).grow().row();
|
||||
propView.image().color(Pal.gray).height(3f).fillX().bottom().row();
|
||||
}
|
||||
|
||||
private void filterDialog(Runnable hidden){
|
||||
BaseDialog dialog = new BaseDialog("@locales.filter");
|
||||
|
||||
dialog.cont.table(t -> {
|
||||
t.add("@search").row();
|
||||
t.table(b -> {
|
||||
b.button("@locales.byname", Styles.togglet, () -> searchByValue = false).size(300f, 50f).checked(v -> !searchByValue);
|
||||
b.button("@locales.byvalue", Styles.togglet, () -> searchByValue = true).padLeft(10f).size(300f, 50f).checked(v -> searchByValue);
|
||||
}).padTop(5f);
|
||||
}).row();
|
||||
|
||||
dialog.cont.table(Tex.whitePane, t ->
|
||||
t.button("@locales.showcorrect", Icon.ok, Styles.nonet, () -> showCorrect = !showCorrect).update(b -> {
|
||||
((Image)b.getChildren().get(1)).setDrawable(showCorrect ? Icon.ok : Icon.cancel);
|
||||
b.setChecked(showCorrect);
|
||||
}).grow().pad(15f)).size(450f, 100f).color(Pal.gray).padTop(50f);
|
||||
|
||||
dialog.cont.row();
|
||||
|
||||
dialog.cont.table(Tex.whitePane, t ->
|
||||
t.button("@locales.showmissing", Icon.ok, Styles.nonet, () -> showMissing = !showMissing).update(b -> {
|
||||
((Image)b.getChildren().get(1)).setDrawable(showMissing ? Icon.ok : Icon.cancel);
|
||||
b.setChecked(showMissing);
|
||||
}).grow().pad(15f)).size(450f, 100f).color(Pal.accent).padTop(50f);
|
||||
|
||||
dialog.cont.row();
|
||||
dialog.cont.table(Tex.whitePane, t ->
|
||||
t.button("@locales.showsame", Icon.ok, Styles.nonet, () -> showSame = !showSame).update(b -> {
|
||||
((Image)b.getChildren().get(1)).setDrawable(showSame ? Icon.ok : Icon.cancel);
|
||||
b.setChecked(showSame);
|
||||
}).grow().pad(15f)).size(450f, 100f).color(Pal.techBlue).padTop(50f);
|
||||
|
||||
dialog.buttons.button("@back", Icon.left, () -> {
|
||||
hidden.run();
|
||||
dialog.hide();
|
||||
}).size(210f, 64f);
|
||||
dialog.closeOnBack(hidden);
|
||||
|
||||
dialog.show();
|
||||
}
|
||||
|
||||
private void addIconDialog(Cons<String> cons){
|
||||
BaseDialog dialog = new BaseDialog("@locales.addicon");
|
||||
|
||||
Table icons = new Table();
|
||||
TextField search = Elem.newField("", v -> iconsTable(icons, v.replace(" ", "").toLowerCase(), dialog, cons));
|
||||
search.setMessageText("@search");
|
||||
|
||||
dialog.cont.table(t -> {
|
||||
t.add(search).maxTextLength(64).padLeft(10f).width(250f);
|
||||
|
||||
t.button(Icon.cancel, Styles.emptyi, () -> {
|
||||
search.setText("");
|
||||
iconsTable(icons, "", dialog, cons);
|
||||
}).padLeft(10f).size(35f);
|
||||
}).row();
|
||||
|
||||
dialog.cont.pane(icons).scrollX(false);
|
||||
dialog.resized(true, () -> iconsTable(icons, search.getText().replace(" ", "").toLowerCase(), dialog, cons));
|
||||
|
||||
dialog.addCloseButton();
|
||||
dialog.closeOnBack();
|
||||
dialog.setFillParent(true);
|
||||
dialog.show();
|
||||
}
|
||||
|
||||
private void iconsTable(Table table, String search, Dialog dialog, Cons<String> cons){
|
||||
table.clear();
|
||||
|
||||
table.marginRight(19f).marginLeft(12f);
|
||||
table.defaults().size(48f);
|
||||
|
||||
int cols = (int)Math.min(20, Core.graphics.getWidth() / Scl.scl(52f));
|
||||
|
||||
int i = 0;
|
||||
|
||||
var codes = new ObjectIntMap<>(Iconc.codes);
|
||||
|
||||
for(var name : codes.keys()){
|
||||
if(!name.toLowerCase().contains(search)) codes.remove(name);
|
||||
}
|
||||
|
||||
if(codes.size > 0) table.image().colspan(cols).growX().width(-1f).height(3f).color(Pal.accent).row();
|
||||
|
||||
for(var icon : codes){
|
||||
String res = (char)icon.value + "";
|
||||
|
||||
table.button(Icon.icons.get(icon.key), Styles.flati, iconMed, () -> {
|
||||
cons.get(res);
|
||||
dialog.hide();
|
||||
}).tooltip(icon.key);
|
||||
|
||||
if(++i % cols == 0) table.row();
|
||||
}
|
||||
|
||||
for(ContentType ctype : contentIcons){
|
||||
var all = content.getBy(ctype).<UnlockableContent>as().select(u -> u.localizedName.replace(" ", "").toLowerCase().contains(search) && u.uiIcon.found());
|
||||
|
||||
table.row();
|
||||
if(all.size > 0) table.image().colspan(cols).growX().width(-1f).height(3f).color(Pal.accent).row();
|
||||
|
||||
i = 0;
|
||||
for(UnlockableContent u : all){
|
||||
table.button(new TextureRegionDrawable(u.uiIcon), Styles.flati, iconMed, () -> {
|
||||
cons.get(u.emoji() + "");
|
||||
dialog.hide();
|
||||
}).tooltip(u.localizedName);
|
||||
|
||||
if(++i % cols == 0) table.row();
|
||||
}
|
||||
}
|
||||
|
||||
var teams = new Seq<>(Team.baseTeams);
|
||||
teams = teams.select(u -> u.localized().toLowerCase().contains(search) && Core.atlas.has("team-" + u.name));
|
||||
|
||||
table.row();
|
||||
if(teams.size > 0) table.image().colspan(cols).growX().width(-1f).height(3f).color(Pal.accent).row();
|
||||
|
||||
for(Team team : teams){
|
||||
var region = Core.atlas.find("team-" + team.name);
|
||||
|
||||
table.button(new TextureRegionDrawable(region), Styles.flati, iconMed, () -> {
|
||||
cons.get(team.emoji);
|
||||
dialog.hide();
|
||||
}).tooltip(team.localized());
|
||||
|
||||
if(++i % cols == 0) table.row();
|
||||
}
|
||||
}
|
||||
|
||||
private String writeBundles(){
|
||||
StringBuilder data = new StringBuilder();
|
||||
|
||||
for(var locale : locales.keys()){
|
||||
data.append(locale).append(":\n").append(writeLocale(locale));
|
||||
}
|
||||
|
||||
return data.toString();
|
||||
}
|
||||
|
||||
private String writeLocale(String key){
|
||||
StringBuilder data = new StringBuilder();
|
||||
|
||||
if(!locales.containsKey(key)) return "";
|
||||
|
||||
for(var prop : locales.get(key).entries()){
|
||||
data.append(prop.key).append(" = ").append(prop.value).append("\n");
|
||||
}
|
||||
|
||||
return data.toString();
|
||||
}
|
||||
|
||||
private MapLocales readBundles(String data){
|
||||
MapLocales bundles = new MapLocales();
|
||||
|
||||
String currentLocale = "";
|
||||
|
||||
for(var line : data.split("\\r?\\n|\\r")){
|
||||
if(line.endsWith(":") && !line.contains("=")){
|
||||
currentLocale = line.substring(0, line.length() - 1);
|
||||
bundles.put(currentLocale, new StringMap());
|
||||
}else{
|
||||
int sepIndex = line.indexOf(" = ");
|
||||
if(sepIndex != -1 && !currentLocale.isEmpty()){
|
||||
bundles.get(currentLocale).put(line.substring(0, sepIndex), line.substring(sepIndex + 3));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return bundles;
|
||||
}
|
||||
|
||||
private StringMap readLocale(String data){
|
||||
StringMap map = new StringMap();
|
||||
|
||||
for(var line : data.split("\\r?\\n|\\r")){
|
||||
int sepIndex = line.indexOf(" = ");
|
||||
if(sepIndex != -1){
|
||||
map.put(line.substring(0, sepIndex), line.substring(sepIndex + 3));
|
||||
}
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
private enum PropertyStatus{
|
||||
correct,
|
||||
missing,
|
||||
same
|
||||
}
|
||||
}
|
||||
@@ -109,8 +109,8 @@ abstract class BuilderComp implements Posc, Statusc, Teamc, Rotc{
|
||||
|
||||
var core = core();
|
||||
|
||||
//nothing to build.
|
||||
if(buildPlan() == null) return;
|
||||
//nothing to build, or core doesn't exist
|
||||
if(buildPlan() == null || (core == null && !infinite)) return;
|
||||
|
||||
//find the next build plan
|
||||
if(plans.size > 1){
|
||||
@@ -163,7 +163,7 @@ abstract class BuilderComp implements Posc, Statusc, Teamc, Rotc{
|
||||
}
|
||||
|
||||
//if there is no core to build with or no build entity, stop building!
|
||||
if((core == null && !infinite) || !(tile.build instanceof ConstructBuild entity)){
|
||||
if(!(tile.build instanceof ConstructBuild entity)){
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
@@ -40,6 +40,8 @@ public class WeaponMount{
|
||||
public int totalShots;
|
||||
/** counter for which barrel bullets have been fired from; used for alternating patterns */
|
||||
public int barrelCounter;
|
||||
/** Last aim length of weapon. Only used for point lasers. */
|
||||
public float lastLength;
|
||||
/** current bullet for continuous weapons */
|
||||
public @Nullable Bullet bullet;
|
||||
/** sound loop for continuous weapons */
|
||||
|
||||
@@ -484,6 +484,14 @@ public class MapObjectives implements Iterable<MapObjective>, Eachable<MapObject
|
||||
timeString.append(s);
|
||||
|
||||
if(text.startsWith("@")){
|
||||
if(state.mapLocales.containsProperty(text.substring(1))){
|
||||
try{
|
||||
return state.mapLocales.getFormatted(text.substring(1), timeString.toString());
|
||||
}catch(IllegalArgumentException e){
|
||||
//illegal text.
|
||||
text = "";
|
||||
}
|
||||
}
|
||||
return Core.bundle.format(text.substring(1), timeString.toString());
|
||||
}else{
|
||||
try{
|
||||
@@ -590,9 +598,17 @@ public class MapObjectives implements Iterable<MapObjective>, Eachable<MapObject
|
||||
return state.rules.objectiveFlags.contains(flag);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String text(){
|
||||
return text != null && text.startsWith("@") ? Core.bundle.get(text.substring(1)) : text;
|
||||
if(text == null) return null;
|
||||
|
||||
if(text.startsWith("@")){
|
||||
if(state.mapLocales.containsProperty(text.substring(1))) return state.mapLocales.getProperty(text.substring(1));
|
||||
return Core.bundle.get(text.substring(1));
|
||||
}else{
|
||||
return text;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -651,12 +667,23 @@ public class MapObjectives implements Iterable<MapObjective>, Eachable<MapObject
|
||||
}
|
||||
|
||||
public static String fetchText(String text){
|
||||
return text.startsWith("@") ?
|
||||
//on mobile, try ${text}.mobile first for mobile-specific hints.
|
||||
mobile ? Core.bundle.get(text.substring(1) + ".mobile", Core.bundle.get(text.substring(1))) :
|
||||
Core.bundle.get(text.substring(1)) :
|
||||
text;
|
||||
if(text == null) return "";
|
||||
|
||||
if(text.startsWith("@")){
|
||||
String key = text.substring(1);
|
||||
|
||||
if(mobile){
|
||||
return state.mapLocales.containsProperty(key + ".mobile") ?
|
||||
state.mapLocales.getProperty(key + ".mobile") :
|
||||
Core.bundle.get(key + ".mobile", Core.bundle.get(key));
|
||||
}else{
|
||||
return state.mapLocales.containsProperty(key) ?
|
||||
state.mapLocales.getProperty(key) :
|
||||
Core.bundle.get(key);
|
||||
}
|
||||
}else{
|
||||
return text;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -654,7 +654,13 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
|
||||
}
|
||||
|
||||
if(player.team() == build.team && build.canControlSelect(player.unit())){
|
||||
var before = player.unit();
|
||||
|
||||
build.onControlSelect(player.unit());
|
||||
|
||||
if(!before.dead && before.spawnedByCore && !before.isPlayer()){
|
||||
Call.unitDespawn(before);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -699,6 +705,10 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
|
||||
//direct dock transfer???
|
||||
unit.dockedType = before.dockedType;
|
||||
}
|
||||
|
||||
if(before.spawnedByCore && !before.isPlayer()){
|
||||
Call.unitDespawn(before);
|
||||
}
|
||||
}
|
||||
|
||||
Time.run(Fx.unitSpirit.lifetime, () -> Fx.unitControl.at(unit.x, unit.y, 0f, unit));
|
||||
|
||||
@@ -17,6 +17,7 @@ import mindustry.game.MapObjectives.*;
|
||||
import mindustry.game.Teams.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.maps.Map;
|
||||
import mindustry.type.*;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.meta.*;
|
||||
|
||||
@@ -141,6 +142,7 @@ public abstract class SaveVersion extends SaveFileReader{
|
||||
"wavetime", state.wavetime,
|
||||
"stats", JsonIO.write(state.stats),
|
||||
"rules", JsonIO.write(state.rules),
|
||||
"locales", JsonIO.write(state.mapLocales),
|
||||
"mods", JsonIO.write(mods.getModStrings().toArray(String.class)),
|
||||
"controlGroups", headless || control == null ? "null" : JsonIO.write(control.input.controlGroups),
|
||||
"width", world.width(),
|
||||
@@ -160,6 +162,7 @@ public abstract class SaveVersion extends SaveFileReader{
|
||||
state.tick = map.getFloat("tick");
|
||||
state.stats = JsonIO.read(GameStats.class, map.get("stats", "{}"));
|
||||
state.rules = JsonIO.read(Rules.class, map.get("rules", "{}"));
|
||||
state.mapLocales = JsonIO.read(MapLocales.class, map.get("locales", "{}"));
|
||||
if(state.rules.spawns.isEmpty()) state.rules.spawns = waves.get();
|
||||
lastReadBuild = map.getInt("build", -1);
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ public class GlobalVars{
|
||||
public static final Rand rand = new Rand();
|
||||
|
||||
//non-constants that depend on state
|
||||
private static int varTime, varTick, varSecond, varMinute, varWave, varWaveTime, varServer, varClient, varClientLocale, varClientUnit, varClientName, varClientTeam;
|
||||
private static int varTime, varTick, varSecond, varMinute, varWave, varWaveTime, varServer, varClient, varClientLocale, varClientUnit, varClientName, varClientTeam, varClientMobile;
|
||||
|
||||
private ObjectIntMap<String> namesToIds = new ObjectIntMap<>();
|
||||
private Seq<Var> vars = new Seq<>(Var.class);
|
||||
@@ -65,6 +65,7 @@ public class GlobalVars{
|
||||
varClientUnit = put("@clientUnit", null, true);
|
||||
varClientName = put("@clientName", null, true);
|
||||
varClientTeam = put("@clientTeam", 0, true);
|
||||
varClientMobile = put("@clientMobile", 0, true);
|
||||
|
||||
//special enums
|
||||
put("@ctrlProcessor", ctrlProcessor);
|
||||
@@ -168,6 +169,7 @@ public class GlobalVars{
|
||||
vars.items[varClientUnit].objval = player.unit();
|
||||
vars.items[varClientName].objval = player.name();
|
||||
vars.items[varClientTeam].numval = player.team().id;
|
||||
vars.items[varClientMobile].numval = mobile ? 1 : 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -183,14 +185,18 @@ public class GlobalVars{
|
||||
return arr != null && content.id >= 0 && content.id < arr.length ? arr[content.id] : -1;
|
||||
}
|
||||
|
||||
/** @return a constant ID > 0 if there is a constant with this name, otherwise -1.
|
||||
* Attempt to get privileged variable id from non-privileged logic executor returns null constant id. */
|
||||
/**
|
||||
* @return a constant ID > 0 if there is a constant with this name, otherwise -1.
|
||||
* Attempt to get privileged variable id from non-privileged logic executor returns null constant id.
|
||||
*/
|
||||
public int get(String name){
|
||||
return namesToIds.get(name, -1);
|
||||
}
|
||||
|
||||
/** @return a constant variable by ID. ID is not bound checked and must be positive.
|
||||
* Attempt to get privileged variable from non-privileged logic executor returns null constant */
|
||||
/**
|
||||
* @return a constant variable by ID. ID is not bound checked and must be positive.
|
||||
* Attempt to get privileged variable from non-privileged logic executor returns null constant
|
||||
*/
|
||||
public Var get(int id, boolean privileged){
|
||||
if(!privileged && privilegedIds.contains(id)) return vars.get(namesToIds.get("null"));
|
||||
return vars.items[id];
|
||||
|
||||
@@ -1116,6 +1116,55 @@ public class LExecutor{
|
||||
}
|
||||
}
|
||||
|
||||
public static class FormatI implements LInstruction{
|
||||
public int value;
|
||||
|
||||
public FormatI(int value){
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
FormatI(){}
|
||||
|
||||
@Override
|
||||
public void run(LExecutor exec){
|
||||
|
||||
if(exec.textBuffer.length() >= maxTextBuffer) return;
|
||||
|
||||
int placeholderIndex = -1;
|
||||
int placeholderNumber = 10;
|
||||
|
||||
for(int i = 0; i < exec.textBuffer.length(); i++){
|
||||
if(exec.textBuffer.charAt(i) == '{' && exec.textBuffer.length() - i > 2){
|
||||
char numChar = exec.textBuffer.charAt(i + 1);
|
||||
|
||||
if(numChar >= '0' && numChar <= '9' && exec.textBuffer.charAt(i + 2) == '}'){
|
||||
if(numChar - '0' < placeholderNumber){
|
||||
placeholderNumber = numChar - '0';
|
||||
placeholderIndex = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(placeholderIndex == -1) return;
|
||||
|
||||
//this should avoid any garbage allocation
|
||||
Var v = exec.var(value);
|
||||
if(v.isobj && value != 0){
|
||||
String strValue = PrintI.toString(v.objval);
|
||||
|
||||
exec.textBuffer.replace(placeholderIndex, placeholderIndex + 3, strValue);
|
||||
}else{
|
||||
//display integer version when possible
|
||||
if(Math.abs(v.numval - (long)v.numval) < 0.00001){
|
||||
exec.textBuffer.replace(placeholderIndex, placeholderIndex + 3, (long)v.numval + "");
|
||||
}else{
|
||||
exec.textBuffer.replace(placeholderIndex, placeholderIndex + 3, v.numval + "");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class PrintFlushI implements LInstruction{
|
||||
public int target;
|
||||
|
||||
@@ -2028,5 +2077,39 @@ public class LExecutor{
|
||||
}
|
||||
}
|
||||
|
||||
public static class LocalePrintI implements LInstruction{
|
||||
public int name;
|
||||
|
||||
public LocalePrintI(int name){
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public LocalePrintI(){
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(LExecutor exec){
|
||||
if(exec.textBuffer.length() >= maxTextBuffer) return;
|
||||
|
||||
//this should avoid any garbage allocation
|
||||
Var v = exec.var(name);
|
||||
if(v.isobj){
|
||||
String name = PrintI.toString(v.objval);
|
||||
|
||||
String strValue;
|
||||
|
||||
if(mobile){
|
||||
strValue = state.mapLocales.containsProperty(name + ".mobile") ?
|
||||
state.mapLocales.getProperty(name + ".mobile") :
|
||||
state.mapLocales.getProperty(name);
|
||||
}else{
|
||||
strValue = state.mapLocales.getProperty(name);
|
||||
}
|
||||
|
||||
exec.textBuffer.append(strValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//endregion
|
||||
}
|
||||
|
||||
@@ -164,7 +164,7 @@ public class LStatements{
|
||||
}
|
||||
|
||||
if(type == GraphicsType.print){
|
||||
p2 = "bottomLeft";
|
||||
p1 = "bottomLeft";
|
||||
}
|
||||
|
||||
rebuild(table);
|
||||
@@ -305,6 +305,27 @@ public class LStatements{
|
||||
}
|
||||
}
|
||||
|
||||
@RegisterStatement("format")
|
||||
public static class FormatStatement extends LStatement{
|
||||
public String value = "\"frog\"";
|
||||
|
||||
@Override
|
||||
public void build(Table table){
|
||||
field(table, value, str -> value = str).width(0f).growX().padRight(3);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LInstruction build(LAssembler builder){
|
||||
return new FormatI(builder.var(value));
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public LCategory category(){
|
||||
return LCategory.io;
|
||||
}
|
||||
}
|
||||
|
||||
@RegisterStatement("drawflush")
|
||||
public static class DrawFlushStatement extends LStatement{
|
||||
public String target = "display1";
|
||||
@@ -2071,4 +2092,29 @@ public class LStatements{
|
||||
return LCategory.world;
|
||||
}
|
||||
}
|
||||
|
||||
@RegisterStatement("localeprint")
|
||||
public static class LocalePrintStatement extends LStatement{
|
||||
public String value = "\"name\"";
|
||||
|
||||
@Override
|
||||
public void build(Table table){
|
||||
field(table, value, str -> value = str).width(0f).growX().padRight(3);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean privileged(){
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LInstruction build(LAssembler builder){
|
||||
return new LocalePrintI(builder.var(value));
|
||||
}
|
||||
|
||||
@Override
|
||||
public LCategory category(){
|
||||
return LCategory.world;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package mindustry.mod;
|
||||
|
||||
import arc.*;
|
||||
import arc.assets.*;
|
||||
import arc.assets.loaders.MusicLoader.*;
|
||||
import arc.assets.loaders.SoundLoader.*;
|
||||
import arc.audio.*;
|
||||
import arc.files.*;
|
||||
@@ -60,7 +61,6 @@ public class ContentParser{
|
||||
|
||||
ObjectMap<Class<?>, ContentType> contentTypes = new ObjectMap<>();
|
||||
ObjectSet<Class<?>> implicitNullable = ObjectSet.with(TextureRegion.class, TextureRegion[].class, TextureRegion[][].class, TextureRegion[][][].class);
|
||||
ObjectMap<String, AssetDescriptor<?>> sounds = new ObjectMap<>();
|
||||
Seq<ParseListener> listeners = new Seq<>();
|
||||
|
||||
ObjectMap<Class<?>, FieldParser> classParsers = new ObjectMap<>(){{
|
||||
@@ -271,18 +271,14 @@ public class ContentParser{
|
||||
return new Vec3(data.getFloat("x", 0f), data.getFloat("y", 0f), data.getFloat("z", 0f));
|
||||
});
|
||||
put(Sound.class, (type, data) -> {
|
||||
if(fieldOpt(Sounds.class, data) != null) return fieldOpt(Sounds.class, data);
|
||||
if(Vars.headless) return new Sound();
|
||||
var field = fieldOpt(Sounds.class, data);
|
||||
|
||||
String name = "sounds/" + data.asString();
|
||||
String path = Vars.tree.get(name + ".ogg").exists() ? name + ".ogg" : name + ".mp3";
|
||||
return field != null ? field : Vars.tree.loadSound(data.asString());
|
||||
});
|
||||
put(Music.class, (type, data) -> {
|
||||
var field = fieldOpt(Musics.class, data);
|
||||
|
||||
if(sounds.containsKey(path)) return ((SoundParameter)sounds.get(path).params).sound;
|
||||
var sound = new Sound();
|
||||
AssetDescriptor<?> desc = Core.assets.load(path, Sound.class, new SoundParameter(sound));
|
||||
desc.errored = Throwable::printStackTrace;
|
||||
sounds.put(path, desc);
|
||||
return sound;
|
||||
return field != null ? field : Vars.tree.loadMusic(data.asString());
|
||||
});
|
||||
put(Objectives.Objective.class, (type, data) -> {
|
||||
if(data.isString()){
|
||||
@@ -995,7 +991,6 @@ public class ContentParser{
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
Object fieldOpt(Class<?> type, JsonValue value){
|
||||
try{
|
||||
return type.getField(value.asString()).get(null);
|
||||
|
||||
@@ -11,6 +11,7 @@ import mindustry.io.*;
|
||||
import mindustry.logic.*;
|
||||
import mindustry.maps.Map;
|
||||
import mindustry.net.Administration.*;
|
||||
import mindustry.type.*;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.*;
|
||||
@@ -36,6 +37,7 @@ public class NetworkIO{
|
||||
}
|
||||
|
||||
stream.writeUTF(JsonIO.write(state.rules));
|
||||
stream.writeUTF(JsonIO.write(state.mapLocales));
|
||||
SaveIO.getSaveWriter().writeStringMap(stream, state.map.tags);
|
||||
|
||||
stream.writeInt(state.wave);
|
||||
@@ -62,6 +64,7 @@ public class NetworkIO{
|
||||
try(DataInputStream stream = new DataInputStream(is)){
|
||||
Time.clear();
|
||||
state.rules = JsonIO.read(Rules.class, stream.readUTF());
|
||||
state.mapLocales = JsonIO.read(MapLocales.class, stream.readUTF());
|
||||
state.map = new Map(SaveIO.getSaveWriter().readStringMap(stream));
|
||||
|
||||
state.wave = stream.readInt();
|
||||
|
||||
97
core/src/mindustry/type/MapLocales.java
Normal file
97
core/src/mindustry/type/MapLocales.java
Normal file
@@ -0,0 +1,97 @@
|
||||
package mindustry.type;
|
||||
|
||||
import arc.struct.*;
|
||||
import arc.util.serialization.*;
|
||||
import arc.util.serialization.Json.*;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import static arc.Core.*;
|
||||
|
||||
/** Class for storing map-specific locale bundles */
|
||||
public class MapLocales extends ObjectMap<String, StringMap> implements JsonSerializable{
|
||||
|
||||
@Override
|
||||
public void write(Json json){
|
||||
for(var entry : entries()){
|
||||
json.writeValue(entry.key, entry.value, StringMap.class, String.class);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(Json json, JsonValue jsonData){
|
||||
for(JsonValue value : jsonData){
|
||||
put(value.name, json.readValue(StringMap.class, value));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public MapLocales copy(){
|
||||
MapLocales out = new MapLocales();
|
||||
|
||||
for(var entry : this.entries()){
|
||||
StringMap map = new StringMap();
|
||||
map.putAll(entry.value);
|
||||
out.put(entry.key, map);
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
public String getProperty(String key){
|
||||
if(!containsProperty(currentLocale(), key)){
|
||||
if(containsProperty("en", key)) return get("en").get(key);
|
||||
return "???" + key + "???";
|
||||
}
|
||||
return get(currentLocale()).get(key);
|
||||
}
|
||||
|
||||
private String getProperty(String locale, String key){
|
||||
if(!containsProperty(locale, key)){
|
||||
if(containsProperty("en", key)) return get("en").get(key);
|
||||
return "???" + key + "???";
|
||||
}
|
||||
return get(locale).get(key);
|
||||
}
|
||||
|
||||
public boolean containsProperty(String key){
|
||||
return containsProperty(currentLocale(), key) || containsProperty("en", key);
|
||||
}
|
||||
|
||||
private boolean containsProperty(String locale, String key){
|
||||
if(!containsKey(locale)) return false;
|
||||
return get(locale).containsKey(key);
|
||||
}
|
||||
|
||||
public String getFormatted(String key, Object... args){
|
||||
StringBuilder result = new StringBuilder();
|
||||
if(!containsProperty(currentLocale(), key)){
|
||||
if(containsProperty("en", key)){
|
||||
result.append(getProperty("en", key));
|
||||
}else{
|
||||
return "???" + key + "???";
|
||||
}
|
||||
}else{
|
||||
result.append(getProperty(currentLocale(), key));
|
||||
}
|
||||
|
||||
for(var arg : args){
|
||||
int placeholderIndex = result.indexOf("@");
|
||||
|
||||
if(placeholderIndex == -1) break;
|
||||
|
||||
result.replace(placeholderIndex, placeholderIndex + 1, arg.toString());
|
||||
}
|
||||
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
// To handle default locale properly
|
||||
public static String currentLocale(){
|
||||
String locale = settings.getString("locale");
|
||||
if(locale.equals("default")){
|
||||
locale = Locale.getDefault().getLanguage();
|
||||
}
|
||||
return locale;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package mindustry.type;
|
||||
|
||||
import arc.*;
|
||||
import arc.audio.*;
|
||||
import arc.func.*;
|
||||
import arc.graphics.*;
|
||||
import arc.graphics.g3d.*;
|
||||
@@ -15,6 +16,7 @@ import mindustry.content.TechTree.*;
|
||||
import mindustry.ctype.*;
|
||||
import mindustry.game.*;
|
||||
import mindustry.game.EventType.ContentInitEvent;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.graphics.g3d.*;
|
||||
import mindustry.graphics.g3d.PlanetGrid.*;
|
||||
@@ -128,6 +130,8 @@ public class Planet extends UnlockableContent{
|
||||
public boolean allowLaunchToNumbered = true;
|
||||
/** Icon as displayed in the planet selection dialog. This is a string, as drawables are null at load time. */
|
||||
public String icon = "planet";
|
||||
/** Plays in the planet dialog when this planet is selected. */
|
||||
public Music launchMusic = Musics.launch;
|
||||
/** Default core block for launching. */
|
||||
public Block defaultCore = Blocks.coreShard;
|
||||
/** Sets up rules on game load for any sector on this planet. */
|
||||
|
||||
@@ -54,6 +54,8 @@ public class Weapon implements Cloneable{
|
||||
public boolean continuous;
|
||||
/** whether this weapon uses continuous fire without reloading; implies continuous = true */
|
||||
public boolean alwaysContinuous;
|
||||
/** Speed at which the turret can change its bullet "aim" distance. This is only used for point laser bullets. */
|
||||
public float aimChangeSpeed = Float.POSITIVE_INFINITY;
|
||||
/** whether this weapon can be aimed manually by players */
|
||||
public boolean controllable = true;
|
||||
/** whether this weapon can be automatically aimed by the unit */
|
||||
@@ -370,6 +372,18 @@ public class Weapon implements Cloneable{
|
||||
mount.sound.update(bulletX, bulletY, true);
|
||||
}
|
||||
|
||||
//target length of laser
|
||||
float shootLength = Math.min(Mathf.dst(bulletX, bulletY, mount.aimX, mount.aimY), range());
|
||||
//current length of laser
|
||||
float curLength = Mathf.dst(bulletX, bulletY, mount.bullet.aimX, mount.bullet.aimY);
|
||||
//resulting length of the bullet (smoothed)
|
||||
float resultLength = Mathf.approachDelta(curLength, shootLength, aimChangeSpeed);
|
||||
//actual aim end point based on length
|
||||
Tmp.v1.trns(shootAngle, mount.lastLength = resultLength).add(bulletX, bulletY);
|
||||
|
||||
mount.bullet.aimX = Tmp.v1.x;
|
||||
mount.bullet.aimY = Tmp.v1.y;
|
||||
|
||||
if(alwaysContinuous && mount.shoot){
|
||||
mount.bullet.time = mount.bullet.lifetime * mount.bullet.type.optimalLifeFract * mount.warmup;
|
||||
mount.bullet.keepAlive = true;
|
||||
@@ -468,7 +482,7 @@ public class Weapon implements Cloneable{
|
||||
bulletY = mountY + Angles.trnsy(weaponRotation, this.shootX + xOffset + xSpread, this.shootY + yOffset),
|
||||
shootAngle = bulletRotation(unit, mount, bulletX, bulletY) + angleOffset,
|
||||
lifeScl = bullet.scaleLife ? Mathf.clamp(Mathf.dst(bulletX, bulletY, mount.aimX, mount.aimY) / bullet.range) : 1f,
|
||||
angle = angleOffset + shootAngle + Mathf.range(inaccuracy + bullet.inaccuracy);
|
||||
angle = shootAngle + Mathf.range(inaccuracy + bullet.inaccuracy);
|
||||
|
||||
Entityc shooter = unit.controller() instanceof MissileAI ai ? ai.shooter : unit; //Pass the missile's shooter down to its bullets
|
||||
mount.bullet = bullet.create(unit, shooter, unit.team, bulletX, bulletY, angle, -1f, (1f - velocityRnd) + Mathf.random(velocityRnd), lifeScl, null, mover, mount.aimX, mount.aimY);
|
||||
@@ -493,7 +507,18 @@ public class Weapon implements Cloneable{
|
||||
|
||||
//override to do special things to a bullet after spawning
|
||||
protected void handleBullet(Unit unit, WeaponMount mount, Bullet bullet){
|
||||
|
||||
if(continuous){
|
||||
float
|
||||
weaponRotation = unit.rotation - 90 + (rotate ? mount.rotation : baseRotation),
|
||||
mountX = unit.x + Angles.trnsx(unit.rotation - 90, x, y),
|
||||
mountY = unit.y + Angles.trnsy(unit.rotation - 90, x, y),
|
||||
bulletX = mountX + Angles.trnsx(weaponRotation, this.shootX, this.shootY),
|
||||
bulletY = mountY + Angles.trnsy(weaponRotation, this.shootX, this.shootY);
|
||||
//make sure the length updates to the last set value
|
||||
Tmp.v1.trns(bulletRotation(unit, mount, bulletX, bulletY), shootY + mount.lastLength).add(bulletX, bulletY);
|
||||
bullet.aimX = Tmp.v1.x;
|
||||
bullet.aimY = Tmp.v1.y;
|
||||
}
|
||||
}
|
||||
|
||||
public void flip(){
|
||||
|
||||
@@ -50,7 +50,7 @@ public class CustomRulesDialog extends BaseDialog{
|
||||
t.defaults().size(280f, 64f).pad(2f);
|
||||
|
||||
t.button("@waves.copy", Icon.copy, style, () -> {
|
||||
ui.showInfoFade("@waves.copied");
|
||||
ui.showInfoFade("@copied");
|
||||
|
||||
//hack: don't write the spawns, they just waste space
|
||||
var spawns = rules.spawns;
|
||||
|
||||
@@ -5,6 +5,7 @@ import arc.scene.ui.*;
|
||||
import arc.scene.ui.layout.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.ui.*;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
Reference in New Issue
Block a user