From 50fc921ddf4e380a4b0e996e111a21f31ae3a687 Mon Sep 17 00:00:00 2001 From: WayZer Date: Tue, 17 Sep 2024 07:08:56 +0000 Subject: [PATCH] filter-repo src git filter-repo --path core/src/mindustryX --invert-paths --prune-empty=never --force --- patches/client/0002-API-MindustryXApi.patch | 26 - patches/client/0004-API-Hooks.patch | 33 +- patches/client/0005-CS-Bundle-Extend.patch | 49 +- .../client/0008-API-add-SendPacketEvent.patch | 47 +- .../0011-API-add-LogicAssembledEvent.patch | 37 +- patches/client/0014-API-Hooks-onHttp.patch | 39 - patches/client/0015-API-RenderExt.patch | 65 +- ...etShow-showMineBeam-displayAllMessag.patch | 48 +- .../0017-C-RenderExt-arcChoiceUiIcon.patch | 22 +- .../0018-C-RenderExt-researchViewer.patch | 23 +- ...9-C-RenderExt-hiddenItemTransparency.patch | 23 +- ...0-C-RenderExt-overdriveZone-mendZone.patch | 24 +- patches/client/0023-C-StatExt.patch | 106 +- .../0024-API-Hooks-onHandleSendMessage.patch | 27 +- patches/client/0025-F-InternalMods.patch | 55 +- patches/client/0026-C-InternalMods-claj.patch | 438 --- ...0027-C-RenderExt-showPlacementEffect.patch | 95 - patches/client/0028-FC-TimeControl.patch | 109 - patches/client/0029-BUILD-Kotlin-1.9.20.patch | 19 +- patches/client/0030-API-ui.Format.patch | 135 - patches/client/0031-API-UIExt.patch | 63 - .../0032-API-UIExt-TeamSelectDialog.patch | 83 - .../0033-API-UIExt-SimpleCollapser.patch | 55 - patches/client/0034-FC-Loader-Mod.patch | 324 +- patches/client/0037-C-ReplayController.patch | 266 +- patches/client/0038-API-VarsX.isLoader.patch | 29 - patches/client/0039-UI-Settings.patch | 123 +- .../client/0040-ARC-bundle-and-settings.patch | 242 +- patches/client/0041-C-floatLabel.patch | 48 +- patches/client/0043-C-AutoUpdate.patch | 199 +- patches/client/0049-API-Hooks-pollKeys.patch | 23 +- .../client/0050-C-RenderExt-unitHide.patch | 52 +- ...xt-logicDisplayNoBorder-arcDrillMode.patch | 81 +- patches/client/0053-FC-MarkerType.patch | 308 +- patches/client/0056-API-C-DebugUtil.patch | 44 +- .../0057-C-RenderExt-blockRenderLevel.patch | 55 +- patches/client/0059-OC-Batch.patch | 23 +- patches/client/0060-C.patch | 257 +- patches/client/0062-API-UI.patch | 131 - patches/client/0063-UI-Mod-Mod.patch | 287 +- patches/client/0064-UI-TeamsStatDisplay.patch | 212 - ...UI-HudSettingsTable-AdvanceBuildTool.patch | 687 ---- patches/client/0066-API-LogicExt.patch | 36 - .../0067-API-LogicExt-limitUpdate.patch | 30 +- .../0068-FC-LogicExt-terrainSchematic.patch | 24 +- ...I-UpdateExt-worldCreator-allUnlocked.patch | 25 +- patches/client/0070-UI-AdvanceToolTable.patch | 598 --- .../client/0071-UI-ARC-AuxiliaryTools.patch | 1383 +------ patches/client/0073-ARC-arcScanMode.patch | 399 +- .../0076-C-RenderExt-massDriverLine.patch | 77 - .../client/0077-ARC-bundle-and-settings.patch | 103 - ...I-UIExt-announce-and-sendChatMessage.patch | 65 - patches/client/0083-API-FuncX-drawText.patch | 72 - .../0084-C-RenderExt-playerEffectColor.patch | 40 - .../0085-API-add-PlayerTeamChangedEvent.patch | 29 +- .../0091-FC-RenderExt-payloadPreview.patch | 135 - .../0092-FC-RenderExt-deadOverlay.patch | 23 +- .../0093-FC-LogicExt-invertMapClick.patch | 24 +- .../client/0095-OC-fix-slow-of-LCanvas.patch | 48 +- .../0096-FC-FuncX-focusLogicController.patch | 71 +- patches/client/0098-ARC-merged.patch | 3411 +---------------- 61 files changed, 117 insertions(+), 11488 deletions(-) diff --git a/patches/client/0002-API-MindustryXApi.patch b/patches/client/0002-API-MindustryXApi.patch index 4072b943ad2e..41c6e69b3dc6 100644 --- a/patches/client/0002-API-MindustryXApi.patch +++ b/patches/client/0002-API-MindustryXApi.patch @@ -3,29 +3,3 @@ From: way-zer Date: Sun, 17 Sep 2023 11:51:44 +0800 Subject: [PATCH] API: @MindustryXApi ---- - core/src/mindustryX/MindustryXApi.java | 15 +++++++++++++++ - 1 file changed, 15 insertions(+) - create mode 100644 core/src/mindustryX/MindustryXApi.java - -diff --git a/core/src/mindustryX/MindustryXApi.java b/core/src/mindustryX/MindustryXApi.java -new file mode 100644 -index 0000000000000000000000000000000000000000..04318b009a90b632bf7947910ca24055c3f7e7bb ---- /dev/null -+++ b/core/src/mindustryX/MindustryXApi.java -@@ -0,0 +1,15 @@ -+package mindustryX; -+ -+import java.lang.annotation.*; -+ -+@Documented -+@Retention(RetentionPolicy.SOURCE) -+@Target({ElementType.TYPE, ElementType.FIELD, ElementType.CONSTRUCTOR, ElementType.METHOD}) -+public @interface MindustryXApi{ -+ /** Need Keep for compatibility with vanilla */ -+ @Documented -+ @Retention(RetentionPolicy.SOURCE) -+ @Target({ElementType.TYPE, ElementType.FIELD, ElementType.CONSTRUCTOR, ElementType.METHOD}) -+ public @interface Keep{ -+ } -+} diff --git a/patches/client/0004-API-Hooks.patch b/patches/client/0004-API-Hooks.patch index 7a0b2048efde..b7aef3e3a0af 100644 --- a/patches/client/0004-API-Hooks.patch +++ b/patches/client/0004-API-Hooks.patch @@ -4,12 +4,10 @@ Date: Wed, 3 Apr 2024 21:57:24 +0800 Subject: [PATCH] API: Hooks --- - core/src/mindustry/ClientLauncher.java | 1 + - core/src/mindustry/Vars.java | 1 + - core/src/mindustryX/Hooks.java | 17 +++++++++++++++++ - server/src/mindustry/server/ServerLauncher.java | 1 + - 4 files changed, 20 insertions(+) - create mode 100644 core/src/mindustryX/Hooks.java + core/src/mindustry/ClientLauncher.java | 1 + + core/src/mindustry/Vars.java | 1 + + server/src/mindustry/server/ServerLauncher.java | 1 + + 3 files changed, 3 insertions(+) diff --git a/core/src/mindustry/ClientLauncher.java b/core/src/mindustry/ClientLauncher.java index c68681b7a9d6caf8e0ed8dace53b839efbf34eb5..1b5a25a7cbbd00c5a8e463ec722fb3b3ac5da9d6 100644 @@ -35,29 +33,6 @@ index d7756c3df025ba0e76882f656501d7d560c87114..d6b74b8a99a5ae12248f4dead618a374 Groups.init(); if(loadLocales){ -diff --git a/core/src/mindustryX/Hooks.java b/core/src/mindustryX/Hooks.java -new file mode 100644 -index 0000000000000000000000000000000000000000..499a9f3403c6769288ad44e7fb4a68ffb36bc591 ---- /dev/null -+++ b/core/src/mindustryX/Hooks.java -@@ -0,0 +1,17 @@ -+package mindustryX; -+ -+import arc.*; -+import arc.util.*; -+ -+public class Hooks implements ApplicationListener{ -+ /** invoke before `Vars.init`. Note that may be executed from `Vars.loadAsync` */ -+ public static void beforeInit(){ -+ Log.infoTag("MindustryX", "Hooks.beforeInit"); -+ } -+ -+ /** invoke after loading, just before `Mod::init` */ -+ @Override -+ public void init(){ -+ Log.infoTag("MindustryX", "Hooks.init"); -+ } -+} diff --git a/server/src/mindustry/server/ServerLauncher.java b/server/src/mindustry/server/ServerLauncher.java index 1aea11b2cc877ef9a35280575e6419d9fab16f01..1799c999b2affdc0de392841212edc1f544e4751 100644 --- a/server/src/mindustry/server/ServerLauncher.java diff --git a/patches/client/0005-CS-Bundle-Extend.patch b/patches/client/0005-CS-Bundle-Extend.patch index 946579f2608d..c12b3faf860a 100644 --- a/patches/client/0005-CS-Bundle-Extend.patch +++ b/patches/client/0005-CS-Bundle-Extend.patch @@ -4,55 +4,10 @@ Date: Tue, 9 Apr 2024 18:21:28 +0800 Subject: [PATCH] CS: Bundle Extend --- - core/assets/bundles/bundle-mdtx.properties | 0 - core/src/mindustryX/Hooks.java | 22 ++++++++++++++++++++++ - 2 files changed, 22 insertions(+) + core/assets/bundles/bundle-mdtx.properties | 0 + 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 core/assets/bundles/bundle-mdtx.properties diff --git a/core/assets/bundles/bundle-mdtx.properties b/core/assets/bundles/bundle-mdtx.properties new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 -diff --git a/core/src/mindustryX/Hooks.java b/core/src/mindustryX/Hooks.java -index 499a9f3403c6769288ad44e7fb4a68ffb36bc591..ca6144798666f96ee074838e489b51df1a10d19d 100644 ---- a/core/src/mindustryX/Hooks.java -+++ b/core/src/mindustryX/Hooks.java -@@ -1,12 +1,16 @@ - package mindustryX; - - import arc.*; -+import arc.files.*; - import arc.util.*; - -+import java.util.*; -+ - public class Hooks implements ApplicationListener{ - /** invoke before `Vars.init`. Note that may be executed from `Vars.loadAsync` */ - public static void beforeInit(){ - Log.infoTag("MindustryX", "Hooks.beforeInit"); -+ registerBundle(); - } - - /** invoke after loading, just before `Mod::init` */ -@@ -14,4 +18,22 @@ public class Hooks implements ApplicationListener{ - public void init(){ - Log.infoTag("MindustryX", "Hooks.init"); - } -+ -+ private static void registerBundle(){ -+ //MDTX: bundle overwrite -+ try{ -+ I18NBundle originBundle = Core.bundle; -+ Fi handle = Core.files.internal("bundles/bundle-mdtx"); -+ Core.bundle = I18NBundle.createBundle(handle, Locale.getDefault()); -+ Reflect.set(Core.bundle, "locale", originBundle.getLocale()); -+ Log.info("MDTX: bundle has been loaded."); -+ var rootBundle = Core.bundle; -+ while(rootBundle.getParent() != null){ -+ rootBundle = rootBundle.getParent(); -+ } -+ Reflect.set(rootBundle, "parent", originBundle); -+ }catch(Throwable e){ -+ Log.err(e); -+ } -+ } - } diff --git a/patches/client/0008-API-add-SendPacketEvent.patch b/patches/client/0008-API-add-SendPacketEvent.patch index 6f6a1db0b57d..06c096de5b79 100644 --- a/patches/client/0008-API-add-SendPacketEvent.patch +++ b/patches/client/0008-API-add-SendPacketEvent.patch @@ -4,11 +4,9 @@ Date: Fri, 23 Jun 2023 16:18:22 +0800 Subject: [PATCH] API: add SendPacketEvent --- - core/src/mindustry/net/ArcNetProvider.java | 3 ++ - core/src/mindustry/net/Net.java | 6 +++- - .../mindustryX/events/SendPacketEvent.java | 33 +++++++++++++++++++ - 3 files changed, 41 insertions(+), 1 deletion(-) - create mode 100644 core/src/mindustryX/events/SendPacketEvent.java + core/src/mindustry/net/ArcNetProvider.java | 3 +++ + core/src/mindustry/net/Net.java | 6 +++++- + 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/core/src/mindustry/net/ArcNetProvider.java b/core/src/mindustry/net/ArcNetProvider.java index e7a1a33dd6b4a523900d841d08246d0383ce2f30..23e8b294d646f2835750a1b085ae826032c3c627 100644 @@ -72,42 +70,3 @@ index 145238bf8856c7b83da468b89362c3d1df5d5928..7db098b1985ea7df7516f43ab4b0c49f con.send(object, reliable); } } -diff --git a/core/src/mindustryX/events/SendPacketEvent.java b/core/src/mindustryX/events/SendPacketEvent.java -new file mode 100644 -index 0000000000000000000000000000000000000000..11e0817d8d2f2fc95cb40027660372f6662b9b6b ---- /dev/null -+++ b/core/src/mindustryX/events/SendPacketEvent.java -@@ -0,0 +1,33 @@ -+package mindustryX.events; -+ -+import arc.*; -+import arc.util.*; -+import mindustry.net.*; -+import mindustryX.*; -+ -+@MindustryXApi -+public class SendPacketEvent{ -+ /** null for all, may emit again */ -+ @Nullable -+ public NetConnection con; -+ /** only when call in sendExcept */ -+ @Nullable -+ public NetConnection except; -+ public Object packet; -+ public boolean isCancelled; -+ -+ private SendPacketEvent(){ -+ } -+ -+ private static final SendPacketEvent inst = new SendPacketEvent(); -+ -+ /** @return isCancelled */ -+ public static boolean emit(@Nullable NetConnection con, @Nullable NetConnection except, Object packet){ -+ inst.isCancelled = false; -+ inst.con = con; -+ inst.except = except; -+ inst.packet = packet; -+ Events.fire(inst); -+ return inst.isCancelled; -+ } -+} diff --git a/patches/client/0011-API-add-LogicAssembledEvent.patch b/patches/client/0011-API-add-LogicAssembledEvent.patch index 50eae2039deb..40e31f4db2b9 100644 --- a/patches/client/0011-API-add-LogicAssembledEvent.patch +++ b/patches/client/0011-API-add-LogicAssembledEvent.patch @@ -4,12 +4,10 @@ Date: Thu, 3 Aug 2023 12:20:38 +0800 Subject: [PATCH] API: add LogicAssembledEvent --- - core/src/mindustry/logic/LParser.java | 4 +++- - core/src/mindustry/logic/LStatements.java | 11 ++++++++++ - .../world/blocks/logic/LogicBlock.java | 4 ++++ - .../events/LogicAssembledEvent.java | 21 +++++++++++++++++++ - 4 files changed, 39 insertions(+), 1 deletion(-) - create mode 100644 core/src/mindustryX/events/LogicAssembledEvent.java + core/src/mindustry/logic/LParser.java | 4 +++- + core/src/mindustry/logic/LStatements.java | 11 +++++++++++ + core/src/mindustry/world/blocks/logic/LogicBlock.java | 4 ++++ + 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/core/src/mindustry/logic/LParser.java b/core/src/mindustry/logic/LParser.java index 6f65e9981f2291d1c892c23f3ec8603aec8e3a4c..32133826223d3e4fe1be991c8abd3bc23d720779 100644 @@ -90,30 +88,3 @@ index b0af04af3eb5311bee7ef27fa0208f99d23d7894..12cda95f81156b8b5ce60d3ab97aa6db executor.load(asm); }catch(Exception e){ //handle malformed code and replace it with nothing -diff --git a/core/src/mindustryX/events/LogicAssembledEvent.java b/core/src/mindustryX/events/LogicAssembledEvent.java -new file mode 100644 -index 0000000000000000000000000000000000000000..4db97d52e70a6cd0b53346d86461b3677da44138 ---- /dev/null -+++ b/core/src/mindustryX/events/LogicAssembledEvent.java -@@ -0,0 +1,21 @@ -+package mindustryX.events; -+ -+import mindustry.logic.*; -+import mindustry.world.blocks.logic.LogicBlock.*; -+import mindustryX.*; -+ -+/** -+ * Fired after LAssembler finish assemble and about to load to LExecutor -+ * Allow mods to modify LAssembler, try parse InvalidStatement again. -+ * Not fired when LogicBlock load empty code. -+ */ -+@MindustryXApi -+public class LogicAssembledEvent{ -+ public LogicBuild build; -+ public LAssembler assembler; -+ -+ public LogicAssembledEvent(LogicBuild build, LAssembler assembler){ -+ this.build = build; -+ this.assembler = assembler; -+ } -+} diff --git a/patches/client/0014-API-Hooks-onHttp.patch b/patches/client/0014-API-Hooks-onHttp.patch index b0132f54bbde..d65abf36a045 100644 --- a/patches/client/0014-API-Hooks-onHttp.patch +++ b/patches/client/0014-API-Hooks-onHttp.patch @@ -3,42 +3,3 @@ From: way-zer Date: Tue, 9 Apr 2024 18:23:19 +0800 Subject: [PATCH] API(Hooks) onHttp ---- - core/src/mindustryX/Hooks.java | 17 +++++++++++++++++ - 1 file changed, 17 insertions(+) - -diff --git a/core/src/mindustryX/Hooks.java b/core/src/mindustryX/Hooks.java -index ca6144798666f96ee074838e489b51df1a10d19d..7cace088409de5b701577465f38ae01eb8b87a1c 100644 ---- a/core/src/mindustryX/Hooks.java -+++ b/core/src/mindustryX/Hooks.java -@@ -4,6 +4,7 @@ import arc.*; - import arc.files.*; - import arc.util.*; - -+import java.net.*; - import java.util.*; - - public class Hooks implements ApplicationListener{ -@@ -19,6 +20,22 @@ public class Hooks implements ApplicationListener{ - Log.infoTag("MindustryX", "Hooks.init"); - } - -+ @SuppressWarnings("unused")//call before arc.util.Http$HttpRequest.block -+ public static void onHttp(Http.HttpRequest req){ -+ if(Core.settings.getBool("githubMirror")){ -+ try{ -+ String url = req.url; -+ String host = new URL(url).getHost(); -+ if(host.contains("github.com") || host.contains("raw.githubusercontent.com")){ -+ url = "https://gh.tinylake.tech/" + url; -+ req.url = url; -+ } -+ }catch(Exception e){ -+ //ignore -+ } -+ } -+ } -+ - private static void registerBundle(){ - //MDTX: bundle overwrite - try{ diff --git a/patches/client/0015-API-RenderExt.patch b/patches/client/0015-API-RenderExt.patch index b8d70f07303a..b7c4b3cdf84a 100644 --- a/patches/client/0015-API-RenderExt.patch +++ b/patches/client/0015-API-RenderExt.patch @@ -4,12 +4,9 @@ Date: Tue, 26 Mar 2024 19:22:00 +0800 Subject: [PATCH] API: RenderExt --- - core/src/mindustry/core/Renderer.java | 3 ++- - .../src/mindustry/graphics/BlockRenderer.java | 3 ++- - core/src/mindustryX/Hooks.java | 5 ++++ - core/src/mindustryX/features/RenderExt.java | 27 +++++++++++++++++++ - 4 files changed, 36 insertions(+), 2 deletions(-) - create mode 100644 core/src/mindustryX/features/RenderExt.java + core/src/mindustry/core/Renderer.java | 3 ++- + core/src/mindustry/graphics/BlockRenderer.java | 3 ++- + 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/core/src/mindustry/core/Renderer.java b/core/src/mindustry/core/Renderer.java index ae3ae07e384731434e66a31ee13bfaa1576a83a8..01b2872b6ab744ad12f44049feb6146ce8ad84d5 100644 @@ -53,59 +50,3 @@ index aec39b666137666976321336b698e80177b05f84..5c57b1309660e500c38099d362b46a62 Draw.reset(); Draw.z(Layer.block); -diff --git a/core/src/mindustryX/Hooks.java b/core/src/mindustryX/Hooks.java -index 7cace088409de5b701577465f38ae01eb8b87a1c..1548f3d6ae0d362575a05ef38ea1575af65f5afc 100644 ---- a/core/src/mindustryX/Hooks.java -+++ b/core/src/mindustryX/Hooks.java -@@ -3,6 +3,8 @@ package mindustryX; - import arc.*; - import arc.files.*; - import arc.util.*; -+import mindustry.*; -+import mindustryX.features.*; - - import java.net.*; - import java.util.*; -@@ -18,6 +20,9 @@ public class Hooks implements ApplicationListener{ - @Override - public void init(){ - Log.infoTag("MindustryX", "Hooks.init"); -+ if(!Vars.headless){ -+ RenderExt.init(); -+ } - } - - @SuppressWarnings("unused")//call before arc.util.Http$HttpRequest.block -diff --git a/core/src/mindustryX/features/RenderExt.java b/core/src/mindustryX/features/RenderExt.java -new file mode 100644 -index 0000000000000000000000000000000000000000..835b5d7b801f03683cbb7502ea98025ff2c24d40 ---- /dev/null -+++ b/core/src/mindustryX/features/RenderExt.java -@@ -0,0 +1,27 @@ -+package mindustryX.features; -+ -+import arc.*; -+import arc.util.*; -+import mindustry.game.EventType.*; -+import mindustry.gen.*; -+import mindustry.world.*; -+ -+public class RenderExt{ -+ public static void init(){ -+ Events.run(Trigger.preDraw, () -> { -+ }); -+ Events.run(Trigger.draw, RenderExt::draw); -+ } -+ -+ private static void draw(){ -+ -+ } -+ -+ public static void onGroupDraw(Drawc t){ -+ t.draw(); -+ } -+ -+ public static void onBlockDraw(Tile tile, Block block, @Nullable Building build){ -+ block.drawBase(tile); -+ } -+} diff --git a/patches/client/0016-C-RenderExt-bulletShow-showMineBeam-displayAllMessag.patch b/patches/client/0016-C-RenderExt-bulletShow-showMineBeam-displayAllMessag.patch index 8338ccb858c2..ec29ddbd9b0f 100644 --- a/patches/client/0016-C-RenderExt-bulletShow-showMineBeam-displayAllMessag.patch +++ b/patches/client/0016-C-RenderExt-bulletShow-showMineBeam-displayAllMessag.patch @@ -5,10 +5,9 @@ Subject: [PATCH] C(RenderExt) bulletShow showMineBeam displayAllMessage arcTurretPlaceCheck --- - core/src/mindustry/type/UnitType.java | 3 ++- - .../world/blocks/defense/turrets/BaseTurret.java | 6 ++++++ - core/src/mindustryX/features/RenderExt.java | 11 +++++++++++ - 3 files changed, 19 insertions(+), 1 deletion(-) + core/src/mindustry/type/UnitType.java | 3 ++- + .../mindustry/world/blocks/defense/turrets/BaseTurret.java | 6 ++++++ + 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/core/src/mindustry/type/UnitType.java b/core/src/mindustry/type/UnitType.java index af3cd1b562c8eaa7750ba7d1c665bf5b052ebdfa..8db65f2bdfe17ec8e9250b7cd599616092812a6c 100644 @@ -54,44 +53,3 @@ index dbe779461193af3f8b882a2db305eedfd457bd82..eced3dc7581a4916970634e600d0f5cd if(fogRadiusMultiplier < 0.99f && state.rules.fog){ Drawf.dashCircle(x * tilesize + offset, y * tilesize + offset, range * fogRadiusMultiplier, Pal.lightishGray); } -diff --git a/core/src/mindustryX/features/RenderExt.java b/core/src/mindustryX/features/RenderExt.java -index 835b5d7b801f03683cbb7502ea98025ff2c24d40..c0fe214232b848835a30a51ac3c0904366858bd3 100644 ---- a/core/src/mindustryX/features/RenderExt.java -+++ b/core/src/mindustryX/features/RenderExt.java -@@ -1,14 +1,22 @@ - package mindustryX.features; - - import arc.*; -+import arc.graphics.g2d.*; - import arc.util.*; - import mindustry.game.EventType.*; - import mindustry.gen.*; -+import mindustry.graphics.*; - import mindustry.world.*; -+import mindustry.world.blocks.logic.MessageBlock.*; - - public class RenderExt{ -+ public static boolean bulletShow, showMineBeam, displayAllMessage; -+ - public static void init(){ - Events.run(Trigger.preDraw, () -> { -+ bulletShow = Core.settings.getBool("bulletShow"); -+ showMineBeam = Core.settings.getBool("showminebeam"); -+ displayAllMessage = Core.settings.getBool("displayallmessage"); - }); - Events.run(Trigger.draw, RenderExt::draw); - } -@@ -18,10 +26,13 @@ public class RenderExt{ - } - - public static void onGroupDraw(Drawc t){ -+ if(!bulletShow && t instanceof Bulletc) return; - t.draw(); - } - - public static void onBlockDraw(Tile tile, Block block, @Nullable Building build){ - block.drawBase(tile); -+ if(displayAllMessage && build instanceof MessageBuild) -+ Draw.draw(Layer.overlayUI - 0.1f, build::drawSelect); - } - } diff --git a/patches/client/0017-C-RenderExt-arcChoiceUiIcon.patch b/patches/client/0017-C-RenderExt-arcChoiceUiIcon.patch index bac6e7944ca1..4f2d2d456911 100644 --- a/patches/client/0017-C-RenderExt-arcChoiceUiIcon.patch +++ b/patches/client/0017-C-RenderExt-arcChoiceUiIcon.patch @@ -15,8 +15,7 @@ Content-Transfer-Encoding: 8bit core/src/mindustry/world/blocks/sandbox/ItemSource.java | 2 ++ core/src/mindustry/world/blocks/storage/Unloader.java | 6 ++++++ .../mindustry/world/blocks/units/UnitCargoUnloadPoint.java | 2 ++ - core/src/mindustryX/features/RenderExt.java | 2 ++ - 7 files changed, 18 insertions(+) + 6 files changed, 16 insertions(+) diff --git a/core/src/mindustry/world/blocks/distribution/DirectionalUnloader.java b/core/src/mindustry/world/blocks/distribution/DirectionalUnloader.java index 92ccae0e1122e1501ab2d96facb3c0cef5ce8842..010404a6f75731b1f9ac504c1d4c55259f082827 100644 @@ -142,22 +141,3 @@ index d7a2e02c0d81c1935482c3bbb4edb7b555788a43..3b3e49cca2b424aeddd693d261b5e242 } } -diff --git a/core/src/mindustryX/features/RenderExt.java b/core/src/mindustryX/features/RenderExt.java -index c0fe214232b848835a30a51ac3c0904366858bd3..9f2735a5acc756c9b0b3d6aa5a6bbc8ea6861a0f 100644 ---- a/core/src/mindustryX/features/RenderExt.java -+++ b/core/src/mindustryX/features/RenderExt.java -@@ -11,12 +11,14 @@ import mindustry.world.blocks.logic.MessageBlock.*; - - public class RenderExt{ - public static boolean bulletShow, showMineBeam, displayAllMessage; -+ public static boolean arcChoiceUiIcon; - - public static void init(){ - Events.run(Trigger.preDraw, () -> { - bulletShow = Core.settings.getBool("bulletShow"); - showMineBeam = Core.settings.getBool("showminebeam"); - displayAllMessage = Core.settings.getBool("displayallmessage"); -+ arcChoiceUiIcon = Core.settings.getBool("arcchoiceuiIcon"); - }); - Events.run(Trigger.draw, RenderExt::draw); - } diff --git a/patches/client/0018-C-RenderExt-researchViewer.patch b/patches/client/0018-C-RenderExt-researchViewer.patch index 337554a1520d..59485b5abdc2 100644 --- a/patches/client/0018-C-RenderExt-researchViewer.patch +++ b/patches/client/0018-C-RenderExt-researchViewer.patch @@ -10,8 +10,7 @@ Content-Transfer-Encoding: 8bit --- core/src/mindustry/game/Objectives.java | 4 +++- core/src/mindustry/ui/dialogs/ResearchDialog.java | 9 +++++---- - core/src/mindustryX/features/RenderExt.java | 2 ++ - 3 files changed, 10 insertions(+), 5 deletions(-) + 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/core/src/mindustry/game/Objectives.java b/core/src/mindustry/game/Objectives.java index f50c8c0ca180f07cc2ce13068540910c915189d5..fbb783f85810db49bd6a321d729f5214fd0d22b3 100644 @@ -88,23 +87,3 @@ index e601e02b5df2ea34d97a338b992ca30554a7e3fa..416587393a5e27822e95196cc964a902 desc.row(); if(locked(node) || debugShowRequirements){ -diff --git a/core/src/mindustryX/features/RenderExt.java b/core/src/mindustryX/features/RenderExt.java -index 9f2735a5acc756c9b0b3d6aa5a6bbc8ea6861a0f..d08e081e7ae0bdaa67d903d97116d0c714e015bc 100644 ---- a/core/src/mindustryX/features/RenderExt.java -+++ b/core/src/mindustryX/features/RenderExt.java -@@ -12,6 +12,7 @@ import mindustry.world.blocks.logic.MessageBlock.*; - public class RenderExt{ - public static boolean bulletShow, showMineBeam, displayAllMessage; - public static boolean arcChoiceUiIcon; -+ public static boolean researchViewer; - - public static void init(){ - Events.run(Trigger.preDraw, () -> { -@@ -19,6 +20,7 @@ public class RenderExt{ - showMineBeam = Core.settings.getBool("showminebeam"); - displayAllMessage = Core.settings.getBool("displayallmessage"); - arcChoiceUiIcon = Core.settings.getBool("arcchoiceuiIcon"); -+ researchViewer = Core.settings.getBool("researchViewer"); - }); - Events.run(Trigger.draw, RenderExt::draw); - } diff --git a/patches/client/0019-C-RenderExt-hiddenItemTransparency.patch b/patches/client/0019-C-RenderExt-hiddenItemTransparency.patch index c3af895dd3d4..47a4d9129a31 100644 --- a/patches/client/0019-C-RenderExt-hiddenItemTransparency.patch +++ b/patches/client/0019-C-RenderExt-hiddenItemTransparency.patch @@ -13,8 +13,7 @@ Content-Transfer-Encoding: 8bit .../distribution/BufferedItemBridge.java | 47 +++++++++++++++++++ .../world/blocks/distribution/ItemBridge.java | 15 ++++++ .../world/blocks/distribution/Junction.java | 30 ++++++++++++ - core/src/mindustryX/features/RenderExt.java | 2 + - 6 files changed, 114 insertions(+) + 5 files changed, 112 insertions(+) diff --git a/core/src/mindustry/world/DirectionalItemBuffer.java b/core/src/mindustry/world/DirectionalItemBuffer.java index 48cbdc84c09863a52f82282a70b3882e36fbed00..a55e582e7f670494adce17090e209caa7db3972f 100644 @@ -212,23 +211,3 @@ index d02fb844045f4cf7b11050e86661ae6f5529e0dc..a34377db8cec36032d8a27014ea9d1e7 @Override public void write(Writes write){ super.write(write); -diff --git a/core/src/mindustryX/features/RenderExt.java b/core/src/mindustryX/features/RenderExt.java -index d08e081e7ae0bdaa67d903d97116d0c714e015bc..62100afee7c0df3fd36781244ccd02cb318316c3 100644 ---- a/core/src/mindustryX/features/RenderExt.java -+++ b/core/src/mindustryX/features/RenderExt.java -@@ -13,6 +13,7 @@ public class RenderExt{ - public static boolean bulletShow, showMineBeam, displayAllMessage; - public static boolean arcChoiceUiIcon; - public static boolean researchViewer; -+ public static int hiddenItemTransparency; - - public static void init(){ - Events.run(Trigger.preDraw, () -> { -@@ -21,6 +22,7 @@ public class RenderExt{ - displayAllMessage = Core.settings.getBool("displayallmessage"); - arcChoiceUiIcon = Core.settings.getBool("arcchoiceuiIcon"); - researchViewer = Core.settings.getBool("researchViewer"); -+ hiddenItemTransparency = Core.settings.getInt("HiddleItemTransparency"); - }); - Events.run(Trigger.draw, RenderExt::draw); - } diff --git a/patches/client/0020-C-RenderExt-overdriveZone-mendZone.patch b/patches/client/0020-C-RenderExt-overdriveZone-mendZone.patch index 5cdfbfcc9cd4..c788c952ab2d 100644 --- a/patches/client/0020-C-RenderExt-overdriveZone-mendZone.patch +++ b/patches/client/0020-C-RenderExt-overdriveZone-mendZone.patch @@ -8,8 +8,7 @@ Subject: [PATCH] C(RenderExt) overdriveZone mendZone core/src/mindustry/graphics/Layer.java | 3 +++ .../world/blocks/defense/MendProjector.java | 8 ++++++++ .../blocks/defense/OverdriveProjector.java | 19 +++++++++++++++++++ - core/src/mindustryX/features/RenderExt.java | 3 +++ - 5 files changed, 38 insertions(+) + 4 files changed, 35 insertions(+) diff --git a/core/src/mindustry/core/Renderer.java b/core/src/mindustry/core/Renderer.java index 01b2872b6ab744ad12f44049feb6146ce8ad84d5..e987766b74de1fbb8d3d9e0a8f1863b0821a49db 100644 @@ -104,24 +103,3 @@ index 423462318a31075f047a39d07caee50e031c1390..89b28401796d1d560330328e4bea54f2 float f = 1f - (Time.time / 100f) % 1f; Draw.color(baseColor, phaseColor, phaseHeat); -diff --git a/core/src/mindustryX/features/RenderExt.java b/core/src/mindustryX/features/RenderExt.java -index 62100afee7c0df3fd36781244ccd02cb318316c3..32fa3691159506083aa110c90a182665aeb49844 100644 ---- a/core/src/mindustryX/features/RenderExt.java -+++ b/core/src/mindustryX/features/RenderExt.java -@@ -14,6 +14,7 @@ public class RenderExt{ - public static boolean arcChoiceUiIcon; - public static boolean researchViewer; - public static int hiddenItemTransparency; -+ public static float overdriveZoneTransparency, mendZoneTransparency; - - public static void init(){ - Events.run(Trigger.preDraw, () -> { -@@ -23,6 +24,8 @@ public class RenderExt{ - arcChoiceUiIcon = Core.settings.getBool("arcchoiceuiIcon"); - researchViewer = Core.settings.getBool("researchViewer"); - hiddenItemTransparency = Core.settings.getInt("HiddleItemTransparency"); -+ overdriveZoneTransparency = Core.settings.getInt("overdrive_zone") / 100f; -+ mendZoneTransparency = Core.settings.getInt("mend_zone") / 100f; - }); - Events.run(Trigger.draw, RenderExt::draw); - } diff --git a/patches/client/0023-C-StatExt.patch b/patches/client/0023-C-StatExt.patch index bee362d45598..53d1be5017c0 100644 --- a/patches/client/0023-C-StatExt.patch +++ b/patches/client/0023-C-StatExt.patch @@ -37,9 +37,7 @@ way-zer on 2024/5/4 at 22:17 core/src/mindustry/world/meta/Stat.java | 21 +- core/src/mindustry/world/meta/StatCat.java | 5 +- core/src/mindustry/world/meta/StatValues.java | 254 ++++++++++++++---- - core/src/mindustryX/features/StatExt.java | 96 +++++++ - 29 files changed, 492 insertions(+), 89 deletions(-) - create mode 100644 core/src/mindustryX/features/StatExt.java + 28 files changed, 396 insertions(+), 89 deletions(-) diff --git a/core/assets/bundles/bundle-mdtx.properties b/core/assets/bundles/bundle-mdtx.properties index 87ca026cadc01f80b5e927ed210bc78f7c7fc13b..0cb666446175aa5e952ceaa07669e10a3a023d10 100644 @@ -1130,105 +1128,3 @@ index 9c548f2836dc0e3ca068245651611d4d84579d04..eb651d09535c0edf270af4e0a037ee25 private static TextureRegion icon(UnlockableContent t){ return t.uiIcon; } -diff --git a/core/src/mindustryX/features/StatExt.java b/core/src/mindustryX/features/StatExt.java -new file mode 100644 -index 0000000000000000000000000000000000000000..fd3f53c9098e6462cce3668b8e80ba3133bd2fe7 ---- /dev/null -+++ b/core/src/mindustryX/features/StatExt.java -@@ -0,0 +1,96 @@ -+package mindustryX.features; -+ -+import arc.util.*; -+import mindustry.entities.abilities.*; -+import mindustry.entities.pattern.*; -+import mindustry.type.*; -+import mindustry.world.meta.*; -+ -+import static mindustry.Vars.tilesize; -+ -+public class StatExt{ -+ public static Stat -+ unitrange = new Stat("unit_range"), -+ canOverdrive = new Stat("can_overdrive"), -+ cost = new Stat("cost"), -+ healthScaling = new Stat("health_scaling"), -+ hardness = new Stat("hardness"), -+ buildable = new Stat("buildable"), -+ boilPoint = new Stat("boil_point"), -+ dragMultiplier = new Stat("drag_multiplier"),//移动阻力倍率 -+ -+ bufferCapacity = new Stat("buffer_capacity", StatCat.items), -+ sepOutput = new Stat("sep_output", StatCat.crafting), -+ regenSpeed = new Stat("regen_speed", StatCat.function),//力墙 回复速度 -+ regenSpeedBroken = new Stat("regen_speed_broken", StatCat.function),//力墙 过热时回复速度 -+ mend = new Stat("mend", StatCat.function),//治疗 修复量 -+ mendReload = new Stat("mend_reload", StatCat.function),//治疗 修复间隔 -+ mendSpeed = new Stat("mend_speed", StatCat.function),//治疗 修复速度 -+ warmupPartial = new Stat("warmup_partial", StatCat.power),//冲击 启动时间 -+ warmupTime = new Stat("warmup_time", StatCat.power),//冲击 完全启动时间 -+ warmupPower = new Stat("warmup_power", StatCat.power),//冲击 启动总耗电 -+ -+ rotateSpeed = new Stat("rotate_speed", StatCat.movement), -+ boostMultiplier = new Stat("boost_multiplier", StatCat.movement), -+ drownTimeMultiplier = new Stat("drown_time_multiplier", StatCat.movement), -+ mineLevel = new Stat("mine_level", StatCat.support), -+ unitItemCapacity = new Stat("unit_item_capacity", StatCat.support), -+ -+ crushDamage = new Stat("crush_damage", StatCat.combat),//碾压伤害(每格) -+ estimateDPS = new Stat("estimate_dps", StatCat.combat), -+ aiController = new Stat("ai_controller", StatCat.combat), -+ targets = new Stat("targets", StatCat.combat), -+ ammoType = new Stat("ammo_type", StatCat.combat), -+ ammoCapacity = new Stat("ammo_capacity", StatCat.combat); -+ -+ private static String abilityFormat(String format, Object... values){ -+ for(int i = 0; i < values.length; i++){ -+ if(values[i] instanceof Number n) -+ values[i] = "[stat]" + Strings.autoFixed(n.floatValue(), 1) + "[]"; -+ else -+ values[i] = "[white]" + values[i] + "[]"; -+ } -+ return Strings.format("[lightgray]" + format.replace("~", "[accent]~[]"), values); -+ } -+ -+ public static @Nullable String description(Ability ability, UnitType unit){ -+ if(ability instanceof ForceFieldAbility a){ -+ return abilityFormat("@盾容~@格~@恢复~@s冷却", -+ a.max, a.radius / tilesize, a.regen * 60f, a.cooldown / 60f -+ ); -+ }else if(ability instanceof LiquidExplodeAbility a){ -+ float rad = Math.max(unit.hitSize / tilesize * a.radScale, 1); -+ return abilityFormat("总计@@@~@格半径", -+ 1f / 3f * Math.PI * rad * rad * a.amount * a.radAmountScale,// 1/3πr²h -+ a.liquid.localizedName, a.liquid.emoji(), rad -+ ); -+ }else if(ability instanceof LiquidRegenAbility a){ -+ return abilityFormat("每格吸收@/s@@~@/s回血~最大@/s", -+ a.slurpSpeed, a.liquid.localizedName, a.liquid.emoji(), a.slurpSpeed * a.regenPerSlurp, -+ Math.PI * Math.pow(Math.max(unit.hitSize / tilesize * 0.6f, 1), 2) * a.slurpSpeed * a.regenPerSlurp -+ ); -+ }else if(ability instanceof MoveLightningAbility a){ -+ return abilityFormat("闪电@概率~@伤害~@长度 @x速度", -+ a.chance * 100, a.damage, a.length, a.maxSpeed -+ ); -+ }else if(ability instanceof SuppressionFieldAbility a){ -+ return abilityFormat("@s~@格", -+ a.reload / 60f, a.range / tilesize -+ ); -+ } -+ return null; -+ } -+ -+ public static int totalShots(ShootPattern pattern){ -+ if(pattern instanceof ShootHelix){ -+ return pattern.shots * 2; -+ }else if(pattern instanceof ShootMulti s){ -+ int total = 0; -+ for(var p : s.dest){ -+ total += totalShots(p); -+ } -+ return s.source.shots * total; -+ } -+ return pattern.shots; -+ } -+} diff --git a/patches/client/0024-API-Hooks-onHandleSendMessage.patch b/patches/client/0024-API-Hooks-onHandleSendMessage.patch index 3700934e31a5..ac82141579b1 100644 --- a/patches/client/0024-API-Hooks-onHandleSendMessage.patch +++ b/patches/client/0024-API-Hooks-onHandleSendMessage.patch @@ -5,8 +5,7 @@ Subject: [PATCH] API(Hooks) onHandleSendMessage --- core/src/mindustry/core/NetClient.java | 4 ++++ - core/src/mindustryX/Hooks.java | 6 ++++++ - 2 files changed, 10 insertions(+) + 1 file changed, 4 insertions(+) diff --git a/core/src/mindustry/core/NetClient.java b/core/src/mindustry/core/NetClient.java index 0948c4a880fe17643171857951722cd798248892..c6f8015a4616cb845c091c88425fd1cb33e0c8f4 100644 @@ -30,27 +29,3 @@ index 0948c4a880fe17643171857951722cd798248892..c6f8015a4616cb845c091c88425fd1cb if(Vars.ui != null){ Vars.ui.chatfrag.addMessage(message); Sounds.chatMessage.play(); -diff --git a/core/src/mindustryX/Hooks.java b/core/src/mindustryX/Hooks.java -index 1548f3d6ae0d362575a05ef38ea1575af65f5afc..b4687306b3fe782e096db1943e72cecc566e5e46 100644 ---- a/core/src/mindustryX/Hooks.java -+++ b/core/src/mindustryX/Hooks.java -@@ -4,6 +4,7 @@ import arc.*; - import arc.files.*; - import arc.util.*; - import mindustry.*; -+import mindustry.gen.*; - import mindustryX.features.*; - - import java.net.*; -@@ -41,6 +42,11 @@ public class Hooks implements ApplicationListener{ - } - } - -+ public static @Nullable String onHandleSendMessage(String message, @Nullable Player sender){ -+ if(message == null) return null; -+ return message; -+ } -+ - private static void registerBundle(){ - //MDTX: bundle overwrite - try{ diff --git a/patches/client/0025-F-InternalMods.patch b/patches/client/0025-F-InternalMods.patch index 5d41c4cd6808..cddd8a11014f 100644 --- a/patches/client/0025-F-InternalMods.patch +++ b/patches/client/0025-F-InternalMods.patch @@ -4,10 +4,8 @@ Date: Fri, 23 Feb 2024 16:37:59 +0800 Subject: [PATCH] F: InternalMods --- - core/src/mindustry/mod/Mods.java | 2 +- - .../src/mindustryX/features/InternalMods.java | 43 +++++++++++++++++++ - 2 files changed, 44 insertions(+), 1 deletion(-) - create mode 100644 core/src/mindustryX/features/InternalMods.java + core/src/mindustry/mod/Mods.java | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/mindustry/mod/Mods.java b/core/src/mindustry/mod/Mods.java index 9fa83c39eed59a3b4b7a8f973ec45f8376f2adf6..7268b78aa02b0571246593310df60cc6e3f5be9e 100644 @@ -22,52 +20,3 @@ index 9fa83c39eed59a3b4b7a8f973ec45f8376f2adf6..7268b78aa02b0571246593310df60cc6 // Add local mods Seq.with(modDirectory.list()) .retainAll(f -> f.extEquals("jar") || f.extEquals("zip") || (f.isDirectory() && Structs.contains(metaFiles, meta -> f.child(meta).exists()))) -diff --git a/core/src/mindustryX/features/InternalMods.java b/core/src/mindustryX/features/InternalMods.java -new file mode 100644 -index 0000000000000000000000000000000000000000..58af44c6fe4224643a87b23fd79e1e3e220a5ecc ---- /dev/null -+++ b/core/src/mindustryX/features/InternalMods.java -@@ -0,0 +1,43 @@ -+package mindustryX.features; -+ -+import arc.files.*; -+import arc.struct.*; -+import mindustry.core.*; -+import mindustry.mod.*; -+import mindustry.mod.Mods.*; -+import mindustryX.*; -+ -+import static arc.Core.files; -+import static mindustry.Vars.*; -+ -+public class InternalMods{ -+ public static Seq load(){ -+ Seq mods = new Seq<>(); -+ if(!VarsX.isLoader) -+ mods.add(internalMod(meta("MindustryX", "MindustryX", Version.mdtXBuild, ""))); -+ return mods; -+ } -+ -+ private static ModMeta meta(String id, String displayName, String version, String author){ -+ ModMeta meta = new ModMeta(); -+ meta.name = id; -+ meta.displayName = "[内置]" + displayName; -+ meta.version = version; -+ meta.author = author; -+ meta.minGameVersion = Version.buildString(); -+ meta.hidden = true; -+ meta.cleanup(); -+ return meta; -+ } -+ -+ private static LoadedMod internalMod(ModMeta meta, Mod main){ -+ Fi file = modDirectory.child("internal-" + meta.name + ".jar"); -+ Fi root = files.internal("/mindustryX/mods/" + meta.name); -+ return new LoadedMod(file, root, main, InternalMods.class.getClassLoader(), meta); -+ } -+ -+ private static LoadedMod internalMod(ModMeta meta){ -+ return internalMod(meta, new Mod(){ -+ }); -+ } -+} diff --git a/patches/client/0026-C-InternalMods-claj.patch b/patches/client/0026-C-InternalMods-claj.patch index 27e83fd3f553..3fe5d410dcc0 100644 --- a/patches/client/0026-C-InternalMods-claj.patch +++ b/patches/client/0026-C-InternalMods-claj.patch @@ -17,441 +17,3 @@ way-zer on 2024/4/13 at 19:18 不可用时隐藏claj按钮 way-zer on 2024/4/22 at 20:1 ---- - .../src/mindustryX/features/InternalMods.java | 4 +- - core/src/mindustryX/mods/claj/Claj.java | 31 ++++ - .../mindustryX/mods/claj/ClajIntegration.java | 139 ++++++++++++++++++ - .../mods/claj/dialogs/JoinViaClajDialog.java | 108 ++++++++++++++ - .../mods/claj/dialogs/ManageRoomsDialog.java | 99 +++++++++++++ - 5 files changed, 380 insertions(+), 1 deletion(-) - create mode 100644 core/src/mindustryX/mods/claj/Claj.java - create mode 100644 core/src/mindustryX/mods/claj/ClajIntegration.java - create mode 100644 core/src/mindustryX/mods/claj/dialogs/JoinViaClajDialog.java - create mode 100644 core/src/mindustryX/mods/claj/dialogs/ManageRoomsDialog.java - -diff --git a/core/src/mindustryX/features/InternalMods.java b/core/src/mindustryX/features/InternalMods.java -index 58af44c6fe4224643a87b23fd79e1e3e220a5ecc..63f413ea5fe200cd88c48bb5efeca4ffe342f438 100644 ---- a/core/src/mindustryX/features/InternalMods.java -+++ b/core/src/mindustryX/features/InternalMods.java -@@ -6,13 +6,15 @@ import mindustry.core.*; - import mindustry.mod.*; - import mindustry.mod.Mods.*; - import mindustryX.*; -+import mindustryX.mods.claj.*; - - import static arc.Core.files; --import static mindustry.Vars.*; -+import static mindustry.Vars.modDirectory; - - public class InternalMods{ - public static Seq load(){ - Seq mods = new Seq<>(); -+ mods.add(internalMod(meta("claj", "Claj联机", "1.1", "[#0096FF]xzxADIxzx cong重写 WayZer合并进MDTX"), new Claj())); - if(!VarsX.isLoader) - mods.add(internalMod(meta("MindustryX", "MindustryX", Version.mdtXBuild, ""))); - return mods; -diff --git a/core/src/mindustryX/mods/claj/Claj.java b/core/src/mindustryX/mods/claj/Claj.java -new file mode 100644 -index 0000000000000000000000000000000000000000..fd2d5ad910433779dc0324a5f86e3d28e9f6832c ---- /dev/null -+++ b/core/src/mindustryX/mods/claj/Claj.java -@@ -0,0 +1,31 @@ -+package mindustryX.mods.claj; -+ -+import arc.scene.ui.layout.*; -+import mindustry.*; -+import mindustry.gen.*; -+import mindustry.mod.*; -+import mindustryX.mods.claj.dialogs.*; -+ -+public class Claj extends Plugin{ -+ public JoinViaClajDialog joinViaClaj; -+ public ManageRoomsDialog manageRooms; -+ -+ @Override -+ public void init(){ -+ if(Vars.headless) return; -+ ClajIntegration.load(); -+ joinViaClaj = new JoinViaClajDialog(); -+ manageRooms = new ManageRoomsDialog(); -+ -+ Table buttons = Vars.ui.join.buttons; -+ buttons.button("通过claj代码加入游戏", Icon.play, joinViaClaj::show); -+ -+ var pausedDialog = Vars.ui.paused; -+ pausedDialog.shown(() -> { -+ if(!Vars.net.server()) return; -+ pausedDialog.cont.row() -+ .button("管理claj房间", Icon.planet, () -> manageRooms.show()).name("ClajInfo") -+ .size(0, 60).colspan(pausedDialog.cont.getColumns()).fill(); -+ }); -+ } -+} -\ No newline at end of file -diff --git a/core/src/mindustryX/mods/claj/ClajIntegration.java b/core/src/mindustryX/mods/claj/ClajIntegration.java -new file mode 100644 -index 0000000000000000000000000000000000000000..e586f677e9d116407af2b0ccd47e7ab2a946b348 ---- /dev/null -+++ b/core/src/mindustryX/mods/claj/ClajIntegration.java -@@ -0,0 +1,139 @@ -+package mindustryX.mods.claj; -+ -+import arc.Events; -+import arc.func.Cons; -+import arc.net.Client; -+import arc.net.Connection; -+import arc.net.DcReason; -+import arc.net.NetListener; -+import arc.struct.Seq; -+import arc.util.Reflect; -+import arc.util.Threads; -+import mindustry.Vars; -+import mindustry.game.EventType; -+import mindustry.game.EventType.ClientPreConnectEvent; -+import mindustry.gen.Call; -+import mindustry.io.TypeIO; -+import mindustry.net.ArcNetProvider.PacketSerializer; -+ -+import java.io.IOException; -+import java.nio.ByteBuffer; -+ -+public class ClajIntegration { -+ private static final Seq clients = new Seq<>(); -+ private static NetListener serverListener = null; -+ -+ public static void load() { -+ Events.run(EventType.HostEvent.class, ClajIntegration::clear); -+ Events.run(ClientPreConnectEvent.class, ClajIntegration::clear); -+ -+ var provider = Reflect.get(Vars.net, "provider"); -+ if (Vars.steam) provider = Reflect.get(provider, "provider"); // thanks -+ -+ -+ var server = Reflect.get(provider, "server"); -+ serverListener = Reflect.get(server, "dispatchListener"); -+ } -+ -+// region room management -+ -+ public static Client createRoom(String ip, int port, Cons link, Runnable disconnected) throws IOException { -+ var client = new Client(8192, 8192, new Serializer()); -+ Threads.daemon("CLaJ Room", client); -+ -+ client.addListener(new NetListener() { -+ /** Used when creating redirectors. */ -+ String key = null; -+ -+ @Override -+ public void connected(Connection connection) { -+ client.sendTCP("new"); -+ } -+ -+ @Override -+ public void disconnected(Connection connection, DcReason reason) { -+ disconnected.run(); -+ } -+ -+ @Override -+ public void received(Connection connection, Object object) { -+ if (object instanceof String s) { -+ if (s.startsWith("CLaJ")) { -+ key = s; -+ link.get(key + '#' + ip + ':' + port); -+ } else if (s.equals("new")) { -+ try { -+ createRedirector(ip, port, key); -+ } catch (Exception ignored) { -+ } -+ } else Call.sendMessage(s); -+ } -+ } -+ }); -+ -+ client.connect(5000, ip, port, port); -+ clients.add(client); -+ -+ return client; -+ } -+ -+ public static void createRedirector(String ip, int port, String key) throws IOException { -+ var client = new Client(8192, 8192, new Serializer()); -+ Threads.daemon("CLaJ Redirector", client); -+ -+ client.addListener(serverListener); -+ client.addListener(new NetListener() { -+ @Override -+ public void connected(Connection connection) { -+ client.sendTCP("host" + key); -+ } -+ }); -+ -+ client.connect(5000, ip, port, port); -+ clients.add(client); -+ } -+ -+ public static void joinRoom(String ip, int port, String key, Runnable success) { -+ Vars.logic.reset(); -+ Vars.net.reset(); -+ -+ Vars.netClient.beginConnecting(); -+ Vars.net.connect(ip, port, () -> { -+ if (!Vars.net.client()) return; -+ success.run(); -+ -+ var buffer = ByteBuffer.allocate(8192); -+ buffer.put(Serializer.linkID); -+ TypeIO.writeString(buffer, "join" + key); -+ -+ buffer.limit(buffer.position()).position(0); -+ Vars.net.send(buffer, true); -+ }); -+ } -+ -+ private static void clear() { -+ clients.each(Client::close); -+ clients.clear(); -+ } -+ -+// endregion -+ -+ -+ static class Serializer extends PacketSerializer { -+ public static final byte linkID = -3; -+ -+ public void write(ByteBuffer buffer, Object object) { -+ if (object instanceof String s) { -+ buffer.put(linkID); -+ TypeIO.writeString(buffer, s); -+ } else super.write(buffer, object); -+ } -+ -+ public Object read(ByteBuffer buffer) { -+ if (buffer.get() == linkID) return TypeIO.readString(buffer); -+ -+ buffer.position(buffer.position() - 1); -+ return super.read(buffer); -+ } -+ } -+} -\ No newline at end of file -diff --git a/core/src/mindustryX/mods/claj/dialogs/JoinViaClajDialog.java b/core/src/mindustryX/mods/claj/dialogs/JoinViaClajDialog.java -new file mode 100644 -index 0000000000000000000000000000000000000000..8e8e959090e6a75386ca59259f5dd0b0be958419 ---- /dev/null -+++ b/core/src/mindustryX/mods/claj/dialogs/JoinViaClajDialog.java -@@ -0,0 +1,108 @@ -+package mindustryX.mods.claj.dialogs; -+ -+import arc.*; -+import arc.scene.ui.*; -+import mindustry.*; -+import mindustry.gen.*; -+import mindustry.ui.*; -+import mindustry.ui.dialogs.*; -+import mindustryX.mods.claj.*; -+ -+import java.io.*; -+import java.util.*; -+ -+public class JoinViaClajDialog extends BaseDialog{ -+ private String lastLink = "请输入您的claj代码"; -+ -+ private Boolean valid = false; -+ private String output = null; -+ -+ public JoinViaClajDialog(){ -+ super("通过claj加入游戏"); -+ cont.table(table -> { -+ table.add("房间代码:").padRight(5f).left(); -+ TextField tf = table.field(lastLink, this::setLink).size(550f, 54f).maxTextLength(100).valid(this::setLink).get(); -+ tf.setProgrammaticChangeEvents(true); -+ -+ table.defaults().size(48f).padLeft(8f); -+ table.button(Icon.paste, Styles.clearNonei, () -> tf.setText(Core.app.getClipboardText())); -+ }).row(); -+ -+ cont.label(() -> output).width(550f).left(); -+ -+ buttons.defaults().size(140f, 60f).pad(4f); -+ buttons.button("@cancel", this::hide); -+ buttons.button("@ok", () -> { -+ try{ -+ if(Vars.player.name.trim().isEmpty()){ -+ Vars.ui.showInfo("@noname"); -+ return; -+ } -+ -+ var link = parseLink(lastLink); -+ ClajIntegration.joinRoom(link.ip, link.port, link.key, () -> { -+ Vars.ui.join.hide(); -+ hide(); -+ }); -+ -+ Vars.ui.loadfrag.show("@connecting"); -+ Vars.ui.loadfrag.setButton(() -> { -+ Vars.ui.loadfrag.hide(); -+ Vars.netClient.disconnectQuietly(); -+ }); -+ }catch(Throwable e){ -+ Vars.ui.showErrorMessage(e.getMessage()); -+ } -+ }).disabled(b -> lastLink.isEmpty() || Vars.net.active()); -+ } -+ -+ private boolean setLink(String link){ -+ if(Objects.equals(lastLink, link)) return valid; -+ -+ try{ -+ parseLink(link); -+ -+ output = "[lime]代码格式正确, 点击下方按钮尝试连接!"; -+ valid = true; -+ }catch(Throwable e){ -+ output = e.getMessage(); -+ valid = false; -+ } -+ -+ lastLink = link; -+ return valid; -+ } -+ -+ private Link parseLink(String link) throws IOException{ -+ var link1 = link; -+ link1 = link1.trim(); -+ if(!link1.startsWith("CLaJ")) throw new IOException("无效的claj代码:无CLaJ前缀"); -+ -+ var hash = link1.indexOf('#'); -+ if(hash != 42 + 4) throw new IOException("无效的claj代码:长度错误"); -+ -+ var semicolon = link1.indexOf(':'); -+ if(semicolon == -1) throw new IOException("无效的claj代码:服务器地址格式不正确"); -+ -+ int port; -+ try{ -+ port = Integer.parseInt(link1.substring(semicolon + 1)); -+ }catch(Throwable ignored){ -+ throw new IOException("无效的claj代码:找不到服务器端口"); -+ } -+ -+ return new Link(link1.substring(0, hash), link1.substring(hash + 1, semicolon), port); -+ } -+ -+ public static final class Link{ -+ private final String key; -+ private final String ip; -+ private final int port; -+ -+ public Link(String key, String ip, int port){ -+ this.key = key; -+ this.ip = ip; -+ this.port = port; -+ } -+ } -+} -\ No newline at end of file -diff --git a/core/src/mindustryX/mods/claj/dialogs/ManageRoomsDialog.java b/core/src/mindustryX/mods/claj/dialogs/ManageRoomsDialog.java -new file mode 100644 -index 0000000000000000000000000000000000000000..9ad970eec19bdabd464732a97396d9a1ecac1c84 ---- /dev/null -+++ b/core/src/mindustryX/mods/claj/dialogs/ManageRoomsDialog.java -@@ -0,0 +1,99 @@ -+package mindustryX.mods.claj.dialogs; -+ -+import arc.*; -+import arc.graphics.Color; -+import arc.net.Client; -+import arc.scene.ui.TextField; -+import arc.scene.ui.layout.Table; -+import arc.struct.Seq; -+import arc.util.Strings; -+import mindustry.Vars; -+import mindustry.gen.Icon; -+import mindustry.gen.Tex; -+import mindustry.ui.Styles; -+import mindustry.ui.dialogs.BaseDialog; -+import mindustryX.mods.claj.*; -+ -+import java.io.IOException; -+ -+public class ManageRoomsDialog extends BaseDialog { -+ static String serverIP = null; -+ static int serverPort = 0; -+ -+ private Table list = null; -+ private TextField field = null; -+ private boolean valid = false; -+ private boolean flip = false; -+ private final Seq clajURLs = Seq.with("new.xem8k5.top:1050"); -+ -+ public ManageRoomsDialog() { -+ super("管理claj房间"); -+ addCloseButton(); -+ -+ cont.defaults().width(Vars.mobile ? 550f : 750f); -+ -+ cont.table(list -> { -+ list.defaults().growX().padBottom(8f); -+ list.update(() -> list.getCells().retainAll(cell -> cell.get() != null)); // remove closed rooms -+ this.list = list; -+ }).row(); -+ -+ cont.table(url -> { -+ url.field(clajURLs.first(), this::setURL).maxTextLength(100).valid(this::validURL).with(f -> field = f).growX(); -+ url.button(Icon.downOpen, Styles.clearNonei, () -> flip = !flip).size(48f).padLeft(8f); -+ }).row(); -+ -+ cont.collapser(list -> clajURLs.each(url -> list.button(url, Styles.cleart, () -> setURL(url)).height(32f).growX().row()), true, () -> flip).row(); -+ -+ cont.button("新建房间并生成claj代码", () -> { -+ try { -+ list.add(new Room()).row(); -+ } catch (Exception e) { -+ Vars.ui.showErrorMessage(e.getMessage()); -+ } -+ }).disabled(b -> list.getChildren().size >= 4 || !valid).row(); -+ -+ cont.labelWrap("允许你的朋友通过claj代码联机").labelAlign(2, 8).padTop(16f).width(400f).get().getStyle().fontColor = Color.lightGray; -+ -+ setURL(clajURLs.first()); -+ } -+ -+ // region URL -+ private void setURL(String url) { -+ field.setText(url); -+ -+ var semicolon = url.indexOf(':'); -+ serverIP = url.substring(0, semicolon); -+ serverPort = Strings.parseInt(url.substring(semicolon + 1)); -+ } -+ -+ private boolean validURL(String url) { -+ return valid = url.contains(":") && Strings.canParseInt(url.substring(url.indexOf(':') + 1)); -+ } -+ -+ // endregion -+ static class Room extends Table { -+ private final Client client; -+ private String link = null; -+ -+ Room() throws IOException { -+ client = ClajIntegration.createRoom(serverIP, serverPort, link -> this.link = link, this::close); -+ -+ table(Tex.underline, cont -> cont.label(() -> link)).growX().left().fontScale(.7f).ellipsis(true).growX(); -+ -+ table(btns -> { -+ btns.defaults().size(48f).padLeft(8f); -+ btns.button(Icon.copy, Styles.clearNonei, () -> { -+ Core.app.setClipboardText(link); -+ Vars.ui.showInfoFade("@copied"); -+ }).disabled(_it -> link == null); -+ btns.button(Icon.cancel, Styles.clearNonei, this::close); -+ }); -+ } -+ -+ private void close() { -+ client.close(); -+ remove(); -+ } -+ } -+} -\ No newline at end of file diff --git a/patches/client/0027-C-RenderExt-showPlacementEffect.patch b/patches/client/0027-C-RenderExt-showPlacementEffect.patch index 39fe3ba6f9ec..df92e3e3cccb 100644 --- a/patches/client/0027-C-RenderExt-showPlacementEffect.patch +++ b/patches/client/0027-C-RenderExt-showPlacementEffect.patch @@ -3,98 +3,3 @@ From: way-zer Date: Thu, 29 Feb 2024 19:30:10 +0800 Subject: [PATCH] C(RenderExt) showPlacementEffect ---- - core/src/mindustryX/features/RenderExt.java | 48 +++++++++++++++++++++ - 1 file changed, 48 insertions(+) - -diff --git a/core/src/mindustryX/features/RenderExt.java b/core/src/mindustryX/features/RenderExt.java -index 32fa3691159506083aa110c90a182665aeb49844..6cd82be98b657e3aa36b29c46d6d172f5f4d9dd6 100644 ---- a/core/src/mindustryX/features/RenderExt.java -+++ b/core/src/mindustryX/features/RenderExt.java -@@ -1,33 +1,58 @@ - package mindustryX.features; - - import arc.*; -+import arc.graphics.*; - import arc.graphics.g2d.*; - import arc.util.*; -+import mindustry.entities.*; - import mindustry.game.EventType.*; - import mindustry.gen.*; - import mindustry.graphics.*; - import mindustry.world.*; -+import mindustry.world.blocks.defense.*; -+import mindustry.world.blocks.defense.turrets.*; -+import mindustry.world.blocks.logic.*; - import mindustry.world.blocks.logic.MessageBlock.*; -+import mindustry.world.blocks.storage.*; -+ -+import static mindustry.Vars.tilesize; - - public class RenderExt{ - public static boolean bulletShow, showMineBeam, displayAllMessage; - public static boolean arcChoiceUiIcon; - public static boolean researchViewer; -+ public static boolean showPlacementEffect; - public static int hiddenItemTransparency; -+ public static int blockBarMinHealth; - public static float overdriveZoneTransparency, mendZoneTransparency; -+ private static Effect placementEffect; - - public static void init(){ -+ placementEffect = new Effect(0f, e -> { -+ Draw.color(e.color); -+ float range = e.rotation; -+ Lines.stroke((1.5f - e.fin()) * (range / 100)); -+ if(e.fin() < 0.7f) Lines.circle(e.x, e.y, (float)((1 - Math.pow((0.7f - e.fin()) / 0.7f, 2f)) * range)); -+ else{ -+ Draw.alpha((1 - e.fin()) * 5f); -+ Lines.circle(e.x, e.y, range); -+ } -+ }); -+ - Events.run(Trigger.preDraw, () -> { - bulletShow = Core.settings.getBool("bulletShow"); - showMineBeam = Core.settings.getBool("showminebeam"); - displayAllMessage = Core.settings.getBool("displayallmessage"); - arcChoiceUiIcon = Core.settings.getBool("arcchoiceuiIcon"); - researchViewer = Core.settings.getBool("researchViewer"); -+ showPlacementEffect = Core.settings.getBool("arcPlacementEffect"); - hiddenItemTransparency = Core.settings.getInt("HiddleItemTransparency"); -+ blockBarMinHealth = Core.settings.getInt("blockbarminhealth"); - overdriveZoneTransparency = Core.settings.getInt("overdrive_zone") / 100f; - mendZoneTransparency = Core.settings.getInt("mend_zone") / 100f; - }); - Events.run(Trigger.draw, RenderExt::draw); -+ Events.on(TileChangeEvent.class, RenderExt::onSetBlock); - } - - private static void draw(){ -@@ -44,4 +69,27 @@ public class RenderExt{ - if(displayAllMessage && build instanceof MessageBuild) - Draw.draw(Layer.overlayUI - 0.1f, build::drawSelect); - } -+ -+ private static void placementEffect(float x, float y, float lifetime, float range, Color color){ -+ placementEffect.lifetime = lifetime; -+ placementEffect.at(x, y, range, color); -+ } -+ -+ public static void onSetBlock(TileChangeEvent event){ -+ Building build = event.tile.build; -+ if(build != null && showPlacementEffect){ -+ if(build.block instanceof BaseTurret t && build.health > blockBarMinHealth) -+ placementEffect(build.x, build.y, 120f, t.range, build.team.color); -+ else if(build.block instanceof Radar t) -+ placementEffect(build.x, build.y, 120f, t.fogRadius * tilesize, build.team.color); -+ else if(build.block instanceof CoreBlock t) -+ placementEffect(build.x, build.y, 180f, t.fogRadius * tilesize, build.team.color); -+ else if(build.block instanceof MendProjector t) -+ placementEffect(build.x, build.y, 120f, t.range, Pal.heal); -+ else if(build.block instanceof OverdriveProjector t) -+ placementEffect(build.x, build.y, 120f, t.range, t.baseColor); -+ else if(build.block instanceof LogicBlock t) -+ placementEffect(build.x, build.y, 120f, t.range, t.mapColor); -+ } -+ } - } diff --git a/patches/client/0028-FC-TimeControl.patch b/patches/client/0028-FC-TimeControl.patch index 34512f0fe470..b9c79f4b915c 100644 --- a/patches/client/0028-FC-TimeControl.patch +++ b/patches/client/0028-FC-TimeControl.patch @@ -3,112 +3,3 @@ From: way-zer Date: Thu, 29 Feb 2024 18:48:33 +0800 Subject: [PATCH] FC: TimeControl ---- - core/src/mindustryX/Hooks.java | 1 + - core/src/mindustryX/features/TimeControl.java | 85 +++++++++++++++++++ - 2 files changed, 86 insertions(+) - create mode 100644 core/src/mindustryX/features/TimeControl.java - -diff --git a/core/src/mindustryX/Hooks.java b/core/src/mindustryX/Hooks.java -index b4687306b3fe782e096db1943e72cecc566e5e46..9b7e280fb96aa4e5ffd26f432ecf938df542880e 100644 ---- a/core/src/mindustryX/Hooks.java -+++ b/core/src/mindustryX/Hooks.java -@@ -23,6 +23,7 @@ public class Hooks implements ApplicationListener{ - Log.infoTag("MindustryX", "Hooks.init"); - if(!Vars.headless){ - RenderExt.init(); -+ TimeControl.init(); - } - } - -diff --git a/core/src/mindustryX/features/TimeControl.java b/core/src/mindustryX/features/TimeControl.java -new file mode 100644 -index 0000000000000000000000000000000000000000..f2833ec179764dbb088fb6caa769f32172e863c8 ---- /dev/null -+++ b/core/src/mindustryX/features/TimeControl.java -@@ -0,0 +1,85 @@ -+package mindustryX.features; -+ -+import arc.Events; -+import arc.func.Floatp; -+import arc.math.WindowedMean; -+import arc.scene.ui.layout.*; -+import arc.util.*; -+import mindustry.Vars; -+import mindustry.game.EventType; -+import mindustry.ui.*; -+ -+//move from mindustry.arcModule.TimeControl -+public class TimeControl{ -+ public static float gameSpeed = 1f; -+ public static int targetFps = 60; -+ public static boolean fpsLock = false; -+ -+ private static final WindowedMean gameSpeedBalance = new WindowedMean(120); -+ private static Floatp origin; -+ private static final Floatp deltaProvider = () -> { -+ float delta = origin.get(); -+ if(fpsLock){ -+ gameSpeedBalance.add(60f / (delta * targetFps)); -+ return 60f / targetFps; -+ }else{ -+ return delta * gameSpeed; -+ } -+ }; -+ -+ public static void init(){ -+ origin = Reflect.get(Time.class, "deltaimpl"); -+ Events.on(EventType.ResetEvent.class, e -> { -+ gameSpeed = 1f; -+ targetFps = 60; -+ fpsLock = false; -+ }); -+ } -+ -+ -+ public static void setGameSpeed(float speed){ -+ gameSpeed = speed; -+ if(fpsLock){ -+ fpsLock = false; -+ Vars.ui.announce(Strings.format("已关闭帧率锁定模式\n当前游戏速度:@倍", gameSpeed)); -+ }else{ -+ Vars.ui.announce(Strings.format("当前游戏速度:@倍", gameSpeed)); -+ } -+ Time.setDeltaProvider(gameSpeed == 1f ? origin : deltaProvider); -+ } -+ -+ public static void setFpsLock(){ -+ gameSpeedBalance.clear(); -+ fpsLock = true; -+ Vars.ui.announce(Strings.format("已开启帧率锁定模式\n当前帧率锁定:@", targetFps)); -+ Time.setDeltaProvider(deltaProvider); -+ } -+ -+ public static float getGameSpeed(){ -+ if(fpsLock) return gameSpeedBalance.rawMean(); -+ return gameSpeed; -+ } -+ -+ public static void draw(Table table){ -+ table.label(() -> "x" + Strings.autoFixed(getGameSpeed(), 2)).width(18f * 3); -+ -+ table.button("/2", Styles.cleart, () -> setGameSpeed(gameSpeed * 0.5f)).tooltip("[acid]将时间流速放慢到一半").size(40f, 30f); -+ table.button("×2", Styles.cleart, () -> setGameSpeed(gameSpeed * 2f)).tooltip("[acid]将时间流速加快到两倍").size(40f, 30f); -+ table.button("[red]S", Styles.cleart, () -> setGameSpeed(0f)).tooltip("[acid]暂停时间").size(30f, 30f); -+ table.button("[green]N", Styles.cleart, () -> setGameSpeed(1f)).tooltip("[acid]恢复原速").size(30f, 30f); -+ table.button("[white]F", Styles.cleart, TimeControl::setFpsLock).tooltip("[acid]帧率模拟").size(30f, 30f); -+ -+ table.field(Integer.toString(targetFps), s -> { -+ int num = Integer.parseInt(s); -+ if(num < 2 || num > 10000) return; -+ targetFps = num; -+ if(fpsLock){ -+ Vars.ui.announce(Strings.format("当前帧率锁定:@", targetFps)); -+ } -+ }).valid(s -> { -+ if(!Strings.canParsePositiveInt(s)) return false; -+ int num = Integer.parseInt(s); -+ return 2 <= num && num < 10000; -+ }).tooltip("允许的范围:2~9999").size(80f, 30f); -+ } -+} diff --git a/patches/client/0029-BUILD-Kotlin-1.9.20.patch b/patches/client/0029-BUILD-Kotlin-1.9.20.patch index 22a134519b86..7323b1cf4b9c 100644 --- a/patches/client/0029-BUILD-Kotlin-1.9.20.patch +++ b/patches/client/0029-BUILD-Kotlin-1.9.20.patch @@ -4,10 +4,9 @@ Date: Sat, 2 Mar 2024 20:04:19 +0800 Subject: [PATCH] BUILD Kotlin 1.9.20 --- - build.gradle | 11 ++--------- - core/src/mindustryX/features/InternalMods.java | 1 + - gradle.properties | 2 +- - 3 files changed, 4 insertions(+), 10 deletions(-) + build.gradle | 11 ++--------- + gradle.properties | 2 +- + 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/build.gradle b/build.gradle index d64047c2ff2f1f6b4ead7354f4edee748c2347b1..e13f9df614deb5d24668053bf6e6ef0a417341bc 100644 @@ -38,18 +37,6 @@ index d64047c2ff2f1f6b4ead7354f4edee748c2347b1..e13f9df614deb5d24668053bf6e6ef0a //comp** classes are only used for code generation jar{ exclude("mindustry/entities/comp/**") -diff --git a/core/src/mindustryX/features/InternalMods.java b/core/src/mindustryX/features/InternalMods.java -index 63f413ea5fe200cd88c48bb5efeca4ffe342f438..c2e653f692f743e6ef33a647002f68b5761b8175 100644 ---- a/core/src/mindustryX/features/InternalMods.java -+++ b/core/src/mindustryX/features/InternalMods.java -@@ -15,6 +15,7 @@ public class InternalMods{ - public static Seq load(){ - Seq mods = new Seq<>(); - mods.add(internalMod(meta("claj", "Claj联机", "1.1", "[#0096FF]xzxADIxzx cong重写 WayZer合并进MDTX"), new Claj())); -+ mods.add(internalMod(meta("Kotlin", "Kotlin语言标准库", "1.9.20", "Jetbrains"))); - if(!VarsX.isLoader) - mods.add(internalMod(meta("MindustryX", "MindustryX", Version.mdtXBuild, ""))); - return mods; diff --git a/gradle.properties b/gradle.properties index 5fcc2f6a7455a09fc17437e4f73e6e6f4d818f86..fad521726aa1c9205be065f31e31c75e845aeadd 100644 --- a/gradle.properties diff --git a/patches/client/0030-API-ui.Format.patch b/patches/client/0030-API-ui.Format.patch index b9f8b3e7a6b6..fecc2fa61994 100644 --- a/patches/client/0030-API-ui.Format.patch +++ b/patches/client/0030-API-ui.Format.patch @@ -11,138 +11,3 @@ way-zer on 2024/6/10 Add Duration API way-zer on 2024/6/14 ---- - core/src/mindustryX/features/ui/Format.kt | 123 ++++++++++++++++++++++ - 1 file changed, 123 insertions(+) - create mode 100644 core/src/mindustryX/features/ui/Format.kt - -diff --git a/core/src/mindustryX/features/ui/Format.kt b/core/src/mindustryX/features/ui/Format.kt -new file mode 100644 -index 0000000000000000000000000000000000000000..63dc96567e610f20f1244b90fc2fb731549cc989 ---- /dev/null -+++ b/core/src/mindustryX/features/ui/Format.kt -@@ -0,0 +1,123 @@ -+package mindustryX.features.ui -+ -+import arc.math.Mathf -+import arc.math.geom.Position -+import arc.util.Strings -+import mindustry.core.World -+import kotlin.math.abs -+ -+@Suppress("MemberVisibilityCanBePrivate") -+data class Format @JvmOverloads constructor(var decimal: Int = 2, var fixDecimals: Boolean = false) { -+ /**以固定的有效位数输出*/ -+ fun fixedPrecision(v: Float): String { -+ val exponent = Mathf.floor(Mathf.log(10f, abs(v))).coerceAtLeast(0) -+ if (exponent >= decimal) return v.toInt().toString() -+ return Strings.fixed(v, decimal - exponent) -+ } -+ -+ /** 科学计数法输出 */ -+ fun scienceFormat(number: Float): String { -+ val exponent = Mathf.floor(Mathf.log(10f, abs(number))) -+ val mantissa = number / Mathf.pow(10f, exponent.toFloat()) -+ return "${Strings.fixed(mantissa, decimal)}[gray]E$exponent[]" -+ } -+ -+ private fun format0(number: Float): String { -+ if (fixDecimals) return Strings.fixed(number, decimal) -+ return fixedPrecision(number) -+ } -+ -+ /** 格式化浮点数,设计特性如下 -+ * 1. 支持特殊数值显示 -+ * 2. 支持超大超小数显示科学计数法, 支持K,M,B显示 -+ * 3. 避免出现1000K,0.99M这种数值 -+ */ -+ fun format(number: Float): String { -+ if (java.lang.Float.isNaN(number)) return "NaN" -+ if (number == Float.POSITIVE_INFINITY) return "Inf" -+ if (number == Float.NEGATIVE_INFINITY) return "-Inf" -+ -+ val abs = abs(number) -+ return when { -+ abs <= java.lang.Float.MIN_NORMAL -> format0(0f) -+ abs < Mathf.pow(10f, -decimal.toFloat()) -> scienceFormat(number) -+ abs < 1e3f || abs < Mathf.pow(10f, 1f + decimal) -> format0(number) //直接渲染 -+ abs < 1e6f -> "${format0(number / 1e3f)}[gray]K[]" -+ abs < 1e9f -> "${format0(number / 1e6f)}[gray]M[]" -+ abs < 1e12f -> "${format0(number / 1e9f)}[gray]B[]" -+ else -> scienceFormat(number) -+ } -+ } -+ -+ /** @see format */ -+ fun format(number: Long): String { -+ if (number == Long.MAX_VALUE) return "∞" -+ if (number == Long.MIN_VALUE) return "-∞" -+ if (-1000 < number && number < 1000) return number.toString() -+ return format(number.toFloat()) -+ } -+ -+ fun format(pos: Position): String { -+ return "(${format0(pos.x)},${format0(pos.y)})" -+ } -+ -+ fun formatTile(pos: Position): String { -+ return "(${World.toTile(pos.x)},${World.toTile(pos.y)})" -+ } -+ -+ fun duration(seconds: Float, unit: Boolean = true) = buildString { -+ append(if (seconds > 0) "[orange]" else "[acid]") -+ val s = abs(seconds) -+ if (s >= 60) { -+ append((seconds / 60).toInt()) -+ append(" : ") -+ append((s % 60).toInt()) -+ } else { -+ append(format0(seconds)) -+ } -+ append("[]") -+ if (unit) append(if (s >= 60) "min" else "s") -+ } -+ -+ @JvmOverloads -+ fun percent(cur: Float, max: Float, percent: Float = cur / max, showPercent: Boolean = percent < 0.95f): String { -+ return buildString { -+ append(format(cur)) -+ if (percent < 0.99f) { -+ append('/') -+ append(format(max)) -+ } -+ if (showPercent) { -+ append(" [lightgray]| ") -+ append((percent * 100).toInt()) -+ append('%') -+ } -+ } -+ } -+ -+ companion object { -+ val default = Format() -+ } -+} -+ -+object FormatDefault { -+ @JvmOverloads -+ @JvmStatic -+ fun percent(cur: Float, max: Float, percent: Float = cur / max, showPercent: Boolean = percent <= 0.99f): String = Format.default.percent(cur, max, percent, showPercent) -+ -+ @JvmStatic -+ fun format(number: Float): String = Format.default.format(number) -+ -+ @JvmStatic -+ fun format(number: Long): String = Format.default.format(number) -+ -+ @JvmStatic -+ fun format(pos: Position): String = Format.default.format(pos) -+ -+ @JvmStatic -+ fun formatTile(pos: Position): String = Format.default.formatTile(pos) -+ -+ @JvmOverloads -+ @JvmStatic -+ fun duration(seconds: Float, unit: Boolean = true): String = Format.default.duration(seconds, unit) -+} -\ No newline at end of file diff --git a/patches/client/0031-API-UIExt.patch b/patches/client/0031-API-UIExt.patch index aca8aabb145f..7a0fde8edc7d 100644 --- a/patches/client/0031-API-UIExt.patch +++ b/patches/client/0031-API-UIExt.patch @@ -3,66 +3,3 @@ From: way-zer Date: Sat, 2 Mar 2024 00:52:10 +0800 Subject: [PATCH] API: UIExt ---- - core/src/mindustryX/Hooks.java | 1 + - core/src/mindustryX/features/UIExt.java | 39 +++++++++++++++++++++++++ - 2 files changed, 40 insertions(+) - create mode 100644 core/src/mindustryX/features/UIExt.java - -diff --git a/core/src/mindustryX/Hooks.java b/core/src/mindustryX/Hooks.java -index 9b7e280fb96aa4e5ffd26f432ecf938df542880e..e073bd101614f55da9e496aa6e60fe062475079b 100644 ---- a/core/src/mindustryX/Hooks.java -+++ b/core/src/mindustryX/Hooks.java -@@ -24,6 +24,7 @@ public class Hooks implements ApplicationListener{ - if(!Vars.headless){ - RenderExt.init(); - TimeControl.init(); -+ UIExt.init(); - } - } - -diff --git a/core/src/mindustryX/features/UIExt.java b/core/src/mindustryX/features/UIExt.java -new file mode 100644 -index 0000000000000000000000000000000000000000..1d5ca9e1bd544ddd4b749036f68512e9833e8e67 ---- /dev/null -+++ b/core/src/mindustryX/features/UIExt.java -@@ -0,0 +1,39 @@ -+package mindustryX.features; -+ -+import arc.math.geom.*; -+import arc.scene.ui.*; -+import arc.scene.ui.layout.*; -+import arc.util.*; -+import mindustry.content.*; -+ -+import static mindustry.Vars.*; -+ -+public class UIExt{ -+ public static void init(){ -+ } -+ -+ public static void buildPositionRow(Table tt, Vec2 vec){ -+ tt.add("x= "); -+ TextField x = tt.field(Strings.autoFixed(vec.x, 2), text -> { -+ vec.x = Float.parseFloat(text); -+ }).valid(Strings::canParseFloat).maxTextLength(8).get(); -+ -+ tt.add("y= ").marginLeft(32f); -+ TextField y = tt.field(Strings.autoFixed(vec.y, 2), text -> { -+ vec.y = Float.parseFloat(text); -+ }).valid(Strings::canParseFloat).maxTextLength(8).get(); -+ -+ tt.button(UnitTypes.gamma.emoji(), () -> { -+ vec.set(player.tileX(), player.tileY()); -+ x.setText(String.valueOf(vec.x)); -+ y.setText(String.valueOf(vec.y)); -+ }).tooltip(b -> b.label(() -> "选择玩家当前位置:" + player.tileX() + "," + player.tileY())).height(50f); -+ -+// tt.button(StatusEffects.blasted.emoji(), () -> { -+// if(Marker.markList.size == 0) return; -+// vec.set(World.toTile(Marker.markList.peek().markPos.x), World.toTile(Marker.markList.peek().markPos.y)); -+// x.setText(World.toTile(Marker.markList.peek().markPos.x) + ""); -+// y.setText(World.toTile(Marker.markList.peek().markPos.y) + ""); -+// }).tooltip(Marker.markList.size == 0 ? "[red]未标记" : ("选择上个标记点:" + World.toTile(Marker.markList.peek().markPos.x) + "," + World.toTile(Marker.markList.peek().markPos.y))).height(50f); -+ } -+} diff --git a/patches/client/0032-API-UIExt-TeamSelectDialog.patch b/patches/client/0032-API-UIExt-TeamSelectDialog.patch index 7878e3bce445..cf8dd4751682 100644 --- a/patches/client/0032-API-UIExt-TeamSelectDialog.patch +++ b/patches/client/0032-API-UIExt-TeamSelectDialog.patch @@ -3,86 +3,3 @@ From: way-zer Date: Sat, 2 Mar 2024 00:52:10 +0800 Subject: [PATCH] API(UIExt) TeamSelectDialog ---- - core/src/mindustryX/features/UIExt.java | 4 ++ - .../features/ui/TeamSelectDialog.java | 51 +++++++++++++++++++ - 2 files changed, 55 insertions(+) - create mode 100644 core/src/mindustryX/features/ui/TeamSelectDialog.java - -diff --git a/core/src/mindustryX/features/UIExt.java b/core/src/mindustryX/features/UIExt.java -index 1d5ca9e1bd544ddd4b749036f68512e9833e8e67..d73cc555f9dfd3b4248707351e1ecfd6c261e686 100644 ---- a/core/src/mindustryX/features/UIExt.java -+++ b/core/src/mindustryX/features/UIExt.java -@@ -5,11 +5,15 @@ import arc.scene.ui.*; - import arc.scene.ui.layout.*; - import arc.util.*; - import mindustry.content.*; -+import mindustryX.features.ui.*; - - import static mindustry.Vars.*; - - public class UIExt{ -+ public static TeamSelectDialog teamSelect; -+ - public static void init(){ -+ teamSelect = new TeamSelectDialog(); - } - - public static void buildPositionRow(Table tt, Vec2 vec){ -diff --git a/core/src/mindustryX/features/ui/TeamSelectDialog.java b/core/src/mindustryX/features/ui/TeamSelectDialog.java -new file mode 100644 -index 0000000000000000000000000000000000000000..a0c11ca045115a81be97fbdab2ba8c4f9f080bf7 ---- /dev/null -+++ b/core/src/mindustryX/features/ui/TeamSelectDialog.java -@@ -0,0 +1,51 @@ -+package mindustryX.features.ui; -+ -+import arc.func.*; -+import mindustry.game.*; -+import mindustry.gen.*; -+import mindustry.ui.*; -+import mindustry.ui.dialogs.*; -+ -+//move from mindustry.arcModule.ui.dialogs.TeamSelectDialog -+public class TeamSelectDialog extends BaseDialog{ -+ private Boolf checked; -+ private Team lastTeam; -+ private Cons cons; -+ -+ public TeamSelectDialog(){ -+ super("队伍选择器"); -+ cont.pane(td -> { -+ for(Team team : Team.all){ -+ if(team.id % 10 == 6){ -+ td.row(); -+ td.add("队伍:" + team.id + "~" + (team.id + 9)); -+ } -+ td.button(Tex.whiteui, Styles.clearTogglei, 36f, () -> { -+ lastTeam = team; -+ cons.get(team); -+ }) -+ .checked(b -> checked.get(team)).pad(3f).size(50f).with(b -> b.getStyle().imageUpColor = team.color); -+ } -+ }); -+ closeOnBack(); -+ addCloseButton(); -+ } -+ -+ public void pickOne(Cons cons, Team selectedTeam){ -+ if(selectedTeam != null) lastTeam = selectedTeam; -+ pickOne(cons); -+ } -+ -+ public void pickOne(Cons cons){ -+ select((t) -> t == lastTeam, (t) -> { -+ hide(); -+ cons.get(t); -+ }); -+ } -+ -+ public void select(Boolf checked, Cons cons){ -+ this.checked = checked; -+ this.cons = cons; -+ show(); -+ } -+} diff --git a/patches/client/0033-API-UIExt-SimpleCollapser.patch b/patches/client/0033-API-UIExt-SimpleCollapser.patch index 3a643f168cf8..34e253898602 100644 --- a/patches/client/0033-API-UIExt-SimpleCollapser.patch +++ b/patches/client/0033-API-UIExt-SimpleCollapser.patch @@ -3,58 +3,3 @@ From: way-zer Date: Mon, 10 Jun 2024 20:47:13 +0800 Subject: [PATCH] API(UIExt) SimpleCollapser ---- - .../mindustryX/features/ui/SimpleCollapse.kt | 43 +++++++++++++++++++ - 1 file changed, 43 insertions(+) - create mode 100644 core/src/mindustryX/features/ui/SimpleCollapse.kt - -diff --git a/core/src/mindustryX/features/ui/SimpleCollapse.kt b/core/src/mindustryX/features/ui/SimpleCollapse.kt -new file mode 100644 -index 0000000000000000000000000000000000000000..122e9dad97e6ed58568d842ce594fbb246340eb8 ---- /dev/null -+++ b/core/src/mindustryX/features/ui/SimpleCollapse.kt -@@ -0,0 +1,43 @@ -+package mindustryX.features.ui -+ -+import arc.scene.event.Touchable -+import arc.scene.ui.layout.Table -+import arc.scene.ui.layout.WidgetGroup -+ -+class SimpleCollapser(val table: Table = Table(), collapsed: Boolean = true) : WidgetGroup(table) { -+ var collapsed: Boolean = false -+ set(value) { -+ if (field == value) return -+ field = value -+ this.touchable = if (collapsed) Touchable.disabled else Touchable.enabled -+ this.visible = !collapsed -+ invalidateHierarchy() -+ } -+ -+ init { -+ if (collapsed) this.collapsed = true -+ } -+ -+ fun toggle() { -+ collapsed = !collapsed -+ } -+ -+ override fun draw() { -+ if (collapsed) return -+ super.draw() -+ } -+ -+ override fun layout() { -+ table.setBounds(0f, 0f, width, height) -+ } -+ -+ override fun getPrefWidth(): Float = if (collapsed) 0f else table.prefWidth -+ override fun getPrefHeight(): Float = if (collapsed) 0f else table.prefHeight -+ override fun getMaxWidth(): Float = 0f -+ override fun getMinHeight(): Float = 0f -+ -+ inline fun table(block: (Table).() -> Unit): SimpleCollapser { -+ table.block() -+ return this -+ } -+} -\ No newline at end of file diff --git a/patches/client/0034-FC-Loader-Mod.patch b/patches/client/0034-FC-Loader-Mod.patch index ee4630608dfd..b853fc9cc349 100644 --- a/patches/client/0034-FC-Loader-Mod.patch +++ b/patches/client/0034-FC-Loader-Mod.patch @@ -13,15 +13,9 @@ way-zer on 2024/6/9 way-zer on 2024/8/3 --- .../src/mindustryX/loader/AndroidImpl.java | 273 ++++++++++++++ - core/src/mindustryX/loader/DesktopImpl.java | 136 +++++++ - .../src/mindustryX/loader/LoaderPlatform.java | 46 +++ - core/src/mindustryX/loader/Main.java | 116 ++++++ .../src/arc/backend/sdl/SdlApplication.java | 355 ++++++++++++++++++ - 5 files changed, 926 insertions(+) + 2 files changed, 628 insertions(+) create mode 100644 android/src/mindustryX/loader/AndroidImpl.java - create mode 100644 core/src/mindustryX/loader/DesktopImpl.java - create mode 100644 core/src/mindustryX/loader/LoaderPlatform.java - create mode 100644 core/src/mindustryX/loader/Main.java create mode 100644 desktop/src/arc/backend/sdl/SdlApplication.java diff --git a/android/src/mindustryX/loader/AndroidImpl.java b/android/src/mindustryX/loader/AndroidImpl.java @@ -303,322 +297,6 @@ index 0000000000000000000000000000000000000000..b940ea6d550cc1d7e47c3c82573d8945 + } + } +} -diff --git a/core/src/mindustryX/loader/DesktopImpl.java b/core/src/mindustryX/loader/DesktopImpl.java -new file mode 100644 -index 0000000000000000000000000000000000000000..436debef14096ac48c4532f472d3c057365da296 ---- /dev/null -+++ b/core/src/mindustryX/loader/DesktopImpl.java -@@ -0,0 +1,136 @@ -+package mindustryX.loader; -+ -+import arc.*; -+import arc.struct.*; -+import arc.util.*; -+import arc.util.io.*; -+import mindustry.game.*; -+ -+import java.io.*; -+import java.net.*; -+import java.util.*; -+ -+public class DesktopImpl implements LoaderPlatform{ -+ @Override -+ public void withSafeClassloader(String method){ -+ URL file = ((URLClassLoader)Main.class.getClassLoader()).getURLs()[0]; -+ ClassLoader parent = Core.class.getClassLoader(); -+ try(var classLoader = new URLClassLoader(new URL[]{file}, parent)){ -+ Reflect.invoke(classLoader.loadClass(Main.class.getName()), method); -+ }catch(Exception e){ -+ throw new RuntimeException(e); -+ } -+ } -+ -+ @Override -+ public void beforeLaunch(){ -+ //fix steam -+ //noinspection unchecked -+ Seq listeners = ((ObjectMap>)Reflect.get(Events.class, "events")).get(EventType.DisposeEvent.class); -+ if(listeners != null) listeners.clear(); -+ -+ for(ApplicationListener l : Core.app.getListeners()){ -+ l.pause(); -+ try{ -+ l.dispose(); -+ }catch(Throwable e){ -+ Log.err("Cleanup", e); -+ } -+ } -+ Core.app.dispose(); -+// try{ -+// Class sdl = Class.forName("arc.backend.sdl.jni.SDL"); -+// Reflect.invoke(sdl, "SDL_DestroyWindow", new Object[]{Reflect.get(Core.app, "window")}, long.class); -+//// Reflect.invoke(sdl, "SDL_Quit"); -+// }catch(Throwable e){ -+// throw new RuntimeException(e); -+// } -+ -+ System.setProperty("MDTX-SDL-width", "" + Core.graphics.getWidth()); -+ System.setProperty("MDTX-SDL-height", "" + Core.graphics.getHeight()); -+ System.setProperty("MDTX-SDL-window", Reflect.get(Core.app, "window").toString()); -+ System.setProperty("MDTX-SDL-context", Reflect.get(Core.app, "context").toString()); -+ } -+ -+ @Override -+ public ClassLoader createClassloader(){ -+ URL file = ((URLClassLoader)Main.class.getClassLoader()).getURLs()[0]; -+ ClassLoader parent = Core.class.getClassLoader(); -+ return new URLClassLoader(new URL[]{file}, parent){ -+ @Override -+ protected Class loadClass(String name, boolean resolve) throws ClassNotFoundException{ -+ synchronized(getClassLoadingLock(name)){ -+ //check for loaded state -+ Class loadedClass = findLoadedClass(name); -+ if(loadedClass == null){ -+ try{ -+ //try to load own class first -+ loadedClass = findClass(name); -+ }catch(ClassNotFoundException e){ -+ //use parent if not found -+ return parent.loadClass(name); -+ } -+ } -+ -+ if(resolve){ -+ resolveClass(loadedClass); -+ } -+ return loadedClass; -+ } -+ } -+ -+ @Override -+ protected Class findClass(String name) throws ClassNotFoundException{ -+ try{ -+ return super.findClass(name); -+ }catch(ClassNotFoundException e){ -+ if(overwrite(name)){ -+ InputStream res = parent.getResourceAsStream(name.replace('.', '/').concat(".class")); -+ if(res != null){ -+ try{ -+ byte[] bs = Streams.copyBytes(res); -+ return defineClass(name, bs, 0, bs.length); -+ }catch(IOException | ClassFormatError e2){ -+ e.addSuppressed(e2); -+ }finally{ -+ Streams.close(res); -+ } -+ } -+ } -+ throw e; -+ } -+ } -+ -+ private Boolean overwrite(String name){ -+ if(name.startsWith("arc.backend.sdl.jni")) return false; -+ return name.startsWith("mindustry") || name.startsWith("arc"); -+ } -+ -+ @Override -+ public URL getResource(String name){ -+ if(name.equals("MindustryX.hjson")) -+ return findResource("mod.hjson"); -+ if(name.equals("mod.hjson") || name.equals("icon.png")) return null; -+ //self first -+ URL url = findResource(name); -+ if(url == null) -+ url = parent.getResource(name); -+ return url; -+ } -+ -+ @Override -+ public Enumeration getResources(String name) throws IOException{ -+ return new CompoundURLEnumeration( -+ //self first -+ findResources(name), parent.getResources(name) -+ ); -+ } -+ }; -+ } -+ -+ @Override -+ public void launch(ClassLoader loader) throws Exception{ -+ Reflect.invoke(loader.loadClass("mindustry.desktop.DesktopLauncher"), "main", new Object[]{new String[]{}}, String[].class); -+ System.exit(0); -+ } -+} -diff --git a/core/src/mindustryX/loader/LoaderPlatform.java b/core/src/mindustryX/loader/LoaderPlatform.java -new file mode 100644 -index 0000000000000000000000000000000000000000..ae7a1740e384f49f7b85c84d4268c68f51b63840 ---- /dev/null -+++ b/core/src/mindustryX/loader/LoaderPlatform.java -@@ -0,0 +1,46 @@ -+package mindustryX.loader; -+ -+import java.net.*; -+import java.util.*; -+ -+interface LoaderPlatform{ -+ void withSafeClassloader(String method); -+ -+ ClassLoader createClassloader(); -+ -+ default void beforeLaunch(){ -+ } -+ -+ void launch(ClassLoader loader) throws Exception; -+ -+ final class CompoundURLEnumeration implements Enumeration{ -+ private final Enumeration[] enums; -+ private int index; -+ -+ @SafeVarargs -+ public CompoundURLEnumeration(Enumeration... enums){ -+ this.enums = enums; -+ } -+ -+ private boolean next(){ -+ while(index < enums.length){ -+ if(enums[index] != null && enums[index].hasMoreElements()){ -+ return true; -+ } -+ index++; -+ } -+ return false; -+ } -+ -+ public boolean hasMoreElements(){ -+ return next(); -+ } -+ -+ public URL nextElement(){ -+ if(!next()){ -+ throw new NoSuchElementException(); -+ } -+ return enums[index].nextElement(); -+ } -+ } -+} -diff --git a/core/src/mindustryX/loader/Main.java b/core/src/mindustryX/loader/Main.java -new file mode 100644 -index 0000000000000000000000000000000000000000..ffe311d2a8e095c1e3262cdd2ede50ff42235c61 ---- /dev/null -+++ b/core/src/mindustryX/loader/Main.java -@@ -0,0 +1,116 @@ -+package mindustryX.loader; -+ -+import arc.*; -+import arc.struct.*; -+import arc.struct.ObjectMap.*; -+import arc.util.*; -+import arc.util.Log.*; -+import mindustry.*; -+import mindustry.core.*; -+import mindustry.game.EventType.*; -+import mindustry.mod.*; -+import mindustry.mod.Mods.*; -+ -+import java.util.*; -+ -+/** @author WayZer */ -+@SuppressWarnings("unused") -+public class Main extends Mod{ -+ static LoaderPlatform getLoaderPlatform(){ -+ try{ -+ if(Core.app.isDesktop()){ -+ return new DesktopImpl(); -+ }else if(Core.app.isAndroid()){ -+ return (LoaderPlatform)Class.forName("mindustryX.loader.AndroidImpl").getConstructor().newInstance(); -+ } -+ }catch(Exception e){ -+ Log.err(e); -+ } -+ return null; -+ } -+ -+ public Main(){ -+ //When import mod, the constructor will be invoked. -+ if(System.getProperty("MDTX-loaded") == null){ -+ System.setProperty("MDTX-loaded", "true"); -+ LoaderPlatform impl = getLoaderPlatform(); -+ if(impl == null){ -+ loadError("Not support platform, skip."); -+ return; -+ } -+ impl.withSafeClassloader("preload"); -+ }else{ -+ Log.infoTag("MindustryX", "Already inside MindustryX."); -+ } -+ } -+ -+ private static LoaderPlatform impl; -+ -+ private static void loadError(String msg){ -+ Log.errTag("MindustryX", msg); -+ Events.on(ClientLoadEvent.class, (e) -> Vars.ui.showErrorMessage("Exception when load MindustryX:\n" + msg)); -+ } -+ -+ @SuppressWarnings("unused")//invoke in safe classloader -+ static void preload(){ -+ if(Vars.clientLoaded) return; -+ impl = getLoaderPlatform(); -+ if(!checkVersion()) return; -+ Core.app.post(Main::load); -+ try{ -+ Thread.sleep(9999999999999999L); -+ }catch(InterruptedException e){ -+ throw new RuntimeException(e); -+ } -+ } -+ -+ private static boolean checkVersion(){ -+ if(!Version.type.equals("official") || !Version.combined().startsWith(Version.modifier)){ -+ loadError("Not official version, skip: get " + Version.combined()); -+ return false; -+ } -+ -+ try{ -+ loadError("Detected ARC client, skip: " + Reflect.get(Version.class, "arcBuild")); -+ return false; -+ }catch(Exception e){/*ignore*/} -+ -+ try{ -+ loadError("Detected FOO client, skip: " + Reflect.get(Version.class, "clientVersion")); -+ return false; -+ }catch(Exception e){/*ignore*/} -+ -+ ModMeta meta = null; -+ @SuppressWarnings("unchecked") -+ var metas = ((ObjectMap, ModMeta>)Reflect.get(Vars.mods, "metas")); -+ for(Entry, ModMeta> entry : metas.entries()){ -+ if(entry.key.getName().equals(Main.class.getName())){//the class is not the same one. -+ meta = entry.value; -+ break; -+ } -+ } -+ Objects.requireNonNull(meta, "Can't get mod meta"); -+ String version = meta.minGameVersion; -+ if(!Version.buildString().equals(version)){ -+ loadError("Version not match, skip. (expect " + version + ", get " + Version.buildString() + ")"); -+ return false; -+ } -+ return true; -+ } -+ -+ static void load(){ -+ ClassLoader classLoader = impl.createClassloader(); -+ impl.beforeLaunch(); -+ Vars.finishLaunch();//mark a successful launch -+ Log.info("=========== Start mindustryX client ==============="); -+ Log.logger = new NoopLogHandler(); -+ try{ -+ Thread.currentThread().setContextClassLoader(classLoader); -+ impl.launch(classLoader); -+ Thread.currentThread().interrupt(); -+ }catch(Exception e){ -+ e.printStackTrace(System.err); -+ Vars.launchIDFile.writeString(e.toString());//mark failed -+ } -+ } -+} diff --git a/desktop/src/arc/backend/sdl/SdlApplication.java b/desktop/src/arc/backend/sdl/SdlApplication.java new file mode 100644 index 0000000000000000000000000000000000000000..bb63fa602677dfaa7b838e43d5c7f183968fab22 diff --git a/patches/client/0037-C-ReplayController.patch b/patches/client/0037-C-ReplayController.patch index 4dec5a6a86a7..2c1308266295 100644 --- a/patches/client/0037-C-ReplayController.patch +++ b/patches/client/0037-C-ReplayController.patch @@ -12,12 +12,9 @@ way-zer on 2024/4/22 at 18:10 实现功能按钮 way-zer on 2024/4/22 at 20:15 --- - core/src/mindustry/core/NetClient.java | 3 + - core/src/mindustry/net/Net.java | 2 + - core/src/mindustryX/Hooks.java | 1 + - .../mindustryX/features/ReplayController.java | 239 ++++++++++++++++++ - 4 files changed, 245 insertions(+) - create mode 100644 core/src/mindustryX/features/ReplayController.java + core/src/mindustry/core/NetClient.java | 3 +++ + core/src/mindustry/net/Net.java | 2 ++ + 2 files changed, 5 insertions(+) diff --git a/core/src/mindustry/core/NetClient.java b/core/src/mindustry/core/NetClient.java index c6f8015a4616cb845c091c88425fd1cb33e0c8f4..096f637032d6ad33967d62732ba2732a054b5e22 100644 @@ -66,260 +63,3 @@ index 7db098b1985ea7df7516f43ab4b0c49f05c723fa..638b11dc980ce4001faaaab28ec70c2b if(object instanceof StreamBegin b){ streams.put(b.id, currentStream = new StreamBuilder(b)); -diff --git a/core/src/mindustryX/Hooks.java b/core/src/mindustryX/Hooks.java -index e073bd101614f55da9e496aa6e60fe062475079b..01d7b62ec79aaef21c2bd0bd7dfcc1dc072b057c 100644 ---- a/core/src/mindustryX/Hooks.java -+++ b/core/src/mindustryX/Hooks.java -@@ -25,6 +25,7 @@ public class Hooks implements ApplicationListener{ - RenderExt.init(); - TimeControl.init(); - UIExt.init(); -+ ReplayController.init(); - } - } - -diff --git a/core/src/mindustryX/features/ReplayController.java b/core/src/mindustryX/features/ReplayController.java -new file mode 100644 -index 0000000000000000000000000000000000000000..5f3ba9615f3e53aa83c970d0c593f14e53fe3750 ---- /dev/null -+++ b/core/src/mindustryX/features/ReplayController.java -@@ -0,0 +1,239 @@ -+package mindustryX.features; -+ -+import arc.*; -+import arc.files.*; -+import arc.scene.ui.layout.*; -+import arc.struct.*; -+import arc.util.*; -+import arc.util.io.*; -+import mindustry.*; -+import mindustry.core.*; -+import mindustry.game.*; -+import mindustry.gen.*; -+import mindustry.net.*; -+import mindustry.net.Packets.*; -+import mindustry.ui.dialogs.*; -+ -+import java.io.*; -+import java.nio.*; -+import java.util.*; -+import java.util.zip.*; -+ -+import static mindustry.Vars.*; -+ -+/** -+ * 回放录制 -+ * 原作者 cong0707, 原文件路径 mindustry.arcModule.ReplayController -+ * WayZer修改优化 -+ */ -+public class ReplayController{ -+ public static boolean recording, replaying; -+ -+ private static Writes writes; -+ private static float startTime; -+ private static final ByteBuffer tmpBuf = ByteBuffer.allocate(32768); -+ private static final Writes tmpWr = new Writes(new ByteBufferOutput(tmpBuf)); -+ private static ReplayData now = null; -+ -+ public static void init(){ -+ Events.run(EventType.Trigger.update, () -> { -+ if(replaying && state.isMenu() && !netClient.isConnecting()){ -+ stopPlay(); -+ } -+ }); -+ { -+ Table buttons = Vars.ui.join.buttons; -+ buttons.button("加载回放文件", Icon.file, () -> { -+ FileChooser.setLastDirectory(saveDirectory); -+ platform.showFileChooser(true, "打开回放文件", "mrep", f -> Core.app.post(() -> ReplayController.startPlay(f))); -+ }); -+ } -+ { -+ var pausedDialog = Vars.ui.paused; -+ pausedDialog.shown(() -> { -+ if(!replaying) return; -+ pausedDialog.cont.row() -+ .button("查看录制信息", Icon.fileImage, ReplayController::showInfo).name("ReplayInfo") -+ .size(0, 60).colspan(pausedDialog.cont.getColumns()).fill(); -+ }); -+ } -+ } -+ -+ private static class ReplayData{ -+ int version; -+ Date time; -+ String ip; -+ String name; -+ float length; -+ private final IntIntMap packetCount = new IntIntMap(); -+ -+ ReplayData(int version, Date time, String ip, String name){ -+ this.version = version; -+ this.time = time; -+ this.ip = ip; -+ this.name = name; -+ } -+ } -+ -+ public static void onConnect(String ip){ -+ if(!Core.settings.getBool("replayRecord")) return; -+ if(replaying) return; -+ var file = saveDirectory.child(new Date().getTime() + ".mrep"); -+ try{ -+ writes = new Writes(new DataOutputStream(new DeflaterOutputStream(file.write(false, 8192)))); -+ }catch(Exception e){ -+ Log.err("创建回放出错!", e); -+ return; -+ } -+ boolean anonymous = Core.settings.getBool("anonymous", false); -+ writes.i(Version.build); -+ writes.l(new Date().getTime()); -+ writes.str(anonymous ? "anonymous" : ip); -+ writes.str(anonymous ? "anonymous" : Vars.player.name.trim()); -+ startTime = Time.time; -+ recording = true; -+ Log.info("录制中: @", file.absolutePath()); -+ } -+ -+ public static void stop(){ -+ recording = false; -+ try{ -+ writes.close(); -+ }catch(Exception ignored){ -+ } -+ writes = null; -+ } -+ -+ private static Net fakeServer = new Net(null){ -+ @Override -+ public boolean server(){ -+ return true; -+ } -+ }; -+ public static void onClientPacket(Packet p){ -+ if(!recording || p instanceof Streamable) return; -+ if(p instanceof Disconnect){ -+ stop(); -+ Log.info("录制结束"); -+ return; -+ } -+ try{ -+ byte id = Net.getPacketId(p); -+ try{ -+ writes.f(Time.time - startTime); -+ writes.b(id); -+ tmpBuf.position(0); -+ var bak = net; -+ net = fakeServer; -+ p.write(tmpWr); -+ net = bak; -+ int l = tmpBuf.position(); -+ writes.s(l); -+ writes.b(tmpBuf.array(), 0, l); -+ }catch(Exception e){ -+ net.disconnect(); -+ Core.app.post(() -> ui.showException("录制出错!", e)); -+ } -+ }catch(Exception e){ -+ Log.err(e); -+ } -+ } -+ -+ //replay -+ -+ public static Reads createReads(Fi input){ -+ try{ -+ return new Reads(new DataInputStream(new InflaterInputStream(input.read(32768)))); -+ }catch(Exception e){ -+ Core.app.post(() -> ui.showException("读取回放失败!", e)); -+ } -+ return null; -+ } -+ -+ public static void startPlay(Fi input){ -+ try(Reads r = createReads(input)){ -+ int version = r.i(); -+ Date time = new Date(r.l()); -+ String ip = r.str(); -+ String name = r.str(); -+ Log.infoTag("Replay", Strings.format("version: @, time: @, ip: @, name: @", version, time, ip, name)); -+ now = new ReplayData(version, time, ip, name); -+ while(true){ -+ float l = version > 10 ? r.f() : -+ (r.l() * Time.toSeconds / Time.nanosPerMilli / 1000); -+ byte id = r.b(); -+ r.skip(r.us()); -+ now.packetCount.put(id, now.packetCount.get(id, 0) + 1); -+ now.length = l; -+ } -+ }catch(Exception e){ -+ if(!(e.getCause() instanceof EOFException)){ -+ Log.err(e); -+ return; -+ } -+ } -+ -+ Reads reads = createReads(input); -+ reads.skip(12); -+ reads.str(); -+ reads.str(); -+ replaying = true; -+ -+ ui.loadfrag.show("@connecting"); -+ ui.loadfrag.setButton(() -> { -+ replaying = false; -+ ui.loadfrag.hide(); -+ netClient.disconnectQuietly(); -+ }); -+ -+ logic.reset(); -+ net.reset(); -+ netClient.beginConnecting(); -+ Reflect.set(net, "active", true); -+ -+ startTime = Time.time; -+ Threads.daemon("Replay Controller", () -> { -+ try{ -+ while(replaying){ -+ float nextTime = now.version > 10 ? reads.f() : -+ (reads.l() * Time.toSeconds / Time.nanosPerMilli / 1000); -+ Packet p = Net.newPacket(reads.b()); -+ p.read(reads, reads.us()); -+ while(Time.time - startTime < nextTime) -+ Thread.sleep(1); -+ Core.app.post(() -> net.handleClientReceived(p)); -+ } -+ }catch(Exception e){ -+ replaying = false; -+ reads.close(); -+ net.disconnect(); -+ Core.app.post(() -> logic.reset()); -+ } -+ }); -+ } -+ -+ public static void stopPlay(){ -+ replaying = false; -+ Log.infoTag("Replay", "stop"); -+ } -+ -+ -+ public static void showInfo(){ -+ BaseDialog dialog = new BaseDialog("回放统计"); -+ var replay = now; -+ if(replay == null){ -+ dialog.cont.add("未加载回放!"); -+ return; -+ } -+ dialog.cont.add("回放版本:" + replay.version).row(); -+ dialog.cont.add("回放创建时间:" + replay.time).row(); -+ dialog.cont.add("服务器ip:" + replay.ip).row(); -+ dialog.cont.add("玩家名:" + replay.name).row(); -+ int secs = (int)(replay.length / 60); -+ dialog.cont.add("回放长度:" + (secs / 3600) + ":" + (secs / 60 % 60) + ":" + (secs % 60)).row(); -+ dialog.cont.pane(t -> replay.packetCount.keys().toArray().each(b -> -+ t.add(Net.newPacket((byte)b).getClass().getSimpleName() + " " + replay.packetCount.get(b)).row())).growX().row(); -+ dialog.addCloseButton(); -+ dialog.show(); -+ } -+} diff --git a/patches/client/0038-API-VarsX.isLoader.patch b/patches/client/0038-API-VarsX.isLoader.patch index 97f5e0002002..95c762648f29 100644 --- a/patches/client/0038-API-VarsX.isLoader.patch +++ b/patches/client/0038-API-VarsX.isLoader.patch @@ -3,32 +3,3 @@ From: way-zer Date: Tue, 26 Mar 2024 19:50:47 +0800 Subject: [PATCH] API: VarsX.isLoader ---- - core/src/mindustryX/VarsX.java | 5 +++++ - core/src/mindustryX/loader/Main.java | 1 + - 2 files changed, 6 insertions(+) - create mode 100644 core/src/mindustryX/VarsX.java - -diff --git a/core/src/mindustryX/VarsX.java b/core/src/mindustryX/VarsX.java -new file mode 100644 -index 0000000000000000000000000000000000000000..69a28133bf43daa1b1e507ff5e2de415f5a45241 ---- /dev/null -+++ b/core/src/mindustryX/VarsX.java -@@ -0,0 +1,5 @@ -+package mindustryX; -+ -+public class VarsX{ -+ public static boolean isLoader = false; -+} -diff --git a/core/src/mindustryX/loader/Main.java b/core/src/mindustryX/loader/Main.java -index ffe311d2a8e095c1e3262cdd2ede50ff42235c61..13344f03b3dd53e5de64b3bf2af376e6178614b3 100644 ---- a/core/src/mindustryX/loader/Main.java -+++ b/core/src/mindustryX/loader/Main.java -@@ -106,6 +106,7 @@ public class Main extends Mod{ - Log.logger = new NoopLogHandler(); - try{ - Thread.currentThread().setContextClassLoader(classLoader); -+ Reflect.set(classLoader.loadClass("mindustryX.VarsX"), "isLoader", true); - impl.launch(classLoader); - Thread.currentThread().interrupt(); - }catch(Exception e){ diff --git a/patches/client/0039-UI-Settings.patch b/patches/client/0039-UI-Settings.patch index d44d601973e0..dc907acf2aa6 100644 --- a/patches/client/0039-UI-Settings.patch +++ b/patches/client/0039-UI-Settings.patch @@ -16,11 +16,8 @@ API(Settings) toggle and cycle way-zer on 2024/7/1 --- core/assets/bundles/bundle-mdtx.properties | 37 ++++++++ - .../ui/dialogs/SettingsMenuDialog.java | 87 +++++++++++++----- - core/src/mindustryX/Hooks.java | 2 + - core/src/mindustryX/features/Settings.java | 90 +++++++++++++++++++ - 4 files changed, 192 insertions(+), 24 deletions(-) - create mode 100644 core/src/mindustryX/features/Settings.java + .../ui/dialogs/SettingsMenuDialog.java | 87 ++++++++++++++----- + 2 files changed, 100 insertions(+), 24 deletions(-) diff --git a/core/assets/bundles/bundle-mdtx.properties b/core/assets/bundles/bundle-mdtx.properties index 0cb666446175aa5e952ceaa07669e10a3a023d10..623d778207282303a60e9efc99981131a99eb253 100644 @@ -289,119 +286,3 @@ index 01f7c05a53a570163a1fd83dfd8783c80142ad8c..b93dced65be5c1eea38adc80f8dc2d84 addDesc(prefTable); table.row(); } -diff --git a/core/src/mindustryX/Hooks.java b/core/src/mindustryX/Hooks.java -index 01d7b62ec79aaef21c2bd0bd7dfcc1dc072b057c..a1f9e743989ef91d28eb1dc17dc607e1656aa1b0 100644 ---- a/core/src/mindustryX/Hooks.java -+++ b/core/src/mindustryX/Hooks.java -@@ -6,6 +6,7 @@ import arc.util.*; - import mindustry.*; - import mindustry.gen.*; - import mindustryX.features.*; -+import mindustryX.features.Settings; - - import java.net.*; - import java.util.*; -@@ -15,6 +16,7 @@ public class Hooks implements ApplicationListener{ - public static void beforeInit(){ - Log.infoTag("MindustryX", "Hooks.beforeInit"); - registerBundle(); -+ Settings.addSettings(); - } - - /** invoke after loading, just before `Mod::init` */ -diff --git a/core/src/mindustryX/features/Settings.java b/core/src/mindustryX/features/Settings.java -new file mode 100644 -index 0000000000000000000000000000000000000000..16e86681d53afc40025ca4376d490d1cb574b786 ---- /dev/null -+++ b/core/src/mindustryX/features/Settings.java -@@ -0,0 +1,90 @@ -+package mindustryX.features; -+ -+import arc.*; -+import arc.func.*; -+import arc.scene.style.*; -+import arc.struct.*; -+import mindustry.*; -+import mindustry.game.EventType.*; -+import mindustry.gen.*; -+import mindustry.ui.dialogs.SettingsMenuDialog.*; -+ -+import static mindustry.Vars.maxSchematicSize; -+ -+public class Settings{ -+ public static class LazySettingsCategory extends SettingsCategory{ -+ private final Prov iconProv; -+ -+ public LazySettingsCategory(String name, Prov icon, Cons builder){ -+ super(name, null, builder); -+ iconProv = icon; -+ } -+ -+ public void init(){ -+ icon = iconProv.get(); -+ } -+ } -+ -+ public static final Seq categories = new Seq<>(); -+ -+ public static void addSettings(){ -+ categories.add(new LazySettingsCategory("@settings.category.mindustryX", () -> Icon.box, (c) -> { -+ c.checkPref("showUpdateDialog", true); -+ c.checkPref("githubMirror", false); -+ -+ c.addCategory("gameSettings"); -+ c.checkPref("deadOverlay", false); -+ c.checkPref("invertMapClick", false); -+ -+ c.addCategory("arcReWork"); -+ c.checkPref("replayRecord", false); -+ c.checkPref("menuFloatText", true); -+ c.checkPref("researchViewer", false); -+ c.sliderPref("minimapSize", 140, 40, 400, 10, i -> i + ""); -+ c.sliderPref("maxSchematicSize", 64, 64, 257, 1, v -> { -+ maxSchematicSize = v == 257 ? Integer.MAX_VALUE : v; -+ return v == 257 ? "无限" : String.valueOf(v); -+ }); -+ { -+ var v = Core.settings.getInt("maxSchematicSize"); -+ maxSchematicSize = v == 257 ? Integer.MAX_VALUE : v; -+ } -+ -+ c.addCategory("blockSettings"); -+ c.checkPref("staticShieldsBorder", false); -+ c.checkPref("arcTurretPlaceCheck", false); -+ c.checkPref("arcchoiceuiIcon", false); -+ c.sliderPref("HiddleItemTransparency", 0, 0, 100, 2, i -> i > 0 ? i + "%" : "关闭"); -+ c.sliderPref("overdrive_zone", 0, 0, 100, 2, i -> i > 0 ? i + "%" : "关闭"); -+ c.checkPref("arcPlacementEffect", false); -+ c.sliderPref("blockbarminhealth", 0, 0, 4000, 50, i -> i + "[red]HP"); -+ c.sliderPref("blockRenderLevel", 2, 0, 2, 1, s -> switch(s){ -+ case 0 -> "隐藏全部建筑"; -+ case 1 -> "只显示建筑状态"; -+ default -> "全部显示"; -+ }); -+ -+ c.addCategory("entitySettings"); -+ c.checkPref("bulletShow", true); -+ c.checkPref("showMineBeam".toLowerCase(), true); -+ c.checkPref("noPlayerHitBox", false); -+ c.checkPref("payloadpreview", true); -+ -+ c.addCategory("developerMode"); -+ c.checkPref("renderMerge", true); -+ c.checkPref("renderSort", false); -+ })); -+ Events.on(ClientLoadEvent.class, e -> { -+ categories.each(LazySettingsCategory::init); -+ Vars.ui.settings.getCategories().addAll(categories); -+ }); -+ } -+ -+ public static void toggle(String name){ -+ Core.settings.put(name, !Core.settings.getBool(name)); -+ } -+ -+ public static void cycle(String name, int max){ -+ Core.settings.put(name, (Core.settings.getInt(name) + 1) % max); -+ } -+} diff --git a/patches/client/0040-ARC-bundle-and-settings.patch b/patches/client/0040-ARC-bundle-and-settings.patch index 6c13c7a476f6..5c2bc4e0742d 100644 --- a/patches/client/0040-ARC-bundle-and-settings.patch +++ b/patches/client/0040-ARC-bundle-and-settings.patch @@ -11,10 +11,7 @@ way-zer on 2024/4/20 at 21:59 --- core/assets/bundles/bundle-mdtx.properties | 237 ++++++++++++++++++ .../ui/dialogs/SettingsMenuDialog.java | 28 +-- - core/src/mindustryX/features/ArcOld.java | 219 ++++++++++++++++ - core/src/mindustryX/features/Settings.java | 1 + - 4 files changed, 470 insertions(+), 15 deletions(-) - create mode 100644 core/src/mindustryX/features/ArcOld.java + 2 files changed, 250 insertions(+), 15 deletions(-) diff --git a/core/assets/bundles/bundle-mdtx.properties b/core/assets/bundles/bundle-mdtx.properties index 623d778207282303a60e9efc99981131a99eb253..9d9cbc07ea828ebbcc4557994b36328cf9764e45 100644 @@ -328,240 +325,3 @@ index b93dced65be5c1eea38adc80f8dc2d84c2b3864d..b71452eca5b3527f0c2ee15edd8d6ef0 graphics.checkPref("showweather", true); graphics.checkPref("animatedwater", true); -diff --git a/core/src/mindustryX/features/ArcOld.java b/core/src/mindustryX/features/ArcOld.java -new file mode 100644 -index 0000000000000000000000000000000000000000..47ea958a84d4e7f33241ad98da20db3d061eab78 ---- /dev/null -+++ b/core/src/mindustryX/features/ArcOld.java -@@ -0,0 +1,219 @@ -+package mindustryX.features; -+ -+import arc.func.*; -+import arc.graphics.*; -+import arc.struct.*; -+import arc.util.*; -+import mindustry.gen.*; -+import mindustry.graphics.*; -+import mindustryX.features.Settings.*; -+import mindustryX.features.ui.*; -+ -+import static arc.Core.settings; -+import static mindustry.Vars.*; -+ -+public class ArcOld{ -+ public static void init(Seq categories){ -+ categories.add(new LazySettingsCategory("@settings.arc", () -> Icon.star, (c) -> { -+ c.addCategory("arcHudToolbox"); -+ c.sliderPref("AuxiliaryTable", 0, 0, 3, 1, s -> switch(s){ -+ case 0 -> "关闭"; -+ case 1 -> "左上-右"; -+ case 2 -> "左上-下"; -+ case 3 -> "右上-下"; -+ default -> ""; -+ }); -+ c.checkPref("arcSpecificTable", true); -+ c.checkPref("logicSupport", true); -+ c.checkPref("powerStatistic", true); -+ c.sliderPref("arccoreitems", 3, 0, 3, 1, s -> switch(s){ -+ case 0 -> "不显示"; -+ case 1 -> "资源状态"; -+ case 2 -> "兵种状态"; -+ default -> "显示资源和兵种"; -+ }); -+ c.sliderPref("arcCoreItemsCol", 5, 4, 15, 1, i -> i + "列"); -+ c.checkPref("showQuickToolTable", true); -+ -+ c.addCategory("arcCgameview"); -+ c.checkPref("hoveredTileInfo", false); -+ c.checkPref("alwaysshowdropzone", false); -+ c.checkPref("showFlyerSpawn", false); -+ c.checkPref("showFlyerSpawnLine", false); -+ c.checkPref("bulletShow", true); -+ if(Shaders.shield != null){ -+ c.checkPref("staticShieldsBorder", false); -+ } -+ -+ c.addCategory("arcCDisplayBlock"); -+ c.sliderPref("blockRenderLevel", 2, 0, 2, 1, s -> switch(s){ -+ case 0 -> "隐藏全部建筑"; -+ case 1 -> "只显示建筑状态"; -+ default -> "全部显示"; -+ }); -+ c.checkPref("forceEnableDarkness", true, (b) -> enableDarkness = b); -+ enableDarkness = settings.getBool("forceEnableDarkness"); -+ c.sliderPref("HiddleItemTransparency", 0, 0, 100, 2, i -> i > 0 ? i + "%" : "关闭"); -+ c.sliderPref("overdrive_zone", 0, 0, 100, 2, i -> i > 0 ? i + "%" : "关闭"); -+ c.sliderPref("mend_zone", 0, 0, 100, 2, i -> i > 0 ? i + "%" : "关闭"); -+ c.checkPref("blockdisabled", false); -+ c.checkPref("blockBars", false); -+ c.sliderPref("blockbarminhealth", 0, 0, 4000, 50, i -> i + "[red]HP"); -+ c.checkPref("blockBars_mend", false); -+ c.checkPref("arcdrillmode", false); -+ c.checkPref("arcDrillProgress", false); -+ c.checkPref("arcchoiceuiIcon", false); -+ c.checkPref("arclogicbordershow", true); -+ c.checkPref("arcPlacementEffect", false); -+ -+ c.checkPref("mass_driver_line", true); -+ c.sliderPref("mass_driver_line_interval", 40, 8, 400, 4, i -> i / 8f + "格"); -+ { -+ Cons changed = (t) -> { -+ try{ -+ RenderExt.massDriverLineColor = Color.valueOf(t); -+ }catch(Exception e){ -+ RenderExt.massDriverLineColor = Color.valueOf("ff8c66"); -+ } -+ }; -+ c.textPref("mass_driver_line_color", "ff8c66", changed); -+ changed.get(settings.getString("mass_driver_line_color")); -+ } -+ -+ c.addCategory("arcAddTurretInfo"); -+ c.checkPref("showTurretAmmo", false); -+ c.checkPref("showTurretAmmoAmount", false); -+ c.checkPref("arcTurretPlacementItem", false); -+ c.checkPref("arcTurretPlaceCheck", false); -+ c.sliderPref("turretShowRange", 0, 0, 3, 1, s -> switch(s){ -+ case 0 -> "关闭"; -+ case 1 -> "仅对地"; -+ case 2 -> "仅对空"; -+ case 3 -> "全部"; -+ default -> ""; -+ }); -+ c.checkPref("turretForceShowRange", false); -+ c.sliderPref("turretAlertRange", 0, 0, 30, 1, i -> i > 0 ? i + "格" : "关闭"); -+ c.checkPref("blockWeaponTargetLine", false); -+ c.checkPref("blockWeaponTargetLineWhenIdle", false); -+ -+ c.addCategory("arcAddUnitInfo"); -+ c.checkPref("alwaysShowPlayerUnit", false); -+ -+ c.sliderPref("unitTransparency", 100, 0, 100, 5, i -> i > 0 ? i + "%" : "关闭"); -+ c.sliderPref("unitDrawMinHealth", settings.getInt("minhealth_unitshown", 0), 0, 2500, 50, i -> i + "[red]HP"); -+ -+ c.checkPref("unitHealthBar", false); -+ c.sliderPref("unitBarDrawMinHealth", settings.getInt("minhealth_unithealthbarshown", 0), 0, 2500, 100, i -> i + "[red]HP"); -+ -+ -+ c.sliderPref("unitWeaponRange", settings.getInt("unitAlertRange", 0), 0, 30, 1, s -> switch(s){ -+ case 0 -> "关闭"; -+ case 30 -> "一直开启"; -+ default -> s + "格"; -+ }); -+ c.sliderPref("unitWeaponRangeAlpha", settings.getInt("unitweapon_range", 0), 0, 100, 1, i -> i > 0 ? i + "%" : "关闭"); -+ -+ c.checkPref("unitWeaponTargetLine", false); -+ c.checkPref("showminebeam", true); -+ c.checkPref("unitItemCarried", true); -+ c.checkPref("unithitbox", false); -+ c.checkPref("unitLogicMoveLine", false); -+ c.checkPref("unitLogicTimerBars", false); -+ c.checkPref("arcBuildInfo", false); -+ c.checkPref("unitbuildplan", false); -+ c.checkPref("arcCommandTable", true); -+ c.checkPref("alwaysShowUnitRTSAi", false); -+ c.sliderPref("rtsWoundUnit", 0, 0, 100, 2, s -> s + "%"); -+ -+ c.addCategory("arcPlayerEffect"); -+ c.textPref("playerEffectColor", "ffd37f"); -+ c.sliderPref("unitTargetType", 0, 0, 5, 1, s -> switch(s){ -+ case 0 -> "关闭"; -+ case 1 -> "虚圆"; -+ case 2 -> "攻击"; -+ case 3 -> "攻击去边框"; -+ case 4 -> "圆十字"; -+ case 5 -> "十字"; -+ default -> s + ""; -+ }); -+ c.sliderPref("superUnitEffect", 0, 0, 2, 1, s -> switch(s){ -+ case 0 -> "关闭"; -+ case 1 -> "独一无二"; -+ case 2 -> "全部玩家"; -+ default -> s + ""; -+ }); -+ c.sliderPref("playerEffectCurStroke", 0, 1, 30, 1, i -> (float)i / 10f + "Pixel(s)"); -+ -+ -+ c.addCategory("arcShareinfo"); -+ c.sliderPref("chatValidType", 0, 0, 3, 1, s -> switch(s){ -+ case 0 -> "原版模式"; -+ case 1 -> "纯净聊天"; -+ case 2 -> "服务器记录"; -+ case 3 -> "全部记录"; -+ default -> s + ""; -+ }); -+ c.checkPref("ShowInfoPopup", true); -+ c.checkPref("arcShareWaveInfo", false); -+ c.checkPref("arcAlwaysTeamColor", false); -+ c.checkPref("arcSelfName", false); -+ -+ c.addCategory("arcWeakCheat"); -+ c.checkPref("save_more_map", false); -+ c.checkPref("forceIgnoreAttack", false); -+ c.checkPref("allUnlocked", false); -+ c.checkPref("worldCreator", false); -+ c.checkPref("overrideSkipWave", false); -+ c.checkPref("forceConfigInventory", false); -+ c.addCategory("arcStrongCheat"); -+ c.checkPref("showOtherTeamResource", false); -+ c.checkPref("showOtherTeamState", false); -+ c.checkPref("playerNeedShooting", false); -+ })); -+ categories.add(new LazySettingsCategory("@settings.specmode", () -> Icon.info, (c) -> { -+ c.addCategory("moreContent"); -+ c.checkPref("override_boss_shown", false); -+ c.sliderPref("minimapSize", 140, 40, 400, 10, i -> i + ""); -+ c.sliderPref("maxSchematicSize", 64, 64, 257, 1, v -> { -+ maxSchematicSize = v == 257 ? Integer.MAX_VALUE : v; -+ return v == 257 ? "无限" : String.valueOf(v); -+ }); -+ c.sliderPref("itemSelectionHeight", 4, 4, 12, i -> i + "行"); -+ c.sliderPref("itemSelectionWidth", 4, 4, 12, i -> i + "列"); -+ c.sliderPref("blockInventoryWidth", 3, 3, 16, i -> i + ""); -+ c.sliderPref("editorBrush", 4, 3, 12, i -> i + ""); -+ c.checkPref("autoSelSchematic", false); -+ c.checkPref("researchViewer", false); -+ -+ -+ c.addCategory("arcRadar"); -+ c.sliderPref("radarMode", 0, 0, 30, 1, s -> switch(s){ -+ case 0 -> "关闭"; -+ case 30 -> "一键开关"; -+ default -> "[lightgray]x[white]" + Strings.autoFixed(s * 0.2f, 1) + "倍搜索"; -+ }); -+ c.sliderPref("radarSize", 0, 0, 50, 1, s -> { -+ if(s == 0) return "固定大小"; -+ return "[lightgray]x[white]" + Strings.autoFixed(s * 0.1f, 1) + "倍"; -+ }); -+ -+ c.addCategory("personalized"); -+ c.checkPref("menuFloatText", true); -+ c.checkPref("colorizedContent", false); -+ c.textPref("arcBackgroundPath", ""); -+ -+ c.addCategory("developerMode"); -+ c.checkPref("rotateCanvas", false); -+ c.checkPref("limitupdate", false, v -> { -+ if(!v) return; -+ settings.put("limitupdate", false); -+ ui.showConfirm("确认开启限制更新", "此功能可以大幅提升fps,但会导致视角外的一切停止更新\n在服务器里会造成不同步\n强烈不建议在单人开启\n\n[darkgray]在帧数和体验里二选一", () -> { -+ settings.put("limitupdate", true); -+ }); -+ }); -+ c.sliderPref("limitdst", 10, 0, 100, 1, s -> s + "格"); -+ c.checkPref("developMode", false); -+ })); -+ } -+} -diff --git a/core/src/mindustryX/features/Settings.java b/core/src/mindustryX/features/Settings.java -index 16e86681d53afc40025ca4376d490d1cb574b786..e39fe4572259cc157de0351765ff7b6f75ad1696 100644 ---- a/core/src/mindustryX/features/Settings.java -+++ b/core/src/mindustryX/features/Settings.java -@@ -74,6 +74,7 @@ public class Settings{ - c.checkPref("renderMerge", true); - c.checkPref("renderSort", false); - })); -+ ArcOld.init(categories); - Events.on(ClientLoadEvent.class, e -> { - categories.each(LazySettingsCategory::init); - Vars.ui.settings.getCategories().addAll(categories); diff --git a/patches/client/0041-C-floatLabel.patch b/patches/client/0041-C-floatLabel.patch index f995e9f34a3b..3518db8d5161 100644 --- a/patches/client/0041-C-floatLabel.patch +++ b/patches/client/0041-C-floatLabel.patch @@ -10,10 +10,8 @@ Content-Transfer-Encoding: 8bit --- core/assets/labels | 74 +++++++++++++++++++ .../mindustry/ui/fragments/MenuFragment.java | 7 ++ - .../features/ui/MenuFloatLabel.java | 38 ++++++++++ - 3 files changed, 119 insertions(+) + 2 files changed, 81 insertions(+) create mode 100644 core/assets/labels - create mode 100644 core/src/mindustryX/features/ui/MenuFloatLabel.java diff --git a/core/assets/labels b/core/assets/labels new file mode 100644 @@ -134,47 +132,3 @@ index 497b2077e17316a2582a5abdea274c4f773b99fd..593d8e2bfbb8c1e156b998dc44d35543 Draw.color(); Draw.rect(logo, fx, fy, logow, logoh); -diff --git a/core/src/mindustryX/features/ui/MenuFloatLabel.java b/core/src/mindustryX/features/ui/MenuFloatLabel.java -new file mode 100644 -index 0000000000000000000000000000000000000000..a0423b616af0ac1499339d03f44cae839115725a ---- /dev/null -+++ b/core/src/mindustryX/features/ui/MenuFloatLabel.java -@@ -0,0 +1,38 @@ -+package mindustryX.features.ui; -+ -+import arc.*; -+import arc.math.*; -+import arc.scene.*; -+import arc.scene.ui.*; -+import arc.scene.ui.layout.*; -+import arc.util.*; -+ -+public class MenuFloatLabel extends WidgetGroup{ -+ private static final float period = 75f, varSize = 0.8f; -+ private final Label textLabel; -+ private final String[] labels; -+ public float baseScale = 1f; -+ private long lastVisible; -+ -+ public MenuFloatLabel(){ -+ super(); -+ setTransform(true); -+ setRotation(20); -+ addChild(textLabel = new Label("")); -+ visible(() -> Core.settings.getBool("menuFloatText", true)); -+ textLabel.setAlignment(Align.center); -+ update(() -> { -+ textLabel.setFontScale(baseScale * Math.abs(Time.time % period / period - 0.5f) * varSize + 1); -+ if(Core.graphics.getFrameId() - lastVisible > 1){ -+ randomLabel(); -+ } -+ lastVisible = Core.graphics.getFrameId(); -+ }); -+ labels = Core.files.internal("labels").readString("UTF-8").replace("\r", "").replace("\\n", "\n").replace("/n", "\n").split("\n"); -+ randomLabel(); -+ } -+ -+ public void randomLabel(){ -+ Timer.schedule(() -> textLabel.setText("[yellow]" + labels[new Rand().random(0, labels.length - 1)]), 0.11f); -+ } -+} diff --git a/patches/client/0043-C-AutoUpdate.patch b/patches/client/0043-C-AutoUpdate.patch index 23cbfaaa33ef..26bdec287ec8 100644 --- a/patches/client/0043-C-AutoUpdate.patch +++ b/patches/client/0043-C-AutoUpdate.patch @@ -4,12 +4,9 @@ Date: Tue, 26 Mar 2024 22:36:18 +0800 Subject: [PATCH] C: AutoUpdate --- - core/src/mindustry/net/BeControl.java | 10 +- - .../mindustry/ui/fragments/MenuFragment.java | 21 +-- - core/src/mindustryX/Hooks.java | 2 + - core/src/mindustryX/features/AutoUpdate.kt | 170 ++++++++++++++++++ - 4 files changed, 182 insertions(+), 21 deletions(-) - create mode 100644 core/src/mindustryX/features/AutoUpdate.kt + core/src/mindustry/net/BeControl.java | 10 +-------- + .../mindustry/ui/fragments/MenuFragment.java | 21 ++++++++----------- + 2 files changed, 10 insertions(+), 21 deletions(-) diff --git a/core/src/mindustry/net/BeControl.java b/core/src/mindustry/net/BeControl.java index 2b96a3f231a78f6edb51d9459a1cbbcdeaa6d68e..7172b63844e32b48537d1b53a4da7db633bb4998 100644 @@ -84,193 +81,3 @@ index 593d8e2bfbb8c1e156b998dc44d3554317e171bf..25d953a861fbcddea24225be733445e9 })); } -diff --git a/core/src/mindustryX/Hooks.java b/core/src/mindustryX/Hooks.java -index a1f9e743989ef91d28eb1dc17dc607e1656aa1b0..390913c56aa6088fa3092826cdc772073883113d 100644 ---- a/core/src/mindustryX/Hooks.java -+++ b/core/src/mindustryX/Hooks.java -@@ -23,6 +23,8 @@ public class Hooks implements ApplicationListener{ - @Override - public void init(){ - Log.infoTag("MindustryX", "Hooks.init"); -+ if(AutoUpdate.INSTANCE.getActive()) -+ AutoUpdate.INSTANCE.checkUpdate(); - if(!Vars.headless){ - RenderExt.init(); - TimeControl.init(); -diff --git a/core/src/mindustryX/features/AutoUpdate.kt b/core/src/mindustryX/features/AutoUpdate.kt -new file mode 100644 -index 0000000000000000000000000000000000000000..fffc9f5b78420be48f50e636c89c82b9c1a52470 ---- /dev/null -+++ b/core/src/mindustryX/features/AutoUpdate.kt -@@ -0,0 +1,170 @@ -+package mindustryX.features -+ -+import arc.Core -+import arc.Events -+import arc.files.Fi -+import arc.util.Align -+import arc.util.Http -+import arc.util.Log -+import arc.util.OS -+import arc.util.io.Streams -+import arc.util.serialization.Jval -+import mindustry.Vars -+import mindustry.core.Version -+import mindustry.game.EventType -+import mindustry.gen.Icon -+import mindustry.graphics.Pal -+import mindustry.net.BeControl -+import mindustry.ui.Bar -+import mindustry.ui.dialogs.BaseDialog -+import mindustryX.VarsX -+import mindustryX.features.ui.Format -+ -+object AutoUpdate { -+ data class Release(val tag: String, val version: String, val json: Jval) { -+ data class Asset(val name: String, val url: String) -+ -+ fun matchCurrent(): Boolean { -+ return tag == "$currentBranch-build" || json.getString("body", "").contains("REPLACE $currentBranch") -+ } -+ -+ fun findAsset(): Asset? { -+ val assets = json.get("assets").asArray().asIterable() -+ .map { Asset(it.getString("name"), it.getString("browser_download_url", "")) } -+ .sortedByDescending { it.name } -+ return assets.firstOrNull { -+ when { -+ VarsX.isLoader -> it.name.endsWith("loader.dex.jar") -+ OS.isAndroid -> it.name.endsWith(".apk") -+ else -> it.name.endsWith("Desktop.jar") -+ } -+ } -+ } -+ } -+ -+ val active get() = !Version.mdtXBuild.endsWith("-dev") -+ -+ val repo = "TinyLake/MindustryX-work" -+ var versions = emptyList() -+ val currentBranch get() = Version.mdtXBuild.split('-', limit = 2).getOrNull(1) -+ var latest: Release? = null -+ val newVersion: Release? get() = latest?.takeIf { it.version > Version.mdtXBuild } -+ -+ fun checkUpdate() { -+ if (versions.isNotEmpty()) return -+ Http.get("https://api.github.com/repos/$repo/releases") -+ .timeout(10000) -+ .error { Log.warn("Fetch releases fail: $it") } -+ .submit { res -> -+ val json = Jval.read(res.resultAsString) -+ versions = json.asArray().map { -+ Release(it.getString("tag_name"), it.getString("name"), it) -+ }.toList() -+ Core.app.post(::fetchSuccess) -+ } -+ } -+ -+ private fun fetchSuccess() { -+ val available = versions.filter { it.matchCurrent() } -+ latest = available.maxByOrNull { it.version } ?: return -+ newVersion?.takeIf { Core.settings.getBool("showUpdateDialog", true) }?.let { -+ if (Vars.clientLoaded) return showDialog(newVersion) -+ Events.on(EventType.ClientLoadEvent::class.java) { -+ Vars.ui.showConfirm("检测到新版MindustryX!\n打开更新列表?", ::showDialog) -+ } -+ } -+ } -+ -+ @JvmOverloads -+ fun showDialog(version: Release? = latest) { -+ checkUpdate() -+ val dialog = BaseDialog("自动更新") -+ dialog.cont.apply { -+ add("当前版本号: ${Version.mdtXBuild}").labelAlign(Align.center).width(500f).row() -+ newVersion?.let { -+ add("新版本: ${it.version}").labelAlign(Align.center).width(500f).row() -+ } -+ if (versions.isEmpty()) { -+ add("检查更新失败,请稍后再试").row() -+ return@apply -+ } -+ versions.forEach { -+ check(it.version, version == it) { _ -> -+ dialog.hide() -+ showDialog(it) -+ }.left().row() -+ } -+ if (version == null) { -+ add("你已是最新版本,不需要更新!") -+ return@apply -+ } -+ -+ val asset = version.findAsset() -+ var url = asset?.url.orEmpty() -+ field(url) { url = it }.fillX() -+ button("♐") { -+ if (!Core.app.openURI(url)) { -+ Vars.ui.showErrorMessage("打开失败,网址已复制到粘贴板\n请自行在浏览器打开") -+ Core.app.clipboardText = url -+ } -+ }.width(50f) -+ -+ row().button("自动下载更新") { -+ if (asset == null) return@button -+ if (!VarsX.isLoader && OS.isAndroid) { -+ Vars.ui.showErrorMessage("目前不支持Apk自动安卓,请在浏览器打开后手动安装") -+ return@button -+ } -+ startDownload(asset.copy(url = url)) -+ }.fillX() -+ } -+ dialog.addCloseButton() -+ dialog.show() -+ } -+ -+ private fun startDownload(asset: Release.Asset) { -+ val file = Vars.bebuildDirectory.child(asset.name) -+ -+ var progress = 0f -+ var length = 0f -+ var canceled = false -+ val dialog = BaseDialog("@be.updating").apply { -+ cont.add(Bar({ -+ if (length == 0f) return@Bar Core.bundle["be.updating"] -+ with(Format(fixDecimals = true)) { "${format(progress * length)}/${format(length)}MB" } -+ }, { Pal.accent }, { progress })).width(400f).height(70f) -+ buttons.button("@cancel", Icon.cancel) { -+ canceled = true -+ hide() -+ }.size(210f, 64f) -+ setFillParent(false) -+ show() -+ } -+ Http.get(asset.url, { res -> -+ length = res.contentLength.toFloat() / 1024 / 1024 -+ file.write(false, 4096).use { out -> -+ Streams.copyProgress(res.resultAsStream, out, res.contentLength, 4096) { progress = it } -+ } -+ if (canceled) return@get -+ endDownload(file) -+ }) { -+ dialog.hide() -+ Vars.ui.showException(it) -+ } -+ } -+ -+ private fun endDownload(file: Fi) { -+ if (VarsX.isLoader) { -+ Vars.mods.importMod(file) -+ file.delete() -+ Vars.ui.mods.show() -+ return -+ } -+ val fileDest = if (OS.hasProp("becopy")) Fi.get(OS.prop("becopy")) -+ else Fi.get(BeControl::class.java.protectionDomain.codeSource.location.toURI().path) -+ val args = if (OS.isMac) arrayOf(Vars.javaPath, "-XstartOnFirstThread", "-DlastBuild=" + Version.build, "-Dberestart", "-Dbecopy=" + fileDest.absolutePath(), "-jar", file.absolutePath()) -+ else arrayOf(Vars.javaPath, "-DlastBuild=" + Version.build, "-Dberestart", "-Dbecopy=" + fileDest.absolutePath(), "-jar", file.absolutePath()) -+ Runtime.getRuntime().exec(args) -+ Core.app.exit() -+ } -+} -\ No newline at end of file diff --git a/patches/client/0049-API-Hooks-pollKeys.patch b/patches/client/0049-API-Hooks-pollKeys.patch index 496e39abb80c..f483104a4b6c 100644 --- a/patches/client/0049-API-Hooks-pollKeys.patch +++ b/patches/client/0049-API-Hooks-pollKeys.patch @@ -5,8 +5,7 @@ Subject: [PATCH] API(Hooks) pollKeys --- core/src/mindustry/input/Binding.java | 3 +++ - core/src/mindustryX/Hooks.java | 9 +++++++++ - 2 files changed, 12 insertions(+) + 1 file changed, 3 insertions(+) diff --git a/core/src/mindustry/input/Binding.java b/core/src/mindustry/input/Binding.java index 039829ebd447f97c72917b91fd52f3fef6aeccfa..8376e3f5a5f4aef6c5397567aaba13780eda86ab 100644 @@ -22,23 +21,3 @@ index 039829ebd447f97c72917b91fd52f3fef6aeccfa..8376e3f5a5f4aef6c5397567aaba1378 ; private final KeybindValue defaultValue; -diff --git a/core/src/mindustryX/Hooks.java b/core/src/mindustryX/Hooks.java -index 390913c56aa6088fa3092826cdc772073883113d..66c54877a8c14ea43294c2db818583fcd7191351 100644 ---- a/core/src/mindustryX/Hooks.java -+++ b/core/src/mindustryX/Hooks.java -@@ -54,6 +54,15 @@ public class Hooks implements ApplicationListener{ - return message; - } - -+ @Override -+ public void update(){ -+ pollKeys(); -+ } -+ -+ public static void pollKeys(){ -+ if(Vars.headless || Core.scene.hasField()) return; -+ } -+ - private static void registerBundle(){ - //MDTX: bundle overwrite - try{ diff --git a/patches/client/0050-C-RenderExt-unitHide.patch b/patches/client/0050-C-RenderExt-unitHide.patch index 2859ad3740d3..6da53b39dd58 100644 --- a/patches/client/0050-C-RenderExt-unitHide.patch +++ b/patches/client/0050-C-RenderExt-unitHide.patch @@ -4,11 +4,9 @@ Date: Thu, 4 Apr 2024 14:56:59 +0800 Subject: [PATCH] C(RenderExt) unitHide --- - core/src/mindustry/input/Binding.java | 2 +- - core/src/mindustry/input/InputHandler.java | 3 ++- - core/src/mindustryX/Hooks.java | 4 ++++ - core/src/mindustryX/features/RenderExt.java | 4 +++- - 4 files changed, 10 insertions(+), 3 deletions(-) + core/src/mindustry/input/Binding.java | 2 +- + core/src/mindustry/input/InputHandler.java | 3 ++- + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/core/src/mindustry/input/Binding.java b/core/src/mindustry/input/Binding.java index 8376e3f5a5f4aef6c5397567aaba13780eda86ab..e06b393ff7e9de55181a2c069e8a16b37c021bbb 100644 @@ -44,47 +42,3 @@ index 5079c0afc523f36b870527b580c6b4f2bfe565c2..9be58ec46ccab9a86d84f75e05e200c0 unit.hitbox(Tmp.r1); Tmp.r1.grow(6f); if(Tmp.r1.contains(Core.input.mouseWorld())){ -diff --git a/core/src/mindustryX/Hooks.java b/core/src/mindustryX/Hooks.java -index 66c54877a8c14ea43294c2db818583fcd7191351..af7d31b4674b0efb198643fdfa7dc8b65e17d38a 100644 ---- a/core/src/mindustryX/Hooks.java -+++ b/core/src/mindustryX/Hooks.java -@@ -5,6 +5,7 @@ import arc.files.*; - import arc.util.*; - import mindustry.*; - import mindustry.gen.*; -+import mindustry.input.*; - import mindustryX.features.*; - import mindustryX.features.Settings; - -@@ -61,6 +62,9 @@ public class Hooks implements ApplicationListener{ - - public static void pollKeys(){ - if(Vars.headless || Core.scene.hasField()) return; -+ if(Core.input.keyTap(Binding.toggle_unit)){ -+ RenderExt.unitHide = !RenderExt.unitHide; -+ } - } - - private static void registerBundle(){ -diff --git a/core/src/mindustryX/features/RenderExt.java b/core/src/mindustryX/features/RenderExt.java -index 6cd82be98b657e3aa36b29c46d6d172f5f4d9dd6..d09842fd55f775dc9c6a238ba98e04638540b8fd 100644 ---- a/core/src/mindustryX/features/RenderExt.java -+++ b/core/src/mindustryX/features/RenderExt.java -@@ -25,6 +25,8 @@ public class RenderExt{ - public static int hiddenItemTransparency; - public static int blockBarMinHealth; - public static float overdriveZoneTransparency, mendZoneTransparency; -+ -+ public static boolean unitHide = false; - private static Effect placementEffect; - - public static void init(){ -@@ -41,7 +43,7 @@ public class RenderExt{ - - Events.run(Trigger.preDraw, () -> { - bulletShow = Core.settings.getBool("bulletShow"); -- showMineBeam = Core.settings.getBool("showminebeam"); -+ showMineBeam = !unitHide && Core.settings.getBool("showminebeam"); - displayAllMessage = Core.settings.getBool("displayallmessage"); - arcChoiceUiIcon = Core.settings.getBool("arcchoiceuiIcon"); - researchViewer = Core.settings.getBool("researchViewer"); diff --git a/patches/client/0051-C-RenderExt-logicDisplayNoBorder-arcDrillMode.patch b/patches/client/0051-C-RenderExt-logicDisplayNoBorder-arcDrillMode.patch index 6f5a82759396..352798929df3 100644 --- a/patches/client/0051-C-RenderExt-logicDisplayNoBorder-arcDrillMode.patch +++ b/patches/client/0051-C-RenderExt-logicDisplayNoBorder-arcDrillMode.patch @@ -9,10 +9,9 @@ Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- - .../world/blocks/logic/LogicDisplay.java | 7 ++++- - .../world/blocks/production/Drill.java | 2 ++ - core/src/mindustryX/features/RenderExt.java | 27 +++++++++++++++++++ - 3 files changed, 35 insertions(+), 1 deletion(-) + core/src/mindustry/world/blocks/logic/LogicDisplay.java | 7 ++++++- + core/src/mindustry/world/blocks/production/Drill.java | 2 ++ + 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/core/src/mindustry/world/blocks/logic/LogicDisplay.java b/core/src/mindustry/world/blocks/logic/LogicDisplay.java index 1816245ad2a2e21501da4817c00ba8ba0de04f9f..1db6409a264c5977e4ccfbf7709d1db430ac31e6 100644 @@ -66,77 +65,3 @@ index 1ca61ee0140d29c28239ed87a165f1fedf11cd97..5a7b4ccabac13d75cd273904d9528e9b if(dominantItem != null){ float dx = x - size * tilesize/2f, dy = y + size * tilesize/2f, s = iconSmall / 4f; Draw.mixcol(Color.darkGray, 1f); -diff --git a/core/src/mindustryX/features/RenderExt.java b/core/src/mindustryX/features/RenderExt.java -index d09842fd55f775dc9c6a238ba98e04638540b8fd..43fe44b6e0e7793d1e88a92a06fe010aa5968196 100644 ---- a/core/src/mindustryX/features/RenderExt.java -+++ b/core/src/mindustryX/features/RenderExt.java -@@ -3,16 +3,19 @@ package mindustryX.features; - import arc.*; - import arc.graphics.*; - import arc.graphics.g2d.*; -+import arc.math.*; - import arc.util.*; - import mindustry.entities.*; - import mindustry.game.EventType.*; - import mindustry.gen.*; - import mindustry.graphics.*; -+import mindustry.type.*; - import mindustry.world.*; - import mindustry.world.blocks.defense.*; - import mindustry.world.blocks.defense.turrets.*; - import mindustry.world.blocks.logic.*; - import mindustry.world.blocks.logic.MessageBlock.*; -+import mindustry.world.blocks.production.Drill.*; - import mindustry.world.blocks.storage.*; - - import static mindustry.Vars.tilesize; -@@ -25,6 +28,7 @@ public class RenderExt{ - public static int hiddenItemTransparency; - public static int blockBarMinHealth; - public static float overdriveZoneTransparency, mendZoneTransparency; -+ public static boolean logicDisplayNoBorder, arcDrillMode; - - public static boolean unitHide = false; - private static Effect placementEffect; -@@ -52,6 +56,8 @@ public class RenderExt{ - blockBarMinHealth = Core.settings.getInt("blockbarminhealth"); - overdriveZoneTransparency = Core.settings.getInt("overdrive_zone") / 100f; - mendZoneTransparency = Core.settings.getInt("mend_zone") / 100f; -+ logicDisplayNoBorder = Core.settings.getBool("arclogicbordershow"); -+ arcDrillMode = Core.settings.getBool("arcdrillmode"); - }); - Events.run(Trigger.draw, RenderExt::draw); - Events.on(TileChangeEvent.class, RenderExt::onSetBlock); -@@ -70,6 +76,8 @@ public class RenderExt{ - block.drawBase(tile); - if(displayAllMessage && build instanceof MessageBuild) - Draw.draw(Layer.overlayUI - 0.1f, build::drawSelect); -+ if(arcDrillMode && build instanceof DrillBuild drill) -+ arcDrillModeDraw(block, drill); - } - - private static void placementEffect(float x, float y, float lifetime, float range, Color color){ -@@ -94,4 +102,23 @@ public class RenderExt{ - placementEffect(build.x, build.y, 120f, t.range, t.mapColor); - } - } -+ -+ /** 在转头旁边显示矿物类型 */ -+ private static void arcDrillModeDraw(Block block, DrillBuild build){ -+ Item dominantItem = build.dominantItem; -+ if(dominantItem == null) return; -+ int size = block.size; -+ float dx = build.x - size * tilesize / 2f + 5, dy = build.y - size * tilesize / 2f + 5; -+ float iconSize = 5f; -+ Draw.rect(dominantItem.fullIcon, dx, dy, iconSize, iconSize); -+ Draw.reset(); -+ -+ float eff = Mathf.lerp(0, 1, Math.min(1f, (float)build.dominantItems / (size * size))); -+ if(eff < 0.9f){ -+ Draw.alpha(0.5f); -+ Draw.color(dominantItem.color); -+ Lines.stroke(1f); -+ Lines.arc(dx, dy, iconSize * 0.75f, eff); -+ } -+ } - } diff --git a/patches/client/0053-FC-MarkerType.patch b/patches/client/0053-FC-MarkerType.patch index 55d2a964fa93..a5056134c8f9 100644 --- a/patches/client/0053-FC-MarkerType.patch +++ b/patches/client/0053-FC-MarkerType.patch @@ -10,13 +10,9 @@ Content-Transfer-Encoding: 8bit 支持\t前缀 way-zer on 2024/6/10 --- - core/src/mindustry/input/Binding.java | 2 + - .../mindustry/ui/fragments/ChatFragment.java | 4 +- - core/src/mindustryX/Hooks.java | 9 + - core/src/mindustryX/features/MarkerType.java | 225 ++++++++++++++++++ - core/src/mindustryX/features/UIExt.java | 23 +- - 5 files changed, 255 insertions(+), 8 deletions(-) - create mode 100644 core/src/mindustryX/features/MarkerType.java + core/src/mindustry/input/Binding.java | 2 ++ + core/src/mindustry/ui/fragments/ChatFragment.java | 4 ++-- + 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/core/src/mindustry/input/Binding.java b/core/src/mindustry/input/Binding.java index e06b393ff7e9de55181a2c069e8a16b37c021bbb..f0f548f7e432ac6c206be6689b0c15489326f9a9 100644 @@ -53,301 +49,3 @@ index 7a968f1c21275eb4a571350adfcf59df1fa37df9..89b9a23ce2bb850df5c7ae99a5f96475 normal(""), team("/t"), admin("/a", player::admin) -diff --git a/core/src/mindustryX/Hooks.java b/core/src/mindustryX/Hooks.java -index af7d31b4674b0efb198643fdfa7dc8b65e17d38a..3d1491a497b4f3a7dad96620cdc6638376938b4d 100644 ---- a/core/src/mindustryX/Hooks.java -+++ b/core/src/mindustryX/Hooks.java -@@ -52,6 +52,9 @@ public class Hooks implements ApplicationListener{ - - public static @Nullable String onHandleSendMessage(String message, @Nullable Player sender){ - if(message == null) return null; -+ if(Vars.ui != null){ -+ if(MarkerType.resolveMessage(message)) return message; -+ } - return message; - } - -@@ -65,6 +68,12 @@ public class Hooks implements ApplicationListener{ - if(Core.input.keyTap(Binding.toggle_unit)){ - RenderExt.unitHide = !RenderExt.unitHide; - } -+ if(Core.input.keyTap(Binding.lockonLastMark)){ -+ MarkerType.lockOnLastMark(); -+ } -+ if(Core.input.keyTap(Binding.point)){ -+ MarkerType.selected.markWithMessage(Core.input.mouseWorld()); -+ } - } - - private static void registerBundle(){ -diff --git a/core/src/mindustryX/features/MarkerType.java b/core/src/mindustryX/features/MarkerType.java -new file mode 100644 -index 0000000000000000000000000000000000000000..58a3232027734be1786229aa86b626c4eb18d7e7 ---- /dev/null -+++ b/core/src/mindustryX/features/MarkerType.java -@@ -0,0 +1,225 @@ -+package mindustryX.features; -+ -+import arc.*; -+import arc.func.*; -+import arc.graphics.*; -+import arc.graphics.g2d.*; -+import arc.math.*; -+import arc.math.geom.*; -+import arc.scene.ui.layout.*; -+import arc.struct.*; -+import arc.util.*; -+import mindustry.*; -+import mindustry.entities.*; -+import mindustry.game.EventType.*; -+import mindustry.gen.*; -+import mindustry.graphics.*; -+import mindustryX.features.func.*; -+import mindustryX.features.ui.*; -+ -+import java.util.regex.*; -+ -+import static arc.graphics.g2d.Draw.color; -+import static arc.graphics.g2d.Lines.stroke; -+import static mindustry.Vars.*; -+ -+public class MarkerType{ -+ private static final Pattern posPattern = Pattern.compile("(?<[A-Za-z]+>)?\\((?\\d+),(?\\d+)\\)"); -+ /** 冷却时间 */ -+ public static final float heatTime = 60f; -+ /** 滞留时间 */ -+ public static final float retainTime = 1800f; -+ public static MarkerType mark, gatherMark, attackMark, defenseMark, quesMark; -+ public static Seq allTypes; -+ public static MarkerType selected = mark; -+ private static MarkElement last; -+ -+ public static void init(){ -+ mark = new MarkerType("Mark", new Effect(1800, e -> { -+ color(e.color); -+ stroke(2f); -+ Lines.circle(e.x, e.y, 1f * tilesize); -+ stroke(e.fout() * 1.5f + 0.5f); -+ Lines.circle(e.x, e.y, 8f + e.finpow() * 92f); -+ //Drawf.arrow(player.x, player.y, e.x, e.y, 5f * tilesize, 4f, Pal.command); -+ }), Color.valueOf("eab678")); -+ gatherMark = new MarkerType("Gather", new Effect(1800, e -> { -+ color(e.color, 0.8f); -+ stroke(2f); -+ Lines.circle(e.x, e.y, 4f * tilesize); -+ stroke(1f); -+ Lines.circle(e.x, e.y, 4f * tilesize * (e.finpow() * 8f - (int)(e.finpow() * 8f))); -+ for(int j = 0; j < 4; j++){ -+ Draw.alpha(Math.min(1f, j - e.fout() * 3)); -+ if(e.fout() * 3 < j){ -+ for(int i = 0; i < 8; i++){ -+ float rot = i * 45f; -+ float radius = 4f * tilesize + j * 6f + 4f; -+ drawSimpleArrow(e.x + Angles.trnsx(rot, radius), e.y + Angles.trnsy(rot, radius), e.x, e.y, 4f, 2f); -+ } -+ } -+ } -+ Draw.color(); -+ }), Color.cyan); -+ attackMark = new MarkerType("Attack", new Effect(1800, e -> { -+ color(e.color); -+ stroke(2f); -+ Lines.circle(e.x, e.y, 1f * tilesize); -+ float radius = 20f + e.finpow() * 80f; -+ Lines.circle(e.x, e.y, radius); -+ for(int i = 0; i < 4; i++){ -+ float rot = i * 90f + 45f + (-Time.time) % 360f; -+ drawSimpleArrow(e.x + Angles.trnsx(rot, radius), e.y + Angles.trnsy(rot, radius), e.x, e.y, 6f + 4 * e.finpow(), 2f + 4 * e.finpow()); -+ } -+ }), Color.valueOf("#DC143C")); -+ defenseMark = new MarkerType("Defend", new Effect(1800, e -> { -+ color(Pal.heal); -+ if(e.fin() < 0.2f){ -+ Lines.circle(e.x, e.y, 20f + e.fin() * 400f); -+ return; -+ } -+ Lines.circle(e.x, e.y, 101f); -+ Lines.circle(e.x, e.y, 93f); -+ for(int i = 0; i < 16; i++){ -+ float rot = i * 22.5f; -+ if((e.fin() - 0.2f) * 50 > i) -+ drawSimpleArrow(e.x, e.y, e.x + Angles.trnsx(rot, 120f), e.y + Angles.trnsy(rot, 120f), 96f, 4f); -+ } -+ }), Color.acid); -+ quesMark = new MarkerType("What", new Effect(1200, e -> { -+ color(Color.violet); -+ stroke(2f); -+ Draw.alpha(Math.min(e.fin() * 5, 1)); -+ Lines.arc(e.x, e.y + 25f, 10, 0.75f, 270); -+ Lines.line(e.x, e.y + 15f, e.x, e.y + 7f); -+ Lines.circle(e.x, e.y, 3f); -+ Lines.circle(e.x, e.y + 18.5f, 27f); -+ }), Color.pink); -+ allTypes = Seq.with(mark, gatherMark, attackMark, defenseMark, quesMark); -+ } -+ -+ static{ -+ init(); -+ selected = mark; -+ Events.run(WorldLoadEvent.class, () -> last = null); -+ } -+ -+ -+ public final Color color; -+ private final String name; -+ private final Effect effect; -+ public String localizedName; -+ -+ public MarkerType(String name, Effect effect, Color color){ -+ this.name = name; -+ this.effect = effect; -+ this.color = color; -+ -+ localizedName = Core.bundle.get("marker." + name + ".name", "unknown"); -+ } -+ -+ public String shortName(){ -+ return "[#" + color + "]" + localizedName.charAt(0); -+ } -+ -+ public MarkElement at(Position pos){ -+ var element = new MarkElement(this, pos); -+ element.show(); -+ return element; -+ } -+ -+ public void markWithMessage(Vec2 pos){ -+ if(last != null && last.time < heatTime){ -+ Vars.ui.announce("请不要频繁标记!"); -+ return; -+ } -+ last = at(pos); -+ UIExt.sendChatMessage(Strings.format("[#@]<@>[]@", color, name, FormatDefault.formatTile(pos))); -+ } -+ -+ public static boolean resolveMessage(String text){ -+ var matcher = posPattern.matcher(Strings.stripColors(text)); -+ if(!matcher.find()) return false; -+ var typeName = matcher.group("type"); -+ Vec2 pos = Tmp.v1.set(Strings.parseInt(matcher.group("x")), Strings.parseInt(matcher.group("y"))); -+ -+ MarkerType type = mark; -+ if(typeName != null){ -+ typeName = typeName.substring(1, typeName.length() - 1); -+ for(var it : allTypes){ -+ if(it.name.equals(typeName)) -+ type = it; -+ } -+ }else if(text.contains("集合")){ -+ type = gatherMark; -+ } -+ -+ var exists = (MarkElement)Groups.draw.find(it -> it instanceof MarkElement e && e.message == null && e.within(pos.scl(tilesize), 2 * tilesize)); -+ last = exists != null ? exists : type.at(pos.scl(tilesize)); -+ last.message = text; -+ return true; -+ } -+ -+ public static void eachActive(Cons cons){ -+ if(last == null) return; -+ Groups.draw.each(d -> { -+ if(d instanceof MarkElement e) cons.get(e); -+ }); -+ } -+ -+ public static void lockOnLastMark(){ -+ if(last == null) return; -+ control.input.panCamera(Tmp.v1.set(last)); -+ last.show(); -+ } -+ -+ public static @Nullable Position getLastPos(){ -+ return last; -+ } -+ -+ /** 在x,y附近length范围,绘制一个指向x2,y2的三角箭头,箭头大小radius */ -+ private static void drawSimpleArrow(float x, float y, float x2, float y2, float length, float radius){ -+ float angle = Angles.angle(x, y, x2, y2); -+ Tmp.v1.set(x2, y2).sub(x, y).limit(length); -+ float vx = Tmp.v1.x + x, vy = Tmp.v1.y + y; -+ Fill.poly(vx, vy, 3, radius, angle); -+ } -+ -+ -+ public static class MarkElement extends EffectState{ -+ public final MarkerType type; -+ @Nullable -+ public String message; -+ -+ public MarkElement(MarkerType MarkerType, Position markPos){ -+ this.type = MarkerType; -+ -+ set(markPos); -+ this.effect = MarkerType.effect; -+ this.lifetime = effect.lifetime; -+ this.color.set(MarkerType.color); -+ } -+ -+ public void show(){ -+ time = 0; -+ add(); -+ } -+ -+ @Override -+ public void draw(){ -+ super.draw(); -+ Draw.z(Layer.overlayUI); -+ showArrow(); -+ if(message != null) -+ FuncX.drawText(this, message); -+ } -+ -+ private void showArrow(){ -+ Draw.reset(); -+ Drawf.arrow(player.x, player.y, x, y, 5f * tilesize, 4f, color); -+ -+ var p = Tmp.v1.set(this).sub(player).limit(4.5f * tilesize).add(player); -+ FuncX.drawText(p, (int)(dst(player) / 8) + "", Scl.scl(1.25f), color); -+ } -+ } -+} -diff --git a/core/src/mindustryX/features/UIExt.java b/core/src/mindustryX/features/UIExt.java -index d73cc555f9dfd3b4248707351e1ecfd6c261e686..a8740bc693f27bbfd3917a69c78417919eed6cad 100644 ---- a/core/src/mindustryX/features/UIExt.java -+++ b/core/src/mindustryX/features/UIExt.java -@@ -5,6 +5,8 @@ import arc.scene.ui.*; - import arc.scene.ui.layout.*; - import arc.util.*; - import mindustry.content.*; -+import mindustry.core.*; -+import mindustry.gen.*; - import mindustryX.features.ui.*; - - import static mindustry.Vars.*; -@@ -33,11 +35,20 @@ public class UIExt{ - y.setText(String.valueOf(vec.y)); - }).tooltip(b -> b.label(() -> "选择玩家当前位置:" + player.tileX() + "," + player.tileY())).height(50f); - --// tt.button(StatusEffects.blasted.emoji(), () -> { --// if(Marker.markList.size == 0) return; --// vec.set(World.toTile(Marker.markList.peek().markPos.x), World.toTile(Marker.markList.peek().markPos.y)); --// x.setText(World.toTile(Marker.markList.peek().markPos.x) + ""); --// y.setText(World.toTile(Marker.markList.peek().markPos.y) + ""); --// }).tooltip(Marker.markList.size == 0 ? "[red]未标记" : ("选择上个标记点:" + World.toTile(Marker.markList.peek().markPos.x) + "," + World.toTile(Marker.markList.peek().markPos.y))).height(50f); -+ tt.button(StatusEffects.blasted.emoji(), () -> { -+ var last = MarkerType.getLastPos(); -+ if(last == null) return; -+ vec.set(World.toTile(last.getX()), World.toTile(last.getY())); -+ x.setText(String.valueOf(vec.x)); -+ y.setText(String.valueOf(vec.y)); -+ }).height(50f).tooltip((t) -> t.label(() -> { -+ var last = MarkerType.getLastPos(); -+ if(last == null) return "[red]未标记"; -+ return "选择上个标记点:" + FormatDefault.formatTile(last); -+ })); -+ } -+ -+ public static void sendChatMessage(String message){ -+ Call.sendChatMessage(ui.chatfrag.mode.normalizedPrefix() + message); - } - } diff --git a/patches/client/0056-API-C-DebugUtil.patch b/patches/client/0056-API-C-DebugUtil.patch index 15acded0f60f..f4a62f4d1fd5 100644 --- a/patches/client/0056-API-C-DebugUtil.patch +++ b/patches/client/0056-API-C-DebugUtil.patch @@ -13,11 +13,8 @@ way-zer on 2024/7/21 core/src/mindustry/ClientLauncher.java | 2 +- core/src/mindustry/core/Logic.java | 3 +++ .../mindustry/ui/fragments/HudFragment.java | 7 +++++ - core/src/mindustryX/Hooks.java | 1 + - core/src/mindustryX/features/DebugUtil.java | 21 +++++++++++++++ - 6 files changed, 59 insertions(+), 1 deletion(-) + 4 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 core/src/arc/graphics/g2d/MySpriteBatch.java - create mode 100644 core/src/mindustryX/features/DebugUtil.java diff --git a/core/src/arc/graphics/g2d/MySpriteBatch.java b/core/src/arc/graphics/g2d/MySpriteBatch.java new file mode 100644 @@ -117,42 +114,3 @@ index 23d4ad03c71131265f82478a0285110cdf71059d..6f8bb852edfe397c4abbbe9268f8b564 if(android){ info.label(() -> memnative.get((int)(Core.app.getJavaHeap() / 1024 / 1024), (int)(Core.app.getNativeHeap() / 1024 / 1024))).left().style(Styles.outlineLabel).name("memory2"); -diff --git a/core/src/mindustryX/Hooks.java b/core/src/mindustryX/Hooks.java -index 3d1491a497b4f3a7dad96620cdc6638376938b4d..9981f2963b730173d8c03e29440e2e9ee404d4d3 100644 ---- a/core/src/mindustryX/Hooks.java -+++ b/core/src/mindustryX/Hooks.java -@@ -18,6 +18,7 @@ public class Hooks implements ApplicationListener{ - Log.infoTag("MindustryX", "Hooks.beforeInit"); - registerBundle(); - Settings.addSettings(); -+ DebugUtil.init();//this is safe, and better at beforeInit, - } - - /** invoke after loading, just before `Mod::init` */ -diff --git a/core/src/mindustryX/features/DebugUtil.java b/core/src/mindustryX/features/DebugUtil.java -new file mode 100644 -index 0000000000000000000000000000000000000000..1af11ccb664b52555d5ade24343c26ff3d6d1181 ---- /dev/null -+++ b/core/src/mindustryX/features/DebugUtil.java -@@ -0,0 +1,21 @@ -+package mindustryX.features; -+ -+import arc.*; -+import arc.util.*; -+import mindustry.game.EventType.*; -+ -+public class DebugUtil{ -+ public static int lastDrawRequests, lastVertices, lastFlushCount, lastSwitchTexture; -+ public static long logicTime, rendererTime, uiTime;//nanos -+ private static long rendererStart, uiStart;//nanos -+ -+ public static void init(){ -+ Events.run(Trigger.preDraw, () -> { -+ lastDrawRequests = lastVertices = lastFlushCount = lastSwitchTexture = 0; -+ rendererStart = Time.nanos(); -+ }); -+ Events.run(Trigger.postDraw, () -> rendererTime = Time.timeSinceNanos(rendererStart)); -+ Events.run(Trigger.uiDrawBegin, () -> uiStart = Time.nanos()); -+ Events.run(Trigger.uiDrawEnd, () -> uiTime = Time.timeSinceNanos(uiStart)); -+ } -+} diff --git a/patches/client/0057-C-RenderExt-blockRenderLevel.patch b/patches/client/0057-C-RenderExt-blockRenderLevel.patch index 3f1a80837789..2bcc9ed2977e 100644 --- a/patches/client/0057-C-RenderExt-blockRenderLevel.patch +++ b/patches/client/0057-C-RenderExt-blockRenderLevel.patch @@ -9,9 +9,7 @@ Subject: [PATCH] C(RenderExt) blockRenderLevel core/src/mindustry/input/Binding.java | 1 + core/src/mindustry/world/blocks/production/Drill.java | 4 ++-- .../mindustry/world/blocks/production/GenericCrafter.java | 5 ++++- - core/src/mindustryX/Hooks.java | 5 +++++ - core/src/mindustryX/features/RenderExt.java | 3 +++ - 7 files changed, 20 insertions(+), 6 deletions(-) + 5 files changed, 12 insertions(+), 6 deletions(-) diff --git a/core/src/mindustry/core/Renderer.java b/core/src/mindustry/core/Renderer.java index b7fb1f87c211a34ec3698fe3176446be3c4c1430..d21aa28efe895c7d76dfd903a9942a03f5a012b9 100644 @@ -114,54 +112,3 @@ index 03a7b867bc5af050d217fc8fb7ea9f31f9279b86..a55bc18a055c74d4e7de3332bd1315be progress %= 1f; } -diff --git a/core/src/mindustryX/Hooks.java b/core/src/mindustryX/Hooks.java -index 9981f2963b730173d8c03e29440e2e9ee404d4d3..28892eb7f43aa7d903541b9d8412018c41975868 100644 ---- a/core/src/mindustryX/Hooks.java -+++ b/core/src/mindustryX/Hooks.java -@@ -12,6 +12,8 @@ import mindustryX.features.Settings; - import java.net.*; - import java.util.*; - -+import static arc.Core.*; -+ - public class Hooks implements ApplicationListener{ - /** invoke before `Vars.init`. Note that may be executed from `Vars.loadAsync` */ - public static void beforeInit(){ -@@ -75,6 +77,9 @@ public class Hooks implements ApplicationListener{ - if(Core.input.keyTap(Binding.point)){ - MarkerType.selected.markWithMessage(Core.input.mouseWorld()); - } -+ if(input.keyTap(Binding.toggle_block_render)){ -+ settings.put("blockRenderLevel", (RenderExt.blockRenderLevel + 1) % 3); -+ } - } - - private static void registerBundle(){ -diff --git a/core/src/mindustryX/features/RenderExt.java b/core/src/mindustryX/features/RenderExt.java -index 43fe44b6e0e7793d1e88a92a06fe010aa5968196..7b5d21f77cd8d15d0541cd70a051aad660c21e1d 100644 ---- a/core/src/mindustryX/features/RenderExt.java -+++ b/core/src/mindustryX/features/RenderExt.java -@@ -29,6 +29,7 @@ public class RenderExt{ - public static int blockBarMinHealth; - public static float overdriveZoneTransparency, mendZoneTransparency; - public static boolean logicDisplayNoBorder, arcDrillMode; -+ public static int blockRenderLevel; - - public static boolean unitHide = false; - private static Effect placementEffect; -@@ -58,6 +59,7 @@ public class RenderExt{ - mendZoneTransparency = Core.settings.getInt("mend_zone") / 100f; - logicDisplayNoBorder = Core.settings.getBool("arclogicbordershow"); - arcDrillMode = Core.settings.getBool("arcdrillmode"); -+ blockRenderLevel = Core.settings.getInt("blockRenderLevel"); - }); - Events.run(Trigger.draw, RenderExt::draw); - Events.on(TileChangeEvent.class, RenderExt::onSetBlock); -@@ -73,6 +75,7 @@ public class RenderExt{ - } - - public static void onBlockDraw(Tile tile, Block block, @Nullable Building build){ -+ if(blockRenderLevel < 2) return; - block.drawBase(tile); - if(displayAllMessage && build instanceof MessageBuild) - Draw.draw(Layer.overlayUI - 0.1f, build::drawSelect); diff --git a/patches/client/0059-OC-Batch.patch b/patches/client/0059-OC-Batch.patch index 94ba5388f5c5..d1563af44e46 100644 --- a/patches/client/0059-OC-Batch.patch +++ b/patches/client/0059-OC-Batch.patch @@ -24,8 +24,7 @@ way-zer on 2024/8/4 --- core/src/arc/graphics/g2d/MySpriteBatch.java | 230 +++++++++++++++++++ core/src/mindustry/graphics/MultiPacker.java | 2 +- - core/src/mindustryX/features/RenderExt.java | 2 + - 3 files changed, 233 insertions(+), 1 deletion(-) + 2 files changed, 231 insertions(+), 1 deletion(-) diff --git a/core/src/arc/graphics/g2d/MySpriteBatch.java b/core/src/arc/graphics/g2d/MySpriteBatch.java index e2bdb30e190a210f25130c423c853892798e4c28..97228b7fadd15f0491a50849bfb19f4f3ced8bb6 100644 @@ -290,23 +289,3 @@ index e5f473a7651495f7f61856e6df711fba12dfb1be..f1a3f1cbbac0fbda6dfabe689f25c210 //TODO stuff like this throws OOM on some devices environment(4096, 2048), -diff --git a/core/src/mindustryX/features/RenderExt.java b/core/src/mindustryX/features/RenderExt.java -index 7b5d21f77cd8d15d0541cd70a051aad660c21e1d..4386f5f364e847324b147c44f8c8748fd5c16537 100644 ---- a/core/src/mindustryX/features/RenderExt.java -+++ b/core/src/mindustryX/features/RenderExt.java -@@ -30,6 +30,7 @@ public class RenderExt{ - public static float overdriveZoneTransparency, mendZoneTransparency; - public static boolean logicDisplayNoBorder, arcDrillMode; - public static int blockRenderLevel; -+ public static boolean renderSort; - - public static boolean unitHide = false; - private static Effect placementEffect; -@@ -60,6 +61,7 @@ public class RenderExt{ - logicDisplayNoBorder = Core.settings.getBool("arclogicbordershow"); - arcDrillMode = Core.settings.getBool("arcdrillmode"); - blockRenderLevel = Core.settings.getInt("blockRenderLevel"); -+ renderSort = Core.settings.getBool("renderSort"); - }); - Events.run(Trigger.draw, RenderExt::draw); - Events.on(TileChangeEvent.class, RenderExt::onSetBlock); diff --git a/patches/client/0060-C.patch b/patches/client/0060-C.patch index ba27e5d6dba3..a53cb7977518 100644 --- a/patches/client/0060-C.patch +++ b/patches/client/0060-C.patch @@ -18,10 +18,8 @@ MinRi2 <2275045670@qq.com> on 2024/5/4 at 15:25 优化显示效果;更紧凑的Item布局 way-zer on 2024/5/17 at 20:50 --- - .../mindustry/ui/fragments/HudFragment.java | 11 +- - .../features/ui/NewCoreItemsDisplay.java | 245 ++++++++++++++++++ - 2 files changed, 249 insertions(+), 7 deletions(-) - create mode 100644 core/src/mindustryX/features/ui/NewCoreItemsDisplay.java + core/src/mindustry/ui/fragments/HudFragment.java | 11 ++++------- + 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/core/src/mindustry/ui/fragments/HudFragment.java b/core/src/mindustry/ui/fragments/HudFragment.java index 6f8bb852edfe397c4abbbe9268f8b5648c19add3..37a57b425a423ddff029268c938423f6711340c8 100644 @@ -67,254 +65,3 @@ index 6f8bb852edfe397c4abbbe9268f8b5648c19add3..37a57b425a423ddff029268c938423f6 float notifDuration = 240f; float[] coreAttackTime = {0}; -diff --git a/core/src/mindustryX/features/ui/NewCoreItemsDisplay.java b/core/src/mindustryX/features/ui/NewCoreItemsDisplay.java -new file mode 100644 -index 0000000000000000000000000000000000000000..d51e2f8f410239be0924c45ea5471edbda7d71ee ---- /dev/null -+++ b/core/src/mindustryX/features/ui/NewCoreItemsDisplay.java -@@ -0,0 +1,245 @@ -+package mindustryX.features.ui; -+ -+import arc.*; -+import arc.graphics.*; -+import arc.scene.ui.*; -+import arc.scene.ui.layout.*; -+import arc.struct.*; -+import arc.util.*; -+import mindustry.core.*; -+import mindustry.entities.*; -+import mindustry.game.EventType.*; -+import mindustry.graphics.*; -+import mindustry.type.*; -+import mindustry.ui.*; -+import mindustry.world.*; -+import mindustry.world.blocks.ConstructBlock.*; -+import mindustry.world.blocks.power.*; -+import mindustry.world.blocks.storage.*; -+ -+import java.util.*; -+ -+import static mindustry.Vars.*; -+ -+//moved from mindustry.arcModule.ui.RCoreItemsDisplay -+public class NewCoreItemsDisplay extends Table{ -+ public static final float MIN_WIDTH = 64f; -+ -+ private Table itemsTable, unitsTable, plansTable; -+ -+ private static final Interval timer = new Interval(2); -+ private int columns = -1; -+ -+ private final int[] itemDelta; -+ private final int[] lastItemAmount; -+ private final ObjectSet usedItems = new ObjectSet<>(); -+ private final ObjectSet usedUnits = new ObjectSet<>(); -+ -+ private final ItemSeq planItems = new ItemSeq(); -+ private final ObjectIntMap planCounter = new ObjectIntMap<>(); -+ -+ public NewCoreItemsDisplay(){ -+ itemDelta = new int[content.items().size]; -+ lastItemAmount = new int[content.items().size]; -+ Events.on(ResetEvent.class, e -> { -+ usedItems.clear(); -+ usedUnits.clear(); -+ Arrays.fill(itemDelta, 0); -+ Arrays.fill(lastItemAmount, 0); -+ itemsTable.clearChildren(); -+ unitsTable.clearChildren(); -+ plansTable.clearChildren(); -+ }); -+ -+ setup(); -+ } -+ -+ private void setup(){ -+ itemsTable = new Table(Styles.black3); -+ unitsTable = new Table(Styles.black3); -+ plansTable = new Table(Styles.black3); -+ -+ var itemCol = add(new SimpleCollapser(itemsTable, true)).growX().get(); -+ var unitsCol = row().add(new SimpleCollapser(unitsTable, true)).growX().get(); -+ var emptyLine = row().add(); -+ var plansCol = row().add(new SimpleCollapser(plansTable, true)).growX().get(); -+ update(() -> { -+ var columns = Core.settings.getInt("arcCoreItemsCol"); -+ int displayType = Core.settings.getInt("arccoreitems"); -+ itemCol.setCollapsed(displayType != 1 && displayType != 3); -+ unitsCol.setCollapsed(displayType != 2 && displayType != 3); -+ plansCol.setCollapsed(displayType < 1); -+ var newHeight = plansTable.hasChildren() ? 12f : 0f; -+ if(emptyLine.maxHeight() != newHeight){ -+ emptyLine.height(newHeight); -+ emptyLine.getTable().invalidate(); -+ } -+ -+ if(this.columns != columns){ -+ this.columns = columns; -+ rebuildItems(); -+ rebuildUnits(); -+ rebuildPlans(); -+ } -+ }); -+ -+ itemsTable.update(() -> { -+ updateItemMeans(); -+ if(content.items().contains(item -> player.team().items().get(item) > 0 && usedItems.add(item))){ -+ rebuildItems(); -+ } -+ }); -+ unitsTable.update(() -> { -+ if(content.units().contains(unit -> player.team().data().countType(unit) > 0 && usedUnits.add(unit))){ -+ rebuildUnits(); -+ } -+ }); -+ plansTable.update(() -> { -+ if(timer.get(1, 10f)){ -+ rebuildPlans(); -+ } -+ }); -+ } -+ -+ private void updateItemMeans(){ -+ if(!timer.get(0, 60f)) return; -+ var items = player.team().items(); -+ for(Item item : usedItems){ -+ short id = item.id; -+ int coreAmount = items.get(id); -+ int lastAmount = lastItemAmount[id]; -+ itemDelta[id] = coreAmount - lastAmount; -+ lastItemAmount[id] = coreAmount; -+ } -+ } -+ -+ private void rebuildItems(){ -+ itemsTable.clearChildren(); -+ if(player.team().core() == null) return; -+ -+ int i = 0; -+ for(Item item : content.items()){ -+ if(!usedItems.contains(item)){ -+ continue; -+ } -+ -+ itemsTable.stack( -+ new Table(t -> -+ t.image(item.uiIcon).size(iconMed - 4).scaling(Scaling.fit).pad(2f) -+ .tooltip(tooltip -> tooltip.background(Styles.black6).margin(4f).add(item.localizedName).style(Styles.outlineLabel)) -+ ), -+ new Table(t -> t.label(() -> { -+ int update = itemDelta[item.id]; -+ if(update == 0) return ""; -+ return (update < 0 ? "[red]" : "[green]+") + UI.formatAmount(update); -+ }).fontScale(0.85f)).top().left() -+ ); -+ -+ itemsTable.table(amountTable -> { -+ amountTable.defaults().expand().left(); -+ -+ Label amountLabel = amountTable.add("").growY().get(); -+ amountTable.row(); -+ var planLabel = amountTable.add("").fontScale(0.6f).height(0.01f); -+ -+ amountTable.update(() -> { -+ int planAmount = planItems.get(item); -+ int amount = player.team().items().get(item); -+ -+ float newFontScale = 1f; -+ Color amountColor = Color.white; -+ if(planAmount == 0){ -+ var core = player.team().core(); -+ if(core != null && amount >= core.storageCapacity * 0.99){ -+ amountColor = Pal.accent; -+ } -+ planLabel.height(0.01f);//can't use 0 as maxHeight; -+ planLabel.get().setText(""); -+ }else{ -+ amountColor = (amount > planAmount ? Color.green -+ : amount > planAmount / 2 ? Pal.stat -+ : Color.scarlet); -+ planLabel.height(Float.NEGATIVE_INFINITY); -+ planLabel.color(planAmount > 0 ? Color.scarlet : Color.green); -+ planLabel.get().setText(UI.formatAmount(planAmount)); -+ newFontScale = 0.7f; -+ } -+ -+ if(amountLabel.getFontScaleX() != newFontScale) -+ amountLabel.setFontScale(newFontScale); -+ amountLabel.setColor(amountColor); -+ amountLabel.setText(UI.formatAmount(amount)); -+ }); -+ }).minWidth(MIN_WIDTH).left(); -+ -+ if(++i % columns == 0){ -+ itemsTable.row(); -+ } -+ } -+ } -+ -+ private void rebuildUnits(){ -+ unitsTable.clearChildren(); -+ -+ int i = 0; -+ for(UnitType unit : content.units()){ -+ if(usedUnits.contains(unit)){ -+ unitsTable.image(unit.uiIcon).size(iconSmall).scaling(Scaling.fit).pad(2f) -+ .tooltip(t -> t.background(Styles.black6).margin(4f).add(unit.localizedName).style(Styles.outlineLabel)); -+ unitsTable.label(() -> { -+ int typeCount = player.team().data().countType(unit); -+ return (typeCount == Units.getCap(player.team()) ? "[stat]" : "") + typeCount; -+ }).minWidth(MIN_WIDTH).left(); -+ -+ if(++i % columns == 0){ -+ unitsTable.row(); -+ } -+ } -+ } -+ } -+ -+ private void rebuildPlans(){ -+ planItems.clear(); -+ planCounter.clear(); -+ -+ control.input.allPlans().each(plan -> { -+ Block block = plan.block; -+ -+ if(block instanceof CoreBlock) return; -+ -+ if(plan.build() instanceof ConstructBuild build){ -+ block = build.current; -+ } -+ -+ planCounter.increment(block, plan.breaking ? -1 : 1); -+ -+ for(ItemStack stack : block.requirements){ -+ int planAmount = (int)(plan.breaking ? -state.rules.buildCostMultiplier * state.rules.deconstructRefundMultiplier * stack.amount * plan.progress -+ : state.rules.buildCostMultiplier * stack.amount * (1 - plan.progress)); -+ planItems.add(stack.item, planAmount); -+ } -+ }); -+ -+ plansTable.clearChildren(); -+ if(planCounter.isEmpty()) return; -+ int i = 0; -+ for(Block block : content.blocks()){ -+ int count = planCounter.get(block, 0); -+ if(count == 0 || block.category == Category.distribution && block.size < 3 -+ || block.category == Category.liquid && block.size < 3 -+ || block instanceof PowerNode -+ || block instanceof BeamNode) continue; -+ -+ plansTable.image(block.uiIcon).size(iconSmall).scaling(Scaling.fit).pad(2f); -+ plansTable.label(() -> (count > 0 ? "[green]+" : "[red]") + count).minWidth(MIN_WIDTH).left(); -+ -+ if(++i % columns == 0){ -+ plansTable.row(); -+ } -+ } -+ } -+ -+ public boolean hadItem(Item item){ -+ return usedItems.contains(item); -+ } -+} diff --git a/patches/client/0062-API-UI.patch b/patches/client/0062-API-UI.patch index b8ee79a57642..49551dd75c55 100644 --- a/patches/client/0062-API-UI.patch +++ b/patches/client/0062-API-UI.patch @@ -6,134 +6,3 @@ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ---- - core/src/mindustryX/features/ui/Card.java | 120 ++++++++++++++++++++++ - 1 file changed, 120 insertions(+) - create mode 100644 core/src/mindustryX/features/ui/Card.java - -diff --git a/core/src/mindustryX/features/ui/Card.java b/core/src/mindustryX/features/ui/Card.java -new file mode 100644 -index 0000000000000000000000000000000000000000..40634fec87b60b5e8b1c53077b4f3087c6d85e06 ---- /dev/null -+++ b/core/src/mindustryX/features/ui/Card.java -@@ -0,0 +1,120 @@ -+package mindustryX.features.ui; -+ -+import arc.func.*; -+import arc.graphics.*; -+import arc.scene.actions.*; -+import arc.scene.ui.layout.*; -+import arc.util.*; -+import mindustry.gen.*; -+import mindustry.graphics.*; -+ -+/** -+ * @author minri2 -+ * Create by 2024/4/13 -+ */ -+public class Card extends Table{ -+ public static CardStyle -+ grayOuterDark = new CardStyle(){{ -+ shadowStyle = CardShadowStyle.outer; -+ shadowDark = Pal.darkerGray; -+ }}, -+ accentOutBack = new CardStyle(){{ -+ shadowStyle = CardShadowStyle.outer; -+ shadowDark = Pal.accentBack; -+ }}; -+ -+ public CardStyle style; -+ -+ public Card(CardStyle style, Cons contCons){ -+ this(Pal.gray, style, contCons); -+ } -+ -+ public Card(Color cardColor, CardStyle style, Cons
contCons){ -+ this.style = style; -+ -+ if(cardColor != null){ -+ background(Tex.whiteui); -+ setColor(cardColor); -+ } -+ -+ setup(contCons); -+ } -+ -+ private void setup(Cons
contCons){ -+ Color topLeft, rightBottom; -+ -+ if(style.shadowStyle == CardShadowStyle.inner){ -+ topLeft = style.shadowDark; -+ rightBottom = style.shadowLight; -+ }else if(style.shadowStyle == CardShadowStyle.outer){ -+ topLeft = style.shadowLight; -+ rightBottom = style.shadowDark; -+ }else{ -+ throw new RuntimeException("Card got an unknown shadowStyle:" + style.shadowStyle); -+ } -+ -+ float size = style.shadowSize; -+ Cell topShadow = null, bottomShadow = null; -+ if(topLeft != null){ -+ topShadow = image().color(topLeft).height(size).padRight(-1).growX(); -+ row(); -+ image().color(topLeft).width(size).growY(); -+ } -+ -+ table(contCons).grow(); -+ -+ if(rightBottom != null){ -+ image().color(rightBottom).width(size).growY(); -+ row(); -+ bottomShadow = image().color(rightBottom).height(size).padRight(-1).growX(); -+ } -+ -+ int columns = getColumns(); -+ if(topShadow != null) topShadow.colspan(columns); -+ if(bottomShadow != null) bottomShadow.colspan(columns); -+ } -+ -+ public void setCardColor(Color cardColor){ -+ setCardColor(cardColor, 1.5f); -+ } -+ -+ public void setCardColor(Color cardColor, float duration){ -+ if(color.equals(cardColor)) return; -+ -+ addAction(Actions.color(cardColor, duration)); -+ } -+ -+ public enum CardShadowStyle{ -+ /** -+ * 内阴影 有内陷的效果 -+ */ -+ inner, -+ /** -+ * 外阴影 有外凸的效果 -+ */ -+ outer; -+ } -+ -+ public static class CardStyle{ -+ public float shadowSize = 6f; -+ public CardShadowStyle shadowStyle = CardShadowStyle.inner; -+ public @Nullable Color shadowLight, shadowDark = Pal.darkestGray; -+ } -+ -+ /** -+ * 为单行表格添加阴影 -+ * 由于表格没有rowspan所以只能为单行表格添加 -+ * @param table 添加阴影的表格 -+ * @param size 阴影大小 -+ * @param color 阴影的颜色 -+ */ -+ public static void cardShadow(Table table, float size, Color color){ -+ table.image().width(size).color(color).growY().right(); -+ table.row(); -+ table.image().height(size).color(color).growX().colspan(table.getColumns()); -+ } -+ -+ public static void cardShadow(Table table){ -+ cardShadow(table, 8f, Pal.darkerGray); -+ } -+} diff --git a/patches/client/0063-UI-Mod-Mod.patch b/patches/client/0063-UI-Mod-Mod.patch index c669b8b61c9f..7738532c4c41 100644 --- a/patches/client/0063-UI-Mod-Mod.patch +++ b/patches/client/0063-UI-Mod-Mod.patch @@ -11,11 +11,8 @@ Content-Transfer-Encoding: 8bit core/assets/bundles/bundle-mdtx.properties | 19 ++ core/assets/recommendMods.json | 17 ++ core/src/mindustry/ui/dialogs/ModsDialog.java | 264 +++++++++++------- - core/src/mindustryX/features/UIExt.java | 1 + - .../features/ui/ModsRecommendDialog.java | 264 ++++++++++++++++++ - 5 files changed, 462 insertions(+), 103 deletions(-) + 3 files changed, 197 insertions(+), 103 deletions(-) create mode 100644 core/assets/recommendMods.json - create mode 100644 core/src/mindustryX/features/ui/ModsRecommendDialog.java diff --git a/core/assets/bundles/bundle-mdtx.properties b/core/assets/bundles/bundle-mdtx.properties index 9d9cbc07ea828ebbcc4557994b36328cf9764e45..d4b9a4bbc5a9c0c5c298a1744a3c99a6385224a7 100644 @@ -406,285 +403,3 @@ index cfc64166ab474960ad13e1784605e9078c33d16e..5a4e034deba4bb29890ffd5edcd1ecdb } private @Nullable String getStateText(LoadedMod item){ -diff --git a/core/src/mindustryX/features/UIExt.java b/core/src/mindustryX/features/UIExt.java -index a8740bc693f27bbfd3917a69c78417919eed6cad..27409ae7886370c9dc9b29a71c13e12e71f8b3d2 100644 ---- a/core/src/mindustryX/features/UIExt.java -+++ b/core/src/mindustryX/features/UIExt.java -@@ -13,6 +13,7 @@ import static mindustry.Vars.*; - - public class UIExt{ - public static TeamSelectDialog teamSelect; -+ public static ModsRecommendDialog modsRecommend = new ModsRecommendDialog(); - - public static void init(){ - teamSelect = new TeamSelectDialog(); -diff --git a/core/src/mindustryX/features/ui/ModsRecommendDialog.java b/core/src/mindustryX/features/ui/ModsRecommendDialog.java -new file mode 100644 -index 0000000000000000000000000000000000000000..add3a53cf92a66126cac448321913419b5b06a14 ---- /dev/null -+++ b/core/src/mindustryX/features/ui/ModsRecommendDialog.java -@@ -0,0 +1,264 @@ -+package mindustryX.features.ui; -+ -+import arc.*; -+import arc.flabel.*; -+import arc.func.*; -+import arc.graphics.*; -+import arc.graphics.Texture.*; -+import arc.graphics.g2d.*; -+import arc.scene.*; -+import arc.scene.actions.*; -+import arc.scene.style.*; -+import arc.scene.ui.*; -+import arc.scene.ui.layout.*; -+import arc.struct.*; -+import arc.util.*; -+import mindustry.*; -+import mindustry.game.EventType.*; -+import mindustry.gen.*; -+import mindustry.graphics.*; -+import mindustry.io.*; -+import mindustry.mod.*; -+import mindustry.ui.*; -+import mindustry.ui.dialogs.*; -+ -+import java.text.*; -+import java.util.*; -+ -+/** -+ * @author minri2 -+ * Create by 2024/4/12 -+ */ -+public class ModsRecommendDialog extends BaseDialog{ -+ private static final TextureRegion defaultModIcon = ((TextureRegionDrawable)Tex.nomap).getRegion(); -+ private static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'"); -+ -+ public static Color -+ lightBlue = Color.valueOf("#a5dee5"), -+ pink = Color.valueOf("#ffcfdf"); -+ -+ private ObjectMap textureCache; -+ private RecommendMeta meta; -+ private boolean fetchModList; -+ -+ public ModsRecommendDialog(){ -+ super(""); -+ -+ setup(); -+ -+ shown(this::rebuild); -+ addCloseButton(); -+ -+ Events.run(Trigger.importMod, () -> Core.app.post(this::rebuild)); -+ } -+ -+ private void setup(){ -+ titleTable.clearChildren(); -+ } -+ -+ private void rebuild(){ -+ if(textureCache == null){ -+ textureCache = Reflect.get(Vars.ui.mods, "textureCache"); -+ } -+ -+ if(meta == null){ -+ String json = Core.files.internal("recommendMods.json").readString(); -+ -+ meta = JsonIO.json.fromJson(RecommendMeta.class, json); -+ } -+ -+ if(!fetchModList){ -+ setLoading(cont); -+ -+ Reflect.invoke(Vars.ui.mods, "getModList", new Cons[]{listings -> { -+ Seq modListings = (Seq)listings; -+ -+ // ??? -+ if(modListings == null){ -+ setLoadFailed(cont); -+ return; -+ } -+ -+ for(RecommendModMeta modMeta : meta.modRecommend){ -+ modMeta.listing = modListings.find(modListing -> modMeta.repo.equals(modListing.repo)); -+ } -+ -+ fetchModList = true; -+ rebuildCont(); -+ }}, Cons.class); -+ -+ return; -+ } -+ -+ rebuildCont(); -+ } -+ -+ private void rebuildCont(){ -+ float width = Math.min(Core.graphics.getWidth() / Scl.scl(1.05f), 556f); -+ -+ cont.top().clearChildren(); -+ cont.defaults(); -+ -+ cont.add(new Card(Pal.lightishGray, Card.grayOuterDark, info -> { -+ info.top(); -+ info.defaults().expandX().center(); -+ -+ info.add("@mods.recommend").pad(12f).row(); -+ info.add(Core.bundle.format("mods.recommend.lastUpdated", meta.lastUpdated)).pad(6f).row(); -+ info.add("@mods.recommend.info").pad(6f); -+ -+ for(Element child : info.getChildren()){ -+ if(child instanceof Label label){ -+ label.setColor(Pal.accent); -+ label.setStyle(Styles.outlineLabel); -+ } -+ } -+ })).fillX(); -+ -+ cont.row(); -+ -+ cont.pane(Styles.noBarPane, t -> { -+ t.background(Tex.whiteui).setColor(Pal.lightishGray); -+ -+ for(RecommendModMeta modMeta : meta.modRecommend){ -+ if(modMeta.listing == null){ -+ Log.warn("Recommend Mod '@' not found in github.", modMeta.repo); -+ continue; -+ } -+ -+ t.table(Tex.whiteui, card -> { -+ setupModCard(card, modMeta); -+ }).color(Pal.darkerGray).width(width).pad(12f).with(card -> { -+ if(installed(modMeta)){ -+ card.addAction(Actions.color(Pal.accent, 1.5f)); -+ } -+ }); -+ -+ Card.cardShadow(t); -+ -+ t.row(); -+ } -+ }).scrollX(false); -+ } -+ -+ private void setupModCard(Table table, RecommendModMeta modMeta){ -+ table.defaults().growX(); -+ -+ ModListing modListing = modMeta.listing; -+ -+ table.table(title -> { -+ title.add(new Card(Pal.gray, Card.grayOuterDark, info -> { -+ info.top(); -+ info.defaults().padTop(2f).expandX().left(); -+ -+ addInfo(info, "name", modListing.name).color(Pal.accent).pad(8f); -+ addInfo(info, "author", modListing.author).color(pink).padTop(4f); -+ addInfo(info, "minGameVersion", modListing.minGameVersion).color(lightBlue); -+ addInfo(info, "lastUpdated", getLastUpdatedTime(modListing)).color(lightBlue); -+ addInfo(info, "stars", modListing.stars).color(lightBlue); -+ -+ for(Element child : info.getChildren()){ -+ if(child instanceof Label label){ -+ label.setStyle(Styles.outlineLabel); -+ } -+ } -+ })).pad(4f).padRight(12f).grow(); -+ -+ title.add(new BorderImage(){{ -+ border(Pal.darkestGray); -+ }}).size(128f).pad(4f).with(image -> { -+ getModIcon(modMeta.repo, image::setDrawable); -+ }); -+ }); -+ -+ table.row(); -+ -+ table.add(new Card(Pal.gray, Card.grayOuterDark, body -> { -+ body.add(modMeta.reason).pad(4f).grow().wrap(); -+ -+ body.addChild(new Table(buttons -> { -+ buttons.setFillParent(true); -+ buttons.right().bottom(); -+ buttons.defaults().size(32f); -+ -+ buttons.button(Icon.download, Styles.cleari, 24f, () -> { -+ Vars.ui.mods.githubImportMod(modListing.repo, modListing.hasJava); -+ }); -+ })); -+ })).minHeight(48f).pad(8f); -+ } -+ -+ private Cell addInfo(Table table, String bundle, Object value){ -+ Cell cell = table.add(Core.bundle.format("mods.recommend.mod." + bundle, value)).color(pink); -+ -+ table.row(); -+ -+ return cell; -+ } -+ -+ private boolean installed(RecommendModMeta modMeta){ -+ return Vars.mods.list().find(mod -> modMeta.repo.equals(mod.getRepo())) != null; -+ } -+ -+ private String getLastUpdatedTime(ModListing listing){ -+ try{ -+ Date date = dateFormat.parse(listing.lastUpdated); -+ return DateFormat.getInstance().format(date); -+ }catch(ParseException e){ -+ return "Unknown"; -+ } -+ } -+ -+ private void getModIcon(String repo, Cons callback){ -+ TextureRegion cache = textureCache.get(repo); -+ -+ if(cache != null){ -+ callback.get(cache); -+ return; -+ } -+ -+ Http.get("https://raw.githubusercontent.com/Anuken/MindustryMods/master/icons/" + repo.replace("/", "_"), res -> { -+ Pixmap pix = new Pixmap(res.getResult()); -+ Core.app.post(() -> { -+ try{ -+ Texture texture = new Texture(pix); -+ texture.setFilter(TextureFilter.linear); -+ TextureRegion region = new TextureRegion(texture); -+ textureCache.put(repo, region); -+ pix.dispose(); -+ -+ callback.get(region); -+ }catch(Exception e){ -+ Log.err(e); -+ -+ textureCache.put(repo, defaultModIcon); -+ callback.get(defaultModIcon); -+ } -+ }); -+ }, err -> { -+ textureCache.put(repo, defaultModIcon); -+ callback.get(defaultModIcon); -+ }); -+ } -+ -+ private static void setLoading(Table table){ -+ table.clearChildren(); -+ table.add(new FLabel("@alphaLoading")).style(Styles.outlineLabel).expand().center(); -+ } -+ -+ private static void setLoadFailed(Table table){ -+ table.clearChildren(); -+ table.add(new FLabel("@alphaLoadFailed")).style(Styles.outlineLabel).expand().center(); -+ } -+ -+ private static class RecommendMeta{ -+ public String lastUpdated; -+ public Seq modRecommend; -+ } -+ -+ private static class RecommendModMeta{ -+ public String repo; -+ public String reason; -+ public ModListing listing; -+ } -+} diff --git a/patches/client/0064-UI-TeamsStatDisplay.patch b/patches/client/0064-UI-TeamsStatDisplay.patch index 83e2b088fbbc..25ba91f24d8d 100644 --- a/patches/client/0064-UI-TeamsStatDisplay.patch +++ b/patches/client/0064-UI-TeamsStatDisplay.patch @@ -3,215 +3,3 @@ From: way-zer Date: Wed, 1 May 2024 16:09:53 +0800 Subject: [PATCH] UI: TeamsStatDisplay ---- - core/src/mindustryX/features/UIExt.java | 9 + - .../features/ui/TeamsStatDisplay.java | 170 ++++++++++++++++++ - 2 files changed, 179 insertions(+) - create mode 100644 core/src/mindustryX/features/ui/TeamsStatDisplay.java - -diff --git a/core/src/mindustryX/features/UIExt.java b/core/src/mindustryX/features/UIExt.java -index 27409ae7886370c9dc9b29a71c13e12e71f8b3d2..7664e5cd240e9e933373cbd1a3eeb382886e94f8 100644 ---- a/core/src/mindustryX/features/UIExt.java -+++ b/core/src/mindustryX/features/UIExt.java -@@ -1,5 +1,6 @@ - package mindustryX.features; - -+import arc.*; - import arc.math.geom.*; - import arc.scene.ui.*; - import arc.scene.ui.layout.*; -@@ -14,9 +15,17 @@ import static mindustry.Vars.*; - public class UIExt{ - public static TeamSelectDialog teamSelect; - public static ModsRecommendDialog modsRecommend = new ModsRecommendDialog(); -+ public static TeamsStatDisplay teamsStatDisplay; - - public static void init(){ - teamSelect = new TeamSelectDialog(); -+ -+ teamsStatDisplay = new TeamsStatDisplay(); -+ ui.hudGroup.fill(t -> { -+ t.name = "otherCore"; -+ t.left().add(teamsStatDisplay); -+ t.visible(() -> ui.hudfrag.shown && Core.settings.getBool("showOtherTeamResource")); -+ }); - } - - public static void buildPositionRow(Table tt, Vec2 vec){ -diff --git a/core/src/mindustryX/features/ui/TeamsStatDisplay.java b/core/src/mindustryX/features/ui/TeamsStatDisplay.java -new file mode 100644 -index 0000000000000000000000000000000000000000..dbd5a730fc2691520e9e9bc06f9cbd4e61cddf59 ---- /dev/null -+++ b/core/src/mindustryX/features/ui/TeamsStatDisplay.java -@@ -0,0 +1,170 @@ -+package mindustryX.features.ui; -+ -+import arc.*; -+import arc.func.*; -+import arc.graphics.g2d.*; -+import arc.scene.event.*; -+import arc.scene.ui.layout.*; -+import arc.struct.*; -+import arc.util.*; -+import mindustry.*; -+import mindustry.content.*; -+import mindustry.core.*; -+import mindustry.game.*; -+import mindustry.gen.*; -+import mindustry.graphics.*; -+import mindustry.type.*; -+import mindustryX.features.*; -+ -+import static mindustry.Vars.*; -+import static mindustry.ui.Styles.*; -+ -+//moved from mindustry.arcModule.ui.OtherCoreItemDisplay -+public class TeamsStatDisplay extends Table{ -+ private final float fontScl = 0.8f; -+ private final SimpleCollapser teamsColl = new SimpleCollapser(); -+ -+ private final Seq forceShowTeam = new Seq<>(); -+ public final Seq teams = new Seq<>(); -+ private final Interval timer = new Interval(); -+ private boolean showStat = true, showItem = true, showUnit = true; -+ -+ -+ public TeamsStatDisplay(){ -+ table(buttons -> { -+ buttons.button("[red]+", flatTogglet, teamsColl::toggle).update(t -> { -+ t.setChecked(false); -+ t.setText(teamsColl.getCollapsed() ? "[red]+" : "[red]×"); -+ }).size(40).row(); -+ buttons.collapser(t -> { -+ t.button("T", flatTogglet, () -> UIExt.teamSelect.select(team -> teams.contains(team.data()), team -> { -+ if(forceShowTeam.contains(team.data())) forceShowTeam.remove(team.data()); -+ else forceShowTeam.add(team.data()); -+ teamsRebuild(); -+ })).checked(gg -> false).size(40).row(); -+ t.button(Blocks.worldProcessor.emoji(), flatTogglet, () -> { -+ showStat = !showStat; -+ teamsRebuild(); -+ }).checked(a -> showStat).size(40).row(); -+ t.button(content.items().get(0).emoji(), flatTogglet, () -> { -+ showItem = !showItem; -+ teamsRebuild(); -+ }).checked(a -> showItem).size(40).row(); -+ t.button(UnitTypes.mono.emoji(), flatTogglet, () -> { -+ showUnit = !showUnit; -+ teamsRebuild(); -+ }).checked(a -> showUnit).size(40).row(); -+ }, () -> !teamsColl.getCollapsed()); -+ }); -+ add(teamsColl).touchable(Touchable.disabled); -+ var teamsTable = teamsColl.getTable(); -+ teamsTable.background(black6); -+ teamsTable.update(() -> { -+ if(timer.get(120f)) -+ teamsRebuild(); -+ }); -+ -+ Events.on(EventType.ResetEvent.class, e -> { -+ forceShowTeam.clear(); -+ teams.clear(); -+ teamsTable.clearChildren(); -+ }); -+ } -+ -+ private void teamsRebuild(){ -+ teams.clear(); -+ teams.addAll(Vars.state.teams.getActive()); -+ if(state.rules.waveTimer) teams.addUnique(state.rules.waveTeam.data()); -+ forceShowTeam.each(teams::addUnique); -+ teams.sort(teamData -> -teamData.cores.size); -+ -+ var teamsTable = teamsColl.getTable(); -+ teamsTable.clear(); -+ -+ //name + cores + units -+ addTeamData(teamsTable, Icon.players.getRegion(), team -> team.team.id < 6 ? team.team.localized() : String.valueOf(team.team.id)); -+ addTeamData(teamsTable, Blocks.coreNucleus.uiIcon, team -> UI.formatAmount(team.cores.size)); -+ addTeamData(teamsTable, UnitTypes.mono.uiIcon, team -> UI.formatAmount(team.units.size)); -+ addTeamData(teamsTable, UnitTypes.gamma.uiIcon, team -> String.valueOf(team.players.size)); -+ -+ if(showStat){ -+ teamsTable.image().color(Pal.accent).fillX().height(1).colspan(999).padTop(3).padBottom(3).row(); -+ addTeamDataCheckB(teamsTable, Blocks.siliconSmelter.uiIcon, team -> team.team.rules().cheat); -+ addTeamDataCheck(teamsTable, Blocks.arc.uiIcon, team -> state.rules.blockDamage(team.team)); -+ addTeamDataCheck(teamsTable, Blocks.titaniumWall.uiIcon, team -> state.rules.blockHealth(team.team)); -+ addTeamDataCheck(teamsTable, Blocks.buildTower.uiIcon, team -> state.rules.buildSpeed(team.team)); -+ addTeamDataCheck(teamsTable, UnitTypes.corvus.uiIcon, team -> state.rules.unitDamage(team.team)); -+ addTeamDataCheck(teamsTable, UnitTypes.oct.uiIcon, team -> state.rules.unitHealth(team.team)); -+ addTeamDataCheck(teamsTable, UnitTypes.zenith.uiIcon, team -> state.rules.unitCrashDamage(team.team)); -+ addTeamDataCheck(teamsTable, Blocks.tetrativeReconstructor.uiIcon, team -> state.rules.unitBuildSpeed(team.team)); -+ addTeamDataCheck(teamsTable, Blocks.basicAssemblerModule.uiIcon, team -> state.rules.unitCost(team.team)); -+ teamsTable.row(); -+ } -+ -+ if(showItem){ -+ teamsTable.image().color(Pal.accent).fillX().height(1).colspan(999).padTop(3).padBottom(3).row(); -+ for(Item item : content.items()){ -+ boolean show = false; -+ for(Teams.TeamData team : teams){ -+ if(team.hasCore() && team.core().items.get(item) > 0) -+ show = true; -+ } -+ if(show){ -+ addTeamData(teamsTable, item.uiIcon, team -> (team.hasCore() && team.core().items.get(item) > 0) ? UI.formatAmount(team.core().items.get(item)) : "-"); -+ } -+ } -+ } -+ -+ if(showUnit){ -+ teamsTable.image().color(Pal.accent).fillX().height(1).colspan(999).padTop(3).padBottom(3).row(); -+ for(UnitType unit : content.units()){ -+ boolean show = false; -+ for(Teams.TeamData team : teams){ -+ if(team.countType(unit) > 0) -+ show = true; -+ } -+ if(show){ -+ addTeamData(teamsTable, unit.uiIcon, team -> team.countType(unit) > 0 ? String.valueOf(team.countType(unit)) : "-"); -+ } -+ } -+ } -+ } -+ -+ private void addTeamDataCheck(Table table, TextureRegion icon, Floatf checked){ -+ if(teams.isEmpty() || teams.allMatch(it -> checked.get(it) == 1f)) return; -+ //check allSame -+ float value = checked.get(teams.get(0)); -+ if(teams.allMatch(it -> checked.get(it) == value)){ -+ addTeamData(table, icon, FormatDefault.format(value)); -+ return; -+ } -+ addTeamData(table, icon, team -> FormatDefault.format(checked.get(team))); -+ } -+ -+ private void addTeamDataCheckB(Table table, TextureRegion icon, Boolf checked){ -+ if(teams.isEmpty() || teams.allMatch(it -> !checked.get(it))) return; -+ //check allSame -+ boolean value = checked.get(teams.get(0)); -+ if(teams.allMatch(it -> checked.get(it) == value)){ -+ addTeamData(table, icon, value ? "+" : "x"); -+ return; -+ } -+ addTeamData(table, icon, team -> checked.get(team) ? "+" : "×"); -+ } -+ -+ private void addTeamData(Table table, TextureRegion icon, String value){ -+ // 只显示一个数值 -+ table.image(icon).size(15, 15).left(); -+ table.label(() -> "[#" + Pal.accent + "]" + value).align(Align.center).fontScale(fontScl).colspan(table.getColumns() - 1); -+ table.row(); -+ } -+ -+ private void addTeamData(Table table, TextureRegion icon, Func teamFunc){ -+ // 通用情况 -+ table.image(icon).size(15, 15).left(); -+ for(Teams.TeamData teamData : teams){ -+ table.label(() -> "[#" + teamData.team.color + "]" + teamFunc.get(teamData)).fontScale(fontScl); -+ } -+ table.row(); -+ } -+} -\ No newline at end of file diff --git a/patches/client/0065-UI-HudSettingsTable-AdvanceBuildTool.patch b/patches/client/0065-UI-HudSettingsTable-AdvanceBuildTool.patch index b0bf035aca5b..5d8f537a66be 100644 --- a/patches/client/0065-UI-HudSettingsTable-AdvanceBuildTool.patch +++ b/patches/client/0065-UI-HudSettingsTable-AdvanceBuildTool.patch @@ -13,690 +13,3 @@ way-zer on 2024/6/10 at 14:28 整理AdvanceBuildTool way-zer on 2024/7/25 ---- - core/src/mindustryX/features/UIExt.java | 9 + - .../features/ui/AdvanceBuildTool.java | 377 ++++++++++++++++++ - .../features/ui/HudSettingsTable.java | 231 +++++++++++ - .../mindustryX/features/ui/ToolTableBase.java | 24 ++ - 4 files changed, 641 insertions(+) - create mode 100644 core/src/mindustryX/features/ui/AdvanceBuildTool.java - create mode 100644 core/src/mindustryX/features/ui/HudSettingsTable.java - create mode 100644 core/src/mindustryX/features/ui/ToolTableBase.java - -diff --git a/core/src/mindustryX/features/UIExt.java b/core/src/mindustryX/features/UIExt.java -index 7664e5cd240e9e933373cbd1a3eeb382886e94f8..1a33e4f6c9509d0b34422eb360687ffc6c35f3ab 100644 ---- a/core/src/mindustryX/features/UIExt.java -+++ b/core/src/mindustryX/features/UIExt.java -@@ -16,6 +16,8 @@ public class UIExt{ - public static TeamSelectDialog teamSelect; - public static ModsRecommendDialog modsRecommend = new ModsRecommendDialog(); - public static TeamsStatDisplay teamsStatDisplay; -+ public static HudSettingsTable hudSettingsTable = new HudSettingsTable(); -+ public static AdvanceBuildTool advanceBuildTool = new AdvanceBuildTool(); - - public static void init(){ - teamSelect = new TeamSelectDialog(); -@@ -26,6 +28,13 @@ public class UIExt{ - t.left().add(teamsStatDisplay); - t.visible(() -> ui.hudfrag.shown && Core.settings.getBool("showOtherTeamResource")); - }); -+ -+ ui.hudGroup.fill(t -> { -+ t.name = "quickTool"; -+ t.right().add(hudSettingsTable).growX(); -+ t.row().add(advanceBuildTool).growX(); -+ t.visible(() -> ui.hudfrag.shown && Core.settings.getBool("showQuickToolTable")); -+ }); - } - - public static void buildPositionRow(Table tt, Vec2 vec){ -diff --git a/core/src/mindustryX/features/ui/AdvanceBuildTool.java b/core/src/mindustryX/features/ui/AdvanceBuildTool.java -new file mode 100644 -index 0000000000000000000000000000000000000000..1a8bcf05dc875853598e50841f1558a9d41733a3 ---- /dev/null -+++ b/core/src/mindustryX/features/ui/AdvanceBuildTool.java -@@ -0,0 +1,377 @@ -+package mindustryX.features.ui; -+ -+import arc.*; -+import arc.func.*; -+import arc.graphics.*; -+import arc.graphics.g2d.*; -+import arc.math.geom.*; -+import arc.scene.ui.*; -+import arc.scene.ui.layout.*; -+import arc.struct.*; -+import arc.util.*; -+import mindustry.content.*; -+import mindustry.entities.units.*; -+import mindustry.game.*; -+import mindustry.gen.*; -+import mindustry.graphics.*; -+import mindustry.ui.*; -+import mindustry.ui.dialogs.*; -+import mindustry.world.*; -+import mindustry.world.blocks.*; -+import mindustry.world.blocks.environment.*; -+import mindustry.world.blocks.logic.*; -+import mindustry.world.blocks.power.*; -+import mindustry.world.blocks.production.*; -+import mindustry.world.blocks.storage.*; -+import mindustryX.features.func.*; -+ -+import static mindustry.Vars.*; -+ -+//moved from mindustry.arcModule.ui.quickTool.AdvanceBuildTool -+public class AdvanceBuildTool extends ToolTableBase{ -+ BuildRange placement = BuildRange.player; -+ Rect selection = new Rect(); -+ private Block find = Blocks.turbineCondenser, target = Blocks.titaniumConveyor; -+ private Building searchBuild = null; -+ private int searchBlockIndex = -1; -+ -+ public Seq buildingSeq = new Seq<>(); -+ private final BuildTiles buildTiles = new BuildTiles(); -+ private final ObjectFloatMap buildEff = new ObjectFloatMap<>();//default 0f -+ -+ -+ public AdvanceBuildTool(){ -+ icon = Blocks.buildTower.emoji(); -+ Events.on(EventType.WorldLoadEvent.class, e -> rebuild()); -+ } -+ -+ @Override -+ protected void buildTable(){ -+ table(Styles.black6, t -> { -+ t.row().add("区域:"); -+ t.table(tt -> { -+ tt.button((placement == BuildRange.global ? "[cyan]" : "[gray]") + "", Styles.flatBordert, () -> { -+ placement = BuildRange.global; -+ rebuild(); -+ }).tooltip("[cyan]全局检查").size(30f); -+ tt.button((placement == BuildRange.zone ? "[cyan]" : "[gray]") + "\uE818", Styles.flatBordert, () -> { -+ selection = control.input.lastSelection; -+ if(selection.area() < 10f){ -+ ui.announce("当前选定区域为空,请通过F规划区域"); -+ return; -+ } -+ placement = BuildRange.zone; -+ rebuild(); -+ }).tooltip("[cyan]选择范围").size(30f); -+ tt.button((placement == BuildRange.team ? "" : "[gray]") + Blocks.coreShard.emoji(), Styles.flatBordert, () -> { -+ placement = BuildRange.team; -+ rebuild(); -+ }).tooltip("[cyan]队伍区域").size(30f); -+ tt.button((placement == BuildRange.player ? "" : "[gray]") + UnitTypes.gamma.emoji(), Styles.flatBordert, () -> { -+ placement = BuildRange.player; -+ rebuild(); -+ }).tooltip("[cyan]玩家建造区").size(30f); -+ tt.update(() -> { -+ if(placement != BuildRange.zone) return; -+ FuncX.drawText(selection.getCenter(Tmp.v1).scl(tilesize), "建造区域", Scl.scl(1.25f), Color.white); -+ Draw.color(Pal.stat, 0.7f); -+ Draw.z(Layer.effect - 1f); -+ Lines.stroke(Math.min(Math.abs(width), Math.abs(height)) / tilesize / 10f); -+ Lines.rect(selection.x * tilesize - tilesize / 2f, selection.y * tilesize - tilesize / 2f, selection.width * tilesize + tilesize, selection.height * tilesize + tilesize); -+ Draw.reset(); -+ }); -+ }).fillX().row(); -+ t.row().add("设定:"); -+ t.table(tt -> { -+ tt.update(() -> { -+ if(control.input.selectedBlock() && target != control.input.block){ -+ target = control.input.block; -+ rebuild(); -+ } -+ }); -+ tt.button(find.emoji(), Styles.flatBordert, () -> { -+ new BlockSelectDialog(Block::isPlaceable, block -> this.find = block, block -> this.find == block).show(); -+ rebuild(); -+ }).size(30f).tooltip("源方块"); -+ tt.button("", Styles.flatBordert, this::searchBlock).update(button -> { -+ buildingSeq.clear(); -+ if(find.privileged){ -+ for(Team team : Team.all){ -+ buildingSeq.add(team.data().getBuildings(find)); -+ } -+ }else{ -+ buildingSeq.add(player.team().data().getBuildings(find)); -+ } -+ -+ if(!buildingSeq.contains(searchBuild)){ -+ searchBuild = null; -+ searchBlockIndex = -1; -+ } -+ -+ if(buildingSeq.isEmpty()) button.setText("0/0"); -+ else button.setText((searchBlockIndex + 1) + "/" + buildingSeq.size); -+ }).tooltip("搜索方块").growX().width(90f).height(30f); -+ tt.button("\uE803", Styles.flatBordert, this::replaceBlockSetting).tooltip("快速设置").size(30f); -+ tt.button(target.emoji(), Styles.flatBordert, () -> { -+ new BlockSelectDialog(Block::isPlaceable, block -> target = block, block -> target == block).show(); -+ rebuild(); -+ }).size(30f).tooltip("目标方块"); -+ tt.add().width(16); -+ }).fillX().row(); -+ t.row().add("操作:"); -+ t.table(tt -> { -+ tt.button("\uE88A" + Blocks.worldProcessor.emoji(), Styles.flatBordert, () -> { -+ showWorldProcessorInfo(); -+ find = Blocks.worldProcessor; -+ rebuild(); -+ }).tooltip("地图世处信息").width(60f).height(30f); -+ tt.button("P", Styles.flatBordert, () -> buildTiles.buildBlock(target, tile -> { -+ if(target instanceof ThermalGenerator) return target.sumAttribute(((ThermalGenerator)target).attribute, tile); -+ if(target instanceof Drill) return ((Drill)target).countOreArc(tile); -+ return 1f; -+ })).tooltip("自动放置").size(30f); -+ tt.button("R", Styles.flatBordert, () -> replaceBlock(find, target)).tooltip("一键替换").size(30f); -+ if(!net.client()){ -+ tt.button("\uE800", Styles.flatBordert, () -> { -+ instantBuild(); -+ if(mobile) -+ ui.announce("瞬间建造\n[cyan]强制瞬间建造[acid]选择范围内[cyan]内规划中的所有建筑\n[orange]可能出现bug"); -+ }).size(30, 30).tooltip("瞬间建造\n[cyan]强制瞬间建造[acid]选择范围内[cyan]规划中的所有建筑\n[orange]可能出现bug"); -+ } -+ }).fillX().row(); -+ }); -+ } -+ -+ void replaceBlockGroup(Dialog dialog, Table t, Block ori, Block re){ -+ t.button(ori.emoji() + "\uE803" + re.emoji(), () -> { -+ find = ori; -+ target = re; -+ dialog.hide(); -+ }).width(100f).height(30f); -+ } -+ -+ void replaceBlockSetting(){ -+ BaseDialog dialog = new BaseDialog("方块替换器"); -+ dialog.cont.table(t -> { -+ t.table(tt -> tt.label(() -> "当前选择:" + find.emoji() + "\uE803" + target.emoji())).row(); -+ t.image().color(Pal.accent).fillX().row(); -+ t.table(tt -> { -+ replaceBlockGroup(dialog, tt, Blocks.conveyor, Blocks.titaniumConveyor); -+ replaceBlockGroup(dialog, tt, Blocks.conveyor, Blocks.duct); -+ replaceBlockGroup(dialog, tt, Blocks.conduit, Blocks.pulseConduit); -+ replaceBlockGroup(dialog, tt, Blocks.conduit, Blocks.reinforcedConduit); -+ }).padTop(5f).row(); -+ t.image().color(Pal.accent).padTop(5f).fillX().row(); -+ }); -+ dialog.hidden(this::rebuild); -+ dialog.addCloseButton(); -+ dialog.show(); -+ } -+ -+ public static void showWorldProcessorInfo(){ -+ Log.info("当前地图:@", state.map.name()); -+ int[] data = new int[3]; -+ Groups.build.each(b -> { -+ if(b instanceof LogicBlock.LogicBuild lb && lb.block.privileged){ -+ data[0] += 1; -+ data[1] += lb.code.split("\n").length + 1; -+ data[2] += lb.code.length(); -+ } -+ }); -+ Log.info("地图共有@个世处,总共@行指令,@个字符", data[0], data[1], data[2]); -+ ui.announce(Strings.format("地图共有@个世处,总共@行指令,@个字符", data[0], data[1], data[2]), 10); -+ } -+ -+ void replaceBlock(Block ori, Block re){ -+ player.team().data().buildings.each(building -> building.block() == ori && contain(building.tile), -+ building -> player.unit().addBuild(new BuildPlan(building.tile.x, building.tile.y, building.rotation, re, building.config()))); -+ } -+ -+ boolean contain(Tile tile){ -+ if(placement == BuildRange.global) return true; -+ if(placement == BuildRange.zone) return selection.contains(tile.x, tile.y); -+ if(placement == BuildRange.player) return tile.within(player.x, player.y, buildingRange); -+ if(placement == BuildRange.team){ -+ if(state.rules.polygonCoreProtection){ -+ float mindst = Float.MAX_VALUE; -+ CoreBlock.CoreBuild closest = null; -+ for(Teams.TeamData data : state.teams.active){ -+ for(CoreBlock.CoreBuild tiles : data.cores){ -+ float dst = tiles.dst2(tile.x * tilesize, tile.y * tilesize); -+ if(dst < mindst){ -+ closest = tiles; -+ mindst = dst; -+ } -+ } -+ } -+ return closest == null || closest.team == player.team(); -+ }else return !state.teams.anyEnemyCoresWithin(player.team(), tile.x * tilesize, tile.y * tilesize, state.rules.enemyCoreBuildRadius + tilesize); -+ } -+ return true; -+ } -+ -+ void searchBlock(){ -+ if(buildingSeq.size == 0){ -+ ui.announce("[violet]方块搜索\n[acid]未找到此方块"); -+ return; -+ } -+ searchBlockIndex = (searchBlockIndex + 1) % buildingSeq.size; -+ searchBuild = buildingSeq.get(searchBlockIndex); -+ -+ control.input.panCamera(Tmp.v1.set(searchBuild)); -+ ui.announce("[violet]方块搜索\n[white]找到方块[cyan]" + (searchBlockIndex + 1) + "[]/[cyan]" + buildingSeq.size + "[]" + find.emoji()); -+ } -+ -+ void instantBuild(){ -+ player.unit().plans.each(buildPlan -> { -+ if(!contain(buildPlan.tile())) return; -+ forceBuildBlock(buildPlan.block, buildPlan.tile(), player.team(), buildPlan.rotation, buildPlan.config); -+ }); -+ } -+ -+ void forceBuildBlock(Block block, Tile tile, Team team, int rotation, Object config){ -+ if(block == Blocks.cliff) buildCliff(tile); -+ else if(block instanceof OverlayFloor){ -+ tile.setOverlay(block); -+ }else if(block instanceof Floor floor){ -+ tile.setFloor(floor); -+ }else{ -+ tile.setBlock(block, team, rotation); -+ tile.build.configure(config); -+ } -+ pathfinder.updateTile(tile); -+ } -+ -+ void buildCliff(Tile tile){ -+ int rotation = 0; -+ for(int i = 0; i < 8; i++){ -+ Tile other = world.tiles.get(tile.x + Geometry.d8[i].x, tile.y + Geometry.d8[i].y); -+ if(other != null && !other.floor().hasSurface()){ -+ rotation |= (1 << i); -+ } -+ } -+ -+ if(rotation != 0){ -+ tile.setBlock(Blocks.cliff); -+ } -+ -+ tile.data = (byte)rotation; -+ } -+ -+ enum BuildRange{ -+ global, zone, team, player -+ } -+ -+ class BuildTiles{ -+ public int minx, miny, maxx, maxy, width, height; -+ Seq validTile = new Seq<>(); -+ Seq eff = new Seq<>(); -+ float efficiency = 0; -+ Block block; -+ boolean canBuild = true; -+ -+ public BuildTiles(){ -+ } -+ -+ void buildBlock(Block buildBlock, Floatf tilef){ -+ block = buildBlock; -+ updateTiles(); -+ checkValid(); -+ calBlockEff(tilef); -+ eff.sort().reverse().remove(0f); -+ eff.each(this::buildEff); -+ } -+ -+ public void updateTiles(){ -+ minx = 9999; -+ miny = 9999; -+ maxx = -999; -+ maxy = -999; -+ validTile.clear(); -+ eff.clear(); -+ world.tiles.eachTile(tile -> { -+ if(tile == null) return; -+ if(!contain(tile)) return; -+ validTile.add(tile); -+ minx = Math.min(minx, tile.x); -+ miny = Math.min(miny, tile.y); -+ maxx = Math.max(maxx, tile.x); -+ maxy = Math.max(maxy, tile.y); -+ }); -+ buildEff.clear(); -+ width = maxx - minx; -+ height = maxy - miny; -+ } -+ -+ void checkValid(){ -+ validTile.each(tile -> { -+ if( -+ (block.size == 2 && world.getDarkness(tile.x, tile.y) >= 3) || -+ (state.rules.staticFog && state.rules.fog && !fogControl.isDiscovered(player.team(), tile.x, tile.y)) || -+ (tile.floor().isDeep() && !block.floating && !block.requiresWater && !block.placeableLiquid) || //deep water -+ (block == tile.block() && tile.build != null && rotation == tile.build.rotation && block.rotate) || //same block, same rotation -+ !tile.interactable(player.team()) || //cannot interact -+ !tile.floor().placeableOn || //solid wall -+ //replacing a block that should be replaced (e.g. payload placement) -+ !((block.canReplace(tile.block()) || //can replace type -+ (tile.build instanceof ConstructBlock.ConstructBuild build && build.current == block && tile.centerX() == tile.x && tile.centerY() == tile.y)) && //same type in construction -+ block.bounds(tile.x, tile.y, Tmp.r1).grow(0.01f).contains(tile.block().bounds(tile.centerX(), tile.centerY(), Tmp.r2))) || //no replacement -+ (block.requiresWater && tile.floor().liquidDrop != Liquids.water) //requires water but none found -+ ) buildEff.put(tile, -1); // cannot build -+ }); -+ } -+ -+ void calBlockEff(Floatf tilef){ -+ validTile.each(tile -> { -+ canBuild = true; -+ getLinkedTiles(tile, tile1 -> canBuild = buildEff.get(tile, 0f) != -1 && canBuild); //不可能建造 -+ if(canBuild){ -+ efficiency = tilef.get(tile); -+ buildEff.put(tile, efficiency); -+ if(!eff.contains(efficiency)) eff.add(efficiency); -+ }else{ -+ buildEff.remove(tile, 0); -+ } -+ }); -+ } -+ -+ void buildEff(float e){ -+ if(e == 0) return; -+ validTile.each(tile -> { -+ if(buildEff.get(tile, 0f) != e) return; -+ if(!block.canPlaceOn(tile, player.team(), 0)) return; -+ player.unit().addBuild(new BuildPlan(tile.x, tile.y, 0, block)); -+ getFullLinkedTiles(tile, tile1 -> buildEff.remove(tile1, 0f)); -+ }); -+ } -+ -+ private void getLinkedTiles(Tile tile, Cons cons){ -+ if(block.isMultiblock()){ -+ int size = block.size, o = block.sizeOffset; -+ for(int dx = 0; dx < size; dx++){ -+ for(int dy = 0; dy < size; dy++){ -+ Tile other = world.tile(tile.x + dx + o, tile.y + dy + o); -+ if(other != null) cons.get(other); -+ } -+ } -+ }else{ -+ cons.get(tile); -+ } -+ } -+ -+ private void getFullLinkedTiles(Tile tile, Cons cons){ -+ if(block.isMultiblock()){ -+ int size = block.size, o = 0; -+ for(int dx = -size + 1; dx < size; dx++){ -+ for(int dy = -size + 1; dy < size; dy++){ -+ Tile other = world.tile(tile.x + dx + o, tile.y + dy + o); -+ if(other != null) cons.get(other); -+ } -+ } -+ }else{ -+ cons.get(tile); -+ } -+ } -+ -+ } -+} -diff --git a/core/src/mindustryX/features/ui/HudSettingsTable.java b/core/src/mindustryX/features/ui/HudSettingsTable.java -new file mode 100644 -index 0000000000000000000000000000000000000000..8c8b64c30c397e7147f6f6de5bc56f9801f17be1 ---- /dev/null -+++ b/core/src/mindustryX/features/ui/HudSettingsTable.java -@@ -0,0 +1,231 @@ -+package mindustryX.features.ui; -+ -+import arc.*; -+import arc.scene.*; -+import arc.scene.event.*; -+import arc.scene.ui.*; -+import arc.scene.ui.layout.*; -+import arc.util.*; -+import mindustry.content.*; -+import mindustry.game.*; -+import mindustry.gen.*; -+import mindustry.ui.*; -+import mindustry.ui.dialogs.*; -+import mindustryX.features.Settings; -+import mindustryX.features.*; -+ -+import static arc.Core.*; -+import static mindustry.Vars.*; -+import static mindustry.ui.Styles.flatTogglet; -+ -+//moved from mindustry.arcModule.ui.quickTool.HudSettingsTable -+public class HudSettingsTable extends ToolTableBase{ -+ private final Table cont = new Table(); -+ -+ public HudSettingsTable(){ -+ icon = String.valueOf(Iconc.settings); -+ rebuild(); -+ Events.on(EventType.WorldLoadEvent.class, e -> Core.settings.put("removeLogicLock", false)); -+ } -+ -+ @Override -+ protected void buildTable(){ -+ cont.clearChildren(); -+ -+ cont.background(Styles.black6); -+ cont.table(t -> { -+ t.button("[cyan]S", Styles.flatBordert, () -> Call.sendChatMessage("/sync")).size(30).tooltip("同步一波"); -+ t.button("[cyan]观", Styles.flatBordert, () -> Call.sendChatMessage("/ob")).size(30).tooltip("观察者模式"); -+ t.button("[cyan]技", Styles.flatBordert, () -> Call.sendChatMessage("/skill")).size(30).tooltip("技能!"); -+ t.button("[cyan]版", Styles.flatBordert, () -> Call.sendChatMessage("/broad")).size(30).tooltip("服务器信息版"); -+ t.button("[red]版", Styles.flatTogglet, () -> Settings.toggle("ShowInfoPopup")).checked(a -> Core.settings.getBool("ShowInfoPopup")).size(30, 30).tooltip("关闭所有信息版"); -+ t.button("[white]法", Styles.flatBordert, () -> ui.showConfirm("受不了,直接投降?", () -> Call.sendChatMessage("/vote gameover"))).size(30, 30).tooltip("法国军礼"); -+ if(settings.getInt("arcQuickMsg", 0) == 0) -+ t.button("\uE87C", Styles.flatBordert, this::arcQuickMsgTable).size(30, 30).tooltip("快捷消息"); -+ }).left().row(); -+ -+ if(settings.getInt("arcQuickMsg") > 0){ -+ cont.table(t -> { -+ for(int i = 0; i < settings.getInt("arcQuickMsg"); i++){ -+ if(i % settings.getInt("arcQuickMsgKey", 8) == 0) t.row(); -+ int finalI = i; -+ t.button(settings.getString(getArcQuickMsgShortName(i)), Styles.flatBordert, () -> { -+ if(settings.getBool(getArcQuickMsgJs(finalI))) mods.getScripts().runConsole(settings.getString(getArcQuickMsgName(finalI))); -+ else Call.sendChatMessage(settings.getString(getArcQuickMsgName(finalI))); -+ } -+ ).size(30); -+ } -+ t.button("\uE87C", Styles.flatBordert, this::arcQuickMsgTable).size(30, 30).tooltip("快捷消息"); -+ }).left().row(); -+ } -+ cont.table(t -> { -+ t.button("[cyan]块", Styles.flatTogglet, () -> Settings.cycle("blockRenderLevel", 3)) -+ .checked((a) -> RenderExt.blockRenderLevel > 0).size(30, 30).tooltip("建筑显示"); -+ t.button("[cyan]兵", Styles.flatTogglet, () -> RenderExt.unitHide = !RenderExt.unitHide) -+ .checked(a -> !RenderExt.unitHide).size(30, 30).tooltip("兵种显示"); -+ t.button("[cyan]箱", Styles.flatTogglet, () -> Settings.toggle("unithitbox")) -+ .checked(a -> Core.settings.getBool("unithitbox")).size(30, 30).tooltip("碰撞箱显示"); -+ t.button("[cyan]弹", Styles.flatTogglet, () -> Settings.toggle("bulletShow")) -+ .checked(a -> Core.settings.getBool("bulletShow")).size(30, 30).tooltip("子弹显示"); -+ t.button("[cyan]" + Iconc.map, Styles.flatTogglet, () -> Settings.toggle("minimap")) -+ .checked(a -> Core.settings.getBool("minimap")).size(30, 30).tooltip("小地图显示"); -+ t.button("[violet]锁", Styles.flatTogglet, () -> { -+ Settings.toggle("removeLogicLock"); -+ control.input.logicCutscene = false; -+ ui.announce("已移除逻辑视角锁定"); -+ }).checked(a -> Core.settings.getBool("removeLogicLock")).size(30, 30).tooltip("逻辑锁定"); -+ t.button("[cyan]雾", Styles.flatTogglet, () -> { -+ if(!state.rules.pvp || player.team().id == 255) renderer.fogEnabled = !renderer.fogEnabled; -+ }).checked(a -> renderer.fogEnabled).size(30, 30).tooltip("战争迷雾").visible(() -> !state.rules.pvp || player.team().id == 255); -+ }).left().row(); -+ cont.table(t -> { -+ t.button("[red]灯", Styles.flatTogglet, () -> Settings.toggle("drawlight")) -+ .checked(a -> state.rules.lighting).size(30, 30).name("灯光").tooltip("[cyan]开灯啊!"); -+ t.button("[acid]效", Styles.flatTogglet, () -> Settings.toggle("effects")) -+ .checked(a -> Core.settings.getBool("effects")).size(30, 30).tooltip("特效显示"); -+ t.button("[acid]光", Styles.flatTogglet, () -> { -+ Settings.toggle("bloom"); -+ renderer.toggleBloom(settings.getBool("bloom")); -+ }).checked(a -> Core.settings.getBool("bloom")).size(30, 30).tooltip("光效显示"); -+ t.button("[acid]墙", Styles.flatTogglet, () -> enableDarkness ^= true) -+ .checked(a -> enableDarkness).size(30, 30).tooltip("墙体阴影显示"); -+ t.button("[acid]天", Styles.flatTogglet, () -> Settings.toggle("showweather")) -+ .checked(a -> Core.settings.getBool("showweather")).size(30, 30).tooltip("天气显示"); -+ t.button("[cyan]扫", Styles.flatTogglet, () -> ArcScanMode.enabled = !ArcScanMode.enabled) -+ .checked(a -> ArcScanMode.enabled).size(30, 30).tooltip("扫描模式"); -+ t.button(Blocks.worldMessage.emoji(), flatTogglet, () -> Settings.toggle("displayallmessage")).checked(a -> RenderExt.displayAllMessage).size(30, 30).tooltip("开关信息板全显示"); -+ }).left().row(); -+ -+ sliderPref("turretShowRange", 0, 3, 1, s -> switch(s){ -+ case 0 -> "关闭"; -+ case 1 -> "仅对地"; -+ case 2 -> "仅对空"; -+ case 3 -> "全部"; -+ default -> s + ""; -+ }); -+ sliderPref("chatValidType", 0, 3, 1, s -> switch(s){ -+ case 0 -> "原版模式"; -+ case 1 -> "纯净聊天"; -+ case 2 -> "服务器记录"; -+ case 3 -> "全部记录"; -+ default -> s + ""; -+ }); -+ checkPref("unitHealthBar"); -+ sliderPref("unitTransparency", 0, 100, 5, i -> i > 0 ? i + "%" : "关闭"); -+ sliderPref("unitDrawMinHealth", 0, 2500, 50, i -> i + "[red]HP"); -+ sliderPref("unitBarDrawMinHealth", 0, 2500, 100, i -> i + "[red]HP"); -+ sliderPref("unitWeaponRange", 0, 100, 1, i -> i > 0 ? i + "%" : "关闭"); -+ -+ checkPref("alwaysShowUnitRTSAi"); -+ checkPref("unitLogicMoveLine"); -+ checkPref("unitWeaponTargetLine"); -+ -+ checkPref("blockWeaponTargetLine"); -+ checkPref("unitbuildplan"); -+ sliderPref("minimapSize", 40, 400, 10, i -> i + ""); -+ -+ ScrollPane pane = pane(cont).maxSize(800f, 300f).get(); -+ pane.update(() -> { -+ Element e = Core.scene.hit(Core.input.mouseX(), Core.input.mouseY(), true); -+ if(e != null && e.isDescendantOf(pane)){ -+ pane.requestScroll(); -+ }else if(pane.hasScroll()){ -+ Core.scene.setScrollFocus(null); -+ } -+ }); -+ } -+ -+ private void arcQuickMsgTable(){ -+ BaseDialog dialog = new BaseDialog("快捷信息"); -+ dialog.cont.table(t -> { -+ t.add(""" -+ 在此编辑快速消息,可在快捷设置面板显示。如设置: -+ [white]法 /vote gameover -+ 这一指令会添加一个“[white]法的按钮,点击会自动输入/vote gameover。 -+ 由于懒得写更新,请修改滑块后[orange]关闭此窗口后再打开一次[white] -+ 快捷设置面板同样需要[orange]关闭后再打开一次[white]才能生效""").center().fillX().row(); -+ t.table(tt -> { -+ tt.add("快捷消息个数: "); -+ Label label = tt.add(String.valueOf(settings.getInt("arcQuickMsg", 0))).get(); -+ tt.slider(0, 50, 1, settings.getInt("arcQuickMsg", 0), i -> { -+ settings.put("arcQuickMsg", (int)i); -+ label.setText(String.valueOf(settings.getInt("arcQuickMsg"))); -+ }).width(200f).row(); -+ tt.add("每行多少个按键: "); -+ Label label2 = tt.add(String.valueOf(settings.getInt("arcQuickMsgKey", 0))).get(); -+ tt.slider(3, 10, 1, settings.getInt("arcQuickMsgKey", 0), i -> { -+ settings.put("arcQuickMsgKey", (int)i); -+ label2.setText(String.valueOf(settings.getInt("arcQuickMsgKey"))); -+ }).width(200f); -+ }).row(); -+ t.pane(tt -> { -+ tt.add("第i个").width(50f); -+ tt.add("JS").width(50f); -+ tt.add("按钮显示\n(建议单个字符)").width(100f); -+ tt.add(" 输入信息").width(400f).center().row(); -+ -+ for(int i = 0; i < settings.getInt("arcQuickMsg", 0); i++){ -+ tt.add(i + " "); -+ int finalI = i; -+ tt.check("", settings.getBool(getArcQuickMsgJs(finalI)), js -> settings.put(getArcQuickMsgJs(finalI), js)); -+ tt.field(settings.getString(getArcQuickMsgShortName(finalI), "?"), text -> settings.put(getArcQuickMsgShortName(finalI), text)).maxTextLength(10); -+ tt.field(settings.getString(getArcQuickMsgName(finalI), "未输入指令"), text -> settings.put(getArcQuickMsgName(finalI), text)).maxTextLength(300).width(350f); -+ tt.row(); -+ } -+ }); -+ }); -+ dialog.addCloseButton(); -+ dialog.show(); -+ } -+ -+ private String getArcQuickMsgShortName(int i){ -+ return "arcQuickMsgShort" + i; -+ } -+ -+ private String getArcQuickMsgName(int i){ -+ return "arcQuickMsg" + i; -+ } -+ -+ private String getArcQuickMsgJs(int i){ -+ return "arcQuickMsgJs" + i; -+ } -+ -+ public interface StringProcessor{ -+ String get(int i); -+ } -+ -+ public void sliderPref(String name, int min, int max, int step, StringProcessor s){ -+ Slider slider = new Slider(min, max, step, false); -+ Label value = new Label("", Styles.outlineLabel); -+ slider.update(() -> { -+ slider.setValue(settings.getInt(name)); -+ value.setText(s.get((int)slider.getValue())); -+ }); -+ slider.changed(() -> settings.put(name, (int)slider.getValue())); -+ -+ Table content = new Table(); -+ content.add(bundle.get("setting." + name + ".name"), Styles.outlineLabel).left().growX().wrap(); -+ content.add(value).padLeft(10f).right(); -+ content.margin(3f, 33f, 3f, 33f); -+ content.touchable = Touchable.disabled; -+ -+ cont.stack(slider, content).width(Math.min(Core.graphics.getWidth() / 1.2f, 300f)).left().padTop(4f).get(); -+ cont.row(); -+ -+ if(settings.getDefault(name) == null) -+ Log.warn("no default value for " + name); -+ } -+ -+ public void checkPref(String name){ -+ CheckBox box = new CheckBox(bundle.get("setting." + name + ".name")); -+ box.update(() -> box.setChecked(settings.getBool(name))); -+ box.changed(() -> settings.put(name, box.isChecked())); -+ -+ box.left(); -+ cont.add(box).left().padTop(0.5f); -+ cont.row(); -+ -+ if(settings.getDefault(name) == null) -+ Log.warn("no default value for " + name); -+ } -+} -diff --git a/core/src/mindustryX/features/ui/ToolTableBase.java b/core/src/mindustryX/features/ui/ToolTableBase.java -new file mode 100644 -index 0000000000000000000000000000000000000000..726972db14897a81e6f26efddc0404a029d1bd46 ---- /dev/null -+++ b/core/src/mindustryX/features/ui/ToolTableBase.java -@@ -0,0 +1,24 @@ -+package mindustryX.features.ui; -+ -+import arc.scene.ui.layout.*; -+import mindustry.ui.*; -+ -+public abstract class ToolTableBase extends Table{ -+ public String icon = ""; -+ public boolean expand = false; -+ -+ -+ public void rebuild(){ -+ clear(); -+ table().growX().left(); -+ if(expand){ -+ buildTable(); -+ } -+ button((expand ? "" : "[lightgray]") + icon, Styles.flatBordert, () -> { -+ expand = !expand; -+ rebuild(); -+ }).right().width(40f).minHeight(40f).fillY(); -+ } -+ -+ protected abstract void buildTable(); -+} diff --git a/patches/client/0066-API-LogicExt.patch b/patches/client/0066-API-LogicExt.patch index 0ecd941f100a..08581f8aa262 100644 --- a/patches/client/0066-API-LogicExt.patch +++ b/patches/client/0066-API-LogicExt.patch @@ -3,39 +3,3 @@ From: way-zer Date: Fri, 24 May 2024 19:44:57 +0800 Subject: [PATCH] API: LogicExt ---- - core/src/mindustryX/Hooks.java | 1 + - core/src/mindustryX/features/LogicExt.java | 11 +++++++++++ - 2 files changed, 12 insertions(+) - create mode 100644 core/src/mindustryX/features/LogicExt.java - -diff --git a/core/src/mindustryX/Hooks.java b/core/src/mindustryX/Hooks.java -index 28892eb7f43aa7d903541b9d8412018c41975868..f323267b874868d3c8c05919ccc1ea92821f4918 100644 ---- a/core/src/mindustryX/Hooks.java -+++ b/core/src/mindustryX/Hooks.java -@@ -27,6 +27,7 @@ public class Hooks implements ApplicationListener{ - @Override - public void init(){ - Log.infoTag("MindustryX", "Hooks.init"); -+ LogicExt.init(); - if(AutoUpdate.INSTANCE.getActive()) - AutoUpdate.INSTANCE.checkUpdate(); - if(!Vars.headless){ -diff --git a/core/src/mindustryX/features/LogicExt.java b/core/src/mindustryX/features/LogicExt.java -new file mode 100644 -index 0000000000000000000000000000000000000000..e71f51b27d8576727f19b3273a9a6f0536e610c7 ---- /dev/null -+++ b/core/src/mindustryX/features/LogicExt.java -@@ -0,0 +1,11 @@ -+package mindustryX.features; -+ -+import arc.*; -+import mindustry.game.EventType.*; -+ -+public class LogicExt{ -+ public static void init(){ -+ Events.run(Trigger.update, () -> { -+ }); -+ } -+} -\ No newline at end of file diff --git a/patches/client/0067-API-LogicExt-limitUpdate.patch b/patches/client/0067-API-LogicExt-limitUpdate.patch index 5904eb21529f..1af8deb32093 100644 --- a/patches/client/0067-API-LogicExt-limitUpdate.patch +++ b/patches/client/0067-API-LogicExt-limitUpdate.patch @@ -5,8 +5,7 @@ Subject: [PATCH] API(LogicExt) limitUpdate --- core/src/mindustry/entities/EntityGroup.java | 12 ++++++++++++ - core/src/mindustryX/features/LogicExt.java | 10 ++++++++++ - 2 files changed, 22 insertions(+) + 1 file changed, 12 insertions(+) diff --git a/core/src/mindustry/entities/EntityGroup.java b/core/src/mindustry/entities/EntityGroup.java index ea8ef0cdb8a30b54bb304543ea92dc99da44acb6..9c17cf445d723093d45e8b4e5181f86829d1798a 100644 @@ -38,30 +37,3 @@ index ea8ef0cdb8a30b54bb304543ea92dc99da44acb6..9c17cf445d723093d45e8b4e5181f868 for(index = 0; index < array.size; index++){ array.items[index].update(); } -diff --git a/core/src/mindustryX/features/LogicExt.java b/core/src/mindustryX/features/LogicExt.java -index e71f51b27d8576727f19b3273a9a6f0536e610c7..3d0141b5c308df84fea6c061d5b9e810e31b83fd 100644 ---- a/core/src/mindustryX/features/LogicExt.java -+++ b/core/src/mindustryX/features/LogicExt.java -@@ -1,11 +1,21 @@ - package mindustryX.features; - - import arc.*; -+import mindustry.*; - import mindustry.game.EventType.*; - - public class LogicExt{ -+ public static boolean limitUpdate = false; -+ public static int limitDst = 0, limitTimer = 10; -+ - public static void init(){ - Events.run(Trigger.update, () -> { -+ limitUpdate = Core.settings.getBool("limitupdate"); -+ limitDst = Core.settings.getInt("limitdst") * Vars.tilesize; -+ if(limitUpdate && limitTimer-- < 0){ -+ limitUpdate = false; -+ limitTimer = 10; -+ } - }); - } - } -\ No newline at end of file diff --git a/patches/client/0068-FC-LogicExt-terrainSchematic.patch b/patches/client/0068-FC-LogicExt-terrainSchematic.patch index 17a42852e4a9..89b2eabecec4 100644 --- a/patches/client/0068-FC-LogicExt-terrainSchematic.patch +++ b/patches/client/0068-FC-LogicExt-terrainSchematic.patch @@ -10,8 +10,7 @@ Content-Transfer-Encoding: 8bit --- core/src/mindustry/game/Schematics.java | 39 ++++++++++++++++--- .../world/blocks/environment/Floor.java | 1 + - core/src/mindustryX/features/LogicExt.java | 2 + - 3 files changed, 36 insertions(+), 6 deletions(-) + 2 files changed, 34 insertions(+), 6 deletions(-) diff --git a/core/src/mindustry/game/Schematics.java b/core/src/mindustry/game/Schematics.java index 0a7394838bbb36aa985383eae069f7bf6a1fb15d..e207fcc9bbebd1cbcd09800c8cd969aa0892e7bc 100644 @@ -110,24 +109,3 @@ index dfd29de65b8b4b0b07a5610cbe366f13f21a0373..d5482461eebb80f0dac8ce45a9b7a860 } @Override -diff --git a/core/src/mindustryX/features/LogicExt.java b/core/src/mindustryX/features/LogicExt.java -index 3d0141b5c308df84fea6c061d5b9e810e31b83fd..bdc81ee89e34a125a22f05096231a72fec2e3172 100644 ---- a/core/src/mindustryX/features/LogicExt.java -+++ b/core/src/mindustryX/features/LogicExt.java -@@ -7,6 +7,7 @@ import mindustry.game.EventType.*; - public class LogicExt{ - public static boolean limitUpdate = false; - public static int limitDst = 0, limitTimer = 10; -+ public static boolean terrainSchematic = false; - - public static void init(){ - Events.run(Trigger.update, () -> { -@@ -16,6 +17,7 @@ public class LogicExt{ - limitUpdate = false; - limitTimer = 10; - } -+ terrainSchematic = Core.settings.getBool("terrainSchematic"); - }); - } - } -\ No newline at end of file diff --git a/patches/client/0069-API-UpdateExt-worldCreator-allUnlocked.patch b/patches/client/0069-API-UpdateExt-worldCreator-allUnlocked.patch index 1bef59266e04..bbef71879aa5 100644 --- a/patches/client/0069-API-UpdateExt-worldCreator-allUnlocked.patch +++ b/patches/client/0069-API-UpdateExt-worldCreator-allUnlocked.patch @@ -14,8 +14,7 @@ Content-Transfer-Encoding: 8bit core/src/mindustry/world/Block.java | 4 +- core/src/mindustry/world/Build.java | 45 +++++++++++++++++++ .../mindustry/world/blocks/ItemSelection.java | 3 +- - core/src/mindustryX/features/LogicExt.java | 4 ++ - 7 files changed, 90 insertions(+), 6 deletions(-) + 6 files changed, 86 insertions(+), 6 deletions(-) diff --git a/core/src/mindustry/ctype/UnlockableContent.java b/core/src/mindustry/ctype/UnlockableContent.java index e86da73fea326457ac06c5b36ed06c11735a24cf..63ba88bd02ed596c2799d809e71a84cfc63869ae 100644 @@ -262,25 +261,3 @@ index 24e49706de56896a8fbbda3bcf83503457965798..b0beb00affe5107e12a9a64626c6ead2 ImageButton button = cont.button(Tex.whiteui, Styles.clearNoneTogglei, Mathf.clamp(item.selectionSize, 0f, 40f), () -> { if(closeSelect) control.input.config.hideConfig(); -diff --git a/core/src/mindustryX/features/LogicExt.java b/core/src/mindustryX/features/LogicExt.java -index bdc81ee89e34a125a22f05096231a72fec2e3172..d148408a6bac91abe640a07fd06e81d4f8e35c64 100644 ---- a/core/src/mindustryX/features/LogicExt.java -+++ b/core/src/mindustryX/features/LogicExt.java -@@ -7,6 +7,8 @@ import mindustry.game.EventType.*; - public class LogicExt{ - public static boolean limitUpdate = false; - public static int limitDst = 0, limitTimer = 10; -+ public static boolean worldCreator = false; -+ public static boolean allUnlocked = false; - public static boolean terrainSchematic = false; - - public static void init(){ -@@ -17,6 +19,8 @@ public class LogicExt{ - limitUpdate = false; - limitTimer = 10; - } -+ worldCreator = Core.settings.getBool("worldCreator"); -+ allUnlocked = Core.settings.getBool("allUnlocked"); - terrainSchematic = Core.settings.getBool("terrainSchematic"); - }); - } diff --git a/patches/client/0070-UI-AdvanceToolTable.patch b/patches/client/0070-UI-AdvanceToolTable.patch index 382d33a60cbf..f9f60595a428 100644 --- a/patches/client/0070-UI-AdvanceToolTable.patch +++ b/patches/client/0070-UI-AdvanceToolTable.patch @@ -22,601 +22,3 @@ way-zer on 2024/6/10 * 增加警告和规则编辑快捷按钮 * 整理ARC单位工厂界面 way-zer on 2024/9/8 ---- - core/src/mindustryX/features/UIExt.java | 8 +- - .../features/ui/AdvanceToolTable.kt | 111 +++++ - .../features/ui/ArcUnitFactoryDialog.java | 440 ++++++++++++++++++ - 3 files changed, 556 insertions(+), 3 deletions(-) - create mode 100644 core/src/mindustryX/features/ui/AdvanceToolTable.kt - create mode 100644 core/src/mindustryX/features/ui/ArcUnitFactoryDialog.java - -diff --git a/core/src/mindustryX/features/UIExt.java b/core/src/mindustryX/features/UIExt.java -index 1a33e4f6c9509d0b34422eb360687ffc6c35f3ab..c09b83d494c64733e6ec9389fa8dfb2b7bcee49d 100644 ---- a/core/src/mindustryX/features/UIExt.java -+++ b/core/src/mindustryX/features/UIExt.java -@@ -17,6 +17,7 @@ public class UIExt{ - public static ModsRecommendDialog modsRecommend = new ModsRecommendDialog(); - public static TeamsStatDisplay teamsStatDisplay; - public static HudSettingsTable hudSettingsTable = new HudSettingsTable(); -+ public static AdvanceToolTable advanceToolTable = new AdvanceToolTable(); - public static AdvanceBuildTool advanceBuildTool = new AdvanceBuildTool(); - - public static void init(){ -@@ -30,9 +31,10 @@ public class UIExt{ - }); - - ui.hudGroup.fill(t -> { -- t.name = "quickTool"; -- t.right().add(hudSettingsTable).growX(); -- t.row().add(advanceBuildTool).growX(); -+ t.right().name = "quickTool"; -+ t.add(hudSettingsTable).growX().row(); -+ t.add(advanceToolTable).growX().row(); -+ t.add(advanceBuildTool).growX().row(); - t.visible(() -> ui.hudfrag.shown && Core.settings.getBool("showQuickToolTable")); - }); - } -diff --git a/core/src/mindustryX/features/ui/AdvanceToolTable.kt b/core/src/mindustryX/features/ui/AdvanceToolTable.kt -new file mode 100644 -index 0000000000000000000000000000000000000000..c8b246040bb04c2581ebeb042dbc791552ba2998 ---- /dev/null -+++ b/core/src/mindustryX/features/ui/AdvanceToolTable.kt -@@ -0,0 +1,111 @@ -+package mindustryX.features.ui -+ -+import arc.graphics.Color -+import arc.math.Mathf -+import arc.struct.Seq -+import mindustry.Vars -+import mindustry.content.Items -+import mindustry.content.UnitTypes -+import mindustry.game.Team -+import mindustry.gen.Icon -+import mindustry.gen.Iconc -+import mindustry.gen.Payloadc -+import mindustry.gen.Unit -+import mindustry.ui.Styles -+import mindustry.ui.dialogs.CustomRulesDialog -+import mindustry.world.blocks.payloads.Payload -+import mindustryX.features.LogicExt -+import mindustryX.features.Settings -+import mindustryX.features.TimeControl -+import mindustryX.features.UIExt -+ -+//move from mindustry.arcModule.ui.AdvanceToolTable -+class AdvanceToolTable : ToolTableBase() { -+ val factoryDialog: ArcUnitFactoryDialog = ArcUnitFactoryDialog() -+ private val rulesDialog = CustomRulesDialog() -+ -+ init { -+ icon = Iconc.wrench.toString() -+ rebuild() -+ } -+ -+ override fun buildTable() { -+ with(table().get()) { -+ background = Styles.black6 -+ row().add("警告:该页功能主要供单机使用").color(Color.yellow).colspan(2) -+ -+ row().add("单位:") -+ with(table().get()) { -+ button(Items.copper.emoji() + "[acid]+", Styles.cleart) { -+ for (item in Vars.content.items()) Vars.player.core().items[item] = Vars.player.core().storageCapacity -+ }.width(40f).tooltip("[acid]填满核心的所有资源") -+ button(Items.copper.emoji() + "[red]-", Styles.cleart) { -+ for (item in Vars.content.items()) Vars.player.core().items[item] = 0 -+ }.width(40f).tooltip("[acid]清空核心的所有资源") -+ button(UnitTypes.gamma.emoji() + "[acid]+", Styles.cleart) { -+ val cloneUnit = cloneExactUnit(Vars.player.unit()) -+ cloneUnit[Vars.player.x + Mathf.range(8f)] = Vars.player.y + Mathf.range(8f) -+ cloneUnit.add() -+ }.width(40f).tooltip("[acid]克隆") -+ button(UnitTypes.gamma.emoji() + "[red]×", Styles.cleart) { Vars.player.unit().kill() }.width(40f).tooltip("[red]自杀") -+ button(Icon.waves, Styles.clearNonei) { factoryDialog.show() }.width(40f).tooltip("[acid]单位工厂-ARC") -+ } -+ -+ -+ row().add("队伍:") -+ with(table().get()) { -+ for (team in Team.baseTeams) { -+ button(String.format("[#%s]%s", team.color, team.localized()), Styles.flatToggleMenut) { Vars.player.team(team) } -+ .checked { Vars.player.team() === team }.size(30f, 30f) -+ } -+ button("[violet]+", Styles.flatToggleMenut) { UIExt.teamSelect.pickOne({ team: Team? -> Vars.player.team(team) }, Vars.player.team()) } -+ .checked { !Seq.with(*Team.baseTeams).contains(Vars.player.team()) } -+ .tooltip("[acid]更多队伍选择").size(30f, 30f) -+ } -+ -+ row().add("建筑:") -+ with(table().get()) { -+ button("创世神", Styles.flatToggleMenut) { Settings.toggle("worldCreator") } -+ .checked { LogicExt.worldCreator }.size(70f, 30f) -+ button("解禁", Styles.flatToggleMenut) { -+ Settings.toggle("allUnlocked") -+ }.checked { LogicExt.allUnlocked } -+ .tooltip("[acid]显示并允许建造所有物品").size(50f, 30f) -+ button("地形蓝图", Styles.flatToggleMenut) { Settings.toggle("terrainSchematic") } -+ .checked { LogicExt.terrainSchematic }.size(72f, 30f) -+ } -+ -+ row().add("规则:") -+ with(table().get()) { -+ button("无限火力", Styles.flatToggleMenut) { Vars.player.team().rules().cheat = !Vars.player.team().rules().cheat } -+ .checked { Vars.player.team().rules().cheat }.tooltip("[acid]开关自己队的无限火力").size(90f, 30f) -+ button("编辑器", Styles.flatToggleMenut) { Vars.state.rules.editor = !Vars.state.rules.editor } -+ .checked { Vars.state.rules.editor }.size(70f, 30f) -+ button("沙盒", Styles.flatToggleMenut) { Vars.state.rules.infiniteResources = !Vars.state.rules.infiniteResources } -+ .checked { Vars.state.rules.infiniteResources }.size(50f, 30f) -+ button(Iconc.edit.toString(), Styles.cleart) { -+ rulesDialog.show(Vars.state.rules) { Vars.state.rules } -+ }.width(40f) -+ } -+ -+ row().add("沙漏:") -+ table(TimeControl::draw) -+ } -+ } -+ -+ private fun cloneExactUnit(unit: Unit): Unit { -+ val reUnit = unit.type.create(unit.team) -+ reUnit.health = unit.health -+ reUnit.shield = unit.shield -+ reUnit.stack = unit.stack -+ -+ for (effects in Vars.content.statusEffects()) { -+ if (unit.getDuration(effects) > 0f) reUnit.apply(effects, unit.getDuration(effects)) -+ } -+ -+ if (unit is Payloadc && reUnit is Payloadc) { -+ unit.payloads().each { load: Payload? -> reUnit.addPayload(load) } -+ } -+ return reUnit -+ } -+} -\ No newline at end of file -diff --git a/core/src/mindustryX/features/ui/ArcUnitFactoryDialog.java b/core/src/mindustryX/features/ui/ArcUnitFactoryDialog.java -new file mode 100644 -index 0000000000000000000000000000000000000000..310de89da824ff51939c28465ecfd46e35ade7a3 ---- /dev/null -+++ b/core/src/mindustryX/features/ui/ArcUnitFactoryDialog.java -@@ -0,0 +1,440 @@ -+package mindustryX.features.ui; -+ -+import arc.*; -+import arc.graphics.*; -+import arc.math.*; -+import arc.math.geom.*; -+import arc.scene.ui.*; -+import arc.scene.ui.layout.*; -+import arc.struct.*; -+import arc.util.*; -+import mindustry.content.*; -+import mindustry.entities.units.*; -+import mindustry.game.*; -+import mindustry.gen.*; -+import mindustry.type.*; -+import mindustry.ui.*; -+import mindustry.ui.dialogs.*; -+import mindustry.world.*; -+import mindustry.world.blocks.payloads.*; -+import mindustryX.features.*; -+ -+import java.util.*; -+ -+import static mindustry.Vars.*; -+import static mindustry.ui.Styles.*; -+ -+public class ArcUnitFactoryDialog extends BaseDialog{ -+ private int unitCount = 1; -+ private float unitRandDst = 1f; -+ private final Vec2 unitLoc = new Vec2(0, 0); -+ private Unit spawnUnit = UnitTypes.emanate.create(Team.sharded); -+ private final OrderedSet unitStatus = new OrderedSet<>(); -+ private final float[] statusTime = {10, 30f, 60f, 120f, 180f, 300f, 600f, 900f, 1200f, 1500f, 1800f, 2700f, 3600f, Float.MAX_VALUE}; -+ private float chatTime = 0; -+ private boolean showUnitSelect, showUnitPro, showStatesEffect, showItems, showPayload, showSelectPayload, showPayloadBlock, elevation; -+ -+ public ArcUnitFactoryDialog(){ -+ super("单位工厂-ARC"); -+ //noinspection unchecked -+ getCell(cont).setElement(new ScrollPane(cont)).growX(); -+ -+ closeOnBack(); -+ addCloseButton(); -+ //Lazy build -+ shown(() -> { -+ if(cont.hasChildren()) return; -+ setup(); -+ }); -+ } -+ -+ private void setup(){ -+ cont.table(t -> { -+ t.add("目标单位:"); -+ t.image().update(it -> it.setDrawable(spawnUnit.type.uiIcon)).scaling(Scaling.fit).size(iconMed); -+ t.label(() -> spawnUnit.type.localizedName); -+ }).row(); -+ -+ cont.table((r) -> { -+ r.add("数量:"); -+ r.field("" + unitCount, text -> unitCount = Integer.parseInt(text)) -+ .valid(Strings::canParsePositiveInt).maxTextLength(4); -+ -+ r.add("生成范围:"); -+ r.field(Strings.autoFixed(unitRandDst, 3), text -> unitRandDst = Float.parseFloat(text)) -+ .valid(Strings::canParsePositiveFloat).maxTextLength(8).tooltip("在目标点附近的这个范围内随机生成"); -+ r.add("格"); -+ }).row(); -+ -+ cont.table(t -> { -+ t.add("坐标: "); -+ UIExt.buildPositionRow(t, unitLoc); -+ }).row(); -+ -+ cont.table(this::buildUnitFabricator).fillX().row(); -+ -+ cont.button("[orange]生成!", Icon.modeAttack, () -> { -+ Vec2 pos = Tmp.v1.set(unitLoc).scl(tilesize); -+ for(var n = 0; n < unitCount; n++){ -+ Tmp.v2.rnd(Mathf.random(unitRandDst * tilesize)); -+ Unit unit = createUnit(); -+ unit.set(Tmp.v2.add(pos)); -+ unit.add(); -+ } -+ control.input.panCamera(pos); -+ }).fillX().row(); -+ -+ cont.button("[orange]生成(/js)", Icon.modeAttack, () -> { -+ if(chatTime > 0f){ -+ ui.showInfoFade("为了防止因ddos被服务器ban,请勿太快操作", 5f); -+ return; -+ } -+ chatTime = 1f; -+ ui.showInfoFade("已生成单个单位。\n[gray]请不要短时多次使用本功能,否则容易因ddos被服务器ban", 5f); -+ Tmp.v1.rnd(Mathf.random(unitRandDst)).add(unitLoc.x, unitLoc.y).scl(tilesize); -+ sendFormatChat("/js u = UnitTypes.@.create(Team.get(@))", spawnUnit.type.name, spawnUnit.team.id); -+ sendFormatChat("/js u.set(@,@)", unitLoc.x * tilesize, unitLoc.y * tilesize); -+ if(spawnUnit.health != spawnUnit.type.health){ -+ sendFormatChat("/js u.health = @", spawnUnit.health); -+ if(spawnUnit.health > spawnUnit.type.health){ -+ sendFormatChat("/js u.maxHealth = @", spawnUnit.health); -+ } -+ } -+ if(spawnUnit.shield != 0) -+ sendFormatChat("/js u.shield = @", spawnUnit.shield); -+ if(elevation) -+ sendFormatChat("/js u.elevation = 1"); -+ if(!unitStatus.isEmpty()){ -+ sendFormatChat("/js gs=(t,o,n)=>{try{let f=t.getDeclaredField(n);f.setAccessible(true);return f.get(o)}catch(e){let s=t.getSuperclass();return s?gs(s,o,n):null}}"); -+ sendFormatChat("/js statuses = gs(u.class,u,\"statuses\")"); -+ unitStatus.each(entry -> { -+ if(!entry.effect.reactive){ -+ sendFormatChat("/js {e = new StatusEntry().set(StatusEffects.@, @);statuses.add(e);statuses.size}", entry.effect.name, entry.time * 60f); -+ }else sendFormatChat("/js u.apply(StatusEffects.@)", entry.effect.name); -+ }); -+ sendFormatChat("/js delete statuses"); -+ } -+ if(spawnUnit.hasItem()){ -+ sendFormatChat("/js u.addItem(Items.@, @)", spawnUnit.stack.item.name, spawnUnit.stack.amount); -+ } -+ sendFormatChat("/js u.add()"); -+ sendFormatChat("/js delete u"); -+ Time.run(chatTime, () -> chatTime = 0f); -+ control.input.panCamera(Tmp.v1.set(unitLoc).scl(tilesize)); -+ }).fillX().visible(() -> Core.settings.getBool("easyJS")).row(); -+ } -+ -+ void buildUnitFabricator(Table table){ -+ table.clearChildren(); -+ table.button((b) -> { -+ b.image(showUnitSelect ? Icon.upOpen : Icon.downOpen); -+ b.table(t -> { -+ t.add("加工单位:"); -+ t.image(spawnUnit.type.uiIcon).scaling(Scaling.fit).size(iconMed); -+ }).grow(); -+ }, Styles.togglet, () -> showUnitSelect = !showUnitSelect).growX().minWidth(400f).row(); -+ table.collapser(list -> { -+ int i = 0; -+ for(UnitType type : content.units()){ -+ if(i++ % 8 == 0) list.row(); -+ list.button((b) -> b.image(type.uiIcon).scaling(Scaling.fit), cleart, () -> { -+ if(spawnUnit.type != type){ -+ spawnUnit = type.create(spawnUnit.team); -+ buildUnitFabricator(table); -+ } -+ showUnitSelect = !showUnitSelect; -+ buildUnitFabricator(table); -+ }).tooltip(type.localizedName).width(50f).height(50f); -+ } -+ }, () -> showUnitSelect).row(); -+ -+ table.button("[#" + spawnUnit.team.color + "]单位属性", showUnitPro ? Icon.upOpen : Icon.downOpen, Styles.togglet, () -> showUnitPro = !showUnitPro).fillX().row(); -+ table.collapser(t -> { -+ t.table(tt -> { -+ tt.add("[red]血:"); -+ tt.field(Strings.autoFixed(spawnUnit.health, 1), text -> spawnUnit.health = Float.parseFloat(text)).valid(Strings::canParsePositiveFloat); -+ tt.add("[yellow]盾:"); -+ tt.field(Strings.autoFixed(spawnUnit.shield, 1), text -> spawnUnit.shield = Float.parseFloat(text)).valid(Strings::canParsePositiveFloat); -+ }).row(); -+ t.table(tt -> { -+ tt.add("队伍:"); -+ var f = tt.field(String.valueOf(spawnUnit.team.id), text -> spawnUnit.team = Team.get(Integer.parseInt(text))) -+ .valid(text -> Strings.canParsePositiveInt(text) && Integer.parseInt(text) < Team.all.length).maxTextLength(4).get(); -+ for(Team team : Team.baseTeams){ -+ tt.button("[#" + team.color + "]" + team.localized(), flatToggleMenut, () -> { -+ spawnUnit.team = team; -+ f.setText(String.valueOf(team.id)); -+ }).checked(b -> spawnUnit.team == team).size(30, 30); -+ } -+ tt.button("[violet]+", flatToggleMenut, -+ () -> UIExt.teamSelect.pickOne(team -> { -+ spawnUnit.team = team; -+ f.setText(String.valueOf(team.id)); -+ }, spawnUnit.team) -+ ).checked(b -> !Seq.with(Team.baseTeams).contains(spawnUnit.team)).tooltip("[acid]更多队伍选择").center().width(50f).row(); -+ }).row(); -+ t.check("飞行模式 [orange]生成的单位会飞起来", elevation, a -> elevation = !elevation).center().padBottom(5f).padRight(10f); -+ }, () -> showUnitPro).row(); -+ -+ table.button((b) -> { -+ b.image(showStatesEffect ? Icon.upOpen : Icon.downOpen); -+ b.table((t) -> { -+ t.add("单位状态 ").fill(); -+ for(var entry : unitStatus){ -+ t.image(entry.effect.uiIcon).scaling(Scaling.fit); -+ } -+ }).grow(); -+ }, Styles.togglet, () -> showStatesEffect = !showStatesEffect).fillX().row(); -+ -+ table.collapser(t -> { -+ t.table(list -> { -+ int i = 0; -+ for(StatusEffect effect : content.statusEffects()){ -+ if(effect == StatusEffects.none) continue; -+ if(i++ % 8 == 0) list.row(); -+ list.button((b) -> b.image(effect.uiIcon).scaling(Scaling.fit).size(iconMed), squareTogglet, () -> { -+ unitStatus.add(new StatusEntry().set(effect, unitStatus.isEmpty() ? 600f : unitStatus.orderedItems().peek().time)); -+ buildUnitFabricator(table); -+ }).size(50f).color(unitStatus.select(e -> e.effect == effect).isEmpty() ? Color.gray : Color.white).tooltip(effect.localizedName); -+ } -+ }).top().center(); -+ -+ t.row(); -+ -+ t.table(tt -> { -+ tt.defaults().pad(0, 8, 0, 8); -+ tt.add("[acid]血量"); -+ tt.add("[red]伤害"); -+ tt.add("[violet]攻速"); -+ tt.add("[cyan]移速"); -+ tt.row(); -+ float[] status = {1f, 1f, 1f, 1f}; -+ unitStatus.each(s -> { -+ status[0] *= s.effect.healthMultiplier; -+ status[1] *= s.effect.damageMultiplier; -+ status[2] *= s.effect.reloadMultiplier; -+ status[3] *= s.effect.speedMultiplier; -+ }); -+ tt.add(FormatDefault.format(status[0])); -+ tt.add(FormatDefault.format(status[1])); -+ tt.add(FormatDefault.format(status[2])); -+ tt.add(FormatDefault.format(status[3])); -+ }).row(); -+ t.table(list -> { -+ for(var entry : unitStatus){ -+ list.image(entry.effect.uiIcon).scaling(Scaling.fit).size(iconMed); -+ list.add(entry.effect.localizedName).padRight(4f); -+ -+ if(entry.effect.permanent){ -+ list.add("<永久状态>"); -+ }else if(entry.effect.reactive){ -+ list.add("<瞬间状态>"); -+ }else{ -+ list.table(et -> { -+ TextField sField = et.field(checkInf(entry.time), text -> entry.time = Objects.equals(text, "Inf") ? Float.MAX_VALUE : Float.parseFloat(text)) -+ .valid(text -> Objects.equals(text, "Inf") || Strings.canParsePositiveFloat(text)).tooltip("buff持续时间(单位:秒)").maxTextLength(10).get(); -+ et.add("秒"); -+ -+ Slider sSlider = et.slider(0f, statusTime.length - 1f, 1f, statusTimeIndex(entry.time), n -> { -+ if(statusTimeIndex(entry.time) == n) return; -+ sField.setText(checkInf(entry.time = statusTime[(int)n])); -+ }).get(); -+ sField.update(() -> sSlider.setValue(statusTimeIndex(entry.time))); -+ }); -+ } -+ -+ list.button(Icon.cancel, () -> { -+ unitStatus.remove(entry); -+ buildUnitFabricator(table); -+ }); -+ list.row(); -+ } -+ }); -+ }, () -> showStatesEffect).row(); -+ -+ table.button((b) -> { -+ b.image(showItems ? Icon.upOpen : Icon.downOpen); -+ b.table(t -> { -+ t.add("携带物品"); -+ if(spawnUnit.stack.amount > 0){ -+ t.image(spawnUnit.stack.item.uiIcon).scaling(Scaling.fit).size(iconMed); -+ t.add("" + spawnUnit.stack.amount).padRight(4f); -+ } -+ }).grow(); -+ }, Styles.togglet, () -> showItems = !showItems).fillX().row(); -+ table.collapser(pt -> { -+ pt.table(ptt -> { -+ int i = 0; -+ for(Item item : content.items()){ -+ ptt.button(b -> b.image(item.uiIcon).scaling(Scaling.fit).size(iconMed), cleart, () -> { -+ spawnUnit.stack.item = item; -+ if(spawnUnit.stack.amount == 0){ -+ spawnUnit.stack.amount = spawnUnit.itemCapacity(); -+ } -+ buildUnitFabricator(table); -+ }).size(50f).left().tooltip(item.localizedName); -+ if(++i % 8 == 0) ptt.row(); -+ } -+ }); -+ if(spawnUnit.stack.amount > 0){ -+ pt.row(); -+ pt.table(ptt -> { -+ ptt.image(spawnUnit.stack.item.uiIcon).scaling(Scaling.fit).size(iconMed); -+ ptt.add(" 数量:"); -+ ptt.field(String.valueOf(spawnUnit.stack.amount), text -> spawnUnit.stack.amount = Integer.parseInt(text)).valid(value -> { -+ if(!Strings.canParsePositiveInt(value)) return false; -+ int val = Integer.parseInt(value); -+ return 0 < val && val <= spawnUnit.type.itemCapacity; -+ }).maxTextLength(4); -+ ptt.add("/ " + spawnUnit.type.itemCapacity + " "); -+ ptt.button(Icon.up, cleari, () -> { -+ spawnUnit.stack.amount = spawnUnit.type.itemCapacity; -+ buildUnitFabricator(table); -+ }).tooltip("设置物品数量为单位最大容量"); -+ ptt.button(Icon.cancel, cleari, () -> { -+ spawnUnit.stack.amount = 0; -+ buildUnitFabricator(table); -+ }).tooltip("清空单位物品"); -+ }); -+ } -+ }, () -> showItems).row(); -+ -+ if(spawnUnit instanceof Payloadc pay){ -+ table.button((b) -> { -+ b.image(showPayload ? Icon.upOpen : Icon.downOpen); -+ b.table(t -> { -+ t.add("携带负载"); -+ for(Payload payload : pay.payloads()){ -+ t.image(payload.content().uiIcon).scaling(Scaling.fit).maxWidth(32); -+ } -+ }).grow(); -+ }, Styles.togglet, () -> showPayload = !showPayload).fillX().checked(showPayload).row(); -+ table.collapser(p -> { -+ p.defaults().growX().padLeft(32).padRight(32); -+ p.table(pt -> pay.payloads().each(payload -> { -+ if(payload instanceof Payloadc payloadUnit){ -+ pt.button(b -> b.image(payload.content().uiIcon).scaling(Scaling.fit).size(iconMed).getTable().add("[red]*"), squareTogglet, () -> { -+ pay.payloads().remove(payload); -+ buildUnitFabricator(table); -+ }).color(payloadUnit.team().color).size(50f).left(); -+ }else{ -+ pt.button(b -> b.image(payload.content().uiIcon).scaling(Scaling.fit).size(iconMed), squareTogglet, () -> { -+ pay.payloads().remove(payload); -+ buildUnitFabricator(table); -+ }).size(50f).left(); -+ } -+ if(pay.payloads().indexOf(payload) % 8 == 7) pt.row(); -+ })).row(); -+ -+ p.button("载入单位 " + UnitTypes.mono.emoji(), showSelectPayload ? Icon.upOpen : Icon.downOpen, Styles.togglet, () -> showSelectPayload = !showSelectPayload).row(); -+ p.collapser((c) -> { -+ c.table(list -> { -+ int i = 0; -+ for(UnitType units : content.units()){ -+ list.button(b -> b.image(units.uiIcon).scaling(Scaling.fit).size(iconMed), () -> { -+ pay.addPayload(new UnitPayload(units.create(spawnUnit.team))); -+ buildUnitFabricator(table); -+ }).size(50f).tooltip(units.localizedName); -+ if(++i % 8 == 0) list.row(); -+ } -+ }); -+ c.row(); -+ c.table(pt -> { -+ pt.button("[cyan]自递归", () -> { -+ pay.pickup(createUnit()); -+ buildUnitFabricator(table); -+ }).width(200f); -+ pt.button("?", () -> ui.showInfo(""" -+ 使用说明:携带的单位存在一个序列,每个单位可以具备特定的属性。 -+ [cyan]自递归[white]是指根据当前的配置生成一个单位,并储存到载荷序列上 -+ 这一单位具备所有目前设置的属性,包括buff、物品和载荷。 -+ 合理使用自递归可以发掘无限的可能性 -+ [orange][警告]可能导致地图损坏!请备份地图后再使用!""")).size(50f); -+ }).row(); -+ }, () -> showSelectPayload).row(); -+ -+ p.button("载入建筑 " + Blocks.surgeWallLarge.emoji(), showPayloadBlock ? Icon.upOpen : Icon.downOpen, Styles.togglet, () -> showPayloadBlock = !showPayloadBlock).row(); -+ p.collapser(list -> { -+ int i = 0; -+ for(Block payBlock : content.blocks()){ -+ if(!payBlock.isVisible() || !payBlock.isAccessible() || payBlock.isFloor()) -+ continue; -+ list.button(b -> b.image(payBlock.uiIcon).scaling(Scaling.fit).size(iconMed), () -> { -+ pay.addPayload(new BuildPayload(payBlock, spawnUnit.team)); -+ buildUnitFabricator(table); -+ }).size(50f).tooltip(payBlock.localizedName); -+ if(++i % 8 == 0) list.row(); -+ } -+ }, () -> showPayloadBlock); -+ }, () -> showPayload).fillX().row(); -+ } -+ -+ table.button("[red]重置出厂状态", () -> { -+ elevation = false; -+ spawnUnit = spawnUnit.type.create(spawnUnit.team); -+ unitStatus.clear(); -+ buildUnitFabricator(table); -+ }).fillX().row(); -+ //table.add("[orange]单位加工车间。 [white]Made by [violet]Lucky Clover\n").width(400f); -+ } -+ -+ private String checkInf(float value){ -+ if(value == Float.MAX_VALUE){ -+ return "Inf"; -+ } -+ return Strings.autoFixed(value, 1); -+ } -+ -+ private int statusTimeIndex(float time){ -+ for(int i = statusTime.length - 1; i >= 0; i--){ -+ if(statusTime[i] <= time){ -+ return i; -+ } -+ } -+ return 0; -+ } -+ -+ private Unit createUnit(){ -+ Unit unit = spawnUnit; -+ Unit reUnit = unit.type.create(unit.team); -+ reUnit.afterRead(); -+ reUnit.health = unit.health; -+ reUnit.shield = unit.shield; -+ reUnit.stack = unit.stack.copy(); -+ -+ if(unit instanceof Payloadc pay && reUnit instanceof Payloadc rePay){ -+ pay.payloads().each(rePay::addPayload); -+ } -+ -+ if(elevation) reUnit.elevation = 1f; -+ var statuses = getStatuses(reUnit); -+ for(var it : unitStatus){ -+ statuses.add(new StatusEntry().set(it.effect, it.time * 60f)); -+ } -+ -+ return reUnit; -+ } -+ -+ private void sendFormatChat(String format, Object... args){ -+ for(int i = 0; i < args.length; i++){ -+ if(args[i] instanceof Float f){ -+ args[i] = Strings.autoFixed(f, 1); -+ } -+ } -+ Time.run(chatTime, () -> Call.sendChatMessage(Strings.format(format, args))); -+ chatTime = chatTime + 10f; -+ } -+ -+ public static Seq getStatuses(Unit unit){ -+ Class cls = unit.getClass(); -+ if(cls.isAnonymousClass()) cls = cls.getSuperclass(); -+ while(true){ -+ try{ -+ return Reflect.get(cls, unit, "statuses"); -+ }catch(Exception e){ -+ cls = cls.getSuperclass(); -+ if(cls == Unit.class) return Seq.with(); -+ } -+ } -+ } -+} diff --git a/patches/client/0071-UI-ARC-AuxiliaryTools.patch b/patches/client/0071-UI-ARC-AuxiliaryTools.patch index 9ac0a8314689..d314fed57bd6 100644 --- a/patches/client/0071-UI-ARC-AuxiliaryTools.patch +++ b/patches/client/0071-UI-ARC-AuxiliaryTools.patch @@ -27,32 +27,8 @@ way-zer on 2024/6/27 * clean way-zer on 2024/9/8 --- - .../mindustry/ui/fragments/HudFragment.java | 13 ++ - .../features/ui/AuxiliaryTools.java | 73 ++++++ - .../features/ui/auxiliary/AITools.java | 115 ++++++++++ - .../features/ui/auxiliary/MapInfoTable.java | 143 ++++++++++++ - .../features/ui/auxiliary/MarkTable.java | 53 +++++ - .../ui/auxiliary/MobileScriptButtons.java | 46 ++++ - .../features/ui/auxiliary/RStyles.java | 71 ++++++ - .../features/ui/auxiliary/ScriptButtons.java | 61 +++++ - .../features/ui/auxiliary/WaveInfoTable.java | 132 +++++++++++ - .../features/ui/auxiliary/ai/ATRIAI.java | 210 ++++++++++++++++++ - .../ui/auxiliary/ai/ArcBuilderAI.java | 182 +++++++++++++++ - .../features/ui/auxiliary/ai/ArcMinerAI.java | 122 ++++++++++ - .../features/ui/auxiliary/ai/ArcRepairAI.java | 75 +++++++ - 13 files changed, 1296 insertions(+) - create mode 100644 core/src/mindustryX/features/ui/AuxiliaryTools.java - create mode 100644 core/src/mindustryX/features/ui/auxiliary/AITools.java - create mode 100644 core/src/mindustryX/features/ui/auxiliary/MapInfoTable.java - create mode 100644 core/src/mindustryX/features/ui/auxiliary/MarkTable.java - create mode 100644 core/src/mindustryX/features/ui/auxiliary/MobileScriptButtons.java - create mode 100644 core/src/mindustryX/features/ui/auxiliary/RStyles.java - create mode 100644 core/src/mindustryX/features/ui/auxiliary/ScriptButtons.java - create mode 100644 core/src/mindustryX/features/ui/auxiliary/WaveInfoTable.java - create mode 100644 core/src/mindustryX/features/ui/auxiliary/ai/ATRIAI.java - create mode 100644 core/src/mindustryX/features/ui/auxiliary/ai/ArcBuilderAI.java - create mode 100644 core/src/mindustryX/features/ui/auxiliary/ai/ArcMinerAI.java - create mode 100644 core/src/mindustryX/features/ui/auxiliary/ai/ArcRepairAI.java + core/src/mindustry/ui/fragments/HudFragment.java | 13 +++++++++++++ + 1 file changed, 13 insertions(+) diff --git a/core/src/mindustry/ui/fragments/HudFragment.java b/core/src/mindustry/ui/fragments/HudFragment.java index 37a57b425a423ddff029268c938423f6711340c8..6a9359568e73967b34be82618ec7a95583e5cf6e 100644 @@ -99,1358 +75,3 @@ index 37a57b425a423ddff029268c938423f6711340c8..6a9359568e73967b34be82618ec7a955 //fps display cont.table(info -> { info.name = "fps/ping"; -diff --git a/core/src/mindustryX/features/ui/AuxiliaryTools.java b/core/src/mindustryX/features/ui/AuxiliaryTools.java -new file mode 100644 -index 0000000000000000000000000000000000000000..dc3b163caf2f8adddb1aa4528cd162a07a9b91d7 ---- /dev/null -+++ b/core/src/mindustryX/features/ui/AuxiliaryTools.java -@@ -0,0 +1,73 @@ -+package mindustryX.features.ui; -+ -+import arc.*; -+import arc.graphics.g2d.*; -+import arc.scene.style.*; -+import arc.scene.ui.layout.*; -+import arc.struct.*; -+import mindustry.game.EventType.*; -+import mindustry.ui.*; -+import mindustryX.features.ui.auxiliary.*; -+ -+public class AuxiliaryTools extends Table{ -+ static{ -+ RStyles.load(); -+ } -+ -+ private boolean shown = true; -+ private final Seq
toolsTables = Seq.with( -+ new MapInfoTable(), -+ new WaveInfoTable(), -+ new AITools(), -+ new ScriptButtons(), -+ new MobileScriptButtons(), -+ new MarkTable() -+ ); -+ -+ public AuxiliaryTools(){ -+ name = "AuxiliaryTable"; -+ Events.on(WorldLoadEvent.class, e -> rebuild()); -+ Events.on(ResetEvent.class, e -> clearChildren()); -+ rebuild(); -+ } -+ -+ public void toggle(){ -+ shown = !shown; -+ rebuild(); -+ } -+ -+ private void rebuild(){ -+ clearChildren(); -+ -+ table(Styles.black5, buttons -> { -+ buttons.button("[acid]辅助器", RStyles.clearLineNoneTogglet, this::toggle).size(80f, 40f).tooltip((shown ? "关闭" : "开启") + "辅助器"); -+ -+ if(!shown) return; -+ for(Table table : toolsTables){ -+ buttons.button(table.icon, RStyles.clearAccentNoneTogglei, 30, () -> table.shown ^= true) -+ .size(40).checked(b -> table.shown); -+ } -+ }).fillX().row(); -+ if(!shown) return; -+ table(Styles.black3, body -> { -+ body.defaults().expandX().left(); -+ for(Table table : toolsTables){ -+ table.margin(4); -+ body.collapser(table, () -> table.shown).growX().row(); -+ } -+ }).fillX().left(); -+ } -+ -+ public abstract static class Table extends arc.scene.ui.layout.Table{ -+ public boolean shown; -+ protected Drawable icon; -+ -+ public Table(TextureRegion region){ -+ this(new TextureRegionDrawable(region)); -+ } -+ -+ public Table(Drawable icon){ -+ this.icon = icon; -+ } -+ } -+} -diff --git a/core/src/mindustryX/features/ui/auxiliary/AITools.java b/core/src/mindustryX/features/ui/auxiliary/AITools.java -new file mode 100644 -index 0000000000000000000000000000000000000000..ce38210184665e23528e97e1e8115ed7d6428555 ---- /dev/null -+++ b/core/src/mindustryX/features/ui/auxiliary/AITools.java -@@ -0,0 +1,115 @@ -+package mindustryX.features.ui.auxiliary; -+ -+import arc.*; -+import arc.graphics.g2d.*; -+import arc.scene.style.*; -+import arc.scene.ui.*; -+import arc.scene.ui.layout.*; -+import arc.util.*; -+import mindustry.ai.types.*; -+import mindustry.content.*; -+import mindustry.entities.units.*; -+import mindustry.game.*; -+import mindustry.gen.*; -+import mindustry.graphics.*; -+import mindustry.ui.*; -+import mindustry.ui.dialogs.*; -+import mindustry.world.*; -+import mindustryX.features.ui.*; -+import mindustryX.features.ui.auxiliary.ai.*; -+ -+import static mindustry.Vars.*; -+ -+public class AITools extends AuxiliaryTools.Table{ -+ private AIController selectAI; -+ -+ public AITools(){ -+ super(Icon.android); -+ -+ Events.run(EventType.Trigger.update, () -> { -+ if(selectAI != null){ -+ selectAI.unit(player.unit()); -+ selectAI.updateUnit(); -+ } -+ }); -+ -+ button(Icon.settingsSmall, RStyles.clearLineNoneTogglei, 30, this::showSettingDialog); -+ -+ if(false) aiButton(new ATRIAI(), Blocks.worldProcessor.region, "ATRI AI"); -+ aiButton(new ArcMinerAI(), UnitTypes.mono.region, "矿机AI"); -+ aiButton(new ArcBuilderAI(), UnitTypes.poly.region, "重建AI"); -+ aiButton(new ArcRepairAI(), UnitTypes.mega.region, "修复AI"); -+ aiButton(new DefenderAI(), UnitTypes.oct.region, "保护AI"); -+ } -+ -+ private void aiButton(AIController ai, TextureRegion textureRegion, String describe){ -+ button(new TextureRegionDrawable(textureRegion), RStyles.clearLineNoneTogglei, 30, () -> { -+ if(selectAI != null) selectAI = null; -+ else selectAI = ai; -+ }).checked(b -> selectAI == ai).size(40).tooltip(describe); -+ } -+ -+ private void showSettingDialog(){ -+ int cols = (int)Math.max(Core.graphics.getWidth() / Scl.scl(480), 1); -+ -+ BaseDialog dialog = new BaseDialog("ARC-AI设定器"); -+ -+ dialog.cont.table(t -> { -+ t.add("minerAI-矿物筛选器").color(Pal.accent).pad(cols / 2f).center().row(); -+ -+ t.image().color(Pal.accent).fillX().row(); -+ -+ t.table(c -> { -+ c.add("地表矿").row(); -+ -+ c.table(list -> { -+ int i = 0; -+ for(Block block : ArcMinerAI.oreAllList){ -+ if(indexer.floorOresCount[block.id] == 0) continue; -+ if(i++ % 3 == 0) list.row(); -+ list.button(block.emoji() + "\n" + indexer.floorOresCount[block.id], Styles.flatToggleMenut, () -> { -+ if(ArcMinerAI.oreList.contains(block)) ArcMinerAI.oreList.remove(block); -+ else if(!ArcMinerAI.oreList.contains(block)) ArcMinerAI.oreList.add(block); -+ }).tooltip(block.localizedName).checked(k -> ArcMinerAI.oreList.contains(block)).width(100f).height(50f); -+ } -+ }).row(); -+ -+ c.add("墙矿").row(); -+ -+ c.table(list -> { -+ int i = 0; -+ for(Block block : ArcMinerAI.oreAllWallList){ -+ if(indexer.wallOresCount[block.id] == 0) continue; -+ if(i++ % 3 == 0) list.row(); -+ list.button(block.emoji() + "\n" + indexer.wallOresCount[block.id], Styles.flatToggleMenut, () -> { -+ if(ArcMinerAI.oreWallList.contains(block)) ArcMinerAI.oreWallList.remove(block); -+ else if(!ArcMinerAI.oreWallList.contains(block)) ArcMinerAI.oreWallList.add(block); -+ }).tooltip(block.localizedName).checked(k -> ArcMinerAI.oreWallList.contains(block)).width(100f).height(50f); -+ } -+ }).row(); -+ -+ }).growX(); -+ }).growX().row(); -+ -+ dialog.cont.table(t -> { -+ t.add("builderAI").color(Pal.accent).pad(cols / 2f).center().row(); -+ -+ t.image().color(Pal.accent).fillX().row(); -+ -+ t.table(tt -> { -+ tt.add("重建冷却时间: "); -+ -+ TextField sField = tt.field(ArcBuilderAI.rebuildTime + "", text -> ArcBuilderAI.rebuildTime = Math.max(5f, Float.parseFloat(text))).valid(Strings::canParsePositiveFloat).width(200f).get(); -+ -+ tt.slider(5, 200, 5, i -> { -+ ArcBuilderAI.rebuildTime = i; -+ sField.setText(ArcBuilderAI.rebuildTime + ""); -+ }).width(200f); -+ }).growX(); -+ }).growX(); -+ -+ dialog.addCloseButton(); -+ dialog.show(); -+ } -+ -+} -diff --git a/core/src/mindustryX/features/ui/auxiliary/MapInfoTable.java b/core/src/mindustryX/features/ui/auxiliary/MapInfoTable.java -new file mode 100644 -index 0000000000000000000000000000000000000000..2e6e313c2bc7434072b079c88f48db5f7411c796 ---- /dev/null -+++ b/core/src/mindustryX/features/ui/auxiliary/MapInfoTable.java -@@ -0,0 +1,143 @@ -+package mindustryX.features.ui.auxiliary; -+ -+ -+import arc.*; -+import arc.graphics.*; -+import arc.scene.ui.*; -+import arc.scene.ui.layout.*; -+import mindustry.content.*; -+import mindustry.editor.*; -+import mindustry.gen.*; -+import mindustry.graphics.*; -+import mindustry.input.*; -+import mindustry.ui.*; -+import mindustry.ui.dialogs.*; -+import mindustry.world.*; -+import mindustry.world.blocks.environment.*; -+import mindustryX.features.*; -+import mindustryX.features.ui.*; -+ -+import static mindustry.Vars.*; -+ -+public class MapInfoTable extends AuxiliaryTools.Table{ -+ public MapInfoTable(){ -+ super(Icon.map); -+ defaults().size(40); -+ -+ MapInfoDialog mapInfoDialog = new MapInfoDialog(); -+ button(Icon.map, RStyles.clearAccentNonei, mapInfoDialog::show).tooltip("地图信息"); -+ button(Items.copper.emoji(), RStyles.clearLineNonet, this::floorStatisticDialog).tooltip("矿物信息"); -+ button(Icon.chatSmall, RStyles.clearAccentNonei, () -> UIExt.arcMessageDialog.show()).tooltip("中央监控室"); -+ button(Icon.playersSmall, RStyles.clearAccentNonei, () -> { -+ var players = Groups.player.copy(); -+ if(players.isEmpty()) return; -+ if(control.input instanceof DesktopInput){ -+ ((DesktopInput)control.input).panning = true; -+ } -+ InputHandler.follow = players.get((players.indexOf(InputHandler.follow, true) + 1) % players.size); -+ UIExt.announce("视角追踪:" + InputHandler.follow.name); -+ }).tooltip("切换跟踪玩家"); -+ if(!mobile) button(Icon.editSmall, RStyles.clearAccentNonei, this::uiTable).tooltip("ui大全"); -+ button(Icon.pencilSmall, RStyles.clearAccentNonei, () -> EffectsDialog.withAllEffects().show()).tooltip("特效大全"); -+ } -+ -+ private void floorStatisticDialog(){ -+ BaseDialog dialog = new BaseDialog("ARC-矿物统计"); -+ Table table = dialog.cont; -+ table.clear(); -+ -+ table.table(c -> { -+ c.add("地表矿").color(Pal.accent).center().fillX().row(); -+ c.image().color(Pal.accent).fillX().row(); -+ c.table(list -> { -+ int i = 0; -+ for(Block block : content.blocks().select(b -> b instanceof Floor f && !f.wallOre && f.itemDrop != null)){ -+ if(indexer.floorOresCount[block.id] == 0) continue; -+ if(i++ % 4 == 0) list.row(); -+ list.add(block.emoji() + " " + block.localizedName + "\n" + indexer.floorOresCount[block.id]).width(100f).height(50f); -+ } -+ }).row(); -+ -+ c.add("墙矿").color(Pal.accent).center().fillX().row(); -+ c.image().color(Pal.accent).fillX().row(); -+ c.table(list -> { -+ int i = 0; -+ for(Block block : content.blocks().select(b -> ((b instanceof Floor f && f.wallOre) || b instanceof StaticWall) && b.itemDrop != null)){ -+ if(indexer.wallOresCount[block.id] == 0) continue; -+ if(i++ % 4 == 0) list.row(); -+ list.add(block.emoji() + " " + block.localizedName + "\n" + indexer.wallOresCount[block.id]).width(100f).height(50f); -+ } -+ }).row(); -+ -+ c.add("液体").color(Pal.accent).center().fillX().row(); -+ c.image().color(Pal.accent).fillX().row(); -+ c.table(list -> { -+ int i = 0; -+ for(Block block : content.blocks().select(b -> ((b instanceof Floor f && f.liquidDrop != null)))){ -+ if(indexer.floorOresCount[block.id] == 0) continue; -+ if(i++ % 4 == 0) list.row(); -+ list.add(block.emoji() + " " + block.localizedName + "\n" + indexer.floorOresCount[block.id]).width(100f).height(50f); -+ } -+ }).row(); -+ }); -+ dialog.addCloseButton(); -+ dialog.show(); -+ } -+ -+ private void uiTable(){ -+ BaseDialog dialog = new BaseDialog("ARC-ui大全"); -+ TextField sField = dialog.cont.field("", text -> { -+ }).fillX().get(); -+ dialog.cont.row(); -+ -+ dialog.cont.pane(c -> { -+ c.add("颜色").color(Pal.accent).center().fillX().row(); -+ c.image().color(Pal.accent).fillX().row(); -+ c.table(ct -> { -+ int i = 0; -+ for(var colorEntry : Colors.getColors()){ -+ Color value = colorEntry.value; -+ String key = colorEntry.key; -+ ct.button("[#" + value + "]" + key, Styles.cleart, () -> { -+ Core.app.setClipboardText("[#" + value + "]"); -+ sField.appendText("[#" + value + "]"); -+ }).size(50f).tooltip(key); -+ i += 1; -+ if(i % 15 == 0) ct.row(); -+ } -+ }).row(); -+ c.add("物品").color(Pal.accent).center().fillX().row(); -+ c.image().color(Pal.accent).fillX().row(); -+ c.table(ct -> { -+ int i = 0; -+ for(var it : Fonts.stringIcons){ -+ final String icon = it.value; -+ ct.button(icon, Styles.cleart, () -> { -+ Core.app.setClipboardText(icon); -+ sField.appendText(icon); -+ }).size(50f).tooltip(it.key); -+ i += 1; -+ if(i % 15 == 0) ct.row(); -+ } -+ }).row(); -+ c.add("图标").color(Pal.accent).center().fillX().row(); -+ c.image().color(Pal.accent).fillX().row(); -+ c.table(ct -> { -+ int i = 0; -+ for(var it : Iconc.codes){ -+ String icon = String.valueOf((char)it.value), internal = it.key; -+ ct.button(icon, Styles.cleart, () -> { -+ Core.app.setClipboardText(icon); -+ sField.appendText(icon); -+ }).size(50f).tooltip(internal); -+ i += 1; -+ if(i % 15 == 0) ct.row(); -+ } -+ }).row(); -+ }).row(); -+ -+ dialog.addCloseButton(); -+ dialog.show(); -+ } -+ -+} -diff --git a/core/src/mindustryX/features/ui/auxiliary/MarkTable.java b/core/src/mindustryX/features/ui/auxiliary/MarkTable.java -new file mode 100644 -index 0000000000000000000000000000000000000000..a6ab2e3fa2b59fdb859e7fa4062dc8b656bec8a5 ---- /dev/null -+++ b/core/src/mindustryX/features/ui/auxiliary/MarkTable.java -@@ -0,0 +1,53 @@ -+package mindustryX.features.ui.auxiliary; -+ -+import arc.*; -+import arc.input.*; -+import arc.scene.*; -+import arc.scene.event.*; -+import mindustry.content.*; -+import mindustry.gen.*; -+import mindustry.ui.fragments.*; -+import mindustryX.features.*; -+import mindustryX.features.ui.AuxiliaryTools.*; -+ -+import static mindustry.Vars.*; -+ -+public class MarkTable extends Table{ -+ public final Element mobileHitter = new Element(); -+ -+ public MarkTable(){ -+ super(Icon.effect); -+ -+ mobileHitter.fillParent = true; -+ mobileHitter.addListener(new ElementGestureListener(20, 0.4f, MarkerType.heatTime / 60f, 0.15f){ -+ @Override -+ public boolean longPress(Element actor, float x, float y){ -+ MarkerType.selected.markWithMessage(Core.input.mouseWorld()); -+ mobileHitter.remove(); -+ return true; -+ } -+ -+ @Override -+ public void fling(InputEvent event, float velocityX, float velocityY, KeyCode button){ -+ mobileHitter.remove(); -+ ui.announce("[yellow]你已退出标记模式"); -+ } -+ }); -+ -+ if(mobile){ -+ button("♐ >", RStyles.clearLineNonet, () -> { -+ ui.hudGroup.addChild(mobileHitter); -+ ui.announce("[cyan]你已进入标记模式,长按屏幕可进行一次标记(外划可以退出)."); -+ }).height(40).width(70f).tooltip("开启手机标记"); -+ } -+ -+ for(var type : MarkerType.allTypes){ -+ button(type.shortName(), RStyles.clearLineNoneTogglet, () -> MarkerType.selected = type) -+ .checked(b -> MarkerType.selected == type).size(40).tooltip(type.localizedName); -+ } -+ -+ button("T", RStyles.clearLineNoneTogglet, () -> ui.chatfrag.nextMode()) -+ .checked(b -> ui.chatfrag.mode == ChatFragment.ChatMode.team).size(40).tooltip("前缀添加/t"); -+ button("" + Iconc.zoom, RStyles.clearLineNonet, MarkerType::lockOnLastMark).size(40).tooltip("锁定上个标记点"); -+ } -+} -diff --git a/core/src/mindustryX/features/ui/auxiliary/MobileScriptButtons.java b/core/src/mindustryX/features/ui/auxiliary/MobileScriptButtons.java -new file mode 100644 -index 0000000000000000000000000000000000000000..62f739019f8aa3dc7fb6965eebe107a6a6bd81f1 ---- /dev/null -+++ b/core/src/mindustryX/features/ui/auxiliary/MobileScriptButtons.java -@@ -0,0 +1,46 @@ -+package mindustryX.features.ui.auxiliary; -+ -+import arc.graphics.g2d.*; -+import arc.scene.style.*; -+import arc.scene.ui.*; -+import arc.scene.ui.layout.*; -+import mindustry.content.*; -+import mindustry.gen.*; -+import mindustryX.features.*; -+import mindustryX.features.ui.*; -+ -+import static mindustry.Vars.*; -+ -+/** -+ * 专为手机制备的脚本按钮 -+ */ -+public class MobileScriptButtons extends AuxiliaryTools.Table{ -+ -+ public MobileScriptButtons(){ -+ super(UnitTypes.emanate.uiIcon); -+ defaults().size(40); -+ if(mobile) shown = true; -+ -+ scriptButton(Icon.unitsSmall, "指挥模式", () -> control.input.commandMode = !control.input.commandMode).checked(b -> control.input.commandMode); -+ scriptButton(Icon.pause, "暂停建造", () -> control.input.isBuilding = !control.input.isBuilding).checked(b -> control.input.isBuilding); -+ scriptButton(Icon.up, "捡起载荷", () -> control.input.tryPickupPayload()); -+ scriptButton(Icon.down, "丢下载荷", () -> control.input.tryDropPayload()); -+ scriptButton(Blocks.payloadConveyor.uiIcon, "进入传送带", () -> { -+ Building build = player.buildOn(); -+ -+ if(build == null) return; -+ -+ Unit unit = player.unit(); -+ Call.unitBuildingControlSelect(unit, build); -+ }); -+ scriptButton(Blocks.radar.uiIcon, "雷达扫描", () -> ArcRadar.mobileRadar = !ArcRadar.mobileRadar); -+ } -+ -+ protected void scriptButton(TextureRegion region, String description, Runnable runnable){ -+ scriptButton(new TextureRegionDrawable(region), description, runnable); -+ } -+ -+ protected Cell scriptButton(Drawable icon, String description, Runnable runnable){ -+ return button(icon, RStyles.clearLineNonei, iconMed, runnable).tooltip(description, true); -+ } -+} -diff --git a/core/src/mindustryX/features/ui/auxiliary/RStyles.java b/core/src/mindustryX/features/ui/auxiliary/RStyles.java -new file mode 100644 -index 0000000000000000000000000000000000000000..49ed101b4a24b034fc313468441a213b02fc4d9d ---- /dev/null -+++ b/core/src/mindustryX/features/ui/auxiliary/RStyles.java -@@ -0,0 +1,71 @@ -+package mindustryX.features.ui.auxiliary; -+ -+import arc.graphics.*; -+import arc.scene.style.*; -+import arc.scene.ui.ImageButton.*; -+import arc.scene.ui.TextButton.*; -+import mindustry.gen.*; -+import mindustry.ui.*; -+ -+import static mindustry.gen.Tex.underlineWhite; -+import static mindustry.ui.Styles.*; -+ -+//move from mindustry.arcModule.ui.RStyles -+public class RStyles{ -+ public static Drawable black1; -+ -+ public static TextButtonStyle -+ clearLineNonet, -+ clearLineNoneTogglet; -+ -+ public static ImageButtonStyle -+ clearAccentNonei, -+ clearAccentNoneTogglei, -+ clearLineNonei, -+ clearLineNoneTogglei; -+ -+ public static void load(){ -+ var whiteuir = (TextureRegionDrawable)Tex.whiteui; -+ -+ black1 = whiteuir.tint(0f, 0f, 0f, 0.1f); -+ -+ clearLineNonet = new TextButtonStyle(){{ -+ font = Fonts.def; -+ fontColor = Color.white; -+ disabled = black; -+ disabledFontColor = Color.gray; -+ up = none; -+ over = accentDrawable; -+ down = underlineWhite; -+ }}; -+ -+ clearLineNoneTogglet = new TextButtonStyle(fullTogglet){{ -+ up = none; -+ over = accentDrawable; -+ down = underlineWhite; -+ checked = underlineWhite; -+ disabledFontColor = Color.white; -+ }}; -+ -+ clearAccentNonei = new ImageButtonStyle(clearNonei){{ -+ up = none; -+ over = black3; -+ down = none; -+ }}; -+ -+ clearAccentNoneTogglei = new ImageButtonStyle(clearAccentNonei){{ -+ checked = accentDrawable; -+ }}; -+ -+ clearLineNonei = new ImageButtonStyle(clearNonei){{ -+ up = none; -+ over = accentDrawable; -+ down = none; -+ }}; -+ -+ clearLineNoneTogglei = new ImageButtonStyle(clearLineNonei){{ -+ checked = underlineWhite; -+ }}; -+ } -+ -+} -diff --git a/core/src/mindustryX/features/ui/auxiliary/ScriptButtons.java b/core/src/mindustryX/features/ui/auxiliary/ScriptButtons.java -new file mode 100644 -index 0000000000000000000000000000000000000000..bd363d9dc2812d3bd5971cf37aa2e4503a6753eb ---- /dev/null -+++ b/core/src/mindustryX/features/ui/auxiliary/ScriptButtons.java -@@ -0,0 +1,61 @@ -+package mindustryX.features.ui.auxiliary; -+ -+import arc.*; -+import arc.func.*; -+import arc.graphics.g2d.*; -+import arc.scene.style.*; -+import arc.scene.ui.*; -+import arc.scene.ui.layout.*; -+import mindustry.content.*; -+import mindustry.gen.*; -+import mindustry.input.*; -+import mindustry.ui.dialogs.*; -+import mindustryX.features.*; -+import mindustryX.features.ui.*; -+ -+import static mindustry.Vars.*; -+ -+public class ScriptButtons extends AuxiliaryTools.Table{ -+ public ScriptButtons(){ -+ super(UnitTypes.gamma.uiIcon); -+ defaults().size(40); -+ -+ scriptButton(Blocks.buildTower.uiIcon, "在建造列表加入被摧毁建筑", () -> player.buildDestroyedBlocks()); -+ scriptButton(Items.copper.uiIcon, "一键放置", () -> player.dropItems()); -+ addSettingButton(Icon.modeAttack, "autotarget", "自动攻击", null); -+ addSettingButton(UnitTypes.vela.uiIcon, "forceBoost", "强制助推", null); -+ addSettingButton(Icon.eyeSmall, "viewMode", "视角脱离玩家", s -> { -+ if(s){ -+ if(control.input instanceof DesktopInput desktopInput){ -+ desktopInput.panning = true; -+ } -+ }else{ -+ Core.camera.position.set(player); -+ } -+ }); -+ } -+ -+ protected void addSettingButton(TextureRegion region, String settingName, String description, Boolc onClick){ -+ addSettingButton(new TextureRegionDrawable(region), settingName, description, onClick); -+ } -+ -+ protected void addSettingButton(Drawable icon, String settingName, String description, Boolc onClick){ -+ scriptButton(icon, description, () -> { -+ boolean setting = Core.settings.getBool(settingName); -+ -+ Core.settings.put(settingName, !setting); -+ UIExt.announce("已" + (setting ? "取消" : "开启") + description); -+ -+ if(onClick != null) onClick.get(!setting); -+ }).checked(b -> Core.settings.getBool(settingName)); -+ } -+ -+ protected void scriptButton(TextureRegion region, String description, Runnable runnable){ -+ scriptButton(new TextureRegionDrawable(region), description, runnable); -+ } -+ -+ protected Cell scriptButton(Drawable icon, String description, Runnable runnable){ -+ return button(icon, RStyles.clearLineNonei, iconMed, runnable).tooltip(description, true); -+ } -+ -+} -diff --git a/core/src/mindustryX/features/ui/auxiliary/WaveInfoTable.java b/core/src/mindustryX/features/ui/auxiliary/WaveInfoTable.java -new file mode 100644 -index 0000000000000000000000000000000000000000..cdf8bdf4a92a88b121592555f8d6153bc11541e0 ---- /dev/null -+++ b/core/src/mindustryX/features/ui/auxiliary/WaveInfoTable.java -@@ -0,0 +1,132 @@ -+package mindustryX.features.ui.auxiliary; -+ -+import arc.*; -+import arc.scene.ui.*; -+import arc.scene.ui.layout.*; -+import arc.util.*; -+import mindustry.content.*; -+import mindustry.core.*; -+import mindustry.game.EventType.*; -+import mindustry.game.*; -+import mindustry.gen.*; -+import mindustry.type.*; -+import mindustry.ui.*; -+import mindustry.ui.dialogs.*; -+import mindustryX.features.*; -+import mindustryX.features.ui.*; -+ -+import static mindustry.Vars.*; -+ -+public class WaveInfoTable extends AuxiliaryTools.Table{ -+ public static final float fontScl = 0.8f; -+ private int waveOffset = 0; -+ private final Table waveInfo; -+ -+ public WaveInfoTable(){ -+ super(Icon.waves); -+ -+ Events.on(WorldLoadEvent.class, e -> { -+ waveOffset = 0; -+ rebuildWaveInfo(); -+ }); -+ -+ Events.on(WaveEvent.class, e -> rebuildWaveInfo()); -+ -+ left().top(); -+ -+ ArcWaveInfoDialog waveInfoDialog = new ArcWaveInfoDialog(); -+ button(Icon.waves, RStyles.clearAccentNonei, waveInfoDialog::show).size(40).tooltip("波次信息"); -+ -+ table(buttons -> { -+ buttons.defaults().size(40); -+ -+ buttons.button("<", RStyles.clearLineNonet, () -> shiftWaveOffset(-1)); -+ -+ buttons.button("O", RStyles.clearLineNonet, () -> setWaveOffset(0)); -+ -+ buttons.button(">", RStyles.clearLineNonet, () -> shiftWaveOffset(1)); -+ -+ buttons.button("Go", RStyles.clearLineNonet, () -> { -+ state.wave += waveOffset; -+ setWaveOffset(0); -+ }); -+ -+ buttons.button("♐", RStyles.clearLineNonet, () -> ArcMessageDialog.shareWaveInfo(state.wave + waveOffset)) -+ .disabled((b) -> !state.rules.waves && !Core.settings.getBool("arcShareWaveInfo")); -+ -+ }).left().row(); -+ -+ table(setWave -> { -+ setWave.label(() -> "" + getDisplayWaves()).fontScale(fontScl).row(); -+ setWave.button(Icon.settingsSmall, RStyles.clearAccentNonei, iconMed, () -> { -+ Dialog lsSet = new BaseDialog("波次设定"); -+ lsSet.cont.add("设定查询波次").padRight(5f).left(); -+ TextField field = lsSet.cont.field(state.wave + waveOffset + "", text -> waveOffset = Integer.parseInt(text) - state.wave).size(320f, 54f).valid(Strings::canParsePositiveInt).maxTextLength(100).get(); -+ lsSet.cont.row(); -+ lsSet.cont.slider(1, ArcWaveSpawner.calWinWave(), 1, res -> { -+ waveOffset = (int)res - state.wave; -+ field.setText((int)res + ""); -+ }); -+ lsSet.addCloseButton(); -+ lsSet.show(); -+ }); -+ }); -+ waveInfo = new Table(Tex.pane).left().top(); -+ add(new ScrollPane(waveInfo, Styles.noBarPane){ -+ { -+ setScrollingDisabledY(true); -+ setForceScroll(true, false); -+ // 自动失焦 -+ update(() -> { -+ if(hasScroll() && !hasMouse()){ -+ Core.scene.setScrollFocus(null); -+ } -+ }); -+ } -+ -+ @Override -+ public float getPrefWidth(){ -+ return 0f; -+ } -+ }).growX(); -+ } -+ -+ private void rebuildWaveInfo(){ -+ waveInfo.clearChildren(); -+ -+ int curInfoWave = getDisplayWaves(); -+ for(SpawnGroup group : state.rules.spawns){ -+ int amount = group.getSpawned(curInfoWave); -+ -+ if(amount == 0) continue; -+ -+ float shield = group.getShield(curInfoWave); -+ StatusEffect effect = group.effect; -+ -+ waveInfo.table(groupT -> { -+ groupT.image(group.type.uiIcon).scaling(Scaling.fit).size(iconSmall).row(); -+ groupT.add("" + amount, fontScl).row(); -+ groupT.add((shield > 0 ? UI.formatAmount((long)shield) : ""), fontScl).row(); -+ -+ if(effect != null && effect != StatusEffects.none){ -+ groupT.image(effect.uiIcon).size(iconSmall); -+ } -+ }).pad(4).left().top(); -+ } -+ } -+ -+ private void shiftWaveOffset(int shiftCount){ -+ int offset = Math.max(waveOffset + shiftCount, -state.wave + 1); -+ setWaveOffset(offset); -+ } -+ -+ private void setWaveOffset(int waveOffset){ -+ this.waveOffset = waveOffset; -+ rebuildWaveInfo(); -+ } -+ -+ private int getDisplayWaves(){ -+ return state.wave - 1 + waveOffset; -+ } -+ -+} -diff --git a/core/src/mindustryX/features/ui/auxiliary/ai/ATRIAI.java b/core/src/mindustryX/features/ui/auxiliary/ai/ATRIAI.java -new file mode 100644 -index 0000000000000000000000000000000000000000..46c9a044491de8dcf45284ef4e73a6309c03d80c ---- /dev/null -+++ b/core/src/mindustryX/features/ui/auxiliary/ai/ATRIAI.java -@@ -0,0 +1,210 @@ -+package mindustryX.features.ui.auxiliary.ai; -+ -+import arc.struct.*; -+import arc.util.*; -+import mindustry.*; -+import mindustry.ai.types.*; -+import mindustry.entities.*; -+import mindustry.entities.units.*; -+import mindustry.game.*; -+import mindustry.gen.*; -+import mindustry.input.*; -+import mindustry.type.*; -+import mindustry.world.*; -+import mindustry.world.blocks.*; -+import mindustry.world.blocks.environment.*; -+ -+import static mindustry.Vars.*; -+ -+public class ATRIAI extends AIController{ -+ //builderAI -+ -+ public static final float buildRadius = 1500; -+ public static final float retreatDst = 110f; -+ public static final float retreatDelay = Time.toSeconds * 2f; -+ public static final float rebuildTime = 120f; -+ -+ public @Nullable Unit following; -+ public @Nullable Teamc enemy; -+ public @Nullable Teams.BlockPlan lastPlan; -+ -+ public float fleeRange = 370f; -+ -+ boolean found = false; -+ float retreatTimer; -+ -+ //minerAI -+ public static final Seq oreAllList = content.blocks().select(b -> b instanceof Floor f && !f.wallOre && f.itemDrop != null); -+ public static final Seq oreAllWallList = content.blocks().select(b -> ((b instanceof Floor f && f.wallOre) || b instanceof StaticWall) && b.itemDrop != null); -+ public static final Seq oreList = content.blocks().select(b -> b instanceof Floor f && !f.wallOre && f.itemDrop != null); -+ public static final Seq oreWallList = content.blocks().select(b -> ((b instanceof Floor f && f.wallOre) || b instanceof StaticWall) && b.itemDrop != null); -+ -+ public Seq canMineList; -+ public boolean mining = true; -+ public Item targetItem; -+ public Tile ore; -+ -+ -+ public ATRIAI(float fleeRange){ -+ this.fleeRange = fleeRange; -+ } -+ -+ public ATRIAI(){ -+ } -+ -+ @Override -+ public void init(){ -+ if(!unit.canMine()) return; -+ -+ if(unit.type.mineFloor){ -+ canMineList = oreList.map(b -> b.itemDrop).select(i -> unit.canMine(i)); -+ }else if(unit.type.mineWalls){ -+ canMineList = oreWallList.map(b -> b.itemDrop).select(i -> unit.canMine(i)); -+ } -+ } -+ -+ @Override -+ public void updateMovement(){ -+ builderMode(); -+ } -+ -+ private void builderMode(){ -+ -+ if(target != null && shouldShoot()){ -+ unit.lookAt(target); -+ } -+ -+ unit.updateBuilding = true; -+ -+ if(following != null){ -+ retreatTimer = 0f; -+ //try to follow and mimic someone -+ -+ //validate follower -+ if(!following.isValid() || !following.activelyBuilding()){ -+ following = null; -+ unit.plans.clear(); -+ return; -+ } -+ -+ //set to follower's first build plan, whatever that is -+ unit.plans.clear(); -+ unit.plans.addFirst(following.buildPlan()); -+ lastPlan = null; -+ }else if(unit.buildPlan() == null){ -+ //not following anyone or building -+ if(timer.get(timerTarget4, 40)){ -+ enemy = target(unit.x, unit.y, fleeRange, true, true); -+ } -+ -+ //fly away from enemy when not doing anything, but only after a delay -+ if((retreatTimer += Time.delta) >= retreatDelay){ -+ if(enemy != null){ -+ unit.clearBuilding(); -+ var core = unit.closestCore(); -+ if(core != null && !unit.within(core, retreatDst)){ -+ moveTo(core, retreatDst); -+ } -+ } -+ } -+ } -+ -+ if(unit.buildPlan() != null){ -+ if(unit.controller() == Vars.player && control.input instanceof DesktopInput di) di.isBuilding = true; -+ //approach plan if building -+ BuildPlan req = unit.buildPlan(); -+ -+ //clear break plan if another player is breaking something -+ if(!req.breaking && timer.get(timerTarget2, 40f)){ -+ for(Player player : Groups.player){ -+ if(player.isBuilder() && player.unit().activelyBuilding() && player.unit().buildPlan().samePos(req) && player.unit().buildPlan().breaking){ -+ unit.plans.removeFirst(); -+ //remove from list of plans -+ unit.team.data().plans.remove(p -> p.x == req.x && p.y == req.y); -+ return; -+ } -+ } -+ } -+ -+ boolean valid = -+ !(lastPlan != null && lastPlan.removed) && -+ ((req.tile() != null && req.tile().build instanceof ConstructBlock.ConstructBuild cons && cons.current == req.block) || -+ (req.breaking ? -+ Build.validBreak(unit.team(), req.x, req.y) : -+ Build.validPlace(req.block, unit.team(), req.x, req.y, req.rotation))); -+ -+ if(valid){ -+ //move toward the plan -+ moveTo(req.tile(), unit.type.buildRange - 20f); -+ }else{ -+ //discard invalid plan -+ unit.plans.removeFirst(); -+ lastPlan = null; -+ } -+ }else{ -+ //follow someone and help them build -+ if(timer.get(timerTarget2, 60f)){ -+ found = false; -+ -+ Units.nearby(unit.team, unit.x, unit.y, buildRadius, u -> { -+ if(found) return; -+ -+ if(u.canBuild() && u != unit && u.activelyBuilding()){ -+ BuildPlan plan = u.buildPlan(); -+ -+ Building build = world.build(plan.x, plan.y); -+ if(build instanceof ConstructBlock.ConstructBuild cons){ -+ float dist = Math.min(cons.dst(unit) - unit.type.buildRange, 0); -+ -+ //make sure you can reach the plan in time -+ if(dist / unit.speed() < cons.buildCost * 0.9f){ -+ following = u; -+ found = true; -+ } -+ } -+ } -+ }); -+ } -+ -+ //find new plan -+ if(!unit.team.data().plans.isEmpty() && following == null && timer.get(timerTarget3, rebuildTime)){ -+ Queue blocks = unit.team.data().plans; -+ Teams.BlockPlan block = blocks.first(); -+ -+ //check if it's already been placed -+ if(world.tile(block.x, block.y) != null && world.tile(block.x, block.y).block().id == block.block){ -+ blocks.removeFirst(); -+ }else if(Build.validPlace(content.block(block.block), unit.team(), block.x, block.y, block.rotation) && (!nearEnemy(block.x, block.y))){ //it's valid -+ lastPlan = block; -+ //add build plan -+ unit.addBuild(new BuildPlan(block.x, block.y, block.rotation, content.block(block.block), block.config)); -+ //shift build plan to tail so next unit builds something else -+ blocks.addLast(blocks.removeFirst()); -+ }else{ -+ //shift head of queue to tail, try something else next time -+ blocks.addLast(blocks.removeFirst()); -+ } -+ } -+ } -+ } -+ -+ -+ protected boolean nearEnemy(int x, int y){ -+ return Units.nearEnemy(unit.team, x * tilesize - fleeRange / 2f, y * tilesize - fleeRange / 2f, fleeRange, fleeRange); -+ } -+ -+ @Override -+ public AIController fallback(){ -+ return unit.type.flying ? new FlyingAI() : new GroundAI(); -+ } -+ -+ @Override -+ public boolean useFallback(){ -+ return state.rules.waves && unit.team == state.rules.waveTeam && !unit.team.rules().rtsAi; -+ } -+ -+ @Override -+ public boolean shouldShoot(){ -+ return !unit.isBuilding() && unit.type.canAttack; -+ } -+} -diff --git a/core/src/mindustryX/features/ui/auxiliary/ai/ArcBuilderAI.java b/core/src/mindustryX/features/ui/auxiliary/ai/ArcBuilderAI.java -new file mode 100644 -index 0000000000000000000000000000000000000000..b336ba6618476c5dbee6b416582aee03b5885b0f ---- /dev/null -+++ b/core/src/mindustryX/features/ui/auxiliary/ai/ArcBuilderAI.java -@@ -0,0 +1,182 @@ -+package mindustryX.features.ui.auxiliary.ai; -+ -+import arc.struct.*; -+import arc.util.*; -+import mindustry.*; -+import mindustry.ai.types.*; -+import mindustry.entities.*; -+import mindustry.entities.units.*; -+import mindustry.game.Teams.*; -+import mindustry.gen.*; -+import mindustry.input.*; -+import mindustry.world.*; -+import mindustry.world.blocks.ConstructBlock.*; -+ -+import static mindustry.Vars.*; -+ -+public class ArcBuilderAI extends AIController{ -+ public static final float buildRadius = 1500; -+ public static final float retreatDst = 110f; -+ public static final float retreatDelay = Time.toSeconds * 2f; -+ public static float rebuildTime = 120f; -+ -+ public @Nullable Unit following; -+ public @Nullable Teamc enemy; -+ public @Nullable BlockPlan lastPlan; -+ -+ public float fleeRange = 370f; -+ public boolean alwaysFlee; -+ -+ boolean found = false; -+ float retreatTimer; -+ -+ public ArcBuilderAI(boolean alwaysFlee, float fleeRange){ -+ this.alwaysFlee = alwaysFlee; -+ this.fleeRange = fleeRange; -+ } -+ -+ public ArcBuilderAI(){ -+ } -+ -+ @Override -+ public void updateMovement(){ -+ -+ if(target != null && shouldShoot()){ -+ unit.lookAt(target); -+ } -+ -+ unit.updateBuilding = true; -+ -+ if(following != null){ -+ retreatTimer = 0f; -+ //try to follow and mimic someone -+ -+ //validate follower -+ if(!following.isValid() || !following.activelyBuilding()){ -+ following = null; -+ unit.plans.clear(); -+ return; -+ } -+ -+ //set to follower's first build plan, whatever that is -+ unit.plans.clear(); -+ unit.plans.addFirst(following.buildPlan()); -+ lastPlan = null; -+ }else if(unit.buildPlan() == null || alwaysFlee){ -+ //not following anyone or building -+ if(timer.get(timerTarget4, 40)){ -+ enemy = target(unit.x, unit.y, fleeRange, true, true); -+ } -+ -+ //fly away from enemy when not doing anything, but only after a delay -+ if((retreatTimer += Time.delta) >= retreatDelay || alwaysFlee){ -+ if(enemy != null){ -+ unit.clearBuilding(); -+ var core = unit.closestCore(); -+ if(core != null && !unit.within(core, retreatDst)){ -+ moveTo(core, retreatDst); -+ } -+ } -+ } -+ } -+ -+ if(unit.buildPlan() != null){ -+ if(unit.controller() == Vars.player && control.input instanceof DesktopInput di) di.isBuilding = true; -+ if(!alwaysFlee) retreatTimer = 0f; -+ //approach plan if building -+ BuildPlan req = unit.buildPlan(); -+ -+ //clear break plan if another player is breaking something -+ if(!req.breaking && timer.get(timerTarget2, 40f)){ -+ for(Player player : Groups.player){ -+ if(player.isBuilder() && player.unit().activelyBuilding() && player.unit().buildPlan().samePos(req) && player.unit().buildPlan().breaking){ -+ unit.plans.removeFirst(); -+ //remove from list of plans -+ unit.team.data().plans.remove(p -> p.x == req.x && p.y == req.y); -+ return; -+ } -+ } -+ } -+ -+ boolean valid = -+ !(lastPlan != null && lastPlan.removed) && -+ ((req.tile() != null && req.tile().build instanceof ConstructBuild cons && cons.current == req.block) || -+ (req.breaking ? -+ Build.validBreak(unit.team(), req.x, req.y) : -+ Build.validPlace(req.block, unit.team(), req.x, req.y, req.rotation))); -+ -+ if(valid){ -+ //move toward the plan -+ moveTo(req.tile(), unit.type.buildRange - 20f); -+ }else{ -+ //discard invalid plan -+ unit.plans.removeFirst(); -+ lastPlan = null; -+ } -+ }else{ -+ -+ //follow someone and help them build -+ if(timer.get(timerTarget2, 60f)){ -+ found = false; -+ -+ Units.nearby(unit.team, unit.x, unit.y, buildRadius, u -> { -+ if(found) return; -+ -+ if(u.canBuild() && u != unit && u.activelyBuilding()){ -+ BuildPlan plan = u.buildPlan(); -+ -+ Building build = world.build(plan.x, plan.y); -+ if(build instanceof ConstructBuild cons){ -+ float dist = Math.min(cons.dst(unit) - unit.type.buildRange, 0); -+ -+ //make sure you can reach the plan in time -+ if(dist / unit.speed() < cons.buildCost * 0.9f){ -+ following = u; -+ found = true; -+ } -+ } -+ } -+ }); -+ } -+ -+ //find new plan -+ if(!unit.team.data().plans.isEmpty() && following == null && timer.get(timerTarget3, rebuildTime)){ -+ Queue blocks = unit.team.data().plans; -+ BlockPlan block = blocks.first(); -+ -+ //check if it's already been placed -+ if(world.tile(block.x, block.y) != null && world.tile(block.x, block.y).block().id == block.block){ -+ blocks.removeFirst(); -+ }else if(Build.validPlace(content.block(block.block), unit.team(), block.x, block.y, block.rotation) && (!alwaysFlee || !nearEnemy(block.x, block.y))){ //it's valid -+ lastPlan = block; -+ //add build plan -+ unit.addBuild(new BuildPlan(block.x, block.y, block.rotation, content.block(block.block), block.config)); -+ //shift build plan to tail so next unit builds something else -+ blocks.addLast(blocks.removeFirst()); -+ }else{ -+ //shift head of queue to tail, try something else next time -+ blocks.addLast(blocks.removeFirst()); -+ } -+ } -+ } -+ } -+ -+ protected boolean nearEnemy(int x, int y){ -+ return Units.nearEnemy(unit.team, x * tilesize - fleeRange / 2f, y * tilesize - fleeRange / 2f, fleeRange, fleeRange); -+ } -+ -+ @Override -+ public AIController fallback(){ -+ return unit.type.flying ? new FlyingAI() : new GroundAI(); -+ } -+ -+ @Override -+ public boolean useFallback(){ -+ return state.rules.waves && unit.team == state.rules.waveTeam && !unit.team.rules().rtsAi; -+ } -+ -+ @Override -+ public boolean shouldShoot(){ -+ return !unit.isBuilding() && unit.type.canAttack; -+ } -+} -diff --git a/core/src/mindustryX/features/ui/auxiliary/ai/ArcMinerAI.java b/core/src/mindustryX/features/ui/auxiliary/ai/ArcMinerAI.java -new file mode 100644 -index 0000000000000000000000000000000000000000..d7f717c09df1731e614b1673ae0eecbc6fc23ec2 ---- /dev/null -+++ b/core/src/mindustryX/features/ui/auxiliary/ai/ArcMinerAI.java -@@ -0,0 +1,122 @@ -+package mindustryX.features.ui.auxiliary.ai; -+ -+import arc.struct.*; -+import arc.util.*; -+import mindustry.entities.units.*; -+import mindustry.gen.*; -+import mindustry.input.*; -+import mindustry.type.*; -+import mindustry.world.*; -+import mindustry.world.blocks.environment.*; -+import mindustry.world.blocks.storage.*; -+ -+import static mindustry.Vars.*; -+ -+public class ArcMinerAI extends AIController{ -+ public static final Seq oreAllList = content.blocks().select(b -> b instanceof Floor f && !f.wallOre && f.itemDrop != null); -+ public static final Seq oreAllWallList = content.blocks().select(b -> ((b instanceof Floor f && f.wallOre) || b instanceof StaticWall) && b.itemDrop != null); -+ public static final Seq oreList = content.blocks().select(b -> b instanceof Floor f && !f.wallOre && f.itemDrop != null); -+ public static final Seq oreWallList = content.blocks().select(b -> ((b instanceof Floor f && f.wallOre) || b instanceof StaticWall) && b.itemDrop != null); -+ -+ public Seq canMineList; -+ public boolean mining = true; -+ public Item targetItem; -+ public Tile ore; -+ -+ @Override -+ public void init(){ -+ if(!unit.canMine()) return; -+ -+ if(unit.type.mineFloor){ -+ canMineList = oreList.map(b -> b.itemDrop).select(i -> unit.canMine(i)); -+ }else if(unit.type.mineWalls){ -+ canMineList = oreWallList.map(b -> b.itemDrop).select(i -> unit.canMine(i)); -+ } -+ } -+ -+ private Item updateTargetItem(boolean canMineNonBuildable){ -+ //reverse是因为min取最后一个最小的 -+ if(unit.type.mineFloor){ -+ canMineList = oreList.map(b -> b.itemDrop).select(i -> unit.canMine(i)); -+ }else if(unit.type.mineWalls){ -+ canMineList = oreWallList.map(b -> b.itemDrop).select(i -> unit.canMine(i)); -+ } -+ return canMineList.select(i -> (unit.type.mineFloor ? indexer.hasOre(i) : indexer.hasOreWall(i)) -+ && (canMineNonBuildable || i.buildable) -+ && unit.core().acceptItem(null, i) -+ ).reverse().min(i -> unit.core().items.get(i)); -+ } -+ -+ private Tile findClosetOre(Building build){ -+ if(unit.type.mineFloor){ -+ return indexer.findClosestOre(build.x, build.y, targetItem); -+ } -+ return indexer.findClosestWallOre(build.x, build.y, targetItem); -+ } -+ -+ @Override -+ public void updateMovement(){ -+ if(!unit.canMine() || canMineList.isEmpty() || unit.core() == null) return; -+ -+ CoreBlock.CoreBuild core = unit.closestCore(); -+ //变量命名不知道叫啥了 -+ //最近的可以塞入非建筑物品的核心 -+ CoreBlock.CoreBuild core2 = unit.team.data().cores.select(c -> !((CoreBlock)c.block).incinerateNonBuildable).min(c -> unit.dst(c)); -+ -+ CoreBlock.CoreBuild targetCore = targetItem == null || targetItem.buildable || core2 == null ? core : core2; -+ -+ if(unit.type.canBoost){ -+ player.boosting = true; -+ } -+ if(mining){ -+ -+ if(targetItem != null && (!core.acceptItem(null, targetItem) || (core2 == null && !targetItem.buildable))){ -+ unit.mineTile = null; -+ targetItem = null; -+ } -+ -+ if(targetItem == null || timer.get(timerTarget2, 300f)){ -+ targetItem = updateTargetItem(core2 != null); -+ if(targetItem == null) return; -+ } -+ -+ if(!unit.acceptsItem(targetItem) || unit.stack.amount >= unit.type.itemCapacity){ -+ mining = false; -+ return; -+ } -+ -+ if(ore == null || !unit.validMine(ore, false) || ore.drop() != targetItem || timer.get(timerTarget3, 120f)){ -+ ore = findClosetOre(targetCore); -+ if(ore == null) return; -+ } -+ -+ -+ Tmp.v1.setLength(unit.type.mineRange * 0.9f).limit(ore.dst(targetCore) - 0.5f).setAngle(ore.angleTo(targetCore)).add(ore); -+ moveTo(Tmp.v1, 0.1f); -+ if(unit.validMine(ore)){ -+ unit.mineTile = ore; -+ } -+ -+ }else{ -+ unit.mineTile = null; -+ -+ if(unit.stack.amount == 0){ -+ mining = true; -+ return; -+ } -+ if(!core.acceptItem(null, unit.stack.item)){ -+ unit.clearItem(); -+ } -+ -+ moveTo(targetCore, core.hitSize()); -+ if(unit.within(targetCore, itemTransferRange) && targetCore.acceptItem(null, targetItem)){ -+ Call.transferInventory(player, core); -+ targetItem = updateTargetItem(core2 != null); -+ } -+ } -+ } -+ -+ @Override -+ public void updateVisuals(){ -+ } -+} -diff --git a/core/src/mindustryX/features/ui/auxiliary/ai/ArcRepairAI.java b/core/src/mindustryX/features/ui/auxiliary/ai/ArcRepairAI.java -new file mode 100644 -index 0000000000000000000000000000000000000000..c75797d3b4bb5b5b6181ae7a0bddd6669b20b9f1 ---- /dev/null -+++ b/core/src/mindustryX/features/ui/auxiliary/ai/ArcRepairAI.java -@@ -0,0 +1,75 @@ -+package mindustryX.features.ui.auxiliary.ai; -+ -+import arc.util.*; -+import mindustry.entities.*; -+import mindustry.entities.units.*; -+import mindustry.gen.*; -+import mindustry.world.blocks.ConstructBlock.*; -+ -+public class ArcRepairAI extends AIController{ -+ public static final float retreatDst = 160f; -+ public static final float fleeRange = 310f; -+ public static final float retreatDelay = Time.toSeconds * 3f; -+ -+ @Nullable -+ Teamc avoid; -+ float retreatTimer; -+ Building damagedTarget; -+ -+ @Override -+ public void updateMovement(){ -+ if(target instanceof Building){ -+ boolean shoot = false; -+ -+ if(target.within(unit, unit.type.range)){ -+ unit.aim(target); -+ shoot = true; -+ } -+ -+ unit.controlWeapons(shoot); -+ }else if(target == null){ -+ unit.controlWeapons(false); -+ } -+ -+ if(target != null){ -+ if(!target.within(unit, unit.type.range * 0.65f) && target instanceof Building b && b.team == unit.team){ -+ moveTo(target, unit.type.range * 0.65f); -+ } -+ -+ unit.lookAt(target); -+ } -+ -+ //not repairing -+ if(!(target instanceof Building)){ -+ if(timer.get(timerTarget4, 40)){ -+ avoid = target(unit.x, unit.y, fleeRange, true, true); -+ } -+ -+ if((retreatTimer += Time.delta) >= retreatDelay){ -+ //fly away from enemy when not doing anything -+ if(avoid != null){ -+ var core = unit.closestCore(); -+ if(core != null && !unit.within(core, retreatDst)){ -+ moveTo(core, retreatDst); -+ } -+ } -+ } -+ }else{ -+ retreatTimer = 0f; -+ } -+ } -+ -+ @Override -+ public void updateTargeting(){ -+ if(timer.get(timerTarget, 15)){ -+ damagedTarget = Units.findDamagedTile(unit.team, unit.x, unit.y); -+ if(damagedTarget instanceof ConstructBuild) damagedTarget = null; -+ } -+ -+ if(damagedTarget == null){ -+ super.updateTargeting(); -+ }else{ -+ this.target = damagedTarget; -+ } -+ } -+} diff --git a/patches/client/0073-ARC-arcScanMode.patch b/patches/client/0073-ARC-arcScanMode.patch index 079175bd42d8..121e1a600712 100644 --- a/patches/client/0073-ARC-arcScanMode.patch +++ b/patches/client/0073-ARC-arcScanMode.patch @@ -4,12 +4,10 @@ Date: Sun, 19 May 2024 19:40:31 +0800 Subject: [PATCH] ARC arcScanMode --- - .../blocks/distribution/DirectionBridge.java | 7 + - .../world/blocks/distribution/ItemBridge.java | 12 + - .../world/blocks/distribution/MassDriver.java | 3 + - core/src/mindustryX/features/ArcScanMode.java | 383 ++++++++++++++++++ - 4 files changed, 405 insertions(+) - create mode 100644 core/src/mindustryX/features/ArcScanMode.java + .../world/blocks/distribution/DirectionBridge.java | 7 +++++++ + .../world/blocks/distribution/ItemBridge.java | 12 ++++++++++++ + .../world/blocks/distribution/MassDriver.java | 3 +++ + 3 files changed, 22 insertions(+) diff --git a/core/src/mindustry/world/blocks/distribution/DirectionBridge.java b/core/src/mindustry/world/blocks/distribution/DirectionBridge.java index 2c0077cc99557c80f49d0cf496c92309b31e482e..341b81c8ec8cae795b3e5c3872e253f9317975bc 100644 @@ -64,392 +62,3 @@ index 7fc0d5f0ec12e7d2aaa70d32beebed9027914449..f749a280f0307a1c44294b6ebcba2580 protected boolean linkValid(){ if(link == -1) return false; return world.build(this.link) instanceof MassDriverBuild other && other.block == block && other.team == team && within(other, range); -diff --git a/core/src/mindustryX/features/ArcScanMode.java b/core/src/mindustryX/features/ArcScanMode.java -new file mode 100644 -index 0000000000000000000000000000000000000000..6c9f51ad54cba00567aebe684ed63b6368339c8c ---- /dev/null -+++ b/core/src/mindustryX/features/ArcScanMode.java -@@ -0,0 +1,383 @@ -+package mindustryX.features; -+ -+import arc.*; -+import arc.func.*; -+import arc.graphics.*; -+import arc.graphics.g2d.*; -+import arc.math.*; -+import arc.math.geom.*; -+import arc.scene.event.*; -+import arc.scene.ui.*; -+import arc.scene.ui.layout.*; -+import arc.struct.*; -+import arc.util.*; -+import mindustry.gen.*; -+import mindustry.graphics.*; -+import mindustry.type.*; -+import mindustry.ui.*; -+import mindustry.world.*; -+import mindustry.world.blocks.distribution.*; -+import mindustry.world.blocks.liquid.*; -+import mindustry.world.blocks.production.*; -+import mindustry.world.blocks.storage.*; -+import mindustry.world.meta.*; -+import mindustryX.features.ui.*; -+ -+import static mindustry.Vars.*; -+ -+//move from mindustry.arcModule.toolpack.ArcScanMode -+public class ArcScanMode{ -+ public static boolean enabled = false; -+ private static final Label ctTable = new Label(""); -+ private static final Table spawnerTable = new Table(); -+ private static final Table flyerTable = new Table(); -+ -+ /** -+ * conveyor -+ */ -+ static final int maxLoop = 200; -+ -+ -+ static{ -+ ctTable.touchable = spawnerTable.touchable = flyerTable.touchable = Touchable.disabled; -+ ui.hudGroup.addChild(ctTable); -+ ui.hudGroup.addChild(spawnerTable); -+ ui.hudGroup.addChild(flyerTable); -+ -+ ctTable.setAlignment(Align.bottom, Align.center); -+ ctTable.visible(() -> enabled && state.isPlaying()); -+ ctTable.update(() -> { -+ ctTable.setPosition(Core.input.mouseX(), Core.input.mouseY()); -+ var pos = Core.input.mouseWorld(); -+ ctTable.setText(Strings.format("@,@\n距离: @", -+ (int)(pos.x / tilesize), (int)(pos.y / tilesize), (int)(player.dst(pos) / tilesize))); -+ }); -+ } -+ -+ public static void draw(){ -+ if(!enabled || !state.isPlaying()){ -+ ctTable.clear(); -+ spawnerTable.clear(); -+ flyerTable.clear(); -+ return; -+ } -+ updateSpawnerDisplay(); -+ drawTransportFlow(); -+ } -+ -+ private static void updateSpawnerDisplay(){ -+ spawnerTable.clear(); -+ flyerTable.clear(); -+ if(ArcWaveSpawner.arcWave.isEmpty()) return; -+ ArcWaveSpawner.waveInfo thisWave = ArcWaveSpawner.arcWave.get(Math.min(state.wave - 1, ArcWaveSpawner.arcWave.size - 1)); -+ for(Tile tile : spawner.getSpawns()){ -+ if(Mathf.dst(tile.worldx(), tile.worldy(), Core.input.mouseWorldX(), Core.input.mouseWorldY()) < state.rules.dropZoneRadius){ -+ float curve = Mathf.curve(Time.time % 240f, 120f, 240f); -+ Draw.z(Layer.effect - 2f); -+ Draw.color(state.rules.waveTeam.color); -+ Lines.stroke(4f); -+ //flyer -+ float flyerAngle = Angles.angle(world.width() / 2f, world.height() / 2f, tile.x, tile.y); -+ float trns = Math.max(world.width(), world.height()) * Mathf.sqrt2 * tilesize; -+ float spawnX = Mathf.clamp(world.width() * tilesize / 2f + Angles.trnsx(flyerAngle, trns), 0, world.width() * tilesize); -+ float spawnY = Mathf.clamp(world.height() * tilesize / 2f + Angles.trnsy(flyerAngle, trns), 0, world.height() * tilesize); -+ -+ if(ArcWaveSpawner.hasFlyer){ -+ Lines.line(tile.worldx(), tile.worldy(), spawnX, spawnY); -+ Tmp.v1.set(spawnX - tile.worldx(), spawnY - tile.worldy()); -+ Tmp.v1.setLength(Tmp.v1.len() * curve); -+ Fill.circle(tile.worldx() + Tmp.v1.x, tile.worldy() + Tmp.v1.y, 8f); -+ -+ Vec2 v = Core.camera.project(spawnX, spawnY); -+ flyerTable.setPosition(v.x, v.y); -+ flyerTable.table(Styles.black3, tt -> { -+ tt.add(FormatDefault.duration(state.wavetime / 60, false)).row(); -+ thisWave.specLoc(tile.pos(), group -> group.type.flying); -+ tt.add(thisWave.proTable(false)); -+ tt.row(); -+ tt.add(thisWave.unitTable(tile.pos(), group -> group.type.flying)).maxWidth(mobile ? 400f : 750f).growX(); -+ }); -+ } -+ //ground -+ -+ if(curve > 0) -+ Lines.circle(tile.worldx(), tile.worldy(), state.rules.dropZoneRadius * Interp.pow3Out.apply(curve)); -+ Lines.circle(tile.worldx(), tile.worldy(), state.rules.dropZoneRadius); -+ Lines.arc(tile.worldx(), tile.worldy(), state.rules.dropZoneRadius - 3f, state.wavetime / state.rules.waveSpacing, 90f); -+ float angle = Mathf.pi / 2 + state.wavetime / state.rules.waveSpacing * 2 * Mathf.pi; -+ Draw.color(state.rules.waveTeam.color); -+ Fill.circle(tile.worldx() + state.rules.dropZoneRadius * Mathf.cos(angle), tile.worldy() + state.rules.dropZoneRadius * Mathf.sin(angle), 8f); -+ -+ Vec2 v = Core.camera.project(tile.worldx(), tile.worldy()); -+ spawnerTable.setPosition(v.x, v.y); -+ spawnerTable.table(Styles.black3, tt -> { -+ tt.add(FormatDefault.duration(state.wavetime / 60, false)).row(); -+ thisWave.specLoc(tile.pos(), group -> !group.type.flying); -+ tt.add(thisWave.proTable(false)); -+ tt.row(); -+ tt.add(thisWave.unitTable(tile.pos(), group -> !group.type.flying)).maxWidth(mobile ? 400f : 750f).growX(); -+ }); -+ return; -+ } -+ } -+ } -+ -+ private static boolean canAccept(Block block){ -+ if(block.group == BlockGroup.transportation) return true; -+ for(Item item : content.items()){ -+ if(block.consumesItem(item) || block.itemCapacity > 0){ -+ return true; -+ } -+ } -+ return false; -+ } -+ -+ public static final Seq path = new Seq<>(); -+ -+ public static void drawTransportFlow(){ -+ if(!enabled) return; -+ -+ //check tile being hovered over -+ Tile hoverTile = world.tileWorld(Core.input.mouseWorld().x, Core.input.mouseWorld().y); -+ if(hoverTile == null || hoverTile.build == null || !hoverTile.build.isDiscovered(player.team())){ -+ return; -+ } -+ -+ path.clear(); -+ travelPath(new Point(hoverTile.build, null), ArcScanMode::getPrevious); -+ drawPath(false); -+ -+ path.clear(); -+ travelPath(new Point(hoverTile.build, null), ArcScanMode::getNext); -+ drawPath(true); -+ } -+ -+ private static void travelPath(Point point, Func> getNext){ -+ if(point.build == null || path.size > maxLoop) return; -+ if(!point.trans) return; -+ -+ Point same = path.find(other -> point.build == other.build && (other.from == null || point.from.build == other.from.build)); -+ if(same != null){ -+ if(point.conduit >= same.conduit) return; -+ else path.replace(same, point); -+ }else path.add(point); -+ -+ getNext.get(point).each(p -> travelPath(p, getNext)); -+ } -+ -+ private static Seq getPrevious(Point point){ -+ Building build = point.build; -+ if(build == null) return new Seq<>(); -+ Seq previous = new Seq<>(); -+ //质驱 -+ if(build instanceof MassDriver.MassDriverBuild){ -+ //暂时搞不定 -+ }//桥 -+ else if(build instanceof ItemBridge.ItemBridgeBuild bridge && !(build instanceof LiquidBridge.LiquidBridgeBuild)){ -+ bridge.incoming.each(pos -> previous.add(new Point(world.tile(pos).build, point))); -+ }//导管桥 -+ else if(build instanceof DirectionBridge.DirectionBridgeBuild bridge){ -+ for(Building b : bridge.occupied){ -+ if(b != null){ -+ previous.add(new Point(b, point)); -+ } -+ } -+ } -+ for(Building b : build.proximity){ -+ Point from = new Point(b, b.relativeTo(build), b.block.instantTransfer ? point.conduit + 1 : 0, point); -+ if(canInput(point, b, true) && canOutput(from, build, false)){ -+ previous.add(from); -+ }else if(canOutput(from, build, false)){ -+ from.trans = false; -+ previous.add(from); -+ } -+ } -+ return previous; -+ } -+ -+ private static Seq getNext(Point point){ -+ Building build = point.build; -+ if(build == null) return new Seq<>(); -+ Seq next = new Seq<>(); -+ //质驱 -+ if(build instanceof MassDriver.MassDriverBuild massDriverBuild){ -+ if(massDriverBuild.arcLinkValid()){ -+ next.add(new Point(world.build(massDriverBuild.link), point)); -+ } -+ }//桥 -+ else if(build instanceof ItemBridge.ItemBridgeBuild itemBridgeBuild && !(build instanceof LiquidBridge.LiquidBridgeBuild)){ -+ if(itemBridgeBuild.arcLinkValid()){ -+ next.add(new Point(world.build(itemBridgeBuild.link), point)); -+ } -+ }//导管桥 -+ else if(build instanceof DirectionBridge.DirectionBridgeBuild directionBridgeBuild){ -+ DirectionBridge.DirectionBridgeBuild link = directionBridgeBuild.findLink(); -+ if(link != null){ -+ next.add(new Point(link, point)); -+ } -+ } -+ -+ for(Building b : build.proximity){ -+ Point to = new Point(b, build.relativeTo(b), b.block.instantTransfer ? point.conduit + 1 : 0, point); -+ if(canInput(to, build, false) && canOutput(point, b, true)){ -+ next.add(to); -+ }else if(canInput(to, build, false)){ -+ to.trans = false; -+ next.add(to); -+ } -+ } -+ return next; -+ } -+ -+ private static boolean canInput(Point point, Building from, boolean active){ -+ Building build = point.build; -+ if(build == null || from == null) return false; -+ if(from.block.instantTransfer && point.conduit > 2) return false; -+ //装甲传送带 -+ if(build instanceof ArmoredConveyor.ArmoredConveyorBuild){ -+ return from != build.front() && (from instanceof Conveyor.ConveyorBuild || from == build.back()); -+ }//装甲导管 -+ else if(build instanceof Duct.DuctBuild ductBuild && ((Duct)ductBuild.block).armored){ -+ return from != build.front() && (from.block.isDuct || from == build.back()); -+ }//传送带和导管 -+ else if(build instanceof Conveyor.ConveyorBuild || build instanceof Duct.DuctBuild){ -+ return from != build.front(); -+ }//塑钢带 -+ else if(build instanceof StackConveyor.StackConveyorBuild stackConveyorBuild){ -+ return switch(stackConveyorBuild.state){ -+ case 2 -> from == build.back() && from instanceof StackConveyor.StackConveyorBuild; -+ case 1 -> from != build.front(); -+ default -> from instanceof StackConveyor.StackConveyorBuild; -+ }; -+ }//交叉器 -+ else if(build instanceof Junction.JunctionBuild){ -+ return point.facing == -1 || from.relativeTo(build) == point.facing; -+ }//分类 -+ else if(build instanceof Sorter.SorterBuild sorterBuild){ -+ return !active || build.relativeTo(from) != point.facing && (sorterBuild.sortItem != null || (from.relativeTo(build) == point.facing) == ((Sorter)sorterBuild.block).invert); -+ }//溢流 -+ else if(build instanceof OverflowGate.OverflowGateBuild){ -+ return !active || build.relativeTo(from) != point.facing; -+ }//导管路由器与导管溢流 -+ else if(build instanceof DuctRouter.DuctRouterBuild || build instanceof OverflowDuct.OverflowDuctBuild){ -+ return from == build.back(); -+ }//桥 -+ else if(build instanceof ItemBridge.ItemBridgeBuild itemBridgeBuild){ -+ return itemBridgeBuild.arcCheckAccept(from); -+ }//导管桥 -+ else if(build instanceof DirectionBridge.DirectionBridgeBuild directionBridgeBuild){ -+ return directionBridgeBuild.arcCheckAccept(from); -+ }else if(build instanceof Router.RouterBuild){ -+ return true; -+ }else if(canAccept(build.block)){ -+ point.trans = false; -+ return true; -+ } -+ return false; -+ } -+ -+ private static boolean canOutput(Point point, Building to, boolean active){ -+ Building build = point.build; -+ if(build == null || to == null) return false; -+ if(to.block.instantTransfer && point.conduit > 2) return false; -+ //传送带和导管 -+ if(build instanceof Conveyor.ConveyorBuild || build instanceof Duct.DuctBuild){ -+ return to == build.front(); -+ }//塑钢带 -+ else if(build instanceof StackConveyor.StackConveyorBuild stackConveyor){ -+ if(stackConveyor.state == 2 && ((StackConveyor)stackConveyor.block).outputRouter){ -+ return to != build.back(); -+ } -+ return to == build.front(); -+ }//交叉器 -+ else if(build instanceof Junction.JunctionBuild){ -+ return point.facing == -1 || build.relativeTo(to) == point.facing; -+ }//分类 -+ else if(build instanceof Sorter.SorterBuild sorterBuild){ -+ return !active || to.relativeTo(build) != point.facing && (sorterBuild.sortItem != null || (build.relativeTo(to) == point.facing) == ((Sorter)sorterBuild.block).invert); -+ }//溢流 -+ else if(build instanceof OverflowGate.OverflowGateBuild){ -+ return !active || to.relativeTo(build) != point.facing; -+ }//导管路由器与导管溢流 -+ else if(build instanceof DuctRouter.DuctRouterBuild || build instanceof OverflowDuct.OverflowDuctBuild){ -+ return to != build.back(); -+ }//桥 -+ else if(build instanceof ItemBridge.ItemBridgeBuild bridge){ -+ return bridge.arcCheckDump(to); -+ }//导管桥 -+ else if(build instanceof DirectionBridge.DirectionBridgeBuild directionBridgeBuild){ -+ DirectionBridge.DirectionBridgeBuild link = directionBridgeBuild.findLink(); -+ return link == null && build.relativeTo(to) == build.rotation; -+ }else if(build instanceof Router.RouterBuild || build instanceof Unloader.UnloaderBuild){ -+ return true; -+ }else if(build instanceof GenericCrafter.GenericCrafterBuild){ -+ point.trans = false; -+ return true; -+ } -+ return false; -+ } -+ -+ private static void drawPath(boolean forward){ -+ Color mainColor = forward ? Color.valueOf("80ff00") : Color.valueOf("ff8000"); -+ Color highlightColor = forward ? Color.valueOf("00cc00") : Color.red; -+ path.each(p -> { -+ if(p.from != null && p.trans){ -+ float x1 = p.build.tile.drawx(), y1 = p.build.tile.drawy(); -+ float x2 = p.from.build.tile.drawx(), y2 = p.from.build.tile.drawy(); -+ -+ Draw.color(mainColor); -+ Draw.color(Tmp.c1.set(mainColor).a(Mathf.absin(4f, 1f) * 0.4f + 0.6f)); -+ Lines.stroke(1.5f); -+ Lines.line(x1, y1, x2, y2); -+ }else{ -+ Drawf.selected(p.build, Tmp.c1.set(highlightColor).a(Mathf.absin(4f, 1f) * 0.5f + 0.5f)); -+ } -+ Draw.reset(); -+ }); -+ path.each(p -> { -+ if(p.from != null && p.trans){ -+ float x1 = p.build.tile.drawx(), y1 = p.build.tile.drawy(); -+ float x2 = p.from.build.tile.drawx(), y2 = p.from.build.tile.drawy(); -+ float dst = Mathf.dst(x1, y1, x2, y2); -+ -+ Draw.color(highlightColor); -+ Fill.circle(x1, y1, 1.8f); -+ -+ if(dst > tilesize){ -+ Draw.color(highlightColor); -+ Vec2 to = Tmp.v1.set(x1, y1), from = Tmp.v2.set(x2, y2); -+ if(!forward){ -+ from.set(to); -+ to.set(x2, y2); -+ } -+ //在中间位置,按朝向方向绘制一个三角形 -+ Vec2 v = to.div(from).scl(0.5f); -+ Fill.poly(v.x + from.x, v.y + from.y, 3, 3f, v.angle()); -+ } -+ } -+ Draw.reset(); -+ }); -+ } -+ -+ private static class Point{ -+ public final Building build; -+ public byte facing = -1; -+ public int conduit = 0; -+ //用于记录端点方块 -+ public boolean trans = true; -+ -+ public final Point from; -+ -+ public Point(Building build, Point from){ -+ this.build = build; -+ this.from = from; -+ } -+ -+ public Point(Building build, byte facing, int conduit, Point from){ -+ this.build = build; -+ this.facing = facing; -+ this.conduit = conduit; -+ this.from = from; -+ } -+ } -+} diff --git a/patches/client/0076-C-RenderExt-massDriverLine.patch b/patches/client/0076-C-RenderExt-massDriverLine.patch index 2fb8dec4bc06..26307f6bad1c 100644 --- a/patches/client/0076-C-RenderExt-massDriverLine.patch +++ b/patches/client/0076-C-RenderExt-massDriverLine.patch @@ -3,80 +3,3 @@ From: way-zer Date: Sat, 25 May 2024 20:34:18 +0800 Subject: [PATCH] C(RenderExt) massDriverLine ---- - core/src/mindustryX/features/RenderExt.java | 27 +++++++++++++++++++++ - 1 file changed, 27 insertions(+) - -diff --git a/core/src/mindustryX/features/RenderExt.java b/core/src/mindustryX/features/RenderExt.java -index 4386f5f364e847324b147c44f8c8748fd5c16537..41bb25494e541e74e1112fd24ec0bce733707f30 100644 ---- a/core/src/mindustryX/features/RenderExt.java -+++ b/core/src/mindustryX/features/RenderExt.java -@@ -4,6 +4,7 @@ import arc.*; - import arc.graphics.*; - import arc.graphics.g2d.*; - import arc.math.*; -+import arc.math.geom.*; - import arc.util.*; - import mindustry.entities.*; - import mindustry.game.EventType.*; -@@ -13,6 +14,7 @@ import mindustry.type.*; - import mindustry.world.*; - import mindustry.world.blocks.defense.*; - import mindustry.world.blocks.defense.turrets.*; -+import mindustry.world.blocks.distribution.MassDriver.*; - import mindustry.world.blocks.logic.*; - import mindustry.world.blocks.logic.MessageBlock.*; - import mindustry.world.blocks.production.Drill.*; -@@ -31,8 +33,12 @@ public class RenderExt{ - public static boolean logicDisplayNoBorder, arcDrillMode; - public static int blockRenderLevel; - public static boolean renderSort; -+ public static boolean massDriverLine; -+ public static int massDriverLineInterval; - - public static boolean unitHide = false; -+ public static Color massDriverLineColor = Color.clear; -+ - private static Effect placementEffect; - - public static void init(){ -@@ -62,6 +68,8 @@ public class RenderExt{ - arcDrillMode = Core.settings.getBool("arcdrillmode"); - blockRenderLevel = Core.settings.getInt("blockRenderLevel"); - renderSort = Core.settings.getBool("renderSort"); -+ massDriverLine = Core.settings.getBool("mass_driver_line"); -+ massDriverLineInterval = Core.settings.getInt("mass_driver_line_interval"); - }); - Events.run(Trigger.draw, RenderExt::draw); - Events.on(TileChangeEvent.class, RenderExt::onSetBlock); -@@ -83,6 +91,8 @@ public class RenderExt{ - Draw.draw(Layer.overlayUI - 0.1f, build::drawSelect); - if(arcDrillMode && build instanceof DrillBuild drill) - arcDrillModeDraw(block, drill); -+ if(massDriverLine && build instanceof MassDriverBuild b) -+ drawMassDriverLine(b); - } - - private static void placementEffect(float x, float y, float lifetime, float range, Color color){ -@@ -126,4 +136,21 @@ public class RenderExt{ - Lines.arc(dx, dy, iconSize * 0.75f, eff); - } - } -+ -+ private static void drawMassDriverLine(MassDriverBuild build){ -+ if(build.waitingShooters.isEmpty()) return; -+ Draw.z(Layer.effect); -+ float x = build.x, y = build.y, size = build.block.size; -+ float sin = Mathf.absin(Time.time, 6f, 1f); -+ for(var shooter : build.waitingShooters){ -+ Lines.stroke(2f, Pal.placing); -+ Drawf.dashLine(RenderExt.massDriverLineColor, shooter.x, shooter.y, x, y); -+ int slice = Mathf.floorPositive(build.dst(shooter) / RenderExt.massDriverLineInterval); -+ Vec2 interval = Tmp.v1.set(build).sub(shooter).setLength(RenderExt.massDriverLineInterval); -+ float dx = interval.x, dy = interval.y; -+ for(int i = 0; i < slice; i++){ -+ Drawf.arrow(shooter.x + dx * i, shooter.y + dy * i, x, y, size * tilesize + sin, 4f + sin, RenderExt.massDriverLineColor); -+ } -+ } -+ } - } diff --git a/patches/client/0077-ARC-bundle-and-settings.patch b/patches/client/0077-ARC-bundle-and-settings.patch index a8dd984985ad..ce2b02778f25 100644 --- a/patches/client/0077-ARC-bundle-and-settings.patch +++ b/patches/client/0077-ARC-bundle-and-settings.patch @@ -8,106 +8,3 @@ Content-Transfer-Encoding: 8bit 移除一些无效设置 way-zer on 2024/4/20 at 21:59 ---- - core/src/mindustryX/features/RenderExt.java | 55 +++++++++++++++++++++ - 1 file changed, 55 insertions(+) - -diff --git a/core/src/mindustryX/features/RenderExt.java b/core/src/mindustryX/features/RenderExt.java -index 41bb25494e541e74e1112fd24ec0bce733707f30..9c0cdc44d75e857cdb5711fd9930767fe4ed19a0 100644 ---- a/core/src/mindustryX/features/RenderExt.java -+++ b/core/src/mindustryX/features/RenderExt.java -@@ -6,6 +6,7 @@ import arc.graphics.g2d.*; - import arc.math.*; - import arc.math.geom.*; - import arc.util.*; -+import mindustry.*; - import mindustry.entities.*; - import mindustry.game.EventType.*; - import mindustry.gen.*; -@@ -19,6 +20,7 @@ import mindustry.world.blocks.logic.*; - import mindustry.world.blocks.logic.MessageBlock.*; - import mindustry.world.blocks.production.Drill.*; - import mindustry.world.blocks.storage.*; -+import mindustry.world.blocks.units.*; - - import static mindustry.Vars.tilesize; - -@@ -35,6 +37,8 @@ public class RenderExt{ - public static boolean renderSort; - public static boolean massDriverLine; - public static int massDriverLineInterval; -+ public static boolean drawBars, drawBarsMend; -+ public static float healthBarMinHealth; - - public static boolean unitHide = false; - public static Color massDriverLineColor = Color.clear; -@@ -70,6 +74,9 @@ public class RenderExt{ - renderSort = Core.settings.getBool("renderSort"); - massDriverLine = Core.settings.getBool("mass_driver_line"); - massDriverLineInterval = Core.settings.getInt("mass_driver_line_interval"); -+ drawBars = Core.settings.getBool("blockBars"); -+ drawBarsMend = Core.settings.getBool("blockBars_mend"); -+ healthBarMinHealth = Core.settings.getInt("blockbarminhealth"); - }); - Events.run(Trigger.draw, RenderExt::draw); - Events.on(TileChangeEvent.class, RenderExt::onSetBlock); -@@ -93,6 +100,8 @@ public class RenderExt{ - arcDrillModeDraw(block, drill); - if(massDriverLine && build instanceof MassDriverBuild b) - drawMassDriverLine(b); -+ if(build != null && drawBars) -+ drawBars(build); - } - - private static void placementEffect(float x, float y, float lifetime, float range, Color color){ -@@ -153,4 +162,50 @@ public class RenderExt{ - } - } - } -+ -+ private static void drawBars(Building build){ -+ if(build.health / build.maxHealth < 0.9f && build.maxHealth > healthBarMinHealth) -+ drawBar(build, build.team.color, Pal.health, build.health / build.maxHealth); -+ if(drawBarsMend){ -+ if(build instanceof MendProjector.MendBuild b){ -+ var block = (MendProjector)build.block; -+ drawBar(build, Color.black, Pal.heal, b.charge / block.reload); -+ }else if(build instanceof ForceProjector.ForceBuild b && b.buildup > 0){ -+ var block = (ForceProjector)build.block; -+ float ratio = 1 - b.buildup / (block.shieldHealth + block.phaseShieldBoost * b.phaseHeat); -+ drawBar(build, Color.black, b.broken ? Pal.remove : Pal.stat, ratio); -+ } -+ } -+ float buildRatio = -1, leftTime = 0; -+ if(build instanceof Reconstructor.ReconstructorBuild b){ -+ buildRatio = b.fraction(); -+ leftTime = ((Reconstructor)build.block).constructTime - b.progress; -+ }else if(build instanceof UnitAssembler.UnitAssemblerBuild b){ -+ buildRatio = b.progress; -+ leftTime = b.plan().time * (1 - b.progress); -+ }else if(build instanceof UnitFactory.UnitFactoryBuild b){ -+ buildRatio = b.fraction(); -+ leftTime = b.currentPlan == -1 ? -1 : (((UnitFactory)build.block).plans.get(b.currentPlan).time - b.progress); -+ } -+ if(buildRatio >= 0){ -+ drawBar(build, Color.black, Pal.accent, buildRatio); -+ String progressT = Strings.format("[stat]@% | @s", (int)(Mathf.clamp(buildRatio, 0f, 1f) * 100), leftTime < 0 ? Iconc.cancel : Strings.fixed(leftTime / (60f * Vars.state.rules.unitBuildSpeed(build.team) * build.timeScale()), 0)); -+ WorldLabel.drawAt(progressT, build.x, build.y + build.block.offset * 0.8f - 5f, Draw.z(), WorldLabel.flagOutline, 0.9f); -+ } -+ } -+ -+ private static void drawBar(Building build, Color bg, Color fg, Float ratio){ -+ Draw.z(Layer.turret + 4f); -+ float x = build.x, size = build.block.size * tilesize * 0.5f; -+ float x1 = x - size * 0.6f, x2 = x + size * 0.6f, y = build.y + size * 0.8f; -+ Draw.color(bg, 0.3f); -+ Lines.stroke(4f); -+ Lines.line(x1, y, x2, y); -+ -+ Draw.color(fg, 0.6f); -+ Lines.stroke(2f); -+ Lines.line(x1, y, Mathf.lerp(x1, x2, Mathf.clamp(ratio, 0f, 1f)), y); -+ -+ Draw.reset(); -+ } - } diff --git a/patches/client/0081-API-UIExt-announce-and-sendChatMessage.patch b/patches/client/0081-API-UIExt-announce-and-sendChatMessage.patch index f84260e52b5b..e201f2dccdcc 100644 --- a/patches/client/0081-API-UIExt-announce-and-sendChatMessage.patch +++ b/patches/client/0081-API-UIExt-announce-and-sendChatMessage.patch @@ -3,68 +3,3 @@ From: way-zer Date: Fri, 14 Jun 2024 21:22:41 +0800 Subject: [PATCH] API(UIExt) announce and sendChatMessage ---- - core/src/mindustryX/features/UIExt.java | 35 +++++++++++++++++++++++++ - 1 file changed, 35 insertions(+) - -diff --git a/core/src/mindustryX/features/UIExt.java b/core/src/mindustryX/features/UIExt.java -index c09b83d494c64733e6ec9389fa8dfb2b7bcee49d..651e6408d6132a194f63fba2613abac62b60aaf1 100644 ---- a/core/src/mindustryX/features/UIExt.java -+++ b/core/src/mindustryX/features/UIExt.java -@@ -1,13 +1,17 @@ - package mindustryX.features; - - import arc.*; -+import arc.math.*; - import arc.math.geom.*; -+import arc.scene.actions.*; -+import arc.scene.event.*; - import arc.scene.ui.*; - import arc.scene.ui.layout.*; - import arc.util.*; - import mindustry.content.*; - import mindustry.core.*; - import mindustry.gen.*; -+import mindustry.ui.*; - import mindustryX.features.ui.*; - - import static mindustry.Vars.*; -@@ -69,7 +73,38 @@ public class UIExt{ - })); - } - -+ public static void announce(String text){ -+ announce(text, 3); -+ } -+ -+ public static void announce(String text, float duration){ -+ //Copy from UI.announce, no set lastAnnouncement and add offset to y -+ Table t = new Table(Styles.black3); -+ t.touchable = Touchable.disabled; -+ t.margin(8f).add(text).style(Styles.outlineLabel).labelAlign(Align.center); -+ t.update(() -> t.setPosition(Core.graphics.getWidth() / 2f, Core.graphics.getHeight() / 2f + 30f, Align.center)); -+ t.actions(Actions.fadeOut(Math.min(duration, 30f), Interp.pow4In), Actions.remove()); -+ t.pack(); -+ t.act(0.1f); -+ Core.scene.add(t); -+ } -+ - public static void sendChatMessage(String message){ -+ int maxSize = 140; -+ if(message.length() > maxSize){ -+ int i = 0; -+ while(i < message.length() - maxSize){ -+ int add = maxSize; -+ //避免分割颜色 -+ int sp = message.lastIndexOf('[', i + add); -+ int sp2 = message.lastIndexOf(']', i + add); -+ if(sp2 > sp && i + add - sp < 10) add = sp - i; -+ -+ sendChatMessage(message.substring(i, i + add)); -+ i += add; -+ } -+ sendChatMessage(message.substring(i)); -+ } - Call.sendChatMessage(ui.chatfrag.mode.normalizedPrefix() + message); - } - } diff --git a/patches/client/0083-API-FuncX-drawText.patch b/patches/client/0083-API-FuncX-drawText.patch index 4357fcbe44c2..3f940e34fdf2 100644 --- a/patches/client/0083-API-FuncX-drawText.patch +++ b/patches/client/0083-API-FuncX-drawText.patch @@ -3,75 +3,3 @@ From: way-zer Date: Sat, 15 Jun 2024 20:22:04 +0800 Subject: [PATCH] API(FuncX) drawText ---- - core/src/mindustryX/features/func/drawText.kt | 60 +++++++++++++++++++ - 1 file changed, 60 insertions(+) - create mode 100644 core/src/mindustryX/features/func/drawText.kt - -diff --git a/core/src/mindustryX/features/func/drawText.kt b/core/src/mindustryX/features/func/drawText.kt -new file mode 100644 -index 0000000000000000000000000000000000000000..0b32ad4b2c62da8321d55eb26569e1dfc6fbc7e7 ---- /dev/null -+++ b/core/src/mindustryX/features/func/drawText.kt -@@ -0,0 +1,60 @@ -+@file:JvmName("FuncX") -+@file:JvmMultifileClass -+ -+package mindustryX.features.func -+ -+import arc.graphics.Color -+import arc.graphics.g2d.Draw -+import arc.graphics.g2d.Fill -+import arc.graphics.g2d.Font -+import arc.graphics.g2d.GlyphLayout -+import arc.math.geom.Position -+import arc.scene.ui.layout.Scl -+import arc.util.Align -+import arc.util.Tmp -+import arc.util.pooling.Pools -+import mindustry.graphics.Drawf -+import mindustry.ui.Fonts -+ -+/** -+ * 绘制文字 -+ * @param fontScl 字体大小,相对世界尺寸,约一格方块大小。如果要按UI绘制,使用[Scl.scl] -+ */ -+@JvmOverloads -+fun drawText( -+ pos: Position, text: String, -+ fontScl: Float = 1f, color: Color = Color.white, anchor: Int = Align.center, -+ font: Font = Fonts.outline, background: Boolean = false, -+) { -+ val p = Tmp.v1.set(pos) -+ //参考来源 mindustry.gen.WorldLabel.drawAt -+ val z = Drawf.text() -+ val ints = font.usesIntegerPositions() -+ font.setUseIntegerPositions(false) -+ font.data.setScale(0.25f / Scl.scl() * fontScl) -+ font.color = color -+ -+ Pools.obtain(GlyphLayout::class.java, ::GlyphLayout).apply { -+ setText(font, text) -+ -+ if (Align.isCenterVertical(anchor)) p.y += height / 2 -+ else if (Align.isBottom(anchor)) p.y += height -+ var centerX = p.x -+ if (Align.isLeft(anchor)) centerX += width / 2 -+ else if (Align.isRight(anchor)) centerX -= width / 2 -+ if (background) { -+ Draw.color(Color.black, 0.3f) -+ Fill.rect(centerX, p.y - height / 2, width + 2, height + 3) -+ Draw.color() -+ } -+ }.let(Pools::free) -+ val align = if (Align.isCenterHorizontal(anchor)) anchor or Align.center else anchor -+ //此次x为绘制区域顶部 -+ font.draw(text, p.x, p.y, 0f, align, false) -+ Draw.reset() -+ -+ font.color.set(Color.white) -+ font.data.setScale(1f) -+ font.setUseIntegerPositions(ints) -+ Draw.z(z) -+} -\ No newline at end of file diff --git a/patches/client/0084-C-RenderExt-playerEffectColor.patch b/patches/client/0084-C-RenderExt-playerEffectColor.patch index debd90f3e293..1f714a84206a 100644 --- a/patches/client/0084-C-RenderExt-playerEffectColor.patch +++ b/patches/client/0084-C-RenderExt-playerEffectColor.patch @@ -3,43 +3,3 @@ From: way-zer Date: Sat, 15 Jun 2024 13:57:02 +0800 Subject: [PATCH] C(RenderExt) playerEffectColor ---- - core/src/mindustryX/features/ArcOld.java | 12 +++++++++++- - core/src/mindustryX/features/RenderExt.java | 1 + - 2 files changed, 12 insertions(+), 1 deletion(-) - -diff --git a/core/src/mindustryX/features/ArcOld.java b/core/src/mindustryX/features/ArcOld.java -index 47ea958a84d4e7f33241ad98da20db3d061eab78..c2e398bcb71566cc3f3aaa4a081174c19df9596a 100644 ---- a/core/src/mindustryX/features/ArcOld.java -+++ b/core/src/mindustryX/features/ArcOld.java -@@ -127,7 +127,17 @@ public class ArcOld{ - c.sliderPref("rtsWoundUnit", 0, 0, 100, 2, s -> s + "%"); - - c.addCategory("arcPlayerEffect"); -- c.textPref("playerEffectColor", "ffd37f"); -+ { -+ Cons changed = (t) -> { -+ try{ -+ RenderExt.playerEffectColor = Color.valueOf(t); -+ }catch(Exception e){ -+ RenderExt.playerEffectColor = Pal.accent; -+ } -+ }; -+ c.textPref("playerEffectColor", "ffd37f", changed); -+ changed.get(settings.getString("playerEffectColor")); -+ } - c.sliderPref("unitTargetType", 0, 0, 5, 1, s -> switch(s){ - case 0 -> "关闭"; - case 1 -> "虚圆"; -diff --git a/core/src/mindustryX/features/RenderExt.java b/core/src/mindustryX/features/RenderExt.java -index 9c0cdc44d75e857cdb5711fd9930767fe4ed19a0..20bf23e68ce8e751b66905b022c54aa2f72d9862 100644 ---- a/core/src/mindustryX/features/RenderExt.java -+++ b/core/src/mindustryX/features/RenderExt.java -@@ -42,6 +42,7 @@ public class RenderExt{ - - public static boolean unitHide = false; - public static Color massDriverLineColor = Color.clear; -+ public static Color playerEffectColor = Color.clear; - - private static Effect placementEffect; - diff --git a/patches/client/0085-API-add-PlayerTeamChangedEvent.patch b/patches/client/0085-API-add-PlayerTeamChangedEvent.patch index ecf11f0581e2..6b6cd25cdc04 100644 --- a/patches/client/0085-API-add-PlayerTeamChangedEvent.patch +++ b/patches/client/0085-API-add-PlayerTeamChangedEvent.patch @@ -4,10 +4,8 @@ Date: Sun, 3 Dec 2023 15:55:56 +0800 Subject: [PATCH] API: add PlayerTeamChangedEvent --- - .../src/mindustry/entities/comp/PlayerComp.java | 4 ++++ - .../events/PlayerTeamChangedEvent.java | 17 +++++++++++++++++ - 2 files changed, 21 insertions(+) - create mode 100644 core/src/mindustryX/events/PlayerTeamChangedEvent.java + core/src/mindustry/entities/comp/PlayerComp.java | 4 ++++ + 1 file changed, 4 insertions(+) diff --git a/core/src/mindustry/entities/comp/PlayerComp.java b/core/src/mindustry/entities/comp/PlayerComp.java index fee7e8c71fc07f8cbea36997afc8a4fda8c8c3f1..6646df00e2c060899ec80ea5bde1e1fa444654a9 100644 @@ -35,26 +33,3 @@ index fee7e8c71fc07f8cbea36997afc8a4fda8c8c3f1..6646df00e2c060899ec80ea5bde1e1fa } public void clearUnit(){ -diff --git a/core/src/mindustryX/events/PlayerTeamChangedEvent.java b/core/src/mindustryX/events/PlayerTeamChangedEvent.java -new file mode 100644 -index 0000000000000000000000000000000000000000..4011f009f9e6adf14887e42d5b76c42ced94e4cb ---- /dev/null -+++ b/core/src/mindustryX/events/PlayerTeamChangedEvent.java -@@ -0,0 +1,17 @@ -+package mindustryX.events; -+ -+import mindustry.game.*; -+import mindustry.gen.*; -+import mindustryX.*; -+ -+/** Called after the team of a player changed. */ -+@MindustryXApi -+public class PlayerTeamChangedEvent{ -+ public final Team previous; -+ public final Player player; -+ public PlayerTeamChangedEvent(Team previous, Player player) { -+ this.previous = previous; -+ this.player = player; -+ } -+} -+ diff --git a/patches/client/0091-FC-RenderExt-payloadPreview.patch b/patches/client/0091-FC-RenderExt-payloadPreview.patch index f742e747c5ea..02fa4a7d748e 100644 --- a/patches/client/0091-FC-RenderExt-payloadPreview.patch +++ b/patches/client/0091-FC-RenderExt-payloadPreview.patch @@ -3,138 +3,3 @@ From: way-zer Date: Fri, 2 Aug 2024 23:32:56 +0800 Subject: [PATCH] FC(RenderExt) payloadPreview ---- - core/src/mindustryX/features/RenderExt.java | 7 +- - .../features/draw/PayloadDropHint.java | 83 +++++++++++++++++++ - 2 files changed, 88 insertions(+), 2 deletions(-) - create mode 100644 core/src/mindustryX/features/draw/PayloadDropHint.java - -diff --git a/core/src/mindustryX/features/RenderExt.java b/core/src/mindustryX/features/RenderExt.java -index 20bf23e68ce8e751b66905b022c54aa2f72d9862..d2d9a53895e5d44302b6532307d12d26ad876970 100644 ---- a/core/src/mindustryX/features/RenderExt.java -+++ b/core/src/mindustryX/features/RenderExt.java -@@ -21,8 +21,9 @@ import mindustry.world.blocks.logic.MessageBlock.*; - import mindustry.world.blocks.production.Drill.*; - import mindustry.world.blocks.storage.*; - import mindustry.world.blocks.units.*; -+import mindustryX.features.draw.*; - --import static mindustry.Vars.tilesize; -+import static mindustry.Vars.*; - - public class RenderExt{ - public static boolean bulletShow, showMineBeam, displayAllMessage; -@@ -39,6 +40,7 @@ public class RenderExt{ - public static int massDriverLineInterval; - public static boolean drawBars, drawBarsMend; - public static float healthBarMinHealth; -+ public static boolean payloadPreview; - - public static boolean unitHide = false; - public static Color massDriverLineColor = Color.clear; -@@ -78,13 +80,14 @@ public class RenderExt{ - drawBars = Core.settings.getBool("blockBars"); - drawBarsMend = Core.settings.getBool("blockBars_mend"); - healthBarMinHealth = Core.settings.getInt("blockbarminhealth"); -+ payloadPreview = Core.settings.getBool("payloadpreview"); - }); - Events.run(Trigger.draw, RenderExt::draw); - Events.on(TileChangeEvent.class, RenderExt::onSetBlock); - } - - private static void draw(){ -- -+ if(RenderExt.payloadPreview) PayloadDropHint.draw(player); - } - - public static void onGroupDraw(Drawc t){ -diff --git a/core/src/mindustryX/features/draw/PayloadDropHint.java b/core/src/mindustryX/features/draw/PayloadDropHint.java -new file mode 100644 -index 0000000000000000000000000000000000000000..e48f5dbd754de2dd65d0a1003b248852236a04aa ---- /dev/null -+++ b/core/src/mindustryX/features/draw/PayloadDropHint.java -@@ -0,0 +1,83 @@ -+package mindustryX.features.draw; -+ -+import arc.graphics.*; -+import arc.graphics.g2d.*; -+import arc.math.geom.*; -+import arc.util.*; -+import mindustry.*; -+import mindustry.core.*; -+import mindustry.ctype.*; -+import mindustry.entities.*; -+import mindustry.gen.*; -+import mindustry.graphics.*; -+import mindustry.type.*; -+import mindustry.world.*; -+import mindustry.world.blocks.payloads.*; -+import mindustry.world.meta.*; -+ -+// pick from https://github.com/MinRi2/MinerTools/blob/8ab2fe090cf24f0a5c8eaa0dcbea01f0a5447dd8/src/MinerTools/graphics/draw/player/PayloadDropHint.java -+// 重制修正:WayZer -+public class PayloadDropHint{ -+ public static void draw(Player player){ -+ var unit = player.unit() instanceof Payloadc ? (Unit & Payloadc)player.unit() : null; -+ if(unit == null) return; -+ Tile on = unit.tileOn(); -+ if(on == null) return; -+ -+ Draw.z(Layer.flyingUnit + 0.1f); -+ //dropHint -+ if(unit.payloads().any()){ -+ Payload payload = unit.payloads().peek(); -+ if(on.build != null && on.build.acceptPayload(on.build, payload)){ -+ draw(on.build, payload.content(), payload instanceof BuildPayload b ? b.build.rotation * 90 : payload.rotation() - 90); -+ }else if(payload instanceof BuildPayload p){ -+ Building build = p.build; -+ Block block = build.block; -+ int tx = World.toTile(build.x - block.offset), ty = World.toTile(build.y - block.offset); -+ boolean valid = Build.validPlace(block, build.team, tx, ty, build.rotation, false); -+ -+ Vec2 center = block.bounds(tx, ty, Tmp.r1).getCenter(Tmp.v1); -+ draw(center, block, build.rotation * 90, valid); -+ }else if(payload instanceof UnitPayload p){ -+ var u = p.unit; -+ boolean valid = u.canPass(on.x, on.y) && Units.count(u.x, u.y, u.physicSize(), Flyingc::isGrounded) <= 1; -+ draw(payload, payload.content(), u.rotation - 90, valid); -+ } -+ } -+ //pickHint -+ { -+ Unit target = Units.closest(unit.team(), unit.x, unit.y, unit.type.hitSize * 2f, u -> u.isAI() && u.isGrounded() && unit.canPickup(u) && u.within(unit, u.hitSize + unit.hitSize)); -+ if(target != null){ -+ draw(target, target.type, target.rotation - 90); -+ return; -+ } -+ Building build = on.build; -+ if(build == null) return; -+ Payload payload = build.getPayload(); -+ if(payload != null && unit.canPickupPayload(payload)){ -+ draw(payload, payload.content(), payload instanceof BuildPayload b ? b.build.rotation * 90 : payload.rotation()); -+ return; -+ } -+ if(build.block.buildVisibility != BuildVisibility.hidden && build.canPickup() && unit.canPickup(build)){ -+ draw(build, build.block, build.rotation * 90); -+ } -+ } -+ } -+ -+ private static void draw(Position pos, UnlockableContent type, float rotation){ -+ draw(pos, type, rotation, true); -+ } -+ -+ private static void draw(Position pos, UnlockableContent type, float rotation, boolean valid){ -+ Draw.color(!valid ? Color.red : Pal.accent, 0.6f); -+ if(type instanceof Block block){ -+ float size = block.size * Vars.tilesize; -+ Draw.rect(block.fullIcon, pos.getX(), pos.getY(), size, size, rotation); -+ Lines.square(pos.getX(), pos.getY(), size * 0.9f, 20); -+ }else if(type instanceof UnitType unit){ -+ Draw.rect(type.fullIcon, pos.getX(), pos.getY(), rotation); -+ Lines.square(pos.getX(), pos.getY(), unit.hitSize, 20); -+ } -+ Draw.color(); -+ } -+} -\ No newline at end of file diff --git a/patches/client/0092-FC-RenderExt-deadOverlay.patch b/patches/client/0092-FC-RenderExt-deadOverlay.patch index 90ae81e96f56..94c197d2be41 100644 --- a/patches/client/0092-FC-RenderExt-deadOverlay.patch +++ b/patches/client/0092-FC-RenderExt-deadOverlay.patch @@ -5,8 +5,7 @@ Subject: [PATCH] FC(RenderExt) deadOverlay --- core/src/mindustry/graphics/OverlayRenderer.java | 4 ++-- - core/src/mindustryX/features/RenderExt.java | 2 ++ - 2 files changed, 4 insertions(+), 2 deletions(-) + 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/mindustry/graphics/OverlayRenderer.java b/core/src/mindustry/graphics/OverlayRenderer.java index 8346eb94d62f3d9cb57e4f63ed39ac34cb07c84d..fe8d7dc18df91b5f8484e87f0d37ab8dce06b54f 100644 @@ -30,23 +29,3 @@ index 8346eb94d62f3d9cb57e4f63ed39ac34cb07c84d..fe8d7dc18df91b5f8484e87f0d37ab8d InputHandler input = control.input; -diff --git a/core/src/mindustryX/features/RenderExt.java b/core/src/mindustryX/features/RenderExt.java -index d2d9a53895e5d44302b6532307d12d26ad876970..bd74e46042b9d40a5bd3e7eb6f4ae3ca60cc8840 100644 ---- a/core/src/mindustryX/features/RenderExt.java -+++ b/core/src/mindustryX/features/RenderExt.java -@@ -41,6 +41,7 @@ public class RenderExt{ - public static boolean drawBars, drawBarsMend; - public static float healthBarMinHealth; - public static boolean payloadPreview; -+ public static boolean deadOverlay; - - public static boolean unitHide = false; - public static Color massDriverLineColor = Color.clear; -@@ -81,6 +82,7 @@ public class RenderExt{ - drawBarsMend = Core.settings.getBool("blockBars_mend"); - healthBarMinHealth = Core.settings.getInt("blockbarminhealth"); - payloadPreview = Core.settings.getBool("payloadpreview"); -+ deadOverlay = Core.settings.getBool("deadOverlay"); - }); - Events.run(Trigger.draw, RenderExt::draw); - Events.on(TileChangeEvent.class, RenderExt::onSetBlock); diff --git a/patches/client/0093-FC-LogicExt-invertMapClick.patch b/patches/client/0093-FC-LogicExt-invertMapClick.patch index c44e326417cb..1765ae4f1590 100644 --- a/patches/client/0093-FC-LogicExt-invertMapClick.patch +++ b/patches/client/0093-FC-LogicExt-invertMapClick.patch @@ -6,8 +6,7 @@ Subject: [PATCH] FC(LogicExt) invertMapClick --- core/src/mindustry/ui/Minimap.java | 8 ++++++-- core/src/mindustry/ui/fragments/MinimapFragment.java | 5 +++-- - core/src/mindustryX/features/LogicExt.java | 2 ++ - 3 files changed, 11 insertions(+), 4 deletions(-) + 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/core/src/mindustry/ui/Minimap.java b/core/src/mindustry/ui/Minimap.java index 037dea9ae4e54db3661071cb388d3850f6a156c0..ad214460bb8458b1eb323c2d10d314fd6fec3522 100644 @@ -84,24 +83,3 @@ index 2fe8df586eb0a3907334e45b1639b9534e4b292c..89e00d6356c65d1bff200857e3769583 panTo(x, y); } } -diff --git a/core/src/mindustryX/features/LogicExt.java b/core/src/mindustryX/features/LogicExt.java -index d148408a6bac91abe640a07fd06e81d4f8e35c64..00ef53c2c05761e9fd10861b825575f754762639 100644 ---- a/core/src/mindustryX/features/LogicExt.java -+++ b/core/src/mindustryX/features/LogicExt.java -@@ -10,6 +10,7 @@ public class LogicExt{ - public static boolean worldCreator = false; - public static boolean allUnlocked = false; - public static boolean terrainSchematic = false; -+ public static boolean invertMapClick = false; - - public static void init(){ - Events.run(Trigger.update, () -> { -@@ -22,6 +23,7 @@ public class LogicExt{ - worldCreator = Core.settings.getBool("worldCreator"); - allUnlocked = Core.settings.getBool("allUnlocked"); - terrainSchematic = Core.settings.getBool("terrainSchematic"); -+ invertMapClick = Core.settings.getBool("invertMapClick"); - }); - } - } -\ No newline at end of file diff --git a/patches/client/0095-OC-fix-slow-of-LCanvas.patch b/patches/client/0095-OC-fix-slow-of-LCanvas.patch index 48453b1bde21..efc7e0946e6d 100644 --- a/patches/client/0095-OC-fix-slow-of-LCanvas.patch +++ b/patches/client/0095-OC-fix-slow-of-LCanvas.patch @@ -4,9 +4,8 @@ Date: Tue, 6 Aug 2024 01:30:38 +0800 Subject: [PATCH] OC: fix slow of LCanvas --- - core/src/mindustry/logic/LCanvas.java | 113 ++++++++++---------- - core/src/mindustryX/features/RenderExt.java | 25 +++++ - 2 files changed, 84 insertions(+), 54 deletions(-) + core/src/mindustry/logic/LCanvas.java | 113 ++++++++++++++------------ + 1 file changed, 59 insertions(+), 54 deletions(-) diff --git a/core/src/mindustry/logic/LCanvas.java b/core/src/mindustry/logic/LCanvas.java index e40b426a3c3ec656f9364c25ac762ed3b0121390..f92c4152ebaa794e33f1f4df864a4ea66bd8e7c5 100644 @@ -272,46 +271,3 @@ index e40b426a3c3ec656f9364c25ac762ed3b0121390..f92c4152ebaa794e33f1f4df864a4ea6 } public void drawCurve(float x, float y, float x2, float y2){ -diff --git a/core/src/mindustryX/features/RenderExt.java b/core/src/mindustryX/features/RenderExt.java -index bd74e46042b9d40a5bd3e7eb6f4ae3ca60cc8840..76adae3a529d20bf1c58dc42bd8aa4d439402df9 100644 ---- a/core/src/mindustryX/features/RenderExt.java -+++ b/core/src/mindustryX/features/RenderExt.java -@@ -3,6 +3,7 @@ package mindustryX.features; - import arc.*; - import arc.graphics.*; - import arc.graphics.g2d.*; -+import arc.graphics.g2d.TextureAtlas.*; - import arc.math.*; - import arc.math.geom.*; - import arc.util.*; -@@ -86,6 +87,30 @@ public class RenderExt{ - }); - Events.run(Trigger.draw, RenderExt::draw); - Events.on(TileChangeEvent.class, RenderExt::onSetBlock); -+ -+ //Optimize white() for ui -+ AtlasRegion white = Core.atlas.white(), -+ whiteUI = Core.atlas.find("whiteui"), -+ whiteSet = new AtlasRegion(white){ -+ @Override -+ public void set(TextureRegion region0){ -+ super.set(region0); -+ if(region0 instanceof AtlasRegion region){ -+ name = region.name; -+ offsetX = region.offsetX; -+ offsetY = region.offsetY; -+ packedWidth = region.packedWidth; -+ packedHeight = region.packedHeight; -+ originalWidth = region.originalWidth; -+ originalHeight = region.originalHeight; -+ rotate = region.rotate; -+ splits = region.splits; -+ } -+ } -+ }; -+ Reflect.set(TextureAtlas.class, Core.atlas, "white", whiteSet); -+ Events.run(Trigger.uiDrawBegin, () -> whiteSet.set(whiteUI)); -+ Events.run(Trigger.uiDrawEnd, () -> whiteSet.set(white)); - } - - private static void draw(){ diff --git a/patches/client/0096-FC-FuncX-focusLogicController.patch b/patches/client/0096-FC-FuncX-focusLogicController.patch index 83dd6d184a89..0a568dfb456a 100644 --- a/patches/client/0096-FC-FuncX-focusLogicController.patch +++ b/patches/client/0096-FC-FuncX-focusLogicController.patch @@ -4,11 +4,8 @@ Date: Wed, 28 Aug 2024 21:57:23 +0800 Subject: [PATCH] FC(FuncX) focusLogicController --- - core/src/mindustry/input/Binding.java | 7 +++--- - core/src/mindustryX/Hooks.java | 12 +++++----- - .../features/func/focusLogicController.kt | 22 +++++++++++++++++++ - 3 files changed, 33 insertions(+), 8 deletions(-) - create mode 100644 core/src/mindustryX/features/func/focusLogicController.kt + core/src/mindustry/input/Binding.java | 7 ++++--- + 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/core/src/mindustry/input/Binding.java b/core/src/mindustry/input/Binding.java index 4bb4579c66cc80af91b8e78a0f37dc9749a22822..fcbcc2483e5d7dbc71ce769840cdbbd61c3e8095 100644 @@ -29,67 +26,3 @@ index 4bb4579c66cc80af91b8e78a0f37dc9749a22822..fcbcc2483e5d7dbc71ce769840cdbbd6 ; private final KeybindValue defaultValue; -diff --git a/core/src/mindustryX/Hooks.java b/core/src/mindustryX/Hooks.java -index f323267b874868d3c8c05919ccc1ea92821f4918..b852e5ec3bb8a949b58d1909ecf93764db33ea19 100644 ---- a/core/src/mindustryX/Hooks.java -+++ b/core/src/mindustryX/Hooks.java -@@ -6,14 +6,13 @@ import arc.util.*; - import mindustry.*; - import mindustry.gen.*; - import mindustry.input.*; --import mindustryX.features.*; - import mindustryX.features.Settings; -+import mindustryX.features.*; -+import mindustryX.features.func.*; - - import java.net.*; - import java.util.*; - --import static arc.Core.*; -- - public class Hooks implements ApplicationListener{ - /** invoke before `Vars.init`. Note that may be executed from `Vars.loadAsync` */ - public static void beforeInit(){ -@@ -78,8 +77,11 @@ public class Hooks implements ApplicationListener{ - if(Core.input.keyTap(Binding.point)){ - MarkerType.selected.markWithMessage(Core.input.mouseWorld()); - } -- if(input.keyTap(Binding.toggle_block_render)){ -- settings.put("blockRenderLevel", (RenderExt.blockRenderLevel + 1) % 3); -+ if(Core.input.keyTap(Binding.toggle_block_render)){ -+ Core.settings.put("blockRenderLevel", (RenderExt.blockRenderLevel + 1) % 3); -+ } -+ if(Core.input.keyTap(Binding.focusLogicController)){ -+ FuncX.focusLogicController(); - } - } - -diff --git a/core/src/mindustryX/features/func/focusLogicController.kt b/core/src/mindustryX/features/func/focusLogicController.kt -new file mode 100644 -index 0000000000000000000000000000000000000000..dc1694e19041ad94236b10bc2bad7353efc9ab72 ---- /dev/null -+++ b/core/src/mindustryX/features/func/focusLogicController.kt -@@ -0,0 +1,22 @@ -+@file:JvmName("FuncX") -+@file:JvmMultifileClass -+ -+package mindustryX.features.func -+ -+import arc.Core -+import arc.util.Tmp -+import mindustry.Vars -+import mindustry.ai.types.LogicAI -+import mindustry.entities.Units -+import mindustryX.features.MarkerType -+import mindustryX.features.RenderExt -+ -+fun focusLogicController() { -+ val mouse = Core.input.mouseWorld() -+ val logic = Units.closestOverlap(Vars.player.team(), mouse.x, mouse.y, 5f) { true }?.let { (it.controller() as? LogicAI)?.controller } -+ ?: (if (RenderExt.showOtherInfo) Units.closestEnemy(Vars.player.team(), mouse.x, mouse.y, 5f) { true }?.let { (it.controller() as? LogicAI)?.controller } else null) -+ ?: Core.input.mouseWorld().let { Vars.world.buildWorld(it.x, it.y)?.lastLogicController } -+ ?: return -+ Vars.control.input.panCamera(Tmp.v1.set(logic)) -+ MarkerType.mark.at(logic) -+} -\ No newline at end of file diff --git a/patches/client/0098-ARC-merged.patch b/patches/client/0098-ARC-merged.patch index f266490c6417..0d35fdb8824b 100644 --- a/patches/client/0098-ARC-merged.patch +++ b/patches/client/0098-ARC-merged.patch @@ -93,73 +93,49 @@ way-zer on 2024/7/28 * 更改开雾功能实现 way-zer on 2024/9/8 --- - core/src/mindustry/ai/BlockIndexer.java | 116 ++ - core/src/mindustry/core/Renderer.java | 1 + - core/src/mindustry/core/UI.java | 3 + - core/src/mindustry/editor/MapInfoDialog.java | 139 +- - .../mindustry/entities/comp/BuildingComp.java | 18 +- - .../mindustry/entities/comp/PlayerComp.java | 22 +- - .../mindustry/entities/comp/ShieldComp.java | 6 + - .../mindustry/entities/comp/StatusComp.java | 12 + - core/src/mindustry/game/Schematic.java | 158 +++ - core/src/mindustry/game/Universe.java | 2 +- - .../src/mindustry/graphics/BlockRenderer.java | 17 +- - core/src/mindustry/graphics/Drawf.java | 54 + - .../mindustry/graphics/OverlayRenderer.java | 84 +- - core/src/mindustry/input/Binding.java | 6 + - core/src/mindustry/input/DesktopInput.java | 85 +- - core/src/mindustry/input/InputHandler.java | 55 +- - core/src/mindustry/input/MobileInput.java | 16 +- - core/src/mindustry/type/UnitType.java | 162 ++- - core/src/mindustry/type/Weapon.java | 25 +- - core/src/mindustry/ui/Fonts.java | 2 +- - .../ui/dialogs/ContentInfoDialog.java | 27 +- - .../ui/dialogs/CustomRulesDialog.java | 121 +- - .../mindustry/ui/dialogs/DatabaseDialog.java | 5 +- - .../mindustry/ui/dialogs/PausedDialog.java | 12 +- - .../mindustry/ui/dialogs/PlanetDialog.java | 70 +- - .../ui/dialogs/SchematicsDialog.java | 383 +++++- - .../mindustry/ui/fragments/ChatFragment.java | 25 +- - .../ui/fragments/ConsoleFragment.java | 3 + - .../mindustry/ui/fragments/HudFragment.java | 229 +++- - .../mindustry/ui/fragments/MenuFragment.java | 59 +- - .../ui/fragments/PlacementFragment.java | 166 ++- - .../ui/fragments/PlayerListFragment.java | 93 +- - core/src/mindustry/world/Block.java | 16 +- - .../world/blocks/ConstructBlock.java | 42 +- - .../world/blocks/defense/ForceProjector.java | 2 + - .../world/blocks/defense/MendProjector.java | 5 +- - .../blocks/defense/OverdriveProjector.java | 6 +- - .../blocks/defense/turrets/BaseTurret.java | 5 +- - .../world/blocks/defense/turrets/Turret.java | 12 + - .../world/blocks/production/Drill.java | 23 +- - .../blocks/production/GenericCrafter.java | 29 + - .../world/blocks/storage/CoreBlock.java | 9 + - core/src/mindustryX/Hooks.java | 18 + - core/src/mindustryX/features/ArcBuilds.java | 163 +++ - core/src/mindustryX/features/ArcOld.java | 42 + - core/src/mindustryX/features/ArcRadar.java | 239 ++++ - core/src/mindustryX/features/ArcUnits.java | 349 +++++ - .../mindustryX/features/ArcWaveSpawner.java | 258 ++++ - core/src/mindustryX/features/MarkerType.java | 2 + - .../mindustryX/features/PicToMindustry.java | 343 +++++ - core/src/mindustryX/features/RenderExt.java | 12 +- - core/src/mindustryX/features/UIExt.java | 1 + - .../features/ui/ArcMessageDialog.java | 419 +++++++ - .../mindustryX/features/ui/ArcPowerInfo.java | 70 ++ - .../features/ui/ArcWaveInfoDialog.java | 1117 +++++++++++++++++ - .../features/ui/BlockSelectDialog.java | 65 + - .../features/ui/HudSettingsTable.java | 4 +- - 57 files changed, 5196 insertions(+), 231 deletions(-) - create mode 100644 core/src/mindustryX/features/ArcBuilds.java - create mode 100644 core/src/mindustryX/features/ArcRadar.java - create mode 100644 core/src/mindustryX/features/ArcUnits.java - create mode 100644 core/src/mindustryX/features/ArcWaveSpawner.java - create mode 100644 core/src/mindustryX/features/PicToMindustry.java - create mode 100644 core/src/mindustryX/features/ui/ArcMessageDialog.java - create mode 100644 core/src/mindustryX/features/ui/ArcPowerInfo.java - create mode 100644 core/src/mindustryX/features/ui/ArcWaveInfoDialog.java - create mode 100644 core/src/mindustryX/features/ui/BlockSelectDialog.java + core/src/mindustry/ai/BlockIndexer.java | 116 ++++++ + core/src/mindustry/core/Renderer.java | 1 + + core/src/mindustry/core/UI.java | 3 + + core/src/mindustry/editor/MapInfoDialog.java | 139 ++++++- + .../mindustry/entities/comp/BuildingComp.java | 18 +- + .../mindustry/entities/comp/PlayerComp.java | 22 +- + .../mindustry/entities/comp/ShieldComp.java | 6 + + .../mindustry/entities/comp/StatusComp.java | 12 + + core/src/mindustry/game/Schematic.java | 158 ++++++++ + core/src/mindustry/game/Universe.java | 2 +- + .../src/mindustry/graphics/BlockRenderer.java | 17 +- + core/src/mindustry/graphics/Drawf.java | 54 +++ + .../mindustry/graphics/OverlayRenderer.java | 84 +++- + core/src/mindustry/input/Binding.java | 6 + + core/src/mindustry/input/DesktopInput.java | 85 +++- + core/src/mindustry/input/InputHandler.java | 55 ++- + core/src/mindustry/input/MobileInput.java | 16 +- + core/src/mindustry/type/UnitType.java | 162 ++++++-- + core/src/mindustry/type/Weapon.java | 25 +- + core/src/mindustry/ui/Fonts.java | 2 +- + .../ui/dialogs/ContentInfoDialog.java | 27 +- + .../ui/dialogs/CustomRulesDialog.java | 121 +++++- + .../mindustry/ui/dialogs/DatabaseDialog.java | 5 +- + .../mindustry/ui/dialogs/PausedDialog.java | 12 +- + .../mindustry/ui/dialogs/PlanetDialog.java | 70 +++- + .../ui/dialogs/SchematicsDialog.java | 383 +++++++++++++++++- + .../mindustry/ui/fragments/ChatFragment.java | 25 +- + .../ui/fragments/ConsoleFragment.java | 3 + + .../mindustry/ui/fragments/HudFragment.java | 229 ++++++++++- + .../mindustry/ui/fragments/MenuFragment.java | 59 ++- + .../ui/fragments/PlacementFragment.java | 166 ++++++-- + .../ui/fragments/PlayerListFragment.java | 93 ++--- + core/src/mindustry/world/Block.java | 16 +- + .../world/blocks/ConstructBlock.java | 42 +- + .../world/blocks/defense/ForceProjector.java | 2 + + .../world/blocks/defense/MendProjector.java | 5 +- + .../blocks/defense/OverdriveProjector.java | 6 +- + .../blocks/defense/turrets/BaseTurret.java | 5 +- + .../world/blocks/defense/turrets/Turret.java | 12 + + .../world/blocks/production/Drill.java | 23 +- + .../blocks/production/GenericCrafter.java | 29 ++ + .../world/blocks/storage/CoreBlock.java | 9 + + 42 files changed, 2097 insertions(+), 228 deletions(-) diff --git a/core/src/mindustry/ai/BlockIndexer.java b/core/src/mindustry/ai/BlockIndexer.java index 7f944a3eb06180f9e2b760bbd5aa41709ddf6611..4972f424edd41b8a29d926a09bc55987066d33aa 100644 @@ -4514,3304 +4490,3 @@ index 15a80f0c6e61210c702cf42d672ebc24a6cfc0f6..a3feeffe80258f74116ab767bf1b12b3 Draw.reset(); } -diff --git a/core/src/mindustryX/Hooks.java b/core/src/mindustryX/Hooks.java -index b852e5ec3bb8a949b58d1909ecf93764db33ea19..25c6b92e86ad357d52d92c4f912898d8cb8b9de7 100644 ---- a/core/src/mindustryX/Hooks.java -+++ b/core/src/mindustryX/Hooks.java -@@ -9,6 +9,7 @@ import mindustry.input.*; - import mindustryX.features.Settings; - import mindustryX.features.*; - import mindustryX.features.func.*; -+import mindustryX.features.ui.*; - - import java.net.*; - import java.util.*; -@@ -57,6 +58,14 @@ public class Hooks implements ApplicationListener{ - if(message == null) return null; - if(Vars.ui != null){ - if(MarkerType.resolveMessage(message)) return message; -+ try{ -+ ArcMessageDialog.resolveMsg(message, sender); -+ if(sender != null){ -+ message = (sender.dead() ? Iconc.alphaaaa : sender.unit().type.emoji()) + " " + message; -+ } -+ }catch(Exception e){ -+ Log.err(e); -+ } - } - return message; - } -@@ -83,6 +92,15 @@ public class Hooks implements ApplicationListener{ - if(Core.input.keyTap(Binding.focusLogicController)){ - FuncX.focusLogicController(); - } -+ if(Core.input.keyTap(Binding.arcScanMode)){ -+ ArcScanMode.enabled = !ArcScanMode.enabled; -+ } -+ if(Core.input.keyTap(Binding.showRTSAi)){ -+ Settings.toggle("alwaysShowUnitRTSAi"); -+ } -+ if(Core.input.keyTap(Binding.superUnitEffect)){ -+ Settings.cycle("superUnitEffect", 3); -+ } - } - - private static void registerBundle(){ -diff --git a/core/src/mindustryX/features/ArcBuilds.java b/core/src/mindustryX/features/ArcBuilds.java -new file mode 100644 -index 0000000000000000000000000000000000000000..94e5146690c6394393ea218f67ef1db802d742c8 ---- /dev/null -+++ b/core/src/mindustryX/features/ArcBuilds.java -@@ -0,0 +1,163 @@ -+package mindustryX.features; -+ -+import arc.*; -+import arc.graphics.g2d.*; -+import arc.math.*; -+import arc.math.geom.*; -+import arc.struct.*; -+import arc.util.*; -+import mindustry.ctype.*; -+import mindustry.entities.bullet.*; -+import mindustry.game.*; -+import mindustry.gen.*; -+import mindustry.graphics.*; -+import mindustry.type.*; -+import mindustry.world.blocks.defense.turrets.*; -+ -+import static mindustry.Vars.*; -+ -+//move from mindustry.arcModule.draw.ARCBuilds -+public class ArcBuilds{ -+ private static boolean targetAir = false, targetGround = false, canShoot = false; -+ private static boolean turretForceShowRange = false; -+ private static int turretShowRange = 0, turretAlertRange; -+ private static boolean showTurretAmmo = false, showTurretAmmoAmount = false; -+ private static boolean blockWeaponTargetLine = false, blockWeaponTargetLineWhenIdle = false; -+ -+ static{ -+ // 减少性能开销 -+ Events.run(EventType.Trigger.update, () -> { -+ turretForceShowRange = Core.settings.getBool("turretForceShowRange"); -+ turretShowRange = Core.settings.getInt("turretShowRange"); -+ -+ turretAlertRange = Core.settings.getInt("turretAlertRange") * tilesize; -+ -+ showTurretAmmo = Core.settings.getBool("showTurretAmmo"); -+ showTurretAmmoAmount = Core.settings.getBool("showTurretAmmoAmount"); -+ -+ blockWeaponTargetLine = Core.settings.getBool("blockWeaponTargetLine"); -+ blockWeaponTargetLineWhenIdle = Core.settings.getBool("blockWeaponTargetLineWhenIdle"); -+ }); -+ } -+ -+ private static void drawRange(BaseTurret.BaseTurretBuild build){ -+ Draw.z(Layer.turret - 0.8f); -+ //Draw.color(build.team.color, 0.05f); -+ //Fill.circle(build.x, build.y, build.range()); -+ Draw.color(build.team.color, 0.6f); -+ Lines.circle(build.x, build.y, build.range()); -+ Draw.reset(); -+ } -+ -+ public static void arcTurret(BaseTurret.BaseTurretBuild build){ -+ if(build == null || !(build.team == player.team() || RenderExt.showOtherInfo)) return; -+ Draw.z(Layer.turret); -+ -+ Vec2 targetPos = Vec2.ZERO; -+ if(build.block instanceof Turret t){ -+ targetAir = t.targetAir; -+ targetGround = t.targetGround; -+ targetPos = ((Turret.TurretBuild)build).targetPos; -+ canShoot = ((Turret.TurretBuild)build).hasAmmo(); -+ }else if(build.block instanceof TractorBeamTurret t){ -+ targetAir = t.targetAir; -+ targetGround = t.targetGround; -+ Unit target = ((TractorBeamTurret.TractorBeamBuild)build).target; -+ if(target != null){ -+ targetPos = Tmp.v1.set(target.x, target.y); -+ } -+ canShoot = build.potentialEfficiency > 0; -+ } -+ if(build instanceof PowerTurret.PowerTurretBuild){ -+ canShoot = build.efficiency > 0; -+ } -+ -+ if(turretForceShowRange || canShoot){ -+ if((turretShowRange == 3 || (turretShowRange == 2 && targetAir) || (turretShowRange == 1 && targetGround))) -+ drawRange(build); -+ else if(turretAlertRange > 0 && build.team != player.team()){ -+ boolean canHitPlayer = !player.dead() && player.unit().hittable() && (player.unit().isFlying() ? targetAir : targetGround) -+ && build.within(player.unit().x, player.unit().y, build.range() + turretAlertRange); -+ boolean canHitMouse = build.within(Core.input.mouseWorldX(), Core.input.mouseWorldY(), build.range() + turretAlertRange); -+ boolean canHitCommand = control.input.commandMode && ((ArcUnits.selectedUnitsFlyer && targetAir) || (ArcUnits.selectedUnitsLand && targetGround)); -+ boolean canHitPlans = (control.input.block != null || control.input.selectPlans.size > 0) && targetGround; -+ if(canHitPlayer || (canHitMouse && (canHitCommand || canHitPlans))) drawRange(build); -+ } -+ -+ if(showTurretAmmo && build instanceof ItemTurret.ItemTurretBuild it && it.ammo.any()){ -+ //lc参考miner代码 -+ ItemTurret.ItemEntry entry = (ItemTurret.ItemEntry)it.ammo.peek(); -+ Item lastAmmo = entry.item; -+ -+ Draw.z(Layer.turret + 0.1f); -+ -+ float size = Math.max(4f, build.block.size * tilesize / 2.5f); -+ float ammoX = build.x - (build.block.size * tilesize / 2.0F) + (size / 2); -+ float ammoY = build.y - (build.block.size * tilesize / 2.0F) + (size / 2); -+ -+ Draw.rect(lastAmmo.fullIcon, ammoX, ammoY, size, size); -+ -+ float leftAmmo = Mathf.lerp(0, 1, Math.min(1f, (float)entry.amount / ((ItemTurret)it.block).maxAmmo)); -+ if(leftAmmo < 0.75f && showTurretAmmoAmount){ -+ Draw.alpha(0.5f); -+ Draw.color(lastAmmo.color); -+ Lines.stroke(Lines.getStroke() * build.block.size * 0.5f); -+ Lines.arc(ammoX, ammoY, size * 0.5f, leftAmmo); -+ } -+ -+ Draw.reset(); -+ } -+ if(targetPos.x != 0 && targetPos.y != 0 && blockWeaponTargetLine && Mathf.len(targetPos.x - build.x, targetPos.y - build.y) <= 1500f){ -+ if(!(build instanceof Turret.TurretBuild) || ((Turret.TurretBuild)build).isShooting() || ((Turret.TurretBuild)build).isControlled()){ -+ Draw.color(1f, 0.2f, 0.2f, 0.8f); -+ Lines.stroke(1.5f); -+ Lines.line(build.x, build.y, targetPos.x, targetPos.y); -+ Lines.dashCircle(targetPos.x, targetPos.y, 8); -+ }else if(blockWeaponTargetLineWhenIdle){ -+ Draw.color(1f, 1f, 1f, 0.3f); -+ Lines.stroke(1.5f); -+ Lines.line(build.x, build.y, targetPos.x, targetPos.y); -+ Lines.dashCircle(targetPos.x, targetPos.y, 8); -+ } -+ } -+ } -+ } -+ -+ public static void turretPlaceDraw(float x, float y, BaseTurret block){ -+ float oldZ = Draw.z(); -+ Draw.z(oldZ+0.1f);//MDTX: There no replace for Icon.power, so we offset the layer. -+ float iconSize = 6f + 2f * block.size, range = block.range; -+ ObjectMap ammoTypes; -+ if(block instanceof ContinuousLiquidTurret t){ -+ ammoTypes = t.ammoTypes; -+ }else if(block instanceof LiquidTurret t){ -+ ammoTypes = t.ammoTypes; -+ }else if(block instanceof ItemTurret t){ -+ ammoTypes = t.ammoTypes; -+ }else if(block instanceof PowerTurret){ -+ turretBulletDraw(x, y, Icon.power.getRegion(), iconSize, range, 0f); -+ return; -+ }else return; -+ -+ int drawIndex = 0; -+ for(var e : ammoTypes.entries()){ -+ var item = e.key; -+ var bulletType = e.value; -+ drawIndex += 1; -+ if(!item.unlockedNow()) return; -+ if(bulletType.rangeChange > 0) Drawf.dashCircle(x, y, range + bulletType.rangeChange, Pal.placing); -+ turretBulletDraw(x, y, item.uiIcon, iconSize, range + bulletType.rangeChange, (float)drawIndex / ammoTypes.size); -+ } -+ Draw.z(oldZ); -+ } -+ -+ private static void turretBulletDraw(float x, float y, TextureRegion icon, float iconSize, float range, float rotOffset){ -+ for(int i = 0; i < 4; i++){ -+ float rot = (i + rotOffset) * 90f + Time.time * 0.5f; -+ Draw.rect(icon, -+ x + (Mathf.sin((float)Math.toRadians(rot)) * (range )), -+ y + (Mathf.cos((float)Math.toRadians(rot)) * (range )), -+ iconSize, iconSize, -rot); -+ } -+ } -+} -diff --git a/core/src/mindustryX/features/ArcOld.java b/core/src/mindustryX/features/ArcOld.java -index c2e398bcb71566cc3f3aaa4a081174c19df9596a..98fa331ff11da0821e4ca81f61a37fefe60b2da2 100644 ---- a/core/src/mindustryX/features/ArcOld.java -+++ b/core/src/mindustryX/features/ArcOld.java -@@ -1,15 +1,20 @@ - package mindustryX.features; - -+import arc.*; - import arc.func.*; - import arc.graphics.*; -+import arc.graphics.g2d.*; - import arc.struct.*; - import arc.util.*; -+import mindustry.ctype.*; - import mindustry.gen.*; - import mindustry.graphics.*; -+import mindustry.world.*; - import mindustryX.features.Settings.*; - import mindustryX.features.ui.*; - - import static arc.Core.settings; -+import static arc.graphics.Color.RGBtoHSV; - import static mindustry.Vars.*; - - public class ArcOld{ -@@ -226,4 +231,41 @@ public class ArcOld{ - c.checkPref("developMode", false); - })); - } -+ -+ public static void colorizeContent(){ -+ if(!settings.getBool("colorizedContent")) return; -+ content.items().each(c -> colorizeContent(c, c.color)); -+ content.liquids().each(c -> colorizeContent(c, c.color)); -+ content.statusEffects().each(c -> colorizeContent(c, c.color)); -+ content.planets().each(c -> colorizeContent(c, c.atmosphereColor)); -+ content.blocks().each(c -> { -+ if(c.hasColor) colorizeContent(c, blockColor(c)); -+ else if(c.itemDrop != null) colorizeContent(c, c.itemDrop.color); -+ }); -+ } -+ -+ private static void colorizeContent(UnlockableContent c, Color color){ -+ c.localizedName = "[#" + color + "]" + c.localizedName + "[]"; -+ } -+ -+ private static Color blockColor(Block block){ -+ Color bc = new Color(0, 0, 0, 1); -+ Color bestColor = new Color(0, 0, 0, 1); -+ int highestS = 0; -+ if(!block.synthetic()){ -+ PixmapRegion image = Core.atlas.getPixmap(block.fullIcon); -+ for(int x = 0; x < image.width; x++) -+ for(int y = 0; y < image.height; y++){ -+ bc.set(image.get(x, y)); -+ int s = RGBtoHSV(bc)[1] * RGBtoHSV(bc)[1] + RGBtoHSV(bc)[2] + RGBtoHSV(bc)[2]; -+ if(s > highestS){ -+ highestS = s; -+ bestColor = bc; -+ } -+ } -+ }else{ -+ return block.mapColor.cpy().mul(1.2f); -+ } -+ return bestColor; -+ } - } -diff --git a/core/src/mindustryX/features/ArcRadar.java b/core/src/mindustryX/features/ArcRadar.java -new file mode 100644 -index 0000000000000000000000000000000000000000..21abee2343532b732130d2209137a6c064d8ea70 ---- /dev/null -+++ b/core/src/mindustryX/features/ArcRadar.java -@@ -0,0 +1,239 @@ -+package mindustryX.features; -+ -+import arc.*; -+import arc.graphics.*; -+import arc.graphics.g2d.*; -+import arc.math.*; -+import arc.scene.event.*; -+import arc.scene.ui.layout.*; -+import arc.util.*; -+import mindustry.game.*; -+import mindustry.gen.*; -+import mindustry.graphics.*; -+import mindustry.input.*; -+import mindustry.ui.*; -+import mindustry.world.*; -+import mindustry.world.blocks.storage.*; -+import mindustryX.features.func.*; -+ -+import static mindustry.Vars.*; -+ -+//move from mindustry.arcModule.toolpack.arcScanner -+public class ArcRadar{ -+ /** 基础缩放倍率,最重要的参数 */ -+ private static final float ratio = 10f; -+ private static final float unitSize = 0.1f; -+ private static final float markerSize = 15f * tilesize; -+ /** 范围倍率 */ -+ private static final int basicRadarCir = 25; -+ private static final Table t = new Table(Styles.black3); -+ public static boolean mobileRadar = false; -+ /** 真实大小 */ -+ private static float rRatio; -+ private static float rMarkerSize; -+ /** 每多少范围一个雷达圈 */ -+ private static float radarCir = 25f; -+ /** 默认扫描时间,仅用于特效 */ -+ private static float scanTime = 5; -+ /** 当前扫描的百分比 */ -+ private static float scanRate = 0; -+ /** 扫描线旋转倍率 */ -+ private static final float scanSpeed = -0.02f; -+ /** 实际扫描范围,不是参数 */ -+ private static float curScanRange = 0; -+ private static float expandRate = 1f; -+ private static float time = 0; -+ -+ static{ -+ t.touchable = Touchable.disabled; -+ t.margin(8f).add(">> 雷达扫描中 <<").color(Pal.accent).style(Styles.outlineLabel).labelAlign(Align.center); -+ t.visible = false; -+ t.update(() -> t.setPosition(Core.graphics.getWidth() / 2f, Core.graphics.getHeight() * 0.1f, Align.center)); -+ t.pack(); -+ t.act(0.1f); -+ t.update(() -> t.visible = t.visible && state.isPlaying()); -+ -+ Core.scene.add(t); -+ -+ Events.on(EventType.WorldLoadEvent.class, event -> scanTime = Math.max(Mathf.dst(world.width(), world.height()) / 20f, 7.5f)); -+ } -+ -+ public static void drawScanner(){ -+ if(Core.settings.getInt("radarMode") == 0) return; -+ float extendSpd = Core.settings.getInt("radarMode") * 0.2f; -+ Draw.reset(); -+ -+ if(mobile){ -+ if(extendSpd >= 6){ -+ t.visible = mobileRadar; -+ scanRate = t.visible ? 1f : 0f; -+ }else{ -+ if(mobileRadar){ -+ t.visible = true; -+ if(scanRate < 1f) scanRate = Math.min(scanRate + 1 / 60f / scanTime * extendSpd, 1f); -+ }else{ -+ t.visible = false; -+ if(scanRate > 0f) scanRate = Math.max(scanRate - 3 / 60f / scanTime * extendSpd, 0f); -+ } -+ } -+ }else{ -+ if(extendSpd >= 6){ -+ if(Core.input.keyDown(Binding.arcDetail) && Time.time - time > 60f){ -+ time = Time.time; -+ t.visible = !t.visible; -+ scanRate = t.visible ? 1f : 0f; -+ } -+ }else{ -+ if(Core.input.keyDown(Binding.arcDetail)){ -+ t.visible = true; -+ if(scanRate < 1f) scanRate = Math.min(scanRate + 1 / 60f / scanTime * extendSpd, 1f); -+ }else{ -+ t.visible = false; -+ if(scanRate > 0f) scanRate = Math.max(scanRate - 3 / 60f / scanTime * extendSpd, 0f); -+ } -+ } -+ } -+ -+ if(scanRate <= 0) return; -+ -+ float playerToBorder = Math.max(Math.max(Math.max(Mathf.dst(player.tileX(), player.tileY()), Mathf.dst(world.width() - player.tileX(), player.tileY())), Mathf.dst(world.width() - player.tileX(), world.height() - player.tileY())), Mathf.dst(player.tileX(), world.height() - player.tileY())); -+ float worldSize = Math.min(playerToBorder, (int)(Mathf.dst(world.width(), world.height()) / radarCir) * radarCir); -+ -+ float playerSize = Math.min(world.width(), world.height()) * tilesize * 0.03f; -+ -+ /* 整体缩放倍率,最重要的可调参数 */ -+ float sizeRate = Core.settings.getInt("radarSize") == 0 ? 1f : Core.settings.getInt("radarSize") * 0.1f / renderer.getScale(); -+ sizeRate *= Math.min(Core.scene.getHeight() / (world.height() * tilesize), Core.scene.getWidth() / (world.width() * tilesize)) * 2f; -+ rRatio = ratio / sizeRate; -+ float rUnitSize = unitSize * sizeRate; -+ rMarkerSize = markerSize * sizeRate; -+ -+ -+ expandRate = worldSize / basicRadarCir / 10 + 1; -+ radarCir = (int)expandRate * basicRadarCir; //地图越大,radar间隔越大。此处选择最多10圈 -+ curScanRange = worldSize * tilesize * scanRate; -+ -+ expandRate *= sizeRate; -+ -+ for(int i = 1; i < curScanRange / radarCir / tilesize + 1; i++){ -+ Draw.color(player.team().color, 0.45f); -+ Lines.stroke(expandRate * 0.75f); -+ Lines.circle(player.x, player.y, (radarCir * i * tilesize) / rRatio); -+ float cirRatio = (radarCir * i * tilesize) / rRatio + 2f; -+ FuncX.drawText(Tmp.v1.trns(30, cirRatio).add(player), i * (int)radarCir + "", 1.25f * expandRate, Pal.accent); -+ FuncX.drawText(Tmp.v1.trns(150, cirRatio).add(player), i * (int)radarCir + "", 1.25f * expandRate, Pal.accent); -+ FuncX.drawText(Tmp.v1.trns(270, cirRatio).add(player), i * (int)radarCir + "", 1.25f * expandRate, Pal.accent); -+ } -+ -+ if(scanRate < 1f){ -+ Draw.color(player.team().color, 0.8f); -+ Lines.stroke(expandRate); -+ Lines.circle(player.x, player.y, curScanRange / rRatio); -+ Draw.color(player.team().color, 0.1f); -+ Fill.circle(player.x, player.y, curScanRange / rRatio); -+ }else{ -+ curScanRange = (int)(curScanRange / radarCir / tilesize + 1) * radarCir * tilesize; -+ -+ Draw.color(player.team().color, 0.1f); -+ Fill.circle(player.x, player.y, curScanRange / rRatio); -+ -+ Draw.color(player.team().color, 0.6f); -+ float curve = Mathf.curve(Time.time % 360f, 120f, 360f); -+ Lines.stroke(expandRate * 1.5f); -+ Lines.circle(player.x, player.y, curScanRange / rRatio); -+ Lines.stroke(expandRate * 1.5f); -+ Lines.circle(player.x, player.y, curScanRange * Interp.pow3Out.apply(curve) / rRatio); -+ Lines.stroke(expandRate * 1.5f); -+ -+ Draw.color(player.team().color, 0.1f); -+ Fill.rect(player.x - player.x / rRatio + world.width() * tilesize / rRatio / 2, player.y - player.y / rRatio + world.height() * tilesize / rRatio / 2, world.width() * tilesize / rRatio, world.height() * tilesize / rRatio); -+ Draw.color(player.team().color, 0.85f); -+ Lines.rect(player.x - player.x / rRatio, player.y - player.y / rRatio, world.width() * tilesize / rRatio, world.height() * tilesize / rRatio); -+ } -+ -+ Draw.color(player.team().color, 0.8f); -+ Lines.line(player.x, player.y, player.x + curScanRange * Mathf.cos(Time.time * scanSpeed) / rRatio, player.y + curScanRange * Mathf.sin(Time.time * scanSpeed) / rRatio); -+ Draw.reset(); -+ -+ // 出怪点 -+ if(spawner.countSpawns() < 25 && !state.rules.pvp){ -+ for(Tile tile : spawner.getSpawns()){ -+ if(scanRate < 1f && Mathf.dst(tile.worldx() - player.x, tile.worldy() - player.y) > curScanRange) -+ continue; -+ -+ Draw.color(state.rules.waveTeam.color, 1f); -+ arcDrawNearby(Icon.units.getRegion(), tile, Math.max(6 * expandRate, state.rules.dropZoneRadius / rRatio / 2), state.rules.waveTeam.color); -+ -+ float curve = Mathf.curve(Time.time % 200f, 60f, 200f); -+ Draw.color(state.rules.waveTeam.color, 1f); -+ Lines.stroke(expandRate); -+ Lines.circle(transX(tile.worldx()), transY(tile.worldy()), state.rules.dropZoneRadius * Interp.pow3Out.apply(curve) / rRatio); -+ Draw.color(state.rules.waveTeam.color, 0.5f); -+ Lines.stroke(expandRate * 0.8f); -+ Lines.dashCircle(transX(tile.worldx()), transY(tile.worldy()), state.rules.dropZoneRadius / rRatio); -+ } -+ } -+ //绘制核心 -+ for(Team team : Team.all){ -+ for(CoreBlock.CoreBuild core : team.cores()){ -+ if(state.rules.pvp && core.inFogTo(player.team())) continue; -+ if(scanRate < 1f && Mathf.dst(core.x - player.x, core.y - player.y) > curScanRange) continue; -+ Draw.color(core.team.color, 1f); -+ Draw.rect(core.block.fullIcon, transX(core.tile.worldx()), transY(core.tile.worldy()), 4 * expandRate, 4 * expandRate); -+ -+ } -+ } -+ //绘制搜索的方块 -+ for(Building build : UIExt.advanceBuildTool.buildingSeq){ -+ if(scanRate < 1f && Mathf.dst(build.x - player.x, build.y - player.y) > curScanRange) continue; -+ Draw.color(build.team.color, 1f); -+ Draw.rect(build.block.fullIcon, transX(build.tile.worldx()), transY(build.tile.worldy()), 4 * expandRate, 4 * expandRate); -+ } -+ //绘制单位 -+ for(Unit unit : Groups.unit){ -+ if(scanRate < 1f && Mathf.dst(unit.x - player.x, unit.y - player.y) > curScanRange) continue; -+ Draw.color(unit.team.color, 0.6f); -+ Fill.circle(transX(unit.x), transY(unit.y), unit.hitSize * rUnitSize); -+ } -+ //绘制玩家 -+ for(Player unit : Groups.player){ -+ if(player.dead() || player.unit().health <= 0) continue; -+ if(scanRate < 1f && Mathf.dst(unit.x - player.x, unit.y - player.y) > curScanRange) continue; -+ -+ Draw.color(unit.team().color, 0.9f); -+ -+ float angle = unit.unit().rotation * Mathf.degreesToRadians; -+ Fill.tri(transX(unit.x + Mathf.cos(angle) * playerSize), transY(unit.y + Mathf.sin(angle) * playerSize), -+ transX(unit.x + Mathf.cos(angle + Mathf.PI * 2 / 3) * playerSize * 0.75f), transY(unit.y + Mathf.sin(angle + Mathf.PI * 2 / 3) * playerSize * 0.75f), -+ transX(unit.x + Mathf.cos(angle + Mathf.PI * 4 / 3) * playerSize * 0.75f), transY(unit.y + Mathf.sin(angle + Mathf.PI * 4 / 3) * playerSize * 0.75f)); -+ } -+ //绘制arc标记 -+ MarkerType.eachActive(a -> { -+ Draw.color(a.color); -+ Lines.stroke(expandRate * (1 - (Time.time % 180 + 30) / 210)); -+ -+ Lines.circle(transX(a.x), transY(a.y), rMarkerSize / rRatio * (Time.time % 180) / 180); -+ Lines.stroke(expandRate); -+ Lines.circle(transX(a.x), transY(a.y), rMarkerSize / rRatio); -+ Lines.arc(transX(a.x), transY(a.y), (rMarkerSize - expandRate) / rRatio, 1 - (Time.time - a.time) / MarkerType.retainTime); -+ Draw.reset(); -+ }); -+ } -+ -+ public static void arcDrawNearby(TextureRegion region, Tile tile, float size, Color color){ -+ float range = Mathf.dst(tile.worldy() - player.y, tile.worldx() - player.x); -+ if(range > curScanRange) return; -+ float nx = player.x + (tile.worldx() - player.x) / rRatio; -+ float ny = player.y + (tile.worldy() - player.y) / rRatio; -+ Draw.rect(region, nx, ny, size, size); -+ } -+ -+ private static float transX(float x){ -+ return player.x + (x - player.x) / rRatio; -+ } -+ -+ private static float transY(float y){ -+ return player.y + (y - player.y) / rRatio; -+ } -+ -+} -diff --git a/core/src/mindustryX/features/ArcUnits.java b/core/src/mindustryX/features/ArcUnits.java -new file mode 100644 -index 0000000000000000000000000000000000000000..396a0f12c9ab1aa1beec1abd18b42f0a9212e9f4 ---- /dev/null -+++ b/core/src/mindustryX/features/ArcUnits.java -@@ -0,0 +1,349 @@ -+package mindustryX.features; -+ -+import arc.*; -+import arc.graphics.*; -+import arc.graphics.g2d.*; -+import arc.math.*; -+import arc.util.*; -+import mindustry.ai.types.*; -+import mindustry.entities.units.*; -+import mindustry.game.*; -+import mindustry.gen.*; -+import mindustry.graphics.*; -+import mindustry.world.blocks.payloads.*; -+ -+import static arc.graphics.g2d.Draw.color; -+import static arc.graphics.g2d.Lines.*; -+import static mindustry.Vars.*; -+ -+//move from mindustry.arcModule.draw.ARCUnits -+public class ArcUnits{ -+ private static final int maxBuildPlans = 100; -+ private static boolean alwaysShowPlayerUnit, alwaysShowUnitRTSAi, unitHealthBar, unitLogicMoveLine, unitLogicTimerBars, unithitbox, unitBuildPlan; -+ private static float defaultUnitTrans, unitDrawMinHealth, unitBarDrawMinHealth; -+ private static float unitWeaponRange, unitWeaponRangeAlpha; -+ public static boolean selectedUnitsFlyer, selectedUnitsLand; -+ public static boolean unitWeaponTargetLine, unitItemCarried; -+ -+ private static float curStroke; -+ private static int unitTargetType, superUnitEffect; -+ private static boolean arcBuildInfo; -+ -+ static{ -+ // 减少性能开销 -+ Events.run(EventType.Trigger.update, () -> { -+ alwaysShowPlayerUnit = Core.settings.getBool("alwaysShowPlayerUnit"); -+ alwaysShowUnitRTSAi = Core.settings.getBool("alwaysShowUnitRTSAi"); -+ unitHealthBar = Core.settings.getBool("unitHealthBar"); -+ unitLogicMoveLine = Core.settings.getBool("unitLogicMoveLine"); -+ unitLogicTimerBars = Core.settings.getBool("unitLogicTimerBars"); -+ unithitbox = Core.settings.getBool("unithitbox"); -+ unitBuildPlan = Core.settings.getBool("unitbuildplan"); -+ -+ defaultUnitTrans = RenderExt.unitHide ? 0 : Core.settings.getInt("unitTransparency") / 100f; -+ unitDrawMinHealth = Core.settings.getInt("unitDrawMinHealth"); -+ unitBarDrawMinHealth = Core.settings.getInt("unitBarDrawMinHealth"); -+ -+ unitWeaponRange = Core.settings.getInt("unitWeaponRange") * tilesize; -+ unitWeaponRangeAlpha = Core.settings.getInt("unitWeaponRangeAlpha") / 100f; -+ -+ selectedUnitsFlyer = control.input.selectedUnits.contains(Flyingc::isFlying); -+ selectedUnitsLand = control.input.selectedUnits.contains(unit -> !unit.isFlying()); -+ -+ curStroke = (float)Core.settings.getInt("playerEffectCurStroke") / 10f; -+ unitTargetType = Core.settings.getInt("unitTargetType"); -+ superUnitEffect = Core.settings.getInt("superUnitEffect"); -+ arcBuildInfo = Core.settings.getBool("arcBuildInfo"); -+ -+ unitWeaponTargetLine = Core.settings.getBool("unitWeaponTargetLine"); -+ unitItemCarried = Core.settings.getBool("unitItemCarried"); -+ }); -+ } -+ -+ public static float drawARCUnits(Unit unit){ -+ if(unit.controller() instanceof Player){ -+ drawPlayerEffect(unit); -+ if(alwaysShowPlayerUnit){ -+ drawUnitBar(unit); -+ return 1f; -+ } -+ } -+ if(defaultUnitTrans == 0 || (unit.maxHealth + unit.shield) < unitDrawMinHealth) return 0f; -+ if((unit.maxHealth + unit.shield) >= unitBarDrawMinHealth) drawUnitBar(unit); -+ return defaultUnitTrans; -+ } -+ -+ private static void drawUnitBar(Unit unit){ -+ Draw.z(Draw.z() + 0.1f); -+ if(unit.team() == player.team() || RenderExt.showOtherInfo){ -+ drawWeaponRange(unit); -+ drawRTSAI(unit); -+ drawHealthBar(unit); -+ drawLogic(unit); -+ drawBuildPlan(unit); -+ } -+ drawHitBox(unit); -+ } -+ -+ private static void drawPlayerEffect(Unit unit){ -+ Color effectColor = unit.controller() == player ? RenderExt.playerEffectColor : unit.team.color; -+ -+ boolean drawCircle = (unit.controller() == player && superUnitEffect != 0) || (unit.controller() instanceof Player && superUnitEffect == 2); -+ if(drawCircle){ -+ // 射程圈 -+ Lines.stroke(Lines.getStroke() * curStroke); -+ -+ Draw.z(Layer.effect - 2f); -+ Draw.color(effectColor); -+ -+ Tmp.v1.trns(unit.rotation - 90, unit.x, unit.y).add(unit.x, unit.y); -+ -+ if(curStroke > 0){ -+ for(int i = 0; i < 5; i++){ -+ float rot = unit.rotation + i * 360f / 5 + Time.time * 0.5f; -+ Lines.arc(unit.x, unit.y, unit.type.maxRange, 0.14f, rot, (int)(50 + unit.type.maxRange / 10)); -+ } -+ } -+ } -+ // 武器圈 -+ if(unitTargetType > 0){ -+ Draw.z(Layer.effect); -+ Draw.color(effectColor, 0.8f); -+ Lines.stroke(1f); -+ Lines.line(unit.x, unit.y, unit.aimX, unit.aimY); -+ switch(unitTargetType){ -+ case 1: -+ Lines.dashCircle(unit.aimX, unit.aimY, 8); -+ break; -+ case 2: -+ Drawf.target(unit.aimX, unit.aimY, 6f, 0.7f, effectColor); -+ break; -+ case 3: -+ Drawf.target2(unit.aimX, unit.aimY, 6f, 0.7f, effectColor); -+ break; -+ case 4: -+ Drawf.targetc(unit.aimX, unit.aimY, 6f, 0.7f, effectColor); -+ break; -+ case 5: -+ Drawf.targetd(unit.aimX, unit.aimY, 6f, 0.7f, effectColor); -+ break; -+ } -+ } -+ -+ //玩家专属特效 -+ if(unit.controller() == player){ -+ detailBuildMode(); -+ } -+ } -+ -+ private static void drawWeaponRange(Unit unit){ -+ if(unitWeaponRange == 0 || unitWeaponRangeAlpha == 0) return; -+ if(unitWeaponRange == 30){ -+ drawWeaponRange(unit, unitWeaponRangeAlpha); -+ }else if(unit.team != player.team()){ -+ boolean canHitPlayer = !player.dead() && player.unit().hittable() && (player.unit().isFlying() ? unit.type.targetAir : unit.type.targetGround) -+ && unit.within(player.unit().x, player.unit().y, unit.type.maxRange + unitWeaponRange); -+ boolean canHitCommand = control.input.commandMode && ((selectedUnitsFlyer && unit.type.targetAir) || (selectedUnitsLand && unit.type.targetGround)); -+ boolean canHitPlans = (control.input.block != null || control.input.selectPlans.size > 0) && unit.type.targetGround; -+ boolean canHitMouse = unit.within(Core.input.mouseWorldX(), Core.input.mouseWorldY(), unit.type.maxRange + unitWeaponRange); -+ if(canHitPlayer || (canHitMouse && (canHitCommand || canHitPlans))) -+ drawWeaponRange(unit, unitWeaponRangeAlpha); -+ } -+ } -+ -+ private static void drawWeaponRange(Unit unit, float alpha){ -+ Draw.color(unit.team.color); -+ Draw.alpha(alpha); -+ Lines.dashCircle(unit.x, unit.y, unit.type.maxRange); -+ Draw.reset(); -+ } -+ -+ private static void drawRTSAI(Unit unit){ -+ if(!control.input.commandMode && alwaysShowUnitRTSAi && unit.isCommandable() && unit.command().command != null){ -+ Draw.z(Layer.effect); -+ CommandAI ai = unit.command(); -+ if(ai.attackTarget != null){ -+ Draw.color(unit.team.color); -+ if(ai.targetPos != null) -+ Drawf.limitLineColor(unit, ai.attackTarget, unit.hitSize / 2f, 3.5f, unit.team.color); -+ Drawf.target(ai.attackTarget.getX(), ai.attackTarget.getY(), 6f, unit.team.color); -+ }else if(ai.targetPos != null){ -+ Draw.color(unit.team.color); -+ Drawf.limitLineColor(unit, ai.targetPos, unit.hitSize / 2f, 3.5f, unit.team.color); -+ Draw.color(unit.team.color); -+ Drawf.square(ai.targetPos.getX(), ai.targetPos.getY(), 3.5f, unit.team.color); -+ } -+ Draw.reset(); -+ } -+ } -+ -+ private static void drawHealthBar(Unit unit){ -+ if(!unitHealthBar) return; -+ Draw.z(Layer.shields + 6f); -+ float y_corr = 0f; -+ if(unit.hitSize < 30f && unit.hitSize > 20f && unit.controller().isBeingControlled(player.unit())) y_corr = 2f; -+ if(unit.health < unit.maxHealth){ -+ Draw.reset(); -+ Lines.stroke(4f); -+ Draw.color(unit.team.color, 0.5f); -+ Lines.line(unit.x - unit.hitSize() * 0.6f, unit.y + (unit.hitSize() / 2f) + y_corr, unit.x + unit.hitSize() * 0.6f, unit.y + (unit.hitSize() / 2f) + y_corr); -+ Lines.stroke(2f); -+ Draw.color(Pal.health, 0.8f); -+ Lines.line( -+ unit.x - unit.hitSize() * 0.6f, unit.y + (unit.hitSize() / 2f) + y_corr, -+ unit.x + unit.hitSize() * (Math.min(Mathf.maxZero(unit.health), unit.maxHealth) * 1.2f / unit.maxHealth - 0.6f), unit.y + (unit.hitSize() / 2f) + y_corr); -+ Lines.stroke(2f); -+ } -+ if(unit.shield > 0 && unit.shield < 1e20){ -+ for(int didgt = 1; didgt <= Mathf.digits((int)(unit.shield / unit.maxHealth)) + 1; didgt++){ -+ Draw.color(Pal.shield, 0.8f); -+ float shieldAmountScale = unit.shield / (unit.maxHealth * Mathf.pow(10f, (float)didgt - 1f)); -+ if(didgt > 1){ -+ Lines.line(unit.x - unit.hitSize() * 0.6f, -+ unit.y + (unit.hitSize() / 2f) + (float)didgt * 2f + y_corr, -+ unit.x + unit.hitSize() * ((Mathf.ceil((shieldAmountScale - Mathf.floor(shieldAmountScale)) * 10f) - 1f + 0.0001f) * 1.2f * (1f / 9f) - 0.6f), -+ unit.y + (unit.hitSize() / 2f) + (float)didgt * 2f + y_corr); -+ //(s-1)*(1/9)because line(0) will draw length of 1 -+ }else{ -+ Lines.line(unit.x - unit.hitSize() * 0.6f, -+ unit.y + (unit.hitSize() / 2f) + (float)didgt * 2f + y_corr, -+ unit.x + unit.hitSize() * ((shieldAmountScale - Mathf.floor(shieldAmountScale) - 0.001f) * 1.2f - 0.6f), -+ unit.y + (unit.hitSize() / 2f) + (float)didgt * 2f + y_corr); -+ } -+ } -+ } -+ Draw.reset(); -+ -+ float oldZ = Draw.z(); -+ Draw.z(oldZ+0.1f);//MDTX: There no replace for effect.uiIcon, so we offset the layer. -+ float index = 0f; -+ float iconSize = 4f; -+ int iconColumns = Math.max((int)(unit.hitSize() / (iconSize + 1f)), 4); -+ float iconWidth = Math.min(unit.hitSize() / iconColumns, iconSize + 1f); -+ for(var entry : unit.statuses()){ -+ Draw.rect(entry.effect.uiIcon, -+ unit.x - unit.hitSize() * 0.6f + iconWidth * (index % iconColumns), -+ unit.y + (unit.hitSize() / 2f) + 3f + iconSize * Mathf.floor(index / iconColumns), -+ iconSize, iconSize); -+ index++; -+ } -+ Draw.z(oldZ); -+ -+ index = 0f; -+ if(unit instanceof Payloadc payload && payload.payloads().any()){ -+ for(Payload p : payload.payloads()){ -+ Draw.rect(p.content().fullIcon, -+ unit.x - unit.hitSize() * 0.6f + 0.5f * iconSize * index, -+ unit.y + (unit.hitSize() / 2f) - 4f, -+ 4f, 4f); -+ index++; -+ } -+ } -+ Draw.reset(); -+ } -+ -+ private static void drawLogic(Unit unit){ -+ if(unit.controller() instanceof LogicAI logicai){ -+ if(unitLogicMoveLine && Mathf.len(logicai.moveX - unit.x, logicai.moveY - unit.y) <= 1200f){ -+ Lines.stroke(1f); -+ Draw.color(0.2f, 0.2f, 1f, 0.9f); -+ Lines.dashLine(unit.x, unit.y, logicai.moveX, logicai.moveY, (int)(Mathf.len(logicai.moveX - unit.x, logicai.moveY - unit.y) / 8)); -+ Lines.dashCircle(logicai.moveX, logicai.moveY, logicai.moveRad); -+ Draw.reset(); -+ } -+ if(unitLogicTimerBars){ -+ Lines.stroke(2f); -+ Draw.color(Pal.heal); -+ Lines.line(unit.x - (unit.hitSize() / 2f), unit.y - (unit.hitSize() / 2f), unit.x - (unit.hitSize() / 2f), unit.y + unit.hitSize() * (logicai.controlTimer / LogicAI.logicControlTimeout - 0.5f)); -+ Draw.reset(); -+ } -+ } -+ } -+ -+ private static void drawBuildPlan(Unit unit){ -+ if(unitBuildPlan && !unit.plans().isEmpty()){ -+ int counter = 0; -+ if(unit != player.unit()){ -+ for(BuildPlan b : unit.plans()){ -+ unit.drawPlan(b, 0.5f); -+ counter += 1; -+ if(counter >= maxBuildPlans) break; -+ } -+ } -+ counter = 0; -+ Draw.color(Pal.gray); -+ Lines.stroke(2f); -+ float x = unit.x, y = unit.y, s = unit.hitSize / 2f; -+ for(BuildPlan b : unit.plans()){ -+ Tmp.v2.trns(Angles.angle(x, y, b.drawx(), b.drawy()), s); -+ Tmp.v3.trns(Angles.angle(x, y, b.drawx(), b.drawy()), b.block.size * 2f); -+ Lines.circle(b.drawx(), b.drawy(), b.block.size * 2f); -+ Lines.line(x + Tmp.v2.x, y + Tmp.v2.y, b.drawx() - Tmp.v3.x, b.drawy() - Tmp.v3.y); -+ x = b.drawx(); -+ y = b.drawy(); -+ s = b.block.size * 2f; -+ counter += 1; -+ if(counter >= maxBuildPlans) break; -+ } -+ -+ counter = 0; -+ Draw.color(unit.team.color); -+ Lines.stroke(0.75f); -+ x = unit.x; -+ y = unit.y; -+ s = unit.hitSize / 2f; -+ for(BuildPlan b : unit.plans()){ -+ Tmp.v2.trns(Angles.angle(x, y, b.drawx(), b.drawy()), s); -+ Tmp.v3.trns(Angles.angle(x, y, b.drawx(), b.drawy()), b.block.size * 2f); -+ Lines.circle(b.drawx(), b.drawy(), b.block.size * 2f); -+ Draw.color(unit.team.color); -+ Lines.line(x + Tmp.v2.x, y + Tmp.v2.y, b.drawx() - Tmp.v3.x, b.drawy() - Tmp.v3.y); -+ x = b.drawx(); -+ y = b.drawy(); -+ s = b.block.size * 2f; -+ counter += 1; -+ if(counter >= maxBuildPlans) break; -+ } -+ Draw.reset(); -+ } -+ } -+ -+ private static void drawHitBox(Unit unit){ -+ if(unithitbox){ -+ Draw.color(unit.team.color, 0.5f); -+ Lines.circle(unit.x, unit.y, unit.hitSize / 2f); -+ Draw.reset(); -+ } -+ } -+ -+ private static void detailBuildMode(){ -+ if(!arcBuildInfo) return; -+ if(control.input.droppingItem){ -+ Color color = player.within(Core.input.mouseWorld(control.input.getMouseX(), control.input.getMouseY()), itemTransferRange) ? Color.gold : Color.red; -+ drawNSideRegion(player.unit().x, player.unit().y, 3, player.unit().type.buildRange, player.unit().rotation, color, 0.25f, player.unit().stack.item.fullIcon, false); -+ }else if(control.input.isBuilding || control.input.selectedBlock() || !player.unit().plans().isEmpty()){ -+ drawNSideRegion(player.unit().x, player.unit().y, 3, player.unit().type.buildRange, player.unit().rotation, Pal.heal, 0.25f, Icon.wrench.getRegion(), true); -+ } -+ } -+ -+ public static void drawNSideRegion(float x, float y, int n, float range, float rotation, Color color, float fraction, TextureRegion region, boolean regionColor){ -+ Draw.z(Layer.effect - 2f); -+ color(color); -+ -+ stroke(2f); -+ -+ for(int i = 0; i < n; i++){ -+ float frac = 360f * (1 - fraction * n) / n / 2; -+ float rot = rotation + i * 360f / n + frac; -+ if(!regionColor){ -+ color(color); -+ arc(x, y, range, 0.25f, rot, (int)(50 + range / 10)); -+ color(); -+ }else{ -+ arc(x, y, range, 0.25f, rot, (int)(50 + range / 10)); -+ } -+ Draw.rect(region, x + range * Mathf.cos((float)Math.toRadians(rot - frac)), y + range * Mathf.sin((float)Math.toRadians(rot - frac)), 12f, 12f); -+ } -+ Draw.reset(); -+ } -+} -diff --git a/core/src/mindustryX/features/ArcWaveSpawner.java b/core/src/mindustryX/features/ArcWaveSpawner.java -new file mode 100644 -index 0000000000000000000000000000000000000000..ef4e313c601ad1ce64355761f420ef4173e5f766 ---- /dev/null -+++ b/core/src/mindustryX/features/ArcWaveSpawner.java -@@ -0,0 +1,258 @@ -+package mindustryX.features; -+ -+import arc.*; -+import arc.func.*; -+import arc.graphics.*; -+import arc.graphics.g2d.*; -+import arc.math.*; -+import arc.scene.ui.layout.*; -+import arc.struct.*; -+import arc.util.*; -+import mindustry.content.*; -+import mindustry.core.*; -+import mindustry.game.*; -+import mindustry.world.*; -+import mindustryX.features.ui.*; -+ -+import static mindustry.Vars.*; -+ -+//move from mindustry.arcModule.toolpack.arcWaveSpawner -+public class ArcWaveSpawner{ -+ public static boolean hasFlyer = true; -+ -+ public static final float flyerSpawnerRadius = 5f * tilesize; -+ -+ static final float spawnerMargin = tilesize * 11f; -+ -+ public static final Seq arcWave = new Seq<>(); -+ -+ static{ -+ Events.on(EventType.WorldLoadEvent.class, event -> { -+ hasFlyer = false; -+ for(SpawnGroup sg : state.rules.spawns){ -+ if(sg.type.flying){ -+ hasFlyer = true; -+ break; -+ } -+ } -+ arcWave.clear(); -+ for(int i = 0; i <= calWinWave(); i++){ -+ arcWave.add(new waveInfo(i)); -+ } -+ }); -+ } -+ -+ public static void drawSpawner(){ -+ if(state.hasSpawns()){ -+ Lines.stroke(2f); -+ Draw.color(Color.gray, Color.lightGray, Mathf.absin(Time.time, 8f, 1f)); -+ -+ if(Core.settings.getBool("alwaysshowdropzone")){ -+ Draw.alpha(0.8f); -+ for(Tile tile : spawner.getSpawns()){ -+ arcDashCircling(tile.worldx(), tile.worldy(), state.rules.dropZoneRadius, -flyerSpawnerRadius / state.rules.dropZoneRadius * 0.1f); -+ } -+ }else{ -+ for(Tile tile : spawner.getSpawns()){ -+ if(tile.within(player.x, player.y, state.rules.dropZoneRadius + spawnerMargin)){ -+ Draw.alpha(Mathf.clamp(1f - (player.dst(tile) - state.rules.dropZoneRadius) / spawnerMargin)); -+ Lines.dashCircle(tile.worldx(), tile.worldy(), state.rules.dropZoneRadius); -+ } -+ } -+ } -+ if(hasFlyer && Core.settings.getBool("showFlyerSpawn") && spawner.countSpawns() < 20){ -+ for(Tile tile : spawner.getSpawns()){ -+ float angle = Angles.angle(world.width() / 2f, world.height() / 2f, tile.x, tile.y); -+ float trns = Math.max(world.width(), world.height()) * Mathf.sqrt2 * tilesize; -+ float spawnX = Mathf.clamp(world.width() * tilesize / 2f + Angles.trnsx(angle, trns), 0, world.width() * tilesize); -+ float spawnY = Mathf.clamp(world.height() * tilesize / 2f + Angles.trnsy(angle, trns), 0, world.height() * tilesize); -+ if(Core.settings.getBool("showFlyerSpawnLine")){ -+ Draw.color(Color.red, 0.5f); -+ Lines.line(tile.worldx(), tile.worldy(), spawnX, spawnY); -+ } -+ Draw.color(Color.gray, Color.lightGray, Mathf.absin(Time.time, 8f, 1f)); -+ Draw.alpha(0.8f); -+ arcDashCircling(spawnX, spawnY, flyerSpawnerRadius, 0.1f); -+ -+ Draw.color(); -+ Draw.alpha(0.5f); -+ Draw.rect(UnitTypes.zenith.fullIcon, spawnX, spawnY); -+ } -+ } -+ Draw.reset(); -+ } -+ } -+ -+ public static waveInfo getOrInit(int wave){ -+ while(arcWave.size <= wave) arcWave.add(new waveInfo(wave)); -+ return arcWave.get(wave); -+ } -+ -+ public static int calWinWave(){ -+ if(state.rules.winWave >= 1) return state.rules.winWave; -+ int maxwave = 0; -+ for(SpawnGroup group : state.rules.spawns){ -+ if(group.end > 99999) continue; -+ maxwave = Math.max(maxwave, group.end); -+ } -+ if(maxwave > 5000) return 200; -+ if(maxwave < 2 && state.rules.waveSpacing > 30f) return (int)(1800000 / state.rules.waveSpacing); -+ return maxwave + 1; -+ } -+ -+ public static void arcDashCircling(float x, float y, float radius, float speed){ -+ arcDashCircle(x, y, radius, Time.time * speed); -+ } -+ -+ public static void arcDashCircle(float x, float y, float radius, float rotation){ -+ float scaleFactor = 0.6f; -+ int sides = 10 + (int)(radius * scaleFactor); -+ if(sides % 2 == 1) sides++; -+ -+ for(int i = 0; i < sides; i += 2){ -+ var v = Tmp.v1; -+ v.set(radius, 0).rotate(360f / sides * i + 90 + rotation); -+ float x1 = v.x, y1 = v.y; -+ v.set(radius, 0).rotate(360f / sides * (i + 1) + 90 + rotation); -+ float x2 = v.x, y2 = v.y; -+ Lines.line(x + x1, y + y1, x + x2, y + y2); -+ } -+ } -+ -+ /** -+ * 单一波次详情 -+ */ -+ public static class waveInfo{ -+ public final int waveIndex; -+ public final Seq groups = new Seq<>(); -+ -+ public int amount = 0, amountL = 0; -+ -+ public float health = 0, effHealth = 0, dps = 0; -+ /** -+ * 临时数据记录 -+ */ -+ public long healthL = 0, effHealthL = 0, dpsL = 0; -+ -+ waveInfo(int waveIndex){ -+ this.waveIndex = waveIndex; -+ for(SpawnGroup group : state.rules.spawns){ -+ int amount = group.getSpawned(waveIndex); -+ if(amount == 0) continue; -+ groups.add(new waveGroup(waveIndex, group)); -+ } -+ initProperty(); -+ } -+ -+ private void initProperty(){ -+ groups.each(group -> { -+ amount += group.amountT; -+ health += group.healthT; -+ effHealth += group.effHealthT; -+ dps += group.dpsT; -+ }); -+ } -+ -+ public void specLoc(int spawn, Boolf pre){ -+ amountL = 0; -+ healthL = 0; -+ effHealthL = 0; -+ dpsL = 0; -+ groups.each(waveGroup -> (spawn == -1 || waveGroup.group.spawn == -1 || waveGroup.group.spawn == spawn) && pre.get(waveGroup.group), -+ group -> { -+ amountL += group.amountT; -+ healthL += group.healthT; -+ effHealthL += group.effHealthT; -+ dpsL += group.dpsT; -+ }); -+ } -+ -+ public Table proTable(boolean doesRow){ -+ if(amountL == 0) return new Table(t -> t.add("该波次没有敌人")); -+ return new Table(t -> { -+ t.add("\uE86D").width(50f); -+ t.add("[accent]" + amountL).growX().padRight(50f); -+ if(doesRow) t.row(); -+ t.add("\uE813").width(50f); -+ t.add("[accent]" + UI.formatAmount(healthL)).growX().padRight(50f); -+ if(doesRow) t.row(); -+ if(effHealthL != healthL){ -+ t.add("\uE810").width(50f); -+ t.add("[accent]" + UI.formatAmount(effHealthL)).growX().padRight(50f); -+ if(doesRow) t.row(); -+ } -+ t.add("\uE86E").width(50f); -+ t.add("[accent]" + UI.formatAmount(dpsL)).growX(); -+ }); -+ } -+ -+ public Table unitTable(int spawn, Boolf pre){ -+ return unitTable(spawn, pre, 10); -+ } -+ -+ public Table unitTable(int spawn, Boolf pre, int perCol){ -+ int[] count = new int[1]; -+ return new Table(t -> groups.each(waveGroup -> (spawn == -1 || waveGroup.group.spawn == -1 || waveGroup.group.spawn == spawn) && pre.get(waveGroup.group), wg -> { -+ count[0]++; -+ if(count[0] % perCol == 0) t.row(); -+ t.table(tt -> { -+ tt.table(ttt -> { -+ ttt.image(wg.group.type.uiIcon).size(30); -+ ttt.add(wg.group.type.typeColor() + wg.amount).fillX(); -+ }).row(); -+ StringBuilder groupInfo = new StringBuilder(); -+ if(wg.shield > 0f) -+ groupInfo.append(FormatDefault.format(wg.shield)); -+ groupInfo.append("\n[]"); -+ if(wg.group.spawn != -1 && spawn == -1) groupInfo.append("*"); -+ if(wg.group.effect != null && wg.group.effect != StatusEffects.none) -+ groupInfo.append(wg.group.effect.emoji()); -+ if(wg.group.items != null && wg.group.items.amount > 0) -+ groupInfo.append(wg.group.items.item.emoji()); -+ if(wg.group.payloads != null && wg.group.payloads.size > 0) -+ groupInfo.append("\uE87B"); -+ tt.add(groupInfo.toString()).fill(); -+ }).height(80f).width(70f); -+ -+ })); -+ } -+ -+ } -+ -+ /** -+ * 一种更为详细的spawnGroup -+ */ -+ public static class waveGroup{ -+ public final int waveIndex; -+ public final SpawnGroup group; -+ public final int amount; -+ public final int amountT; -+ public final float shield; -+ public final float health; -+ public float effHealth; -+ public float dps; -+ public final float healthT; -+ public final float effHealthT; -+ public final float dpsT; -+ -+ public waveGroup(int waveIndex, SpawnGroup group){ -+ this.waveIndex = waveIndex; -+ this.group = group; -+ this.amount = group.getSpawned(waveIndex); -+ this.shield = group.getShield(waveIndex); //盾 -+ this.health = (group.type.health + shield) * amount; //盾+血 -+ this.dps = group.type.estimateDps() * amount; -+ this.effHealth = health; -+ if(group.effect != null){ -+ this.effHealth *= group.effect.healthMultiplier; -+ this.dps *= group.effect.damageMultiplier * group.effect.reloadMultiplier; -+ } -+ -+ int multiplier = group.spawn != -1 || spawner.countSpawns() < 2 ? 1 : spawner.countSpawns(); -+ this.amountT = amount * multiplier; -+ this.healthT = health * multiplier; -+ this.effHealthT = effHealth * multiplier; -+ this.dpsT = dps * multiplier; -+ } -+ } -+} -\ No newline at end of file -diff --git a/core/src/mindustryX/features/MarkerType.java b/core/src/mindustryX/features/MarkerType.java -index 58a3232027734be1786229aa86b626c4eb18d7e7..4a3bbacbf6ed57fdb177fcae845115d39ac5c6d9 100644 ---- a/core/src/mindustryX/features/MarkerType.java -+++ b/core/src/mindustryX/features/MarkerType.java -@@ -16,6 +16,7 @@ import mindustry.gen.*; - import mindustry.graphics.*; - import mindustryX.features.func.*; - import mindustryX.features.ui.*; -+import mindustryX.features.ui.ArcMessageDialog.*; - - import java.util.regex.*; - -@@ -157,6 +158,7 @@ public class MarkerType{ - var exists = (MarkElement)Groups.draw.find(it -> it instanceof MarkElement e && e.message == null && e.within(pos.scl(tilesize), 2 * tilesize)); - last = exists != null ? exists : type.at(pos.scl(tilesize)); - last.message = text; -+ ArcMessageDialog.addMsg(new Msg(Type.markLoc, text, pos)); - return true; - } - -diff --git a/core/src/mindustryX/features/PicToMindustry.java b/core/src/mindustryX/features/PicToMindustry.java -new file mode 100644 -index 0000000000000000000000000000000000000000..b3bf32d2d3da28f6adc38c56ea3d24404ace7d26 ---- /dev/null -+++ b/core/src/mindustryX/features/PicToMindustry.java -@@ -0,0 +1,343 @@ -+package mindustryX.features; -+ -+import arc.*; -+import arc.files.*; -+import arc.graphics.*; -+import arc.scene.ui.*; -+import arc.scene.ui.layout.*; -+import arc.struct.*; -+import arc.util.*; -+import mindustry.*; -+import mindustry.content.*; -+import mindustry.game.*; -+import mindustry.graphics.*; -+import mindustry.ui.*; -+import mindustry.ui.dialogs.*; -+import mindustry.world.blocks.distribution.*; -+import mindustry.world.blocks.logic.*; -+import mindustry.world.blocks.logic.CanvasBlock.*; -+ -+import static mindustry.Vars.*; -+import static mindustry.content.Blocks.*; -+ -+//move from mindustry.arcModule.toolpack.picToMindustry -+public class PicToMindustry{ -+ static Pixmap oriImage, image, Cimage; -+ static Integer closest = null; -+ static Table tTable; -+ static Fi originFile; -+ -+ static final int[] palette; -+ static final int canvasSize; -+ -+ static float scale = 1f; -+ static final float[] scaleList = {0.02f, 0.05f, 0.1f, 0.15f, 0.2f, 0.25f, 0.3f, 0.4f, 0.5f, 0.65f, 0.8f, 1f, 1.25f, 1.5f, 2f, 3f, 5f}; -+ static int colorDisFun = 0; -+ static final String[] disFunList = {"基础对比", "平方对比", "LAB"}; -+ -+ static{ -+ CanvasBlock canva = (CanvasBlock)Blocks.canvas; -+ palette = canva.palette; -+ canvasSize = canva.canvasSize; -+ } -+ -+ public static void show(){ -+ ptDialog().show(); -+ } -+ -+ public static Dialog ptDialog(){ -+ Dialog pt = new BaseDialog("arc-图片转换器"); -+ pt.cont.table(t -> { -+ t.add("选择并导入图片,可将其转成画板、像素画或是逻辑画").padBottom(20f).row(); -+ t.button("[cyan]选择图片[white](png)", () -> Vars.platform.showFileChooser(false, "png", file -> { -+ try{ -+ originFile = file; -+ byte[] bytes = file.readBytes(); -+ oriImage = new Pixmap(bytes); -+ rebuilt(); -+ if(oriImage.width > 500 || oriImage.height > 500) -+ UIExt.announce("[orange]警告:图片可能过大,请尝试压缩图片", (float)5); -+ }catch(Throwable e){ -+ UIExt.announce("读取图片失败,请尝试更换图片\n" + e); -+ } -+ })).size(240, 50).padBottom(20f).row(); -+ t.check("自动保存为蓝图", Core.settings.getBool("autoSavePTM"), ta -> Core.settings.put("autoSavePTM", ta)); -+ }).padBottom(20f).row(); -+ pt.cont.table(t -> { -+ t.add("缩放: \uE815 "); -+ Label zoom = t.add(String.valueOf(scale)).padRight(20f).get(); -+ t.slider(0, scaleList.length - 1, 1, 11, s -> { -+ scale = scaleList[(int)s]; -+ zoom.setText(Strings.fixed(scale, 2)); -+ rebuilt(); -+ }).width(200f); -+ }).padBottom(20f).visible(() -> oriImage != null).row(); -+ pt.cont.table(t -> { -+ t.add("色调函数: "); -+ Label zoom = t.add(disFunList[0]).padRight(20f).get(); -+ t.slider(0, disFunList.length - 1, 1, 0, s -> { -+ colorDisFun = (int)s; -+ zoom.setText(disFunList[colorDisFun]); -+ }).width(200f); -+ }).padBottom(20f).visible(() -> oriImage != null).row(); -+ pt.cont.table(a -> tTable = a); -+ pt.cont.row(); -+ pt.cont.button("逻辑画网站 " + Blocks.logicDisplay.emoji(), () -> { -+ String imageUrl = "https://buibiu.github.io/imageToMLogicPage/#/"; -+ if(!Core.app.openURI(imageUrl)){ -+ ui.showErrorMessage("打开失败,网址已复制到粘贴板\n请自行在阅览器打开"); -+ Core.app.setClipboardText(imageUrl); -+ } -+ }).width(200f); -+ pt.addCloseButton(); -+ return pt; -+ } -+ -+ private static String formatNumber(int number){ -+ return formatNumber(number, 1f); -+ } -+ -+ private static String formatNumber(int number, float alert){ -+ if(number >= 500 * alert) return "[red]" + number + "[]"; -+ else if(number >= 200 * alert) return "[orange]" + number + "[]"; -+ else return String.valueOf(number); -+ } -+ -+ private static void rebuilt(){ -+ image = Pixmaps.scale(oriImage, scale); -+ tTable.clear(); -+ tTable.table(t -> { -+ t.add("路径").color(Pal.accent).padRight(25f).padBottom(10f); -+ t.button("\uE874", () -> Core.app.setClipboardText(originFile.absolutePath())); -+ t.add(originFile.absolutePath()).padBottom(10f).row(); -+ -+ t.add("名称").color(Pal.accent).padRight(25f).padBottom(10f); -+ t.button("\uE874", () -> Core.app.setClipboardText(originFile.name())); -+ t.add(originFile.name()).padBottom(10f).row(); -+ -+ t.add("原始大小").color(Pal.accent).padRight(25f); -+ t.add(formatNumber(oriImage.width) + "\uE815" + formatNumber(oriImage.height)); -+ }).padBottom(20f).row(); -+ tTable.table(t -> { -+ t.table(tt -> { -+ tt.button("画板 " + canvas.emoji(), Styles.cleart, () -> { -+ Cimage = image.copy(); -+ create_rbg(palette); -+ canvasGenerator(); -+ }).size(100, 50); -+ tt.add("大小:" + formatNumber(image.width / canvasSize, 0.5f) + "\uE815" + formatNumber(image.height / canvasSize + 1, 0.5f)); -+ }); -+ t.row(); -+ t.table(tt -> { -+ tt.button("画板++ " + canvas.emoji(), Styles.cleart, () -> { -+ Cimage = image.copy(); -+ canvasPlus(Cimage); -+ canvasGenerator(); -+ }).size(100, 50); -+ tt.add("大小:" + formatNumber(image.width / canvasSize, 0.5f) + "\uE815" + formatNumber(image.height / canvasSize + 1, 0.5f)); -+ }).row(); -+ t.table(tt -> { -+ tt.button("像素画 " + Blocks.sorter.emoji(), Styles.cleart, () -> { -+ Cimage = image.copy(); -+ sorterGenerator(); -+ }).size(100, 50); -+ tt.add("大小:" + formatNumber(image.width) + "\uE815" + formatNumber(image.height)); -+ }).row(); -+ }); -+ } -+ -+ private static float diff_rbg(Integer a, Integer b){ -+ int ar = a >> 24 & 0xFF, -+ ag = a >> 16 & 0xFF, -+ ab = a >> 8 & 0xFF; -+ // get in -+ int br = b >> 24 & 0xFF, -+ bg = b >> 16 & 0xFF, -+ bb = b >> 8 & 0xFF; -+ int dr = Math.abs(ar - br), -+ dg = Math.abs(ag - bg), -+ db = Math.abs(ab - bb); -+ switch(colorDisFun){ -+ case 1 -> { -+ return dr * dr + dg * dg + db * db; -+ } -+ case 2 -> { -+ float Rmean = (ar + br) / 2f; -+ return (float)Math.sqrt((2 + Rmean / 256) * (dr * dr) + 4 * (dg * dg) + (2 + (255 - Rmean) / 256) * (db * db)); -+ } -+ default -> { -+ return dr + dg + db; -+ } -+ } -+ } -+ -+ private static void create_rbg(int[] colorBar){ -+ for(int x = 0; x < image.width; x++){ -+ for(int y = 0; y < image.height; y++){ -+ Integer pixel = image.get(x, y); -+ float egg = 1000; -+ for(int other : colorBar){ -+ float h = diff_rbg(pixel, other); -+ if(h < egg){ -+ closest = other; -+ egg = h; -+ } -+ } -+ Cimage.set(x, y, closest); -+ } -+ } -+ } -+ -+ private static void canvasGenerator(){ -+ int width = Cimage.width / canvasSize, height = Cimage.height / canvasSize + 1; -+ Seq tiles = new Seq<>(); -+ for(int y = 0; y < height; y++){ -+ for(int x = 0; x < width; x++){ -+ // add canvas to the schematic -+ CanvasBuild build = (CanvasBuild)canvas.newBuilding(); -+ // get max 12x12 region of the image -+ Pixmap region = Cimage.crop(x * canvasSize, (height - y - 1) * canvasSize, canvasSize, canvasSize); -+ // convert pixel data of the region -+ byte[] bytes = build.packPixmap(region); -+ Schematic.Stile stile = new Schematic.Stile(canvas, x * 2, y * 2, bytes, (byte)0); -+ tiles.add(stile); -+ } -+ } -+ StringMap tags = new StringMap(); -+ tags.put("name", originFile.name()); -+ Schematic schem = new Schematic(tiles, tags, width * 2, height * 2); -+ saveSchem(schem, canvas.emoji()); -+ } -+ -+ private static void saveSchem(Schematic schem, String l){ -+ schem.labels.add(l); -+ if(Core.settings.getBool("autoSavePTM")){ -+ Vars.schematics.add(schem); -+ String text = "已保存蓝图:" + originFile.name(); -+ UIExt.announce(text, (float)10); -+ } -+ if(state.isGame()){ -+ Vars.ui.schematics.hide(); -+ Vars.control.input.useSchematic(schem); -+ } -+ } -+ -+ private static void sorterGenerator(){ -+ Seq tiles = new Seq<>(); -+ for(int y = 0; y < image.height; y++){ -+ for(int x = 0; x < image.width; x++){ -+ if(image.get(x, y) == 0) continue; -+ Sorter.SorterBuild build = (Sorter.SorterBuild)sorter.newBuilding(); -+ final float[] closestItem = {99999}; -+ int finalX = x; -+ int finalY = y; -+ content.items().each(t -> { -+ float dst = diff_rbg(t.color.rgba(), image.get(finalX, finalY)); -+ if(dst > closestItem[0]) return; -+ build.sortItem = t; -+ closestItem[0] = dst; -+ }); -+ Schematic.Stile stile = new Schematic.Stile(sorter, x, image.height - y - 1, build.config(), (byte)0); -+ tiles.add(stile); -+ } -+ } -+ StringMap tags = new StringMap(); -+ tags.put("name", originFile.name()); -+ Schematic schem = new Schematic(tiles, tags, image.width, image.height); -+ saveSchem(schem, sorter.emoji()); -+ } -+ -+ private static int trans(RGB c1, RGB c2, int mul){ -+ return c1.add(c2.cpy().mul(mul).mv(4)).rgba(); -+ } -+ -+ private static void canvasPlus(Pixmap image){ -+ for(int y = 0; y < image.height; y++){ -+ for(int x = 0; x < image.width; x++){ -+ RGB pix = new RGB(image.get(x, y)); -+ int nearest = findNearestColor(pix); -+ image.set(x, y, nearest); -+ pix.sub(new RGB(nearest)); -+ if(x + 1 < image.width){ -+ image.set(x + 1, y, trans(new RGB(image.get(x + 1, y)), pix, 7)); -+ } -+ if(y + 1 < image.height){ -+ if(x - 1 > 0){ -+ image.set(x - 1, y + 1, trans(new RGB(image.get(x - 1, y + 1)), pix, 3)); -+ } -+ image.set(x, y + 1, trans(new RGB(image.get(x, y + 1)), pix, 5)); -+ if(x + 1 < image.width){ -+ image.set(x + 1, y + 1, trans(new RGB(image.get(x + 1, y + 1)), pix, 1)); -+ } -+ } -+ } -+ } -+ } -+ -+ private static int findNearestColor(RGB color){ -+ int max = 255 * 255 + 255 * 255 + 255 * 255 + 1; -+ int output = 0; -+ for(int i : palette){ -+ int delta = color.cpy().sub(new RGB(i)).pow(); -+ if(delta < max){ -+ max = delta; -+ output = i; -+ } -+ } -+ return output; -+ } -+ -+ private static class RGB{ -+ int r, g, b; -+ -+ RGB(int r, int g, int b){ -+ this.r = r; -+ this.g = g; -+ this.b = b; -+ } -+ -+ RGB(int rgba){ -+ this(rgba >> 24 & 0xff, rgba >> 16 & 0xff, rgba >> 8 & 0xff); -+ } -+ -+ public RGB sub(RGB c){ -+ r = r - c.r; -+ g = g - c.g; -+ b = b - c.b; -+ return this; -+ } -+ -+ public RGB add(RGB c){ -+ r = Math.max(Math.min(c.r + r, 255), 0); -+ g = Math.max(Math.min(c.g + g, 255), 0); -+ b = Math.max(Math.min(c.b + b, 255), 0); -+ return this; -+ } -+ -+ public RGB mul(int m){ -+ r *= m; -+ g *= m; -+ b *= m; -+ return this; -+ } -+ -+ public RGB mv(int s){ -+ r >>= s; -+ g >>= s; -+ b >>= s; -+ return this; -+ } -+ -+ public int pow(){ -+ return r * r + g * g + b * b; -+ } -+ -+ public int rgba(){ -+ return r << 24 | g << 16 | b << 8 | 0xff; -+ } -+ -+ public RGB cpy(){ -+ return new RGB(r, g, b); -+ } -+ } -+} -diff --git a/core/src/mindustryX/features/RenderExt.java b/core/src/mindustryX/features/RenderExt.java -index 76adae3a529d20bf1c58dc42bd8aa4d439402df9..486328876d6927c9acf06276f96c4136aee3965f 100644 ---- a/core/src/mindustryX/features/RenderExt.java -+++ b/core/src/mindustryX/features/RenderExt.java -@@ -10,12 +10,14 @@ import arc.util.*; - import mindustry.*; - import mindustry.entities.*; - import mindustry.game.EventType.*; -+import mindustry.game.*; - import mindustry.gen.*; - import mindustry.graphics.*; - import mindustry.type.*; - import mindustry.world.*; - import mindustry.world.blocks.defense.*; - import mindustry.world.blocks.defense.turrets.*; -+import mindustry.world.blocks.defense.turrets.BaseTurret.*; - import mindustry.world.blocks.distribution.MassDriver.*; - import mindustry.world.blocks.logic.*; - import mindustry.world.blocks.logic.MessageBlock.*; -@@ -23,6 +25,7 @@ import mindustry.world.blocks.production.Drill.*; - import mindustry.world.blocks.storage.*; - import mindustry.world.blocks.units.*; - import mindustryX.features.draw.*; -+import mindustryX.features.func.*; - - import static mindustry.Vars.*; - -@@ -43,6 +46,8 @@ public class RenderExt{ - public static float healthBarMinHealth; - public static boolean payloadPreview; - public static boolean deadOverlay; -+ public static boolean drawBlockDisabled; -+ public static boolean showOtherInfo; - - public static boolean unitHide = false; - public static Color massDriverLineColor = Color.clear; -@@ -84,6 +89,9 @@ public class RenderExt{ - healthBarMinHealth = Core.settings.getInt("blockbarminhealth"); - payloadPreview = Core.settings.getBool("payloadpreview"); - deadOverlay = Core.settings.getBool("deadOverlay"); -+ drawBlockDisabled = Core.settings.getBool("blockdisabled"); -+ showOtherInfo = Core.settings.getBool("showOtherTeamState"); -+ showOtherInfo |= Vars.player.team().id == 255 || Vars.state.rules.mode() != Gamemode.pvp; - }); - Events.run(Trigger.draw, RenderExt::draw); - Events.on(TileChangeEvent.class, RenderExt::onSetBlock); -@@ -133,6 +141,8 @@ public class RenderExt{ - drawMassDriverLine(b); - if(build != null && drawBars) - drawBars(build); -+ if(build instanceof BaseTurretBuild turretBuild) -+ ArcBuilds.arcTurret(turretBuild); - } - - private static void placementEffect(float x, float y, float lifetime, float range, Color color){ -@@ -221,7 +231,7 @@ public class RenderExt{ - if(buildRatio >= 0){ - drawBar(build, Color.black, Pal.accent, buildRatio); - String progressT = Strings.format("[stat]@% | @s", (int)(Mathf.clamp(buildRatio, 0f, 1f) * 100), leftTime < 0 ? Iconc.cancel : Strings.fixed(leftTime / (60f * Vars.state.rules.unitBuildSpeed(build.team) * build.timeScale()), 0)); -- WorldLabel.drawAt(progressT, build.x, build.y + build.block.offset * 0.8f - 5f, Draw.z(), WorldLabel.flagOutline, 0.9f); -+ FuncX.drawText(Tmp.v1.set(build).add(0, build.block.offset * 0.8f - 5f), progressT, 0.9f); - } - } - -diff --git a/core/src/mindustryX/features/UIExt.java b/core/src/mindustryX/features/UIExt.java -index 651e6408d6132a194f63fba2613abac62b60aaf1..1962d9b7db710f08cb7050723d2d48270e9c9963 100644 ---- a/core/src/mindustryX/features/UIExt.java -+++ b/core/src/mindustryX/features/UIExt.java -@@ -20,6 +20,7 @@ public class UIExt{ - public static TeamSelectDialog teamSelect; - public static ModsRecommendDialog modsRecommend = new ModsRecommendDialog(); - public static TeamsStatDisplay teamsStatDisplay; -+ public static ArcMessageDialog arcMessageDialog = new ArcMessageDialog(); - public static HudSettingsTable hudSettingsTable = new HudSettingsTable(); - public static AdvanceToolTable advanceToolTable = new AdvanceToolTable(); - public static AdvanceBuildTool advanceBuildTool = new AdvanceBuildTool(); -diff --git a/core/src/mindustryX/features/ui/ArcMessageDialog.java b/core/src/mindustryX/features/ui/ArcMessageDialog.java -new file mode 100644 -index 0000000000000000000000000000000000000000..14b2cf4fc7e110852f7a0b27a0a6d4996dc9355c ---- /dev/null -+++ b/core/src/mindustryX/features/ui/ArcMessageDialog.java -@@ -0,0 +1,419 @@ -+package mindustryX.features.ui; -+ -+import arc.*; -+import arc.graphics.*; -+import arc.math.geom.*; -+import arc.scene.ui.*; -+import arc.scene.ui.layout.*; -+import arc.struct.Queue; -+import arc.struct.*; -+import arc.util.*; -+import mindustry.*; -+import mindustry.content.*; -+import mindustry.core.*; -+import mindustry.ctype.*; -+import mindustry.game.*; -+import mindustry.gen.*; -+import mindustry.ui.*; -+import mindustry.ui.dialogs.*; -+import mindustry.world.blocks.storage.*; -+import mindustryX.features.*; -+ -+import java.text.*; -+import java.util.*; -+ -+import static mindustry.Vars.*; -+ -+//move from mindustry.arcModule.ui.dialogs.MessageDialog -+public class ArcMessageDialog extends BaseDialog{ -+ public static final Queue msgList = new Queue<>();//队头为新添加的 -+ private static int maxMsgRecorded = Math.max(Core.settings.getInt("maxMsgRecorded"), 20); -+ private Table historyTable; -+ private boolean fieldMode = false; -+ -+ public ArcMessageDialog(){ -+ super("ARC-中央监控室"); -+ -+ //voiceControl.voiceControlDialog(); -+ cont.pane(t -> historyTable = t).maxWidth(1000).scrollX(false); -+ -+ addCloseButton(); -+ buttons.button("设置", Icon.settings, this::arcMsgSettingTable); -+ buttons.button("导出", Icon.upload, this::exportMsg).name("导出聊天记录"); -+ -+ buttons.row(); -+ buttons.button("清空", Icon.trash, () -> { -+ msgList.clear(); -+ build(); -+ }); -+ -+ shown(this::build); -+ onResize(this::build); -+ -+ Events.on(EventType.WorldLoadEvent.class, e -> { -+ addMsg(new Msg(Type.eventWorldLoad, "载入地图: " + state.map.name())); -+ addMsg(new Msg(Type.eventWorldLoad, "简介: " + state.map.description())); -+ while(msgList.size >= maxMsgRecorded) msgList.removeLast(); -+ }); -+ -+ Events.on(EventType.WaveEvent.class, e -> { -+ if(state.wavetime < 60f) return; -+ addMsg(new Msg(Type.eventWave, "波次: " + state.wave + " | " + getWaveInfo(state.wave - 1))); -+ }); -+ -+ Events.on(EventType.BlockDestroyEvent.class, e -> { -+ if(e.tile.build instanceof CoreBlock.CoreBuild) -+ addMsg(new Msg(Type.eventCoreDestory, "核心摧毁: " + "(" + (int)e.tile.x + "," + (int)e.tile.y + ")", new Vec2(e.tile.x * 8, e.tile.y * 8))); -+ }); -+ } -+ -+ public static void share(String type, String content){ -+ UIExt.sendChatMessage("<" + type + ">" + content); -+ } -+ -+ public static void shareWaveInfo(int waves){ -+ if(!state.rules.waves) return; -+ StringBuilder builder = new StringBuilder(); -+ builder.append("标记了第").append(waves).append("波"); -+ if(waves < state.wave){ -+ builder.append("。"); -+ }else{ -+ if(waves > state.wave){ -+ builder.append(",还有").append(waves - state.wave).append("波"); -+ } -+ int timer = (int)(state.wavetime + (waves - state.wave) * state.rules.waveSpacing); -+ builder.append("[[").append(FormatDefault.duration((float)timer / 60)).append("]。"); -+ } -+ -+ builder.append(getWaveInfo(waves)); -+ share("Wave", builder.toString()); -+ } -+ -+ public static void shareContent(UnlockableContent content, boolean description){ -+ StringBuilder builder = new StringBuilder(); -+ builder.append("标记了").append(content.localizedName).append(content.emoji()); -+ builder.append("(").append(content.name).append(")"); -+ if(content.description != null && description){ -+ builder.append("。介绍: ").append(content.description); -+ } -+ ArcMessageDialog.share("Content", builder.toString()); -+ } -+ -+ public static String getWaveInfo(int waves){ -+ StringBuilder builder = new StringBuilder(); -+ if(state.rules.attackMode){ -+ int sum = Math.max(state.teams.present.sum(t -> t.team != player.team() ? t.cores.size : 0), 1) + Vars.spawner.countSpawns(); -+ builder.append("包含(×").append(sum).append(")"); -+ }else{ -+ builder.append("包含(×").append(Vars.spawner.countSpawns()).append("):"); -+ } -+ for(SpawnGroup group : state.rules.spawns){ -+ if(group.getSpawned(waves - 1) > 0){ -+ builder.append((char)Fonts.getUnicode(group.type.name)).append("("); -+ if(group.effect != StatusEffects.invincible && group.effect != StatusEffects.none && group.effect != null){ -+ builder.append((char)Fonts.getUnicode(group.effect.name)).append("|"); -+ } -+ if(group.getShield(waves - 1) > 0){ -+ builder.append(FormatDefault.format(group.getShield(waves - 1))).append("|"); -+ } -+ builder.append(group.getSpawned(waves - 1)).append(")"); -+ } -+ } -+ return builder.toString(); -+ } -+ -+ void build(){ -+ historyTable.clear(); -+ historyTable.setWidth(800f); -+ int i = 0; -+ for(var msg : msgList){ -+ i++; -+ int id = i; -+ if(!msg.msgType.show) continue; -+ historyTable.table(Tex.whitePane, t -> { -+ t.setColor(msg.msgType.color); -+ t.marginTop(5); -+ -+ t.table(Tex.whiteui, tt -> { -+ tt.color.set(msg.msgType.color); -+ -+ if(msg.msgType == Type.chat) -+ tt.add(getPlayerName(msg)).style(Styles.outlineLabel).left().width(300f); -+ else -+ tt.add(msg.msgType.name).style(Styles.outlineLabel).color(msg.msgType.color).left().width(300f); -+ -+ tt.add(formatTime(msg.time)).style(Styles.outlineLabel).color(msg.msgType.color).left().padLeft(20f).width(100f); -+ -+ if(msg.msgLoc != null){ -+ tt.button("♐: " + (int)(msg.msgLoc.x / tilesize) + "," + (int)(msg.msgLoc.y / tilesize), Styles.logict, () -> { -+ control.input.panCamera(msg.msgLoc); -+ MarkerType.mark.at(Tmp.v1.scl(msg.msgLoc.x, msg.msgLoc.y)).color = color; -+ hide(); -+ }).padLeft(50f).height(24f).width(150f); -+ } -+ -+ tt.add().growX(); -+ tt.add(" " + id).style(Styles.outlineLabel).color(msg.msgType.color).padRight(10); -+ -+ tt.button(Icon.copy, Styles.logici, () -> { -+ Core.app.setClipboardText(msg.message); -+ ui.announce("已导出本条聊天记录"); -+ }).size(24f).padRight(6); -+ tt.button(Icon.cancel, Styles.logici, () -> { -+ msgList.remove(msg); -+ build(); -+ }).size(24f); -+ -+ }).growX().height(30); -+ -+ t.row(); -+ -+ t.table(tt -> { -+ tt.left(); -+ tt.marginLeft(4); -+ tt.setColor(msg.msgType.color); -+ if(fieldMode) tt.field(msg.message, Styles.nodeArea, text -> { -+ }).growX(); -+ else tt.labelWrap(getPlayerMsg(msg)).growX(); -+ }).pad(4).padTop(2).growX().grow(); -+ -+ t.marginBottom(7); -+ }).growX().padBottom(15f).row(); -+ } -+ } -+ -+ private String getPlayerName(Msg msgElement){ -+ int typeStart = msgElement.message.indexOf("[coral]["); -+ int typeEnd = msgElement.message.indexOf("[coral]]"); -+ if(typeStart == -1 || typeEnd == -1 || typeEnd <= typeStart){ -+ return msgElement.msgType.name; -+ } -+ -+ return msgElement.message.substring(typeStart + 20, typeEnd); -+ } -+ -+ private String getPlayerMsg(Msg msgElement){ -+ if(msgElement.msgType != Type.normal) return msgElement.message; -+ int typeStart = msgElement.message.indexOf("[coral]["); -+ int typeEnd = msgElement.message.indexOf("[coral]]"); -+ if(typeStart == -1 || typeEnd == -1 || typeEnd <= typeStart){ -+ return msgElement.message; -+ } -+ return msgElement.message.substring(typeEnd + 9); -+ } -+ -+ private void arcMsgSettingTable(){ -+ BaseDialog setDialog = new BaseDialog("中央监控室-设置"); -+ if(Core.settings.getInt("maxMsgRecorded") == 0) Core.settings.put("maxMsgRecorded", 500); -+ -+ setDialog.cont.table(t -> { -+ t.check("信息编辑模式", fieldMode, a -> { -+ fieldMode = a; -+ build(); -+ }).left().width(200f).row(); -+ -+ t.add("调整显示的信息").height(50f).row(); -+ t.table(tt -> { -+ tt.button("关闭全部", Styles.cleart, () -> { -+ for(Type type : Type.values()) type.show = false; -+ }).width(200f).height(50f); -+ tt.button("默认", Styles.cleart, () -> { -+ for(Type type : Type.values()) type.show = true; -+ Type.serverTips.show = false; -+ }).width(200f).height(50f); -+ }).row(); -+ t.table(Tex.button, tt -> tt.pane(tp -> { -+ for(Type type : Type.values()){ -+ -+ CheckBox box = new CheckBox("[#" + type.color.toString() + "]" + type.name); -+ -+ box.update(() -> box.setChecked(type.show)); -+ box.changed(() -> { -+ type.show = !type.show; -+ build(); -+ }); -+ -+ box.left(); -+ tp.add(box).left().padTop(3f).row(); -+ } -+ }).maxHeight(500).width(400f)); -+ }); -+ -+ setDialog.cont.row(); -+ -+ setDialog.cont.table(t -> { -+ t.add("最大储存聊天记录(过高可能导致卡顿):"); -+ t.field(maxMsgRecorded + "", text -> { -+ int record = Math.min(Math.max(Integer.parseInt(text), 1), 9999); -+ maxMsgRecorded = record; -+ Core.settings.put("maxMsgRecorded", record); -+ }).valid(Strings::canParsePositiveInt).width(200f).get(); -+ t.row(); -+ t.add("超出限制的聊天记录将在载入地图时清除"); -+ }); -+ -+ setDialog.addCloseButton(); -+ setDialog.button("刷新", Icon.refresh, this::build); -+ -+ setDialog.show(); -+ } -+ -+ public String formatTime(Date time){ -+ return new SimpleDateFormat("HH:mm:ss", Locale.US).format(time); -+ } -+ -+ public static void resolveMsg(String message, @Nullable Player sender){ -+ Type type = resolveMarkType(message); -+ if(type == null) type = resolveServerType(message); -+ if(type == null) type = sender != null ? Type.chat : Type.normal; -+ -+ addMsg(new Msg(type, message, sender != null ? sender.name() : null, sender != null ? new Vec2(sender.x, sender.y) : null)); -+ if(!type.show) return; -+ switch(type){ -+ case schematic -> { -+ String id = message.split("")[1]; -+ id = id.substring(id.indexOf(' ') + 1); -+ Http.get("https://pastebin.com/raw/" + id, r -> ui.schematics.readShare(r.getResultAsString().replace(" ", "+"), sender)); -+ } -+ case markPlayer -> { -+ if(!message.split("AT")[1].contains(player.name)) return; -+ if(sender != null) -+ ui.announce("[gold]你被[white] " + sender.name + " [gold]戳了一下,请注意查看信息框哦~", 10); -+ else ui.announce("[orange]你被戳了一下,请注意查看信息框哦~", 10); -+ } -+ } -+ } -+ -+ public static Type resolveMarkType(String message){ -+ if(!message.contains("")) return Type.markPlayer; -+ if(message.contains("")) return Type.schematic; -+ return null; -+ } -+ -+ private static final Seq serverMsg = Seq.with("加入了服务器", "离开了服务器", "自动存档完成", "登录成功", "经验+", "[YELLOW]本局游戏时长:", "[YELLOW]单人快速投票", "[GREEN]回档成功", -+ "[YELLOW]PVP保护时间, 全力进攻吧", "[YELLOW]发起", "[YELLOW]你可以在投票结束前使用", "[GREEN]投票成功", "[GREEN]换图成功,当前地图", -+ "[RED]本地图禁用单位", "[RED]该地图限制空军,禁止进入敌方领空", "[yellow]本地图限制空军", "[YELLOW]火焰过多造成服务器卡顿,自动关闭火焰", -+ " [GREEN]====", "[RED]无效指令", "[RED]该技能", "切换成功", -+ "[violet][投票系统][]", "[coral][-]野生的", "[CYAN][+]野生的" // xem相关 -+ ); -+ -+ public static Type resolveServerType(String message){ -+ if(message.contains("小贴士")) return Type.serverTips; -+ if(message.contains("[YELLOW][技能]")) return Type.serverSkill; -+ for(int i = 0; i < serverMsg.size; i++){ -+ if(message.contains(serverMsg.get(i))){ -+ return Type.serverMsg; -+ } -+ } -+ return null; -+ } -+ -+ public static void addMsg(Msg msg){ -+ msgList.addFirst(msg); -+ } -+ -+ void exportMsg(){ -+ StringBuilder messageHis = new StringBuilder(); -+ messageHis.append("下面是[MDTX-").append(Version.mdtXBuild).append("] 导出的游戏内聊天记录").append("\n"); -+ messageHis.append("*** 当前地图名称: ").append(state.map.name()).append("(模式:").append(state.rules.modeName).append(")\n"); -+ messageHis.append("*** 当前波次: ").append(state.wave).append("\n"); -+ messageHis.append("成功选取共 ").append(msgList.size).append(" 条记录,如下:\n"); -+ for(var msg : msgList){ -+ messageHis.append(Strings.stripColors(msg.message)).append("\n"); -+ } -+ Core.app.setClipboardText(Strings.stripGlyphs(Strings.stripColors(messageHis.toString()))); -+ } -+ -+ public static class Msg{ -+ public final Type msgType; -+ public final String message; -+ public final Date time; -+ public final String sender; -+ public boolean selected; -+ public final @Nullable Vec2 msgLoc; -+ -+ public Msg(Type msgType, String message, Date time, String sender, Vec2 msgLoc){ -+ this.msgType = msgType; -+ this.message = message; -+ this.time = time; -+ this.sender = sender; -+ this.msgLoc = msgLoc; -+ } -+ -+ public Msg(Type msgType, String message, String sender, Vec2 msgLoc){ -+ this(msgType, message, new Date(), sender, msgLoc); -+ } -+ -+ public Msg(Type msgType, String message, Vec2 msgLoc){ -+ this(msgType, message, "null", msgLoc); -+ } -+ -+ public Msg(Type msgType, String message){ -+ this(msgType, message, null); -+ } -+ -+ -+ public Msg sendMessage(){ -+ ui.chatfrag.addMessage(msgType.arcMsgPreFix() + message); -+ return this; -+ } -+ } -+ -+ public enum Type{ -+ normal("消息", Color.gray), -+ -+ chat("聊天", Color.valueOf("#778899")), -+ console("指令", Color.gold), -+ -+ markLoc("标记", "坐标", Color.valueOf("#7FFFD4")), -+ markWave("标记", "波次", Color.valueOf("#7FFFD4")), -+ markContent("标记", "内容", Color.valueOf("#7FFFD4")), -+ markPlayer("标记", "玩家", Color.valueOf("#7FFFD4")), -+ arcChatPicture("分享", "图片", Color.yellow), -+ music("分享", "音乐", Color.pink), -+ schematic("分享", "蓝图", Color.blue), -+ district("规划区", "", Color.violet), -+ -+ serverTips("服务器", "小贴士", Color.valueOf("#98FB98"), false), -+ serverMsg("服务器", "信息", Color.valueOf("#cefdce")), -+ serverToast("服务器", "通报", Color.valueOf("#00FA9A")), -+ serverSkill("服务器", "技能", Color.valueOf("#e6ffcc")), -+ -+ logicNotify("逻辑", "通报", Color.valueOf("#ffccff")), -+ logicAnnounce("逻辑", "公告", Color.valueOf("#ffccff")), -+ -+ eventWorldLoad("事件", "载入地图", Color.valueOf("#ff9999")), -+ eventCoreDestory("事件", "核心摧毁", Color.valueOf("#ffcccc")), -+ eventWave("事件", "波次", Color.valueOf("#ffcc99")); -+ -+ public final String name; -+ public final String type; -+ public final String subClass; -+ public Color color; -+ public Boolean show; -+ -+ Type(String type, String subClass, Color color, Boolean show){ -+ this.name = subClass.isEmpty() ? type : (type + "~" + subClass); -+ this.type = type; -+ this.subClass = subClass; -+ this.color = color; -+ this.show = show; -+ } -+ -+ Type(String type, String subClass, Color color){ -+ this(type, subClass, color, true); -+ } -+ -+ Type(String type, Color color){ -+ this(type, "", color); -+ } -+ -+ public String arcMsgPreFix(){ -+ return "[#" + color.toString() + "]" + "[" + name + "][]"; -+ } -+ -+ } -+} -\ No newline at end of file -diff --git a/core/src/mindustryX/features/ui/ArcPowerInfo.java b/core/src/mindustryX/features/ui/ArcPowerInfo.java -new file mode 100644 -index 0000000000000000000000000000000000000000..7f6737204adc9856a176f6f36e8b316dc6e74938 ---- /dev/null -+++ b/core/src/mindustryX/features/ui/ArcPowerInfo.java -@@ -0,0 +1,70 @@ -+package mindustryX.features.ui; -+ -+import arc.*; -+import arc.math.*; -+import arc.scene.*; -+import arc.scene.ui.layout.*; -+import mindustry.*; -+import mindustry.core.*; -+import mindustry.gen.*; -+import mindustry.graphics.*; -+import mindustry.ui.*; -+import mindustryX.features.Settings; -+ -+//move from mindustry.arcModule.ui.PowerInfo -+public class ArcPowerInfo{ -+ public static float balance, stored, capacity, produced, need; -+ -+ public static void update(){ -+ balance = 0; -+ stored = 0; -+ capacity = 0; -+ produced = 0; -+ need = 0; -+ Groups.powerGraph.each(item -> { -+ var graph = item.graph(); -+ if(graph.all.isEmpty() || graph.all.first().team != Vars.player.team()) return; -+ balance += graph.getPowerBalance(); -+ stored += graph.getLastPowerStored(); -+ capacity += graph.getLastCapacity(); -+ produced += graph.getLastPowerProduced(); -+ need += graph.getLastPowerNeeded(); -+ }); -+ } -+ -+ public static int getPowerBalance(){ -+ return (int)(balance * 60); -+ } -+ -+ public static float getSatisfaction(){ -+ if(Mathf.zero(produced)){ -+ return 0f; -+ }else if(Mathf.zero(need)){ -+ return 1f; -+ } -+ return produced / need; -+ } -+ -+ -+ public static Element getBars(){ -+ Table power = new Table(Tex.wavepane).marginTop(6); -+ -+ Bar powerBar = new Bar( -+ () -> Core.bundle.format("bar.powerbalance", (getPowerBalance() >= 0 ? "+" : "") + UI.formatAmount(getPowerBalance())) + -+ (getSatisfaction() >= 1 ? "" : " [gray]" + (int)(getSatisfaction() * 100) + "%"), -+ () -> Pal.powerBar, ArcPowerInfo::getSatisfaction); -+ Bar batteryBar = new Bar( -+ () -> Core.bundle.format("bar.powerstored", UI.formatAmount((long)stored), UI.formatAmount((long)capacity)), -+ () -> Pal.powerBar, -+ () -> stored / capacity); -+ -+ power.clicked(() -> Settings.cycle("arccoreitems",4)); -+ power.margin(0); -+ power.add(powerBar).height(18).growX().padBottom(1); -+ power.row(); -+ power.add(batteryBar).height(18).growX().padBottom(1); -+ -+ power.update(ArcPowerInfo::update); -+ return power; -+ } -+} -\ No newline at end of file -diff --git a/core/src/mindustryX/features/ui/ArcWaveInfoDialog.java b/core/src/mindustryX/features/ui/ArcWaveInfoDialog.java -new file mode 100644 -index 0000000000000000000000000000000000000000..3d0fc7ec9d8b3f400cd4d9270aef4a03569ff1c2 ---- /dev/null -+++ b/core/src/mindustryX/features/ui/ArcWaveInfoDialog.java -@@ -0,0 +1,1117 @@ -+package mindustryX.features.ui; -+ -+import arc.*; -+import arc.func.*; -+import arc.math.*; -+import arc.math.geom.*; -+import arc.scene.event.*; -+import arc.scene.style.*; -+import arc.scene.ui.*; -+import arc.scene.ui.TextField.*; -+import arc.scene.ui.layout.*; -+import arc.struct.*; -+import arc.util.*; -+import mindustry.ai.types.*; -+import mindustry.content.*; -+import mindustry.core.*; -+import mindustry.editor.*; -+import mindustry.game.*; -+import mindustry.gen.*; -+import mindustry.graphics.*; -+import mindustry.io.*; -+import mindustry.type.*; -+import mindustry.type.unit.*; -+import mindustry.ui.*; -+import mindustry.ui.dialogs.*; -+import mindustryX.features.*; -+ -+import java.util.*; -+ -+import static mindustry.Vars.*; -+import static mindustryX.features.ArcWaveSpawner.calWinWave; -+import static mindustry.game.SpawnGroup.never; -+import static mindustry.ui.Styles.*; -+ -+//move from mindustry.arcModule.ui.ArcWaveInfoDialog -+public class ArcWaveInfoDialog extends BaseDialog{ -+ private int start = 0; -+ private int displayed = 20; -+ private int graphSpeed = 1; -+ private final int maxGraphSpeed = 16; -+ Seq groups = new Seq<>(); -+ private SpawnGroup expandedGroup; -+ -+ private Table table, nTable, oTable, iTable, eTable, uTable; -+ private int search = -1; -+ private final int maxVisible = 30; -+ private int filterHealth, filterBegin = -1, filterEnd = -1, filterAmount, filterAmountWave; -+ private boolean expandPane = false, filterHealthMode = false, filterStrict = false; -+ private UnitType lastType = UnitTypes.dagger; -+ private StatusEffect filterEffect = StatusEffects.none; -+ private Sort sort = Sort.begin; -+ private boolean reverseSort = false; -+ private float updateTimer; -+ private final float updatePeriod = 1f; -+ private TextField amountField = new TextField(); -+ private boolean checkedSpawns; -+ private WaveGraph graph = new WaveGraph(); -+ -+ public int arcWaveIndex = 0; -+ private final float handerSize = 40f; -+ /** -+ * 是否显示波次界面。如果否,则显示原图 -+ */ -+ private boolean waveInfo = true; -+ private float waveMulti = 1f; -+ -+ private int winWave; -+ -+ //波次生成 -+ Float difficult = 1f; -+ final Seq spawnUnit = content.units().copy().retainAll(unitType -> !(unitType instanceof MissileUnitType || unitType.controller instanceof BuilderAI || unitType.controller instanceof MinerAI || unitType.controller instanceof RepairAI)); -+ final Seq allowUnit = content.units().copy().retainAll(unitType -> !(unitType instanceof MissileUnitType)); -+ boolean surplusUnit = true, ErekirUnit = true; -+ boolean showUnitSelect = true; -+ boolean flyingUnit = true, navalUnit = true, supportUnit = true; -+ -+ public ArcWaveInfoDialog(){ -+ super("ARC-波次编辑器"); -+ -+ shown(() -> { -+ checkedSpawns = false; -+ winWave = calWinWave(); -+ -+ setup(); -+ }); -+ hidden(() -> state.rules.spawns = groups); -+ -+ addCloseListener(); -+ -+ onResize(this::setup); -+ -+ addCloseButton(); -+ -+ buttons.button("@waves.edit", Icon.pencil, () -> { -+ BaseDialog dialog = new BaseDialog("@waves.edit"); -+ dialog.addCloseButton(); -+ dialog.setFillParent(false); -+ dialog.cont.table(Tex.button, t -> { -+ var style = Styles.flatt; -+ t.defaults().size(210f, 58f); -+ -+ t.button("@waves.copy", Icon.copy, style, () -> { -+ ui.showInfoFade("@waves.copied"); -+ Core.app.setClipboardText(maps.writeWaves(groups)); -+ dialog.hide(); -+ }).disabled(b -> groups == null).marginLeft(12f).row(); -+ -+ t.button("@waves.load", Icon.download, style, () -> { -+ try{ -+ groups = maps.readWaves(Core.app.getClipboardText()); -+ buildGroups(); -+ }catch(Exception e){ -+ ui.showException("@waves.invalid", e); -+ } -+ dialog.hide(); -+ }).marginLeft(12f).disabled(b -> Core.app.getClipboardText() == null || Core.app.getClipboardText().isEmpty()).row(); -+ -+ t.button("@settings.reset", Icon.upload, style, () -> ui.showConfirm("@confirm", "@settings.clear.confirm", () -> { -+ groups = JsonIO.copy(waves.get()); -+ buildGroups(); -+ dialog.hide(); -+ })).marginLeft(12f).row(); -+ -+ t.button("@clear", Icon.cancel, style, () -> ui.showConfirm("@confirm", "@settings.clear.confirm", () -> { -+ groups.clear(); -+ buildGroups(); -+ dialog.hide(); -+ })).marginLeft(12f); -+ }); -+ -+ dialog.show(); -+ }).size(250f, 64f); -+ -+ buttons.defaults().width(60f); -+ -+ buttons.button("切换显示模式", () -> { -+ waveInfo = !waveInfo; -+ waveMulti = 1; -+ setup(); -+ }).size(250f, 64f); -+ -+ if(true){ -+ buttons.button("随机", Icon.refresh, this::arcSpawner).width(200f); -+ } -+ } -+ -+ void view(int amount){ -+ updateTimer += Time.delta; -+ if(updateTimer >= updatePeriod){ -+ displayed += amount; -+ if(displayed < 5) displayed = 5; -+ updateTimer = 0f; -+ updateWaves(); -+ } -+ } -+ -+ void shift(int amount){ -+ updateTimer += Time.delta; -+ if(updateTimer >= updatePeriod){ -+ start += amount; -+ if(start < 0) start = 0; -+ updateTimer = 0f; -+ updateWaves(); -+ } -+ } -+ -+ void setup(){ -+ groups = JsonIO.copy(state.rules.spawns.isEmpty() ? waves.get() : state.rules.spawns); -+ -+ cont.clear(); -+ cont.stack(new Table(Tex.clear, main -> { -+ main.table(s -> { -+ s.image(Icon.zoom).padRight(8); -+ s.field(search < 0 ? "" : search + "", TextFieldFilter.digitsOnly, text -> { -+ search = !text.isEmpty() ? Math.max(Strings.parseInt(text) - 1, -1) : -1; -+ start = Math.max(search - (displayed / 2) - (displayed % 2), 0); -+ buildGroups(); -+ }).growX().maxTextLength(8).get().setMessageText("@waves.search"); -+ s.button(Icon.filter, Styles.emptyi, this::showFilter).size(46f).tooltip("@waves.filter"); -+ }).fillX().pad(6f).row(); -+ main.pane(t -> table = t).growX().growY().padRight(8f).scrollX(false); -+ main.row(); -+ main.table(f -> { -+ f.button("@add", () -> { -+ if(groups == null) groups = new Seq<>(); -+ SpawnGroup newGroup = new SpawnGroup(lastType); -+ groups.add(newGroup); -+ expandedGroup = newGroup; -+ showUpdate(newGroup, false); -+ buildGroups(); -+ clearFilter(); -+ }).growX().height(70f); -+ f.button(Icon.filter, () -> { -+ BaseDialog dialog = new BaseDialog("@waves.sort"); -+ dialog.setFillParent(false); -+ dialog.cont.table(Tex.button, t -> { -+ for(Sort s : Sort.all){ -+ t.button("@waves.sort." + s, Styles.cleart, () -> { -+ sort = s; -+ dialog.hide(); -+ buildGroups(); -+ }).size(150f, 60f).checked(s == sort); -+ } -+ }).row(); -+ dialog.cont.check("@waves.sort.reverse", b -> { -+ reverseSort = b; -+ buildGroups(); -+ }).padTop(4).checked(reverseSort).padBottom(8f); -+ dialog.addCloseButton(); -+ dialog.show(); -+ buildGroups(); -+ }).size(64f, 70f).padLeft(4f); -+ }).fillX(); -+ }), new Label("@waves.none"){{ -+ visible(() -> groups.isEmpty()); -+ this.touchable = Touchable.disabled; -+ setWrap(true); -+ setAlignment(Align.center, Align.center); -+ }}).width(390f).growY(); -+ cont.table(tb -> { -+ if(waveInfo){ -+ tb.table(t -> { -+ t.table(buttons -> { -+ buttons.clear(); -+ buttons.button("<<", cleart, () -> { -+ arcWaveIndex -= 10; -+ if(arcWaveIndex < 0) arcWaveIndex = 0; -+ setup(); -+ }).size(handerSize); -+ -+ buttons.button("<", cleart, () -> { -+ arcWaveIndex -= 1; -+ if(arcWaveIndex < 0) arcWaveIndex = 1; -+ setup(); -+ }).size(handerSize); -+ -+ TextField sField = buttons.field((arcWaveIndex + 1) + "", text -> { -+ if(Strings.canParseInt(text)){ -+ arcWaveIndex = Integer.parseInt(text) - 1; -+ setup(); -+ } -+ }).get(); -+ -+ buttons.button(">", cleart, () -> { -+ arcWaveIndex += 1; -+ setup(); -+ }).size(handerSize); -+ -+ buttons.button(">>", cleart, () -> { -+ arcWaveIndex += 10; -+ setup(); -+ }).size(handerSize); -+ -+ buttons.button("×", cleart, () -> { -+ arcWaveIndex = 0; -+ setup(); -+ }).size(handerSize); -+ -+ buttons.slider(0, winWave, 1, res -> { -+ arcWaveIndex = (int)res; -+ sField.setText((arcWaveIndex + 1) + ""); -+ }); -+ -+ }).left().row(); -+ t.pane(waveInfo -> { -+ waveInfo.clear(); -+ waveInfo.table(wi -> { -+ int curInfoWave = arcWaveIndex; -+ for(SpawnGroup group : state.rules.spawns){ -+ int amount = group.getSpawned(curInfoWave); -+ if(amount > 0){ -+ StringBuilder groupInfo = new StringBuilder(); -+ groupInfo.append(group.type.emoji()).append("\n"); -+ groupInfo.append(amount).append("\n"); -+ if(group.getShield(curInfoWave) > 0f) -+ groupInfo.append(UI.formatAmount((long)group.getShield(curInfoWave))).append("\n"); -+ if(group.effect != null && group.effect != StatusEffects.none) -+ groupInfo.append(group.effect.emoji()).append("\n"); -+ wi.button(groupInfo.toString(), cleart, () -> unitSettingDialog(group)).height(130f).width(50f); -+ } -+ } -+ }); -+ }).scrollY(false).left(); -+ }).growX(); -+ tb.row(); -+ tb.add(graph = new WaveGraph()).grow(); -+ tb.row(); -+ tb.table(tbt -> { -+ tbt.button("<", () -> { -+ }).update(t -> { -+ if(t.getClickListener().isPressed()){ -+ shift(-graphSpeed); -+ } -+ }).width(150f); -+ tbt.button(">", () -> { -+ }).update(t -> { -+ if(t.getClickListener().isPressed()){ -+ shift(graphSpeed); -+ } -+ }).width(150f); -+ -+ tbt.button("-", () -> { -+ }).update(t -> { -+ if(t.getClickListener().isPressed()){ -+ view(-graphSpeed); -+ } -+ }).width(150f); -+ tbt.button("+", () -> { -+ }).update(t -> { -+ if(t.getClickListener().isPressed()){ -+ view(graphSpeed); -+ } -+ }).width(150f); -+ -+ tbt.button("x" + graphSpeed, () -> { -+ graphSpeed *= 2; -+ if(graphSpeed > maxGraphSpeed) graphSpeed = 1; -+ }).update(b -> b.setText("x" + graphSpeed)).width(150f); -+ }).growX(); -+ }else{ -+ tb.pane(p -> { -+ p.table(Tex.button, t -> { -+ p.margin(0).defaults().pad(5).growX(); -+ t.add("\uE86D 为单位数量;\uE813 为单位血+盾;\uE810 为计算buff的血+盾;\uE86E 为预估DPS。在游戏中时会考虑地图出怪点数目").color(Pal.accent); -+ }).scrollX(false).growX().row(); -+ for(int wave = 0; wave < winWave * waveMulti; wave++){ -+ ArcWaveSpawner.waveInfo thisWave = ArcWaveSpawner.getOrInit(wave); -+ thisWave.specLoc(-1, group -> true); -+ int finalWave = wave; -+ p.table(Tex.button, t -> { -+ t.table(tt -> { -+ tt.add("第[accent]" + (finalWave + 1) + "[]波"); -+ tt.row(); -+ float firstWaveTime = state.rules.initialWaveSpacing <= 0 ? (2 * state.rules.waveSpacing) : state.rules.initialWaveSpacing; -+ int thisTime = (int)(finalWave * state.rules.waveSpacing + firstWaveTime); -+ tt.add(FormatDefault.duration(thisTime / 60f, false)).row(); -+ Label waveTime = tt.add("").get(); -+ tt.update(() -> { -+ if(!state.isGame()) waveTime.setText(""); -+ else{ -+ int deltaTime = thisTime - (int)(state.wave <= 1 ? (firstWaveTime - state.wavetime) : (firstWaveTime + state.rules.waveSpacing * (state.wave - 1) - state.wavetime)); -+ waveTime.setText(FormatDefault.duration(deltaTime / 60, false)); -+ } -+ }); -+ }).width(120f).left(); -+ if(thisWave.amount == 0) t.add("该波次没有敌人"); -+ else{ -+ t.add(thisWave.proTable(true)); -+ t.pane(thisWave.unitTable(-1, group -> true, mobile ? 8 : 15)).scrollX(true).scrollY(false).growX(); -+ } -+ }).growX().row(); -+ p.margin(0).defaults().pad(5).growX(); -+ } -+ }).scrollX(false).growX().row(); -+ tb.table(tbb -> { -+ tbb.button("刷新波次显示", this::setup).width(200f); -+ TextField sField = tbb.field(winWave * (int)waveMulti + "", text -> waveMulti = (Float.parseFloat(text) / winWave)).valid(Strings::canParsePositiveFloat).width(200f).get(); -+ tbb.slider(0.25f, 10f, 0.25f, 1f, t -> { -+ waveMulti = t; -+ sField.setText(winWave * (int)waveMulti + ""); -+ }).width(300f); -+ }); -+ } -+ }).grow(); -+ -+ -+ buildGroups(); -+ } -+ -+ void buildGroups(){ -+ table.clear(); -+ table.top(); -+ table.margin(10f); -+ -+ if(groups != null){ -+ groups.sort(sort.sort); -+ if(reverseSort) groups.reverse(); -+ -+ for(SpawnGroup group : groups){ -+ if((search >= 0 && group.getSpawned(search) <= 0) -+ || (filterHealth != 0 && !(filterHealthMode ? group.type.health * (search >= 0 ? group.getSpawned(search) : 1) > filterHealth : group.type.health * (search >= 0 ? group.getSpawned(search) : 1) < filterHealth)) -+ || (filterBegin >= 0 && !(filterStrict ? group.begin == filterBegin : group.begin - 2 <= filterBegin && group.begin + 2 >= filterBegin)) -+ || (filterEnd >= 0 && !(filterStrict ? group.end == filterEnd : group.end - 2 <= filterEnd && group.end + 2 >= filterEnd)) -+ || (filterAmount != 0 && !(filterStrict ? group.getSpawned(filterAmountWave) == filterAmount : filterAmount - 5 <= group.getSpawned(filterAmountWave) && filterAmount + 5 >= group.getSpawned(filterAmountWave))) -+ || (filterEffect != StatusEffects.none && group.effect != filterEffect)) continue; -+ -+ table.table(Tex.button, t -> { -+ t.margin(0).defaults().pad(3).padLeft(5f).growX().left(); -+ t.button(b -> { -+ b.left(); -+ b.image(group.type.uiIcon).size(32f).padRight(3).scaling(Scaling.fit); -+ b.add(group.type.typeColor() + group.type.localizedName); -+ if(group.effect != null && group.effect != StatusEffects.none) -+ b.image(group.effect.uiIcon).size(20f).padRight(3).scaling(Scaling.fit); -+ if(group.items != null && group.items.amount > 0) -+ b.image(group.items.item.uiIcon).size(20f).padRight(3).scaling(Scaling.fit); -+ if(group.payloads != null && group.payloads.size > 0) -+ b.image(Icon.uploadSmall).size(20f).padRight(3).scaling(Scaling.fit); -+ -+ b.add().growX(); -+ -+ b.label(() -> { -+ StringBuilder builder = new StringBuilder(); -+ builder.append("[lightgray]").append(group.begin + 1); -+ if(group.begin == group.end) return builder.toString(); -+ if(group.end > 999999) builder.append("+"); -+ else builder.append("~").append(group.end + 1); -+ if(group.spacing > 1) builder.append("[white]|[lightgray]").append(group.spacing); -+ return builder.append(" ").toString(); -+ }).minWidth(45f).labelAlign(Align.left).left(); -+ -+ b.button(Icon.settingsSmall, Styles.emptyi, () -> unitSettingDialog(group)).pad(-6).size(46f); -+ b.button(Icon.unitsSmall, Styles.emptyi, () -> showUpdate(group, false)).pad(-6).size(46f); -+ b.button(Icon.cancel, Styles.emptyi, () -> { -+ if(expandedGroup == group) expandedGroup = null; -+ groups.remove(group); -+ table.getCell(t).pad(0f); -+ t.remove(); -+ buildGroups(); -+ }).pad(-6).size(46f).padRight(-12f); -+ }, () -> { -+ expandedGroup = expandedGroup == group ? null : group; -+ buildGroups(); -+ }).height(46f).pad(-6f).padBottom(0f).row(); -+ -+ if(expandedGroup == group){ -+ t.table(spawns -> { -+ spawns.field("" + (group.begin + 1), TextFieldFilter.digitsOnly, text -> { -+ if(Strings.canParsePositiveInt(text)){ -+ group.begin = Strings.parseInt(text) - 1; -+ updateWaves(); -+ } -+ }).width(100f); -+ spawns.add("@waves.to").padLeft(4).padRight(4); -+ spawns.field(group.end == never ? "" : (group.end + 1) + "", TextFieldFilter.digitsOnly, text -> { -+ if(Strings.canParsePositiveInt(text)){ -+ group.end = Strings.parseInt(text) - 1; -+ updateWaves(); -+ }else if(text.isEmpty()){ -+ group.end = never; -+ updateWaves(); -+ } -+ }).width(100f).get().setMessageText("∞"); -+ }).row(); -+ -+ t.table(p -> { -+ p.add("@waves.every").padRight(4); -+ p.field(group.spacing + "", TextFieldFilter.digitsOnly, text -> { -+ if(Strings.canParsePositiveInt(text) && Strings.parseInt(text) > 0){ -+ group.spacing = Strings.parseInt(text); -+ updateWaves(); -+ } -+ }).width(100f); -+ p.add("@waves.waves").padLeft(4); -+ }).row(); -+ -+ t.table(a -> { -+ a.field(group.unitAmount + "", TextFieldFilter.digitsOnly, text -> { -+ if(Strings.canParsePositiveInt(text)){ -+ group.unitAmount = Strings.parseInt(text); -+ updateWaves(); -+ } -+ }).width(80f); -+ -+ a.add(" + "); -+ a.field(Strings.fixed(Math.max((Mathf.zero(group.unitScaling) ? 0 : 1f / group.unitScaling), 0), 2), TextFieldFilter.floatsOnly, text -> { -+ if(Strings.canParsePositiveFloat(text)){ -+ group.unitScaling = 1f / Strings.parseFloat(text); -+ updateWaves(); -+ } -+ }).width(80f); -+ a.add("@waves.perspawn").padLeft(4); -+ }).row(); -+ -+ t.table(a -> { -+ a.field(group.max + "", TextFieldFilter.digitsOnly, text -> { -+ if(Strings.canParsePositiveInt(text)){ -+ group.max = Strings.parseInt(text); -+ updateWaves(); -+ } -+ }).width(80f); -+ -+ a.add("@waves.max").padLeft(5); -+ }).row(); -+ -+ t.table(a -> { -+ a.field((int)group.shields + "", TextFieldFilter.digitsOnly, text -> { -+ if(Strings.canParsePositiveInt(text)){ -+ group.shields = Strings.parseInt(text); -+ updateWaves(); -+ } -+ }).width(80f); -+ -+ a.add(" + "); -+ a.field((int)group.shieldScaling + "", TextFieldFilter.digitsOnly, text -> { -+ if(Strings.canParsePositiveInt(text)){ -+ group.shieldScaling = Strings.parseInt(text); -+ updateWaves(); -+ } -+ }).width(80f); -+ a.add("@waves.shields").padLeft(4); -+ }).row(); -+ -+ t.table(a -> { -+ a.add("@waves.spawn").padRight(8); -+ -+ a.button("", () -> { -+ if(!checkedSpawns){ -+ //recalculate waves when changed -+ spawner.reset(); -+ checkedSpawns = true; -+ } -+ -+ BaseDialog dialog = new BaseDialog("@waves.spawn.select"); -+ dialog.cont.pane(p -> { -+ p.background(Tex.button).margin(10f); -+ int i = 0; -+ int cols = 4; -+ int max = 20; -+ -+ if(spawner.getSpawns().size >= max){ -+ p.add("[lightgray](first " + max + ")").colspan(cols).padBottom(4).row(); -+ } -+ -+ for(var spawn : spawner.getSpawns()){ -+ p.button(spawn.x + ", " + spawn.y, Styles.flatTogglet, () -> { -+ group.spawn = Point2.pack(spawn.x, spawn.y); -+ dialog.hide(); -+ }).size(110f, 45f).checked(spawn.pos() == group.spawn); -+ -+ if(++i % cols == 0){ -+ p.row(); -+ } -+ -+ //only display first 20 spawns, you don't need to see more. -+ if(i >= 20){ -+ break; -+ } -+ } -+ -+ p.button("@waves.spawn.all", Styles.flatTogglet, () -> { -+ group.spawn = -1; -+ dialog.hide(); -+ }).size(110f, 45f).checked(-1 == group.spawn); -+ -+ if(spawner.getSpawns().isEmpty()){ -+ p.add("@waves.spawn.none"); -+ } -+ }); -+ dialog.setFillParent(false); -+ dialog.addCloseButton(); -+ dialog.show(); -+ }).width(160f).height(36f).get().getLabel().setText(() -> group.spawn == -1 ? "@waves.spawn.all" : Point2.x(group.spawn) + ", " + Point2.y(group.spawn)); -+ -+ }).padBottom(8f).row(); -+ } -+ }).width(350f).pad(8); -+ -+ table.row(); -+ } -+ }else{ -+ table.add("@editor.default"); -+ } -+ -+ updateWaves(); -+ } -+ -+ void showUpdate(SpawnGroup group, boolean payloads){ -+ BaseDialog dialog = new BaseDialog(""); -+ dialog.setFillParent(true); -+ if(payloads && group.payloads == null) group.payloads = Seq.with(); -+ if(payloads) dialog.cont.table(e -> { -+ uTable = e; -+ updateIcons(group); -+ }).padBottom(6f).row(); -+ dialog.cont.pane(p -> { -+ int i = 0; -+ for(UnitType type : content.units()){ -+ if(type.internal) continue; -+ if(type.isHidden() && !(Core.settings.getBool("developmode"))) continue; -+ p.button(t -> { -+ t.left(); -+ t.image(type.uiIcon).size(8 * 4).scaling(Scaling.fit).padRight(2f); -+ t.add(type.localizedName); -+ }, () -> { -+ if(payloads){ -+ group.payloads.add(type); -+ updateIcons(group); -+ }else{ -+ group.type = lastType = type; -+ updateIcons(group); -+ dialog.hide(); -+ } -+ if(group.payloads != null && group.type.payloadCapacity <= 8) group.payloads.clear(); -+ if(group.items != null) -+ group.items.amount = Mathf.clamp(group.items.amount, 0, group.type.itemCapacity); -+ buildGroups(); -+ }).pad(2).margin(12f).fillX(); -+ if(++i % 5 == 0) p.row(); -+ } -+ }); -+ dialog.addCloseButton(); -+ dialog.show(); -+ } -+ -+ void showEffect(SpawnGroup group){ -+ BaseDialog dialog = new BaseDialog(""); -+ dialog.setFillParent(true); -+ dialog.cont.pane(p -> { -+ int i = 0; -+ for(StatusEffect effect : content.statusEffects()){ -+ if(effect != StatusEffects.none && effect.reactive) continue; -+ -+ p.button(t -> { -+ t.left(); -+ if(effect.uiIcon != null && effect != StatusEffects.none){ -+ t.image(effect.uiIcon).size(8 * 4).scaling(Scaling.fit).padRight(2f); -+ }else{ -+ t.image(Icon.none).size(8 * 4).scaling(Scaling.fit).padRight(2f); -+ } -+ -+ if(effect != StatusEffects.none){ -+ t.add(effect.localizedName); -+ }else{ -+ t.add("@settings.resetKey"); -+ } -+ }, () -> { -+ if(group == null){ -+ filterEffect = effect; -+ }else{ -+ group.effect = effect; -+ } -+ updateIcons(group); -+ dialog.hide(); -+ buildGroups(); -+ }).pad(2).margin(12f).fillX(); -+ if(++i % 3 == 0) p.row(); -+ } -+ }); -+ dialog.addCloseButton(); -+ dialog.show(); -+ } -+ -+ void showItems(SpawnGroup group){ -+ BaseDialog dialog = new BaseDialog(""); -+ dialog.setFillParent(true); -+ dialog.cont.table(items -> { -+ items.add(Core.bundle.get("filter.option.amount") + ":"); -+ amountField = items.field(group.items != null ? group.items.amount + "" : "", TextFieldFilter.digitsOnly, text -> { -+ if(Strings.canParsePositiveInt(text) && group.items != null){ -+ group.items.amount = Strings.parseInt(text) <= 0 ? group.type.itemCapacity : Mathf.clamp(Strings.parseInt(text), 0, group.type.itemCapacity); -+ } -+ }).width(120f).pad(2).margin(12f).maxTextLength((group.type.itemCapacity + "").length() + 1).get(); -+ amountField.setMessageText(group.type.itemCapacity + ""); -+ }).padBottom(6f).row(); -+ dialog.cont.pane(p -> { -+ int i = 1; -+ p.defaults().pad(2).margin(12f).minWidth(200f).fillX(); -+ p.button(icon -> { -+ icon.left(); -+ icon.image(Icon.none).size(8 * 4).scaling(Scaling.fit).padRight(2f); -+ icon.add("@settings.resetKey"); -+ }, () -> { -+ group.items = null; -+ updateIcons(group); -+ dialog.hide(); -+ buildGroups(); -+ }); -+ for(Item item : content.items()){ -+ p.button(t -> { -+ t.left(); -+ if(item.uiIcon != null) t.image(item.uiIcon).size(8 * 4).scaling(Scaling.fit).padRight(2f); -+ t.add(item.localizedName); -+ }, () -> { -+ group.items = new ItemStack(item, Strings.parseInt(amountField.getText()) <= 0 ? group.type.itemCapacity : Mathf.clamp(Strings.parseInt(amountField.getText()), 0, group.type.itemCapacity)); -+ updateIcons(group); -+ dialog.hide(); -+ buildGroups(); -+ }); -+ if(++i % 3 == 0) p.row(); -+ } -+ }); -+ dialog.addCloseButton(); -+ dialog.show(); -+ } -+ -+ void showFilter(){ -+ BaseDialog dialog = new BaseDialog("@waves.filter"); -+ dialog.setFillParent(false); -+ dialog.cont.defaults().size(210f, 64f); -+ dialog.cont.add(Core.bundle.get("waves.sort.health") + ":"); -+ dialog.cont.table(filter -> { -+ filter.button(">", Styles.cleart, () -> { -+ filterHealthMode = !filterHealthMode; -+ buildGroups(); -+ }).update(b -> b.setText(filterHealthMode ? ">" : "<")).size(40f).padRight(4f); -+ filter.defaults().width(170f); -+ numField("", filter, f -> filterHealth = f, () -> filterHealth, 15); -+ }).row(); -+ -+ dialog.cont.add("@waves.filter.begin"); -+ dialog.cont.table(filter -> { -+ filter.defaults().maxWidth(120f); -+ numField("", filter, f -> filterBegin = f - 1, () -> filterBegin + 1, 8); -+ numField("@waves.to", filter, f -> filterEnd = f - 1, () -> filterEnd + 1, 8); -+ }).row(); -+ -+ dialog.cont.add(Core.bundle.get("waves.filter.amount") + ":"); -+ dialog.cont.table(filter -> { -+ filter.defaults().maxWidth(120f); -+ numField("", filter, f -> filterAmount = f, () -> filterAmount, 12); -+ numField("@waves.filter.onwave", filter, f -> filterAmountWave = f, () -> filterAmountWave, 8); -+ }).row(); -+ -+ dialog.cont.table(t -> { -+ eTable = t; -+ updateIcons(null); -+ }).row(); -+ dialog.row(); -+ dialog.check("@waves.filter.strict", b -> { -+ filterStrict = b; -+ buildGroups(); -+ }).checked(filterStrict).padBottom(10f).row(); -+ -+ dialog.table(p -> { -+ p.defaults().size(210f, 64f).padLeft(4f).padRight(4f); -+ p.button("@back", Icon.left, dialog::hide); -+ p.button("@clear", Icon.refresh, () -> { -+ clearFilter(); -+ buildGroups(); -+ dialog.hide(); -+ }); -+ }); -+ dialog.addCloseListener(); -+ dialog.show(); -+ } -+ -+ void unitSettingDialog(SpawnGroup group){ -+ BaseDialog dialog = new BaseDialog("设置出怪组"); -+ dialog.setFillParent(false); -+ dialog.cont.table(Tex.button, a -> nTable = a).row(); -+ dialog.cont.table(Tex.button, a -> oTable = a).row(); -+ dialog.cont.table(Tex.button, a -> iTable = a).row(); -+ dialog.cont.table(c -> { -+ c.defaults().size(210f, 64f).pad(2f); -+ c.button("@waves.duplicate", Icon.copy, () -> { -+ SpawnGroup newGroup = group.copy(); -+ groups.add(newGroup); -+ expandedGroup = newGroup; -+ buildGroups(); -+ dialog.hide(); -+ }); -+ c.button("@settings.resetKey", Icon.refresh, () -> ui.showConfirm("@confirm", "@settings.clear.confirm", () -> { -+ group.effect = StatusEffects.none; -+ group.payloads = Seq.with(); -+ group.items = null; -+ buildGroups(); -+ dialog.hide(); -+ })); -+ }); -+ buildGroups(); -+ updateIcons(group); -+ dialog.addCloseButton(); -+ dialog.show(); -+ } -+ -+ void updateIcons(SpawnGroup group){ -+ if(nTable != null && group != null){ -+ nTable.clear(); -+ nTable.defaults().size(400f, 50f).pad(2f); -+ nTable.table(t -> { -+ t.image(group.type.uiIcon).size(32f).padRight(5).scaling(Scaling.fit).get(); -+ //if(group.effect != null && group.effect != StatusEffects.none) b.image(group.effect.uiIcon).size(20f).padRight(3).scaling(Scaling.fit); -+ t.add(group.type.localizedName).padRight(20).color(Pal.accent); -+ t.button(Icon.units, Styles.emptyi, () -> showUpdate(group, false)).pad(-6).size(50f); -+ }).growX().row(); -+ } -+ -+ if(oTable != null && group != null){ -+ oTable.clear(); -+ oTable.defaults().size(400f, 250f).pad(2f); -+ oTable.table(t -> { -+ t.table(spawns -> { -+ spawns.field("" + (group.begin + 1), TextFieldFilter.digitsOnly, text -> { -+ if(Strings.canParsePositiveInt(text)){ -+ group.begin = Strings.parseInt(text) - 1; -+ updateWaves(); -+ } -+ }).width(150f); -+ spawns.add("@waves.to").padLeft(4).padRight(4); -+ spawns.field(group.end == never ? "" : (group.end + 1) + "", TextFieldFilter.digitsOnly, text -> { -+ if(Strings.canParsePositiveInt(text)){ -+ group.end = Strings.parseInt(text) - 1; -+ updateWaves(); -+ }else if(text.isEmpty()){ -+ group.end = never; -+ updateWaves(); -+ } -+ }).width(150f).get().setMessageText("∞"); -+ }).row(); -+ -+ t.table(p -> { -+ p.add("@waves.every").padRight(4); -+ p.field(group.spacing + "", TextFieldFilter.digitsOnly, text -> { -+ if(Strings.canParsePositiveInt(text) && Strings.parseInt(text) > 0){ -+ group.spacing = Strings.parseInt(text); -+ updateWaves(); -+ } -+ }).width(150f); -+ p.add("@waves.waves").padLeft(4); -+ }).row(); -+ -+ t.table(a -> { -+ a.field(group.unitAmount + "", TextFieldFilter.digitsOnly, text -> { -+ if(Strings.canParsePositiveInt(text)){ -+ group.unitAmount = Strings.parseInt(text); -+ updateWaves(); -+ } -+ }).width(150f); -+ -+ a.add(" + "); -+ a.field(Strings.fixed(Math.max((Mathf.zero(group.unitScaling) ? 0 : 1f / group.unitScaling), 0), 2), TextFieldFilter.floatsOnly, text -> { -+ if(Strings.canParsePositiveFloat(text)){ -+ group.unitScaling = 1f / Strings.parseFloat(text); -+ updateWaves(); -+ } -+ }).width(150f); -+ a.add("@waves.perspawn").padLeft(4); -+ }).row(); -+ -+ t.table(a -> { -+ a.field(group.max + "", TextFieldFilter.digitsOnly, text -> { -+ if(Strings.canParsePositiveInt(text)){ -+ group.max = Strings.parseInt(text); -+ updateWaves(); -+ } -+ }).width(150f); -+ -+ a.add("@waves.max").padLeft(5); -+ }).row(); -+ -+ t.table(a -> { -+ a.field((int)group.shields + "", TextFieldFilter.digitsOnly, text -> { -+ if(Strings.canParsePositiveInt(text)){ -+ group.shields = Strings.parseInt(text); -+ updateWaves(); -+ } -+ }).width(150f); -+ -+ a.add(" + "); -+ a.field((int)group.shieldScaling + "", TextFieldFilter.digitsOnly, text -> { -+ if(Strings.canParsePositiveInt(text)){ -+ group.shieldScaling = Strings.parseInt(text); -+ updateWaves(); -+ } -+ }).width(150f); -+ a.add("@waves.shields").padLeft(4); -+ }).row(); -+ -+ t.table(a -> { -+ a.add("@waves.spawn").padRight(8); -+ -+ a.button("", () -> { -+ if(!checkedSpawns){ -+ //recalculate waves when changed -+ spawner.reset(); -+ checkedSpawns = true; -+ } -+ -+ BaseDialog dialog = new BaseDialog("@waves.spawn.select"); -+ dialog.cont.pane(p -> { -+ p.background(Tex.button).margin(10f); -+ int i = 0; -+ int cols = 4; -+ int max = 20; -+ -+ if(spawner.getSpawns().size >= max){ -+ p.add("[lightgray](first " + max + ")").colspan(cols).padBottom(4).row(); -+ } -+ -+ for(var spawn : spawner.getSpawns()){ -+ p.button(spawn.x + ", " + spawn.y, Styles.flatTogglet, () -> { -+ group.spawn = Point2.pack(spawn.x, spawn.y); -+ dialog.hide(); -+ }).size(110f, 45f).checked(spawn.pos() == group.spawn); -+ -+ if(++i % cols == 0){ -+ p.row(); -+ } -+ -+ //only display first 20 spawns, you don't need to see more. -+ if(i >= 20){ -+ break; -+ } -+ } -+ -+ p.button("@waves.spawn.all", Styles.flatTogglet, () -> { -+ group.spawn = -1; -+ dialog.hide(); -+ }).size(110f, 45f).checked(-1 == group.spawn); -+ -+ if(spawner.getSpawns().isEmpty()){ -+ p.add("@waves.spawn.none"); -+ } -+ }); -+ dialog.setFillParent(false); -+ dialog.addCloseButton(); -+ dialog.show(); -+ }).width(160f).height(36f).get().getLabel().setText(() -> group.spawn == -1 ? "@waves.spawn.all" : Point2.x(group.spawn) + ", " + Point2.y(group.spawn)); -+ -+ }).padBottom(8f).row(); -+ -+ -+ }).growX().row(); -+ } -+ -+ if(iTable != null && group != null){ -+ iTable.clear(); -+ iTable.defaults().size(200f, 60f).pad(2f); -+ iTable.button(icon -> { -+ icon.add("状态"); -+ if(group.effect != null && group.effect != StatusEffects.none){ -+ icon.image(group.effect.uiIcon).padLeft(6f); -+ }else{ -+ icon.image(Icon.logic).padLeft(6f); -+ } -+ }, Styles.cleart, () -> showEffect(group)); -+ if(group.type.payloadCapacity > 0) iTable.button("添加载荷", Styles.cleart, () -> showUpdate(group, true)); -+ iTable.button(icon -> { -+ icon.add("物品"); -+ if(group.items != null){ -+ icon.image(group.items.item.uiIcon).padLeft(6f); -+ icon.add("" + group.items.amount).padLeft(6f); -+ }else{ -+ icon.image(Icon.effect).padLeft(6f); -+ } -+ -+ }, Styles.cleart, () -> showItems(group)); -+ } -+ -+ if(eTable != null){ -+ eTable.clear(); -+ eTable.add(Core.bundle.get("waves.filter.effect") + ":"); -+ eTable.button(filterEffect != null && filterEffect != StatusEffects.none ? -+ new TextureRegionDrawable(filterEffect.uiIcon) : -+ Icon.logic, () -> showEffect(null)).padLeft(30f).size(60f); -+ } -+ -+ if(uTable != null && group != null && group.payloads != null){ -+ uTable.clear(); -+ uTable.left(); -+ uTable.defaults().pad(3); -+ uTable.table(units -> { -+ int i = 0; -+ for(UnitType payl : group.payloads){ -+ if(i < maxVisible || expandPane) units.table(Tex.button, s -> { -+ s.image(payl.uiIcon).size(45f); -+ s.button(Icon.cancelSmall, Styles.emptyi, () -> { -+ group.payloads.remove(payl); -+ updateIcons(group); -+ buildGroups(); -+ }).size(20f).padRight(-9f).padLeft(-6f); -+ }).pad(2).margin(12f).fillX(); -+ if(++i % 10 == 0) units.row(); -+ } -+ }); -+ uTable.table(b -> { -+ b.defaults().pad(2); -+ if(group.payloads.size > 1) b.button(Icon.cancel, () -> { -+ group.payloads.clear(); -+ updateIcons(group); -+ buildGroups(); -+ }).tooltip("@clear").row(); -+ if(group.payloads.size > maxVisible) b.button(expandPane ? Icon.eyeSmall : Icon.eyeOffSmall, () -> { -+ expandPane = !expandPane; -+ updateIcons(group); -+ }).size(45f).tooltip(expandPane ? "@server.shown" : "@server.hidden"); -+ }).padLeft(6f); -+ } -+ } -+ -+ void numField(String text, Table t, Intc cons, Intp prov, int maxLength){ -+ if(!text.isEmpty()) t.add(text); -+ t.field(prov.get() + "", TextFieldFilter.digitsOnly, input -> { -+ if(Strings.canParsePositiveInt(input)){ -+ cons.get(!input.isEmpty() ? Strings.parseInt(input) : 0); -+ buildGroups(); -+ } -+ }).maxTextLength(maxLength); -+ } -+ -+ void clearFilter(){ -+ filterHealth = filterAmount = filterAmountWave = 0; -+ filterStrict = filterHealthMode = false; -+ filterBegin = filterEnd = -1; -+ filterEffect = StatusEffects.none; -+ } -+ -+ enum Sort{ -+ begin(Structs.comps(Structs.comparingFloat(g -> g.begin), Structs.comparingFloat(g -> g.type.id))), -+ health(Structs.comps(Structs.comparingFloat(g -> g.type.health), Structs.comparingFloat(g -> g.begin))), -+ type(Structs.comps(Structs.comparingFloat(g -> g.type.id), Structs.comparingFloat(g -> g.begin))); -+ -+ static final Sort[] all = values(); -+ -+ final Comparator sort; -+ -+ Sort(Comparator sort){ -+ this.sort = sort; -+ } -+ } -+ -+ void updateWaves(){ -+ graph.groups = groups; -+ graph.from = start; -+ graph.to = start + displayed; -+ graph.rebuild(); -+ } -+ -+ void arcSpawner(){ -+ BaseDialog dialog = new BaseDialog("ARC-随机生成器"); -+ -+ Table table = dialog.cont; -+ Runnable[] rebuild = {null}; -+ rebuild[0] = () -> { -+ -+ table.clear(); -+ table.table(c -> { -+ c.table(ct -> { -+ ct.add("难度:").width(100f); -+ ct.field(difficult + "", text -> difficult = Float.parseFloat(text)).valid(Strings::canParsePositiveFloat).width(200f); -+ }).width(300f); -+ c.row(); -+ c.button("单位设置", showUnitSelect ? Icon.upOpen : Icon.downOpen, Styles.togglet, () -> { -+ showUnitSelect = !showUnitSelect; -+ rebuild[0].run(); -+ }).fillX().minWidth(400f).row(); -+ c.row(); -+ if(showUnitSelect){ -+ c.table(list -> { -+ for(UnitType unit : content.units()){ -+ if(unit.internal) continue; -+ list.button(unit.emoji(), flatToggleMenut, () -> { -+ if(spawnUnit.contains(unit)) spawnUnit.remove(unit); -+ else spawnUnit.add(unit); -+ rebuild[0].run(); -+ }).tooltip(unit.localizedName).checked(spawnUnit.contains(unit)).size(50f); -+ if(list.getChildren().size % 8 == 0) list.row(); -+ } -+ }).row(); -+ c.table(ct -> { -+ ct.add("环境").width(50f); -+ ct.button("Surplus", flatToggleMenut, () -> { -+ surplusUnit = !surplusUnit; -+ for(UnitType unit : allowUnit.copy().retainAll(unitType -> !(unitType instanceof ErekirUnitType))){ -+ filterUnit(unit, surplusUnit); -+ } -+ rebuild[0].run(); -+ }).checked(surplusUnit).width(120f); -+ ct.button("Erekir", flatToggleMenut, () -> { -+ ErekirUnit = !ErekirUnit; -+ for(UnitType unit : allowUnit.copy().retainAll(unitType -> unitType instanceof ErekirUnitType)){ -+ filterUnit(unit, ErekirUnit); -+ } -+ rebuild[0].run(); -+ }).checked(ErekirUnit).width(120f); -+ }); -+ c.row(); -+ c.table(ct -> { -+ ct.add("兵种").width(50f); -+ ct.button("空军", flatToggleMenut, () -> { -+ flyingUnit = !flyingUnit; -+ for(UnitType unit : allowUnit.copy().retainAll(unitType -> unitType.flying)){ -+ filterUnit(unit, flyingUnit); -+ } -+ rebuild[0].run(); -+ }).checked(flyingUnit).width(70f); -+ ct.button("海军", flatToggleMenut, () -> { -+ navalUnit = !navalUnit; -+ for(UnitType unit : allowUnit.copy().retainAll(unitType -> unitType.naval)){ -+ filterUnit(unit, navalUnit); -+ } -+ rebuild[0].run(); -+ }).checked(navalUnit).width(70f); -+ ct.button("支援", flatToggleMenut, () -> { -+ supportUnit = !supportUnit; -+ for(UnitType unit : allowUnit.copy().retainAll(unitType -> unitType.controller instanceof BuilderAI || unitType.controller instanceof MinerAI || unitType.controller instanceof RepairAI)){ -+ filterUnit(unit, supportUnit); -+ } -+ rebuild[0].run(); -+ }).checked(supportUnit).width(70f); -+ }); -+ } -+ c.row(); -+ c.button("生成!", () -> { -+ groups.clear(); -+ groups = Waves.generate(difficult / 10, new Rand(), flyingUnit, navalUnit, supportUnit); -+ updateWaves(); -+ buildGroups(); -+ }).width(300f); -+ }); -+ -+ }; -+ rebuild[0].run(); -+ dialog.addCloseButton(); -+ dialog.show(); -+ ui.announce("功能制作中..请等待完成\n[orange]目前可调整:难度|空|海|辅,其他功能均无效", 15); -+ } -+ -+ private void filterUnit(UnitType unit, boolean filter){ -+ if(filter && !spawnUnit.contains(unit)){ -+ spawnUnit.add(unit); -+ }else if(!filter && spawnUnit.contains(unit)){ -+ spawnUnit.remove(unit); -+ } -+ } -+} -diff --git a/core/src/mindustryX/features/ui/BlockSelectDialog.java b/core/src/mindustryX/features/ui/BlockSelectDialog.java -new file mode 100644 -index 0000000000000000000000000000000000000000..c760d546abc01ce03ce6d58e0e12d0b5acff22e6 ---- /dev/null -+++ b/core/src/mindustryX/features/ui/BlockSelectDialog.java -@@ -0,0 +1,65 @@ -+package mindustryX.features.ui; -+ -+import arc.func.*; -+import arc.scene.style.*; -+import arc.scene.ui.layout.*; -+import arc.struct.*; -+import mindustry.graphics.*; -+import mindustry.ui.*; -+import mindustry.ui.dialogs.*; -+import mindustry.world.*; -+import mindustry.world.meta.*; -+ -+import static mindustry.Vars.*; -+ -+//move from mindustry.arcModule.ui.dialogs.BlockSelectDialog -+public class BlockSelectDialog extends BaseDialog{ -+ private final Boolf condition; -+ private final Cons cons; -+ private final Boolf checked; -+ private final boolean autoHide; -+ private String searchBlock = ""; -+ private final Table blockTable = new Table(); -+ -+ public BlockSelectDialog(Boolf condition, Cons cons, Boolf checked){ -+ this(condition, cons, checked, true); -+ } -+ -+ public BlockSelectDialog(Boolf condition, Cons cons, Boolf checked, boolean autoHide){ -+ super("方块选择器"); -+ this.condition = condition; -+ this.cons = cons; -+ this.checked = checked; -+ this.autoHide = autoHide; -+ cont.pane(td -> { -+ td.field("", t -> { -+ searchBlock = !t.isEmpty() ? t.toLowerCase() : ""; -+ rebuild(); -+ }).maxTextLength(50).growX().get().setMessageText("搜索..."); -+ td.row(); -+ td.add(blockTable); -+ }); -+ rebuild(); -+ addCloseButton(); -+ } -+ -+ private void rebuild(){ -+ blockTable.clear(); -+ blockTable.table(td -> { -+ Seq blocks = content.blocks().select(block -> condition.get(block) && (searchBlock.isEmpty() || block.name.contains(searchBlock) || block.localizedName.contains(searchBlock)) && block.isVisible()).sort(block -> block.group.ordinal()); -+ Seq blockGroups = blocks.map(block -> block.group).distinct(); -+ blockGroups.each(blockGroup -> { -+ td.row(); -+ td.add(blockGroup.toString()).row(); -+ td.image().color(Pal.accent).fillX().row(); -+ td.table(ttd -> blocks.select(block1 -> block1.group == blockGroup).each(block1 -> { -+ ttd.button(new TextureRegionDrawable(block1.uiIcon), Styles.cleari, iconSmall, () -> { -+ cons.get(block1); -+ if(autoHide) hide(); -+ }).tooltip(block1.localizedName).pad(3f).checked(b -> checked.get(block1)).size(50f); -+ if(ttd.getChildren().size % 10 == 0) ttd.row(); -+ })); -+ }); -+ }); -+ } -+} -diff --git a/core/src/mindustryX/features/ui/HudSettingsTable.java b/core/src/mindustryX/features/ui/HudSettingsTable.java -index 8c8b64c30c397e7147f6f6de5bc56f9801f17be1..0561ba8c9525729524ab247f9c4ee48a273ec7db 100644 ---- a/core/src/mindustryX/features/ui/HudSettingsTable.java -+++ b/core/src/mindustryX/features/ui/HudSettingsTable.java -@@ -75,8 +75,8 @@ public class HudSettingsTable extends ToolTableBase{ - ui.announce("已移除逻辑视角锁定"); - }).checked(a -> Core.settings.getBool("removeLogicLock")).size(30, 30).tooltip("逻辑锁定"); - t.button("[cyan]雾", Styles.flatTogglet, () -> { -- if(!state.rules.pvp || player.team().id == 255) renderer.fogEnabled = !renderer.fogEnabled; -- }).checked(a -> renderer.fogEnabled).size(30, 30).tooltip("战争迷雾").visible(() -> !state.rules.pvp || player.team().id == 255); -+ state.rules.fog ^= true; -+ }).checked(a -> state.rules.fog).size(30, 30).tooltip("战争迷雾").disabled((_t) -> state.rules.pvp && player.team().id != 255); - }).left().row(); - cont.table(t -> { - t.button("[red]灯", Styles.flatTogglet, () -> Settings.toggle("drawlight"))