├── Config ├── icon.png ├── thunderstore.toml └── README.md ├── AssemblyPublicizer.exe ├── Libs ├── UnityEngine.UI.dll └── Assembly-CSharp.dll ├── Staging ├── plugins │ └── commonapi └── CHANGELOG.md ├── CommonAPIPreloader ├── CommonAPIPreloader.csproj ├── Directory.Build.props └── Preloader.cs ├── CommonAPI ├── Util │ ├── Interfaces │ │ └── Update │ │ │ ├── IDrawUpdate.cs │ │ │ ├── IUpdate.cs │ │ │ ├── IPowerUpdate.cs │ │ │ ├── IPreUpdate.cs │ │ │ ├── IPostUpdate.cs │ │ │ ├── IUpdateMultithread.cs │ │ │ ├── IPowerUpdateMultithread.cs │ │ │ ├── IPreUpdateMultithread.cs │ │ │ └── IPostUpdateMultithread.cs │ ├── Data │ │ ├── IPoolable.cs │ │ ├── ISerializeState.cs │ │ └── InstanceRegistry.cs │ ├── Logging │ │ ├── CommonLogger.cs │ │ ├── ICommonLogger.cs │ │ └── LoggerWrapper.cs │ ├── Extensions │ │ ├── GameVersionUtil.cs │ │ ├── CodeMatcherExtension.cs │ │ ├── CecilExtension.cs │ │ ├── GeneralExtensions.cs │ │ ├── HarmonyRegisterExtension.cs │ │ ├── TopologicalSortExtension.cs │ │ └── ConfigFileExtension.cs │ ├── UI │ │ ├── ColorProperties.cs │ │ └── MaterialFixer.cs │ ├── Submodules │ │ ├── CommonAPISubmoduleDependency.cs │ │ └── BaseSubmodule.cs │ ├── ShotScene │ │ ├── Patches │ │ │ └── PostProcessingMaterialFactoryPatch.cs │ │ └── TPCameraController.cs │ ├── AcceptableValueOptionsList.cs │ └── PointsHelper.cs ├── Systems │ ├── LocalizationModule │ │ ├── documentation │ │ │ ├── folders.png │ │ │ └── file-format.png │ │ ├── Patch │ │ │ ├── Text_Patch.cs │ │ │ └── UIBehaviour_Patch.cs │ │ ├── ExtraLanguageData.cs │ │ ├── TextDefaultFont.cs │ │ └── README.md │ ├── CustomDescSystem │ │ ├── CustomDesc.cs │ │ ├── CustomDescSystem.cs │ │ ├── Patches │ │ │ └── PrefabDescPatch.cs │ │ ├── Extensions │ │ │ └── PrefabDescExtensions.cs │ │ └── ConfigurableDesc.cs │ ├── Networks │ │ ├── INetwork.cs │ │ ├── NodeExtensions.cs │ │ ├── PowerNetworkWrapper.cs │ │ └── NetworksSystem.cs │ ├── StarExtensionSystem │ │ ├── IStarExtension.cs │ │ ├── Nebula │ │ │ ├── StarExtensionData.cs │ │ │ └── StarExtensionLoadRequest.cs │ │ └── Patches │ │ │ └── StarExtensionHooks.cs │ ├── PlanetExtensionSystem │ │ ├── Extensions │ │ │ ├── IPropertySerializer.cs │ │ │ └── PlanetFactoryExtensions.cs │ │ ├── IPlanetExtension.cs │ │ └── Nebula │ │ │ ├── PlanetExtensionData.cs │ │ │ └── PlanetSystemLoadRequest.cs │ ├── ComponentSystem │ │ ├── ICopyPasteSettings.cs │ │ ├── Extensions │ │ │ └── EntityExtensions.cs │ │ ├── UI │ │ │ ├── Widgets │ │ │ │ ├── IStorage.cs │ │ │ │ └── UIPowerIndicator.cs │ │ │ ├── CustomMachineUISystem.cs │ │ │ └── CustomMachineWindow.cs │ │ ├── IComponentStateListener.cs │ │ ├── ComponentDesc.cs │ │ ├── Patches │ │ │ ├── CustomDataPatches.cs │ │ │ └── UIGamePatch.cs │ │ ├── ComponentExtensionModule.cs │ │ ├── FactoryComponent.cs │ │ └── ComponentTypePool.cs │ ├── TabSystem │ │ ├── TabData.cs │ │ ├── UI │ │ │ └── UITabButton.cs │ │ ├── Patches │ │ │ ├── UIItemPickerPatch.cs │ │ │ ├── UIRecipePickerPatch.cs │ │ │ └── UIReplicatorPatch.cs │ │ └── TabSystem.cs │ ├── ProtoRegistrySystem │ │ ├── Patches │ │ │ ├── GameMain_Patch.cs │ │ │ ├── StorageComponentPatch.cs │ │ │ ├── UIBuildMenuPatch.cs │ │ │ ├── VertaBufferPatch.cs │ │ │ ├── IconSetPatch.cs │ │ │ ├── ProtoSet_Patch.cs │ │ │ └── ResourcesPatch.cs │ │ ├── ModLoadState.cs │ │ ├── Extensions │ │ │ └── ItemProtoExtenstion.cs │ │ ├── LodMaterials.cs │ │ └── ResourceData.cs │ ├── AssemblerRecipeSystem │ │ ├── RecipeExtensions.cs │ │ ├── ExtendedAssemberDesc.cs │ │ ├── Patches │ │ │ ├── AssemblerComponentPatch.cs │ │ │ └── UIAssemblerWindowPatch.cs │ │ └── AssemblerRecipeSystem.cs │ ├── UtilSystem │ │ ├── UtilSystem.cs │ │ ├── LoadSaveOnLoad.cs │ │ └── Patches │ │ │ └── GameLoaderPatch.cs │ ├── KeyBindSystem │ │ ├── Patches │ │ │ └── KeyBindPatches.cs │ │ ├── KeyBindConflict.cs │ │ ├── PressKeyBind.cs │ │ └── README.md │ ├── PickerExtensionSystem │ │ ├── README.md │ │ ├── IPickerExtension.cs │ │ ├── UI │ │ │ └── UIShowSignalTipExtension.cs │ │ ├── PickerExtensionsSystem.cs │ │ ├── UIItemPickerExtension.cs │ │ ├── UISignalPickerExtension.cs │ │ ├── UIRecipePickerExtension.cs │ │ └── Patches │ │ │ └── UIItemPicker_Patch.cs │ └── README.md ├── CommonAPI.csproj ├── Directory.Build.props └── NebulaCompatPlugin.cs ├── NuGet.Config ├── CommonAPITests ├── CommonAPITests.csproj ├── Util.cs ├── FakeLogger.cs ├── Factory │ └── FactoryComponentTest.cs ├── Data │ ├── PrefabDataTest.cs │ └── EntityDataTest.cs └── Directory.Build.props ├── Directory.Build.targets ├── version.json ├── DevEnv.targets.example ├── .run ├── RunDSP.run.xml └── PublicizeAssembly.run.xml ├── Licenses └── R2API LICENSE ├── CommonAPIPublish.sln ├── CommonAPI.sln ├── SharedConfig.targets ├── .github └── workflows │ └── workflow.yml └── README.md /Config/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/limoka/CommonAPI/HEAD/Config/icon.png -------------------------------------------------------------------------------- /AssemblyPublicizer.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/limoka/CommonAPI/HEAD/AssemblyPublicizer.exe -------------------------------------------------------------------------------- /Libs/UnityEngine.UI.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/limoka/CommonAPI/HEAD/Libs/UnityEngine.UI.dll -------------------------------------------------------------------------------- /Libs/Assembly-CSharp.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/limoka/CommonAPI/HEAD/Libs/Assembly-CSharp.dll -------------------------------------------------------------------------------- /Staging/plugins/commonapi: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/limoka/CommonAPI/HEAD/Staging/plugins/commonapi -------------------------------------------------------------------------------- /CommonAPIPreloader/CommonAPIPreloader.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /CommonAPI/Util/Interfaces/Update/IDrawUpdate.cs: -------------------------------------------------------------------------------- 1 | namespace CommonAPI 2 | { 3 | public interface IDrawUpdate 4 | { 5 | void Draw(); 6 | } 7 | } -------------------------------------------------------------------------------- /CommonAPI/Systems/LocalizationModule/documentation/folders.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/limoka/CommonAPI/HEAD/CommonAPI/Systems/LocalizationModule/documentation/folders.png -------------------------------------------------------------------------------- /CommonAPI/Systems/LocalizationModule/documentation/file-format.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/limoka/CommonAPI/HEAD/CommonAPI/Systems/LocalizationModule/documentation/file-format.png -------------------------------------------------------------------------------- /NuGet.Config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /CommonAPITests/CommonAPITests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | false 5 | 6 | 7 | -------------------------------------------------------------------------------- /CommonAPI/Systems/CustomDescSystem/CustomDesc.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | namespace CommonAPI.Systems 4 | { 5 | public abstract class CustomDesc : MonoBehaviour 6 | { 7 | public abstract void ApplyProperties(PrefabDesc desc); 8 | } 9 | } -------------------------------------------------------------------------------- /CommonAPI/Systems/Networks/INetwork.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using PowerNetworkStructures; 3 | 4 | namespace CommonAPI 5 | { 6 | public interface INetwork 7 | { 8 | int GetId(); 9 | 10 | List GetNodes(); 11 | } 12 | } -------------------------------------------------------------------------------- /CommonAPI/Systems/StarExtensionSystem/IStarExtension.cs: -------------------------------------------------------------------------------- 1 | namespace CommonAPI.Systems 2 | { 3 | /// 4 | /// Defines a extension, which has one instance per star. 5 | /// 6 | public interface IStarExtension : ISerializeState 7 | { 8 | void Init(StarData star); 9 | } 10 | } -------------------------------------------------------------------------------- /CommonAPI/Systems/CustomDescSystem/CustomDescSystem.cs: -------------------------------------------------------------------------------- 1 | using CommonAPI.Patches; 2 | 3 | namespace CommonAPI.Systems 4 | { 5 | public class CustomDescSystem : BaseSubmodule 6 | { 7 | internal override void SetHooks() 8 | { 9 | CommonAPIPlugin.harmony.PatchAll(typeof(PrefabDescPatch)); 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /CommonAPI/Systems/Networks/NodeExtensions.cs: -------------------------------------------------------------------------------- 1 | using PowerNetworkStructures; 2 | using UnityEngine; 3 | 4 | namespace CommonAPI 5 | { 6 | public static class NodeExtensions 7 | { 8 | public static Vector3 GetPoint(this Node node) 9 | { 10 | return new Vector3(node.x, node.y, node.z); 11 | } 12 | } 13 | } -------------------------------------------------------------------------------- /CommonAPI/Systems/PlanetExtensionSystem/Extensions/IPropertySerializer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | 4 | namespace CommonAPI 5 | { 6 | public interface IPropertySerializer 7 | { 8 | void Export(object obj, BinaryWriter w); 9 | object Import(BinaryReader r); 10 | Type GetTargetType(); 11 | } 12 | 13 | } -------------------------------------------------------------------------------- /CommonAPI/Util/Data/IPoolable.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | 3 | namespace CommonAPI 4 | { 5 | /// 6 | /// Interface for objects that can be contained an a 7 | /// 8 | public interface IPoolable : ISerializeState 9 | { 10 | int GetId(); 11 | void SetId(int id); 12 | } 13 | } -------------------------------------------------------------------------------- /Directory.Build.targets: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /CommonAPI/Util/Interfaces/Update/IUpdate.cs: -------------------------------------------------------------------------------- 1 | namespace CommonAPI 2 | { 3 | /// 4 | /// Allow this factory system to receive update calls 5 | /// 6 | public interface IUpdate 7 | { 8 | /// 9 | /// This call will happen after main factory update 10 | /// 11 | void Update(); 12 | } 13 | } -------------------------------------------------------------------------------- /CommonAPI/Util/Interfaces/Update/IPowerUpdate.cs: -------------------------------------------------------------------------------- 1 | namespace CommonAPI 2 | { 3 | /// 4 | /// Allow this factory system to receive update calls 5 | /// 6 | public interface IPowerUpdate 7 | { 8 | /// 9 | /// This call will happen before power update 10 | /// 11 | void PowerUpdate(); 12 | } 13 | } -------------------------------------------------------------------------------- /CommonAPI/Util/Interfaces/Update/IPreUpdate.cs: -------------------------------------------------------------------------------- 1 | namespace CommonAPI 2 | { 3 | /// 4 | /// Allow this factory system to receive update calls 5 | /// 6 | public interface IPreUpdate 7 | { 8 | /// 9 | /// This call will happen before main factory update 10 | /// 11 | void PreUpdate(); 12 | } 13 | } -------------------------------------------------------------------------------- /CommonAPI/Util/Data/ISerializeState.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | 3 | namespace CommonAPI 4 | { 5 | /// 6 | /// Interface used to save and load class data to Binary Reader/Writer 7 | /// 8 | public interface ISerializeState 9 | { 10 | void Free(); 11 | 12 | void Export(BinaryWriter w); 13 | 14 | void Import(BinaryReader r); 15 | } 16 | } -------------------------------------------------------------------------------- /CommonAPI/Systems/PlanetExtensionSystem/IPlanetExtension.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.IO; 3 | 4 | namespace CommonAPI.Systems 5 | { 6 | /// 7 | /// Defines a extension, which has one instance per planet. 8 | /// 9 | public interface IPlanetExtension : ISerializeState 10 | { 11 | void Init(PlanetFactory factory); 12 | } 13 | } -------------------------------------------------------------------------------- /CommonAPI/Util/Interfaces/Update/IPostUpdate.cs: -------------------------------------------------------------------------------- 1 | namespace CommonAPI 2 | { 3 | /// 4 | /// Allow this factory system to receive update calls 5 | /// 6 | public interface IPostUpdate 7 | { 8 | /// 9 | /// This call will happen after everything else had already updated 10 | /// 11 | void PostUpdate(); 12 | } 13 | } -------------------------------------------------------------------------------- /CommonAPI/Util/Logging/CommonLogger.cs: -------------------------------------------------------------------------------- 1 | namespace CommonAPI 2 | { 3 | /// 4 | /// Static container for logger interface to appease Unit testing assembly... 5 | /// 6 | public class CommonLogger 7 | { 8 | public static ICommonLogger logger; 9 | 10 | public static void SetLogger(ICommonLogger _logger) 11 | { 12 | logger = _logger; 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /CommonAPI/Systems/ComponentSystem/ICopyPasteSettings.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | 3 | namespace CommonAPI.Systems 4 | { 5 | public interface ICopyPasteSettings 6 | { 7 | void WriteCopyData(BinaryWriter w); 8 | bool CanPasteSettings(FactoryComponent originalObject, BinaryReader r); 9 | void PasteSettings(int sourceType, BinaryReader r); 10 | string GetCopyMessage(); 11 | string GetPasteMessage(); 12 | } 13 | } -------------------------------------------------------------------------------- /CommonAPI/Systems/ComponentSystem/Extensions/EntityExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace CommonAPI.Systems.Extensions 2 | { 3 | public static class EntityExtensions 4 | { 5 | public static int GetCustomId(this EntityData entity) 6 | { 7 | return entity.customId; 8 | } 9 | 10 | public static int GetCustomType(this EntityData entity) 11 | { 12 | return entity.customType; 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /CommonAPI/Systems/ComponentSystem/UI/Widgets/IStorage.cs: -------------------------------------------------------------------------------- 1 | namespace CommonAPI 2 | { 3 | 4 | public interface IItem 5 | { 6 | int GetItemId(); 7 | int GetCount(); 8 | int GetMaxStackSize(); 9 | 10 | } 11 | 12 | public interface IStorage 13 | { 14 | int size { get; } 15 | bool changed { get; set; } 16 | 17 | IItem GetAt(int index); 18 | void SetAt(int index, IItem stack); 19 | } 20 | } -------------------------------------------------------------------------------- /CommonAPI/Systems/LocalizationModule/Patch/Text_Patch.cs: -------------------------------------------------------------------------------- 1 | using HarmonyLib; 2 | using UnityEngine.UI; 3 | 4 | namespace CommonAPI.Systems.ModLocalization.Patch 5 | { 6 | public class Text_Patch 7 | { 8 | 9 | [HarmonyPatch(typeof(Text), nameof(Text.font), MethodType.Getter)] 10 | [HarmonyPrefix] 11 | public static void Prefix(Text __instance) 12 | { 13 | LocalizationModule.Get(__instance)?.OnGetFont(); 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /version.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", 3 | "version": "1.6.7", 4 | "assemblyVersion": { 5 | "precision": "build" 6 | }, 7 | "gitCommitIdShortFixedLength": 7, 8 | "publicReleaseRefSpec": [ 9 | "^refs/heads/master$" 10 | ], 11 | "release": { 12 | "versionIncrement": "build", 13 | "firstUnstableTag": "" 14 | }, 15 | "inherit": false 16 | } -------------------------------------------------------------------------------- /CommonAPI/Systems/ComponentSystem/IComponentStateListener.cs: -------------------------------------------------------------------------------- 1 | namespace CommonAPI.Systems 2 | { 3 | /// 4 | /// Allows to listen to factory component add/remove events 5 | /// 6 | public interface IComponentStateListener 7 | { 8 | void OnLogicComponentsAdd(int entityId, PrefabDesc desc, int prebuildId); 9 | void OnPostlogicComponentsAdd(int entityId, PrefabDesc desc, int prebuildId); 10 | void OnLogicComponentsRemove(int entityId); 11 | } 12 | } -------------------------------------------------------------------------------- /CommonAPI/Systems/TabSystem/TabData.cs: -------------------------------------------------------------------------------- 1 | using JetBrains.Annotations; 2 | 3 | namespace CommonAPI.Systems 4 | { 5 | [UsedImplicitly] 6 | public class TabData 7 | { 8 | public int tabIndex; 9 | public readonly string tabName; 10 | public readonly string tabIconPath; 11 | 12 | public TabData(string tabName, string tabIconPath) 13 | { 14 | this.tabName = tabName; 15 | this.tabIconPath = tabIconPath; 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /CommonAPI/Systems/ComponentSystem/ComponentDesc.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace CommonAPI.Systems 3 | { 4 | public class ComponentDesc : CustomDesc 5 | { 6 | public const string FIELD_NAME = CommonAPIPlugin.ID + ":componentId"; 7 | 8 | public string componentId; 9 | public override void ApplyProperties(PrefabDesc desc) 10 | { 11 | int id = ComponentExtension.componentRegistry.GetUniqueId(componentId); 12 | desc.SetProperty(FIELD_NAME, id); 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /CommonAPI/Systems/ProtoRegistrySystem/Patches/GameMain_Patch.cs: -------------------------------------------------------------------------------- 1 | using CommonAPI.Systems; 2 | using HarmonyLib; 3 | 4 | namespace CommonAPI.Patches 5 | { 6 | [HarmonyPatch] 7 | public static class GameMain_Patch 8 | { 9 | [HarmonyPatch(typeof(GameMain), "Begin")] 10 | [HarmonyPostfix] 11 | public static void OnGameBegin() 12 | { 13 | ModProtoHistory.CheckMissingMachines(); 14 | ModProtoHistory.DisplayRemovedMessage(); 15 | } 16 | 17 | } 18 | } -------------------------------------------------------------------------------- /CommonAPI/Systems/PlanetExtensionSystem/Extensions/PlanetFactoryExtensions.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace CommonAPI.Systems 3 | { 4 | public static class PlanetFactoryExtensions 5 | { 6 | public static T GetSystem(this PlanetFactory factory, int systemId) where T : IPlanetExtension 7 | { 8 | PlanetExtensionSystem.Instance.ThrowIfNotLoaded(); 9 | if (systemId <= 0) return default; 10 | return (T)PlanetExtensionSystem.extensions[systemId].GetExtension(factory); 11 | } 12 | } 13 | } -------------------------------------------------------------------------------- /CommonAPI/Systems/ProtoRegistrySystem/ModLoadState.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace CommonAPI.Systems 4 | { 5 | public class ModLoadState : IDisposable 6 | { 7 | public string modGUID; 8 | 9 | public ModLoadState(string modGUID) 10 | { 11 | this.modGUID = modGUID; 12 | } 13 | 14 | public void Dispose() 15 | { 16 | ProtoRegistry.currentMod = ""; 17 | CommonAPIPlugin.logger.LogInfo($"Mod {modGUID} loading phase is over."); 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /CommonAPI/Util/Logging/ICommonLogger.cs: -------------------------------------------------------------------------------- 1 | namespace CommonAPI 2 | { 3 | /// 4 | /// Logger interface to appease Unit testing assembly... 5 | /// 6 | public interface ICommonLogger 7 | { 8 | public void LogFatal(object data); 9 | 10 | public void LogError(object data); 11 | 12 | public void LogWarning(object data); 13 | 14 | public void LogMessage(object data); 15 | 16 | public void LogInfo(object data); 17 | 18 | public void LogDebug(object data); 19 | } 20 | } -------------------------------------------------------------------------------- /CommonAPI/Util/Interfaces/Update/IUpdateMultithread.cs: -------------------------------------------------------------------------------- 1 | namespace CommonAPI 2 | { 3 | /// 4 | /// Allow this factory system to receive update calls 5 | /// This version also supports multithreading. Note that non single thread version will not get called! 6 | /// 7 | public interface IUpdateMultithread : IUpdate 8 | { 9 | /// 10 | /// This call will happen after main factory update 11 | /// 12 | void UpdateMultithread(int usedThreadCount, int currentThreadIdx, int minimumCount); 13 | } 14 | } -------------------------------------------------------------------------------- /DevEnv.targets.example: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | C:\Program Files (x86)\Steam\ 6 | 7 | C:\Program Files (x86)\Steam\steamapps\common\Dyson Sphere Program\ 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /CommonAPI/Systems/Networks/PowerNetworkWrapper.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using PowerNetworkStructures; 3 | 4 | namespace CommonAPI 5 | { 6 | public class PowerNetworkWrapper : INetwork 7 | { 8 | public PowerNetwork data; 9 | 10 | public PowerNetworkWrapper(PowerNetwork network) 11 | { 12 | data = network; 13 | } 14 | 15 | public int GetId() 16 | { 17 | return data.id; 18 | } 19 | 20 | public List GetNodes() 21 | { 22 | return data.nodes; 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /CommonAPI/Util/Interfaces/Update/IPowerUpdateMultithread.cs: -------------------------------------------------------------------------------- 1 | namespace CommonAPI 2 | { 3 | /// 4 | /// Allow this factory system to receive update calls 5 | /// This version also supports multithreading. Note that non single thread version will not get called! 6 | /// 7 | public interface IPowerUpdateMultithread : IPowerUpdate 8 | { 9 | /// 10 | /// This call will happen before power update 11 | /// 12 | void PowerUpdateMultithread(int usedThreadCount, int currentThreadIdx, int minimumCount); 13 | } 14 | } -------------------------------------------------------------------------------- /CommonAPI/Util/Interfaces/Update/IPreUpdateMultithread.cs: -------------------------------------------------------------------------------- 1 | namespace CommonAPI 2 | { 3 | /// 4 | /// Allow this factory system to receive update calls 5 | /// This version also supports multithreading. Note that non single thread version will not get called! 6 | /// 7 | public interface IPreUpdateMultithread : IPreUpdate 8 | { 9 | /// 10 | /// This call will happen before main factory update 11 | /// 12 | void PreUpdateMultithread(int usedThreadCount, int currentThreadIdx, int minimumCount); 13 | } 14 | } -------------------------------------------------------------------------------- /CommonAPITests/Util.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | 4 | namespace CommonAPITests 5 | { 6 | public static class Util 7 | { 8 | public static void GetSerializationSetup(Action write, Action read) 9 | { 10 | MemoryStream stream = new MemoryStream(); 11 | BinaryWriter writer = new BinaryWriter(stream); 12 | 13 | write(writer); 14 | 15 | stream.Position = 0; 16 | BinaryReader reader = new BinaryReader(stream); 17 | 18 | read(reader); 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /CommonAPI/Util/Extensions/GameVersionUtil.cs: -------------------------------------------------------------------------------- 1 | namespace CommonAPI 2 | { 3 | public static class GameVersionUtil 4 | { 5 | public static Version GetVersion(int major, int minor, int release, int build) 6 | { 7 | return new Version(major, minor, release) 8 | { 9 | Build = build 10 | }; 11 | } 12 | 13 | public static bool CompatibleWith(this Version first, Version other) 14 | { 15 | return first.Major == other.Major && 16 | first.Minor == other.Minor && 17 | first.Release == other.Release; 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /CommonAPI/CommonAPI.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | false 5 | A modding library for Dyson Sphere Program. Provides multiple features to make adding custom content to DSP easier. 6 | https://github.com/kremnev8/CommonAPI 7 | DysonSphereProgram.Modding.CommonAPI 8 | DSP Common API 9 | $(MSBuildThisFileDirectory)/Nuget/ 10 | GPL-3.0-only 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /CommonAPI/Util/Extensions/CodeMatcherExtension.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection.Emit; 2 | using HarmonyLib; 3 | 4 | namespace CommonAPI 5 | { 6 | public static class CodeMatcherExtension 7 | { 8 | public static CodeMatcher GetInstructionAndAdvance(this CodeMatcher matcher, out OpCode opCode, out object operand) 9 | { 10 | opCode = matcher.Opcode; 11 | operand = matcher.Operand; 12 | matcher.Advance(1); 13 | return matcher; 14 | } 15 | 16 | public static CodeMatcher GetLabel(this CodeMatcher matcher, out Label label) 17 | { 18 | label = (Label) matcher.Instruction.operand; 19 | return matcher; 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /.run/RunDSP.run.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 12 | -------------------------------------------------------------------------------- /Config/thunderstore.toml: -------------------------------------------------------------------------------- 1 | [config] 2 | schemaVersion = "0.0.1" 3 | 4 | [package] 5 | namespace = "CommonAPI" 6 | name = "CommonAPI" 7 | description = "A modding library for Dyson Sphere Program. Currently is under development." 8 | websiteUrl = "https://github.com/kremnev8/CommonAPI" 9 | containsNsfwContent = false 10 | 11 | [package.dependencies] 12 | xiaoye97-BepInEx = "5.4.17" 13 | xiaoye97-LDBTool = "3.0.1" 14 | CommonAPI-DSPModSave = "1.2.1" 15 | 16 | [build] 17 | icon = "./icon.png" 18 | readme = "./README.md" 19 | outdir = "./../Build" 20 | 21 | [[build.copy]] 22 | source = "./../Staging" 23 | target = "./" 24 | 25 | [publish] 26 | repository = "https://thunderstore.io" 27 | communities = [ "dyson-sphere-program" ] 28 | categories = [ ] -------------------------------------------------------------------------------- /.run/PublicizeAssembly.run.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 10 | -------------------------------------------------------------------------------- /CommonAPI/Util/UI/ColorProperties.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | namespace CommonAPI 4 | { 5 | [CreateAssetMenu(fileName = "Color Properties", menuName = "DSP/Color Properties", order = 0)] 6 | public class ColorProperties : ScriptableObject 7 | { 8 | [Header("Colors & Settings")] public Color powerNormalColor; 9 | 10 | public Color powerLowColor; 11 | 12 | public Color powerNormalIconColor; 13 | 14 | public Color powerLowIconColor; 15 | 16 | public Color powerOffColor; 17 | 18 | public Color idleColor; 19 | 20 | public Color workNormalColor; 21 | 22 | public Color workStoppedColor; 23 | 24 | public Color marqueeOnColor; 25 | 26 | public Color marqueeOffColor; 27 | } 28 | } -------------------------------------------------------------------------------- /CommonAPI/Util/Submodules/CommonAPISubmoduleDependency.cs: -------------------------------------------------------------------------------- 1 | #nullable enable 2 | using System; 3 | 4 | namespace CommonAPI 5 | { 6 | #pragma warning disable 649 7 | /// 8 | /// Attribute to have at the top of your BaseUnityPlugin class if you want to load a specific R2API Submodule. 9 | /// Parameter(s) are the nameof the submodules. 10 | /// e.g: [CommonAPISubmoduleDependency("", "")] 11 | /// 12 | [AttributeUsage(AttributeTargets.All, AllowMultiple = true)] 13 | public class CommonAPISubmoduleDependency : Attribute { 14 | public string?[]? SubmoduleNames { get; } 15 | 16 | public CommonAPISubmoduleDependency(params string[] submoduleName) { 17 | SubmoduleNames = submoduleName; 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /CommonAPI/Systems/ComponentSystem/Patches/CustomDataPatches.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using HarmonyLib; 5 | using UnityEngine; 6 | 7 | // ReSharper disable InconsistentNaming 8 | 9 | // ReSharper disable UnusedParameter.Global 10 | // ReSharper disable RedundantAssignment 11 | 12 | namespace CommonAPI.Patches 13 | { 14 | 15 | [HarmonyPatch] 16 | static class EntityDataSetNullPatch 17 | { 18 | [HarmonyPatch(typeof(EntityData), "SetNull")] 19 | [HarmonyPostfix] 20 | public static void SetNull(ref EntityData __instance) 21 | { 22 | __instance.customId = 0; 23 | __instance.customType = -1; 24 | __instance.customData = null; 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /CommonAPI/Util/Interfaces/Update/IPostUpdateMultithread.cs: -------------------------------------------------------------------------------- 1 | namespace CommonAPI 2 | { 3 | /// 4 | /// Allow this factory system to receive update calls 5 | /// This version also supports multithreading. Note that non single thread version will not get called! 6 | /// 7 | public interface IPostUpdateMultithread : IPostUpdate 8 | { 9 | 10 | /// 11 | /// This call will happen after everything else had already updated 12 | /// 13 | /// 14 | /// 15 | /// 16 | void PostUpdateMultithread(int usedThreadCount, int currentThreadIdx, int minimumCount); 17 | } 18 | } -------------------------------------------------------------------------------- /CommonAPI/Systems/AssemblerRecipeSystem/RecipeExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace CommonAPI.Systems 2 | { 3 | public static class RecipeExtensions 4 | { 5 | /// 6 | /// Checks if provided belongs to recipe type 7 | /// 8 | /// Recipe 9 | /// Integer ID 10 | /// 11 | public static bool BelongsToType(this RecipeProto proto, int typeId) 12 | { 13 | AssemblerRecipeSystem.Instance.ThrowIfNotLoaded(); 14 | if (typeId >= AssemblerRecipeSystem.recipeTypeLists.Count) return false; 15 | 16 | return AssemblerRecipeSystem.recipeTypeLists[typeId].BinarySearch(proto.ID) >= 0; 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /CommonAPI/Systems/UtilSystem/UtilSystem.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using CommonAPI.Systems.Patches; 4 | 5 | namespace CommonAPI.Systems 6 | { 7 | public class UtilSystem : BaseSubmodule 8 | { 9 | internal static List> messageHandlers = new List>(); 10 | 11 | internal static UtilSystem Instance => CommonAPIPlugin.GetModuleInstance(); 12 | 13 | internal override void SetHooks() 14 | { 15 | CommonAPIPlugin.harmony.PatchAll(typeof(GameLoaderPatch)); 16 | } 17 | 18 | 19 | public static void AddLoadMessageHandler(Func handler) 20 | { 21 | Instance.ThrowIfNotLoaded(); 22 | messageHandlers.Add(handler); 23 | } 24 | 25 | } 26 | } -------------------------------------------------------------------------------- /CommonAPI/Util/Extensions/CecilExtension.cs: -------------------------------------------------------------------------------- 1 | using Mono.Cecil; 2 | 3 | namespace CommonAPI { 4 | 5 | // Source code is taken from R2API: https://github.com/risk-of-thunder/R2API/tree/master 6 | 7 | public static class CecilExtension { 8 | 9 | internal static bool IsSubTypeOf(this TypeDefinition typeDefinition, string typeFullName) { 10 | if (typeDefinition.FullName == typeFullName) { 11 | return true; 12 | } 13 | 14 | var typeDefBaseType = typeDefinition.BaseType?.Resolve(); 15 | while (typeDefBaseType != null) { 16 | if (typeDefBaseType.FullName == typeFullName) { 17 | return true; 18 | } 19 | 20 | typeDefBaseType = typeDefBaseType.BaseType?.Resolve(); 21 | } 22 | 23 | return false; 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /CommonAPI/Systems/ProtoRegistrySystem/Patches/StorageComponentPatch.cs: -------------------------------------------------------------------------------- 1 | using CommonAPI.Systems; 2 | using HarmonyLib; 3 | 4 | namespace CommonAPI.Patches 5 | { 6 | //Fix item stack size not working 7 | [HarmonyPatch] 8 | static class StorageComponentPatch 9 | { 10 | private static bool staticLoad; 11 | 12 | [HarmonyPatch(typeof(StorageComponent), "LoadStatic")] 13 | [HarmonyPostfix] 14 | public static void Postfix() 15 | { 16 | if (!staticLoad) 17 | { 18 | foreach (var kv in ProtoRegistry.items) 19 | { 20 | StorageComponent.itemIsFuel[kv.Key] = (kv.Value.HeatValue > 0L); 21 | StorageComponent.itemStackCount[kv.Key] = kv.Value.StackSize; 22 | } 23 | 24 | staticLoad = true; 25 | } 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /CommonAPITests/FakeLogger.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using CommonAPI; 3 | 4 | namespace CommonAPITests 5 | { 6 | public class FakeLogger : ICommonLogger 7 | { 8 | public void LogFatal(object data) 9 | { 10 | Console.WriteLine(data); 11 | } 12 | 13 | public void LogError(object data) 14 | { 15 | Console.WriteLine(data); 16 | } 17 | 18 | public void LogWarning(object data) 19 | { 20 | Console.WriteLine(data); 21 | } 22 | 23 | public void LogMessage(object data) 24 | { 25 | Console.WriteLine(data); 26 | } 27 | 28 | public void LogInfo(object data) 29 | { 30 | Console.WriteLine(data); 31 | } 32 | 33 | public void LogDebug(object data) 34 | { 35 | Console.WriteLine(data); 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /CommonAPI/Systems/LocalizationModule/Patch/UIBehaviour_Patch.cs: -------------------------------------------------------------------------------- 1 | using HarmonyLib; 2 | using UnityEngine.EventSystems; 3 | using UnityEngine.UI; 4 | 5 | namespace CommonAPI.Systems.ModLocalization.Patch 6 | { 7 | public class UIBehaviour_Patch 8 | { 9 | [HarmonyPatch(typeof(UIBehaviour), "Awake")] 10 | [HarmonyPrefix] 11 | public static void OnAwake(UIBehaviour __instance) 12 | { 13 | if (__instance is Text text) 14 | { 15 | LocalizationModule.Add(text); 16 | } 17 | } 18 | 19 | [HarmonyPatch(typeof(UIBehaviour), "OnDestroy")] 20 | [HarmonyPrefix] 21 | public static void OnOnDestroy(UIBehaviour __instance) 22 | { 23 | if (__instance is Text text) 24 | { 25 | LocalizationModule.Remove(text); 26 | } 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /CommonAPI/Systems/LocalizationModule/ExtraLanguageData.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using UnityEngine; 3 | 4 | namespace CommonAPI.Systems.ModLocalization 5 | { 6 | public class ExtraLanguageData 7 | { 8 | private Dictionary _modStrings; 9 | private Dictionary _stringsToEdit; 10 | public Font customFont; 11 | 12 | public Dictionary modStrings 13 | { 14 | get 15 | { 16 | _modStrings ??= new Dictionary(); 17 | return _modStrings; 18 | } 19 | } 20 | 21 | public Dictionary stringsToEdit 22 | { 23 | get 24 | { 25 | _stringsToEdit ??= new Dictionary(); 26 | return _stringsToEdit; 27 | } 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /CommonAPI/Systems/AssemblerRecipeSystem/ExtendedAssemberDesc.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using UnityEngine; 3 | 4 | namespace CommonAPI.Systems 5 | { 6 | public class ExtendedAssemberDesc : CustomDesc 7 | { 8 | public const string RECIPE_TYPE_NAME = CommonAPIPlugin.ID + ":recipeType"; 9 | 10 | public string recipeType; 11 | 12 | public float speed = 1f; 13 | 14 | public override void ApplyProperties(PrefabDesc desc) 15 | { 16 | AssemblerRecipeSystem.Instance.ThrowIfNotLoaded(); 17 | desc.assemblerSpeed = Mathf.RoundToInt(speed * 10000f); 18 | desc.assemblerRecipeType = ERecipeType.Custom; 19 | desc.SetProperty(RECIPE_TYPE_NAME, AssemblerRecipeSystem.recipeTypes.GetUniqueId(recipeType)); 20 | 21 | desc.isAssembler = (desc.assemblerRecipeType > ERecipeType.None); 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /CommonAPI/Systems/ProtoRegistrySystem/Patches/UIBuildMenuPatch.cs: -------------------------------------------------------------------------------- 1 | using CommonAPI.Systems; 2 | using HarmonyLib; 3 | 4 | namespace CommonAPI.Patches 5 | { 6 | [HarmonyPatch] 7 | static class UIBuildMenuPatch 8 | { 9 | [HarmonyPatch(typeof(UIBuildMenu), "StaticLoad")] 10 | [HarmonyPostfix] 11 | public static void Postfix(ItemProto[,] ___protos) 12 | { 13 | foreach (var kv in ProtoRegistry.items) 14 | { 15 | int buildIndex = kv.Value.BuildIndex; 16 | if (buildIndex > 0) 17 | { 18 | int num = buildIndex / 100; 19 | int num2 = buildIndex % 100; 20 | if (num <= 12 && num2 <= 12) 21 | { 22 | ___protos[num, num2] = kv.Value; 23 | } 24 | } 25 | } 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /CommonAPI/Systems/ProtoRegistrySystem/Extensions/ItemProtoExtenstion.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | namespace CommonAPI 4 | { 5 | public static class ItemProtoExtenstion 6 | { 7 | public static void SetIcon(this ItemProto proto, string path, bool propageToRecipe = true) 8 | { 9 | if (string.IsNullOrEmpty(path)) return; 10 | 11 | Sprite sprite = Resources.Load(path); 12 | 13 | proto.IconPath = path; 14 | proto._iconSprite = sprite; 15 | 16 | if (propageToRecipe && proto.maincraft != null) 17 | { 18 | RecipeProto recipe = LDB.recipes.Select(proto.maincraft.ID); 19 | CommonAPIPlugin.logger.LogInfo($"Changing recipe icon: {recipe != null}"); 20 | 21 | recipe.IconPath = ""; 22 | recipe._iconSprite = sprite; 23 | } 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /CommonAPI/Systems/ComponentSystem/ComponentExtensionModule.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using CommonAPI.Patches; 3 | 4 | namespace CommonAPI.Systems 5 | { 6 | public class ComponentExtensionModule : BaseSubmodule 7 | { 8 | internal override Type[] Dependencies => new[] { typeof(CustomDescSystem) }; 9 | 10 | internal override void SetHooks() 11 | { 12 | CommonAPIPlugin.harmony.PatchAll(typeof(CopyPastePatch)); 13 | CommonAPIPlugin.harmony.PatchAll(typeof(EntityDataSetNullPatch)); 14 | CommonAPIPlugin.harmony.PatchAll(typeof(UIGamePatch)); 15 | } 16 | 17 | internal override void Load() 18 | { 19 | CommonAPIPlugin.registries.Add($"{CommonAPIPlugin.ID}:ComponentRegistry", ComponentExtension.componentRegistry); 20 | PlanetExtensionSystem.registry.Register(ComponentExtension.systemID, typeof(ComponentExtension)); 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /CommonAPI/Systems/KeyBindSystem/Patches/KeyBindPatches.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using CommonAPI.Systems; 4 | using HarmonyLib; 5 | 6 | namespace CommonAPI.Patches 7 | { 8 | [HarmonyPatch] 9 | public class KeyBindPatches 10 | { 11 | [HarmonyPatch(typeof(UIOptionWindow), "_OnCreate")] 12 | [HarmonyPrefix] 13 | private static void AddKeyBind(UIOptionWindow __instance) 14 | { 15 | PressKeyBind[] newKeys = CustomKeyBindSystem.customKeys.Values.ToArray(); 16 | if (newKeys.Length == 0) return; 17 | 18 | int index = DSPGame.key.builtinKeys.Length; 19 | Array.Resize(ref DSPGame.key.builtinKeys, index + CustomKeyBindSystem.customKeys.Count); 20 | 21 | for (int i = 0; i < newKeys.Length; i++) 22 | { 23 | DSPGame.key.builtinKeys[index + i] = newKeys[i].defaultBind; 24 | } 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /CommonAPI/Systems/KeyBindSystem/KeyBindConflict.cs: -------------------------------------------------------------------------------- 1 | namespace CommonAPI.Systems 2 | { 3 | public class KeyBindConflict 4 | { 5 | // Conflict group bit usages by game's keybindings 6 | // Each bit defines a group where two keybinds cannot have same keys 7 | // Bits after 4096 are not used 8 | public const int MOVEMENT = 1; 9 | public const int UI = 2; 10 | public const int BUILD_MODE_1 = 4; 11 | public const int BUILD_MODE_2 = 8; 12 | public const int BUILD_MODE_3 = 16; 13 | public const int INVENTORY = 32; 14 | public const int CAMERA_1 = 64; 15 | public const int CAMERA_2 = 128; 16 | public const int FLYING = 256; 17 | public const int SAILING = 512; 18 | public const int EXTRA = 1024; 19 | 20 | //Defines whether keybind uses keyboard or mouse 21 | public const int KEYBOARD_KEYBIND = 2048; 22 | public const int MOUSE_KEYBIND = 4096; 23 | } 24 | } -------------------------------------------------------------------------------- /CommonAPI/Systems/TabSystem/UI/UITabButton.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using UnityEngine; 3 | using UnityEngine.UI; 4 | 5 | namespace CommonAPI.Systems 6 | { 7 | public class UITabButton : MonoBehaviour 8 | { 9 | public string tabName; 10 | public int tabIndex; 11 | 12 | public Localizer localizer; 13 | public UIButton button; 14 | public Image icon; 15 | 16 | 17 | public void Init(Sprite newIcon, string name, int index, Action pressCallback) 18 | { 19 | tabName = name; 20 | tabIndex = index; 21 | localizer.stringKey = tabName; 22 | icon.sprite = newIcon; 23 | button.data = index; 24 | button.onClick += pressCallback; 25 | } 26 | 27 | public void TabSelected(int index) 28 | { 29 | button.highlighted = index == tabIndex; 30 | button.button.interactable = index != tabIndex; 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /CommonAPI/Systems/PlanetExtensionSystem/Nebula/PlanetExtensionData.cs: -------------------------------------------------------------------------------- 1 | using CommonAPI.Systems; 2 | using NebulaAPI; 3 | using NebulaAPI.Networking; 4 | using NebulaAPI.Packets; 5 | 6 | namespace CommonAPI.Nebula 7 | { 8 | public class PlanetExtensionData 9 | { 10 | public int planetId { get; set; } 11 | public byte[] binaryData { get; set; } 12 | 13 | public PlanetExtensionData() { } 14 | public PlanetExtensionData(int id, byte[] data) 15 | { 16 | planetId = id; 17 | binaryData = data; 18 | } 19 | } 20 | 21 | [RegisterPacketProcessor] 22 | public class PlanetExtensionDataProcessor : BasePacketProcessor 23 | { 24 | public override void ProcessPacket(PlanetExtensionData packet, INebulaConnection conn) 25 | { 26 | if (IsHost) return; 27 | 28 | PlanetExtensionSystem.pendingData.Add(packet.planetId, packet.binaryData); 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /CommonAPI/Systems/ProtoRegistrySystem/Patches/VertaBufferPatch.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using CommonAPI.Systems; 3 | using HarmonyLib; 4 | 5 | namespace CommonAPI.Patches 6 | { 7 | [HarmonyPatch] 8 | static class VertaBufferPatch 9 | { 10 | [HarmonyPatch(typeof(VertaBuffer), "LoadFromFile")] 11 | [HarmonyPrefix] 12 | public static bool Prefix(ref string filename) 13 | { 14 | foreach (var resource in ProtoRegistry.modResources) 15 | { 16 | if (!filename.ToLower().Contains(resource.keyWord.ToLower()) || !resource.HasVertaFolder()) continue; 17 | 18 | string newName = $"{resource.vertaFolder}/{filename}"; 19 | if (!File.Exists(newName)) continue; 20 | 21 | filename = newName; 22 | CommonAPIPlugin.logger.LogDebug("Loading registered verta file " + filename); 23 | break; 24 | } 25 | 26 | return true; 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /CommonAPI/Util/Extensions/GeneralExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace CommonAPI 4 | { 5 | public static class GeneralExtensions 6 | { 7 | public static String[] units = {"", "k", "M", "G", "T"}; 8 | 9 | public static String FormatNumber(this int number) 10 | { 11 | if (number == 0) return "0"; 12 | bool sign = false; 13 | if (number <= 0) 14 | { 15 | number = Math.Abs(number); 16 | sign = true; 17 | } 18 | int digitGroups = (int) (Math.Log10(number) / Math.Log10(1000)); 19 | return $"{(sign ? "-" : "")}{number / Math.Pow(1000, digitGroups):0.#}" + units[digitGroups]; 20 | } 21 | 22 | public static bool IsModInstalled(this string modGUID) 23 | { 24 | if (BepInEx.Bootstrap.Chainloader.PluginInfos == null) return false; 25 | 26 | return BepInEx.Bootstrap.Chainloader.PluginInfos.ContainsKey(modGUID); 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /CommonAPI/Systems/ProtoRegistrySystem/LodMaterials.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | namespace CommonAPI.Systems 4 | { 5 | public class LodMaterials 6 | { 7 | public Material[] this[int key] 8 | { 9 | get => materials[key]; 10 | set => materials[key] = value; 11 | } 12 | 13 | public LodMaterials() 14 | { 15 | materials = new Material[4][]; 16 | } 17 | 18 | public LodMaterials(int lod, Material[] lod0) 19 | { 20 | materials = new Material[4][]; 21 | AddLod(lod, lod0); 22 | } 23 | 24 | public LodMaterials(Material[] lod0) : this(0, lod0) { } 25 | 26 | public void AddLod(int lod, Material[] mats) 27 | { 28 | if (lod >= 0 && lod < 4) 29 | { 30 | materials[lod] = mats; 31 | } 32 | } 33 | 34 | public bool HasLod(int lod) 35 | { 36 | return materials[lod] != null; 37 | } 38 | 39 | public Material[][] materials; 40 | } 41 | } -------------------------------------------------------------------------------- /CommonAPI/Systems/CustomDescSystem/Patches/PrefabDescPatch.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using CommonAPI.Systems; 3 | using HarmonyLib; 4 | 5 | namespace CommonAPI.Patches 6 | { 7 | [HarmonyPatch] 8 | public static class PrefabDescPatch 9 | { 10 | [HarmonyPatch(typeof(PrefabDesc), "ReadPrefab")] 11 | [HarmonyPostfix] 12 | public static void Postfix(PrefabDesc __instance) 13 | { 14 | if (__instance.prefab != null) 15 | { 16 | __instance.customData = new Dictionary(); 17 | CustomDesc[] descs = __instance.prefab.GetComponentsInChildren(); 18 | foreach (CustomDesc desc in descs) 19 | { 20 | desc.ApplyProperties(__instance); 21 | } 22 | } 23 | } 24 | 25 | 26 | [HarmonyPatch(typeof(PrefabDesc), "Free")] 27 | [HarmonyPostfix] 28 | public static void Free(PrefabDesc __instance) 29 | { 30 | __instance.customData = null; 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /Licenses/R2API LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Risk of Thunder 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /CommonAPI/Util/Logging/LoggerWrapper.cs: -------------------------------------------------------------------------------- 1 | using BepInEx.Logging; 2 | 3 | namespace CommonAPI 4 | { 5 | /// 6 | /// Logger wrapper to appease Unit testing assembly... 7 | /// 8 | public class LoggerWrapper : ICommonLogger 9 | { 10 | public ManualLogSource logSource; 11 | 12 | public LoggerWrapper(ManualLogSource logSource) 13 | { 14 | this.logSource = logSource; 15 | } 16 | 17 | 18 | public void LogFatal(object data) 19 | { 20 | logSource.LogFatal(data); 21 | } 22 | 23 | public void LogError(object data) 24 | { 25 | logSource.LogError(data); 26 | } 27 | 28 | public void LogWarning(object data) 29 | { 30 | logSource.LogWarning(data); 31 | } 32 | 33 | public void LogMessage(object data) 34 | { 35 | logSource.LogMessage(data); 36 | } 37 | 38 | public void LogInfo(object data) 39 | { 40 | logSource.LogInfo(data); 41 | } 42 | 43 | public void LogDebug(object data) 44 | { 45 | logSource.LogDebug(data); 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /CommonAPI/Systems/AssemblerRecipeSystem/Patches/AssemblerComponentPatch.cs: -------------------------------------------------------------------------------- 1 | using CommonAPI.Systems; 2 | using HarmonyLib; 3 | 4 | namespace CommonAPI.Patches 5 | { 6 | [HarmonyPatch] 7 | public static class AssemblerComponentPatch 8 | { 9 | [HarmonyPatch(typeof(AssemblerComponent), "SetRecipe")] 10 | [HarmonyPrefix] 11 | public static bool CheckRecipe(ref AssemblerComponent __instance, int recpId, SignData[] signPool) 12 | { 13 | if (recpId > 0) 14 | { 15 | RecipeProto recipeProto = LDB.recipes.Select(recpId); 16 | if (recipeProto.Type == ERecipeType.Custom) 17 | { 18 | // ReSharper disable once Harmony003 19 | int protoId = GameMain.localPlanet.factory.entityPool[__instance.entityId].protoId; 20 | int recipeType = LDB.items.Select(protoId).prefabDesc.GetProperty(ExtendedAssemberDesc.RECIPE_TYPE_NAME); 21 | if (!recipeProto.BelongsToType(recipeType)) 22 | { 23 | return false; 24 | } 25 | } 26 | } 27 | 28 | return true; 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /CommonAPI/Systems/PickerExtensionSystem/README.md: -------------------------------------------------------------------------------- 1 | # Picker Extension System 2 | Picker Extension system is a submodule that allows to extend behavior of Item and Recipe picker UI. The extension allows to filter which items/recipes will be shown to the player. 3 | 4 | ### Usage example: 5 | Make sure to add `[CommonAPISubmoduleDependency(nameof(PickerExtensionsSystem))]` to your plugin attributes. This will load the submodule. 6 | 7 | Show only buildings to which Assembler Mk.I can be upgraded 8 | ```cs 9 | int itemId = 2303; //Assembling machine Mk.I 10 | Vector2 pos = new Vector2(-300, 238); //Roughly center of the screen 11 | UIItemPickerExtension.Popup(pos, proto => 12 | { 13 | // Do something 14 | }, proto => proto.Upgrades.Contains(itemId)); 15 | ``` 16 | 17 | Show only recipes which can be crafted by hand 18 | ```cs 19 | Vector2 pos = new Vector2(-300, 238); //Roughly center of the screen 20 | UIRecipePickerExtension.Popup(pos, proto => 21 | { 22 | // Do something 23 | }, proto => proto.Handcraft); 24 | ``` 25 | 26 | There is also an advanced usage for ItemPickerExtension which allows to define a class which acts like addon to UIItemPicker. To do so implement `IItemPickerExtension` interface. Contact me if you want an example. 27 | -------------------------------------------------------------------------------- /CommonAPI/Util/ShotScene/Patches/PostProcessingMaterialFactoryPatch.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using HarmonyLib; 3 | using UnityEngine; 4 | using UnityEngine.PostProcessing; 5 | 6 | namespace CommonAPI.ShotScene.Patches 7 | { 8 | [HarmonyPatch] 9 | public static class PostProcessingMaterialFactoryPatch 10 | { 11 | public static Material modifiedMaterial; 12 | 13 | [HarmonyPatch(typeof(MaterialFactory), "Get")] 14 | [HarmonyPostfix] 15 | public static void ChangeShader(MaterialFactory __instance, string shaderName, ref Material __result) 16 | { 17 | if (!shaderName.Equals("Hidden/Post FX/Uber Shader")) return; 18 | if (!GeneratorSceneController.isShotSceneLoaded) return; 19 | 20 | if (modifiedMaterial == null) 21 | { 22 | Shader shader = (Shader)CommonAPIPlugin.resource.bundle.LoadAsset("Assets/CommonAPI/PostProcessingV1/Shaders/Uber.shader"); 23 | modifiedMaterial = new Material(shader) 24 | { 25 | name = $"PostFX - {shaderName.Substring(shaderName.LastIndexOf("/", StringComparison.Ordinal) + 1)}", 26 | hideFlags = HideFlags.DontSave 27 | }; 28 | } 29 | __result = modifiedMaterial; 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /CommonAPI/Util/Submodules/BaseSubmodule.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace CommonAPI 4 | { 5 | public abstract class BaseSubmodule 6 | { 7 | /// 8 | /// Return true if the submodule is loaded. 9 | /// 10 | public bool Loaded { get; internal set; } 11 | 12 | internal virtual Version Build => new Version(); 13 | internal virtual Type[] Dependencies => Type.EmptyTypes; 14 | 15 | internal void ThrowIfNotLoaded() 16 | { 17 | if (!Loaded) 18 | { 19 | var submoduleName = GetType().Name; 20 | string message = $"{submoduleName} is not loaded. Please use [{nameof(CommonAPISubmoduleDependency)}(nameof({submoduleName})]"; 21 | throw new InvalidOperationException(message); 22 | } 23 | } 24 | 25 | internal virtual void SetHooks() {} 26 | internal virtual void Load() {} 27 | internal virtual void PostLoad() {} 28 | internal virtual void Unload() {} 29 | internal virtual void UnsetHooks() {} 30 | 31 | internal virtual bool LoadCheck() 32 | { 33 | return true; 34 | } 35 | 36 | internal virtual Type[] GetOptionalDependencies() 37 | { 38 | return Array.Empty(); 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /CommonAPI/Systems/StarExtensionSystem/Nebula/StarExtensionData.cs: -------------------------------------------------------------------------------- 1 | using CommonAPI.Systems; 2 | using NebulaAPI; 3 | using NebulaAPI.Interfaces; 4 | using NebulaAPI.Networking; 5 | using NebulaAPI.Packets; 6 | 7 | namespace CommonAPI.Nebula 8 | { 9 | public class StarExtensionData 10 | { 11 | public int starIndex { get; set; } 12 | public byte[] binaryData { get; set; } 13 | 14 | public StarExtensionData() { } 15 | public StarExtensionData(int starIndex, byte[] data) 16 | { 17 | this.starIndex = starIndex; 18 | binaryData = data; 19 | } 20 | } 21 | 22 | [RegisterPacketProcessor] 23 | public class StarExtensionDataProcessor : BasePacketProcessor 24 | { 25 | public override void ProcessPacket(StarExtensionData packet, INebulaConnection conn) 26 | { 27 | if (IsHost) return; 28 | 29 | StarData star = GameMain.galaxy.StarById(packet.starIndex + 1); 30 | using IReaderProvider p = NebulaModAPI.GetBinaryReader(packet.binaryData); 31 | 32 | for (int i = 1; i < StarExtensionSystem.registry.data.Count; i++) 33 | { 34 | StarExtensionStorage extension = StarExtensionSystem.extensions[i]; 35 | extension.GetSystem(star).Import(p.BinaryReader); 36 | } 37 | } 38 | } 39 | } -------------------------------------------------------------------------------- /CommonAPI/Systems/CustomDescSystem/Extensions/PrefabDescExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace CommonAPI 2 | { 3 | public static class PrefabDescExtensions 4 | { 5 | 6 | public static bool HasProperty(this PrefabDesc desc, string name) 7 | { 8 | return desc.customData.ContainsKey(name); 9 | } 10 | 11 | public static void SetProperty(this PrefabDesc desc, string name, T value) 12 | { 13 | if (desc.customData.ContainsKey(name)) 14 | { 15 | desc.customData[name] = value; 16 | } 17 | else 18 | { 19 | desc.customData.Add(name, value); 20 | } 21 | } 22 | 23 | public static T GetProperty(this PrefabDesc desc, string name) 24 | { 25 | 26 | 27 | if (desc.customData.ContainsKey(name)) 28 | { 29 | return (T)desc.customData[name]; 30 | } 31 | 32 | return default; 33 | } 34 | 35 | public static T GetOrAddProperty(this PrefabDesc desc, string name) where T : new() 36 | { 37 | if (desc.customData.ContainsKey(name)) 38 | { 39 | return (T) desc.customData[name]; 40 | } 41 | 42 | T result = new T(); 43 | desc.customData.Add(name, result); 44 | 45 | return result; 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /CommonAPITests/Factory/FactoryComponentTest.cs: -------------------------------------------------------------------------------- 1 | using CommonAPI; 2 | using CommonAPI.Systems; 3 | using NUnit.Framework; 4 | using static NUnit.Framework.Assert; 5 | 6 | namespace CommonAPITests.Factory 7 | { 8 | [TestFixture] 9 | public class FactoryComponentTest 10 | { 11 | public FactoryComponent component; 12 | 13 | [SetUp] 14 | public void Setup() 15 | { 16 | component = new FactoryComponent(); 17 | } 18 | 19 | [Test] 20 | public void Test() 21 | { 22 | component.SetId(4); 23 | AreEqual(4, component.GetId()); 24 | 25 | component.entityId = 2; 26 | component.pcId = 8; 27 | 28 | component.Free(); 29 | AreEqual(0, component.GetId()); 30 | AreEqual(0, component.entityId); 31 | AreEqual(0, component.pcId); 32 | 33 | component.SetId(4); 34 | component.entityId = 2; 35 | component.pcId = 8; 36 | 37 | Util.GetSerializationSetup(w => 38 | { 39 | component.Export(w); 40 | }, r => 41 | { 42 | component.Free(); 43 | component.Import(r); 44 | }); 45 | 46 | AreEqual(4, component.GetId()); 47 | AreEqual(2, component.entityId); 48 | AreEqual(8, component.pcId); 49 | } 50 | 51 | } 52 | } -------------------------------------------------------------------------------- /CommonAPI/Systems/StarExtensionSystem/Nebula/StarExtensionLoadRequest.cs: -------------------------------------------------------------------------------- 1 | using CommonAPI.Systems; 2 | using NebulaAPI; 3 | using NebulaAPI.Interfaces; 4 | using NebulaAPI.Networking; 5 | using NebulaAPI.Packets; 6 | 7 | namespace CommonAPI.Nebula 8 | { 9 | public class StarExtensionLoadRequest 10 | { 11 | public int starIndex { get; set; } 12 | 13 | public StarExtensionLoadRequest() { } 14 | public StarExtensionLoadRequest(int starIndex) 15 | { 16 | this.starIndex = starIndex; 17 | } 18 | } 19 | 20 | [RegisterPacketProcessor] 21 | public class StarExtensionLoadRequestProcessor : BasePacketProcessor 22 | { 23 | public override void ProcessPacket(StarExtensionLoadRequest packet, INebulaConnection conn) 24 | { 25 | if (IsClient) return; 26 | 27 | StarData star = GameMain.galaxy.StarById(packet.starIndex + 1); 28 | StarExtensionSystem.InitNewStar(star); 29 | 30 | using IWriterProvider p = NebulaModAPI.GetBinaryWriter(); 31 | 32 | for (int i = 1; i < StarExtensionSystem.registry.data.Count; i++) 33 | { 34 | StarExtensionStorage extension = StarExtensionSystem.extensions[i]; 35 | extension.GetSystem(star).Export(p.BinaryWriter); 36 | } 37 | conn.SendPacket(new StarExtensionData(packet.starIndex, p.CloseAndGetBytes())); 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /CommonAPI/Systems/UtilSystem/LoadSaveOnLoad.cs: -------------------------------------------------------------------------------- 1 | using HarmonyLib; 2 | 3 | namespace CommonAPI 4 | { 5 | /// 6 | /// Allows to load directly into a save once the game has loaded.
7 | /// To use add loadSave <Save File Name> argument when launching the game 8 | ///
9 | [HarmonyPatch] 10 | public static class LoadSaveOnLoad 11 | { 12 | internal static string saveName; 13 | internal static bool isValid => saveName != null && !saveName.Equals(""); 14 | 15 | internal static string GetArg(string name) 16 | { 17 | var args = System.Environment.GetCommandLineArgs(); 18 | for (int i = 0; i < args.Length; i++) 19 | { 20 | if (args[i] == name && args.Length > i + 1) 21 | { 22 | return args[i + 1]; 23 | } 24 | } 25 | return null; 26 | } 27 | 28 | internal static void Init() 29 | { 30 | saveName = GetArg("loadSave"); 31 | if (isValid) 32 | { 33 | DSPGame.LoadFile = saveName; 34 | CommonAPIPlugin.logger.LogInfo($"Loading save {saveName} by default!"); 35 | } 36 | } 37 | 38 | internal static void LoadSave() 39 | { 40 | if (!isValid || !GameSave.SaveExist(saveName)) return; 41 | 42 | DSPGame.StartGame(saveName); 43 | } 44 | } 45 | } -------------------------------------------------------------------------------- /CommonAPI/Systems/PlanetExtensionSystem/Nebula/PlanetSystemLoadRequest.cs: -------------------------------------------------------------------------------- 1 | using CommonAPI.Systems; 2 | using NebulaAPI; 3 | using NebulaAPI.Interfaces; 4 | using NebulaAPI.Networking; 5 | using NebulaAPI.Packets; 6 | 7 | namespace CommonAPI.Nebula 8 | { 9 | public class PlanetSystemLoadRequest 10 | { 11 | public int planetID { get; set; } 12 | 13 | public PlanetSystemLoadRequest() { } 14 | public PlanetSystemLoadRequest(int planetID) 15 | { 16 | this.planetID = planetID; 17 | } 18 | } 19 | 20 | [RegisterPacketProcessor] 21 | public class PlanetSystemLoadRequestProcessor : BasePacketProcessor 22 | { 23 | public override void ProcessPacket(PlanetSystemLoadRequest packet, INebulaConnection conn) 24 | { 25 | if (IsClient) return; 26 | 27 | PlanetData planet = GameMain.galaxy.PlanetById(packet.planetID); 28 | PlanetFactory factory = GameMain.data.GetOrCreateFactory(planet); 29 | 30 | using IWriterProvider p = NebulaModAPI.GetBinaryWriter(); 31 | 32 | for (int i = 1; i < PlanetExtensionSystem.registry.data.Count; i++) 33 | { 34 | PlanetExtensionStorage extension = PlanetExtensionSystem.extensions[i]; 35 | extension.GetExtension(factory).Export(p.BinaryWriter); 36 | } 37 | conn.SendPacket(new PlanetExtensionData(packet.planetID, p.CloseAndGetBytes())); 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /CommonAPI/Util/UI/MaterialFixer.cs: -------------------------------------------------------------------------------- 1 | using CommonAPI; 2 | using CommonAPI.Systems; 3 | using UnityEngine; 4 | 5 | namespace CommonAPI 6 | { 7 | public class MaterialFixer : MonoBehaviour 8 | { 9 | private static readonly int colorGlass = Shader.PropertyToID("_ColorGlass"); 10 | private static readonly int vibrancy = Shader.PropertyToID("_Vibrancy"); 11 | private static readonly int brightness = Shader.PropertyToID("_Brightness"); 12 | private static readonly int flatten = Shader.PropertyToID("_Flatten"); 13 | 14 | private void Start() 15 | { 16 | if (!Application.isEditor) 17 | { 18 | Material trsmat = ProtoRegistry.CreateMaterial("UI/TranslucentImage", "trs-mat", "#00000000", null, 19 | new[] {"_EMISSION"}); 20 | 21 | trsmat.SetFloat(colorGlass, 1f); 22 | trsmat.SetFloat(vibrancy, 1.1f); 23 | trsmat.SetFloat(brightness, -0.5f); 24 | trsmat.SetFloat(flatten, 0.005f); 25 | 26 | TranslucentImage[] images = GetComponentsInChildren(true); 27 | foreach (TranslucentImage image in images) 28 | { 29 | image.material = trsmat; 30 | //image.vibrancy = 1.1f; 31 | //image.brightness = -0.5f; 32 | //image.flatten = 0.005f; 33 | image.spriteBlending = 0.7f; 34 | } 35 | } 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /CommonAPI/Systems/TabSystem/Patches/UIItemPickerPatch.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using CommonAPI.Systems; 3 | using HarmonyLib; 4 | using UnityEngine; 5 | 6 | namespace CommonAPI.Patches 7 | { 8 | [HarmonyPatch] 9 | public class UIItemPickerPatch 10 | { 11 | private static List tabs; 12 | 13 | [HarmonyPatch(typeof(UIItemPicker), "_OnCreate")] 14 | [HarmonyPostfix] 15 | public static void Create(UIItemPicker __instance) 16 | { 17 | var datas = TabSystem.GetAllTabs(); 18 | tabs = new List(datas.Length - 3); 19 | 20 | foreach (TabData tab in datas) 21 | { 22 | if (tab == null) continue; 23 | 24 | GameObject button = Object.Instantiate(TabSystem.GetTabPrefab(), __instance.pickerTrans, false); 25 | ((RectTransform)button.transform).anchoredPosition = new Vector2(70 * tab.tabIndex - 54, -75); 26 | UITabButton tabButton = button.GetComponent(); 27 | Sprite sprite = Resources.Load(tab.tabIconPath); 28 | tabButton.Init(sprite, tab.tabName, tab.tabIndex, __instance.OnTypeButtonClick); 29 | tabs.Add(tabButton); 30 | } 31 | } 32 | 33 | [HarmonyPatch(typeof(UIItemPicker), "OnTypeButtonClick")] 34 | [HarmonyPostfix] 35 | public static void OnTypeClicked(int type) 36 | { 37 | foreach (UITabButton tab in tabs) 38 | { 39 | tab.TabSelected(type); 40 | } 41 | } 42 | } 43 | } -------------------------------------------------------------------------------- /CommonAPI/Util/Extensions/HarmonyRegisterExtension.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection; 5 | using HarmonyLib; 6 | 7 | namespace CommonAPI 8 | { 9 | [AttributeUsage(AttributeTargets.Class)] 10 | public class RegisterPatch : Attribute 11 | { 12 | public string typeKey; 13 | 14 | public RegisterPatch(string typeKey) 15 | { 16 | this.typeKey = typeKey; 17 | } 18 | } 19 | 20 | public static class HarmonyRegisterExtension 21 | { 22 | public static IEnumerable GetTypesWithAttributeInAssembly(Assembly assembly) where T : Attribute 23 | { 24 | return assembly.GetTypes().Where(t => t.GetCustomAttributes(typeof(T), true).Length > 0); 25 | } 26 | 27 | public static void PatchAll(this Harmony harmony, string typeKey) 28 | { 29 | Assembly assembly = Assembly.GetCallingAssembly(); 30 | var types = GetTypesWithAttributeInAssembly(assembly); 31 | foreach (Type type in types) 32 | { 33 | if (type.IsClass) 34 | { 35 | RegisterPatch attribute = type.GetCustomAttribute(); 36 | if (attribute.typeKey.Equals(typeKey)) 37 | { 38 | harmony.PatchAll(type); 39 | } 40 | } 41 | else 42 | { 43 | CommonAPIPlugin.logger.LogInfo($"Failed to patch: {type.FullName}."); 44 | } 45 | } 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /CommonAPI/Systems/TabSystem/Patches/UIRecipePickerPatch.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Reflection.Emit; 4 | using CommonAPI.Systems; 5 | using HarmonyLib; 6 | using UnityEngine; 7 | using Object = UnityEngine.Object; 8 | 9 | namespace CommonAPI.Patches 10 | { 11 | [HarmonyPatch] 12 | public class UIRecipePickerPatch 13 | { 14 | private static List tabs; 15 | 16 | [HarmonyPatch(typeof(UIRecipePicker), "_OnCreate")] 17 | [HarmonyPostfix] 18 | public static void Create(UIRecipePicker __instance) 19 | { 20 | var datas = TabSystem.GetAllTabs(); 21 | tabs = new List(datas.Length - 3); 22 | 23 | foreach (TabData tab in datas) 24 | { 25 | if (tab == null) continue; 26 | 27 | GameObject button = Object.Instantiate(TabSystem.GetTabPrefab(), __instance.pickerTrans, false); 28 | ((RectTransform)button.transform).anchoredPosition = new Vector2(70 * tab.tabIndex - 54, -75); 29 | UITabButton tabButton = button.GetComponent(); 30 | Sprite sprite = Resources.Load(tab.tabIconPath); 31 | tabButton.Init(sprite, tab.tabName, tab.tabIndex, __instance.OnTypeButtonClick); 32 | tabs.Add(tabButton); 33 | } 34 | } 35 | 36 | [HarmonyPatch(typeof(UIRecipePicker), "OnTypeButtonClick")] 37 | [HarmonyPostfix] 38 | public static void OnTypeClicked(int type) 39 | { 40 | foreach (UITabButton tab in tabs) 41 | { 42 | tab.TabSelected(type); 43 | } 44 | } 45 | } 46 | } -------------------------------------------------------------------------------- /CommonAPI/Util/Data/InstanceRegistry.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace CommonAPI 5 | { 6 | /// 7 | /// Data structure that allows to register instances of objects with unique string ID's 8 | /// 9 | /// Base class for all instances 10 | public class InstanceRegistry : Registry 11 | { 12 | public List data = new List(); 13 | 14 | public InstanceRegistry() 15 | { 16 | data.Add(default); 17 | } 18 | 19 | public InstanceRegistry(int startId) : this(startId, false) 20 | { 21 | } 22 | 23 | public InstanceRegistry(int startId, bool throwErrorOnConflict) : base(startId, throwErrorOnConflict) 24 | { 25 | for (int i = 0; i < startId; i++) 26 | { 27 | data.Add(default); 28 | } 29 | } 30 | 31 | /// 32 | /// Register new instance 33 | /// 34 | /// Unique string ID 35 | /// instance of object 36 | /// Assigned integer ID 37 | public virtual int Register(string key, T item) 38 | { 39 | return Register(key, (object)item); 40 | } 41 | 42 | protected override void OnItemRegistered(string key, int id, object item) 43 | { 44 | if (item is T o) 45 | { 46 | data.Add(o); 47 | return; 48 | } 49 | throw new ArgumentException($"Tried to register invalid type {item.GetType().FullName}, expected {typeof(T).FullName}"); 50 | } 51 | } 52 | } -------------------------------------------------------------------------------- /CommonAPI/Systems/PickerExtensionSystem/IPickerExtension.cs: -------------------------------------------------------------------------------- 1 | namespace CommonAPI 2 | { 3 | /// 4 | /// Extend Picker behavior 5 | /// 6 | /// Target Picker 7 | public interface IPickerExtension 8 | { 9 | /// 10 | /// Called when picker with extension is open 11 | /// 12 | void Open(T picker); 13 | /// 14 | /// Called when picker with extension is closed 15 | /// 16 | void Close(T picker); 17 | /// 18 | /// Called before picker is open 19 | /// 20 | void OnPopup(T picker); 21 | /// 22 | /// Called after picker is open 23 | /// 24 | void PostPopup(T picker); 25 | } 26 | 27 | /// 28 | /// Extend Picker behavior 29 | /// 30 | /// Target Picker 31 | public interface IMouseHandlerExtension : IPickerExtension 32 | { 33 | /// 34 | /// Called when player click left mouse button 35 | /// 36 | /// Should picker close 37 | bool OnBoxMouseDown(T picker); 38 | 39 | /// 40 | /// Called when mouse position being checked for intersection 41 | /// 42 | void TestMouseIndex(T picker); 43 | } 44 | 45 | public interface IUpdatePickerExtension : IPickerExtension 46 | { 47 | void OnUpdate(T picker); 48 | } 49 | 50 | /// 51 | /// If implemented all items will be shown, even if locked 52 | /// 53 | public interface ShowLocked 54 | { 55 | 56 | } 57 | } -------------------------------------------------------------------------------- /CommonAPI/Systems/ComponentSystem/FactoryComponent.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | 3 | namespace CommonAPI.Systems 4 | { 5 | public class FactoryComponent : IPoolable 6 | { 7 | public int id; 8 | public int entityId; 9 | public int pcId; 10 | 11 | public virtual void Free() 12 | { 13 | id = 0; 14 | entityId = 0; 15 | pcId = 0; 16 | } 17 | 18 | public int GetId() 19 | { 20 | return id; 21 | } 22 | 23 | public void SetId(int _id) 24 | { 25 | id = _id; 26 | } 27 | 28 | public virtual void Import(BinaryReader r) 29 | { 30 | id = r.ReadInt32(); 31 | entityId = r.ReadInt32(); 32 | pcId = r.ReadInt32(); 33 | } 34 | 35 | public virtual void Export(BinaryWriter w) 36 | { 37 | w.Write(id); 38 | w.Write(entityId); 39 | w.Write(pcId); 40 | } 41 | 42 | public virtual void OnAdded(PrebuildData data, PlanetFactory factory) { } 43 | 44 | public virtual void OnRemoved(PlanetFactory factory) { } 45 | 46 | public virtual int InternalUpdate(float power, PlanetFactory factory) 47 | { 48 | return 0; 49 | } 50 | 51 | public virtual void UpdateAnimation(ref AnimData data, int updateResult, float power) 52 | { 53 | } 54 | 55 | public virtual void UpdateSigns(ref SignData data, int updateResult, float power, PlanetFactory factory) 56 | { 57 | } 58 | 59 | public virtual void UpdatePowerState(ref PowerConsumerComponent component) { } 60 | 61 | public virtual int[] UpdateNeeds() 62 | { 63 | return System.Array.Empty(); 64 | } 65 | } 66 | } -------------------------------------------------------------------------------- /CommonAPI/Systems/UtilSystem/Patches/GameLoaderPatch.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Reflection.Emit; 4 | using HarmonyLib; 5 | 6 | namespace CommonAPI.Systems.Patches 7 | { 8 | [HarmonyPatch] 9 | public class GameLoaderPatch 10 | { 11 | public delegate void RefAction(ref T1 arg1); 12 | 13 | [HarmonyPatch(typeof(GameLoader), "FixedUpdate")] 14 | [HarmonyTranspiler] 15 | public static IEnumerable AddModificationWarn(IEnumerable instructions) 16 | { 17 | CodeMatcher matcher = new CodeMatcher(instructions) 18 | .MatchForward(false, 19 | new CodeMatch(OpCodes.Ldloc_0), 20 | new CodeMatch(OpCodes.Call, AccessTools.Method(typeof(string), nameof(string.IsNullOrEmpty))), 21 | new CodeMatch(OpCodes.Brtrue) 22 | ) 23 | .InsertAndAdvance(new CodeInstruction(OpCodes.Ldloca_S, 0)) 24 | .InsertAndAdvance( 25 | Transpilers.EmitDelegate>((ref string text) => 26 | { 27 | if (UtilSystem.messageHandlers.Count > 0) 28 | { 29 | foreach (Func handler in UtilSystem.messageHandlers) 30 | { 31 | string message = handler(); 32 | if (!string.IsNullOrEmpty(message)) 33 | { 34 | text = text + "\r\n" + message; 35 | } 36 | } 37 | } 38 | })); 39 | 40 | 41 | return matcher.InstructionEnumeration(); 42 | } 43 | } 44 | } -------------------------------------------------------------------------------- /CommonAPITests/Data/PrefabDataTest.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using CommonAPI; 3 | using CommonAPI.Systems; 4 | using NUnit.Framework; 5 | using static NUnit.Framework.Assert; 6 | 7 | namespace CommonAPITests 8 | { 9 | public class TestComponent : FactoryComponent 10 | { 11 | 12 | } 13 | 14 | [TestFixture] 15 | public class PrefabDataTest 16 | { 17 | public ComponentDesc component; 18 | public PrefabDesc desc; 19 | 20 | [SetUp] 21 | public void Setup() 22 | { 23 | component = new ComponentDesc(); 24 | desc = new PrefabDesc 25 | { 26 | customData = new Dictionary() 27 | }; 28 | } 29 | 30 | [Test] 31 | public void TestComponentDesc() 32 | { 33 | component.componentId = "TEST:ID1"; 34 | 35 | ComponentExtension.componentRegistry.Register("TEST:ID1", typeof(TestComponent)); 36 | 37 | int id = ComponentExtension.componentRegistry.GetUniqueId("TEST:ID1"); 38 | 39 | component.ApplyProperties(desc); 40 | int id2 = desc.GetProperty(ComponentDesc.FIELD_NAME); 41 | 42 | AreEqual(id, id2); 43 | } 44 | 45 | [Test] 46 | public void TestPrefabProperties() 47 | { 48 | False(desc.HasProperty("Test")); 49 | desc.SetProperty("Test", 5); 50 | desc.SetProperty("Test", 7); 51 | 52 | True(desc.HasProperty("Test")); 53 | 54 | AreEqual(7, desc.GetProperty("Test")); 55 | AreEqual(0, desc.GetProperty("Hello")); 56 | 57 | AreEqual(7, desc.GetOrAddProperty("Test")); 58 | AreEqual(0, desc.GetOrAddProperty("Hello")); 59 | } 60 | } 61 | } -------------------------------------------------------------------------------- /CommonAPI/Systems/ProtoRegistrySystem/Patches/IconSetPatch.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using CommonAPI.Systems; 3 | using HarmonyLib; 4 | using UnityEngine; 5 | 6 | namespace CommonAPI.Patches 7 | { 8 | [HarmonyPatch] 9 | public static class IconSetPatch 10 | { 11 | [HarmonyPatch(typeof(IconSet), "Create")] 12 | [HarmonyPostfix] 13 | public static void AddIconDescs(IconSet __instance) 14 | { 15 | foreach (var kv in ProtoRegistry.itemIconDescs) 16 | { 17 | if (kv.Key <= 0 || kv.Key >= 12000) continue; 18 | uint index = __instance.itemIconIndex[kv.Key]; 19 | if (index <= 0U) continue; 20 | 21 | IconToolNew.IconDesc desc = kv.Value; 22 | 23 | FieldInfo[] fields = typeof(IconToolNew.IconDesc).GetFields(BindingFlags.Instance | BindingFlags.Public); 24 | uint offset = 0; 25 | 26 | foreach (var field in fields) 27 | { 28 | if (field.FieldType == typeof(float)) 29 | { 30 | __instance.itemDescArr[index * 40U + offset++] = (float)field.GetValue(desc); 31 | }else if (field.FieldType == typeof(Color)) 32 | { 33 | Color color = (Color)field.GetValue(desc); 34 | __instance.itemDescArr[index * 40U + offset++] = color.r; 35 | __instance.itemDescArr[index * 40U + offset++] = color.g; 36 | __instance.itemDescArr[index * 40U + offset++] = color.b; 37 | __instance.itemDescArr[index * 40U + offset++] = color.a; 38 | } 39 | } 40 | } 41 | 42 | __instance.itemIconDescBuffer.SetData(__instance.itemDescArr); 43 | } 44 | } 45 | } -------------------------------------------------------------------------------- /CommonAPI/Systems/TabSystem/TabSystem.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Reflection; 4 | using CommonAPI.Patches; 5 | using CommonAPI.Systems; 6 | using UnityEngine; 7 | 8 | namespace CommonAPI.Systems 9 | { 10 | public class TabSystem : BaseSubmodule 11 | { 12 | internal static InstanceRegistry tabsRegistry = new InstanceRegistry(3); 13 | private static GameObject tabPrefab; 14 | 15 | internal static TabSystem Instance => CommonAPIPlugin.GetModuleInstance(); 16 | internal override Type[] Dependencies => new[] { typeof(ProtoRegistry) }; 17 | 18 | internal override void SetHooks() 19 | { 20 | CommonAPIPlugin.harmony.PatchAll(typeof(UIItemPickerPatch)); 21 | CommonAPIPlugin.harmony.PatchAll(typeof(UIRecipePickerPatch)); 22 | CommonAPIPlugin.harmony.PatchAll(typeof(UIReplicatorPatch)); 23 | } 24 | 25 | 26 | public static int RegisterTab(string tabId, TabData tab) 27 | { 28 | Instance.ThrowIfNotLoaded(); 29 | int tabIndex = tabsRegistry.Register(tabId, tab); 30 | tab.tabIndex = tabIndex; 31 | 32 | return tabIndex; 33 | } 34 | 35 | public static int GetTabId(string tabId) 36 | { 37 | Instance.ThrowIfNotLoaded(); 38 | return tabsRegistry.GetUniqueId(tabId); 39 | } 40 | 41 | public static TabData[] GetAllTabs() 42 | { 43 | return tabsRegistry.data.ToArray(); 44 | } 45 | 46 | public static GameObject GetTabPrefab() 47 | { 48 | if (tabPrefab == null) 49 | { 50 | tabPrefab = CommonAPIPlugin.resource.bundle.LoadAsset("Assets/CommonAPI/UI/tab-button.prefab"); 51 | } 52 | 53 | return tabPrefab; 54 | } 55 | } 56 | } -------------------------------------------------------------------------------- /CommonAPI/Util/AcceptableValueOptionsList.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using BepInEx.Configuration; 4 | using HarmonyLib; 5 | 6 | namespace CommonAPI 7 | { 8 | public class AcceptableValueOptionsList : AcceptableValueBase 9 | { 10 | public AcceptableValueOptionsList(string[] acceptableValues) : base(typeof (string)) 11 | { 12 | if (acceptableValues == null) 13 | throw new ArgumentNullException(nameof (acceptableValues)); 14 | AcceptableValues = acceptableValues.Length != 0 ? acceptableValues : throw new ArgumentException("At least one acceptable value is needed", nameof (acceptableValues)); 15 | } 16 | 17 | /// List of values that a setting can take. 18 | public string[] AcceptableValues { get; } 19 | 20 | /// 21 | public override object Clamp(object value) 22 | { 23 | if (!(value is string obj)) 24 | return ""; 25 | string[] options = obj.Split(','); 26 | return options 27 | .Select(s => s.Trim()) 28 | .Where(s => AcceptableValues.Contains(s)) 29 | .Join(); 30 | } 31 | 32 | /// 33 | public override bool IsValid(object value) 34 | { 35 | if (!(value is string obj)) 36 | return false; 37 | string[] options = obj.Split(','); 38 | return options.All(s => AcceptableValues.Contains(s.Trim())); 39 | } 40 | 41 | /// 42 | public override string ToDescriptionString() 43 | { 44 | return $"# Acceptable values: {string.Join(", ", AcceptableValues.Select((Func)(x => x.ToString())).ToArray())}\n" + 45 | "# Multiple values can be set at the same time by separating them with , (e.g. Debug, Warning)"; 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /CommonAPIPublish.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{08DB0E29-5C9B-41F3-A130-4CB9CAFAED70}" 4 | ProjectSection(SolutionItems) = preProject 5 | SharedConfig.targets = SharedConfig.targets 6 | Directory.Build.targets = Directory.Build.targets 7 | EndProjectSection 8 | EndProject 9 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CommonAPI", "CommonAPI\CommonAPI.csproj", "{AF2A8839-E3B2-4CF6-8636-D3389E517974}" 10 | EndProject 11 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CommonAPIPreloader", "CommonAPIPreloader\CommonAPIPreloader.csproj", "{4C01DC6A-E738-47BC-841D-946A977664B2}" 12 | EndProject 13 | Global 14 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 15 | Debug|Any CPU = Debug|Any CPU 16 | Release|Any CPU = Release|Any CPU 17 | EndGlobalSection 18 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 19 | {AF2A8839-E3B2-4CF6-8636-D3389E517974}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 20 | {AF2A8839-E3B2-4CF6-8636-D3389E517974}.Debug|Any CPU.Build.0 = Debug|Any CPU 21 | {AF2A8839-E3B2-4CF6-8636-D3389E517974}.Release|Any CPU.ActiveCfg = Release|Any CPU 22 | {AF2A8839-E3B2-4CF6-8636-D3389E517974}.Release|Any CPU.Build.0 = Release|Any CPU 23 | {FE453D25-4104-41B2-AC55-A804CCA3212E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 24 | {FE453D25-4104-41B2-AC55-A804CCA3212E}.Debug|Any CPU.Build.0 = Debug|Any CPU 25 | {FE453D25-4104-41B2-AC55-A804CCA3212E}.Release|Any CPU.ActiveCfg = Release|Any CPU 26 | {FE453D25-4104-41B2-AC55-A804CCA3212E}.Release|Any CPU.Build.0 = Release|Any CPU 27 | {4C01DC6A-E738-47BC-841D-946A977664B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 28 | {4C01DC6A-E738-47BC-841D-946A977664B2}.Debug|Any CPU.Build.0 = Debug|Any CPU 29 | {4C01DC6A-E738-47BC-841D-946A977664B2}.Release|Any CPU.ActiveCfg = Release|Any CPU 30 | {4C01DC6A-E738-47BC-841D-946A977664B2}.Release|Any CPU.Build.0 = Release|Any CPU 31 | EndGlobalSection 32 | EndGlobal 33 | -------------------------------------------------------------------------------- /CommonAPIPreloader/Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | $(MSBuildThisFileDirectory)/../DevEnv.targets 6 | 7 | 8 | 9 | 10 | 11 | 12 | C:\Program Files (x86)\Steam\steamapps\common\Dyson Sphere Program\ 13 | $([MSBuild]::EnsureTrailingSlash('$(DSPGameDir)')) 14 | $([MSBuild]::EnsureTrailingSlash('$(SteamDir)')) 15 | $(DSPGameDir)BepInEx\core\ 16 | $(DSPGameDir)DSPGAME_Data\Managed\ 17 | $(DSPGameDir)BepInEx\patchers\CommonAPI\ 18 | 0 19 | 20 | 21 | 22 | false 23 | false 24 | 25 | $(PluginOutputDirectory) 26 | $(MSBuildThisFileDirectory)\..\Staging\patchers\ 27 | $(OutputPath) 28 | net472 29 | 8.0 30 | true 31 | true 32 | $(DefaultItemExcludes);*.binlog 33 | 34 | portable 35 | true 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /CommonAPI.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{08DB0E29-5C9B-41F3-A130-4CB9CAFAED70}" 4 | ProjectSection(SolutionItems) = preProject 5 | SharedConfig.targets = SharedConfig.targets 6 | Directory.Build.targets = Directory.Build.targets 7 | EndProjectSection 8 | EndProject 9 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CommonAPI", "CommonAPI\CommonAPI.csproj", "{AF2A8839-E3B2-4CF6-8636-D3389E517974}" 10 | EndProject 11 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CommonAPITests", "CommonAPITests\CommonAPITests.csproj", "{FE453D25-4104-41B2-AC55-A804CCA3212E}" 12 | EndProject 13 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CommonAPIPreloader", "CommonAPIPreloader\CommonAPIPreloader.csproj", "{4C01DC6A-E738-47BC-841D-946A977664B2}" 14 | EndProject 15 | Global 16 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 17 | Debug|Any CPU = Debug|Any CPU 18 | Release|Any CPU = Release|Any CPU 19 | EndGlobalSection 20 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 21 | {AF2A8839-E3B2-4CF6-8636-D3389E517974}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 22 | {AF2A8839-E3B2-4CF6-8636-D3389E517974}.Debug|Any CPU.Build.0 = Debug|Any CPU 23 | {AF2A8839-E3B2-4CF6-8636-D3389E517974}.Release|Any CPU.ActiveCfg = Release|Any CPU 24 | {AF2A8839-E3B2-4CF6-8636-D3389E517974}.Release|Any CPU.Build.0 = Release|Any CPU 25 | {FE453D25-4104-41B2-AC55-A804CCA3212E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 26 | {FE453D25-4104-41B2-AC55-A804CCA3212E}.Debug|Any CPU.Build.0 = Debug|Any CPU 27 | {FE453D25-4104-41B2-AC55-A804CCA3212E}.Release|Any CPU.ActiveCfg = Release|Any CPU 28 | {FE453D25-4104-41B2-AC55-A804CCA3212E}.Release|Any CPU.Build.0 = Release|Any CPU 29 | {4C01DC6A-E738-47BC-841D-946A977664B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 30 | {4C01DC6A-E738-47BC-841D-946A977664B2}.Debug|Any CPU.Build.0 = Debug|Any CPU 31 | {4C01DC6A-E738-47BC-841D-946A977664B2}.Release|Any CPU.ActiveCfg = Release|Any CPU 32 | {4C01DC6A-E738-47BC-841D-946A977664B2}.Release|Any CPU.Build.0 = Release|Any CPU 33 | EndGlobalSection 34 | EndGlobal 35 | -------------------------------------------------------------------------------- /CommonAPI/Util/ShotScene/TPCameraController.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | namespace CommonAPI.ShotScene 4 | { 5 | public class TPCameraController : MonoBehaviour 6 | { 7 | public Transform lookAt; 8 | public Camera camera; 9 | 10 | private float currentX = 70; 11 | private float currentY = 40; 12 | 13 | public float yAngleMin = 10f; 14 | public float yAngleMax = 85f; 15 | public float cameraDistance = 100f; 16 | public float minCameraDistance = 1f; 17 | public float scrollSensitivity = 20f; 18 | public float sensitivity = 3; 19 | public Vector3 cameraOffset = new Vector3(0,2,0); 20 | 21 | private float currentDistance; 22 | 23 | 24 | private void Start() 25 | { 26 | currentY = Mathf.Clamp(currentY, yAngleMin, yAngleMax); 27 | currentDistance = 10; 28 | camera = GetComponent(); 29 | 30 | } 31 | 32 | public void RecalculatePosition() 33 | { 34 | if (lookAt == null) return; 35 | 36 | Vector3 dir = new Vector3(0, 0, -currentDistance); 37 | Quaternion rotation = Quaternion.Euler(currentY, currentX, 0); 38 | var position = lookAt.position + cameraOffset; 39 | 40 | transform.position = position + rotation * dir; 41 | 42 | transform.LookAt(position); 43 | } 44 | 45 | private void Update() 46 | { 47 | if (GeneratorSceneController.pointerInside) return; 48 | 49 | float mouseScroll = -Input.mouseScrollDelta.y * scrollSensitivity; 50 | currentDistance += mouseScroll * Time.deltaTime; 51 | currentDistance = Mathf.Clamp(currentDistance, minCameraDistance, cameraDistance); 52 | 53 | if (!Input.GetMouseButton(0)) return; 54 | 55 | currentX += Input.GetAxis("Mouse X") * sensitivity; 56 | currentY += -Input.GetAxis("Mouse Y") * sensitivity; 57 | 58 | currentY = Mathf.Clamp(currentY, yAngleMin, yAngleMax); 59 | } 60 | 61 | private void LateUpdate() 62 | { 63 | RecalculatePosition(); 64 | } 65 | } 66 | } -------------------------------------------------------------------------------- /CommonAPI/Systems/AssemblerRecipeSystem/Patches/UIAssemblerWindowPatch.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection; 5 | using System.Reflection.Emit; 6 | using CommonAPI.Systems; 7 | using HarmonyLib; 8 | using UnityEngine; 9 | 10 | namespace CommonAPI.Patches 11 | { 12 | [HarmonyPatch] 13 | public static class UIAssemblerWindowPatch 14 | { 15 | [HarmonyPatch(typeof(UIAssemblerWindow), "OnSelectRecipeClick")] 16 | [HarmonyTranspiler] 17 | public static IEnumerable ChangePicker(IEnumerable instructions) 18 | { 19 | CodeMatcher matcher = new CodeMatcher(instructions) 20 | .MatchForward(false, 21 | new CodeMatch(i => i.opcode == OpCodes.Call && ((MethodInfo) i.operand).Name == "Popup")) 22 | .Advance(-1) 23 | .SetAndAdvance(OpCodes.Ldarg_0, null) 24 | .InsertAndAdvance(Transpilers.EmitDelegate>>(window => 25 | { 26 | int entityId = window.factorySystem.assemblerPool[window.assemblerId].entityId; 27 | ItemProto itemProto = LDB.items.Select(window.factory.entityPool[entityId].protoId); 28 | ERecipeType assemblerRecipeType = itemProto.prefabDesc.assemblerRecipeType; 29 | int customRecipeType = itemProto.prefabDesc.GetProperty(ExtendedAssemberDesc.RECIPE_TYPE_NAME); 30 | 31 | return proto => 32 | { 33 | if (proto.Type != assemblerRecipeType) return false; 34 | 35 | if (assemblerRecipeType == ERecipeType.Custom) 36 | { 37 | return proto.BelongsToType(customRecipeType); 38 | } 39 | return true; 40 | }; 41 | })) 42 | .SetInstruction(Transpilers.EmitDelegate, Func>>(UIRecipePickerExtension.Popup)); 43 | 44 | return matcher.InstructionEnumeration(); 45 | } 46 | } 47 | } -------------------------------------------------------------------------------- /CommonAPI/Util/Extensions/TopologicalSortExtension.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace CommonAPI 5 | { 6 | public static class TopologicalSortExtension 7 | { 8 | public class CyclicDependencyException : Exception 9 | { 10 | public CyclicDependencyException() : base("Cyclic dependency found") 11 | { 12 | } 13 | } 14 | 15 | public static IEnumerable TopologicalSort( this IEnumerable source, Func> dependencies, Action circularDepHandler) 16 | { 17 | var sorted = new List(); 18 | var visited = new HashSet(); 19 | 20 | foreach( var item in source ) 21 | Visit( item, visited, sorted, dependencies, circularDepHandler); 22 | 23 | return sorted; 24 | } 25 | 26 | public static IEnumerable GetDependants( this T source, Func> dependencies, Action circularDepHandler) 27 | { 28 | var sorted = new List(); 29 | var visited = new HashSet(); 30 | 31 | Visit( source, visited, sorted, dependencies, circularDepHandler); 32 | 33 | return sorted; 34 | } 35 | 36 | 37 | private static void Visit(T item, HashSet visited, List sorted, Func> dependencies , Action circularDepHandler) 38 | { 39 | if( !visited.Contains( item ) ) 40 | { 41 | visited.Add( item ); 42 | 43 | T lastDep = default; 44 | 45 | try 46 | { 47 | foreach (var dep in dependencies(item)) 48 | { 49 | lastDep = dep; 50 | Visit(dep, visited, sorted, dependencies, circularDepHandler); 51 | } 52 | 53 | sorted.Add( item ); 54 | } 55 | catch (CyclicDependencyException) 56 | { 57 | circularDepHandler(item, lastDep); 58 | } 59 | } 60 | else 61 | { 62 | if( !sorted.Contains( item ) ) 63 | throw new CyclicDependencyException(); 64 | } 65 | } 66 | } 67 | } -------------------------------------------------------------------------------- /CommonAPI/Systems/AssemblerRecipeSystem/AssemblerRecipeSystem.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Reflection; 4 | using CommonAPI.Patches; 5 | 6 | namespace CommonAPI.Systems 7 | { 8 | public class AssemblerRecipeSystem : BaseSubmodule 9 | { 10 | internal static Registry recipeTypes = new Registry(); 11 | internal static List> recipeTypeLists = new List>(); 12 | 13 | 14 | internal static AssemblerRecipeSystem Instance => CommonAPIPlugin.GetModuleInstance(); 15 | 16 | internal override Type[] Dependencies => new[] { typeof(PickerExtensionsSystem), typeof(CustomDescSystem), typeof(ProtoRegistry) }; 17 | 18 | internal override void SetHooks() 19 | { 20 | CommonAPIPlugin.harmony.PatchAll(typeof(AssemblerComponentPatch)); 21 | CommonAPIPlugin.harmony.PatchAll(typeof(UIAssemblerWindowPatch)); 22 | } 23 | 24 | 25 | internal override void Load() 26 | { 27 | CommonAPIPlugin.registries.Add($"{ CommonAPIPlugin.ID}:RecipeTypeRegistry", recipeTypes); 28 | recipeTypeLists.Add(null); 29 | } 30 | 31 | public static bool IsRecipeTypeRegistered(int type) 32 | { 33 | Instance.ThrowIfNotLoaded(); 34 | return type < recipeTypeLists.Count; 35 | } 36 | 37 | /// 38 | /// Register new recipe type. This can be used to create new machine types independent of vanilla machines. 39 | /// 40 | /// Unique string ID 41 | /// Assigned integer ID 42 | public static int RegisterRecipeType(string typeId) 43 | { 44 | Instance.ThrowIfNotLoaded(); 45 | int id = recipeTypes.Register(typeId); 46 | if (id >= recipeTypeLists.Capacity) 47 | { 48 | recipeTypeLists.Capacity *= 2; 49 | } 50 | 51 | recipeTypeLists.Add(new List()); 52 | return id; 53 | } 54 | 55 | internal static void BindRecipeToType(RecipeProto recipe, int type) 56 | { 57 | Instance.ThrowIfNotLoaded(); 58 | recipeTypeLists[type].Add(recipe.ID); 59 | Algorithms.ListSortedAdd(recipeTypeLists[type], recipe.ID); 60 | } 61 | } 62 | } -------------------------------------------------------------------------------- /CommonAPI/Systems/KeyBindSystem/PressKeyBind.cs: -------------------------------------------------------------------------------- 1 | namespace CommonAPI.Systems 2 | { 3 | /// 4 | /// Default implementation for KeyBind press type. 5 | /// Defines what keypresses should be detected. 6 | /// Reacts only when key is pressed 7 | /// 8 | public class PressKeyBind 9 | { 10 | /// 11 | /// Is KeyBind activated? 12 | /// 13 | public bool keyValue 14 | { 15 | get 16 | { 17 | if (!VFInput.override_keys[defaultBind.id].IsNull()) 18 | { 19 | return ReadKey(VFInput.override_keys[defaultBind.id]); 20 | } 21 | 22 | return ReadDefaultKey(); 23 | } 24 | } 25 | 26 | /// 27 | /// Default KeyBind 28 | /// 29 | public BuiltinKey defaultBind; 30 | 31 | public void Init(BuiltinKey defaultBind) 32 | { 33 | this.defaultBind = defaultBind; 34 | } 35 | 36 | /// 37 | /// Defines how this type of KeyBind should check default KeyBind 38 | /// 39 | /// If KeyBind is activated 40 | protected virtual bool ReadDefaultKey() 41 | { 42 | return ReadKey(defaultBind.key); 43 | } 44 | 45 | /// 46 | /// Defines how this type of KeyBind should check provided KeyBind 47 | /// 48 | /// Key to check 49 | /// If KeyBind is activated 50 | protected virtual bool ReadKey(CombineKey key) 51 | { 52 | return key.GetKeyDown(); 53 | } 54 | } 55 | 56 | /// 57 | /// Alternate implementation of KeyBind. Reacts only when key is held 58 | /// 59 | public class HoldKeyBind : PressKeyBind 60 | { 61 | protected override bool ReadKey(CombineKey key) 62 | { 63 | return key.GetKey(); 64 | } 65 | } 66 | 67 | /// 68 | /// Alternate implementation of KeyBind. Reacts only when key is released 69 | /// 70 | public class ReleaseKeyBind : PressKeyBind 71 | { 72 | protected override bool ReadKey(CombineKey key) 73 | { 74 | return key.GetKeyUp(); 75 | } 76 | } 77 | } -------------------------------------------------------------------------------- /CommonAPITests/Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | $(MSBuildThisFileDirectory)/../DevEnv.targets 6 | 7 | 8 | 9 | 10 | 11 | 12 | C:\Program Files (x86)\Steam\steamapps\common\Dyson Sphere Program\ 13 | $([MSBuild]::EnsureTrailingSlash('$(DSPGameDir)')) 14 | $([MSBuild]::EnsureTrailingSlash('$(SteamDir)')) 15 | $(DSPGameDir)BepInEx\core\ 16 | $(DSPGameDir)DSPGAME_Data\Managed\ 17 | $(DSPGameDir)BepInEx\plugins\CommonAPI\ 18 | $(DSPGameDir)BepInEx\DumpedAssemblies\ 19 | $(MSBuildThisFileDirectory)\ 20 | 21 | 22 | 23 | false 24 | false 25 | 26 | net472 27 | 8.0 28 | true 29 | true 30 | $(DefaultItemExcludes);*.binlog 31 | 32 | portable 33 | true 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | $(DumpedAssembliesDir)Assembly-CSharp.dll 50 | 51 | 52 | $(DSPAssemblyDir)UnityEngine.CoreModule.dll 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /CommonAPI/Systems/ProtoRegistrySystem/Patches/ProtoSet_Patch.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Reflection; 4 | using HarmonyLib; 5 | using UnityEngine; 6 | 7 | namespace CommonAPI.Patches 8 | { 9 | [HarmonyPatch] 10 | public static class ProtoSet_Patch 11 | { 12 | public static bool isEnabled = true; 13 | 14 | private static Sprite icon; 15 | private static ItemProto missingItem; 16 | 17 | static MethodBase TargetMethod() 18 | { 19 | return AccessTools.Method(typeof(ProtoSet<>).MakeGenericType(typeof(ItemProto)), "Select"); 20 | } 21 | 22 | [HarmonyPostfix] 23 | public static void ChangeSelectDefault(object __instance, int id, ref object __result) 24 | { 25 | if (!isEnabled) return; 26 | if (id <= 0) return; 27 | 28 | if (__instance is ProtoSet) 29 | { 30 | if (__result == null) 31 | { 32 | if (icon == null) 33 | { 34 | icon = CommonAPIPlugin.resource.bundle.LoadAsset("Assets/CommonAPI/Textures/Icons/missing-icon.png"); 35 | } 36 | 37 | if (missingItem == null) 38 | { 39 | missingItem = new ItemProto() 40 | { 41 | ID = id, 42 | Name = "Unknown Item", 43 | name = "Unknown Item", 44 | Type = EItemType.Material, 45 | StackSize = 500, 46 | FuelType = 0, 47 | IconPath = "", 48 | Description = "Unknown Item", 49 | description = "Unknown Item", 50 | produceFrom = "None", 51 | GridIndex = 0, 52 | DescFields = Array.Empty(), 53 | prefabDesc = PrefabDesc.none, 54 | Upgrades = Array.Empty(), 55 | recipes = new List(), 56 | handcrafts = new List(), 57 | makes = new List(), 58 | rawMats = new List(), 59 | _iconSprite = icon 60 | }; 61 | } 62 | 63 | __result = missingItem; 64 | } 65 | } 66 | } 67 | } 68 | } -------------------------------------------------------------------------------- /CommonAPI/Systems/KeyBindSystem/README.md: -------------------------------------------------------------------------------- 1 | # Custom KeyBind system 2 | Custom KeyBind system allows to register new keybinds that: 3 | - Can be rebinded by players using options menu 4 | - Have a short localized description 5 | - Have conflict groups which define which keybinds can't share same keys 6 | - Can be easily addressed in code without using static variables 7 | 8 | ### Example usage 9 | Make sure to add `[CommonAPISubmoduleDependency(nameof(CustomKeyBindSystem))]` to your plugin attributes. This will load the submodule. 10 | 11 | First you need to define default key user has to press. This is done using CombineKey class: 12 | ```cs 13 | CombineKey key = new CombineKey(int _keyCode, byte _modifier, ECombineKeyAction _action, bool _noneKey) 14 | ``` 15 | Then you can call RegisterKeyBind method to register the keybind. 16 | ```cs 17 | CustomKeyBindSystem.RegisterKeyBind(new BuiltinKey 18 | { 19 | key = key, // Default KeyBind 20 | conflictGroup = 2052, // Conflict group is a bitfield. Each bit corresponds to a key group 21 | name = "ForceBPPlace", // Name of your KeyBind. 22 | canOverride = true // Can player rebind this KeyBind? 23 | }); 24 | ``` 25 | When registering new KeyBind apart from parameters you also have to specify KeyBind Type. By default there are three types: 26 | - PressKeyBind 27 | - HoldKeyBind 28 | - ReleasekeyBind 29 | 30 | You can also define your own KeyBind type by creating new class extending PressKeyBind class 31 | 32 | You also need to register the localized string for the KeyBind. To do that use ProtoRegistry system. Do note that there is `KEY` appended to the localized string. This is always the case. 33 | ```cs 34 | ProtoRegistry.RegisterString("KEYForceBPPlace", "Force Blueprint placement", "强制蓝图放置"); 35 | ``` 36 | Finally you can use your KeyBind anywhere in your code to get current state of that key 37 | ```cs 38 | CustomKeyBindSystem.GetKeyBind("ForceBPPlace").keyValue 39 | ``` 40 | ## KeyBind conflict group descriptions 41 | As stated above `conflictGroup` is a bitfield, where each bit corresponds to a key group. No KeyBinds that share the same group can have the same Key bound to them. Vanilla game has already made use of some of these. I have listed here my assumed use for each group: 42 | Bit(In decimal) | Use 43 | ---- | ---- 44 | 1 | Player movement KeyBinds 45 | 2 | UI 46 | 4 | Build mode key #1 47 | 8 | Build mode key #2 48 | 16 | Build mode key #3 49 | 32 | Inventory keys 50 | 64 | Camera control keys #1 51 | 128 | Camera control keys #1 52 | 256 | Player Flying 53 | 512 | Player Sailing 54 | 1024 | No sure 55 | 2048 | Is this key on a keyboard 56 | 4096 | Is this key on a mouse 57 | Other| Not Used 58 | -------------------------------------------------------------------------------- /CommonAPI/Util/PointsHelper.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using UnityEngine; 4 | 5 | namespace CommonAPI 6 | { 7 | public interface IPointsAssignable 8 | { 9 | void SetPoints(Vector3[] points); 10 | } 11 | 12 | public enum PointsType 13 | { 14 | Land, 15 | Water, 16 | Insert, 17 | Belt, 18 | Custom 19 | } 20 | 21 | /// 22 | /// Helper class to assign position data to classes such as or that implement 23 | /// 24 | public class PointsHelper : MonoBehaviour 25 | { 26 | public Component target; 27 | public Transform searchPoint; 28 | public PointsType pointsType; 29 | 30 | public void Assign() 31 | { 32 | if (searchPoint == null) 33 | { 34 | searchPoint = gameObject.transform; 35 | } 36 | 37 | Transform[] transforms = searchPoint.Cast().ToArray(); 38 | Vector3[] points = transforms.Select(trs => trs.position).ToArray(); 39 | if (target == null) 40 | { 41 | target = GetComponent(); 42 | }else if (pointsType == PointsType.Land || pointsType == PointsType.Water) 43 | { 44 | target = target.gameObject.GetComponent(); 45 | }else if (pointsType == PointsType.Insert || pointsType == PointsType.Belt) 46 | { 47 | target = target.gameObject.GetComponent(); 48 | } 49 | 50 | if (target == null) return; 51 | 52 | if (target is IPointsAssignable trg && pointsType == PointsType.Custom) 53 | { 54 | trg.SetPoints(points); 55 | return; 56 | } 57 | 58 | if (target is BuildConditionConfig config) 59 | { 60 | if (pointsType == PointsType.Land) 61 | { 62 | config.landPoints = points; 63 | } 64 | else 65 | { 66 | config.waterPoints = points; 67 | } 68 | } 69 | 70 | if (target is SlotConfig slots) 71 | { 72 | if (pointsType == PointsType.Insert) 73 | { 74 | slots.insertPoses = transforms; 75 | } 76 | else 77 | { 78 | slots.slotPoses = transforms; 79 | } 80 | } 81 | } 82 | } 83 | } -------------------------------------------------------------------------------- /CommonAPI/Systems/ComponentSystem/UI/Widgets/UIPowerIndicator.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | using UnityEngine; 3 | using UnityEngine.UI; 4 | 5 | namespace CommonAPI.Systems 6 | { 7 | public class UIPowerIndicator : MonoBehaviour 8 | { 9 | public Image powerIcon; 10 | public Text powerText; 11 | public Text stateText; 12 | 13 | public ColorProperties colors; 14 | private StringBuilder powerServedSb; 15 | 16 | public CustomMachineWindow window; 17 | 18 | public void Init(CustomMachineWindow window) 19 | { 20 | this.window = window; 21 | powerServedSb = new StringBuilder(" W %", 20); 22 | } 23 | 24 | public void OnUpdate(int pcId) 25 | { 26 | PowerConsumerComponent powerConsumerComponent = window.powerSystem.consumerPool[pcId]; 27 | int networkId = powerConsumerComponent.networkId; 28 | PowerNetwork powerNetwork = window.powerSystem.netPool[networkId]; 29 | float num = powerNetwork == null || networkId <= 0 ? 0f : (float) powerNetwork.consumerRatio; 30 | double num2 = powerConsumerComponent.requiredEnergy * 60L; 31 | long valuel = (long) (num2 * num); 32 | StringBuilderUtility.WriteKMG(powerServedSb, 8, valuel); 33 | StringBuilderUtility.WriteUInt(powerServedSb, 12, 3, (uint) (num * 100f)); 34 | if (num == 1f) 35 | { 36 | powerText.text = powerServedSb.ToString(); 37 | powerIcon.color = colors.powerNormalIconColor; 38 | powerText.color = colors.powerNormalColor; 39 | } 40 | else if (num > 0.1f) 41 | { 42 | powerText.text = powerServedSb.ToString(); 43 | powerIcon.color = colors.powerLowIconColor; 44 | powerText.color = colors.powerLowColor; 45 | } 46 | else 47 | { 48 | powerText.text = "未供电".Translate(); 49 | powerIcon.color = Color.clear; 50 | powerText.color = colors.powerOffColor; 51 | } 52 | 53 | if (num == 1f) 54 | { 55 | stateText.text = "待机".Translate(); 56 | stateText.color = colors.idleColor; 57 | } 58 | else if (num > 0.1f) 59 | { 60 | stateText.text = "电力不足".Translate(); 61 | stateText.color = colors.powerLowColor; 62 | } 63 | else 64 | { 65 | stateText.text = "停止运转".Translate(); 66 | stateText.color = colors.powerOffColor; 67 | } 68 | } 69 | } 70 | } -------------------------------------------------------------------------------- /CommonAPI/Systems/README.md: -------------------------------------------------------------------------------- 1 | # Submodules 2 | ## What is a submodule 3 | Each folder on this page defines a submodule. Submodule is a building block of Common API. Each submodule has a single responsibility. For example ProtoRegistrySystem allows to register new Proto objects into game's LDB to add new content. 4 | 5 | A submodule is not loaded unless another mod requests that. This is needed to ensure that CommonAPI does not break in an event where one of submodules gets broken due to game update. If that happens the player only needs to uninstall all mods that depend on the broken module, and other mods will continue to work. Because of this it is preferable to split unrelated functions into different submodules. 6 | 7 | ## Using submodules in your mods 8 | To use any submodule you have to declare that in your plugin class. Use `CommonAPISubmoduleDependency` to declare all used submodules. make sure to only request submodules your code actually uses. Also don't forget to declare a dependency on CommonAPI plugin. 9 | ```cs 10 | [BepInPlugin(GUID, NAME, VERSION)] 11 | 12 | [BepInDependency(CommonAPIPlugin.GUID)] 13 | [CommonAPISubmoduleDependency(nameof(ProtoRegistry), nameof(CustomDescSystem))] 14 | public class MyPlugin : BaseUnityPlugin 15 | { 16 | public const string MODID = "myplugin"; 17 | public const string GUID = "org.myname.plugin." + MODID; 18 | public const string NAME = "My Plugin"; 19 | 20 | void Awake() 21 | { 22 | //Make use of modules here 23 | } 24 | } 25 | ``` 26 | 27 | ## Creating Submodules 28 | To create a new submodule you need to create a new folder with the name of the module. In it create a new class with the same name. Here is a template of submodule class: 29 | ```cs 30 | public static class SubmoduleName : BaseSubmodule 31 | { 32 | 33 | public static void SomeAPIMethod() 34 | { 35 | // Ensure that you call this method in ALL interface methods 36 | // This ensures that if your module is not loaded, a error will be thrown 37 | Instance.ThrowIfNotLoaded(); 38 | } 39 | 40 | internal static SubmoduleName Instance => CommonAPIPlugin.GetModuleInstance(); 41 | 42 | // To declare submodule dependency use this property 43 | internal override Type[] Dependencies => new[] { typeof(LocalizationModule) }; 44 | 45 | internal override void SetHooks() 46 | { 47 | // Register all patches needed for this submodule here 48 | } 49 | 50 | internal override void load() 51 | { 52 | // Other actions not related to patches can be done here 53 | } 54 | 55 | internal override void PostLoad() 56 | { 57 | // This method will be called after all modules are loaded 58 | // Here you can use other modules functions. 59 | } 60 | } 61 | 62 | ``` 63 | -------------------------------------------------------------------------------- /CommonAPI/Systems/TabSystem/Patches/UIReplicatorPatch.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Reflection.Emit; 4 | using CommonAPI.Systems; 5 | using HarmonyLib; 6 | using UnityEngine; 7 | using Object = UnityEngine.Object; 8 | 9 | namespace CommonAPI.Patches 10 | { 11 | 12 | [HarmonyPatch] 13 | public static class UIReplicatorPatch 14 | { 15 | private static List tabs; 16 | 17 | [HarmonyPatch(typeof(UIReplicatorWindow), "_OnCreate")] 18 | [HarmonyPostfix] 19 | public static void Create(UIReplicatorWindow __instance) 20 | { 21 | var datas = TabSystem.GetAllTabs(); 22 | tabs = new List(datas.Length - 3); 23 | 24 | foreach (TabData tab in datas) 25 | { 26 | if (tab == null) continue; 27 | 28 | GameObject button = Object.Instantiate(TabSystem.GetTabPrefab(), __instance.recipeGroup, false); 29 | ((RectTransform)button.transform).anchoredPosition = new Vector2( 70 * tab.tabIndex - 95, 50); 30 | UITabButton tabButton = button.GetComponent(); 31 | Sprite sprite = Resources.Load(tab.tabIconPath); 32 | tabButton.Init(sprite, tab.tabName, tab.tabIndex, __instance.OnTypeButtonClick); 33 | tabs.Add(tabButton); 34 | } 35 | } 36 | 37 | [HarmonyPatch(typeof(UIReplicatorWindow), "SetSelectedRecipe")] 38 | [HarmonyTranspiler] 39 | static IEnumerable AddNewProperty(IEnumerable instructions) 40 | { 41 | CodeMatcher matcher = new CodeMatcher(instructions) 42 | .MatchForward(false, 43 | new CodeMatch(OpCodes.Ldloc_0), 44 | new CodeMatch(OpCodes.Ldc_I4_1) 45 | ).Advance(2); 46 | 47 | Label continueLabel = (Label)matcher.Operand; 48 | 49 | matcher.Advance(-1) 50 | .InsertAndAdvance(Transpilers.EmitDelegate>(type => type >= 3 && type < TabSystem.tabsRegistry.data.Count)) 51 | .InsertAndAdvance(new CodeInstruction(OpCodes.Brtrue_S, continueLabel)) 52 | .InsertAndAdvance(new CodeInstruction(OpCodes.Ldloc_0)); 53 | 54 | return matcher.InstructionEnumeration(); 55 | } 56 | 57 | 58 | [HarmonyPatch(typeof(UIReplicatorWindow), "OnTypeButtonClick")] 59 | [HarmonyPostfix] 60 | public static void OnTypeClicked(int type) 61 | { 62 | foreach (UITabButton tab in tabs) 63 | { 64 | tab.TabSelected(type); 65 | } 66 | } 67 | } 68 | } -------------------------------------------------------------------------------- /CommonAPI/Systems/PickerExtensionSystem/UI/UIShowSignalTipExtension.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | namespace CommonAPI.Systems.UI 4 | { 5 | public class UIShowSignalTipExtension : IUpdatePickerExtension 6 | { 7 | private float mouseInTime; 8 | private UISignalTip screenSignalTip; 9 | 10 | public void OnUpdate(UISignalPicker picker) 11 | { 12 | int xPos = -1; 13 | int yPos = -1; 14 | int signalId = 0; 15 | if (picker.hoveredIndex >= 0) 16 | { 17 | signalId = picker.signalArray[picker.hoveredIndex]; 18 | xPos = picker.hoveredIndex % 14; 19 | yPos = picker.hoveredIndex / 14; 20 | } 21 | 22 | if (signalId != 0 && signalId < 1000) 23 | { 24 | mouseInTime += Time.deltaTime; 25 | if (mouseInTime > picker.showItemTipsDelay) 26 | { 27 | if (screenSignalTip == null) 28 | { 29 | screenSignalTip = UISignalTip.Create(signalId, picker.itemTipAnchor, new Vector2(xPos * 46 + 15, -(float)yPos * 46 - 50), picker.iconImage.transform); 30 | } 31 | if (!screenSignalTip.gameObject.activeSelf) 32 | { 33 | screenSignalTip.gameObject.SetActive(true); 34 | screenSignalTip.SetTip(signalId, picker.itemTipAnchor, new Vector2(xPos * 46 + 15, -(float)yPos * 46 - 50), picker.iconImage.transform); 35 | return; 36 | } 37 | if (screenSignalTip.showingSignalId != signalId) 38 | { 39 | screenSignalTip.SetTip(signalId, picker.itemTipAnchor, new Vector2(xPos * 46 + 15, -(float)yPos * 46 - 50), picker.iconImage.transform); 40 | } 41 | } 42 | } 43 | else 44 | { 45 | CloseTip(); 46 | } 47 | } 48 | 49 | public void Open(UISignalPicker picker) 50 | { 51 | CloseTip(); 52 | } 53 | 54 | public void Close(UISignalPicker picker) 55 | { 56 | CloseTip(); 57 | } 58 | 59 | private void CloseTip() 60 | { 61 | if (mouseInTime > 0f) 62 | { 63 | mouseInTime = 0f; 64 | } 65 | 66 | if (screenSignalTip != null) 67 | { 68 | screenSignalTip.showingSignalId = 0; 69 | screenSignalTip.gameObject.SetActive(false); 70 | } 71 | } 72 | 73 | public void OnPopup(UISignalPicker picker) 74 | { 75 | } 76 | 77 | public void PostPopup(UISignalPicker picker) 78 | { 79 | } 80 | } 81 | } -------------------------------------------------------------------------------- /CommonAPI/Systems/LocalizationModule/TextDefaultFont.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using HarmonyLib; 3 | using UnityEngine; 4 | using UnityEngine.UI; 5 | 6 | namespace CommonAPI.Systems.ModLocalization 7 | { 8 | /// 9 | /// Additional container for Text component - this should be always present but it's not a monobehaviour. 10 | /// Use static api of to request underlying font information 11 | /// 12 | public class TextDefaultFont 13 | { 14 | /// 15 | /// Field info helper to access font data 16 | /// 17 | private static readonly FieldInfo FieldInfo_FontData = AccessTools.Field(typeof(Text), "m_FontData"); 18 | 19 | /// 20 | /// Default font used by this component 21 | /// 22 | public Font DefaultFont; 23 | 24 | /// 25 | /// Stored text refernece 26 | /// 27 | public Text Reference; 28 | 29 | /// 30 | /// FontData private field reference 31 | /// 32 | private FontData FontData; 33 | 34 | /// 35 | /// Default constructor 36 | /// 37 | /// 38 | public TextDefaultFont(Text reference) 39 | { 40 | Reference = reference; 41 | FontData = (FontData)FieldInfo_FontData.GetValue(Reference); 42 | DefaultFont = FontData.font; 43 | } 44 | 45 | /// 46 | /// Method invoked exclusively by 47 | /// 48 | public void OnGetFont() 49 | { 50 | if (DefaultFont == null || FontData == null) return; 51 | 52 | int currentLanguageId = Localization.CurrentLanguageLCID; 53 | if (!LocalizationModule.extraDataEntires.ContainsKey(currentLanguageId)) return; 54 | 55 | var languageData = LocalizationModule.extraDataEntires[currentLanguageId]; 56 | 57 | if (languageData.customFont != null) 58 | { 59 | if (FontData.font != languageData.customFont) 60 | { 61 | FontData.font = languageData.customFont; 62 | } 63 | } 64 | else 65 | { 66 | if (FontData.font != DefaultFont) 67 | { 68 | FontData.font = DefaultFont; 69 | } 70 | } 71 | } 72 | 73 | /// 74 | /// Apply custom fond immediately skipping TextFontManager 75 | /// 76 | /// 77 | public void UseCustomFontImmediate(Font fontToUse) 78 | { 79 | Reference.font = fontToUse; 80 | } 81 | } 82 | } -------------------------------------------------------------------------------- /CommonAPIPreloader/Preloader.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using BepInEx.Logging; 5 | using Mono.Cecil; 6 | using Mono.Cecil.Rocks; 7 | 8 | 9 | namespace CommonAPI 10 | { 11 | public static class Preloader 12 | { 13 | public static ManualLogSource logSource; 14 | 15 | public static void Initialize() 16 | { 17 | logSource = Logger.CreateLogSource("Common API Preloader"); 18 | } 19 | // List of assemblies to patch 20 | // ReSharper disable once InconsistentNaming 21 | public static IEnumerable TargetDLLs { get; } = new[] {"Assembly-CSharp.dll"}; 22 | 23 | // Patches the assemblies 24 | public static void Patch(AssemblyDefinition assembly) 25 | { 26 | ModuleDefinition module = assembly.MainModule; 27 | TypeDefinition entityData = module.Types.First (t => t.FullName == "EntityData"); 28 | TypeDefinition prefabDesc = module.Types.First (t => t.FullName == "PrefabDesc"); 29 | 30 | TypeDefinition recipeType = module.Types.First (t => t.FullName == "ERecipeType"); 31 | 32 | bool flag = entityData == null || prefabDesc == null || recipeType == null; 33 | if (flag) 34 | { 35 | logSource.LogInfo("Preloader patching failed!"); 36 | return; 37 | } 38 | 39 | //FieldDefinition field = new FieldDefinition("customType", FieldAttributes.Public, module.ImportReference(typeof(int))); 40 | //recipeProto.Fields.Add(field); 41 | //var ca_2 = new CustomAttribute(assembly.MainModule.ImportReference(typeof(NonSerializedAttribute).GetConstructor(new Type[] {}))); 42 | //field.CustomAttributes.Add(ca_2); 43 | 44 | var enumCustomValue = new FieldDefinition("Custom", FieldAttributes.Static | FieldAttributes.Literal | FieldAttributes.Public | FieldAttributes.HasDefault, recipeType) { Constant = 20 }; 45 | recipeType.Fields.Add(enumCustomValue); 46 | 47 | entityData.Fields.Add(new FieldDefinition("customId", FieldAttributes.Public, module.ImportReference(typeof(int)))); 48 | entityData.Fields.Add(new FieldDefinition("customType", FieldAttributes.Public, module.ImportReference(typeof(int)))); 49 | 50 | entityData.Fields.Add(new FieldDefinition("customData", FieldAttributes.Public, 51 | module.ImportReference(typeof(Dictionary<,>)).MakeGenericInstanceType(module.TypeSystem.String, module.TypeSystem.Object))); 52 | 53 | prefabDesc.Fields.Add(new FieldDefinition("customData", FieldAttributes.Public, 54 | module.ImportReference(typeof(Dictionary<,>)).MakeGenericInstanceType(module.TypeSystem.String, module.TypeSystem.Object))); 55 | 56 | logSource.LogInfo("Preloader patching is successful!"); 57 | } 58 | } 59 | } -------------------------------------------------------------------------------- /CommonAPI/Systems/PickerExtensionSystem/PickerExtensionsSystem.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using CommonAPI.Patches; 3 | using CommonAPI.Systems.ModLocalization; 4 | 5 | namespace CommonAPI.Systems 6 | { 7 | public class PickerExtensionsSystem : BaseSubmodule 8 | { 9 | internal static PickerExtensionsSystem Instance => CommonAPIPlugin.GetModuleInstance(); 10 | internal override Type[] Dependencies => new[] { typeof(LocalizationModule) }; 11 | 12 | 13 | internal override void SetHooks() 14 | { 15 | CommonAPIPlugin.harmony.PatchAll(typeof(UIItemPicker_Patch)); 16 | CommonAPIPlugin.harmony.PatchAll(typeof(UIRecipePicker_Patch)); 17 | CommonAPIPlugin.harmony.PatchAll(typeof(UISignalPicker_Patch)); 18 | } 19 | 20 | internal override void PostLoad() 21 | { 22 | LocalizationModule.RegisterTranslation("SIGNAL-401", "Signal Information"); 23 | LocalizationModule.RegisterTranslation("SIGNAL-402", "Signal Warning"); 24 | LocalizationModule.RegisterTranslation("SIGNAL-403", "Signal Critical warning"); 25 | LocalizationModule.RegisterTranslation("SIGNAL-404", "Signal Error"); 26 | LocalizationModule.RegisterTranslation("SIGNAL-405", "Signal Settings"); 27 | 28 | LocalizationModule.RegisterTranslation("SIGNAL-501", "Signal Missing power connection"); 29 | LocalizationModule.RegisterTranslation("SIGNAL-502", "Signal Not Enough Power"); 30 | LocalizationModule.RegisterTranslation("SIGNAL-503", "Signal Lightning"); 31 | LocalizationModule.RegisterTranslation("SIGNAL-504", "Signal Set Recipe"); 32 | LocalizationModule.RegisterTranslation("SIGNAL-506", "Signal Product stacking"); 33 | LocalizationModule.RegisterTranslation("SIGNAL-507", "Signal Vein depleting"); 34 | LocalizationModule.RegisterTranslation("SIGNAL-508", "Signal No fuel"); 35 | LocalizationModule.RegisterTranslation("SIGNAL-509", "Signal Can't do"); 36 | LocalizationModule.RegisterTranslation("SIGNAL-510", "Signal Missing connection"); 37 | 38 | for (int i = 0; i < 10; i++) 39 | { 40 | LocalizationModule.RegisterTranslation($"SIGNAL-60{i}", $"Signal {i}"); 41 | } 42 | 43 | LocalizationModule.RegisterTranslation("setCountManually", "Select value"); 44 | LocalizationModule.RegisterTranslation("CountLabel", "Value"); 45 | LocalizationModule.RegisterTranslation("ConfirmButtonLabel", "Confirm"); 46 | 47 | ProtoRegistry.onLoadingFinished += () => 48 | { 49 | for (int i = 4; i <= 6; i++) 50 | { 51 | for (int j = 0; j <= 10; j++) 52 | { 53 | SignalProto proto = LDB.signals.Select(i*100+j); 54 | proto?.Preload(); 55 | } 56 | } 57 | }; 58 | } 59 | } 60 | } -------------------------------------------------------------------------------- /CommonAPI/Systems/PickerExtensionSystem/UIItemPickerExtension.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using UnityEngine; 3 | 4 | namespace CommonAPI.Systems 5 | { 6 | public class PickerNotReadyException : Exception 7 | { 8 | public PickerNotReadyException() : base("UIItemPicker is not ready!") { } 9 | } 10 | 11 | public class UIItemPickerExtension 12 | { 13 | public static Func currentFilter; 14 | public static bool showLocked = false; 15 | 16 | public static IPickerExtension currentExtension; 17 | 18 | 19 | public static UIItemPicker PreparePicker() 20 | { 21 | PickerExtensionsSystem.Instance.ThrowIfNotLoaded(); 22 | if (UIRoot.instance == null) 23 | { 24 | throw new PickerNotReadyException(); 25 | } 26 | 27 | UIItemPicker itemPicker = UIRoot.instance.uiGame.itemPicker; 28 | if (!itemPicker.inited || itemPicker.active) 29 | { 30 | throw new PickerNotReadyException(); 31 | } 32 | 33 | return itemPicker; 34 | } 35 | 36 | public static void Popup(Vector2 pos, Action _onReturn, bool showLockedItems, Func filter) 37 | { 38 | PickerExtensionsSystem.Instance.ThrowIfNotLoaded(); 39 | try 40 | { 41 | showLocked = showLockedItems; 42 | currentExtension = null; 43 | UIItemPicker itemPicker = PreparePicker(); 44 | if (itemPicker == null) 45 | { 46 | _onReturn?.Invoke(null); 47 | } 48 | 49 | currentFilter = filter; 50 | 51 | itemPicker.onReturn = _onReturn; 52 | itemPicker._Open(); 53 | itemPicker.pickerTrans.anchoredPosition = pos; 54 | } 55 | catch (PickerNotReadyException) 56 | { 57 | _onReturn?.Invoke(null); 58 | } 59 | } 60 | 61 | public static void Popup(Vector2 pos, Action _onReturn, Func filter) 62 | { 63 | Popup(pos, _onReturn, false, filter); 64 | } 65 | 66 | public static void Popup(Vector2 pos, IPickerExtension extension) 67 | { 68 | PickerExtensionsSystem.Instance.ThrowIfNotLoaded(); 69 | try 70 | { 71 | // ReSharper disable once SuspiciousTypeConversion.Global 72 | showLocked = extension is ShowLocked; 73 | currentExtension = extension; 74 | UIItemPicker itemPicker = PreparePicker(); 75 | 76 | extension.OnPopup(itemPicker); 77 | 78 | itemPicker._Open(); 79 | itemPicker.pickerTrans.anchoredPosition = pos; 80 | 81 | extension.PostPopup(itemPicker); 82 | } 83 | catch (PickerNotReadyException) { } 84 | } 85 | } 86 | } -------------------------------------------------------------------------------- /SharedConfig.targets: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | $(MSBuildThisFileDirectory)/DevEnv.targets 7 | 8 | 9 | 10 | C:\Program Files (x86)\Steam\steamapps\common\Dyson Sphere Program\ 11 | $([MSBuild]::EnsureTrailingSlash('$(DSPGameDir)')) 12 | $(DSPGameDir)BepInEx\DumpedAssemblies\DSPGAME\ 13 | $(MSBuildThisFileDirectory)\ 14 | $([MSBuild]::EnsureTrailingSlash('$(UnityProject)'))Assets\StreamingAssets\AssetBundles\commonapi 15 | 0 16 | 17 | 18 | 22 | 23 | 26 | 27 | 28 | 29 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 40 | 41 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 55 | 56 | -------------------------------------------------------------------------------- /CommonAPI/Systems/CustomDescSystem/ConfigurableDesc.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | using BepInEx.Configuration; 4 | using HarmonyLib; 5 | 6 | namespace CommonAPI.Systems 7 | { 8 | [AttributeUsage(AttributeTargets.Field)] 9 | public class UseConfigFile : Attribute 10 | { 11 | public string description; 12 | 13 | public UseConfigFile(){} 14 | 15 | public UseConfigFile(string description) 16 | { 17 | this.description = description; 18 | } 19 | } 20 | 21 | public abstract class ConfigurableDesc : CustomDesc 22 | { 23 | public int tier; 24 | 25 | public abstract string configCategory { get; } 26 | public abstract ConfigFile modConfig { get; } 27 | 28 | internal static MethodInfo bindMethod; 29 | 30 | static ConfigurableDesc() 31 | { 32 | MethodInfo[] methods = typeof(ConfigFile).GetMethods(BindingFlags.Instance | BindingFlags.Public); 33 | foreach (MethodInfo method in methods) 34 | { 35 | ParameterInfo[] parameters = method.GetParameters(); 36 | if (method.Name != nameof(ConfigFile.Bind) || parameters.Length != 4) continue; 37 | 38 | if (parameters[0].ParameterType == typeof(string) && parameters[1].ParameterType == typeof(string) 39 | && parameters[3].ParameterType == typeof(string)) 40 | { 41 | bindMethod = method; 42 | return; 43 | } 44 | } 45 | 46 | CommonAPIPlugin.logger.LogDebug("Failed to find MethodInfo for ConfigFile.Bind!"); 47 | 48 | } 49 | 50 | public override void ApplyProperties(PrefabDesc desc) 51 | { 52 | FieldInfo[] fields = GetType().GetFields(BindingFlags.Instance | BindingFlags.Public); 53 | foreach (FieldInfo field in fields) 54 | { 55 | if (!Attribute.IsDefined(field, typeof(UseConfigFile))) continue; 56 | 57 | UseConfigFile[] attributes = (UseConfigFile[])field.GetCustomAttributes(false); 58 | if (attributes.Length <= 0) continue; 59 | 60 | MethodInfo method = bindMethod.MakeGenericMethod(field.FieldType); 61 | object entry = method.Invoke(modConfig, 62 | new[] {configCategory, $"Tier-{tier}_{field.Name}", field.GetValue(this), attributes[0].description}); 63 | 64 | Type entryGenericType = typeof(ConfigEntry<>); 65 | Type entryType = entryGenericType.MakeGenericType(field.FieldType); 66 | 67 | PropertyInfo valueProperty = entryType.GetProperty(nameof(ConfigEntry.BoxedValue)); 68 | 69 | object value = valueProperty.GetValue(entry); 70 | field.SetValue(this, value); 71 | } 72 | } 73 | } 74 | } -------------------------------------------------------------------------------- /CommonAPI/Systems/ComponentSystem/Patches/UIGamePatch.cs: -------------------------------------------------------------------------------- 1 | using HarmonyLib; 2 | using CommonAPI; 3 | using CommonAPI.Systems; 4 | 5 | // ReSharper disable InconsistentNaming 6 | 7 | namespace CommonAPI.Patches 8 | { 9 | [HarmonyPatch] 10 | public class UIGamePatch 11 | { 12 | [HarmonyPatch(typeof(UIGame), "_OnCreate")] 13 | [HarmonyPostfix] 14 | public static void OnCreate(UIGame __instance) 15 | { 16 | CustomMachineUISystem.OnCreate(__instance); 17 | } 18 | 19 | [HarmonyPatch(typeof(UIGame), "_OnDestroy")] 20 | [HarmonyPostfix] 21 | public static void OnDestroy() 22 | { 23 | foreach (var window in CustomMachineUISystem.windows) 24 | { 25 | window._Destroy(); 26 | } 27 | } 28 | 29 | [HarmonyPatch(typeof(UIGame), "_OnInit")] 30 | [HarmonyPostfix] 31 | public static void OnInit(UIGame __instance) 32 | { 33 | foreach (var window in CustomMachineUISystem.windows) 34 | { 35 | window._Init(__instance.gameData); 36 | window.Open(0); 37 | } 38 | } 39 | 40 | [HarmonyPatch(typeof(UIGame), "_OnFree")] 41 | [HarmonyPostfix] 42 | public static void OnFree() 43 | { 44 | foreach (var window in CustomMachineUISystem.windows) 45 | { 46 | window._Free(); 47 | } 48 | } 49 | 50 | [HarmonyPatch(typeof(UIGame), "_OnUpdate")] 51 | [HarmonyPostfix] 52 | public static void OnUpdate() 53 | { 54 | foreach (var window in CustomMachineUISystem.windows) 55 | { 56 | window.OnUpdateUI(); 57 | } 58 | } 59 | 60 | [HarmonyPatch(typeof(UIGame), "ShutInventoryConflictsWindows")] 61 | [HarmonyPatch(typeof(UIGame), "ShutAllFunctionWindow")] 62 | [HarmonyPostfix] 63 | public static void ShutAllFunctionWindow() 64 | { 65 | foreach (var window in CustomMachineUISystem.windows) 66 | { 67 | window.Close(); 68 | } 69 | } 70 | 71 | [HarmonyPatch(typeof(UIGame), "isAnyFunctionWindowActive", MethodType.Getter)] 72 | [HarmonyPostfix] 73 | public static void IsAnyActive(ref bool __result) 74 | { 75 | if (__result) return; 76 | 77 | foreach (var window in CustomMachineUISystem.windows) 78 | { 79 | if (window.active) 80 | { 81 | __result = true; 82 | return; 83 | } 84 | } 85 | } 86 | 87 | [HarmonyPatch(typeof(UIGame), "OnPlayerInspecteeChange")] 88 | [HarmonyPostfix] 89 | public static void OnPlayerInspecteeChange(UIGame __instance, EObjectType objType, int objId) 90 | { 91 | CustomMachineUISystem.OnPlayerInspecteeChange(__instance, objType, objId); 92 | } 93 | } 94 | } -------------------------------------------------------------------------------- /CommonAPI/Systems/LocalizationModule/README.md: -------------------------------------------------------------------------------- 1 | # Localization Module 2 | Localization module allows to add new translations, add new translation keys and edit existing translations. 3 | 4 | ## Example usage 5 | Make sure to add `[CommonAPISubmoduleDependency(nameof(LocalizationModule))]` to your plugin attributes. This will load the submodule. 6 | 7 | ## Adding new translations 8 | There are multiple ways of adding translations. 9 | 10 | ### Through method calls 11 | using method `RegisterTranslation()`. This method is useful if you don't have a lot of strings. Example: 12 | 13 | ```cs 14 | LocalizationModule.RegisterTranslation( 15 | "copperWireDesc", 16 | "By extruding copper we can make a component which allows current to be carried"); 17 | ``` 18 | 19 | ### By loading a set of localization files. 20 | 21 | To do this make the following structure in your plugin folder: 22 | 23 | ![Folder Structure](./documentation/folders.png)
24 | 25 | You can use language ids or abbreviations, whichever you like most. For example enUS id is `1033`. For modded languages, abbreviations are the only option. 26 | 27 | Inside language folders you need to add at least one file (Can be named freely, including file extension) as you want with following structure: 28 | 29 | ![Folder Structure](./documentation/file-format.png)
30 | 31 | There shouldn't be any headers, and in total 4 values tab separated. First is key, and last is the translation. 2'nd and 3'rd values aren't used, and only exist for compatibility with vanilla format. 32 | 33 | Then in your plugin awake do this: 34 | 35 | ```cs 36 | string pluginfolder = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); 37 | string localePath = Path.Combine(pluginfolder, "Locale"); 38 | 39 | LocalizationModule.LoadTranslationsFromFolder(localePath); 40 | ``` 41 | 42 | ### From raw data strings 43 | This method can be useful if you want to load translations from a bundle. To do that, read all text of your files and call this method in your awake method: 44 | 45 | ```cs 46 | var rawString = "bla bla"; // load the raw string data here 47 | LocalizationModule.LoadTranslationsFromString(rawString, "enUS"); 48 | ``` 49 | 50 | The raw string must follow the same format, as described in the file section. 51 | 52 | ## Adding custom languages 53 | Localization module supports adding new languages, to do that in your plugin awake call: 54 | 55 | ```cs 56 | LocalizationModule.AddLanguage(new Localization.Language() 57 | { 58 | name = "German", 59 | abbr = "deDE", 60 | abbr2 = "de", 61 | fallback = Localization.LCID_ENUS, 62 | glyph = Localization.EGlyph.Latin 63 | }); 64 | ``` 65 | 66 | From here you can add translations using all ways by using `deDE` abbreviation as the key or folder name. By doing this you can even add custom localizations for the game. 67 | 68 | ## Editing existing localizations 69 | Localization module also allows to edit existing localizations. To do so write this in your plugin awake: 70 | 71 | ```cs 72 | LocalizationModule.EditTranslation("非常困难", "Very Easy!"); 73 | ``` 74 | The localization must already exist, for this to work. You also can edit other mods localizations. 75 | -------------------------------------------------------------------------------- /CommonAPI/Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | $(MSBuildThisFileDirectory)/../DevEnv.targets 6 | 7 | 8 | 9 | 10 | 11 | 12 | C:\Program Files (x86)\Steam\steamapps\common\Dyson Sphere Program\ 13 | $([MSBuild]::EnsureTrailingSlash('$(DSPGameDir)')) 14 | $([MSBuild]::EnsureTrailingSlash('$(SteamDir)')) 15 | $(DSPGameDir)BepInEx\core\ 16 | $(DSPGameDir)DSPGAME_Data\Managed\ 17 | $(DSPGameDir)BepInEx\plugins\CommonAPI\ 18 | $(DSPGameDir)BepInEx\DumpedAssemblies\ 19 | $(MSBuildThisFileDirectory)\ 20 | 0 21 | 22 | 23 | 24 | false 25 | false 26 | 27 | $(PluginOutputDirectory) 28 | $(MSBuildThisFileDirectory)\..\Staging\plugins\ 29 | $(OutputPath) 30 | net472 31 | 8.0 32 | true 33 | true 34 | $(DefaultItemExcludes);*.binlog 35 | _UNLOCK_DEBUG 36 | 37 | portable 38 | true 39 | 40 | $(AssemblyVersion) 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /CommonAPI/NebulaCompatPlugin.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | using BepInEx; 4 | using CommonAPI.Nebula; 5 | using CommonAPI.Systems; 6 | using NebulaAPI; 7 | using NebulaAPI.Interfaces; 8 | 9 | namespace CommonAPI 10 | { 11 | [BepInPlugin(GUID, NAME, CommonAPIPlugin.VERSION)] 12 | [BepInDependency(NebulaModAPI.API_GUID)] 13 | [BepInDependency(CommonAPIPlugin.GUID)] 14 | public class NebulaCompatPlugin : BaseUnityPlugin, IMultiplayerMod 15 | { 16 | public const string ID = "common-api-nebula-compat"; 17 | public const string GUID = "dsp.common-tools." + ID; 18 | public const string NAME = "Common API Nebula Compatibility"; 19 | 20 | 21 | private void Start() 22 | { 23 | //Moved Custom Star and Planet Systems behavior here for easier compatibility with Nebula 24 | if (CommonAPIPlugin.IsSubmoduleLoaded(nameof(PlanetExtensionSystem))) 25 | { 26 | NebulaModAPI.OnPlanetLoadRequest += planetId => 27 | { 28 | NebulaModAPI.MultiplayerSession.Network.SendPacket(new PlanetSystemLoadRequest(planetId)); 29 | }; 30 | } 31 | 32 | if (CommonAPIPlugin.IsSubmoduleLoaded(nameof(StarExtensionSystem))) 33 | { 34 | NebulaModAPI.OnStarLoadRequest += starIndex => 35 | { 36 | NebulaModAPI.MultiplayerSession.Network.SendPacket(new StarExtensionLoadRequest(starIndex)); 37 | }; 38 | } 39 | 40 | NebulaModAPI.RegisterPackets(Assembly.GetExecutingAssembly()); 41 | CommonAPIPlugin.onIntoOtherSave = CheckNebulaInIntoOtherSave; 42 | PlanetExtensionSystem.onInitNewPlanet = HandleNebulaPacket; 43 | 44 | Logger.LogInfo("Common API Nebula Compatibility ready!"); 45 | } 46 | 47 | public static void CheckNebulaInIntoOtherSave() 48 | { 49 | if (NebulaModAPI.IsMultiplayerActive && !NebulaModAPI.MultiplayerSession.LocalPlayer.IsHost) 50 | { 51 | foreach (var kv in CommonAPIPlugin.registries) 52 | { 53 | kv.Value.InitUnitMigrationMap(); 54 | } 55 | } 56 | } 57 | 58 | public static void HandleNebulaPacket(PlanetData planet) 59 | { 60 | if (!NebulaModAPI.IsMultiplayerActive || NebulaModAPI.MultiplayerSession.LocalPlayer.IsHost) return; 61 | if (!PlanetExtensionSystem.pendingData.TryGetValue(planet.id, out byte[] bytes)) return; 62 | PlanetExtensionSystem.pendingData.Remove(planet.id); 63 | 64 | using IReaderProvider p = NebulaModAPI.GetBinaryReader(bytes); 65 | 66 | for (int i = 1; i < PlanetExtensionSystem.registry.data.Count; i++) 67 | { 68 | PlanetExtensionStorage extension = PlanetExtensionSystem.extensions[i]; 69 | extension.GetExtension(planet.factory).Import(p.BinaryReader); 70 | } 71 | } 72 | 73 | public bool CheckVersion(string hostVersion, string clientVersion) 74 | { 75 | return hostVersion.Equals(clientVersion); 76 | } 77 | 78 | public string Version => CommonAPIPlugin.VERSION; 79 | } 80 | } -------------------------------------------------------------------------------- /CommonAPI/Systems/PickerExtensionSystem/UISignalPickerExtension.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using CommonAPI.Systems.UI; 4 | using UnityEngine; 5 | using Object = UnityEngine.Object; 6 | 7 | namespace CommonAPI.Systems 8 | { 9 | public class UISignalPickerExtension 10 | { 11 | public static Func currentFilter; 12 | 13 | public static List> extensions = new List>(); 14 | 15 | private static UIShowSignalTipExtension tipHandler; 16 | 17 | public static UISignalPicker PreparePicker() 18 | { 19 | PickerExtensionsSystem.Instance.ThrowIfNotLoaded(); 20 | if (UIRoot.instance == null) 21 | { 22 | throw new PickerNotReadyException(); 23 | } 24 | 25 | UISignalPicker signalPicker = UIRoot.instance.uiGame.signalPicker; 26 | if (!signalPicker.inited || signalPicker.active) 27 | { 28 | throw new PickerNotReadyException(); 29 | } 30 | 31 | return signalPicker; 32 | } 33 | 34 | internal static UIShowSignalTipExtension GetTipExtension() 35 | { 36 | if (tipHandler == null) 37 | { 38 | tipHandler = new UIShowSignalTipExtension(); 39 | } 40 | 41 | return tipHandler; 42 | } 43 | 44 | /// 45 | /// Open UIRecipePicker with custom filters 46 | /// 47 | /// position on screen 48 | /// callback to call, when user selects an item 49 | /// Filter function 50 | public static void Popup(Vector2 pos, Action _onReturn, Func filter) 51 | { 52 | PickerExtensionsSystem.Instance.ThrowIfNotLoaded(); 53 | try 54 | { 55 | extensions.Clear(); 56 | extensions.Add(GetTipExtension()); 57 | 58 | UISignalPicker signalPicker = PreparePicker(); 59 | if (signalPicker == null) 60 | { 61 | _onReturn?.Invoke(0); 62 | } 63 | 64 | currentFilter = filter; 65 | 66 | signalPicker.onReturn = _onReturn; 67 | signalPicker._Open(); 68 | signalPicker.pickerTrans.anchoredPosition = pos; 69 | } 70 | catch (PickerNotReadyException) 71 | { 72 | _onReturn?.Invoke(0); 73 | } 74 | } 75 | 76 | public static void Popup(Vector2 pos, IPickerExtension extension) 77 | { 78 | PickerExtensionsSystem.Instance.ThrowIfNotLoaded(); 79 | try 80 | { 81 | extensions.Clear(); 82 | extensions.Add(GetTipExtension()); 83 | extensions.Add(extension); 84 | UISignalPicker signalPicker = PreparePicker(); 85 | 86 | extension.OnPopup(signalPicker); 87 | 88 | signalPicker._Open(); 89 | signalPicker.pickerTrans.anchoredPosition = pos; 90 | 91 | extension.PostPopup(signalPicker); 92 | } 93 | catch (PickerNotReadyException) { } 94 | } 95 | } 96 | } -------------------------------------------------------------------------------- /CommonAPI/Systems/Networks/NetworksSystem.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace CommonAPI 6 | { 7 | public class NetworksSystem : BaseSubmodule 8 | { 9 | public static List handlers = new List(); 10 | 11 | internal static NetworksSystem Instance => CommonAPIPlugin.GetModuleInstance(); 12 | 13 | 14 | internal override void Load() 15 | { 16 | AddHandler(new PowerNetworkHandler()); 17 | } 18 | 19 | public static void AddHandler(NetworkHandler handler) 20 | { 21 | handlers.Add(handler); 22 | } 23 | 24 | public static bool IsConnectedToNetwork(PlanetFactory factory, int objId) 25 | { 26 | Instance.ThrowIfNotLoaded(); 27 | try 28 | { 29 | NetworkHandler handler = GetNetworkHandler(factory, objId); 30 | return handler != null; 31 | } 32 | catch (InvalidOperationException) 33 | { 34 | return false; 35 | } 36 | } 37 | 38 | public static bool IsConnectedToSameNetwork(PlanetFactory factory, int firstId, int secondId) 39 | { 40 | Instance.ThrowIfNotLoaded(); 41 | try 42 | { 43 | NetworkHandler handler = GetCommonNetwork(factory, firstId, secondId); 44 | return handler != null; 45 | } 46 | catch (InvalidOperationException) 47 | { 48 | return false; 49 | } 50 | } 51 | 52 | public static NetworkHandler GetNetworkHandler(PlanetFactory factory, int objId) 53 | { 54 | Instance.ThrowIfNotLoaded(); 55 | if (objId == 0) return null; 56 | 57 | int protoId = objId > 0 ? factory.entityPool[objId].protoId : factory.prebuildPool[-objId].protoId; 58 | ItemProto itemProto = LDB.items.Select(protoId); 59 | 60 | if (itemProto == null) return null; 61 | try 62 | { 63 | return handlers.First(handler => handler.IsRelatedTo(itemProto)).Prepare(factory); 64 | } 65 | catch (InvalidOperationException) 66 | { 67 | return null; 68 | } 69 | } 70 | 71 | public static NetworkHandler GetCommonNetwork(PlanetFactory factory, int firstId, int secondId) 72 | { 73 | Instance.ThrowIfNotLoaded(); 74 | if (firstId == 0 || secondId == 0) return null; 75 | 76 | int firstProtoId = firstId > 0 ? factory.entityPool[firstId].protoId : factory.prebuildPool[-firstId].protoId; 77 | int secondProtoId = secondId > 0 ? factory.entityPool[secondId].protoId : factory.prebuildPool[-secondId].protoId; 78 | ItemProto firstItemProto = LDB.items.Select(firstProtoId); 79 | ItemProto secondItemProto = LDB.items.Select(secondProtoId); 80 | 81 | if (firstItemProto == null || secondItemProto == null) return null; 82 | 83 | try 84 | { 85 | return handlers.First(handler => handler.IsRelatedTo(firstItemProto) && handler.IsRelatedTo(secondItemProto)).Prepare(factory); 86 | } 87 | catch (InvalidOperationException) 88 | { 89 | return null; 90 | } 91 | } 92 | } 93 | } -------------------------------------------------------------------------------- /CommonAPI/Systems/ProtoRegistrySystem/Patches/ResourcesPatch.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using CommonAPI.Systems; 3 | using HarmonyLib; 4 | using UnityEngine; 5 | using Object = UnityEngine.Object; 6 | 7 | namespace CommonAPI.Patches 8 | { 9 | //Loading custom resources 10 | [HarmonyPatch] 11 | static class ResourcesPatch 12 | { 13 | [HarmonyPatch(typeof(Resources), "Load", typeof(string), typeof(Type))] 14 | [HarmonyPrefix] 15 | public static bool Prefix(ref string path, Type systemTypeInstance, ref Object __result) 16 | { 17 | foreach (ResourceData resource in ProtoRegistry.modResources) 18 | { 19 | if (!path.Contains(resource.keyWord) || !resource.HasAssetBundle()) continue; 20 | 21 | if (resource.bundle.Contains(path + ".prefab") && systemTypeInstance == typeof(GameObject)) 22 | { 23 | Object myPrefab = resource.bundle.LoadAsset(path + ".prefab"); 24 | CommonAPIPlugin.logger.LogDebug($"Loading registered asset {path}: {(myPrefab != null ? "Success" : "Failure")}"); 25 | 26 | if (!ProtoRegistry.modelMats.ContainsKey(path)) 27 | { 28 | __result = myPrefab; 29 | return false; 30 | } 31 | 32 | LodMaterials mats = ProtoRegistry.modelMats[path]; 33 | if (myPrefab != null && mats.HasLod(0)) 34 | { 35 | MeshRenderer[] renderers = ((GameObject) myPrefab).GetComponentsInChildren(); 36 | foreach (MeshRenderer renderer in renderers) 37 | { 38 | Material[] newMats = new Material[renderer.sharedMaterials.Length]; 39 | for (int i = 0; i < newMats.Length; i++) 40 | { 41 | newMats[i] = mats[0][i]; 42 | } 43 | 44 | renderer.sharedMaterials = newMats; 45 | } 46 | } 47 | 48 | __result = myPrefab; 49 | return false; 50 | } 51 | 52 | foreach (string extension in ProtoRegistry.spriteFileExtensions) 53 | { 54 | if (!resource.bundle.Contains(path + extension)) continue; 55 | 56 | Object mySprite = resource.bundle.LoadAsset(path + extension, systemTypeInstance); 57 | 58 | CommonAPIPlugin.logger.LogDebug($"Loading registered asset {path}: {(mySprite != null ? "Success" : "Failure")}"); 59 | 60 | __result = mySprite; 61 | return false; 62 | } 63 | 64 | foreach (string extension in ProtoRegistry.audioClipFileExtensions) 65 | { 66 | if (!resource.bundle.Contains(path + extension)) continue; 67 | 68 | Object myAudioClip = resource.bundle.LoadAsset(path + extension, systemTypeInstance); 69 | 70 | CommonAPIPlugin.logger.LogDebug($"Loading registered asset {path}: {(myAudioClip != null ? "Success" : "Failure")}"); 71 | 72 | __result = myAudioClip; 73 | return false; 74 | } 75 | } 76 | 77 | return true; 78 | } 79 | } 80 | } -------------------------------------------------------------------------------- /CommonAPI/Systems/StarExtensionSystem/Patches/StarExtensionHooks.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using CommonAPI.Systems; 3 | using HarmonyLib; 4 | 5 | namespace CommonAPI.Patches 6 | { 7 | [HarmonyPatch] 8 | public static class StarExtensionHooks 9 | { 10 | [HarmonyPatch(typeof(GameData), "OnDraw")] 11 | [HarmonyPostfix] 12 | public static void DrawCall(GameData __instance, int frame) 13 | { 14 | // PerformanceMonitor.BeginSample(ECpuWorkEntry.DrawCall); 15 | StarExtensionSystem.DrawUpdate(); 16 | //PerformanceMonitor.EndSample(ECpuWorkEntry.DrawCall); 17 | } 18 | 19 | //Single thread update calls 20 | /* [HarmonyPatch(typeof(GameData), "GameTick")] 21 | [HarmonyPrefix] 22 | public static void PreUpdateST(GameData __instance, long time) 23 | { 24 | if (GameMain.multithreadSystem.multithreadSystemEnable) 25 | { 26 | PerformanceMonitor.BeginSample(ECpuWorkEntry.DysonSphere); 27 | StarExtensionSystem.PreUpdateOnlySinglethread(); 28 | PerformanceMonitor.EndSample(ECpuWorkEntry.DysonSphere); 29 | return; 30 | } 31 | 32 | PerformanceMonitor.BeginSample(ECpuWorkEntry.DysonSphere); 33 | for (int i = 0; i < GameMain.galaxy.starCount; i++) 34 | { 35 | StarData star = GameMain.galaxy.stars[i]; 36 | if (star == null) continue; 37 | 38 | StarExtensionSystem.PreUpdate(star); 39 | } 40 | PerformanceMonitor.EndSample(ECpuWorkEntry.DysonSphere); 41 | }*/ 42 | /* 43 | [HarmonyPatch(typeof(TrashSystem), "GameTick")] 44 | [HarmonyPostfix] 45 | public static void UpdateST(TrashSystem __instance, long time) 46 | { 47 | if (GameMain.multithreadSystem.multithreadSystemEnable) 48 | { 49 | PerformanceMonitor.EndSample(ECpuWorkEntry.Trash); 50 | PerformanceMonitor.BeginSample(ECpuWorkEntry.DysonSphere); 51 | StarExtensionSystem.UpdateOnlySinglethread(); 52 | PerformanceMonitor.EndSample(ECpuWorkEntry.DysonSphere); 53 | PerformanceMonitor.BeginSample(ECpuWorkEntry.Trash); 54 | return; 55 | } 56 | 57 | PerformanceMonitor.EndSample(ECpuWorkEntry.Trash); 58 | PerformanceMonitor.BeginSample(ECpuWorkEntry.DysonSphere); 59 | for (int i = 0; i < GameMain.galaxy.starCount; i++) 60 | { 61 | StarData star = GameMain.galaxy.stars[i]; 62 | if (star == null) continue; 63 | 64 | StarExtensionSystem.Update(star); 65 | } 66 | PerformanceMonitor.EndSample(ECpuWorkEntry.DysonSphere); 67 | PerformanceMonitor.BeginSample(ECpuWorkEntry.Trash); 68 | }*/ 69 | 70 | //Multi-thread update calls, used only if player system support multithreading 71 | 72 | //TODO improve multi-thread calls 73 | 74 | [HarmonyPatch(typeof(DysonSphere), "RocketGameTick", typeof(int), typeof(int), typeof(int))] 75 | [HarmonyPostfix] 76 | public static void PowerTickMultithread(DysonSphere __instance, int _usedThreadCnt, int _curThreadIdx) 77 | { 78 | StarExtensionSystem.UpdateMultithread(__instance.starData, _usedThreadCnt, _curThreadIdx, 12); 79 | } 80 | } 81 | } -------------------------------------------------------------------------------- /CommonAPI/Systems/ComponentSystem/UI/CustomMachineUISystem.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using CommonAPI.Systems; 3 | using HarmonyLib; 4 | using UnityEngine; 5 | 6 | namespace CommonAPI.Systems 7 | { 8 | public static class CustomMachineUISystem 9 | { 10 | public static List windows = new List(); 11 | public static List registeredPrefabs = new List(); 12 | 13 | public static int customInspectId; 14 | public static CustomMachineWindow openWindow; 15 | 16 | public static void RegisterWindow(string prefabPath) 17 | { 18 | if (!registeredPrefabs.Contains(prefabPath)) 19 | { 20 | registeredPrefabs.Add(prefabPath); 21 | CommonAPIPlugin.logger.LogDebug("Registering machine window, prefab: " + prefabPath); 22 | } 23 | } 24 | 25 | public static void RefreshOpenWindow() 26 | { 27 | if (openWindow != null) 28 | { 29 | openWindow.OnIdChange(); 30 | } 31 | } 32 | 33 | internal static void OnCreate(UIGame uiGame) 34 | { 35 | CommonAPIPlugin.logger.LogInfo("Loading custom UI's"); 36 | Transform windowsObject = uiGame.canvasGroup.transform.Find("Windows"); 37 | if (windowsObject == null) return; 38 | 39 | foreach (var path in registeredPrefabs) 40 | { 41 | GameObject windowPrefab = Resources.Load(path); 42 | if (windowPrefab == null) 43 | { 44 | CommonAPIPlugin.logger.LogError($"Error loading UI prefab: {path}!"); 45 | continue; 46 | } 47 | GameObject windowObject = Object.Instantiate(windowPrefab, windowsObject, false); 48 | 49 | CustomMachineWindow machineWindow = windowObject.GetComponent(); 50 | machineWindow._Create(); 51 | windows.Add(machineWindow); 52 | } 53 | 54 | registeredPrefabs.Clear(); 55 | } 56 | 57 | internal static void OnPlayerInspecteeChange(UIGame uiGame, EObjectType objType, int objId) 58 | { 59 | PlanetFactory factory = GameMain.mainPlayer.factory; 60 | int componentId = -1; 61 | int protoId = 0; 62 | if (factory != null && objType == EObjectType.Entity && objId > 0) 63 | { 64 | componentId = factory.entityPool[objId].customType; 65 | protoId = factory.entityPool[objId].protoId; 66 | } 67 | 68 | CustomMachineWindow currentWindow = null; 69 | 70 | foreach (var window in windows) 71 | { 72 | if (window.ShouldOpen(componentId, protoId)) 73 | { 74 | currentWindow = window; 75 | break; 76 | } 77 | } 78 | 79 | if (currentWindow == null) return; 80 | 81 | if (objId > 0 && customInspectId != objId) 82 | { 83 | if (currentWindow.DoCloseOtherWindows()) 84 | uiGame.ShutAllFunctionWindow(); 85 | 86 | if (currentWindow.DoClosePlayerInventory()) 87 | uiGame.ShutPlayerInventory(); 88 | 89 | currentWindow.Open(objId); 90 | } 91 | else if (objId == 0 && customInspectId > 0) 92 | { 93 | currentWindow.Close(); 94 | } 95 | 96 | customInspectId = objId; 97 | } 98 | } 99 | } -------------------------------------------------------------------------------- /CommonAPI/Systems/PickerExtensionSystem/UIRecipePickerExtension.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using UnityEngine; 3 | 4 | namespace CommonAPI.Systems 5 | { 6 | public class UIRecipePickerExtension 7 | { 8 | public static Func currentFilter; 9 | public static bool showLocked = false; 10 | 11 | public static IPickerExtension currentExtension; 12 | 13 | 14 | public static UIRecipePicker PreparePicker() 15 | { 16 | PickerExtensionsSystem.Instance.ThrowIfNotLoaded(); 17 | if (UIRoot.instance == null) 18 | { 19 | throw new PickerNotReadyException(); 20 | } 21 | 22 | UIRecipePicker recipePicker = UIRoot.instance.uiGame.recipePicker; 23 | if (!recipePicker.inited || recipePicker.active) 24 | { 25 | throw new PickerNotReadyException(); 26 | } 27 | 28 | return recipePicker; 29 | } 30 | 31 | /// 32 | /// Open UIRecipePicker with custom filters 33 | /// 34 | /// position on screen 35 | /// callback to call, when user selects an item 36 | /// Should locked items be visible 37 | /// Filter function 38 | public static void Popup(Vector2 pos, Action _onReturn, bool showLockedRecipes, Func filter) 39 | { 40 | PickerExtensionsSystem.Instance.ThrowIfNotLoaded(); 41 | try 42 | { 43 | showLocked = showLockedRecipes; 44 | currentExtension = null; 45 | UIRecipePicker recipePicker = PreparePicker(); 46 | if (recipePicker == null) 47 | { 48 | _onReturn?.Invoke(null); 49 | } 50 | 51 | currentFilter = filter; 52 | 53 | recipePicker.filter = ERecipeType.None; 54 | recipePicker.onReturn = _onReturn; 55 | recipePicker._Open(); 56 | recipePicker.pickerTrans.anchoredPosition = pos; 57 | } 58 | catch (PickerNotReadyException) 59 | { 60 | _onReturn?.Invoke(null); 61 | } 62 | } 63 | 64 | /// 65 | /// Open UIRecipePicker with custom filters 66 | /// 67 | /// position on screen 68 | /// callback to call, when user selects an item 69 | /// Filter function 70 | public static void Popup(Vector2 pos, Action _onReturn, Func filter) 71 | { 72 | Popup(pos, _onReturn, false, filter); 73 | } 74 | 75 | public static void Popup(Vector2 pos, IPickerExtension extension) 76 | { 77 | PickerExtensionsSystem.Instance.ThrowIfNotLoaded(); 78 | try 79 | { 80 | // ReSharper disable once SuspiciousTypeConversion.Global 81 | showLocked = extension is ShowLocked; 82 | currentExtension = extension; 83 | UIRecipePicker recipePicker = PreparePicker(); 84 | 85 | extension.OnPopup(recipePicker); 86 | 87 | recipePicker._Open(); 88 | recipePicker.pickerTrans.anchoredPosition = pos; 89 | 90 | extension.PostPopup(recipePicker); 91 | } 92 | catch (PickerNotReadyException) { } 93 | } 94 | } 95 | } -------------------------------------------------------------------------------- /.github/workflows/workflow.yml: -------------------------------------------------------------------------------- 1 | name: Publish Release 2 | 3 | on: 4 | workflow_dispatch: 5 | 6 | jobs: 7 | build: 8 | runs-on: ubuntu-latest 9 | env: 10 | BundleBepInEx : false 11 | BepInExVersion : 5 12 | BepInExArch : x64 13 | BepInExRuntime : IL2CPP 14 | PluginName : CommonAPI-CommonAPI 15 | GithubRelease : true 16 | ThunderstoreRelease: true 17 | PublishNuget: true 18 | steps: 19 | - name: Checkout 20 | uses: actions/checkout@v2 21 | with: 22 | submodules: true 23 | fetch-depth: 0 # can't shallowly clone due to git versioning 24 | 25 | - name: Get version of the project 26 | id: get-version 27 | uses: 'euberdeveloper/ga-project-version@main' 28 | with: 29 | path: 'version.json' 30 | 31 | - name: Check Tag 32 | id: check-tag 33 | uses: actions-ecosystem/action-regex-match@v2 34 | with: 35 | text: ${{ steps.get-version.outputs.version }} 36 | regex: '^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$' 37 | 38 | - name: Fail if invalid 39 | if: steps.check-tag.outputs.match == '' 40 | uses: Actions/github-script@v3 41 | with: 42 | script: | 43 | core.setFailed('Invalid tag') 44 | 45 | - uses: actions/setup-dotnet@v1 46 | 47 | - name: BepInEx 6 48 | run: | 49 | bepinexurl = $(curl -vs https://builds.bepinex.dev/projects/bepinex_be 2>&1 | grep -Po '(?<=href=")(\/projects\/bepinex_be\/(.*)\/BepInEx_Unity${{env.BepInExRuntime}}_${{env.BepInExArch}}(.*))(?=")' | head -1)" 50 | wget $bepinexurl > bepinex.zip 51 | if: env.BundleBepInEx == 'true' && env.BepInExVersion == '6' 52 | 53 | - name: BepInEx 5 54 | shell: pwsh 55 | run: | 56 | $webData = Invoke-WebRequest -Uri "https://api.github.com/repos/BepInEx/BepInEx/releases/latest" 57 | $release = ConvertFrom-Json $webData.content 58 | $bepinexurl = $release.assets.browser_download_url -match '${{env.BepInExArch}}' 59 | wget $bepinexurl > bepinex.zip 60 | if: env.BundleBepInEx == 'true' && env.BepInExVersion == '5' 61 | 62 | - name: Extract BepInEx 63 | run: | 64 | mkdir out; 65 | unzip BepInEx* -d out; 66 | if: env.BundleBepInEx == 'true' 67 | 68 | - name: Setup Thunderstore CLI 69 | run: | 70 | dotnet tool install -g tcli 71 | 72 | - name: Build & zip 73 | run: | 74 | dotnet build CommonAPIPublish.sln -c Release /p:ReleaseMode=1 75 | tcli build --config-path Config/thunderstore.toml --package-version ${{ steps.get-version.outputs.version }} 76 | 77 | - name: Publish to Github 78 | uses: ncipollo/release-action@v1 79 | with: 80 | artifacts: "Build/*.zip" 81 | tag: ${{ steps.get-version.outputs.version }} 82 | commit: ${{ github.ref }} 83 | artifactErrorsFailBuild: true 84 | draft: true 85 | allowUpdates: true 86 | if: env.GithubRelease == 'true' 87 | 88 | - name: Publish to Thunderstore 89 | env: 90 | TCLI_AUTH_TOKEN: ${{ secrets.TCLI_AUTH_TOKEN }} 91 | run: | 92 | tcli publish --config-path Config/thunderstore.toml --package-version ${{ steps.get-version.outputs.version }} 93 | if: env.ThunderstoreRelease == 'true' 94 | 95 | - name: Publish to NuGet 96 | run: | 97 | dotnet pack CommonAPI/CommonAPI.csproj /p:ReleaseMode=1 -c Release -o "." 98 | dotnet nuget push *.nupkg -s https://api.nuget.org/v3/index.json -k ${{secrets.NUGET_API_KEY}} --skip-duplicate 99 | if: env.PublishNuget == 'true' -------------------------------------------------------------------------------- /CommonAPI/Systems/ProtoRegistrySystem/ResourceData.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using System.Reflection; 3 | using JetBrains.Annotations; 4 | using UnityEngine; 5 | 6 | namespace CommonAPI.Systems 7 | { 8 | /// 9 | /// Mod resources definition class. Use this class to load your asset bundles and resolve verta folder paths 10 | /// 11 | [UsedImplicitly] 12 | public class ResourceData 13 | { 14 | public string modId; 15 | public string modPath; 16 | public string keyWord; 17 | 18 | public AssetBundle bundle; 19 | public string vertaFolder; 20 | 21 | /// 22 | /// Create new resource definition 23 | /// 24 | /// Your mod ID 25 | /// Unique Keyword used only by your mods 26 | /// Path to mod's main assembly 27 | public ResourceData(string modId, string keyWord, string modPath) 28 | { 29 | this.modId = modId; 30 | this.modPath = modPath; 31 | this.keyWord = keyWord; 32 | } 33 | 34 | /// 35 | /// Create new resource definition. Path is inferred from what assembly is calling. 36 | /// 37 | /// Your mod ID 38 | /// Unique Keyword used only by your mods 39 | public ResourceData(string modId, string keyWord) 40 | { 41 | this.modId = modId; 42 | this.modPath = Path.GetDirectoryName(Assembly.GetCallingAssembly().Location); 43 | this.keyWord = keyWord; 44 | } 45 | 46 | /// 47 | /// Does this resource definition have a resolved verta folder 48 | /// 49 | public bool HasVertaFolder() 50 | { 51 | return !vertaFolder.Equals(""); 52 | } 53 | 54 | /// 55 | /// Does this resource definition have a asset bundle loaded 56 | /// 57 | public bool HasAssetBundle() 58 | { 59 | return bundle != null; 60 | } 61 | 62 | /// 63 | /// Load asset bundle from mod path. 64 | /// 65 | /// Bundle name 66 | /// Thrown if loading an asset bundle has failed 67 | public void LoadAssetBundle(string bundleName) 68 | { 69 | bundle = AssetBundle.LoadFromFile($"{modPath}/{bundleName}"); 70 | if (bundle == null) 71 | { 72 | throw new LoadException($"Failed to load asset bundle at {modPath}/{bundleName}"); 73 | } 74 | } 75 | 76 | /// 77 | /// Try to resolve where verta folder is located. Two checked paths are [modPath]/Verta and [modPath]/plugins/Verta 78 | /// 79 | /// 80 | public void ResolveVertaFolder() 81 | { 82 | FileInfo folder = new FileInfo($"{modPath}/Verta/"); 83 | FileInfo folder1 = new FileInfo($"{modPath}/plugins/"); 84 | 85 | if (Directory.Exists(folder.Directory?.FullName)) 86 | { 87 | vertaFolder = modPath; 88 | } 89 | else if (Directory.Exists(folder1.Directory?.FullName)) 90 | { 91 | vertaFolder = $"{modPath}/plugins"; 92 | } 93 | else 94 | { 95 | vertaFolder = ""; 96 | throw new LoadException($"Failed to resolve verta folder at {modPath}"); 97 | } 98 | } 99 | } 100 | } -------------------------------------------------------------------------------- /CommonAPI/Util/Extensions/ConfigFileExtension.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Reflection; 5 | using BepInEx.Configuration; 6 | using HarmonyLib; 7 | 8 | namespace CommonAPI 9 | { 10 | public static class ConfigFileExtension 11 | { 12 | public static Type configFile; 13 | public static PropertyInfo OrphanedEntriesProp; 14 | 15 | static ConfigFileExtension() 16 | { 17 | configFile = AccessTools.TypeByName("BepInEx.Configuration.ConfigFile"); 18 | OrphanedEntriesProp = configFile.GetProperty("OrphanedEntries", AccessTools.all); 19 | } 20 | 21 | /// 22 | /// Migrate BepInEx config file entries from one category to another. 23 | /// 24 | /// target BepInEx config file 25 | /// Section to migrate from 26 | /// Section to migrate to 27 | /// Array of keys to migrate 28 | /// Type of config entries 29 | public static void MigrateConfig(this ConfigFile file, string oldSection, string newSection, string[] keyFilter) 30 | { 31 | Dictionary oldEntries = (Dictionary)OrphanedEntriesProp.GetValue(file); 32 | List keysToRemove = new List(); 33 | 34 | foreach (var kv in oldEntries) 35 | { 36 | string key = kv.Key.Key; 37 | if (kv.Key.Section.Equals(oldSection) && ((IList) keyFilter).Contains(key)) 38 | { 39 | if (!file.TryGetEntry(newSection, key, out ConfigEntry entry)) continue; 40 | 41 | entry.SetSerializedValue(kv.Value); 42 | keysToRemove.Add(kv.Key); 43 | CommonAPIPlugin.logger.LogInfo($"Migrating config from {oldSection}:{key} to {newSection}:{key}"); 44 | 45 | } 46 | } 47 | 48 | foreach (var key in keysToRemove) 49 | { 50 | oldEntries.Remove(key); 51 | } 52 | } 53 | 54 | /// 55 | /// Migrate BepInEx config file entry. 56 | /// 57 | /// target BepInEx config file 58 | /// Section to migrate from 59 | /// Old entry key 60 | /// Section to migrate to 61 | /// New entry key 62 | /// Type of config entry 63 | public static void MigrateConfig(this ConfigFile file, string oldSection, string oldName, string newSection, string newName) 64 | { 65 | Dictionary oldEntries = (Dictionary)OrphanedEntriesProp.GetValue(file); 66 | List keysToRemove = new List(); 67 | 68 | foreach (var kv in oldEntries) 69 | { 70 | ConfigDefinition config = kv.Key; 71 | if (config.Section.Equals(oldSection) && config.Key.Equals(oldName)) 72 | { 73 | if (!file.TryGetEntry(newSection, newName, out ConfigEntry entry)) continue; 74 | 75 | entry.SetSerializedValue(kv.Value); 76 | keysToRemove.Add(config); 77 | CommonAPIPlugin.logger.LogInfo($"Migrating config from {oldSection}:{oldName} to {newSection}:{newName}"); 78 | 79 | } 80 | } 81 | 82 | foreach (var key in keysToRemove) 83 | { 84 | oldEntries.Remove(key); 85 | } 86 | } 87 | } 88 | } -------------------------------------------------------------------------------- /Config/README.md: -------------------------------------------------------------------------------- 1 | # CommonAPI 2 | A modding library for Dyson Sphere Program. Provides multiple features to make adding custom content to DSP easier. 3 | 4 | 5 | # List of features 6 | - Easily register new items, recipes and more using [ProtoRegistry](https://github.com/kremnev8/CommonAPI/tree/master/CommonAPI/Systems/ProtoRegistrySystem) system 7 | - Create new buildings with custom behavior and custom UI using [ComponentSystem](https://github.com/kremnev8/CommonAPI/tree/master/CommonAPI/Systems/ComponentSystem) 8 | - Register new recipe types. This allows to create new machine types without writing almost any code. 9 | - Register and use custom tabs. 10 | - Register new KeyBinds that players can rebind 11 | - Easily create new systems that exist in each Planet or Star. An example of such system is [ComponentSystem](https://github.com/kremnev8/CommonAPI/tree/master/CommonAPI/Systems/ComponentSystem) 12 | - Picker Extension tool allows to extend behavior of Item and Recipe pickers. For example use any filter defined by a function. 13 | - Support translation for at least for English, Chinese and Franch by using String Protos 14 | 15 | Full list of modules and other utilities can be found [here](https://github.com/kremnev8/CommonAPI/wiki). 16 | More will come in the future. If want write your own module and add it to the list you can open a Pull Request. Contrubitions are welcome. 17 | 18 | # Installation 19 | ### With Mod Manager 20 | 21 | Simply open the mod manager (if you don't have it install it [here](https://dsp.thunderstore.io/package/ebkr/r2modman/)), select **CommonAPI by CommonAPI**, then **Download**. 22 | 23 | If prompted to download with dependencies, select `Yes`. 24 | Then just click **Start modded**, and the game will run with the mod installed. 25 | 26 | ### Manually 27 | Install BepInEx from [here](https://dsp.thunderstore.io/package/xiaoye97/BepInEx/)
28 | Install LDBTool from [here](https://dsp.thunderstore.io/package/xiaoye97/LDBTool/)
29 | Install DSPModSave from [here](https://dsp.thunderstore.io/package/CommonAPI/DSPModSave/)
30 | 31 | Unzip folder `patchers` into `Dyson Sphere Program/BepInEx/patchers/CommonAPI/` (Create folder named `CommonAPI`)
32 | Unzip folder `plugins` into `Dyson Sphere Program/BepInEx/plugins/CommonAPI/`. (Create folder named `CommonAPI`)
33 | 34 | ## Feedback and Bug Report 35 | Feel free to contact me via Discord (Kremnev8#3756) for any feedback, bug-reports or suggestions. 36 | 37 | # How develop mods using CommonAPI 38 | All features are written as self-contained modules (Inspired by [R2API](https://github.com/risk-of-thunder/R2API)). By default NO modules are loaded. To use a module at the top of your BepInEx plugin class add an attribute `CommonAPISubmoduleDependency`. That will ensure that specified modules are loaded. Make sure you don't ask to load modules that you are not using. 39 | 40 | ## How to setup development environment 41 | 1. Download and install [CommonAPI](https://dsp.thunderstore.io/package/CommonAPI/CommonAPI/) and its dependencies 42 | 2. Create development environment. You can find how to do that [here](https://docs.bepinex.dev/master/articles/dev_guide/plugin_tutorial/index.html#sidetoggle) 43 | 3. Add LDBTool, DSPModSave and CommonAPI assemblies to your references. You can use NuGet to get them. You can find them by typing `DysonSphereProgram.Modding` into nuget package search. 44 | 4. You also likely will need a Unity Project. You can find instructions on setting that up [here](https://github.com/kremnev8/DSP-Mods/wiki/Setting-up-development-environment) 45 | 46 | ### Usage Example 47 | ```csharp 48 | [BepInPlugin(GUID, NAME, VERSION)] 49 | 50 | [BepInDependency(CommonAPIPlugin.GUID)] 51 | [CommonAPISubmoduleDependency(nameof(ProtoRegistry), nameof(CustomDescSystem))] 52 | public class MyPlugin : BaseUnityPlugin 53 | { 54 | public const string MODID = "myplugin"; 55 | public const string GUID = "org.myname.plugin." + MODID; 56 | public const string NAME = "My Plugin"; 57 | 58 | void Awake() 59 | { 60 | //Make use of modules here 61 | } 62 | } 63 | ``` 64 | 65 | This library is still under development. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CommonAPI 2 | A modding library for Dyson Sphere Program. Provides multiple features to make adding custom content to DSP easier. 3 | 4 | 5 | # List of features 6 | - Easily register new items, recipes and more using [ProtoRegistry](https://github.com/kremnev8/CommonAPI/tree/master/CommonAPI/Systems/ProtoRegistrySystem) system 7 | - Create new buildings with custom behavior and custom UI using [ComponentSystem](https://github.com/kremnev8/CommonAPI/tree/master/CommonAPI/Systems/ComponentSystem) 8 | - Register new recipe types. This allows to create new machine types without writing almost any code. 9 | - Register and use custom tabs. 10 | - Register new KeyBinds that players can rebind 11 | - Easily create new systems that exist in each Planet or Star. An example of such system is [ComponentSystem](https://github.com/kremnev8/CommonAPI/tree/master/CommonAPI/Systems/ComponentSystem) 12 | - Picker Extension tool allows to extend behavior of Item and Recipe pickers. For example use any filter defined by a function. 13 | - Support translation for at least for English, Chinese and Franch by using String Protos 14 | 15 | Full list of modules and other utilities can be found [here](https://github.com/kremnev8/CommonAPI/wiki). 16 | More will come in the future. If want write your own module and add it to the list you can open a Pull Request. Contrubitions are welcome. 17 | 18 | # Installation 19 | ### With Mod Manager 20 | 21 | Simply open the mod manager (if you don't have it install it [here](https://dsp.thunderstore.io/package/ebkr/r2modman/)), select **CommonAPI by CommonAPI**, then **Download**. 22 | 23 | If prompted to download with dependencies, select `Yes`. 24 | Then just click **Start modded**, and the game will run with the mod installed. 25 | 26 | ### Manually 27 | Install BepInEx from [here](https://dsp.thunderstore.io/package/xiaoye97/BepInEx/)
28 | Install LDBTool from [here](https://dsp.thunderstore.io/package/xiaoye97/LDBTool/)
29 | Install DSPModSave from [here](https://dsp.thunderstore.io/package/CommonAPI/DSPModSave/)
30 | 31 | Unzip folder `patchers` into `Dyson Sphere Program/BepInEx/patchers/CommonAPI/` (Create folder named `CommonAPI`)
32 | Unzip folder `plugins` into `Dyson Sphere Program/BepInEx/plugins/CommonAPI/`. (Create folder named `CommonAPI`)
33 | 34 | ## Feedback and Bug Report 35 | Feel free to contact me via Discord (Kremnev8#3756) for any feedback, bug-reports or suggestions. 36 | 37 | # How develop mods using CommonAPI 38 | All features are written as self-contained modules (Inspired by [R2API](https://github.com/risk-of-thunder/R2API)). By default NO modules are loaded. To use a module at the top of your BepInEx plugin class add an attribute `CommonAPISubmoduleDependency`. That will ensure that specified modules are loaded. Make sure you don't ask to load modules that you are not using. 39 | 40 | ## How to setup development environment 41 | 1. Download and install [CommonAPI](https://dsp.thunderstore.io/package/CommonAPI/CommonAPI/) and its dependencies 42 | 2. Create development environment. You can find how to do that [here](https://docs.bepinex.dev/master/articles/dev_guide/plugin_tutorial/index.html#sidetoggle) 43 | 3. Add LDBTool, DSPModSave and CommonAPI assemblies to your references. You can use NuGet to get them. You can find them by typing `DysonSphereProgram.Modding` into nuget package search. 44 | 4. You also likely will need a Unity Project. You can find instructions on setting that up [here](https://github.com/kremnev8/DSP-Mods/wiki/Setting-up-development-environment) 45 | 46 | ### Usage Example 47 | ```csharp 48 | [BepInPlugin(GUID, NAME, VERSION)] 49 | 50 | [BepInDependency(CommonAPIPlugin.GUID)] 51 | [CommonAPISubmoduleDependency(nameof(ProtoRegistry), nameof(CustomDescSystem))] 52 | public class MyPlugin : BaseUnityPlugin 53 | { 54 | public const string MODID = "myplugin"; 55 | public const string GUID = "org.myname.plugin." + MODID; 56 | public const string NAME = "My Plugin"; 57 | 58 | void Awake() 59 | { 60 | //Make use of modules here 61 | } 62 | } 63 | ``` 64 | 65 | This library is still under development. Used by many of my mods. 66 | -------------------------------------------------------------------------------- /CommonAPI/Systems/ComponentSystem/ComponentTypePool.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace CommonAPI.Systems 5 | { 6 | public class ComponentTypePool : Pool, IPoolable 7 | { 8 | public int id; 9 | 10 | public const float DT = 0.016666668f; 11 | 12 | private PlanetFactory factory; 13 | private int PoolTypeId; 14 | 15 | private FactoryComponent lastAddedComponent; 16 | private PrebuildData lastAddedData; 17 | 18 | 19 | 20 | public ComponentTypePool(PlanetFactory factory, int type) 21 | { 22 | this.factory = factory; 23 | PoolTypeId = type; 24 | this._cachedInitUpdate = _internalInitUpdate; 25 | this._cachedInitPowerUpdate = _internalInitPowerUpdate; 26 | } 27 | 28 | public ComponentTypePool() 29 | { 30 | } 31 | 32 | public int AddComponent(int entityId, PrebuildData data = default) 33 | { 34 | int num = AddPoolItem(new object[] {entityId, data}); 35 | return num; 36 | } 37 | 38 | protected override FactoryComponent GetNewInstance() 39 | { 40 | return ComponentExtension.componentRegistry.GetNew(PoolTypeId); 41 | } 42 | 43 | public void OnPostComponentAdded() 44 | { 45 | if (lastAddedComponent == null) return; 46 | 47 | lastAddedComponent.OnAdded(lastAddedData, factory); 48 | lastAddedComponent = null; 49 | } 50 | 51 | protected override void InitPoolItem(FactoryComponent item, object[] data) 52 | { 53 | int entityId = (int) data[0]; 54 | 55 | int pcId = factory.entityPool[entityId].powerConId; 56 | 57 | item.entityId = entityId; 58 | item.pcId = pcId; 59 | factory.entityPool[entityId].customId = item.id; 60 | factory.entityPool[entityId].customType = PoolTypeId; 61 | lastAddedComponent = item; 62 | lastAddedData = (PrebuildData)data[1]; 63 | } 64 | 65 | protected override void RemovePoolItem(FactoryComponent item) 66 | { 67 | item.OnRemoved(factory); 68 | 69 | base.RemovePoolItem(item); 70 | } 71 | 72 | private Action _cachedInitUpdate; 73 | protected override Action InitUpdate() => _cachedInitUpdate; 74 | private void _internalInitUpdate(FactoryComponent item) 75 | { 76 | PowerSystem powerSystem = factory.powerSystem; 77 | PowerConsumerComponent[] consumerPool = powerSystem.consumerPool; 78 | float[] networkServes = powerSystem.networkServes; 79 | AnimData[] entityAnimPool = factory.entityAnimPool; 80 | SignData[] entitySignPool = factory.entitySignPool; 81 | int[][] entityNeeds = factory.entityNeeds; 82 | 83 | int entityId = item.entityId; 84 | float power = networkServes[consumerPool[item.pcId].networkId]; 85 | 86 | int animationDelta = item.InternalUpdate(power, factory); 87 | item.UpdateAnimation(ref entityAnimPool[entityId], animationDelta, power); 88 | entityAnimPool[entityId].power = power; 89 | item.UpdateSigns(ref entitySignPool[entityId], animationDelta, power, factory); 90 | entityNeeds[entityId] = item.UpdateNeeds(); 91 | } 92 | 93 | private Action _cachedInitPowerUpdate; 94 | public Action InitPowerUpdate() => _cachedInitPowerUpdate; 95 | private void _internalInitPowerUpdate(FactoryComponent item) 96 | { 97 | PowerConsumerComponent[] consumerPool = factory.powerSystem.consumerPool; 98 | item.UpdatePowerState(ref consumerPool[item.pcId]); 99 | } 100 | 101 | public int GetId() 102 | { 103 | return id; 104 | } 105 | 106 | public void SetId(int id) 107 | { 108 | this.id = id; 109 | } 110 | } 111 | } -------------------------------------------------------------------------------- /CommonAPI/Systems/ComponentSystem/UI/CustomMachineWindow.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine.UI; 2 | 3 | namespace CommonAPI.Systems 4 | { 5 | public abstract class CustomMachineWindow : ManualBehaviour 6 | { 7 | public abstract bool ShouldOpen(int componentId, int protoId); 8 | public abstract bool DoClosePlayerInventory(); 9 | public abstract bool DoCloseOtherWindows(); 10 | protected abstract void OnMachineChanged(); 11 | 12 | public int customId { get; protected set; } 13 | 14 | public int entityId { get; protected set; } 15 | 16 | public PlanetFactory factory; 17 | 18 | public PowerSystem powerSystem; 19 | 20 | public FactoryComponent component; 21 | 22 | public Text titleText; 23 | public UIPowerIndicator powerIndicator; 24 | 25 | public override void _OnCreate() 26 | { 27 | powerIndicator.Init(this); 28 | } 29 | 30 | public void Open(int newEntityId) 31 | { 32 | entityId = newEntityId; 33 | if (GameMain.localPlanet != null && GameMain.localPlanet.factory != null) 34 | { 35 | factory = GameMain.localPlanet.factory; 36 | powerSystem = factory.powerSystem; 37 | GameMain.mainPlayer.SetHandItems(0, 0); 38 | 39 | customId = entityId > 0 ? factory.entityPool[entityId].customId : 0; 40 | 41 | OnIdChange(); 42 | } 43 | transform.SetAsLastSibling(); 44 | CustomMachineUISystem.openWindow = this; 45 | _Open(); 46 | } 47 | 48 | public void Close() 49 | { 50 | entityId = 0; 51 | customId = 0; 52 | _Close(); 53 | 54 | if (entityId != 0) 55 | { 56 | PlayerAction_Inspect actionInspect = GameMain.mainPlayer.controller.actionInspect; 57 | if (actionInspect.inspectId > 0 && actionInspect.inspectType == EObjectType.Entity && 58 | factory.entityPool[actionInspect.inspectId].id == entityId) 59 | { 60 | actionInspect.InspectNothing(); 61 | } 62 | } 63 | 64 | factory = null; 65 | powerSystem = null; 66 | 67 | CustomMachineUISystem.customInspectId = 0; 68 | CustomMachineUISystem.openWindow = null; 69 | } 70 | 71 | internal void OnIdChange() 72 | { 73 | if (entityId == 0 || factory == null) 74 | { 75 | Close(); 76 | return; 77 | } 78 | 79 | EntityData entity = factory.entityPool[entityId]; 80 | 81 | if (entity.id != entityId || !ShouldOpen(entity.customType, entity.protoId)) 82 | { 83 | Close(); 84 | return; 85 | } 86 | 87 | if (customId > 0) 88 | { 89 | component = ComponentExtension.GetComponent(factory, entity.customType, customId); 90 | } 91 | 92 | OnMachineChanged(); 93 | } 94 | 95 | public void OnUpdateUI() 96 | { 97 | if (entityId == 0 || factory == null) 98 | { 99 | Close(); 100 | return; 101 | } 102 | 103 | EntityData entity = factory.entityPool[entityId]; 104 | 105 | if (entity.id != entityId || !ShouldOpen(entity.customType, entity.protoId)) 106 | { 107 | Close(); 108 | return; 109 | } 110 | 111 | if (customId > 0) 112 | { 113 | component = ComponentExtension.GetComponent(factory, entity.customType, customId); 114 | } 115 | 116 | if (component != null) 117 | { 118 | ItemProto itemProto = LDB.items.Select(factory.entityPool[component.entityId].protoId); 119 | titleText.text = itemProto.name; 120 | 121 | powerIndicator.OnUpdate(component.pcId); 122 | } 123 | 124 | _OnUpdate(); 125 | } 126 | } 127 | } -------------------------------------------------------------------------------- /CommonAPITests/Data/EntityDataTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using CommonAPI; 4 | using NUnit.Framework; 5 | using static NUnit.Framework.Assert; 6 | 7 | namespace CommonAPITests 8 | { 9 | [TestFixture] 10 | public class EntityDataTest 11 | { 12 | public EntityData data; 13 | public EntityData data2; 14 | 15 | [SetUp] 16 | public void Setup() 17 | { 18 | data = new EntityData 19 | { 20 | customData = new Dictionary() 21 | }; 22 | data2 = new EntityData(); 23 | EntityDataExtensions.propertySerializers.Clear(); 24 | } 25 | 26 | [Test] 27 | public void TestSerializerWarnProperties() 28 | { 29 | False(data.HasProperty("Test")); 30 | 31 | Throws(typeof(ArgumentException), () => 32 | { 33 | data.SetProperty("Test", 5); 34 | }); 35 | 36 | EntityDataExtensions.DefineProperty("Test", new IntArrayPropertySerializer()); 37 | 38 | Throws(typeof(ArgumentException), () => 39 | { 40 | data.SetProperty("Test", 5); 41 | }); 42 | 43 | EntityDataExtensions.DefineProperty("Test1", new IntPropertySerializer()); 44 | 45 | DoesNotThrow(() => 46 | { 47 | data.SetProperty("Test1", 5); 48 | }); 49 | } 50 | 51 | [Test] 52 | public void TestProperties() 53 | { 54 | EntityDataExtensions.DefineProperty("Test", new IntPropertySerializer()); 55 | EntityDataExtensions.DefineProperty("Hello", new IntPropertySerializer()); 56 | 57 | False(data.HasProperty("Test")); 58 | 59 | data.SetProperty("Test", 5); 60 | data.SetProperty("Test", 7); 61 | 62 | True(data.HasProperty("Test")); 63 | 64 | AreEqual(7, data.GetProperty("Test")); 65 | AreEqual(0, data.GetProperty("Hello")); 66 | 67 | AreEqual(7, data.GetOrAddProperty("Test")); 68 | AreEqual(0, data.GetOrAddProperty("Hello")); 69 | } 70 | 71 | [Test] 72 | public void TestPropertiesWNullData() 73 | { 74 | data.customData = null; 75 | EntityDataExtensions.DefineProperty("Test", new IntPropertySerializer()); 76 | EntityDataExtensions.DefineProperty("Hello", new IntPropertySerializer()); 77 | 78 | False(data.HasProperty("Test")); 79 | 80 | data.customData = null; 81 | data.SetProperty("Test", 5); 82 | data.SetProperty("Test", 7); 83 | 84 | True(data.HasProperty("Test")); 85 | 86 | AreEqual(7, data.GetProperty("Test")); 87 | data.customData = null; 88 | AreEqual(0, data.GetProperty("Hello")); 89 | 90 | data.customData = null; 91 | AreEqual(0, data.GetOrAddProperty("Test")); 92 | AreEqual(0, data.GetOrAddProperty("Hello")); 93 | 94 | data.customData = null; 95 | DoesNotThrow(() => 96 | { 97 | Util.GetSerializationSetup(w => 98 | { 99 | EntityDataExtensions.ExportData(ref data, w); 100 | }, r => 101 | { 102 | EntityDataExtensions.ImportData(ref data, r); 103 | }); 104 | }); 105 | } 106 | 107 | [Test] 108 | public void TestSerialization() 109 | { 110 | EntityDataExtensions.DefineProperty("Test", new IntArrayPropertySerializer()); 111 | EntityDataExtensions.DefineProperty("Test", new IntPropertySerializer()); 112 | EntityDataExtensions.DefineProperty("Test1", new IntArrayPropertySerializer()); 113 | 114 | data.SetProperty("Test", 5); 115 | data.SetProperty("Test1", new[]{1,2,3}); 116 | 117 | Util.GetSerializationSetup(w => 118 | { 119 | EntityDataExtensions.ExportData(ref data, w); 120 | }, r => 121 | { 122 | EntityDataExtensions.ImportData(ref data2, r); 123 | }); 124 | 125 | AreEqual(5, data2.GetProperty("Test")); 126 | AreEqual(new[]{1,2,3}, data2.GetProperty("Test1")); 127 | } 128 | } 129 | } -------------------------------------------------------------------------------- /Staging/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ### v1.6.7 2 | - Updated to work with game version 0.10.33.26934 or higher 3 | - Fixed known issues for KeyBindSystem (Thanks [soarqin](https://github.com/soarqin)) 4 | 5 | **Note:** Version v1.6.7 was **NOT** sufficiently tested. Some functionality might not work. Report an issue if that's the case. 6 | 7 |
8 | Changelog 9 | 10 | ### v1.6.6 11 | - Fixed custom languages not loading 12 | 13 | ### v1.6.5 14 | - Fixed issues when running with Nebula Multiplayer Mod API version 2.0.0 or higher 15 | 16 | ### v1.6.4 17 | - Fixed UINumberEntryExtension breaking layout and functionality of the signal picker. 18 | - Fixed picker extension translation entries not being added 19 | - Fixed NRE issues when adding TechProtos via ProtoRegistry 20 | 21 | ### v1.6.3 22 | - Added `RegisterKeyBindWithReturn()` method to `CustomKeyBindSystem` 23 | 24 | ### v1.6.2 25 | - Add ability to change text fonts 26 | 27 | ### v1.6.1 28 | - Fix errors when adding custom protos 29 | - Fix warnings about Common API not being built for 0.10.28.20729 30 | 31 | ### v1.6.0 32 | - Updated to work with game version 0.10.28.20729 or higher 33 | - Added Localization Module to support custom localizations. 34 | - ProtoRegistry methods related to StringProto are deprecated in favor of Localization Module 35 | - Internal restructure of the submodule system 36 | 37 | ### v1.5.7 38 | - Fix modded items not appearing on the production graphs. 39 | ### v1.5.6 40 | - Remove game exe name targeting 41 | ### v1.5.5 42 | - Update for Dyson Sphere Program update 43 | ### v1.5.4 44 | - Fixed errors when loading a save with one or more mods that add buildings (For example Better Machines) removed 45 | ### v1.5.3 46 | - Fix errors when model index is much bigger than maximum used by game. 47 | ### v1.5.2 48 | - Added UINumberPickerExtension for picking signal together with a value. 49 | ### v1.5.1 50 | - Testing release, no changes 51 | ### v1.5.0 52 | - Updated to work with game version 0.9.25.11985 or higher 53 | ### v1.4.9 54 | - @Raptor: Prevent creation of delegates every tick using alternate logic for Pool, which should improve performance of Various Facility. 55 | ### v1.4.8 56 | - Add checks to container export. Any mod issues should be logged and contained. 57 | - Fix Registry exporting data of empty items 58 | ### v1.4.7 59 | - Add public method to make other mods compatibility easier. Internal refactor. 60 | ### v1.4.6 61 | - Fix dynamic KeyBind ID assignment and migration being broken. Playes might lose some of previously rebound keybinds. 62 | ### v1.4.5 63 | - Fix issues adding multiple techologies with the same pretech 64 | - KeyBinds now dynamically assign ID's. To all modders using Custom KeyBinds: please stop assigning ID's when calling `RegisterKeyBind()` 65 | - FactoryComponent now has a method `UpdateNeeds()` that allows to set entityNeeds. 66 | ### v1.4.4 67 | - Add extension methods for customId and customType fields on EntityData class 68 | ### v1.4.3 69 | - Fixed `GetTabId` being impossible to call 70 | - Improved appearance of mod created tabs 71 | ### v1.4.2 72 | - Fix NRE in UISingalTip 73 | ### v1.4.1 74 | - Added UIWindowResize class, made by Raptor 75 | - Added ability to specify iconPath and name for recipes manually 76 | ### v1.4.0 77 | - Fix lava ocean type being displayed as missing item 78 | - Allow submodules have dependencies 79 | - Add AssemblerRecipeSystem 80 | - Refactor PickerExtensionSystem 81 | - Allow adding Signal Proto using ProtoRegistry 82 | ### v1.3.4 83 | - Fix missing items appearing instead of no item id 0 84 | ### v1.3.3 85 | - Fix missing items being broken. Also make it possible to delete them 86 | ### v1.3.2 87 | - Change StartModLoad function behavior 88 | ### v1.3.1 89 | - Now Machines added by mods will be automatically removed from save game if mod is uninstalled. 90 | - Corrected Game version CommonAPI is built for. 91 | ### v1.3.0 92 | - Add ability to register Audio using ProtoRegistry 93 | - Updated LDBTool to 2.0.1. Please make sure you are using 2.0.0 or higher. 94 | ### v1.2.2 95 | - Added plugin catergories on Thunderstore page. 96 | ### v1.2.1 97 | - Added ability to load modules manually. Useful for testing with ScriptEngine. 98 | ### v1.2.0 99 | - Migrated to CommonAPI-DSPModSave package. 100 | ### v1.1.0 101 | - Renamed CustomPlanetSystem to PlanetExtensionSystem 102 | - Renamed CustomStarSystem to StarExtensionSystem 103 | - Add show locked item and recipes feature to PickerExtensionModule 104 | - Improved Icon Generator 105 | ### v1.0.1 106 | - Fix issues selecting recipes in Assembler UI 107 | ### v1.0.0 108 | - Initial Release 109 |
-------------------------------------------------------------------------------- /CommonAPI/Systems/PickerExtensionSystem/Patches/UIItemPicker_Patch.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Reflection.Emit; 4 | using CommonAPI.Systems; 5 | using HarmonyLib; 6 | using UnityEngine; 7 | 8 | namespace CommonAPI.Patches 9 | { 10 | 11 | [HarmonyPatch] 12 | public class UIItemPicker_Patch 13 | { 14 | [HarmonyPatch(typeof(UIItemPicker), "RefreshIcons")] 15 | [HarmonyTranspiler] 16 | public static IEnumerable AddItemFilter(IEnumerable instructions) 17 | { 18 | CodeMatcher matcher = new CodeMatcher(instructions) 19 | .MatchForward(true, 20 | new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(ItemProto), nameof(ItemProto.GridIndex))), 21 | new CodeMatch(OpCodes.Ldc_I4) 22 | ).Advance(1); 23 | Label label = (Label) matcher.Instruction.operand; 24 | 25 | matcher.Advance(-2) 26 | .InsertAndAdvance(Transpilers.EmitDelegate>(proto => UIItemPickerExtension.currentFilter == null || UIItemPickerExtension.currentFilter.Invoke(proto))) 27 | .InsertAndAdvance(new CodeInstruction(OpCodes.Brfalse, label)) 28 | .InsertAndAdvance(new CodeInstruction(OpCodes.Ldloc_1)) 29 | .InsertAndAdvance(new CodeInstruction(OpCodes.Ldloc_3)) 30 | .InsertAndAdvance(new CodeInstruction(OpCodes.Ldelem_Ref)); 31 | 32 | matcher.MatchForward(true, 33 | new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(Proto), nameof(Proto.ID))), 34 | new CodeMatch(OpCodes.Callvirt)) 35 | .SetInstruction(Transpilers.EmitDelegate>(CheckItem)); 36 | 37 | 38 | return matcher.InstructionEnumeration(); 39 | } 40 | 41 | public static bool CheckItem(GameHistoryData history,int itemId) 42 | { 43 | if (UIItemPickerExtension.showLocked) return true; 44 | return history.ItemUnlocked(itemId); 45 | } 46 | 47 | [HarmonyPatch(typeof(UIItemPicker), "Popup", typeof(Vector2), typeof(Action))] 48 | [HarmonyPrefix] 49 | public static void IgnoreFilter(UIItemPicker __instance) 50 | { 51 | UIItemPickerExtension.currentFilter = null; 52 | UIItemPickerExtension.currentExtension = null; 53 | UIItemPickerExtension.showLocked = false; 54 | } 55 | 56 | [HarmonyPatch(typeof(UIItemPicker), "OnBoxMouseDown")] 57 | [HarmonyPrefix] 58 | public static bool OnBoxMouseDown(UIItemPicker __instance) 59 | { 60 | if (UIItemPickerExtension.currentExtension == null) return true; 61 | 62 | if (UIItemPickerExtension.currentExtension is IMouseHandlerExtension mouseHandler) 63 | { 64 | return mouseHandler.OnBoxMouseDown(__instance); 65 | } 66 | return true; 67 | } 68 | 69 | [HarmonyPatch(typeof(UIItemPicker), "TestMouseIndex")] 70 | [HarmonyPostfix] 71 | public static void TestMouseIndex(UIItemPicker __instance) 72 | { 73 | if (UIItemPickerExtension.currentExtension == null) return; 74 | 75 | if (UIItemPickerExtension.currentExtension is IMouseHandlerExtension mouseHandler) 76 | { 77 | mouseHandler.TestMouseIndex(__instance); 78 | } 79 | } 80 | 81 | [HarmonyPatch(typeof(UIItemPicker), "_OnOpen")] 82 | [HarmonyPostfix] 83 | public static void Open(UIItemPicker __instance) 84 | { 85 | if (UIItemPickerExtension.currentExtension == null) return; 86 | 87 | UIItemPickerExtension.currentExtension.Open(__instance); 88 | } 89 | 90 | [HarmonyPatch(typeof(UIItemPicker), "_OnClose")] 91 | [HarmonyPostfix] 92 | public static void Close(UIItemPicker __instance) 93 | { 94 | if (UIItemPickerExtension.currentExtension == null) return; 95 | UIItemPickerExtension.currentExtension.Close(__instance); 96 | } 97 | 98 | [HarmonyPatch(typeof(UIItemPicker), "_OnUpdate")] 99 | [HarmonyPostfix] 100 | public static void Update(UIItemPicker __instance) 101 | { 102 | if (UIItemPickerExtension.currentExtension == null) return; 103 | 104 | if (UIItemPickerExtension.currentExtension is IUpdatePickerExtension mouseHandler) 105 | { 106 | mouseHandler.OnUpdate(__instance); 107 | } 108 | } 109 | } 110 | } --------------------------------------------------------------------------------