diff --git a/core/assets/bundles/bundle.properties b/core/assets/bundles/bundle.properties index 12687c54ad..9150b552c5 100644 --- a/core/assets/bundles/bundle.properties +++ b/core/assets/bundles/bundle.properties @@ -122,6 +122,7 @@ mods.openfolder = Open Folder mods.viewcontent = View Content mods.reload = Reload mods.reloadexit = The game will now exit, to reload mods. +mod.installed = [[Installed] mod.display = [gray]Mod:[orange] {0} mod.enabled = [lightgray]Enabled mod.disabled = [scarlet]Disabled diff --git a/core/src/mindustry/Vars.java b/core/src/mindustry/Vars.java index 32bd7103dc..6126bb77a6 100644 --- a/core/src/mindustry/Vars.java +++ b/core/src/mindustry/Vars.java @@ -345,7 +345,7 @@ public class Vars implements Loadable{ } public static void loadSettings(){ - settings.setJson(JsonIO.json()); + settings.setJson(JsonIO.json); settings.setAppName(appName); if(steam || (Version.modifier != null && Version.modifier.contains("steam"))){ diff --git a/core/src/mindustry/content/UnitTypes.java b/core/src/mindustry/content/UnitTypes.java index deb57f0c90..8d7a6e254c 100644 --- a/core/src/mindustry/content/UnitTypes.java +++ b/core/src/mindustry/content/UnitTypes.java @@ -435,7 +435,7 @@ public class UnitTypes implements ContentList{ firstShotDelay = Fx.greenLaserChargeSmall.lifetime - 1f; - reload = 160f; + reload = 155f; recoil = 0f; chargeSound = Sounds.lasercharge2; shootSound = Sounds.beam; @@ -443,7 +443,7 @@ public class UnitTypes implements ContentList{ cooldownTime = 200f; bullet = new ContinuousLaserBulletType(){{ - damage = 23f; + damage = 24f; length = 160f; hitEffect = Fx.hitMeltHeal; drawSize = 420f; @@ -454,7 +454,7 @@ public class UnitTypes implements ContentList{ shootEffect = Fx.greenLaserChargeSmall; - incendChance = 0.075f; + incendChance = 0.08f; incendSpread = 5f; incendAmount = 1; diff --git a/core/src/mindustry/entities/EntityCollisions.java b/core/src/mindustry/entities/EntityCollisions.java index 4787c833e7..ce492bc95d 100644 --- a/core/src/mindustry/entities/EntityCollisions.java +++ b/core/src/mindustry/entities/EntityCollisions.java @@ -3,6 +3,7 @@ package mindustry.entities; import arc.math.*; import arc.math.geom.*; import arc.struct.*; +import mindustry.content.*; import mindustry.gen.*; import mindustry.world.*; @@ -126,7 +127,7 @@ public class EntityCollisions{ public static boolean legsSolid(int x, int y){ Tile tile = world.tile(x, y); - return tile == null || tile.staticDarkness() >= 2 || tile.floor().solid; + return tile == null || tile.staticDarkness() >= 2 || (tile.floor().solid && tile.block() == Blocks.air); } public static boolean waterSolid(int x, int y){ diff --git a/core/src/mindustry/io/JsonIO.java b/core/src/mindustry/io/JsonIO.java index aa395a425b..c8e43d5a20 100644 --- a/core/src/mindustry/io/JsonIO.java +++ b/core/src/mindustry/io/JsonIO.java @@ -13,8 +13,9 @@ import java.io.*; @SuppressWarnings("unchecked") public class JsonIO{ - private static CustomJson jsonBase = new CustomJson(); - private static Json json = new Json(){ + private static final CustomJson jsonBase = new CustomJson(); + + public static final Json json = new Json(){ { apply(this); } @Override @@ -39,10 +40,6 @@ public class JsonIO{ } }; - public static Json json(){ - return json; - } - public static String write(Object object){ return json.toJson(object, object.getClass()); } @@ -69,7 +66,6 @@ public class JsonIO{ } static void apply(Json json){ - json.setIgnoreUnknownFields(true); json.setElementType(Rules.class, "spawns", SpawnGroup.class); json.setElementType(Rules.class, "loadout", ItemStack.class); diff --git a/core/src/mindustry/ui/BorderImage.java b/core/src/mindustry/ui/BorderImage.java index 262a02a197..4cbdda9ba3 100644 --- a/core/src/mindustry/ui/BorderImage.java +++ b/core/src/mindustry/ui/BorderImage.java @@ -7,7 +7,7 @@ import arc.scene.ui.layout.*; import mindustry.graphics.*; public class BorderImage extends Image{ - public float thickness = 4f; + public float thickness = 4f, pad = 0f; public Color borderColor = Pal.gray; public BorderImage(){ @@ -40,7 +40,7 @@ public class BorderImage extends Image{ Draw.color(borderColor); Draw.alpha(parentAlpha); Lines.stroke(Scl.scl(thickness)); - Lines.rect(x + imageX, y + imageY, imageWidth * scaleX, imageHeight * scaleY); + Lines.rect(x + imageX - pad, y + imageY - pad, imageWidth * scaleX + pad*2, imageHeight * scaleY + pad*2); Draw.reset(); } } diff --git a/core/src/mindustry/ui/dialogs/MapPlayDialog.java b/core/src/mindustry/ui/dialogs/MapPlayDialog.java index 8655e07208..004c990f00 100644 --- a/core/src/mindustry/ui/dialogs/MapPlayDialog.java +++ b/core/src/mindustry/ui/dialogs/MapPlayDialog.java @@ -66,7 +66,7 @@ public class MapPlayDialog extends BaseDialog{ cont.add(selmode); cont.row(); - cont.button("@customize", Icon.settings, () -> dialog.show(rules, () -> rules = map.applyRules(selectedGamemode))).width(230); + cont.button("@customize", Icon.settings, () -> dialog.show(rules, () -> rules = map.applyRules(selectedGamemode))).height(50f).width(230); cont.row(); cont.add(new BorderImage(map.safeTexture(), 3f)).size(mobile && !Core.graphics.isPortrait() ? 150f : 250f).get().setScaling(Scaling.fit); //only maps with survival are valid for high scores diff --git a/core/src/mindustry/ui/dialogs/ModsDialog.java b/core/src/mindustry/ui/dialogs/ModsDialog.java index 0957fd51b9..3463fb7b8c 100644 --- a/core/src/mindustry/ui/dialogs/ModsDialog.java +++ b/core/src/mindustry/ui/dialogs/ModsDialog.java @@ -5,6 +5,7 @@ import arc.Net.*; import arc.files.*; import arc.func.*; import arc.graphics.*; +import arc.graphics.Texture.*; import arc.graphics.g2d.*; import arc.input.*; import arc.scene.style.*; @@ -21,6 +22,7 @@ import mindustry.ctype.*; import mindustry.game.EventType.*; import mindustry.gen.*; import mindustry.graphics.*; +import mindustry.io.*; import mindustry.mod.*; import mindustry.mod.Mods.*; import mindustry.ui.*; @@ -32,6 +34,8 @@ import java.util.*; import static mindustry.Vars.*; public class ModsDialog extends BaseDialog{ + private ObjectMap textureCache = new ObjectMap<>(); + private String searchtxt = ""; private @Nullable Seq modList; private boolean orderDate = true; @@ -44,6 +48,15 @@ public class ModsDialog extends BaseDialog{ super("@mods"); addCloseButton(); + Events.on(DisposeEvent.class, e -> { + textureCache.each((key, val) -> { + if(val.texture.width == val.width){ + val.texture.dispose(); + } + }); + textureCache.clear(); + }); + browser = new BaseDialog("@mods.browser"); browser.cont.table(table -> { @@ -122,9 +135,7 @@ public class ModsDialog extends BaseDialog{ ui.showErrorMessage(Core.bundle.format("connectfail", status)); }else{ try{ - var j = new Json(); - j.setIgnoreUnknownFields(true); - modList = j.fromJson(Seq.class, ModListing.class, strResult); + modList = JsonIO.json.fromJson(Seq.class, ModListing.class, strResult); var d = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'"); Func parser = text -> { @@ -397,7 +408,7 @@ public class ModsDialog extends BaseDialog{ browserTable.clear(); browserTable.add("@loading"); - int cols = Math.max(Core.graphics.getWidth() / 475, 1); + int cols = Math.max(Core.graphics.getWidth() / 482, 1); getModList(rlistings -> { browserTable.clear(); @@ -412,23 +423,65 @@ public class ModsDialog extends BaseDialog{ for(ModListing mod : listings){ if((mod.hasJava && Vars.ios) || !searchtxt.isEmpty() && !mod.repo.toLowerCase().contains(searchtxt.toLowerCase()) || (Vars.ios && mod.hasScripts)) continue; - browserTable.button(btn -> { - btn.top().left(); - btn.margin(12f); - btn.table(con -> { - con.left(); - con.add( - "[accent]" + mod.name.replace("\n", "") + - (installed.contains(mod.repo) ? " [scarlet][[Installed]" : "") + - "[white]\n[lightgray]Author:[] " + trimText(mod.author) + - "\n[lightgray]\uE809 " + mod.stars + - (Version.isAtLeast(mod.minGameVersion) ? "" : "\n" + Core.bundle.format("mod.requiresversion", mod.minGameVersion))) - .width(388f).wrap().growX().pad(0f, 6f, 0f, 6f).left().labelAlign(Align.left); - con.add().growX().pad(0f, 6f, 0f, 6f); - }).fillY().growX().pad(0f, 6f, 0f, 6f); - }, Styles.modsb, () -> { + float s = 64f; + + browserTable.button(con -> { + con.margin(0f); + con.left(); + + String repo = mod.repo; + con.add(new BorderImage(){ + TextureRegion last; + + { + border(installed.contains(repo) ? Pal.accent : Color.lightGray); + setDrawable(Tex.nomap); + pad = Scl.scl(4f); + } + + @Override + public void draw(){ + super.draw(); + + //textures are only requested when the rendering happens; this assists with culling + if(!textureCache.containsKey(repo)){ + textureCache.put(repo, last = Tex.nomap.getRegion()); + Core.net.httpGet("https://raw.githubusercontent.com/Anuken/MindustryMods/master/icons/" + repo.replace("/", "_"), res -> { + if(res.getStatus() == HttpStatus.OK){ + Pixmap pix = new Pixmap(res.getResult()); + Core.app.post(() -> { + try{ + var tex = new Texture(pix); + tex.setFilter(TextureFilter.linear); + textureCache.put(repo, new TextureRegion(tex)); + pix.dispose(); + }catch(Exception e){ + Log.err(e); + } + }); + } + }, err -> {}); + } + + var next = textureCache.get(repo); + if(last != next){ + last = next; + setDrawable(next); + } + } + }).size(s).pad(4f * 2f); + + con.add( + "[accent]" + mod.name.replace("\n", "") + + (installed.contains(mod.repo) ? "\n[lightgray]" + Core.bundle.get("mod.installed") : "") + + //"[white]\n[lightgray]Author:[] " + trimText(mod.author) + + "\n[lightgray]\uE809 " + mod.stars + + (Version.isAtLeast(mod.minGameVersion) ? "" : "\n" + Core.bundle.format("mod.requiresversion", mod.minGameVersion))) + .width(358f).wrap().grow().pad(4f, 2f, 4f, 6f).top().left().labelAlign(Align.topLeft); + + }, Styles.clearPartialt, () -> { var sel = new BaseDialog(mod.name); - sel.cont.add(mod.description).width(mobile ? 400f : 500f).wrap().pad(4f).labelAlign(Align.center, Align.left); + sel.cont.add(mod.description + "\n\n[accent]" + Core.bundle.get("editor.author") + "[lightgray] " + mod.author).width(mobile ? 400f : 500f).wrap().pad(4f).labelAlign(Align.center, Align.left); sel.buttons.defaults().size(150f, 54f).pad(2f); sel.setFillParent(false); sel.buttons.button("@back", Icon.left, () -> { @@ -447,7 +500,8 @@ public class ModsDialog extends BaseDialog{ sel.keyDown(KeyCode.escape, sel::hide); sel.keyDown(KeyCode.back, sel::hide); sel.show(); - }).width(460f).growX().left().fillY(); + }).width(460f).pad(4).growX().left().height(s + 8*2f).fillY(); + if(++i % cols == 0) browserTable.row(); } }); @@ -485,11 +539,13 @@ public class ModsDialog extends BaseDialog{ } private void githubImportMod(String repo, boolean isJava){ - ui.loadfrag.show(); - if(isJava){ - githubImportJavaMod(repo); + ui.showConfirm("@warning", "@mod.jarwarn", () -> { + ui.loadfrag.show(); + githubImportJavaMod(repo); + }); }else{ + ui.loadfrag.show(); Core.net.httpGet(ghApi + "/repos/" + repo, res -> { if(checkError(res)){ var json = Jval.read(res.getResultAsString()); diff --git a/gradle.properties b/gradle.properties index 3899e10f05..56e72d1b8a 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,3 +1,3 @@ org.gradle.daemon=true org.gradle.jvmargs=-Xms256m -Xmx1024m -archash=f7e9cc8f62d2509fc4c508442b082bdc88692b42 +archash=c9986a9770c3d287939399f3ae7171390cb1ae24 diff --git a/server/src/mindustry/server/ServerControl.java b/server/src/mindustry/server/ServerControl.java index 27b539c13a..66ba27b107 100644 --- a/server/src/mindustry/server/ServerControl.java +++ b/server/src/mindustry/server/ServerControl.java @@ -238,8 +238,8 @@ public class ServerControl implements ApplicationListener{ Events.on(PlayEvent.class, e -> { try{ - JsonValue value = JsonIO.json().fromJson(null, Core.settings.getString("globalrules")); - JsonIO.json().readFields(state.rules, value); + JsonValue value = JsonIO.json.fromJson(null, Core.settings.getString("globalrules")); + JsonIO.json.readFields(state.rules, value); }catch(Throwable t){ err("Error applying custom rules, proceeding without them.", t); } @@ -433,7 +433,7 @@ public class ServerControl implements ApplicationListener{ handler.register("rules", "[remove/add] [name] [value...]", "List, remove or add global rules. These will apply regardless of map.", arg -> { String rules = Core.settings.getString("globalrules"); - JsonValue base = JsonIO.json().fromJson(null, rules); + JsonValue base = JsonIO.json.fromJson(null, rules); if(arg.length == 0){ info("Rules:\n@", JsonIO.print(rules)); @@ -467,7 +467,7 @@ public class ServerControl implements ApplicationListener{ JsonValue parent = new JsonValue(ValueType.object); parent.addChild(value); - JsonIO.json().readField(state.rules, value.name, parent); + JsonIO.json.readField(state.rules, value.name, parent); if(base.has(value.name)){ base.remove(value.name); }