1
0
mirror of https://github.com/soarqin/DSP_Mods.git synced 2026-05-14 12:37:11 +08:00

Compare commits

1 Commits

Author SHA1 Message Date
01dcf90f35 WIP 2024-01-12 19:37:20 +08:00
117 changed files with 3675 additions and 12066 deletions

View File

@@ -0,0 +1,69 @@
using System.Collections.Generic;
using BepInEx.Configuration;
using HarmonyLib;
namespace CheatEnabler;
public static class AbnormalDisabler
{
public static ConfigEntry<bool> Enabled;
private static Dictionary<int, AbnormalityDeterminator> _savedDeterminators;
private static Harmony _patch;
public static void Init()
{
_patch ??= Harmony.CreateAndPatchAll(typeof(AbnormalDisabler));
}
public static void Uninit()
{
_patch?.UnpatchSelf();
_patch = null;
}
[HarmonyPrefix]
[HarmonyPatch(typeof(AbnormalityLogic), "NotifyBeforeGameSave")]
[HarmonyPatch(typeof(AbnormalityLogic), "NotifyOnAssemblerRecipePick")]
[HarmonyPatch(typeof(AbnormalityLogic), "NotifyOnGameBegin")]
[HarmonyPatch(typeof(AbnormalityLogic), "NotifyOnMechaForgeTaskComplete")]
[HarmonyPatch(typeof(AbnormalityLogic), "NotifyOnUnlockTech")]
[HarmonyPatch(typeof(AbnormalityLogic), "NotifyOnUseConsole")]
private static bool DisableAbnormalLogic()
{
return !Enabled.Value;
}
[HarmonyPostfix]
[HarmonyPatch(typeof(AbnormalityLogic), "InitDeterminators")]
private static void DisableAbnormalDeterminators(AbnormalityLogic __instance)
{
_savedDeterminators = __instance.determinators;
Enabled.SettingChanged += (_, _) =>
{
if (Enabled.Value)
{
_savedDeterminators = __instance.determinators;
__instance.determinators = new Dictionary<int, AbnormalityDeterminator>();
foreach (var p in _savedDeterminators)
{
p.Value.OnUnregEvent();
}
}
else
{
__instance.determinators = _savedDeterminators;
foreach (var p in _savedDeterminators)
{
p.Value.OnRegEvent();
}
}
};
_savedDeterminators = __instance.determinators;
if (!Enabled.Value) return;
__instance.determinators = new Dictionary<int, AbnormalityDeterminator>();
foreach (var p in _savedDeterminators)
{
p.Value.OnUnregEvent();
}
}
}

View File

@@ -1,295 +0,0 @@
## Changlog
* 2.3.29
+ Fix compatibility with game update 0.10.32.25779
* 2.3.28
+ New feature: `Instant hand-craft`.
+ Fix some panels' display while `Infinite Natural Resources` is enabled.
* 2.3.27
+ `Skip bullet period` & `Eject anyway`: Fix compatibility with `Dyson Sphere Program v0.10.32.25496`.
* 2.3.26
+ Refactor codes to adapt to UXAssist 1.2.0
- You should update UXAssist to 1.2.0 or later before using this version.
+ `Complete Dyson Sphere Shells instantly`: Fix possible wrong production records.
* 2.3.25
+ New feature: `Enable warp without space warpers`
+ New feature: `Wind Turbines do global power coverage`
+ Fix an issue that `Complete Dyson Sphere Shells instantly` does not generate production records for solar sails.
<details>
<summary>Older versions</summary>
* 2.3.24
+ `Complete Dyson Sphere Shells instantly`: Fix a bug that may cause negative power in some cases
* 2.3.23
+ New feature: `Complete Dyson Sphere Shells instantly`
+ Fix a crash when config panel is opened before game is fully loaded
* 2.3.22
+ Fix `Pump Anywhere`
* 2.3.21
+ `Retrieve/Place items from/to remote planets on logistics control panel`: Items are put back to player's inventory when a slot is removed from the logistics station on remote planet.
+ `Dev Shortcuts`: Camera Pose related shortcurts are working now.
* 2.3.20
+ New feature: `Retrieve/Place items from/to remote planets on logistics control panel`
* 2.3.19
+ New features:
+ `Remove all metadata consumption records`
+ `Remove metadata consumption record in current game`
+ `Clear metadata flag which bans achievements`
* 2.3.18
+ New features:
+ `Teleport to outer space`, this will teleport you to the outer space which is 50 LYs far from the farthest star.
+ `Teleport to selected astronomical`
+ Fix logic of `Unlock techs with key-modifiers`.
+ `No condition build` does not hide rotation info of belts now.
* 2.3.17
+ Make compatible with game version 0.10.30.23292
* 2.3.16
+ Add 2 options to `Belt signal item generation`:
- `Count generations as production in statistics`
- `Count removals as consumption in statistics`
+ New feature: `Increase maximum power usage in Logistic Stations and Advanced Mining Machines`
- Logistic Stations: Increased max charging power to 3GW(ILS) and 600MW(PLS) (10x of original)
- Advanced Mining Machines: Increased max mining speed to 1000%
* 2.3.15
+ New features:
- `Instant teleport (like that in Sandbox mode)`
- `Mecha and Drones/Fleets invicible`
- `Buildings invicible`
* 2.3.14
+ Remove default shortcut key for `No condition build` and `No collision`, to avoid misoperation. You can still set them in system settings window manually if needed.
+ Fix translation issue.
* 2.3.13
+ Fix a bug that shortcuts are not working and have display issue on settings window.
* 2.3.12
+ Add a shortcut to toggle `No collision`, you can modify the shortcut on system settings window.
+ Add realtime tips when toggling `No condition build` and `No collision` with shortcuts.
* 2.3.11
+ Add a shortcut to toggle `No condition build`, you can modify the shortcut on system settings window. This depends on [UXAssist](https://dsp.thunderstore.io/package/soarqin/UXAssist) 1.0.15 or later.
* 2.3.10
+ Fix following functions not working in new game updates:
- `Pump Anywhere`
- `Terraform without enough soil piles`
* 2.3.9
+ Support game version 0.10.28.21219
* 2.3.8
+ Fix a crash on starting new games while `Finish build immediately` is enabled.
+ Fix UI button width.
* 2.3.7
+ Support game version 0.10.28.20759
+ Fix belt signal that items' generation speed is not fit to number set sometimes.
* 2.3.6
+ Support for UXAssist's new function within `Finish build immediately`.
+ Add a warning message when `Build without condition` is enabled.
+ Fix an issue in `Finish build immediately` that some buildings are not finished immediately.
* 2.3.5
+ Fix another crash in `Skip bullet period`.
* 2.3.4
+ Use new tab layout of UXAssist 1.0.2
+ Minor bug fixes
* 2.3.3
+ Fix a crash in `Skip bullet period`.
+ Unlock techs with Alt unlocks VeinUtil to 10000 instead of 7200 now, as bug fixed in UXAssist.
* 2.3.2
+ Birth star options moved to [UniverseGenTweaks](https://dsp.thunderstore.io/package/soarqin/UniverseGenTweaks/)
+ Optimize `Quick absorb`, consumes less CPU time and take turns firing to nodes.
+ `Fast Mining` ensures full output of oil extractors now.
+ Fix issue that `Belt signal generator` not working after switched off then on again.
+ Fix absorption issue by `Quick absorb` and `Skip bullet period` enabled at the same time.
+ Crash fix for some options
* 2.3.1
+ Add UXAssist to dependencies in manifest.
* 2.3.0
+ Move some functions to an individual mod: [UXAssist](https://dsp.thunderstore.io/package/soarqin/UXAssist)
+ Depends on [UXAssist](https://dsp.thunderstore.io/package/soarqin/UXAssist) now, so that config panel is unified with UXAssist.
+ Remove `LCtrl+A` from Dev Shortcuts, to avoid misoperation.
+ Infinite bots/drones/vessels in `Architect mode` now.
* 2.2.7
+ New function: `Construct only nodes but frames`
+ Opening config panel does not close inventory panel now
+ Remove `Input direction conflict` check while using `Remove some build conditions`
+ Fix a bug that prevents `Belt signal alt format` from switching number formats for current belt signals
* 2.2.6
+ New function: `Stop ejectors when available nodes are all filled up`
+ Fix a bug that absorb solar sails on unfinised nodes
* 2.2.5
+ Skip all intermediate states and absorb solar sails instantly while enable `Quick absorb`, `Skip bullet period` and `Skip absorption period` at the same time.
+ Fix a problem that `Quick absorb` does not absorb all solar sails instantly when most nodes are full.
+ Fix crash while using with some mods
* 2.2.4
+ New function: `Enable player actions in globe view`
+ Fix UI bug
* 2.2.3
+ New function: `Remove some build conditions`
+ Fix compatibility with some mods
* 2.2.2
+ New function: `Assign gamesave to currrnet account`
+ New subfunction: `Belt signal alt format`
+ Fix a crash on using `Initialize this Planet`
+ Fix belt build in `Finish build immediately`
* 2.2.1
+ Check condition for miners even when `Build without condition` is enabled.
+ Fix a patch issue that may cause `Build without condition` not working.
* 2.2.0
+ Add some power related functions
+ Add a subfunction to belt signal item generation, which simulates production process of raws and intermediates on statistics
+ Split some functions from Architect mode
* 2.1.0
+ Belt signal item generation
+ Fix window display priority which may cause tips to be covered by main window
* 2.0.0
+ Refactorying codes
+ UI implementation
+ Add a lot of functions
* 1.0.0
+ Initial release
</details>
## 更新日志
* 2.3.29
+ 修复了与游戏更新0.10.32.25779的兼容性
* 2.3.28
+ 新功能:`快速手动制造`
+ 修复了启用`自然资源采集不消耗`时部分面板的显示问题
* 2.3.27
+ `跳过子弹阶段``全球弹射`:修复了与`戴森球计划 v0.10.32.25496`的兼容性
* 2.3.26
+ 重构代码以适应UXAssist 1.2.0
- 在使用此版本之前您应先更新UXAssist到1.2.0或更高版本。
+ `立即完成戴森壳建造`:修复了可能导致错误的生产记录的问题
* 2.3.25
+ 新功能:`无需空间翘曲器即可曲速飞行`
+ 新功能:`风力涡轮机供电覆盖全球`
+ 修复了`立即完成戴森壳建造`未生成太阳帆生产记录的问题
<details>
<summary>更早的版本</summary>
* 2.3.24
+ `立即完成戴森壳建造`:修复了在某些情况下可能导致发电为负的问题
* 2.3.23
+ 新功能:`立即完成戴森壳建造`
+ 修复了在游戏完全加载前打开配置面板可能导致的崩溃问题
* 2.3.22
+ 修复了`平地抽水`
* 2.3.21
+ `在物流总控面板上可以从非本地行星取放物品`:当从非本地星球的物流站移除槽位时,物品会放回玩家的背包
+ `开发模式快捷键`:摄像机位(Pose)相关的快捷键现在生效了
* 2.3.20
+ 新功能:`在物流总控面板上可以从非本地行星取放物品`
* 2.3.19
+ 新功能:
+ `移除所有元数据消耗记录`
+ `移除当前存档的元数据消耗记录`
+ `解除当前存档因使用元数据导致的成就限制`
* 2.3.18
+ 新功能:
+ `传送到外太空`这会将你传送到距离最远星球50光年的外太空
+ `传送到选中天体`
+ 修复了`组合键解锁科技`的逻辑
+ `无条件建造`现在不会隐藏传送带的旋转信息了
* 2.3.17
+ 适配游戏版本0.10.30.23292
* 2.3.16
+ 为`传送带信号物品生成`添加了两个选项:
- `统计信息里将生成计算为产物`
- `统计信息里将移除计算为消耗`
+ 新功能:`提升物流塔和大型采矿机的最大功耗`
- 物流塔将最大充电功率提高到3GW(星际物流塔)和600MW(行星物流塔)原来的10倍
- 大型采矿机将最大采矿速度提高到1000%
* 2.3.15
+ 新功能:
- `快速传送(和沙盒模式一样)`
- `机甲和战斗无人机无敌`
- `建筑无敌`
* 2.3.14
+ 移除了`无条件建造``无碰撞`的默认快捷键,以避免误操作。如有需要请手动在系统选项窗口中设置。
+ 修复了翻译问题。
* 2.3.13
+ 修复了快捷键无效和设置窗口上的按键显示问题
* 2.3.12
+ 添加了一个快捷键来切换`无碰撞`,你可以在系统设置面板中修改快捷键。
+ 在使用快捷键切换`无条件建造``无碰撞`时添加了实时提示信息。
* 2.3.11
+ 添加了一个快捷键来切换`无条件建造`,你可以在系统设置面板中修改快捷键。这依赖于[UXAssist](https://dsp.thunderstore.io/package/soarqin/UXAssist) 1.0.15或更高版本
* 2.3.10
+ 修复了以下功能在新游戏版本中不生效的问题:
- `平地抽水`
- `沙土不够时依然可以整改地形`
* 2.3.9
+ 支持游戏版本0.10.28.21219
* 2.3.8
+ 修复了启用`建造秒完成`时开新游戏可能导致崩溃的问题
+ 修复了UI按钮宽度
* 2.3.7
+ 支持游戏版本0.10.28.20759
+ 修复了传送带信号有时候物品生成速度和设置不匹配的问题
* 2.3.6
+ 在`建造秒完成`中支持UXAssist的新功能
+ 在启用`无条件建造`时添加警告信息
+ 修复了`建造秒完成`可能导致部分建筑无法立即完成的问题
* 2.3.5
+ 修复了`跳过子弹阶段`可能导致崩溃的问题
* 2.3.4
+ 使用UXAssist 1.0.2的新页签布局
+ 修复了一些小bug
* 2.3.3
+ 修复了`跳过子弹阶段`可能导致崩溃的问题
+ 使用Alt解锁科技时现在`矿物利用`的科技解锁到10000级而不是7200级因为UXAssist已修复对应bug
* 2.3.2
+ 母星系的选项移动到了[UniverseGenTweaks](https://dsp.thunderstore.io/package/soarqin/UniverseGenTweaks/)
+ 优化了`快速吸收`现在消耗更少的CPU并且会轮流打向各节点
+ `高速采集`现在可以保证油井的最大产出
+ 修复了`传送带信号物品生成`在选项关闭后再次启用时不生效的问题
+ 修复了`快速吸收``跳过子弹阶段`同时启用时可能导致吸收计算错误的问题
+ 修复了一些选项可能导致崩溃的问题
* 2.3.1
+ 在manifest中添加UXAssist到依赖
* 2.3.0
+ 将部分功能移动到单独的mod[UXAssist](https://dsp.thunderstore.io/package/soarqin/UXAssist)
+ 现在依赖[UXAssist](https://dsp.thunderstore.io/package/soarqin/UXAssist)因此配置面板与UXAssist合并
+ 从开发模式快捷键中移除`LCtrl+A`,以避免误操作
+ 现在`建筑师模式`中配送机/物流机/物流船也无限了
* 2.2.7
+ 新功能:`只建造节点不建造框架`
+ 打开设置面板时不再关闭背包面板
+ 在`移除部分不影响游戏逻辑的建造条件`启用时移除`输入方向冲突`的检查条件
+ 修复导致`传送带信号替换格式`不切换传送带信号数字格式的问题
* 2.2.6
+ 新功能:`可用节点全部造完时停止弹射`
+ 修复了在未完成的节点上吸收太阳帆的问题
* 2.2.5
+ 在同时启用`快速吸收``跳过子弹阶段``跳过吸收阶段`时,所有弹射的太阳帆会跳过所有中间环节立即吸收
+ 修复了`快速吸收`在大部分节点已满时无法立即吸收所有太阳帆的问题
+ 修复了与一些mod的兼容性问题
* 2.2.4
+ 新功能:`在行星视图中允许玩家操作`
+ 修复了UI显示问题
* 2.2.3
+ 新功能:`移除部分不影响游戏逻辑的建造条件`
+ 修复了与一些mod的兼容性问题
* 2.2.2
+ 新功能:`将游戏存档绑定给当前账号`
+ 新子功能:`传送带信号替换格式`
+ 修复了`初始化本行星`可能导致崩溃的问题
+ 修复了`建造秒完成`中传送带建造的问题
* 2.2.1
+ 即使在启用`无条件建造`时依然检查矿机的建造条件
+ 修复一个可能导致`无条件建造`不生效的问题
* 2.2.0
+ 添加了一些发电相关功能
+ 为传送带信号物品生成添加了一个子功能,在统计面板模拟了原材料和中间产物的生产过程
+ 从建筑师模式中分离了一些功能
* 2.1.0
+ 传送带信号物品生成
+ 修复窗口显示优先级可能导致提示信息被主窗口遮挡的问题
* 2.0.0
+ 重构代码
+ UI实现
+ 添加了很多功能
* 1.0.0
+ 初始版本
</details>

View File

@@ -1,9 +1,4 @@
using System; using BepInEx;
using System.Reflection;
using BepInEx;
using CheatEnabler.Patches;
using HarmonyLib;
using UXAssist.Common;
namespace CheatEnabler; namespace CheatEnabler;
@@ -16,10 +11,10 @@ public class CheatEnabler : BaseUnityPlugin
private void Awake() private void Awake()
{ {
GamePatch.DevShortcutsEnabled = Config.Bind("General", "DevShortcuts", false, "Enable DevMode shortcuts"); DevShortcuts.Enabled = Config.Bind("General", "DevShortcuts", false, "Enable DevMode shortcuts");
GamePatch.AbnormalDisablerEnabled = Config.Bind("General", "DisableAbnormalChecks", false, AbnormalDisabler.Enabled = Config.Bind("General", "DisableAbnormalChecks", false,
"disable all abnormal checks"); "disable all abnormal checks");
GamePatch.UnlockTechEnabled = Config.Bind("General", "UnlockTech", false, TechPatch.Enabled = Config.Bind("General", "UnlockTech", false,
"Unlock clicked tech by holding key-modifilers(Shift/Alt/Ctrl)"); "Unlock clicked tech by holding key-modifilers(Shift/Alt/Ctrl)");
FactoryPatch.ImmediateEnabled = Config.Bind("Build", "ImmediateBuild", false, FactoryPatch.ImmediateEnabled = Config.Bind("Build", "ImmediateBuild", false,
"Build immediately"); "Build immediately");
@@ -33,10 +28,6 @@ public class CheatEnabler : BaseUnityPlugin
"Belt signal generator"); "Belt signal generator");
FactoryPatch.BeltSignalNumberAltFormat = Config.Bind("Build", "BeltSignalNumberFormat", false, FactoryPatch.BeltSignalNumberAltFormat = Config.Bind("Build", "BeltSignalNumberFormat", false,
"Belt signal number format alternative format (AAAA=generation speed in minutes, B=proliferate points, C=stack count):\n AAAABC by default\n BCAAAA as alternative"); "Belt signal number format alternative format (AAAA=generation speed in minutes, B=proliferate points, C=stack count):\n AAAABC by default\n BCAAAA as alternative");
FactoryPatch.BeltSignalCountGenEnabled = Config.Bind("Build", "BeltSignalCountGenerations", true,
"Belt signal count generations as production in statistics");
FactoryPatch.BeltSignalCountRemEnabled = Config.Bind("Build", "BeltSignalCountRemovals", true,
"Belt signal count removals as comsumption in statistics");
FactoryPatch.BeltSignalCountRecipeEnabled = Config.Bind("Build", "BeltSignalCountRecipe", false, FactoryPatch.BeltSignalCountRecipeEnabled = Config.Bind("Build", "BeltSignalCountRecipe", false,
"Belt signal count all raws and intermediates in statistics"); "Belt signal count all raws and intermediates in statistics");
FactoryPatch.RemovePowerSpaceLimitEnabled = Config.Bind("Build", "RemovePowerDistanceLimit", false, FactoryPatch.RemovePowerSpaceLimitEnabled = Config.Bind("Build", "RemovePowerDistanceLimit", false,
@@ -49,12 +40,6 @@ public class CheatEnabler : BaseUnityPlugin
"Boost fuel power"); "Boost fuel power");
FactoryPatch.BoostGeothermalPowerEnabled = Config.Bind("Build", "BoostGeothermalPower", false, FactoryPatch.BoostGeothermalPowerEnabled = Config.Bind("Build", "BoostGeothermalPower", false,
"Boost geothermal power"); "Boost geothermal power");
FactoryPatch.WindTurbinesPowerGlobalCoverageEnabled = Config.Bind("Build", "PowerGlobalCoverage", false,
"Global power coverage");
FactoryPatch.GreaterPowerUsageInLogisticsEnabled = Config.Bind("Build", "GreaterPowerUsageInLogistics", false,
"Increase maximum power usage in Logistic Stations and Advanced Mining Machines");
FactoryPatch.ControlPanelRemoteLogisticsEnabled = Config.Bind("Build", "ControlPanelRemoteLogistics", false,
"Retrieve/Place items from/to remote planets on logistics control panel");
ResourcePatch.InfiniteResourceEnabled = Config.Bind("Planet", "AlwaysInfiniteResource", false, ResourcePatch.InfiniteResourceEnabled = Config.Bind("Planet", "AlwaysInfiniteResource", false,
"always infinite natural resource"); "always infinite natural resource");
ResourcePatch.FastMiningEnabled = Config.Bind("Planet", "FastMining", false, ResourcePatch.FastMiningEnabled = Config.Bind("Planet", "FastMining", false,
@@ -62,13 +47,7 @@ public class CheatEnabler : BaseUnityPlugin
PlanetPatch.WaterPumpAnywhereEnabled = Config.Bind("Planet", "WaterPumpAnywhere", false, PlanetPatch.WaterPumpAnywhereEnabled = Config.Bind("Planet", "WaterPumpAnywhere", false,
"Can pump water anywhere (while water type is not None)"); "Can pump water anywhere (while water type is not None)");
PlanetPatch.TerraformAnywayEnabled = Config.Bind("Planet", "TerraformAnyway", false, PlanetPatch.TerraformAnywayEnabled = Config.Bind("Planet", "TerraformAnyway", false,
"Can do terraform without enough soil piless"); "Can do terraform without enough sands");
PlayerPatch.InstantHandCraftEnabled = Config.Bind("Player", "InstantHandCraft", false,
"Enable instant hand-craft");
PlayerPatch.InstantTeleportEnabled = Config.Bind("Player", "InstantTeleport", false,
"Enable instant teleport (like that in Sandbox mode)");
PlayerPatch.WarpWithoutSpaceWarpersEnabled = Config.Bind("Player", "WarpWithoutWarper", false,
"Enable warp without warper");
DysonSpherePatch.SkipBulletEnabled = Config.Bind("DysonSphere", "SkipBullet", false, DysonSpherePatch.SkipBulletEnabled = Config.Bind("DysonSphere", "SkipBullet", false,
"Skip bullet"); "Skip bullet");
DysonSpherePatch.SkipAbsorbEnabled = Config.Bind("DysonSphere", "SkipAbsorb", false, DysonSpherePatch.SkipAbsorbEnabled = Config.Bind("DysonSphere", "SkipAbsorb", false,
@@ -81,31 +60,26 @@ public class CheatEnabler : BaseUnityPlugin
"Overclock ejector"); "Overclock ejector");
DysonSpherePatch.OverclockSiloEnabled = Config.Bind("DysonSphere", "OverclockSilo", false, DysonSpherePatch.OverclockSiloEnabled = Config.Bind("DysonSphere", "OverclockSilo", false,
"Overclock silo"); "Overclock silo");
CombatPatch.MechaInvincibleEnabled = Config.Bind("Battle", "MechaInvincible", false,
"Mecha and Drones/Fleets invincible");
CombatPatch.BuildingsInvincibleEnabled = Config.Bind("Battle", "BuildingsInvincible", false,
"Buildings invincible");
UIConfigWindow.Init(); UIConfigWindow.Init();
_patches = Util.GetTypesFiltered(Assembly.GetExecutingAssembly(),
t => string.Equals(t.Namespace, "CheatEnabler.Patches", StringComparison.Ordinal) || string.Equals(t.Namespace, "CheatEnabler.Functions", StringComparison.Ordinal));
_patches?.Do(type => type.GetMethod("Init")?.Invoke(null, null));
}
private Type[] _patches; DevShortcuts.Init();
AbnormalDisabler.Init();
private void Start() TechPatch.Init();
{ FactoryPatch.Init();
_patches?.Do(type => type.GetMethod("Start")?.Invoke(null, null)); ResourcePatch.Init();
PlanetPatch.Init();
DysonSpherePatch.Init();
} }
private void OnDestroy() private void OnDestroy()
{ {
_patches?.Do(type => type.GetMethod("Uninit")?.Invoke(null, null)); DysonSpherePatch.Uninit();
} PlanetPatch.Uninit();
ResourcePatch.Uninit();
private void Update() FactoryPatch.Uninit();
{ TechPatch.Uninit();
if (VFInput.inputing) return; AbnormalDisabler.Uninit();
FactoryPatch.OnUpdate(); DevShortcuts.Uninit();
} }
} }

View File

@@ -5,7 +5,7 @@
<TargetFramework>net472</TargetFramework> <TargetFramework>net472</TargetFramework>
<BepInExPluginGuid>org.soardev.cheatenabler</BepInExPluginGuid> <BepInExPluginGuid>org.soardev.cheatenabler</BepInExPluginGuid>
<Description>DSP MOD - CheatEnabler</Description> <Description>DSP MOD - CheatEnabler</Description>
<Version>2.3.29</Version> <Version>2.3.9</Version>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<LangVersion>latest</LangVersion> <LangVersion>latest</LangVersion>
<PackageId>CheatEnabler</PackageId> <PackageId>CheatEnabler</PackageId>
@@ -16,7 +16,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="BepInEx.Core" Version="5.*" /> <PackageReference Include="BepInEx.Core" Version="5.*" />
<PackageReference Include="BepInEx.PluginInfoProps" Version="1.*" /> <PackageReference Include="BepInEx.PluginInfoProps" Version="1.*" />
<PackageReference Include="DysonSphereProgram.GameLibs" Version="*-r.*" /> <PackageReference Include="DysonSphereProgram.GameLibs" Version="0.10.28.21219-r.0" />
<PackageReference Include="UnityEngine.Modules" Version="2018.4.12" IncludeAssets="compile" /> <PackageReference Include="UnityEngine.Modules" Version="2018.4.12" IncludeAssets="compile" />
</ItemGroup> </ItemGroup>
@@ -24,22 +24,11 @@
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.3" PrivateAssets="all" /> <PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.3" PrivateAssets="all" />
</ItemGroup> </ItemGroup>
<!--
<ItemGroup>
<Reference Include="Assembly-CSharp">
<HintPath>..\AssemblyFromGame\Assembly-CSharp.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.UI">
<HintPath>..\AssemblyFromGame\UnityEngine.UI.dll</HintPath>
</Reference>
</ItemGroup>
-->
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\UXAssist\UXAssist.csproj" /> <ProjectReference Include="..\UXAssist\UXAssist.csproj" />
</ItemGroup> </ItemGroup>
<Target Name="PostBuild" AfterTargets="PostBuildEvent" Condition="'$(Configuration)' == 'Release'"> <Target Name="PostBuild" AfterTargets="PostBuildEvent">
<Exec Command="del /F /Q package\$(ProjectName)-$(Version).zip&#xA;powershell Compress-Archive -Force -DestinationPath 'package/$(ProjectName)-$(Version).zip' -Path '$(TargetPath)', package/icon.png, package/manifest.json, README.md, CHANGELOG.md" /> <Exec Command="del /F /Q package\$(ProjectName)-$(Version).zip&#xA;zip -9 -j package/$(ProjectName)-$(Version).zip $(TargetPath) package/icon.png package/manifest.json README.md" />
</Target> </Target>
</Project> </Project>

View File

@@ -0,0 +1,85 @@
using System.Collections.Generic;
using System.Reflection.Emit;
using BepInEx.Configuration;
using HarmonyLib;
namespace CheatEnabler;
public static class DevShortcuts
{
public static ConfigEntry<bool> Enabled;
private static Harmony _patch;
private static PlayerAction_Test _test;
public static void Init()
{
_patch ??= Harmony.CreateAndPatchAll(typeof(DevShortcuts));
Enabled.SettingChanged += (_, _) =>
{
if (_test != null) _test.active = Enabled.Value;
};
}
public static void Uninit()
{
_patch?.UnpatchSelf();
_patch = null;
}
[HarmonyPostfix]
[HarmonyPatch(typeof(PlayerController), "Init")]
private static void PlayerController_Init_Postfix(PlayerController __instance)
{
var cnt = __instance.actions.Length;
var newActions = new PlayerAction[cnt + 1];
for (var i = 0; i < cnt; i++)
{
newActions[i] = __instance.actions[i];
}
_test = new PlayerAction_Test();
_test.Init(__instance.player);
_test.active = Enabled.Value;
newActions[cnt] = _test;
__instance.actions = newActions;
}
[HarmonyPostfix]
[HarmonyPatch(typeof(PlayerAction_Test), "GameTick")]
private static void PlayerAction_Test_GameTick_Postfix(PlayerAction_Test __instance)
{
__instance.Update();
}
[HarmonyTranspiler]
[HarmonyPatch(typeof(PlayerAction_Test), nameof(PlayerAction_Test.Update))]
private static IEnumerable<CodeInstruction> PlayerAction_Test_Update_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{
var matcher = new CodeMatcher(instructions, generator);
matcher.End().MatchBack(false,
new CodeMatch(OpCodes.Ldarg_0),
new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(PlayerAction_Test), nameof(PlayerAction_Test.active)))
);
var pos = matcher.Pos;
/* Remove Shift+F4 part of the method */
matcher.Start().RemoveInstructions(pos).MatchForward(false,
new CodeMatch(OpCodes.Call, AccessTools.Method(typeof(GameMain), "get_sandboxToolsEnabled")),
new CodeMatch(OpCodes.Ldc_I4_0),
new CodeMatch(OpCodes.Ceq)
);
var labels = matcher.Labels;
matcher.SetInstructionAndAdvance(
new CodeInstruction(OpCodes.Ldc_I4_1).WithLabels(labels)
).RemoveInstructions(2);
/* Remove Ctrl+A */
matcher.Start().MatchForward(false,
new CodeMatch(instr => (instr.opcode == OpCodes.Ldc_I4_S || instr.opcode == OpCodes.Ldc_I4) && instr.OperandIs(0x61)),
new CodeMatch(OpCodes.Call, AccessTools.Method(typeof(UnityEngine.Input), nameof(UnityEngine.Input.GetKeyDown), new[] { typeof(UnityEngine.KeyCode) }))
);
labels = matcher.Labels;
matcher.Labels = null;
matcher.RemoveInstructions(2);
matcher.Opcode = OpCodes.Br;
matcher.Labels = labels;
return matcher.InstructionEnumeration();
}
}

View File

@@ -3,11 +3,10 @@ using System.Collections.Generic;
using System.Reflection.Emit; using System.Reflection.Emit;
using BepInEx.Configuration; using BepInEx.Configuration;
using HarmonyLib; using HarmonyLib;
using UXAssist.Common;
namespace CheatEnabler.Patches; namespace CheatEnabler;
public class DysonSpherePatch: PatchImpl<DysonSpherePatch> public static class DysonSpherePatch
{ {
public static ConfigEntry<bool> SkipBulletEnabled; public static ConfigEntry<bool> SkipBulletEnabled;
public static ConfigEntry<bool> SkipAbsorbEnabled; public static ConfigEntry<bool> SkipAbsorbEnabled;
@@ -17,6 +16,8 @@ public class DysonSpherePatch: PatchImpl<DysonSpherePatch>
public static ConfigEntry<bool> OverclockSiloEnabled; public static ConfigEntry<bool> OverclockSiloEnabled;
private static bool _instantAbsorb; private static bool _instantAbsorb;
private static Harmony _dysonSpherePatch;
public static void Init() public static void Init()
{ {
SkipBulletEnabled.SettingChanged += (_, _) => SkipBulletPatch.Enable(SkipBulletEnabled.Value); SkipBulletEnabled.SettingChanged += (_, _) => SkipBulletPatch.Enable(SkipBulletEnabled.Value);
@@ -25,22 +26,19 @@ public class DysonSpherePatch: PatchImpl<DysonSpherePatch>
EjectAnywayEnabled.SettingChanged += (_, _) => EjectAnywayPatch.Enable(EjectAnywayEnabled.Value); EjectAnywayEnabled.SettingChanged += (_, _) => EjectAnywayPatch.Enable(EjectAnywayEnabled.Value);
OverclockEjectorEnabled.SettingChanged += (_, _) => OverclockEjector.Enable(OverclockEjectorEnabled.Value); OverclockEjectorEnabled.SettingChanged += (_, _) => OverclockEjector.Enable(OverclockEjectorEnabled.Value);
OverclockSiloEnabled.SettingChanged += (_, _) => OverclockSilo.Enable(OverclockSiloEnabled.Value); OverclockSiloEnabled.SettingChanged += (_, _) => OverclockSilo.Enable(OverclockSiloEnabled.Value);
}
public static void Start()
{
SkipBulletPatch.Enable(SkipBulletEnabled.Value); SkipBulletPatch.Enable(SkipBulletEnabled.Value);
SkipAbsorbPatch.Enable(SkipAbsorbEnabled.Value); SkipAbsorbPatch.Enable(SkipAbsorbEnabled.Value);
QuickAbsorbPatch.Enable(QuickAbsorbEnabled.Value); QuickAbsorbPatch.Enable(QuickAbsorbEnabled.Value);
EjectAnywayPatch.Enable(EjectAnywayEnabled.Value); EjectAnywayPatch.Enable(EjectAnywayEnabled.Value);
OverclockEjector.Enable(OverclockEjectorEnabled.Value); OverclockEjector.Enable(OverclockEjectorEnabled.Value);
OverclockSilo.Enable(OverclockSiloEnabled.Value); OverclockSilo.Enable(OverclockSiloEnabled.Value);
Enable(true); _dysonSpherePatch ??= Harmony.CreateAndPatchAll(typeof(DysonSpherePatch));
} }
public static void Uninit() public static void Uninit()
{ {
Enable(false); _dysonSpherePatch?.UnpatchSelf();
_dysonSpherePatch = null;
SkipBulletPatch.Enable(false); SkipBulletPatch.Enable(false);
SkipAbsorbPatch.Enable(false); SkipAbsorbPatch.Enable(false);
QuickAbsorbPatch.Enable(false); QuickAbsorbPatch.Enable(false);
@@ -83,11 +81,12 @@ public class DysonSpherePatch: PatchImpl<DysonSpherePatch>
return matcher.InstructionEnumeration(); return matcher.InstructionEnumeration();
} }
private class SkipBulletPatch: PatchImpl<SkipBulletPatch> private static class SkipBulletPatch
{ {
private static long _sailLifeTime; private static long _sailLifeTime;
private static DysonSailCache[][] _sailsCache; private static DysonSailCache[][] _sailsCache;
private static int[] _sailsCacheLen, _sailsCacheCapacity; private static int[] _sailsCacheLen, _sailsCacheCapacity;
private static Harmony _patch;
private struct DysonSailCache private struct DysonSailCache
{ {
@@ -107,16 +106,19 @@ public class DysonSpherePatch: PatchImpl<DysonSpherePatch>
} }
} }
protected override void OnEnable() public static void Enable(bool on)
{
if (on)
{ {
UpdateSailLifeTime(); UpdateSailLifeTime();
UpdateSailsCacheForThisGame(); UpdateSailsCacheForThisGame();
GameLogic.OnGameBegin += GameMain_Begin_Postfix; _patch ??= Harmony.CreateAndPatchAll(typeof(SkipBulletPatch));
} }
else
protected override void OnDisable()
{ {
GameLogic.OnGameBegin -= GameMain_Begin_Postfix; _patch?.UnpatchSelf();
_patch = null;
}
} }
private static void UpdateSailLifeTime() private static void UpdateSailLifeTime()
@@ -149,6 +151,8 @@ public class DysonSpherePatch: PatchImpl<DysonSpherePatch>
_sailsCacheCapacity[index] = capacity; _sailsCacheCapacity[index] = capacity;
} }
[HarmonyPostfix]
[HarmonyPatch(typeof(GameMain), nameof(GameMain.Begin))]
private static void GameMain_Begin_Postfix() private static void GameMain_Begin_Postfix()
{ {
UpdateSailsCacheForThisGame(); UpdateSailsCacheForThisGame();
@@ -179,11 +183,11 @@ public class DysonSpherePatch: PatchImpl<DysonSpherePatch>
).Advance(1); ).Advance(1);
var end = matcher.Pos; var end = matcher.Pos;
matcher.Start().Advance(start).RemoveInstructions(end - start).Insert( matcher.Start().Advance(start).RemoveInstructions(end - start).Insert(
new CodeInstruction(OpCodes.Ldarg_3), new CodeInstruction(OpCodes.Ldarg_2),
new CodeInstruction(OpCodes.Ldarg_0), new CodeInstruction(OpCodes.Ldarg_0),
new CodeInstruction(OpCodes.Ldfld, AccessTools.Field(typeof(EjectorComponent), nameof(EjectorComponent.orbitId))), new CodeInstruction(OpCodes.Ldfld, AccessTools.Field(typeof(EjectorComponent), nameof(EjectorComponent.orbitId))),
new CodeInstruction(OpCodes.Ldloc_S, 9), new CodeInstruction(OpCodes.Ldloc_S, 8),
new CodeInstruction(OpCodes.Ldloc_S, 11), new CodeInstruction(OpCodes.Ldloc_S, 10),
new CodeInstruction(OpCodes.Call, AccessTools.Method(typeof(SkipBulletPatch), nameof(SkipBulletPatch.AddDysonSail))) new CodeInstruction(OpCodes.Call, AccessTools.Method(typeof(SkipBulletPatch), nameof(SkipBulletPatch.AddDysonSail)))
); );
return matcher.InstructionEnumeration(); return matcher.InstructionEnumeration();
@@ -281,16 +285,20 @@ public class DysonSpherePatch: PatchImpl<DysonSpherePatch>
} }
} }
private class SkipAbsorbPatch: PatchImpl<SkipAbsorbPatch> private static class SkipAbsorbPatch
{ {
protected override void OnEnable() private static Harmony _patch;
{
_instantAbsorb = QuickAbsorbEnabled.Value;
}
protected override void OnDisable() public static void Enable(bool on)
{ {
_instantAbsorb = false; _instantAbsorb = SkipAbsorbEnabled.Value && QuickAbsorbEnabled.Value;
if (on)
{
_patch ??= Harmony.CreateAndPatchAll(typeof(SkipAbsorbPatch));
return;
}
_patch?.UnpatchSelf();
_patch = null;
} }
[HarmonyTranspiler] [HarmonyTranspiler]
@@ -333,18 +341,23 @@ public class DysonSpherePatch: PatchImpl<DysonSpherePatch>
} }
} }
private class QuickAbsorbPatch: PatchImpl<QuickAbsorbPatch> private static class QuickAbsorbPatch
{ {
protected override void OnEnable() private static Harmony _patch;
{
_instantAbsorb = SkipAbsorbEnabled.Value;
}
protected override void OnDisable() public static void Enable(bool on)
{ {
_instantAbsorb = false; _instantAbsorb = SkipAbsorbEnabled.Value && QuickAbsorbEnabled.Value;
if (on)
{
_patch ??= Harmony.CreateAndPatchAll(typeof(QuickAbsorbPatch));
}
else
{
_patch?.UnpatchSelf();
_patch = null;
}
} }
[HarmonyTranspiler] [HarmonyTranspiler]
[HarmonyPatch(typeof(DysonSphereLayer), nameof(DysonSphereLayer.GameTick))] [HarmonyPatch(typeof(DysonSphereLayer), nameof(DysonSphereLayer.GameTick))]
private static IEnumerable<CodeInstruction> DysonSphereLayer_GameTick_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator) private static IEnumerable<CodeInstruction> DysonSphereLayer_GameTick_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
@@ -384,41 +397,71 @@ public class DysonSpherePatch: PatchImpl<DysonSpherePatch>
} }
} }
private class EjectAnywayPatch: PatchImpl<EjectAnywayPatch> private static class EjectAnywayPatch
{ {
private static Harmony _patch;
public static void Enable(bool on)
{
if (on)
{
_patch ??= Harmony.CreateAndPatchAll(typeof(EjectAnywayPatch));
}
else
{
_patch?.UnpatchSelf();
_patch = null;
}
}
[HarmonyTranspiler] [HarmonyTranspiler]
[HarmonyPatch(typeof(EjectorComponent), nameof(EjectorComponent.InternalUpdate))] [HarmonyPatch(typeof(EjectorComponent), nameof(EjectorComponent.InternalUpdate))]
private static IEnumerable<CodeInstruction> EjectorComponent_InternalUpdate_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator) private static IEnumerable<CodeInstruction> EjectorComponent_InternalUpdate_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{ {
var matcher = new CodeMatcher(instructions, generator); var matcher = new CodeMatcher(instructions, generator);
matcher.End().MatchBack(false, matcher.MatchForward(false,
new CodeMatch(instr => instr.opcode == OpCodes.Ldc_R8 && Math.Abs((double)instr.operand - 0.08715574) < 0.00000001) new CodeMatch(instr => instr.opcode == OpCodes.Ldc_R8 && Math.Abs((double)instr.operand - 0.08715574) < 0.00000001)
); );
var start = matcher.Pos - 3; var start = matcher.Pos - 3;
matcher.MatchForward(false, matcher.MatchForward(false,
new CodeMatch(OpCodes.Ldarg_0), new CodeMatch(OpCodes.And)
new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(EjectorComponent), nameof(EjectorComponent.bulletCount))) ).Advance(1).MatchForward(false,
new CodeMatch(OpCodes.And)
); );
var end = matcher.Pos; var end = matcher.Pos - 2;
matcher.Start().Advance(start).RemoveInstructions(end - start).MatchForward(false, /* Remove angle checking codes, then add:
new CodeMatch(ci => ci.IsStloc()) * V_13 = this.bulletCount > 0;
).Advance(1); */
start = matcher.Pos; matcher.Start().Advance(start).RemoveInstructions(end - start).Insert(
matcher.MatchForward(false, new CodeInstruction(OpCodes.Ldarg_0),
new CodeMatch(OpCodes.Ldarg_0), new CodeInstruction(OpCodes.Ldfld, AccessTools.Field(typeof(EjectorComponent), nameof(EjectorComponent.bulletCount))),
new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(EjectorComponent), nameof(EjectorComponent.autoOrbit))) new CodeInstruction(OpCodes.Ldc_I4_0),
new CodeInstruction(OpCodes.Cgt),
new CodeInstruction(OpCodes.Stloc_S, 13)
); );
end = matcher.Pos;
matcher.Start().Advance(start).RemoveInstructions(end - start);
return matcher.InstructionEnumeration(); return matcher.InstructionEnumeration();
} }
} }
private class OverclockEjector: PatchImpl<OverclockEjector> private static class OverclockEjector
{ {
private static Harmony _patch;
public static void Enable(bool on)
{
if (on)
{
_patch ??= Harmony.CreateAndPatchAll(typeof(OverclockEjector));
}
else
{
_patch?.UnpatchSelf();
_patch = null;
}
}
[HarmonyTranspiler] [HarmonyTranspiler]
[HarmonyPatch(typeof(EjectorComponent), nameof(EjectorComponent.InternalUpdate))] [HarmonyPatch(typeof(EjectorComponent), nameof(EjectorComponent.InternalUpdate))]
private static IEnumerable<CodeInstruction> EjectComponent_InternalUpdate_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator) private static IEnumerable<CodeInstruction> EjectAndSiloComponent_InternalUpdate_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{ {
var matcher = new CodeMatcher(instructions, generator); var matcher = new CodeMatcher(instructions, generator);
/* Add a multiply to ejector speed */ /* Add a multiply to ejector speed */
@@ -467,11 +510,25 @@ public class DysonSpherePatch: PatchImpl<DysonSpherePatch>
} }
} }
private class OverclockSilo: PatchImpl<OverclockSilo> private static class OverclockSilo
{ {
private static Harmony _patch;
public static void Enable(bool on)
{
if (on)
{
_patch ??= Harmony.CreateAndPatchAll(typeof(OverclockSilo));
}
else
{
_patch?.UnpatchSelf();
_patch = null;
}
}
[HarmonyTranspiler] [HarmonyTranspiler]
[HarmonyPatch(typeof(SiloComponent), nameof(SiloComponent.InternalUpdate))] [HarmonyPatch(typeof(SiloComponent), nameof(SiloComponent.InternalUpdate))]
private static IEnumerable<CodeInstruction> SiloComponent_InternalUpdate_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator) private static IEnumerable<CodeInstruction> EjectAndSiloComponent_InternalUpdate_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{ {
var matcher = new CodeMatcher(instructions, generator); var matcher = new CodeMatcher(instructions, generator);
/* Add a multiply to ejector speed */ /* Add a multiply to ejector speed */

View File

@@ -2,15 +2,12 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Reflection.Emit; using System.Reflection.Emit;
using BepInEx.Configuration; using BepInEx.Configuration;
using CommonAPI.Systems;
using HarmonyLib; using HarmonyLib;
using UnityEngine; using UnityEngine;
using UnityEngine.UI;
using UXAssist.Common;
namespace CheatEnabler.Patches; namespace CheatEnabler;
public class FactoryPatch: PatchImpl<FactoryPatch> public static class FactoryPatch
{ {
public static ConfigEntry<bool> ImmediateEnabled; public static ConfigEntry<bool> ImmediateEnabled;
public static ConfigEntry<bool> ArchitectModeEnabled; public static ConfigEntry<bool> ArchitectModeEnabled;
@@ -18,48 +15,18 @@ public class FactoryPatch: PatchImpl<FactoryPatch>
public static ConfigEntry<bool> NoCollisionEnabled; public static ConfigEntry<bool> NoCollisionEnabled;
public static ConfigEntry<bool> BeltSignalGeneratorEnabled; public static ConfigEntry<bool> BeltSignalGeneratorEnabled;
public static ConfigEntry<bool> BeltSignalNumberAltFormat; public static ConfigEntry<bool> BeltSignalNumberAltFormat;
public static ConfigEntry<bool> BeltSignalCountGenEnabled;
public static ConfigEntry<bool> BeltSignalCountRemEnabled;
public static ConfigEntry<bool> BeltSignalCountRecipeEnabled; public static ConfigEntry<bool> BeltSignalCountRecipeEnabled;
public static ConfigEntry<bool> RemovePowerSpaceLimitEnabled; public static ConfigEntry<bool> RemovePowerSpaceLimitEnabled;
public static ConfigEntry<bool> BoostWindPowerEnabled; public static ConfigEntry<bool> BoostWindPowerEnabled;
public static ConfigEntry<bool> BoostSolarPowerEnabled; public static ConfigEntry<bool> BoostSolarPowerEnabled;
public static ConfigEntry<bool> BoostFuelPowerEnabled; public static ConfigEntry<bool> BoostFuelPowerEnabled;
public static ConfigEntry<bool> BoostGeothermalPowerEnabled; public static ConfigEntry<bool> BoostGeothermalPowerEnabled;
public static ConfigEntry<bool> WindTurbinesPowerGlobalCoverageEnabled;
public static ConfigEntry<bool> GreaterPowerUsageInLogisticsEnabled;
public static ConfigEntry<bool> ControlPanelRemoteLogisticsEnabled;
private static PressKeyBind _noConditionKey; private static Harmony _factoryPatch;
private static PressKeyBind _noCollisionKey;
public static void Init() public static void Init()
{ {
_noConditionKey = KeyBindings.RegisterKeyBinding(new BuiltinKey if (_factoryPatch != null) return;
{
key = new CombineKey(0, 0, ECombineKeyAction.OnceClick, true),
conflictGroup = KeyBindConflict.MOVEMENT | KeyBindConflict.FLYING | KeyBindConflict.SAILING | KeyBindConflict.BUILD_MODE_1 | KeyBindConflict.KEYBOARD_KEYBIND,
name = "ToggleNoCondition",
canOverride = true
}
);
_noCollisionKey = KeyBindings.RegisterKeyBinding(new BuiltinKey
{
key = new CombineKey(0, 0, ECombineKeyAction.OnceClick, true),
conflictGroup = KeyBindConflict.MOVEMENT | KeyBindConflict.FLYING | KeyBindConflict.SAILING | KeyBindConflict.BUILD_MODE_1 | KeyBindConflict.KEYBOARD_KEYBIND,
name = "ToggleNoCollision",
canOverride = true
}
);
I18N.Add("KEYToggleNoCondition", "[CE] Toggle No Condition Build", "[CE] 切换无条件建造");
I18N.Add("KEYToggleNoCollision", "[CE] Toggle No Collision", "[CE] 切换无碰撞");
I18N.Add("NoConditionOn", "No condition build is enabled!", "无条件建造已开启");
I18N.Add("NoConditionOff", "No condition build is disabled!", "无条件建造已关闭");
I18N.Add("NoCollisionOn", "No collision is enabled!", "无碰撞已开启");
I18N.Add("NoCollisionOff", "No collision is disabled!", "无碰撞已关闭");
I18N.Add("Build without condition is enabled!", "!!Build without condition is enabled!!", "!!无条件建造已开启!!");
I18N.Add("No collision is enabled!", "!!No collision is enabled!!", "!!无碰撞已开启!!");
ImmediateEnabled.SettingChanged += (_, _) => ImmediateBuild.Enable(ImmediateEnabled.Value); ImmediateEnabled.SettingChanged += (_, _) => ImmediateBuild.Enable(ImmediateEnabled.Value);
ArchitectModeEnabled.SettingChanged += (_, _) => ArchitectMode.Enable(ArchitectModeEnabled.Value); ArchitectModeEnabled.SettingChanged += (_, _) => ArchitectMode.Enable(ArchitectModeEnabled.Value);
NoConditionEnabled.SettingChanged += (_, _) => NoConditionBuild.Enable(NoConditionEnabled.Value); NoConditionEnabled.SettingChanged += (_, _) => NoConditionBuild.Enable(NoConditionEnabled.Value);
@@ -71,13 +38,6 @@ public class FactoryPatch: PatchImpl<FactoryPatch>
BoostSolarPowerEnabled.SettingChanged += (_, _) => BoostSolarPower.Enable(BoostSolarPowerEnabled.Value); BoostSolarPowerEnabled.SettingChanged += (_, _) => BoostSolarPower.Enable(BoostSolarPowerEnabled.Value);
BoostFuelPowerEnabled.SettingChanged += (_, _) => BoostFuelPower.Enable(BoostFuelPowerEnabled.Value); BoostFuelPowerEnabled.SettingChanged += (_, _) => BoostFuelPower.Enable(BoostFuelPowerEnabled.Value);
BoostGeothermalPowerEnabled.SettingChanged += (_, _) => BoostGeothermalPower.Enable(BoostGeothermalPowerEnabled.Value); BoostGeothermalPowerEnabled.SettingChanged += (_, _) => BoostGeothermalPower.Enable(BoostGeothermalPowerEnabled.Value);
WindTurbinesPowerGlobalCoverageEnabled.SettingChanged += (_, _) => WindTurbinesPowerGlobalCoverage.Enable(WindTurbinesPowerGlobalCoverageEnabled.Value);
GreaterPowerUsageInLogisticsEnabled.SettingChanged += (_, _) => GreaterPowerUsageInLogistics.Enable(GreaterPowerUsageInLogisticsEnabled.Value);
ControlPanelRemoteLogisticsEnabled.SettingChanged += (_, _) => ControlPanelRemoteLogistics.Enable(ControlPanelRemoteLogisticsEnabled.Value);
}
public static void Start()
{
ImmediateBuild.Enable(ImmediateEnabled.Value); ImmediateBuild.Enable(ImmediateEnabled.Value);
ArchitectMode.Enable(ArchitectModeEnabled.Value); ArchitectMode.Enable(ArchitectModeEnabled.Value);
NoConditionBuild.Enable(NoConditionEnabled.Value); NoConditionBuild.Enable(NoConditionEnabled.Value);
@@ -88,18 +48,13 @@ public class FactoryPatch: PatchImpl<FactoryPatch>
BoostSolarPower.Enable(BoostSolarPowerEnabled.Value); BoostSolarPower.Enable(BoostSolarPowerEnabled.Value);
BoostFuelPower.Enable(BoostFuelPowerEnabled.Value); BoostFuelPower.Enable(BoostFuelPowerEnabled.Value);
BoostGeothermalPower.Enable(BoostGeothermalPowerEnabled.Value); BoostGeothermalPower.Enable(BoostGeothermalPowerEnabled.Value);
GreaterPowerUsageInLogistics.Enable(GreaterPowerUsageInLogisticsEnabled.Value); _factoryPatch = Harmony.CreateAndPatchAll(typeof(FactoryPatch));
ControlPanelRemoteLogistics.Enable(ControlPanelRemoteLogisticsEnabled.Value);
Enable(true);
GameLogic.OnGameBegin += GameMain_Begin_Postfix_For_ImmBuild;
GameLogic.OnDataLoaded += OnDataLoaded;
} }
public static void Uninit() public static void Uninit()
{ {
GameLogic.OnDataLoaded -= OnDataLoaded; _factoryPatch?.UnpatchSelf();
GameLogic.OnGameBegin -= GameMain_Begin_Postfix_For_ImmBuild; _factoryPatch = null;
Enable(false);
ImmediateBuild.Enable(false); ImmediateBuild.Enable(false);
ArchitectMode.Enable(false); ArchitectMode.Enable(false);
NoConditionBuild.Enable(false); NoConditionBuild.Enable(false);
@@ -109,31 +64,6 @@ public class FactoryPatch: PatchImpl<FactoryPatch>
BoostSolarPower.Enable(false); BoostSolarPower.Enable(false);
BoostFuelPower.Enable(false); BoostFuelPower.Enable(false);
BoostGeothermalPower.Enable(false); BoostGeothermalPower.Enable(false);
WindTurbinesPowerGlobalCoverage.Enable(false);
GreaterPowerUsageInLogistics.Enable(false);
ControlPanelRemoteLogistics.Enable(false);
}
private static void OnDataLoaded() => WindTurbinesPowerGlobalCoverage.Enable(WindTurbinesPowerGlobalCoverageEnabled.Value);
public static void OnUpdate()
{
if (_noConditionKey.keyValue)
{
NoConditionEnabled.Value = !NoConditionEnabled.Value;
if (!DSPGame.IsMenuDemo && GameMain.isRunning)
{
UIRoot.instance.uiGame.generalTips.InvokeRealtimeTipAhead((NoConditionEnabled.Value ? "NoConditionOn" : "NoConditionOff").Translate());
}
}
if (_noCollisionKey.keyValue)
{
NoCollisionEnabled.Value = !NoCollisionEnabled.Value;
if (!DSPGame.IsMenuDemo && GameMain.isRunning)
{
UIRoot.instance.uiGame.generalTips.InvokeRealtimeTipAhead((NoCollisionEnabled.Value ? "NoCollisionOn" : "NoCollisionOff").Translate());
}
}
} }
private static void NoCollisionValueChanged() private static void NoCollisionValueChanged()
@@ -143,7 +73,6 @@ public class FactoryPatch: PatchImpl<FactoryPatch>
var obj = coll.gameObject; var obj = coll.gameObject;
if (obj == null) return; if (obj == null) return;
obj.gameObject.SetActive(!NoCollisionEnabled.Value); obj.gameObject.SetActive(!NoCollisionEnabled.Value);
GameMain.data?.warningSystem?.UpdateCriticalWarningText();
} }
public static void ArrivePlanet(PlanetFactory factory) public static void ArrivePlanet(PlanetFactory factory)
@@ -186,6 +115,8 @@ public class FactoryPatch: PatchImpl<FactoryPatch>
} }
} }
[HarmonyPostfix]
[HarmonyPatch(typeof(GameMain), nameof(GameMain.Begin))]
private static void GameMain_Begin_Postfix_For_ImmBuild() private static void GameMain_Begin_Postfix_For_ImmBuild()
{ {
var factory = GameMain.mainPlayer?.factory; var factory = GameMain.mainPlayer?.factory;
@@ -201,23 +132,13 @@ public class FactoryPatch: PatchImpl<FactoryPatch>
private static IEnumerable<CodeInstruction> WarningSystem_hasCriticalWarning_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator) private static IEnumerable<CodeInstruction> WarningSystem_hasCriticalWarning_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{ {
var matcher = new CodeMatcher(instructions, generator); var matcher = new CodeMatcher(instructions, generator);
var label1 = generator.DefineLabel();
var label2 = generator.DefineLabel();
matcher.End().MatchBack(false, matcher.End().MatchBack(false,
new CodeMatch(OpCodes.Ret) new CodeMatch(OpCodes.Ret)
).RemoveInstructions(1); );
matcher.InsertAndAdvance( matcher.InsertAndAdvance(
new CodeInstruction(OpCodes.Brfalse, label1), new CodeInstruction(OpCodes.Ldsfld, AccessTools.Field(typeof(FactoryPatch), nameof(NoConditionEnabled))),
new CodeInstruction(OpCodes.Ldc_I4_1),
new CodeInstruction(OpCodes.Ret),
new CodeInstruction(OpCodes.Ldsfld, AccessTools.Field(typeof(FactoryPatch), nameof(NoConditionEnabled))).WithLabels(label1),
new CodeInstruction(OpCodes.Call, AccessTools.PropertyGetter(typeof(ConfigEntry<bool>), nameof(ConfigEntry<bool>.Value))), new CodeInstruction(OpCodes.Call, AccessTools.PropertyGetter(typeof(ConfigEntry<bool>), nameof(ConfigEntry<bool>.Value))),
new CodeInstruction(OpCodes.Brfalse, label2), new CodeInstruction(OpCodes.Or)
new CodeInstruction(OpCodes.Ldc_I4_1),
new CodeInstruction(OpCodes.Ret),
new CodeInstruction(OpCodes.Ldsfld, AccessTools.Field(typeof(FactoryPatch), nameof(NoCollisionEnabled))).WithLabels(label2),
new CodeInstruction(OpCodes.Call, AccessTools.PropertyGetter(typeof(ConfigEntry<bool>), nameof(ConfigEntry<bool>.Value))),
new CodeInstruction(OpCodes.Ret)
); );
return matcher.InstructionEnumeration(); return matcher.InstructionEnumeration();
} }
@@ -229,52 +150,61 @@ public class FactoryPatch: PatchImpl<FactoryPatch>
var matcher = new CodeMatcher(instructions, generator); var matcher = new CodeMatcher(instructions, generator);
matcher.MatchForward(false, matcher.MatchForward(false,
new CodeMatch(OpCodes.Ldarg_0), new CodeMatch(OpCodes.Ldarg_0),
new CodeMatch(OpCodes.Ldstr, ""), new CodeMatch(OpCodes.Ldstr),
new CodeMatch(OpCodes.Call, AccessTools.PropertySetter(typeof(WarningSystem), nameof(WarningSystem.criticalWarningTexts))) new CodeMatch(OpCodes.Call, AccessTools.PropertySetter(typeof(WarningSystem), nameof(WarningSystem.criticalWarningTexts)))
); );
matcher.Repeat(m => matcher.Repeat(m =>
{ {
var label1 = generator.DefineLabel(); var label1 = generator.DefineLabel();
m.Advance(3).InsertAndAdvance( var label2 = generator.DefineLabel();
new CodeInstruction(OpCodes.Ldarg_0), m.Advance(1).Labels.Add(label1);
Transpilers.EmitDelegate((WarningSystem w) => m.InsertAndAdvance(
{ new CodeInstruction(OpCodes.Ldsfld, AccessTools.Field(typeof(FactoryPatch), nameof(NoConditionEnabled))),
if (NoConditionEnabled.Value) new CodeInstruction(OpCodes.Call, AccessTools.PropertyGetter(typeof(ConfigEntry<bool>), nameof(ConfigEntry<bool>.Value))),
{ new CodeInstruction(OpCodes.Brfalse, label1),
w.criticalWarningTexts = "Build without condition is enabled!".Translate() + "\r\n"; new CodeInstruction(OpCodes.Ldstr, "Build without condition is enabled!"),
} new CodeInstruction(OpCodes.Call, AccessTools.Method(typeof(Localization), nameof(Localization.Translate), new[] { typeof(string) })),
else if (NoCollisionEnabled.Value) new CodeInstruction(OpCodes.Ldstr, "\r\n"),
{ new CodeInstruction(OpCodes.Call, AccessTools.Method(typeof(string), nameof(string.Concat), new[] { typeof(string), typeof(string) })),
w.criticalWarningTexts = "No collision is enabled!".Translate() + "\r\n"; new CodeInstruction(OpCodes.Call, AccessTools.PropertySetter(typeof(WarningSystem), nameof(WarningSystem.criticalWarningTexts)))
}
}
)
); );
if (m.Opcode == OpCodes.Ret) if (m.InstructionAt(2).opcode == OpCodes.Ret)
{ {
m.InsertAndAdvance( m.InsertAndAdvance(
new CodeInstruction(OpCodes.Ldarg_0), new CodeInstruction(OpCodes.Ldarg_0),
new CodeInstruction(OpCodes.Ldfld, AccessTools.Field(typeof(WarningSystem), nameof(WarningSystem.onCriticalWarningTextChanged))), new CodeInstruction(OpCodes.Ldfld, AccessTools.Field(typeof(WarningSystem), nameof(WarningSystem.onCriticalWarningTextChanged))),
new CodeInstruction(OpCodes.Brfalse_S, label1), new CodeInstruction(OpCodes.Brfalse_S, label2),
new CodeInstruction(OpCodes.Ldarg_0), new CodeInstruction(OpCodes.Ldarg_0),
new CodeInstruction(OpCodes.Ldfld, AccessTools.Field(typeof(WarningSystem), nameof(WarningSystem.onCriticalWarningTextChanged))), new CodeInstruction(OpCodes.Ldfld, AccessTools.Field(typeof(WarningSystem), nameof(WarningSystem.onCriticalWarningTextChanged))),
new CodeInstruction(OpCodes.Callvirt, AccessTools.Method(typeof(Action), nameof(Action.Invoke))) new CodeInstruction(OpCodes.Callvirt, AccessTools.Method(typeof(Action), nameof(Action.Invoke)))
); );
m.Labels.Add(label1);
} }
m.InsertAndAdvance(
new CodeInstruction(OpCodes.Br, label2)
).Advance(2).Labels.Add(label2);
}); });
return matcher.InstructionEnumeration(); return matcher.InstructionEnumeration();
} }
private class ImmediateBuild: PatchImpl<ImmediateBuild> private static class ImmediateBuild
{ {
protected override void OnEnable() private static Harmony _immediatePatch;
public static void Enable(bool enable)
{ {
if (enable)
{
if (_immediatePatch != null) return;
var factory = GameMain.mainPlayer?.factory; var factory = GameMain.mainPlayer?.factory;
if (factory != null) if (factory != null)
{ {
ArrivePlanet(factory); ArrivePlanet(factory);
} }
_immediatePatch = Harmony.CreateAndPatchAll(typeof(ImmediateBuild));
return;
}
_immediatePatch?.UnpatchSelf();
_immediatePatch = null;
} }
[HarmonyTranspiler] [HarmonyTranspiler]
@@ -313,7 +243,7 @@ public class FactoryPatch: PatchImpl<FactoryPatch>
} }
[HarmonyPostfix] [HarmonyPostfix]
[HarmonyPatch(typeof(UXAssist.Functions.PlanetFunctions), nameof(UXAssist.Functions.PlanetFunctions.BuildOrbitalCollectors))] [HarmonyPatch(typeof(UXAssist.PlanetFunctions), nameof(UXAssist.PlanetFunctions.BuildOrbitalCollectors))]
private static void UXAssist_PlanetFunctions_BuildOrbitalCollectors_Postfix() private static void UXAssist_PlanetFunctions_BuildOrbitalCollectors_Postfix()
{ {
var factory = GameMain.mainPlayer?.factory; var factory = GameMain.mainPlayer?.factory;
@@ -324,24 +254,33 @@ public class FactoryPatch: PatchImpl<FactoryPatch>
} }
} }
private class ArchitectMode: PatchImpl<ArchitectMode> private static class ArchitectMode
{ {
private static Harmony _patch;
private static bool[] _canBuildItems; private static bool[] _canBuildItems;
protected override void OnEnable() public static void Enable(bool enable)
{ {
if (enable)
{
if (_patch != null) return;
var factory = GameMain.mainPlayer?.factory; var factory = GameMain.mainPlayer?.factory;
if (factory != null) if (factory != null)
{ {
ArrivePlanet(factory); ArrivePlanet(factory);
} }
_patch = Harmony.CreateAndPatchAll(typeof(ArchitectMode));
return;
}
_patch?.UnpatchSelf();
_patch = null;
} }
[HarmonyPrefix] [HarmonyPrefix]
[HarmonyPatch(typeof(StorageComponent), nameof(StorageComponent.TakeTailItems), [typeof(int), typeof(int), typeof(int), typeof(bool)], [HarmonyPatch(typeof(StorageComponent), nameof(StorageComponent.TakeTailItems), new[] { typeof(int), typeof(int), typeof(int), typeof(bool) },
[ArgumentType.Ref, ArgumentType.Ref, ArgumentType.Out, ArgumentType.Normal])] new[] { ArgumentType.Ref, ArgumentType.Ref, ArgumentType.Out, ArgumentType.Normal })]
[HarmonyPatch(typeof(StorageComponent), nameof(StorageComponent.TakeTailItems), [typeof(int), typeof(int), typeof(int[]), typeof(int), typeof(bool)], [HarmonyPatch(typeof(StorageComponent), nameof(StorageComponent.TakeTailItems), new[] { typeof(int), typeof(int), typeof(int[]), typeof(int), typeof(bool) },
[ArgumentType.Ref, ArgumentType.Ref, ArgumentType.Normal, ArgumentType.Out, ArgumentType.Normal])] new[] { ArgumentType.Ref, ArgumentType.Ref, ArgumentType.Normal, ArgumentType.Out, ArgumentType.Normal })]
public static bool TakeTailItemsPatch(StorageComponent __instance, int itemId) public static bool TakeTailItemsPatch(StorageComponent __instance, int itemId)
{ {
if (__instance == null || __instance.id != GameMain.mainPlayer.package.id) return true; if (__instance == null || __instance.id != GameMain.mainPlayer.package.id) return true;
@@ -378,64 +317,33 @@ public class FactoryPatch: PatchImpl<FactoryPatch>
} }
} }
private class NoConditionBuild: PatchImpl<NoConditionBuild> private static class NoConditionBuild
{ {
protected override void OnEnable() private static Harmony _noConditionPatch;
public static void Enable(bool on)
{ {
GameMain.data?.warningSystem?.UpdateCriticalWarningText(); GameMain.data?.warningSystem?.UpdateCriticalWarningText();
if (on)
{
_noConditionPatch ??= Harmony.CreateAndPatchAll(typeof(NoConditionBuild));
return;
} }
_noConditionPatch?.UnpatchSelf();
protected override void OnDisable() _noConditionPatch = null;
{
GameMain.data?.warningSystem?.UpdateCriticalWarningText();
} }
[HarmonyTranspiler, HarmonyPriority(Priority.Last)] [HarmonyTranspiler, HarmonyPriority(Priority.Last)]
[HarmonyPatch(typeof(BuildTool_Addon), nameof(BuildTool_Addon.CheckBuildConditions))] [HarmonyPatch(typeof(BuildTool_Addon), nameof(BuildTool_Addon.CheckBuildConditions))]
// [HarmonyPatch(typeof(BuildTool_Click), nameof(BuildTool_Click.CheckBuildConditions))]
[HarmonyPatch(typeof(BuildTool_Inserter), nameof(BuildTool_Inserter.CheckBuildConditions))] [HarmonyPatch(typeof(BuildTool_Inserter), nameof(BuildTool_Inserter.CheckBuildConditions))]
[HarmonyPatch(typeof(BuildTool_Path), nameof(BuildTool_Path.CheckBuildConditions))]
private static IEnumerable<CodeInstruction> BuildTool_CheckBuildConditions_Transpiler(IEnumerable<CodeInstruction> instructions) private static IEnumerable<CodeInstruction> BuildTool_CheckBuildConditions_Transpiler(IEnumerable<CodeInstruction> instructions)
{ {
yield return new CodeInstruction(OpCodes.Ldc_I4_1); yield return new CodeInstruction(OpCodes.Ldc_I4_1);
yield return new CodeInstruction(OpCodes.Ret); yield return new CodeInstruction(OpCodes.Ret);
} }
[HarmonyTranspiler, HarmonyPriority(Priority.First)]
[HarmonyPatch(typeof(BuildTool_Path), nameof(BuildTool_Path.CheckBuildConditions))]
private static IEnumerable<CodeInstruction> BuildTool_Path_CheckBuildConditions_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{
var matcher = new CodeMatcher(instructions, generator);
var label1 = generator.DefineLabel();
var label2 = generator.DefineLabel();
matcher.Start().InsertAndAdvance(
new CodeInstruction(OpCodes.Br, label1)
);
matcher.MatchForward(false,
new CodeMatch(OpCodes.Ldarg_0),
new CodeMatch(OpCodes.Call, AccessTools.PropertyGetter(typeof(BuildTool), nameof(BuildTool.buildPreviews))),
new CodeMatch(OpCodes.Callvirt, AccessTools.PropertyGetter(typeof(List<BuildPreview>), nameof(List<BuildPreview>.Count))),
new CodeMatch(ci => ci.IsStloc())
);
matcher.Labels.Add(label1);
matcher.Advance(4).InsertAndAdvance(
new CodeInstruction(OpCodes.Br, label2)
);
matcher.MatchForward(false,
new CodeMatch(ci => ci.IsLdloc()),
new CodeMatch(ci => ci.Branches(out _)),
new CodeMatch(OpCodes.Ldarg_0),
new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(BuildTool_Path), nameof(BuildTool_Path.waitForConfirm))),
new CodeMatch(ci => ci.Branches(out _))
);
var operand = matcher.Operand;
matcher.InsertAndAdvance(
new CodeInstruction(OpCodes.Ldc_I4_1).WithLabels(label2),
new CodeInstruction(OpCodes.Stloc_S, operand)
);
return matcher.InstructionEnumeration();
}
[HarmonyTranspiler, HarmonyPriority(Priority.Last)] [HarmonyTranspiler, HarmonyPriority(Priority.Last)]
[HarmonyPatch(typeof(BuildTool_BlueprintPaste), nameof(BuildTool_BlueprintPaste.CheckBuildConditions))]
[HarmonyPatch(typeof(BuildTool_Click), nameof(BuildTool_Click.CheckBuildConditions))] [HarmonyPatch(typeof(BuildTool_Click), nameof(BuildTool_Click.CheckBuildConditions))]
private static IEnumerable<CodeInstruction> BuildTool_Click_CheckBuildConditions_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator) private static IEnumerable<CodeInstruction> BuildTool_Click_CheckBuildConditions_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{ {
@@ -466,8 +374,9 @@ public class FactoryPatch: PatchImpl<FactoryPatch>
} }
} }
public class BeltSignalGenerator: PatchImpl<BeltSignalGenerator> public static class BeltSignalGenerator
{ {
private static Harmony _beltSignalPatch;
private static Dictionary<int, BeltSignal>[] _signalBelts; private static Dictionary<int, BeltSignal>[] _signalBelts;
private static Dictionary<long, int> _portalFrom; private static Dictionary<long, int> _portalFrom;
private static Dictionary<int, HashSet<long>> _portalTo; private static Dictionary<int, HashSet<long>> _portalTo;
@@ -485,15 +394,16 @@ public class FactoryPatch: PatchImpl<FactoryPatch>
public float[] SourceProgress; public float[] SourceProgress;
} }
protected override void OnEnable() public static void Enable(bool on)
{
if (on)
{ {
InitSignalBelts(); InitSignalBelts();
GameLogic.OnGameBegin += GameMain_Begin_Postfix; _beltSignalPatch ??= Harmony.CreateAndPatchAll(typeof(BeltSignalGenerator));
return;
} }
_beltSignalPatch?.UnpatchSelf();
protected override void OnDisable() _beltSignalPatch = null;
{
GameLogic.OnGameBegin -= GameMain_Begin_Postfix;
_initialized = false; _initialized = false;
_signalBelts = null; _signalBelts = null;
_signalBeltsCapacity = 0; _signalBeltsCapacity = 0;
@@ -724,6 +634,8 @@ public class FactoryPatch: PatchImpl<FactoryPatch>
set.Remove(v); set.Remove(v);
} }
[HarmonyPostfix]
[HarmonyPatch(typeof(GameMain), nameof(GameMain.Begin))]
private static void GameMain_Begin_Postfix() private static void GameMain_Begin_Postfix()
{ {
if (BeltSignalGeneratorEnabled.Value) InitSignalBelts(); if (BeltSignalGeneratorEnabled.Value) InitSignalBelts();
@@ -850,33 +762,21 @@ public class FactoryPatch: PatchImpl<FactoryPatch>
public static void ProcessBeltSignals() public static void ProcessBeltSignals()
{ {
if (!_initialized) return; if (!_initialized) return;
var data = GameMain.data; var factories = GameMain.data?.factories;
var factories = data?.factories;
if (factories == null) return; if (factories == null) return;
PerformanceMonitor.BeginSample(ECpuWorkEntry.Belt); PerformanceMonitor.BeginSample(ECpuWorkEntry.Belt);
for (var index = data.factoryCount - 1; index >= 0; index--) foreach (var factory in factories)
{ {
var factory = factories[index];
if (factory == null) continue; if (factory == null) continue;
var belts = GetSignalBelts(index); var belts = GetSignalBelts(factory.index);
if (belts == null || belts.Count == 0) continue; if (belts == null || belts.Count == 0) continue;
var factoryProductionStat = GameMain.statistics.production.factoryStatPool[index]; var factoryProductionStat = GameMain.statistics.production.factoryStatPool[factory.index];
var productRegister = factoryProductionStat.productRegister; var productRegister = factoryProductionStat.productRegister;
var consumeRegister = factoryProductionStat.consumeRegister; var consumeRegister = factoryProductionStat.consumeRegister;
var countRecipe = BeltSignalCountRecipeEnabled.Value; var countRecipe = BeltSignalCountRecipeEnabled.Value;
var cargoTraffic = factory.cargoTraffic; var cargoTraffic = factory.cargoTraffic;
var beltCount = cargoTraffic.beltCursor;
List<int> beltsToRemove = null;
foreach (var pair in belts) foreach (var pair in belts)
{ {
if (pair.Key >= beltCount)
{
if (beltsToRemove == null)
beltsToRemove = [pair.Key];
else
beltsToRemove.Add(pair.Key);
continue;
}
var beltSignal = pair.Value; var beltSignal = pair.Value;
var signalId = beltSignal.SignalId; var signalId = beltSignal.SignalId;
switch (signalId) switch (signalId)
@@ -890,7 +790,7 @@ public class FactoryPatch: PatchImpl<FactoryPatch>
int itemId; int itemId;
if ((itemId = cargoPath.TryPickItem(belt.segIndex + belt.segPivotOffset - 5, 12, out var stack, out _)) > 0) if ((itemId = cargoPath.TryPickItem(belt.segIndex + belt.segPivotOffset - 5, 12, out var stack, out _)) > 0)
{ {
if (BeltSignalCountRemEnabled.Value) consumeRegister[itemId] += stack; consumeRegister[itemId] += stack;
} }
continue; continue;
@@ -947,7 +847,7 @@ public class FactoryPatch: PatchImpl<FactoryPatch>
var inc = beltSignal.Inc; var inc = beltSignal.Inc;
if (!cargoPath.TryInsertItem(belt.segIndex + belt.segPivotOffset, signalId, stack, inc)) continue; if (!cargoPath.TryInsertItem(belt.segIndex + belt.segPivotOffset, signalId, stack, inc)) continue;
if (hasSpeedLimit) beltSignal.Progress -= 3600; if (hasSpeedLimit) beltSignal.Progress -= 3600;
if (BeltSignalCountGenEnabled.Value) productRegister[signalId] += stack; productRegister[signalId] += stack;
if (!countRecipe) continue; if (!countRecipe) continue;
var sources = beltSignal.Sources; var sources = beltSignal.Sources;
if (sources == null) continue; if (sources == null) continue;
@@ -974,11 +874,6 @@ public class FactoryPatch: PatchImpl<FactoryPatch>
} }
} }
} }
if (beltsToRemove == null) continue;
foreach (var beltId in beltsToRemove)
{
belts.Remove(beltId);
}
} }
PerformanceMonitor.EndSample(ECpuWorkEntry.Belt); PerformanceMonitor.EndSample(ECpuWorkEntry.Belt);
@@ -1133,8 +1028,20 @@ public class FactoryPatch: PatchImpl<FactoryPatch>
/* END: Item sources calculation */ /* END: Item sources calculation */
} }
private class RemovePowerSpaceLimit: PatchImpl<RemovePowerSpaceLimit> private static class RemovePowerSpaceLimit
{ {
private static Harmony _patch;
public static void Enable(bool enable)
{
if (enable)
{
_patch ??= Harmony.CreateAndPatchAll(typeof(RemovePowerSpaceLimit));
return;
}
_patch?.UnpatchSelf();
_patch = null;
}
[HarmonyTranspiler] [HarmonyTranspiler]
[HarmonyPatch(typeof(BuildTool_Click), nameof(BuildTool_Click.CheckBuildConditions))] [HarmonyPatch(typeof(BuildTool_Click), nameof(BuildTool_Click.CheckBuildConditions))]
[HarmonyPatch(typeof(BuildTool_BlueprintPaste), nameof(BuildTool_BlueprintPaste.CheckBuildConditions))] [HarmonyPatch(typeof(BuildTool_BlueprintPaste), nameof(BuildTool_BlueprintPaste.CheckBuildConditions))]
@@ -1163,8 +1070,19 @@ public class FactoryPatch: PatchImpl<FactoryPatch>
} }
} }
private class BoostWindPower: PatchImpl<BoostWindPower> private static class BoostWindPower
{ {
private static Harmony _patch;
public static void Enable(bool enable)
{
if (enable)
{
_patch ??= Harmony.CreateAndPatchAll(typeof(BoostWindPower));
return;
}
_patch?.UnpatchSelf();
_patch = null;
}
[HarmonyTranspiler] [HarmonyTranspiler]
[HarmonyPatch(typeof(PowerGeneratorComponent), nameof(PowerGeneratorComponent.EnergyCap_Wind))] [HarmonyPatch(typeof(PowerGeneratorComponent), nameof(PowerGeneratorComponent.EnergyCap_Wind))]
private static IEnumerable<CodeInstruction> PowerGeneratorComponent_EnergyCap_Wind_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator) private static IEnumerable<CodeInstruction> PowerGeneratorComponent_EnergyCap_Wind_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
@@ -1188,8 +1106,19 @@ public class FactoryPatch: PatchImpl<FactoryPatch>
} }
} }
private class BoostSolarPower: PatchImpl<BoostSolarPower> private static class BoostSolarPower
{ {
private static Harmony _patch;
public static void Enable(bool enable)
{
if (enable)
{
_patch ??= Harmony.CreateAndPatchAll(typeof(BoostSolarPower));
return;
}
_patch?.UnpatchSelf();
_patch = null;
}
[HarmonyTranspiler] [HarmonyTranspiler]
[HarmonyPatch(typeof(PowerGeneratorComponent), nameof(PowerGeneratorComponent.EnergyCap_PV))] [HarmonyPatch(typeof(PowerGeneratorComponent), nameof(PowerGeneratorComponent.EnergyCap_PV))]
private static IEnumerable<CodeInstruction> PowerGeneratorComponent_EnergyCap_PV_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator) private static IEnumerable<CodeInstruction> PowerGeneratorComponent_EnergyCap_PV_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
@@ -1212,8 +1141,19 @@ public class FactoryPatch: PatchImpl<FactoryPatch>
} }
} }
private class BoostFuelPower: PatchImpl<BoostFuelPower> private static class BoostFuelPower
{ {
private static Harmony _patch;
public static void Enable(bool enable)
{
if (enable)
{
_patch ??= Harmony.CreateAndPatchAll(typeof(BoostFuelPower));
return;
}
_patch?.UnpatchSelf();
_patch = null;
}
[HarmonyTranspiler] [HarmonyTranspiler]
[HarmonyPatch(typeof(PowerGeneratorComponent), nameof(PowerGeneratorComponent.EnergyCap_Fuel))] [HarmonyPatch(typeof(PowerGeneratorComponent), nameof(PowerGeneratorComponent.EnergyCap_Fuel))]
private static IEnumerable<CodeInstruction> PowerGeneratorComponent_EnergyCap_Fuel_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator) private static IEnumerable<CodeInstruction> PowerGeneratorComponent_EnergyCap_Fuel_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
@@ -1253,8 +1193,19 @@ public class FactoryPatch: PatchImpl<FactoryPatch>
} }
} }
private class BoostGeothermalPower: PatchImpl<BoostGeothermalPower> private static class BoostGeothermalPower
{ {
private static Harmony _patch;
public static void Enable(bool enable)
{
if (enable)
{
_patch ??= Harmony.CreateAndPatchAll(typeof(BoostGeothermalPower));
return;
}
_patch?.UnpatchSelf();
_patch = null;
}
[HarmonyTranspiler] [HarmonyTranspiler]
[HarmonyPatch(typeof(PowerGeneratorComponent), nameof(PowerGeneratorComponent.EnergyCap_GTH))] [HarmonyPatch(typeof(PowerGeneratorComponent), nameof(PowerGeneratorComponent.EnergyCap_GTH))]
private static IEnumerable<CodeInstruction> PowerGeneratorComponent_EnergyCap_GTH_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator) private static IEnumerable<CodeInstruction> PowerGeneratorComponent_EnergyCap_GTH_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
@@ -1278,345 +1229,4 @@ public class FactoryPatch: PatchImpl<FactoryPatch>
} }
} }
private static class WindTurbinesPowerGlobalCoverage
{
private static bool _patched;
private static PrefabDesc _prefabdesc;
private static float _oldCoverRadius;
private static float _oldConnectDistance;
private const int WindTurbineId = 2203;
private const float WindTurbineNewCoverageDistance = 500f;
public static void Enable(bool enable)
{
if (enable)
{
if (_patched) return;
_patched = true;
var itemProto = LDB.items.Select(WindTurbineId);
_oldCoverRadius = itemProto.prefabDesc.powerCoverRadius;
_oldConnectDistance = itemProto.prefabDesc.powerConnectDistance;
itemProto.prefabDesc.powerCoverRadius = WindTurbineNewCoverageDistance;
itemProto.prefabDesc.powerConnectDistance = WindTurbineNewCoverageDistance;
_prefabdesc = itemProto.prefabDesc;
}
else
{
if (!_patched) return;
_patched = false;
_prefabdesc.powerCoverRadius = _oldCoverRadius;
_prefabdesc.powerConnectDistance = _oldConnectDistance;
}
// Iterate all factories and update wind turbines power nodes
foreach (var factory in GameMain.data.factories)
{
var powerSystem = factory?.powerSystem;
if (powerSystem == null) continue;
for (var i = powerSystem.nodeCursor - 1; i >= 0; i--)
{
ref var node = ref powerSystem.nodePool[i];
if (node.id != i) continue;
ref var entity = ref factory.entityPool[node.entityId];
if (entity.protoId != WindTurbineId) continue;
// Disconnect from power system
powerSystem.OnNodeRemoving(i);
// Set new properties
node.connectDistance = _prefabdesc.powerConnectDistance;
node.coverRadius = _prefabdesc.powerCoverRadius;
// Connect back to power system
powerSystem.OnNodeAdded(i);
}
// Refresh power nodes rendering if factory is loaded
if (factory.planet.factoryLoaded)
{
factory.planet.factoryModel.RefreshPowerNodes();
}
}
}
}
private class GreaterPowerUsageInLogistics: PatchImpl<GreaterPowerUsageInLogistics>
{
protected override void OnEnable()
{
var window = UIRoot.instance?.uiGame?.stationWindow;
if (window == null) return;
window._Close();
window.maxMiningSpeedSlider.maxValue = 27f;
}
protected override void OnDisable()
{
var window = UIRoot.instance?.uiGame?.stationWindow;
if (window == null) return;
window._Close();
window.maxMiningSpeedSlider.maxValue = 20f;
}
[HarmonyTranspiler]
[HarmonyPatch(typeof(UIStationWindow), nameof(UIStationWindow.OnStationIdChange))]
private static IEnumerable<CodeInstruction> UIStationWindow_OnStationIdChange_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{
var matcher = new CodeMatcher(instructions, generator);
matcher.Start().Insert(
new CodeInstruction(OpCodes.Ldarg_0),
Transpilers.EmitDelegate((UIStationWindow window) =>
{
window.maxMiningSpeedSlider.maxValue = 27f;
})
).MatchForward(false,
new CodeMatch(OpCodes.Ldarg_0),
new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(UIStationWindow), nameof(UIStationWindow.maxChargePowerSlider))),
new CodeMatch(ci => ci.IsLdloc()),
new CodeMatch(ci => ci.opcode == OpCodes.Ldc_I4 && ci.OperandIs(0xC350)),
new CodeMatch(OpCodes.Conv_I8)
);
var pos = matcher.Pos + 1;
matcher.Advance(5).MatchForward(false,
new CodeMatch(OpCodes.Conv_R4),
new CodeMatch(OpCodes.Callvirt, AccessTools.PropertySetter(typeof(Slider), nameof(Slider.value)))
);
var pos2 = matcher.Pos + 2;
matcher.Start().Advance(pos);
var ldvar = matcher.InstructionAt(1).Clone();
var locWorkEnergyPerTick = matcher.InstructionAt(-2).operand;
matcher.RemoveInstructions(pos2 - pos).InsertAndAdvance(
ldvar,
new CodeInstruction(OpCodes.Ldloc_S, locWorkEnergyPerTick),
Transpilers.EmitDelegate((UIStationWindow window, long maxWorkEnergy, long workEnergyPerTick) =>
{
var maxSliderValue = maxWorkEnergy / 50000L;
window.maxChargePowerSlider.maxValue = maxSliderValue + 9;
window.maxChargePowerSlider.minValue = maxWorkEnergy / 500000L;
if (workEnergyPerTick <= maxWorkEnergy)
window.maxChargePowerSlider.Set(workEnergyPerTick / 50000L, false);
else
window.maxChargePowerSlider.Set(maxSliderValue + (workEnergyPerTick - 1) / maxWorkEnergy + 1, false);
})
);
matcher.MatchForward(false,
new CodeMatch(OpCodes.Ldarg_0),
new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(UIStationWindow), nameof(UIStationWindow.maxMiningSpeedSlider))),
new CodeMatch(OpCodes.Ldarg_0),
new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(UIStationWindow), nameof(UIStationWindow.factorySystem))),
new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(FactorySystem), nameof(FactorySystem.minerPool))),
new CodeMatch(ci => ci.IsLdloc()),
new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(StationComponent), nameof(StationComponent.minerId))),
new CodeMatch(OpCodes.Ldelema, typeof(MinerComponent)),
new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(MinerComponent), nameof(MinerComponent.speed)))
);
pos = matcher.Pos + 9;
matcher.Advance(5).MatchForward(false,
new CodeMatch(OpCodes.Conv_R4),
new CodeMatch(OpCodes.Callvirt, AccessTools.PropertySetter(typeof(Slider), nameof(Slider.value)))
);
pos2 = matcher.Pos;
matcher.Start().Advance(pos).RemoveInstructions(pos2 - pos).Insert(
Transpilers.EmitDelegate((int speed) =>
{
if (speed <= 30000)
return (speed - 10000) / 1000;
return (speed - 30000) / 10000 + 20;
})
);
return matcher.InstructionEnumeration();
}
[HarmonyTranspiler]
[HarmonyPatch(typeof(UIStationWindow), nameof(UIStationWindow.OnMaxMiningSpeedChange))]
private static IEnumerable<CodeInstruction> UIStationWindow_OnMaxMiningSpeedChange_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{
var matcher = new CodeMatcher(instructions, generator);
matcher.MatchForward(false,
new CodeMatch(ci => ci.opcode == OpCodes.Ldc_I4 && ci.OperandIs(10000)),
new CodeMatch(OpCodes.Ldarg_1)
);
var pos = matcher.Pos;
matcher.MatchForward(false,
new CodeMatch(OpCodes.Stloc_1)
);
var pos2 = matcher.Pos;
matcher.Start().Advance(pos);
var labels = matcher.Labels;
matcher.RemoveInstructions(pos2 - pos);
matcher.Insert(
new CodeInstruction(OpCodes.Ldarg_1).WithLabels(labels),
Transpilers.EmitDelegate((float value) =>
{
var intval = (int)(value + 0.5f);
if (intval <= 20)
return intval * 1000 + 10000;
return (intval - 20) * 10000 + 30000;
})
);
return matcher.InstructionEnumeration();
}
[HarmonyTranspiler]
[HarmonyPatch(typeof(UIStationWindow), nameof(UIStationWindow.OnMaxChargePowerSliderValueChange))]
private static IEnumerable<CodeInstruction> UIStationWindow_OnMaxChargePowerSliderValueChange_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{
var matcher = new CodeMatcher(instructions, generator);
matcher.MatchForward(false,
new CodeMatch(OpCodes.Ldarg_0),
new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(UIStationWindow), nameof(UIStationWindow.factory))),
new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(PlanetFactory), nameof(PlanetFactory.powerSystem))),
new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(PowerSystem), nameof(PowerSystem.consumerPool)))
);
var labels = matcher.Labels;
matcher.Labels = null;
matcher.Insert(
new CodeInstruction(OpCodes.Ldarg_0).WithLabels(labels),
new CodeInstruction(OpCodes.Ldarg_1),
Transpilers.EmitDelegate((UIStationWindow window, float value) =>
{
float prevMax = window.workEnergyPrefab * 5L / 50000L;
if (value <= prevMax)
{
return value;
}
return prevMax * (value - prevMax + 1);
}),
new CodeInstruction(OpCodes.Starg_S, 1)
);
return matcher.InstructionEnumeration();
}
}
private class ControlPanelRemoteLogistics: PatchImpl<ControlPanelRemoteLogistics>
{
[HarmonyTranspiler]
[HarmonyPatch(typeof(UIControlPanelDispenserInspector), nameof(UIControlPanelDispenserInspector.OnItemIconMouseDown))]
[HarmonyPatch(typeof(UIControlPanelDispenserInspector), nameof(UIControlPanelDispenserInspector.OnHoldupItemClick))]
[HarmonyPatch(typeof(UIControlPanelDispenserInspector), nameof(UIControlPanelDispenserInspector.OnCourierIconClick))]
private static IEnumerable<CodeInstruction> UIControlPanelDispenserInspector_OnItemIconMouseDown_Transpiler(IEnumerable<CodeInstruction> instructions)
{
var matcher = new CodeMatcher(instructions);
Label? branch = null;
matcher.MatchForward(false,
new CodeMatch(OpCodes.Ldarg_0),
new CodeMatch(OpCodes.Call, AccessTools.PropertyGetter(typeof(UIControlPanelDispenserInspector), nameof(UIControlPanelDispenserInspector.isLocal))),
new CodeMatch(ci => ci.Branches(out branch))
).Repeat(
m =>
{
if (branch == null)
{
m.Advance(3);
return;
}
var labels = m.Labels;
m.RemoveInstructions(3).InsertAndAdvance(
new CodeInstruction(OpCodes.Br, branch.Value).WithLabels(labels)
);
}
);
return matcher.InstructionEnumeration();
}
[HarmonyTranspiler]
[HarmonyPatch(typeof(UIControlPanelStationInspector), nameof(UIControlPanelStationInspector.OnShipIconClick))]
[HarmonyPatch(typeof(UIControlPanelStationInspector), nameof(UIControlPanelStationInspector.OnWarperIconClick))]
[HarmonyPatch(typeof(UIControlPanelStationInspector), nameof(UIControlPanelStationInspector.OnDroneIconClick))]
private static IEnumerable<CodeInstruction> UIControlPanelStationInspector_OnShipIconClick_Transpiler(IEnumerable<CodeInstruction> instructions)
{
var matcher = new CodeMatcher(instructions);
Label? branch = null;
matcher.MatchForward(false,
new CodeMatch(OpCodes.Ldarg_0),
new CodeMatch(OpCodes.Call, AccessTools.PropertyGetter(typeof(UIControlPanelStationInspector), nameof(UIControlPanelStationInspector.isLocal))),
new CodeMatch(ci => ci.Branches(out branch))
).Repeat(
m =>
{
if (branch == null)
{
m.Advance(3);
return;
}
var labels = m.Labels;
m.RemoveInstructions(3).InsertAndAdvance(
new CodeInstruction(OpCodes.Br, branch.Value).WithLabels(labels)
);
}
);
return matcher.InstructionEnumeration();
}
[HarmonyTranspiler]
[HarmonyPatch(typeof(UIControlPanelStationStorage), nameof(UIControlPanelStationStorage.OnItemIconMouseDown))]
private static IEnumerable<CodeInstruction> UIControlPanelStationStorage_OnItemIconMouseDown_Transpiler(IEnumerable<CodeInstruction> instructions)
{
var matcher = new CodeMatcher(instructions);
Label? branch = null;
matcher.MatchForward(false,
new CodeMatch(OpCodes.Ldarg_0),
new CodeMatch(OpCodes.Call, AccessTools.PropertyGetter(typeof(UIControlPanelStationStorage), nameof(UIControlPanelStationStorage.isLocal))),
new CodeMatch(ci => ci.Branches(out branch))
).Repeat(
m =>
{
if (branch == null)
{
m.Advance(3);
return;
}
var labels = m.Labels;
m.RemoveInstructions(3).InsertAndAdvance(
new CodeInstruction(OpCodes.Br, branch.Value).WithLabels(labels)
);
}
);
return matcher.InstructionEnumeration();
}
[HarmonyTranspiler]
[HarmonyPatch(typeof(UIControlPanelStationStorage), nameof(UIControlPanelStationStorage.OnTakeBackButtonClick))]
private static IEnumerable<CodeInstruction> UIControlPanelStationStorage_OnTakeBackButtonClick_Transpiler(IEnumerable<CodeInstruction> instructions)
{
var matcher = new CodeMatcher(instructions);
matcher.MatchForward(false,
new CodeMatch(OpCodes.Ldarg_0),
new CodeMatch(OpCodes.Call, AccessTools.PropertyGetter(typeof(UIControlPanelStationStorage), nameof(UIControlPanelStationStorage.isLocal))),
new CodeMatch(ci => ci.Branches(out _))
).Repeat(
m =>
{
var labels = m.Labels;
m.RemoveInstructions(3).Labels.AddRange(labels);
}
);
return matcher.InstructionEnumeration();
}
[HarmonyTranspiler]
[HarmonyPatch(typeof(UIControlPanelVeinCollectorPanel), nameof(UIControlPanelVeinCollectorPanel.OnProductIconClick))]
private static IEnumerable<CodeInstruction> UIControlPanelVeinCollectorPanel_OnProductIconClick_Transpiler(IEnumerable<CodeInstruction> instructions)
{
var matcher = new CodeMatcher(instructions);
Label? branch = null;
matcher.MatchForward(false,
new CodeMatch(OpCodes.Ldarg_0),
new CodeMatch(OpCodes.Call, AccessTools.PropertyGetter(typeof(UIControlPanelVeinCollectorPanel), nameof(UIControlPanelVeinCollectorPanel.isLocal))),
new CodeMatch(ci => ci.Branches(out branch))
).Repeat(
m =>
{
if (branch == null)
{
m.Advance(3);
return;
}
var labels = m.Labels;
m.RemoveInstructions(3).InsertAndAdvance(
new CodeInstruction(OpCodes.Br, branch.Value).WithLabels(labels)
);
}
);
return matcher.InstructionEnumeration();
}
}
} }

View File

@@ -1,152 +0,0 @@
using HarmonyLib;
using UXAssist.Common;
namespace CheatEnabler.Functions;
public static class DysonSphereFunctions
{
public static void Init()
{
I18N.Add("You are not in any system.", "You are not in any system.", "你不在任何星系中");
I18N.Add("There is no Dyson Sphere shell on \"{0}\".", "There is no Dyson Sphere shell on \"{0}\".", "“{0}”上没有可建造的戴森壳");
I18N.Add("This will complete all Dyson Sphere shells on \"{0}\" instantly. Are you sure?", "This will complete all Dyson Sphere shells on \"{0}\" instantly. Are you sure?", "这将立即完成“{0}”上的所有戴森壳。你确定吗?");
}
public static void CompleteShellsInstantly()
{
StarData star = null;
var dysonEditor = UIRoot.instance?.uiGame?.dysonEditor;
if (dysonEditor != null && dysonEditor.gameObject.activeSelf)
{
star = dysonEditor.selection.viewStar;
}
if (star == null)
{
star = GameMain.data?.localStar;
if (star == null)
{
UIMessageBox.Show("CheatEnabler".Translate(), "You are not in any system.".Translate(), "确定".Translate(), 3, null);
return;
}
}
var dysonSphere = GameMain.data?.dysonSpheres[star.index];
if (dysonSphere == null || dysonSphere.layerCount == 0)
{
UIMessageBox.Show("CheatEnabler".Translate(), string.Format("There is no Dyson Sphere shell on \"{0}\".".Translate(), star.displayName), "确定".Translate(), 3, null);
return;
}
UIMessageBox.Show("CheatEnabler".Translate(), string.Format("This will complete all Dyson Sphere shells on \"{0}\" instantly. Are you sure?".Translate(), star.displayName), "取消".Translate(), "确定".Translate(), 2, null, () =>
{
var totalNodeSpInfo = AccessTools.Field(typeof(DysonSphereLayer), "totalNodeSP");
var totalFrameSpInfo = AccessTools.Field(typeof(DysonSphereLayer), "totalFrameSP");
var totalCpInfo = AccessTools.Field(typeof(DysonSphereLayer), "totalCP");
var rocketCount = 0L;
var solarSailCount = 0L;
foreach (var dysonSphereLayer in dysonSphere.layersIdBased)
{
if (dysonSphereLayer == null) continue;
long totalNodeSp = 0;
long totalFrameSp = 0;
long totalCp = 0;
for (var i = dysonSphereLayer.frameCursor - 1; i >= 0; i--)
{
var dysonFrame = dysonSphereLayer.framePool[i];
if (dysonFrame == null || dysonFrame.id != i) continue;
totalFrameSp += dysonFrame.spMax;
var spMax = dysonFrame.spMax / 2;
if (dysonFrame.spA < spMax)
{
rocketCount += spMax - dysonFrame.spA;
dysonFrame.spA = spMax;
dysonSphere.UpdateProgress(dysonFrame);
}
if (dysonFrame.spB < spMax)
{
rocketCount += spMax - dysonFrame.spB;
dysonFrame.spB = spMax;
dysonSphere.UpdateProgress(dysonFrame);
}
}
for (var i = dysonSphereLayer.nodeCursor - 1; i >= 0; i--)
{
var dysonNode = dysonSphereLayer.nodePool[i];
if (dysonNode == null || dysonNode.id != i) continue;
dysonNode.spOrdered = 0;
dysonNode._spReq = 0;
totalNodeSp += dysonNode.spMax;
var diff = dysonNode.spMax - dysonNode.sp;
if (diff > 0)
{
rocketCount += diff;
dysonNode.sp = dysonNode.spMax;
dysonSphere.UpdateProgress(dysonNode);
}
dysonNode._cpReq = 0;
dysonNode.cpOrdered = 0;
foreach (var shell in dysonNode.shells)
{
var nodeIndex = shell.nodeIndexMap[dysonNode.id];
var cpMax = (shell.vertsqOffset[nodeIndex + 1] - shell.vertsqOffset[nodeIndex]) * shell.cpPerVertex;
totalCp += cpMax;
diff = cpMax - shell.nodecps[nodeIndex];
shell.nodecps[nodeIndex] = cpMax;
shell.nodecps[shell.nodecps.Length - 1] += diff;
solarSailCount += diff;
if (totalCpInfo != null)
{
shell.SetMaterialDynamicVars();
}
}
}
totalNodeSpInfo?.SetValue(dysonSphereLayer, totalNodeSp);
totalFrameSpInfo?.SetValue(dysonSphereLayer, totalFrameSp);
totalCpInfo?.SetValue(dysonSphereLayer, totalCp);
}
dysonSphere.CheckAutoNodes();
var productRegister = dysonSphere.productRegister;
if (productRegister != null)
{
lock (productRegister)
{
var count = rocketCount;
while (count > 0x40000000L)
{
productRegister[11902] += 0x40000000;
count -= 0x40000000;
}
if (count > 0L) productRegister[11902] += (int)count;
count = solarSailCount;
while (count > 0x40000000L)
{
productRegister[11901] += 0x40000000;
productRegister[11903] += 0x40000000;
count -= 0x40000000;
}
if (count > 0L)
{
productRegister[11901] += (int)count;
productRegister[11903] += (int)count;
}
}
}
var consumeRegister = dysonSphere.consumeRegister;
if (consumeRegister != null)
{
lock (consumeRegister)
{
var count = solarSailCount;
while (count > 0x40000000L)
{
consumeRegister[11901] += 0x40000000;
count -= 0x40000000;
}
if (count > 0L) consumeRegister[11901] += (int)count;
}
}
});
}
}

View File

@@ -1,184 +0,0 @@
using System;
using System.Linq;
using UXAssist.Common;
namespace CheatEnabler.Functions;
public static class PlayerFunctions
{
public static void Init()
{
I18N.Add("ClearAllMetadataConsumptionDetails",
"""
Metadata consumption records of all gamesaves are about to be cleared.
You will gain following metadata back:
""",
"""
""");
I18N.Add("ClearCurrentMetadataConsumptionDetails",
"""
Metadata consumption records of current gamesave are about to be cleared.
You will gain following metadata back:
""",
"""
""");
I18N.Add("NoMetadataConsumptionRecord",
"No metadata consumption records found.",
"未找到元数据消耗记录。");
}
public static void TeleportToOuterSpace()
{
var maxSqrDistance = 0.0;
var starPosition = VectorLF3.zero;
foreach (var star in GameMain.galaxy.stars)
{
var sqrDistance = star.position.sqrMagnitude;
if (sqrDistance > maxSqrDistance)
{
maxSqrDistance = sqrDistance;
starPosition = star.position;
}
}
if (starPosition == VectorLF3.zero) return;
var distance = Math.Sqrt(maxSqrDistance);
GameMain.mainPlayer.controller.actionSail.StartFastTravelToUPosition((starPosition + starPosition.normalized * 50) * GalaxyData.LY);
}
public static void TeleportToSelectedAstronomical()
{
var starmap = UIRoot.instance?.uiGame?.starmap;
if (starmap == null) return;
if (starmap.focusPlanet != null)
{
GameMain.mainPlayer.controller.actionSail.StartFastTravelToPlanet(starmap.focusPlanet.planet);
return;
}
var targetUPos = VectorLF3.zero;
if (starmap.focusStar != null)
{
var star = starmap.focusStar.star;
targetUPos = star.uPosition + VectorLF3.unit_x * star.physicsRadius;
}
else if (starmap.focusHive != null)
{
var hive = starmap.focusHive.hive;
var id = hive.hiveAstroId - 1000000;
if (id > 0 && id < starmap.spaceSector.astros.Length)
{
ref var astro = ref starmap.spaceSector.astros[id];
targetUPos = astro.uPos + VectorLF3.unit_x * astro.uRadius;
}
}
GameMain.mainPlayer.controller.actionSail.StartFastTravelToUPosition(targetUPos);
}
private static void PurgePropertySystem(PropertySystem propertySystem)
{
var propertyDatas = propertySystem.propertyDatas;
for (var i = 0; i < propertyDatas.Count;)
{
if (propertyDatas[i].totalProduction.Any(idcnt => idcnt.count > 0) || propertyDatas[i].totalConsumption.Any(idcnt => idcnt.count > 0))
{
i++;
}
else
{
propertyDatas.RemoveAt(i);
}
}
}
public static void RemoveAllMetadataConsumptions()
{
var propertySystem = DSPGame.propertySystem;
if (propertySystem == null) return;
PurgePropertySystem(propertySystem);
var itemCnt = new int[6];
foreach (var cons in propertySystem.propertyDatas.SelectMany(data => data.totalConsumption.Where(cons => cons.id is >= 6001 and <= 6006)))
{
itemCnt[cons.id - 6001] += cons.count;
}
if (itemCnt.All(cnt => cnt == 0))
{
UIMessageBox.Show("Remove all metadata consumption records".Translate(), "NoMetadataConsumptionRecord".Translate(), "OK".Translate(), 0);
return;
}
var msg = "ClearAllMetadataConsumptionDetails".Translate();
for (var i = 0; i < 6; i++)
{
if (itemCnt[i] > 0)
{
msg += $"\n {LDB.items.Select(i + 6001).propertyName} x{itemCnt[i]}";
}
}
UIMessageBox.Show("Remove all metadata consumption records".Translate(), msg, "取消".Translate(), "确定".Translate(), 2, null, () =>
{
foreach (var data in propertySystem.propertyDatas)
{
for (var i = 0; i < data.totalConsumption.Count; i++)
{
if (data.totalConsumption[i].count == 0) continue;
var id = data.totalConsumption[i].id;
data.totalConsumption[i] = new IDCNT(id, 0);
}
}
PurgePropertySystem(propertySystem);
propertySystem.SaveToFile();
});
}
public static void RemoveCurrentMetadataConsumptions()
{
var propertySystem = DSPGame.propertySystem;
if (propertySystem == null) return;
PurgePropertySystem(propertySystem);
var itemCnt = new int[6];
var seedKey = DSPGame.GameDesc.seedKey64;
var clusterPropertyData = propertySystem.propertyDatas.FirstOrDefault(cpd => cpd.seedKey == seedKey);
if (clusterPropertyData == null)
{
UIMessageBox.Show("Remove metadata consumption record in current game".Translate(), "NoMetadataConsumptionRecord".Translate(), "OK".Translate(), 0);
return;
}
foreach (var cons in clusterPropertyData.totalConsumption.Where(cons => cons.id is >= 6001 and <= 6006))
{
itemCnt[cons.id - 6001] += cons.count;
}
if (itemCnt.All(cnt => cnt == 0))
{
UIMessageBox.Show("Remove metadata consumption record in current game".Translate(), "NoMetadataConsumptionRecord".Translate(), "OK".Translate(), 0);
return;
}
var msg = "ClearCurrentMetadataConsumptionDetails".Translate();
for (var i = 0; i < 6; i++)
{
if (itemCnt[i] > 0)
{
msg += $"\n {LDB.items.Select(i + 6001).propertyName} x{itemCnt[i]}";
}
}
UIMessageBox.Show("Remove metadata consumption record in current game".Translate(), msg, "取消".Translate(), "确定".Translate(), 2, null, () =>
{
for (var i = 0; i < clusterPropertyData.totalConsumption.Count; i++)
{
if (clusterPropertyData.totalConsumption[i].count == 0) continue;
var id = clusterPropertyData.totalConsumption[i].id;
clusterPropertyData.totalConsumption[i] = new IDCNT(id, 0);
}
PurgePropertySystem(propertySystem);
propertySystem.SaveToFile();
});
}
public static void ClearMetadataBanAchievements()
{
GameMain.history.hasUsedPropertyBanAchievement = false;
}
}

View File

@@ -1,88 +0,0 @@
using System.Collections.Generic;
using System.Reflection;
using System.Reflection.Emit;
using BepInEx.Configuration;
using HarmonyLib;
using UXAssist.Common;
namespace CheatEnabler.Patches;
public static class CombatPatch
{
public static ConfigEntry<bool> MechaInvincibleEnabled;
public static ConfigEntry<bool> BuildingsInvincibleEnabled;
public static void Init()
{
MechaInvincibleEnabled.SettingChanged += (_, _) => MechaInvincible.Enable(MechaInvincibleEnabled.Value);
BuildingsInvincibleEnabled.SettingChanged += (_, _) => BuildingsInvincible.Enable(BuildingsInvincibleEnabled.Value);
}
public static void Start()
{
MechaInvincible.Enable(MechaInvincibleEnabled.Value);
BuildingsInvincible.Enable(BuildingsInvincibleEnabled.Value);
}
public static void Uninit()
{
BuildingsInvincible.Enable(false);
MechaInvincible.Enable(false);
}
private class MechaInvincible: PatchImpl<MechaInvincible>
{
[HarmonyTranspiler]
[HarmonyPatch(typeof(Player), nameof(Player.invincible), MethodType.Getter)]
private static IEnumerable<CodeInstruction> Player_get_invincible_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{
var matcher = new CodeMatcher(instructions, generator);
matcher.Start().RemoveInstructions(matcher.Length).Insert(
new CodeInstruction(OpCodes.Ldc_I4_1),
new CodeInstruction(OpCodes.Ret)
);
return matcher.InstructionEnumeration();
}
[HarmonyTranspiler]
[HarmonyPatch(typeof(SkillSystem), nameof(SkillSystem.DamageGroundObjectByLocalCaster))]
[HarmonyPatch(typeof(SkillSystem), nameof(SkillSystem.DamageGroundObjectByRemoteCaster))]
[HarmonyPatch(typeof(SkillSystem), nameof(SkillSystem.DamageObject))]
private static IEnumerable<CodeInstruction> SkillSystem_DamageObject_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator, MethodBase __originalMethod)
{
var matcher = new CodeMatcher(instructions, generator);
matcher.MatchForward(false,
new CodeMatch(ci => ci.IsLdarg()),
new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(SkillTargetLocal), nameof(SkillTargetLocal.type))),
new CodeMatch(OpCodes.Ldc_I4_6),
new CodeMatch(ci => ci.opcode == OpCodes.Bne_Un || ci.opcode == OpCodes.Bne_Un_S)
);
matcher.Repeat(m => m.Advance(4).InsertAndAdvance(
new CodeInstruction(OpCodes.Ldc_I4_0),
new CodeInstruction(OpCodes.Starg_S, __originalMethod.Name == "DamageObject" ? 1 : 2)
));
return matcher.InstructionEnumeration();
}
}
private class BuildingsInvincible: PatchImpl<BuildingsInvincible>
{
[HarmonyTranspiler]
[HarmonyPatch(typeof(SkillSystem), nameof(SkillSystem.DamageGroundObjectByLocalCaster))]
[HarmonyPatch(typeof(SkillSystem), nameof(SkillSystem.DamageGroundObjectByRemoteCaster))]
private static IEnumerable<CodeInstruction> SkillSystem_DamageObject_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{
var matcher = new CodeMatcher(instructions, generator);
matcher.MatchForward(false,
new CodeMatch(ci => ci.IsLdarg()),
new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(SkillTargetLocal), nameof(SkillTargetLocal.type))),
new CodeMatch(ci => ci.opcode == OpCodes.Brtrue || ci.opcode == OpCodes.Brtrue_S),
new CodeMatch(OpCodes.Ldarg_1)
).Advance(3).Insert(
new CodeInstruction(OpCodes.Ldc_I4_0),
new CodeInstruction(OpCodes.Starg_S, 2)
);
return matcher.InstructionEnumeration();
}
}
}

View File

@@ -1,310 +0,0 @@
using System;
using System.Collections.Generic;
using System.Reflection.Emit;
using BepInEx.Configuration;
using HarmonyLib;
using UnityEngine.Bindings;
using UXAssist.Common;
namespace CheatEnabler.Patches;
public static class GamePatch
{
public static ConfigEntry<bool> DevShortcutsEnabled;
public static ConfigEntry<bool> AbnormalDisablerEnabled;
public static ConfigEntry<bool> UnlockTechEnabled;
public static void Init()
{
DevShortcutsEnabled.SettingChanged += (_, _) => DevShortcuts.Enable(DevShortcutsEnabled.Value);
AbnormalDisablerEnabled.SettingChanged += (_, _) => AbnormalDisabler.Enable(AbnormalDisablerEnabled.Value);
UnlockTechEnabled.SettingChanged += (_, _) => UnlockTech.Enable(UnlockTechEnabled.Value);
}
public static void Start()
{
DevShortcuts.Enable(DevShortcutsEnabled.Value);
AbnormalDisabler.Enable(AbnormalDisablerEnabled.Value);
UnlockTech.Enable(UnlockTechEnabled.Value);
}
public static void Uninit()
{
UnlockTech.Enable(false);
AbnormalDisabler.Enable(false);
DevShortcuts.Enable(false);
}
public class AbnormalDisabler : PatchImpl<AbnormalDisabler>
{
private static Dictionary<int, AbnormalityDeterminator> _savedDeterminators;
protected override void OnEnable()
{
if (_savedDeterminators == null) return;
var abnormalLogic = GameMain.gameScenario.abnormalityLogic;
foreach (var p in _savedDeterminators)
{
p.Value.OnUnregEvent();
}
abnormalLogic.determinators = new Dictionary<int, AbnormalityDeterminator>();
}
protected override void OnDisable()
{
if (_savedDeterminators == null) return;
var abnormalLogic = GameMain.gameScenario.abnormalityLogic;
abnormalLogic.determinators = _savedDeterminators;
foreach (var p in _savedDeterminators)
{
p.Value.OnRegEvent();
}
}
[HarmonyPrefix]
[HarmonyPatch(typeof(AbnormalityLogic), "NotifyBeforeGameSave")]
[HarmonyPatch(typeof(AbnormalityLogic), "NotifyOnAssemblerRecipePick")]
[HarmonyPatch(typeof(AbnormalityLogic), "NotifyOnGameBegin")]
[HarmonyPatch(typeof(AbnormalityLogic), "NotifyOnMechaForgeTaskComplete")]
[HarmonyPatch(typeof(AbnormalityLogic), "NotifyOnUnlockTech")]
[HarmonyPatch(typeof(AbnormalityLogic), "NotifyOnUseConsole")]
private static bool DisableAbnormalLogic()
{
return false;
}
[HarmonyPostfix]
[HarmonyPatch(typeof(AbnormalityLogic), "InitDeterminators")]
private static void DisableAbnormalDeterminators(AbnormalityLogic __instance)
{
_savedDeterminators = __instance.determinators;
if (!AbnormalDisablerEnabled.Value) return;
__instance.determinators = new Dictionary<int, AbnormalityDeterminator>();
foreach (var p in _savedDeterminators)
{
p.Value.OnUnregEvent();
}
}
}
public class DevShortcuts : PatchImpl<DevShortcuts>
{
private static PlayerAction_Test _test;
protected override void OnEnable()
{
if (_test != null) _test.active = true;
}
protected override void OnDisable()
{
if (_test != null) _test.active = false;
}
[HarmonyPostfix]
[HarmonyPatch(typeof(PlayerController), nameof(PlayerController.Init))]
private static void PlayerController_Init_Postfix(PlayerController __instance)
{
var cnt = __instance.actions.Length;
var newActions = new PlayerAction[cnt + 1];
for (var i = 0; i < cnt; i++)
{
newActions[i] = __instance.actions[i];
}
_test = new PlayerAction_Test();
_test.Init(__instance.player);
_test.active = DevShortcutsEnabled.Value;
newActions[cnt] = _test;
__instance.actions = newActions;
}
[HarmonyPostfix]
[HarmonyPatch(typeof(PlayerAction_Test), nameof(PlayerAction_Test.GameTick))]
private static void PlayerAction_Test_GameTick_Postfix(PlayerAction_Test __instance)
{
__instance.Update();
}
[HarmonyTranspiler]
[HarmonyPatch(typeof(PlayerAction_Test), nameof(PlayerAction_Test.Update))]
private static IEnumerable<CodeInstruction> PlayerAction_Test_Update_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{
var matcher = new CodeMatcher(instructions, generator);
matcher.End().MatchBack(false,
new CodeMatch(OpCodes.Ldarg_0),
new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(PlayerAction_Test), nameof(PlayerAction_Test.active)))
);
var pos = matcher.Pos;
/* Remove Shift+F4 part of the method */
matcher.Start().RemoveInstructions(pos).MatchForward(false,
new CodeMatch(OpCodes.Call, AccessTools.Method(typeof(GameMain), "get_sandboxToolsEnabled")),
new CodeMatch(OpCodes.Ldc_I4_0),
new CodeMatch(OpCodes.Ceq)
);
var labels = matcher.Labels;
matcher.SetInstructionAndAdvance(
new CodeInstruction(OpCodes.Ldc_I4_1).WithLabels(labels)
).RemoveInstructions(2);
/* Remove Ctrl+A */
matcher.Start().MatchForward(false,
new CodeMatch(instr => (instr.opcode == OpCodes.Ldc_I4_S || instr.opcode == OpCodes.Ldc_I4) && instr.OperandIs(0x61)),
new CodeMatch(OpCodes.Call, AccessTools.Method(typeof(UnityEngine.Input), nameof(UnityEngine.Input.GetKeyDown), [typeof(UnityEngine.KeyCode)]))
);
labels = matcher.Labels;
matcher.Labels = null;
matcher.RemoveInstructions(2);
matcher.Opcode = OpCodes.Br;
matcher.Labels = labels;
return matcher.InstructionEnumeration();
}
[HarmonyTranspiler]
[HarmonyPatch(typeof(GameCamera), nameof(GameCamera.FrameLogic))]
private static IEnumerable<CodeInstruction> GameCamera_Logic_Transpiler(IEnumerable<CodeInstruction> instructions)
{
var matcher = new CodeMatcher(instructions);
matcher.MatchForward(false,
new CodeMatch(OpCodes.Ldarg_0),
new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(GameCamera), nameof(GameCamera.finalPoser))),
new CodeMatch(OpCodes.Callvirt, AccessTools.PropertyGetter(typeof(CameraPoser), nameof(CameraPoser.cameraPose)))
);
var labels = matcher.Labels;
matcher.Labels = null;
matcher.Insert(
new CodeInstruction(OpCodes.Ldarg_0).WithLabels(labels),
Transpilers.EmitDelegate((GameCamera camera) =>
{
if (PlayerAction_Test.lockCam)
{
camera.finalPoser.cameraPose = PlayerAction_Test.camPose;
}
})
);
return matcher.InstructionEnumeration();
}
}
public class UnlockTech: PatchImpl<UnlockTech>
{
private static void UnlockTechRecursive(GameHistoryData history, [NotNull] TechProto techProto, int maxLevel = 10000)
{
var techStates = history.techStates;
var techID = techProto.ID;
if (techStates == null || !techStates.TryGetValue(techID, out var value))
{
return;
}
if (value.unlocked)
{
return;
}
var maxLvl = Math.Min(maxLevel < 0 ? value.curLevel - maxLevel - 1 : maxLevel, value.maxLevel);
foreach (var preid in techProto.PreTechs)
{
var preProto = LDB.techs.Select(preid);
if (preProto != null)
UnlockTechRecursive(history, preProto, techProto.PreTechsMax ? 10000 : -1);
}
foreach (var preid in techProto.PreTechsImplicit)
{
var preProto = LDB.techs.Select(preid);
if (preProto != null)
UnlockTechRecursive(history, preProto, techProto.PreTechsMax ? 10000 : -1);
}
if (value.curLevel < techProto.Level) value.curLevel = techProto.Level;
while (value.curLevel <= maxLvl)
{
if (value.curLevel == 0)
{
foreach (var recipe in techProto.UnlockRecipes)
{
history.UnlockRecipe(recipe);
}
}
for (var j = 0; j < techProto.UnlockFunctions.Length; j++)
{
history.UnlockTechFunction(techProto.UnlockFunctions[j], techProto.UnlockValues[j], value.curLevel);
}
for (var k = 0; k < techProto.AddItems.Length; k++)
{
history.GainTechAwards(techProto.AddItems[k], techProto.AddItemCounts[k]);
}
value.curLevel++;
}
value.unlocked = maxLvl >= value.maxLevel;
value.curLevel = value.unlocked ? maxLvl : maxLvl + 1;
value.hashNeeded = techProto.GetHashNeeded(value.curLevel);
value.hashUploaded = value.unlocked ? value.hashNeeded : 0;
techStates[techID] = value;
history.RegFeatureKey(1000100);
history.NotifyTechUnlock(techID, maxLvl, true);
}
private static void OnClickTech(UITechNode node)
{
var history = GameMain.history;
if (VFInput.shift)
{
if (VFInput.alt) return;
if (VFInput.control)
UnlockTechRecursive(history, node.techProto, -100);
else
UnlockTechRecursive(history, node.techProto, -1);
}
else
{
if (VFInput.control)
{
if (!VFInput.alt)
UnlockTechRecursive(history, node.techProto, -10);
else
return;
}
else if (VFInput.alt)
{
UnlockTechRecursive(history, node.techProto);
}
else
{
return;
}
}
history.VarifyTechQueue();
if (history.currentTech != history.techQueue[0])
{
history.currentTech = history.techQueue[0];
}
}
[HarmonyTranspiler]
[HarmonyPatch(typeof(UITechNode), nameof(UITechNode.OnPointerDown))]
private static IEnumerable<CodeInstruction> UITechNode_OnPointerDown_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{
var matcher = new CodeMatcher(instructions, generator);
matcher.MatchForward(false,
new CodeMatch(OpCodes.Ldarg_0),
new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(UITechNode), nameof(UITechNode.tree))),
new CodeMatch(OpCodes.Callvirt, AccessTools.Method(typeof(UITechTree), "get_selected"))
);
var labels = matcher.Labels;
matcher.Labels = null;
matcher.Insert(
new CodeInstruction(OpCodes.Ldarg_0).WithLabels(labels),
new CodeInstruction(OpCodes.Call, AccessTools.Method(typeof(UnlockTech), nameof(UnlockTech.OnClickTech)))
);
return matcher.InstructionEnumeration();
}
}
}

View File

@@ -1,86 +0,0 @@
using System.Collections.Generic;
using System.Reflection.Emit;
using BepInEx.Configuration;
using HarmonyLib;
using UXAssist.Common;
namespace CheatEnabler.Patches;
public static class PlayerPatch
{
public static ConfigEntry<bool> InstantHandCraftEnabled;
public static ConfigEntry<bool> InstantTeleportEnabled;
public static ConfigEntry<bool> WarpWithoutSpaceWarpersEnabled;
public static void Init()
{
InstantHandCraftEnabled.SettingChanged += (_, _) => InstantHandCraft.Enable(InstantHandCraftEnabled.Value);
InstantTeleportEnabled.SettingChanged += (_, _) => InstantTeleport.Enable(InstantTeleportEnabled.Value);
WarpWithoutSpaceWarpersEnabled.SettingChanged += (_, _) => WarpWithoutSpaceWarpers.Enable(WarpWithoutSpaceWarpersEnabled.Value);
}
public static void Start()
{
InstantHandCraft.Enable(InstantHandCraftEnabled.Value);
InstantTeleport.Enable(InstantTeleportEnabled.Value);
WarpWithoutSpaceWarpers.Enable(WarpWithoutSpaceWarpersEnabled.Value);
}
public static void Uninit()
{
InstantHandCraft.Enable(false);
InstantTeleport.Enable(false);
WarpWithoutSpaceWarpers.Enable(false);
}
private class InstantHandCraft: PatchImpl<InstantHandCraft>
{
[HarmonyPostfix]
[HarmonyPatch(typeof(ForgeTask), MethodType.Constructor, typeof(int), typeof(int))]
private static void ForgeTask_Ctor_Postfix(ForgeTask __instance)
{
__instance.tickSpend = 0;
}
}
private class InstantTeleport: PatchImpl<InstantTeleport>
{
[HarmonyTranspiler]
[HarmonyPatch(typeof(UIGlobemap), nameof(UIGlobemap._OnUpdate))]
[HarmonyPatch(typeof(UIStarmap), nameof(UIStarmap.DoRightClickFastTravel))]
[HarmonyPatch(typeof(UIStarmap), nameof(UIStarmap.OnFastTravelButtonClick))]
[HarmonyPatch(typeof(UIStarmap), nameof(UIStarmap.OnScreenClick))]
[HarmonyPatch(typeof(UIStarmap), nameof(UIStarmap.SandboxRightClickFastTravelLogic))]
[HarmonyPatch(typeof(UIStarmap), nameof(UIStarmap.StartFastTravelToPlanet))]
[HarmonyPatch(typeof(UIStarmap), nameof(UIStarmap.StartFastTravelToUPosition))]
[HarmonyPatch(typeof(UIStarmap), nameof(UIStarmap.UpdateCursorView))]
[HarmonyPatch(typeof(UIStarmap), nameof(UIStarmap._OnUpdate))]
private static IEnumerable<CodeInstruction> UIGlobemap__OnUpdate_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{
var matcher = new CodeMatcher(instructions, generator);
matcher.MatchForward(false,
new CodeMatch(OpCodes.Call, AccessTools.PropertyGetter(typeof(GameMain), nameof(GameMain.sandboxToolsEnabled)))
);
matcher.Repeat(cm => cm.SetAndAdvance(OpCodes.Ldc_I4_1, null));
return matcher.InstructionEnumeration();
}
}
private class WarpWithoutSpaceWarpers: PatchImpl<WarpWithoutSpaceWarpers>
{
[HarmonyPrefix]
[HarmonyPatch(typeof(Mecha), nameof(Mecha.HasWarper))]
private static bool Mecha_HasWarper_Prefix(ref bool __result)
{
__result = true;
return false;
}
[HarmonyPostfix]
[HarmonyPatch(typeof(Mecha), nameof(Mecha.UseWarper))]
private static void Mecha_UseWarper_Postfix(ref bool __result)
{
__result = true;
}
}
}

View File

@@ -1,6 +1,6 @@
using Random = UnityEngine.Random; using Random = UnityEngine.Random;
namespace CheatEnabler.Functions; namespace CheatEnabler;
public static class PlanetFunctions public static class PlanetFunctions
{ {
public static void BuryAllVeins(bool bury) public static void BuryAllVeins(bool bury)

View File

@@ -3,9 +3,8 @@ using System.Collections.Generic;
using System.Reflection.Emit; using System.Reflection.Emit;
using BepInEx.Configuration; using BepInEx.Configuration;
using HarmonyLib; using HarmonyLib;
using UXAssist.Common;
namespace CheatEnabler.Patches; namespace CheatEnabler;
public static class PlanetPatch public static class PlanetPatch
{ {
public static ConfigEntry<bool> WaterPumpAnywhereEnabled; public static ConfigEntry<bool> WaterPumpAnywhereEnabled;
@@ -15,10 +14,6 @@ public static class PlanetPatch
{ {
WaterPumpAnywhereEnabled.SettingChanged += (_, _) => WaterPumperPatch.Enable(WaterPumpAnywhereEnabled.Value); WaterPumpAnywhereEnabled.SettingChanged += (_, _) => WaterPumperPatch.Enable(WaterPumpAnywhereEnabled.Value);
TerraformAnywayEnabled.SettingChanged += (_, _) => TerraformAnyway.Enable(TerraformAnywayEnabled.Value); TerraformAnywayEnabled.SettingChanged += (_, _) => TerraformAnyway.Enable(TerraformAnywayEnabled.Value);
}
public static void Start()
{
WaterPumperPatch.Enable(WaterPumpAnywhereEnabled.Value); WaterPumperPatch.Enable(WaterPumpAnywhereEnabled.Value);
TerraformAnyway.Enable(TerraformAnywayEnabled.Value); TerraformAnyway.Enable(TerraformAnywayEnabled.Value);
} }
@@ -29,19 +24,34 @@ public static class PlanetPatch
TerraformAnyway.Enable(false); TerraformAnyway.Enable(false);
} }
private class WaterPumperPatch: PatchImpl<WaterPumperPatch> private static class WaterPumperPatch
{ {
private static Harmony _patch;
public static void Enable(bool on)
{
if (on)
{
_patch ??= Harmony.CreateAndPatchAll(typeof(WaterPumperPatch));
}
else
{
_patch?.UnpatchSelf();
_patch = null;
}
}
[HarmonyTranspiler] [HarmonyTranspiler]
[HarmonyPatch(typeof(BuildTool_BlueprintPaste), nameof(BuildTool_BlueprintPaste.CheckBuildConditions))] [HarmonyPatch(typeof(BuildTool_BlueprintPaste), "CheckBuildConditions")]
[HarmonyPatch(typeof(BuildTool_Click), nameof(BuildTool_Click.CheckBuildConditions))] [HarmonyPatch(typeof(BuildTool_Click), "CheckBuildConditions")]
private static IEnumerable<CodeInstruction> BuildTool_CheckBuildConditions_Transpiler( private static IEnumerable<CodeInstruction> BuildTool_CheckBuildConditions_Transpiler(
IEnumerable<CodeInstruction> instructions, ILGenerator generator) IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{ {
var matcher = new CodeMatcher(instructions, generator); var matcher = new CodeMatcher(instructions, generator);
matcher.MatchForward(false, matcher.MatchForward(false,
new CodeMatch(instr => instr.opcode == OpCodes.Ldc_I4_S && instr.OperandIs((int)EBuildCondition.NeedWater)) new CodeMatch(instr => instr.opcode == OpCodes.Ldc_I4_S && instr.OperandIs(22))
).Advance(1).MatchForward(false, ).Advance(1).MatchForward(false,
new CodeMatch(instr => instr.opcode == OpCodes.Ldc_I4_S && instr.OperandIs((int)EBuildCondition.NeedWater)) new CodeMatch(instr => instr.opcode == OpCodes.Ldc_I4_S && instr.OperandIs(22))
); );
matcher.Repeat(codeMatcher => matcher.Repeat(codeMatcher =>
{ {
@@ -50,29 +60,34 @@ public static class PlanetPatch
return matcher.InstructionEnumeration(); return matcher.InstructionEnumeration();
} }
} }
private static class TerraformAnyway
private class TerraformAnyway: PatchImpl<TerraformAnyway>
{ {
private static Harmony _patch;
public static void Enable(bool on)
{
if (on)
{
_patch ??= Harmony.CreateAndPatchAll(typeof(TerraformAnyway));
}
else
{
_patch?.UnpatchSelf();
_patch = null;
}
}
[HarmonyTranspiler] [HarmonyTranspiler]
[HarmonyPatch(typeof(BuildTool_Reform), nameof(BuildTool_Reform.ReformAction))] [HarmonyPatch(typeof(BuildTool_Reform), nameof(BuildTool_Reform.ReformAction))]
private static IEnumerable<CodeInstruction> BuildTool_Reform_ReformAction_Patch(IEnumerable<CodeInstruction> instructions, ILGenerator generator) private static IEnumerable<CodeInstruction> BuildTool_Reform_ReformAction_Patch(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{ {
var matcher = new CodeMatcher(instructions, generator); var matcher = new CodeMatcher(instructions, generator);
matcher.MatchForward(false, matcher.MatchForward(false,
new CodeMatch(OpCodes.Ldarg_0),
new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(BuildTool_Reform), nameof(BuildTool_Reform.cursorPointCount))),
new CodeMatch(ci => ci.opcode == OpCodes.Blt || ci.opcode == OpCodes.Blt_S)
).RemoveInstructions(2).InsertAndAdvance(
new CodeInstruction(OpCodes.Ldc_I4_0)
).MatchForward(false,
new CodeMatch(OpCodes.Callvirt, AccessTools.Method(typeof(Player), "get_sandCount")) new CodeMatch(OpCodes.Callvirt, AccessTools.Method(typeof(Player), "get_sandCount"))
).Advance(1).MatchForward(false, ).Advance(4).InsertAndAdvance(
new CodeMatch(OpCodes.Conv_I8),
new CodeMatch(OpCodes.Sub)
).Advance(2).InsertAndAdvance(
new CodeInstruction(OpCodes.Ldc_I8, 0L), new CodeInstruction(OpCodes.Ldc_I8, 0L),
new CodeInstruction(OpCodes.Call, AccessTools.Method(typeof(Math), "Max", [typeof(long), typeof(long)])) new CodeInstruction(OpCodes.Call, AccessTools.Method(typeof(Math), "Max", new[] { typeof(long), typeof(long) }))
); ).Advance(1).RemoveInstructions(4);
return matcher.InstructionEnumeration(); return matcher.InstructionEnumeration();
} }
} }

View File

@@ -3,6 +3,81 @@
#### Add various cheat functions while disabling abnormal determinants #### Add various cheat functions while disabling abnormal determinants
#### 添加一些作弊功能,同时屏蔽异常检测 #### 添加一些作弊功能,同时屏蔽异常检测
## Changlog
* 2.3.9
+ Support game version 0.10.28.21219
* 2.3.8
+ Fix a crash on starting new games while `Finish build immediately` is enabled.
+ Fix UI button width.
* 2.3.7
+ Support game version 0.10.28.20759
+ Fix belt signal that items' generation speed is not fit to number set sometimes.
* 2.3.6
+ Support for UXAssist's new function within `Finish build immediately`.
+ Add a warning message when `Build without condition` is enabled.
+ Fix a issue in `Finish build immediately` that some buildings are not finished immediately.
* 2.3.5
+ Fix another crash in `Skip bullet period`.
* 2.3.4
+ Use new tab layout of UXAssist 1.0.2
+ Minor bug fixes
* 2.3.3
+ Fix a crash in `Skip bullet period`.
+ Unlock techs with Alt unlocks VeinUtil to 10000 instead of 7200 now, as bug fixed in UXAssist.
* 2.3.2
+ Birth star options moved to [UniverseGenTweaks](https://dsp.thunderstore.io/package/soarqin/UniverseGenTweaks/)
+ Optimize `Quick absorb`, consumes less CPU time and take turns firing to nodes.
+ `Fast Mining` ensures full output of oil extractors now.
+ Fix issue that `Belt signal generator` not working after switched off then on again.
+ Fix absorption issue by `Quick absorb` and `Skip bullet period` enabled at the same time.
+ Crash fix for some options
* 2.3.1
+ Add UXAssist to dependencies in manifest.
* 2.3.0
+ Move some functions to an individual mod: [UXAssist](https://dsp.thunderstore.io/package/soarqin/UXAssist)
+ Depends on [UXAssist](https://dsp.thunderstore.io/package/soarqin/UXAssist) now, so that config panel is unified with UXAssist.
+ Remove `LCtrl+A` from Dev Shortcuts, to avoid misoperation.
+ Infinite bots/drones/vessels in `Architect mode` now.
* 2.2.7
+ New function: `Construct only nodes but frames`
+ Opening config panel does not close inventory panel now
+ Remove `Input direction conflict` check while using `Remove some build conditions`
+ Fix a bug that prevents `Belt signal alt format` from switching number formats for current belt signals
* 2.2.6
+ New function: `Stop ejectors when available nodes are all filled up`
+ Fix a bug that absorb solar sails on unfinised nodes
* 2.2.5
+ Skip all intermediate states and absorb solar sails instantly while enable `Quick absorb`, `Skip bullet period` and `Skip absorption period` at the same time.
+ Fix a problem that `Quick absorb` does not absorb all solar sails instantly when most nodes are full.
+ Fix crash while using with some mods
* 2.2.4
+ New function: `Enable player actions in globe view`
+ Fix UI bug
* 2.2.3
+ New function: `Remove some build conditions`
+ Fix compatibility with some mods
* 2.2.2
+ New function: `Assign game to currrnet account`
+ New subfunction: `Belt signal alt format`
+ Fix a crash on using `Initialize this Planet`
+ Fix belt build in `Finish build immediately`
* 2.2.1
+ Check condition for miners even when `Build without condition` is enabled.
+ Fix a patch issue that may cause `Build without condition` not working.
* 2.2.0
+ Add some power related functions
+ Add a subfunction to belt signal item generation, which simulates production process of raws and intermediates on statistics
+ Split some functions from Architect mode
* 2.1.0
+ Belt signal item generation
+ Fix window display priority which may cause tips to be covered by main window
* 2.0.0
+ Refactorying codes
+ UI implementation
+ Add a lot of functions
* 1.0.0
+ Initial release
## Usage ## Usage
* Config panel is unified with UXAssist. * Config panel is unified with UXAssist.
@@ -12,46 +87,27 @@
+ Enable Dev Shortcuts (check config panel for usage) + Enable Dev Shortcuts (check config panel for usage)
+ Disable Abnormal Checks + Disable Abnormal Checks
+ Unlock techs with key-modifiers (Ctrl/Alt/Shift) + Unlock techs with key-modifiers (Ctrl/Alt/Shift)
+ Remove all metadata consumption records + Assign game to currrnet account
+ Remove metadata consumption record in current game
+ Clear metadata flag which bans achievements
+ Assign gamesave to currrnet account
+ Factory: + Factory:
+ Finish build immediately + Finish build immediately
+ Architect mode (Infinite buildings) + Architect mode (Infinite buildings)
+ Build without condition + Build without condition
+ No collision + No collision
+ Belt signal item generation + Belt signal item generation
- Count generations as production in statistics
- Count removals as consumption in statistics
- Count all raws and intermediates in statistics - Count all raws and intermediates in statistics
- Belt signal alt format - Belt signal alt format
+ Increase maximum power usage in Logistic Stations and Advanced Mining Machines
- Logistic Stations: Increased max charging power to 3GW(ILS) and 600MW(PLS) (10x of original)
- Advanced Mining Machines: Increased max mining speed to 1000%
+ Retrieve/Place items from/to remote planets on logistics control panel
+ Remove space limit between wind turbines and solar panels + Remove space limit between wind turbines and solar panels
+ Wind Turbines do global power coverage
+ Boost power generations for kinds of power generators + Boost power generations for kinds of power generators
+ Planet: + Planet:
+ Instant hand-craft
+ Infinite Natural Resources + Infinite Natural Resources
+ Fast Mining + Fast Mining
+ Pump Anywhere + Pump Anywhere
+ Terraform without enought soil piles + Terraform without enought sands
+ Instant teleport (like that in Sandbox mode)
+ Dyson Sphere: + Dyson Sphere:
+ Skip bullet period + Skip bullet period
+ Skip absorption period + Skip absorption period
+ Quick absorb + Quick absorb
+ Eject anyway + Eject anyway
+ Complete Dyson Sphere Shells instantly
+ Mecha/Combat:
+ Mecha and Drones/Fleets invicible
+ Buildings invicible
+ Enable warp without space warpers
+ Teleport to outer space
+ Teleport to selected astronomical
## Notes ## Notes
* Please upgrade `BepInEx` 5.4.21 or later if using with [BlueprintTweaks](https://dsp.thunderstore.io/package/kremnev8/BlueprintTweaks/) to avoid possible conflicts. * Please upgrade `BepInEx` 5.4.21 or later if using with [BlueprintTweaks](https://dsp.thunderstore.io/package/kremnev8/BlueprintTweaks/) to avoid possible conflicts.
@@ -63,6 +119,80 @@
* [BepInEx](https://bepinex.dev/): Base modding framework * [BepInEx](https://bepinex.dev/): Base modding framework
* [Multifunction_mod](https://github.com/blacksnipebiu/Multifunction_mod): Some cheat functions * [Multifunction_mod](https://github.com/blacksnipebiu/Multifunction_mod): Some cheat functions
## 更新日志
* 2.3.9
+ 支持游戏版本0.10.28.21219
* 2.3.8
+ 修复了启用`建造秒完成`时开新游戏可能导致崩溃的问题
+ 修复了UI按钮宽度
* 2.3.7
+ 支持游戏版本0.10.28.20759
+ 修复了传送带信号有时候物品生成速度和设置不匹配的问题
* 2.3.6
+ 在`建造秒完成`中支持UXAssist的新功能
+ 在启用`无条件建造`时添加警告信息
+ 修复了`建造秒完成`可能导致部分建筑无法立即完成的问题
* 2.3.5
+ 修复了`跳过子弹阶段`可能导致崩溃的问题
* 2.3.4
+ 使用UXAssist 1.0.2的新页签布局
+ 修复了一些小bug
* 2.3.3
+ 修复了`跳过子弹阶段`可能导致崩溃的问题
+ 使用Alt解锁科技时现在`矿物利用`的科技解锁到10000级而不是7200级因为UXAssist已修复对应bug
* 2.3.2
+ 母星系的选项移动到了[UniverseGenTweaks](https://dsp.thunderstore.io/package/soarqin/UniverseGenTweaks/)
+ 优化了`快速吸收`现在消耗更少的CPU并且会轮流打向各节点
+ `高速采集`现在可以保证油井的最大产出
+ 修复了`传送带信号物品生成`在选项关闭后再次启用时不生效的问题
+ 修复了`快速吸收``跳过子弹阶段`同时启用时可能导致吸收计算错误的问题
+ 修复了一些选项可能导致崩溃的问题
* 2.3.1
+ 在manifest中添加UXAssist到依赖
* 2.3.0
+ 将部分功能移动到单独的mod[UXAssist](https://dsp.thunderstore.io/package/soarqin/UXAssist)
+ 现在依赖[UXAssist](https://dsp.thunderstore.io/package/soarqin/UXAssist)因此配置面板与UXAssist合并
+ 从开发模式快捷键中移除`LCtrl+A`,以避免误操作
+ 现在`建筑师模式`中配送机/物流机/物流船也无限了
* 2.2.7
+ 新功能:`只建造节点不建造框架`
+ 打开设置面板时不再关闭背包面板
+ 在`移除部分不影响游戏逻辑的建造条件`启用时移除`输入方向冲突`的检查条件
+ 修复导致`传送带信号替换格式`不切换传送带信号数字格式的问题
* 2.2.6
+ 新功能:`可用节点全部造完时停止弹射`
+ 修复了在未完成的节点上吸收太阳帆的问题
* 2.2.5
+ 在同时启用`快速吸收``跳过子弹阶段``跳过吸收阶段`时,所有弹射的太阳帆会跳过所有中间环节立即吸收
+ 修复了`快速吸收`在大部分节点已满时无法立即吸收所有太阳帆的问题
+ 修复了与一些mod的兼容性问题
* 2.2.4
+ 新功能:`在行星视图中允许玩家操作`
+ 修复了UI显示问题
* 2.2.3
+ 新功能:`移除部分不影响游戏逻辑的建造条件`
+ 修复了与一些mod的兼容性问题
* 2.2.2
+ 新功能:`将游戏绑定给当前账号`
+ 新子功能:`传送带信号替换格式`
+ 修复了`初始化本行星`可能导致崩溃的问题
+ 修复了`建造秒完成`中传送带建造的问题
* 2.2.1
+ 即使在启用`无条件建造`时依然检查矿机的建造条件
+ 修复一个可能导致`无条件建造`不生效的问题
* 2.2.0
+ 添加了一些发电相关功能
+ 为传送带信号物品生成添加了一个子功能,在统计面板模拟了原材料和中间产物的生产过程
+ 从建筑师模式中分离了一些功能
* 2.1.0
+ 传送带信号物品生成
+ 修复窗口显示优先级可能导致提示信息被主窗口遮挡的问题
* 2.0.0
+ 重构代码
+ UI实现
+ 添加了很多功能
* 1.0.0
+ 初始版本
## 使用说明 ## 使用说明
@@ -73,46 +203,27 @@
+ 启用开发模式快捷键(使用说明见设置面板) + 启用开发模式快捷键(使用说明见设置面板)
+ 屏蔽异常检测 + 屏蔽异常检测
+ 使用组合键解锁科技Ctrl/Alt/Shift + 使用组合键解锁科技Ctrl/Alt/Shift
+ 移除所有元数据消耗记录 + 将游戏绑定给当前账号
+ 移除当前存档的元数据消耗记录
+ 解除当前存档因使用元数据导致的成就限制
+ 将游戏存档绑定给当前账号
+ 工厂: + 工厂:
+ 建造秒完成 + 建造秒完成
+ 建筑师模式(无限建筑) + 建筑师模式(无限建筑)
+ 无条件建造 + 无条件建造
+ 无碰撞 + 无碰撞
+ 传送带信号物品生成 + 传送带信号物品生成
- 统计信息里将生成计算为产物
- 统计信息里将移除计算为消耗
- 统计面板中计算所有原材料和中间产物 - 统计面板中计算所有原材料和中间产物
- 传送带信号替换格式 - 传送带信号替换格式
+ 提升物流塔和大型采矿机的最大功耗
- 物流塔将最大充电功率提高到3GW(星际物流塔)和600MW(行星物流塔)原来的10倍
- 大型采矿机将最大采矿速度提高到1000%
+ 在物流总控面板上可以从非本地行星取放物品
+ 风力发电机和太阳能板无间距限制 + 风力发电机和太阳能板无间距限制
+ 风力涡轮机供电覆盖全球
+ 提升各种发电设备发电量 + 提升各种发电设备发电量
+ 行星: + 行星:
+ 快速手动制造
+ 自然资源采集不消耗 + 自然资源采集不消耗
+ 高速采集 + 高速采集
+ 平地抽水 + 平地抽水
+ 沙土不够时依然可以整改地形 + 沙土不够时依然可以整改地形
+ 快速传送(和沙盒模式一样)
+ 戴森球: + 戴森球:
+ 跳过子弹阶段 + 跳过子弹阶段
+ 跳过吸收阶段 + 跳过吸收阶段
+ 快速吸收 + 快速吸收
+ 全球弹射 + 全球弹射
+ 立即完成戴森壳建造
+ 机甲/战斗:
+ 机甲和战斗无人机无敌
+ 建筑无敌
+ 无需空间翘曲器即可曲速飞行
+ 传送到外太空
+ 传送到选定的天体
## 注意事项 ## 注意事项
* 如果和[BlueprintTweaks](https://dsp.thunderstore.io/package/kremnev8/BlueprintTweaks/)一起使用,请升级`BepInEx`到5.4.21或更高版本,以避免可能的冲突 * 如果和[BlueprintTweaks](https://dsp.thunderstore.io/package/kremnev8/BlueprintTweaks/)一起使用,请升级`BepInEx`到5.4.21或更高版本,以避免可能的冲突

View File

@@ -2,9 +2,8 @@
using System.Reflection.Emit; using System.Reflection.Emit;
using BepInEx.Configuration; using BepInEx.Configuration;
using HarmonyLib; using HarmonyLib;
using UXAssist.Common;
namespace CheatEnabler.Patches; namespace CheatEnabler;
public static class ResourcePatch public static class ResourcePatch
{ {
@@ -15,10 +14,6 @@ public static class ResourcePatch
{ {
InfiniteResourceEnabled.SettingChanged += (_, _) => InfiniteResource.Enable(InfiniteResourceEnabled.Value); InfiniteResourceEnabled.SettingChanged += (_, _) => InfiniteResource.Enable(InfiniteResourceEnabled.Value);
FastMiningEnabled.SettingChanged += (_, _) => FastMining.Enable(FastMiningEnabled.Value); FastMiningEnabled.SettingChanged += (_, _) => FastMining.Enable(FastMiningEnabled.Value);
}
public static void Start()
{
InfiniteResource.Enable(InfiniteResourceEnabled.Value); InfiniteResource.Enable(InfiniteResourceEnabled.Value);
FastMining.Enable(FastMiningEnabled.Value); FastMining.Enable(FastMiningEnabled.Value);
} }
@@ -29,21 +24,36 @@ public static class ResourcePatch
FastMining.Enable(false); FastMining.Enable(false);
} }
private class InfiniteResource: PatchImpl<InfiniteResource> private static class InfiniteResource
{ {
private static Harmony _patch;
public static void Enable(bool on)
{
if (on)
{
_patch ??= Harmony.CreateAndPatchAll(typeof(InfiniteResource));
}
else
{
_patch?.UnpatchSelf();
_patch = null;
}
}
[HarmonyTranspiler] [HarmonyTranspiler]
[HarmonyPatch(typeof(FactorySystem), nameof(FactorySystem.GameTick), typeof(long), typeof(bool))] [HarmonyPatch(typeof(FactorySystem), "GameTick", typeof(long), typeof(bool))]
[HarmonyPatch(typeof(FactorySystem), nameof(FactorySystem.GameTick), typeof(long), typeof(bool), typeof(int), typeof(int), typeof(int))] [HarmonyPatch(typeof(FactorySystem), "GameTick", typeof(long), typeof(bool), typeof(int), typeof(int), typeof(int))]
[HarmonyPatch(typeof(ItemProto), nameof(ItemProto.GetPropValue))] [HarmonyPatch(typeof(ItemProto), "GetPropValue")]
[HarmonyPatch(typeof(PlanetTransport), nameof(PlanetTransport.GameTick))] [HarmonyPatch(typeof(PlanetTransport), "GameTick")]
[HarmonyPatch(typeof(UIChartAstroResource), nameof(UIChartAstroResource.CalculateMaxAmount))] [HarmonyPatch(typeof(UIMinerWindow), "_OnUpdate")]
[HarmonyPatch(typeof(UIChartVeinGroup), nameof(UIChartVeinGroup.CalculateMaxAmount))] [HarmonyPatch(typeof(UIMiningUpgradeLabel), "Update")]
[HarmonyPatch(typeof(UIControlPanelAdvancedMinerEntry), nameof(UIControlPanelAdvancedMinerEntry._OnUpdate))] [HarmonyPatch(typeof(UIPlanetDetail), "OnPlanetDataSet")]
[HarmonyPatch(typeof(UIControlPanelVeinCollectorPanel), nameof(UIControlPanelVeinCollectorPanel._OnUpdate))] [HarmonyPatch(typeof(UIPlanetDetail), "RefreshDynamicProperties")]
[HarmonyPatch(typeof(UIMinerWindow), nameof(UIMinerWindow._OnUpdate))] [HarmonyPatch(typeof(UIStarDetail), "OnStarDataSet")]
[HarmonyPatch(typeof(UIMiningUpgradeLabel), nameof(UIMiningUpgradeLabel.Update))] [HarmonyPatch(typeof(UIStarDetail), "RefreshDynamicProperties")]
[HarmonyPatch(typeof(UIStationStorage), nameof(UIStationStorage.RefreshValues))] [HarmonyPatch(typeof(UIStationStorage), "RefreshValues")]
[HarmonyPatch(typeof(UIVeinCollectorPanel), nameof(UIVeinCollectorPanel._OnUpdate))] [HarmonyPatch(typeof(UIVeinCollectorPanel), "_OnUpdate")]
private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator) private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{ {
var matcher = new CodeMatcher(instructions, generator); var matcher = new CodeMatcher(instructions, generator);
@@ -59,8 +69,23 @@ public static class ResourcePatch
} }
} }
private class FastMining: PatchImpl<FastMining> private static class FastMining
{ {
private static Harmony _patch;
public static void Enable(bool on)
{
if (on)
{
_patch ??= Harmony.CreateAndPatchAll(typeof(FastMining));
}
else
{
_patch?.UnpatchSelf();
_patch = null;
}
}
[HarmonyTranspiler] [HarmonyTranspiler]
[HarmonyPatch(typeof(FactorySystem), "GameTick", typeof(long), typeof(bool))] [HarmonyPatch(typeof(FactorySystem), "GameTick", typeof(long), typeof(bool))]
[HarmonyPatch(typeof(FactorySystem), "GameTick", typeof(long), typeof(bool), typeof(int), typeof(int), typeof(int))] [HarmonyPatch(typeof(FactorySystem), "GameTick", typeof(long), typeof(bool), typeof(int), typeof(int), typeof(int))]

149
CheatEnabler/TechPatch.cs Normal file
View File

@@ -0,0 +1,149 @@
using System;
using System.Collections.Generic;
using System.Reflection.Emit;
using BepInEx.Configuration;
using HarmonyLib;
using JetBrains.Annotations;
namespace CheatEnabler;
public static class TechPatch
{
public static ConfigEntry<bool> Enabled;
private static Harmony _patch;
public static void Init()
{
Enabled.SettingChanged += (_, _) => ValueChanged();
ValueChanged();
}
public static void Uninit()
{
_patch?.UnpatchSelf();
_patch = null;
}
private static void ValueChanged()
{
if (Enabled.Value)
{
_patch ??= Harmony.CreateAndPatchAll(typeof(TechPatch));
}
else
{
_patch?.UnpatchSelf();
_patch = null;
}
}
private static void UnlockTechRecursive([NotNull] TechProto techProto, int maxLevel = 10000)
{
var history = GameMain.history;
var techStates = history.techStates;
var techID = techProto.ID;
if (techStates == null || !techStates.ContainsKey(techID))
{
return;
}
var value = techStates[techID];
if (value.unlocked)
{
return;
}
var maxLvl = Math.Min(maxLevel < 0 ? value.curLevel - maxLevel - 1 : maxLevel, value.maxLevel);
foreach (var preid in techProto.PreTechs)
{
var preProto = LDB.techs.Select(preid);
if (preProto != null)
UnlockTechRecursive(preProto, maxLevel);
}
var techQueue = history.techQueue;
if (techQueue != null)
{
for (var i = 0; i < techQueue.Length; i++)
{
if (techQueue[i] == techID)
{
techQueue[i] = 0;
}
}
}
if (value.curLevel < techProto.Level) value.curLevel = techProto.Level;
while (value.curLevel <= maxLvl)
{
if (value.curLevel == 0)
{
foreach (var recipe in techProto.UnlockRecipes)
{
history.UnlockRecipe(recipe);
}
}
for (var j = 0; j < techProto.UnlockFunctions.Length; j++)
{
history.UnlockTechFunction(techProto.UnlockFunctions[j], techProto.UnlockValues[j], value.curLevel);
}
for (var k = 0; k < techProto.AddItems.Length; k++)
{
history.GainTechAwards(techProto.AddItems[k], techProto.AddItemCounts[k]);
}
value.curLevel++;
}
value.unlocked = maxLvl >= value.maxLevel;
value.curLevel = value.unlocked ? maxLvl : maxLvl + 1;
value.hashNeeded = techProto.GetHashNeeded(value.curLevel);
value.hashUploaded = value.unlocked ? value.hashNeeded : 0;
techStates[techID] = value;
history.RegFeatureKey(1000100);
history.NotifyTechUnlock(techID, maxLvl, true);
}
private static void OnClickTech(UITechNode node)
{
if (VFInput.shift)
{
if (VFInput.alt) return;
if (VFInput.control)
UnlockTechRecursive(node.techProto, -100);
else
UnlockTechRecursive(node.techProto, -1);
return;
}
if (VFInput.control)
{
if (!VFInput.alt)
UnlockTechRecursive(node.techProto, -10);
}
else if (VFInput.alt)
{
UnlockTechRecursive(node.techProto);
}
}
[HarmonyTranspiler]
[HarmonyPatch(typeof(UITechNode), nameof(UITechNode.OnPointerDown))]
private static IEnumerable<CodeInstruction> UITechNode_OnPointerDown_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{
var matcher = new CodeMatcher(instructions, generator);
matcher.MatchForward(false,
new CodeMatch(OpCodes.Ldarg_0),
new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(UITechNode), nameof(UITechNode.tree))),
new CodeMatch(OpCodes.Callvirt, AccessTools.Method(typeof(UITechTree), "get_selected"))
);
var labels = matcher.Labels;
matcher.Labels = null;
matcher.Insert(
new CodeInstruction(OpCodes.Ldarg_0).WithLabels(labels),
new CodeInstruction(OpCodes.Call, AccessTools.Method(typeof(TechPatch), nameof(TechPatch.OnClickTech)))
);
return matcher.InstructionEnumeration();
}
}

View File

@@ -1,6 +1,4 @@
using CheatEnabler.Functions; using UnityEngine;
using CheatEnabler.Patches;
using UnityEngine;
using UXAssist.UI; using UXAssist.UI;
using UXAssist.Common; using UXAssist.Common;
@@ -11,13 +9,11 @@ public static class UIConfigWindow
private static RectTransform _windowTrans; private static RectTransform _windowTrans;
private static UIButton _resignGameBtn; private static UIButton _resignGameBtn;
private static UIButton _clearBanBtn;
public static void Init() public static void Init()
{ {
I18N.Add("Factory", "Factory", "工厂"); I18N.Add("Factory", "Factory", "工厂");
I18N.Add("Planet", "Planet", "行星"); I18N.Add("Planet", "Planet", "行星");
I18N.Add("Mecha/Combat", "Mecha/Combat", "机甲/战斗");
I18N.Add("Enable Dev Shortcuts", "Enable Dev Shortcuts", "开发模式快捷键"); I18N.Add("Enable Dev Shortcuts", "Enable Dev Shortcuts", "开发模式快捷键");
I18N.Add("Disable Abnormal Checks", "Disable Abnormal Checks", "关闭数据异常检查"); I18N.Add("Disable Abnormal Checks", "Disable Abnormal Checks", "关闭数据异常检查");
I18N.Add("Hotkey", "Hotkey", "快捷键"); I18N.Add("Hotkey", "Hotkey", "快捷键");
@@ -29,31 +25,24 @@ public static class UIConfigWindow
I18N.Add("Unlock Tech with Key-Modifiers Tips", I18N.Add("Unlock Tech with Key-Modifiers Tips",
"Click tech on tree while holding:\n Shift: Tech level + 1\n Ctrl: Tech level + 10\n Ctrl + Shift: Tech level + 100\n Alt: Tech level to MAX\n\nNote: all direct prerequisites will be unlocked as well.", "Click tech on tree while holding:\n Shift: Tech level + 1\n Ctrl: Tech level + 10\n Ctrl + Shift: Tech level + 100\n Alt: Tech level to MAX\n\nNote: all direct prerequisites will be unlocked as well.",
"按住以下组合键点击科技树:\n Shift科技等级+1\n Ctrl科技等级+10\n Ctrl+Shift科技等级+100\n Alt科技等级升到最大\n\n注意所有直接前置科技也会被解锁"); "按住以下组合键点击科技树:\n Shift科技等级+1\n Ctrl科技等级+10\n Ctrl+Shift科技等级+100\n Alt科技等级升到最大\n\n注意所有直接前置科技也会被解锁");
I18N.Add("Remove all metadata consumption records", "Remove all metadata consumption records", "移除所有元数据消耗记录"); I18N.Add("Assign game to current account", "Assign game to current account", "将游戏绑定给当前账号");
I18N.Add("Remove metadata consumption record in current game", "Remove metadata consumption record in current game", "移除当前存档的元数据消耗记录");
I18N.Add("Clear metadata flag which bans achievements", "Clear metadata flag which bans achievements in current game", "解除当前存档因使用元数据导致的成就限制");
I18N.Add("Assign gamesave to current account", "Assign gamesave to current account", "将游戏存档绑定给当前账号");
I18N.Add("Finish build immediately", "Finish build immediately", "建造秒完成"); I18N.Add("Finish build immediately", "Finish build immediately", "建造秒完成");
I18N.Add("Architect mode", "Architect mode", "建筑师模式"); I18N.Add("Architect mode", "Architect mode", "建筑师模式");
I18N.Add("Build without condition", "Build without condition check", "无条件建造"); I18N.Add("Build without condition", "Build without condition check", "无条件建造");
I18N.Add("Build without condition is enabled!", "!!Build without condition is enabled!!", "!!无条件建造已开启!!");
I18N.Add("No collision", "No collision", "无碰撞"); I18N.Add("No collision", "No collision", "无碰撞");
I18N.Add("Belt signal generator", "Belt signal generator", "传送带信号物品生成"); I18N.Add("Belt signal generator", "Belt signal generator", "传送带信号物品生成");
I18N.Add("Belt signal alt format", "Belt signal alt format", "传送带信号替换格式"); I18N.Add("Belt signal alt format", "Belt signal alt format", "传送带信号替换格式");
I18N.Add("Belt signal alt format tips", I18N.Add("Belt signal alt format tips",
"Belt signal number format alternative format:\n AAAABC by default\n BCAAAA as alternative\nAAAA=generation speed in minutes, B=proliferate points, C=stack count", "Belt signal number format alternative format:\n AAAABC by default\n BCAAAA as alternative\nAAAA=generation speed in minutes, B=proliferate points, C=stack count",
"传送带信号物品生成数量格式:\n 默认为AAAABC\n 勾选替换为BCAAAA\nAAAA=生成速度B=增产点数C=堆叠数量"); "传送带信号物品生成数量格式:\n 默认为AAAABC\n 勾选替换为BCAAAA\nAAAA=生成速度B=增产点数C=堆叠数量");
I18N.Add("Count generations as production in statistics", "Count generations as production in statistics", "统计信息里将生成计算为产物");
I18N.Add("Count removals as consumption in statistics", "Count removals as consumption in statistics", "统计信息里将移除计算为消耗");
I18N.Add("Count all raws and intermediates in statistics","Count all raw materials in statistics", "统计信息里计算所有原料和中间产物"); I18N.Add("Count all raws and intermediates in statistics","Count all raw materials in statistics", "统计信息里计算所有原料和中间产物");
I18N.Add("Remove power space limit", "Remove space limit for winds and geothermals", "移除风力发电和地热发电的间距限制"); I18N.Add("Remove power space limit", "Remove space limit for winds and geothermals", "移除风力发电和地热发电的间距限制");
I18N.Add("Boost wind power", "Boost wind power(x100,000)", "提升风力发电(x100,000)"); I18N.Add("Boost wind power", "Boost wind power(x100,000)", "提升风力发电(x100,000)");
I18N.Add("Boost solar power", "Boost solar power(x100,000)", "提升太阳能发电(x100,000)"); I18N.Add("Boost solar power", "Boost solar power(x100,000)", "提升太阳能发电(x100,000)");
I18N.Add("Boost fuel power", "Boost fuel power(x50,000)", "提升燃料发电(x50,000)"); I18N.Add("Boost fuel power", "Boost fuel power(x50,000)", "提升燃料发电(x50,000)");
I18N.Add("Boost fuel power 2", "(x20,000 for deuteron, x10,000 for antimatter)", "(氘核燃料棒x20,000反物质燃料棒x10,000)"); I18N.Add("Boost fuel power 2", "(x20,000 for deuteron, x10,000 for antimatter)", "(氘核燃料棒x20,000反物质燃料棒x10,000)");
I18N.Add("Wind Turbines do global power coverage", "Wind Turbines do global power coverage", "风力涡轮机供电覆盖全球");
I18N.Add("Boost geothermal power", "Boost geothermal power(x50,000)", "提升地热发电(x50,000)"); I18N.Add("Boost geothermal power", "Boost geothermal power(x50,000)", "提升地热发电(x50,000)");
I18N.Add("Increase maximum power usage in Logistic Stations and Advanced Mining Machines", "Increase maximum power usage in Logistic Stations and Advanced Mining Machines", "提升物流塔和大型采矿机的最大功耗");
I18N.Add("Retrieve/Place items from/to remote planets on logistics control panel", "Retrieve/Place items from/to remote planets on logistics control panel", "在物流总控面板上可以从非本地行星取放物品");
I18N.Add("Infinite Natural Resources", "Infinite natural resources", "自然资源采集不消耗"); I18N.Add("Infinite Natural Resources", "Infinite natural resources", "自然资源采集不消耗");
I18N.Add("Fast Mining", "Fast mining", "高速采集"); I18N.Add("Fast Mining", "Fast mining", "高速采集");
I18N.Add("Pump Anywhere", "Pump anywhere", "平地抽水"); I18N.Add("Pump Anywhere", "Pump anywhere", "平地抽水");
@@ -63,15 +52,7 @@ public static class UIConfigWindow
I18N.Add("Eject anyway", "Eject anyway", "全球弹射"); I18N.Add("Eject anyway", "Eject anyway", "全球弹射");
I18N.Add("Overclock Ejectors", "Overclock Ejectors (10x)", "高速弹射器(10倍射速)"); I18N.Add("Overclock Ejectors", "Overclock Ejectors (10x)", "高速弹射器(10倍射速)");
I18N.Add("Overclock Silos", "Overclock Silos (10x)", "高速发射井(10倍射速)"); I18N.Add("Overclock Silos", "Overclock Silos (10x)", "高速发射井(10倍射速)");
I18N.Add("Complete Dyson Sphere shells instantly", "Complete Dyson Sphere shells instantly", "立即完成戴森壳建造"); I18N.Add("Terraform without enough sands", "Terraform without enough sands", "沙土不够时依然可以整改地形");
I18N.Add("Terraform without enough soil piles", "Terraform without enough soil piles", "沙土不够时依然可以整改地形");
I18N.Add("Instant hand-craft", "Instant hand-craft", "快速手动制造");
I18N.Add("Instant teleport (like that in Sandbox mode)", "Instant teleport (like that in Sandbox mode)", "快速传送(和沙盒模式一样)");
I18N.Add("Mecha and Drones/Fleets invicible", "Mecha and Drones/Fleets invicible", "机甲和战斗无人机无敌");
I18N.Add("Buildings invicible", "Buildings invincible", "建筑无敌");
I18N.Add("Enable warp without space warpers", "Enable warp without space warpers", "无需空间翘曲器即可曲速飞行");
I18N.Add("Teleport to outer space", "Teleport to outer space", "传送到外太空");
I18N.Add("Teleport to selected astronomical", "Teleport to selected astronomical", "传送到选中的天体");
I18N.Apply(); I18N.Apply();
MyConfigWindow.OnUICreated += CreateUI; MyConfigWindow.OnUICreated += CreateUI;
MyConfigWindow.OnUpdateUI += UpdateUI; MyConfigWindow.OnUpdateUI += UpdateUI;
@@ -86,58 +67,41 @@ public static class UIConfigWindow
wnd.AddSplitter(trans, 10f); wnd.AddSplitter(trans, 10f);
wnd.AddTabGroup(trans, "Cheat Enabler", "tab-group-cheatenabler"); wnd.AddTabGroup(trans, "Cheat Enabler", "tab-group-cheatenabler");
var tab1 = wnd.AddTab(_windowTrans, "General"); var tab1 = wnd.AddTab(_windowTrans, "General");
var cb = wnd.AddCheckBox(x, y, tab1, GamePatch.DevShortcutsEnabled, "Enable Dev Shortcuts"); MyCheckBox.CreateCheckBox(x, y, tab1, DevShortcuts.Enabled, "Enable Dev Shortcuts");
x += cb.Width + 5f;
y += 6f;
wnd.AddTipsButton2(x, y, tab1, "Dev Shortcuts", "Dev Shortcuts Tips", "dev-shortcuts-tips");
x = 0;
y += 30f;
wnd.AddCheckBox(x, y, tab1, GamePatch.AbnormalDisablerEnabled, "Disable Abnormal Checks");
y += 36f; y += 36f;
cb = wnd.AddCheckBox(x, y, tab1, GamePatch.UnlockTechEnabled, "Unlock Tech with Key-Modifiers"); MyCheckBox.CreateCheckBox(x, y, tab1, AbnormalDisabler.Enabled, "Disable Abnormal Checks");
x += cb.Width + 5f;
y += 6f;
wnd.AddTipsButton2(x, y, tab1, "Unlock Tech with Key-Modifiers", "Unlock Tech with Key-Modifiers Tips", "unlock-tech-tips");
x = 0f;
y += 30f + 36f;
wnd.AddButton(x, y, 400f, tab1, "Remove all metadata consumption records", 16, "button-remove-all-metadata-consumption", PlayerFunctions.RemoveAllMetadataConsumptions);
y += 36f; y += 36f;
wnd.AddButton(x, y, 400f, tab1, "Remove metadata consumption record in current game", 16, "button-remove-current-metadata-consumption", PlayerFunctions.RemoveCurrentMetadataConsumptions); MyCheckBox.CreateCheckBox(x, y, tab1, TechPatch.Enabled, "Unlock Tech with Key-Modifiers");
y += 36f; x = 156f;
_clearBanBtn = wnd.AddButton(x, y, 400f, tab1, "Clear metadata flag which bans achievements", 16, "button-clear-ban-list", PlayerFunctions.ClearMetadataBanAchievements); y = 16f;
MyWindow.AddTipsButton(x, y, tab1, "Dev Shortcuts", "Dev Shortcuts Tips", "dev-shortcuts-tips");
x += 52f;
y += 72f;
MyWindow.AddTipsButton(x, y, tab1, "Unlock Tech with Key-Modifiers", "Unlock Tech with Key-Modifiers Tips", "unlock-tech-tips");
x = 300f; x = 300f;
y = 10f; y = 10f;
_resignGameBtn = wnd.AddButton(x, y, 300f, tab1, "Assign gamesave to current account", 16, "resign-game-btn", () => { GameMain.data.account = AccountData.me; }); _resignGameBtn = wnd.AddButton(x, y, 200f, tab1, "Assign game to current account", 16, "resign-game-btn", () => { GameMain.data.account = AccountData.me; });
var tab2 = wnd.AddTab(_windowTrans, "Factory"); var tab2 = wnd.AddTab(_windowTrans, "Factory");
x = 0f; x = 0f;
y = 10f; y = 10f;
wnd.AddCheckBox(x, y, tab2, FactoryPatch.ImmediateEnabled, "Finish build immediately"); MyCheckBox.CreateCheckBox(x, y, tab2, FactoryPatch.ImmediateEnabled, "Finish build immediately");
y += 36f; y += 36f;
wnd.AddCheckBox(x, y, tab2, FactoryPatch.ArchitectModeEnabled, "Architect mode"); MyCheckBox.CreateCheckBox(x, y, tab2, FactoryPatch.ArchitectModeEnabled, "Architect mode");
y += 36f; y += 36f;
wnd.AddCheckBox(x, y, tab2, FactoryPatch.NoConditionEnabled, "Build without condition"); MyCheckBox.CreateCheckBox(x, y, tab2, FactoryPatch.NoConditionEnabled, "Build without condition");
y += 36f; y += 36f;
wnd.AddCheckBox(x, y, tab2, FactoryPatch.NoCollisionEnabled, "No collision"); MyCheckBox.CreateCheckBox(x, y, tab2, FactoryPatch.NoCollisionEnabled, "No collision");
y += 36f; y += 36f;
wnd.AddCheckBox(x, y, tab2, FactoryPatch.BeltSignalGeneratorEnabled, "Belt signal generator"); MyCheckBox.CreateCheckBox(x, y, tab2, FactoryPatch.BeltSignalGeneratorEnabled, "Belt signal generator");
y += 26f;
x += 26f; x += 26f;
var cb = MyCheckBox.CreateCheckBox(x, y, tab2, FactoryPatch.BeltSignalCountRecipeEnabled, "Count all raws and intermediates in statistics", 13);
y += 26f; y += 26f;
var cb1 = wnd.AddCheckBox(x, y, tab2, FactoryPatch.BeltSignalCountGenEnabled, "Count generations as production in statistics", 13); var cb2 = MyCheckBox.CreateCheckBox(x, y, tab2, FactoryPatch.BeltSignalNumberAltFormat, "Belt signal alt format", 13);
y += 26f; x += 180f;
var cb2 = wnd.AddCheckBox(x, y, tab2, FactoryPatch.BeltSignalCountRemEnabled, "Count removals as consumption in statistics", 13);
y += 26f;
var cb3 = wnd.AddCheckBox(x, y, tab2, FactoryPatch.BeltSignalCountRecipeEnabled, "Count all raws and intermediates in statistics", 13);
y += 26f;
var cb4 = wnd.AddCheckBox(x, y, tab2, FactoryPatch.BeltSignalNumberAltFormat, "Belt signal alt format", 13);
x += cb4.Width + 5f;
y += 6f; y += 6f;
var tip1 = wnd.AddTipsButton2(x, y, tab2, "Belt signal alt format", "Belt signal alt format tips", "belt-signal-alt-format-tips"); var tip1 = MyWindow.AddTipsButton(x, y, tab2, "Belt signal alt format", "Belt signal alt format tips", "belt-signal-alt-format-tips");
x = 0f;
y += 30f;
wnd.AddCheckBox(x, y, tab2, FactoryPatch.GreaterPowerUsageInLogisticsEnabled, "Increase maximum power usage in Logistic Stations and Advanced Mining Machines");
y += 36f;
wnd.AddCheckBox(x, y, tab2, FactoryPatch.ControlPanelRemoteLogisticsEnabled, "Retrieve/Place items from/to remote planets on logistics control panel");
FactoryPatch.BeltSignalGeneratorEnabled.SettingChanged += (_, _) => FactoryPatch.BeltSignalGeneratorEnabled.SettingChanged += (_, _) =>
{ {
@@ -146,35 +110,30 @@ public static class UIConfigWindow
OnBeltSignalChanged(); OnBeltSignalChanged();
x = 350f; x = 350f;
y = 10f; y = 10f;
wnd.AddCheckBox(x, y, tab2, FactoryPatch.RemovePowerSpaceLimitEnabled, "Remove power space limit"); MyCheckBox.CreateCheckBox(x, y, tab2, FactoryPatch.RemovePowerSpaceLimitEnabled, "Remove power space limit");
y += 36f; y += 36f;
wnd.AddCheckBox(x, y, tab2, FactoryPatch.WindTurbinesPowerGlobalCoverageEnabled, "Wind Turbines do global power coverage"); MyCheckBox.CreateCheckBox(x, y, tab2, FactoryPatch.BoostWindPowerEnabled, "Boost wind power");
y += 36f; y += 36f;
wnd.AddCheckBox(x, y, tab2, FactoryPatch.BoostWindPowerEnabled, "Boost wind power"); MyCheckBox.CreateCheckBox(x, y, tab2, FactoryPatch.BoostSolarPowerEnabled, "Boost solar power");
y += 36f; y += 36f;
wnd.AddCheckBox(x, y, tab2, FactoryPatch.BoostSolarPowerEnabled, "Boost solar power"); MyCheckBox.CreateCheckBox(x, y, tab2, FactoryPatch.BoostGeothermalPowerEnabled, "Boost geothermal power");
y += 36f; y += 36f;
wnd.AddCheckBox(x, y, tab2, FactoryPatch.BoostGeothermalPowerEnabled, "Boost geothermal power"); MyCheckBox.CreateCheckBox(x, y, tab2, FactoryPatch.BoostFuelPowerEnabled, "Boost fuel power");
y += 36f; x += 32f;
wnd.AddCheckBox(x, y, tab2, FactoryPatch.BoostFuelPowerEnabled, "Boost fuel power");
y += 26f; y += 26f;
wnd.AddText2(x + 32f, y, tab2, "Boost fuel power 2", 13); MyWindow.AddText(x, y, tab2, "Boost fuel power 2", 13);
// Planet Tab // Planet Tab
var tab3 = wnd.AddTab(_windowTrans, "Planet"); var tab3 = wnd.AddTab(_windowTrans, "Planet");
x = 0f; x = 0f;
y = 10f; y = 10f;
wnd.AddCheckBox(x, y, tab3, ResourcePatch.InfiniteResourceEnabled, "Infinite Natural Resources"); MyCheckBox.CreateCheckBox(x, y, tab3, ResourcePatch.InfiniteResourceEnabled, "Infinite Natural Resources");
y += 36f; y += 36f;
wnd.AddCheckBox(x, y, tab3, ResourcePatch.FastMiningEnabled, "Fast Mining"); MyCheckBox.CreateCheckBox(x, y, tab3, ResourcePatch.FastMiningEnabled, "Fast Mining");
y += 36f; y += 36f;
wnd.AddCheckBox(x, y, tab3, PlanetPatch.WaterPumpAnywhereEnabled, "Pump Anywhere"); MyCheckBox.CreateCheckBox(x, y, tab3, PlanetPatch.WaterPumpAnywhereEnabled, "Pump Anywhere");
y += 36f; y += 36f;
wnd.AddCheckBox(x, y, tab3, PlanetPatch.TerraformAnywayEnabled, "Terraform without enough soil piles"); MyCheckBox.CreateCheckBox(x, y, tab3, PlanetPatch.TerraformAnywayEnabled, "Terraform without enough sands");
y += 36f;
wnd.AddCheckBox(x, y, tab3, PlayerPatch.InstantHandCraftEnabled, "Instant hand-craft");
y += 36f;
wnd.AddCheckBox(x, y, tab3, PlayerPatch.InstantTeleportEnabled, "Instant teleport (like that in Sandbox mode)");
x = 400f; x = 400f;
y = 10f; y = 10f;
wnd.AddButton(x, y, 200f, tab3, "矿物掩埋标题", 16, "button-bury-all", () => { PlanetFunctions.BuryAllVeins(true); }); wnd.AddButton(x, y, 200f, tab3, "矿物掩埋标题", 16, "button-bury-all", () => { PlanetFunctions.BuryAllVeins(true); });
@@ -201,67 +160,37 @@ public static class UIConfigWindow
var tab4 = wnd.AddTab(_windowTrans, "Dyson Sphere"); var tab4 = wnd.AddTab(_windowTrans, "Dyson Sphere");
x = 0f; x = 0f;
y = 10f; y = 10f;
wnd.AddCheckBox(x, y, tab4, DysonSpherePatch.SkipBulletEnabled, "Skip bullet period"); MyCheckBox.CreateCheckBox(x, y, tab4, DysonSpherePatch.SkipBulletEnabled, "Skip bullet period");
y += 36f; y += 36f;
wnd.AddCheckBox(x, y, tab4, DysonSpherePatch.SkipAbsorbEnabled, "Skip absorption period"); MyCheckBox.CreateCheckBox(x, y, tab4, DysonSpherePatch.SkipAbsorbEnabled, "Skip absorption period");
y += 36f; y += 36f;
wnd.AddCheckBox(x, y, tab4, DysonSpherePatch.QuickAbsorbEnabled, "Quick absorb"); MyCheckBox.CreateCheckBox(x, y, tab4, DysonSpherePatch.QuickAbsorbEnabled, "Quick absorb");
y += 36f; y += 36f;
wnd.AddCheckBox(x, y, tab4, DysonSpherePatch.EjectAnywayEnabled, "Eject anyway"); MyCheckBox.CreateCheckBox(x, y, tab4, DysonSpherePatch.EjectAnywayEnabled, "Eject anyway");
y += 36f; y += 36f;
wnd.AddCheckBox(x, y, tab4, DysonSpherePatch.OverclockEjectorEnabled, "Overclock Ejectors"); MyCheckBox.CreateCheckBox(x, y, tab4, DysonSpherePatch.OverclockEjectorEnabled, "Overclock Ejectors");
y += 36f; y += 36f;
wnd.AddCheckBox(x, y, tab4, DysonSpherePatch.OverclockSiloEnabled, "Overclock Silos"); MyCheckBox.CreateCheckBox(x, y, tab4, DysonSpherePatch.OverclockSiloEnabled, "Overclock Silos");
x = 300f;
y = 10f;
wnd.AddButton(x, y, 300f, tab4, "Complete Dyson Sphere shells instantly", 16, "button-complete-dyson-sphere-shells-instantly", DysonSphereFunctions.CompleteShellsInstantly);
var tab5 = wnd.AddTab(_windowTrans, "Mecha/Combat");
x = 0f;
y = 10f;
wnd.AddCheckBox(x, y, tab5, CombatPatch.MechaInvincibleEnabled, "Mecha and Drones/Fleets invicible");
y += 36f;
wnd.AddCheckBox(x, y, tab5, CombatPatch.BuildingsInvincibleEnabled, "Buildings invicible");
y += 36f;
wnd.AddCheckBox(x, y, tab5, PlayerPatch.WarpWithoutSpaceWarpersEnabled, "Enable warp without space warpers");
x = 400f;
y = 10f;
wnd.AddButton(x, y, 200f, tab5, "Teleport to outer space", 16, "button-teleport-to-outer-space", PlayerFunctions.TeleportToOuterSpace);
y += 36f;
wnd.AddButton(x, y, 200f, tab5, "Teleport to selected astronomical", 16, "button-teleport-to-selected-astronomical", PlayerFunctions.TeleportToSelectedAstronomical);
return; return;
void OnBeltSignalChanged() void OnBeltSignalChanged()
{ {
var on = FactoryPatch.BeltSignalGeneratorEnabled.Value; var on = FactoryPatch.BeltSignalGeneratorEnabled.Value;
cb1.gameObject.SetActive(on); cb.gameObject.SetActive(on);
cb2.gameObject.SetActive(on); cb2.gameObject.SetActive(on);
cb3.gameObject.SetActive(on);
cb4.gameObject.SetActive(on);
tip1.gameObject.SetActive(on); tip1.gameObject.SetActive(on);
} }
} }
private static void UpdateUI() private static void UpdateUI()
{ {
UpdateButtons(); UpdateResignButton();
} }
private static void UpdateButtons() private static void UpdateResignButton()
{
var data = GameMain.data;
if (data == null) return;
var resignEnabled = data.account != AccountData.me;
if (_resignGameBtn.gameObject.activeSelf != resignEnabled)
{ {
var resignEnabled = GameMain.data.account != AccountData.me;
if (_resignGameBtn.gameObject.activeSelf == resignEnabled) return;
_resignGameBtn.gameObject.SetActive(resignEnabled); _resignGameBtn.gameObject.SetActive(resignEnabled);
} }
var history = data.history;
if (history == null) return;
var banEnabled = history.hasUsedPropertyBanAchievement;
if (_clearBanBtn.gameObject.activeSelf != banEnabled)
{
_clearBanBtn.gameObject.SetActive(banEnabled);
}
}
} }

View File

@@ -1,10 +1,10 @@
{ {
"name": "CheatEnabler", "name": "CheatEnabler",
"version_number": "2.3.29", "version_number": "2.3.9",
"website_url": "https://github.com/soarqin/DSP_Mods/tree/master/CheatEnabler", "website_url": "https://github.com/soarqin/DSP_Mods/tree/master/CheatEnabler",
"description": "Add various cheat functions while disabling abnormal determinants / 添加一些作弊功能,同时屏蔽异常检测", "description": "Add various cheat functions while disabling abnormal determinants / 添加一些作弊功能,同时屏蔽异常检测",
"dependencies": [ "dependencies": [
"xiaoye97-BepInEx-5.4.17", "xiaoye97-BepInEx-5.4.17",
"soarqin-UXAssist-1.2.0" "soarqin-UXAssist-1.0.2"
] ]
} }

View File

@@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Reflection.Emit; using System.Reflection.Emit;
@@ -13,6 +14,7 @@ public static class BeltSignal
private static HashSet<int>[] _signalBelts; private static HashSet<int>[] _signalBelts;
private static int _signalBeltsCapacity; private static int _signalBeltsCapacity;
private static bool _initialized; private static bool _initialized;
private static AssetBundle _bundle;
private static Harmony _patch; private static Harmony _patch;
public static void Enable(bool on) public static void Enable(bool on)
@@ -31,40 +33,12 @@ public static class BeltSignal
} }
} }
private static byte[] LoadEmbeddedResource(string path, Assembly assembly = null) [HarmonyPostfix, HarmonyPriority(Priority.Last), HarmonyPatch(typeof(VFPreload), "InvokeOnLoadWorkEnded")]
{
if (assembly == null)
{
assembly = Assembly.GetCallingAssembly();
}
var info = assembly.GetName();
var name = info.Name;
using var stream = assembly.GetManifestResourceStream($"{name}.{path.Replace('/', '.')}")!;
var buffer = new byte[stream.Length];
_ = stream.Read(buffer, 0, buffer.Length);
return buffer;
}
private static Texture2D LoadEmbeddedTexture(string path, Assembly assembly = null)
{
var fileData = LoadEmbeddedResource(path, assembly);
var tex = new Texture2D(2, 2);
tex.LoadImage(fileData);
return tex;
}
private static Sprite LoadEmbeddedSprite(string path, Assembly assembly = null)
{
var tex = LoadEmbeddedTexture(path, assembly);
return Sprite.Create(tex, new Rect(0, 0, tex.width, tex.height), new Vector2(0.5f, 0.5f));
}
[HarmonyPostfix, HarmonyPriority(Priority.Last)]
[HarmonyPatch(typeof(VFPreload), nameof(VFPreload.InvokeOnLoadWorkEnded))]
private static void VFPreload_InvokeOnLoadWorkEnded_Postfix() private static void VFPreload_InvokeOnLoadWorkEnded_Postfix()
{ {
if (_initialized) return; if (_initialized) return;
var assembly = Assembly.GetExecutingAssembly(); var pluginfolder = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
_bundle = AssetBundle.LoadFromFile($"{pluginfolder}/dustbin.assetbundle");
var signals = LDB._signals; var signals = LDB._signals;
var index = signals.dataArray.Length; var index = signals.dataArray.Length;
var p = new SignalProto var p = new SignalProto
@@ -73,7 +47,7 @@ public static class BeltSignal
Name = "DUSTBIN", Name = "DUSTBIN",
GridIndex = 3110, GridIndex = 3110,
IconPath = "Assets/signal-410.png", IconPath = "Assets/signal-410.png",
_iconSprite = LoadEmbeddedSprite("assets/icon/signal-410.png", assembly), _iconSprite = _bundle.LoadAsset<Sprite>("Assets/signal-410.png"),
SID = "" SID = ""
}; };
p.name = p.Name.Translate(); p.name = p.Name.Translate();
@@ -198,14 +172,19 @@ public static class BeltSignal
} }
[HarmonyTranspiler] [HarmonyTranspiler]
[HarmonyPatch(typeof(GameData), nameof(GameData.GameTick))] [HarmonyPatch(typeof(GameData), "GameTick")]
public static IEnumerable<CodeInstruction> GameData_GameTick_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator) public static IEnumerable<CodeInstruction> GameData_GameTick_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{ {
var matcher = new CodeMatcher(instructions, generator); var matcher = new CodeMatcher(instructions, generator);
matcher.MatchForward(false, matcher.MatchForward(false,
new CodeMatch(OpCodes.Call, AccessTools.Method(typeof(PerformanceMonitor), nameof(PerformanceMonitor.EndSample))) new CodeMatch(OpCodes.Call, AccessTools.Method(typeof(PerformanceMonitor), nameof(PerformanceMonitor.EndSample)))
).Advance(1).Insert( ).Advance(1).Insert(
Transpilers.EmitDelegate(() => new CodeInstruction(OpCodes.Call, AccessTools.Method(typeof(BeltSignal), nameof(ProcessBeltSignals)))
);
return matcher.InstructionEnumeration();
}
public static void ProcessBeltSignals()
{ {
var factories = GameMain.data?.factories; var factories = GameMain.data?.factories;
if (factories == null) return; if (factories == null) return;
@@ -228,8 +207,5 @@ public static class BeltSignal
Dustbin.CalcGetSands(itemId, stack); Dustbin.CalcGetSands(itemId, stack);
} }
} }
})
);
return matcher.InstructionEnumeration();
} }
} }

View File

@@ -1,67 +0,0 @@
## Changelog
* 1.4.0
* Refactorying Tank input logic codes, for better performance, and resolve a bug [#53](https://github.com/soarqin/DSP_Mods/issues/53)
+ Remove use of AssetBundle, move the belt signal icon into `Assembly Resources`, for better flexibility.
* 1.3.3
+ Support for NebulaMultiplayerModApi 2.0.0
* 1.3.2
+ Fix a display issue that the dustbin checkbox is overlapped with the filter button in storage UI.
* 1.3.1
+ Support for game version 0.10.28.20759
* 1.3.0
+ Add a belt signal(you can find it in first tab of signal selection panel) as dustbin, which is the simplest way to destroy items.
+ Reworked dustbin support for Tanks, to improve performance and resolve known bugs.
- Be note that the whole tank logic is optimized which may get a slight better performance even if you don't use them as dustbin.
+ Config entry for soil piless gain from destroyed items are changed to a more flexible format.
+ [Nebula Mupltiplayer Mod](https://dsp.thunderstore.io/package/nebula/NebulaMultiplayerMod/) and bug fixes from [ModFixerOne](https://dsp.thunderstore.io/package/starfi5h/ModFixerOne/) by [starfi5h](https://github.com/starfi5h/).
* 1.2.1
+ Fix dynamic array bug in codes, which causes various bugs and errors.
* 1.2.0
+ Use [DSPModSave](https://dsp.thunderstore.io/package/CommonAPI/DSPModSave/) to save dustbin specified data now, which fixes [#1](https://github.com/soarqin/DSP_Mods/issues/1).
+ Fix issue for storages on multiple planets.
+ Fix issue for multi-level tanks.
+ Add a note in README for known bug on tank.
* 1.1.0
+ Rewrite whole plugin, make a checkbox on UI so that you can turn storages into dustbin by just ticking it.
+ Can turn tank into dustbin now.
## 更新日志
* 1.4.0
+ 重构储液罐的输入逻辑代码以提高性能并解决bug [#53](https://github.com/soarqin/DSP_Mods/issues/53)
+ 移除了AssetBundle的使用将传送带信号图标移入`Assembly资源`,以获得更好的灵活性
* 1.3.3
+ 支持NebulaMultiplayerModApi 2.0.0
* 1.3.2
+ 修正了储物仓UI中的垃圾桶勾选框与筛选按钮重叠的显示问题
* 1.3.1
+ 支持游戏版本 0.10.28.20759
* 1.3.0
+ 添加了一个传送带信号(可以在信号选择面板的第一个页签中找到)作为垃圾桶,这是目前销毁物品最简单的方法
+ 重写了储液罐的垃圾桶实现以提高性能并解决已知的bug
- 注意:整个储液罐逻辑都被优化了,即使你不把他们作为垃圾桶使用,也可能会获得轻微的性能提升
+ 从销毁的物品中获得沙子的配置已变为更灵活的设置项格式
+ [Nebula Mupltiplayer Mod](https://dsp.thunderstore.io/package/nebula/NebulaMultiplayerMod/)支持和Bug修正来自[starfi5h](https://github.com/starfi5h/)的[ModFixerOne](https://dsp.thunderstore.io/package/starfi5h/ModFixerOne/)
* 1.2.1
+ 修正了代码中的动态数组Bug该Bug可能导致各种问题
* 1.2.0
+ 现在使用[DSPModSave](https://dsp.thunderstore.io/package/CommonAPI/DSPModSave/)来保存垃圾桶的数据,修正了[#1](https://github.com/soarqin/DSP_Mods/issues/1)
+ 修正了多星球上的储物仓问题
+ 修正了多层储液罐的问题
+ 在README中添加了一个已知储液罐Bug的说明
* 1.1.0
+ 重写了整个插件现在可以在仓储类建筑的UI上勾选来将其转变为垃圾桶
+ 现在可以将储液罐转变为垃圾桶

View File

@@ -5,13 +5,12 @@ using System.Runtime.CompilerServices;
using BepInEx; using BepInEx;
using crecheng.DSPModSave; using crecheng.DSPModSave;
using NebulaAPI; using NebulaAPI;
using NebulaAPI.Interfaces;
namespace Dustbin; namespace Dustbin;
[BepInPlugin(PluginInfo.PLUGIN_GUID, PluginInfo.PLUGIN_NAME, PluginInfo.PLUGIN_VERSION)] [BepInPlugin(PluginInfo.PLUGIN_GUID, PluginInfo.PLUGIN_NAME, PluginInfo.PLUGIN_VERSION)]
[BepInDependency(DSPModSavePlugin.MODGUID)] [BepInDependency(DSPModSavePlugin.MODGUID)]
[BepInDependency(NebulaModAPI.API_GUID, BepInDependency.DependencyFlags.SoftDependency)] [BepInDependency(NebulaModAPI.API_GUID)]
public class Dustbin : BaseUnityPlugin, IModCanSave, IMultiplayerMod public class Dustbin : BaseUnityPlugin, IModCanSave, IMultiplayerMod
{ {
public string Version => PluginInfo.PLUGIN_VERSION; public string Version => PluginInfo.PLUGIN_VERSION;
@@ -31,7 +30,7 @@ public class Dustbin : BaseUnityPlugin, IModCanSave, IMultiplayerMod
var storageDustbin = Config.Bind("General", "StorageDustbin", true, "Can turn storages into dustbins").Value; var storageDustbin = Config.Bind("General", "StorageDustbin", true, "Can turn storages into dustbins").Value;
var tankDustbin = Config.Bind("General", "TankDustbin", true, "Can turn tanks into dustbins").Value; var tankDustbin = Config.Bind("General", "TankDustbin", true, "Can turn tanks into dustbins").Value;
var belgSignalDustbin = Config.Bind("General", "BeltSignalDustbin", true, "Add belt signal as dustbin").Value; var belgSignalDustbin = Config.Bind("General", "BeltSignalDustbin", true, "Add belt signal as dustbin").Value;
var sandsFactorsStr = Config.Bind("General", "SandsFactors", "", "Soil piles get from different items\nFormat: id1:value1|id2:value2|...").Value; var sandsFactorsStr = Config.Bind("General", "SandsFactors", "", "Sands get from different items\nFormat: id1:value1|id2:value2|...").Value;
foreach (var s in sandsFactorsStr.Split('|')) foreach (var s in sandsFactorsStr.Split('|'))
{ {
var sp = s.Split(':'); var sp = s.Split(':');
@@ -42,13 +41,16 @@ public class Dustbin : BaseUnityPlugin, IModCanSave, IMultiplayerMod
} }
if (storageDustbin) StoragePatch.Enable(true); if (storageDustbin) StoragePatch.Enable(true);
if (tankDustbin) TankPatch.Enable(true); if (tankDustbin) TankPatch.Enable(true);
if (belgSignalDustbin) BeltSignal.Enable(true);
if (!BepInEx.Bootstrap.Chainloader.PluginInfos.ContainsKey("dsp.nebula-multiplayer") || (!storageDustbin && !tankDustbin)) return; if (storageDustbin || tankDustbin)
{
NebulaModAPI.RegisterPackets(Assembly.GetExecutingAssembly()); NebulaModAPI.RegisterPackets(Assembly.GetExecutingAssembly());
NebulaModAPI.OnPlanetLoadFinished += RequestPlanetDustbinData; NebulaModAPI.OnPlanetLoadFinished += RequestPlanetDustbinData;
} }
if (belgSignalDustbin) BeltSignal.Enable(true);
}
private void OnDestroy() private void OnDestroy()
{ {
TankPatch.Enable(false); TankPatch.Enable(false);
@@ -176,7 +178,7 @@ public class Dustbin : BaseUnityPlugin, IModCanSave, IMultiplayerMod
public void RequestPlanetDustbinData(int planetId) public void RequestPlanetDustbinData(int planetId)
{ {
if (NebulaModAPI.IsMultiplayerActive && NebulaModAPI.MultiplayerSession.IsClient) if (NebulaModAPI.IsMultiplayerActive && NebulaModAPI.MultiplayerSession.LocalPlayer.IsClient)
NebulaModAPI.MultiplayerSession.Network.SendPacket(new NebulaSupport.Packet.ToggleEvent(planetId, 0, false)); NebulaModAPI.MultiplayerSession.Network.SendPacket(new NebulaSupport.Packet.ToggleEvent(planetId, 0, false));
} }
#endregion #endregion

View File

@@ -5,7 +5,7 @@
<TargetFramework>net472</TargetFramework> <TargetFramework>net472</TargetFramework>
<BepInExPluginGuid>org.soardev.dustbin</BepInExPluginGuid> <BepInExPluginGuid>org.soardev.dustbin</BepInExPluginGuid>
<Description>DSP MOD - Dustbin</Description> <Description>DSP MOD - Dustbin</Description>
<Version>1.4.0</Version> <Version>1.3.2</Version>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<LangVersion>latest</LangVersion> <LangVersion>latest</LangVersion>
<PackageId>Dustbin</PackageId> <PackageId>Dustbin</PackageId>
@@ -18,12 +18,9 @@
<PackageReference Include="BepInEx.PluginInfoProps" Version="1.*" /> <PackageReference Include="BepInEx.PluginInfoProps" Version="1.*" />
<PackageReference Include="DysonSphereProgram.GameLibs" Version="*-r.*" /> <PackageReference Include="DysonSphereProgram.GameLibs" Version="*-r.*" />
<PackageReference Include="DysonSphereProgram.Modding.DSPModSave" Version="1.*" /> <PackageReference Include="DysonSphereProgram.Modding.DSPModSave" Version="1.*" />
<PackageReference Include="DysonSphereProgram.Modding.NebulaMultiplayerModApi" Version="2.0.0" /> <PackageReference Include="DysonSphereProgram.Modding.NebulaMultiplayerModApi" Version="1.3.1" />
<PackageReference Include="UnityEngine.Modules" Version="2018.4.12" IncludeAssets="compile" /> <PackageReference Include="UnityEngine.Modules" Version="2018.4.12" IncludeAssets="compile" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<EmbeddedResource Include="assets/icon/signal-410.png" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework.TrimEnd(`0123456789`))' == 'net'"> <ItemGroup Condition="'$(TargetFramework.TrimEnd(`0123456789`))' == 'net'">
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.3" PrivateAssets="all" /> <PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.3" PrivateAssets="all" />
@@ -35,7 +32,7 @@
</Reference> </Reference>
</ItemGroup> </ItemGroup>
<Target Name="PostBuild" AfterTargets="PostBuildEvent" Condition="'$(Configuration)' == 'Release'"> <Target Name="PostBuild" AfterTargets="PostBuildEvent">
<Exec Command="del /F /Q package\$(ProjectName)-$(Version).zip&#xA;IF NOT EXIST package\plugins (mkdir package\plugins)&#xA;copy /y $(TargetPath) package\plugins\&#xA;cd package&#xA;powershell Compress-Archive -Force -DestinationPath '$(ProjectName)-$(Version).zip' -Path patchers, plugins, icon.png, manifest.json, ../README.md, ../CHANGELOG.md" /> <Exec Command="del /F /Q package\$(ProjectName)-$(Version).zip&#xA;IF NOT EXIST package\plugins (mkdir package\plugins)&#xA;copy /y $(TargetPath) package\plugins\&#xA;zip -9 -j package/$(ProjectName)-$(Version).zip package/icon.png package/manifest.json README.md&#xA;cd package&#xA;zip -9 -r $(ProjectName)-$(Version).zip patchers plugins" />
</Target> </Target>
</Project> </Project>

View File

@@ -1,6 +1,4 @@
using NebulaAPI; using NebulaAPI;
using NebulaAPI.Networking;
using NebulaAPI.Packets;
namespace Dustbin.NebulaSupport namespace Dustbin.NebulaSupport
{ {

View File

@@ -3,13 +3,67 @@
#### Can turn Storages and Tanks into Dustbin(Destroy incoming items) #### Can turn Storages and Tanks into Dustbin(Destroy incoming items)
#### 储物仓和储液罐可以转变为垃圾桶(销毁送进的物品) #### 储物仓和储液罐可以转变为垃圾桶(销毁送进的物品)
## Changelog
* 1.3.2
+ Fix a display issue that the dustbin checkbox is overlapped with the filter button in storage UI.
* 1.3.1
+ Support for game version 0.10.28.20759
* 1.3.0
+ Add a belt signal(you can find it in first tab of signal selection panel) as dustbin, which is the simplest way to destroy items.
+ Reworked dustbin support for Tanks, to improve performance and resolve known bugs.
- Be note that the whole tank logic is optimized which may get a slight better performance even if you don't use them as dustbin.
+ Config entry for sands gain from destroyed items are changed to a more flexible format.
+ [Nebula Mupltiplayer Mod](https://dsp.thunderstore.io/package/nebula/NebulaMultiplayerMod/) and bug fixes from [ModFixerOne](https://dsp.thunderstore.io/package/starfi5h/ModFixerOne/) by [starfi5h](https://github.com/starfi5h/).
* 1.2.1
+ Fix dynamic array bug in codes, which causes various bugs and errors.
* 1.2.0
+ Use [DSPModSave](https://dsp.thunderstore.io/package/CommonAPI/DSPModSave/) to save dustbin specified data now, which fixes [#1](https://github.com/soarqin/DSP_Mods/issues/1).
+ Fix issue for storages on multiple planets.
+ Fix issue for multi-level tanks.
+ Add a note in README for known bug on tank.
* 1.1.0
+ Rewrite whole plugin, make a checkbox on UI so that you can turn storages into dustbin by just ticking it.
+ Can turn tank into dustbin now.
## Usage ## Usage
* A checkbox is added to Storages and Tanks UI, which turns them into dustbins. * A checkbox is added to Storages and Tanks UI, which turns them into dustbins.
* Items sent into dustbins are removed immediately. * Items sent into dustbins are removed immediately.
* Can get soil piless from destroyed items, configurable through a json encoded config entry. * Can get sands from destroyed items, configurable through a json encoded config entry.
+ You can get item ID list [here](https://dsp-wiki.com/Modding:Items_IDs). + You can get item ID list [here](https://dsp-wiki.com/Modding:Items_IDs).
## 更新日志
* 1.3.2
+ 修正了储物仓UI中的垃圾桶勾选框与筛选按钮重叠的显示问题
* 1.3.1
+ 支持游戏版本 0.10.28.20759
* 1.3.0
+ 添加了一个传送带信号(可以在信号选择面板的第一个页签中找到)作为垃圾桶,这是目前销毁物品最简单的方法
+ 重写了储液罐的垃圾桶实现以提高性能并解决已知的bug
- 注意:整个储液罐逻辑都被优化了,即使你不把他们作为垃圾桶使用,也可能会获得轻微的性能提升
+ 从销毁的物品中获得沙子的配置已变为更灵活的设置项格式
+ [Nebula Mupltiplayer Mod](https://dsp.thunderstore.io/package/nebula/NebulaMultiplayerMod/)支持和Bug修正来自[starfi5h](https://github.com/starfi5h/)的[ModFixerOne](https://dsp.thunderstore.io/package/starfi5h/ModFixerOne/)
* 1.2.1
+ 修正了代码中的动态数组Bug该Bug可能导致各种问题
* 1.2.0
+ 现在使用[DSPModSave](https://dsp.thunderstore.io/package/CommonAPI/DSPModSave/)来保存垃圾桶的数据,修正了[#1](https://github.com/soarqin/DSP_Mods/issues/1)
+ 修正了多星球上的储物仓问题
+ 修正了多层储液罐的问题
+ 在README中添加了一个已知储液罐Bug的说明
* 1.1.0
+ 重写了整个插件现在可以在仓储类建筑的UI上勾选来将其转变为垃圾桶
+ 现在可以将储液罐转变为垃圾桶
## 使用说明 ## 使用说明
* 在储物仓和储液罐上增加一个垃圾桶的勾选框。 * 在储物仓和储液罐上增加一个垃圾桶的勾选框。

View File

@@ -96,14 +96,14 @@ public static class StoragePatch
} }
[HarmonyPrefix] [HarmonyPrefix]
[HarmonyPatch(typeof(GameMain), nameof(GameMain.Start))] [HarmonyPatch(typeof(GameMain), "Start")]
private static void GameMain_Start_Prefix() private static void GameMain_Start_Prefix()
{ {
Reset(); Reset();
} }
[HarmonyPostfix] [HarmonyPostfix]
[HarmonyPatch(typeof(UIStorageWindow), nameof(UIStorageWindow._OnCreate))] [HarmonyPatch(typeof(UIStorageWindow), "_OnCreate")]
private static void UIStorageWindow__OnCreate_Postfix(UIStorageWindow __instance) private static void UIStorageWindow__OnCreate_Postfix(UIStorageWindow __instance)
{ {
_storageDustbinCheckBox = UI.MyCheckBox.CreateCheckBox(false, __instance.transform, 50f, 50f, Localization.CurrentLanguageLCID == Localization.LCID_ZHCN ? "垃圾桶" : "Dustbin"); _storageDustbinCheckBox = UI.MyCheckBox.CreateCheckBox(false, __instance.transform, 50f, 50f, Localization.CurrentLanguageLCID == Localization.LCID_ZHCN ? "垃圾桶" : "Dustbin");
@@ -123,7 +123,7 @@ public static class StoragePatch
} }
[HarmonyPostfix] [HarmonyPostfix]
[HarmonyPatch(typeof(UIStorageWindow), nameof(UIStorageWindow._OnUpdate))] [HarmonyPatch(typeof(UIStorageWindow), "_OnUpdate")]
private static void UIStorageWindow__OnUpdate_Postfix(UIStorageWindow __instance) private static void UIStorageWindow__OnUpdate_Postfix(UIStorageWindow __instance)
{ {
var storageId = __instance.storageId; var storageId = __instance.storageId;
@@ -141,7 +141,7 @@ public static class StoragePatch
/* Adopt fix from starfi5h's NebulaCompatiblilityAssist */ /* Adopt fix from starfi5h's NebulaCompatiblilityAssist */
[HarmonyPostfix] [HarmonyPostfix]
[HarmonyPatch(typeof(UIStorageGrid), nameof(UIStorageGrid.OnStorageSizeChanged))] [HarmonyPatch(typeof(UIStorageGrid), "OnStorageSizeChanged")]
private static void UIStorageGrid_OnStorageSizeChanged_Postfix() private static void UIStorageGrid_OnStorageSizeChanged_Postfix()
{ {
// Due to storage window is empty when open in client, the size will change after receiving data from host // Due to storage window is empty when open in client, the size will change after receiving data from host
@@ -149,8 +149,8 @@ public static class StoragePatch
} }
[HarmonyTranspiler] [HarmonyTranspiler]
[HarmonyPatch(typeof(StorageComponent), nameof(StorageComponent.AddItem), [typeof(int), typeof(int), typeof(int), typeof(int), typeof(bool)], [HarmonyPatch(typeof(StorageComponent), "AddItem", new[] { typeof(int), typeof(int), typeof(int), typeof(int), typeof(bool) },
[ArgumentType.Normal, ArgumentType.Normal, ArgumentType.Normal, ArgumentType.Out, ArgumentType.Normal])] new[] { ArgumentType.Normal, ArgumentType.Normal, ArgumentType.Normal, ArgumentType.Out, ArgumentType.Normal })]
private static IEnumerable<CodeInstruction> StorageComponent_AddItem_HarmonyTranspiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator) private static IEnumerable<CodeInstruction> StorageComponent_AddItem_HarmonyTranspiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{ {
var matcher = new CodeMatcher(instructions, generator); var matcher = new CodeMatcher(instructions, generator);
@@ -172,7 +172,7 @@ public static class StoragePatch
/* We keep this to make MOD compatible with older version */ /* We keep this to make MOD compatible with older version */
[HarmonyPostfix] [HarmonyPostfix]
[HarmonyPatch(typeof(StorageComponent), nameof(StorageComponent.Import))] [HarmonyPatch(typeof(StorageComponent), "Import")]
private static void StorageComponent_Import_Postfix(StorageComponent __instance) private static void StorageComponent_Import_Postfix(StorageComponent __instance)
{ {
if (__instance.bans >= 0) if (__instance.bans >= 0)

View File

@@ -100,14 +100,14 @@ public static class TankPatch
} }
[HarmonyPrefix] [HarmonyPrefix]
[HarmonyPatch(typeof(GameMain), nameof(GameMain.Start))] [HarmonyPatch(typeof(GameMain), "Start")]
private static void GameMain_Start_Prefix() private static void GameMain_Start_Prefix()
{ {
Reset(); Reset();
} }
[HarmonyPostfix] [HarmonyPostfix]
[HarmonyPatch(typeof(UITankWindow), nameof(UITankWindow._OnCreate))] [HarmonyPatch(typeof(UITankWindow), "_OnCreate")]
private static void UITankWindow__OnCreate_Postfix(UITankWindow __instance) private static void UITankWindow__OnCreate_Postfix(UITankWindow __instance)
{ {
_tankDustbinCheckBox = UI.MyCheckBox.CreateCheckBox(false, __instance.transform, 120f, 20f, Localization.CurrentLanguageLCID == Localization.LCID_ZHCN ? "垃圾桶" : "Dustbin"); _tankDustbinCheckBox = UI.MyCheckBox.CreateCheckBox(false, __instance.transform, 120f, 20f, Localization.CurrentLanguageLCID == Localization.LCID_ZHCN ? "垃圾桶" : "Dustbin");
@@ -127,7 +127,7 @@ public static class TankPatch
} }
[HarmonyPostfix] [HarmonyPostfix]
[HarmonyPatch(typeof(UITankWindow), nameof(UITankWindow._OnUpdate))] [HarmonyPatch(typeof(UITankWindow), "_OnUpdate")]
private static void UITankWindow__OnUpdate_Postfix(UITankWindow __instance) private static void UITankWindow__OnUpdate_Postfix(UITankWindow __instance)
{ {
var tankId = __instance.tankId; var tankId = __instance.tankId;
@@ -142,7 +142,7 @@ public static class TankPatch
} }
[HarmonyPrefix] [HarmonyPrefix]
[HarmonyPatch(typeof(TankComponent), nameof(TankComponent.GameTick))] [HarmonyPatch(typeof(TankComponent), "GameTick")]
private static bool TankComponent_GameTick_Prefix(ref TankComponent __instance, PlanetFactory factory) private static bool TankComponent_GameTick_Prefix(ref TankComponent __instance, PlanetFactory factory)
{ {
if (__instance.fluidInc < 0) if (__instance.fluidInc < 0)
@@ -183,68 +183,94 @@ public static class TankPatch
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void TankComponentUpdateBelt(ref TankComponent thisTank, int belt, bool isOutput, ref CargoTraffic cargoTraffic, ref TankComponent[] tankPool) private static void TankComponentUpdateBelt(ref TankComponent thisTank, int belt, bool isOutput, ref CargoTraffic cargoTraffic, ref TankComponent[] tankPool)
{ {
if (isOutput) switch (isOutput)
{ {
if (thisTank.outputSwitch) case true when thisTank.outputSwitch:
{ {
if (thisTank.fluidId <= 0 || thisTank.fluidCount <= 0) return; if (thisTank.fluidId <= 0 || thisTank.fluidCount <= 0) return;
var inc = thisTank.fluidInc == 0 ? 0 : thisTank.fluidInc / thisTank.fluidCount; var inc = thisTank.fluidInc == 0 ? 0 : thisTank.fluidInc / thisTank.fluidCount;
if (!cargoTraffic.TryInsertItemAtHead(belt, thisTank.fluidId, 1, (byte)inc)) return; if (!cargoTraffic.TryInsertItemAtHead(belt, thisTank.fluidId, 1, (byte)inc)) return;
thisTank.fluidCount--; thisTank.fluidCount--;
thisTank.fluidInc -= inc; thisTank.fluidInc -= inc;
return;
} }
} case false when thisTank.inputSwitch:
else
{
if (thisTank.inputSwitch)
{ {
byte stack; byte stack;
byte inc; byte inc;
var thisFluidId = thisTank.fluidId; switch (thisTank.fluidId)
if (thisTank.fluidCount <= 0)
{ {
var fluidId = cargoTraffic.TryPickItemAtRear(belt, 0, ItemProto.fluids, out stack, out inc); case > 0 when thisTank.fluidCount < thisTank.fluidCapacity && cargoTraffic.TryPickItemAtRear(belt, thisTank.fluidId, null, out stack, out inc) > 0 &&
if (fluidId <= 0 || thisTank.IsDustbin) return; !thisTank.IsDustbin:
thisTank.fluidId = fluidId; thisTank.fluidCount += stack;
thisTank.fluidCount = stack; thisTank.fluidInc += inc;
thisTank.fluidInc = inc;
return; return;
} case 0:
if (thisTank.fluidCount < thisTank.fluidCapacity || thisTank.IsDustbin)
{ {
if (cargoTraffic.GetItemIdAtRear(belt) != thisFluidId || thisTank.nextTankId <= 0) return; var count = cargoTraffic.TryPickItemAtRear(belt, 0, ItemProto.fluids, out stack, out inc);
if (cargoTraffic.TryPickItemAtRear(belt, thisFluidId, null, out stack, out inc) <= 0 || thisTank.IsDustbin) return; if (count <= 0 || thisTank.IsDustbin) return;
thisTank.fluidId = count;
thisTank.fluidCount += stack; thisTank.fluidCount += stack;
thisTank.fluidInc += inc; thisTank.fluidInc += inc;
return; return;
} }
if (thisTank.nextTankId <= 0) return; }
if (thisTank.fluidCount < thisTank.fluidCapacity || cargoTraffic.GetItemIdAtRear(belt) != thisTank.fluidId || thisTank.nextTankId <= 0) return;
ref var targetTank = ref tankPool[thisTank.nextTankId]; ref var targetTank = ref tankPool[thisTank.nextTankId];
while (true) while (targetTank.fluidCount >= targetTank.fluidCapacity)
{ {
if (targetTank.fluidCount < targetTank.fluidCapacity || targetTank.IsDustbin) ref var lastTank = ref tankPool[targetTank.lastTankId];
if (targetTank.fluidId != lastTank.fluidId)
{ {
if (!targetTank.inputSwitch) return; if (lastTank.id == thisTank.id || targetTank.fluidCount >= targetTank.fluidCapacity) return;
var targetFluidId = targetTank.fluidId; targetTank = ref lastTank;
if (targetFluidId != 0 && targetFluidId != thisFluidId) return;
break; break;
} }
if (targetTank.nextTankId <= 0) return;
if (!targetTank.inputSwitch)
{
if (lastTank.id == thisTank.id || targetTank.fluidCount >= targetTank.fluidCapacity) return;
targetTank = ref lastTank;
break;
}
if (targetTank.nextTankId <= 0)
{
break;
}
targetTank = ref tankPool[targetTank.nextTankId]; targetTank = ref tankPool[targetTank.nextTankId];
} }
if (cargoTraffic.TryPickItemAtRear(belt, thisFluidId, null, out stack, out inc) <= 0 || targetTank.IsDustbin) return; ref var lastTank2 = ref tankPool[targetTank.lastTankId];
if (targetTank.fluidCount <= 0) if (!targetTank.inputSwitch)
{ {
targetTank.fluidId = thisFluidId; targetTank = ref lastTank2;
targetTank.fluidCount = stack;
targetTank.fluidInc = inc;
} }
else else
{ {
var fluidId = targetTank.fluidId;
if (fluidId != 0 && fluidId != lastTank2.fluidId)
{
if (lastTank2.id == thisTank.id || targetTank.fluidCount >= targetTank.fluidCapacity) return;
targetTank = ref lastTank2;
}
else if (!lastTank2.outputSwitch)
{
return;
}
}
if (cargoTraffic.TryPickItemAtRear(belt, thisTank.fluidId, null, out stack, out inc) <= 0 || targetTank.IsDustbin) return;
if (targetTank.fluidCount == 0)
{
targetTank.fluidId = thisTank.fluidId;
}
targetTank.fluidCount += stack; targetTank.fluidCount += stack;
targetTank.fluidInc += inc; targetTank.fluidInc += inc;
} return;
} }
} }
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

View File

@@ -1,11 +1,11 @@
{ {
"name": "Dustbin", "name": "Dustbin",
"version_number": "1.4.0", "version_number": "1.3.2",
"website_url": "https://github.com/soarqin/DSP_Mods/tree/master/Dustbin", "website_url": "https://github.com/soarqin/DSP_Mods/tree/master/Dustbin",
"description": "Can turn Storages and Tanks into Dustbin(Destroy incoming items) / 储物仓和储液罐可以转变为垃圾桶(销毁送进的物品)", "description": "Can turn Storages and Tanks into Dustbin(Destroy incoming items) / 储物仓和储液罐可以转变为垃圾桶(销毁送进的物品)",
"dependencies": [ "dependencies": [
"xiaoye97-BepInEx-5.4.17", "xiaoye97-BepInEx-5.4.17",
"CommonAPI-DSPModSave-1.1.4", "CommonAPI-DSPModSave-1.1.4",
"nebula-NebulaMultiplayerModApi-2.0.0" "nebula-NebulaMultiplayerModApi-1.3.1"
] ]
} }

Binary file not shown.

View File

@@ -22,7 +22,7 @@
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.3" PrivateAssets="all" /> <PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.3" PrivateAssets="all" />
</ItemGroup> </ItemGroup>
<Target Name="PostBuild" AfterTargets="PostBuildEvent" Condition="'$(Configuration)' == 'Release'"> <Target Name="PostBuild" AfterTargets="PostBuildEvent">
<Exec Command="IF NOT EXIST ..\Dustbin\package\patchers (mkdir ..\Dustbin\package\patchers)&#xA;copy /y $(TargetPath) ..\Dustbin\package\patchers\" /> <Exec Command="IF NOT EXIST ..\Dustbin\package\patchers (mkdir ..\Dustbin\package\patchers)&#xA;copy /y $(TargetPath) ..\Dustbin\package\patchers\" />
</Target> </Target>
</Project> </Project>

View File

@@ -20,7 +20,7 @@ public class HideTips : BaseUnityPlugin
private static bool _noResearchCompletionPopups = true; private static bool _noResearchCompletionPopups = true;
private static bool _noResearchCompletionTips; private static bool _noResearchCompletionTips;
private static bool _skipPrologue = true; private static bool _skipPrologue = true;
// private static bool _hideMenuDemo; private static bool _hideMenuDemo;
private static Harmony _patch; private static Harmony _patch;
@@ -34,15 +34,13 @@ public class HideTips : BaseUnityPlugin
_noResearchCompletionPopups = Config.Bind("General", "NoResearchCompletionPopups", _noResearchCompletionPopups, "Disable Research Completion Popup Windows").Value; _noResearchCompletionPopups = Config.Bind("General", "NoResearchCompletionPopups", _noResearchCompletionPopups, "Disable Research Completion Popup Windows").Value;
_noResearchCompletionTips = Config.Bind("General", "NoResearchCompletionTips", _noResearchCompletionTips, "Disable Research Completion Tips").Value; _noResearchCompletionTips = Config.Bind("General", "NoResearchCompletionTips", _noResearchCompletionTips, "Disable Research Completion Tips").Value;
_skipPrologue = Config.Bind("General", "SkipPrologue", _skipPrologue, "Skip prologue for new game").Value; _skipPrologue = Config.Bind("General", "SkipPrologue", _skipPrologue, "Skip prologue for new game").Value;
// _hideMenuDemo = Config.Bind("General", "HideMenuDemo", _hideMenuDemo, "Disable title screen demo scene loading").Value; _hideMenuDemo = Config.Bind("General", "HideMenuDemo", _hideMenuDemo, "Disable title screen demo scene loading").Value;
if (!_cfgEnabled) return; if (!_cfgEnabled) return;
Harmony.CreateAndPatchAll(typeof(HideTips)); Harmony.CreateAndPatchAll(typeof(HideTips));
/*
if (_hideMenuDemo) if (_hideMenuDemo)
{ {
_patch = Harmony.CreateAndPatchAll(typeof(HideMenuDemo)); _patch = Harmony.CreateAndPatchAll(typeof(HideMenuDemo));
} }
*/
} }
private void OnDestroy() private void OnDestroy()
@@ -59,7 +57,7 @@ public class HideTips : BaseUnityPlugin
} }
[HarmonyPostfix] [HarmonyPostfix]
[HarmonyPatch(typeof(UIBuildMenu), nameof(UIBuildMenu._OnCreate))] [HarmonyPatch(typeof(UIBuildMenu), "_OnCreate")]
private static void ClearRandReminderTips(UIBuildMenu __instance) private static void ClearRandReminderTips(UIBuildMenu __instance)
{ {
if (!_noRandomReminderTips) return; if (!_noRandomReminderTips) return;
@@ -70,52 +68,48 @@ public class HideTips : BaseUnityPlugin
randTip._Free(); randTip._Free();
} }
} }
__instance.randRemindTips = Array.Empty<UIRandomTip>(); __instance.randRemindTips = Array.Empty<UIRandomTip>();
} }
[HarmonyPostfix] [HarmonyPostfix]
[HarmonyPatch(typeof(UIGameMenu), nameof(UIGameMenu._OnCreate))] [HarmonyPatch(typeof(UIGameMenu), "_OnCreate")]
private static void UIGameMenu__OnCreate_Postfix(UIGameMenu __instance) private static void UIGameMenu__OnCreate_Postfix(UIGameMenu __instance)
{ {
if (!_noRandomReminderTips) return;
__instance.randTipButton0.pop = __instance.randTipButton0.popCount; __instance.randTipButton0.pop = __instance.randTipButton0.popCount;
__instance.randTipButton1.pop = __instance.randTipButton1.popCount; __instance.randTipButton1.pop = __instance.randTipButton1.popCount;
__instance.randTipButton2.pop = __instance.randTipButton2.popCount; __instance.randTipButton2.pop = __instance.randTipButton2.popCount;
} }
[HarmonyPrefix] [HarmonyPrefix]
[HarmonyPatch(typeof(UITutorialTip), nameof(UITutorialTip.PopupTutorialTip))] [HarmonyPatch(typeof(UITutorialTip), "PopupTutorialTip")]
private static bool UITutorialTip_PopupTutorialTip_Prefix(int tutorialId) private static bool UITutorialTip_PopupTutorialTip_Prefix()
{ {
if (!_noTutorialTips) return true; return !_noTutorialTips;
GameMain.history.UnlockTutorial(tutorialId);
return false;
} }
[HarmonyPrefix] [HarmonyPrefix]
[HarmonyPatch(typeof(UIVariousPopupGroup), nameof(UIVariousPopupGroup.CreateAchievementPopupCard))] [HarmonyPatch(typeof(UIVariousPopupGroup), "CreateAchievementPopupCard")]
private static bool UIVariousPopupGroup_CreateAchievementPopupCard_Prefix() private static bool UIVariousPopupGroup_CreateAchievementPopupCard_Prefix()
{ {
return !_noAchievementCardPopups; return !_noAchievementCardPopups;
} }
[HarmonyPrefix] [HarmonyPrefix]
[HarmonyPatch(typeof(UIVariousPopupGroup), nameof(UIVariousPopupGroup.CreateMilestonePopupCard))] [HarmonyPatch(typeof(UIVariousPopupGroup), "CreateMilestonePopupCard")]
private static bool UIVariousPopupGroup_CreateMilestonePopupCard_Prefix() private static bool UIVariousPopupGroup_CreateMilestonePopupCard_Prefix()
{ {
return !_noMilestoneCardPopups; return !_noMilestoneCardPopups;
} }
[HarmonyPrefix] [HarmonyPrefix]
[HarmonyPatch(typeof(UIResearchResultWindow), nameof(UIResearchResultWindow.SetTechId))] [HarmonyPatch(typeof(UIResearchResultWindow), "SetTechId")]
private static bool UIResearchResultWindow_SetTechId_Prefix() private static bool UIResearchResultWindow_SetTechId_Prefix()
{ {
return !_noResearchCompletionPopups; return !_noResearchCompletionPopups;
} }
[HarmonyTranspiler] [HarmonyTranspiler]
[HarmonyPatch(typeof(UIGeneralTips), nameof(UIGeneralTips.OnTechUnlocked))] [HarmonyPatch(typeof(UIGeneralTips), "OnTechUnlocked")]
private static IEnumerable<CodeInstruction> UIGeneralTips_OnTechUnlocked_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator) private static IEnumerable<CodeInstruction> UIGeneralTips_OnTechUnlocked_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{ {
var matcher = new CodeMatcher(instructions, generator); var matcher = new CodeMatcher(instructions, generator);
@@ -138,7 +132,7 @@ public class HideTips : BaseUnityPlugin
} }
[HarmonyPrefix] [HarmonyPrefix]
[HarmonyPatch(typeof(DSPGame), nameof(DSPGame.StartGame), typeof(GameDesc))] [HarmonyPatch(typeof(DSPGame), "StartGame", typeof(GameDesc))]
private static bool DSPGame_StartGame_Prefix(GameDesc _gameDesc) private static bool DSPGame_StartGame_Prefix(GameDesc _gameDesc)
{ {
if (!_skipPrologue) return true; if (!_skipPrologue) return true;
@@ -147,7 +141,6 @@ public class HideTips : BaseUnityPlugin
} }
} }
/*
[HarmonyPatch] [HarmonyPatch]
class HideMenuDemo class HideMenuDemo
{ {
@@ -216,4 +209,3 @@ class HideMenuDemo
return false; return false;
} }
} }
*/

View File

@@ -6,7 +6,7 @@
<AssemblyName>HideTips</AssemblyName> <AssemblyName>HideTips</AssemblyName>
<BepInExPluginGuid>org.soardev.hidetips</BepInExPluginGuid> <BepInExPluginGuid>org.soardev.hidetips</BepInExPluginGuid>
<Description>DSP MOD - HideTips</Description> <Description>DSP MOD - HideTips</Description>
<Version>1.0.4</Version> <Version>1.0.3</Version>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<LangVersion>latest</LangVersion> <LangVersion>latest</LangVersion>
<RestoreAdditionalProjectSources>https://nuget.bepinex.dev/v3/index.json</RestoreAdditionalProjectSources> <RestoreAdditionalProjectSources>https://nuget.bepinex.dev/v3/index.json</RestoreAdditionalProjectSources>
@@ -23,7 +23,7 @@
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.3" PrivateAssets="all" /> <PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.3" PrivateAssets="all" />
</ItemGroup> </ItemGroup>
<Target Name="PostBuild" AfterTargets="PostBuildEvent" Condition="'$(Configuration)' == 'Release'"> <Target Name="PostBuild" AfterTargets="PostBuildEvent">
<Exec Command="del /F /Q package\$(ProjectName)-$(Version).zip&#xA;powershell Compress-Archive -Force -DestinationPath 'package/$(ProjectName)-$(Version).zip' -Path '$(TargetPath)', package/icon.png, package/manifest.json, README.md" /> <Exec Command="del /F /Q package\$(ProjectName)-$(Version).zip&#xA;zip -9 -j package/$(ProjectName)-$(Version).zip $(TargetPath) package/icon.png package/manifest.json README.md" />
</Target> </Target>
</Project> </Project>

View File

@@ -4,9 +4,6 @@
#### 隐藏/屏蔽各种引导提示/消息 #### 隐藏/屏蔽各种引导提示/消息
## Changelog ## Changelog
* 1.0.4
+ Remove `Disable title screen demo scene loading`, as it is buggy in recent updates.
+ Fix a bug that when tutorials are not added to the guides panel when they are hidden by this MOD.
* 1.0.3 * 1.0.3
+ Add config entries to disable research completion tips/popup windows + Add config entries to disable research completion tips/popup windows
+ Add a note to README for mod compatibility on `Disable title screen demo scene loading`. + Add a note to README for mod compatibility on `Disable title screen demo scene loading`.
@@ -20,12 +17,10 @@
## Usage ## Usage
* Tips/messages that can be hidden: random-reminder, tutorial, achievement/milestone card, research completion tips/popup windows. * Tips/messages that can be hidden: random-reminder, tutorial, achievement/milestone card, research completion tips/popup windows.
* Skip prologue for new game. * Skip prologue for new game.
* Disable title screen demo scene loading. Be note that this may conflict with some mods, disable this if you get any error popup on title screen.
* Each type of tips/messages is configurable individually. * Each type of tips/messages is configurable individually.
## 更新日志 ## 更新日志
* 1.0.4
+ 移除`关闭标题画面的演示场景加载`设置因为在最近的更新中有较多bug。
+ 修复了一个bug当教程指引被这个MOD隐藏时它们不会被添加到教程面板。
* 1.0.3 * 1.0.3
+ 增加设置项以关闭研究完成的提示/弹窗 + 增加设置项以关闭研究完成的提示/弹窗
+ 在README中增加了一个关于`关闭标题画面的演示场景加载`设置的兼容性说明 + 在README中增加了一个关于`关闭标题画面的演示场景加载`设置的兼容性说明
@@ -39,4 +34,5 @@
## 使用说明 ## 使用说明
* 可以屏蔽的消息:随机提醒,教程,成就/里程碑卡片,研究完成的提示/弹窗。 * 可以屏蔽的消息:随机提醒,教程,成就/里程碑卡片,研究完成的提示/弹窗。
* 跳过新游戏引导动画。 * 跳过新游戏引导动画。
* 关闭标题画面的演示场景加载。注意这可能会和其他mod冲突如果在标题画面出现错误弹窗请关闭这个设置。
* 各种屏蔽功能都可以在配置文件里开关。 * 各种屏蔽功能都可以在配置文件里开关。

View File

@@ -1,6 +1,6 @@
{ {
"name": "HideTips", "name": "HideTips",
"version_number": "1.0.4", "version_number": "1.0.3",
"website_url": "https://github.com/soarqin/DSP_Mods/tree/master/HideTips", "website_url": "https://github.com/soarqin/DSP_Mods/tree/master/HideTips",
"description": "Hide/Disable various tutorial tips/messages / 隐藏/屏蔽各种引导提示/消息", "description": "Hide/Disable various tutorial tips/messages / 隐藏/屏蔽各种引导提示/消息",
"dependencies": [ "dependencies": [

View File

@@ -23,7 +23,7 @@
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.3" PrivateAssets="all" /> <PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.3" PrivateAssets="all" />
</ItemGroup> </ItemGroup>
<Target Name="PostBuild" AfterTargets="PostBuildEvent" Condition="'$(Configuration)' == 'Release'"> <Target Name="PostBuild" AfterTargets="PostBuildEvent">
<Exec Command="del /F /Q package\$(ProjectName)-$(Version).zip&#xA;IF NOT EXIST package\plugins (mkdir package\plugins)&#xA;copy /y $(TargetPath) package\plugins\&#xA;cd package&#xA;powershell Compress-Archive -Force -DestinationPath '$(ProjectName)-$(Version).zip' -Path patchers, plugins, icon.png, manifest.json, ../README.md" /> <Exec Command="del /F /Q package\$(ProjectName)-$(Version).zip&#xA;IF NOT EXIST package\plugins (mkdir package\plugins)&#xA;copy /y $(TargetPath) package\plugins\&#xA;zip -9 -j package/$(ProjectName)-$(Version).zip package/icon.png package/manifest.json README.md&#xA;cd package&#xA;zip -9 -r $(ProjectName)-$(Version).zip patchers plugins" />
</Target> </Target>
</Project> </Project>

View File

@@ -22,7 +22,7 @@
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.3" PrivateAssets="all" /> <PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.3" PrivateAssets="all" />
</ItemGroup> </ItemGroup>
<Target Name="PostBuild" AfterTargets="PostBuildEvent" Condition="'$(Configuration)' == 'Release'"> <Target Name="PostBuild" AfterTargets="PostBuildEvent">
<Exec Command="IF NOT EXIST ..\LabOpt\package\patchers (mkdir ..\LabOpt\package\patchers)&#xA;copy /y $(TargetPath) ..\LabOpt\package\patchers\" /> <Exec Command="IF NOT EXIST ..\LabOpt\package\patchers (mkdir ..\LabOpt\package\patchers)&#xA;copy /y $(TargetPath) ..\LabOpt\package\patchers\" />
</Target> </Target>
</Project> </Project>

View File

@@ -73,6 +73,7 @@ public static class MechaDronesTweaks
public static float EnergyMultiplier = 0.1f; public static float EnergyMultiplier = 0.1f;
[HarmonyTranspiler] [HarmonyTranspiler]
[HarmonyPatch(typeof(ConstructionModuleComponent), nameof(ConstructionModuleComponent.SearchForNewTargets))]
[HarmonyPatch(typeof(ConstructionSystem), nameof(ConstructionSystem.UpdateDrones))] [HarmonyPatch(typeof(ConstructionSystem), nameof(ConstructionSystem.UpdateDrones))]
[HarmonyPatch(typeof(UIMechaWindow), nameof(UIMechaWindow.UpdateProps))] [HarmonyPatch(typeof(UIMechaWindow), nameof(UIMechaWindow.UpdateProps))]
[HarmonyPatch(typeof(UITechTree), nameof(UITechTree.RefreshDataValueText))] [HarmonyPatch(typeof(UITechTree), nameof(UITechTree.RefreshDataValueText))]
@@ -111,7 +112,7 @@ public static class MechaDronesTweaks
matcher.MatchForward(false, matcher.MatchForward(false,
new CodeMatch(OpCodes.Ldc_I4_1), new CodeMatch(OpCodes.Ldc_I4_1),
new CodeMatch(OpCodes.Stfld, AccessTools.Field(typeof(DroneComponent), nameof(DroneComponent.stage))) new CodeMatch(OpCodes.Stfld, AccessTools.Field(typeof(DroneComponent), nameof(DroneComponent.stage)))
).Opcode = OpCodes.Ldc_I4_2; ).Operand = 2;
return matcher.InstructionEnumeration(); return matcher.InstructionEnumeration();
} }

View File

@@ -6,7 +6,7 @@
<AssemblyName>MechaDronesTweaks</AssemblyName> <AssemblyName>MechaDronesTweaks</AssemblyName>
<BepInExPluginGuid>org.soardev.mechadronestweaks</BepInExPluginGuid> <BepInExPluginGuid>org.soardev.mechadronestweaks</BepInExPluginGuid>
<Description>DSP MOD - MechaDronesTweaks</Description> <Description>DSP MOD - MechaDronesTweaks</Description>
<Version>1.1.4</Version> <Version>1.1.3</Version>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<LangVersion>latest</LangVersion> <LangVersion>latest</LangVersion>
<RestoreAdditionalProjectSources>https://nuget.bepinex.dev/v3/index.json</RestoreAdditionalProjectSources> <RestoreAdditionalProjectSources>https://nuget.bepinex.dev/v3/index.json</RestoreAdditionalProjectSources>
@@ -23,7 +23,7 @@
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.3" PrivateAssets="all" /> <PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.3" PrivateAssets="all" />
</ItemGroup> </ItemGroup>
<Target Name="PostBuild" AfterTargets="PostBuildEvent" Condition="'$(Configuration)' == 'Release'"> <Target Name="PostBuild" AfterTargets="PostBuildEvent">
<Exec Command="del /F /Q package\$(ProjectName)-$(Version).zip&#xA;powershell Compress-Archive -Force -DestinationPath 'package/$(ProjectName)-$(Version).zip' -Path '$(TargetPath)', package/icon.png, package/manifest.json, README.md" /> <Exec Command="del /F /Q package\$(ProjectName)-$(Version).zip&#xA;zip -9 -j package/$(ProjectName)-$(Version).zip $(TargetPath) package/icon.png package/manifest.json README.md" />
</Target> </Target>
</Project> </Project>

View File

@@ -4,9 +4,6 @@
#### 机甲建设机调整(FastDrones MOD的后继者) #### 机甲建设机调整(FastDrones MOD的后继者)
## Updates ## Updates
* 1.1.4
+ Fixed support for game version 0.10.29
* 1.1.3 * 1.1.3
+ Support for game version 0.10.28.20759+ + Support for game version 0.10.28.20759+
+ Fixed a minor bug that `RemoveSpeedLimitForStage1` not working while `UseFixedSpeed` set to false and `SpeedMultiplier` set to 1 + Fixed a minor bug that `RemoveSpeedLimitForStage1` not working while `UseFixedSpeed` set to false and `SpeedMultiplier` set to 1
@@ -38,9 +35,6 @@
* Note: This MOD will disable `FastDrones` if the MOD is installed, to avoid conflict in functions. * Note: This MOD will disable `FastDrones` if the MOD is installed, to avoid conflict in functions.
## 更新日志 ## 更新日志
* 1.1.4
+ 修复了对游戏版本0.10.29的支持
* 1.1.3 * 1.1.3
+ 支持游戏版本0.10.28.20759+ + 支持游戏版本0.10.28.20759+
+ 修复了当`UseFixedSpeed`设置为false且`SpeedMultiplier`设置为1时`RemoveSpeedLimitForStage1`无效的问题 + 修复了当`UseFixedSpeed`设置为false且`SpeedMultiplier`设置为1时`RemoveSpeedLimitForStage1`无效的问题

View File

@@ -1,6 +1,6 @@
{ {
"name": "MechaDronesTweaks", "name": "MechaDronesTweaks",
"version_number": "1.1.4", "version_number": "1.1.3",
"website_url": "https://github.com/soarqin/DSP_Mods/tree/master/MechaDronesTweaks", "website_url": "https://github.com/soarqin/DSP_Mods/tree/master/MechaDronesTweaks",
"description": "Some tweaks for mecha drones and build functions(Successor to FastDrones MOD) / 机甲建设机和建设功能调整(FastDrones MOD的后继者)", "description": "Some tweaks for mecha drones and build functions(Successor to FastDrones MOD) / 机甲建设机和建设功能调整(FastDrones MOD的后继者)",
"dependencies": [ "dependencies": [

View File

@@ -23,7 +23,7 @@
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.3" PrivateAssets="all" /> <PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.3" PrivateAssets="all" />
</ItemGroup> </ItemGroup>
<Target Name="PostBuild" AfterTargets="PostBuildEvent" Condition="'$(Configuration)' == 'Release'"> <Target Name="PostBuild" AfterTargets="PostBuildEvent">
<Exec Command="del /F /Q package\$(ProjectName)-$(Version).zip&#xA;powershell Compress-Archive -Force -DestinationPath 'package/$(ProjectName)-$(Version).zip' -Path '$(TargetPath)', package/icon.png, package/manifest.json, README.md" /> <Exec Command="del /F /Q package\$(ProjectName)-$(Version).zip&#xA;zip -9 -j package/$(ProjectName)-$(Version).zip $(TargetPath) package/icon.png package/manifest.json README.md" />
</Target> </Target>
</Project> </Project>

View File

@@ -25,7 +25,7 @@
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.3" PrivateAssets="all" /> <PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.3" PrivateAssets="all" />
</ItemGroup> </ItemGroup>
<Target Name="PostBuild" AfterTargets="PostBuildEvent" Condition="'$(Configuration)' == 'Release'"> <Target Name="PostBuild" AfterTargets="PostBuildEvent">
<Exec Command="del /F /Q package\$(ProjectName)-$(Version).zip&#xA;powershell Compress-Archive -Force -DestinationPath 'package/$(ProjectName)-$(Version).zip' -Path '$(TargetPath)', package/icon.png, package/manifest.json, README.md" /> <Exec Command="del /F /Q package\$(ProjectName)-$(Version).zip&#xA;zip -9 -j package/$(ProjectName)-$(Version).zip $(TargetPath) package/icon.png package/manifest.json README.md" />
</Target> </Target>
</Project> </Project>

View File

@@ -23,7 +23,7 @@
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.3" PrivateAssets="all" /> <PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.3" PrivateAssets="all" />
</ItemGroup> </ItemGroup>
<Target Name="PostBuild" AfterTargets="PostBuildEvent" Condition="'$(Configuration)' == 'Release'"> <Target Name="PostBuild" AfterTargets="PostBuildEvent">
<Exec Command="del /F /Q package\$(ProjectName)-$(Version).zip&#xA;powershell Compress-Archive -Force -DestinationPath 'package/$(ProjectName)-$(Version).zip' -Path '$(TargetPath)', package/icon.png, package/manifest.json, README.md" /> <Exec Command="del /F /Q package\$(ProjectName)-$(Version).zip&#xA;zip -9 -j package/$(ProjectName)-$(Version).zip $(TargetPath) package/icon.png package/manifest.json README.md" />
</Target> </Target>
</Project> </Project>

View File

@@ -1,543 +0,0 @@
## Changlog
* 1.2.20
+ New feature: `Dismantle blueprint selected buildings`
- Press shortcut key in blueprint copy mode to dismantle selected buildings.
- The default shortcut key is Ctrl+X, you can set it in system options panel.
+ New feature: `Auto-config logistic stations`
- Auto-config buildings include: Logistics Distributor, PLS, ILS, Advanced Mining Machine.
+ `Night Sunlight`: Fix bugs that sunlight angle is not updated as expected again.
* 1.2.19
+ New feature: `Tweak building buffer`
- Factory recipe buffer formula: take the larger value between `Assembler buffer time multiplier(in seconds) * items needed per second` and `Assembler buffer minimum multiplier * items needed per recipe`
- `Assembler buffer time multiplier(in seconds)`: Range 2-10, default is 4 (same as game)
- `Assembler buffer minimum multiplier`: Range 2-10, default is 2 (same as game)
- Matrix Lab assembly mode formula: Default buffer is `Buffer count for assembling in labs`, when using Self-evolution Lab, if recipe's original production time is not greater than 9 seconds, add `Extra buffer count for Self-evolution Labs` * (`Lab speed` - 1)
- `Buffer count for assembling in labs`: Range 2-20, default is 6 (same as game)
- `Extra buffer count for Self-evolution Labs`: Range 1-10, default is 3 (same as game)
- `Buffer count for researching in labs`: Range 2-20, default is 10 (same as game)
- `Ray Receiver Graviton Lens buffer count`: Range 1-20, default is 1 (game default is 20)
+ New feature: `Shortcut keys for showing stars' name`
- Add a shortcut key to always show all star names in starmap when holding, default is `Alt`
- Add a shortcut key to toggle between three star name display states in starmap: `Original state`, `Show all names`, `Hide all names`, default is `Tab`, will restore to original state when closing starmap
+ `Cut conveyor belt`: Fix a bug that entity logic connection is not cut so that belt is not cut off on copying as a blueprint.
<details>
<summary>Older versions</summary>
* 1.2.18
+ `Protect veins from exhaustion`: Optimized implementation, now veins will not be protected once you have upgrade `Veins Utilization` to level 390+, while the cost rate becomes absolute 0.
+ `Night Sunlight`: Fix bugs that sunlight angle is not updated as expected.
* 1.2.17
+ Fix wrong implementation of `Protect veins from exhaustion` which causes wrong display of vein stats and veins not consumed.
* 1.2.16
+ New feature: `Cut conveyor belt`
- Press shortcut key to cut conveyor belt under cursor.
- The default shortcut key is Alt+X, you can set it in system options panel.
+ New feature: `Profile based option`
- Option file is stored as `Options\<ProfileName>.xml`.
+ Fix compatibility with game update 0.10.32.25779
* 1.2.15
+ `Off-grid building and stepped rotation`: Fix compatibility with DSP 0.10.32.25682. (#57)
+ `Enhanced control for logistic storage limits`: Try to fix possible crash. (#54)
* 1.2.14
+ Fix an issue that an unexpected menu icon is shown in the top-right corner of the config panel.
+ `Stop ejectors when available nodes are all filled up`: Fix compatibility with `Dyson Sphere Program v0.10.32.25496`.
* 1.2.13
+ `Belt signals for buy out dark fog items automatically`: Fix possible crashes.
+ `Logistics Control Panel Improvement`: Auto apply filter with in-hand item now.
+ Fix an alignment issue on UI panel.
* 1.2.12
+ `Construct only structure points but frames`: Fix a bug that frames are still not constructed when this function is disabled.
+ `Drag building power poles in maximum connection range`: Fix a bug that single power pole cannot be placed at some positions.
* 1.2.11
+ Fix an issue caused by game update: tips are not shown when mouse hovering on tips button.
* 1.2.10
+ `Set enabled CPU threads`: Fix hybrid-architect check for CPUs without hyper-threading
+ `Re-initialize Dyson Spheres` and `Quick dismantle Dyson Shells`: Fix possible crashes and a display issue, while Dyson Sphere panel is actived.
* 1.2.9
+ `Protect veins from exhaustion`:
- Fix a bug that vein protection causes crashes (#50).
- Fix a bug that minimum oil speed in config is not working (#50).
- Fix a bug that oil is not extracted when vein protection is enabled in infinite resource mode (#52).
* 1.2.8
+ New feature: `Fast fill in to and take out from tanks`
- You can set multiplier for tanks' operation speed
- This affects manually fill in to and/or take out from tanks, as well as transfer from upper to lower level.
+ Fixes to `Append mod profile name to game window title`:
- Fix a bug that window title is not set correctly when multiple instance is launched.
- Fix a bug that window title is not set correctly if BepInEx debug console is enabled.
+ `Real-time logistic stations info panel`: Fix a bug that item status bar appears unexpectedly.
* 1.2.7
+ Fix some minor issues
* 1.2.6
+ `Remember window position and size on last exit`
- Fix a bug that window position is restored even the option is disabled.
- Fix a bug that the last window position is wrongly remembere when game is closed at minimized state.
* 1.2.5
+ New feature: `Set process priority`
+ New feature: `Set enabled CPU threads`
+ `Drag building power poles in maximum connection range`: Add a new config option `Build Tesla Tower and Wireless Power Tower alternately`
* 1.2.4
+ `Sunlight at night`:
- Fix flickering issue while mecha is sailing.
- Can configure the light angles now.
+ `Scale up mouse cursor`: Fix known issues.
+ `Buy out techs with their prerequisites`: Fix a bug that warning popup from invalid data.
+ Does not patch `BulletTime`'s speed control now, as `BulletTime` has been updated to support configurable maximum speed.
+ Some minor fixes and tweaks.
* 1.2.3
+ `Real-time logistic stations info panel`: Fix bar length not match with item amount when item amount is more than capacity.
+ `Sunlight at night`: Fix not working.
* 1.2.2
+ `Real-time logistic stations info panel`: Fix text color mismatch sometimes
+ `Logical Frame Rate`: Set default shortcut key to `Ctrl`+`-/+` to avoid conflict with other shortcut keys
* 1.2.1
+ `Off-grid building and stepped rotation`:
- Fix off-grid building's default shortcut key for belts
- Fix coordinate display issue
* 1.2.0
+ New feature: `Logical Frame Rate`
- This will change game running speed, down to 0.1x slower and up to 10x faster.
- A pair of shortcut keys (`-` and `+`) to change the logical frame rate by -0.5x and +0.5x.
- Note:
- High logical frame rate is not guaranteed to be stable, especially when factories are under heavy load.
- This will not affect some game animations.
- When set game speed in mod `Auxilaryfunction`, this feature will be disabled.
- When mod `BulletTime` is installed, this feature will be hidden, but patch `BulletTime`'s speed control, to make its maximum speed 10x.
+ `Off-grid building and stepped rotation`: Due to conflict with shortcut key in new game update, the shortcut key for belts is changed to `Ctrl` by default, and can be set in system options now.
+ `Real-time logistic stations info panel`: Fix a crash issue.
+ `Dyson Sphere "Auto Fast Build"`: Fix possible wrong production records.
+ Codes refactored, for better maintainability.
* 1.1.6
+ New feature: `Scale up mouse cursor`
- Note: This will enable software cursor mode, which may cause mouse movement lag on heavy load.
+ New feature: `Real-time logistic stations info panel`
- Note: This function will be hidden if you enabled `Show station info` in mod `Auxilaryfunction`.
+ Fix an issue that `Dyson Sphere "Auto Fast Build"` does not generate production records for solar sails.
+ Remove use of AssetBundle, move all icons into `Assembly Resources`, for better flexibility.
* 1.1.5
+ New feature: `Logistics Control Panel Improvement`
- Auto apply filter with item under mouse cursor while opening the panel
- Quick-set item filter while right-clicking item icons in storage list on the panel
+ New feature: `Dyson Sphere "Auto Fast Build" speed multiplier`
- Note: this only applies to `Dyson Sphere "Auto Fast Build"` in sandbox mode
+ New feature: `Mod manager profile based save folder`
- Save files are stored in `Save\&lt;ProfileName&gt;` folder.
- Will use original save location if matching default profile name.
+ `Quick build and dismantle stacking labs`: works for storages and tanks now
+ `Enable game window resize`: Keep window resizable on applying game options.
+ `Remember window position and size on last exit`: Do not resize window on applying game options if resolution related config entries are not changed.
+ Auto resize panel to fit content, for better support of multilanguages and mods dependent on UX Assist config panel functions.
* 1.1.4
+ Fix `Remove some build conditions`
* 1.1.3
+ UI texts are updated following game settings now
+ Fix hover area for checkboxes in config panel
+ Fix an issue which makes `Convert Peace-Mode saves to Combat-Mode on loading` not working
* 1.1.2
+ `Belt signals for buy out dark fog items automatically`: Always add belt signals to the panel to fix missing belt icons when disabled.
* 1.1.1
+ Fix assetbundle loading issue
* 1.1.0
+ `Stop ejectors when available nodes are all filled up`: Show `No node to fill` on ejector panel when all dyson sphere nodes are filled up.
+ Append mod profile name to game window title, if using mod managers (`Thunderstore Mod Manager` or `r2modman`).
+ New features:
- `Buy out techs with their prerequisites`: This enables batch buying out techs with their prerequisites. Buy-out button is shown for all locked techs/upgrads.
- `Belt signals for buy out dark fog items automatically`, while enabled:
- 6 belt signals are added to the signal panel, which can be used to buy out dark fog items automatically.
- Generated items are stacked in 4 items.
- Exchange ratio is following the original game design, aka:
- 1 Metaverse = 20 Dark Fog Matrices
- 1 Metaverse = 60 Engery Shards
- 1 Metaverse = 30 Silicon-based Neurons
- 1 Metaverse = 30 Negentropy Singularities
- 1 Metaverse = 30 Matter Recombinators
- 1 Metaverse = 10 Core Elements
* 1.0.26
+ New features:
- Restore upgrades of `Sorter Cargo Stacking` on panel
- Set `Sorter Cargo Stacking` to unresearched state
+ Changes to `Protect veins from exhaustion` configuration:
- The vein amount is protected at 1000 by default now
- The maximum vein amount is changed to 10000, and the maximum oil speed is changed to 10.0/s
* 1.0.25
+ Fix an issue that building entites can not be clicked through when `Do not render factory entities (except belts and sorters)` is enabled
* 1.0.24
+ Changes to `Do not render factory entities (except belts and sorters)`
- Add shortcut key in config panel to toggle this function
- Can click on both belts and sorters now
+ New feature: `Drag building power poles in maximum connection range`
+ New feature: `Allow overflow for Logistic Stations and Advanced Mining Machines`
- Allow overflow when trying to insert in-hand items
- Allow `Enhanced control for logistic storage limits` to exceed tech capacity limits
- Remove logistic strorage limit check on loading game
* 1.0.23
+ New features:
- `Do not render factory entities (except belts and sorters)`
- This also makes players click though factory entities but belts
- `Open Dark Fog Communicator` anywhere
+ Belts can be built off-grid now, by pressing the shortcut key for `Switch Splitter model`(`Tab` by default)
+ Add a suboption `Auto boost` to `Auto-cruise`
+ `Auto-cruise` does warp when core energy at least 80% now
* 1.0.22
+ Fix a crash issue caused by `Quick build and dismantle stacking labs`
* 1.0.21
+ Fix a bug that stepped rotation is not working in `Off-grid building and stepped rotation`, which is caused by latest game update
+ Fix some issues in `Auto nativation` and `Auto-cruise`, now only boosts when core energy at least 10% and warps when core energy at least 50%
* 1.0.20
+ Fix an infinite-loop issue when `Quick build and dismantle stacking labs` and `No condition build` are both enabled
+ Fix a crash caused by `Re-initialize planet` in combat mode
* 1.0.19
+ New functions:
- `Quick build and dismantle stacking labs`
- `Protect veins from exhaustion`
- By default, the vein amount is protected at 100, and oil speed is protected at 1.0/s, you can set them yourself in config file.
- When reach the protection value, veins/oils steeps will not be mined/extracted any longer.
- Close this function to resume mining and pumping, usually when you have enough level on `Veins Utilization`
+ Remove default shortcut key for `Auto-cruise`, to avoid misoperation. Please set it in the system options window manually if needed.
* 1.0.18
+ Fix crash while coursing to a dark-fog hive.
+ Auto-cruise does not bypass dark-fog hives if they are targeted.
* 1.0.17
+ New function: `Auto navigation on sailings`, which is inspired by [CruiseAssist](https://dsp.thunderstore.io/package/tanu/CruiseAssist/) and its extension [AutoPilot](https://dsp.thunderstore.io/package/tanu/AutoPilot/)
- It keeps Icarus on course to the target planet
- It will try to bypass any obstacles(planets, stars or dark-fog hives) on the way
- Furthermore, there is also a shortcut key which can be set in the system options window, which is used to toggle `Auto-cruise` that enables flying to targeted planets fully automatically.
- Auto-cruise will start when you target a planet on star map
- It will use warper to fly to the target planet if the planet is too far away, the range can be configured.
- It will speed down when approaching the target planet, to avoid overshooting
+ Fix a crash caused by `Stop ejectors when available nodes are all filled up` in latest game update
+ `Off-grid building and stepped rotation`: Hide Z coordinate from display if it is zero
* 1.0.16
+ Add CommonAPI to package manifest dependencies(missing in last version)
+ New function: `Hide tips for soil piles changes`
* 1.0.15
+ Move shortcut key settings to system options window, which depends on [CommonAPI](https://dsp.thunderstore.io/package/CommonAPI/CommonAPI)
+ Enable `Hide UI` function(`F11` by default) while on Star Map view
+ New function: `Treat stack items as single in monitor components`
* 1.0.14
+ Fix crash in `Re-initialize planet` again
+ `Off-grid building and stepped rotation`: Add Z coordinate to display, and adjust the precision to 4 decimal after point
* 1.0.13
+ `Off-grid building and stepped rotation`: show building coordinates(relative to grids) on building preview and building info panel now
+ Increase maximum count of Metadata Instantiations to 20000 (from 2000)
+ Increase capacity of player order queue to 128 (from 16)
+ Fix issue caused by game updates
- `Remove some build conditions`: fixed issue that some conditions are not eliminated
- `Re-initialize planet`: fixed crash issue
* 1.0.12
+ Fix a bug that ejectors aimed at even-numbered orbits stop working when `Stop ejectors when available nodes are all filled up` is enabled.
* 1.0.11
+ Remove `Better auto-save mechanism` due to conflicts with DSPModSave and some other mods.
* 1.0.10
+ Fix a button display bug
+ Fix a possible crash while `Enhanced control for logistic storage limits` is enabled
* 1.0.9
+ New function: `Better auto-save mechanism`
- Auto saves are stored in 'Save\AutoSaves' folder, filenames are combined with cluster address and date-time
- Note: this will sort gamesaves by modified time on save/load window, so you don't have to use [DSP_Save_Game_Sorter] anymore
* 1.0.8
+ New function: `Enhanced control for logistic storage limits`
* 1.0.7
+ Fix a crash issue on choosing language other than English and Chinese
+ Games saved in Peace-Mode after Dark-Fog update can also be loaded as Combat-Mode now.
* 1.0.6
+ Convert old saves to Combat-Mode on loading
* 1.0.5
+ Support game version 0.10.28.20759
+ Sort blueprint structures before saving, to reduce generated blueprint data size a little.
* 1.0.4
+ Add new function: `Off-grid building and stepped rotation`
+ Fix an issue that window position not restored and can not be resized when function is enabled but game is started with different mod profiles.
* 1.0.3
+ Add new function: `Quick build Orbital Collectors`.
+ Add confirmation popup for `Re-intialize planet`, `Quick dismantle all buildings`, `Re-initialize Dyson Spheres` and `Quick dismantle Dyson Shells`.
+ Fix error on `Remove build count and range limit` when building a large amount of belts.
+ Fix an issue that window position not saved correctly when quit game without using in-game menu.
* 1.0.2
+ Redesign config tabs, for clearer layout.
+ Add 2 new options:
- Enable game window resize.
- Remember window position and size on last exit.
* 1.0.1
+ Fix config button text and tips while returning to title menu.
+ Fix that error occurs while returning to title menu, with `Stop ejectors when available nodes are all filled up` enabled.
+ Add a patch to fix the bug that warning popup on `Veins Utilization` upgraded to level 8000+.
* 1.0.0
+ Initial release
+ Functions moved from [MechaDronesTweaks](https://dsp.thunderstore.io/package/soarqin/MechaDronesTweaks/) and [CheatEnabler](https://dsp.thunderstore.io/package/soarqin/CheatEnabler/)
</details>
## 更新日志
* 1.2.20
+ 新功能:`拆除蓝图选中的建筑`
- 在蓝图复制模式下按快捷键拆除选中的建筑
- 默认快捷键是Ctrl+X可以在系统选项面板中设置
+ 新功能:`自动配置物流站`
- 自动配置的建筑包括:物流配送器、行星物流站、星际物流站、高级采矿机
+ `夜间日光灯`:再次修复了光照角度未正确更新的问题
* 1.2.19
+ 新功能:`调整建筑输入缓冲`
- 工厂配方计算公式,在`工厂配方缓冲时间倍率秒数x每秒需要的原料数量``工厂配方缓冲最小倍率x每生产一次配方需要的原料数量`中取更大的那个值
- `工厂配方缓冲时间倍率(秒)`范围2-10默认为4(同游戏)
- `工厂配方缓冲最小倍率`范围2-10默认为2(同游戏)
- 研究站矩阵合成模式计算公式,默认缓存`研究站矩阵合成模式缓存数量`当使用自演化研究站时如果配方的原始生产时间不大于9秒则增加`自演化研究站矩阵额外缓冲数量`*(`研究站速度倍率`-1)
- `研究站矩阵合成模式缓存数量`范围2-20默认为6(同游戏)
- `自演化研究站矩阵额外缓冲数量`范围1-10默认为3(同游戏)
- `研究站科研模式缓存数量`范围2-20默认为10(同游戏)
- `射线接收器透镜缓冲数量`范围1-20默认为1(游戏默认为20)
+ 新功能:`启用显示所有星系名称的快捷键`
- 新增一个快捷键,按住后始终在星图显示所有星系名称,默认为`Alt`
- 新增一个快捷键,在星图视图切换三种星系名称显示状态:`原始显示状态``显示所有名称``隐藏所有名称`,默认为`Tab`,关闭星图时会恢复到原始状态
+ `切割传送带`:修复了实体逻辑连接未切断导致复制为蓝图时传送带未被切断的问题。
<details>
<summary>Older versions</summary>
* 1.2.18
+ `保护矿脉不会耗尽`:优化实现,当`矿物利用`升级到390级以上时消耗速度变为0时矿脉将不再被保护。
+ `夜间日光灯`:修复了光照角度未正确更新的问题。
* 1.2.17
+ 修复了`保护矿脉不会耗尽`导致矿脉状态显示错误和矿脉未被消耗的错误实现
* 1.2.16
+ 新功能:`切割传送带`
- 按快捷键切割光标位置的传送带
- 默认快捷键是Alt+X可以在系统选项面板中设置
+ 新功能:`基于mod管理器配置档案名`
- 选项文件存储在`Options\<ProfileName>.xml`
+ 修复了与游戏更新0.10.32.25779的兼容性
* 1.2.15
+ `脱离网格建造和小角度旋转`修复了与0.10.32.25682的兼容性 (#57)
+ `物流塔存储数量限制控制改进`:修复了可能导致崩溃的问题 (#54)
* 1.2.14
+ 修正设置窗口右上角多出一个菜单图标的问题
+ `当可用节点全部造完时停止弹射`:修复了与`戴森球计划 v0.10.32.25496`的兼容性
* 1.2.13
+ `用于自动购买黑雾物品的传送带信号`:修复了可能导致崩溃的问题
+ `物流控制面板改进`:现在也自动将拿着的物品设为筛选条件
+ 修复了UI面板上的对齐问题
* 1.2.12
+ `只建造节点不建造框架`:修复了关闭此功能时框架不进行建造的问题
+ `拖动建造电线杆时自动使用最大连接距离间隔`:修复了某些位置无法放置单个电线杆的问题
* 1.2.11
+ 修复了游戏更新导致的提示按钮鼠标悬停时不显示提示文字的问题
* 1.2.10
+ `设置使用的CPU线程`修复了对没有超线程的CPU的大小核检查
+ `初始化戴森球``快速拆除戴森壳`:修复了在戴森球面板激活时可能导致崩溃的问题,以及显示错误的问题。
* 1.2.9
+ `保护矿脉不会耗尽`
- 修复了矿脉保护导致崩溃的问题(#50)
- 修复了配置中的最小采油速度不起作用的问题(#50)
- 修复了无限资源模式下油井保护导致无法采油的问题(#52)
* 1.2.8
+ 新功能:`储液罐快速注入和抽取液体`
- 你可以设置储液罐操作速度的倍率
- 影响手动注入和抽取,以及从储液罐上层传输到下层的速度
+ 在游戏窗口标题中追加mod配置档案名的修复
- 修复了多实例启动时窗口标题未正确设置的问题
- 修复了启用BepInEx调试控制台时窗口标题未正确设置的问题
+ `物流运输站实时信息面板`:修复了一个物品状态条意外显示的问题
* 1.2.7
+ 修复了一些小问题
* 1.2.6
+ `记住上次退出时的窗口位置和大小`
- 修复了即使选项被禁用也恢复窗口位置的问题
- 修复了窗口最小化时关闭游戏导致窗口位置被错误记录的问题
* 1.2.5
+ 新功能:`设置进程优先级`
+ 新功能:`设置使用的CPU线程`
+ `拖动建造电线杆时自动使用最大连接距离间隔`:添加一个新的设置项`交替建造电力感应塔和无线输电塔`
* 1.2.4
+ `夜间日光灯`
- 修复了航行时闪烁的问题
- 现在可以配置入射光线角度了
+ `放大鼠标指针`:修复已知问题
+ `买断科技也同时买断所有前置科技`:修复了数据错误警告弹窗的问题
+ 不再对`BulletTime`的速度控制打补丁,因为`BulletTime`已更新支持可配置最大速度
+ 一些小修复和调整
* 1.2.3
+ `物流运输站实时信息面板`:修复了物品数量超过容量限制时条长度不匹配的问题
+ `夜间日光灯`:修复了不起作用的问题
* 1.2.2
+ `物流运输站实时信息面板`:修复了文本颜色不匹配的问题
+ `逻辑帧倍率`:将默认快捷键设置为`Ctrl`+`-/+`,以避免与其他快捷键冲突
* 1.2.1
+ `脱离网格建造和小角度旋转`
- 修复了传送带脱离网格建造的默认快捷键
- 修复了坐标显示问题
* 1.2.0
+ 新功能:`逻辑帧倍率`
- 这将改变游戏运行速度最慢0.1倍最快10倍
- 设置了一对快捷键(`-``+`),可以-/+0.5倍改变逻辑帧倍率
- 注意:
- 高逻辑帧倍率不能保证稳定性,特别是在工厂负载较重时
- 这不会影响一些游戏动画
- 当在`Auxilaryfunction`mod中设置游戏速度时此功能将被禁用
- 当安装了`BulletTime`mod时此功能将被隐藏但会对`BulletTime`的速度控制打补丁使其最大速度变为10倍
+ `脱离网格建造和小角度旋转`:由于与新游戏更新中的快捷键冲突,传送带脱离网格建造的快捷键默认更改为`Ctrl`,并且现在可以在系统选项中设置
+ `物流运输站实时信息面板`:修复了一个崩溃问题
+ `戴森球自动快速建造`:修复了可能出现的错误生产记录
+ 代码重构,以获得更好的可维护性
* 1.1.6
+ 新功能:`放大鼠标指针`
- 注意这将启用软件指针模式可能会在CPU负载较重时导致鼠标移动延迟
+ 新功能:`物流运输站实时信息面板`
- 注意:如果你启用了`Auxilaryfunction`中的`展示物流站信息`,此功能将被隐藏
+ 修复了`戴森球自动快速建造`未生成太阳帆生产记录的问题
+ 移除了AssetBundle的使用将所有图标移入`Assembly资源`,以获得更好的灵活性
* 1.1.5
+ 新功能:`物流控制面板改进`
- 打开面板时自动将鼠标指向物品设为筛选条件
- 在控制面板物流塔列表中右键点击物品图标快速设置为筛选条件
+ 新功能:`戴森球自动快速建造速度倍率`
- 注意:这仅适用于沙盒模式下的`戴森球自动快速建造`功能
+ 新功能:`基于mod管理器配置档案名的存档文件夹`
- 存档文件会存储在`Save\&lt;ProfileName&gt;`文件夹中
- 如果匹配默认配置档案名则使用原始存档位置
+ `快速建造和拆除堆叠研究站`:现在也支持储物仓和储液罐
+ `允许调整游戏窗口大小`:在应用游戏选项时保持窗口可调整大小
+ `记住上次退出时的窗口位置和大小`:如果分辨率相关的配置项未改变,则在应用游戏选项时不调整窗口大小
+ 自动调整面板大小适应内容以更好地支持多语言和依赖于UX助手配置面板功能的mod
* 1.1.4
+ 修复了`移除部分不影响游戏逻辑的建造条件`
* 1.1.3
+ 界面文本现在完全跟随游戏语言设置改变
+ 修复了配置面板中勾选框的鼠标悬停区域
+ 修复了`加载和平模式存档时将其转换为战斗模式`不起作用的问题
* 1.1.2
+ `用于自动购买黑雾物品的传送带信号`: 总是将传送带信号添加到面板,以修复禁用时传送带图标丢失的问题。
* 1.1.1
+ 修复了资源包加载问题
* 1.1.0
+ `可用节点全部造完时停止弹射`: 当所有戴森球节点都造完时,在弹射器面板上显示`没有可建造节点`
+ 如果使用mod管理器(`Thunderstore Mod Manager``r2modman`)启动游戏在游戏窗口标题中追加mod配置档案名
+ 新功能:
- `买断科技也同时买断所有前置科技`:可以批量买断科技及其所有前置科技。所有未解锁的科技/升级都会显示买断按钮。
- `用于自动购买黑雾物品的传送带信号`,启用时:
- 在信号面板上添加了6个传送带信号可以用于自动购买黑雾道具。
- 生成的物品堆叠数为4。
- 兑换比率遵循原始游戏设计,即:
- 1个元宇宙 = 20个黑雾矩阵
- 1个元宇宙 = 60个能量碎片
- 1个元宇宙 = 30个硅基神经元
- 1个元宇宙 = 30个负熵奇点
- 1个元宇宙 = 30个物质重组器
- 1个元宇宙 = 10个核心素
* 1.0.26
+ 新功能:
- 在升级面板上恢复`分拣器货物堆叠`的升级
-`分拣器货物堆叠`设为未研究状态
+ `保护矿脉不会耗尽`配置的改动:
- 现在默认矿脉数量保护在1000
- 最大矿脉数量改为10000最大采油速度改为10.0/s
* 1.0.25
+ 修复了`不渲染工厂建筑实体(除了传送带和分拣器)`启用时无法点穿工厂实体的问题
* 1.0.24
+ `不渲染工厂建筑实体(除了传送带和分拣器)`的改动
- 在配置面板中添加了一个快捷键来切换此功能
- 现在也可以点击到分拣器了
+ 新功能:`拖动建造电线杆时自动使用最大连接距离间隔`
+ 新功能:`允许物流塔和大型采矿机物品溢出`
- 当尝试塞入手中物品时允许溢出
- 允许`物流塔存储数量限制控制改进`超过科技容量限制
- 在加载游戏时移除物流塔容量限制检查
* 1.0.23
+ 新功能:
- `不渲染工厂建筑实体(除了传送带和分拣器)`
- 这使得玩家可以点穿工厂实体直接点到传送带
- 在任意位置`打开黑雾通讯器`
+ 传送带现在可以脱离网格建造了,通过按住`切换分流器样式`的快捷键(默认`Tab`)
+ 为`自动巡航`添加一个子选项`自动加速`
+ `自动巡航`现在在核心能量至少80%时才加速
* 1.0.22
+ 修复了`快速建造和拆除堆叠研究站`导致的崩溃问题
* 1.0.21
+ 修复了`脱离网格建造和小角度旋转`在最新游戏更新后无法小角度旋转的问题
+ 修复了`航行时自动导航``自动巡航`的一些问题。现在只有能量至少10%时才加速能量至少50%时才启动曲速
* 1.0.20
+ 修复了`快速建造和拆除堆叠研究站``无条件建造`同时启用时可能导致的逻辑死循环问题
+ 修复了在战斗模式下`初始化本行星`导致的崩溃问题
* 1.0.19
+ 新功能:
- `快速建造和拆除堆叠研究站`
- `保护矿脉不会耗尽`
- 默认矿脉数量保护于剩余100采油速保护于速度1.0/s你可以在配置文件中自行设置。
- 当达到保护值时,矿脉和油井将不再被开采。
- 关闭此功能以恢复开采,一般是当你在`矿物利用`上有足够的等级时。
+ 移除了`自动巡航`的默认快捷键,以避免误操作。如有需要请手动在系统选项窗口中设置。
* 1.0.18
+ 修复了以黑雾巢穴为目标时导致崩溃的问题
+ 当黑雾巢穴是目标时,自动导航不会绕过它
* 1.0.17
+ 新功能:`航行时自动导航`,想法来自[CruiseAssist](https://dsp.thunderstore.io/package/tanu/CruiseAssist/)及其扩展[AutoPilot](https://dsp.thunderstore.io/package/tanu/AutoPilot/)
- 它会保持伊卡洛斯飞向目标星球
- 它会尝试绕过途中的任何障碍物(行星、恒星或黑雾巢穴)
- 此外,还有一个快捷键可以在系统选项窗口中设置,用于切换`自动巡航`,实现完全自动化的飞行至目标星球。
- 当你选择目标星球后,自动巡航就会开始
- 如果目标星球距离过远会自动使用曲速(超过5AU),你可以在面板上更改这个值。
- 它会在接近目标星球时减速,以避免发生越过目标的情况
+ 修复了最新游戏更新后`当可用节点全部造完时停止弹射`引起崩溃问题
+ `脱离网格建造和小角度旋转`如果Z坐标为零则从显示中隐藏
* 1.0.16
+ 添加了对CommonAPI的包依赖(上个版本忘记加了)
+ 新功能:`隐藏沙土数量变动的提示`
* 1.0.15
+ 将快捷键设置移动到系统选项窗口,依赖于[CommonAPI](https://dsp.thunderstore.io/package/CommonAPI/CommonAPI)
+ 在星图视图中启用`隐藏UI`功能(默认按键为`F11`)
+ 新功能:`在流速计中将堆叠物品视为单个物品`
* 1.0.14
+ 再次尝试修复`初始化本行星`导致的崩溃问题
+ `脱离网格建造和小角度旋转`现在显示建筑Z坐标并将精度调整为小数点后4位
* 1.0.13
+ `脱离网格建造和小角度旋转`:现在在建造预览和建筑信息面板上显示建筑坐标(相对于网格)
+ 将元数据提取的最大数量增加到20000(原来为2000)
+ 将玩家指令队列的容量增加到128(原来为16)
+ 修复了游戏更新导致的问题
- `移除部分不影响游戏逻辑的建造条件`:修复了一些条件未被移除的问题
- `初始化本行星`:修复了崩溃问题
* 1.0.12
+ 修复了当`当可用节点全部造完时停止弹射`选项启用时瞄准偶数轨道的弹射器停止工作的bug
* 1.0.11
+ 移除`更好的自动保存机制`因为与DSPModSave和其他一些mod冲突
* 1.0.10
+ 修复了一个按钮显示错误
+ 修复了`物流塔存储数量限制控制改进`启用时可能导致的崩溃问题
* 1.0.9
+ 新功能:`更好的自动保存机制`
- 自动存档会以星区地址和日期时间组合为文件名存储在'Save\AutoSaves'文件夹中
- 注意:此功能会在保存/读取菜单按最后修改时间对存档进行排序,因此你不再需要[DSP_Save_Game_Sorter]了
* 1.0.8
+ 新功能:`物流塔存储数量限制控制改进`
* 1.0.7
+ 修复了选择英文和中文以外的语言时的崩溃问题
+ 黑雾更新后使用和平模式保存的存档现在也可以转换为战斗模式了
* 1.0.6
+ 在加载旧存档时将其转换为战斗模式
* 1.0.5
+ 支持游戏版本0.10.28.20759
+ 保存蓝图前对建筑进行排序,以减少生成的蓝图数据大小
* 1.0.4
+ 添加了新功能:`脱离网格建造和小角度旋转`
+ 修复了当功能启用但游戏使用不同的mod配置文件启动时窗口位置无法正确恢复和不可拖动改变大小的问题
* 1.0.3
+ 添加了新功能:`快速建造轨道采集器`
+ 为`初始化行星``快速拆除所有建筑``初始化戴森球``快速拆除戴森壳`添加了确认弹窗
+ 修复了`移除建造数量和范围限制`在建造大量传送带时可能导致的错误
+ 修复了在不使用游戏内菜单退出游戏时窗口位置无法正确保存的问题
* 1.0.2
+ 重新设计了配置面板,使布局更清晰
+ 添加了两个新选项:
- 可调整游戏窗口大小(可最大化和拖动边框)
- 记住上次退出时的窗口位置和大小
* 1.0.1
+ 修复了返回标题界面后设置按钮文本和提示信息不正确的问题
+ 修复了`当可用节点全部造完时停止弹射`选项启用时返回标题界面可能导致崩溃的问题
+ 添加了一个补丁,修复了`矿物利用`升级到8000级以上时弹出警告的bug
* 1.0.0
+ 初始版本
+ 从[MechaDronesTweaks](https://dsp.thunderstore.io/package/soarqin/MechaDronesTweaks/)和[CheatEnabler](https://dsp.thunderstore.io/package/soarqin/CheatEnabler/)移动了部分功能过来
</details>

View File

@@ -1,32 +0,0 @@
using System;
using HarmonyLib;
namespace UXAssist.Common;
public class GameLogic: PatchImpl<GameLogic>
{
public static Action OnDataLoaded;
public static Action OnGameBegin;
public static Action OnGameEnd;
[HarmonyPostfix]
[HarmonyPatch(typeof(VFPreload), nameof(VFPreload.InvokeOnLoadWorkEnded))]
public static void VFPreload_InvokeOnLoadWorkEnded_Postfix()
{
OnDataLoaded?.Invoke();
}
[HarmonyPostfix, HarmonyPriority(Priority.First)]
[HarmonyPatch(typeof(GameMain), nameof(GameMain.Begin))]
public static void GameMain_Begin_Postfix()
{
OnGameBegin?.Invoke();
}
[HarmonyPostfix, HarmonyPriority(Priority.Last)]
[HarmonyPatch(typeof(GameMain), nameof(GameMain.End))]
public static void GameMain_End_Postfix()
{
OnGameEnd?.Invoke();
}
}

View File

@@ -1,39 +0,0 @@
using BepInEx.Configuration;
using CommonAPI.Systems;
using UnityEngine;
namespace UXAssist.Common;
public static class KeyBindings
{
public static PressKeyBind RegisterKeyBinding(BuiltinKey key)
{
return CustomKeyBindSystem.RegisterKeyBindWithReturn<PressKeyBind>(key);
}
public static CombineKey FromKeyboardShortcut(KeyboardShortcut shortcut)
{
byte mod = 0;
foreach (var modifier in shortcut.Modifiers)
{
mod |= modifier switch
{
KeyCode.LeftShift => 1,
KeyCode.RightShift => 1,
KeyCode.LeftControl => 2,
KeyCode.RightControl => 2,
KeyCode.LeftAlt => 4,
KeyCode.RightAlt => 4,
_ => 0
};
}
return new CombineKey((int)shortcut.MainKey, mod, ECombineKeyAction.OnceClick, false);
}
public static bool IsKeyPressing(this PressKeyBind keyBind)
{
var defBind = keyBind.defaultBind;
var overrideKey = VFInput.override_keys[defBind.id];
return overrideKey.IsNull() ? defBind.key.GetKey() : overrideKey.GetKey();
}
}

View File

@@ -1,59 +0,0 @@
using System;
using System.Linq;
using System.Reflection;
using HarmonyLib;
namespace UXAssist.Common;
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
public class PatchGuidAttribute(string guid) : Attribute
{
public string Guid { get; } = guid;
}
public enum PatchCallbackFlag
{
// By default, OnEnable() is called After patch applied, set this flag to call it before patch is applied
CallOnEnableBeforePatch,
// By default, OnDisable() is called Before patch removed, set this flag to call it after patch is removed
CallOnDisableAfterUnpatch,
}
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = true)]
public class PatchSetCallbackFlagAttribute(PatchCallbackFlag flag) : Attribute
{
public PatchCallbackFlag Flag { get; } = flag;
}
public class PatchImpl<T> where T : PatchImpl<T>, new()
{
protected static T Instance { get; } = new();
protected Harmony _patch;
public static void Enable(bool enable)
{
var thisInstance = Instance;
if (enable)
{
if (thisInstance._patch != null) return;
var guid = typeof(T).GetCustomAttribute<PatchGuidAttribute>()?.Guid ?? $"PatchImpl.{typeof(T).FullName ?? typeof(T).ToString()}";
var callOnEnableBefore = typeof(T).GetCustomAttributes<PatchSetCallbackFlagAttribute>().Any(n => n.Flag == PatchCallbackFlag.CallOnEnableBeforePatch);
if (callOnEnableBefore) thisInstance.OnEnable();
thisInstance._patch = Harmony.CreateAndPatchAll(typeof(T), guid);
if (!callOnEnableBefore) thisInstance.OnEnable();
return;
}
if (thisInstance._patch == null) return;
var callOnDisableAfter = typeof(T).GetCustomAttributes<PatchSetCallbackFlagAttribute>().Any(n => n.Flag == PatchCallbackFlag.CallOnDisableAfterUnpatch);
if (!callOnDisableAfter) thisInstance.OnDisable();
thisInstance._patch.UnpatchSelf();
thisInstance._patch = null;
if (callOnDisableAfter) thisInstance.OnDisable();
}
public static Harmony GetHarmony() => Instance._patch;
protected virtual void OnEnable() { }
protected virtual void OnDisable() { }
}

View File

@@ -1,61 +0,0 @@
using System;
using System.IO;
using System.Linq;
using System.Reflection;
using UnityEngine;
namespace UXAssist.Common;
public static class Util
{
public static Type[] GetTypesFiltered(Assembly assembly, Func<Type, bool> predicate)
{
return assembly.GetTypes().Where(predicate).ToArray();
}
public static Type[] GetTypesInNamespace(Assembly assembly, string nameSpace) => GetTypesFiltered(assembly, t => string.Equals(t.Namespace, nameSpace, StringComparison.Ordinal));
public static byte[] LoadEmbeddedResource(string path, Assembly assembly = null)
{
if (assembly == null)
{
assembly = Assembly.GetCallingAssembly();
}
var info = assembly.GetName();
var name = info.Name;
using var stream = assembly.GetManifestResourceStream($"{name}.{path.Replace('/', '.')}")!;
var buffer = new byte[stream.Length];
_ = stream.Read(buffer, 0, buffer.Length);
return buffer;
}
public static Texture2D LoadTexture(string path)
{
var fileData = File.ReadAllBytes(path);
var tex = new Texture2D(2, 2);
tex.LoadImage(fileData);
return tex;
}
public static Sprite LoadSprite(string path)
{
var tex = LoadTexture(path);
return Sprite.Create(tex, new Rect(0, 0, tex.width, tex.height), new Vector2(0.5f, 0.5f));
}
public static Texture2D LoadEmbeddedTexture(string path, Assembly assembly = null)
{
var fileData = LoadEmbeddedResource(path, assembly);
var tex = new Texture2D(2, 2);
tex.LoadImage(fileData);
return tex;
}
public static Sprite LoadEmbeddedSprite(string path, Assembly assembly = null)
{
var tex = LoadEmbeddedTexture(path, assembly);
return Sprite.Create(tex, new Rect(0, 0, tex.width, tex.height), new Vector2(0.5f, 0.5f));
}
public static string PluginFolder(Assembly assembly = null) => Path.GetDirectoryName((assembly == null ? Assembly.GetCallingAssembly() : assembly).Location);
}

View File

@@ -3,91 +3,55 @@ using System.Runtime.InteropServices;
namespace UXAssist.Common; namespace UXAssist.Common;
[Flags]
public enum WindowStyles: int
{
WS_BORDER = 0x00800000,
WS_CAPTION = 0x00C00000,
WS_CHILD = 0x40000000,
WS_CHILDWINDOW = 0x40000000,
WS_CLIPCHILDREN = 0x02000000,
WS_CLIPSIBLINGS = 0x04000000,
WS_DISABLED = 0x08000000,
WS_DLGFRAME = 0x00400000,
WS_GROUP = 0x00020000,
WS_HSCROLL = 0x00100000,
WS_ICONIC = 0x20000000,
WS_MAXIMIZE = 0x01000000,
WS_MAXIMIZEBOX = 0x00010000,
WS_MINIMIZE = 0x20000000,
WS_MINIMIZEBOX = 0x00020000,
WS_OVERLAPPED = 0x00000000,
WS_OVERLAPPEDWINDOW = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,
WS_POPUP = unchecked((int)0x80000000),
WS_POPUPWINDOW = WS_POPUP | WS_BORDER | WS_SYSMENU,
WS_SIZEBOX = 0x00040000,
WS_SYSMENU = 0x00080000,
WS_TABSTOP = 0x00010000,
WS_THICKFRAME = 0x00040000,
WS_TILED = 0x00000000,
WS_TILEDWINDOW = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,
WS_VISIBLE = 0x10000000,
WS_VSCROLL = 0x00200000
}
[Flags]
public enum WindowLongFlags: int
{
GWL_EXSTYLE = -20,
GWLP_HINSTANCE = -6,
GWLP_HWNDPARENT = -8,
GWLP_ID = -12,
GWL_STYLE = -16,
GWLP_USERDATA = -21,
GWLP_WNDPROC = -4,
DWLP_DLGPROC = 0x4,
DWLP_MSGRESULT = 0,
DWLP_USER = 0x8
}
public static class WinApi public static class WinApi
{ {
#region Styles
public const int WS_BORDER = 0x00800000;
public const int WS_CAPTION = 0x00C00000;
public const int WS_CHILD = 0x40000000;
public const int WS_CHILDWINDOW = 0x40000000;
public const int WS_CLIPCHILDREN = 0x02000000;
public const int WS_CLIPSIBLINGS = 0x04000000;
public const int WS_DISABLED = 0x08000000;
public const int WS_DLGFRAME = 0x00400000;
public const int WS_GROUP = 0x00020000;
public const int WS_HSCROLL = 0x00100000;
public const int WS_ICONIC = 0x20000000;
public const int WS_MAXIMIZE = 0x01000000;
public const int WS_MAXIMIZEBOX = 0x00010000;
public const int WS_MINIMIZE = 0x20000000;
public const int WS_MINIMIZEBOX = 0x00020000;
public const int WS_OVERLAPPED = 0x00000000;
public const int WS_OVERLAPPEDWINDOW = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX;
public const int WS_POPUP = unchecked((int)0x80000000);
public const int WS_POPUPWINDOW = WS_POPUP | WS_BORDER | WS_SYSMENU;
public const int WS_SIZEBOX = 0x00040000;
public const int WS_SYSMENU = 0x00080000;
public const int WS_TABSTOP = 0x00010000;
public const int WS_THICKFRAME = 0x00040000;
public const int WS_TILED = 0x00000000;
public const int WS_TILEDWINDOW = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX;
public const int WS_VISIBLE = 0x10000000;
public const int WS_VSCROLL = 0x00200000;
#endregion
#region GetWindowLong and SetWindowLong Flags
public const int GWL_EXSTYLE = -20;
public const int GWLP_HINSTANCE = -6;
public const int GWLP_HWNDPARENT = -8;
public const int GWLP_ID = -12;
public const int GWL_STYLE = -16;
public const int GWLP_USERDATA = -21;
public const int GWLP_WNDPROC = -4;
public const int DWLP_DLGPROC = 0x4;
public const int DWLP_MSGRESULT = 0;
public const int DWLP_USER = 0x8;
#endregion
#region Priorities
public const int HIGH_PRIORITY_CLASS = 0x00000080;
public const int ABOVE_NORMAL_PRIORITY_CLASS = 0x00008000;
public const int NORMAL_PRIORITY_CLASS = 0x00000020;
public const int BELOW_NORMAL_PRIORITY_CLASS = 0x00004000;
public const int IDLE_PRIORITY_CLASS = 0x00000040;
#endregion
#region Messages
public const int WM_CREATE = 0x0001;
public const int WM_DESTROY = 0x0002;
public const int WM_MOVE = 0x0003;
public const int WM_SIZE = 0x0005;
public const int WM_ACTIVATE = 0x0006;
public const int WM_SETFOCUS = 0x0007;
public const int WM_KILLFOCUS = 0x0008;
public const int WM_ENABLE = 0x000A;
public const int WM_CLOSE = 0x0010;
public const int WM_QUIT = 0x0012;
public const int WM_SYSCOMMAND = 0x0112;
public const int WM_SIZING = 0x0214;
public const int WM_MOVING = 0x0216;
public const long SC_MOVE = 0xF010L;
#endregion
#region Errors
private const int ERROR_INSUFFICIENT_BUFFER = 122;
#endregion
#region Structs
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]
public struct Rect public struct Rect
@@ -95,15 +59,6 @@ public static class WinApi
public int Left, Top, Right, Bottom; public int Left, Top, Right, Bottom;
} }
#endregion
#region Functions
public delegate IntPtr WndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);
[DllImport("kernel32", ExactSpelling = true)]
public static extern int GetLastError();
[DllImport("user32", CharSet = CharSet.Unicode)] [DllImport("user32", CharSet = CharSet.Unicode)]
public static extern int GetWindowLong(IntPtr hwnd, int nIndex); public static extern int GetWindowLong(IntPtr hwnd, int nIndex);
@@ -111,10 +66,7 @@ public static class WinApi
public static extern int SetWindowLong(IntPtr hwnd, int nIndex, int dwNewLong); public static extern int SetWindowLong(IntPtr hwnd, int nIndex, int dwNewLong);
[DllImport("user32", CharSet = CharSet.Unicode)] [DllImport("user32", CharSet = CharSet.Unicode)]
public static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpClassName, string lpWindowName); public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32", ExactSpelling = true, SetLastError = true)]
public static extern int GetWindowThreadProcessId(IntPtr hwnd, out int lpdwProcessId);
[DllImport("user32", ExactSpelling = true)] [DllImport("user32", ExactSpelling = true)]
public static extern bool GetWindowRect(IntPtr hwnd, out Rect lpRect); public static extern bool GetWindowRect(IntPtr hwnd, out Rect lpRect);
@@ -122,164 +74,6 @@ public static class WinApi
[DllImport("user32", ExactSpelling = true)] [DllImport("user32", ExactSpelling = true)]
public static extern bool SetWindowPos(IntPtr hwnd, IntPtr hWndInsertAfter, int x, int y, int cx, int cy, int flags); public static extern bool SetWindowPos(IntPtr hwnd, IntPtr hWndInsertAfter, int x, int y, int cx, int cy, int flags);
[DllImport("user32", CharSet = CharSet.Unicode)]
public static extern bool SetWindowText(IntPtr hwnd, string lpString);
[DllImport("user32", ExactSpelling = true)] [DllImport("user32", ExactSpelling = true)]
public static extern bool MoveWindow(IntPtr hWnd, int x, int y, int nWidth, int nHeight, bool bRepaint); public static extern bool MoveWindow(IntPtr hWnd, int x, int y, int nWidth, int nHeight, bool bRepaint);
[DllImport("user32", ExactSpelling = true)]
public static extern IntPtr MonitorFromRect([In] ref Rect lpRect, uint dwFlags);
[DllImport("kernel32", ExactSpelling = true, SetLastError = true)]
public static extern bool GetProcessAffinityMask(IntPtr hProcess, out ulong lpProcessAffinityMask, out ulong lpSystemAffinityMask);
[DllImport("kernel32", ExactSpelling = true, SetLastError = true)]
public static extern IntPtr GetCurrentProcess();
[DllImport("kernel32", ExactSpelling = true)]
public static extern int GetCurrentProcessId();
[DllImport("kernel32", ExactSpelling = true)]
public static extern IntPtr GetConsoleWindow();
[DllImport("kernel32", ExactSpelling = true, SetLastError = true)]
public static extern bool SetProcessAffinityMask(IntPtr hProcess, ulong dwProcessAffinityMask);
// GetPriorityClass and SetPriorityClass
[DllImport("kernel32", ExactSpelling = true, SetLastError = true)]
public static extern int GetPriorityClass(IntPtr hProcess);
[DllImport("kernel32", ExactSpelling = true, SetLastError = true)]
public static extern bool SetPriorityClass(IntPtr hProcess, int dwPriorityClass);
[DllImport("user32", CharSet = CharSet.Unicode)]
public static extern IntPtr SetWindowLongPtr(IntPtr hWnd, int nIndex, IntPtr dwNewLong);
[DllImport("user32", CharSet = CharSet.Unicode)]
public static extern IntPtr CallWindowProc(IntPtr lpPrevWndFunc, IntPtr hWnd, uint uMsg, IntPtr wParam, IntPtr lParam);
#endregion
#region GetLogicalProcessorInformationEx
[Flags]
private enum LOGICAL_PROCESSOR_RELATIONSHIP
{
RelationProcessorCore,
RelationNumaNode,
RelationCache,
RelationProcessorPackage,
RelationGroup,
RelationAll = 0xffff
}
[StructLayout(LayoutKind.Sequential, Pack = 4)]
private struct GROUP_AFFINITY
{
public nuint Mask;
public ushort Group;
public ushort Reserved0;
public ushort Reserved1;
public ushort Reserved3;
}
[StructLayout(LayoutKind.Sequential, Pack = 4)]
private struct PROCESSOR_RELATIONSHIP
{
public byte Flags;
public byte EfficiencyClass;
public ushort Reserved0;
public uint Reserved1;
public uint Reserved2;
public uint Reserved3;
public uint Reserved4;
public ushort Reserved5;
public ushort GroupCount;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)]
public GROUP_AFFINITY[] GroupMask;
}
[StructLayout(LayoutKind.Sequential)]
private struct SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX
{
public LOGICAL_PROCESSOR_RELATIONSHIP Relationship;
public uint Size;
public PROCESSOR_RELATIONSHIP Processor;
}
[DllImport("kernel32", SetLastError = true)]
private static extern bool GetLogicalProcessorInformationEx(
LOGICAL_PROCESSOR_RELATIONSHIP relationshipType,
IntPtr buffer,
ref uint returnLength
);
public struct LogicalProcessorDetails
{
public int CoreCount;
public int ThreadCount;
public int PerformanceCoreCount;
public int EfficiencyCoreCount;
public ulong PerformanceCoreMask;
public ulong EfficiencyCoreMask;
public bool HybridArchitecture => PerformanceCoreCount > 0 && EfficiencyCoreCount > 0;
}
public static LogicalProcessorDetails GetLogicalProcessorDetails()
{
uint returnLength = 0;
GetLogicalProcessorInformationEx(LOGICAL_PROCESSOR_RELATIONSHIP.RelationProcessorCore, IntPtr.Zero, ref returnLength);
var result = new LogicalProcessorDetails();
if (Marshal.GetLastWin32Error() != ERROR_INSUFFICIENT_BUFFER) return result;
var ptr = Marshal.AllocHGlobal((int)returnLength);
try
{
if (!GetLogicalProcessorInformationEx(LOGICAL_PROCESSOR_RELATIONSHIP.RelationProcessorCore, ptr, ref returnLength))
return result;
uint offset = 0;
var item = ptr;
while (offset < returnLength)
{
var buffer = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX)Marshal.PtrToStructure(item, typeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX));
offset += buffer.Size;
item += (int)buffer.Size;
if (buffer.Relationship != LOGICAL_PROCESSOR_RELATIONSHIP.RelationProcessorCore) continue;
result.CoreCount++;
var mask = buffer.Processor.GroupMask[0].Mask;
var tcount = CountBitsSet(mask);
result.ThreadCount += tcount;
if (buffer.Processor.EfficiencyClass > 0)
{
result.PerformanceCoreCount++;
result.PerformanceCoreMask |= mask;
}
else
{
result.EfficiencyCoreCount++;
result.EfficiencyCoreMask |= mask;
}
}
}
finally
{
Marshal.FreeHGlobal(ptr);
}
return result;
int CountBitsSet(ulong mask)
{
var count = 0;
while (mask != 0)
{
mask &= mask - 1;
count++;
}
return count;
}
}
#endregion
} }

View File

@@ -0,0 +1,458 @@
using System.Collections.Generic;
using System.IO;
using System.Reflection.Emit;
using BepInEx.Configuration;
using HarmonyLib;
namespace UXAssist;
public static class DysonSpherePatch
{
public static ConfigEntry<bool> StopEjectOnNodeCompleteEnabled;
public static ConfigEntry<bool> OnlyConstructNodesEnabled;
private static Harmony _dysonSpherePatch;
public static void Init()
{
_dysonSpherePatch ??= Harmony.CreateAndPatchAll(typeof(DysonSpherePatch));
StopEjectOnNodeCompleteEnabled.SettingChanged += (_, _) => StopEjectOnNodeComplete.Enable(StopEjectOnNodeCompleteEnabled.Value);
OnlyConstructNodesEnabled.SettingChanged += (_, _) => OnlyConstructNodes.Enable(OnlyConstructNodesEnabled.Value);
StopEjectOnNodeComplete.Enable(StopEjectOnNodeCompleteEnabled.Value);
OnlyConstructNodes.Enable(OnlyConstructNodesEnabled.Value);
}
public static void Uninit()
{
StopEjectOnNodeComplete.Enable(false);
OnlyConstructNodes.Enable(false);
_dysonSpherePatch?.UnpatchSelf();
_dysonSpherePatch = null;
}
public static void InitCurrentDysonSphere(int index)
{
var star = GameMain.localStar;
if (star == null) return;
var dysonSpheres = GameMain.data?.dysonSpheres;
if (dysonSpheres == null) return;
if (index < 0)
{
if (dysonSpheres[star.index] == null) return;
var dysonSphere = new DysonSphere();
dysonSpheres[star.index] = dysonSphere;
dysonSphere.Init(GameMain.data, star);
dysonSphere.ResetNew();
return;
}
var ds = dysonSpheres[star.index];
if (ds?.layersIdBased[index] == null) return;
var pool = ds.rocketPool;
for (var id = ds.rocketCursor - 1; id > 0; id--)
{
if (pool[id].id != id) continue;
if (pool[id].nodeLayerId != index) continue;
ds.RemoveDysonRocket(id);
}
ds.RemoveLayer(index);
}
[HarmonyTranspiler]
[HarmonyPriority(Priority.First)]
[HarmonyPatch(typeof(DysonNode), nameof(DysonNode.ConstructCp))]
private static IEnumerable<CodeInstruction> DysonSpherePatch_DysonNode_ConstructCp_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{
var matcher = new CodeMatcher(instructions, generator);
matcher.MatchBack(false,
new CodeMatch(OpCodes.Ldc_I4_0),
new CodeMatch(OpCodes.Callvirt, AccessTools.Method(typeof(DysonShell), nameof(DysonShell.Construct)))
).Advance(3).InsertAndAdvance(
// node._cpReq = node._cpReq - 1;
new CodeInstruction(OpCodes.Ldarg_0),
new CodeInstruction(OpCodes.Ldarg_0),
new CodeInstruction(OpCodes.Ldfld, AccessTools.Field(typeof(DysonNode), nameof(DysonNode._cpReq))),
new CodeInstruction(OpCodes.Ldc_I4_1),
new CodeInstruction(OpCodes.Sub),
new CodeInstruction(OpCodes.Stfld, AccessTools.Field(typeof(DysonNode), nameof(DysonNode._cpReq)))
);
// Remove use of RecalcCpReq()
matcher.MatchForward(false,
new CodeMatch(OpCodes.Ldarg_0),
new CodeMatch(OpCodes.Call, AccessTools.Method(typeof(DysonNode), nameof(DysonNode.RecalcCpReq)))
);
var labels = matcher.Labels;
matcher.RemoveInstructions(2).Labels.AddRange(labels);
return matcher.InstructionEnumeration();
}
private static class StopEjectOnNodeComplete
{
private static Harmony _patch;
private static HashSet<int>[] _nodeForAbsorb;
private static bool _initialized;
public static void Enable(bool on)
{
if (on)
{
InitNodeForAbsorb();
_patch ??= Harmony.CreateAndPatchAll(typeof(StopEjectOnNodeComplete));
}
else
{
_patch?.UnpatchSelf();
_patch = null;
_initialized = false;
_nodeForAbsorb = null;
}
}
private static void InitNodeForAbsorb()
{
_initialized = false;
_nodeForAbsorb = null;
var data = GameMain.data;
var galaxy = data?.galaxy;
if (galaxy == null) return;
var galaxyStarCount = galaxy.starCount;
_nodeForAbsorb = new HashSet<int>[galaxyStarCount];
var spheres = data.dysonSpheres;
if (spheres == null) return;
foreach (var sphere in spheres)
{
if (sphere?.layersSorted == null) continue;
var starIndex = sphere.starData.index;
if (starIndex >= galaxyStarCount) continue;
foreach (var layer in sphere.layersSorted)
{
if (layer == null) continue;
for (var i = layer.nodeCursor - 1; i > 0; i--)
{
var node = layer.nodePool[i];
if (node == null || node.id != i || node.sp < node.spMax || node.cpReqOrder == 0) continue;
SetNodeForAbsorb(starIndex, layer.id, node.id, true);
}
}
}
_initialized = true;
}
private static void SetNodeForAbsorb(int index, int layerId, int nodeId, bool canAbsorb)
{
ref var comp = ref _nodeForAbsorb[index];
comp ??= [];
var idx = nodeId * 10 + layerId;
if (canAbsorb)
comp.Add(idx);
else
comp.Remove(idx);
}
private static void UpdateNodeForAbsorbOnSpChange(DysonNode node)
{
if (!_initialized) return;
if (node.sp < node.spMax || node.cpReqOrder <= 0) return;
var shells = node.shells;
if (shells.Count == 0) return;
SetNodeForAbsorb(shells[0].dysonSphere.starData.index, node.layerId, node.id, true);
}
private static void UpdateNodeForAbsorbOnCpChange(DysonNode node)
{
if (!_initialized) return;
if (node.sp < node.spMax || node.cpReqOrder > 0) return;
var shells = node.shells;
if (shells.Count == 0) return;
SetNodeForAbsorb(shells[0].dysonSphere.starData.index, node.layerId, node.id, false);
}
private static bool AnyNodeForAbsorb(int starIndex)
{
var comp = _nodeForAbsorb[starIndex];
return comp != null && comp.Count > 0;
}
[HarmonyPostfix]
[HarmonyPatch(typeof(GameMain), nameof(GameMain.Begin))]
private static void GameMain_Begin_Postfix()
{
InitNodeForAbsorb();
}
[HarmonyPostfix]
[HarmonyPatch(typeof(GameMain), nameof(GameMain.End))]
private static void GameMain_End_Postfix()
{
_initialized = false;
_nodeForAbsorb = null;
}
[HarmonyPostfix]
[HarmonyPatch(typeof(DysonNode), nameof(DysonNode.RecalcCpReq))]
private static void DysonNode_RecalcCpReq_Postfix(DysonNode __instance)
{
UpdateNodeForAbsorbOnCpChange(__instance);
}
[HarmonyPrefix]
[HarmonyPatch(typeof(DysonSphereLayer), nameof(DysonSphereLayer.RemoveDysonNode))]
private static void DysonSphereLayer_RemoveDysonNode_Prefix(DysonSphereLayer __instance, int nodeId)
{
if (_initialized)
SetNodeForAbsorb(__instance.starData.index, __instance.id, nodeId, false);
}
[HarmonyPrefix]
[HarmonyPatch(typeof(DysonSphere), nameof(DysonSphere.ResetNew))]
private static void DysonSphere_ResetNew_Prefix(DysonSphere __instance)
{
var starIndex = __instance.starData.index;
if (_nodeForAbsorb[starIndex] == null) return;
_nodeForAbsorb[starIndex].Clear();
_nodeForAbsorb[starIndex] = null;
}
[HarmonyTranspiler]
[HarmonyPatch(typeof(EjectorComponent), nameof(EjectorComponent.InternalUpdate))]
private static IEnumerable<CodeInstruction> EjectorComponent_InternalUpdate_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{
var matcher = new CodeMatcher(instructions, generator);
matcher.MatchForward(false,
// if (this.orbitId == 0
new CodeMatch(OpCodes.Ldarg_0),
new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(EjectorComponent), nameof(EjectorComponent.orbitId))),
new CodeMatch(OpCodes.Brtrue)
).Advance(2).Insert(
// || !StopEjectOnNodeComplete.AnyNodeForAbsorb(this.starData.index))
new CodeInstruction(OpCodes.Ldarg_2),
new CodeInstruction(OpCodes.Ldfld, AccessTools.Field(typeof(DysonSwarm), nameof(DysonSwarm.starData))),
new CodeInstruction(OpCodes.Ldfld, AccessTools.Field(typeof(StarData), nameof(StarData.index))),
new CodeInstruction(OpCodes.Call, AccessTools.Method(typeof(StopEjectOnNodeComplete), nameof(StopEjectOnNodeComplete.AnyNodeForAbsorb))),
new CodeInstruction(OpCodes.And)
);
return matcher.InstructionEnumeration();
}
[HarmonyTranspiler]
[HarmonyPatch(typeof(DysonNode), nameof(DysonNode.ConstructSp))]
private static IEnumerable<CodeInstruction> DysonNode_ConstructSp_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{
var matcher = new CodeMatcher(instructions, generator);
matcher.Start().MatchForward(false,
new CodeMatch(OpCodes.Stfld, AccessTools.Field(typeof(DysonNode), nameof(DysonNode.sp)))
).Advance(1);
var labels = matcher.Labels;
matcher.Labels = [];
matcher.Insert(
new CodeInstruction(OpCodes.Ldarg_0).WithLabels(labels),
new CodeInstruction(OpCodes.Call, AccessTools.Method(typeof(StopEjectOnNodeComplete), nameof(StopEjectOnNodeComplete.UpdateNodeForAbsorbOnSpChange)))
);
return matcher.InstructionEnumeration();
}
[HarmonyTranspiler]
[HarmonyPatch(typeof(DysonNode), nameof(DysonNode.ConstructCp))]
private static IEnumerable<CodeInstruction> DysonNode_ConstructCp_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{
var matcher = new CodeMatcher(instructions, generator);
matcher.MatchBack(false,
// Search for previous patch:
// node._cpReq = node._cpReq - 1;
new CodeMatch(OpCodes.Ldarg_0),
new CodeMatch(OpCodes.Ldarg_0),
new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(DysonNode), nameof(DysonNode._cpReq))),
new CodeMatch(OpCodes.Ldc_I4_1),
new CodeMatch(OpCodes.Sub),
new CodeMatch(OpCodes.Stfld, AccessTools.Field(typeof(DysonNode), nameof(DysonNode._cpReq)))
).Advance(6).Insert(
new CodeInstruction(OpCodes.Ldarg_0),
new CodeInstruction(OpCodes.Call, AccessTools.Method(typeof(StopEjectOnNodeComplete), nameof(StopEjectOnNodeComplete.UpdateNodeForAbsorbOnCpChange)))
);
return matcher.InstructionEnumeration();
}
}
private static class OnlyConstructNodes
{
private static Harmony _patch;
public static void Enable(bool on)
{
if (on)
{
_patch ??= Harmony.CreateAndPatchAll(typeof(OnlyConstructNodes));
}
else
{
_patch?.UnpatchSelf();
_patch = null;
}
var spheres = GameMain.data?.dysonSpheres;
if (spheres == null) return;
foreach (var sphere in spheres)
{
if (sphere == null) continue;
sphere.CheckAutoNodes();
if (sphere.autoNodeCount > 0) continue;
sphere.PickAutoNode();
sphere.PickAutoNode();
sphere.PickAutoNode();
sphere.PickAutoNode();
}
}
[HarmonyTranspiler]
[HarmonyPatch(typeof(FactorySystem), nameof(FactorySystem.GameTick), typeof(long), typeof(bool))]
[HarmonyPatch(typeof(FactorySystem), nameof(FactorySystem.GameTick), typeof(long), typeof(bool), typeof(int), typeof(int), typeof(int))]
private static IEnumerable<CodeInstruction> FactorySystem_GameTick_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{
/* for (int m = min; m < max; m++)
* =>
* for (int m = min + (gameTick % 9); m < max; m += 9)
*/
var matcher = new CodeMatcher(instructions, generator);
matcher.MatchForward(false,
new CodeMatch(OpCodes.Ldarg_0),
new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(FactorySystem), nameof(FactorySystem.ejectorPool))),
new CodeMatch(instr => instr.opcode == OpCodes.Ldloc || instr.opcode == OpCodes.Ldloc_S),
new CodeMatch(OpCodes.Ldelema, typeof(EjectorComponent)),
new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(EjectorComponent), nameof(EjectorComponent.id))),
new CodeMatch(instr => instr.opcode == OpCodes.Ldloc || instr.opcode == OpCodes.Ldloc_S),
new CodeMatch(instr => instr.opcode == OpCodes.Bne_Un || instr.opcode == OpCodes.Bne_Un_S)
).Advance(-2).InsertAndAdvance(
new CodeInstruction(OpCodes.Ldarg_1),
new CodeInstruction(OpCodes.Ldc_I8, 9L),
new CodeInstruction(OpCodes.Rem),
new CodeInstruction(OpCodes.Conv_I4),
new CodeInstruction(OpCodes.Add)
).Advance(9).MatchForward(false,
new CodeMatch(OpCodes.Stfld, AccessTools.Field(typeof(SignData), nameof(SignData.signType))),
new CodeMatch(instr => instr.opcode == OpCodes.Ldloc || instr.opcode == OpCodes.Ldloc_S),
new CodeMatch(OpCodes.Ldc_I4_1)
).Advance(2).Set(OpCodes.Ldc_I4_S, 9);
return matcher.InstructionEnumeration();
}
[HarmonyTranspiler]
[HarmonyPatch(typeof(EjectorComponent), nameof(EjectorComponent.InternalUpdate))]
private static IEnumerable<CodeInstruction> EjectorComponent_InternalUpdate_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{
/* int num3 = (int)(power * 10000f * (1f + num2) + 0.1f);
* =>
* int num3 = (int)(power * 90000f * (1f + num2) + 0.1f);
*/
var matcher = new CodeMatcher(instructions, generator);
matcher.MatchForward(false,
new CodeMatch(instr => instr.opcode == OpCodes.Ldc_R4 && instr.OperandIs(10000f))
).Operand = 90000f;
return matcher.InstructionEnumeration();
}
[HarmonyTranspiler]
[HarmonyPatch(typeof(FactorySystem), nameof(FactorySystem.NewEjectorComponent))]
private static IEnumerable<CodeInstruction> FactorySystem_NewEjectorComponent_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{
/* this.ejectorPool[num2].chargeSpend = desc.ejectorChargeFrame * 10000;
* this.ejectorPool[num2].coldSpend = desc.ejectorColdFrame * 10000;
* =>
* this.ejectorPool[num2].chargeSpend = (desc.ejectorChargeFrame + desc.ejectorColdFrame) * 10000;
* this.ejectorPool[num2].coldSpend = 0;
*/
var matcher = new CodeMatcher(instructions, generator);
matcher.MatchForward(false,
new CodeMatch(OpCodes.Ldarg_0),
new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(FactorySystem), nameof(FactorySystem.ejectorPool))),
new CodeMatch(OpCodes.Ldloc_0),
new CodeMatch(OpCodes.Ldelema, typeof(EjectorComponent)),
new CodeMatch(OpCodes.Ldarg_2),
new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(PrefabDesc), nameof(PrefabDesc.ejectorChargeFrame))),
new CodeMatch(OpCodes.Ldc_I4, 10000),
new CodeMatch(OpCodes.Mul),
new CodeMatch(OpCodes.Stfld, AccessTools.Field(typeof(EjectorComponent), nameof(EjectorComponent.chargeSpend))),
new CodeMatch(OpCodes.Ldarg_0),
new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(FactorySystem), nameof(FactorySystem.ejectorPool))),
new CodeMatch(OpCodes.Ldloc_0),
new CodeMatch(OpCodes.Ldelema, typeof(EjectorComponent)),
new CodeMatch(OpCodes.Ldarg_2),
new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(PrefabDesc), nameof(PrefabDesc.ejectorColdFrame))),
new CodeMatch(OpCodes.Ldc_I4, 10000),
new CodeMatch(OpCodes.Mul),
new CodeMatch(OpCodes.Stfld, AccessTools.Field(typeof(EjectorComponent), nameof(EjectorComponent.coldSpend)))
).RemoveInstructions(18).Insert(
new CodeInstruction(OpCodes.Ldarg_0),
new CodeInstruction(OpCodes.Ldfld, AccessTools.Field(typeof(FactorySystem), nameof(FactorySystem.ejectorPool))),
new CodeInstruction(OpCodes.Ldloc_0),
new CodeInstruction(OpCodes.Ldelema, typeof(EjectorComponent)),
new CodeInstruction(OpCodes.Ldarg_2),
new CodeInstruction(OpCodes.Ldfld, AccessTools.Field(typeof(PrefabDesc), nameof(PrefabDesc.ejectorChargeFrame))),
new CodeInstruction(OpCodes.Ldarg_2),
new CodeInstruction(OpCodes.Ldfld, AccessTools.Field(typeof(PrefabDesc), nameof(PrefabDesc.ejectorColdFrame))),
new CodeInstruction(OpCodes.Add),
new CodeInstruction(OpCodes.Ldc_I4, 10000),
new CodeInstruction(OpCodes.Mul),
new CodeInstruction(OpCodes.Stfld, AccessTools.Field(typeof(EjectorComponent), nameof(EjectorComponent.chargeSpend))),
new CodeInstruction(OpCodes.Ldarg_0),
new CodeInstruction(OpCodes.Ldfld, AccessTools.Field(typeof(FactorySystem), nameof(FactorySystem.ejectorPool))),
new CodeInstruction(OpCodes.Ldloc_0),
new CodeInstruction(OpCodes.Ldelema, typeof(EjectorComponent)),
new CodeInstruction(OpCodes.Ldc_I4_0),
new CodeInstruction(OpCodes.Stfld, AccessTools.Field(typeof(EjectorComponent), nameof(EjectorComponent.coldSpend)))
);
return matcher.InstructionEnumeration();
}
[HarmonyTranspiler]
[HarmonyPatch(typeof(EjectorComponent), nameof(EjectorComponent.Import))]
private static IEnumerable<CodeInstruction> EjectorComponent_Import_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{
/* this.chargeSpend = r.ReadInt32();
* this.coldSpend = r.ReadInt32();
* =>
* this.chargeSpend = r.ReadInt32() + r.ReadInt32();
* this.coldSpend = 0;
*/
var matcher = new CodeMatcher(instructions, generator);
matcher.MatchForward(false,
new CodeMatch(OpCodes.Ldarg_0),
new CodeMatch(OpCodes.Ldarg_1),
new CodeMatch(OpCodes.Callvirt, AccessTools.Method(typeof(BinaryReader), nameof(BinaryReader.ReadInt32))),
new CodeMatch(OpCodes.Stfld, AccessTools.Field(typeof(EjectorComponent), nameof(EjectorComponent.chargeSpend))),
new CodeMatch(OpCodes.Ldarg_0),
new CodeMatch(OpCodes.Ldarg_1),
new CodeMatch(OpCodes.Callvirt, AccessTools.Method(typeof(BinaryReader), nameof(BinaryReader.ReadInt32))),
new CodeMatch(OpCodes.Stfld, AccessTools.Field(typeof(EjectorComponent), nameof(EjectorComponent.coldSpend)))
).RemoveInstructions(8).Insert(
new CodeInstruction(OpCodes.Ldarg_0),
new CodeInstruction(OpCodes.Ldarg_1),
new CodeInstruction(OpCodes.Callvirt, AccessTools.Method(typeof(BinaryReader), nameof(BinaryReader.ReadInt32))),
new CodeInstruction(OpCodes.Ldarg_1),
new CodeInstruction(OpCodes.Callvirt, AccessTools.Method(typeof(BinaryReader), nameof(BinaryReader.ReadInt32))),
new CodeInstruction(OpCodes.Add),
new CodeInstruction(OpCodes.Stfld, AccessTools.Field(typeof(EjectorComponent), nameof(EjectorComponent.chargeSpend))),
new CodeInstruction(OpCodes.Ldarg_0),
new CodeInstruction(OpCodes.Ldc_I4_0),
new CodeInstruction(OpCodes.Stfld, AccessTools.Field(typeof(EjectorComponent), nameof(EjectorComponent.coldSpend)))
);
return matcher.InstructionEnumeration();
}
[HarmonyTranspiler]
[HarmonyPatch(typeof(DysonNode), nameof(DysonNode.spReqOrder), MethodType.Getter)]
private static IEnumerable<CodeInstruction> DysonNode_spReqOrder_Getter_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{
var matcher = new CodeMatcher(instructions, generator);
matcher.MatchForward(false,
new CodeMatch(OpCodes.Ldarg_0),
new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(DysonNode), nameof(DysonNode._spReq)))
).Advance(1).SetInstructionAndAdvance(
new CodeInstruction(OpCodes.Ldfld, AccessTools.Field(typeof(DysonNode), nameof(DysonNode.spMax)))
).Insert(
new CodeInstruction(OpCodes.Ldarg_0),
new CodeInstruction(OpCodes.Ldfld, AccessTools.Field(typeof(DysonNode), nameof(DysonNode.sp))),
new CodeInstruction(OpCodes.Sub)
);
return matcher.InstructionEnumeration();
}
}
}

836
UXAssist/FactoryPatch.cs Normal file
View File

@@ -0,0 +1,836 @@
using System;
using System.Collections.Generic;
using System.Reflection.Emit;
using BepInEx.Configuration;
using HarmonyLib;
using UnityEngine;
using UnityEngine.EventSystems;
namespace UXAssist;
public static class FactoryPatch
{
public static ConfigEntry<bool> UnlimitInteractiveEnabled;
public static ConfigEntry<bool> RemoveSomeConditionEnabled;
public static ConfigEntry<bool> NightLightEnabled;
public static ConfigEntry<bool> RemoveBuildRangeLimitEnabled;
public static ConfigEntry<bool> LargerAreaForUpgradeAndDismantleEnabled;
public static ConfigEntry<bool> LargerAreaForTerraformEnabled;
public static ConfigEntry<bool> OffGridBuildingEnabled;
public static ConfigEntry<bool> LogisticsCapacityTweaksEnabled;
private static Harmony _factoryPatch;
public static void Init()
{
UnlimitInteractiveEnabled.SettingChanged += (_, _) => UnlimitInteractive.Enable(UnlimitInteractiveEnabled.Value);
RemoveSomeConditionEnabled.SettingChanged += (_, _) => RemoveSomeConditionBuild.Enable(RemoveSomeConditionEnabled.Value);
NightLightEnabled.SettingChanged += (_, _) => NightLight.Enable(NightLightEnabled.Value);
RemoveBuildRangeLimitEnabled.SettingChanged += (_, _) => RemoveBuildRangeLimit.Enable(RemoveBuildRangeLimitEnabled.Value);
LargerAreaForUpgradeAndDismantleEnabled.SettingChanged += (_, _) => LargerAreaForUpgradeAndDismantle.Enable(LargerAreaForUpgradeAndDismantleEnabled.Value);
LargerAreaForTerraformEnabled.SettingChanged += (_, _) => LargerAreaForTerraform.Enable(LargerAreaForTerraformEnabled.Value);
OffGridBuildingEnabled.SettingChanged += (_, _) => OffGridBuilding.Enable(OffGridBuildingEnabled.Value);
LogisticsCapacityTweaksEnabled.SettingChanged += (_, _) => LogisticsCapacityTweaks.Enable(LogisticsCapacityTweaksEnabled.Value);
UnlimitInteractive.Enable(UnlimitInteractiveEnabled.Value);
RemoveSomeConditionBuild.Enable(RemoveSomeConditionEnabled.Value);
NightLight.Enable(NightLightEnabled.Value);
RemoveBuildRangeLimit.Enable(RemoveBuildRangeLimitEnabled.Value);
LargerAreaForUpgradeAndDismantle.Enable(LargerAreaForUpgradeAndDismantleEnabled.Value);
LargerAreaForTerraform.Enable(LargerAreaForTerraformEnabled.Value);
OffGridBuilding.Enable(OffGridBuildingEnabled.Value);
LogisticsCapacityTweaks.Enable(LogisticsCapacityTweaksEnabled.Value);
_factoryPatch ??= Harmony.CreateAndPatchAll(typeof(FactoryPatch));
}
public static void Uninit()
{
RemoveSomeConditionBuild.Enable(false);
UnlimitInteractive.Enable(false);
NightLight.Enable(false);
RemoveBuildRangeLimit.Enable(false);
LargerAreaForUpgradeAndDismantle.Enable(false);
LargerAreaForTerraform.Enable(false);
OffGridBuilding.Enable(false);
LogisticsCapacityTweaks.Enable(false);
_factoryPatch?.UnpatchSelf();
_factoryPatch = null;
}
[HarmonyTranspiler]
[HarmonyPatch(typeof(ConnGizmoGraph), MethodType.Constructor)]
private static IEnumerable<CodeInstruction> ConnGizmoGraph_Constructor_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{
var matcher = new CodeMatcher(instructions, generator);
matcher.MatchForward(false,
new CodeMatch(ci => ci.opcode == OpCodes.Ldc_I4 && ci.OperandIs(256))
);
matcher.Repeat(m => m.SetAndAdvance(OpCodes.Ldc_I4, 2048));
return matcher.InstructionEnumeration();
}
[HarmonyTranspiler]
[HarmonyPatch(typeof(ConnGizmoGraph), nameof(ConnGizmoGraph.SetPointCount))]
private static IEnumerable<CodeInstruction> ConnGizmoGraph_SetPointCount_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{
var matcher = new CodeMatcher(instructions, generator);
matcher.MatchForward(false,
new CodeMatch(ci => ci.opcode == OpCodes.Ldc_I4 && ci.OperandIs(256))
);
matcher.Repeat(m => m.SetAndAdvance(OpCodes.Ldc_I4, 2048));
return matcher.InstructionEnumeration();
}
[HarmonyTranspiler]
[HarmonyPatch(typeof(BuildTool_Path), nameof(BuildTool_Path._OnInit))]
private static IEnumerable<CodeInstruction> BuildTool_Path__OnInit_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{
var matcher = new CodeMatcher(instructions, generator);
matcher.MatchForward(false,
new CodeMatch(ci => ci.opcode == OpCodes.Ldc_I4 && ci.OperandIs(160))
);
matcher.Repeat(m => m.SetAndAdvance(OpCodes.Ldc_I4, 2048));
return matcher.InstructionEnumeration();
}
[HarmonyTranspiler, HarmonyPatch(typeof(BuildTool_Reform), MethodType.Constructor)]
private static IEnumerable<CodeInstruction> BuildTool_Reform_Constructor_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{
var matcher = new CodeMatcher(instructions, generator);
matcher.MatchForward(false,
new CodeMatch(ci => ci.opcode == OpCodes.Ldc_I4_S && ci.OperandIs(100))
);
matcher.Repeat(m => m.SetAndAdvance(OpCodes.Ldc_I4, 900));
return matcher.InstructionEnumeration();
}
public static class NightLight
{
private static Harmony _patch;
private const float NightLightAngleX = -8;
private const float NightLightAngleY = -2;
public static bool Enabled;
private static bool _nightlightInitialized;
private static bool _mechaOnEarth;
private static AnimationState _sail;
private static Light _sunlight;
public static void Enable(bool on)
{
if (on)
{
Enabled = _mechaOnEarth;
_patch ??= Harmony.CreateAndPatchAll(typeof(NightLight));
return;
}
Enabled = false;
_patch?.UnpatchSelf();
_patch = null;
if (_sunlight == null) return;
_sunlight.transform.localEulerAngles = new Vector3(0f, 180f);
}
public static void LateUpdate()
{
if (_patch == null) return;
switch (_nightlightInitialized)
{
case false:
Ready();
break;
case true:
Go();
break;
}
}
private static void Ready()
{
if (!GameMain.isRunning || !GameMain.mainPlayer.controller.model.gameObject.activeInHierarchy) return;
if (_sail == null)
{
_sail = GameMain.mainPlayer.animator.sails[GameMain.mainPlayer.animator.sailAnimIndex];
}
_nightlightInitialized = true;
}
private static void Go()
{
if (!GameMain.isRunning)
{
End();
return;
}
if (_sail.enabled)
{
_mechaOnEarth = false;
Enabled = false;
if (_sunlight == null) return;
_sunlight.transform.localEulerAngles = new Vector3(0f, 180f);
_sunlight = null;
return;
}
if (!_mechaOnEarth)
{
if (_sunlight == null)
{
var simu = GameMain.universeSimulator;
if (simu)
_sunlight = simu.LocalStarSimulator()?.sunLight;
if (_sunlight == null) return;
}
_mechaOnEarth = true;
Enabled = NightLightEnabled.Value;
}
if (Enabled)
{
_sunlight.transform.rotation =
Quaternion.LookRotation(-GameMain.mainPlayer.transform.up + GameMain.mainPlayer.transform.forward * NightLightAngleX / 10f +
GameMain.mainPlayer.transform.right * NightLightAngleY / 10f);
}
}
private static void End()
{
_mechaOnEarth = false;
Enabled = false;
if (_sunlight != null)
{
_sunlight.transform.localEulerAngles = new Vector3(0f, 180f);
_sunlight = null;
}
_sail = null;
_nightlightInitialized = false;
}
[HarmonyTranspiler]
[HarmonyPatch(typeof(StarSimulator), "LateUpdate")]
private static IEnumerable<CodeInstruction> StarSimulator_LateUpdate_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{
// var vec = NightlightEnabled ? GameMain.mainPlayer.transform.up : __instance.transform.forward;
var matcher = new CodeMatcher(instructions, generator);
var label1 = generator.DefineLabel();
var label2 = generator.DefineLabel();
matcher.MatchForward(false,
new CodeMatch(OpCodes.Ldarg_0),
new CodeMatch(OpCodes.Call, AccessTools.PropertyGetter(typeof(Component), nameof(Component.transform)))
).InsertAndAdvance(
new CodeInstruction(OpCodes.Ldsfld, AccessTools.Field(typeof(NightLight), nameof(NightLight.Enabled))),
new CodeInstruction(OpCodes.Brfalse_S, label1),
new CodeInstruction(OpCodes.Call, AccessTools.PropertyGetter(typeof(GameMain), nameof(GameMain.mainPlayer))),
new CodeInstruction(OpCodes.Callvirt, AccessTools.PropertyGetter(typeof(Player), nameof(Player.transform))),
new CodeInstruction(OpCodes.Callvirt, AccessTools.PropertyGetter(typeof(Transform), nameof(Transform.up))),
new CodeInstruction(OpCodes.Stloc_0),
new CodeInstruction(OpCodes.Br_S, label2)
);
matcher.Labels.Add(label1);
matcher.MatchForward(false,
new CodeMatch(OpCodes.Stloc_0)
).Advance(1).Labels.Add(label2);
return matcher.InstructionEnumeration();
}
[HarmonyTranspiler]
[HarmonyPatch(typeof(PlanetSimulator), "LateUpdate")]
private static IEnumerable<CodeInstruction> PlanetSimulator_LateUpdate_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{
// var vec = (NightlightEnabled ? GameMain.mainPlayer.transform.up : (Quaternion.Inverse(localPlanet.runtimeRotation) * (__instance.planetData.star.uPosition - __instance.planetData.uPosition).normalized));
var matcher = new CodeMatcher(instructions, generator);
var label1 = generator.DefineLabel();
var label2 = generator.DefineLabel();
matcher.MatchForward(false,
new CodeMatch(OpCodes.Stloc_1)
).Advance(1).InsertAndAdvance(
new CodeInstruction(OpCodes.Ldsfld, AccessTools.Field(typeof(NightLight), nameof(NightLight.Enabled))),
new CodeInstruction(OpCodes.Brfalse_S, label1),
new CodeInstruction(OpCodes.Call, AccessTools.PropertyGetter(typeof(GameMain), nameof(GameMain.mainPlayer))),
new CodeInstruction(OpCodes.Callvirt, AccessTools.PropertyGetter(typeof(Player), nameof(Player.transform))),
new CodeInstruction(OpCodes.Callvirt, AccessTools.PropertyGetter(typeof(Transform), nameof(Transform.up))),
new CodeInstruction(OpCodes.Stloc_2),
new CodeInstruction(OpCodes.Br_S, label2)
);
matcher.Labels.Add(label1);
matcher.MatchForward(false,
new CodeMatch(OpCodes.Ldsfld, AccessTools.Field(typeof(FactoryModel), nameof(FactoryModel.whiteMode0)))
).Labels.Add(label2);
return matcher.InstructionEnumeration();
}
}
private static class UnlimitInteractive
{
private static Harmony _patch;
public static void Enable(bool enable)
{
if (enable)
{
_patch ??= Harmony.CreateAndPatchAll(typeof(UnlimitInteractive));
return;
}
_patch?.UnpatchSelf();
_patch = null;
}
[HarmonyTranspiler]
[HarmonyPatch(typeof(PlayerAction_Inspect), nameof(PlayerAction_Inspect.GetObjectSelectDistance))]
private static IEnumerable<CodeInstruction> PlayerAction_Inspect_GetObjectSelectDistance_Transpiler(IEnumerable<CodeInstruction> instructions)
{
yield return new CodeInstruction(OpCodes.Ldc_R4, 10000f);
yield return new CodeInstruction(OpCodes.Ret);
}
}
private static class RemoveSomeConditionBuild
{
private static Harmony _patch;
public static void Enable(bool on)
{
if (on)
{
_patch ??= Harmony.CreateAndPatchAll(typeof(RemoveSomeConditionBuild));
return;
}
_patch?.UnpatchSelf();
_patch = null;
}
[HarmonyTranspiler, HarmonyPriority(Priority.First)]
[HarmonyPatch(typeof(BuildTool_BlueprintPaste), nameof(BuildTool_BlueprintPaste.CheckBuildConditions))]
[HarmonyPatch(typeof(BuildTool_Click), nameof(BuildTool_Click.CheckBuildConditions))]
private static IEnumerable<CodeInstruction> BuildTool_Click_CheckBuildConditions_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{
var matcher = new CodeMatcher(instructions, generator);
/* search for:
* ldloc.s V_8 (8)
* ldfld class PrefabDesc BuildPreview::desc
* ldfld bool PrefabDesc::isInserter
* brtrue 2358 (1C12) ldloc.s V_8 (8)
* ldloca.s V_10 (10)
* call instance float32 [UnityEngine.CoreModule]UnityEngine.Vector3::get_magnitude()
*/
matcher.MatchForward(false,
new CodeMatch(OpCodes.Ldloc_S),
new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(BuildPreview), nameof(BuildPreview.desc))),
new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(PrefabDesc), nameof(PrefabDesc.isInserter))),
new CodeMatch(instr => instr.opcode == OpCodes.Brtrue || instr.opcode == OpCodes.Brtrue_S),
new CodeMatch(OpCodes.Ldloca_S),
new CodeMatch(OpCodes.Call, AccessTools.PropertyGetter(typeof(Vector3), nameof(Vector3.magnitude)))
);
var jumpPos = matcher.InstructionAt(3).operand;
var labels = matcher.Labels;
matcher.Labels = [];
/* Insert: br 2358 (1C12) ldloc.s V_8 (8)
*/
matcher.Insert(new CodeInstruction(OpCodes.Br, jumpPos).WithLabels(labels));
return matcher.InstructionEnumeration();
}
[HarmonyTranspiler, HarmonyPriority(Priority.First)]
[HarmonyPatch(typeof(BuildTool_Path), nameof(BuildTool_Path.CheckBuildConditions))]
private static IEnumerable<CodeInstruction> BuildTool_Path_CheckBuildConditions_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{
var matcher = new CodeMatcher(instructions, generator);
/* search for:
* ldloc.s V_88 (88)
* ldloc.s V_120 (120)
* brtrue.s 2054 (173A) ldc.i4.s 17
* ldc.i4.s 18
* br.s 2055 (173C) stfld valuetype EBuildCondition BuildPreview::condition
* ldc.i4.s 17
* stfld valuetype EBuildCondition BuildPreview::condition
*/
matcher.MatchForward(false,
new CodeMatch(OpCodes.Ldloc_S),
new CodeMatch(OpCodes.Ldloc_S),
new CodeMatch(instr => instr.opcode == OpCodes.Brtrue_S || instr.opcode == OpCodes.Brtrue),
new CodeMatch(instr => instr.opcode == OpCodes.Ldc_I4_S && instr.OperandIs(18)),
new CodeMatch(instr => instr.opcode == OpCodes.Br_S || instr.opcode == OpCodes.Br),
new CodeMatch(instr => instr.opcode == OpCodes.Ldc_I4_S && instr.OperandIs(17)),
new CodeMatch(OpCodes.Stfld, AccessTools.Field(typeof(BuildPreview), nameof(BuildPreview.condition)))
);
if (matcher.IsValid)
{
// Remove 7 instructions, if the following instruction is br/br.s, remove it as well
var labels = matcher.Labels;
matcher.Labels = [];
matcher.RemoveInstructions(7);
var opcode = matcher.Opcode;
if (opcode == OpCodes.Br || opcode == OpCodes.Br_S)
matcher.RemoveInstruction();
matcher.Labels.AddRange(labels);
}
/* search for:
* ldloc.s V_88 (88)
* ldc.i4.s 15-19
* stfld valuetype EBuildCondition BuildPreview::condition
*/
matcher.Start().MatchForward(false,
new CodeMatch(instr => instr.opcode == OpCodes.Ldloc_S || instr.opcode == OpCodes.Ldloc),
new CodeMatch(instr => (instr.opcode == OpCodes.Ldc_I4_S || instr.opcode == OpCodes.Ldc_I4) && Convert.ToInt64(instr.operand) is >= 15 and <= 19),
new CodeMatch(OpCodes.Stfld, AccessTools.Field(typeof(BuildPreview), nameof(BuildPreview.condition)))
);
if (matcher.IsValid)
{
// Remove 3 instructions, if the following instruction is br/br.s, remove it as well
matcher.Repeat(codeMatcher =>
{
var labels = codeMatcher.Labels;
codeMatcher.Labels = [];
codeMatcher.RemoveInstructions(3);
var opcode = codeMatcher.Opcode;
if (opcode == OpCodes.Br || opcode == OpCodes.Br_S)
codeMatcher.RemoveInstruction();
codeMatcher.Labels.AddRange(labels);
});
}
return matcher.InstructionEnumeration();
}
}
private static class RemoveBuildRangeLimit
{
private static Harmony _patch;
public static void Enable(bool enable)
{
if (enable)
{
_patch ??= Harmony.CreateAndPatchAll(typeof(RemoveBuildRangeLimit));
}
else
{
_patch?.UnpatchSelf();
_patch = null;
}
var controller = GameMain.mainPlayer?.controller;
if (controller == null) return;
controller.actionBuild?.clickTool?._OnInit();
}
[HarmonyTranspiler]
[HarmonyPatch(typeof(BuildTool_Click), nameof(BuildTool_Click._OnInit))]
private static IEnumerable<CodeInstruction> BuildTool_Click__OnInit_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{
var matcher = new CodeMatcher(instructions, generator);
matcher.MatchForward(false,
new CodeMatch(ci => ci.opcode == OpCodes.Ldc_I4_S && ci.OperandIs(15))
);
matcher.Repeat(m => m.SetAndAdvance(OpCodes.Ldc_I4, 512));
return matcher.InstructionEnumeration();
}
[HarmonyTranspiler]
[HarmonyPatch(typeof(BuildTool_Addon), nameof(BuildTool_Addon.CheckBuildConditions))]
[HarmonyPatch(typeof(BuildTool_Click), nameof(BuildTool_Click.CheckBuildConditions))]
[HarmonyPatch(typeof(BuildTool_Dismantle), nameof(BuildTool_Dismantle.DetermineMoreChainTargets))]
[HarmonyPatch(typeof(BuildTool_Dismantle), nameof(BuildTool_Dismantle.DeterminePreviews))]
[HarmonyPatch(typeof(BuildTool_Inserter), nameof(BuildTool_Inserter.CheckBuildConditions))]
[HarmonyPatch(typeof(BuildTool_Path), nameof(BuildTool_Path.CheckBuildConditions))]
[HarmonyPatch(typeof(BuildTool_Reform), nameof(BuildTool_Reform.ReformAction))]
[HarmonyPatch(typeof(BuildTool_Upgrade), nameof(BuildTool_Upgrade.DetermineMoreChainTargets))]
[HarmonyPatch(typeof(BuildTool_Upgrade), nameof(BuildTool_Upgrade.DeterminePreviews))]
private static IEnumerable<CodeInstruction> BuildAreaLimitRemoval_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{
var matcher = new CodeMatcher(instructions, generator);
/* Patch (player.mecha.buildArea * player.mecha.buildArea) to 100000000 */
matcher.MatchForward(false,
new CodeMatch(),
new CodeMatch(OpCodes.Call, AccessTools.PropertyGetter(typeof(BuildTool), nameof(BuildTool.player))),
new CodeMatch(OpCodes.Callvirt, AccessTools.PropertyGetter(typeof(Player), nameof(Player.mecha))),
new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(Mecha), nameof(Mecha.buildArea))),
new CodeMatch(),
new CodeMatch(OpCodes.Call, AccessTools.PropertyGetter(typeof(BuildTool), nameof(BuildTool.player))),
new CodeMatch(OpCodes.Callvirt, AccessTools.PropertyGetter(typeof(Player), nameof(Player.mecha))),
new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(Mecha), nameof(Mecha.buildArea))),
new CodeMatch(OpCodes.Mul)
);
matcher.Repeat(m => m.RemoveInstructions(9).InsertAndAdvance(new CodeInstruction(OpCodes.Ldc_R4, 100000000.0f)));
return matcher.InstructionEnumeration();
}
}
private static class LargerAreaForUpgradeAndDismantle
{
private static Harmony _patch;
public static void Enable(bool enable)
{
if (enable)
{
_patch ??= Harmony.CreateAndPatchAll(typeof(LargerAreaForUpgradeAndDismantle));
return;
}
_patch?.UnpatchSelf();
_patch = null;
}
[HarmonyTranspiler]
[HarmonyPatch(typeof(BuildTool_Dismantle), nameof(BuildTool_Dismantle.DeterminePreviews))]
[HarmonyPatch(typeof(BuildTool_Upgrade), nameof(BuildTool_Upgrade.DeterminePreviews))]
private static IEnumerable<CodeInstruction> BuildTools_CursorSizePatch_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{
var matcher = new CodeMatcher(instructions, generator);
matcher.MatchForward(false,
new CodeMatch(ci => ci.opcode == OpCodes.Ldc_I4_S && ci.OperandIs(11))
);
matcher.Repeat(m => m.SetAndAdvance(OpCodes.Ldc_I4_S, 31));
return matcher.InstructionEnumeration();
}
}
private static class LargerAreaForTerraform
{
private static Harmony _patch;
public static void Enable(bool enable)
{
if (enable)
{
_patch ??= Harmony.CreateAndPatchAll(typeof(LargerAreaForTerraform));
return;
}
_patch?.UnpatchSelf();
_patch = null;
}
[HarmonyTranspiler, HarmonyPatch(typeof(BuildTool_Reform), nameof(BuildTool_Reform.ReformAction))]
private static IEnumerable<CodeInstruction> BuildTool_Reform_ReformAction_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{
var matcher = new CodeMatcher(instructions, generator);
matcher.MatchForward(false,
new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(BuildTool_Reform), nameof(BuildTool_Reform.brushSize))),
new CodeMatch(ci => ci.opcode == OpCodes.Ldc_I4_S && ci.OperandIs(10))
);
matcher.Repeat(m => m.Advance(1).SetAndAdvance(OpCodes.Ldc_I4_S, 30));
matcher.Start().MatchForward(false,
new CodeMatch(ci => ci.opcode == OpCodes.Ldc_I4_S && ci.OperandIs(10)),
new CodeMatch(OpCodes.Stfld, AccessTools.Field(typeof(BuildTool_Reform), nameof(BuildTool_Reform.brushSize)))
);
matcher.Repeat(m => m.SetAndAdvance(OpCodes.Ldc_I4_S, 30));
return matcher.InstructionEnumeration();
}
}
public static class OffGridBuilding
{
private static Harmony _patch;
private const float SteppedRotationDegrees = 15f;
public static void Enable(bool enable)
{
if (enable)
{
_patch ??= Harmony.CreateAndPatchAll(typeof(OffGridBuilding));
return;
}
_patch?.UnpatchSelf();
_patch = null;
}
private static void MatchIgnoreGridAndCheckIfRotatable(CodeMatcher matcher, out Label? ifBlockEntryLabel, out Label? elseBlockEntryLabel)
{
Label? thisIfBlockEntryLabel = null;
Label? thisElseBlockEntryLabel = null;
matcher.MatchForward(false,
new CodeMatch(ci => ci.Calls(AccessTools.PropertyGetter(typeof(VFInput), nameof(VFInput._ignoreGrid)))),
new CodeMatch(ci => ci.Branches(out thisElseBlockEntryLabel)),
new CodeMatch(ci => ci.IsLdarg()),
new CodeMatch(OpCodes.Ldfld),
new CodeMatch(OpCodes.Ldfld),
new CodeMatch(ci => ci.LoadsConstant(EMinerType.Vein)),
new CodeMatch(ci => ci.Branches(out thisIfBlockEntryLabel)),
new CodeMatch(ci => ci.IsLdarg()),
new CodeMatch(OpCodes.Ldfld),
new CodeMatch(OpCodes.Ldfld),
new CodeMatch(ci => ci.Branches(out _))
);
ifBlockEntryLabel = thisIfBlockEntryLabel;
elseBlockEntryLabel = thisElseBlockEntryLabel;
}
[HarmonyTranspiler]
[HarmonyPatch(typeof(BuildTool_Click), nameof(BuildTool_Click.UpdateRaycast))]
[HarmonyPatch(typeof(BuildTool_Click), nameof(BuildTool_Click.DeterminePreviews))]
public static IEnumerable<CodeInstruction> AllowOffGridConstruction(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{
var matcher = new CodeMatcher(instructions, generator);
MatchIgnoreGridAndCheckIfRotatable(matcher, out var entryLabel, out _);
if (matcher.IsInvalid)
return instructions;
matcher.Advance(2);
matcher.Insert(new CodeInstruction(OpCodes.Br, entryLabel.Value));
return matcher.InstructionEnumeration();
}
[HarmonyTranspiler]
[HarmonyPatch(typeof(BuildTool_Click), nameof(BuildTool_Click.DeterminePreviews))]
public static IEnumerable<CodeInstruction> PreventDraggingWhenOffGrid(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{
var matcher = new CodeMatcher(instructions, generator);
Label? exitLabel = null;
matcher.MatchForward(false,
new CodeMatch(ci => ci.Branches(out exitLabel)),
new CodeMatch(OpCodes.Ldarg_0),
new CodeMatch(ci => ci.LoadsConstant(1)),
new CodeMatch(ci => ci.StoresField(AccessTools.Field(typeof(BuildTool_Click), nameof(BuildTool_Click.isDragging))))
);
if (matcher.IsInvalid)
return instructions;
matcher.Advance(1);
matcher.Insert(
new CodeInstruction(OpCodes.Call, AccessTools.PropertyGetter(typeof(VFInput), nameof(VFInput._ignoreGrid))),
new CodeInstruction(OpCodes.Brtrue, exitLabel)
);
return matcher.InstructionEnumeration();
}
public static IEnumerable<CodeInstruction> PatchToPerformSteppedRotate(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{
var matcher = new CodeMatcher(instructions, generator);
MatchIgnoreGridAndCheckIfRotatable(matcher, out var ifBlockEntryLabel, out var elseBlockEntryLabel);
if (matcher.IsInvalid)
return instructions;
while (!matcher.Labels.Contains(elseBlockEntryLabel.Value))
matcher.Advance(1);
Label? ifBlockExitLabel = null;
matcher.MatchBack(false, new CodeMatch(ci => ci.Branches(out ifBlockExitLabel)));
if (matcher.IsInvalid)
return instructions;
while (!matcher.Labels.Contains(ifBlockEntryLabel.Value))
matcher.Advance(-1);
var instructionToClone = matcher.Instruction.Clone();
var overwriteWith = CodeInstruction.LoadField(typeof(VFInput), nameof(VFInput.control));
matcher.SetAndAdvance(overwriteWith.opcode, overwriteWith.operand);
matcher.Insert(instructionToClone);
matcher.CreateLabel(out var existingEntryLabel);
matcher.InsertAndAdvance(
new CodeInstruction(OpCodes.Brfalse, existingEntryLabel),
new CodeInstruction(OpCodes.Ldarg_0),
CodeInstruction.Call(typeof(OffGridBuilding), nameof(OffGridBuilding.RotateStepped)),
new CodeInstruction(OpCodes.Br, ifBlockExitLabel)
);
return matcher.InstructionEnumeration();
}
public static void RotateStepped(BuildTool_Click instance)
{
if (VFInput._rotate.onDown)
{
instance.yaw += SteppedRotationDegrees;
instance.yaw = Mathf.Repeat(instance.yaw, 360f);
instance.yaw = Mathf.Round(instance.yaw / SteppedRotationDegrees) * SteppedRotationDegrees;
}
if (VFInput._counterRotate.onDown)
{
instance.yaw -= SteppedRotationDegrees;
instance.yaw = Mathf.Repeat(instance.yaw, 360f);
instance.yaw = Mathf.Round(instance.yaw / SteppedRotationDegrees) * SteppedRotationDegrees;
}
}
}
public static class LogisticsCapacityTweaks
{
private static Harmony _patch;
public static void Enable(bool enable)
{
if (enable)
{
_patch ??= Harmony.CreateAndPatchAll(typeof(LogisticsCapacityTweaks));
return;
}
_patch?.UnpatchSelf();
_patch = null;
}
private static KeyCode _lastKey = KeyCode.None;
private static long _nextKeyTick;
private static bool _skipNextEvent;
private static bool UpdateKeyPressed(KeyCode code)
{
if (!Input.GetKey(code))
return false;
if (code != _lastKey)
{
_lastKey = code;
_nextKeyTick = GameMain.instance.timei + 30;
return true;
}
var currTick = GameMain.instance.timei;
if (_nextKeyTick > currTick) return false;
_nextKeyTick = currTick + 4;
return true;
}
public static void OnUpdate()
{
if (_lastKey != KeyCode.None && Input.GetKeyUp(_lastKey))
{
_lastKey = KeyCode.None;
}
if (!VFInput.noModifier) return;
int delta;
if (UpdateKeyPressed(KeyCode.LeftArrow))
{
delta = -10;
}
else if (UpdateKeyPressed(KeyCode.RightArrow))
{
delta = 10;
}
else if (UpdateKeyPressed(KeyCode.DownArrow))
{
delta = -100;
}
else if (UpdateKeyPressed(KeyCode.UpArrow))
{
delta = 100;
}
else
{
return;
}
var targets = new List<RaycastResult>();
EventSystem.current.RaycastAll(new PointerEventData(EventSystem.current) { position = Input.mousePosition }, targets);
foreach (var target in targets)
{
var stationStorage = target.gameObject.GetComponentInParent<UIStationStorage>();
if (stationStorage is null) continue;
var station = stationStorage.station;
ref var storage = ref station.storage[stationStorage.index];
var oldMax = storage.max;
var newMax = oldMax + delta;
if (newMax < 0)
{
newMax = 0;
}
else
{
var modelProto = LDB.models.Select(stationStorage.stationWindow.factory.entityPool[station.entityId].modelIndex);
var itemCountMax = 0;
if (modelProto != null)
{
itemCountMax = modelProto.prefabDesc.stationMaxItemCount;
}
itemCountMax += station.isStellar ? GameMain.history.remoteStationExtraStorage : GameMain.history.localStationExtraStorage;
if (newMax > itemCountMax)
{
newMax = itemCountMax;
}
}
storage.max = newMax;
_skipNextEvent = oldMax / 100 != newMax / 100;
break;
}
}
[HarmonyPrefix]
[HarmonyPatch(typeof(UIStationStorage), nameof(UIStationStorage.OnMaxSliderValueChange))]
private static bool UIStationStorage_OnMaxSliderValueChange_Prefix()
{
if (!_skipNextEvent) return true;
_skipNextEvent = false;
return false;
}
[HarmonyPrefix]
[HarmonyPatch(typeof(PlanetTransport), nameof(PlanetTransport.OnTechFunctionUnlocked))]
private static bool PlanetTransport_OnTechFunctionUnlocked_Prefix(PlanetTransport __instance, int _funcId, double _valuelf, int _level)
{
switch (_funcId)
{
case 30:
{
var stationPool = __instance.stationPool;
var factory = __instance.factory;
var history = GameMain.history;
for (var i = __instance.stationCursor - 1; i > 0; i--)
{
if (stationPool[i] == null || stationPool[i].id != i || (stationPool[i].isStellar && !stationPool[i].isCollector && !stationPool[i].isVeinCollector)) continue;
var modelIndex = factory.entityPool[stationPool[i].entityId].modelIndex;
var maxCount = LDB.models.Select(modelIndex).prefabDesc.stationMaxItemCount;
var oldMaxCount = maxCount + history.localStationExtraStorage - _valuelf;
var intOldMaxCount = (int)Math.Round(oldMaxCount);
var ratio = (maxCount + history.localStationExtraStorage) / oldMaxCount;
var storage = stationPool[i].storage;
for (var j = storage.Length - 1; j >= 0; j--)
{
if (storage[j].max + 10 < intOldMaxCount) continue;
storage[j].max = Mathf.RoundToInt((float)(storage[j].max * ratio / 50.0)) * 50;
}
}
break;
}
case 31:
{
var stationPool = __instance.stationPool;
var factory = __instance.factory;
var history = GameMain.history;
for (var i = __instance.stationCursor - 1; i > 0; i--)
{
if (stationPool[i] == null || stationPool[i].id != i || !stationPool[i].isStellar || stationPool[i].isCollector || stationPool[i].isVeinCollector) continue;
var modelIndex = factory.entityPool[stationPool[i].entityId].modelIndex;
var maxCount = LDB.models.Select(modelIndex).prefabDesc.stationMaxItemCount;
var oldMaxCount = maxCount + history.remoteStationExtraStorage - _valuelf;
var intOldMaxCount = (int)Math.Round(oldMaxCount);
var ratio = (maxCount + history.remoteStationExtraStorage) / oldMaxCount;
var storage = stationPool[i].storage;
for (var j = storage.Length - 1; j >= 0; j--)
{
if (storage[j].max + 10 < intOldMaxCount) continue;
storage[j].max = Mathf.RoundToInt((float)(storage[j].max * ratio / 100.0)) * 100;
}
}
break;
}
}
return false;
}
}
}

View File

@@ -1,64 +0,0 @@
using System.Linq;
namespace UXAssist.Functions;
public static class DysonSphereFunctions
{
public static StarData CurrentStarForDysonSystem()
{
StarData star = null;
var dysonEditor = UIRoot.instance?.uiGame?.dysonEditor;
if (dysonEditor != null && dysonEditor.gameObject.activeSelf)
{
star = dysonEditor.selection.viewStar;
}
return star ?? GameMain.data?.localStar;
}
public static void InitCurrentDysonLayer(StarData star, int layerId)
{
star ??= CurrentStarForDysonSystem();
if (star == null) return;
var dysonSpheres = GameMain.data?.dysonSpheres;
if (dysonSpheres == null) return;
var dysonEditor = UIRoot.instance?.uiGame.dysonEditor;
if (layerId < 0)
{
if (dysonSpheres[star.index] == null) return;
var dysonSphere = new DysonSphere();
dysonSpheres[star.index] = dysonSphere;
dysonSphere.Init(GameMain.data, star);
dysonSphere.ResetNew();
if (!dysonEditor) return;
if (dysonEditor.selection.viewStar == star)
{
dysonEditor.selection.viewDysonSphere = dysonSphere;
dysonEditor.selection.NotifyDysonShpereChange();
}
return;
}
var ds = dysonSpheres[star.index];
if (ds?.layersIdBased[layerId] == null) return;
var pool = ds.rocketPool;
for (var id = ds.rocketCursor - 1; id > 0; id--)
{
if (pool[id].id != id) continue;
if (pool[id].nodeLayerId != layerId) continue;
ds.RemoveDysonRocket(id);
break;
}
ds.RemoveLayer(layerId);
if (!dysonEditor) return;
if (!dysonEditor.IsRender(layerId, false, true))
{
dysonEditor.SwitchRenderState(layerId, false, true);
}
if (!dysonEditor.IsRender(layerId, false, false))
{
dysonEditor.SwitchRenderState(layerId, false, false);
}
dysonEditor.selection.ClearAllSelection();
}
}

View File

@@ -1,96 +0,0 @@
using System.Collections.Generic;
namespace UXAssist.Functions;
public static class FactoryFunctions
{
public static void CutConveyorBelt(CargoTraffic cargoTraffic, int beltId)
{
ref var belt = ref cargoTraffic.beltPool[beltId];
if (belt.id != beltId || belt.outputId <= 0) return;
// Clear entity connection
var factory = cargoTraffic.factory;
factory.ReadObjectConn(belt.entityId, 0, out var isOutput, out var otherObjId, out var otherSlot);
if (isOutput && factory.entityPool[otherObjId].beltId == belt.outputId) {
factory.ClearObjectConnDirect(belt.entityId, 0);
factory.ClearObjectConnDirect(otherObjId, otherSlot);
}
// Alter belt connections
var (i0, i1, i2) = (belt.rightInputId, belt.backInputId, belt.leftInputId);
cargoTraffic._arrInputs(ref i0, ref i1, ref i2);
cargoTraffic.AlterBeltConnections(beltId, 0, i0, i1, i2);
}
public static void DismantleBlueprintSelectedBuildings()
{
var player = GameMain.mainPlayer;
var build = player?.controller?.actionBuild;
if (build == null) return;
var blueprintCopyTool = build.blueprintCopyTool;
if (blueprintCopyTool == null || !blueprintCopyTool.active) return;
var factory = build.factory;
List<int> buildPreviewsToRemove = [];
foreach (var buildPreview in blueprintCopyTool.bpPool)
{
if (buildPreview?.item == null || buildPreview.objId <= 0) continue;
int index;
if ((index = buildPreviewsToRemove.BinarySearch(buildPreview.objId)) < 0)
buildPreviewsToRemove.Insert(~index, buildPreview.objId);
var isBelt = buildPreview.desc.isBelt;
var isInserter = buildPreview.desc.isInserter;
if (isInserter) continue;
var objId = buildPreview.objId;
if (isBelt)
{
var needCheck = false;
for (var j = 0; j < 2; j++)
{
factory.ReadObjectConn(objId, j, out _, out var connObjId, out _);
if (connObjId == 0 || factory.ObjectIsBelt(connObjId) || blueprintCopyTool.ObjectIsInserter(connObjId)) continue;
needCheck = true;
break;
}
if (needCheck)
{
for (var k = 0; k < 16; k++)
{
factory.ReadObjectConn(objId, k, out _, out var connObjId, out _);
if (connObjId != 0 && (index = buildPreviewsToRemove.BinarySearch(connObjId)) < 0 && (factory.ObjectIsBelt(connObjId) || blueprintCopyTool.ObjectIsInserter(connObjId)))
buildPreviewsToRemove.Insert(~index, connObjId);
}
}
for (var m = 0; m < 4; m++)
{
factory.ReadObjectConn(objId, m, out _, out var connObjId, out _);
if (connObjId == 0 || !factory.ObjectIsBelt(connObjId) || buildPreviewsToRemove.BinarySearch(connObjId) >= 0) continue;
for (var j = 0; j < 2; j++)
{
factory.ReadObjectConn(connObjId, j, out _, out var connObjId2, out _);
if (connObjId2 == 0 || (index = buildPreviewsToRemove.BinarySearch(connObjId2)) >= 0 || factory.ObjectIsBelt(connObjId2) || blueprintCopyTool.ObjectIsInserter(connObjId2)) continue;
buildPreviewsToRemove.Insert(~index, connObjId2);
break;
}
}
continue;
}
if (buildPreview.desc.addonType == EAddonType.Belt) continue;
for (var j = 0; j < 16; j++)
{
factory.ReadObjectConn(objId, j, out _, out var connObjId, out _);
if (connObjId != 0 && (index = buildPreviewsToRemove.BinarySearch(connObjId)) < 0 && (factory.ObjectIsBelt(connObjId) || blueprintCopyTool.ObjectIsInserter(connObjId)))
buildPreviewsToRemove.Insert(~index, connObjId);
}
}
foreach (var objId in buildPreviewsToRemove)
{
build.DoDismantleObject(objId);
}
buildPreviewsToRemove = null;
blueprintCopyTool.ClearSelection();
blueprintCopyTool.ClearPreSelection();
blueprintCopyTool.ResetBlueprint();
blueprintCopyTool.ResetBuildPreviews();
blueprintCopyTool.RefreshBlueprintData();
}
}

View File

@@ -1,267 +0,0 @@
using System;
using System.Runtime.InteropServices;
using BepInEx.Configuration;
using UnityEngine;
using UXAssist.Common;
using UXAssist.Patches;
namespace UXAssist.Functions;
public static class WindowFunctions
{
private static bool _initialized;
public static string ProfileName { get; private set; }
private const string GameWindowClass = "UnityWndClass";
private static string _gameWindowTitle = "Dyson Sphere Program";
private static IntPtr _oldWndProc = IntPtr.Zero;
private static IntPtr _gameWindowHandle = IntPtr.Zero;
private static bool _gameLoaded;
public static WinApi.LogicalProcessorDetails ProcessorDetails { get; private set; }
public static ConfigEntry<int> ProcessPriority;
public static ConfigEntry<int> ProcessAffinity;
private static readonly int[] ProrityFlags =
[
WinApi.HIGH_PRIORITY_CLASS,
WinApi.ABOVE_NORMAL_PRIORITY_CLASS,
WinApi.NORMAL_PRIORITY_CLASS,
WinApi.BELOW_NORMAL_PRIORITY_CLASS,
WinApi.IDLE_PRIORITY_CLASS
];
public static void Init()
{
if (_initialized) return;
_initialized = true;
I18N.Add("Cores: {0}\nThreads: {1}", "Cores: {0}\nThreads: {1}", "核心数: {0}\n线程数: {1}");
I18N.Add("\nP-Cores: {0}\nE-Cores: {1}", "\nP-Cores: {0}\nE-Cores: {1}", "\n性能核心: {0}\n能效核心: {1}");
I18N.Add("\nPriority: {0}", "\nProcess priority: {0}", "\n进程优先级: {0}");
I18N.Add("\nEnabled CPUs: ", "\nEnabled CPUs: ", "\n使用的CPU: ");
I18N.Add("Unknown", "Unknown", "未知");
ProcessorDetails = WinApi.GetLogicalProcessorDetails();
SetWindowTitle();
}
public static void Start()
{
GameLogic.OnDataLoaded += OnDataLoaded;
var wndProc = new WinApi.WndProc(GameWndProc);
var gameWnd = FindGameWindow();
if (gameWnd != IntPtr.Zero)
{
_oldWndProc = WinApi.SetWindowLongPtr(gameWnd, WinApi.GWLP_WNDPROC, Marshal.GetFunctionPointerForDelegate(wndProc));
}
if (GamePatch.LoadLastWindowRectEnabled.Value)
GamePatch.LoadLastWindowRect.MoveWindowPosition(true);
ProcessPriority.SettingChanged += (_, _) => WinApi.SetPriorityClass(WinApi.GetCurrentProcess(), ProrityFlags[ProcessPriority.Value]);
WinApi.SetPriorityClass(WinApi.GetCurrentProcess(), ProrityFlags[ProcessPriority.Value]);
ProcessAffinity.SettingChanged += (_, _) => UpdateAffinity();
UpdateAffinity();
return;
void UpdateAffinity()
{
var process = WinApi.GetCurrentProcess();
if (!WinApi.GetProcessAffinityMask(process, out _, out var systemMask))
{
systemMask = ulong.MaxValue;
}
switch (ProcessAffinity.Value)
{
case 0:
WinApi.SetProcessAffinityMask(process, systemMask);
break;
case 1:
WinApi.SetProcessAffinityMask(process, systemMask & ((1UL << (ProcessorDetails.ThreadCount / 2)) - 1UL));
break;
case 2:
WinApi.SetProcessAffinityMask(process, systemMask & (ProcessorDetails.ThreadCount > 16 ? 0xFFUL : 1UL));
break;
case 3:
WinApi.SetProcessAffinityMask(process, systemMask & ProcessorDetails.PerformanceCoreMask);
break;
case 4:
WinApi.SetProcessAffinityMask(process, systemMask & ProcessorDetails.EfficiencyCoreMask);
break;
}
}
}
public static void Uninit()
{
GameLogic.OnDataLoaded -= OnDataLoaded;
}
private static void OnDataLoaded()
{
_gameLoaded = true;
}
private static IntPtr GameWndProc(IntPtr hWnd, uint uMsg, IntPtr wParam, IntPtr lParam)
{
switch (uMsg)
{
case WinApi.WM_ACTIVATE:
WinApi.SetPriorityClass(WinApi.GetCurrentProcess(), ProrityFlags[ProcessPriority.Value]);
break;
case WinApi.WM_DESTROY:
if (_oldWndProc != IntPtr.Zero && _gameWindowHandle != IntPtr.Zero)
{
WinApi.SetWindowLongPtr(_gameWindowHandle, WinApi.GWLP_WNDPROC, _oldWndProc);
}
break;
case WinApi.WM_SYSCOMMAND:
switch ((long)wParam & 0xFFF0L)
{
case WinApi.SC_MOVE:
if (GamePatch.LoadLastWindowRectEnabled.Value && !_gameLoaded) return (IntPtr)1L;
break;
}
break;
case WinApi.WM_MOVING:
if (!GamePatch.LoadLastWindowRectEnabled.Value || _gameLoaded) break;
var rect = GamePatch.LastWindowRect.Value;
if (rect is { z: 0f, w: 0f }) break;
var x = Mathf.RoundToInt(rect.x);
var y = Mathf.RoundToInt(rect.y);
var rect2 = Marshal.PtrToStructure<WinApi.Rect>(lParam);
rect2.Left = x;
rect2.Top = y;
Marshal.StructureToPtr(rect2, lParam, false);
break;
case WinApi.WM_SIZING:
if (!GamePatch.LoadLastWindowRectEnabled.Value || _gameLoaded) break;
rect = GamePatch.LastWindowRect.Value;
if (rect is { z: 0f, w: 0f }) break;
x = Mathf.RoundToInt(rect.x);
y = Mathf.RoundToInt(rect.y);
var w = Mathf.RoundToInt(rect.z);
var h = Mathf.RoundToInt(rect.w);
rect2 = Marshal.PtrToStructure<WinApi.Rect>(lParam);
rect2.Left = x;
rect2.Top = y;
rect2.Right = x + w;
rect2.Bottom = y + h;
Marshal.StructureToPtr(rect2, lParam, false);
break;
}
return WinApi.CallWindowProc(_oldWndProc, hWnd, uMsg, wParam, lParam);
}
private static string GetPriorityName(int priority)
{
return priority switch
{
WinApi.HIGH_PRIORITY_CLASS => "High".Translate(),
WinApi.ABOVE_NORMAL_PRIORITY_CLASS => "Above Normal".Translate(),
WinApi.NORMAL_PRIORITY_CLASS => "Normal".Translate(),
WinApi.BELOW_NORMAL_PRIORITY_CLASS => "Below Normal".Translate(),
WinApi.IDLE_PRIORITY_CLASS => "Idle".Translate(),
_ => "Unknown".Translate()
};
}
public static void ShowCPUInfo()
{
var details = ProcessorDetails;
var msg = string.Format("Cores: {0}\nThreads: {1}".Translate(), details.CoreCount, details.ThreadCount);
var hybrid = details.HybridArchitecture;
if (hybrid)
{
msg += string.Format("\nP-Cores: {0}\nE-Cores: {1}".Translate(), details.PerformanceCoreCount, details.EfficiencyCoreCount);
}
var handle = WinApi.GetCurrentProcess();
var prio = GetPriorityName(WinApi.GetPriorityClass(handle));
msg += string.Format("\nPriority: {0}".Translate(), prio);
var aff = 0UL;
if (WinApi.GetProcessAffinityMask(handle, out var processMask, out var systemMask))
aff = processMask & systemMask;
msg += "\nEnabled CPUs: ".Translate();
var first = true;
for (var i = 0; aff != 0UL; i++)
{
if ((aff & 1UL) != 0)
{
if (first)
first = false;
else
msg += ",";
msg += i;
if (hybrid)
{
if ((details.PerformanceCoreMask & (1UL << i)) != 0)
msg += "(P)";
else if ((details.EfficiencyCoreMask & (1UL << i)) != 0)
msg += "(E)";
}
}
aff >>= 1;
}
UIMessageBox.Show("CPU Info".Translate(), msg, "确定".Translate(), -1);
}
public static void SetWindowTitle()
{
// Get profile name from command line arguments, and set window title accordingly
var args = Environment.GetCommandLineArgs();
for (var i = 0; i < args.Length - 1; i++)
{
// Doorstop 3.x and 4.x use different arguments to pass the target assembly path
if (args[i] != "--doorstop-target" && args[i] != "--doorstop-target-assembly") continue;
var arg = args[i + 1];
const string doorstopPathSuffix = @"\BepInEx\core\BepInEx.Preloader.dll";
if (!arg.EndsWith(doorstopPathSuffix, StringComparison.OrdinalIgnoreCase))
break;
arg = arg.Substring(0, arg.Length - doorstopPathSuffix.Length);
const string profileSuffix = @"\profiles\";
var index = arg.LastIndexOf(profileSuffix, StringComparison.OrdinalIgnoreCase);
if (index < 0)
break;
arg = arg.Substring(index + profileSuffix.Length);
var wnd = FindGameWindow();
if (wnd == IntPtr.Zero) return;
ProfileName = arg;
_gameWindowTitle = $"Dyson Sphere Program - {arg}";
WinApi.SetWindowText(wnd, _gameWindowTitle);
break;
}
}
public static IntPtr FindGameWindow()
{
if (_gameWindowHandle != IntPtr.Zero)
return _gameWindowHandle;
var wnd = IntPtr.Zero;
var consoleWnd = WinApi.GetConsoleWindow();
var currentProcessId = WinApi.GetCurrentProcessId();
while (true)
{
wnd = WinApi.FindWindowEx(IntPtr.Zero, wnd, GameWindowClass, _gameWindowTitle);
if (wnd == IntPtr.Zero)
return IntPtr.Zero;
if (wnd == consoleWnd)
continue;
WinApi.GetWindowThreadProcessId(wnd, out var pid);
if (pid == currentProcessId)
break;
}
_gameWindowHandle = wnd;
return _gameWindowHandle;
}
}

380
UXAssist/GamePatch.cs Normal file
View File

@@ -0,0 +1,380 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection.Emit;
using BepInEx.Configuration;
using HarmonyLib;
using UnityEngine;
using UXAssist.Common;
using Object = UnityEngine.Object;
namespace UXAssist;
public static class GamePatch
{
private const string GameWindowClass = "UnityWndClass";
private const string GameWindowTitle = "Dyson Sphere Program";
public static ConfigEntry<bool> EnableWindowResizeEnabled;
public static ConfigEntry<bool> LoadLastWindowRectEnabled;
// public static ConfigEntry<bool> AutoSaveOptEnabled;
public static ConfigEntry<bool> ConvertSavesFromPeaceEnabled;
public static ConfigEntry<Vector4> LastWindowRect;
private static Harmony _gamePatch;
public static void Init()
{
EnableWindowResizeEnabled.SettingChanged += (_, _) => EnableWindowResize.Enable(EnableWindowResizeEnabled.Value);
LoadLastWindowRectEnabled.SettingChanged += (_, _) => LoadLastWindowRect.Enable(LoadLastWindowRectEnabled.Value);
// AutoSaveOptEnabled.SettingChanged += (_, _) => AutoSaveOpt.Enable(AutoSaveOptEnabled.Value);
ConvertSavesFromPeaceEnabled.SettingChanged += (_, _) => ConvertSavesFromPeace.Enable(ConvertSavesFromPeaceEnabled.Value);
EnableWindowResize.Enable(EnableWindowResizeEnabled.Value);
LoadLastWindowRect.Enable(LoadLastWindowRectEnabled.Value);
// AutoSaveOpt.Enable(AutoSaveOptEnabled.Value);
ConvertSavesFromPeace.Enable(ConvertSavesFromPeaceEnabled.Value);
_gamePatch ??= Harmony.CreateAndPatchAll(typeof(GamePatch));
}
public static void Uninit()
{
LoadLastWindowRect.Enable(false);
EnableWindowResize.Enable(false);
// AutoSaveOpt.Enable(false);
ConvertSavesFromPeace.Enable(false);
_gamePatch?.UnpatchSelf();
_gamePatch = null;
}
[HarmonyPrefix, HarmonyPatch(typeof(GameMain), nameof(GameMain.HandleApplicationQuit))]
private static void GameMain_HandleApplicationQuit_Prefix()
{
var wnd = WinApi.FindWindow(GameWindowClass, GameWindowTitle);
if (wnd == IntPtr.Zero) return;
WinApi.GetWindowRect(wnd, out var rect);
LastWindowRect.Value = new Vector4(rect.Left, rect.Top, Screen.width, Screen.height);
}
private static class EnableWindowResize
{
public static void Enable(bool on)
{
var wnd = WinApi.FindWindow(GameWindowClass, GameWindowTitle);
if (wnd == IntPtr.Zero) return;
if (on)
WinApi.SetWindowLong(wnd, (int)WindowLongFlags.GWL_STYLE,
WinApi.GetWindowLong(wnd, (int)WindowLongFlags.GWL_STYLE) | (int)WindowStyles.WS_THICKFRAME | (int)WindowStyles.WS_MAXIMIZEBOX);
else
WinApi.SetWindowLong(wnd, (int)WindowLongFlags.GWL_STYLE,
WinApi.GetWindowLong(wnd, (int)WindowLongFlags.GWL_STYLE) & ~((int)WindowStyles.WS_THICKFRAME | (int)WindowStyles.WS_MAXIMIZEBOX));
}
}
private static class LoadLastWindowRect
{
private static Harmony _patch;
private static bool _loaded;
public static void Enable(bool on)
{
if (on)
{
_patch ??= Harmony.CreateAndPatchAll(typeof(LoadLastWindowRect));
if (Screen.fullScreenMode is not (FullScreenMode.ExclusiveFullScreen or FullScreenMode.FullScreenWindow or FullScreenMode.MaximizedWindow))
{
var rect = LastWindowRect.Value;
var x = Mathf.RoundToInt(rect.x);
var y = Mathf.RoundToInt(rect.y);
var w = Mathf.RoundToInt(rect.z);
var h = Mathf.RoundToInt(rect.w);
var needFix = false;
if (w < 100)
{
w = 1280;
needFix = true;
}
if (h < 100)
{
h = 720;
needFix = true;
}
var sw = Screen.currentResolution.width;
var sh = Screen.currentResolution.height;
if (x + w > sw)
{
x = sw - w;
needFix = true;
}
if (y + h > sh)
{
y = sh - h;
needFix = true;
}
if (x < 0)
{
x = 0;
needFix = true;
}
if (y < 0)
{
y = 0;
needFix = true;
}
if (needFix)
{
LastWindowRect.Value = new Vector4(x, y, w, h);
}
}
MoveWindowPosition();
return;
}
_patch?.UnpatchSelf();
_patch = null;
}
private static void MoveWindowPosition()
{
if (Screen.fullScreenMode is FullScreenMode.ExclusiveFullScreen or FullScreenMode.FullScreenWindow or FullScreenMode.MaximizedWindow || GameMain.isRunning) return;
var wnd = WinApi.FindWindow(GameWindowClass, GameWindowTitle);
if (wnd == IntPtr.Zero) return;
var rect = LastWindowRect.Value;
if (rect.z == 0f && rect.w == 0f) return;
var x = Mathf.RoundToInt(rect.x);
var y = Mathf.RoundToInt(rect.y);
WinApi.SetWindowPos(wnd, IntPtr.Zero, x, y, 0, 0, 0x0235);
}
[HarmonyPrefix]
[HarmonyPatch(typeof(Screen), nameof(Screen.SetResolution), typeof(int), typeof(int), typeof(FullScreenMode), typeof(int))]
private static void Screen_SetResolution_Prefix(ref int width, ref int height, FullScreenMode fullscreenMode)
{
if (fullscreenMode is FullScreenMode.ExclusiveFullScreen or FullScreenMode.FullScreenWindow or FullScreenMode.MaximizedWindow || GameMain.isRunning) return;
var rect = LastWindowRect.Value;
if (rect.z == 0f && rect.w == 0f) return;
var w = Mathf.RoundToInt(rect.z);
var h = Mathf.RoundToInt(rect.w);
width = w;
height = h;
}
[HarmonyPostfix]
[HarmonyPatch(typeof(Screen), nameof(Screen.SetResolution), typeof(int), typeof(int), typeof(FullScreenMode), typeof(int))]
private static void Screen_SetResolution_Postfix(FullScreenMode fullscreenMode)
{
MoveWindowPosition();
}
[HarmonyPostfix]
[HarmonyPatch(typeof(VFPreload), "InvokeOnLoadWorkEnded")]
private static void VFPreload_InvokeOnLoadWorkEnded_Postfix()
{
if (_loaded || Screen.fullScreenMode is FullScreenMode.ExclusiveFullScreen or FullScreenMode.FullScreenWindow or FullScreenMode.MaximizedWindow) return;
_loaded = true;
var wnd = WinApi.FindWindow(GameWindowClass, GameWindowTitle);
if (wnd == IntPtr.Zero) return;
var rect = LastWindowRect.Value;
if (rect.z == 0f && rect.w == 0f) return;
var x = Mathf.RoundToInt(rect.x);
var y = Mathf.RoundToInt(rect.y);
var w = Mathf.RoundToInt(rect.z);
var h = Mathf.RoundToInt(rect.w);
Screen.SetResolution(w, h, false);
WinApi.SetWindowPos(wnd, IntPtr.Zero, x, y, 0, 0, 0x0235);
if (EnableWindowResizeEnabled.Value)
WinApi.SetWindowLong(wnd, (int)WindowLongFlags.GWL_STYLE,
WinApi.GetWindowLong(wnd, (int)WindowLongFlags.GWL_STYLE) | (int)WindowStyles.WS_THICKFRAME | (int)WindowStyles.WS_MAXIMIZEBOX);
}
}
/*
private static class AutoSaveOpt
{
private static Harmony _patch;
public static void Enable(bool on)
{
if (on)
{
Directory.CreateDirectory(GameConfig.gameSaveFolder + "AutoSaves/");
_patch ??= Harmony.CreateAndPatchAll(typeof(AutoSaveOpt));
return;
}
_patch?.UnpatchSelf();
_patch = null;
}
[HarmonyPrefix]
[HarmonyPatch(typeof(GameSave), nameof(GameSave.AutoSave))]
private static bool GameSave_AutoSave_Prefix(ref bool __result)
{
if (!GameSave.SaveCurrentGame(GameSave.AutoSaveTmp))
{
GlobalObject.SaveOpCounter();
__result = false;
return false;
}
var tmpFilename = GameConfig.gameSaveFolder + GameSave.AutoSaveTmp + GameSave.saveExt;
var targetFilename = $"{GameConfig.gameSaveFolder}AutoSaves/[{GameMain.data.gameDesc.clusterString}] {DateTime.Now:yyyy-MM-dd_hh-mm-ss}{GameSave.saveExt}";
File.Move(tmpFilename, targetFilename);
__result = true;
return false;
}
[HarmonyPostfix]
[HarmonyPatch(typeof(UILoadGameWindow), nameof(UILoadGameWindow.RefreshList))]
public static void UILoadGameWindow_RefreshList_Postfix(UILoadGameWindow __instance)
{
var baseDir = GameConfig.gameSaveFolder + "AutoSaves/";
var files = Directory.GetFiles(baseDir, "*" + GameSave.saveExt, SearchOption.TopDirectoryOnly);
var entries = __instance.entries;
var entries2 = new List<UIGameSaveEntry>();
var entryPrefab = __instance.entryPrefab;
var entryPrefabParent = entryPrefab.transform.parent;
foreach (var f in files)
{
var fileInfo = new FileInfo(f);
var entry = Object.Instantiate(entryPrefab, entryPrefabParent);
entry.fileInfo = fileInfo;
entries2.Add(entry);
}
entries2.Sort((x, y) => -x.fileDate.CompareTo(y.fileDate));
if (entries2.Count > 10)
entries2.RemoveRange(10, entries2.Count - 10);
var autoSaveText = ">> " + "自动存档条目".Translate();
foreach (var entry in entries2)
{
entry.indexText.text = "";
var saveName = entry.saveName;
entry._saveName = $"AutoSaves/{saveName}";
var quoteIndex = saveName.IndexOf('[');
if (quoteIndex >= 0)
{
var quoteIndex2 = saveName.IndexOf(']', quoteIndex + 1);
if (quoteIndex2 > 0) saveName = saveName.Substring(quoteIndex, quoteIndex2 + 1 - quoteIndex);
}
entry.nameText.text = $"{autoSaveText} {saveName}";
entry.nameText.fontStyle = FontStyle.Italic;
entry.nameText.color = new Color(1f, 1f, 1f, 0.7f);
entry.timeText.text = $"{entry.fileDate:yyyy-MM-dd HH:mm:ss}";
GameSave.ReadModes(entry.fileInfo.FullName, out var isSandbox, out var isPeace);
if (entry.sandboxIcon != null)
{
entry.sandboxIcon.gameObject.SetActive(isSandbox);
}
if (entry.combatIcon != null)
{
entry.combatIcon.gameObject.SetActive(!isPeace);
}
entry.selected = false;
entry.gameObject.SetActive(true);
}
entries.AddRange(entries2);
entries.Sort((x, y) => -x.fileDate.CompareTo(y.fileDate));
var displayIndex = 1;
for (var i = 0; i < entries.Count; i++)
{
var entry = entries[i];
entry.index = i + 1;
entry.rectTrans.anchoredPosition = new Vector2(entry.rectTrans.anchoredPosition.x, -40 * i);
if (string.IsNullOrEmpty(entry.indexText.text)) continue;
entry.indexText.text = displayIndex.ToString();
displayIndex++;
}
}
[HarmonyPostfix]
[HarmonyPatch(typeof(UISaveGameWindow), nameof(UISaveGameWindow.RefreshList))]
public static void UISaveGameWindow_RefreshList_Postfix(UISaveGameWindow __instance)
{
var entries = __instance.entries;
entries.Sort((x, y) => -x.fileDate.CompareTo(y.fileDate));
for (var i = 0; i < entries.Count; i++)
{
var entry = entries[i];
entry.index = i + 1;
entry.rectTrans.anchoredPosition = new Vector2(entry.rectTrans.anchoredPosition.x, -40 * i);
entry.indexText.text = (i + 1).ToString();
}
}
[HarmonyTranspiler]
[HarmonyPatch(typeof(UILoadGameWindow), nameof(UILoadGameWindow.DoLoadSelectedGame))]
[HarmonyPatch(typeof(UILoadGameWindow), nameof(UILoadGameWindow.OnSelectedChange))]
private static IEnumerable<CodeInstruction> UILoadGameWindow_ReplaceSaveName_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{
var matcher = new CodeMatcher(instructions, generator);
matcher.Start().MatchForward(false,
new CodeMatch(OpCodes.Callvirt, AccessTools.PropertyGetter(typeof(UIGameSaveEntry), nameof(UIGameSaveEntry.saveName)))
);
matcher.Repeat(m => m.SetAndAdvance(OpCodes.Ldfld, AccessTools.Field(typeof(UIGameSaveEntry), nameof(UIGameSaveEntry._saveName))));
return matcher.InstructionEnumeration();
}
[HarmonyTranspiler]
[HarmonyPatch(typeof(GameSave), nameof(GameSave.LoadCurrentGame))]
[HarmonyPatch(typeof(GameSave), nameof(GameSave.LoadGameDesc))]
[HarmonyPatch(typeof(GameSave), nameof(GameSave.ReadHeader))]
[HarmonyPatch(typeof(GameSave), nameof(GameSave.ReadHeaderAndDescAndProperty))]
[HarmonyPatch(typeof(GameSave), nameof(GameSave.SaveExist))]
[HarmonyPatch(typeof(GameSave), nameof(GameSave.SavePath))]
private static IEnumerable<CodeInstruction> GameSave_RemoveValidateOnLoad_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{
var matcher = new CodeMatcher(instructions, generator);
matcher.Start().MatchForward(false,
new CodeMatch(OpCodes.Call, AccessTools.Method(typeof(CommonUtils), nameof(CommonUtils.ValidFileName)))
);
matcher.RemoveInstruction();
return matcher.InstructionEnumeration();
}
}
*/
private static class ConvertSavesFromPeace
{
private static Harmony _patch;
private static bool _needConvert;
public static void Enable(bool on)
{
if (on)
{
_patch ??= Harmony.CreateAndPatchAll(typeof(ConvertSavesFromPeace));
return;
}
_patch?.UnpatchSelf();
_patch = null;
}
[HarmonyPostfix]
[HarmonyPatch(typeof(GameDesc), nameof(GameDesc.Import))]
private static void GameDesc_Import_Postfix(GameDesc __instance)
{
if (DSPGame.IsMenuDemo || !__instance.isPeaceMode) return;
__instance.combatSettings = UIRoot.instance.galaxySelect.uiCombat.combatSettings;
__instance.isPeaceMode = false;
_needConvert = true;
}
[HarmonyTranspiler]
[HarmonyPatch(typeof(GameData), nameof(GameData.Import))]
private static IEnumerable<CodeInstruction> GameData_Import_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{
var matcher = new CodeMatcher(instructions, generator);
matcher.Start().MatchForward(false,
new CodeMatch(instr => (instr.opcode == OpCodes.Ldc_I4 || instr.opcode == OpCodes.Ldc_I4_S) && instr.OperandIs(0x1B)),
new CodeMatch(OpCodes.Call, AccessTools.Method(typeof(PerformanceMonitor), nameof(PerformanceMonitor.EndData)))
);
matcher.Advance(2).Opcode = OpCodes.Brfalse;
matcher.Insert(
new CodeInstruction(OpCodes.Ldsfld, AccessTools.Field(typeof(ConvertSavesFromPeace), nameof(ConvertSavesFromPeace._needConvert)))
);
return matcher.InstructionEnumeration();
}
[HarmonyPostfix]
[HarmonyPatch(typeof(GameData), nameof(GameData.Import))]
private static void GameData_Import_Postfix()
{
_needConvert = false;
}
}
}

View File

@@ -1,50 +0,0 @@
using System;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using HarmonyLib;
using UXAssist.Patches;
namespace UXAssist.ModsCompat;
public static class AuxilaryfunctionWrapper
{
private const string AuxilaryfunctionGuid = "cn.blacksnipe.dsp.Auxilaryfunction";
public static ConfigEntry<bool> ShowStationInfo;
public static void Start(Harmony harmony)
{
if (!Chainloader.PluginInfos.TryGetValue(AuxilaryfunctionGuid, out var pluginInfo)) return;
var assembly = pluginInfo.Instance.GetType().Assembly;
try
{
var classType = assembly.GetType("Auxilaryfunction.Auxilaryfunction");
ShowStationInfo = (ConfigEntry<bool>)AccessTools.Field(classType, "ShowStationInfo").GetValue(pluginInfo.Instance);
}
catch
{
UXAssist.Logger.LogWarning("Failed to get ShowStationInfo from Auxilaryfunction");
}
try
{
var classType = assembly.GetType("Auxilaryfunction.Patch.SpeedUpPatch");
harmony.Patch(AccessTools.PropertySetter(classType, "Enable"),
new HarmonyMethod(AccessTools.Method(typeof(AuxilaryfunctionWrapper), nameof(PatchSpeedUpPatchEnable))));
}
catch
{
UXAssist.Logger.LogWarning("Failed to patch SpeedUpPatch.set_Enable() from Auxilaryfunction");
}
}
public static void PatchSpeedUpPatchEnable(bool value)
{
if (!value)
{
GamePatch.EnableGameUpsFactor = true;
return;
}
if (Math.Abs(GamePatch.GameUpsFactor.Value - 1.0) < 0.001) return;
GamePatch.EnableGameUpsFactor = false;
UXAssist.Logger.LogInfo("Game UPS changing is disabled when using Auxilaryfunction's speed up feature");
}
}

View File

@@ -1,15 +0,0 @@
using BepInEx.Bootstrap;
using HarmonyLib;
namespace UXAssist.ModsCompat;
public static class BulletTimeWrapper
{
private const string BulletTimeGuid = "com.starfi5h.plugin.BulletTime";
public static bool HasBulletTime;
public static void Start(Harmony _)
{
HasBulletTime = Chainloader.PluginInfos.TryGetValue(BulletTimeGuid, out var _);
}
}

View File

@@ -1,540 +0,0 @@
using System.Collections.Generic;
using System.Reflection;
using System.Reflection.Emit;
using BepInEx.Configuration;
using HarmonyLib;
using UnityEngine.UI;
using UXAssist.Common;
namespace UXAssist.Patches;
public class DysonSpherePatch: PatchImpl<DysonSpherePatch>
{
public static ConfigEntry<bool> StopEjectOnNodeCompleteEnabled;
public static ConfigEntry<bool> OnlyConstructNodesEnabled;
public static ConfigEntry<int> AutoConstructMultiplier;
private static FieldInfo _totalNodeSpInfo, _totalFrameSpInfo, _totalCpInfo;
public static void Init()
{
I18N.Add("[UXAssist] No node to fill", "[UXAssist] No node to fill", "[UXAssist] 无可建造节点");
Enable(true);
StopEjectOnNodeCompleteEnabled.SettingChanged += (_, _) => StopEjectOnNodeComplete.Enable(StopEjectOnNodeCompleteEnabled.Value);
OnlyConstructNodesEnabled.SettingChanged += (_, _) => OnlyConstructNodes.Enable(OnlyConstructNodesEnabled.Value);
_totalNodeSpInfo = AccessTools.Field(typeof(DysonSphereLayer), "totalNodeSP");
_totalFrameSpInfo = AccessTools.Field(typeof(DysonSphereLayer), "totalFrameSP");
_totalCpInfo = AccessTools.Field(typeof(DysonSphereLayer), "totalCP");
}
public static void Start()
{
StopEjectOnNodeComplete.Enable(StopEjectOnNodeCompleteEnabled.Value);
OnlyConstructNodes.Enable(OnlyConstructNodesEnabled.Value);
}
public static void Uninit()
{
StopEjectOnNodeComplete.Enable(false);
OnlyConstructNodes.Enable(false);
Enable(false);
}
[HarmonyPrefix]
[HarmonyPatch(typeof(DysonSwarm), nameof(DysonSwarm.AutoConstruct))]
private static bool DysonSwarm_AutoConstruct_Prefix(DysonSwarm __instance)
{
return __instance.dysonSphere.autoNodeCount == 0;
}
[HarmonyPrefix]
[HarmonyPatch(typeof(DysonSphere), nameof(DysonSphere.AutoConstruct))]
private static bool DysonSphere_AutoConstruct_Prefix(DysonSphere __instance)
{
var totalCount = AutoConstructMultiplier.Value * 6;
foreach (var dysonSphereLayer in __instance.layersIdBased)
{
if (dysonSphereLayer == null) continue;
for (var j = dysonSphereLayer.nodePool.Length - 1; j >= 0; j--)
{
var dysonNode = dysonSphereLayer.nodePool[j];
if (dysonNode == null || dysonNode.id != j) continue;
var count = dysonNode._spReq - dysonNode.spOrdered;
int todoCount;
int[] productRegister;
if (count > 0)
{
if (count > totalCount)
{
count = totalCount;
}
todoCount = count;
if (dysonNode.sp < dysonNode.spMax)
{
int diff;
if (dysonNode.sp + count > dysonNode.spMax)
{
diff = dysonNode.spMax - dysonNode.sp;
count -= diff;
dysonNode._spReq -= diff;
dysonNode.sp = dysonNode.spMax;
}
else
{
diff = count;
dysonNode._spReq -= diff;
dysonNode.sp += diff;
count = 0;
}
// Make compatible with DSPOptimizations
if (_totalNodeSpInfo != null)
_totalNodeSpInfo.SetValue(dysonSphereLayer, (long)_totalNodeSpInfo.GetValue(dysonSphereLayer) + diff - 1);
__instance.UpdateProgress(dysonNode);
}
if (count > 0)
{
var frameCount = dysonNode.frames.Count;
var frameIndex = dysonNode.frameTurn % frameCount;
for (var i = frameCount; i > 0 && count > 0; i--)
{
var dysonFrame = dysonNode.frames[frameIndex];
var spMax = dysonFrame.spMax >> 1;
if (dysonFrame.nodeA == dysonNode && dysonFrame.spA < spMax)
{
int diff;
if (dysonFrame.spA + count > spMax)
{
diff = spMax - dysonFrame.spA;
count -= diff;
dysonNode._spReq -= diff;
dysonFrame.spA = spMax;
}
else
{
diff = count;
dysonNode._spReq -= diff;
dysonFrame.spA += diff;
count = 0;
}
// Make compatible with DSPOptimizations
if (_totalFrameSpInfo != null)
_totalFrameSpInfo.SetValue(dysonSphereLayer, (long)_totalFrameSpInfo.GetValue(dysonSphereLayer) + diff - 1);
__instance.UpdateProgress(dysonFrame);
}
if (count > 0 && dysonFrame.nodeB == dysonNode && dysonFrame.spB < spMax)
{
int diff;
if (dysonFrame.spB + count > spMax)
{
diff = spMax - dysonFrame.spB;
count -= diff;
dysonNode._spReq -= diff;
dysonFrame.spB = spMax;
}
else
{
diff = count;
dysonNode._spReq -= diff;
dysonFrame.spB += diff;
count = 0;
}
// Make compatible with DSPOptimizations
if (_totalFrameSpInfo != null)
_totalFrameSpInfo.SetValue(dysonSphereLayer, (long)_totalFrameSpInfo.GetValue(dysonSphereLayer) + diff - 1);
__instance.UpdateProgress(dysonFrame);
}
frameIndex = (frameIndex + 1) % frameCount;
}
dysonNode.frameTurn = frameIndex;
}
if (dysonNode.spOrdered >= dysonNode._spReq)
{
__instance.RemoveAutoNode(dysonNode);
__instance.PickAutoNode();
}
productRegister = __instance.productRegister;
if (productRegister != null)
{
lock (productRegister)
{
productRegister[11902] += todoCount - count;
}
}
}
count = dysonNode._cpReq - dysonNode.cpOrdered;
if (count > 0)
{
if (count > totalCount) count = totalCount;
todoCount = count;
var shellCount = dysonNode.shells.Count;
var shellIndex = dysonNode.shellTurn % shellCount;
for (var i = shellCount; i > 0 && count > 0; i--)
{
var dysonShell = dysonNode.shells[shellIndex];
var nodeIndex = dysonShell.nodeIndexMap[dysonNode.id];
var diff = (dysonShell.vertsqOffset[nodeIndex + 1] - dysonShell.vertsqOffset[nodeIndex]) * dysonShell.cpPerVertex - dysonShell.nodecps[nodeIndex];
if (diff > count)
diff = count;
count -= diff;
dysonNode._cpReq -= diff;
dysonShell.nodecps[nodeIndex] += diff;
dysonShell.nodecps[dysonShell.nodecps.Length - 1] += diff;
// Make compatible with DSPOptimizations
if (_totalCpInfo != null)
{
_totalCpInfo.SetValue(dysonSphereLayer, (long)_totalCpInfo.GetValue(dysonSphereLayer) + diff);
dysonShell.SetMaterialDynamicVars();
}
shellIndex = (shellIndex + 1) % shellCount;
}
dysonNode.shellTurn = shellIndex;
var solarSailCount = todoCount - count;
productRegister = __instance.productRegister;
if (productRegister != null)
{
lock (productRegister)
{
productRegister[11901] += solarSailCount;
productRegister[11903] += solarSailCount;
}
}
var consumeRegister = __instance.consumeRegister;
if (consumeRegister != null)
{
lock (consumeRegister)
{
consumeRegister[11901] += solarSailCount;
}
}
}
}
}
return false;
}
[HarmonyTranspiler]
[HarmonyPriority(Priority.First)]
[HarmonyPatch(typeof(DysonNode), nameof(DysonNode.ConstructCp))]
private static IEnumerable<CodeInstruction> DysonSpherePatch_DysonNode_ConstructCp_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{
var matcher = new CodeMatcher(instructions, generator);
matcher.MatchBack(false,
new CodeMatch(OpCodes.Ldc_I4_0),
new CodeMatch(OpCodes.Callvirt, AccessTools.Method(typeof(DysonShell), nameof(DysonShell.Construct)))
).Advance(3).InsertAndAdvance(
// node._cpReq = node._cpReq - 1;
new CodeInstruction(OpCodes.Ldarg_0),
new CodeInstruction(OpCodes.Ldarg_0),
new CodeInstruction(OpCodes.Ldfld, AccessTools.Field(typeof(DysonNode), nameof(DysonNode._cpReq))),
new CodeInstruction(OpCodes.Ldc_I4_1),
new CodeInstruction(OpCodes.Sub),
new CodeInstruction(OpCodes.Stfld, AccessTools.Field(typeof(DysonNode), nameof(DysonNode._cpReq)))
);
// Remove use of RecalcCpReq()
matcher.MatchForward(false,
new CodeMatch(OpCodes.Ldarg_0),
new CodeMatch(OpCodes.Call, AccessTools.Method(typeof(DysonNode), nameof(DysonNode.RecalcCpReq)))
);
var labels = matcher.Labels;
matcher.RemoveInstructions(2).Labels.AddRange(labels);
return matcher.InstructionEnumeration();
}
private class StopEjectOnNodeComplete: PatchImpl<StopEjectOnNodeComplete>
{
private static HashSet<int>[] _nodeForAbsorb;
private static bool _initialized;
protected override void OnEnable()
{
InitNodeForAbsorb();
GameLogic.OnGameBegin += GameMain_Begin_Postfix;
GameLogic.OnGameEnd += GameMain_End_Postfix;
}
protected override void OnDisable()
{
GameLogic.OnGameEnd -= GameMain_End_Postfix;
GameLogic.OnGameBegin -= GameMain_Begin_Postfix;
_initialized = false;
_nodeForAbsorb = null;
}
private static void InitNodeForAbsorb()
{
_initialized = false;
_nodeForAbsorb = null;
var data = GameMain.data;
var galaxy = data?.galaxy;
if (galaxy == null) return;
var galaxyStarCount = galaxy.starCount;
_nodeForAbsorb = new HashSet<int>[galaxyStarCount];
var spheres = data.dysonSpheres;
if (spheres == null) return;
foreach (var sphere in spheres)
{
if (sphere?.layersSorted == null) continue;
var starIndex = sphere.starData.index;
if (starIndex >= galaxyStarCount) continue;
foreach (var layer in sphere.layersSorted)
{
if (layer == null) continue;
for (var i = layer.nodeCursor - 1; i > 0; i--)
{
var node = layer.nodePool[i];
if (node == null || node.id != i || node.sp < node.spMax || node.cpReqOrder == 0) continue;
SetNodeForAbsorb(starIndex, layer.id, node.id, true);
}
}
}
_initialized = true;
}
private static void SetNodeForAbsorb(int index, int layerId, int nodeId, bool canAbsorb)
{
ref var comp = ref _nodeForAbsorb[index];
comp ??= [];
var idx = nodeId * 10 + layerId;
if (canAbsorb)
comp.Add(idx);
else
comp.Remove(idx);
}
private static void UpdateNodeForAbsorbOnSpChange(DysonNode node)
{
if (!_initialized) return;
if (node.sp < node.spMax || node.cpReqOrder <= 0) return;
var shells = node.shells;
if (shells.Count == 0) return;
SetNodeForAbsorb(shells[0].dysonSphere.starData.index, node.layerId, node.id, true);
}
private static void UpdateNodeForAbsorbOnCpChange(DysonNode node)
{
if (!_initialized) return;
if (node.sp < node.spMax || node.cpReqOrder > 0) return;
var shells = node.shells;
if (shells.Count == 0) return;
SetNodeForAbsorb(shells[0].dysonSphere.starData.index, node.layerId, node.id, false);
}
private static bool AnyNodeForAbsorb(int starIndex)
{
var comp = _nodeForAbsorb[starIndex];
return comp is { Count: > 0 };
}
private static void GameMain_Begin_Postfix()
{
InitNodeForAbsorb();
}
private static void GameMain_End_Postfix()
{
_initialized = false;
_nodeForAbsorb = null;
}
[HarmonyPostfix]
[HarmonyPatch(typeof(DysonNode), nameof(DysonNode.RecalcCpReq))]
private static void DysonNode_RecalcCpReq_Postfix(DysonNode __instance)
{
UpdateNodeForAbsorbOnCpChange(__instance);
}
[HarmonyPrefix]
[HarmonyPatch(typeof(DysonSphereLayer), nameof(DysonSphereLayer.RemoveDysonNode))]
private static void DysonSphereLayer_RemoveDysonNode_Prefix(DysonSphereLayer __instance, int nodeId)
{
if (_initialized)
SetNodeForAbsorb(__instance.starData.index, __instance.id, nodeId, false);
}
[HarmonyPrefix]
[HarmonyPatch(typeof(DysonSphere), nameof(DysonSphere.ResetNew))]
private static void DysonSphere_ResetNew_Prefix(DysonSphere __instance)
{
if (_nodeForAbsorb == null) return;
var starIndex = __instance.starData.index;
if (starIndex >= _nodeForAbsorb.Length || _nodeForAbsorb[starIndex] == null) return;
_nodeForAbsorb[starIndex].Clear();
_nodeForAbsorb[starIndex] = null;
}
[HarmonyTranspiler]
[HarmonyPatch(typeof(EjectorComponent), nameof(EjectorComponent.InternalUpdate))]
private static IEnumerable<CodeInstruction> EjectorComponent_InternalUpdate_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{
var matcher = new CodeMatcher(instructions, generator);
matcher.MatchForward(false,
// if (this.runtimeOrbitId == 0
new CodeMatch(OpCodes.Ldarg_0),
new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(EjectorComponent), nameof(EjectorComponent.runtimeOrbitId))),
new CodeMatch(OpCodes.Brtrue)
).Advance(2).InsertAndAdvance(
// || !StopEjectOnNodeComplete.AnyNodeForAbsorb(this.starData.index))
new CodeInstruction(OpCodes.Ldc_I4_0),
new CodeInstruction(OpCodes.Cgt),
new CodeInstruction(OpCodes.Ldarg_3),
new CodeInstruction(OpCodes.Ldfld, AccessTools.Field(typeof(DysonSwarm), nameof(DysonSwarm.starData))),
new CodeInstruction(OpCodes.Ldfld, AccessTools.Field(typeof(StarData), nameof(StarData.index))),
new CodeInstruction(OpCodes.Call, AccessTools.Method(typeof(StopEjectOnNodeComplete), nameof(AnyNodeForAbsorb))),
new CodeInstruction(OpCodes.And)
);
// Do not search for next orbit if runtimeOrbitId is not 0 (which means nodes are completed)
Label? jmpTarget = null;
matcher.MatchForward(false,
new CodeMatch(OpCodes.Ldarg_0),
new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(EjectorComponent), nameof(EjectorComponent.autoOrbit))),
new CodeMatch(ci => ci.Branches(out jmpTarget))
);
if (jmpTarget == null) return matcher.InstructionEnumeration();
var labels = matcher.Labels;
matcher.Labels = null;
matcher.Insert(
new CodeInstruction(OpCodes.Ldarg_0).WithLabels(labels),
new CodeInstruction(OpCodes.Ldfld, AccessTools.Field(typeof(EjectorComponent), nameof(EjectorComponent.runtimeOrbitId))),
new CodeInstruction(OpCodes.Brtrue, jmpTarget.Value)
);
return matcher.InstructionEnumeration();
}
[HarmonyTranspiler]
[HarmonyPatch(typeof(DysonNode), nameof(DysonNode.ConstructSp))]
private static IEnumerable<CodeInstruction> DysonNode_ConstructSp_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{
var matcher = new CodeMatcher(instructions, generator);
matcher.Start().MatchForward(false,
new CodeMatch(OpCodes.Stfld, AccessTools.Field(typeof(DysonNode), nameof(DysonNode.sp)))
).Advance(1);
var labels = matcher.Labels;
matcher.Labels = [];
matcher.Insert(
new CodeInstruction(OpCodes.Ldarg_0).WithLabels(labels),
new CodeInstruction(OpCodes.Call, AccessTools.Method(typeof(StopEjectOnNodeComplete), nameof(UpdateNodeForAbsorbOnSpChange)))
);
return matcher.InstructionEnumeration();
}
[HarmonyTranspiler]
[HarmonyPatch(typeof(DysonNode), nameof(DysonNode.ConstructCp))]
private static IEnumerable<CodeInstruction> DysonNode_ConstructCp_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{
var matcher = new CodeMatcher(instructions, generator);
matcher.MatchBack(false,
// Search for previous patch:
// node._cpReq = node._cpReq - 1;
new CodeMatch(OpCodes.Ldarg_0),
new CodeMatch(OpCodes.Ldarg_0),
new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(DysonNode), nameof(DysonNode._cpReq))),
new CodeMatch(OpCodes.Ldc_I4_1),
new CodeMatch(OpCodes.Sub),
new CodeMatch(OpCodes.Stfld, AccessTools.Field(typeof(DysonNode), nameof(DysonNode._cpReq)))
).Advance(6).Insert(
new CodeInstruction(OpCodes.Ldarg_0),
new CodeInstruction(OpCodes.Call, AccessTools.Method(typeof(StopEjectOnNodeComplete), nameof(UpdateNodeForAbsorbOnCpChange)))
);
return matcher.InstructionEnumeration();
}
[HarmonyTranspiler]
[HarmonyPatch(typeof(UIEjectorWindow), nameof(UIEjectorWindow._OnUpdate))]
static IEnumerable<CodeInstruction> UIEjectorWindow__OnUpdate_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{
var matcher = new CodeMatcher(instructions, generator);
var label1 = generator.DefineLabel();
var label2 = generator.DefineLabel();
matcher.MatchForward(false,
// this.stateText.text = "轨道未设置".Translate();
new CodeMatch(OpCodes.Ldstr, "待机"),
new CodeMatch(OpCodes.Call, AccessTools.Method(typeof(Localization), nameof(Localization.Translate))),
new CodeMatch(OpCodes.Callvirt, AccessTools.PropertySetter(typeof(Text), nameof(Text.text)))
).InsertAndAdvance(
// if (StopEjectOnNodeComplete.AnyNodeForAbsorb(this.starData.index))
new CodeInstruction(OpCodes.Ldarg_0),
new CodeInstruction(OpCodes.Ldfld, AccessTools.Field(typeof(UIEjectorWindow), nameof(UIEjectorWindow.factorySystem))),
new CodeInstruction(OpCodes.Ldfld, AccessTools.Field(typeof(FactorySystem), nameof(FactorySystem.planet))),
new CodeInstruction(OpCodes.Ldfld, AccessTools.Field(typeof(PlanetData), nameof(PlanetData.star))),
new CodeInstruction(OpCodes.Ldfld, AccessTools.Field(typeof(StarData), nameof(StarData.index))),
new CodeInstruction(OpCodes.Call, AccessTools.Method(typeof(StopEjectOnNodeComplete), nameof(AnyNodeForAbsorb))),
new CodeInstruction(OpCodes.Brfalse, label1)
).Advance(1).InsertAndAdvance(
new CodeInstruction(OpCodes.Br, label2),
new CodeInstruction(OpCodes.Ldstr, "[UXAssist] No node to fill").WithLabels(label1),
new CodeInstruction(OpCodes.Call, AccessTools.Method(typeof(Localization), nameof(Localization.Translate)))
).Labels.Add(label2);
return matcher.InstructionEnumeration();
}
}
[PatchSetCallbackFlag(PatchCallbackFlag.CallOnDisableAfterUnpatch)]
private class OnlyConstructNodes: PatchImpl<OnlyConstructNodes>
{
protected override void OnEnable()
{
RecheckDysonSphereAutoNodes();
}
protected override void OnDisable()
{
RecheckDysonSphereAutoNodes();
}
private static void RecheckDysonSphereAutoNodes()
{
var spheres = GameMain.data?.dysonSpheres;
if (spheres == null) return;
foreach (var sphere in spheres)
{
if (sphere == null) continue;
sphere.CheckAutoNodes();
if (sphere.autoNodeCount > 0) continue;
sphere.PickAutoNode();
sphere.PickAutoNode();
sphere.PickAutoNode();
sphere.PickAutoNode();
}
}
[HarmonyTranspiler]
[HarmonyPatch(typeof(DysonNode), nameof(DysonNode.spReqOrder), MethodType.Getter)]
private static IEnumerable<CodeInstruction> DysonNode_spReqOrder_Getter_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{
var matcher = new CodeMatcher(instructions, generator);
matcher.MatchForward(false,
new CodeMatch(OpCodes.Ldarg_0),
new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(DysonNode), nameof(DysonNode._spReq)))
).Advance(1).SetInstructionAndAdvance(
new CodeInstruction(OpCodes.Ldfld, AccessTools.Field(typeof(DysonNode), nameof(DysonNode.spMax)))
).Insert(
new CodeInstruction(OpCodes.Ldarg_0),
new CodeInstruction(OpCodes.Ldfld, AccessTools.Field(typeof(DysonNode), nameof(DysonNode.sp))),
new CodeInstruction(OpCodes.Sub)
);
return matcher.InstructionEnumeration();
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,744 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection.Emit;
using System.Xml;
using BepInEx.Configuration;
using CommonAPI.Systems;
using HarmonyLib;
using UnityEngine;
using UXAssist.Common;
using UXAssist.Functions;
namespace UXAssist.Patches;
public class GamePatch : PatchImpl<GamePatch>
{
public static ConfigEntry<bool> EnableWindowResizeEnabled;
public static ConfigEntry<bool> LoadLastWindowRectEnabled;
public static ConfigEntry<int> MouseCursorScaleUpMultiplier;
// public static ConfigEntry<bool> AutoSaveOptEnabled;
public static ConfigEntry<bool> ConvertSavesFromPeaceEnabled;
public static ConfigEntry<Vector4> LastWindowRect;
public static ConfigEntry<bool> ProfileBasedSaveFolderEnabled;
public static ConfigEntry<bool> ProfileBasedOptionEnabled;
public static ConfigEntry<string> DefaultProfileName;
public static ConfigEntry<double> GameUpsFactor;
private static PressKeyBind _speedDownKey;
private static PressKeyBind _speedUpKey;
private static bool _enableGameUpsFactor = true;
public static bool EnableGameUpsFactor
{
get => _enableGameUpsFactor;
set
{
_enableGameUpsFactor = value;
if (value)
{
var oldFixUps = FPSController.instance.fixUPS;
if (oldFixUps <= 1.0)
{
GameUpsFactor.Value = 1.0;
return;
}
GameUpsFactor.Value = Maths.Clamp(FPSController.instance.fixUPS / GameMain.tickPerSec, 0.1, 10.0);
}
else
{
GameUpsFactor.Value = 1.0;
}
}
}
public static void Init()
{
_speedDownKey = KeyBindings.RegisterKeyBinding(new BuiltinKey
{
key = new CombineKey((int)KeyCode.Minus, CombineKey.CTRL_COMB, ECombineKeyAction.OnceClick, false),
conflictGroup = KeyBindConflict.MOVEMENT | KeyBindConflict.FLYING | KeyBindConflict.SAILING | KeyBindConflict.BUILD_MODE_1 | KeyBindConflict.KEYBOARD_KEYBIND,
name = "UPSSpeedDown",
canOverride = true
}
);
I18N.Add("KEYUPSSpeedDown", "[UXA] Decrease logical frame rate", "[UXA] 降低逻辑帧率");
_speedUpKey = KeyBindings.RegisterKeyBinding(new BuiltinKey
{
key = new CombineKey((int)KeyCode.Equals, CombineKey.CTRL_COMB, ECombineKeyAction.OnceClick, false),
conflictGroup = KeyBindConflict.MOVEMENT | KeyBindConflict.UI | KeyBindConflict.FLYING | KeyBindConflict.SAILING | KeyBindConflict.BUILD_MODE_1 | KeyBindConflict.KEYBOARD_KEYBIND,
name = "UPSSpeedUp",
canOverride = true
}
);
I18N.Add("KEYUPSSpeedUp", "[UXA] Increase logical frame rate", "[UXA] 提升逻辑帧率");
I18N.Add("Logical frame rate: {0}x", "[UXA] Logical frame rate: {0}x", "[UXA] 逻辑帧速率: {0}x");
EnableWindowResizeEnabled.SettingChanged += (_, _) => EnableWindowResize.Enable(EnableWindowResizeEnabled.Value);
LoadLastWindowRectEnabled.SettingChanged += (_, _) => LoadLastWindowRect.Enable(LoadLastWindowRectEnabled.Value);
MouseCursorScaleUpMultiplier.SettingChanged += (_, _) =>
{
MouseCursorScaleUp.NeedReloadCursors = true;
MouseCursorScaleUp.Enable(MouseCursorScaleUpMultiplier.Value > 1);
};
// AutoSaveOptEnabled.SettingChanged += (_, _) => AutoSaveOpt.Enable(AutoSaveOptEnabled.Value);
ConvertSavesFromPeaceEnabled.SettingChanged += (_, _) => ConvertSavesFromPeace.Enable(ConvertSavesFromPeaceEnabled.Value);
ProfileBasedSaveFolderEnabled.SettingChanged += (_, _) => RefreshSavePath();
ProfileBasedOptionEnabled.SettingChanged += (_, _) => ProfileBasedOption.Enable(ProfileBasedOptionEnabled.Value);
DefaultProfileName.SettingChanged += (_, _) => RefreshSavePath();
GameUpsFactor.SettingChanged += (_, _) =>
{
if (!EnableGameUpsFactor || GameUpsFactor.Value == 0.0) return;
if (Math.Abs(GameUpsFactor.Value - 1.0) < 0.001)
{
FPSController.SetFixUPS(0.0);
return;
}
FPSController.SetFixUPS(GameMain.tickPerSec * GameUpsFactor.Value);
};
ProfileBasedOption.Enable(ProfileBasedOptionEnabled.Value);
}
public static void Start()
{
RefreshSavePath();
EnableWindowResize.Enable(EnableWindowResizeEnabled.Value);
LoadLastWindowRect.Enable(LoadLastWindowRectEnabled.Value);
MouseCursorScaleUp.NeedReloadCursors = false;
MouseCursorScaleUp.Enable(MouseCursorScaleUpMultiplier.Value > 1);
// AutoSaveOpt.Enable(AutoSaveOptEnabled.Value);
ConvertSavesFromPeace.Enable(ConvertSavesFromPeaceEnabled.Value);
Enable(true);
}
public static void Uninit()
{
Enable(false);
LoadLastWindowRect.Enable(false);
EnableWindowResize.Enable(false);
MouseCursorScaleUp.NeedReloadCursors = false;
MouseCursorScaleUp.Enable(false);
// AutoSaveOpt.Enable(false);
ConvertSavesFromPeace.Enable(false);
}
public static void OnUpdate()
{
if (!_enableGameUpsFactor) return;
if (_speedDownKey.keyValue)
{
GameUpsFactor.Value = Maths.Clamp(Math.Round((GameUpsFactor.Value - 0.5) * 2.0) / 2.0, 0.1, 10.0);
UIRoot.instance.uiGame.generalTips.InvokeRealtimeTipAhead(string.Format("Logical frame rate: {0}x".Translate(), GameUpsFactor.Value));
}
if (_speedUpKey.keyValue)
{
GameUpsFactor.Value = Maths.Clamp(Math.Round((GameUpsFactor.Value + 0.5) * 2.0) / 2.0, 0.1, 10.0);
UIRoot.instance.uiGame.generalTips.InvokeRealtimeTipAhead(string.Format("Logical frame rate: {0}x".Translate(), GameUpsFactor.Value));
}
}
private static void RefreshSavePath()
{
var profileName = WindowFunctions.ProfileName;
if (profileName == null)
{
// We should initialize WindowFunctions before using WindowFunctions.ProfileName
WindowFunctions.Init();
profileName = WindowFunctions.ProfileName;
if (profileName == null) return;
}
if (UIRoot.instance.loadGameWindow.gameObject.activeSelf)
{
UIRoot.instance.loadGameWindow._Close();
}
if (UIRoot.instance.saveGameWindow.gameObject.activeSelf)
{
UIRoot.instance.saveGameWindow._Close();
}
string gameSavePath;
if (ProfileBasedSaveFolderEnabled.Value && string.Compare(DefaultProfileName.Value, profileName, StringComparison.OrdinalIgnoreCase) != 0)
gameSavePath = $"{GameConfig.gameDocumentFolder}Save/{profileName}/";
else
gameSavePath = $"{GameConfig.gameDocumentFolder}Save/";
if (string.Compare(GameConfig.gameSavePath, gameSavePath, StringComparison.OrdinalIgnoreCase) == 0) return;
GameConfig.gameSavePath = gameSavePath;
if (!Directory.Exists(GameConfig.gameSavePath))
{
Directory.CreateDirectory(GameConfig.gameSavePath);
}
}
[HarmonyPrefix, HarmonyPatch(typeof(GameMain), nameof(GameMain.HandleApplicationQuit))]
private static void GameMain_HandleApplicationQuit_Prefix()
{
var wnd = WindowFunctions.FindGameWindow();
if (wnd == IntPtr.Zero) return;
WinApi.GetWindowRect(wnd, out var rect);
LastWindowRect.Value = new Vector4(rect.Left, rect.Top, Screen.width, Screen.height);
}
private class EnableWindowResize : PatchImpl<EnableWindowResize>
{
private static bool _enabled;
protected override void OnEnable()
{
var wnd = WindowFunctions.FindGameWindow();
if (wnd == IntPtr.Zero)
{
Enable(false);
return;
}
_enabled = true;
WinApi.SetWindowLong(wnd, WinApi.GWL_STYLE,
WinApi.GetWindowLong(wnd, WinApi.GWL_STYLE) | WinApi.WS_THICKFRAME | WinApi.WS_MAXIMIZEBOX);
}
protected override void OnDisable()
{
var wnd = WindowFunctions.FindGameWindow();
if (wnd == IntPtr.Zero)
return;
_enabled = false;
WinApi.SetWindowLong(wnd, WinApi.GWL_STYLE,
WinApi.GetWindowLong(wnd, WinApi.GWL_STYLE) & ~(WinApi.WS_THICKFRAME | WinApi.WS_MAXIMIZEBOX));
}
[HarmonyPostfix]
[HarmonyPatch(typeof(UIOptionWindow), nameof(UIOptionWindow.ApplyOptions))]
private static void UIOptionWindow_ApplyOptions_Postfix()
{
var wnd = WindowFunctions.FindGameWindow();
if (wnd == IntPtr.Zero) return;
if (_enabled)
WinApi.SetWindowLong(wnd, WinApi.GWL_STYLE,
WinApi.GetWindowLong(wnd, WinApi.GWL_STYLE) | WinApi.WS_THICKFRAME | WinApi.WS_MAXIMIZEBOX);
else
WinApi.SetWindowLong(wnd, WinApi.GWL_STYLE,
WinApi.GetWindowLong(wnd, WinApi.GWL_STYLE) & ~(WinApi.WS_THICKFRAME | WinApi.WS_MAXIMIZEBOX));
}
}
public class LoadLastWindowRect : PatchImpl<LoadLastWindowRect>
{
private static bool _loaded;
protected override void OnEnable()
{
GameLogic.OnDataLoaded += VFPreload_InvokeOnLoadWorkEnded_Postfix;
if (Screen.fullScreenMode is not (FullScreenMode.ExclusiveFullScreen or FullScreenMode.FullScreenWindow or FullScreenMode.MaximizedWindow))
{
var rect = LastWindowRect.Value;
var x = Mathf.RoundToInt(rect.x);
var y = Mathf.RoundToInt(rect.y);
var w = Mathf.RoundToInt(rect.z);
var h = Mathf.RoundToInt(rect.w);
var needFix = false;
if (w < 100)
{
w = 1280;
needFix = true;
}
if (h < 100)
{
h = 720;
needFix = true;
}
var rc = new WinApi.Rect {Left = x, Top = y, Right = x + w, Bottom = y + h};
if (WinApi.MonitorFromRect(ref rc, 0) == IntPtr.Zero)
{
x = 0;
y = 0;
w = 1280;
h = 720;
needFix = true;
}
if (needFix)
{
LastWindowRect.Value = new Vector4(x, y, w, h);
}
}
MoveWindowPosition();
}
protected override void OnDisable()
{
GameLogic.OnDataLoaded -= VFPreload_InvokeOnLoadWorkEnded_Postfix;
}
public static void MoveWindowPosition(bool setResolution = false)
{
if (Screen.fullScreenMode is FullScreenMode.ExclusiveFullScreen or FullScreenMode.FullScreenWindow or FullScreenMode.MaximizedWindow || GameMain.isRunning) return;
var wnd = WindowFunctions.FindGameWindow();
if (wnd == IntPtr.Zero) return;
var rect = LastWindowRect.Value;
if (rect is { z: 0f, w: 0f }) return;
var x = Mathf.RoundToInt(rect.x);
var y = Mathf.RoundToInt(rect.y);
if (setResolution)
{
var w = Mathf.RoundToInt(rect.z);
var h = Mathf.RoundToInt(rect.w);
Screen.SetResolution(w, h, false);
}
WinApi.SetWindowPos(wnd, IntPtr.Zero, x, y, 0, 0, 0x0235);
}
[HarmonyPrefix]
[HarmonyPatch(typeof(Screen), nameof(Screen.SetResolution), typeof(int), typeof(int), typeof(FullScreenMode), typeof(int))]
private static void Screen_SetResolution_Prefix(ref int width, ref int height, FullScreenMode fullscreenMode)
{
if (fullscreenMode is FullScreenMode.ExclusiveFullScreen or FullScreenMode.FullScreenWindow or FullScreenMode.MaximizedWindow || GameMain.isRunning) return;
var rect = LastWindowRect.Value;
if (rect is { z: 0f, w: 0f }) return;
var w = Mathf.RoundToInt(rect.z);
var h = Mathf.RoundToInt(rect.w);
width = w;
height = h;
}
[HarmonyPostfix]
[HarmonyPatch(typeof(Screen), nameof(Screen.SetResolution), typeof(int), typeof(int), typeof(FullScreenMode), typeof(int))]
private static void Screen_SetResolution_Postfix(FullScreenMode fullscreenMode)
{
MoveWindowPosition();
}
private static void VFPreload_InvokeOnLoadWorkEnded_Postfix()
{
if (_loaded || Screen.fullScreenMode is FullScreenMode.ExclusiveFullScreen or FullScreenMode.FullScreenWindow or FullScreenMode.MaximizedWindow) return;
_loaded = true;
var wnd = WindowFunctions.FindGameWindow();
if (wnd == IntPtr.Zero) return;
var rect = LastWindowRect.Value;
if (rect is { z: 0f, w: 0f }) return;
var x = Mathf.RoundToInt(rect.x);
var y = Mathf.RoundToInt(rect.y);
var w = Mathf.RoundToInt(rect.z);
var h = Mathf.RoundToInt(rect.w);
Screen.SetResolution(w, h, false);
WinApi.SetWindowPos(wnd, IntPtr.Zero, x, y, 0, 0, 0x0235);
if (EnableWindowResizeEnabled.Value)
WinApi.SetWindowLong(wnd, WinApi.GWL_STYLE,
WinApi.GetWindowLong(wnd, WinApi.GWL_STYLE) | WinApi.WS_THICKFRAME | WinApi.WS_MAXIMIZEBOX);
}
private static GameOption _gameOption;
[HarmonyPostfix]
[HarmonyPatch(typeof(UIOptionWindow), nameof(UIOptionWindow._OnOpen))]
private static void UIOptionWindow__OnOpen_Postfix()
{
_gameOption = DSPGame.globalOption;
}
[HarmonyTranspiler]
[HarmonyPatch(typeof(GameOption), nameof(GameOption.Apply))]
private static IEnumerable<CodeInstruction> UIOptionWindow_ApplyOptions_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{
var matcher = new CodeMatcher(instructions, generator);
var label1 = generator.DefineLabel();
matcher.MatchForward(false,
new CodeMatch(OpCodes.Call, AccessTools.Method(typeof(Screen), nameof(Screen.SetResolution), [typeof(int), typeof(int), typeof(bool), typeof(int)]))
).Advance(1).Labels.Add(label1);
matcher.Start().Insert(
Transpilers.EmitDelegate(() =>
_gameOption.fullscreen == DSPGame.globalOption.fullscreen &&
_gameOption.resolution.width == DSPGame.globalOption.resolution.width &&
_gameOption.resolution.height == DSPGame.globalOption.resolution.height &&
_gameOption.resolution.refreshRate == DSPGame.globalOption.resolution.refreshRate
),
new CodeInstruction(OpCodes.Brtrue, label1)
);
return matcher.InstructionEnumeration();
}
}
/*
private class AutoSaveOpt: PatchImpl<AutoSaveOpt>
{
protected override void OnEnable()
{
Directory.CreateDirectory(GameConfig.gameSaveFolder + "AutoSaves/");
}
[HarmonyPrefix]
[HarmonyPatch(typeof(GameSave), nameof(GameSave.AutoSave))]
private static bool GameSave_AutoSave_Prefix(ref bool __result)
{
if (!GameSave.SaveCurrentGame(GameSave.AutoSaveTmp))
{
GlobalObject.SaveOpCounter();
__result = false;
return false;
}
var tmpFilename = GameConfig.gameSaveFolder + GameSave.AutoSaveTmp + GameSave.saveExt;
var targetFilename = $"{GameConfig.gameSaveFolder}AutoSaves/[{GameMain.data.gameDesc.clusterString}] {DateTime.Now:yyyy-MM-dd_hh-mm-ss}{GameSave.saveExt}";
File.Move(tmpFilename, targetFilename);
__result = true;
return false;
}
[HarmonyPostfix]
[HarmonyPatch(typeof(UILoadGameWindow), nameof(UILoadGameWindow.RefreshList))]
public static void UILoadGameWindow_RefreshList_Postfix(UILoadGameWindow __instance)
{
var baseDir = GameConfig.gameSaveFolder + "AutoSaves/";
var files = Directory.GetFiles(baseDir, "*" + GameSave.saveExt, SearchOption.TopDirectoryOnly);
var entries = __instance.entries;
var entries2 = new List<UIGameSaveEntry>();
var entryPrefab = __instance.entryPrefab;
var entryPrefabParent = entryPrefab.transform.parent;
foreach (var f in files)
{
var fileInfo = new FileInfo(f);
var entry = Object.Instantiate(entryPrefab, entryPrefabParent);
entry.fileInfo = fileInfo;
entries2.Add(entry);
}
entries2.Sort((x, y) => -x.fileDate.CompareTo(y.fileDate));
if (entries2.Count > 10)
entries2.RemoveRange(10, entries2.Count - 10);
var autoSaveText = ">> " + "自动存档条目".Translate();
foreach (var entry in entries2)
{
entry.indexText.text = "";
var saveName = entry.saveName;
entry._saveName = $"AutoSaves/{saveName}";
var quoteIndex = saveName.IndexOf('[');
if (quoteIndex >= 0)
{
var quoteIndex2 = saveName.IndexOf(']', quoteIndex + 1);
if (quoteIndex2 > 0) saveName = saveName.Substring(quoteIndex, quoteIndex2 + 1 - quoteIndex);
}
entry.nameText.text = $"{autoSaveText} {saveName}";
entry.nameText.fontStyle = FontStyle.Italic;
entry.nameText.color = new Color(1f, 1f, 1f, 0.7f);
entry.timeText.text = $"{entry.fileDate:yyyy-MM-dd HH:mm:ss}";
GameSave.ReadModes(entry.fileInfo.FullName, out var isSandbox, out var isPeace);
if (entry.sandboxIcon != null)
{
entry.sandboxIcon.gameObject.SetActive(isSandbox);
}
if (entry.combatIcon != null)
{
entry.combatIcon.gameObject.SetActive(!isPeace);
}
entry.selected = false;
entry.gameObject.SetActive(true);
}
entries.AddRange(entries2);
entries.Sort((x, y) => -x.fileDate.CompareTo(y.fileDate));
var displayIndex = 1;
for (var i = 0; i < entries.Count; i++)
{
var entry = entries[i];
entry.index = i + 1;
entry.rectTrans.anchoredPosition = new Vector2(entry.rectTrans.anchoredPosition.x, -40 * i);
if (string.IsNullOrEmpty(entry.indexText.text)) continue;
entry.indexText.text = displayIndex.ToString();
displayIndex++;
}
}
[HarmonyPostfix]
[HarmonyPatch(typeof(UISaveGameWindow), nameof(UISaveGameWindow.RefreshList))]
public static void UISaveGameWindow_RefreshList_Postfix(UISaveGameWindow __instance)
{
var entries = __instance.entries;
entries.Sort((x, y) => -x.fileDate.CompareTo(y.fileDate));
for (var i = 0; i < entries.Count; i++)
{
var entry = entries[i];
entry.index = i + 1;
entry.rectTrans.anchoredPosition = new Vector2(entry.rectTrans.anchoredPosition.x, -40 * i);
entry.indexText.text = (i + 1).ToString();
}
}
[HarmonyTranspiler]
[HarmonyPatch(typeof(UILoadGameWindow), nameof(UILoadGameWindow.DoLoadSelectedGame))]
[HarmonyPatch(typeof(UILoadGameWindow), nameof(UILoadGameWindow.OnSelectedChange))]
private static IEnumerable<CodeInstruction> UILoadGameWindow_ReplaceSaveName_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{
var matcher = new CodeMatcher(instructions, generator);
matcher.Start().MatchForward(false,
new CodeMatch(OpCodes.Callvirt, AccessTools.PropertyGetter(typeof(UIGameSaveEntry), nameof(UIGameSaveEntry.saveName)))
);
matcher.Repeat(m => m.SetAndAdvance(OpCodes.Ldfld, AccessTools.Field(typeof(UIGameSaveEntry), nameof(UIGameSaveEntry._saveName))));
return matcher.InstructionEnumeration();
}
[HarmonyTranspiler]
[HarmonyPatch(typeof(GameSave), nameof(GameSave.LoadCurrentGame))]
[HarmonyPatch(typeof(GameSave), nameof(GameSave.LoadGameDesc))]
[HarmonyPatch(typeof(GameSave), nameof(GameSave.ReadHeader))]
[HarmonyPatch(typeof(GameSave), nameof(GameSave.ReadHeaderAndDescAndProperty))]
[HarmonyPatch(typeof(GameSave), nameof(GameSave.SaveExist))]
[HarmonyPatch(typeof(GameSave), nameof(GameSave.SavePath))]
private static IEnumerable<CodeInstruction> GameSave_RemoveValidateOnLoad_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{
var matcher = new CodeMatcher(instructions, generator);
matcher.Start().MatchForward(false,
new CodeMatch(OpCodes.Call, AccessTools.Method(typeof(CommonUtils), nameof(CommonUtils.ValidFileName)))
);
matcher.RemoveInstruction();
return matcher.InstructionEnumeration();
}
}
*/
private class ConvertSavesFromPeace : PatchImpl<ConvertSavesFromPeace>
{
private static bool _needConvert;
[HarmonyPostfix]
[HarmonyPatch(typeof(GameDesc), nameof(GameDesc.Import))]
private static void GameDesc_Import_Postfix(GameDesc __instance)
{
if (DSPGame.IsMenuDemo || !__instance.isPeaceMode) return;
__instance.combatSettings = UIRoot.instance.galaxySelect.uiCombat.combatSettings;
__instance.isPeaceMode = false;
_needConvert = true;
}
[HarmonyTranspiler]
[HarmonyPatch(typeof(GameData), nameof(GameData.Import))]
private static IEnumerable<CodeInstruction> GameData_Import_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{
var matcher = new CodeMatcher(instructions, generator);
matcher.Start().MatchForward(false,
new CodeMatch(OpCodes.Callvirt, AccessTools.PropertyGetter(typeof(Player), nameof(Player.mecha))),
new CodeMatch(OpCodes.Callvirt, AccessTools.Method(typeof(Mecha), nameof(Mecha.CheckCombatModuleDataIsValidPatch)))
);
matcher.Advance(2).Opcode = OpCodes.Brfalse;
matcher.Insert(
new CodeInstruction(OpCodes.Ldsfld, AccessTools.Field(typeof(ConvertSavesFromPeace), nameof(_needConvert)))
);
return matcher.InstructionEnumeration();
}
[HarmonyPostfix]
[HarmonyPatch(typeof(GameData), nameof(GameData.Import))]
private static void GameData_Import_Postfix()
{
_needConvert = false;
}
}
private class ProfileBasedOption : PatchImpl<ProfileBasedOption>
{
[HarmonyPrefix]
[HarmonyPatch(typeof(GameOption), nameof(GameOption.LoadGlobal))]
private static bool GameOption_LoadGlobal_Prefix(ref GameOption __instance)
{
UXAssist.Logger.LogDebug("Loading global option");
var profileName = WindowFunctions.ProfileName;
if (profileName == null)
{
// We should initialize WindowFunctions before using WindowFunctions.ProfileName
WindowFunctions.Init();
profileName = WindowFunctions.ProfileName;
if (profileName == null) return true;
}
if (string.Compare(DefaultProfileName.Value, profileName, StringComparison.OrdinalIgnoreCase) == 0) return true;
var optionPath = $"{GameConfig.gameDocumentFolder}Option/{profileName}.xml";
if (File.Exists(optionPath))
{
try
{
__instance.ImportXML(optionPath);
return false;
}
catch
{
}
}
var gameXMLOptionPath = GameConfig.gameXMLOptionPath;
if (File.Exists(gameXMLOptionPath))
{
try
{
__instance.ImportXML(gameXMLOptionPath);
return false;
}
catch (Exception ex)
{
Debug.LogException(ex);
}
}
GameOption.newlyCreated = true;
__instance.SetDefault();
return false;
}
[HarmonyPrefix]
[HarmonyPatch(typeof(GameOption), nameof(GameOption.SaveGlobal))]
private static bool GameOption_SaveGlobal_Prefix(ref GameOption __instance)
{
var profileName = WindowFunctions.ProfileName;
if (profileName == null) return true;
if (string.Compare(DefaultProfileName.Value, profileName, StringComparison.OrdinalIgnoreCase) == 0) return true;
var path = $"{GameConfig.gameDocumentFolder}Option";
if (!Directory.Exists(path))
{
Directory.CreateDirectory(path);
}
try
{
using FileStream fileStream = new($"{path}/{profileName}.xml", FileMode.Create, FileAccess.Write, FileShare.None);
using XmlTextWriter xmlTextWriter = new(fileStream, Console.OutputEncoding);
xmlTextWriter.Formatting = Formatting.Indented;
__instance.ExportXML(xmlTextWriter);
xmlTextWriter.Close();
}
catch (Exception ex)
{
Debug.LogException(ex);
}
return false;
}
}
[PatchSetCallbackFlag(PatchCallbackFlag.CallOnDisableAfterUnpatch)]
private class MouseCursorScaleUp : PatchImpl<MouseCursorScaleUp>
{
public static bool NeedReloadCursors;
protected override void OnEnable()
{
if (!NeedReloadCursors) return;
if (!UICursor.loaded) return;
UICursor.loaded = false;
UICursor.LoadCursors();
}
protected override void OnDisable()
{
if (!NeedReloadCursors) return;
if (!UICursor.loaded) return;
UICursor.loaded = false;
UICursor.LoadCursors();
}
[HarmonyTranspiler]
[HarmonyPatch(typeof(UICursor), nameof(UICursor.LoadCursors))]
private static IEnumerable<CodeInstruction> UICursor_LoadCursors_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{
var matcher = new CodeMatcher(instructions, generator);
/*
matcher.MatchForward(false,
new CodeMatch(OpCodes.Ldc_I4_S),
new CodeMatch(OpCodes.Newarr)
);
var startPos = matcher.Pos;
matcher.Advance(2).MatchForward(false,
new CodeMatch(OpCodes.Stsfld, AccessTools.Field(typeof(UICursor), nameof(UICursor.cursorTexs)))
);
var endPos = matcher.Pos + 1;
matcher.Start().Advance(startPos).RemoveInstructions(endPos - startPos);
matcher.InsertAndAdvance(
Transpilers.EmitDelegate(() =>
{
var pluginfolder = Util.PluginFolder;
UICursor.cursorTexs =
[
Util.LoadTexture($"{pluginfolder}/assets/cursor/cursor.png"),
Util.LoadTexture($"{pluginfolder}/assets/cursor/cursor-transfer.png"),
Util.LoadTexture($"{pluginfolder}/assets/cursor/cursor-target-in.png"),
Util.LoadTexture($"{pluginfolder}/assets/cursor/cursor-target-out.png"),
Util.LoadTexture($"{pluginfolder}/assets/cursor/cursor-target-a.png"),
Util.LoadTexture($"{pluginfolder}/assets/cursor/cursor-target-b.png"),
Util.LoadTexture($"{pluginfolder}/assets/cursor/cursor-ban.png"),
Util.LoadTexture($"{pluginfolder}/assets/cursor/cursor-delete.png"),
Util.LoadTexture($"{pluginfolder}/assets/cursor/cursor-reform.png"),
Util.LoadTexture($"{pluginfolder}/assets/cursor/cursor-dyson-node-create.png"),
Util.LoadTexture($"{pluginfolder}/assets/cursor/cursor-painter.png"),
Util.LoadTexture($"{pluginfolder}/assets/cursor/cursor-eyedropper.png"),
Util.LoadTexture($"{pluginfolder}/assets/cursor/cursor-eraser.png"),
Util.LoadTexture($"{pluginfolder}/assets/cursor/cursor-upgrade.png"),
Util.LoadTexture($"{pluginfolder}/assets/cursor/cursor-downgrade.png"),
Util.LoadTexture($"{pluginfolder}/assets/cursor/cursor-blank.png"),
Util.LoadTexture($"{pluginfolder}/assets/cursor/cursor-remove.png")
];
})
);
*/
matcher.MatchForward(false,
new CodeMatch(OpCodes.Stsfld, AccessTools.Field(typeof(UICursor), nameof(UICursor.cursorHots))),
new CodeMatch(OpCodes.Ldc_I4_1),
new CodeMatch(OpCodes.Stsfld, AccessTools.Field(typeof(UICursor), nameof(UICursor.loaded)))
).Advance(1).InsertAndAdvance(
Transpilers.EmitDelegate(() =>
{
var multiplier = MouseCursorScaleUpMultiplier.Value;
for (var i = 0; i < UICursor.cursorTexs.Length; i++)
{
var cursor = UICursor.cursorTexs[i];
if (cursor == null) continue;
var newWidth = 32 * multiplier;
var newHeight = 32 * multiplier;
if (cursor.width == newWidth && cursor.height == newHeight) continue;
UICursor.cursorTexs[i] = ResizeTexture2D(cursor, newWidth, newHeight);
}
if (multiplier <= 1) return;
for (var i = UICursor.cursorHots.Length - 1; i >= 0; i--)
{
UICursor.cursorHots[i] = new Vector2(UICursor.cursorHots[i].x * multiplier, UICursor.cursorHots[i].y * multiplier);
}
})
).MatchForward(false,
new CodeMatch(OpCodes.Ldc_I4_0),
new CodeMatch(OpCodes.Call, AccessTools.Method(typeof(Cursor), nameof(Cursor.SetCursor), [typeof(Texture2D), typeof(Vector2), typeof(CursorMode)]))
).SetInstruction(new CodeInstruction(OpCodes.Ldc_I4_1));
return matcher.InstructionEnumeration();
Texture2D ResizeTexture2D(Texture2D texture2D, int targetWidth, int targetHeight)
{
var oldActive = RenderTexture.active;
var rt = new RenderTexture(targetWidth, targetHeight, 32)
{
antiAliasing = 8
};
RenderTexture.active = rt;
Graphics.Blit(texture2D, rt);
rt.ResolveAntiAliasedSurface();
var result = new Texture2D(targetWidth, targetHeight, texture2D.format, false);
result.ReadPixels(new Rect(0, 0, targetWidth, targetHeight), 0, 0);
result.filterMode = FilterMode.Trilinear;
result.Apply();
RenderTexture.active = oldActive;
rt.Release();
return result;
}
}
[HarmonyTranspiler]
[HarmonyPatch(typeof(UICursor), nameof(UICursor.cursorIndexApply), MethodType.Setter)]
private static IEnumerable<CodeInstruction> UICursor_set_cursorIndexApply_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{
var matcher = new CodeMatcher(instructions, generator);
matcher.Start().MatchForward(false,
new CodeMatch(OpCodes.Ldc_I4_0),
new CodeMatch(OpCodes.Call, AccessTools.Method(typeof(Cursor), nameof(Cursor.SetCursor), [typeof(Texture2D), typeof(Vector2), typeof(CursorMode)]))
).SetInstruction(new CodeInstruction(OpCodes.Ldc_I4_1));
return matcher.InstructionEnumeration();
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,202 +0,0 @@
using System;
using System.Collections.Generic;
using System.Reflection.Emit;
using HarmonyLib;
using UnityEngine;
using UXAssist.Common;
namespace UXAssist.Patches;
public class PersistPatch : PatchImpl<PersistPatch>
{
public static void Start()
{
Enable(true);
}
public static void Uninit()
{
Enable(false);
}
// Check for noModifier while pressing hotkeys on build bar
[HarmonyTranspiler]
[HarmonyPatch(typeof(UIBuildMenu), nameof(UIBuildMenu._OnUpdate))]
private static IEnumerable<CodeInstruction> UIBuildMenu__OnUpdate_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{
var matcher = new CodeMatcher(instructions, generator);
matcher.MatchForward(false,
new CodeMatch(OpCodes.Ldsfld, AccessTools.Field(typeof(VFInput), nameof(VFInput.inScreen)))
);
matcher.Repeat(codeMatcher =>
{
var jumpPos = codeMatcher.Advance(1).Operand;
codeMatcher.Advance(-1).InsertAndAdvance(
new CodeInstruction(OpCodes.Ldsfld, AccessTools.Field(typeof(VFInput), nameof(VFInput.noModifier))),
new CodeInstruction(OpCodes.Brfalse_S, jumpPos)
).Advance(2);
});
return matcher.InstructionEnumeration();
}
// Patch to fix the issue that warning popup on VeinUtil upgraded to level 8000+
[HarmonyTranspiler]
[HarmonyPatch(typeof(ABN_VeinsUtil), nameof(ABN_VeinsUtil.CheckValue))]
private static IEnumerable<CodeInstruction> ABN_VeinsUtil_CheckValue_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{
var matcher = new CodeMatcher(instructions, generator);
matcher.MatchForward(false,
new CodeMatch(OpCodes.Ldelem_R8),
new CodeMatch(OpCodes.Conv_R4),
new CodeMatch(OpCodes.Add),
new CodeMatch(OpCodes.Stloc_1)
);
// loc1 = Mathf.Round(n * 1000f) / 1000f;
matcher.Advance(3).Insert(
new CodeInstruction(OpCodes.Ldc_R4, 1000f),
new CodeInstruction(OpCodes.Mul),
new CodeInstruction(OpCodes.Call, AccessTools.Method(typeof(Mathf), nameof(Mathf.Round))),
new CodeInstruction(OpCodes.Ldc_R4, 1000f),
new CodeInstruction(OpCodes.Div)
);
return matcher.InstructionEnumeration();
}
// Bring popup tip window to top layer
[HarmonyTranspiler]
[HarmonyPatch(typeof(UIButton), nameof(UIButton.LateUpdate))]
private static IEnumerable<CodeInstruction> UIButton_LateUpdate_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{
var matcher = new CodeMatcher(instructions, generator);
matcher.MatchForward(false,
new CodeMatch(ci => ci.opcode == OpCodes.Ldloc || ci.opcode == OpCodes.Ldloc_S),
new CodeMatch(OpCodes.Callvirt, AccessTools.PropertyGetter(typeof(Component), nameof(Component.gameObject))),
new CodeMatch(OpCodes.Callvirt, AccessTools.PropertyGetter(typeof(GameObject), nameof(GameObject.activeSelf)))
);
var ldLocOpr = matcher.Operand;
var labels = matcher.Labels;
matcher.Labels = null;
matcher.Insert(
new CodeInstruction(OpCodes.Ldloc_S, ldLocOpr).WithLabels(labels),
new CodeInstruction(OpCodes.Callvirt, AccessTools.PropertyGetter(typeof(Component), nameof(Component.transform))),
new CodeInstruction(OpCodes.Callvirt, AccessTools.PropertyGetter(typeof(Transform), nameof(Transform.parent))),
new CodeInstruction(OpCodes.Callvirt, AccessTools.PropertyGetter(typeof(Transform), nameof(Transform.parent))),
new CodeInstruction(OpCodes.Callvirt, AccessTools.Method(typeof(Transform), nameof(Transform.SetAsLastSibling)))
);
return matcher.InstructionEnumeration();
}
// Sort blueprint structures by item id, model index, recipe id, area index, and position before saving
[HarmonyPostfix]
[HarmonyPatch(typeof(BlueprintUtils), nameof(BlueprintUtils.GenerateBlueprintData))]
private static void BlueprintUtils_GenerateBlueprintData_Postfix(BlueprintData _blueprintData)
{
var buildings = _blueprintData.buildings;
Array.Sort(buildings, (a, b) =>
{
var tmpItemId = a.itemId - b.itemId;
if (tmpItemId != 0) return tmpItemId;
var tmpModelIndex = a.modelIndex - b.modelIndex;
if (tmpModelIndex != 0) return tmpModelIndex;
var tmpRecipeId = a.recipeId - b.recipeId;
if (tmpRecipeId != 0) return tmpRecipeId;
var tmpAreaIndex = a.areaIndex - b.areaIndex;
if (tmpAreaIndex != 0) return tmpAreaIndex;
var sign = Math.Sign(b.localOffset_y - a.localOffset_y);
if (sign != 0) return sign;
sign = Math.Sign(b.localOffset_x - a.localOffset_x);
return sign != 0 ? sign : Math.Sign(b.localOffset_z - a.localOffset_z);
});
for (var i = buildings.Length - 1; i >= 0; i--)
{
buildings[i].index = i;
}
}
// Increase maximum value of property realizing, 2000 -> 20000
[HarmonyTranspiler]
[HarmonyPatch(typeof(UIPropertyEntry), nameof(UIPropertyEntry.UpdateUIElements))]
[HarmonyPatch(typeof(UIPropertyEntry), nameof(UIPropertyEntry.OnRealizeButtonClick))]
[HarmonyPatch(typeof(UIPropertyEntry), nameof(UIPropertyEntry.OnInputValueEnd))]
private static IEnumerable<CodeInstruction> UIProductEntry_UpdateUIElements_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{
var matcher = new CodeMatcher(instructions, generator);
matcher.MatchForward(false,
new CodeMatch(OpCodes.Ldc_I4, 2000)
);
matcher.Repeat(m => { m.SetAndAdvance(OpCodes.Ldc_I4, 20000); });
return matcher.InstructionEnumeration();
}
[HarmonyTranspiler]
[HarmonyPatch(typeof(UIPropertyEntry), nameof(UIPropertyEntry.OnInputValueEnd))]
private static IEnumerable<CodeInstruction> UIProductEntry_OnInputValueEnd_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{
var matcher = new CodeMatcher(instructions, generator);
matcher.MatchForward(false,
new CodeMatch(ci => ci.opcode == OpCodes.Ldc_R4 && ci.OperandIs(2000f))
);
matcher.Repeat(m => { m.SetAndAdvance(OpCodes.Ldc_R4, 20000f); });
return matcher.InstructionEnumeration();
}
// Increase capacity of player order queue, 16 -> 128
[HarmonyTranspiler]
[HarmonyPatch(typeof(PlayerOrder), MethodType.Constructor, typeof(Player))]
private static IEnumerable<CodeInstruction> PlayerOrder_Constructor_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{
var matcher = new CodeMatcher(instructions, generator);
matcher.MatchForward(false,
new CodeMatch(ci => (ci.opcode == OpCodes.Ldc_I4_S || ci.opcode == OpCodes.Ldc_I4) && ci.OperandIs(16))
);
matcher.Repeat(m => { m.SetAndAdvance(OpCodes.Ldc_I4, 128); });
return matcher.InstructionEnumeration();
}
// Increase Player Command Queue from 16 to 128
[HarmonyTranspiler]
[HarmonyPatch(typeof(PlayerOrder), nameof(PlayerOrder._trimEnd))]
[HarmonyPatch(typeof(PlayerOrder), nameof(PlayerOrder.Enqueue))]
private static IEnumerable<CodeInstruction> PlayerOrder_ExtendCount_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{
var matcher = new CodeMatcher(instructions, generator);
matcher.MatchForward(false,
new CodeMatch(ci => (ci.opcode == OpCodes.Ldc_I4_S || ci.opcode == OpCodes.Ldc_I4) && ci.OperandIs(16))
);
matcher.Repeat(m => { m.SetAndAdvance(OpCodes.Ldc_I4, 128); });
return matcher.InstructionEnumeration();
}
// Allow F11 in star map
[HarmonyTranspiler]
[HarmonyPatch(typeof(UIGame), nameof(UIGame._OnLateUpdate))]
private static IEnumerable<CodeInstruction> UIGame__OnLateUpdate_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{
var matcher = new CodeMatcher(instructions, generator);
matcher.MatchForward(false,
new CodeMatch(OpCodes.Ldsfld, AccessTools.Field(typeof(VFInput), nameof(VFInput.inFullscreenGUI))),
new CodeMatch(ci => ci.opcode == OpCodes.Brfalse || ci.opcode == OpCodes.Brfalse_S)
);
var jumpPos = matcher.Advance(1).Operand;
matcher.Advance(-1).Insert(
new CodeInstruction(OpCodes.Ldarg_0),
new CodeInstruction(OpCodes.Ldfld, AccessTools.Field(typeof(UIGame), nameof(UIGame.starmap))),
new CodeInstruction(OpCodes.Callvirt, AccessTools.PropertyGetter(typeof(ManualBehaviour), nameof(ManualBehaviour.active))),
new CodeInstruction(OpCodes.Brtrue_S, jumpPos)
);
return matcher.InstructionEnumeration();
}
// Ignore UIDFCommunicatorWindow.Determine()
[HarmonyPrefix]
[HarmonyPatch(typeof(UIDFCommunicatorWindow), nameof(UIDFCommunicatorWindow.Determine))]
private static bool UIDFCommunicatorWindow_Determine_Prefix()
{
return false;
}
}

View File

@@ -1,532 +0,0 @@
using System.Collections.Generic;
using System.Reflection.Emit;
using BepInEx.Configuration;
using CommonAPI.Systems;
using HarmonyLib;
using UnityEngine;
using UXAssist.Common;
namespace UXAssist.Patches;
public static class PlayerPatch
{
public static ConfigEntry<bool> EnhancedMechaForgeCountControlEnabled;
public static ConfigEntry<bool> HideTipsForSandsChangesEnabled;
public static ConfigEntry<bool> ShortcutKeysForStarsNameEnabled;
public static ConfigEntry<bool> AutoNavigationEnabled;
public static ConfigEntry<bool> AutoCruiseEnabled;
public static ConfigEntry<bool> AutoBoostEnabled;
public static ConfigEntry<double> DistanceToWarp;
private static PressKeyBind _showAllStarsNameKey;
private static PressKeyBind _toggleAllStarsNameKey;
private static PressKeyBind _autoDriveKey;
public static void Init()
{
_showAllStarsNameKey = KeyBindings.RegisterKeyBinding(new BuiltinKey
{
key = new CombineKey(0, CombineKey.ALT_COMB, ECombineKeyAction.OnceClick, false),
conflictGroup = KeyBindConflict.UI | KeyBindConflict.KEYBOARD_KEYBIND,
name = "ShowAllStarsName",
canOverride = true
}
);
I18N.Add("KEYShowAllStarsName", "[UXA] Keep pressing to show all Stars' name", "[UXA] 按住显示所有星系名称");
_toggleAllStarsNameKey = KeyBindings.RegisterKeyBinding(new BuiltinKey
{
key = new CombineKey((int)KeyCode.Tab, 0, ECombineKeyAction.OnceClick, false),
conflictGroup = KeyBindConflict.UI | KeyBindConflict.KEYBOARD_KEYBIND,
name = "ToggleAllStarsName",
canOverride = true
}
);
I18N.Add("KEYToggleAllStarsName", "[UXA] Toggle display of all Stars' name", "[UXA] 切换所有星系名称显示状态");
_autoDriveKey = KeyBindings.RegisterKeyBinding(new BuiltinKey
{
key = new CombineKey(0, 0, ECombineKeyAction.OnceClick, true),
conflictGroup = KeyBindConflict.MOVEMENT | KeyBindConflict.FLYING | KeyBindConflict.SAILING | KeyBindConflict.BUILD_MODE_1 | KeyBindConflict.KEYBOARD_KEYBIND,
name = "ToggleAutoCruise",
canOverride = true
});
I18N.Add("KEYToggleAutoCruise", "[UXA] Toggle auto-cruise", "[UXA] 切换自动巡航");
I18N.Add("AutoCruiseOn", "Auto-cruise enabled", "已启用自动巡航");
I18N.Add("AutoCruiseOff", "Auto-cruise disabled", "已禁用自动巡航");
EnhancedMechaForgeCountControlEnabled.SettingChanged += (_, _) => EnhancedMechaForgeCountControl.Enable(EnhancedMechaForgeCountControlEnabled.Value);
HideTipsForSandsChangesEnabled.SettingChanged += (_, _) => HideTipsForSandsChanges.Enable(HideTipsForSandsChangesEnabled.Value);
ShortcutKeysForStarsNameEnabled.SettingChanged += (_, _) => ShortcutKeysForStarsName.Enable(ShortcutKeysForStarsNameEnabled.Value);
AutoNavigationEnabled.SettingChanged += (_, _) => AutoNavigation.Enable(AutoNavigationEnabled.Value);
}
public static void Start()
{
EnhancedMechaForgeCountControl.Enable(EnhancedMechaForgeCountControlEnabled.Value);
HideTipsForSandsChanges.Enable(HideTipsForSandsChangesEnabled.Value);
ShortcutKeysForStarsName.Enable(ShortcutKeysForStarsNameEnabled.Value);
AutoNavigation.Enable(AutoNavigationEnabled.Value);
}
public static void OnUpdate()
{
if (_toggleAllStarsNameKey.keyValue)
{
ShortcutKeysForStarsName.ToggleAllStarsName();
}
if (_autoDriveKey.keyValue)
{
AutoNavigation.ToggleAutoCruise();
}
}
public static void Uninit()
{
EnhancedMechaForgeCountControl.Enable(false);
HideTipsForSandsChanges.Enable(false);
ShortcutKeysForStarsName.Enable(false);
AutoNavigation.Enable(false);
}
private class EnhancedMechaForgeCountControl: PatchImpl<EnhancedMechaForgeCountControl>
{
[HarmonyTranspiler]
[HarmonyPatch(typeof(UIReplicatorWindow), nameof(UIReplicatorWindow.OnOkButtonClick))]
private static IEnumerable<CodeInstruction> UIReplicatorWindow_OnOkButtonClick_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{
var matcher = new CodeMatcher(instructions, generator);
matcher.MatchForward(false,
new CodeMatch(ci => ci.opcode == OpCodes.Ldc_I4_S && ci.OperandIs(10))
);
matcher.Repeat(m => m.SetAndAdvance(OpCodes.Ldc_I4, 1000));
return matcher.InstructionEnumeration();
}
[HarmonyTranspiler]
[HarmonyPatch(typeof(UIReplicatorWindow), nameof(UIReplicatorWindow.OnPlusButtonClick))]
[HarmonyPatch(typeof(UIReplicatorWindow), nameof(UIReplicatorWindow.OnMinusButtonClick))]
private static IEnumerable<CodeInstruction> UIReplicatorWindow_OnPlusButtonClick_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{
var label1 = generator.DefineLabel();
var label2 = generator.DefineLabel();
var label3 = generator.DefineLabel();
var label4 = generator.DefineLabel();
var matcher = new CodeMatcher(instructions, generator);
matcher.MatchForward(false,
new CodeMatch(OpCodes.Ldloc_0),
new CodeMatch(OpCodes.Ldc_I4_1),
new CodeMatch(o => o.opcode == OpCodes.Add || o.opcode == OpCodes.Sub)
).Advance(1).RemoveInstruction().InsertAndAdvance(
new CodeInstruction(OpCodes.Ldsfld, AccessTools.Field(typeof(VFInput), nameof(VFInput.control))),
new CodeInstruction(OpCodes.Brfalse_S, label1),
new CodeInstruction(OpCodes.Ldc_I4_S, 10),
new CodeInstruction(OpCodes.Br_S, label4),
new CodeInstruction(OpCodes.Ldsfld, AccessTools.Field(typeof(VFInput), nameof(VFInput.shift))).WithLabels(label1),
new CodeInstruction(OpCodes.Brfalse_S, label2),
new CodeInstruction(OpCodes.Ldc_I4_S, 100),
new CodeInstruction(OpCodes.Br_S, label4),
new CodeInstruction(OpCodes.Ldsfld, AccessTools.Field(typeof(VFInput), nameof(VFInput.alt))).WithLabels(label2),
new CodeInstruction(OpCodes.Brfalse_S, label3),
new CodeInstruction(OpCodes.Ldc_I4, 1000),
new CodeInstruction(OpCodes.Br_S, label4),
new CodeInstruction(OpCodes.Ldc_I4_1).WithLabels(label3)
).Labels.Add(label4);
matcher.MatchForward(false,
new CodeMatch(ci => ci.opcode == OpCodes.Ldc_I4_S && ci.OperandIs(10))
);
matcher.Repeat(m => m.SetAndAdvance(OpCodes.Ldc_I4, 1000));
return matcher.InstructionEnumeration();
}
}
private class HideTipsForSandsChanges: PatchImpl<HideTipsForSandsChanges>
{
[HarmonyTranspiler]
[HarmonyPatch(typeof(Player), nameof(Player.SetSandCount))]
private static IEnumerable<CodeInstruction> Player_SetSandCount_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{
var matcher = new CodeMatcher(instructions, generator);
matcher.MatchForward(false,
new CodeMatch(OpCodes.Call, AccessTools.PropertySetter(typeof(Player), nameof(Player.sandCount)))
).Advance(1).Insert(new CodeInstruction(OpCodes.Ret));
return matcher.InstructionEnumeration();
}
}
public class ShortcutKeysForStarsName: PatchImpl<ShortcutKeysForStarsName>
{
private static int _showAllStarsNameStatus;
public static void ToggleAllStarsName()
{
_showAllStarsNameStatus = (_showAllStarsNameStatus + 1) % 3;
}
[HarmonyPostfix]
[HarmonyPatch(typeof(UIStarmap), nameof(UIStarmap._OnClose))]
private static void UIStarmap__OnClose_Postfix()
{
_showAllStarsNameStatus = 0;
}
[HarmonyTranspiler]
[HarmonyPatch(typeof(UIStarmapStar), nameof(UIStarmapStar._OnLateUpdate))]
private static IEnumerable<CodeInstruction> UIStarmapStar__OnLateUpdate_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{
var matcher = new CodeMatcher(instructions, generator);
Label? jumpPos = null;
matcher.MatchForward(false,
new CodeMatch(OpCodes.Stfld, AccessTools.Field(typeof(UIStarmapStar), nameof(UIStarmapStar.projectedCoord))),
new CodeMatch(ci => ci.IsLdloc()),
new CodeMatch(ci => ci.Branches(out jumpPos))
);
matcher.Advance(3);
var labels = matcher.Labels;
matcher.Labels = [];
matcher.CreateLabel(out var jumpPos2);
matcher.InsertAndAdvance(
new CodeInstruction(OpCodes.Ldsfld, AccessTools.Field(typeof(ShortcutKeysForStarsName), nameof(_showAllStarsNameStatus))).WithLabels(labels),
new CodeInstruction(OpCodes.Ldc_I4_1),
new CodeInstruction(OpCodes.Ceq),
new CodeInstruction(OpCodes.Brtrue, jumpPos.Value),
new CodeInstruction(OpCodes.Ldsfld, AccessTools.Field(typeof(PlayerPatch), nameof(_showAllStarsNameKey))),
new CodeInstruction(OpCodes.Call, AccessTools.Method(typeof(KeyBindings), nameof(KeyBindings.IsKeyPressing))),
new CodeInstruction(OpCodes.Brtrue, jumpPos.Value),
new CodeInstruction(OpCodes.Ldsfld, AccessTools.Field(typeof(ShortcutKeysForStarsName), nameof(_showAllStarsNameStatus))),
new CodeInstruction(OpCodes.Ldc_I4_2),
new CodeInstruction(OpCodes.Ceq),
new CodeInstruction(OpCodes.Brfalse, jumpPos2),
new CodeInstruction(OpCodes.Ldc_I4_0),
new CodeInstruction(OpCodes.Stloc_1),
new CodeInstruction(OpCodes.Br, jumpPos.Value)
);
return matcher.InstructionEnumeration();
}
/*
[HarmonyTranspiler]
[HarmonyPatch(typeof(UIStarmapPlanet), nameof(UIStarmapPlanet._OnLateUpdate))]
private static IEnumerable<CodeInstruction> UIStarmapPlanet__OnLateUpdate_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{
var matcher = new CodeMatcher(instructions, generator);
matcher.MatchForward(false,
new CodeMatch(OpCodes.Ldarg_0),
new CodeMatch(OpCodes.Ldloc_3),
new CodeMatch(OpCodes.Stfld, AccessTools.Field(typeof(UIStarmapPlanet), nameof(UIStarmapPlanet.projected)))
);
matcher.Advance(3);
matcher.CreateLabel(out var jumpPos1);
matcher.InsertAndAdvance(
new CodeInstruction(OpCodes.Ldsfld, AccessTools.Field(typeof(ShortcutKeysForStarsName), nameof(_showAllStarsNameStatus))),
new CodeInstruction(OpCodes.Ldc_I4_2),
new CodeInstruction(OpCodes.Ceq),
new CodeInstruction(OpCodes.Brfalse, jumpPos1),
new CodeInstruction(OpCodes.Ldc_I4_0),
new CodeInstruction(OpCodes.Stloc_3)
);
matcher.MatchForward(false,
new CodeMatch(OpCodes.Ldarg_0),
new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(UIStarmapPlanet), nameof(UIStarmapPlanet.gameHistory))),
new CodeMatch(OpCodes.Ldarg_0),
new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(UIStarmapPlanet), nameof(UIStarmapPlanet.planet))),
new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(PlanetData), nameof(PlanetData.id))),
new CodeMatch(OpCodes.Callvirt, AccessTools.Field(typeof(GameHistoryData), nameof(GameHistoryData.GetPlanetPin))),
new CodeMatch(OpCodes.Ldc_I4_1),
new CodeMatch(ci => ci.Branches(out _)),
new CodeMatch(OpCodes.Ldc_I4_1),
new CodeMatch(ci => ci.IsStloc()),
new CodeMatch(ci => ci.Branches(out _))
);
matcher.CreateLabelAt(matcher.Pos + 8, out var jumpPos);
var labels = matcher.Labels;
matcher.Labels = null;
matcher.InsertAndAdvance(
new CodeInstruction(OpCodes.Ldsfld, AccessTools.Field(typeof(ShortcutKeysForStarsName), nameof(_showAllStarsNameStatus))).WithLabels(labels),
new CodeInstruction(OpCodes.Ldc_I4_1),
new CodeInstruction(OpCodes.Ceq),
new CodeInstruction(OpCodes.Brtrue, jumpPos),
new CodeInstruction(OpCodes.Ldsfld, AccessTools.Field(typeof(PlayerPatch), nameof(_showAllStarsNameKey))),
new CodeInstruction(OpCodes.Call, AccessTools.Method(typeof(KeyBindings), nameof(KeyBindings.IsKeyPressing))),
new CodeInstruction(OpCodes.Brtrue, jumpPos)
);
return matcher.InstructionEnumeration();
}
[HarmonyTranspiler]
[HarmonyPatch(typeof(UIStarmapDFHive), nameof(UIStarmapDFHive._OnLateUpdate))]
private static IEnumerable<CodeInstruction> UIStarmapDFHive__OnLateUpdate_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{
var matcher = new CodeMatcher(instructions, generator);
matcher.MatchForward(false,
new CodeMatch(OpCodes.Ldarg_0),
new CodeMatch(ci => ci.IsLdloc()),
new CodeMatch(OpCodes.Stfld, AccessTools.Field(typeof(UIStarmapDFHive), nameof(UIStarmapDFHive.projected)))
);
matcher.Advance(3);
matcher.CreateLabel(out var jumpPos1);
matcher.InsertAndAdvance(
new CodeInstruction(OpCodes.Ldsfld, AccessTools.Field(typeof(ShortcutKeysForStarsName), nameof(_showAllStarsNameStatus))),
new CodeInstruction(OpCodes.Ldc_I4_2),
new CodeInstruction(OpCodes.Ceq),
new CodeInstruction(OpCodes.Brfalse, jumpPos1),
new CodeInstruction(OpCodes.Ldc_I4_0),
new CodeInstruction(OpCodes.Stloc_S, 4)
);
matcher.MatchForward(false,
new CodeMatch(OpCodes.Ldarg_0),
new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(UIStarmapDFHive), nameof(UIStarmapDFHive.gameHistory))),
new CodeMatch(OpCodes.Ldarg_0),
new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(UIStarmapDFHive), nameof(UIStarmapDFHive.hive))),
new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(EnemyDFHiveSystem), nameof(EnemyDFHiveSystem.hiveStarId))),
new CodeMatch(OpCodes.Ldc_I4, 1000000),
new CodeMatch(OpCodes.Sub),
new CodeMatch(OpCodes.Callvirt, AccessTools.Field(typeof(GameHistoryData), nameof(GameHistoryData.GetHivePin))),
new CodeMatch(OpCodes.Ldc_I4_1),
new CodeMatch(ci => ci.Branches(out _)),
new CodeMatch(OpCodes.Ldc_I4_1),
new CodeMatch(ci => ci.IsStloc()),
new CodeMatch(ci => ci.Branches(out _))
);
matcher.CreateLabelAt(matcher.Pos + 10, out var jumpPos);
var labels = matcher.Labels;
matcher.Labels = null;
matcher.InsertAndAdvance(
new CodeInstruction(OpCodes.Ldsfld, AccessTools.Field(typeof(ShortcutKeysForStarsName), nameof(_showAllStarsNameStatus))).WithLabels(labels),
new CodeInstruction(OpCodes.Ldc_I4_1),
new CodeInstruction(OpCodes.Ceq),
new CodeInstruction(OpCodes.Brtrue, jumpPos),
new CodeInstruction(OpCodes.Ldsfld, AccessTools.Field(typeof(PlayerPatch), nameof(_showAllStarsNameKey))),
new CodeInstruction(OpCodes.Call, AccessTools.Method(typeof(KeyBindings), nameof(KeyBindings.IsKeyPressing))),
new CodeInstruction(OpCodes.Brtrue, jumpPos)
);
return matcher.InstructionEnumeration();
}
*/
}
public class AutoNavigation: PatchImpl<AutoNavigation>
{
private static bool _canUseWarper;
private static int _indicatorAstroId;
private static bool _speedUp;
private static Vector3 _direction;
public static void ToggleAutoCruise()
{
AutoCruiseEnabled.Value = !AutoCruiseEnabled.Value;
if (!DSPGame.IsMenuDemo && GameMain.isRunning)
{
UIRoot.instance.uiGame.generalTips.InvokeRealtimeTipAhead((AutoCruiseEnabled.Value ? "AutoCruiseOn" : "AutoCruiseOff").Translate());
}
}
[HarmonyTranspiler]
[HarmonyPatch(typeof(PlayerController), nameof(PlayerController.GameTick))]
private static IEnumerable<CodeInstruction> PlayerController_GameTick_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{
var matcher = new CodeMatcher(instructions, generator);
matcher.MatchForward(false,
new CodeMatch(OpCodes.Callvirt, AccessTools.Method(typeof(BuildModel), nameof(BuildModel.EarlyGameTickIgnoreActive)))
).Advance(1).InsertAndAdvance(
new CodeInstruction(OpCodes.Ldarg_0),
Transpilers.EmitDelegate((PlayerController controller) =>
{
/* Update target astro if changed */
_speedUp = false;
var player = controller.player;
var navi = player.navigation;
if (navi.indicatorAstroId != _indicatorAstroId)
{
_indicatorAstroId = navi.indicatorAstroId;
if (_indicatorAstroId == 0) return;
}
else if (_indicatorAstroId == 0) return;
switch (controller.movementStateInFrame)
{
case EMovementState.Walk:
case EMovementState.Drift:
if (!AutoCruiseEnabled.Value) return;
if (GameMain.localStar?.astroId == _indicatorAstroId) return;
/* Press jump key to fly */
controller.input0.z = 1f;
break;
case EMovementState.Fly:
if (!AutoCruiseEnabled.Value) return;
if (GameMain.localStar?.astroId == _indicatorAstroId) return;
/* Keep pressing jump and pullup key to sail */
controller.input0.y = 1f;
controller.input1.y = 1f;
break;
case EMovementState.Sail:
if (VFInput._pullUp.pressing || VFInput._pushDown.pressing || VFInput._moveLeft.pressing || VFInput._moveRight.pressing ||
(!player.warping && UIRoot.instance.uiGame.disableLockCursor && (VFInput._moveForward.pressing || VFInput._moveBackward.pressing)))
return;
var playerPos = player.uPosition;
var isHive = _indicatorAstroId > 1000000;
ref var astro = ref isHive ? ref GameMain.spaceSector.astros[_indicatorAstroId - 1000000] : ref GameMain.galaxy.astrosData[_indicatorAstroId];
var astroVec = astro.uPos - playerPos;
var distance = astroVec.magnitude;
if (distance < astro.type switch
{
EAstroType.Planet => 800.0 + astro.uRadius,
EAstroType.Star => 4000.0 + astro.uRadius,
EAstroType.EnemyHive => 400.0,
_ => 2000.0 + astro.uRadius
})
{
if (isHive)
{
player.uVelocity = Vector3.zero;
}
return;
}
var autoCruise = AutoCruiseEnabled.Value;
if (GameMain.instance.timei % 6 == 0 || _direction == Vector3.zero)
{
_direction = astroVec.normalized;
/* Check nearest astroes, try to bypass them */
var localStar = GameMain.localStar;
_canUseWarper = autoCruise && !player.warping && player.mecha.warpStorage.GetItemCount(1210) > 0;
if (localStar != null)
{
var nearestRange = (playerPos - localStar.uPosition).sqrMagnitude;
var nearestPos = localStar.uPosition;
var nearestAstroId = localStar.astroId;
foreach (var p in localStar.planets)
{
var range = (playerPos - p.uPosition).sqrMagnitude;
if (range >= nearestRange) continue;
nearestRange = range;
nearestPos = p.uPosition;
nearestAstroId = p.astroId;
}
/* If targeting hives, do not bypass them */
if (!isHive)
{
var hiveSys = GameMain.spaceSector.dfHives[localStar.index];
while (hiveSys != null)
{
if (hiveSys.realized && hiveSys.hiveAstroId > 1000000)
{
ref var hiveAstro = ref GameMain.spaceSector.astros[hiveSys.hiveAstroId - 1000000];
/* Divide by 4, so that the real range is 2 times of the calculated range,
which means the minimal range allowed is 4000 */
var range = (playerPos - hiveAstro.uPos).sqrMagnitude / 4.0;
if (range < nearestRange)
{
nearestRange = range;
nearestPos = hiveAstro.uPos;
nearestAstroId = hiveSys.hiveAstroId;
}
}
hiveSys = hiveSys.nextSibling;
}
}
if (nearestAstroId != _indicatorAstroId && nearestRange < 2000.0 * 2000.0)
{
Vector3 leavingDirection = (playerPos - nearestPos).normalized;
var dot = Vector3.Dot(leavingDirection, _direction);
if (dot < 0)
{
var cross = Vector3.Cross(_direction, leavingDirection);
_direction = Vector3.Cross(leavingDirection, cross).normalized;
}
else
{
_direction = leavingDirection;
}
}
}
}
Vector3 uVel = player.uVelocity;
var speed = uVel.magnitude;
if (player.warping)
{
_speedUp = false;
if (autoCruise)
{
/* Speed down if too close */
var actionSail = controller.actionSail;
if (distance < GalaxyData.LY * 1.5)
{
if (distance < actionSail.currentWarpSpeed * distance switch
{
> GalaxyData.LY * 0.6 => 0.33,
> GalaxyData.LY * 0.3 => 0.5,
> GalaxyData.LY * 0.1 => 0.66,
_ => 1.0
})
{
controller.input0.y = -1f;
}
}
}
}
else
{
var mecha = player.mecha;
var energyRatio = mecha.coreEnergy / mecha.coreEnergyCap;
if (_canUseWarper && GameMain.localPlanet == null && distance > GalaxyData.AU * DistanceToWarp.Value && energyRatio >= 0.8 && player.mecha.UseWarper())
{
player.warpCommand = true;
VFAudio.Create("warp-begin", player.transform, Vector3.zero, true);
}
else
{
/* Speed up if needed */
_speedUp = autoCruise && AutoBoostEnabled.Value && speed + 0.2f < player.mecha.maxSailSpeed && energyRatio >= 0.1;
}
}
/* Update direction, gracefully rotate for 2 degrees for each frame */
var angle = Vector3.Angle(uVel, _direction);
if (angle < 2f)
{
player.uVelocity = _direction * speed;
}
else
{
player.uVelocity = Vector3.Slerp(uVel, _direction * speed, 2f / angle);
}
break;
default:
_speedUp = false;
break;
}
})
);
return matcher.InstructionEnumeration();
}
[HarmonyTranspiler]
[HarmonyPatch(typeof(VFInput), nameof(VFInput._sailSpeedUp), MethodType.Getter)]
private static IEnumerable<CodeInstruction> VFInput_sailSpeedUp_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{
var matcher = new CodeMatcher(instructions, generator);
matcher.MatchForward(false,
new CodeMatch(OpCodes.Ret)
);
matcher.Repeat(m => m.InsertAndAdvance(
new CodeInstruction(OpCodes.Ldsfld, AccessTools.Field(typeof(AutoNavigation), nameof(_speedUp))),
new CodeInstruction(OpCodes.Or)
).Advance(1));
return matcher.InstructionEnumeration();
}
/* Disable Lock Cursor Mode on entering sail panel
[HarmonyPrefix]
[HarmonyPatch(typeof(UISailPanel), nameof(UISailPanel._OnOpen))]
public static void OnOpen_Prefix()
{
if (_aimingEnabled)
{
UIRoot.instance.uiGame.disableLockCursor = true;
}
}
*/
}
}

View File

@@ -1,404 +0,0 @@
using System;
using System.Collections.Generic;
using System.Reflection.Emit;
using BepInEx.Configuration;
using HarmonyLib;
using UnityEngine;
using UXAssist.Common;
namespace UXAssist.Patches;
public static class TechPatch
{
public static ConfigEntry<bool> SorterCargoStackingEnabled;
public static ConfigEntry<bool> BatchBuyoutTechEnabled;
public static void Init()
{
I18N.Add("分拣器运货量", "Sorter Mk.III cargo stacking : ", "极速分拣器每次可运送 ");
SorterCargoStackingEnabled.SettingChanged += (_, _) => SorterCargoStacking.Enable(SorterCargoStackingEnabled.Value);
BatchBuyoutTechEnabled.SettingChanged += (_, _) => BatchBuyoutTech.Enable(BatchBuyoutTechEnabled.Value);
}
public static void Start()
{
SorterCargoStacking.Enable(SorterCargoStackingEnabled.Value);
BatchBuyoutTech.Enable(BatchBuyoutTechEnabled.Value);
}
public static void Uninit()
{
BatchBuyoutTech.Enable(false);
SorterCargoStacking.Enable(false);
}
private class SorterCargoStacking: PatchImpl<SorterCargoStacking>
{
private static bool _protoPatched;
protected override void OnEnable()
{
TryPatchProto(true);
GameLogic.OnDataLoaded += VFPreload_InvokeOnLoadWorkEnded_Postfix;
}
protected override void OnDisable()
{
GameLogic.OnDataLoaded -= VFPreload_InvokeOnLoadWorkEnded_Postfix;
TryPatchProto(false);
}
private static void TryPatchProto(bool on)
{
var techs = LDB.techs;
if (techs == null || techs.dataArray == null || techs.dataArray.Length == 0) return;
if (on)
{
var delim = -26.0f;
var tp3301 = techs.Select(3301);
if (tp3301 != null && tp3301.IsObsolete)
{
_protoPatched = false;
delim = tp3301.Position.y + 1.0f;
}
if (_protoPatched) return;
foreach (var tp in techs.dataArray)
{
switch (tp.ID)
{
case >= 3301 and <= 3305:
tp.UnlockValues[0] = tp.ID - 3300 + 1;
tp.IsObsolete = false;
continue;
case 3306:
tp.PreTechs = [];
continue;
}
if (tp.Position.y > delim) continue;
tp.Position.y -= 4.0f;
}
_protoPatched = true;
}
else
{
var delim = -28.0f;
var tp3301 = techs.Select(3301);
if (tp3301 != null && !tp3301.IsObsolete)
{
_protoPatched = true;
delim = tp3301.Position.y - 1.0f;
}
if (!_protoPatched) return;
foreach (var tp in techs.dataArray)
{
if (tp.ID is >= 3301 and <= 3306)
{
tp.IsObsolete = true;
continue;
}
if (tp.Position.y > delim) continue;
tp.Position.y += 4.0f;
}
_protoPatched = false;
}
}
private static void VFPreload_InvokeOnLoadWorkEnded_Postfix()
{
TryPatchProto(true);
}
}
private class BatchBuyoutTech: PatchImpl<BatchBuyoutTech>
{
private static void GenerateTechList(GameHistoryData history, int techId, List<int> techIdList)
{
var techProto = LDB.techs.Select(techId);
if (techProto == null || !techProto.Published) return;
var flag = true;
for (var i = 0; i < 2; i++)
{
var array = techProto.PreTechs;
if (i == 1)
{
array = techProto.PreTechsImplicit;
}
for (var j = 0; j < array.Length; j++)
{
if (!history.techStates.ContainsKey(array[j]) || history.techStates[array[j]].unlocked) continue;
if (history.techStates[array[j]].maxLevel > history.techStates[array[j]].curLevel)
{
flag = false;
}
GenerateTechList(history, array[j], techIdList);
}
}
if (history.techStates.ContainsKey(techId) && !history.techStates[techId].unlocked && flag)
{
techIdList.Add(techId);
}
}
private static void CheckTechUnlockProperties(GameHistoryData history, TechProto techProto, int[] properties, List<Tuple<TechProto, int, int>> techList, int maxLevel = 10000)
{
var techStates = history.techStates;
var techID = techProto.ID;
if (techStates == null || !techStates.TryGetValue(techID, out var value)) return;
if (value.unlocked) return;
var maxLvl = Math.Min(maxLevel < 0 ? value.curLevel - maxLevel - 1 : maxLevel, value.maxLevel);
foreach (var preid in techProto.PreTechs)
{
var preProto = LDB.techs.Select(preid);
if (preProto != null)
CheckTechUnlockProperties(history, preProto, properties, techList, techProto.PreTechsMax ? 10000 : preProto.Level);
}
foreach (var preid in techProto.PreTechsImplicit)
{
var preProto = LDB.techs.Select(preid);
if (preProto != null)
CheckTechUnlockProperties(history, preProto, properties, techList, techProto.PreTechsMax ? 10000 : preProto.Level);
}
if (value.curLevel < techProto.Level) value.curLevel = techProto.Level;
techList.Add(new Tuple<TechProto, int, int>(techProto, value.curLevel, techProto.Level));
while (value.curLevel <= maxLvl)
{
if (techProto.PropertyOverrideItemArray != null)
{
var propertyOverrideItemArray = techProto.PropertyOverrideItemArray;
for (var i = 0; i < propertyOverrideItemArray.Length; i++)
{
var id = propertyOverrideItemArray[i].id;
var count = (float)propertyOverrideItemArray[i].count;
var ratio = Mathf.Clamp01((float)((double)value.hashUploaded / value.hashNeeded));
var consume = Mathf.CeilToInt(count * (1f - ratio));
properties[id - 6001] += consume;
}
}
else
{
for (var j = 0; j < techProto.itemArray.Length; j++)
{
var id = techProto.itemArray[j].ID;
var consume = (int)(techProto.ItemPoints[j] * (value.hashNeeded - value.hashUploaded) / 3600L);
properties[id - 6001] += consume;
}
}
value.curLevel++;
value.hashUploaded = 0;
value.hashNeeded = techProto.GetHashNeeded(value.curLevel);
}
}
private static int UnlockTechRecursiveImpl(GameHistoryData history, TechProto techProto, int maxLevel = 10000)
{
var techStates = history.techStates;
var techID = techProto.ID;
if (techStates == null || !techStates.TryGetValue(techID, out var value))
{
return -1;
}
if (value.unlocked)
{
return -1;
}
var maxLvl = Math.Min(maxLevel < 0 ? value.curLevel - maxLevel - 1 : maxLevel, value.maxLevel);
foreach (var preid in techProto.PreTechs)
{
var preProto = LDB.techs.Select(preid);
if (preProto != null)
UnlockTechRecursiveImpl(history, preProto, techProto.PreTechsMax ? 10000 : preProto.Level);
}
foreach (var preid in techProto.PreTechsImplicit)
{
var preProto = LDB.techs.Select(preid);
if (preProto != null)
UnlockTechRecursiveImpl(history, preProto, techProto.PreTechsMax ? 10000 : preProto.Level);
}
if (value.curLevel < techProto.Level) value.curLevel = techProto.Level;
while (value.curLevel <= maxLvl)
{
if (value.curLevel == 0)
{
foreach (var recipe in techProto.UnlockRecipes)
{
history.UnlockRecipe(recipe);
}
}
for (var j = 0; j < techProto.UnlockFunctions.Length; j++)
{
history.UnlockTechFunction(techProto.UnlockFunctions[j], techProto.UnlockValues[j], value.curLevel);
}
for (var k = 0; k < techProto.AddItems.Length; k++)
{
history.GainTechAwards(techProto.AddItems[k], techProto.AddItemCounts[k]);
}
value.curLevel++;
}
value.unlocked = maxLvl >= value.maxLevel;
value.curLevel = value.unlocked ? maxLvl : maxLvl + 1;
value.hashNeeded = techProto.GetHashNeeded(value.curLevel);
value.hashUploaded = value.unlocked ? value.hashNeeded : 0;
techStates[techID] = value;
return maxLvl;
}
private static bool UnlockTechRecursive(TechProto techProto, int maxLevel = 10000)
{
if (techProto == null) return false;
var history = GameMain.history;
var ulvl = UnlockTechRecursiveImpl(history, techProto, maxLevel);
if (ulvl < 0) return false;
history.RegFeatureKey(1000100);
history.NotifyTechUnlock(techProto.ID, ulvl);
return true;
}
[HarmonyTranspiler]
[HarmonyPatch(typeof(UITechNode), nameof(UITechNode.UpdateInfoDynamic))]
private static IEnumerable<CodeInstruction> UITechNode_UpdateInfoDynamic_Transpiler(IEnumerable<CodeInstruction> instructions)
{
var matcher = new CodeMatcher(instructions);
matcher.MatchForward(false,
new CodeMatch(OpCodes.Ldsfld, AccessTools.Field(typeof(UITechTree), nameof(UITechTree.showProperty))),
new CodeMatch(ci => ci.IsLdloc()),
new CodeMatch(OpCodes.And)
).Advance(1).SetAndAdvance(OpCodes.Ldloc_3, null).InsertAndAdvance(
new CodeInstruction(OpCodes.Ldc_I4_0),
new CodeInstruction(OpCodes.Ceq)
);
return matcher.InstructionEnumeration();
}
[HarmonyPrefix]
[HarmonyPatch(typeof(UITechNode), nameof(UITechNode.OnBuyoutButtonClick))]
private static bool UITechNode_OnBuyoutButtonClick_Prefix(UITechNode __instance)
{
if (GameMain.isFullscreenPaused)
{
return false;
}
var techProto = __instance.techProto;
if (techProto == null) return false;
var properties = new int[6];
List<Tuple<TechProto, int, int>> techList = new();
var history = GameMain.history;
var maxLevel = -1;
CheckTechUnlockProperties(history, techProto, properties, techList, maxLevel);
var propertySystem = DSPGame.propertySystem;
var clusterSeedKey = history.gameData.GetClusterSeedKey();
var enough = true;
for (var i = 0; i < 6; i++)
{
if (propertySystem.GetItemAvaliableProperty(clusterSeedKey, 6001 + i) >= properties[i]) continue;
enough = false;
break;
}
if (!enough)
{
UIRealtimeTip.Popup("元数据不足".Translate());
return false;
}
if (!history.hasUsedPropertyBanAchievement)
{
UIMessageBox.Show("初次使用元数据标题".Translate(), "初次使用元数据描述".Translate(), "取消".Translate(), "确定".Translate(), 2, null, DoUnlockFunc);
return false;
}
DoUnlockFunc();
return false;
void DoUnlockFunc()
{
if (techList.Count <= 1)
{
DoUnlockFuncInternal();
return;
}
var msg = "要使用元数据买断以下科技吗?";
if (techList.Count <= 10)
{
foreach (var tuple in techList)
{
AddToMsg(ref msg, tuple);
}
}
else
{
for (var i = 0; i < 5; i++)
{
AddToMsg(ref msg, techList[i]);
}
msg += " ...\n";
for (var i = techList.Count - 5; i < techList.Count; i++)
{
AddToMsg(ref msg, techList[i]);
}
}
msg += "\n\n";
msg += "以下是买断所需元数据:";
for (var i = 0; i < 6; i++)
{
var itemCount = properties[i];
if (itemCount <= 0) continue;
msg += $"\n {LDB.items.Select(6001 + i).propertyName}x{itemCount}";
}
UIMessageBox.Show("批量买断科技", msg, "取消".Translate(), "确定".Translate(), 2, null, DoUnlockFuncInternal);
return;
void AddToMsg(ref string str, Tuple<TechProto, int, int> tuple)
{
if (tuple.Item2 == tuple.Item3)
{
if (tuple.Item2 <= 0)
str += $"\n {tuple.Item1.name}";
else
str += $"\n {tuple.Item1.name}{"".Translate()}{tuple.Item2}";
}
else
str += $"\n {tuple.Item1.name}{"".Translate()}{tuple.Item2}->{tuple.Item3}";
}
}
void DoUnlockFuncInternal()
{
var mainPlayer = GameMain.mainPlayer;
for (var i = 0; i < 6; i++)
{
var itemCount = properties[i];
if (itemCount <= 0) continue;
var itemId = 6001 + i;
propertySystem.AddItemConsumption(clusterSeedKey, itemId, itemCount);
history.AddPropertyItemConsumption(itemId, itemCount, true);
mainPlayer.mecha.AddProductionStat(itemId, itemCount, mainPlayer.nearestFactory);
mainPlayer.mecha.AddConsumptionStat(itemId, itemCount, mainPlayer.nearestFactory);
}
UnlockTechRecursive(__instance.techProto, maxLevel);
history.VarifyTechQueue();
if (history.currentTech != history.techQueue[0])
{
history.currentTech = history.techQueue[0];
}
}
}
}
}

View File

@@ -1,10 +1,9 @@
using System; using System;
using System.IO;
using System.Threading; using System.Threading;
using BepInEx.Configuration; using BepInEx.Configuration;
using UnityEngine; using UnityEngine;
namespace UXAssist.Functions; namespace UXAssist;
public static class PlanetFunctions public static class PlanetFunctions
{ {
@@ -52,26 +51,11 @@ public static class PlanetFunctions
var planet = GameMain.localPlanet; var planet = GameMain.localPlanet;
var factory = planet?.factory; var factory = planet?.factory;
if (factory == null) return; if (factory == null) return;
var uiGame = UIRoot.instance.uiGame;
if (uiGame)
{
uiGame.ShutAllFunctionWindow();
uiGame.ShutAllFullScreens();
}
player.controller.actionBuild.Close();
var groundCombatModule = player.mecha.groundCombatModule;
for (var i = 0; i < groundCombatModule.moduleFleets.Length; i++)
{
var entry = groundCombatModule.moduleFleets[i];
if (entry.fleetId <= 0 || !entry.fleetEnabled) continue;
entry.fleetEnabled = false;
groundCombatModule.RemoveFleetDirectly(i);
}
//planet.data = new PlanetRawData(planet.precision); //planet.data = new PlanetRawData(planet.precision);
//planet.data.CalcVerts(); //planet.data.CalcVerts();
for (var id = factory.entityCursor - 1; id > 0; id--) for (var id = factory.entityCursor - 1; id > 0; id--)
{ {
ref var ed = ref factory.entityPool[id]; var ed = factory.entityPool[id];
if (ed.id != id) continue; if (ed.id != id) continue;
if (ed.colliderId != 0) if (ed.colliderId != 0)
{ {
@@ -93,27 +77,12 @@ public static class PlanetFunctions
planet.audio?.RemoveAudioData(ed.audioId); planet.audio?.RemoveAudioData(ed.audioId);
} }
if (planet.factory.enemyPool != null)
{
for (var i = planet.factory.enemyCursor - 1; i > 0; i--)
{
ref var enemyData = ref planet.factory.enemyPool[i];
if (enemyData.id != i) continue;
var combatStatId = enemyData.combatStatId;
planet.factory.skillSystem.OnRemovingSkillTarget(combatStatId, planet.factory.skillSystem.combatStats.buffer[combatStatId].originAstroId, ETargetType.CombatStat);
planet.factory.skillSystem.combatStats.Remove(combatStatId);
planet.factory.KillEnemyFinally(player, i, ref CombatStat.empty);
}
planet.factory.enemySystem.Free();
UIRoot.instance.uiGame.dfAssaultTip.ClearAllSpots();
}
var stationPool = factory.transport?.stationPool; var stationPool = factory.transport?.stationPool;
if (stationPool != null) if (stationPool != null)
{ {
foreach (var sc in stationPool) foreach (var sc in stationPool)
{ {
if (sc is not { id: > 0 }) continue; if (sc == null || sc.id <= 0) continue;
sc.storage = new StationStore[sc.storage.Length]; sc.storage = new StationStore[sc.storage.Length];
sc.needs = new int[sc.needs.Length]; sc.needs = new int[sc.needs.Length];
int protoId = factory.entityPool[sc.entityId].protoId; int protoId = factory.entityPool[sc.entityId].protoId;
@@ -151,54 +120,22 @@ public static class PlanetFunctions
if (warningPool[i].id == i && warningPool[i].factoryId == index) if (warningPool[i].id == i && warningPool[i].factoryId == index)
warningSystem.RemoveWarningData(warningPool[i].id); warningSystem.RemoveWarningData(warningPool[i].id);
} }
var isCombatMode = factory.gameData.gameDesc.isCombatMode;
factory.entityCursor = 1; factory.entityCursor = 1;
factory.entityRecycleCursor = 0; factory.entityRecycleCursor = 0;
factory.entityCapacity = 0; factory.entityCapacity = 0;
factory.SetEntityCapacity(1024); factory.SetEntityCapacity(1024);
factory.craftCursor = 1;
factory.craftRecycleCursor = 0;
factory.craftCapacity = 0;
factory.SetCraftCapacity(128);
factory.prebuildCursor = 1; factory.prebuildCursor = 1;
factory.prebuildRecycleCursor = 0; factory.prebuildRecycleCursor = 0;
factory.prebuildCapacity = 0; factory.prebuildCapacity = 0;
factory.SetPrebuildCapacity(256); factory.SetPrebuildCapacity(256);
factory.enemyCursor = 1;
factory.enemyRecycleCursor = 0;
factory.enemyCapacity = 0;
factory.SetEnemyCapacity(isCombatMode ? 1024 : 32);
factory.hashSystemDynamic = new HashSystem();
factory.hashSystemStatic = new HashSystem();
factory.cargoContainer = new CargoContainer(); factory.cargoContainer = new CargoContainer();
factory.cargoTraffic = new CargoTraffic(planet); factory.cargoTraffic = new CargoTraffic(planet);
factory.blockContainer = new MiniBlockContainer(); factory.blockContainer = new MiniBlockContainer();
factory.factoryStorage = new FactoryStorage(planet); factory.factoryStorage = new FactoryStorage(planet);
factory.powerSystem = new PowerSystem(planet); factory.powerSystem = new PowerSystem(planet);
factory.constructionSystem = new ConstructionSystem(planet);
factory.InitVeinHashAddress();
factory.RecalculateAllVeinGroups();
factory.InitVegeHashAddress();
factory.ruinCursor = 1;
factory.ruinRecycleCursor = 0;
factory.ruinCapacity = 0;
factory.SetRuinCapacity(isCombatMode ? 1024 : 32);
factory.factorySystem = new FactorySystem(planet); factory.factorySystem = new FactorySystem(planet);
factory.enemySystem = new EnemyDFGroundSystem(planet);
factory.combatGroundSystem = new CombatGroundSystem(planet);
factory.defenseSystem = new DefenseSystem(planet);
factory.planetATField = new PlanetATField(planet);
factory.transport = new PlanetTransport(GameMain.data, planet); factory.transport = new PlanetTransport(GameMain.data, planet);
factory.transport.Init(); factory.transport.Init();
var mem = new MemoryStream();
var writer = new BinaryWriter(mem);
factory.platformSystem.Export(writer);
factory.platformSystem = new PlatformSystem(planet, true);
mem.Position = 0;
var reader = new BinaryReader(mem);
factory.platformSystem.Import(reader);
mem.Close();
mem.Dispose();
factory.digitalSystem = new DigitalSystem(planet); factory.digitalSystem = new DigitalSystem(planet);
//GameMain.data.statistics.production.CreateFactoryStat(index); //GameMain.data.statistics.production.CreateFactoryStat(index);

View File

@@ -2,9 +2,8 @@
using System.Reflection.Emit; using System.Reflection.Emit;
using BepInEx.Configuration; using BepInEx.Configuration;
using HarmonyLib; using HarmonyLib;
using UXAssist.Common;
namespace UXAssist.Patches; namespace UXAssist;
public static class PlanetPatch public static class PlanetPatch
{ {
public static ConfigEntry<bool> PlayerActionsInGlobeViewEnabled; public static ConfigEntry<bool> PlayerActionsInGlobeViewEnabled;
@@ -12,10 +11,6 @@ public static class PlanetPatch
public static void Init() public static void Init()
{ {
PlayerActionsInGlobeViewEnabled.SettingChanged += (_, _) => PlayerActionsInGlobeView.Enable(PlayerActionsInGlobeViewEnabled.Value); PlayerActionsInGlobeViewEnabled.SettingChanged += (_, _) => PlayerActionsInGlobeView.Enable(PlayerActionsInGlobeViewEnabled.Value);
}
public static void Start()
{
PlayerActionsInGlobeView.Enable(PlayerActionsInGlobeViewEnabled.Value); PlayerActionsInGlobeView.Enable(PlayerActionsInGlobeViewEnabled.Value);
} }
@@ -24,8 +19,21 @@ public static class PlanetPatch
PlayerActionsInGlobeView.Enable(false); PlayerActionsInGlobeView.Enable(false);
} }
public class PlayerActionsInGlobeView: PatchImpl<PlayerActionsInGlobeView> public static class PlayerActionsInGlobeView
{ {
private static Harmony _patch;
public static void Enable(bool on)
{
if (on)
{
_patch ??= Harmony.CreateAndPatchAll(typeof(PlayerActionsInGlobeView));
return;
}
_patch?.UnpatchSelf();
_patch = null;
}
[HarmonyTranspiler] [HarmonyTranspiler]
[HarmonyPatch(typeof(VFInput), nameof(VFInput.UpdateGameStates))] [HarmonyPatch(typeof(VFInput), nameof(VFInput.UpdateGameStates))]
private static IEnumerable<CodeInstruction> VFInput_UpdateGameStates_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator) private static IEnumerable<CodeInstruction> VFInput_UpdateGameStates_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)

88
UXAssist/PlayerPatch.cs Normal file
View File

@@ -0,0 +1,88 @@
using System.Collections.Generic;
using System.Reflection.Emit;
using BepInEx.Configuration;
using HarmonyLib;
namespace UXAssist;
public static class PlayerPatch
{
public static ConfigEntry<bool> EnhancedMechaForgeCountControlEnabled;
public static void Init()
{
EnhancedMechaForgeCountControlEnabled.SettingChanged += (_, _) => EnhancedMechaForgeCountControl.Enable(EnhancedMechaForgeCountControlEnabled.Value);
EnhancedMechaForgeCountControl.Enable(EnhancedMechaForgeCountControlEnabled.Value);
}
public static void Uninit()
{
EnhancedMechaForgeCountControl.Enable(false);
}
private static class EnhancedMechaForgeCountControl
{
private static Harmony _patch;
public static void Enable(bool on)
{
if (on)
{
_patch ??= Harmony.CreateAndPatchAll(typeof(EnhancedMechaForgeCountControl));
}
else
{
_patch?.UnpatchSelf();
_patch = null;
}
}
[HarmonyTranspiler]
[HarmonyPatch(typeof(UIReplicatorWindow), nameof(UIReplicatorWindow.OnOkButtonClick))]
private static IEnumerable<CodeInstruction> UIReplicatorWindow_OnOkButtonClick_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{
var matcher = new CodeMatcher(instructions, generator);
matcher.MatchForward(false,
new CodeMatch(ci => ci.opcode == OpCodes.Ldc_I4_S && ci.OperandIs(10))
);
matcher.Repeat(m => m.SetAndAdvance(OpCodes.Ldc_I4, 1000));
return matcher.InstructionEnumeration();
}
[HarmonyTranspiler]
[HarmonyPatch(typeof(UIReplicatorWindow), nameof(UIReplicatorWindow.OnPlusButtonClick))]
[HarmonyPatch(typeof(UIReplicatorWindow), nameof(UIReplicatorWindow.OnMinusButtonClick))]
private static IEnumerable<CodeInstruction> UIReplicatorWindow_OnPlusButtonClick_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{
var label1 = generator.DefineLabel();
var label2 = generator.DefineLabel();
var label3 = generator.DefineLabel();
var label4 = generator.DefineLabel();
var matcher = new CodeMatcher(instructions, generator);
matcher.MatchForward(false,
new CodeMatch(OpCodes.Ldloc_0),
new CodeMatch(OpCodes.Ldc_I4_1),
new CodeMatch(o => o.opcode == OpCodes.Add || o.opcode == OpCodes.Sub)
).Advance(1).RemoveInstruction().InsertAndAdvance(
new CodeInstruction(OpCodes.Ldsfld, AccessTools.Field(typeof(VFInput), nameof(VFInput.control))),
new CodeInstruction(OpCodes.Brfalse_S, label1),
new CodeInstruction(OpCodes.Ldc_I4_S, 10),
new CodeInstruction(OpCodes.Br_S, label4),
new CodeInstruction(OpCodes.Ldsfld, AccessTools.Field(typeof(VFInput), nameof(VFInput.shift))).WithLabels(label1),
new CodeInstruction(OpCodes.Brfalse_S, label2),
new CodeInstruction(OpCodes.Ldc_I4_S, 100),
new CodeInstruction(OpCodes.Br_S, label4),
new CodeInstruction(OpCodes.Ldsfld, AccessTools.Field(typeof(VFInput), nameof(VFInput.alt))).WithLabels(label2),
new CodeInstruction(OpCodes.Brfalse_S, label3),
new CodeInstruction(OpCodes.Ldc_I4, 1000),
new CodeInstruction(OpCodes.Br_S, label4),
new CodeInstruction(OpCodes.Ldc_I4_1).WithLabels(label3)
).Labels.Add(label4);
matcher.MatchForward(false,
new CodeMatch(ci => ci.opcode == OpCodes.Ldc_I4_S && ci.OperandIs(10))
);
matcher.Repeat(m => m.SetAndAdvance(OpCodes.Ldc_I4, 1000));
return matcher.InstructionEnumeration();
}
}
}

View File

@@ -1,288 +1,176 @@
# UXAssist # UXAssist
#### Some functions and patches for better user experience #### Some functions and patches for better user experience
#### 一些提升用户体验的功能和补丁 #### 一些提升用户体验的功能和补丁
## Changlog
* 1.0.11
+ Remove `Better auto-save mechanism` due to conflicts with DSPModSave and some other mods.
* 1.0.10
+ Fix a button display bug
+ Fix a possible crash while `Enhanced control for logistic storage limits` is enabled
* 1.0.9
+ New function: `Better auto-save mechanism`
- Auto saves are stored in 'Save\AutoSaves' folder, filenames are combined with cluster address and date-time
- Note: this will sort gamesaves by modified time on save/load window, so you don't have to use [DSP_Save_Game_Sorter] anymore
* 1.0.8
+ New function: `Enhanced control for logistic storage limits`
* 1.0.7
+ Fix a crash issue on choosing language other than English and Chinese
+ Games saved in Peace-Mode after Dark-Forg update can also be loaded as Combat-Mode now.
* 1.0.6
+ Convert old saves to Combat-Mode on loading
* 1.0.5
+ Support game version 0.10.28.20759
+ Sort blueprint structures before saving, to reduce generated blueprint data size a little.
* 1.0.4
+ Add new function: `Off-grid building and stepped rotation`
+ Fix an issue that window position not restored and can not be resized when function is enabled but game is started with different mod profiles.
* 1.0.3
+ Add new function: `Quick build Orbital Collectors`.
+ Add confirmation popup for `Re-intialize planet`, `Quick dismantle all buildings`, `Re-initialize Dyson Spheres` and `Quick dismantle Dyson Shells`.
+ Fix error on `Remove build count and range limit` when building a large amount of belts.
+ Fix an issue that window position not saved correctly when quit game without using in-game menu.
* 1.0.2
+ Redesign config tabs, for clearer layout.
+ Add 2 new options:
- Enable game window resize.
- Remember window position and size on last exit.
* 1.0.1
+ Fix config button text and tips while returning to title menu.
+ Fix that error occurs while returning to title menu, with `Stop ejectors when available nodes are all filled up` enabled.
+ Add a patch to fix the bug that warning popup on `Veins Utilization` upgraded to level 8000+.
* 1.0.0
+ Initial release
+ Functions moved from [MechaDronesTweaks](https://dsp.thunderstore.io/package/soarqin/MechaDronesTweaks/) and [CheatEnabler](https://dsp.thunderstore.io/package/soarqin/CheatEnabler/)
## Usage ## Usage
* Press `` Alt+`(BackQuote) `` to call up the config panel. You can change the shortcut on the panel. * Press `` LAlt+`(BackQuote) `` to call up the config panel. You can change the shortcut on the panel.
* There are also buttons on title screen and planet minimap area to call up the config panel. * There are also buttons on title screen and planet minimap area to call up the config panel.
* Patches: * Patches:
+ Strict hotkey dectection for build menu, thus building hotkeys(0~9, F1~F10, X, U) are not triggered while holding Ctrl/Alt/Shift. + Strict hotkey dectection for build menu, thus building hotkeys(0~9, F1~F10, X, U) are not triggered while holding Ctrl/Alt/Shift.
+ Fix a bug that warning popup on `Veins Utilization` upgraded to level 8000+ + Fix a bug that warning popup on `Veins Utilization` upgraded to level 8000+
+ Sort blueprint structures before saving, to reduce generated blueprint data size a little + Sort blueprint structures before saving, to reduce generated blueprint data size a little
+ Increase maximum count of Metadata Instantiations to 20000 (from 2000)
+ Increase capacity of player order queue to 128 (from 16)
+ Enable `Hide UI` function(`F11` by default) while on Star Map view
+ Append mod profile name to game window title, if using mod managers (`Thunderstore Mod Manager` or `r2modman`).
* Features: * Features:
+ General + General
- Enable game window resize - Enable game window resize
- Remember window position and size on last exit - Remember window position and size on last exit
- Convert Peace-Mode saves to Combat-Mode on loading - Convert Peace-Mode saves to Combat-Mode on loading
- Scale up mouse cursor + Planet/Factory
- Note: This will enable software cursor mode, which may cause mouse movement lag on heavy load. - Unlimited interactive range
- Mod manager profile based save folder
- Save files are stored in `Save\<ProfileName>` folder.
- Will use original save location if matching default profile name.
- Mod manager profile based option
- Option file is stored as `Options\<ProfileName>.xml`.
- Logical Frame Rate
- This will change game running speed, down to 0.1x slower and up to 10x faster.
- A pair of shortcut keys (`-` and `+`) to change the logical frame rate by -0.5x and +0.5x.
- Note:
- High logical frame rate is not guaranteed to be stable, especially when factories are under heavy load.
- This will not affect some game animations.
- When set game speed in mod `Auxilaryfunction`, this feature will be disabled.
- When mod `BulletTime` is installed, this feature will be hidden, but patch `BulletTime`'s speed control, to make its maximum speed 10x.
- Set process priority
- Set enabled CPU threads
- Increase maximum count of Metadata Instantiations to 20000 (from 2000)
- Increase capacity of player order queue to 128 (from 16)
+ Factory
- Sunlight at night - Sunlight at night
- Remove some build conditions - Remove some build conditions
- Remove build count and range limit - Remove build count and range limit
- Larger area for upgrade and dismantle(30x30 at max) - Larger area for upgrade and dismantle(30x30 at max)
- Larger area for terraform(30x30 at max) - Larger area for terraform(30x30 at max)
- Off-grid building and stepped rotation - Enable player actions in globe view
- Cut conveyor belt
- Press shortcut key to cut conveyor belt under cursor.
- The default shortcut key is Alt+X, you can set it in system options panel.
- Treat stack items as single in monitor components
- Quick build and dismantle stacking labs/storages/tanks
- Fast fill in to and take out from tanks
- You can set multiplier for tanks' operation speed
- This affects manually fill in to and/or take out from tanks, as well as transfer from upper to lower level.
- Protect veins from exhaustion
- By default, the vein amount is protected at 100, and oil speed is protected at 1.0/s, you can set them yourself in config file.
- When reach the protection value, veins/oils steeps will not be mined/extracted any longer.
- Close this function to resume mining and pumping, usually when you have enough level on `Veins Utilization`
- Do not render factory entities (except belts and sorters)
- This also makes players click though factory entities but belts and sorters
- Drag building power poles in maximum connection range
- Dismantle blueprint selected buildings
- Press shortcut key in blueprint copy mode to dismantle selected buildings.
- The default shortcut key is Ctrl+X, you can set it in system options panel.
- Re-intialize planet (without reseting veins)
- Quick dismantle all buildings (without drops)
- Quick build Orbital Collectors
- Belt signals for buy out dark fog items automatically
- 6 belt signals are added to the signal panel, which can be used to buy out dark fog items automatically.
- Generated items are stacked in 4 items.
- Exchange ratio is following the original game design, aka:
- 1 Metaverse = 20 Dark Fog Matrices
- 1 Metaverse = 60 Engery Shards
- 1 Metaverse = 30 Silicon-based Neurons
- 1 Metaverse = 30 Negentropy Singularities
- 1 Metaverse = 30 Matter Recombinators
- 1 Metaverse = 10 Core Elements
- Tweak building buffer
- Factory recipe buffer formula: take the larger value between `Assembler buffer time multiplier(in seconds) * items needed per second` and `Assembler buffer minimum multiplier * items needed per recipe`
- `Assembler buffer time multiplier(in seconds)`: Range 2-10, default is 4 (same as game)
- `Assembler buffer minimum multiplier`: Range 2-10, default is 2 (same as game)
- Matrix Lab assembly mode formula: Default buffer is `Buffer count for assembling in labs`, when using Self-evolution Lab, if recipe's original production time is not greater than 9 seconds, add `Extra buffer count for Self-evolution Labs` * (`Lab speed` - 1)
- `Buffer count for assembling in labs`: Range 2-20, default is 6 (same as game)
- `Extra buffer count for Self-evolution Labs`: Range 1-10, default is 3 (same as game)
- `Buffer count for researching in labs`: Range 2-20, default is 10 (same as game)
- `Ray Receiver Graviton Lens buffer count`: Range 1-20, default is 1 (game default is 20)
+ Logistics
- Enhanced control for logistic storage limits - Enhanced control for logistic storage limits
- Logistic storage limits are not scaled on upgrading `Logistics Carrier Capacity`, if they are not set to maximum capacity. - Logistic storage limits are not scaled on upgrading `Logistics Carrier Capacity`, if they are not set to maximum capacity.
- You can use arrow keys to adjust logistic storage limits gracefully. - You can use arrow keys to adjust logistic storage limits gracefully.
- Logistics Control Panel Improvement
- Auto apply filter with item under mouse cursor while opening the panel
- Quick-set item filter while right-clicking item icons in storage list on the panel
- Allow overflow for Logistic Stations and Advanced Mining Machines
- Allow overflow when trying to insert in-hand items
- Allow `Enhanced control for logistic storage limits` to exceed tech capacity limits
- Remove logistic strorage limit check on loading game
- Real-time logistic stations info panel
- Auto-config logistic stations
- Auto-config buildings include: Logistics Distributor, PLS, ILS, Advanced Mining Machine
+ Player/Mecha
- Unlimited interactive range
- Enable player actions in globe view
- Hide tips for soil piles changes
- Enhanced count control for hand-make - Enhanced count control for hand-make
- Shortcut keys for showing stars' name - Re-intialize planet (without reseting veins)
- Add a shortcut key to always show all star names in starmap when holding, default is `Alt` - Quick dismantle all buildings (without drops)
- Add a shortcut key to toggle between three star name display states in starmap: `Original state`, `Show all names`, `Hide all names`, default is `Tab`, will restore to original state when closing starmap - Quick build Orbital Collectors
- Auto navigation on sailings
- It keeps Icarus on course to the target planet
- It will try to bypass any obstacles(planets, stars or dark-fog hives) on the way
- Furthermore, you can set a shortcut key in the system options window, which is used to toggle `Auto-cruise` that enables flying to targeted planets fully automatically.
- Auto-cruise will start when you select a planet as target
- It will use warper to fly to the target planet if the planet is too far away, the range can be configured.
- It will speed down when approaching the target planet, to avoid overshooting
+ Dyson Sphere + Dyson Sphere
- Stop ejectors when available nodes are all filled up - Stop ejectors when available nodes are all filled up
- Construct only structure points but frames - Construct only nodes but frames
- Re-initialize Dyson Spheres - Re-initialize Dyson Spheres
- Quick dismantle Dyson Shells - Quick dismantle Dyson Shells
- Dyson Sphere "Auto Fast Build" speed multiplier
- Note: this only applies to `Dyson Sphere "Auto Fast Build"` in sandbox mode
+ Tech
- Restore upgrades of `Sorter Cargo Stacking` on panel
- Set `Sorter Cargo Stacking` to unresearched state
- Buy out techs with their prerequisites
- This enables batch buying out techs with their prerequisites. Buy-out button is shown for all locked techs/upgrads.
+ Combat
- Open Dark Fog Communicator anywhere
## Notes ## Notes
* Please upgrade `BepInEx` 5.4.21 or later if using with [BlueprintTweaks](https://dsp.thunderstore.io/package/kremnev8/BlueprintTweaks/) to avoid possible conflicts. * Please upgrade `BepInEx` 5.4.21 or later if using with [BlueprintTweaks](https://dsp.thunderstore.io/package/kremnev8/BlueprintTweaks/) to avoid possible conflicts.
+ You can download `BepInEx` [here](https://github.com/bepinex/bepinex/releases/latest)(choose x64 edition). + You can download `BepInEx` [here](https://github.com/bepinex/bepinex/releases/latest)(choose x64 edition).
+ If using with r2modman, you can upgrade `BepInEx` by clicking `Settings` -> `Browse profile folder`, then extract downloaded zip to the folder and overwrite existing files. + If using with r2modman, you can upgrade `BepInEx` by clicking `Settings` -> `Browse profile folder`, then extract downloaded zip to the folder and overwrite existing files.
## CREDITS ## CREDITS
* [Dyson Sphere Program](https://store.steampowered.com/app/1366540): The great game * [Dyson Sphere Program](https://store.steampowered.com/app/1366540): The great game
* [Multifunction_mod](https://github.com/blacksnipebiu/Multifunction_mod): Some cheat functions * [Multifunction_mod](https://github.com/blacksnipebiu/Multifunction_mod): Some cheat functions
* [LSTM](https://github.com/hetima/DSP_LSTM) & [PlanetFinder](https://github.com/hetima/DSP_PlanetFinder): UI implementations * [LSTM](https://github.com/hetima/DSP_LSTM) & [PlanetFinder](https://github.com/hetima/DSP_PlanetFinder): UI implementations
* [OffGridConstruction](https://github.com/Velociraptor115-DSPModding/OffGridConstruction): Off-grid building & stepped rotation implementations * [OffGridConstruction](https://github.com/Velociraptor115-DSPModding/OffGridConstruction): Off-grid building & stepped rotation implementations
* [CruiseAssist](https://dsp.thunderstore.io/package/tanu/CruiseAssist/) and its extension [AutoPilot](https://dsp.thunderstore.io/package/tanu/AutoPilot/): `Auto navigation on sailings` and `Auto-cruise` implementations
## 更新日志
* 1.0.11
+ 移除`更好的自动保存机制`因为与DSPModSave和其他一些mod冲突
* 1.0.10
+ 修复了一个按钮显示错误
+ 修复了`物流塔存储数量限制控制改进`启用时可能导致的崩溃问题
* 1.0.9
+ 新功能:`更好的自动保存机制`
- 自动存档会以星区地址和日期时间组合为文件名存储在'Save\AutoSaves'文件夹中
- 注意:此功能会在保存/读取菜单按最后修改时间对存档进行排序,因此你不再需要[DSP_Save_Game_Sorter]了
* 1.0.8
+ 新功能:`物流塔存储数量限制控制改进`
* 1.0.7
+ 修复了选择英文和中文以外的语言时的崩溃问题
+ 黑雾更新后使用和平模式保存的存档现在也可以转换为战斗模式了
* 1.0.6
+ 在加载旧存档时将其转换为战斗模式
* 1.0.5
+ 支持游戏版本0.10.28.20759
+ 保存蓝图前对建筑进行排序,以减少生成的蓝图数据大小
* 1.0.4
+ 添加了新功能:`脱离网格建造和小角度旋转`
+ 修复了当功能启用但游戏使用不同的mod配置文件启动时窗口位置无法正确恢复和不可拖动改变大小的问题
* 1.0.3
+ 添加了新功能:`快速建造轨道采集器`
+ 为`初始化行星``快速拆除所有建筑``初始化戴森球``快速拆除戴森壳`添加了确认弹窗
+ 修复了`移除建造数量和范围限制`在建造大量传送带时可能导致的错误
+ 修复了在不使用游戏内菜单退出游戏时窗口位置无法正确保存的问题
* 1.0.2
+ 重新设计了配置面板,使布局更清晰
+ 添加了两个新选项:
- 可调整游戏窗口大小(可最大化和拖动边框)
- 记住上次退出时的窗口位置和大小
* 1.0.1
+ 修复了返回标题界面后设置按钮文本和提示信息不正确的问题
+ 修复了`当可用节点全部造完时停止弹射`选项启用时返回标题界面可能导致崩溃的问题
+ 添加了一个补丁,修复了`矿物利用`升级到8000级以上时弹出警告的bug
* 1.0.0
+ 初始版本
+ 从[MechaDronesTweaks](https://dsp.thunderstore.io/package/soarqin/MechaDronesTweaks/)和[CheatEnabler](https://dsp.thunderstore.io/package/soarqin/CheatEnabler/)移动了部分功能过来
## 使用说明 ## 使用说明
* 按 `` Alt+`(反引号) `` 键呼出主面板,可以在面板上修改快捷键。 * 按 `` Alt+`(反引号) `` 键呼出主面板,可以在面板上修改快捷键。
* 标题界面和行星小地图旁也有按钮呼出主面板。 * 标题界面和行星小地图旁也有按钮呼出主面板。
* 补丁: * 补丁:
+ 更严格的建造菜单热键检测因此在按住Ctrl/Alt/Shift时不再会触发建造热键(0~9, F1~F10, X, U) + 更严格的建造菜单热键检测因此在按住Ctrl/Alt/Shift时不再会触发建造热键(0~9, F1~F10, X, U)
+ 修复了`矿物利用`升级到8000级以上时弹出警告的bug + 修复了`矿物利用`升级到8000级以上时弹出警告的bug
+ 保存蓝图前对建筑进行排序,以减少生成的蓝图数据大小 + 保存蓝图前对建筑进行排序,以减少生成的蓝图数据大小
+ 将元数据提取的最大数量增加到20000(原来为2000)
+ 将玩家指令队列的容量增加到128(原来为16)
+ 在星图视图中启用`隐藏UI`功能(默认按键为`F11`)
+ 如果使用mod管理器(`Thunderstore Mod Manager``r2modman`)启动游戏在游戏窗口标题中追加mod配置档案名
* 功能: * 功能:
+ 通用 + 通用
- 可调整游戏窗口大小(可最大化和拖动边框) - 可调整游戏窗口大小(可最大化和拖动边框)
- 记住上次退出时的窗口位置和大小 - 记住上次退出时的窗口位置和大小
- 在加载和平模式存档时将其转换为战斗模式 - 在加载和平模式存档时将其转换为战斗模式
- 放大鼠标指针 + 行星/工厂
- 注意这将启用软件指针模式可能会在CPU负载较重时导致鼠标移动延迟 - 无限交互距离
- 基于mod管理器配置档案名的存档文件夹
- 存档文件会存储在`Save\<ProfileName>`文件夹中
- 如果匹配默认配置档案名则使用原始存档位置
- 基于mod管理器配置档案名的选项文件
- 选项文件存储为`Options\<ProfileName>.xml`
- 逻辑帧倍率
- 这将改变游戏运行速度最慢0.1倍最快10倍
- 设置了一对快捷键(`-``+`),可以-/+0.5倍改变逻辑帧倍率
- 注意:
- 高逻辑帧倍率不能保证稳定性,特别是在工厂负载较重时
- 这不会影响一些游戏动画
- 当在`Auxilaryfunction`mod中设置游戏速度时此功能将被禁用
- 当安装了`BulletTime`mod时此功能将被隐藏但会对`BulletTime`的速度控制打补丁使其最大速度变为10倍
- 设置进程优先级
- 设置使用的CPU线程
- 将元数据提取的最大数量增加到20000(原来为2000)
- 将玩家指令队列的容量增加到128(原来为16)
+ 工厂
- 夜间日光灯 - 夜间日光灯
- 移除部分不影响游戏逻辑的建造条件 - 移除部分不影响游戏逻辑的建造条件
- 移除建造数量和范围限制
- 范围升级和拆除的最大区域扩大(最大30x30) - 范围升级和拆除的最大区域扩大(最大30x30)
- 范围铺设地基的最大区域扩大(最大30x30) - 范围铺设地基的最大区域扩大(最大30x30)
- 脱离网格建造以及小角度旋转 - 在行星视图中允许玩家操作
- 切割传送带
- 按快捷键切割光标位置的传送带
- 默认快捷键是Alt+X可以在系统选项面板中设置
- 在流速计中将堆叠物品视为单个物品
- 快速建造和拆除堆叠研究站/储物仓/储液罐
- 储液罐快速注入和抽取液体
- 你可以设置储液罐操作速度的倍率
- 影响手动注入和抽取,以及从储液罐上层传输到下层的速度
- 保护矿脉不会耗尽
- 默认矿脉数量保护在100采油速保护在1.0/s你可以在配置文件中自行设置。
- 当达到保护值时,矿脉和油井将不再被开采。
- 关闭此功能以恢复开采,一般是当你在`矿物利用`上有足够的等级时。
- 不渲染工厂建筑实体(除了传送带和分拣器)
- 这也使玩家可以点穿工厂实体直接点到传送带和分拣器
- 拖动建造电线杆时自动使用最大连接距离间隔
- 拆除蓝图选中的建筑
- 在蓝图复制模式下按快捷键拆除选中的建筑
- 默认快捷键是Ctrl+X可以在系统选项面板中设置
- 初始化本行星(不重置矿脉)
- 快速拆除所有建筑(不掉落)
- 快速建造轨道采集器
- 用于自动购买黑雾物品的传送带信号
- 在信号面板上添加了6个传送带信号可以用于自动购买黑雾道具。
- 生成的物品堆叠数为4。
- 兑换比率遵循原始游戏设计,即:
- 1个元宇宙 = 20个黑雾矩阵
- 1个元宇宙 = 60个能量碎片
- 1个元宇宙 = 30个硅基神经元
- 1个元宇宙 = 30个负熵奇点
- 1个元宇宙 = 30个物质重组器
- 1个元宇宙 = 10个核心素
- 调整建筑输入缓冲
- 工厂配方计算公式,在`工厂配方缓冲时间倍率秒数x每秒需要的原料数量``工厂配方缓冲最小倍率x每生产一次配方需要的原料数量`中取更大的那个值
- `工厂配方缓冲时间倍率(秒)`范围2-10默认为4(同游戏)
- `工厂配方缓冲最小倍率`范围2-10默认为2(同游戏)
- 研究站矩阵合成模式计算公式,默认缓存`研究站矩阵合成模式缓存数量`当使用自演化研究站时如果配方的原始生产时间不大于9秒则增加`自演化研究站矩阵额外缓冲数量`*(`研究站速度倍率`-1)
- `研究站矩阵合成模式缓存数量`范围2-20默认为6(同游戏)
- `自演化研究站矩阵额外缓冲数量`范围1-10默认为3(同游戏)
- `研究站科研模式缓存数量`范围2-20默认为10(同游戏)
- `射线接收器透镜缓冲数量`范围1-20默认为1(游戏默认为20)
+ 物流
- 物流塔存储数量限制控制改进 - 物流塔存储数量限制控制改进
- 当升级`运输机舱扩容`时,不会对各种物流塔的存储限制按比例提升,除非设置为最大允许容量。 - 当升级`运输机舱扩容`时,不会对各种物流塔的存储限制按比例提升,除非设置为最大允许容量。
- 你可以使用方向键微调物流塔存储限制 - 你可以使用方向键微调物流塔存储限制
- 物流控制面板改进
- 打开面板时自动将鼠标指向物品设为筛选条件
- 在控制面板物流塔列表中右键点击物品图标快速设置为筛选条件
- 允许物流塔和大型采矿机物品溢出
- 当尝试塞入手中物品时允许溢出
- 允许`物流塔存储数量限制控制改进`超过科技容量限制
- 在加载游戏时移除物流塔容量限制检查
- 物流运输站实时信息面板
- 注意:如果你启用了`Auxilaryfunction`中的`展示物流站信息`,此功能将被隐藏
- 自动配置物流站
- 自动配置的建筑包括:物流配送器、行星物流站、星际物流站、高级采矿机
+ 玩家/机甲
- 无限交互距离
- 移除建造数量和范围限制
- 在行星视图中允许玩家操作
- 隐藏沙土数量变动的提示
- 手动制造物品的数量控制改进 - 手动制造物品的数量控制改进
- 启用显示所有星系名称的快捷键 - 初始化本行星(不重置矿脉)
- 新增一个快捷键,按住后始终在星图显示所有星系名称,默认为`Alt` - 快速拆除所有建筑(不掉落)
- 新增一个快捷键,在星图视图切换三种星系名称显示状态:`原始显示状态``显示所有名称``隐藏所有名称`,默认为`Tab`,关闭星图时会恢复到原始状态 - 快速建造轨道采集器
- 航行时自动导航
- 它会保持伊卡洛斯飞向目标星球
- 它会尝试绕过途中的任何障碍物(行星、恒星或黑雾巢穴)
- 此外,可以在系统选项窗口中设置快捷键,用于切换`自动巡航`,实现完全自动化的飞行至目标星球。
- 当你选择目标星球后,自动巡航就会开始
- 如果目标星球距离过远会自动使用曲速(超过5AU),你可以在面板上更改这个值。
- 它会在接近目标星球时减速,以避免发生越过目标的情况
+ 戴森球 + 戴森球
- 可用节点全部造完时停止弹射 - 可用节点全部造完时停止弹射
- 只建造节点不建造框架 - 只建造节点不建造框架
- 初始化戴森球 - 初始化戴森球
- 快速拆除戴森壳 - 快速拆除戴森壳
- 戴森球自动快速建造速度倍率
- 注意:这仅适用于沙盒模式下的`戴森球自动快速建造`功能
+ 科研
- 在升级面板上恢复`分拣器货物堆叠`的升级
-`分拣器货物堆叠`设为未研究状态
- 买断科技也同时买断所有前置科技
- 这使得可以批量买断科技及其所有前置科技。所有未解锁的科技/升级都会显示买断按钮。
+ 战斗
- 在任意位置打开黑雾通讯器
## 注意事项 ## 注意事项
* 如果和[BlueprintTweaks](https://dsp.thunderstore.io/package/kremnev8/BlueprintTweaks/)一起使用,请升级`BepInEx`到5.4.21或更高版本,以避免可能的冲突。 * 如果和[BlueprintTweaks](https://dsp.thunderstore.io/package/kremnev8/BlueprintTweaks/)一起使用,请升级`BepInEx`到5.4.21或更高版本,以避免可能的冲突。
+ 你可以在[这里](https://github.com/bepinex/bepinex/releases/latest)选择x64版本下载`BepInEx` + 你可以在[这里](https://github.com/bepinex/bepinex/releases/latest)选择x64版本下载`BepInEx`
+ 如果使用r2modman你可以点击`Settings` -> `Browse profile folder`然后将下载的zip解压到该文件夹并覆盖现有文件。 + 如果使用r2modman你可以点击`Settings` -> `Browse profile folder`然后将下载的zip解压到该文件夹并覆盖现有文件。
## 鸣谢 ## 鸣谢
* [戴森球计划](https://store.steampowered.com/app/1366540): 伟大的游戏 * [戴森球计划](https://store.steampowered.com/app/1366540): 伟大的游戏
* [BepInEx](https://bepinex.dev/): 基础模组框架 * [BepInEx](https://bepinex.dev/): 基础模组框架
* [LSTM](https://github.com/hetima/DSP_LSTM) & [PlanetFinder](https://github.com/hetima/DSP_PlanetFinder): UI实现 * [LSTM](https://github.com/hetima/DSP_LSTM) & [PlanetFinder](https://github.com/hetima/DSP_PlanetFinder): UI实现
* [OffGridConstruction](https://github.com/Velociraptor115-DSPModding/OffGridConstruction): 脱离网格建造以及小角度旋转的实现 * [OffGridConstruction](https://github.com/Velociraptor115-DSPModding/OffGridConstruction): 脱离网格建造以及小角度旋转的实现
* [CruiseAssist](https://dsp.thunderstore.io/package/tanu/CruiseAssist/)及其扩展[AutoPilot](https://dsp.thunderstore.io/package/tanu/AutoPilot/): `航行时自动导航``自动巡航`的实现

View File

@@ -1 +0,0 @@
#### TODO

View File

@@ -8,86 +8,13 @@ namespace UXAssist.UI;
// MyCheckBox modified from LSTM: https://github.com/hetima/DSP_LSTM/blob/main/LSTM/MyCheckBox.cs // MyCheckBox modified from LSTM: https://github.com/hetima/DSP_LSTM/blob/main/LSTM/MyCheckBox.cs
public class MyCheckBox : MonoBehaviour public class MyCheckBox : MonoBehaviour
{ {
public RectTransform rectTrans;
public UIButton uiButton; public UIButton uiButton;
public Image boxImage;
public Image checkImage; public Image checkImage;
public RectTransform rectTrans;
public Text labelText; public Text labelText;
public event Action OnChecked; public event Action OnChecked;
private bool _checked; private bool _checked;
private ConfigEntry<bool> _configAssigned;
private static GameObject _baseObject;
private static readonly Color BoxColor = new Color(1f, 1f, 1f, 100f / 255f);
private static readonly Color CheckColor = new Color(1f, 1f, 1f, 1f);
private static readonly Color TextColor = new Color(178f / 255f, 178f / 255f, 178f / 255f, 168f / 255f);
public static void InitBaseObject()
{
if (_baseObject) return;
var go = Instantiate(UIRoot.instance.uiGame.buildMenu.uxFacilityCheck.gameObject);
go.name = "my-checkbox";
go.SetActive(false);
var comp = go.transform.Find("text");
if (comp)
{
var txt = comp.GetComponent<Text>();
if (txt) txt.text = "";
var localizer = comp.GetComponent<Localizer>();
if (localizer) DestroyImmediate(localizer);
}
_baseObject = go;
}
protected void OnDestroy()
{
_config.SettingChanged -= _configChanged;
}
public static MyCheckBox CreateCheckBox(float x, float y, RectTransform parent, ConfigEntry<bool> config, string label = "", int fontSize = 15)
{
return CreateCheckBox(x, y, parent, config.Value, label, fontSize).WithConfigEntry(config);
}
public static MyCheckBox CreateCheckBox(float x, float y, RectTransform parent, bool check, string label = "", int fontSize = 15)
{
return CreateCheckBox(x, y, parent, fontSize).WithCheck(check).WithLabelText(label);
}
public static MyCheckBox CreateCheckBox(float x, float y, RectTransform parent, int fontSize = 15)
{
var go = Instantiate(_baseObject);
go.name = "my-checkbox";
go.SetActive(true);
var cb = go.AddComponent<MyCheckBox>();
var rect = Util.NormalizeRectWithTopLeft(cb, x, y, parent);
cb.rectTrans = rect;
cb.uiButton = go.GetComponent<UIButton>();
cb.boxImage = go.transform.GetComponent<Image>();
cb.checkImage = go.transform.Find("checked")?.GetComponent<Image>();
Util.NormalizeRectWithTopLeft(cb.checkImage, 0f, 0f);
var child = go.transform.Find("text");
if (child != null)
{
cb.labelText = child.GetComponent<Text>();
if (cb.labelText)
{
cb.labelText.text = "";
cb.labelText.fontSize = fontSize;
cb.UpdateLabelTextWidth();
}
}
cb.uiButton.onClick += cb.OnClick;
return cb;
}
private void UpdateLabelTextWidth()
{
if (labelText) labelText.rectTransform.sizeDelta = new Vector2(labelText.preferredWidth, labelText.rectTransform.sizeDelta.y);
}
public bool Checked public bool Checked
{ {
@@ -99,88 +26,61 @@ public class MyCheckBox : MonoBehaviour
} }
} }
public static MyCheckBox CreateCheckBox(float x, float y, RectTransform parent, ConfigEntry<bool> config, string label = "", int fontSize = 15)
{
var cb = CreateCheckBox(x, y, parent, config.Value, label, fontSize);
cb._configAssigned = config;
cb.OnChecked += () => config.Value = !config.Value;
config.SettingChanged += (_, _) => cb.Checked = config.Value;
return cb;
}
public static MyCheckBox CreateCheckBox(float x, float y, RectTransform parent, bool check, string label = "", int fontSize = 15)
{
var buildMenu = UIRoot.instance.uiGame.buildMenu;
var src = buildMenu.uxFacilityCheck;
var go = Instantiate(src.gameObject);
go.name = "my-checkbox";
var cb = go.AddComponent<MyCheckBox>();
cb._checked = check;
var rect = Util.NormalizeRectWithTopLeft(cb, x, y, parent);
cb.rectTrans = rect;
cb.uiButton = go.GetComponent<UIButton>();
cb.checkImage = go.transform.Find("checked")?.GetComponent<Image>();
var child = go.transform.Find("text");
if (child != null)
{
DestroyImmediate(child.GetComponent<Localizer>());
cb.labelText = child.GetComponent<Text>();
cb.labelText.fontSize = fontSize;
cb.SetLabelText(label);
}
//value
cb.uiButton.onClick += cb.OnClick;
if (cb.checkImage != null)
{
cb.checkImage.enabled = check;
}
return cb;
}
public void SetLabelText(string val) public void SetLabelText(string val)
{ {
if (labelText != null) if (labelText != null)
{ {
labelText.text = val.Translate(); labelText.text = val.Translate();
UpdateLabelTextWidth();
} }
} }
public void SetEnable(bool on)
{
if (uiButton) uiButton.enabled = on;
if (on)
{
if (boxImage) boxImage.color = BoxColor;
if (checkImage) checkImage.color = CheckColor;
if (labelText) labelText.color = TextColor;
}
else
{
if (boxImage) boxImage.color = BoxColor.RGBMultiplied(0.5f);
if (checkImage) checkImage.color = CheckColor.RGBMultiplied(0.5f);
if (labelText) labelText.color = TextColor.RGBMultiplied(0.5f);
}
}
private EventHandler _configChanged;
private Action _checkedChanged;
private ConfigEntry<bool> _config;
public void SetConfigEntry(ConfigEntry<bool> config)
{
if (_checkedChanged != null) OnChecked -= _checkedChanged;
if (_configChanged != null) config.SettingChanged -= _configChanged;
_config = config;
_checkedChanged = () => config.Value = !config.Value;
OnChecked += _checkedChanged;
_configChanged = (_, _) => Checked = config.Value;
config.SettingChanged += _configChanged;
}
public MyCheckBox WithLabelText(string val)
{
SetLabelText(val);
return this;
}
public MyCheckBox WithCheck(bool check)
{
Checked = check;
return this;
}
public MyCheckBox WithSmallerBox(float boxSize = 20f)
{
var oldWidth = rectTrans.sizeDelta.x;
rectTrans.sizeDelta = new Vector2(boxSize, boxSize);
checkImage.rectTransform.sizeDelta = new Vector2(boxSize, boxSize);
labelText.rectTransform.sizeDelta = new Vector2(labelText.rectTransform.sizeDelta.x, boxSize);
labelText.rectTransform.localPosition = new Vector3(labelText.rectTransform.localPosition.x + boxSize - oldWidth, labelText.rectTransform.localPosition.y, labelText.rectTransform.localPosition.z);
return this;
}
public MyCheckBox WithEnable(bool on)
{
SetEnable(on);
return this;
}
public MyCheckBox WithConfigEntry(ConfigEntry<bool> config)
{
SetConfigEntry(config);
return this;
}
public void OnClick(int obj) public void OnClick(int obj)
{ {
_checked = !_checked; _checked = !_checked;
checkImage.enabled = _checked; checkImage.enabled = _checked;
OnChecked?.Invoke(); OnChecked?.Invoke();
} }
public float Width => rectTrans.sizeDelta.x + labelText.rectTransform.sizeDelta.x;
public float Height => Math.Max(rectTrans.sizeDelta.y, labelText.rectTransform.sizeDelta.y);
} }

View File

@@ -1,194 +0,0 @@
using System;
using System.Linq;
using BepInEx.Configuration;
using UnityEngine;
using UnityEngine.UI;
namespace UXAssist.UI;
public class MyComboBox : MonoBehaviour
{
private RectTransform _rectTrans;
private UIComboBox _comboBox;
private Text _text;
public Action<int> OnSelChanged;
private static GameObject _baseObject;
public static void InitBaseObject()
{
if (_baseObject) return;
var fontSource = UIRoot.instance.uiGame.buildMenu.uxFacilityCheck.transform.Find("text")?.GetComponent<Text>();
var go = Instantiate(UIRoot.instance.optionWindow.resolutionComp.transform.parent.gameObject);
go.name = "my-combobox";
go.SetActive(false);
var txt = go.GetComponent<Text>();
if (txt) txt.text = "";
if (txt && fontSource)
{
txt.font = fontSource.font;
txt.fontSize = fontSource.fontSize;
txt.fontStyle = fontSource.fontStyle;
txt.color = new Color(1f, 1f, 1f, 0.6f);
}
var localizer = go.GetComponent<Localizer>();
if (localizer) DestroyImmediate(localizer);
var rect = (RectTransform)go.transform;
var cbctrl = rect.transform.Find("ComboBox").GetComponent<UIComboBox>();
foreach (var button in cbctrl.ItemButtons)
{
Destroy(button.gameObject);
}
cbctrl.Items.Clear();
cbctrl.ItemButtons.Clear();
if (fontSource)
{
var txtComp = cbctrl.m_ListItemRes.GetComponentInChildren<Text>();
if (txtComp)
{
txtComp.font = fontSource.font;
txtComp.fontSize = fontSource.fontSize;
txtComp.fontStyle = fontSource.fontStyle;
}
txtComp = cbctrl.transform.Find("Main Button")?.GetComponentInChildren<Text>();
if (txtComp)
{
txtComp.font = fontSource.font;
txtComp.fontSize = fontSource.fontSize;
txtComp.fontStyle = fontSource.fontStyle;
}
}
cbctrl.onSubmit.RemoveAllListeners();
cbctrl.onItemIndexChange.RemoveAllListeners();
_baseObject = go;
}
public static MyComboBox CreateComboBox(float x, float y, RectTransform parent)
{
var gameObject = Instantiate(_baseObject);
gameObject.name = "my-combobox";
gameObject.SetActive(true);
var cb = gameObject.AddComponent<MyComboBox>();
var rtrans = Util.NormalizeRectWithTopLeft(cb, x, y, parent);
cb._rectTrans = rtrans;
cb._text = gameObject.GetComponent<Text>();
var box = rtrans.Find("ComboBox").GetComponent<UIComboBox>();
cb._comboBox = box;
box.onItemIndexChange.AddListener(() => { cb.OnSelChanged?.Invoke(box.itemIndex); });
cb.UpdateComboBoxPosition();
return cb;
}
protected void OnDestroy()
{
_config.SettingChanged -= _configChanged;
}
private void UpdateComboBoxPosition()
{
var rtrans = (RectTransform)_comboBox.transform;
var oldPosition = rtrans.localPosition;
var pwidth = _text.preferredWidth;
rtrans.localPosition = new Vector3(pwidth + 5f, oldPosition.y, oldPosition.z);
_rectTrans.sizeDelta = new Vector2(rtrans.localPosition.x + 5f + rtrans.sizeDelta.x, _rectTrans.sizeDelta.y);
}
public void SetPrompt(string prompt)
{
_text.text = prompt.Translate();
UpdateComboBoxPosition();
}
public void SetFontSize(int size)
{
_text.fontSize = size;
_comboBox.ItemButtons.ForEach(b => b.GetComponentInChildren<Text>().fontSize = size);
_comboBox.m_ListItemRes.GetComponentInChildren<Text>().fontSize = size;
var txtComp = _comboBox.transform.Find("Main Button")?.GetComponentInChildren<Text>();
if (txtComp) txtComp.fontSize = size;
UpdateComboBoxPosition();
}
public void SetItems(params string[] items)
{
_comboBox.Items = [.. items.Select(s => s.Translate())];
_comboBox.StartItemIndex = 0;
_comboBox.DropDownCount = Math.Min(items.Length, 8);
}
public void SetIndex(int index) => _comboBox.itemIndex = index;
public void SetSize(float width, float height)
{
var rtrans = (RectTransform)_comboBox.transform;
rtrans.sizeDelta = new Vector2(width > 0f ? width : rtrans.sizeDelta.x, height > 0f ? height : rtrans.sizeDelta.y);
_rectTrans.sizeDelta = new Vector2(rtrans.localPosition.x + rtrans.sizeDelta.x, _rectTrans.sizeDelta.y);
}
public void AddOnSelChanged(Action<int> action) => OnSelChanged += action;
private EventHandler _configChanged;
private Action<int> _selChanged;
private ConfigEntry<int> _config;
public void SetConfigEntry(ConfigEntry<int> config)
{
if (_selChanged != null) OnSelChanged -= _selChanged;
if (_configChanged != null) config.SettingChanged -= _configChanged;
_comboBox.itemIndex = config.Value;
_config = config;
_selChanged = value => config.Value = value;
OnSelChanged += _selChanged;
_configChanged = (_, _) => SetIndex(config.Value);
config.SettingChanged += _configChanged;
}
public MyComboBox WithPrompt(string prompt)
{
SetPrompt(prompt);
return this;
}
public MyComboBox WithFontSize(int size)
{
SetFontSize(size);
return this;
}
public MyComboBox WithItems(params string[] items)
{
SetItems(items);
return this;
}
public MyComboBox WithIndex(int index)
{
SetIndex(index);
return this;
}
public MyComboBox WithSize(float width, float height)
{
SetSize(width, height);
return this;
}
public MyComboBox WithOnSelChanged(params Action<int>[] action)
{
foreach (var act in action)
AddOnSelChanged(act);
return this;
}
public MyComboBox WithConfigEntry(ConfigEntry<int> config)
{
SetConfigEntry(config);
return this;
}
public float Width => _rectTrans.sizeDelta.x;
public float Height => _rectTrans.sizeDelta.y;
}

View File

@@ -15,34 +15,38 @@ public class MyConfigWindow : MyWindowWithTabs
return MyWindowManager.CreateWindow<MyConfigWindow>("UXAConfigWindow", "UXAssist Config"); return MyWindowManager.CreateWindow<MyConfigWindow>("UXAConfigWindow", "UXAssist Config");
} }
public static void DestroyInstance(MyConfigWindow win)
{
MyWindowManager.DestroyWindow(win);
}
public override void _OnCreate() public override void _OnCreate()
{ {
base._OnCreate();
_windowTrans = GetComponent<RectTransform>(); _windowTrans = GetComponent<RectTransform>();
_windowTrans.sizeDelta = new Vector2(810f, 440f);
OnUICreated?.Invoke(this, _windowTrans); OnUICreated?.Invoke(this, _windowTrans);
AutoFitWindowSize();
SetCurrentTab(0); SetCurrentTab(0);
OnUpdateUI?.Invoke(); OnUpdateUI?.Invoke();
} }
public override void _OnDestroy() public override void _OnDestroy()
{ {
_windowTrans = null;
base._OnDestroy();
} }
public override bool _OnInit() public override bool _OnInit()
{ {
if (!base._OnInit()) return false;
_windowTrans.anchoredPosition = new Vector2(0, 0); _windowTrans.anchoredPosition = new Vector2(0, 0);
return true; return true;
} }
public override void _OnFree()
{
}
public override void _OnOpen()
{
}
public override void _OnClose()
{
}
public override void _OnUpdate() public override void _OnUpdate()
{ {
base._OnUpdate(); base._OnUpdate();

View File

@@ -10,7 +10,6 @@ namespace UXAssist.UI;
public class MyKeyBinder : MonoBehaviour public class MyKeyBinder : MonoBehaviour
{ {
private ConfigEntry<KeyboardShortcut> _config; private ConfigEntry<KeyboardShortcut> _config;
protected event Action OnFree;
[SerializeField] [SerializeField]
public Text functionText; public Text functionText;
@@ -44,11 +43,6 @@ public class MyKeyBinder : MonoBehaviour
private bool _nextNotOn; private bool _nextNotOn;
protected void OnDestroy()
{
OnFree?.Invoke();
}
public static RectTransform CreateKeyBinder(float x, float y, RectTransform parent, ConfigEntry<KeyboardShortcut> config, string label = "", int fontSize = 17) public static RectTransform CreateKeyBinder(float x, float y, RectTransform parent, ConfigEntry<KeyboardShortcut> config, string label = "", int fontSize = 17)
{ {
var optionWindow = UIRoot.instance.optionWindow; var optionWindow = UIRoot.instance.optionWindow;
@@ -88,10 +82,10 @@ public class MyKeyBinder : MonoBehaviour
Destroy(uikeyEntry); Destroy(uikeyEntry);
kb.setNoneKeyUIButton.gameObject.SetActive(false); kb.setNoneKeyUIButton.gameObject.SetActive(false);
EventHandler func = (_, _) => kb.SettingChanged();
kb.SettingChanged(); kb.SettingChanged();
config.SettingChanged += func; config.SettingChanged += (_, _) => {
kb.OnFree += () => config.SettingChanged -= func; kb.SettingChanged();
};
kb.inputUIButton.onClick += kb.OnInputUIButtonClick; kb.inputUIButton.onClick += kb.OnInputUIButtonClick;
kb.setDefaultUIButton.onClick += kb.OnSetDefaultKeyClick; kb.setDefaultUIButton.onClick += kb.OnSetDefaultKeyClick;
//kb.setNoneKeyUIButton.onClick += kb.OnSetNoneKeyClick; //kb.setNoneKeyUIButton.onClick += kb.OnSetNoneKeyClick;
@@ -112,7 +106,7 @@ public class MyKeyBinder : MonoBehaviour
setTheKeyToggle.isOn = false; setTheKeyToggle.isOn = false;
Reset(); Reset();
} }
else if (!inputUIButton.highlighted) else if (!this.inputUIButton.highlighted)
{ {
setTheKeyToggle.isOn = false; setTheKeyToggle.isOn = false;
Reset(); Reset();
@@ -173,7 +167,7 @@ public class MyKeyBinder : MonoBehaviour
{ {
if (Input.GetKey(modKey)) if (Input.GetKey(modKey))
{ {
mod += "+" + modKey; mod += "+" + modKey.ToString();
} }
} }

View File

@@ -1,166 +0,0 @@
using System;
using UnityEngine;
using UnityEngine.UI;
namespace UXAssist.UI;
// MySlider modified from LSTM: https://github.com/hetima/DSP_LSTM/blob/main/LSTM/MySlider.cs
public class MySideSlider : MonoBehaviour
{
public RectTransform rectTrans;
public Slider slider;
public Text labelText;
public string labelFormat;
public event Action OnValueChanged;
public static MySideSlider CreateSlider(float x, float y, RectTransform parent, float value, float minValue, float maxValue, string format = "G", float width = 0f, float textWidth = 0f)
{
return CreateSlider(x, y, parent, width, textWidth).WithLabelFormat(format).WithMinMaxValue(minValue, maxValue).WithValue(value);
}
public static MySideSlider CreateSlider(float x, float y, RectTransform parent, float width = 0f, float textWidth = 0f)
{
var go = Instantiate(UIRoot.instance.uiGame.stationWindow.maxMiningSpeedGroup.gameObject);
//sizeDelta = 240, 20
go.name = "my-side-slider";
Destroy(go.transform.Find("label").gameObject);
Destroy(go.GetComponent<UIButton>());
go.SetActive(true);
var sl = go.AddComponent<MySideSlider>();
var rect = Util.NormalizeRectWithTopLeft(sl, x, y, parent);
sl.rectTrans = rect;
sl.slider = go.transform.Find("slider").GetComponent<Slider>();
sl.slider.minValue = 0f;
sl.slider.maxValue = 100f;
sl.slider.onValueChanged.RemoveAllListeners();
sl.slider.onValueChanged.AddListener(sl.SliderChanged);
if (width == 0) width = 160f;
if (sl.slider.transform is RectTransform rectTrans)
{
rectTrans.localPosition = new Vector3(width, rectTrans.localPosition.y, rectTrans.localPosition.z);
rectTrans.sizeDelta = new Vector2(width, rectTrans.sizeDelta.y);
}
sl.Value = 0f;
sl.labelText = go.transform.Find("value").GetComponent<Text>();
sl.labelText.alignment = textWidth <= 0f ? TextAnchor.MiddleLeft : TextAnchor.MiddleRight;
if (sl.labelText.transform is RectTransform rectTrans2)
{
if (textWidth > 0f)
{
rectTrans2.sizeDelta = new Vector2(textWidth, rectTrans2.sizeDelta.y);
}
else
{
rectTrans2.sizeDelta = new Vector2(-textWidth, rectTrans2.sizeDelta.y);
}
rectTrans2.pivot = new Vector2(0f, 1f);
rectTrans2.localPosition = new Vector3(textWidth <= 0f ? width + 10f : width, rectTrans2.localPosition.y, rectTrans2.localPosition.z);
}
sl.labelFormat = "G";
// var bg = sl.slider.transform.Find("Background")?.GetComponent<Image>();
// if (bg != null)
// {
// bg.color = new Color(0.5f, 0.5f, 0.5f, 0.5f);
// }
// var fill = sl.slider.fillRect.GetComponent<Image>();
// if (fill != null)
// {
// fill.color = new Color(1f, 1f, 1f, 0.28f);
// }
sl.UpdateLabel();
return sl;
}
public void SetEnable(bool on)
{
lock (this)
{
if (slider) slider.interactable = on;
}
}
public float Value
{
get => slider.value;
set
{
var sliderVal = value;
if (sliderVal.Equals(slider.value)) return;
if (sliderVal > slider.maxValue)
{
sliderVal = slider.maxValue;
}
else if (sliderVal < slider.minValue)
{
sliderVal = slider.minValue;
}
slider.value = sliderVal;
UpdateLabel();
}
}
public MySideSlider WithValue(float value)
{
Value = value;
return this;
}
public MySideSlider WithMinMaxValue(float min, float max)
{
slider.minValue = min;
slider.maxValue = max;
return this;
}
public MySideSlider WithLabelFormat(string format)
{
if (format == labelFormat) return this;
labelFormat = format;
UpdateLabel();
return this;
}
public MySideSlider WithFontSize(int fontSize)
{
labelText.fontSize = fontSize;
return this;
}
public MySideSlider WithEnable(bool on)
{
SetEnable(on);
return this;
}
public void UpdateLabel()
{
if (labelText != null)
{
labelText.text = slider.value.ToString(labelFormat);
}
}
public void SetLabelText(string text)
{
if (labelText != null)
{
labelText.text = text;
}
}
public void SliderChanged(float val)
{
lock (this)
{
UpdateLabel();
OnValueChanged?.Invoke();
}
}
}

View File

@@ -8,19 +8,23 @@ namespace UXAssist.UI;
public class MySlider : MonoBehaviour public class MySlider : MonoBehaviour
{ {
public RectTransform rectTrans;
public Slider slider; public Slider slider;
public RectTransform handleSlideArea; public RectTransform rectTrans;
public Text labelText; public Text labelText;
public string labelFormat; public string labelFormat;
public event Action OnValueChanged; public event Action OnValueChanged;
private float _value;
public static MySlider CreateSlider(float x, float y, RectTransform parent, float value, float minValue, float maxValue, string format = "G", float width = 0f) public float Value
{ {
return CreateSlider(x, y, parent, width).WithLabelFormat(format).WithMinMaxValue(minValue, maxValue).WithValue(value); get => _value;
set
{
_value = value;
OnValueSet();
}
} }
public static MySlider CreateSlider(float x, float y, RectTransform parent, float width = 0f) public static MySlider CreateSlider(float x, float y, RectTransform parent, float value, float minValue, float maxValue, string format = "G", float width = 0f)
{ {
var optionWindow = UIRoot.instance.optionWindow; var optionWindow = UIRoot.instance.optionWindow;
var src = optionWindow.audioVolumeComp; var src = optionWindow.audioVolumeComp;
@@ -30,6 +34,7 @@ public class MySlider : MonoBehaviour
go.name = "my-slider"; go.name = "my-slider";
go.SetActive(true); go.SetActive(true);
var sl = go.AddComponent<MySlider>(); var sl = go.AddComponent<MySlider>();
sl._value = value;
var rect = Util.NormalizeRectWithTopLeft(sl, x, y, parent); var rect = Util.NormalizeRectWithTopLeft(sl, x, y, parent);
sl.rectTrans = rect; sl.rectTrans = rect;
if (width > 0) if (width > 0)
@@ -38,13 +43,12 @@ public class MySlider : MonoBehaviour
} }
sl.slider = go.GetComponent<Slider>(); sl.slider = go.GetComponent<Slider>();
sl.slider.minValue = 0f; sl.slider.minValue = minValue;
sl.slider.maxValue = 100f; sl.slider.maxValue = maxValue;
sl.slider.onValueChanged.RemoveAllListeners(); sl.slider.onValueChanged.RemoveAllListeners();
sl.slider.onValueChanged.AddListener(sl.SliderChanged); sl.slider.onValueChanged.AddListener(sl.SliderChanged);
sl.Value = 0f;
sl.labelText = sl.slider.handleRect.Find("Text")?.GetComponent<Text>(); sl.labelText = sl.slider.handleRect.Find("Text")?.GetComponent<Text>();
if (sl.labelText) if (sl.labelText != null)
{ {
sl.labelText.fontSize = 14; sl.labelText.fontSize = 14;
if (sl.labelText.transform is RectTransform rectTrans) if (sl.labelText.transform is RectTransform rectTrans)
@@ -52,9 +56,7 @@ public class MySlider : MonoBehaviour
rectTrans.sizeDelta = new Vector2(22f, 22f); rectTrans.sizeDelta = new Vector2(22f, 22f);
} }
} }
sl.labelFormat = "G"; sl.labelFormat = format;
sl.handleSlideArea = sl.transform.Find("Handle Slide Area")?.GetComponent<RectTransform>();
var bg = sl.slider.transform.Find("Background")?.GetComponent<Image>(); var bg = sl.slider.transform.Find("Background")?.GetComponent<Image>();
if (bg != null) if (bg != null)
@@ -66,72 +68,35 @@ public class MySlider : MonoBehaviour
{ {
fill.color = new Color(1f, 1f, 1f, 0.28f); fill.color = new Color(1f, 1f, 1f, 0.28f);
} }
sl.OnValueSet();
sl.UpdateLabel(); sl.UpdateLabel();
return sl; return sl;
} }
public void OnValueSet()
public void SetEnable(bool on)
{ {
lock (this) lock (this)
{ {
if (slider) slider.interactable = on; var sliderVal = _value;
}
}
public float Value
{
get => slider.value;
set
{
var sliderVal = value;
if (sliderVal.Equals(slider.value)) return; if (sliderVal.Equals(slider.value)) return;
if (sliderVal > slider.maxValue) if (sliderVal > slider.maxValue)
{ {
sliderVal = slider.maxValue; _value = sliderVal = slider.maxValue;
} }
else if (sliderVal < slider.minValue) else if (sliderVal < slider.minValue)
{ {
sliderVal = slider.minValue; _value = sliderVal = slider.minValue;
} }
slider.value = sliderVal; slider.value = sliderVal;
UpdateLabel(); UpdateLabel();
} }
} }
public MySlider WithValue(float value)
{
Value = value;
return this;
}
public MySlider WithMinMaxValue(float min, float max)
{
slider.minValue = min;
slider.maxValue = max;
return this;
}
public MySlider WithLabelFormat(string format)
{
if (format == labelFormat) return this;
labelFormat = format;
UpdateLabel();
return this;
}
public MySlider WithEnable(bool on)
{
SetEnable(on);
return this;
}
public void UpdateLabel() public void UpdateLabel()
{ {
if (labelText != null) if (labelText != null)
{ {
labelText.text = slider.value.ToString(labelFormat); labelText.text = _value.ToString(labelFormat);
} }
} }
@@ -143,19 +108,13 @@ public class MySlider : MonoBehaviour
} }
} }
public MySlider WithSmallerHandle(float deltaX = 10f, float deltaY = 0f)
{
var oldSize = slider.handleRect.sizeDelta;
slider.handleRect.sizeDelta = new Vector2(oldSize.x - deltaX, oldSize.y - deltaY);
handleSlideArea.offsetMin = new Vector2(handleSlideArea.offsetMin.x - deltaX / 2, handleSlideArea.offsetMin.y);
handleSlideArea.offsetMax = new Vector2(handleSlideArea.offsetMax.x + deltaX / 2, handleSlideArea.offsetMax.y);
return this;
}
public void SliderChanged(float val) public void SliderChanged(float val)
{ {
lock (this) lock (this)
{ {
var newVal = Mathf.Round(slider.value);
if (_value.Equals(newVal)) return;
_value = newVal;
UpdateLabel(); UpdateLabel();
OnValueChanged?.Invoke(); OnValueChanged?.Invoke();
} }

View File

@@ -1,72 +1,20 @@
using System; using System;
using System.Collections.Generic;
using System.Globalization;
using BepInEx.Configuration;
using HarmonyLib; using HarmonyLib;
using System.Collections.Generic;
using UnityEngine; using UnityEngine;
using UnityEngine.Events; using UnityEngine.Events;
using UnityEngine.UI; using UnityEngine.UI;
using UXAssist.Common; using Object = UnityEngine.Object;
namespace UXAssist.UI; namespace UXAssist.UI;
// MyWindow modified from LSTM: https://github.com/hetima/DSP_LSTM/blob/main/LSTM/MyWindowCtl.cs // MyWindow modified from LSTM: https://github.com/hetima/DSP_LSTM/blob/main/LSTM/MyWindowCtl.cs
public class MyWindow : ManualBehaviour public class MyWindow: ManualBehaviour
{ {
private float _maxX; private readonly Dictionary<InputField, Tuple<UnityAction<string>, UnityAction<string>>> _inputFields = new();
protected float MaxY; private readonly Dictionary<UIButton, UnityAction> _buttons = new();
protected const float TitleHeight = 48f; protected bool EventRegistered { get; private set; }
protected const float TabWidth = 105f;
protected const float TabHeight = 27f;
protected const float Margin = 30f;
protected const float Spacing = 10f;
public event Action OnFree;
private static GameObject _baseObject;
public static void InitBaseObject()
{
if (_baseObject) return;
var go = Instantiate(UIRoot.instance.uiGame.inserterWindow.gameObject);
go.SetActive(false);
go.name = "my-window";
Destroy(go.GetComponent<UITankWindow>());
for (var i = go.transform.childCount - 1; i >= 0; i--)
{
var child = go.transform.GetChild(i).gameObject;
if (child.name != "panel-bg" && child.name != "shadow")
{
Destroy(child);
}
}
_baseObject = go;
}
public static T Create<T>(string name, string title = "") where T : MyWindow
{
var go = Instantiate(_baseObject, UIRoot.instance.uiGame.transform.parent);
go.name = name;
go.SetActive(false);
MyWindow win = go.AddComponent<T>();
if (!win) return null;
var btn = go.transform.Find("panel-bg")?.gameObject.GetComponentInChildren<Button>();
if (btn) btn.onClick.AddListener(win._Close);
win.SetTitle(title);
win._Create();
if (MyWindowManager.Initialized)
{
win._Init(win.data);
}
return (T)win;
}
public override void _OnFree()
{
OnFree?.Invoke();
}
public virtual void TryClose() public virtual void TryClose()
{ {
@@ -95,12 +43,6 @@ public class MyWindow : ManualBehaviour
} }
} }
public void AutoFitWindowSize()
{
var trans = GetComponent<RectTransform>();
trans.sizeDelta = new Vector2(_maxX + Margin + TabWidth + Spacing + Margin, MaxY + TitleHeight + Margin);
}
private static void AddElement(float x, float y, RectTransform rect, RectTransform parent = null) private static void AddElement(float x, float y, RectTransform rect, RectTransform parent = null)
{ {
if (rect != null) if (rect != null)
@@ -118,19 +60,14 @@ public class MyWindow : ManualBehaviour
txt.color = new Color(1f, 1f, 1f, 0.4f); txt.color = new Color(1f, 1f, 1f, 0.4f);
txt.alignment = TextAnchor.MiddleLeft; txt.alignment = TextAnchor.MiddleLeft;
txt.fontSize = fontSize; txt.fontSize = fontSize;
txt.rectTransform.sizeDelta = new Vector2(txt.preferredWidth + 8f, txt.preferredHeight + 8f); if (txt.transform is RectTransform rect)
{
rect.sizeDelta = new Vector2(txt.preferredWidth + 40f, 30f);
}
AddElement(x, y, txt.rectTransform, parent); AddElement(x, y, txt.rectTransform, parent);
return txt; return txt;
} }
public Text AddText2(float x, float y, RectTransform parent, string label, int fontSize = 14, string objName = "label")
{
var text = AddText(x, y, parent, label, fontSize, objName);
_maxX = Math.Max(_maxX, x + text.rectTransform.sizeDelta.x);
MaxY = Math.Max(MaxY, y + text.rectTransform.sizeDelta.y);
return text;
}
public static UIButton AddTipsButton(float x, float y, RectTransform parent, string label, string tip, string content, string objName = "tips-button") public static UIButton AddTipsButton(float x, float y, RectTransform parent, string label, string tip, string content, string objName = "tips-button")
{ {
var src = UIRoot.instance.galaxySelect.sandboxToggle.gameObject.transform.parent.Find("tip-button"); var src = UIRoot.instance.galaxySelect.sandboxToggle.gameObject.transform.parent.Find("tip-button");
@@ -145,19 +82,6 @@ public class MyWindow : ManualBehaviour
return btn; return btn;
} }
public UIButton AddTipsButton2(float x, float y, RectTransform parent, string label, string tip, string content, string objName = "tips-button")
{
var tipsButton = AddTipsButton(x, y, parent, label, tip, content, objName);
var rect = tipsButton.transform as RectTransform;
if (rect != null)
{
_maxX = Math.Max(_maxX, x + rect.sizeDelta.x);
MaxY = Math.Max(MaxY, y + rect.sizeDelta.y);
}
return tipsButton;
}
public UIButton AddButton(float x, float y, RectTransform parent, string text = "", int fontSize = 16, string objName = "button", UnityAction onClick = null) public UIButton AddButton(float x, float y, RectTransform parent, string text = "", int fontSize = 16, string objName = "button", UnityAction onClick = null)
{ {
return AddButton(x, y, 150f, parent, text, fontSize, objName, onClick); return AddButton(x, y, 150f, parent, text, fontSize, objName, onClick);
@@ -177,20 +101,20 @@ public class MyWindow : ManualBehaviour
l.stringKey = text; l.stringKey = text;
l.translation = text.Translate(); l.translation = text.Translate();
} }
if (t != null) if (t != null)
{ {
t.text = text.Translate(); t.text = text.Translate();
} }
t.fontSize = fontSize; t.fontSize = fontSize;
btn.button.onClick.RemoveAllListeners();
btn.tip = null; btn.tip = null;
btn.tips = new UIButton.TipSettings(); btn.tips = new UIButton.TipSettings();
btn.button.onClick.RemoveAllListeners(); _buttons[btn] = onClick;
if (onClick != null) btn.button.onClick.AddListener(onClick); if (EventRegistered)
{
_maxX = Math.Max(_maxX, x + rect.sizeDelta.x); if (onClick != null)
MaxY = Math.Max(MaxY, y + rect.sizeDelta.y); btn.button.onClick.AddListener(onClick);
}
return btn; return btn;
} }
@@ -206,215 +130,28 @@ public class MyWindow : ManualBehaviour
img.sprite = panel.buttonDefaultSprite; img.sprite = panel.buttonDefaultSprite;
img.color = new Color(img.color.r, img.color.g, img.color.b, 13f / 255f); img.color = new Color(img.color.r, img.color.g, img.color.b, 13f / 255f);
} }
img = btn.gameObject.transform.Find("frame")?.GetComponent<Image>(); img = btn.gameObject.transform.Find("frame")?.GetComponent<Image>();
if (img != null) if (img != null)
{ {
img.color = new Color(img.color.r, img.color.g, img.color.b, 0f); img.color = new Color(img.color.r, img.color.g, img.color.b, 0f);
} }
Util.NormalizeRectWithTopLeft(btn, x, y, parent);
var rect = Util.NormalizeRectWithTopLeft(btn, x, y, parent);
var t = btn.gameObject.transform.Find("Text")?.GetComponent<Text>(); var t = btn.gameObject.transform.Find("Text")?.GetComponent<Text>();
if (t != null) if (t != null)
{ {
t.text = text.Translate(); t.text = text.Translate();
t.fontSize = fontSize; t.fontSize = fontSize;
} }
btn.button.onClick.RemoveAllListeners(); btn.button.onClick.RemoveAllListeners();
if (onClick != null) btn.button.onClick.AddListener(onClick); _buttons[btn] = onClick;
if (EventRegistered && onClick != null)
_maxX = Math.Max(_maxX, x + rect.sizeDelta.x); {
MaxY = Math.Max(MaxY, y + rect.sizeDelta.y); btn.button.onClick.AddListener(onClick);
}
return btn; return btn;
} }
public MyCheckBox AddCheckBox(float x, float y, RectTransform parent, ConfigEntry<bool> config, string label = "", int fontSize = 15) protected InputField AddInputField(float x, float y, RectTransform parent, string text = "", int fontSize = 16, string objName = "input", UnityAction<string> onChanged = null, UnityAction<string> onEditEnd = null)
{
var cb = MyCheckBox.CreateCheckBox(x, y, parent, config, label, fontSize);
_maxX = Math.Max(_maxX, x + cb.Width);
MaxY = Math.Max(MaxY, y + cb.Height);
return cb;
}
public MyComboBox AddComboBox(float x, float y, RectTransform parent, string label = "", int fontSize = 15)
{
var comboBox = MyComboBox.CreateComboBox(x, y, parent).WithPrompt(label).WithFontSize(fontSize);
_maxX = Math.Max(_maxX, x + comboBox.Width);
MaxY = Math.Max(MaxY, y + comboBox.Height);
return comboBox;
}
// public MySmallComboBox AddSmallComboBox(float x, float y, RectTransform parent, int fontSize = 15)
// {
// var comboBox = MySmallComboBox.CreateComboBox(x, y, parent).WithFontSize(fontSize);
// _maxX = Math.Max(_maxX, x + comboBox.Width);
// MaxY = Math.Max(MaxY, y + comboBox.Height);
// return comboBox;
// }
#region Slider
public class ValueMapper<T>
{
public virtual int Min => 1;
public virtual int Max => 100;
public virtual int ValueToIndex(T value) => (int)Convert.ChangeType(value, typeof(int), CultureInfo.InvariantCulture);
public virtual T IndexToValue(int index) => (T)Convert.ChangeType(index, typeof(T), CultureInfo.InvariantCulture);
public virtual string FormatValue(string format, T value)
{
return string.Format($"{{0:{format}}}", value);
}
}
public class RangeValueMapper<T>(int min, int max) : ValueMapper<T>
{
public override int Min => min;
public override int Max => max;
}
private class ArrayMapper<T> : ValueMapper<T>
{
private readonly T[] _values;
public ArrayMapper(T[] values)
{
Array.Sort(values);
_values = values;
}
public override int Min => 0;
public override int Max => _values.Length - 1;
public override int ValueToIndex(T value)
{
return Array.BinarySearch(_values, value);
}
public override T IndexToValue(int index)
{
return _values[index >= 0 && index < _values.Length ? index : 0];
}
}
public MySlider AddSlider(float x, float y, RectTransform parent, float value, float minValue, float maxValue, string format = "G", float width = 0f)
{
var slider = MySlider.CreateSlider(x, y, parent, value, minValue, maxValue, format, width);
var rect = slider.rectTrans;
if (rect != null)
{
_maxX = Math.Max(_maxX, x + rect.sizeDelta.x);
MaxY = Math.Max(MaxY, y + rect.sizeDelta.y);
}
return slider;
}
public MySideSlider AddSideSlider(float x, float y, RectTransform parent, float value, float minValue, float maxValue, string format = "G", float width = 0f, float textWidth = 0f)
{
var slider = MySideSlider.CreateSlider(x, y, parent, value, minValue, maxValue, format, width, textWidth);
var rect = slider.rectTrans;
if (rect != null)
{
_maxX = Math.Max(_maxX, x + rect.sizeDelta.x);
MaxY = Math.Max(MaxY, y + rect.sizeDelta.y);
}
return slider;
}
public MySlider AddSlider<T>(float x, float y, RectTransform parent, ConfigEntry<T> config, ValueMapper<T> valueMapper, string format = "G", float width = 0f)
{
var slider = MySlider.CreateSlider(x, y, parent, OnConfigValueChanged(config), valueMapper.Min, valueMapper.Max, format, width);
slider.SetLabelText(valueMapper.FormatValue(format, config.Value));
config.SettingChanged += SettingsChanged;
OnFree += () => config.SettingChanged -= SettingsChanged;
slider.OnValueChanged += () =>
{
var index = Mathf.RoundToInt(slider.Value);
config.Value = valueMapper.IndexToValue(index);
slider.SetLabelText(valueMapper.FormatValue(format, config.Value));
};
var rect = slider.rectTrans;
if (rect != null)
{
_maxX = Math.Max(_maxX, x + rect.sizeDelta.x);
MaxY = Math.Max(MaxY, y + rect.sizeDelta.y);
}
return slider;
void SettingsChanged(object o, EventArgs a)
{
var index = OnConfigValueChanged(config);
slider.Value = index;
slider.SetLabelText(valueMapper.FormatValue(format, config.Value));
}
int OnConfigValueChanged(ConfigEntry<T> conf)
{
var index = valueMapper.ValueToIndex(conf.Value);
if (index >= 0) return index;
index = ~index;
index = Math.Max(0, Math.Min(valueMapper.Max, index));
conf.Value = valueMapper.IndexToValue(index);
return index;
}
}
public MySideSlider AddSideSlider<T>(float x, float y, RectTransform parent, ConfigEntry<T> config, ValueMapper<T> valueMapper, string format = "G", float width = 0f, float textWidth = 0f)
{
var slider = MySideSlider.CreateSlider(x, y, parent, OnConfigValueChanged(config), valueMapper.Min, valueMapper.Max, format, width, textWidth);
slider.SetLabelText(valueMapper.FormatValue(format, config.Value));
config.SettingChanged += SettingsChanged;
OnFree += () => config.SettingChanged -= SettingsChanged;
slider.OnValueChanged += () =>
{
var index = Mathf.RoundToInt(slider.Value);
config.Value = valueMapper.IndexToValue(index);
slider.SetLabelText(valueMapper.FormatValue(format, config.Value));
};
var rect = slider.rectTrans;
if (rect != null)
{
_maxX = Math.Max(_maxX, x + rect.sizeDelta.x);
MaxY = Math.Max(MaxY, y + rect.sizeDelta.y);
}
return slider;
void SettingsChanged(object o, EventArgs a)
{
var index = OnConfigValueChanged(config);
slider.Value = index;
slider.SetLabelText(valueMapper.FormatValue(format, config.Value));
}
int OnConfigValueChanged(ConfigEntry<T> conf)
{
var index = valueMapper.ValueToIndex(conf.Value);
if (index >= 0) return index;
index = ~index;
index = Math.Max(0, Math.Min(valueMapper.Max, index));
conf.Value = valueMapper.IndexToValue(index);
return index;
}
}
public MySlider AddSlider<T>(float x, float y, RectTransform parent, ConfigEntry<T> config, T[] valueList, string format = "G", float width = 0f)
{
return AddSlider(x, y, parent, config, new ArrayMapper<T>(valueList), format, width);
}
public MySideSlider AddSideSlider<T>(float x, float y, RectTransform parent, ConfigEntry<T> config, T[] valueList, string format = "G", float width = 0f)
{
return AddSideSlider(x, y, parent, config, new ArrayMapper<T>(valueList), format, width);
}
#endregion
public InputField AddInputField(float x, float y, RectTransform parent, string text = "", int fontSize = 16, string objName = "input", UnityAction<string> onChanged = null,
UnityAction<string> onEditEnd = null)
{ {
var stationWindow = UIRoot.instance.uiGame.stationWindow; var stationWindow = UIRoot.instance.uiGame.stationWindow;
//public InputField nameInput; //public InputField nameInput;
@@ -424,39 +161,61 @@ public class MyWindow : ManualBehaviour
inputField.GetComponent<Image>().color = new Color(1f, 1f, 1f, 0.05f); inputField.GetComponent<Image>().color = new Color(1f, 1f, 1f, 0.05f);
var rect = Util.NormalizeRectWithTopLeft(inputField, x, y, parent); var rect = Util.NormalizeRectWithTopLeft(inputField, x, y, parent);
rect.sizeDelta = new Vector2(210, rect.sizeDelta.y); rect.sizeDelta = new Vector2(210, rect.sizeDelta.y);
inputField.text = text; inputField.textComponent.text = text;
inputField.textComponent.fontSize = fontSize; inputField.textComponent.fontSize = fontSize;
inputField.onValueChanged.RemoveAllListeners(); inputField.onValueChanged.RemoveAllListeners();
if (onChanged != null) inputField.onValueChanged.AddListener(onChanged);
inputField.onEndEdit.RemoveAllListeners(); inputField.onEndEdit.RemoveAllListeners();
if (onEditEnd != null) inputField.onEndEdit.AddListener(onEditEnd); _inputFields[inputField] = Tuple.Create(onChanged, onEditEnd);
if (EventRegistered)
_maxX = Math.Max(_maxX, x + rect.sizeDelta.x); {
MaxY = Math.Max(MaxY, y + rect.sizeDelta.y); if (onChanged != null)
inputField.onValueChanged.AddListener(onChanged);
if (onEditEnd != null)
inputField.onEndEdit.AddListener(onEditEnd);
}
return inputField; return inputField;
} }
public InputField AddInputField(float x, float y, float width, RectTransform parent, ConfigEntry<string> config, int fontSize = 16, string objName = "input") public override void _OnRegEvent()
{ {
var stationWindow = UIRoot.instance.uiGame.stationWindow; base._OnRegEvent();
//public InputField nameInput; if (EventRegistered) return;
var inputField = Instantiate(stationWindow.nameInput); foreach (var t in _inputFields)
inputField.gameObject.name = objName; {
Destroy(inputField.GetComponent<UIButton>()); var inputField = t.Key;
inputField.GetComponent<Image>().color = new Color(1f, 1f, 1f, 0.05f); if (t.Value.Item1 != null)
var rect = Util.NormalizeRectWithTopLeft(inputField, x, y, parent); inputField.onValueChanged.AddListener(t.Value.Item1);
rect.sizeDelta = new Vector2(width, rect.sizeDelta.y); if (t.Value.Item2 != null)
inputField.text = config.Value; inputField.onEndEdit.AddListener(t.Value.Item2);
inputField.textComponent.fontSize = fontSize; }
foreach (var t in _buttons)
{
var btn = t.Key;
if (t.Value != null)
btn.button.onClick.AddListener(t.Value);
}
EventRegistered = true;
}
inputField.onValueChanged.RemoveAllListeners(); public override void _OnUnregEvent()
inputField.onEndEdit.RemoveAllListeners(); {
inputField.onEndEdit.AddListener(value => config.Value = value); base._OnUnregEvent();
if (!EventRegistered) return;
_maxX = Math.Max(_maxX, x + rect.sizeDelta.x); EventRegistered = false;
MaxY = Math.Max(MaxY, y + rect.sizeDelta.y); foreach (var t in _buttons)
return inputField; {
var btn = t.Key;
if (t.Value != null)
btn.button.onClick.RemoveListener(t.Value);
}
foreach (var t in _inputFields)
{
var inputField = t.Key;
if (t.Value.Item1 != null)
inputField.onValueChanged.RemoveListener(t.Value.Item1);
if (t.Value.Item2 != null)
inputField.onEndEdit.RemoveListener(t.Value.Item2);
}
} }
} }
@@ -464,7 +223,6 @@ public class MyWindowWithTabs : MyWindow
{ {
private readonly List<Tuple<RectTransform, UIButton>> _tabs = []; private readonly List<Tuple<RectTransform, UIButton>> _tabs = [];
private float _tabY = 54f; private float _tabY = 54f;
public override void TryClose() public override void TryClose()
{ {
_Close(); _Close();
@@ -479,14 +237,14 @@ public class MyWindowWithTabs : MyWindow
{ {
var tab = new GameObject(); var tab = new GameObject();
var tabRect = tab.AddComponent<RectTransform>(); var tabRect = tab.AddComponent<RectTransform>();
Util.NormalizeRectWithMargin(tabRect, TitleHeight, Margin + TabWidth + Spacing, 0f, 0f, parent); Util.NormalizeRectWithMargin(tabRect, 48f, 145f, 0f, 0f, parent);
tab.name = "tab-" + index; tab.name = "tab-" + index;
var swarmPanel = UIRoot.instance.uiGame.dysonEditor.controlPanel.hierarchy.swarmPanel; var swarmPanel = UIRoot.instance.uiGame.dysonEditor.controlPanel.hierarchy.swarmPanel;
var src = swarmPanel.orbitButtons[0]; var src = swarmPanel.orbitButtons[0];
var btn = Instantiate(src); var btn = Instantiate(src);
var btnRect = Util.NormalizeRectWithTopLeft(btn, Margin, y, parent); var btnRect = Util.NormalizeRectWithTopLeft(btn, 30, y, parent);
btn.name = "tab-btn-" + index; btn.name = "tab-btn-" + index;
btnRect.sizeDelta = new Vector2(TabWidth, TabHeight); btnRect.sizeDelta = new Vector2(105f, 27f);
btn.transform.Find("frame").gameObject.SetActive(false); btn.transform.Find("frame").gameObject.SetActive(false);
if (btn.transitions.Length >= 3) if (btn.transitions.Length >= 3)
{ {
@@ -502,9 +260,10 @@ public class MyWindowWithTabs : MyWindow
btn.data = index; btn.data = index;
_tabs.Add(Tuple.Create(tabRect, btn)); _tabs.Add(Tuple.Create(tabRect, btn));
if (EventRegistered)
{
btn.onClick += OnTabButtonClick; btn.onClick += OnTabButtonClick;
}
MaxY = Math.Max(MaxY, y + TabHeight);
return tabRect; return tabRect;
} }
@@ -531,6 +290,30 @@ public class MyWindowWithTabs : MyWindow
_tabY += 28f; _tabY += 28f;
} }
public override void _OnRegEvent()
{
if (!EventRegistered)
{
foreach (var t in _tabs)
{
t.Item2.onClick += OnTabButtonClick;
}
}
base._OnRegEvent();
}
public override void _OnUnregEvent()
{
if (EventRegistered)
{
foreach (var t in _tabs)
{
t.Item2.onClick -= OnTabButtonClick;
}
}
base._OnUnregEvent();
}
protected void SetCurrentTab(int index) => OnTabButtonClick(index); protected void SetCurrentTab(int index) => OnTabButtonClick(index);
private void OnTabButtonClick(int index) private void OnTabButtonClick(int index)
@@ -543,45 +326,67 @@ public class MyWindowWithTabs : MyWindow
rectTransform.gameObject.SetActive(false); rectTransform.gameObject.SetActive(false);
continue; continue;
} }
btn.highlighted = true; btn.highlighted = true;
rectTransform.gameObject.SetActive(true); rectTransform.gameObject.SetActive(true);
} }
} }
} }
public abstract class MyWindowManager public static class MyWindowManager
{ {
private static readonly List<ManualBehaviour> Windows = new(4); private static readonly List<ManualBehaviour> Windows = new(4);
private static bool _initialized;
private static Harmony _patch;
public static bool Initialized { get; private set; } public static void Init()
public static void Enable(bool on)
{ {
Patch.Enable(on); _patch ??= Harmony.CreateAndPatchAll(typeof(Patch));
} }
public static void InitBaseObjects() public static void Uninit()
{ {
MyWindow.InitBaseObject(); _patch?.UnpatchSelf();
MyCheckBox.InitBaseObject(); _patch = null;
MyComboBox.InitBaseObject();
// MySmallComboBox.InitBaseObject();
} }
public static T CreateWindow<T>(string name, string title = "") where T : MyWindow public static T CreateWindow<T>(string name, string title = "") where T : MyWindow
{ {
var win = MyWindow.Create<T>(name, title); var srcWin = UIRoot.instance.uiGame.tankWindow;
if (win) Windows.Add(win); var src = srcWin.gameObject;
return win; var go = Object.Instantiate(src, UIRoot.instance.uiGame.transform.parent);
go.name = name;
go.SetActive(false);
Object.Destroy(go.GetComponent<UITankWindow>());
var win = go.AddComponent<T>() as MyWindow;
if (win == null)
return null;
for (var i = 0; i < go.transform.childCount; i++)
{
var child = go.transform.GetChild(i).gameObject;
if (child.name == "panel-bg")
{
var btn = child.GetComponentInChildren<Button>();
//close-btn
if (btn != null)
{
btn.onClick.AddListener(win._Close);
}
}
else if (child.name != "shadow" && child.name != "panel-bg")
{
Object.Destroy(child);
}
} }
public static void DestroyWindow(ManualBehaviour win) win.SetTitle(title);
win._Create();
if (_initialized)
{ {
if (win == null) return; win._Init(win.data);
Windows.Remove(win); }
win._Free(); Windows.Add(win);
win._Destroy(); return (T)win;
} }
/* /*
@@ -593,23 +398,8 @@ public abstract class MyWindowManager
} }
*/ */
public class Patch: PatchImpl<Patch> public static class Patch
{ {
protected override void OnEnable()
{
InitAllWindows();
}
private static void InitAllWindows()
{
if (Initialized) return;
if (!UIRoot.instance) return;
foreach (var win in Windows)
{
win._Init(win.data);
}
Initialized = true;
}
/* /*
//_Create -> _Init //_Create -> _Init
@@ -619,7 +409,7 @@ public abstract class MyWindowManager
} }
*/ */
[HarmonyPostfix, HarmonyPatch(typeof(UIRoot), nameof(UIRoot._OnDestroy))] [HarmonyPostfix, HarmonyPatch(typeof(UIRoot), "_OnDestroy")]
public static void UIRoot__OnDestroy_Postfix() public static void UIRoot__OnDestroy_Postfix()
{ {
foreach (var win in Windows) foreach (var win in Windows)
@@ -627,14 +417,18 @@ public abstract class MyWindowManager
win._Free(); win._Free();
win._Destroy(); win._Destroy();
} }
Windows.Clear(); Windows.Clear();
} }
[HarmonyPostfix, HarmonyPatch(typeof(UIRoot), nameof(UIRoot._OnOpen))] [HarmonyPostfix, HarmonyPatch(typeof(UIRoot), "_OnOpen")]
public static void UIRoot__OnOpen_Postfix() public static void UIRoot__OnOpen_Postfix()
{ {
InitAllWindows(); if (_initialized) return;
foreach (var win in Windows)
{
win._Init(win.data);
}
_initialized = true;
} }
/* /*
@@ -648,21 +442,20 @@ public abstract class MyWindowManager
} }
*/ */
[HarmonyPostfix, HarmonyPatch(typeof(UIRoot), nameof(UIRoot._OnUpdate))] [HarmonyPostfix, HarmonyPatch(typeof(UIRoot), "_OnUpdate")]
public static void UIRoot__OnUpdate_Postfix() public static void UIRoot__OnUpdate_Postfix()
{ {
if (GameMain.isPaused || !GameMain.isRunning) if (GameMain.isPaused || !GameMain.isRunning)
{ {
return; return;
} }
foreach (var win in Windows) foreach (var win in Windows)
{ {
win._Update(); win._Update();
} }
} }
[HarmonyPostfix, HarmonyPatch(typeof(UIGame), nameof(UIGame.ShutAllFunctionWindow))] [HarmonyPostfix, HarmonyPatch(typeof(UIGame), "ShutAllFunctionWindow")]
public static void UIGame_ShutAllFunctionWindow_Postfix() public static void UIGame_ShutAllFunctionWindow_Postfix()
{ {
foreach (var win in Windows) foreach (var win in Windows)

View File

@@ -19,20 +19,6 @@ public static class Util
return rect; return rect;
} }
public static RectTransform NormalizeRectWithTopRight(Component cmp, float right, float top, Transform parent = null)
{
if (cmp.transform is not RectTransform rect) return null;
if (parent != null)
{
rect.SetParent(parent, false);
}
rect.anchorMax = new Vector2(1f, 1f);
rect.anchorMin = new Vector2(1f, 1f);
rect.pivot = new Vector2(1f, 1f);
rect.anchoredPosition3D = new Vector3(-right, -top, 0f);
return rect;
}
public static RectTransform NormalizeRectWithBottomLeft(Component cmp, float left, float bottom, Transform parent = null) public static RectTransform NormalizeRectWithBottomLeft(Component cmp, float left, float bottom, Transform parent = null)
{ {
if (cmp.transform is not RectTransform rect) return null; if (cmp.transform is not RectTransform rect) return null;

View File

@@ -1,12 +1,6 @@
using System; using UnityEngine;
using System.Text;
using UnityEngine;
using UnityEngine.UI;
using UXAssist.Common;
using UXAssist.Functions;
using UXAssist.ModsCompat;
using UXAssist.Patches;
using UXAssist.UI; using UXAssist.UI;
using UXAssist.Common;
namespace UXAssist; namespace UXAssist;
@@ -14,124 +8,33 @@ public static class UIConfigWindow
{ {
private static RectTransform _windowTrans; private static RectTransform _windowTrans;
private static RectTransform _dysonTab; private static RectTransform _dysonTab;
private static UIButton _dysonInitBtn;
private static readonly UIButton[] DysonLayerBtn = new UIButton[10]; private static readonly UIButton[] DysonLayerBtn = new UIButton[10];
public static void Init() public static void Init()
{ {
I18N.Add("UXAssist", "UXAssist", "UX助手"); I18N.Add("UXAssist", "UXAssist", "UX助手");
I18N.Add("General", "General", "常规"); I18N.Add("General", "General", "常规");
I18N.Add("Factory", "Factory", "工厂"); I18N.Add("Planet/Factory", "Planet/Factory", "行星/工厂");
I18N.Add("Logistics", "Logistics", "物流");
I18N.Add("Player/Mecha", "Player/Mecha", "玩家/机甲");
I18N.Add("Dyson Sphere", "Dyson Sphere", "戴森球"); I18N.Add("Dyson Sphere", "Dyson Sphere", "戴森球");
I18N.Add("Tech/Combat", "Tech/Combat", "科研/战斗");
I18N.Add("Enable game window resize", "Enable game window resize (maximum box and thick frame)", "可调整游戏窗口大小(可最大化和拖动边框)"); I18N.Add("Enable game window resize", "Enable game window resize (maximum box and thick frame)", "可调整游戏窗口大小(可最大化和拖动边框)");
I18N.Add("Remeber window position and size on last exit", "Remeber window position and size on last exit", "记住上次退出时的窗口位置和大小"); I18N.Add("Remeber window position and size on last exit", "Remeber window position and size on last exit", "记住上次退出时的窗口位置和大小");
I18N.Add("Scale up mouse cursor", "Scale up mouse cursor", "放大鼠标指针");
/* /*
I18N.Add("Better auto-save mechanism", "Better auto-save mechanism", "更好的自动存档机制"); I18N.Add("Better auto-save mechanism", "Better auto-save mechanism", "更好的自动存档机制");
I18N.Add("Better auto-save mechanism tips", "Auto saves are stored in 'Save\\AutoSaves' folder, filenames are combined with cluster address and date-time", "自动存档会以星区地址和日期时间组合为文件名存储在'Save\\AutoSaves'文件夹中"); I18N.Add("Better auto-save mechanism tips", "Auto saves are stored in 'Save\\AutoSaves' folder, filenames are combined with cluster address and date-time", "自动存档会以星区地址和日期时间组合为文件名存储在'Save\\AutoSaves'文件夹中");
*/ */
I18N.Add("Convert old saves to Combat Mode on loading", "Convert old saves to Combat Mode on loading (Use settings in new game panel)", "读取旧档时转为战斗模式(使用新游戏面板的战斗难度设置)"); I18N.Add("Convert old saves to Combat Mode on loading", "Convert old saves to Combat Mode on loading (Use settings in new game panel)", "读取旧档时转为战斗模式(使用新游戏面板的战斗难度设置)");
I18N.Add("Profile-based save folder", "Mod manager profile based save folder", "基于mod管理器配置档案名的存档文件夹");
I18N.Add("Profile-based save folder tips", "Save files are stored in 'Save\\<ProfileName>' folder.\nWill use original save location if matching default profile name",
"存档文件会存储在'Save\\<ProfileName>'文件夹中\n如果匹配默认配置档案名则使用原始存档位置");
I18N.Add("Profile-based option", "Mod manager profile based option", "基于mod管理器配置档案名的选项设置");
I18N.Add("Profile-based option tips", "Options are stored in 'Option\\<ProfileName>.xml'.\nWill use original location if matching default profile name",
"配置选项会存储在'Option\\<ProfileName>.xml'里\n如果匹配默认配置档案名则使用原始位置");
I18N.Add("Default profile name", "Default profile name", "默认配置档案名");
I18N.Add("Logical Frame Rate", "Logical Frame Rate", "逻辑帧倍率");
I18N.Add("Reset", "Reset", "重置");
I18N.Add("Process priority", "Process priority", "进程优先级");
I18N.Add("High", "High", "高");
I18N.Add("Above Normal", "Above Normal", "高于正常");
I18N.Add("Normal", "Normal", "正常");
I18N.Add("Below Normal", "Below Normal", "低于正常");
I18N.Add("Idle", "Idle", "空闲");
I18N.Add("Enabled CPUs", "Enabled CPU Threads", "使用CPU线程");
I18N.Add("All CPUs", "All CPUs", "所有CPU");
I18N.Add("First {0} CPUs", "First {0} CPUs", "前{0}个CPU");
I18N.Add("First 8 CPUs", "First 8 CPUs", "前8个CPU");
I18N.Add("First CPU only", "First CPU only", "仅第一个CPU");
I18N.Add("All P-Cores", "All P-Cores", "所有性能(P)核心");
I18N.Add("All E-Cores", "All E-Cores", "所有能效(E)核心");
I18N.Add("CPU Info", "CPU Info", "CPU信息");
I18N.Add("Unlimited interactive range", "Unlimited interactive range", "无限交互距离"); I18N.Add("Unlimited interactive range", "Unlimited interactive range", "无限交互距离");
I18N.Add("Night Light", "Sunlight at night", "夜间日光灯"); I18N.Add("Night Light", "Sunlight at night", "夜间日光灯");
I18N.Add("Angle X:", "Angle X:", "入射角度X:");
I18N.Add("Remove some build conditions", "Remove some build conditions", "移除部分不影响游戏逻辑的建造条件"); I18N.Add("Remove some build conditions", "Remove some build conditions", "移除部分不影响游戏逻辑的建造条件");
I18N.Add("Remove build range limit", "Remove build count and range limit", "移除建造数量和距离限制"); I18N.Add("Remove build range limit", "Remove build count and range limit", "移除建造数量和距离限制");
I18N.Add("Larger area for upgrade and dismantle", "Larger area for upgrade and dismantle", "范围升级和拆除的最大区域扩大"); I18N.Add("Larger area for upgrade and dismantle", "Larger area for upgrade and dismantle", "范围升级和拆除的最大区域扩大");
I18N.Add("Larger area for terraform", "Larger area for terraform", "范围铺设地基的最大区域扩大"); I18N.Add("Larger area for terraform", "Larger area for terraform", "范围铺设地基的最大区域扩大");
I18N.Add("Off-grid building and stepped rotation", "Off-grid building and stepped rotation (Hold Shift)", "脱离网格建造以及小角度旋转(按住Shift)"); I18N.Add("Off-grid building and stepped rotation", "Off-grid building and stepped rotation (Hold Shift)", "脱离网格建造以及小角度旋转(按住Shift)");
I18N.Add("Enable player actions in globe view", "Enable player actions in globe view", "在行星视图中允许玩家操作"); I18N.Add("Enable player actions in globe view", "Enable player actions in globe view", "在行星视图中允许玩家操作");
I18N.Add("Hide tips for soil piles changes", "Hide tips for soil piles changes", "隐藏沙土数量变动的提示"); I18N.Add("Enhance control for logistic storage limits", "Enhance control for logistic storage limits", "物流塔存储限制控制改进");
I18N.Add("Enhance control for logistic storage limits tips", "Logistic storage limits are not scaled on upgrading 'Logistics Carrier Capacity', if they are not set to maximum capacity.\nUse arrow keys to adjust logistic storage limits:\n \u2190/\u2192: -/+10 \u2193\u2191: -/+100", "当升级'运输机舱扩容'时,不会对各种物流塔的存储限制按比例提升,除非设置为最大允许容量。\n你可以使用方向键微调物流塔存储限制\n \u2190\u2192: -/+10 \u2193\u2191: -/+100");
I18N.Add("Enhanced count control for hand-make", "Enhanced count control for hand-make", "手动制造物品的数量控制改进"); I18N.Add("Enhanced count control for hand-make", "Enhanced count control for hand-make", "手动制造物品的数量控制改进");
I18N.Add("Enhanced count control for hand-make tips", "Maximum count is increased to 1000.\nHold Ctrl/Shift/Alt to change the count rapidly.", "最大数量提升至1000\n按住Ctrl/Shift/Alt可快速改变数量"); I18N.Add("Enhanced count control for hand-make tips", "Maximum count is increased to 1000.\nHold Ctrl/Shift/Alt to change the count rapidly.", "最大数量提升至1000\n按住Ctrl/Shift/Alt可快速改变数量");
I18N.Add("Quick build and dismantle stacking labs", "Quick build and dismantle stacking labs/storages/tanks(hold shift)", "快速建造和拆除堆叠研究站/储物仓/储液罐(按住shift)");
I18N.Add("Fast fill in to and take out from tanks", "Fast fill in to and take out from tanks", "储液罐快速注入和抽取液体");
I18N.Add("Speed Ratio", "Speed Ratio", "速度倍率");
I18N.Add("Cut conveyor belt (with shortcut key)", "Cut conveyor belt (with shortcut key)", "切割传送带(使用快捷键)");
I18N.Add("Protect veins from exhaustion", "Protect veins from exhaustion", "保护矿脉不会耗尽");
I18N.Add("Protect veins from exhaustion tips",
"By default, the vein amount is protected at 100, and oil speed is protected at 1.0/s, you can set them yourself in config file.\nWhen reach the protection value, veins/oils steeps will not be mined/extracted any longer.\nClose this function to resume mining and pumping, usually when you have enough level on `Veins Utilization`",
"默认矿脉数量保护于剩余100采油速保护于速度1.0/s你可以在配置文件中自行设置。\n当达到保护值时矿脉和油井将不再被开采。\n关闭此功能以恢复开采一般是当你在`矿物利用`上有足够的等级时。\n");
I18N.Add("Do not render factory entities", "Do not render factory entities (except belts and sorters)", "不渲染工厂建筑实体(除了传送带和分拣器)");
I18N.Add("Drag building power poles in maximum connection range", "Drag building power poles in maximum connection range", "拖动建造电线杆时自动使用最大连接距离间隔");
I18N.Add("Build Tesla Tower and Wireless Power Tower alternately", "Build Tesla Tower and Wireless Power Tower alternately", "交替建造电力感应塔和无线输电塔");
I18N.Add("Belt signals for buy out dark fog items automatically", "Belt signals for buy out dark fog items automatically", "用于自动购买黑雾物品的传送带信号");
I18N.Add("Auto-config logistic stations", "Auto-config logistic stations", "自动配置物流设施");
I18N.Add("Dispenser", "Logistics Distributor", "物流配送器");
I18N.Add("PLS", "PLS", "行星物流站");
I18N.Add("ILS", "ILS", "星际物流站");
I18N.Add("Advanced Mining Machine", "Advanced Mining Machine", "大型采矿机");
I18N.Add("Max. Charging Power", "Max. Charging Power", "最大充能功率");
I18N.Add("Count of Bots auto-filled", "Count of Bots auto-filled", "自动填充的配送机数量");
I18N.Add("Max. Charging Power", "Max. Charging Power", "最大充能功率");
I18N.Add("Drone transport range", "Drone transport range", "运输机最远路程");
I18N.Add("Min. Load of Drones", "Min. Load of Drones", "运输机起送量");
I18N.Add("Outgoing integration count", "Outgoing integration count", "输出货物集装数量");
I18N.Add("Count of Drones auto-filled", "Count of Drones auto-filled", "自动填充的运输机数量");
I18N.Add("Max. Charging Power", "Max. Charging Power", "最大充能功率");
I18N.Add("Drone transport range", "Drone transport range", "运输机最远路程");
I18N.Add("Vessel transport range", "Vessel transport range", "运输船最远路程");
I18N.Add("Warp distance", "Warp distance", "曲速启用路程");
I18N.Add("Min. Load of Drones", "Min. Load of Drones", "运输机起送量");
I18N.Add("Min. Load of Vessels", "Min. Load of Vessels", "运输船起送量");
I18N.Add("Outgoing integration count", "Outgoing integration count", "输出货物集装数量");
I18N.Add("Include Orbital Collector", "Include Orbital Collector", "包含轨道采集器");
I18N.Add("Warpers required", "Warpers required", "翘曲器必备");
I18N.Add("Count of Drones auto-filled", "Count of Drones auto-filled", "自动填充的运输机数量");
I18N.Add("Count of Vessels auto-filled", "Count of Vessels auto-filled", "自动填充的运输船数量");
I18N.Add("Collecting Speed", "Collecting Speed", "开采速度");
I18N.Add("Min. Piler Value", "Outgoing integration count", "输出货物集装数量");
I18N.Add("Allow overflow for Logistic Stations and Advanced Mining Machines", "Allow overflow for Logistic Stations and Advanced Mining Machines", "允许物流站和大型采矿机物品溢出");
I18N.Add("Enhance control for logistic storage limits", "Enhance control for logistic storage limits", "物流塔存储限制控制改进");
I18N.Add("Enhance control for logistic storage limits tips",
"Logistic storage limits are not scaled on upgrading 'Logistics Carrier Capacity', if they are not set to maximum capacity.\nUse arrow keys to adjust logistic storage limits:\n \u2190/\u2192: -/+10 \u2193\u2191: -/+100",
"当升级'运输机舱扩容'时,不会对各种物流塔的存储限制按比例提升,除非设置为最大允许容量。\n你可以使用方向键微调物流塔存储限制\n \u2190\u2192: -/+10 \u2193\u2191: -/+100");
I18N.Add("Logistics Control Panel Improvement", "Logistics Control Panel Improvement", "物流控制面板改进");
I18N.Add("Logistics Control Panel Improvement tips",
"Auto apply filter with item under mouse cursor while opening the panel\nQuick-set item filter while right-clicking item icons in storage list on the panel",
"打开面板时自动将鼠标指向物品设为筛选条件\n在控制面板物流塔列表中右键点击物品图标快速设置为筛选条件");
I18N.Add("Real-time logistic stations info panel", "Real-time logistic stations info panel", "物流运输站实时信息面板");
I18N.Add("Show status bars for storage items", "Show status bars for storage items", "显示存储物品状态条");
I18N.Add("Tweak building buffers", "Tweak building buffers", "调整建筑输入缓冲");
I18N.Add("Assembler buffer time multiplier(in seconds)", "Assembler buffer time multiplier(in seconds)", "工厂配方缓冲时间倍率(秒)");
I18N.Add("Assembler buffer minimum multiplier", "Assembler buffer minimum multiplier", "工厂配方缓冲最小倍率");
I18N.Add("Buffer count for assembling in labs", "Buffer count for assembling in labs", "研究站矩阵合成模式缓存数量");
I18N.Add("Extra buffer count for Self-evolution Labs", "Extra buffer count for Self-evolution Labs", "自演化研究站矩阵额外缓冲数量");
I18N.Add("Buffer count for researching in labs", "Buffer count for researching in labs", "研究站科研模式缓存数量");
I18N.Add("Ray Receiver Graviton Lens buffer count", "Ray Receiver Graviton Lens buffer count", "射线接收器透镜缓冲数量");
I18N.Add("Dismantle blueprint selected buildings", "Dismantle blueprint selected buildings", "拆除蓝图选中的建筑");
I18N.Add("Shortcut keys for showing stars' name", "Shortcut keys for showing stars' name", "启用显示所有星系名称的快捷键");
I18N.Add("Auto navigation on sailings", "Auto navigation on sailings", "宇宙航行时自动导航");
I18N.Add("Enable auto-cruise", "Enable auto-cruise", "启用自动巡航");
I18N.Add("Auto boost", "Auto boost", "自动加速");
I18N.Add("Distance to use warp", "Distance to use warp (AU)", "使用曲速的距离(AU)");
I18N.Add("Treat stack items as single in monitor components", "Treat stack items as single in monitor components", "在流速计中将堆叠物品视为单个物品");
I18N.Add("Initialize This Planet", "Initialize this planet", "初始化本行星"); I18N.Add("Initialize This Planet", "Initialize this planet", "初始化本行星");
I18N.Add("Initialize This Planet Confirm", "This operation will destroy all buildings and revert terrains on this planet, are you sure?", "此操作将会摧毁本行星上的所有建筑并恢复地形,确定吗?"); I18N.Add("Initialize This Planet Confirm", "This operation will destroy all buildings and revert terrains on this planet, are you sure?", "此操作将会摧毁本行星上的所有建筑并恢复地形,确定吗?");
I18N.Add("Dismantle All Buildings", "Dismantle all buildings", "拆除所有建筑"); I18N.Add("Dismantle All Buildings", "Dismantle all buildings", "拆除所有建筑");
@@ -140,143 +43,17 @@ public static class UIConfigWindow
I18N.Add("Maximum count to build", "Maximum count to build", "最大建造数量"); I18N.Add("Maximum count to build", "Maximum count to build", "最大建造数量");
I18N.Add("max", "max", "最大"); I18N.Add("max", "max", "最大");
I18N.Add("Stop ejectors when available nodes are all filled up", "Stop ejectors when available nodes are all filled up", "可用节点全部造完时停止弹射"); I18N.Add("Stop ejectors when available nodes are all filled up", "Stop ejectors when available nodes are all filled up", "可用节点全部造完时停止弹射");
I18N.Add("Construct only structure points but frames", "Construct only structure points but frames", "只造节点不造框架"); I18N.Add("Construct only nodes but frames", "Construct only nodes but frames", "只造节点不造框架");
I18N.Add("Initialize Dyson Sphere", "Initialize Dyson Sphere", "初始化戴森球"); I18N.Add("Initialize Dyson Sphere", "Initialize Dyson Sphere", "初始化戴森球");
I18N.Add("Initialize Dyson Sphere Confirm", "This operation will destroy all layers on this dyson sphere, are you sure?", "此操作将会摧毁戴森球上的所有层级,确定吗?"); I18N.Add("Initialize Dyson Sphere Confirm", "This operation will destroy all layers on this dyson sphere, are you sure?", "此操作将会摧毁戴森球上的所有层级,确定吗?");
I18N.Add("Click to dismantle selected layer", "Click to dismantle selected layer", "点击拆除对应的戴森壳"); I18N.Add("Click to dismantle selected layer", "Click to dismantle selected layer", "点击拆除对应的戴森壳");
I18N.Add("Dismantle selected layer", "Dismantle selected layer", "拆除选中的戴森壳"); I18N.Add("Dismantle selected layer", "Dismantle selected layer", "拆除选中的戴森壳");
I18N.Add("Dismantle selected layer Confirm", "This operation will dismantle selected layer, are you sure?", "此操作将会拆除选中的戴森壳,确定吗?"); I18N.Add("Dismantle selected layer Confirm", "This operation will dismantle selected layer, are you sure?", "此操作将会拆除选中的戴森壳,确定吗?");
I18N.Add("Auto Fast Build Speed Multiplier", "Auto Fast Build Speed Multiplier", "自动快速建造速度倍率");
I18N.Add("Restore upgrades of \"Sorter Cargo Stacking\" on panel", "Restore upgrades of \"Sorter Cargo Stacking\" on panel", "在升级面板上恢复\"分拣器货物叠加\"的升级");
I18N.Add("Buy out techs with their prerequisites", "Buy out techs with their prerequisites", "购买科技也同时购买所有前置科技");
I18N.Add("Set \"Sorter Cargo Stacking\" to unresearched state", "Set \"Sorter Cargo Stacking\" to unresearched state", "将\"分拣器货物叠加\"设为未研究状态");
I18N.Add("Open Dark Fog Communicator", "Open Dark Fog Communicator", "打开黑雾通讯器");
I18N.Apply(); I18N.Apply();
MyConfigWindow.OnUICreated += CreateUI; MyConfigWindow.OnUICreated += CreateUI;
MyConfigWindow.OnUpdateUI += UpdateUI; MyConfigWindow.OnUpdateUI += UpdateUI;
} }
private class OcMapper : MyWindow.ValueMapper<int>
{
public override int Min => 0;
public override int Max => 40;
public override string FormatValue(string format, int value)
{
return value == 0 ? "max".Translate() : base.FormatValue(format, value);
}
}
private class AngleMapper : MyWindow.ValueMapper<float>
{
public override int Min => 0;
public override int Max => 20;
public override float IndexToValue(int index) => index - 10f;
public override int ValueToIndex(float value) => Mathf.RoundToInt(value + 10f);
}
private class DistanceMapper : MyWindow.ValueMapper<double>
{
public override int Min => 1;
public override int Max => 40;
public override double IndexToValue(int index) => index * 0.5;
public override int ValueToIndex(double value) => Mathf.RoundToInt((float)(value * 2.0));
}
private class UpsMapper : MyWindow.ValueMapper<double>
{
public override int Min => 1;
public override int Max => 100;
public override double IndexToValue(int index) => index * 0.1;
public override int ValueToIndex(double value) => Mathf.RoundToInt((float)(value * 10.0));
}
private class AutoConfigDispenserChargePowerMapper() : MyWindow.RangeValueMapper<int>(3, 30)
{
public override string FormatValue(string format, int value)
{
var sb = new StringBuilder(" ");
StringBuilderUtility.WriteKMG(sb, 8, (long)value * 300000, false);
sb.Append('W');
return sb.ToString().Trim();
}
}
private class AutoConfigPLSChargePowerMapper() : MyWindow.RangeValueMapper<int>(2, 20)
{
public override string FormatValue(string format, int value)
{
var sb = new StringBuilder(" ");
StringBuilderUtility.WriteKMG(sb, 8, (long)value * 3000000L, false);
sb.Append('W');
return sb.ToString().Trim();
}
}
private class AutoConfigCarrierMinDeliverMapper() : MyWindow.RangeValueMapper<int>(0, 10)
{
public override string FormatValue(string format, int value)
{
return (value == 0 ? 1 : (value * 10)).ToString("0\\%");
}
}
private class AutoConfigMinPilerValueMapper() : MyWindow.RangeValueMapper<int>(0, 4)
{
public override string FormatValue(string format, int value)
{
return value == 0 ? "集装使用科技上限".Translate().Trim() : value.ToString();
}
}
private class AutoConfigILSChargePowerMapper() : MyWindow.RangeValueMapper<int>(2, 20)
{
public override string FormatValue(string format, int value)
{
var sb = new StringBuilder(" ");
StringBuilderUtility.WriteKMG(sb, 8, (long)value * 15000000L, false);
sb.Append('W');
return sb.ToString().Trim();
}
}
private class AutoConfigILSMaxTripShipMapper() : MyWindow.RangeValueMapper<int>(1, 41)
{
public override string FormatValue(string format, int value)
{
return value switch
{
<= 20 => value.ToString("0LY"),
<= 40 => (value * 2 - 20).ToString("0LY"),
_ => "∞",
};
}
}
private class AutoConfigILSWarperDistanceMapper() : MyWindow.RangeValueMapper<int>(2, 21)
{
public override string FormatValue(string format, int value)
{
return value switch
{
<= 7 => (value * 0.5 - 0.5).ToString("0.0AU"),
<= 13 => (value - 4.0).ToString("0.0AU"),
<= 16 => (value - 4).ToString("0AU"),
<= 20 => (value * 2 - 20).ToString("0AU"),
_ => "60AU",
};
}
}
private class AutoConfigVeinCollectorHarvestSpeedMapper() : MyWindow.RangeValueMapper<int>(0, 20)
{
public override string FormatValue(string format, int value)
{
return (100 + value * 10).ToString("0\\%");
}
}
private static void CreateUI(MyConfigWindow wnd, RectTransform trans) private static void CreateUI(MyConfigWindow wnd, RectTransform trans)
{ {
_windowTrans = trans; _windowTrans = trans;
@@ -284,419 +61,111 @@ public static class UIConfigWindow
var tab1 = wnd.AddTab(trans, "General"); var tab1 = wnd.AddTab(trans, "General");
var x = 0f; var x = 0f;
var y = 10f; var y = 10f;
wnd.AddCheckBox(x, y, tab1, GamePatch.EnableWindowResizeEnabled, "Enable game window resize"); MyCheckBox.CreateCheckBox(x, y, tab1, GamePatch.EnableWindowResizeEnabled, "Enable game window resize");
y += 36f; y += 36f;
wnd.AddCheckBox(x, y, tab1, GamePatch.LoadLastWindowRectEnabled, "Remeber window position and size on last exit"); MyCheckBox.CreateCheckBox(x, y, tab1, GamePatch.LoadLastWindowRectEnabled, "Remeber window position and size on last exit");
y += 36f; y += 36f;
var txt = wnd.AddText2(x + 2f, y, tab1, "Scale up mouse cursor", 15, "text-scale-up-mouse-cursor");
wnd.AddSlider(x + txt.preferredWidth + 7f, y + 6f, tab1, GamePatch.MouseCursorScaleUpMultiplier, [1, 2, 3, 4], "0x", 100f);
/* /*
y += 30f; MyCheckBox.CreateCheckBox(x, y, tab1, GamePatch.AutoSaveOptEnabled, "Better auto-save mechanism");
wnd.AddCheckBox(x, y, tab1, GamePatch.AutoSaveOptEnabled, "Better auto-save mechanism");
x = 200f; x = 200f;
y += 6f; y += 6f;
wnd.AddTipsButton2(x, y, tab1, "Better auto-save mechanism", "Better auto-save mechanism tips", "auto-save-opt-tips"); MyWindow.AddTipsButton(x, y, tab1, "Better auto-save mechanism", "Better auto-save mechanism tips", "auto-save-opt-tips");
x = 0f; x = 0f;
y += 30f;
*/ */
y += 36f; MyCheckBox.CreateCheckBox(x, y, tab1, GamePatch.ConvertSavesFromPeaceEnabled, "Convert old saves to Combat Mode on loading");
wnd.AddCheckBox(x, y, tab1, GamePatch.ConvertSavesFromPeaceEnabled, "Convert old saves to Combat Mode on loading"); x += 10f;
MyCheckBox checkBoxForMeasureTextWidth; y = 278f;
if (WindowFunctions.ProfileName != null) MyKeyBinder.CreateKeyBinder(x, y, tab1, UXAssist.Hotkey, "Hotkey");
{ var tab2 = wnd.AddTab(trans, "Planet/Factory");
y += 36f;
checkBoxForMeasureTextWidth = wnd.AddCheckBox(x, y, tab1, GamePatch.ProfileBasedSaveFolderEnabled, "Profile-based save folder");
wnd.AddTipsButton2(checkBoxForMeasureTextWidth.Width + 5f, y + 6f, tab1, "Profile-based save folder", "Profile-based save folder tips", "btn-profile-based-save-folder-tips");
y += 36f;
checkBoxForMeasureTextWidth = wnd.AddCheckBox(x, y, tab1, GamePatch.ProfileBasedOptionEnabled, "Profile-based option");
wnd.AddTipsButton2(checkBoxForMeasureTextWidth.Width + 5f, y + 6f, tab1, "Profile-based option", "Profile-based option tips", "btn-profile-based-option-tips");
y += 36f;
wnd.AddText2(x + 2f, y, tab1, "Default profile name", 15, "text-default-profile-name");
y += 24f;
wnd.AddInputField(x + 2f, y, 200f, tab1, GamePatch.DefaultProfileName, 15, "input-profile-save-folder");
y += 18f;
}
if (!BulletTimeWrapper.HasBulletTime)
{
y += 36f;
txt = wnd.AddText2(x + 2f, y, tab1, "Logical Frame Rate", 15, "game-frame-rate");
x += txt.preferredWidth + 7f;
wnd.AddSlider(x, y + 6f, tab1, GamePatch.GameUpsFactor, new UpsMapper(), "0.0x", 100f).WithSmallerHandle();
var btn = wnd.AddFlatButton(x + 104f, y + 6f, tab1, "Reset", 13, "reset-game-frame-rate", () => GamePatch.GameUpsFactor.Value = 1.0f);
((RectTransform)btn.transform).sizeDelta = new Vector2(40f, 20f);
x = 0f;
}
y += 36f;
wnd.AddComboBox(x + 2f, y, tab1, "Process priority").WithItems("High", "Above Normal", "Normal", "Below Normal", "Idle").WithSize(100f, 0f).WithConfigEntry(WindowFunctions.ProcessPriority);
var details = WindowFunctions.ProcessorDetails;
string[] affinities;
if (details.HybridArchitecture)
{
affinities = new string[5];
affinities[3] = "All P-Cores";
affinities[4] = "All E-Cores";
}
else
{
affinities = new string[3];
}
affinities[0] = "All CPUs";
affinities[1] = string.Format("First {0} CPUs".Translate(), details.ThreadCount / 2);
affinities[2] = details.ThreadCount > 16 ? "First 8 CPUs" : "First CPU only";
y += 36f;
wnd.AddComboBox(x + 2f, y, tab1, "Enabled CPUs").WithItems(affinities).WithSize(200f, 0f).WithConfigEntry(WindowFunctions.ProcessAffinity);
y += 36f;
((RectTransform)wnd.AddButton(x, y, tab1, "CPU Info", 16, "button-show-cpu-info", WindowFunctions.ShowCPUInfo).transform).sizeDelta = new Vector2(100f, 25f);
var tab2 = wnd.AddTab(trans, "Factory");
x = 0f; x = 0f;
y = 10f; y = 10f;
wnd.AddCheckBox(x, y, tab2, FactoryPatch.RemoveSomeConditionEnabled, "Remove some build conditions"); MyCheckBox.CreateCheckBox(x, y, tab2, FactoryPatch.UnlimitInteractiveEnabled, "Unlimited interactive range");
y += 36f; y += 36f;
wnd.AddCheckBox(x, y, tab2, FactoryPatch.RemoveBuildRangeLimitEnabled, "Remove build range limit"); MyCheckBox.CreateCheckBox(x, y, tab2, FactoryPatch.RemoveSomeConditionEnabled, "Remove some build conditions");
y += 36f; y += 36f;
checkBoxForMeasureTextWidth = wnd.AddCheckBox(x, y, tab2, FactoryPatch.NightLightEnabled, "Night Light"); MyCheckBox.CreateCheckBox(x, y, tab2, FactoryPatch.RemoveBuildRangeLimitEnabled, "Remove build range limit");
x += checkBoxForMeasureTextWidth.Width + 5f + 10f;
txt = wnd.AddText2(x, y + 2f, tab2, "Angle X:", 13, "text-nightlight-angle-x");
x += txt.preferredWidth + 5f;
wnd.AddSlider(x, y + 7f, tab2, FactoryPatch.NightLightAngleX, new AngleMapper(), "0", 60f).WithSmallerHandle();
x += 70f;
txt = wnd.AddText2(x, y + 2f, tab2, "Y:", 13, "text-nightlight-angle-y");
wnd.AddSlider(x + txt.preferredWidth + 5f, y + 7f, tab2, FactoryPatch.NightLightAngleY, new AngleMapper(), "0", 60f).WithSmallerHandle();
x = 0;
y += 36f; y += 36f;
wnd.AddCheckBox(x, y, tab2, FactoryPatch.LargerAreaForUpgradeAndDismantleEnabled, "Larger area for upgrade and dismantle"); MyCheckBox.CreateCheckBox(x, y, tab2, FactoryPatch.NightLightEnabled, "Night Light");
y += 36f; y += 36f;
wnd.AddCheckBox(x, y, tab2, FactoryPatch.LargerAreaForTerraformEnabled, "Larger area for terraform"); MyCheckBox.CreateCheckBox(x, y, tab2, FactoryPatch.LargerAreaForUpgradeAndDismantleEnabled, "Larger area for upgrade and dismantle");
y += 36f; y += 36f;
wnd.AddCheckBox(x, y, tab2, FactoryPatch.OffGridBuildingEnabled, "Off-grid building and stepped rotation"); MyCheckBox.CreateCheckBox(x, y, tab2, FactoryPatch.LargerAreaForTerraformEnabled, "Larger area for terraform");
y += 36f; y += 36f;
wnd.AddCheckBox(x, y, tab2, FactoryPatch.CutConveyorBeltEnabled, "Cut conveyor belt (with shortcut key)"); MyCheckBox.CreateCheckBox(x, y, tab2, FactoryPatch.OffGridBuildingEnabled, "Off-grid building and stepped rotation");
y += 36f; y += 36f;
checkBoxForMeasureTextWidth = wnd.AddCheckBox(x, y, tab2, FactoryPatch.TreatStackingAsSingleEnabled, "Treat stack items as single in monitor components"); MyCheckBox.CreateCheckBox(x, y, tab2, PlanetPatch.PlayerActionsInGlobeViewEnabled, "Enable player actions in globe view");
y += 36f; y += 36f;
wnd.AddCheckBox(x, y, tab2, FactoryPatch.QuickBuildAndDismantleLabsEnabled, "Quick build and dismantle stacking labs"); MyCheckBox.CreateCheckBox(x, y, tab2, FactoryPatch.LogisticsCapacityTweaksEnabled, "Enhance control for logistic storage limits");
x = 270f;
{ y += 6f;
y += 36f; MyWindow.AddTipsButton(x, y, tab2, "Enhance control for logistic storage limits", "Enhance control for logistic storage limits tips", "enhanced-logistic-limit-tips");
var cb = wnd.AddCheckBox(x, y, tab2, FactoryPatch.TankFastFillInAndTakeOutEnabled, "Fast fill in to and take out from tanks"); x = 0f;
x += cb.Width + 5f; y += 30f;
txt = wnd.AddText2(x, y + 2f, tab2, "Speed Ratio", 13, "text-tank-fast-fill-speed-ratio"); MyCheckBox.CreateCheckBox(x, y, tab2, PlayerPatch.EnhancedMechaForgeCountControlEnabled, "Enhanced count control for hand-make");
var tankSlider = wnd.AddSlider(x + txt.preferredWidth + 5f, y + 7f, tab2, FactoryPatch.TankFastFillInAndTakeOutMultiplier, [2, 5, 10, 20, 50, 100, 500, 1000], "G", 100f).WithSmallerHandle(); x = 270f;
FactoryPatch.TankFastFillInAndTakeOutEnabled.SettingChanged += TankSettingChanged; y += 6f;
wnd.OnFree += () => { FactoryPatch.TankFastFillInAndTakeOutEnabled.SettingChanged -= TankSettingChanged; }; MyWindow.AddTipsButton(x, y, tab2, "Enhanced count control for hand-make", "Enhanced count control for hand-make tips", "enhanced-count-control-tips");
TankSettingChanged(null, null);
void TankSettingChanged(object o, EventArgs e)
{
tankSlider.SetEnable(FactoryPatch.TankFastFillInAndTakeOutEnabled.Value);
}
}
x = 0;
y += 36f;
wnd.AddCheckBox(x, y, tab2, FactoryPatch.DoNotRenderEntitiesEnabled, "Do not render factory entities");
y += 36f;
wnd.AddCheckBox(x, y, tab2, FactoryPatch.DismantleBlueprintSelectionEnabled, "Dismantle blueprint selected buildings");
y += 36f;
wnd.AddCheckBox(x, y, tab2, FactoryPatch.BeltSignalsForBuyOutEnabled, "Belt signals for buy out dark fog items automatically");
y += 36f;
checkBoxForMeasureTextWidth = wnd.AddCheckBox(x, y, tab2, FactoryPatch.ProtectVeinsFromExhaustionEnabled, "Protect veins from exhaustion");
wnd.AddTipsButton2(x + checkBoxForMeasureTextWidth.Width + 5f, y + 6f, tab2, "Protect veins from exhaustion", "Protect veins from exhaustion tips", "protect-veins-tips");
{
y += 36f;
wnd.AddCheckBox(x, y, tab2, FactoryPatch.DragBuildPowerPolesEnabled, "Drag building power poles in maximum connection range");
y += 27f;
var alternatelyCheckBox = wnd.AddCheckBox(x + 20f, y, tab2, FactoryPatch.DragBuildPowerPolesAlternatelyEnabled, "Build Tesla Tower and Wireless Power Tower alternately", 13);
FactoryPatch.DragBuildPowerPolesEnabled.SettingChanged += AlternatelyCheckBoxChanged;
wnd.OnFree += () => { FactoryPatch.DragBuildPowerPolesEnabled.SettingChanged -= AlternatelyCheckBoxChanged; };
AlternatelyCheckBoxChanged(null, null);
void AlternatelyCheckBoxChanged(object o, EventArgs e)
{
alternatelyCheckBox.SetEnable(FactoryPatch.DragBuildPowerPolesEnabled.Value);
}
}
x = 400f; x = 400f;
y = 10f; y = 10f;
wnd.AddButton(x, y, tab2, "Initialize This Planet", 16, "button-init-planet", () => wnd.AddButton(x, y, tab2, "Initialize This Planet", 16, "button-init-planet", () =>
UIMessageBox.Show("Initialize This Planet".Translate(), "Initialize This Planet Confirm".Translate(), "取消".Translate(), "确定".Translate(), 2, null, UIMessageBox.Show("Initialize This Planet".Translate(), "Initialize This Planet Confirm".Translate(), "取消".Translate(), "确定".Translate(), 2, null, () =>
() => { PlanetFunctions.RecreatePlanet(true); }) {
PlanetFunctions.RecreatePlanet(true);
})
); );
y += 36f; y += 36f;
wnd.AddButton(x, y, tab2, "Dismantle All Buildings", 16, "button-dismantle-all", () => wnd.AddButton(x, y, tab2, "Dismantle All Buildings", 16, "button-dismantle-all", () =>
UIMessageBox.Show("Dismantle All Buildings".Translate(), "Dismantle All Buildings Confirm".Translate(), "取消".Translate(), "确定".Translate(), 2, null, UIMessageBox.Show("Dismantle All Buildings".Translate(), "Dismantle All Buildings Confirm".Translate(), "取消".Translate(), "确定".Translate(), 2, null, () =>
() => { PlanetFunctions.DismantleAll(false); }) {
PlanetFunctions.DismantleAll(false);
})
); );
y += 72f; y += 72f;
wnd.AddButton(x, y, 200, tab2, "Quick build Orbital Collectors", 16, "button-init-planet", PlanetFunctions.BuildOrbitalCollectors); wnd.AddButton(x, y, 200, tab2, "Quick build Orbital Collectors", 16, "button-init-planet", PlanetFunctions.BuildOrbitalCollectors);
x += 10f;
y += 30f; y += 30f;
txt = wnd.AddText2(x + 10f, y, tab2, "Maximum count to build", 15, "text-oc-build-count"); MyWindow.AddText(x, y, tab2, "Maximum count to build", 16, "text-oc-build-count");
wnd.AddSlider(x + 10f + txt.preferredWidth + 5f, y + 6f, tab2, PlanetFunctions.OrbitalCollectorMaxBuildCount, new OcMapper(), "G", 160f); y += 26f;
var sl0 = MySlider.CreateSlider(x, y, tab2, PlanetFunctions.OrbitalCollectorMaxBuildCount.Value, 0f, 40f, "G", 200f);
y += 18f; if (PlanetFunctions.OrbitalCollectorMaxBuildCount.Value == 0)
{ {
y += 36f; sl0.SetLabelText("max".Translate());
wnd.AddCheckBox(x, y, tab2, FactoryPatch.TweakBuildingBufferEnabled, "Tweak building buffers"); }
y += 27f; sl0.OnValueChanged += () =>
txt = wnd.AddText2(x + 20f, y, tab2, "Assembler buffer time multiplier(in seconds)", 13);
var nx1 = txt.preferredWidth + 5f;
y += 27f;
txt = wnd.AddText2(x + 20f, y, tab2, "Assembler buffer minimum multiplier", 13);
var nx2 = txt.preferredWidth + 5f;
y += 27f;
txt = wnd.AddText2(x + 20f, y, tab2, "Buffer count for assembling in labs", 13);
var nx3 = txt.preferredWidth + 5f;
y += 27f;
txt = wnd.AddText2(x + 20f, y, tab2, "Extra buffer count for Self-evolution Labs", 13);
var nx4 = txt.preferredWidth + 5f;
y += 27f;
txt = wnd.AddText2(x + 20f, y, tab2, "Buffer count for researching in labs", 13);
var nx5 = txt.preferredWidth + 5f;
y += 27f;
txt = wnd.AddText2(x + 20f, y, tab2, "Ray Receiver Graviton Lens buffer count", 13);
var nx6 = txt.preferredWidth + 5f;
y -= 135f;
var mx = Mathf.Max(nx1, nx2, nx3, nx4, nx5, nx6) + 20f;
var assemblerBufferTimeMultiplierSlider = wnd.AddSlider(x + mx, y + 5f, tab2, FactoryPatch.AssemblerBufferTimeMultiplier, new MyWindow.RangeValueMapper<int>(2, 10), "0", 80f).WithSmallerHandle();
y += 27f;
var assemblerBufferMininumMultiplierSlider = wnd.AddSlider(x + mx, y + 5f, tab2, FactoryPatch.AssemblerBufferMininumMultiplier, new MyWindow.RangeValueMapper<int>(2, 10), "0", 80f).WithSmallerHandle();
y += 27f;
var labBufferMaxCountForAssembleSlider = wnd.AddSlider(x + mx, y + 5f, tab2, FactoryPatch.LabBufferMaxCountForAssemble, new MyWindow.RangeValueMapper<int>(2, 20), "0", 80f).WithSmallerHandle();
y += 27f;
var labBufferExtraCountForAdvancedAssembleSlider = wnd.AddSlider(x + mx, y + 5f, tab2, FactoryPatch.LabBufferExtraCountForAdvancedAssemble, new MyWindow.RangeValueMapper<int>(1, 10), "0", 80f).WithSmallerHandle();
y += 27f;
var labBufferMaxCountForResearchSlider = wnd.AddSlider(x + mx, y + 5f, tab2, FactoryPatch.LabBufferMaxCountForResearch, new MyWindow.RangeValueMapper<int>(2, 20), "0", 80f).WithSmallerHandle();
y += 27f;
var receiverBufferCountSlider = wnd.AddSlider(x + mx, y + 5f, tab2, FactoryPatch.ReceiverBufferCount, new MyWindow.RangeValueMapper<int>(1, 20), "0", 80f).WithSmallerHandle();
FactoryPatch.TweakBuildingBufferEnabled.SettingChanged += TweakBuildingBufferChanged;
wnd.OnFree += () => { FactoryPatch.TweakBuildingBufferEnabled.SettingChanged -= TweakBuildingBufferChanged; };
TweakBuildingBufferChanged(null, null);
void TweakBuildingBufferChanged(object o, EventArgs e)
{ {
assemblerBufferTimeMultiplierSlider.SetEnable(FactoryPatch.TweakBuildingBufferEnabled.Value); PlanetFunctions.OrbitalCollectorMaxBuildCount.Value = Mathf.RoundToInt(sl0.Value);
assemblerBufferMininumMultiplierSlider.SetEnable(FactoryPatch.TweakBuildingBufferEnabled.Value); if (PlanetFunctions.OrbitalCollectorMaxBuildCount.Value == 0)
labBufferMaxCountForAssembleSlider.SetEnable(FactoryPatch.TweakBuildingBufferEnabled.Value); {
labBufferExtraCountForAdvancedAssembleSlider.SetEnable(FactoryPatch.TweakBuildingBufferEnabled.Value); sl0.SetLabelText("max".Translate());
labBufferMaxCountForResearchSlider.SetEnable(FactoryPatch.TweakBuildingBufferEnabled.Value);
receiverBufferCountSlider.SetEnable(FactoryPatch.TweakBuildingBufferEnabled.Value);
}
} }
};
var tab3 = wnd.AddTab(trans, "Logistics"); var tab3 = wnd.AddTab(trans, "Dyson Sphere");
x = 0f; x = 0f;
y = 10f; y = 10f;
MyCheckBox.CreateCheckBox(x, y, tab3, DysonSpherePatch.StopEjectOnNodeCompleteEnabled, "Stop ejectors when available nodes are all filled up");
checkBoxForMeasureTextWidth = wnd.AddCheckBox(x, y, tab3, LogisticsPatch.LogisticsCapacityTweaksEnabled, "Enhance control for logistic storage limits");
wnd.AddTipsButton2(x + checkBoxForMeasureTextWidth.Width + 5f, y + 6f, tab3, "Enhance control for logistic storage limits", "Enhance control for logistic storage limits tips", "enhanced-logistic-limit-tips");
y += 36f; y += 36f;
wnd.AddCheckBox(x, y, tab3, LogisticsPatch.AllowOverflowInLogisticsEnabled, "Allow overflow for Logistic Stations and Advanced Mining Machines"); MyCheckBox.CreateCheckBox(x, y, tab3, DysonSpherePatch.OnlyConstructNodesEnabled, "Construct only nodes but frames");
y += 36f;
checkBoxForMeasureTextWidth = wnd.AddCheckBox(x, y, tab3, LogisticsPatch.LogisticsConstrolPanelImprovementEnabled, "Logistics Control Panel Improvement");
wnd.AddTipsButton2(x + checkBoxForMeasureTextWidth.Width + 5f, y + 6f, tab3, "Logistics Control Panel Improvement", "Logistics Control Panel Improvement tips", "lcp-improvement-tips");
{
y += 36f;
var realtimeLogisticsInfoPanelCheckBox = wnd.AddCheckBox(x, y, tab3, LogisticsPatch.RealtimeLogisticsInfoPanelEnabled, "Real-time logistic stations info panel");
y += 27f;
var realtimeLogisticsInfoPanelBarsCheckBox = wnd.AddCheckBox(x + 20f, y, tab3, LogisticsPatch.RealtimeLogisticsInfoPanelBarsEnabled, "Show status bars for storage items", 13);
if (AuxilaryfunctionWrapper.ShowStationInfo != null)
{
AuxilaryfunctionWrapper.ShowStationInfo.SettingChanged += RealtimeLogisticsInfoPanelChanged;
wnd.OnFree += () => { AuxilaryfunctionWrapper.ShowStationInfo.SettingChanged -= RealtimeLogisticsInfoPanelChanged; };
}
LogisticsPatch.RealtimeLogisticsInfoPanelEnabled.SettingChanged += RealtimeLogisticsInfoPanelChanged;
wnd.OnFree += () => { LogisticsPatch.RealtimeLogisticsInfoPanelEnabled.SettingChanged -= RealtimeLogisticsInfoPanelChanged; };
RealtimeLogisticsInfoPanelChanged(null, null);
void RealtimeLogisticsInfoPanelChanged(object o, EventArgs e)
{
if (AuxilaryfunctionWrapper.ShowStationInfo == null)
{
realtimeLogisticsInfoPanelCheckBox.SetEnable(true);
realtimeLogisticsInfoPanelBarsCheckBox.SetEnable(LogisticsPatch.RealtimeLogisticsInfoPanelEnabled.Value);
return;
}
var on = !AuxilaryfunctionWrapper.ShowStationInfo.Value;
realtimeLogisticsInfoPanelCheckBox.SetEnable(on);
realtimeLogisticsInfoPanelBarsCheckBox.SetEnable(on & LogisticsPatch.RealtimeLogisticsInfoPanelEnabled.Value);
if (!on)
{
LogisticsPatch.RealtimeLogisticsInfoPanelEnabled.Value = false;
}
}
}
y += 36f;
wnd.AddCheckBox(x, y, tab3, LogisticsPatch.AutoConfigLogisticsEnabled, "Auto-config logistic stations");
y += 24f;
var maxWidth = 0f;
wnd.AddText2(10f, y, tab3, "Dispenser", 14, "text-dispenser");
y += 18f;
var oy = y;
x = 20f;
var textForMeasureTextWidth = wnd.AddText2(x, y, tab3, "Max. Charging Power", 13, "text-dispenser-max-charging-power");
maxWidth = Mathf.Max(maxWidth, textForMeasureTextWidth.preferredWidth);
y += 18f;
textForMeasureTextWidth = wnd.AddText2(x, y, tab3, "Count of Bots auto-filled", 13, "text-dispenser-count-of-bots-auto-filled");
maxWidth = Mathf.Max(maxWidth, textForMeasureTextWidth.preferredWidth);
y += 18f;
wnd.AddText2(10f, y, tab3, "PLS", 14, "text-pls");
y += 18f;
textForMeasureTextWidth = wnd.AddText2(x, y, tab3, "Max. Charging Power", 13, "text-pls-max-charging-power");
maxWidth = Mathf.Max(maxWidth, textForMeasureTextWidth.preferredWidth);
y += 18f;
textForMeasureTextWidth = wnd.AddText2(x, y, tab3, "Drone transport range", 13, "text-pls-drone-transport-range");
maxWidth = Mathf.Max(maxWidth, textForMeasureTextWidth.preferredWidth);
y += 18f;
textForMeasureTextWidth = wnd.AddText2(x, y, tab3, "Min. Load of Drones", 13, "text-pls-min-load-of-drones");
maxWidth = Mathf.Max(maxWidth, textForMeasureTextWidth.preferredWidth);
y += 18f;
textForMeasureTextWidth = wnd.AddText2(x, y, tab3, "Outgoing integration count", 13, "text-pls-outgoing-integration-count");
maxWidth = Mathf.Max(maxWidth, textForMeasureTextWidth.preferredWidth);
y += 18f;
textForMeasureTextWidth = wnd.AddText2(x, y, tab3, "Count of Drones auto-filled", 13, "text-pls-count-of-drones-auto-filled");
maxWidth = Mathf.Max(maxWidth, textForMeasureTextWidth.preferredWidth);
y += 18f;
wnd.AddText2(10f, y, tab3, "ILS", 14, "text-ils");
y += 18f;
textForMeasureTextWidth = wnd.AddText2(x, y, tab3, "Max. Charging Power", 13, "text-ils-max-charging-power");
maxWidth = Mathf.Max(maxWidth, textForMeasureTextWidth.preferredWidth);
y += 18f;
textForMeasureTextWidth = wnd.AddText2(x, y, tab3, "Drone transport range", 13, "text-ils-drone-transport-range");
maxWidth = Mathf.Max(maxWidth, textForMeasureTextWidth.preferredWidth);
y += 18f;
textForMeasureTextWidth = wnd.AddText2(x, y, tab3, "Vessel transport range", 13, "text-ils-vessel-transport-range");
maxWidth = Mathf.Max(maxWidth, textForMeasureTextWidth.preferredWidth);
wnd.AddCheckBox(x + 360f, y + 6f, tab3, LogisticsPatch.AutoConfigILSIncludeOrbitCollector, "Include Orbital Collector", 13).WithSmallerBox();
y += 18f;
textForMeasureTextWidth = wnd.AddText2(x, y, tab3, "Warp distance", 13, "text-ils-warp-distance");
maxWidth = Mathf.Max(maxWidth, textForMeasureTextWidth.preferredWidth);
wnd.AddCheckBox(x + 360f, y + 6f, tab3, LogisticsPatch.AutoConfigILSWarperNecessary, "Warpers required", 13).WithSmallerBox();
y += 18f;
textForMeasureTextWidth = wnd.AddText2(x, y, tab3, "Min. Load of Drones", 13, "text-ils-min-load-of-drones");
maxWidth = Mathf.Max(maxWidth, textForMeasureTextWidth.preferredWidth);
y += 18f;
textForMeasureTextWidth = wnd.AddText2(x, y, tab3, "Min. Load of Vessels", 13, "text-ils-min-load-of-vessels");
maxWidth = Mathf.Max(maxWidth, textForMeasureTextWidth.preferredWidth);
y += 18f;
textForMeasureTextWidth = wnd.AddText2(x, y, tab3, "Outgoing integration count", 13, "text-ils-outgoing-integration-count");
maxWidth = Mathf.Max(maxWidth, textForMeasureTextWidth.preferredWidth);
y += 18f;
textForMeasureTextWidth = wnd.AddText2(x, y, tab3, "Count of Drones auto-filled", 13, "text-ils-count-of-drones-auto-filled");
maxWidth = Mathf.Max(maxWidth, textForMeasureTextWidth.preferredWidth);
y += 18f;
textForMeasureTextWidth = wnd.AddText2(x, y, tab3, "Count of Vessels auto-filled", 13, "text-ils-count-of-vessels-auto-filled");
maxWidth = Mathf.Max(maxWidth, textForMeasureTextWidth.preferredWidth);
y += 18f;
wnd.AddText2(10f, y, tab3, "Advanced Mining Machine", 14, "text-amm");
y += 18f;
textForMeasureTextWidth = wnd.AddText2(x, y, tab3, "Collecting Speed", 13, "text-amm-collecting-speed");
maxWidth = Mathf.Max(maxWidth, textForMeasureTextWidth.preferredWidth);
y += 18f;
textForMeasureTextWidth = wnd.AddText2(x, y, tab3, "Min. Piler Value", 13, "text-amm-min-piler-value");
maxWidth = Mathf.Max(maxWidth, textForMeasureTextWidth.preferredWidth);
y = oy + 1;
var nx = x + maxWidth + 5f;
wnd.AddSideSlider(nx, y, tab3, LogisticsPatch.AutoConfigDispenserChargePower, new AutoConfigDispenserChargePowerMapper(), "G", 150f, -100f).WithFontSize(13);
y += 18f;
wnd.AddSideSlider(nx, y, tab3, LogisticsPatch.AutoConfigDispenserCourierCount, new MyWindow.RangeValueMapper<int>(0, 10), "G", 150f, -100f).WithFontSize(13);
y += 36f;
wnd.AddSideSlider(nx, y, tab3, LogisticsPatch.AutoConfigPLSChargePower, new AutoConfigPLSChargePowerMapper(), "G", 150f, -100f).WithFontSize(13);
y += 18f;
wnd.AddSideSlider(nx, y, tab3, LogisticsPatch.AutoConfigPLSMaxTripDrone, new MyWindow.RangeValueMapper<int>(1, 180), "0°", 150f, -100f).WithFontSize(13);
y += 18f;
wnd.AddSideSlider(nx, y, tab3, LogisticsPatch.AutoConfigPLSDroneMinDeliver, new AutoConfigCarrierMinDeliverMapper(), "G", 150f, -100f).WithFontSize(13);
y += 18f;
wnd.AddSideSlider(nx, y, tab3, LogisticsPatch.AutoConfigPLSMinPilerValue, new AutoConfigMinPilerValueMapper(), "G", 150f, -100f).WithFontSize(13);
y += 18f;
wnd.AddSideSlider(nx, y, tab3, LogisticsPatch.AutoConfigPLSDroneCount, new MyWindow.RangeValueMapper<int>(0, 50), "G", 150f, -100f).WithFontSize(13);
y += 36f;
wnd.AddSideSlider(nx, y, tab3, LogisticsPatch.AutoConfigILSChargePower, new AutoConfigILSChargePowerMapper(), "G", 150f, -100f).WithFontSize(13);
y += 18f;
wnd.AddSideSlider(nx, y, tab3, LogisticsPatch.AutoConfigILSMaxTripDrone, new MyWindow.RangeValueMapper<int>(1, 180), "0°", 150f, -100f).WithFontSize(13);
y += 18f;
wnd.AddSideSlider(nx, y, tab3, LogisticsPatch.AutoConfigILSMaxTripShip, new AutoConfigILSMaxTripShipMapper(), "G", 150f, -100f).WithFontSize(13);
y += 18f;
wnd.AddSideSlider(nx, y, tab3, LogisticsPatch.AutoConfigILSWarperDistance, new AutoConfigILSWarperDistanceMapper(), "G", 150f, -100f).WithFontSize(13);
y += 18f;
wnd.AddSideSlider(nx, y, tab3, LogisticsPatch.AutoConfigILSDroneMinDeliver, new AutoConfigCarrierMinDeliverMapper(), "G", 150f, -100f).WithFontSize(13);
y += 18f;
wnd.AddSideSlider(nx, y, tab3, LogisticsPatch.AutoConfigILSShipMinDeliver, new AutoConfigCarrierMinDeliverMapper(), "G", 150f, -100f).WithFontSize(13);
y += 18f;
wnd.AddSideSlider(nx, y, tab3, LogisticsPatch.AutoConfigILSMinPilerValue, new AutoConfigMinPilerValueMapper(), "G", 150f, -100f).WithFontSize(13);
y += 18f;
wnd.AddSideSlider(nx, y, tab3, LogisticsPatch.AutoConfigILSDroneCount, new MyWindow.RangeValueMapper<int>(0, 100), "G", 150f, -100f).WithFontSize(13);
y += 18f;
wnd.AddSideSlider(nx, y, tab3, LogisticsPatch.AutoConfigILSShipCount, new MyWindow.RangeValueMapper<int>(0, 10), "G", 150f, -100f).WithFontSize(13);
y += 36f;
wnd.AddSideSlider(nx, y, tab3, LogisticsPatch.AutoConfigVeinCollectorHarvestSpeed, new AutoConfigVeinCollectorHarvestSpeedMapper(), "G", 150f, -100f).WithFontSize(13);
y += 18f;
wnd.AddSideSlider(nx, y, tab3, LogisticsPatch.AutoConfigVeinCollectorMinPilerValue, new AutoConfigMinPilerValueMapper(), "G", 150f, -100f).WithFontSize(13);
x = 0f;
var tab4 = wnd.AddTab(trans, "Player/Mecha");
x = 0f;
y = 10f;
wnd.AddCheckBox(x, y, tab4, FactoryPatch.UnlimitInteractiveEnabled, "Unlimited interactive range");
y += 36f;
wnd.AddCheckBox(x, y, tab4, PlanetPatch.PlayerActionsInGlobeViewEnabled, "Enable player actions in globe view");
y += 36f;
wnd.AddCheckBox(x, y, tab4, PlayerPatch.HideTipsForSandsChangesEnabled, "Hide tips for soil piles changes");
y += 36f;
checkBoxForMeasureTextWidth = wnd.AddCheckBox(x, y, tab4, PlayerPatch.EnhancedMechaForgeCountControlEnabled, "Enhanced count control for hand-make");
wnd.AddTipsButton2(x + checkBoxForMeasureTextWidth.Width + 5f, y + 6f, tab4, "Enhanced count control for hand-make", "Enhanced count control for hand-make tips", "enhanced-count-control-tips");
y += 36f;
wnd.AddCheckBox(x, y, tab4, PlayerPatch.ShortcutKeysForStarsNameEnabled, "Shortcut keys for showing stars' name");
{
y += 36f;
wnd.AddCheckBox(x, y, tab4, PlayerPatch.AutoNavigationEnabled, "Auto navigation on sailings");
y += 27f;
var autoCruiseCheckBox = wnd.AddCheckBox(x + 20f, y, tab4, PlayerPatch.AutoCruiseEnabled, "Enable auto-cruise", 13);
y += 27f;
var autoBoostCheckBox = wnd.AddCheckBox(x + 20f, y, tab4, PlayerPatch.AutoBoostEnabled, "Auto boost", 13);
y += 27f;
txt = wnd.AddText2(x + 20f, y, tab4, "Distance to use warp", 15, "text-distance-to-warp");
var navDistanceSlider = wnd.AddSlider(x + 20f + txt.preferredWidth + 5f, y + 6f, tab4, PlayerPatch.DistanceToWarp, new DistanceMapper(), "0.0", 100f);
PlayerPatch.AutoNavigationEnabled.SettingChanged += NavSettingChanged;
wnd.OnFree += () => { PlayerPatch.AutoNavigationEnabled.SettingChanged -= NavSettingChanged; };
NavSettingChanged(null, null);
void NavSettingChanged(object o, EventArgs e)
{
autoCruiseCheckBox.SetEnable(PlayerPatch.AutoNavigationEnabled.Value);
autoBoostCheckBox.SetEnable(PlayerPatch.AutoNavigationEnabled.Value);
navDistanceSlider.SetEnable(PlayerPatch.AutoNavigationEnabled.Value);
}
}
var tab5 = wnd.AddTab(trans, "Dyson Sphere");
x = 0f;
y = 10f;
wnd.AddCheckBox(x, y, tab5, DysonSpherePatch.StopEjectOnNodeCompleteEnabled, "Stop ejectors when available nodes are all filled up");
y += 36f;
wnd.AddCheckBox(x, y, tab5, DysonSpherePatch.OnlyConstructNodesEnabled, "Construct only structure points but frames");
x = 400f; x = 400f;
y = 10f; y = 10f;
_dysonInitBtn = wnd.AddButton(x, y, tab5, "Initialize Dyson Sphere", 16, "init-dyson-sphere", () => wnd.AddButton(x, y, tab3, "Initialize Dyson Sphere", 16, "init-dyson-sphere", () =>
UIMessageBox.Show("Initialize Dyson Sphere".Translate(), "Initialize Dyson Sphere Confirm".Translate(), "取消".Translate(), "确定".Translate(), 2, null, UIMessageBox.Show("Initialize Dyson Sphere".Translate(), "Initialize Dyson Sphere Confirm".Translate(), "取消".Translate(), "确定".Translate(), 2, null, () =>
() => { DysonSphereFunctions.InitCurrentDysonLayer(null, -1); }) {
DysonSpherePatch.InitCurrentDysonSphere(-1);
})
); );
y += 36f; y += 36f;
wnd.AddText2(x, y, tab5, "Click to dismantle selected layer", 16, "text-dismantle-layer"); MyWindow.AddText(x, y, tab3, "Click to dismantle selected layer", 16, "text-dismantle-layer");
y += 27f; y += 26f;
for (var i = 0; i < 10; i++) for (var i = 0; i < 10; i++)
{ {
var id = i + 1; var id = i + 1;
var btn = wnd.AddFlatButton(x, y, tab5, id.ToString(), 12, "dismantle-layer-" + id, () => var btn = wnd.AddFlatButton(x, y, tab3, id.ToString(), 12, "dismantle-layer-" + id, () =>
UIMessageBox.Show("Dismantle selected layer".Translate(), "Dismantle selected layer Confirm".Translate(), "取消".Translate(), "确定".Translate(), 2, null, () =>
{ {
var star = DysonSphereFunctions.CurrentStarForDysonSystem(); DysonSpherePatch.InitCurrentDysonSphere(id);
UIMessageBox.Show("Dismantle selected layer".Translate(), "Dismantle selected layer Confirm".Translate(), "取消".Translate(), "确定".Translate(), 2, null, })
() => { DysonSphereFunctions.InitCurrentDysonLayer(star, id); });
}
); );
((RectTransform)btn.transform).sizeDelta = new Vector2(40f, 20f); ((RectTransform)btn.transform).sizeDelta = new Vector2(40f, 20f);
DysonLayerBtn[i] = btn; DysonLayerBtn[i] = btn;
@@ -710,44 +179,7 @@ public static class UIConfigWindow
x += 40f; x += 40f;
} }
} }
_dysonTab = tab3;
x = 400f;
y += 36f;
txt = wnd.AddText2(x, y, tab5, "Auto Fast Build Speed Multiplier", 15, "text-auto-fast-build-multiplier");
wnd.AddSlider(x + txt.preferredWidth + 5f, y + 6f, tab5, DysonSpherePatch.AutoConstructMultiplier, [1, 2, 5, 10, 20, 50, 100], "0", 100f);
_dysonTab = tab5;
var tab6 = wnd.AddTab(trans, "Tech/Combat");
x = 10;
y = 10;
wnd.AddCheckBox(x, y, tab6, TechPatch.BatchBuyoutTechEnabled, "Buy out techs with their prerequisites");
y += 36f;
wnd.AddCheckBox(x, y, tab6, TechPatch.SorterCargoStackingEnabled, "Restore upgrades of \"Sorter Cargo Stacking\" on panel");
y += 36f;
wnd.AddButton(x, y, 300f, tab6, "Set \"Sorter Cargo Stacking\" to unresearched state", 16, "button-remove-cargo-stacking", () =>
{
var history = GameMain.data?.history;
if (history == null) return;
history.inserterStackCountObsolete = 1;
for (var id = 3301; id <= 3305; id++)
{
history.techStates.TryGetValue(id, out var state);
if (!state.unlocked) continue;
state.unlocked = false;
state.hashUploaded = 0;
history.techStates[id] = state;
}
});
y += 36f;
y += 36f;
wnd.AddButton(x, y, 300f, tab6, "Open Dark Fog Communicator", 16, "button-open-df-communicator", () =>
{
if (!(GameMain.data?.gameDesc.isCombatMode ?? false)) return;
var uiGame = UIRoot.instance.uiGame;
uiGame.ShutPlayerInventory();
uiGame.CloseEnemyBriefInfo();
uiGame.OpenCommunicatorWindow(5);
});
} }
private static void UpdateUI() private static void UpdateUI()
@@ -758,22 +190,26 @@ public static class UIConfigWindow
private static void UpdateDysonShells() private static void UpdateDysonShells()
{ {
if (!_dysonTab.gameObject.activeSelf) return; if (!_dysonTab.gameObject.activeSelf) return;
var star = DysonSphereFunctions.CurrentStarForDysonSystem(); var star = GameMain.localStar;
if (star == null) if (star != null)
{ {
for (var i = 0; i < 10; i++)
{
DysonLayerBtn[i].button.interactable = false;
}
return;
}
var dysonSpheres = GameMain.data?.dysonSpheres; var dysonSpheres = GameMain.data?.dysonSpheres;
if (dysonSpheres?[star.index] == null) return; if (dysonSpheres?[star.index] != null)
{
var ds = dysonSpheres[star.index]; var ds = dysonSpheres[star.index];
for (var i = 1; i <= 10; i++) for (var i = 1; i <= 10; i++)
{ {
var layer = ds.layersIdBased[i]; var layer = ds.layersIdBased[i];
DysonLayerBtn[i - 1].button.interactable = layer != null && layer.id == i; DysonLayerBtn[i - 1].button.interactable = layer != null && layer.id == i;
} }
return;
}
}
for (var i = 0; i < 10; i++)
{
DysonLayerBtn[i].button.interactable = false;
}
} }
} }

View File

@@ -1,112 +1,49 @@
using System; using System;
using System.IO; using System.Collections.Generic;
using System.Reflection; using System.Reflection.Emit;
using BepInEx; using BepInEx;
using BepInEx.Configuration; using BepInEx.Configuration;
using BepInEx.Logging;
using CommonAPI;
using CommonAPI.Systems;
using crecheng.DSPModSave;
using HarmonyLib; using HarmonyLib;
using UnityEngine; using UnityEngine;
using UnityEngine.UI; using UnityEngine.UI;
using UXAssist.Common; using UXAssist.Common;
using UXAssist.Functions;
using UXAssist.Patches;
using UXAssist.UI; using UXAssist.UI;
using Util = UXAssist.Common.Util;
namespace UXAssist; namespace UXAssist;
[BepInDependency(CommonAPIPlugin.GUID)]
[BepInDependency(DSPModSavePlugin.MODGUID)]
[CommonAPISubmoduleDependency(nameof(CustomKeyBindSystem))]
[BepInPlugin(PluginInfo.PLUGIN_GUID, PluginInfo.PLUGIN_NAME, PluginInfo.PLUGIN_VERSION)] [BepInPlugin(PluginInfo.PLUGIN_GUID, PluginInfo.PLUGIN_NAME, PluginInfo.PLUGIN_VERSION)]
public class UXAssist : BaseUnityPlugin, IModCanSave public class UXAssist : BaseUnityPlugin
{ {
public new static readonly ManualLogSource Logger = public new static readonly BepInEx.Logging.ManualLogSource Logger =
BepInEx.Logging.Logger.CreateLogSource(PluginInfo.PLUGIN_NAME); BepInEx.Logging.Logger.CreateLogSource(PluginInfo.PLUGIN_NAME);
public static ConfigEntry<KeyboardShortcut> Hotkey;
private static bool _configWinInitialized; private static bool _configWinInitialized;
private static MyConfigWindow _configWin; private static MyConfigWindow _configWin;
private static Harmony _patch;
private static bool _initialized; private static bool _initialized;
private static PressKeyBind _toggleKey;
private static ConfigFile _dummyConfig;
private Type[] _patches, _compats;
#region IModCanSave
private const ushort ModSaveVersion = 1;
public void Export(BinaryWriter w)
{
w.Write(ModSaveVersion);
FactoryPatch.Export(w);
}
public void Import(BinaryReader r)
{
var version = r.ReadUInt16();
if (version <= 0) return;
FactoryPatch.Import(r);
}
public void IntoOtherSave()
{
}
#endregion
private void Awake() private void Awake()
{ {
_dummyConfig = new ConfigFile(Path.Combine(Paths.ConfigPath, PluginInfo.PLUGIN_GUID + "_dummy.cfg"), false) Hotkey = Config.Bind("General", "Shortcut", KeyboardShortcut.Deserialize("BackQuote + LeftAlt"), "Shortcut to open config window");
{
SaveOnConfigSet = false
};
_toggleKey = KeyBindings.RegisterKeyBinding(new BuiltinKey
{
key = new CombineKey((int)KeyCode.BackQuote, CombineKey.ALT_COMB, ECombineKeyAction.OnceClick, false),
conflictGroup = KeyBindConflict.MOVEMENT | KeyBindConflict.FLYING | KeyBindConflict.SAILING | KeyBindConflict.BUILD_MODE_1 | KeyBindConflict.KEYBOARD_KEYBIND,
name = "OpenUXAssistConfigWindow",
canOverride = true
});
GamePatch.EnableWindowResizeEnabled = Config.Bind("Game", "EnableWindowResize", false, GamePatch.EnableWindowResizeEnabled = Config.Bind("Game", "EnableWindowResize", false,
"Enable game window resize (maximum box and thick frame)"); "Enable game window resize (maximum box and thick frame)");
GamePatch.LoadLastWindowRectEnabled = Config.Bind("Game", "LoadLastWindowRect", false, GamePatch.LoadLastWindowRectEnabled = Config.Bind("Game", "LoadLastWindowRect", false,
"Load last window position and size when game starts"); "Load last window position and size when game starts");
GamePatch.LastWindowRect = Config.Bind("Game", "LastWindowRect", new Vector4(0f, 0f, 0f, 0f), GamePatch.LastWindowRect = Config.Bind("Game", "LastWindowRect", new Vector4(0f, 0f, 0f, 0f),
"Last window position and size"); "Last window position and size");
GamePatch.MouseCursorScaleUpMultiplier = Config.Bind("Game", "MouseCursorScaleUpMultiplier", 1,
"Mouse cursor scale up multiplier");
GamePatch.ProfileBasedSaveFolderEnabled = Config.Bind("Game", "ProfileBasedSaveFolder", false,
"Profile-based save folder");
GamePatch.ProfileBasedOptionEnabled = Config.Bind("Game", "ProfileBasedOption", false,
"Profile-based option");
GamePatch.DefaultProfileName = Config.Bind("Game", "DefaultProfileName", "Default",
"Default profile name, used when profile-based save folder is enabled. Use original game save folder if matched");
/* /*
GamePatch.AutoSaveOptEnabled = Config.Bind("Game", "AutoSaveOpt", false, GamePatch.AutoSaveOptEnabled = Config.Bind("Game", "AutoSaveOpt", false,
"Better auto-save mechanism"); "Better auto-save mechanism");
*/ */
GamePatch.ConvertSavesFromPeaceEnabled = Config.Bind("Game", "ConvertSavesFromPeace", false, GamePatch.ConvertSavesFromPeaceEnabled = Config.Bind("Game", "ConvertSavesFromPeace", false,
"Convert saves from Peace mode to Combat mode on save loading"); "Convert saves from Peace mode to Combat mode on save loading");
GamePatch.GameUpsFactor = _dummyConfig.Bind("Game", "GameUpsFactor", 1.0,
"Game UPS factor (1.0 for normal speed)");
WindowFunctions.ProcessPriority = Config.Bind("Game", "ProcessPriority", 2,
new ConfigDescription("Game process priority\n 0: High 1: Above Normal 2: Normal 3: Below Normal 4: Idle", new AcceptableValueRange<int>(0, 4)));
WindowFunctions.ProcessAffinity = Config.Bind("Game", "CPUAffinity", -1,
new ConfigDescription("""
Game process CPU affinity
0: All 1: First-half CPUs 2. First 8 CPUs (if total CPUs are greater than 16)
3. All Performance Cores(If Intel 13th or greater) 4. All Efficiency Cores(If Intel 13th or greater)
""", new AcceptableValueRange<int>(0, 4)));
FactoryPatch.UnlimitInteractiveEnabled = Config.Bind("Factory", "UnlimitInteractive", false, FactoryPatch.UnlimitInteractiveEnabled = Config.Bind("Factory", "UnlimitInteractive", false,
"Unlimit interactive range"); "Unlimit interactive range");
FactoryPatch.RemoveSomeConditionEnabled = Config.Bind("Factory", "RemoveSomeBuildConditionCheck", false, FactoryPatch.RemoveSomeConditionEnabled = Config.Bind("Factory", "RemoveSomeBuildConditionCheck", false,
"Remove part of build condition checks that does not affect game logic"); "Remove part of build condition checks that does not affect game logic");
FactoryPatch.NightLightEnabled = Config.Bind("Factory", "NightLight", false, FactoryPatch.NightLightEnabled = Config.Bind("Factory", "NightLight", false,
"Night light"); "Night light");
FactoryPatch.NightLightAngleX = Config.Bind("Factory", "NightLightAngleX", -8f, "Night light angle X");
FactoryPatch.NightLightAngleY = Config.Bind("Factory", "NightLightAngleY", -2f, "Night light angle Y");
PlanetPatch.PlayerActionsInGlobeViewEnabled = Config.Bind("Planet", "PlayerActionsInGlobeView", false, PlanetPatch.PlayerActionsInGlobeViewEnabled = Config.Bind("Planet", "PlayerActionsInGlobeView", false,
"Enable player actions in globe view"); "Enable player actions in globe view");
FactoryPatch.RemoveBuildRangeLimitEnabled = Config.Bind("Factory", "RemoveBuildRangeLimit", false, FactoryPatch.RemoveBuildRangeLimitEnabled = Config.Bind("Factory", "RemoveBuildRangeLimit", false,
@@ -117,133 +54,43 @@ public class UXAssist : BaseUnityPlugin, IModCanSave
"Increase maximum area size for terraform to 30x30 (from 10x10)\nNote: this may impact game performance while using large area"); "Increase maximum area size for terraform to 30x30 (from 10x10)\nNote: this may impact game performance while using large area");
FactoryPatch.OffGridBuildingEnabled = Config.Bind("Factory", "OffGridBuilding", false, FactoryPatch.OffGridBuildingEnabled = Config.Bind("Factory", "OffGridBuilding", false,
"Enable off grid building and stepped rotation"); "Enable off grid building and stepped rotation");
FactoryPatch.TreatStackingAsSingleEnabled = Config.Bind("Factory", "TreatStackingAsSingle", false, FactoryPatch.LogisticsCapacityTweaksEnabled = Config.Bind("Factory", "LogisticsCapacityTweaks", true,
"Treat stack items as single in monitor components");
FactoryPatch.QuickBuildAndDismantleLabsEnabled = Config.Bind("Factory", "QuickBuildAndDismantleLab", false,
"Quick build and dismantle stacking labs");
FactoryPatch.ProtectVeinsFromExhaustionEnabled = Config.Bind("Factory", "ProtectVeinsFromExhaustion", false,
"Protect veins from exhaustion");
FactoryPatch.ProtectVeinsFromExhaustion.KeepVeinAmount = Config.Bind("Factory", "KeepVeinAmount", 1000, new ConfigDescription("Keep veins amount (0 to disable)", new AcceptableValueRange<int>(0, 10000))).Value;
FactoryPatch.ProtectVeinsFromExhaustion.KeepOilSpeed = Config.Bind("Factory", "KeepOilSpeed", 1.0f, new ConfigDescription("Keep minimal oil speed (< 0.1 to disable)", new AcceptableValueRange<float>(0.0f, 10.0f))).Value;
FactoryPatch.DoNotRenderEntitiesEnabled = Config.Bind("Factory", "DoNotRenderEntities", false,
"Do not render factory entities");
FactoryPatch.DragBuildPowerPolesEnabled = Config.Bind("Factory", "DragBuildPowerPoles", false,
"Drag building power poles in maximum connection range");
FactoryPatch.DragBuildPowerPolesAlternatelyEnabled = Config.Bind("Factory", "DragBuildPowerPolesAlternately", true,
"Build Tesla Tower and Wireless Power Tower alternately");
FactoryPatch.BeltSignalsForBuyOutEnabled = Config.Bind("Factory", "BeltSignalsForBuyOut", false,
"Belt signals for buy out dark fog items automatically");
FactoryPatch.TankFastFillInAndTakeOutEnabled = Config.Bind("Factory", "TankFastFillInAndTakeOut", false,
"Fast fill in to and take out from tanks");
FactoryPatch.TankFastFillInAndTakeOutMultiplier = Config.Bind("Factory", "TankFastFillInAndTakeOutMultiplier", 1000, "Speed multiplier for fast filling in to and takeing out from tanks");
FactoryPatch.CutConveyorBeltEnabled = Config.Bind("Factory", "CutConveyorBeltShortcut", false,
"Cut conveyor belt (with shortcut key)");
FactoryPatch.TweakBuildingBufferEnabled = Config.Bind("Factory", "TweakBuildingBuffer", false,
"Tweak buffer count for assemblers and power generators");
FactoryPatch.AssemblerBufferTimeMultiplier = Config.Bind("Factory", "AssemblerBufferTimeMultiplier", 4, new ConfigDescription("Assembler buffer time multiplier in seconds", new AcceptableValueRange<int>(2, 10)));
FactoryPatch.AssemblerBufferMininumMultiplier = Config.Bind("Factory", "AssemblerBufferMininumMultiplier", 4, new ConfigDescription("Assembler buffer minimum multiplier", new AcceptableValueRange<int>(2, 10)));
FactoryPatch.LabBufferMaxCountForAssemble = Config.Bind("Factory", "LabBufferMaxCountForAssemble", 6, new ConfigDescription("Buffer count for assembling in labs", new AcceptableValueRange<int>(2, 20)));
FactoryPatch.LabBufferExtraCountForAdvancedAssemble = Config.Bind("Factory", "LabBufferExtraCountForAdvancedAssemble", 3, new ConfigDescription("Extra buffer count for Self-evolution Labs", new AcceptableValueRange<int>(1, 10)));
FactoryPatch.LabBufferMaxCountForResearch = Config.Bind("Factory", "LabBufferMaxCountForResearch", 10, new ConfigDescription("Buffer count for researching in labs", new AcceptableValueRange<int>(2, 20)));
FactoryPatch.ReceiverBufferCount = Config.Bind("Factory", "ReceiverBufferCount", 1, new ConfigDescription("Ray Receiver Graviton Lens buffer count", new AcceptableValueRange<int>(1, 20)));
FactoryPatch.DismantleBlueprintSelectionEnabled = Config.Bind("Factory", "DismantleBlueprintSelection", false,
"Dismantle blueprint selected buildings");
LogisticsPatch.AutoConfigLogisticsEnabled = Config.Bind("Factory", "AutoConfigLogistics", false,
"Auto-config logistic stations");
LogisticsPatch.AutoConfigDispenserChargePower = Config.Bind("Factory", "AutoConfigDispenserChargePower", 30, new ConfigDescription("LD: Max. Charging Power", new AcceptableValueRange<int>(3, 30)));
LogisticsPatch.AutoConfigDispenserCourierCount = Config.Bind("Factory", "AutoConfigDispenserCourierCount", 10, new ConfigDescription("LD: Count of Bots auto-filled", new AcceptableValueRange<int>(0, 10)));
LogisticsPatch.AutoConfigPLSChargePower = Config.Bind("Factory", "AutoConfigPLSChargePower", 4, new ConfigDescription("PLS: Max. Charging Power", new AcceptableValueRange<int>(2, 20)));
LogisticsPatch.AutoConfigPLSMaxTripDrone = Config.Bind("Factory", "AutoConfigPLSMaxTripDrone", 180, new ConfigDescription("PLS: Drone transport range", new AcceptableValueRange<int>(1, 180)));
LogisticsPatch.AutoConfigPLSDroneMinDeliver = Config.Bind("Factory", "AutoConfigPLSDroneMinDeliver", 10, new ConfigDescription("PLS: Min. Load of Drones", new AcceptableValueRange<int>(0, 10)));
LogisticsPatch.AutoConfigPLSMinPilerValue = Config.Bind("Factory", "AutoConfigPLSMinPilerValue", 0, new ConfigDescription("PLS: Outgoing integration count", new AcceptableValueRange<int>(0, 4)));
LogisticsPatch.AutoConfigPLSDroneCount = Config.Bind("Factory", "AutoConfigPLSDroneCount", 10, new ConfigDescription("PLS: Count of Drones auto-filled", new AcceptableValueRange<int>(0, 50)));
LogisticsPatch.AutoConfigILSChargePower = Config.Bind("Factory", "AutoConfigILSChargePower", 4, new ConfigDescription("ILS: Max. Charging Power", new AcceptableValueRange<int>(2, 20)));
LogisticsPatch.AutoConfigILSMaxTripDrone = Config.Bind("Factory", "AutoConfigILSMaxTripDrone", 180, new ConfigDescription("ILS: Drone transport range", new AcceptableValueRange<int>(1, 180)));
LogisticsPatch.AutoConfigILSMaxTripShip = Config.Bind("Factory", "AutoConfigILSMaxTripShip", 41, new ConfigDescription("ILS: Vessel transport range", new AcceptableValueRange<int>(1, 41)));
LogisticsPatch.AutoConfigILSWarperDistance = Config.Bind("Factory", "AutoConfigILSWarperDistance", 16, new ConfigDescription("ILS: Warp distance", new AcceptableValueRange<int>(2, 21)));
LogisticsPatch.AutoConfigILSDroneMinDeliver = Config.Bind("Factory", "AutoConfigILSDroneMinDeliver", 10, new ConfigDescription("ILS: Min. Load of Drones", new AcceptableValueRange<int>(0, 10)));
LogisticsPatch.AutoConfigILSShipMinDeliver = Config.Bind("Factory", "AutoConfigILSShipMinDeliver", 10, new ConfigDescription("ILS: Min. Load of Vessels", new AcceptableValueRange<int>(0, 10)));
LogisticsPatch.AutoConfigILSMinPilerValue = Config.Bind("Factory", "AutoConfigILSMinPilerValue", 0, new ConfigDescription("ILS: Outgoing integration count", new AcceptableValueRange<int>(0, 4)));
LogisticsPatch.AutoConfigILSIncludeOrbitCollector = Config.Bind("Factory", "AutoConfigILSIncludeOrbitCollector", true,
"ILS: Include Orbital Collector");
LogisticsPatch.AutoConfigILSWarperNecessary = Config.Bind("Factory", "AutoConfigILSWarperNecessary", true,
"ILS: Warpers required");
LogisticsPatch.AutoConfigILSDroneCount = Config.Bind("Factory", "AutoConfigILSDroneCount", 20, new ConfigDescription("ILS: Count of Drones auto-filled", new AcceptableValueRange<int>(0, 100)));
LogisticsPatch.AutoConfigILSShipCount = Config.Bind("Factory", "AutoConfigILSShipCount", 10, new ConfigDescription("ILS: Count of Vessels auto-filled", new AcceptableValueRange<int>(0, 10)));
LogisticsPatch.AutoConfigVeinCollectorHarvestSpeed = Config.Bind("Factory", "AutoConfigVeinCollectorHarvestSpeed", 20, new ConfigDescription("AMM: Collecting Speed", new AcceptableValueRange<int>(0, 20)));
LogisticsPatch.AutoConfigVeinCollectorMinPilerValue = Config.Bind("Factory", "AutoConfigVeinCollectorMinPilerValue", 0, new ConfigDescription("AMM: Outgoing integration count", new AcceptableValueRange<int>(0, 4)));
LogisticsPatch.LogisticsCapacityTweaksEnabled = Config.Bind("Factory", "LogisticsCapacityTweaks", true,
"Logistics capacity related tweaks"); "Logistics capacity related tweaks");
LogisticsPatch.AllowOverflowInLogisticsEnabled = Config.Bind("Factory", "AllowOverflowInLogistics", false,
"Allow overflow in logistic stations");
LogisticsPatch.LogisticsConstrolPanelImprovementEnabled = Config.Bind("Factory", "LogisticsConstrolPanelImprovement", false,
"Logistics control panel improvement");
LogisticsPatch.RealtimeLogisticsInfoPanelEnabled = Config.Bind("Factory", "RealtimeLogisticsInfoPanel", false,
"Realtime logistics info panel");
LogisticsPatch.RealtimeLogisticsInfoPanelBarsEnabled = Config.Bind("Factory", "RealtimeLogisticsInfoPanelBars", false,
"Realtime logistics info panel - Show status bars for storage item");
PlanetFunctions.OrbitalCollectorMaxBuildCount = Config.Bind("Factory", "OCMaxBuildCount", 0, "Maximum Orbital Collectors to build once, set to 0 to build as many as possible"); PlanetFunctions.OrbitalCollectorMaxBuildCount = Config.Bind("Factory", "OCMaxBuildCount", 0, "Maximum Orbital Collectors to build once, set to 0 to build as many as possible");
PlayerPatch.EnhancedMechaForgeCountControlEnabled = Config.Bind("Player", "EnhancedMechaForgeCountControl", false, PlayerPatch.EnhancedMechaForgeCountControlEnabled = Config.Bind("Player", "EnhancedMechaForgeCountControl", false,
"Enhanced count control for hand-make, increases maximum of count to 1000, and you can hold Ctrl/Shift/Alt to change the count rapidly"); "Enhanced count control for hand-make, increases maximum of count to 1000, and you can hold Ctrl/Shift/Alt to change the count rapidly");
PlayerPatch.HideTipsForSandsChangesEnabled = Config.Bind("Player", "HideTipsForGettingSands", false,
"Hide tips for getting soil piles");
PlayerPatch.ShortcutKeysForStarsNameEnabled = Config.Bind("Player", "ShortcutKeysForStarsName", false,
"Shortcut keys for showing stars' name");
PlayerPatch.AutoNavigationEnabled = Config.Bind("Player", "AutoNavigation", false,
"Auto navigation");
PlayerPatch.AutoCruiseEnabled = Config.Bind("Player", "AutoCruise", false,
"Auto-cruise enabled");
PlayerPatch.AutoBoostEnabled = Config.Bind("Player", "AutoBoost", false,
"Auto boost speed with auto-cruise enabled");
PlayerPatch.DistanceToWarp = Config.Bind("Player", "DistanceToWarp", 5.0, "Distance to warp (in AU)");
TechPatch.SorterCargoStackingEnabled = Config.Bind("Tech", "SorterCargoStacking", false,
"Restore upgrades of `Sorter Cargo Stacking` on panel");
TechPatch.BatchBuyoutTechEnabled = Config.Bind("Tech", "BatchBuyoutTech", false,
"Can buy out techs with their prerequisites");
DysonSpherePatch.StopEjectOnNodeCompleteEnabled = Config.Bind("DysonSphere", "StopEjectOnNodeComplete", false, DysonSpherePatch.StopEjectOnNodeCompleteEnabled = Config.Bind("DysonSphere", "StopEjectOnNodeComplete", false,
"Stop ejectors when available nodes are all filled up"); "Stop ejectors when available nodes are all filled up");
DysonSpherePatch.OnlyConstructNodesEnabled = Config.Bind("DysonSphere", "OnlyConstructNodes", false, DysonSpherePatch.OnlyConstructNodesEnabled = Config.Bind("DysonSphere", "OnlyConstructNodes", false,
"Construct only nodes but frames"); "Construct only nodes but frames");
DysonSpherePatch.AutoConstructMultiplier = Config.Bind("DysonSphere", "AutoConstructMultiplier", 1, "Dyson Sphere auto-construct speed multiplier");
I18N.Init(); I18N.Init();
I18N.Add("UXAssist Config", "UXAssist Config", "UX助手设置"); I18N.Add("UXAssist Config", "UXAssist Config", "UX助手设置");
I18N.Add("KEYOpenUXAssistConfigWindow", "[UXA] Open UXAssist Config Window", "[UXA] 打开UX助手设置面板");
// UI Patches
GameLogic.Enable(true);
UIConfigWindow.Init();
_patches = Util.GetTypesFiltered(Assembly.GetExecutingAssembly(),
t => string.Equals(t.Namespace, "UXAssist.Patches", StringComparison.Ordinal) || string.Equals(t.Namespace, "UXAssist.Functions", StringComparison.Ordinal));
_patches?.Do(type => type.GetMethod("Init")?.Invoke(null, null));
_compats = Util.GetTypesInNamespace(Assembly.GetExecutingAssembly(), "UXAssist.ModsCompat");
_compats?.Do(type => type.GetMethod("Init")?.Invoke(null, null));
I18N.Apply(); I18N.Apply();
I18N.OnInitialized += RecreateConfigWindow;
}
private void Start() // UI Patch
{ _patch ??= Harmony.CreateAndPatchAll(typeof(UXAssist));
MyWindowManager.InitBaseObjects();
MyWindowManager.Enable(true);
UIPatch.Enable(true);
_patches?.Do(type => type.GetMethod("Start")?.Invoke(null, null)); MyWindowManager.Init();
UIConfigWindow.Init();
object[] parameters = [UIPatch.GetHarmony()]; GamePatch.Init();
_compats?.Do(type => type.GetMethod("Start")?.Invoke(null, parameters)); FactoryPatch.Init();
PlanetPatch.Init();
PlayerPatch.Init();
DysonSpherePatch.Init();
} }
private void OnDestroy() private void OnDestroy()
{ {
_patches?.Do(type => type.GetMethod("Uninit")?.Invoke(null, null)); DysonSpherePatch.Uninit();
PlayerPatch.Uninit();
PlanetPatch.Uninit();
FactoryPatch.Uninit();
GamePatch.Uninit();
MyWindowManager.Uninit();
UIPatch.Enable(false); _patch?.UnpatchSelf();
MyWindowManager.Enable(false); _patch = null;
GameLogic.Enable(false);
} }
private void Update() private void Update()
@@ -251,16 +98,14 @@ public class UXAssist : BaseUnityPlugin, IModCanSave
if (VFInput.inputing) return; if (VFInput.inputing) return;
if (VFInput.onGUI) if (VFInput.onGUI)
{ {
LogisticsPatch.LogisticsCapacityTweaks.UpdateInput(); FactoryPatch.LogisticsCapacityTweaks.OnUpdate();
} }
if (_toggleKey.keyValue) if (Hotkey.Value.IsDown()) ToggleConfigWindow();
}
private void LateUpdate()
{ {
ToggleConfigWindow(); FactoryPatch.NightLight.LateUpdate();
}
GamePatch.OnUpdate();
FactoryPatch.OnUpdate();
PlayerPatch.OnUpdate();
LogisticsPatch.OnUpdate();
} }
private static void ToggleConfigWindow() private static void ToggleConfigWindow()
@@ -282,44 +127,23 @@ public class UXAssist : BaseUnityPlugin, IModCanSave
} }
} }
private static void RecreateConfigWindow() // Add config button to main menu
{ [HarmonyPostfix, HarmonyPatch(typeof(UIRoot), nameof(UIRoot.OpenMainMenuUI))]
if (!_configWinInitialized) return; public static void UIRoot_OpenMainMenuUI_Postfix()
var wasActive = _configWin.active;
if (wasActive) _configWin._Close();
MyConfigWindow.DestroyInstance(_configWin);
_configWinInitialized = false;
if (wasActive) ToggleConfigWindow();
}
[PatchGuid(PluginInfo.PLUGIN_GUID)]
private class UIPatch: PatchImpl<UIPatch>
{
private static GameObject _buttonOnPlanetGlobe;
protected override void OnEnable()
{
InitMenuButtons();
}
private static void InitMenuButtons()
{ {
if (_initialized) return; if (_initialized) return;
var uiRoot = UIRoot.instance;
if (!uiRoot) return;
{ {
var mainMenu = uiRoot.uiMainMenu; var mainMenu = UIRoot.instance.uiMainMenu;
var src = mainMenu.newGameButton; var src = mainMenu.newGameButton;
var parent = src.transform.parent; var parent = src.transform.parent;
var btn = Instantiate(src, parent); var btn = Instantiate(src, parent);
btn.name = "button-uxassist-config"; btn.name = "button-cheatenabler-config";
var l = btn.text.GetComponent<Localizer>(); var l = btn.text.GetComponent<Localizer>();
if (l != null) if (l != null)
{ {
l.stringKey = "UXAssist Config"; l.stringKey = "UXAssist Config";
l.translation = "UXAssist Config".Translate(); l.translation = "UXAssist Config".Translate();
} }
btn.text.text = "UXAssist Config".Translate(); btn.text.text = "UXAssist Config".Translate();
btn.text.fontSize = btn.text.fontSize * 7 / 8; btn.text.fontSize = btn.text.fontSize * 7 / 8;
I18N.OnInitialized += () => { btn.text.text = "UXAssist Config".Translate(); }; I18N.OnInitialized += () => { btn.text.text = "UXAssist Config".Translate(); };
@@ -331,25 +155,24 @@ public class UXAssist : BaseUnityPlugin, IModCanSave
btn.button.onClick.AddListener(ToggleConfigWindow); btn.button.onClick.AddListener(ToggleConfigWindow);
} }
{ {
var panel = uiRoot.uiGame.planetGlobe; var panel = UIRoot.instance.uiGame.planetGlobe;
var src = panel.button2; var src = panel.button2;
var sandboxMenu = uiRoot.uiGame.sandboxMenu; var sandboxMenu = UIRoot.instance.uiGame.sandboxMenu;
var icon = sandboxMenu.categoryButtons[6].transform.Find("icon")?.GetComponent<Image>()?.sprite; var icon = sandboxMenu.categoryButtons[6].transform.Find("icon")?.GetComponent<Image>()?.sprite;
var b = Instantiate(src, src.transform.parent); var b = Instantiate(src, src.transform.parent);
_buttonOnPlanetGlobe = b.gameObject; var panelButtonGo = b.gameObject;
var rect = (RectTransform)_buttonOnPlanetGlobe.transform; var rect = (RectTransform)panelButtonGo.transform;
var btn = _buttonOnPlanetGlobe.GetComponent<UIButton>(); var btn = panelButtonGo.GetComponent<UIButton>();
var img = _buttonOnPlanetGlobe.transform.Find("button-2/icon")?.GetComponent<Image>(); var img = panelButtonGo.transform.Find("button-2/icon")?.GetComponent<Image>();
if (img != null) if (img != null)
{ {
img.sprite = icon; img.sprite = icon;
} }
if (panelButtonGo != null && btn != null)
if (_buttonOnPlanetGlobe != null && btn != null)
{ {
_buttonOnPlanetGlobe.name = "open-uxassist-config"; panelButtonGo.name = "open-uxassist-config";
rect.localScale = new Vector3(0.6f, 0.6f, 0.6f); rect.localScale = new Vector3(0.5f, 0.5f, 0.5f);
rect.anchoredPosition3D = new Vector3(64f, -5f, 0f); rect.anchoredPosition3D = new Vector3(128f, -105f, 0f);
b.onClick.RemoveAllListeners(); b.onClick.RemoveAllListeners();
btn.onClick += _ => { ToggleConfigWindow(); }; btn.onClick += _ => { ToggleConfigWindow(); };
btn.tips.tipTitle = "UXAssist Config"; btn.tips.tipTitle = "UXAssist Config";
@@ -357,39 +180,134 @@ public class UXAssist : BaseUnityPlugin, IModCanSave
btn.tips.tipText = null; btn.tips.tipText = null;
btn.tips.corner = 9; btn.tips.corner = 9;
btn.tips.offset = new Vector2(-20f, -20f); btn.tips.offset = new Vector2(-20f, -20f);
_buttonOnPlanetGlobe.SetActive(true); panelButtonGo.SetActive(true);
} }
} }
/*
{
var cb = MySmallComboBox.CreateComboBox(125, 0, uiRoot.uiGame.starmap.transform as RectTransform, true).WithItems("显示原始名称", "显示星球类型+距离");
cb.SetIndex(0);
}
*/
_initialized = true; _initialized = true;
} }
// Add config button to main menu // Check for noModifier while pressing hotkeys on build bar
[HarmonyPostfix, HarmonyPatch(typeof(UIRoot), nameof(UIRoot._OnOpen))] [HarmonyTranspiler]
public static void UIRoot__OnOpen_Postfix() [HarmonyPatch(typeof(UIBuildMenu), nameof(UIBuildMenu._OnUpdate))]
private static IEnumerable<CodeInstruction> UIBuildMenu__OnUpdate_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{ {
InitMenuButtons(); var matcher = new CodeMatcher(instructions, generator);
matcher.MatchForward(false,
new CodeMatch(OpCodes.Ldsfld, AccessTools.Field(typeof(VFInput), nameof(VFInput.inScreen)))
);
matcher.Repeat(codeMatcher =>
{
var jumpPos = codeMatcher.Advance(1).Operand;
codeMatcher.Advance(-1).InsertAndAdvance(
new CodeInstruction(OpCodes.Ldsfld, AccessTools.Field(typeof(VFInput), nameof(VFInput.noModifier))),
new CodeInstruction(OpCodes.Brfalse_S, jumpPos)
).Advance(2);
});
return matcher.InstructionEnumeration();
} }
// Patch to fix the issue that warning popup on VeinUtil upgraded to level 8000+
[HarmonyTranspiler]
[HarmonyPatch(typeof(ABN_VeinsUtil), nameof(ABN_VeinsUtil.CheckValue))]
private static IEnumerable<CodeInstruction> ABN_VeinsUtil_CheckValue_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{
var matcher = new CodeMatcher(instructions, generator);
matcher.MatchForward(false,
new CodeMatch(OpCodes.Ldelem_R8),
new CodeMatch(OpCodes.Conv_R4),
new CodeMatch(OpCodes.Add),
new CodeMatch(OpCodes.Stloc_1)
);
// loc1 = Mathf.Round(n * 1000f) / 1000f;
matcher.Advance(3).Insert(
new CodeInstruction(OpCodes.Ldc_R4, 1000f),
new CodeInstruction(OpCodes.Mul),
new CodeInstruction(OpCodes.Call, AccessTools.Method(typeof(Mathf), nameof(Mathf.Round))),
new CodeInstruction(OpCodes.Ldc_R4, 1000f),
new CodeInstruction(OpCodes.Div)
);
return matcher.InstructionEnumeration();
}
// Bring popup tip window to top layer
[HarmonyTranspiler]
[HarmonyPatch(typeof(UIButton), nameof(UIButton.LateUpdate))]
private static IEnumerable<CodeInstruction> UIButton_LateUpdate_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{
var matcher = new CodeMatcher(instructions, generator);
matcher.MatchForward(false,
new CodeMatch(OpCodes.Ldloc_2),
new CodeMatch(OpCodes.Callvirt, AccessTools.PropertyGetter(typeof(Component), nameof(Component.gameObject))),
new CodeMatch(OpCodes.Callvirt, AccessTools.PropertyGetter(typeof(GameObject), nameof(GameObject.activeSelf)))
);
var labels = matcher.Labels;
matcher.Labels = null;
matcher.Insert(
new CodeInstruction(OpCodes.Ldloc_2).WithLabels(labels),
new CodeInstruction(OpCodes.Callvirt, AccessTools.PropertyGetter(typeof(Component), nameof(Component.transform))),
new CodeInstruction(OpCodes.Callvirt, AccessTools.PropertyGetter(typeof(Transform), nameof(Transform.parent))),
new CodeInstruction(OpCodes.Callvirt, AccessTools.PropertyGetter(typeof(Transform), nameof(Transform.parent))),
new CodeInstruction(OpCodes.Callvirt, AccessTools.Method(typeof(Transform), nameof(Transform.SetAsLastSibling)))
);
return matcher.InstructionEnumeration();
}
// Sort blueprint structures by item id, model index, recipe id, area index, and position before saving
[HarmonyPostfix] [HarmonyPostfix]
[HarmonyPatch(typeof(UIPlanetGlobe), nameof(UIPlanetGlobe.DistributeButtons))] [HarmonyPatch(typeof(BlueprintUtils), nameof(BlueprintUtils.GenerateBlueprintData))]
private static void UIPlanetGlobe_DistributeButtons_Postfix(UIPlanetGlobe __instance) private static void BlueprintUtils_GenerateBlueprintData_Postfix(BlueprintData _blueprintData)
{ {
if (_buttonOnPlanetGlobe == null) return; var buildings = _blueprintData.buildings;
var rect = (RectTransform)_buttonOnPlanetGlobe.transform; Array.Sort(buildings, (a, b) =>
if (__instance.dysonSphereSystemUnlocked || __instance.logisticsSystemUnlocked)
{ {
rect.anchoredPosition3D = new Vector3(64f, -5f, 0f); var tmpItemId = a.itemId - b.itemId;
} if(tmpItemId != 0)
else return tmpItemId;
var tmpModelIndex = a.modelIndex - b.modelIndex;
if(tmpModelIndex != 0)
return tmpModelIndex;
var tmpRecipeId = a.recipeId - b.recipeId;
if(tmpRecipeId != 0)
return tmpRecipeId;
var tmpAreaIndex = a.areaIndex - b.areaIndex;
if(tmpAreaIndex != 0)
return tmpAreaIndex;
const double ky = 256.0;
const double kx = 1024.0;
var scorePosA = (a.localOffset_y * ky + a.localOffset_x) * kx + a.localOffset_z;
var scorePosB = (b.localOffset_y * ky + b.localOffset_x) * kx + b.localOffset_z;
return scorePosA < scorePosB ? 1 : -1;
});
for (var i = buildings.Length - 1; i >= 0; i--)
{ {
rect.anchoredPosition3D = new Vector3(128f, -100f, 0f); buildings[i].index = i;
} }
} }
// Can set belt icon tag to float, and increase maximum belt icon tag length to 8
[HarmonyPostfix]
[HarmonyPatch(typeof(UIBeltWindow), nameof(UIBeltWindow._OnCreate))]
private static void UIBeltWindow_OnCreate_Postfix(UIBeltWindow __instance)
{
__instance.iconTagCountInput.contentType = InputField.ContentType.DecimalNumber;
__instance.iconTagCountInput.characterLimit = 8;
}
[HarmonyTranspiler]
[HarmonyPatch(typeof(UIBeltWindow), nameof(UIBeltWindow._OnUpdate))]
private static IEnumerable<CodeInstruction> UIBeltWindow_OnUpdate_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{
var matcher = new CodeMatcher(instructions, generator);
matcher.End().MatchBack(false,
new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(UIBeltWindow), nameof(UIBeltWindow.iconTagCountInput))),
new CodeMatch(ci => ci.opcode == OpCodes.Ldloca || ci.opcode == OpCodes.Ldloca_S),
new CodeMatch(OpCodes.Call, AccessTools.Method(typeof(float), nameof(float.ToString), []))
);
matcher.Advance(2).InsertAndAdvance(
new CodeInstruction(OpCodes.Ldstr, "G8")
).Set(
OpCodes.Call, AccessTools.Method(typeof(float), nameof(float.ToString), [typeof(string)])
);
return matcher.InstructionEnumeration();
} }
} }

View File

@@ -4,7 +4,7 @@
<TargetFramework>net472</TargetFramework> <TargetFramework>net472</TargetFramework>
<BepInExPluginGuid>org.soardev.uxassist</BepInExPluginGuid> <BepInExPluginGuid>org.soardev.uxassist</BepInExPluginGuid>
<Description>DSP MOD - UXAssist</Description> <Description>DSP MOD - UXAssist</Description>
<Version>1.2.20</Version> <Version>1.0.11</Version>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<LangVersion>latest</LangVersion> <LangVersion>latest</LangVersion>
<PackageId>UXAssist</PackageId> <PackageId>UXAssist</PackageId>
@@ -15,40 +15,15 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="BepInEx.Core" Version="5.*" /> <PackageReference Include="BepInEx.Core" Version="5.*" />
<PackageReference Include="BepInEx.PluginInfoProps" Version="1.*" /> <PackageReference Include="BepInEx.PluginInfoProps" Version="1.*" />
<PackageReference Include="DysonSphereProgram.GameLibs" Version="0.10.28.21219-r.0" />
<PackageReference Include="UnityEngine.Modules" Version="2018.4.12" IncludeAssets="compile" /> <PackageReference Include="UnityEngine.Modules" Version="2018.4.12" IncludeAssets="compile" />
<PackageReference Include="DysonSphereProgram.GameLibs" Version="*-r.*" />
<PackageReference Include="DysonSphereProgram.Modding.CommonAPI" Version="1.6.5" />
<PackageReference Include="DysonSphereProgram.Modding.DSPModSave" Version="1.*" />
</ItemGroup>
<!--
<ItemGroup>
<Reference Include="Assembly-CSharp">
<HintPath>..\AssemblyFromGame\Assembly-CSharp.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.UI">
<HintPath>..\AssemblyFromGame\UnityEngine.UI.dll</HintPath>
</Reference>
</ItemGroup>
-->
<ItemGroup>
<EmbeddedResource Include="assets/signal/energy-fragment.png" />
<EmbeddedResource Include="assets/signal/memory.png" />
<EmbeddedResource Include="assets/signal/negentropy.png" />
<EmbeddedResource Include="assets/signal/reassembler.png" />
<EmbeddedResource Include="assets/signal/silicon-neuron.png" />
<EmbeddedResource Include="assets/signal/virtual-particle.png" />
<EmbeddedResource Include="assets/icon/in.png" />
<EmbeddedResource Include="assets/icon/out.png" />
<EmbeddedResource Include="assets/icon/keep.png" />
</ItemGroup> </ItemGroup>
<ItemGroup Condition="'$(TargetFramework.TrimEnd(`0123456789`))' == 'net'"> <ItemGroup Condition="'$(TargetFramework.TrimEnd(`0123456789`))' == 'net'">
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.3" PrivateAssets="all" /> <PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.3" PrivateAssets="all" />
</ItemGroup> </ItemGroup>
<Target Name="PostBuild" AfterTargets="PostBuildEvent" Condition="'$(Configuration)' == 'Release'"> <Target Name="PostBuild" AfterTargets="PostBuildEvent">
<Exec Command="del /F /Q package\$(ProjectName)-$(Version).zip&#xA;powershell Compress-Archive -Force -DestinationPath 'package/$(ProjectName)-$(Version).zip' -Path &quot;$(TargetPath)&quot;, package/icon.png, package/manifest.json, README.md, CHANGELOG.md" /> <Exec Command="del /F /Q package\$(ProjectName)-$(Version).zip&#xA;zip -9 -j package/$(ProjectName)-$(Version).zip $(TargetPath) package/icon.png package/manifest.json README.md" />
</Target> </Target>
</Project> </Project>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 154 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Some files were not shown because too many files have changed in this diff Show More