commit 3de0e8a993932b0a44f0ba4641515bd49b73029e Author: CWX Date: Sat May 14 20:06:24 2022 +0100 initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0daaec2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,30 @@ +## ALT-Mods +*.exe +*.zip +bin/ +obj/ +*.editorconfig + +## visual studio +.vs +.idea +slnx.sqlite +slnx-journal.sqlite + +## nodejs +node_modules +node.exe +package-lock.json + +## windows +desktop.ini + +## ALT-Mods +Faupi-HideoutMod/Project/bin +Faupi-HideoutMod/Project/obj + +Faupi-MunitionsExpert/MunitionsExpert/bin +Faupi-MunitionsExpert/MunitionsExpert/obj + +KcY-SeeItemValue/itemValue/bin +KcY-SeeItemValue/itemValue/obj \ No newline at end of file diff --git a/Faupi-HideoutMod/HideoutArchitect/bepInEx/plugins/HideoutArchitect.dll b/Faupi-HideoutMod/HideoutArchitect/bepInEx/plugins/HideoutArchitect.dll new file mode 100644 index 0000000..0f710a2 Binary files /dev/null and b/Faupi-HideoutMod/HideoutArchitect/bepInEx/plugins/HideoutArchitect.dll differ diff --git a/Faupi-HideoutMod/HideoutArchitect/user/mods/Faupi-HideoutArchitect/LICENSE b/Faupi-HideoutMod/HideoutArchitect/user/mods/Faupi-HideoutArchitect/LICENSE new file mode 100644 index 0000000..94e9435 --- /dev/null +++ b/Faupi-HideoutMod/HideoutArchitect/user/mods/Faupi-HideoutArchitect/LICENSE @@ -0,0 +1,32 @@ +University of Illinois/NCSA Open Source License Copyright (c) 2021 Faupi. All rights reserved. + +Developed by: + +Faupi + +https://github.com/Faupi-SPTarkov/HideoutArchitect + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +with the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +* Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimers. + +* Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimers in the documentation +and/or other materials provided with the distribution. + +* Neither the names of Faupi, nor the names of its contributors may be used +to endorse or promote products derived from this Software without specific prior +written permission. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE CONTRIBUTORS +OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF +OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE. diff --git a/Faupi-HideoutMod/HideoutArchitect/user/mods/Faupi-HideoutArchitect/config.jsonc b/Faupi-HideoutMod/HideoutArchitect/user/mods/Faupi-HideoutArchitect/config.jsonc new file mode 100644 index 0000000..5014617 --- /dev/null +++ b/Faupi-HideoutMod/HideoutArchitect/user/mods/Faupi-HideoutArchitect/config.jsonc @@ -0,0 +1,9 @@ +{ + "NeededForHideoutDefinition": "NextLevel"/* + NeededForHideoutDefinition - Defines the logic used to determine which items are marked. See mod page for details. + Options: + NextLevel: Generally marks items required for the facility's next upgrade + NextLevelReady: Marks items required for the facility's next upgrade only when its other facility pre-requisites are fulfilled. (e.g. Items for Lavatory 2 won't be marked until Water Collector 1 is finished.)*/, + "TooltipHeaderColor": "#56C427"/* + TooltipHeaderColor - Color of the tooltip 'Needed for hideout' header.*/ +} \ No newline at end of file diff --git a/Faupi-HideoutMod/HideoutArchitect/user/mods/Faupi-HideoutArchitect/package.json b/Faupi-HideoutMod/HideoutArchitect/user/mods/Faupi-HideoutArchitect/package.json new file mode 100644 index 0000000..29c3f66 --- /dev/null +++ b/Faupi-HideoutMod/HideoutArchitect/user/mods/Faupi-HideoutArchitect/package.json @@ -0,0 +1,8 @@ +{ + "name": "Faupi-HideoutArchitect", + "author": "Faupi", + "version": "1.4.1", + "license": "NCSA Open Source", + "main": "./src/hideoutarchitect.js", + "akiVersion": "2.3.1" +} \ No newline at end of file diff --git a/Faupi-HideoutMod/HideoutArchitect/user/mods/Faupi-HideoutArchitect/res/icon_neededforhideout_small.png b/Faupi-HideoutMod/HideoutArchitect/user/mods/Faupi-HideoutArchitect/res/icon_neededforhideout_small.png new file mode 100644 index 0000000..2c08c2c Binary files /dev/null and b/Faupi-HideoutMod/HideoutArchitect/user/mods/Faupi-HideoutArchitect/res/icon_neededforhideout_small.png differ diff --git a/Faupi-HideoutMod/HideoutArchitect/user/mods/Faupi-HideoutArchitect/res/translations.json b/Faupi-HideoutMod/HideoutArchitect/user/mods/Faupi-HideoutArchitect/res/translations.json new file mode 100644 index 0000000..0c42800 --- /dev/null +++ b/Faupi-HideoutMod/HideoutArchitect/user/mods/Faupi-HideoutArchitect/res/translations.json @@ -0,0 +1,47 @@ +{ + "en": { + "NEEDED FOR HIDEOUT": "Needed for hideout" + }, + "cz": { + "NEEDED FOR HIDEOUT": "Potřebné pro úkryt" + }, + "pl": { + "NEEDED FOR HIDEOUT": "Potrzebne do kryjówki" + }, + "po": { + "NEEDED FOR HIDEOUT": "Necessário para esconderijo" + }, + "ch": { + "NEEDED FOR HIDEOUT": "藏身之处所需" + }, + "ru": { + "NEEDED FOR HIDEOUT": "Необходим для укрытия" + }, + "es": { + "NEEDED FOR HIDEOUT": "Se necesita para el escondite" + }, + "es-mx": { + "NEEDED FOR HIDEOUT": "Se necesita para el escondite" + }, + "ge": { + "NEEDED FOR HIDEOUT": "Benötigt für Versteck" + }, + "sk": { + "NEEDED FOR HIDEOUT": "Potrebné pre úkryt" + }, + "tu": { + "NEEDED FOR HIDEOUT": "saklanmak için gerekli" + }, + "it": { + "NEEDED FOR HIDEOUT": "Necessario per il nascondiglio" + }, + "jp": { + "NEEDED FOR HIDEOUT": "アジトに必要なもの" + }, + "fr": { + "NEEDED FOR HIDEOUT": "Nécessaire pour la cachette" + }, + "hu": { + "NEEDED FOR HIDEOUT": "Rejtekhelyhez szükséges" + } +} \ No newline at end of file diff --git a/Faupi-HideoutMod/HideoutArchitect/user/mods/Faupi-HideoutArchitect/src/hideoutarchitect.js b/Faupi-HideoutMod/HideoutArchitect/user/mods/Faupi-HideoutArchitect/src/hideoutarchitect.js new file mode 100644 index 0000000..c95bd72 --- /dev/null +++ b/Faupi-HideoutMod/HideoutArchitect/user/mods/Faupi-HideoutArchitect/src/hideoutarchitect.js @@ -0,0 +1,68 @@ +/* hideoutarchitect.js + * license: NCSA + * copyright: Faupi + * authors: + * - Faupi + */ + +"use strict"; +const path = require('path'); + +class HideoutArchitect { + constructor() { + this.mod = require("../package.json"); + this.translations = require("../res/translations.json"); + Logger.info(`Loading: ${this.mod.name} ${this.mod.version}`); + + ModLoader.onLoad[this.mod.name] = this.init.bind(this); + } + + init(){ + this.hookRoutes(); + this.loadLocalization(); + } + + loadLocalization(){ + var globalLocale = DatabaseServer.tables.locales.global; + + for(let language in this.translations){ + if(!language in globalLocale) continue; + + let attrKvPair = this.translations[language]; + for(let attrKey in attrKvPair){ + let attrValue = attrKvPair[attrKey]; + + globalLocale[language].interface[attrKey] = attrValue; + } + } + } + + log(data){ + // console.log(data); + } + + hookRoutes(){ + HttpRouter.onStaticRoute["/HideoutArchitect/GetInfo"] = { + AttachmentOffset: this.getModInfo.bind(this) + }; + } + + getModInfo(url, info, sessionID, output){ + var output = { + status: 1, + data: null + }; + + // Don't mind this pointless try catch + try{ + output.data = {...this.mod, ...{path: path.resolve(ModLoader.getModPath(this.mod.name))}}; + output.status = 0; + }catch(ex){ + throw ex; + } + + return JsonUtil.serialize(output); + } +} + +module.exports = new HideoutArchitect(); \ No newline at end of file diff --git a/Faupi-HideoutMod/Project/HideoutArchitect.csproj b/Faupi-HideoutMod/Project/HideoutArchitect.csproj new file mode 100644 index 0000000..8ec6e95 --- /dev/null +++ b/Faupi-HideoutMod/Project/HideoutArchitect.csproj @@ -0,0 +1,135 @@ + + + + + Debug + AnyCPU + {21B1900E-2DFA-4C18-A1BD-E10907620FE3} + Library + Properties + HideoutArchitect + HideoutArchitect + v4.7.2 + 512 + true + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + true + + + + + + + + + + + + + + + + + + + + + + + + + + False + ..\..\..\..\..\..\..\AKI PROJECT\AKI 2.3.0 17107 Bepin\EscapeFromTarkov_Data\Managed\Aki.Common.dll + + + False + ..\..\..\..\..\..\..\AKI PROJECT\AKI 2.3.0 17107 Bepin\EscapeFromTarkov_Data\Managed\Aki.Reflection.dll + + + False + ..\..\..\..\..\..\..\AKI PROJECT\AKI 2.3.0 17107 Bepin\EscapeFromTarkov_Data\Managed\Assembly-CSharp.dll + + + ..\..\..\..\..\..\..\AKI PROJECT\AKI 2.3.0 17107 Bepin\BepInEx\core\BepInEx.dll + + + False + ..\..\..\..\..\..\..\AKI PROJECT\AKI 2.3.0 17107 Bepin\EscapeFromTarkov_Data\Managed\Comfort.dll + + + False + ..\..\..\..\..\..\..\AKI PROJECT\AKI 2.3.0 17107 Bepin\EscapeFromTarkov_Data\Managed\Newtonsoft.Json.dll + + + False + ..\..\..\..\..\..\..\AKI PROJECT\AKI 2.3.0 17107 Bepin\EscapeFromTarkov_Data\Managed\Sirenix.Serialization.dll + + + + + False + ..\..\..\..\..\..\..\AKI PROJECT\AKI 2.3.0 17107 Bepin\EscapeFromTarkov_Data\Managed\Unity.TextMeshPro.dll + + + False + ..\..\..\..\..\..\..\AKI PROJECT\AKI 2.3.0 17107 Bepin\EscapeFromTarkov_Data\Managed\UnityEngine.dll + + + False + ..\..\..\..\..\..\..\AKI PROJECT\AKI 2.3.0 17107 Bepin\EscapeFromTarkov_Data\Managed\UnityEngine.CoreModule.dll + + + False + ..\..\..\..\..\..\..\AKI PROJECT\AKI 2.3.0 17107 Bepin\EscapeFromTarkov_Data\Managed\UnityEngine.UI.dll + + + False + ..\..\..\..\..\..\..\AKI PROJECT\AKI 2.3.0 17107 Bepin\EscapeFromTarkov_Data\Managed\UnityEngine.UnityWebRequestModule.dll + + + False + ..\..\..\..\..\..\..\AKI PROJECT\AKI 2.3.0 17107 Bepin\EscapeFromTarkov_Data\Managed\UnityEngine.UnityWebRequestTextureModule.dll + + + False + ..\..\..\..\..\..\..\AKI PROJECT\AKI 2.3.0 17107 Bepin\EscapeFromTarkov_Data\Managed\UnityEngine.UnityWebRequestWWWModule.dll + + + + + set BuildDir=$(ProjectDir)_Build\Faupi-HideoutArchitect + +powershell -Command "Remove-Item '%25BuildDir%25\*' -Recurse -Force" + +copy "$(TargetPath)" "%25BuildDir%25\module.dll" +xcopy "$(ProjectDir)src\server" "%25BuildDir%25\src" /K /D /H /Y /I +xcopy "$(ProjectDir)res" "%25BuildDir%25\res" /K /D /H /Y /I +copy "$(ProjectDir)package.json" "%25BuildDir%25\package.json" + + + x64 + + + + + + \ No newline at end of file diff --git a/Faupi-HideoutMod/Project/HideoutArchitect.sln b/Faupi-HideoutMod/Project/HideoutArchitect.sln new file mode 100644 index 0000000..056db27 --- /dev/null +++ b/Faupi-HideoutMod/Project/HideoutArchitect.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.32014.148 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HideoutArchitect", "HideoutArchitect.csproj", "{21B1900E-2DFA-4C18-A1BD-E10907620FE3}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {21B1900E-2DFA-4C18-A1BD-E10907620FE3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {21B1900E-2DFA-4C18-A1BD-E10907620FE3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {21B1900E-2DFA-4C18-A1BD-E10907620FE3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {21B1900E-2DFA-4C18-A1BD-E10907620FE3}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {DFEE36A3-C9ED-45DF-9015-A34EC8AB8641} + EndGlobalSection +EndGlobal diff --git a/Faupi-HideoutMod/Project/LICENSE b/Faupi-HideoutMod/Project/LICENSE new file mode 100644 index 0000000..94e9435 --- /dev/null +++ b/Faupi-HideoutMod/Project/LICENSE @@ -0,0 +1,32 @@ +University of Illinois/NCSA Open Source License Copyright (c) 2021 Faupi. All rights reserved. + +Developed by: + +Faupi + +https://github.com/Faupi-SPTarkov/HideoutArchitect + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +with the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +* Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimers. + +* Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimers in the documentation +and/or other materials provided with the distribution. + +* Neither the names of Faupi, nor the names of its contributors may be used +to endorse or promote products derived from this Software without specific prior +written permission. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE CONTRIBUTORS +OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF +OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE. diff --git a/Faupi-HideoutMod/Project/Properties/AssemblyInfo.cs b/Faupi-HideoutMod/Project/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..8e8031e --- /dev/null +++ b/Faupi-HideoutMod/Project/Properties/AssemblyInfo.cs @@ -0,0 +1,38 @@ +using System.Resources; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Hideout Architect")] +[assembly: AssemblyDescription("Similar to the Found in raid mark - Adds a Needed for hideout mark to items")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("SPT-AKI - HideoutArchitect")] +[assembly: AssemblyCopyright("Faupi © 2021")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("21b1900e-2dfa-4c18-a1bd-e10907620fe3")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.1.0.0")] +[assembly: AssemblyFileVersion("1.1.0.0")] +[assembly: NeutralResourcesLanguage("en-US")] diff --git a/Faupi-HideoutMod/Project/_Build/Faupi-HideoutArchitect/module.dll b/Faupi-HideoutMod/Project/_Build/Faupi-HideoutArchitect/module.dll new file mode 100644 index 0000000..0f710a2 Binary files /dev/null and b/Faupi-HideoutMod/Project/_Build/Faupi-HideoutArchitect/module.dll differ diff --git a/Faupi-HideoutMod/Project/_Build/Faupi-HideoutArchitect/package.json b/Faupi-HideoutMod/Project/_Build/Faupi-HideoutArchitect/package.json new file mode 100644 index 0000000..5cf480a --- /dev/null +++ b/Faupi-HideoutMod/Project/_Build/Faupi-HideoutArchitect/package.json @@ -0,0 +1,8 @@ +{ + "name": "Faupi-HideoutArchitect", + "author": "Faupi", + "version": "1.2.1", + "license": "NCSA Open Source", + "main": "./src/hideoutarchitect.js", + "akiVersion": "2.1.1" +} \ No newline at end of file diff --git a/Faupi-HideoutMod/Project/_Build/Faupi-HideoutArchitect/res/icon_neededforhideout_small.png b/Faupi-HideoutMod/Project/_Build/Faupi-HideoutArchitect/res/icon_neededforhideout_small.png new file mode 100644 index 0000000..2c08c2c Binary files /dev/null and b/Faupi-HideoutMod/Project/_Build/Faupi-HideoutArchitect/res/icon_neededforhideout_small.png differ diff --git a/Faupi-HideoutMod/Project/_Build/Faupi-HideoutArchitect/res/translations.json b/Faupi-HideoutMod/Project/_Build/Faupi-HideoutArchitect/res/translations.json new file mode 100644 index 0000000..0c42800 --- /dev/null +++ b/Faupi-HideoutMod/Project/_Build/Faupi-HideoutArchitect/res/translations.json @@ -0,0 +1,47 @@ +{ + "en": { + "NEEDED FOR HIDEOUT": "Needed for hideout" + }, + "cz": { + "NEEDED FOR HIDEOUT": "Potřebné pro úkryt" + }, + "pl": { + "NEEDED FOR HIDEOUT": "Potrzebne do kryjówki" + }, + "po": { + "NEEDED FOR HIDEOUT": "Necessário para esconderijo" + }, + "ch": { + "NEEDED FOR HIDEOUT": "藏身之处所需" + }, + "ru": { + "NEEDED FOR HIDEOUT": "Необходим для укрытия" + }, + "es": { + "NEEDED FOR HIDEOUT": "Se necesita para el escondite" + }, + "es-mx": { + "NEEDED FOR HIDEOUT": "Se necesita para el escondite" + }, + "ge": { + "NEEDED FOR HIDEOUT": "Benötigt für Versteck" + }, + "sk": { + "NEEDED FOR HIDEOUT": "Potrebné pre úkryt" + }, + "tu": { + "NEEDED FOR HIDEOUT": "saklanmak için gerekli" + }, + "it": { + "NEEDED FOR HIDEOUT": "Necessario per il nascondiglio" + }, + "jp": { + "NEEDED FOR HIDEOUT": "アジトに必要なもの" + }, + "fr": { + "NEEDED FOR HIDEOUT": "Nécessaire pour la cachette" + }, + "hu": { + "NEEDED FOR HIDEOUT": "Rejtekhelyhez szükséges" + } +} \ No newline at end of file diff --git a/Faupi-HideoutMod/Project/_Build/Faupi-HideoutArchitect/src/hideoutarchitect.js b/Faupi-HideoutMod/Project/_Build/Faupi-HideoutArchitect/src/hideoutarchitect.js new file mode 100644 index 0000000..c95bd72 --- /dev/null +++ b/Faupi-HideoutMod/Project/_Build/Faupi-HideoutArchitect/src/hideoutarchitect.js @@ -0,0 +1,68 @@ +/* hideoutarchitect.js + * license: NCSA + * copyright: Faupi + * authors: + * - Faupi + */ + +"use strict"; +const path = require('path'); + +class HideoutArchitect { + constructor() { + this.mod = require("../package.json"); + this.translations = require("../res/translations.json"); + Logger.info(`Loading: ${this.mod.name} ${this.mod.version}`); + + ModLoader.onLoad[this.mod.name] = this.init.bind(this); + } + + init(){ + this.hookRoutes(); + this.loadLocalization(); + } + + loadLocalization(){ + var globalLocale = DatabaseServer.tables.locales.global; + + for(let language in this.translations){ + if(!language in globalLocale) continue; + + let attrKvPair = this.translations[language]; + for(let attrKey in attrKvPair){ + let attrValue = attrKvPair[attrKey]; + + globalLocale[language].interface[attrKey] = attrValue; + } + } + } + + log(data){ + // console.log(data); + } + + hookRoutes(){ + HttpRouter.onStaticRoute["/HideoutArchitect/GetInfo"] = { + AttachmentOffset: this.getModInfo.bind(this) + }; + } + + getModInfo(url, info, sessionID, output){ + var output = { + status: 1, + data: null + }; + + // Don't mind this pointless try catch + try{ + output.data = {...this.mod, ...{path: path.resolve(ModLoader.getModPath(this.mod.name))}}; + output.status = 0; + }catch(ex){ + throw ex; + } + + return JsonUtil.serialize(output); + } +} + +module.exports = new HideoutArchitect(); \ No newline at end of file diff --git a/Faupi-HideoutMod/Project/icon_neededforhideout_small.pdn b/Faupi-HideoutMod/Project/icon_neededforhideout_small.pdn new file mode 100644 index 0000000..3d02f3b Binary files /dev/null and b/Faupi-HideoutMod/Project/icon_neededforhideout_small.pdn differ diff --git a/Faupi-HideoutMod/Project/package.json b/Faupi-HideoutMod/Project/package.json new file mode 100644 index 0000000..5cf480a --- /dev/null +++ b/Faupi-HideoutMod/Project/package.json @@ -0,0 +1,8 @@ +{ + "name": "Faupi-HideoutArchitect", + "author": "Faupi", + "version": "1.2.1", + "license": "NCSA Open Source", + "main": "./src/hideoutarchitect.js", + "akiVersion": "2.1.1" +} \ No newline at end of file diff --git a/Faupi-HideoutMod/Project/res/icon_neededforhideout_small.png b/Faupi-HideoutMod/Project/res/icon_neededforhideout_small.png new file mode 100644 index 0000000..2c08c2c Binary files /dev/null and b/Faupi-HideoutMod/Project/res/icon_neededforhideout_small.png differ diff --git a/Faupi-HideoutMod/Project/res/translations.json b/Faupi-HideoutMod/Project/res/translations.json new file mode 100644 index 0000000..0c42800 --- /dev/null +++ b/Faupi-HideoutMod/Project/res/translations.json @@ -0,0 +1,47 @@ +{ + "en": { + "NEEDED FOR HIDEOUT": "Needed for hideout" + }, + "cz": { + "NEEDED FOR HIDEOUT": "Potřebné pro úkryt" + }, + "pl": { + "NEEDED FOR HIDEOUT": "Potrzebne do kryjówki" + }, + "po": { + "NEEDED FOR HIDEOUT": "Necessário para esconderijo" + }, + "ch": { + "NEEDED FOR HIDEOUT": "藏身之处所需" + }, + "ru": { + "NEEDED FOR HIDEOUT": "Необходим для укрытия" + }, + "es": { + "NEEDED FOR HIDEOUT": "Se necesita para el escondite" + }, + "es-mx": { + "NEEDED FOR HIDEOUT": "Se necesita para el escondite" + }, + "ge": { + "NEEDED FOR HIDEOUT": "Benötigt für Versteck" + }, + "sk": { + "NEEDED FOR HIDEOUT": "Potrebné pre úkryt" + }, + "tu": { + "NEEDED FOR HIDEOUT": "saklanmak için gerekli" + }, + "it": { + "NEEDED FOR HIDEOUT": "Necessario per il nascondiglio" + }, + "jp": { + "NEEDED FOR HIDEOUT": "アジトに必要なもの" + }, + "fr": { + "NEEDED FOR HIDEOUT": "Nécessaire pour la cachette" + }, + "hu": { + "NEEDED FOR HIDEOUT": "Rejtekhelyhez szükséges" + } +} \ No newline at end of file diff --git a/Faupi-HideoutMod/Project/src/client/HideoutItemViewPanel.cs b/Faupi-HideoutMod/Project/src/client/HideoutItemViewPanel.cs new file mode 100644 index 0000000..1e4007c --- /dev/null +++ b/Faupi-HideoutMod/Project/src/client/HideoutItemViewPanel.cs @@ -0,0 +1,97 @@ +using EFT.Hideout; +using EFT.InventoryLogic; +using EFT.UI; +using EFT.UI.DragAndDrop; +using JetBrains.Annotations; +using System; +using System.Collections.Generic; +using System.Linq; +using TMPro; +using UnityEngine; +using UnityEngine.EventSystems; +using UnityEngine.UI; + +namespace HideoutArchitect +{ + public class HideoutItemViewPanel : UIElement + { + public Image iconImage; //_questIconImage + public TextMeshProUGUI tooltipLabel; //_questItemLabel + public SimpleTooltip tooltip; //simpleTooltip_0 + public string tooltipString; //string_3 + public ItemView itemView; + + public bool initialized; + + public void Init() + { + if (initialized) return; + + HoverTrigger orAddComponent = base.gameObject.GetOrAddComponent(); + orAddComponent.OnHoverStart += this.ShowTooltip; + orAddComponent.OnHoverEnd += this.HideTooltip; + initialized = true; + } + + private void Awake() + { + Init(); + } + + public void Show(Item item, ItemView itemView, [CanBeNull] SimpleTooltip tooltip) + { + Init(); + + this.itemView = itemView; + + if (this.tooltipLabel != null) + this.tooltipLabel.gameObject.SetActive(true); + this.tooltip = tooltip; + + UpdateTooltip(); + + if (HideoutArchitect.IsNeededForHideoutUpgrades(item)) + base.ShowGameObject(); + } + + public void UpdateTooltip() + { + List parts = new List() { $"{"NEEDED FOR HIDEOUT".Localized().ToSentenceCase()}:" }; + + List areasToUpgrade = HideoutArchitect.GetApplicableUpgrades(this.itemView.Item); + if (areasToUpgrade == null || areasToUpgrade.Count < 1) return; + + areasToUpgrade.ForEach(a => + { + try + { + parts.Add($"{a.Template.Name.ToSentenceCase() /*Already localized*/}: {"LVL".Localized().ToSentenceCase()} {a.CurrentLevel + 1}"); + } + catch (Exception ex) + { + Debug.LogError(ex); + } + }); + + tooltipString = String.Join("\n", parts.Select(a => a.Trim())); + } + + private void ShowTooltip(PointerEventData arg) + { + if (!(this.tooltip == null) && !string.IsNullOrEmpty(this.tooltipString)) + { + this.tooltip.Show(this.tooltipString, null, 0f, null, true); + return; + } + } + + private void HideTooltip(PointerEventData arg) + { + if (!(this.tooltip == null) && !string.IsNullOrEmpty(this.tooltipString)) + { + this.tooltip.Close(); + return; + } + } + } +} diff --git a/Faupi-HideoutMod/Project/src/client/Main.cs b/Faupi-HideoutMod/Project/src/client/Main.cs new file mode 100644 index 0000000..2513e42 --- /dev/null +++ b/Faupi-HideoutMod/Project/src/client/Main.cs @@ -0,0 +1,135 @@ +using Comfort.Common; +using EFT.Hideout; +using EFT.InventoryLogic; +using HideoutArchitect.Patches; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using BepInEx; +using UnityEngine; +using Hideout = GClass1588; +using Requirement = GClass1612; + +namespace HideoutArchitect +{ + [BepInPlugin("com.FAUPI.HideoutArchitect", "FAUPI-HideoutArchitect", "2.0.0")] + public class HideoutArchitect : BaseUnityPlugin + { + private void Awake() + { + _ = ModConfig; // Load the mod config + _ = Resources.LoadTexture("neededforhideout", Path.Combine(ModInfo.path, "res/icon_neededforhideout_small.png")); + Patcher.PatchAll(); + } + + private static ModConfiguration _modConfig; + public static ModConfiguration ModConfig + { + private set + { + _modConfig = value; + } + get + { + if (_modConfig == null) + _modConfig = ModConfiguration.Load(ModInfo); + return _modConfig; + } + } + + private static ModInformation _modInfo; + public static ModInformation ModInfo + { + private set + { + _modInfo = value; + } + get + { + if (_modInfo == null) + _modInfo = ModInformation.Load(); + return _modInfo; + } + } + + private static Transform _gameObjectStorage; + public static Transform GameObjectStorage + { + get + { + if (_gameObjectStorage == null) + { + GameObject storage = new GameObject("HideoutArchitect Storage"); + UnityEngine.Object.DontDestroyOnLoad(storage); + storage.SetActive(false); + _gameObjectStorage = storage.transform; + } + + return _gameObjectStorage; + } + } + + public static List GetApplicableUpgrades(Item item) + { + List areas = Singleton.Instance.AreaDatas.Where(area => + { + bool areaActive = area.Status != EAreaStatus.NotSet && area.Template.Enabled == true; + + List targetedRequirements; + switch (ModConfig.NeededForHideoutDefinition) + { + case ENeededDefinition.NextLevel: + case ENeededDefinition.NextLevelReady: + targetedRequirements = area.NextStage.Requirements.Value as List; + break; + default: + throw new NotImplementedException(Enum.GetName(typeof(ENeededDefinition), ModConfig.NeededForHideoutDefinition)); + } + + bool areaHasRequirements = targetedRequirements != null && targetedRequirements.Count > 0; + + bool itemFitsRequirements = targetedRequirements.Any(genericRequirement => + { + ItemRequirement itemRequirement = genericRequirement as ItemRequirement; + if (itemRequirement == null) return false; + return itemRequirement.TemplateId == item.TemplateId; + }); + + bool fitsSpecialFilter = false; + switch (ModConfig.NeededForHideoutDefinition) + { + case ENeededDefinition.NextLevel: + fitsSpecialFilter = true; // None, we're only getting the item req + break; + + case ENeededDefinition.NextLevelReady: + fitsSpecialFilter = targetedRequirements.All(genericRequirement => // Check if area requirement is fulfilled + { + if (genericRequirement is AreaRequirement) + { + return genericRequirement.Fulfilled; // If area requirements are fulfilled + } + else + { + return true; + } + } + ); + break; + default: + throw new NotImplementedException(Enum.GetName(typeof(ENeededDefinition), ModConfig.NeededForHideoutDefinition)); + } + return areaActive && areaHasRequirements && itemFitsRequirements && fitsSpecialFilter; + }).ToList(); + + return areas; + } + + public static bool IsNeededForHideoutUpgrades(Item item) + { + List data = GetApplicableUpgrades(item); + return data != null && data.Count > 0; + } + } +} diff --git a/Faupi-HideoutMod/Project/src/client/ModConfiguration.cs b/Faupi-HideoutMod/Project/src/client/ModConfiguration.cs new file mode 100644 index 0000000..cb49b80 --- /dev/null +++ b/Faupi-HideoutMod/Project/src/client/ModConfiguration.cs @@ -0,0 +1,128 @@ +using Aki.Common.Utils; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using System; +using UnityEngine; + +namespace HideoutArchitect +{ + public enum ENeededDefinition + { + NextLevel, + NextLevelReady + } + public class ModConfiguration + { + private string _tooltipHeaderColor = "#56C427"; + + [JsonConverter(typeof(Utils.StringEnumCommentConverter), "\n\tNeededForHideoutDefinition - Defines the logic used to determine which items are marked. See mod page for details. \n\tOptions: \n\t\tNextLevel: Generally marks items required for the facility's next upgrade\n\t\tNextLevelReady: Marks items required for the facility's next upgrade only when its other facility pre-requisites are fulfilled. (e.g. Items for Lavatory 2 won't be marked until Water Collector 1 is finished.)")] + public ENeededDefinition NeededForHideoutDefinition = ENeededDefinition.NextLevel; + + [JsonConverter(typeof(Utils.JsonCommentConverter), "\n\tTooltipHeaderColor - Color of the tooltip 'Needed for hideout' header.")] + public string TooltipHeaderColor + { + get => _tooltipHeaderColor; + set + { + if (value.IsValidHexColor()) + _tooltipHeaderColor = value; + } + } + + public ModConfiguration() + { + } + + public static ModConfiguration Load(ModInformation ModInfo) + { + string path = VFS.Combine(ModInfo.path, "config.jsonc"); + Debug.LogError($"Loading config from '{path}'"); + + ModConfiguration ModConfig; + + string configJson = null; + try + { + if (!VFS.Exists(path)) + throw new Exception($"No config on path {path} exists!"); + + configJson = VFS.ReadTextFile(path); + ModConfig = JsonConvert.DeserializeObject(configJson); + } + catch (Exception configReadingException) + { + string loadErrorMsg = $"[{ModInfo.name}] Could not load config!"; + Debug.LogError(loadErrorMsg); + Debug.LogError(configReadingException); + + // Recreate/fill config file + JObject defaultConfig = JObject.Parse(JsonConvert.SerializeObject(new ModConfiguration())); + if (configJson != null) + { + void LogBadJsonFormatting(Exception exception = null) + { + string mergeLoadedConfigMsg = $"[{ModInfo.name}] There was a {(exception != null ? "fatal " : string.Empty)}problem with loading config as JSON, there's likely a bad typo."; + Debug.LogError(mergeLoadedConfigMsg); + Debug.LogError(exception); + Debug.LogError("Restoring config defaults completely."); + } + + if (configJson.IsValidJson()) + { + try + { + Debug.LogError($"[{ModInfo.name}] Merging existing config data with defaults."); + // If the file loaded at least partially, overwrite the defaults with it + JObject loadedConfigPart = JObject.Parse(configJson); + defaultConfig.Merge(loadedConfigPart, new JsonMergeSettings + { + // union array values together to avoid duplicates + MergeArrayHandling = MergeArrayHandling.Union + }); + } + catch (Exception mergeLoadedConfigException) + { + LogBadJsonFormatting(mergeLoadedConfigException); + } + } + else + { + LogBadJsonFormatting(); + } + } + + string fixedConfigJson = defaultConfig.ToString(); + try + { + Debug.LogError($"[{ModInfo.name}] Parsing default config to JSON\nJson: {fixedConfigJson}"); + ModConfig = JsonConvert.DeserializeObject(fixedConfigJson); + } + catch (Exception configReconstructionException) + { + string fillErrorMsg = $"[{ModInfo.name}] Could not restore config values!"; + Debug.LogError(fillErrorMsg); + Debug.LogError("Yell at Faupi with the logs."); + + throw configReconstructionException; // Throw because at this point we can't really continue + } + + string completeConfigJson = JsonConvert.SerializeObject(ModConfig, Formatting.Indented); + if (completeConfigJson != configJson) // There's a difference between the config file and actual config + { + try + { + Debug.LogError($"[{ModInfo.name}] Writing fixed config..."); + VFS.WriteTextFile(path, completeConfigJson.ToString(), false); + } + catch + { + Debug.LogError($"[{ModInfo.name}] There was a problem with writing the config."); + throw; + } + } + } + + return ModConfig; + } + } +} diff --git a/Faupi-HideoutMod/Project/src/client/ModInformation.cs b/Faupi-HideoutMod/Project/src/client/ModInformation.cs new file mode 100644 index 0000000..9042070 --- /dev/null +++ b/Faupi-HideoutMod/Project/src/client/ModInformation.cs @@ -0,0 +1,41 @@ +using Newtonsoft.Json.Linq; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; +using UnityEngine.Assertions; + +namespace HideoutArchitect +{ + public class ModInformation + { + public string name; + public string author; + public string version; + public string license; + public string main; + public string path; + + public static ModInformation Load() + { + ModInformation ModInfo; + + JObject response = JObject.Parse(Aki.Common.Http.RequestHandler.GetJson($"/HideoutArchitect/GetInfo")); + try + { + Assert.IsTrue(response.Value("status") == 0); + ModInfo = response["data"].ToObject(); + } + catch (Exception getModInfoException) + { + string errMsg = $"[{typeof(HideoutArchitect)}] Package.json couldn't be found! Make sure you've installed the mod on the server as well!"; + Debug.LogError(errMsg); + throw getModInfoException; + } + + return ModInfo; + } + } +} diff --git a/Faupi-HideoutMod/Project/src/client/Patches.cs b/Faupi-HideoutMod/Project/src/client/Patches.cs new file mode 100644 index 0000000..2396714 --- /dev/null +++ b/Faupi-HideoutMod/Project/src/client/Patches.cs @@ -0,0 +1,121 @@ +using EFT.InventoryLogic; +using EFT.UI; +using EFT.UI.DragAndDrop; +using System.Collections.Generic; +using System.Reflection; +using UnityEngine; +using Aki.Reflection.Patching; + +namespace HideoutArchitect.Patches +{ + class Patcher + { + public static void PatchAll() + { + new PatchManager().RunPatches(); + } + } + + public class PatchManager + { + public PatchManager() + { + this._patches = new List + { + new ItemViewPatches.GridItemViewUpdateInfoPatch(), + new ItemViewPatches.ItemViewInitPatch(), + new ItemViewPatches.NewGridItemViewPatch() + }; + } + + public void RunPatches() + { + foreach (ModulePatch patch in this._patches) + { + patch.Enable(); + } + } + + private readonly List _patches; + } + + public static class ItemViewPatches + { + public static Dictionary hideoutPanels = new Dictionary(); + + public static void SetHideoutItemViewPanel(this ItemView __instance) + { + if (!hideoutPanels.TryGetValue(__instance, out HideoutItemViewPanel hideoutItemViewPanel)) + return; + + ItemUiContext itemUiContext = typeof(ItemView).GetField("ItemUiContext", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(__instance) as ItemUiContext; + + if (hideoutItemViewPanel != null) + { + hideoutItemViewPanel.Show(__instance.Item, __instance, itemUiContext?.Tooltip); + return; + } + } + + public class NewGridItemViewPatch : ModulePatch + { + protected override MethodBase GetTargetMethod() + { + return typeof(GridItemView).GetMethod("NewGridItemView", BindingFlags.Instance | BindingFlags.NonPublic); + } + + [PatchPostfix] + private static void PatchPostfix(ref GridItemView __instance, Item item) + { + if (hideoutPanels.ContainsKey(__instance)) return; + try + { + QuestItemViewPanel questIconPanel = typeof(ItemView).GetField("_questsItemViewPanel", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(__instance) as QuestItemViewPanel; + HideoutItemViewPanel hideoutIconPanel = GameObject.Instantiate(Resources.GetEditOffsetWindowTemplate(questIconPanel), questIconPanel.transform.parent); + hideoutIconPanel.transform.SetAsFirstSibling(); + hideoutPanels[__instance] = hideoutIconPanel; + + hideoutIconPanel.gameObject.SetActive(true); + } + catch { + // Item doesn't have a "quest item" icon panel, so it's probably static + } + } + } + + public class ItemViewInitPatch : ModulePatch + { + protected override MethodBase GetTargetMethod() + { + return typeof(ItemView).GetMethod("Init", BindingFlags.Instance | BindingFlags.NonPublic); + } + + [PatchPostfix] + private static void PatchPostfix(ref ItemView __instance) + { + __instance.SetHideoutItemViewPanel(); + } + } + + public class GridItemViewUpdateInfoPatch : ModulePatch + { + protected override MethodBase GetTargetMethod() + { + return typeof(GridItemView).GetMethod("UpdateInfo", BindingFlags.Instance | BindingFlags.Public); + } + + [PatchPostfix] + private static void PatchPostfix(ref GridItemView __instance) + { + if (!__instance.IsSearched) + return; + + if (!hideoutPanels.TryGetValue(__instance, out HideoutItemViewPanel hideoutItemViewPanel)) + return; + hideoutItemViewPanel.iconImage.gameObject.SetActive(HideoutArchitect.IsNeededForHideoutUpgrades(__instance.Item)); + + __instance.SetHideoutItemViewPanel(); + } + } + } +} diff --git a/Faupi-HideoutMod/Project/src/client/Resources.cs b/Faupi-HideoutMod/Project/src/client/Resources.cs new file mode 100644 index 0000000..25ab9a6 --- /dev/null +++ b/Faupi-HideoutMod/Project/src/client/Resources.cs @@ -0,0 +1,81 @@ +using EFT.UI; +using EFT.UI.DragAndDrop; +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Threading.Tasks; +using TMPro; +using UnityEngine; +using UnityEngine.Networking; +using UnityEngine.UI; + +namespace HideoutArchitect +{ + public static class Resources + { + public static Dictionary iconCache = new Dictionary(); + static HideoutItemViewPanel hideoutItemViewTemplate; + + public static HideoutItemViewPanel GetEditOffsetWindowTemplate(QuestItemViewPanel original = null) + { + if (hideoutItemViewTemplate != null) + return hideoutItemViewTemplate; + + if (original == null) + throw new ArgumentNullException("original", "Can't be null if template isn't created yet!"); + + QuestItemViewPanel clone = GameObject.Instantiate(original); + GameObject newObject = clone.gameObject; + clone.transform.parent = HideoutArchitect.GameObjectStorage; + newObject.name = "HideoutItem"; + + HideoutItemViewPanel result = newObject.AddComponent(); + + //Copy fields over + result.CopyFieldsFromQuestView(clone); + + //Set custom sprite + if(result.iconImage != null) + { + result.iconImage.sprite = iconCache["neededforhideout"] ?? UnityEngine.Resources.Load("characteristics/icons/icon_info_faction"); + } + + GameObject.DestroyImmediate(clone); + + hideoutItemViewTemplate = result; + return hideoutItemViewTemplate; + } + + public static void CopyFieldsFromQuestView(this HideoutItemViewPanel hideoutItem, QuestItemViewPanel questItem) + { + hideoutItem.iconImage = typeof(QuestItemViewPanel).GetField("_questIconImage", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(questItem) as Image; + hideoutItem.tooltipLabel = typeof(QuestItemViewPanel).GetField("_questItemLabel", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(questItem) as TextMeshProUGUI; + hideoutItem.tooltip = typeof(QuestItemViewPanel).GetField("simpleTooltip_0", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(questItem) as SimpleTooltip; + hideoutItem.tooltipString = typeof(QuestItemViewPanel).GetField("string_4", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(questItem) as string; + } + + + public static async Task LoadTexture(string id, string path) + { + using (UnityWebRequest uwr = UnityWebRequestTexture.GetTexture(path)) + { + uwr.SendWebRequest(); + + while (!uwr.isDone) + await Task.Delay(100); + + if (uwr.responseCode != 200) + { + //Logger.Error($"[{HideoutArchitect.ModInfo.name}] Request error {uwr.responseCode}: {uwr.error}"); + } + else + { + // Get downloaded asset bundle + //Logger.Info($"[{HideoutArchitect.ModInfo.name}] Retrieved texture! {id.ToString()} from {path}"); + Texture2D cachedTexture = DownloadHandlerTexture.GetContent(uwr); + iconCache.Add(id, Sprite.Create(cachedTexture, new Rect(0, 0, cachedTexture.width, cachedTexture.height), new Vector2(0, 0))); + } + } + } + } +} diff --git a/Faupi-HideoutMod/Project/src/client/Utils.cs b/Faupi-HideoutMod/Project/src/client/Utils.cs new file mode 100644 index 0000000..278d3f1 --- /dev/null +++ b/Faupi-HideoutMod/Project/src/client/Utils.cs @@ -0,0 +1,113 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using Newtonsoft.Json.Linq; +using System; +using System.Text.RegularExpressions; +using UnityEngine; + +namespace HideoutArchitect +{ + public static class Utils + { + public static string ToSentenceCase(this string text) + { + string result = text; + try + { + // start by converting entire string to lower case + var lowerCase = text.ToLower(); + // matches the first sentence of a string, as well as subsequent sentences + var r = new Regex(@"(^[a-z])|\.\s+(.)", RegexOptions.ExplicitCapture); + // MatchEvaluator delegate defines replacement of setence starts to uppercase + result = r.Replace(lowerCase, s => s.Value.ToUpper()); + } + catch (Exception ex) + { + Debug.LogError($"Error converting string case for '{text}': {ex}"); + } + + return result; + } + + public static bool IsValidHexColor(this string inputColor) + { + //Taken from https://stackoverflow.com/a/13035186 + if (Regex.Match(inputColor, "^#(?:[0-9a-fA-F]{3}){1,2}$").Success) + return true; + + var result = System.Drawing.Color.FromName(inputColor); + return result.IsKnownColor; + } + + public static bool IsValidJson(this string strInput) + { + if (string.IsNullOrWhiteSpace(strInput)) { return false; } + strInput = strInput.Trim(); + if ((strInput.StartsWith("{") && strInput.EndsWith("}")) || //For object + (strInput.StartsWith("[") && strInput.EndsWith("]"))) //For array + { + try + { + var obj = JToken.Parse(strInput); + return true; + } + catch (JsonReaderException jex) + { + //Exception in parsing json + Debug.LogError(jex.Message); + return false; + } + catch (Exception ex) //some other exception + { + Debug.LogError(ex.ToString()); + return false; + } + } + else + { + return false; + } + } + + public class StringEnumCommentConverter : StringEnumConverter + { + //modification of https://stackoverflow.com/a/65001212 + private readonly string _comment; + public StringEnumCommentConverter(string comment) + { + _comment = comment; + } + + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + base.WriteJson(writer, value, serializer); + writer.WriteComment(_comment); // append comment + } + } + + public class JsonCommentConverter : JsonConverter + { + //modification of https://stackoverflow.com/a/65001212 + private readonly string _comment; + public JsonCommentConverter(string comment) + { + _comment = comment; + } + + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, + JsonSerializer serializer) + { + throw new NotImplementedException(); + } + + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + writer.WriteValue(value); + writer.WriteComment(_comment); // append comment + } + + public override bool CanConvert(Type objectType) => true; + public override bool CanRead => false; + } + } +} diff --git a/Faupi-HideoutMod/Project/src/server/hideoutarchitect.js b/Faupi-HideoutMod/Project/src/server/hideoutarchitect.js new file mode 100644 index 0000000..c95bd72 --- /dev/null +++ b/Faupi-HideoutMod/Project/src/server/hideoutarchitect.js @@ -0,0 +1,68 @@ +/* hideoutarchitect.js + * license: NCSA + * copyright: Faupi + * authors: + * - Faupi + */ + +"use strict"; +const path = require('path'); + +class HideoutArchitect { + constructor() { + this.mod = require("../package.json"); + this.translations = require("../res/translations.json"); + Logger.info(`Loading: ${this.mod.name} ${this.mod.version}`); + + ModLoader.onLoad[this.mod.name] = this.init.bind(this); + } + + init(){ + this.hookRoutes(); + this.loadLocalization(); + } + + loadLocalization(){ + var globalLocale = DatabaseServer.tables.locales.global; + + for(let language in this.translations){ + if(!language in globalLocale) continue; + + let attrKvPair = this.translations[language]; + for(let attrKey in attrKvPair){ + let attrValue = attrKvPair[attrKey]; + + globalLocale[language].interface[attrKey] = attrValue; + } + } + } + + log(data){ + // console.log(data); + } + + hookRoutes(){ + HttpRouter.onStaticRoute["/HideoutArchitect/GetInfo"] = { + AttachmentOffset: this.getModInfo.bind(this) + }; + } + + getModInfo(url, info, sessionID, output){ + var output = { + status: 1, + data: null + }; + + // Don't mind this pointless try catch + try{ + output.data = {...this.mod, ...{path: path.resolve(ModLoader.getModPath(this.mod.name))}}; + output.status = 0; + }catch(ex){ + throw ex; + } + + return JsonUtil.serialize(output); + } +} + +module.exports = new HideoutArchitect(); \ No newline at end of file diff --git a/Faupi-HideoutMod/README.md b/Faupi-HideoutMod/README.md new file mode 100644 index 0000000..61bf7c0 --- /dev/null +++ b/Faupi-HideoutMod/README.md @@ -0,0 +1,4 @@ +# HideoutArchitect +## Faupi-HideoutArchitect for EFT - SPT-AKI +### CURRENT AKI VERSION: 2.2.1 +### GAMEVERSION: 0.12.12.15.16584 diff --git a/Faupi-MunitionsExpert/MunitionsExpert/LICENSE b/Faupi-MunitionsExpert/MunitionsExpert/LICENSE new file mode 100644 index 0000000..1dd0454 --- /dev/null +++ b/Faupi-MunitionsExpert/MunitionsExpert/LICENSE @@ -0,0 +1,32 @@ +University of Illinois/NCSA Open Source License Copyright (c) 2021 Faupi. All rights reserved. + +Developed by: + +Faupi + +https://github.com/Faupi-SPTarkov/MunitionsExpert + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +with the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +* Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimers. + +* Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimers in the documentation +and/or other materials provided with the distribution. + +* Neither the names of Faupi, nor the names of its contributors may be used +to endorse or promote products derived from this Software without specific prior +written permission. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE CONTRIBUTORS +OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF +OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE. diff --git a/Faupi-MunitionsExpert/MunitionsExpert/MunitionsExpert.csproj b/Faupi-MunitionsExpert/MunitionsExpert/MunitionsExpert.csproj new file mode 100644 index 0000000..fffda2b --- /dev/null +++ b/Faupi-MunitionsExpert/MunitionsExpert/MunitionsExpert.csproj @@ -0,0 +1,113 @@ + + + + + Debug + AnyCPU + {C5889536-4EE7-40BC-A8FD-7F1A80C3F3E5} + Library + Properties + MunitionsExpert + Faupi-MunitionsExpert + v4.7.2 + 512 + true + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE;/noentry + prompt + 4 + + + true + + + + + + + + + + + + + + + + + + + + + + + + + + ..\..\..\..\..\..\..\AKI PROJECT\AKI 2.3.0 17107 Bepin\BepInEx\core\0Harmony.dll + + + ..\..\..\..\..\..\..\AKI PROJECT\AKI 2.3.0 17107 Bepin\EscapeFromTarkov_Data\Managed\Aki.Common.dll + + + ..\..\..\..\..\..\..\AKI PROJECT\AKI 2.3.0 17107 Bepin\EscapeFromTarkov_Data\Managed\Aki.Reflection.dll + + + ..\..\..\..\..\..\..\AKI PROJECT\AKI 2.3.0 17107 Bepin\EscapeFromTarkov_Data\Managed\Assembly-CSharp.dll + + + ..\..\..\..\..\..\..\AKI PROJECT\AKI 2.3.0 17107 Bepin\BepInEx\core\BepInEx.dll + + + ..\..\..\..\..\..\..\AKI PROJECT\AKI 2.3.0 17107 Bepin\EscapeFromTarkov_Data\Managed\Comfort.dll + + + + False + ..\References\EFT-Managed\Newtonsoft.Json.dll + + + + ..\..\..\..\..\..\..\AKI PROJECT\AKI 2.3.0 17107 Bepin\EscapeFromTarkov_Data\Managed\UnityEngine.dll + + + ..\..\..\..\..\..\..\AKI PROJECT\AKI 2.3.0 17107 Bepin\EscapeFromTarkov_Data\Managed\UnityEngine.CoreModule.dll + + + ..\..\..\..\..\..\..\AKI PROJECT\AKI 2.3.0 17107 Bepin\EscapeFromTarkov_Data\Managed\UnityEngine.UnityWebRequestModule.dll + + + ..\..\..\..\..\..\..\AKI PROJECT\AKI 2.3.0 17107 Bepin\EscapeFromTarkov_Data\Managed\UnityEngine.UnityWebRequestTextureModule.dll + + + ..\..\..\..\..\..\..\AKI PROJECT\AKI 2.3.0 17107 Bepin\EscapeFromTarkov_Data\Managed\UnityEngine.UnityWebRequestWWWModule.dll + + + + + + + + set BuildDir=$(ProjectDir)_Build\Faupi-MunitionsExpert + +powershell -Command "Remove-Item '%25BuildDir%25\*' -Recurse -Force" + +copy "$(TargetPath)" "%25BuildDir%25\module.dll" +xcopy "$(ProjectDir)src\server" "%25BuildDir%25\src" /K /D /H /Y /I +xcopy "$(ProjectDir)res" "%25BuildDir%25\res" /K /D /H /Y /I +copy "$(ProjectDir)package.json" "%25BuildDir%25\package.json" + + \ No newline at end of file diff --git a/Faupi-MunitionsExpert/MunitionsExpert/MunitionsExpert.sln b/Faupi-MunitionsExpert/MunitionsExpert/MunitionsExpert.sln new file mode 100644 index 0000000..12be45c --- /dev/null +++ b/Faupi-MunitionsExpert/MunitionsExpert/MunitionsExpert.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.32014.148 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MunitionsExpert", "MunitionsExpert.csproj", "{C5889536-4EE7-40BC-A8FD-7F1A80C3F3E5}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {C5889536-4EE7-40BC-A8FD-7F1A80C3F3E5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C5889536-4EE7-40BC-A8FD-7F1A80C3F3E5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C5889536-4EE7-40BC-A8FD-7F1A80C3F3E5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C5889536-4EE7-40BC-A8FD-7F1A80C3F3E5}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {C4FBDDAB-36FB-44F2-8D1D-4E7AF0E20BF1} + EndGlobalSection +EndGlobal diff --git a/Faupi-MunitionsExpert/MunitionsExpert/Properties/AssemblyInfo.cs b/Faupi-MunitionsExpert/MunitionsExpert/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..fa4904b --- /dev/null +++ b/Faupi-MunitionsExpert/MunitionsExpert/Properties/AssemblyInfo.cs @@ -0,0 +1,38 @@ +using System.Resources; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("MunitionsExpert - Ammo stats in inspect view")] +[assembly: AssemblyDescription("Adds ammo stats to inspect view as well as improving the format a little")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("SPT-AKI - MunitionsExpert")] +[assembly: AssemblyCopyright("Faupi © 2021")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("c5889536-4ee7-40bc-a8fd-7f1a80c3f3e5")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.2.1.0")] +[assembly: AssemblyFileVersion("1.2.1.0")] +[assembly: NeutralResourcesLanguage("en-US")] diff --git a/Faupi-MunitionsExpert/MunitionsExpert/_Build/Faupi-MunitionsExpert 1.4.1/bepInEx/plugins/Faupi-MunitionsExpert.dll b/Faupi-MunitionsExpert/MunitionsExpert/_Build/Faupi-MunitionsExpert 1.4.1/bepInEx/plugins/Faupi-MunitionsExpert.dll new file mode 100644 index 0000000..711a299 Binary files /dev/null and b/Faupi-MunitionsExpert/MunitionsExpert/_Build/Faupi-MunitionsExpert 1.4.1/bepInEx/plugins/Faupi-MunitionsExpert.dll differ diff --git a/Faupi-MunitionsExpert/MunitionsExpert/_Build/Faupi-MunitionsExpert 1.4.1/user/mods/Faupi-MunitionsExpert 1.4.1/LICENSE b/Faupi-MunitionsExpert/MunitionsExpert/_Build/Faupi-MunitionsExpert 1.4.1/user/mods/Faupi-MunitionsExpert 1.4.1/LICENSE new file mode 100644 index 0000000..1dd0454 --- /dev/null +++ b/Faupi-MunitionsExpert/MunitionsExpert/_Build/Faupi-MunitionsExpert 1.4.1/user/mods/Faupi-MunitionsExpert 1.4.1/LICENSE @@ -0,0 +1,32 @@ +University of Illinois/NCSA Open Source License Copyright (c) 2021 Faupi. All rights reserved. + +Developed by: + +Faupi + +https://github.com/Faupi-SPTarkov/MunitionsExpert + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +with the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +* Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimers. + +* Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimers in the documentation +and/or other materials provided with the distribution. + +* Neither the names of Faupi, nor the names of its contributors may be used +to endorse or promote products derived from this Software without specific prior +written permission. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE CONTRIBUTORS +OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF +OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE. diff --git a/Faupi-MunitionsExpert/MunitionsExpert/_Build/Faupi-MunitionsExpert 1.4.1/user/mods/Faupi-MunitionsExpert 1.4.1/module.dll b/Faupi-MunitionsExpert/MunitionsExpert/_Build/Faupi-MunitionsExpert 1.4.1/user/mods/Faupi-MunitionsExpert 1.4.1/module.dll new file mode 100644 index 0000000..c69526d Binary files /dev/null and b/Faupi-MunitionsExpert/MunitionsExpert/_Build/Faupi-MunitionsExpert 1.4.1/user/mods/Faupi-MunitionsExpert 1.4.1/module.dll differ diff --git a/Faupi-MunitionsExpert/MunitionsExpert/_Build/Faupi-MunitionsExpert 1.4.1/user/mods/Faupi-MunitionsExpert 1.4.1/package.json b/Faupi-MunitionsExpert/MunitionsExpert/_Build/Faupi-MunitionsExpert 1.4.1/user/mods/Faupi-MunitionsExpert 1.4.1/package.json new file mode 100644 index 0000000..824c400 --- /dev/null +++ b/Faupi-MunitionsExpert/MunitionsExpert/_Build/Faupi-MunitionsExpert 1.4.1/user/mods/Faupi-MunitionsExpert 1.4.1/package.json @@ -0,0 +1,9 @@ +{ + "name": "Faupi-MunitionsExpert", + "author": "Faupi", + "version": "1.4.1", + "license": "NCSA Open Source", + "main": "./src/ammostats.js", + "akiVersion": "2.3.1", + "updatedBy": "CWX" +} \ No newline at end of file diff --git a/Faupi-MunitionsExpert/MunitionsExpert/_Build/Faupi-MunitionsExpert 1.4.1/user/mods/Faupi-MunitionsExpert 1.4.1/res/armorDamage.png b/Faupi-MunitionsExpert/MunitionsExpert/_Build/Faupi-MunitionsExpert 1.4.1/user/mods/Faupi-MunitionsExpert 1.4.1/res/armorDamage.png new file mode 100644 index 0000000..70d5e40 Binary files /dev/null and b/Faupi-MunitionsExpert/MunitionsExpert/_Build/Faupi-MunitionsExpert 1.4.1/user/mods/Faupi-MunitionsExpert 1.4.1/res/armorDamage.png differ diff --git a/Faupi-MunitionsExpert/MunitionsExpert/_Build/Faupi-MunitionsExpert 1.4.1/user/mods/Faupi-MunitionsExpert 1.4.1/res/ricochet.png b/Faupi-MunitionsExpert/MunitionsExpert/_Build/Faupi-MunitionsExpert 1.4.1/user/mods/Faupi-MunitionsExpert 1.4.1/res/ricochet.png new file mode 100644 index 0000000..27f371e Binary files /dev/null and b/Faupi-MunitionsExpert/MunitionsExpert/_Build/Faupi-MunitionsExpert 1.4.1/user/mods/Faupi-MunitionsExpert 1.4.1/res/ricochet.png differ diff --git a/Faupi-MunitionsExpert/MunitionsExpert/_Build/Faupi-MunitionsExpert 1.4.1/user/mods/Faupi-MunitionsExpert 1.4.1/res/translations.json b/Faupi-MunitionsExpert/MunitionsExpert/_Build/Faupi-MunitionsExpert 1.4.1/user/mods/Faupi-MunitionsExpert 1.4.1/res/translations.json new file mode 100644 index 0000000..530c6ff --- /dev/null +++ b/Faupi-MunitionsExpert/MunitionsExpert/_Build/Faupi-MunitionsExpert 1.4.1/user/mods/Faupi-MunitionsExpert 1.4.1/res/translations.json @@ -0,0 +1,137 @@ +{ + "en": { + "DAMAGE": "Damage", + "PENETRATION": "Armor penetration", + "ARMOR DAMAGE": "Damage to armor", + "FRAGMENTATION CHANCE": "Fragmentation chance", + "RICOCHET CHANCE": "Ricochet chance", + "ME_class": "Class", + "ME_noarmor": "Unarmored" + }, + "cz": { + "DAMAGE": "Poškození", + "PENETRATION": "Průbojnost", + "ARMOR DAMAGE": "Poškození brnění", + "FRAGMENTATION CHANCE": "Šance na fragmentaci", + "RICOCHET CHANCE": "Šance na odraz", + "ME_class": "Třída", + "ME_noarmor": "Neobrněný" + }, + "pl": { + "DAMAGE": "Szkoda", + "PENETRATION": "Penetracja pancerza", + "ARMOR DAMAGE": "Uszkodzenie zbroi", + "FRAGMENTATION CHANCE": "Szansa na fragmentację", + "RICOCHET CHANCE": "Szansa na rykoszet", + "ME_class": "Klasa", + "ME_noarmor": "Nieumiejętny" + }, + "po": { + "DAMAGE": "Dano", + "PENETRATION": "Penetração de armadura", + "ARMOR DAMAGE": "Danos à armadura", + "FRAGMENTATION CHANCE": "Chance de fragmentação", + "RICOCHET CHANCE": "Chance de ricochete", + "ME_class": "Classe", + "ME_noarmor": "Sem armadura" + }, + "ch": { + "DAMAGE": "损坏", + "PENETRATION": "护甲穿透", + "ARMOR DAMAGE": "对盔甲的伤害", + "FRAGMENTATION CHANCE": "碎片机会", + "RICOCHET CHANCE": "跳弹机会", + "ME_class": "类", + "ME_noarmor": "无所作为" + }, + "ru": { + "DAMAGE": "Повреждать", + "PENETRATION": "Бронепробиваемость", + "ARMOR DAMAGE": "Повреждение брони", + "FRAGMENTATION CHANCE": "Вероятность фрагментации", + "RICOCHET CHANCE": "Шанс рикошета", + "ME_class": "Класс", + "ME_noarmor": "Без оружия" + }, + "es": { + "DAMAGE": "Daño", + "PENETRATION": "Penetración de armadura", + "ARMOR DAMAGE": "Daño a la armadura", + "FRAGMENTATION CHANCE": "Posibilidad de fragmentación", + "RICOCHET CHANCE": "Oportunidad de rebote", + "ME_class": "Clase", + "ME_noarmor": "Sin armadura" + }, + "es-mx": { + "DAMAGE": "Daño", + "PENETRATION": "Penetración de armadura", + "ARMOR DAMAGE": "Daño a la armadura", + "FRAGMENTATION CHANCE": "Posibilidad de fragmentación", + "RICOCHET CHANCE": "Oportunidad de rebote", + "ME_class": "Clase", + "ME_noarmor": "Sin armadura" + }, + "ge": { + "DAMAGE": "Schaden", + "PENETRATION": "Rüstungsdurchdringung", + "ARMOR DAMAGE": "Beschädigung der Rüstung", + "FRAGMENTATION CHANCE": "Fragmentierung Chance", + "RICOCHET CHANCE": "Querschläger-Chance", + "ME_class": "Klasse", + "ME_noarmor": "Ungepanzert" + }, + "sk": { + "DAMAGE": "Poškodenie", + "PENETRATION": "Prienik do brnenia", + "ARMOR DAMAGE": "Poškodenie brnenia", + "FRAGMENTATION CHANCE": "Šanca na fragmentáciu", + "RICOCHET CHANCE": "Šanca na odraz", + "ME_class": "Trieda", + "ME_noarmor": "Neozbrojený" + }, + "tu": { + "DAMAGE": "Hasar", + "PENETRATION": "Zırh penetrasyon", + "ARMOR DAMAGE": "Zırhta hasar", + "FRAGMENTATION CHANCE": "Parçalanma şansı", + "RICOCHET CHANCE": "Sekme şansı", + "ME_class": "Sınıf", + "ME_noarmor": "zırhsız" + }, + "it": { + "DAMAGE": "Danno", + "PENETRATION": "Penetrazione dell'armatura", + "ARMOR DAMAGE": "Danni all'armatura", + "FRAGMENTATION CHANCE": "Possibilità di frammentazione", + "RICOCHET CHANCE": "Possibilità di rimbalzo", + "ME_class": "Classe", + "ME_noarmor": "Disarmato" + }, + "jp": { + "DAMAGE": "ダメージ", + "PENETRATION": "装甲貫通", + "ARMOR DAMAGE": "鎧の損傷", + "FRAGMENTATION CHANCE": "断片化の可能性", + "RICOCHET CHANCE": "跳ね返るチャンス", + "ME_class": "クラス", + "ME_noarmor": "無装甲" + }, + "fr": { + "DAMAGE": "Dommage", + "PENETRATION": "Pénétration d'armure", + "ARMOR DAMAGE": "Dommages à l'armure", + "FRAGMENTATION CHANCE": "Chance de fragmentation", + "RICOCHET CHANCE": "Chance de ricochet", + "ME_class": "Classe", + "ME_noarmor": "Sans armure" + }, + "hu": { + "DAMAGE": "Kár", + "PENETRATION": "Páncélátütő", + "ARMOR DAMAGE": "A páncél sérülése", + "FRAGMENTATION CHANCE": "Töredezettség esélye", + "RICOCHET CHANCE": "Ricochet esély", + "ME_class": "Osztály", + "ME_noarmor": "Fegyvertelen" + } +} \ No newline at end of file diff --git a/Faupi-MunitionsExpert/MunitionsExpert/_Build/Faupi-MunitionsExpert 1.4.1/user/mods/Faupi-MunitionsExpert 1.4.1/src/ammostats.js b/Faupi-MunitionsExpert/MunitionsExpert/_Build/Faupi-MunitionsExpert 1.4.1/user/mods/Faupi-MunitionsExpert 1.4.1/src/ammostats.js new file mode 100644 index 0000000..7d2c973 --- /dev/null +++ b/Faupi-MunitionsExpert/MunitionsExpert/_Build/Faupi-MunitionsExpert 1.4.1/user/mods/Faupi-MunitionsExpert 1.4.1/src/ammostats.js @@ -0,0 +1,91 @@ +/* ammostats.js + * license: NCSA + * copyright: Faupi + * authors: + * - Faupi + */ + +"use strict"; +const path = require('path'); +const cfg = require("./config.json"); +const DATABASE = DatabaseServer.tables.templates.items; + +class AmmoStats { + constructor() { + this.mod = require("../package.json"); + this.translations = require("../res/translations.json"); + Logger.info(`Loading: ${this.mod.name} ${this.mod.version}`); + + ModLoader.onLoad[this.mod.name] = this.init.bind(this); + } + + init(){ + this.hookRoutes(); + this.updateLocalization(); + this.changeBulletColour(); + } + + updateLocalization(){ + var globalLocale = DatabaseServer.tables.locales.global; + + for(let language in this.translations){ + if(!language in globalLocale) continue; + + let attrKvPair = this.translations[language]; + for(let attrKey in attrKvPair){ + let attrValue = attrKvPair[attrKey]; + + globalLocale[language].interface[attrKey] = attrValue; + } + } + } + + hookRoutes(){ + HttpRouter.onStaticRoute["/MunitionsExpert/GetInfo"] = { + MunitionsExpert: this.getModInfo.bind(this) + }; + } + + getModInfo(url, info, sessionID, output){ + var output = { + status: 1, + data: null + }; + + // Don't mind this pointless try catch + try{ + output.data = {...this.mod, ...{path: path.resolve(ModLoader.getModPath(this.mod.name))}}; + output.status = 0; + }catch(ex){ + throw ex; + } + + return JsonUtil.serialize(output); + } + + changeBulletColour() + { + if(cfg.BulletBackgroundColours === true) + { + for (const i in DATABASE) { + let item = DATABASE[i] + + //set baground colour of ammo depending on pen + if (item._parent === "5485a8684bdc2da71d8b4567") { + let pen = item._props.PenetrationPower + let colour = "" + + pen > 60 ? colour = "red" : //SuperPen + pen > 50 ? colour = "yellow" : //HighPen + pen > 40 ? colour = "violet" : //MedHighPen + pen > 30 ? colour = "blue" : //MedPen + pen > 20 ? colour = "green" : //LowMedPen + colour = "grey" //LowPen + item._props.BackgroundColor = colour + } + } + } + } +} + +module.exports = new AmmoStats(); \ No newline at end of file diff --git a/Faupi-MunitionsExpert/MunitionsExpert/_Build/Faupi-MunitionsExpert 1.4.1/user/mods/Faupi-MunitionsExpert 1.4.1/src/config.json b/Faupi-MunitionsExpert/MunitionsExpert/_Build/Faupi-MunitionsExpert 1.4.1/user/mods/Faupi-MunitionsExpert 1.4.1/src/config.json new file mode 100644 index 0000000..b735cd6 --- /dev/null +++ b/Faupi-MunitionsExpert/MunitionsExpert/_Build/Faupi-MunitionsExpert 1.4.1/user/mods/Faupi-MunitionsExpert 1.4.1/src/config.json @@ -0,0 +1,3 @@ +{ + "BulletBackgroundColours": true +} \ No newline at end of file diff --git a/Faupi-MunitionsExpert/MunitionsExpert/res/armorDamage.png b/Faupi-MunitionsExpert/MunitionsExpert/res/armorDamage.png new file mode 100644 index 0000000..70d5e40 Binary files /dev/null and b/Faupi-MunitionsExpert/MunitionsExpert/res/armorDamage.png differ diff --git a/Faupi-MunitionsExpert/MunitionsExpert/res/ricochet.png b/Faupi-MunitionsExpert/MunitionsExpert/res/ricochet.png new file mode 100644 index 0000000..27f371e Binary files /dev/null and b/Faupi-MunitionsExpert/MunitionsExpert/res/ricochet.png differ diff --git a/Faupi-MunitionsExpert/MunitionsExpert/res/translations.json b/Faupi-MunitionsExpert/MunitionsExpert/res/translations.json new file mode 100644 index 0000000..530c6ff --- /dev/null +++ b/Faupi-MunitionsExpert/MunitionsExpert/res/translations.json @@ -0,0 +1,137 @@ +{ + "en": { + "DAMAGE": "Damage", + "PENETRATION": "Armor penetration", + "ARMOR DAMAGE": "Damage to armor", + "FRAGMENTATION CHANCE": "Fragmentation chance", + "RICOCHET CHANCE": "Ricochet chance", + "ME_class": "Class", + "ME_noarmor": "Unarmored" + }, + "cz": { + "DAMAGE": "Poškození", + "PENETRATION": "Průbojnost", + "ARMOR DAMAGE": "Poškození brnění", + "FRAGMENTATION CHANCE": "Šance na fragmentaci", + "RICOCHET CHANCE": "Šance na odraz", + "ME_class": "Třída", + "ME_noarmor": "Neobrněný" + }, + "pl": { + "DAMAGE": "Szkoda", + "PENETRATION": "Penetracja pancerza", + "ARMOR DAMAGE": "Uszkodzenie zbroi", + "FRAGMENTATION CHANCE": "Szansa na fragmentację", + "RICOCHET CHANCE": "Szansa na rykoszet", + "ME_class": "Klasa", + "ME_noarmor": "Nieumiejętny" + }, + "po": { + "DAMAGE": "Dano", + "PENETRATION": "Penetração de armadura", + "ARMOR DAMAGE": "Danos à armadura", + "FRAGMENTATION CHANCE": "Chance de fragmentação", + "RICOCHET CHANCE": "Chance de ricochete", + "ME_class": "Classe", + "ME_noarmor": "Sem armadura" + }, + "ch": { + "DAMAGE": "损坏", + "PENETRATION": "护甲穿透", + "ARMOR DAMAGE": "对盔甲的伤害", + "FRAGMENTATION CHANCE": "碎片机会", + "RICOCHET CHANCE": "跳弹机会", + "ME_class": "类", + "ME_noarmor": "无所作为" + }, + "ru": { + "DAMAGE": "Повреждать", + "PENETRATION": "Бронепробиваемость", + "ARMOR DAMAGE": "Повреждение брони", + "FRAGMENTATION CHANCE": "Вероятность фрагментации", + "RICOCHET CHANCE": "Шанс рикошета", + "ME_class": "Класс", + "ME_noarmor": "Без оружия" + }, + "es": { + "DAMAGE": "Daño", + "PENETRATION": "Penetración de armadura", + "ARMOR DAMAGE": "Daño a la armadura", + "FRAGMENTATION CHANCE": "Posibilidad de fragmentación", + "RICOCHET CHANCE": "Oportunidad de rebote", + "ME_class": "Clase", + "ME_noarmor": "Sin armadura" + }, + "es-mx": { + "DAMAGE": "Daño", + "PENETRATION": "Penetración de armadura", + "ARMOR DAMAGE": "Daño a la armadura", + "FRAGMENTATION CHANCE": "Posibilidad de fragmentación", + "RICOCHET CHANCE": "Oportunidad de rebote", + "ME_class": "Clase", + "ME_noarmor": "Sin armadura" + }, + "ge": { + "DAMAGE": "Schaden", + "PENETRATION": "Rüstungsdurchdringung", + "ARMOR DAMAGE": "Beschädigung der Rüstung", + "FRAGMENTATION CHANCE": "Fragmentierung Chance", + "RICOCHET CHANCE": "Querschläger-Chance", + "ME_class": "Klasse", + "ME_noarmor": "Ungepanzert" + }, + "sk": { + "DAMAGE": "Poškodenie", + "PENETRATION": "Prienik do brnenia", + "ARMOR DAMAGE": "Poškodenie brnenia", + "FRAGMENTATION CHANCE": "Šanca na fragmentáciu", + "RICOCHET CHANCE": "Šanca na odraz", + "ME_class": "Trieda", + "ME_noarmor": "Neozbrojený" + }, + "tu": { + "DAMAGE": "Hasar", + "PENETRATION": "Zırh penetrasyon", + "ARMOR DAMAGE": "Zırhta hasar", + "FRAGMENTATION CHANCE": "Parçalanma şansı", + "RICOCHET CHANCE": "Sekme şansı", + "ME_class": "Sınıf", + "ME_noarmor": "zırhsız" + }, + "it": { + "DAMAGE": "Danno", + "PENETRATION": "Penetrazione dell'armatura", + "ARMOR DAMAGE": "Danni all'armatura", + "FRAGMENTATION CHANCE": "Possibilità di frammentazione", + "RICOCHET CHANCE": "Possibilità di rimbalzo", + "ME_class": "Classe", + "ME_noarmor": "Disarmato" + }, + "jp": { + "DAMAGE": "ダメージ", + "PENETRATION": "装甲貫通", + "ARMOR DAMAGE": "鎧の損傷", + "FRAGMENTATION CHANCE": "断片化の可能性", + "RICOCHET CHANCE": "跳ね返るチャンス", + "ME_class": "クラス", + "ME_noarmor": "無装甲" + }, + "fr": { + "DAMAGE": "Dommage", + "PENETRATION": "Pénétration d'armure", + "ARMOR DAMAGE": "Dommages à l'armure", + "FRAGMENTATION CHANCE": "Chance de fragmentation", + "RICOCHET CHANCE": "Chance de ricochet", + "ME_class": "Classe", + "ME_noarmor": "Sans armure" + }, + "hu": { + "DAMAGE": "Kár", + "PENETRATION": "Páncélátütő", + "ARMOR DAMAGE": "A páncél sérülése", + "FRAGMENTATION CHANCE": "Töredezettség esélye", + "RICOCHET CHANCE": "Ricochet esély", + "ME_class": "Osztály", + "ME_noarmor": "Fegyvertelen" + } +} \ No newline at end of file diff --git a/Faupi-MunitionsExpert/MunitionsExpert/src/client/Attributes.cs b/Faupi-MunitionsExpert/MunitionsExpert/src/client/Attributes.cs new file mode 100644 index 0000000..4368021 --- /dev/null +++ b/Faupi-MunitionsExpert/MunitionsExpert/src/client/Attributes.cs @@ -0,0 +1,33 @@ +namespace MunitionsExpert +{ + public static class Attributes + { + public enum ENewItemAttributeId + { + Damage, + ArmorDamage, + Penetration, + FragmentationChance, + RicochetChance + } + + public static string GetName(this ENewItemAttributeId id) + { + switch (id) + { + case ENewItemAttributeId.Damage: + return "DAMAGE"; + case ENewItemAttributeId.ArmorDamage: + return "ARMOR DAMAGE"; + case ENewItemAttributeId.Penetration: + return "PENETRATION"; + case ENewItemAttributeId.FragmentationChance: + return "FRAGMENTATION CHANCE"; + case ENewItemAttributeId.RicochetChance: + return "RICOCHET CHANCE"; + default: + return id.ToString(); + } + } + } +} diff --git a/Faupi-MunitionsExpert/MunitionsExpert/src/client/CachedAttributesPatch.cs b/Faupi-MunitionsExpert/MunitionsExpert/src/client/CachedAttributesPatch.cs new file mode 100644 index 0000000..665ceea --- /dev/null +++ b/Faupi-MunitionsExpert/MunitionsExpert/src/client/CachedAttributesPatch.cs @@ -0,0 +1,27 @@ +using Aki.Reflection.Patching; +using EFT.InventoryLogic; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using ItemAttributes = GClass2090; + +namespace MunitionsExpert +{ + internal class CachedAttributesPatch : ModulePatch + { + protected override MethodBase GetTargetMethod() + { + return typeof(AmmoTemplate).GetMethod("GetCachedReadonlyQualities", BindingFlags.Instance | BindingFlags.Public); + } + + [PatchPostfix] + private static void PatchPostfix(ref AmmoTemplate __instance, ref List __result) + { + if (!__result.Any((ItemAttributes a) => (Attributes.ENewItemAttributeId)a.Id == Attributes.ENewItemAttributeId.Damage)) + { + //MunitionsExpert.FormatExistingAttributes(ref __result, __instance); + MunitionsExpert.AddNewAttributes(ref __result, __instance); + } + } + } +} \ No newline at end of file diff --git a/Faupi-MunitionsExpert/MunitionsExpert/src/client/ModInformation.cs b/Faupi-MunitionsExpert/MunitionsExpert/src/client/ModInformation.cs new file mode 100644 index 0000000..c390b67 --- /dev/null +++ b/Faupi-MunitionsExpert/MunitionsExpert/src/client/ModInformation.cs @@ -0,0 +1,38 @@ +using Aki.Common.Http; +using Newtonsoft.Json.Linq; +using System; +using UnityEngine; +using UnityEngine.Assertions; + +namespace MunitionsExpert +{ + public class ModInformation + { + public string name; + public string author; + public string version; + public string license; + public string main; + public string path; + + public static ModInformation Load() + { + ModInformation ModInfo; + + JObject response = JObject.Parse(RequestHandler.GetJson($"/MunitionsExpert/GetInfo")); + try + { + Assert.IsTrue(response.Value("status") == 0); + ModInfo = response["data"].ToObject(); + } + catch (Exception getModInfoException) + { + string errMsg = $"[{typeof(MunitionsExpert)}] Package.json couldn't be found! Make sure you've installed the mod on the server as well!"; + Debug.LogError(errMsg); + throw getModInfoException; + } + + return ModInfo; + } + } +} diff --git a/Faupi-MunitionsExpert/MunitionsExpert/src/client/MunitionsExpert.cs b/Faupi-MunitionsExpert/MunitionsExpert/src/client/MunitionsExpert.cs new file mode 100644 index 0000000..71407d3 --- /dev/null +++ b/Faupi-MunitionsExpert/MunitionsExpert/src/client/MunitionsExpert.cs @@ -0,0 +1,166 @@ +using System; +using System.IO; +using System.Collections.Generic; +using UnityEngine; +using EFT.InventoryLogic; +using UnityEngine.Networking; +using System.Threading.Tasks; +using BepInEx; +using Comfort.Common; + +using ItemAttribute = GClass2090; +using ItemAttributeCharacteristic = BulletClass; // left in as commented code uses this class +using static MunitionsExpert.Attributes; +using ServerSettings = GClass1103; + + +namespace MunitionsExpert +{ + [BepInPlugin("com.FAUPI.MunitionsExpert", "FAUPI-MunitionsExpert", "1.4.0")] + public class MunitionsExpert : BaseUnityPlugin + { + private void Awake() + { + new CachedAttributesPatch().Enable(); + new StaticIconsPatch().Enable(); + CacheIcons(); + } + + private static ModInformation _modInfo; + public static ModInformation ModInfo + { + private set + { + _modInfo = value; + } + get + { + if (_modInfo == null) + _modInfo = ModInformation.Load(); + return _modInfo; + } + } + + public static Dictionary iconCache = new Dictionary(); + public static List penAttributes = new List(); // For refreshing armor class rating + public static string modName = ModInfo.name; + + public static void CacheIcons() + { + iconCache.Add(ENewItemAttributeId.Damage, Resources.Load("characteristics/icons/icon_info_damage")); + iconCache.Add(ENewItemAttributeId.FragmentationChance, Resources.Load("characteristics/icons/icon_info_shrapnelcount")); + iconCache.Add(EItemAttributeId.LightBleedingDelta, Resources.Load("characteristics/icons/icon_info_bloodloss")); + iconCache.Add(EItemAttributeId.HeavyBleedingDelta, Resources.Load("characteristics/icon_info_hydration")); + iconCache.Add(ENewItemAttributeId.Penetration, Resources.Load("characteristics/icon_info_penetration")); + _ = LoadTexture(ENewItemAttributeId.ArmorDamage, Path.Combine(ModInfo.path, "res/armorDamage.png")); + _ = LoadTexture(ENewItemAttributeId.RicochetChance, Path.Combine(ModInfo.path, "res/ricochet.png")); + } + + public static async Task LoadTexture(Enum id, string path) + { + using (UnityWebRequest uwr = UnityWebRequestTexture.GetTexture(path)) + { + uwr.SendWebRequest(); + + while (!uwr.isDone) + await Task.Delay(5); + + if (uwr.responseCode != 200) + { + //Log.Error($"[{modName}] Request error {uwr.responseCode}: {uwr.error}"); + } + else + { + // Get downloaded asset bundle + //Log.Info($"[{modName}] Retrieved texture! {id.ToString()} from {path}"); + Texture2D cachedTexture = DownloadHandlerTexture.GetContent(uwr); + iconCache.Add(id, Sprite.Create(cachedTexture, new Rect(0, 0, cachedTexture.width, cachedTexture.height), new Vector2(0, 0))); + } + } + } + + public static void AddNewAttributes(ref List attributes, AmmoTemplate template) + { + int projCount = template.ProjectileCount; + int totalDamage = template.Damage * template.ProjectileCount; + + string damageStr = totalDamage.ToString(); // Total damage + if (template.ProjectileCount > 1) + { + damageStr += $" ({template.Damage} x {template.ProjectileCount})"; // Add the "damage calculation" after total damage (damage per pellet * pellet count) + } + + ItemAttribute at_damage = new ItemAttribute(ENewItemAttributeId.Damage) + { + Name = ENewItemAttributeId.Damage.GetName(), + Base = () => totalDamage, + StringValue = () => damageStr, + DisplayType = () => EItemAttributeDisplayType.Compact + }; + attributes.Add(at_damage); + + if (template.ArmorDamage > 0) + { + ItemAttribute at_armordmg = new ItemAttribute(ENewItemAttributeId.ArmorDamage) + { + Name = ENewItemAttributeId.ArmorDamage.GetName(), + Base = () => template.ArmorDamage, + StringValue = () => $"{(template.ArmorDamage).ToString()}%", + DisplayType = () => EItemAttributeDisplayType.Compact + }; + attributes.Add(at_armordmg); + } + + if (template.PenetrationPower > 0) + { + string getStringValue() + { + int ratedClass = 0; + + if (!Singleton.Instantiated) { return $"CLASS_DATA_MISSING {template.PenetrationPower.ToString()}"; } + ServerSettings.GClass1148.GClass1149[] classes = Singleton.Instance.Armor.ArmorClass; + for (int i = 0; i < classes.Length; i++) + { + if (classes[i].Resistance > template.PenetrationPower) continue; + ratedClass = Math.Max(ratedClass, i); + } + + return $"{(ratedClass > 0 ? $"{"ME_class".Localized()} {ratedClass}" : "ME_noarmor".Localized())} ({template.PenetrationPower.ToString()})"; + } + + ItemAttribute at_pen = new ItemAttribute(ENewItemAttributeId.Penetration) + { + Name = ENewItemAttributeId.Penetration.GetName(), + Base = () => template.PenetrationPower, + StringValue = getStringValue, + DisplayType = () => EItemAttributeDisplayType.Compact + }; + attributes.Add(at_pen); + } + + if (template.FragmentationChance > 0) + { + ItemAttribute at_frag = new ItemAttribute(ENewItemAttributeId.FragmentationChance) + { + Name = ENewItemAttributeId.FragmentationChance.GetName(), + Base = () => template.FragmentationChance, + StringValue = () => $"{(template.FragmentationChance * 100).ToString()}%", + DisplayType = () => EItemAttributeDisplayType.Compact + }; + attributes.Add(at_frag); + } + + if (template.RicochetChance > 0) + { + ItemAttribute at_ricochet = new ItemAttribute(ENewItemAttributeId.RicochetChance) + { + Name = ENewItemAttributeId.RicochetChance.GetName(), + Base = () => template.RicochetChance, + StringValue = () => $"{(template.RicochetChance * 100).ToString()}%", + DisplayType = () => EItemAttributeDisplayType.Compact + }; + attributes.Add(at_ricochet); + } + } + } +} \ No newline at end of file diff --git a/Faupi-MunitionsExpert/MunitionsExpert/src/client/StaticIconsPatch.cs b/Faupi-MunitionsExpert/MunitionsExpert/src/client/StaticIconsPatch.cs new file mode 100644 index 0000000..3ab4d5e --- /dev/null +++ b/Faupi-MunitionsExpert/MunitionsExpert/src/client/StaticIconsPatch.cs @@ -0,0 +1,35 @@ +using System; +using System.Reflection; +using Aki.Reflection.Patching; +using EFT.UI; +using UnityEngine; + +namespace MunitionsExpert +{ + internal class StaticIconsPatch : ModulePatch + { + protected override MethodBase GetTargetMethod() + { + return typeof(StaticIcons).GetMethod("GetAttributeIcon", BindingFlags.Instance | BindingFlags.Public); + } + + [PatchPrefix] + private static bool PatchPrefix(ref Sprite __result, Enum id) + { + if (id == null || !MunitionsExpert.iconCache.ContainsKey(id)) + { + return true; + } + + Sprite sprite = MunitionsExpert.iconCache[id]; + + if (sprite != null) + { + __result = sprite; + return false; + } + + return true; + } + } +} diff --git a/Faupi-MunitionsExpert/MunitionsExpert/src/server/ammostats.js b/Faupi-MunitionsExpert/MunitionsExpert/src/server/ammostats.js new file mode 100644 index 0000000..f575caf --- /dev/null +++ b/Faupi-MunitionsExpert/MunitionsExpert/src/server/ammostats.js @@ -0,0 +1,64 @@ +/* ammostats.js + * license: NCSA + * copyright: Faupi + * authors: + * - Faupi + */ + +"use strict"; +const path = require('path'); + +class AmmoStats { + constructor() { + this.mod = require("../package.json"); + this.translations = require("../res/translations.json"); + Logger.info(`Loading: ${this.mod.name} ${this.mod.version}`); + + ModLoader.onLoad[this.mod.name] = this.init.bind(this); + } + + init(){ + this.hookRoutes(); + this.updateLocalization(); + } + + updateLocalization(){ + var globalLocale = DatabaseServer.tables.locales.global; + + for(let language in this.translations){ + if(!language in globalLocale) continue; + + let attrKvPair = this.translations[language]; + for(let attrKey in attrKvPair){ + let attrValue = attrKvPair[attrKey]; + + globalLocale[language].interface[attrKey] = attrValue; + } + } + } + + hookRoutes(){ + HttpRouter.onStaticRoute["/MunitionsExpert/GetInfo"] = { + MunitionsExpert: this.getModInfo.bind(this) + }; + } + + getModInfo(url, info, sessionID, output){ + var output = { + status: 1, + data: null + }; + + // Don't mind this pointless try catch + try{ + output.data = {...this.mod, ...{path: path.resolve(ModLoader.getModPath(this.mod.name))}}; + output.status = 0; + }catch(ex){ + throw ex; + } + + return JsonUtil.serialize(output); + } +} + +module.exports = new AmmoStats(); \ No newline at end of file diff --git a/Faupi-MunitionsExpert/README.md b/Faupi-MunitionsExpert/README.md new file mode 100644 index 0000000..4c8a3f1 --- /dev/null +++ b/Faupi-MunitionsExpert/README.md @@ -0,0 +1,4 @@ +# Faupi-MunitionsExpert +## Faupi-MunitionsExpert for EFT - SPT-AKI +### CURRENT AKI VERSION: 2.3.0 +### GAMEVERSION: 0.12.12.15.17107 diff --git a/KcY-SeeItemValue/KcY-SeeItemValue 1.2.1/bepInEx/plugins/KcY-SeeItemValue.dll b/KcY-SeeItemValue/KcY-SeeItemValue 1.2.1/bepInEx/plugins/KcY-SeeItemValue.dll new file mode 100644 index 0000000..9cf0971 Binary files /dev/null and b/KcY-SeeItemValue/KcY-SeeItemValue 1.2.1/bepInEx/plugins/KcY-SeeItemValue.dll differ diff --git a/KcY-SeeItemValue/KcY-SeeItemValue 1.2.1/user/mods/KcY-SeeItemValue 1.2.1/LICENSE b/KcY-SeeItemValue/KcY-SeeItemValue 1.2.1/user/mods/KcY-SeeItemValue 1.2.1/LICENSE new file mode 100644 index 0000000..14bbbd7 --- /dev/null +++ b/KcY-SeeItemValue/KcY-SeeItemValue 1.2.1/user/mods/KcY-SeeItemValue 1.2.1/LICENSE @@ -0,0 +1,34 @@ +University of Illinois/NCSA Open Source License + +Copyright (c) [year] [fullname]. All rights reserved. + +Developed by: [SEE ITEM VALUE] + [KcY] + [https://github.com/KeiranY/TarkovLoader] + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation files +(the "Software"), to deal with the Software without restriction, +including without limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of the Software, +and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +* Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimers. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimers in the + documentation and/or other materials provided with the distribution. + +* Neither the names of [fullname], [project] nor the names of its + contributors may be used to endorse or promote products derived from + this Software without specific prior written permission. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH +THE SOFTWARE. \ No newline at end of file diff --git a/KcY-SeeItemValue/KcY-SeeItemValue 1.2.1/user/mods/KcY-SeeItemValue 1.2.1/modloader.js b/KcY-SeeItemValue/KcY-SeeItemValue 1.2.1/user/mods/KcY-SeeItemValue 1.2.1/modloader.js new file mode 100644 index 0000000..acfe313 --- /dev/null +++ b/KcY-SeeItemValue/KcY-SeeItemValue 1.2.1/user/mods/KcY-SeeItemValue 1.2.1/modloader.js @@ -0,0 +1,2 @@ +const { Mod } = require("./src/mod.js"); +module.exports.mod = new Mod(); \ No newline at end of file diff --git a/KcY-SeeItemValue/KcY-SeeItemValue 1.2.1/user/mods/KcY-SeeItemValue 1.2.1/package.json b/KcY-SeeItemValue/KcY-SeeItemValue 1.2.1/user/mods/KcY-SeeItemValue 1.2.1/package.json new file mode 100644 index 0000000..4510210 --- /dev/null +++ b/KcY-SeeItemValue/KcY-SeeItemValue 1.2.1/user/mods/KcY-SeeItemValue 1.2.1/package.json @@ -0,0 +1,10 @@ +{ + "name": "SeeItemValue", + "author": "KcY", + "version": "1.2.1", + "license": "NCSA Open Source", + "main": "modloader.js", + "akiVersion": "2.3.1", + "UpdatedBy": "CWX" +} + diff --git a/KcY-SeeItemValue/KcY-SeeItemValue 1.2.1/user/mods/KcY-SeeItemValue 1.2.1/src/config.json b/KcY-SeeItemValue/KcY-SeeItemValue 1.2.1/user/mods/KcY-SeeItemValue 1.2.1/src/config.json new file mode 100644 index 0000000..7f116ec --- /dev/null +++ b/KcY-SeeItemValue/KcY-SeeItemValue 1.2.1/user/mods/KcY-SeeItemValue 1.2.1/src/config.json @@ -0,0 +1,4 @@ +{ + "TraderPrice": false, + "TraderMultiplier": 0.54 +} \ No newline at end of file diff --git a/KcY-SeeItemValue/KcY-SeeItemValue 1.2.1/user/mods/KcY-SeeItemValue 1.2.1/src/mod.js b/KcY-SeeItemValue/KcY-SeeItemValue 1.2.1/user/mods/KcY-SeeItemValue 1.2.1/src/mod.js new file mode 100644 index 0000000..246b44e --- /dev/null +++ b/KcY-SeeItemValue/KcY-SeeItemValue 1.2.1/user/mods/KcY-SeeItemValue 1.2.1/src/mod.js @@ -0,0 +1,123 @@ +"use strict"; + +const cfg = require("./config.json"); +const database = DatabaseServer.tables; +const livePrice = database.templates.prices; +const handbookTable = database.templates.handbook; +const therapist = database.traders["54cb57776803fa99248b456e"].base +const ragman = database.traders["5ac3b934156ae10c4430e83c"].base +const jaeger = database.traders["5c0647fdd443bc2504c2d371"].base +const mechanic = database.traders["5a7c2eca46aef81a7ca2145d"].base +const prapor = database.traders["54cb50c76803fa8b248b4571"].base +const peacekeeper = database.traders["5935c25fb3acc3127c3d8cd9"].base +const skier = database.traders["58330581ace78e27b8b10cee"].base +const fence = database.traders["579dc571d53a0658a154fbec"].base +const tradersArr = [therapist, ragman, jaeger, mechanic, prapor, peacekeeper, skier, fence]; + +class Mod +{ + name = "KcY-SeeItemValue"; + version = "1.2.0"; + static price = ""; + + constructor() + { + Logger.info(`Loading: ${this.name} : ${this.version}`); + ModLoader.onLoad[this.name] = this.init.bind(this); + } + + init() + { + this.onLoadMod(); + } + + onLoadMod() + { + HttpRouter.onDynamicRoute["/cwx/seeitemvalue/"] = { + ItemValueMod: this.onRequestConfig.bind(this) + }; + } + + onRequestConfig(url, info, sessionID) + { + const splittedUrl = url.split("/"); + const id = splittedUrl[splittedUrl.length - 1].toLowerCase(); + + return HttpResponse.noBody(this.getIdPrice(id)); + } + + getIdPrice(id) + { + let sPrice = 1; + let sMutli = 1; + let parentId = ""; + + // if TraderPrice in cfg is False get price from flea AVG + if(cfg.TraderPrice === false) + { + const result = livePrice[id]; + if(typeof result != `undefined`) + { + return result; + } + // will still default to Handbook if no price is found for flea AVG + } + // if TraderPrice in cfg is True get price from handbook + // as traders have a modifier, avg is 0.54, closest we can get without checking against each trader + // thanks to TEOA for this info + for(let i in handbookTable.Items) + { + if(handbookTable.Items[i].Id === id) + { + parentId = handbookTable.Items[i].ParentId; + sMutli = this.getBestTraderMulti(parentId); + sPrice = handbookTable.Items[i].Price; + let result = parseInt(sPrice*sMutli); + + return result; + } + } + return sPrice; + } + + getBestTraderMulti(parentId) + { + let traderSellCat = ""; + let traderMulti = 0.54; + let traderName = ""; // could be used later to be passed back to module to show trader and price + let altTraderSellCat = ""; + + for(let i in handbookTable.Categories) + { + if(handbookTable.Categories[i].Id === parentId) + { + traderSellCat = handbookTable.Categories[i].Id; + altTraderSellCat = handbookTable.Categories[i].ParentId; + break; + } + } + + for(let iter = 0; iter < 8; iter++) + { + if(tradersArr[iter].sell_category.includes(traderSellCat)) + { + traderMulti = (100 - tradersArr[iter].loyaltyLevels[0].buy_price_coef) / 100; + traderName = tradersArr[iter].nickname; + return traderMulti; + } + } + + for(let iter = 0; iter < 8; iter++) + { + if(tradersArr[iter].sell_category.includes(altTraderSellCat)) + { + traderMulti = (100 - tradersArr[iter].loyaltyLevels[0].buy_price_coef) / 100; + traderName = tradersArr[iter].nickname; + return traderMulti; + } + } + return cfg.TraderMultiplier; + } +} + +module.exports.Mod = Mod; \ No newline at end of file diff --git a/KcY-SeeItemValue/README.md b/KcY-SeeItemValue/README.md new file mode 100644 index 0000000..7040be1 --- /dev/null +++ b/KcY-SeeItemValue/README.md @@ -0,0 +1,7 @@ +# SeeItemValue +## KcY-SeeItemValue for EFT - SPT-AKI +### CURRENT AKI VERSION: 2.2.1 +### GAMEVERSION: 0.12.12.15.16584 + +ItemValue is the project folder +KcY-SeeItemValue is what is uploaded to the hub diff --git a/KcY-SeeItemValue/itemValue/Patches.cs b/KcY-SeeItemValue/itemValue/Patches.cs new file mode 100644 index 0000000..c6cd0e8 --- /dev/null +++ b/KcY-SeeItemValue/itemValue/Patches.cs @@ -0,0 +1,71 @@ +using System; +using System.Reflection; +using Aki.Reflection.Patching; +using Aki.Reflection.Utils; +using EFT; +using EFT.InventoryLogic; +using Ammo = BulletClass; +using Grenade = GClass2079; +using GrenadeTemplate = GClass1975; +using ItemAttribute = GClass2090; +using SecureContainer = GClass2038; +using SecureContainerTemplate = GClass1937; + +namespace itemValueMod +{ + public class ItemPatch : ModulePatch + { + protected override MethodBase GetTargetMethod() + { + return typeof(Item).GetConstructor(new Type[] { typeof(string), typeof(ItemTemplate) }); + } + + [PatchPostfix] + private static void PatchPostFix(ref Item __instance, string id, ItemTemplate template) + { + ItemValue.AddItemValue(ref __instance, id, template); + } + } + + public class AmmoPatch : ModulePatch + { + protected override MethodBase GetTargetMethod() + { + return typeof(Ammo).GetConstructor(new Type[] { typeof(string), typeof(AmmoTemplate) }); + } + + [PatchPostfix] + private static void PatchPostFix(ref Ammo __instance, string id, AmmoTemplate template) + { + ItemValue.AddItemValue(ref __instance, id, template); + } + } + + public class GrenadePatch : ModulePatch + { + protected override MethodBase GetTargetMethod() + { + return typeof(Grenade).GetConstructor(new Type[] { typeof(string), typeof(GrenadeTemplate) }); + } + + [PatchPostfix] + private static void PatchPostFix(ref Grenade __instance, string id, GrenadeTemplate template) + { + ItemValue.AddItemValue(ref __instance, id, template); + } + } + + public class SecureContainerPatch : ModulePatch + { + protected override MethodBase GetTargetMethod() + { + return typeof(SecureContainer).GetConstructor(new Type[] { typeof(string), typeof(SecureContainerTemplate) }); + } + + [PatchPostfix] + private static void PatchPostFix(ref SecureContainer __instance, string id, SecureContainerTemplate template) + { + ItemValue.AddItemValue(ref __instance, id, template); + } + } +} \ No newline at end of file diff --git a/KcY-SeeItemValue/itemValue/Program.cs b/KcY-SeeItemValue/itemValue/Program.cs new file mode 100644 index 0000000..09e0793 --- /dev/null +++ b/KcY-SeeItemValue/itemValue/Program.cs @@ -0,0 +1,17 @@ +using BepInEx; +using HarmonyLib; + +namespace itemValueMod +{ + [BepInPlugin("com.KcY.SeeItemValue", "KcY-SeeItemValue", "1.2.0")] + public class Plugin : BaseUnityPlugin + { + private void Awake() + { + new ItemPatch().Enable(); + new AmmoPatch().Enable(); + new GrenadePatch().Enable(); + new SecureContainerPatch().Enable(); + } + } +} diff --git a/KcY-SeeItemValue/itemValue/_built/LICENSE b/KcY-SeeItemValue/itemValue/_built/LICENSE new file mode 100644 index 0000000..14bbbd7 --- /dev/null +++ b/KcY-SeeItemValue/itemValue/_built/LICENSE @@ -0,0 +1,34 @@ +University of Illinois/NCSA Open Source License + +Copyright (c) [year] [fullname]. All rights reserved. + +Developed by: [SEE ITEM VALUE] + [KcY] + [https://github.com/KeiranY/TarkovLoader] + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation files +(the "Software"), to deal with the Software without restriction, +including without limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of the Software, +and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +* Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimers. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimers in the + documentation and/or other materials provided with the distribution. + +* Neither the names of [fullname], [project] nor the names of its + contributors may be used to endorse or promote products derived from + this Software without specific prior written permission. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH +THE SOFTWARE. \ No newline at end of file diff --git a/KcY-SeeItemValue/itemValue/_built/modloader.js b/KcY-SeeItemValue/itemValue/_built/modloader.js new file mode 100644 index 0000000..acfe313 --- /dev/null +++ b/KcY-SeeItemValue/itemValue/_built/modloader.js @@ -0,0 +1,2 @@ +const { Mod } = require("./src/mod.js"); +module.exports.mod = new Mod(); \ No newline at end of file diff --git a/KcY-SeeItemValue/itemValue/_built/module.dll b/KcY-SeeItemValue/itemValue/_built/module.dll new file mode 100644 index 0000000..effe19b Binary files /dev/null and b/KcY-SeeItemValue/itemValue/_built/module.dll differ diff --git a/KcY-SeeItemValue/itemValue/_built/package.json b/KcY-SeeItemValue/itemValue/_built/package.json new file mode 100644 index 0000000..92c6454 --- /dev/null +++ b/KcY-SeeItemValue/itemValue/_built/package.json @@ -0,0 +1,10 @@ +{ + "name": "SeeItemValue", + "author": "KcY", + "version": "1.1.0", + "license": "NCSA Open Source", + "main": "modloader.js", + "akiVersion": "2.2.1", + "UpdatedBy": "CWX" +} + diff --git a/KcY-SeeItemValue/itemValue/_built/src/config.json b/KcY-SeeItemValue/itemValue/_built/src/config.json new file mode 100644 index 0000000..7f116ec --- /dev/null +++ b/KcY-SeeItemValue/itemValue/_built/src/config.json @@ -0,0 +1,4 @@ +{ + "TraderPrice": false, + "TraderMultiplier": 0.54 +} \ No newline at end of file diff --git a/KcY-SeeItemValue/itemValue/_built/src/mod.js b/KcY-SeeItemValue/itemValue/_built/src/mod.js new file mode 100644 index 0000000..635551a --- /dev/null +++ b/KcY-SeeItemValue/itemValue/_built/src/mod.js @@ -0,0 +1,123 @@ +"use strict"; + +const cfg = require("./config.json"); +const database = DatabaseServer.tables; +const livePrice = database.templates.prices; +const handbookTable = database.templates.handbook; +const therapist = DatabaseServer.tables.traders["54cb57776803fa99248b456e"].base +const ragman = DatabaseServer.tables.traders["5ac3b934156ae10c4430e83c"].base +const jaeger = DatabaseServer.tables.traders["5c0647fdd443bc2504c2d371"].base +const mechanic = DatabaseServer.tables.traders["5a7c2eca46aef81a7ca2145d"].base +const prapor = DatabaseServer.tables.traders["54cb50c76803fa8b248b4571"].base +const peacekeeper = DatabaseServer.tables.traders["5935c25fb3acc3127c3d8cd9"].base +const skier = DatabaseServer.tables.traders["58330581ace78e27b8b10cee"].base +const fence = DatabaseServer.tables.traders["579dc571d53a0658a154fbec"].base +const tradersArr = [therapist, ragman, jaeger, mechanic, prapor, peacekeeper, skier, fence]; + +class Mod +{ + name = "KcY-SeeItemValue"; + version = "1.1.0"; + static price = ""; + + constructor() + { + Logger.info(`Loading: ${this.name} : ${this.version}`); + ModLoader.onLoad[this.name] = this.init.bind(this); + } + + init() + { + this.onLoadMod(); + } + + onLoadMod() + { + HttpRouter.onDynamicRoute["/cwx/itemvaluemod/"] = { + ItemValueMod: this.onRequestConfig.bind(this) + }; + } + + onRequestConfig(url, info, sessionID) + { + const splittedUrl = url.split("/"); + const id = splittedUrl[splittedUrl.length - 1].toLowerCase(); + + return HttpResponse.noBody(this.getIdPrice(id)); + } + + getIdPrice(id) + { + let sPrice = 1; + let sMutli = 1; + let parentId = ""; + + // if TraderPrice in cfg is False get price from flea AVG + if(cfg.TraderPrice === false) + { + const result = livePrice[id]; + if(typeof result != `undefined`) + { + return result; + } + // will still default to Handbook if no price is found for flea AVG + } + // if TraderPrice in cfg is True get price from handbook + // as traders have a modifier, avg is 0.54, closest we can get without checking against each trader + // thanks to TEOA for this info + for(let i in handbookTable.Items) + { + if(handbookTable.Items[i].Id === id) + { + parentId = handbookTable.Items[i].ParentId; + sMutli = this.getBestTraderMulti(parentId); + sPrice = handbookTable.Items[i].Price; + let result = parseInt(sPrice*sMutli); + + return result; + } + } + return sPrice; + } + + getBestTraderMulti(parentId) + { + let traderSellCat = ""; + let traderMulti = 0.54; + let traderName = ""; // could be used later to be passed back to module to show trader and price + let altTraderSellCat = ""; + + for(let i in handbookTable.Categories) + { + if(handbookTable.Categories[i].Id === parentId) + { + traderSellCat = handbookTable.Categories[i].Id; + altTraderSellCat = handbookTable.Categories[i].ParentId; + break; + } + } + + for(let iter = 0; iter < 8; iter++) + { + if(tradersArr[iter].sell_category.includes(traderSellCat)) + { + traderMulti = (100 - tradersArr[iter].loyaltyLevels[0].buy_price_coef) / 100; + traderName = tradersArr[iter].nickname; + return traderMulti; + } + } + + for(let iter = 0; iter < 8; iter++) + { + if(tradersArr[iter].sell_category.includes(altTraderSellCat)) + { + traderMulti = (100 - tradersArr[iter].loyaltyLevels[0].buy_price_coef) / 100; + traderName = tradersArr[iter].nickname; + return traderMulti; + } + } + return cfg.TraderMultiplier; + } +} + +module.exports.Mod = Mod; \ No newline at end of file diff --git a/KcY-SeeItemValue/itemValue/itemGetter.cs b/KcY-SeeItemValue/itemValue/itemGetter.cs new file mode 100644 index 0000000..0f1ae7d --- /dev/null +++ b/KcY-SeeItemValue/itemValue/itemGetter.cs @@ -0,0 +1,7 @@ +//namespace itemValueMod +//{ +// public struct itemGetter +// { +// public double sPrice { get; set; } +// } +//} diff --git a/KcY-SeeItemValue/itemValue/itemValue.sln b/KcY-SeeItemValue/itemValue/itemValue.sln new file mode 100644 index 0000000..06caaa5 --- /dev/null +++ b/KcY-SeeItemValue/itemValue/itemValue.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.32014.148 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "itemValueMod", "itemValueMod.csproj", "{F237CF5E-96D2-41B7-94C4-6990890886C3}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {F237CF5E-96D2-41B7-94C4-6990890886C3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F237CF5E-96D2-41B7-94C4-6990890886C3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F237CF5E-96D2-41B7-94C4-6990890886C3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F237CF5E-96D2-41B7-94C4-6990890886C3}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {E5B08F5A-DDB9-426E-B149-18108C64B406} + EndGlobalSection +EndGlobal diff --git a/KcY-SeeItemValue/itemValue/itemValueMod.cs b/KcY-SeeItemValue/itemValue/itemValueMod.cs new file mode 100644 index 0000000..da53e83 --- /dev/null +++ b/KcY-SeeItemValue/itemValue/itemValueMod.cs @@ -0,0 +1,78 @@ +/* Original Author: KyC + * CWX - updated to work for 2.3.0 AKI + * Client version: 0.12.12.15.17107 + * - removed the need for KyC's ModLoader + * - Commented out the code for stackCount total worth + * - Commented out the code for weapon parts total worth + * - Commented out the code for container total worth + * - Commented out the code for magazine total worth + */ + +using Aki.Common.Http; +using Aki.Common.Utils; +using EFT.InventoryLogic; +using System; +using System.Collections.Generic; +using ItemAttribute = GClass2090; + +namespace itemValueMod +{ + public class ItemValue + { + public static void AddItemValue(ref T __instance, string id, ItemTemplate template) where T : Item + { + // Remove item if it has no value + // if (Math.Round(__instance.Value()) == 0) return; + + // Make a copy of the existing attributes list, this is needed for inherited types of Item that use a global attributes list (ammo) + var atts = new List(); + atts.AddRange(__instance.Attributes); + __instance.Attributes = atts; + ItemAttribute attr = new ItemAttribute(EItemAttributeId.MoneySum) + { + StringValue = new Func(__instance.ValueStr), // ₽ + Name = "RUB ₽", //new Func(ValueExtension.ValueTrName).ToString(), + DisplayType = new Func(() => EItemAttributeDisplayType.Compact) + }; + __instance.Attributes.Add(attr); + } + } + + public static class ValueExtension + { + public static double Value(this Item item) + { + var template = item.Template; + string itemId = template._id; + double _price; + var json = RequestHandler.GetJson($"/cwx/seeitemvalue/{itemId}"); // CWX - sends ID to server + _price = Json.Deserialize(json); + + var medKit = item.GetItemComponent(); + if (medKit != null) + { + _price *= medKit.HpResource / medKit.MaxHpResource; + } + + var repair = item.GetItemComponent(); + if (repair != null) + { + _price *= repair.Durability / repair.MaxDurability; + } + + var dogtag = item.GetItemComponent(); + if (dogtag != null) + { + _price *= dogtag.Level; + } + + //_price *= item.StackObjectsCount; + + return _price; + } + public static string ValueStr(this Item item) + { + return Math.Round(item.Value()).ToString(); + } + } +} \ No newline at end of file diff --git a/KcY-SeeItemValue/itemValue/itemValueMod.csproj b/KcY-SeeItemValue/itemValue/itemValueMod.csproj new file mode 100644 index 0000000..2776470 --- /dev/null +++ b/KcY-SeeItemValue/itemValue/itemValueMod.csproj @@ -0,0 +1,42 @@ + + + + net472 + 1.2.0 + KcY-SeeItemValue + + + + + ..\..\..\..\..\..\..\AKI PROJECT\AKI 2.3.0 17107 Bepin\BepInEx\core\0Harmony.dll + + + ..\..\..\..\..\..\..\AKI PROJECT\AKI 2.3.0 17107 Bepin\EscapeFromTarkov_Data\Managed\Aki.Common.dll + + + ..\..\..\..\..\..\..\AKI PROJECT\AKI 2.3.0 17107 Bepin\EscapeFromTarkov_Data\Managed\Aki.Reflection.dll + + + ..\..\..\..\..\..\..\AKI PROJECT\AKI 2.3.0 17107 Bepin\EscapeFromTarkov_Data\Managed\Assembly-CSharp.dll + + + ..\..\..\..\..\..\..\AKI PROJECT\AKI 2.3.0 17107 Bepin\BepInEx\core\BepInEx.dll + + + ..\..\..\..\..\..\..\AKI PROJECT\AKI 2.3.0 17107 Bepin\EscapeFromTarkov_Data\Managed\Comfort.dll + + + ..\..\..\..\..\..\..\AKI PROJECT\AKI 2.3.0 17107 Bepin\EscapeFromTarkov_Data\Managed\ItemComponent.Types.dll + + + ..\..\..\..\..\..\..\AKI PROJECT\AKI 2.3.0 17107 Bepin\EscapeFromTarkov_Data\Managed\Newtonsoft.Json.dll + + + ..\..\..\..\..\..\..\AKI PROJECT\AKI 2.3.0 17107 Bepin\EscapeFromTarkov_Data\Managed\UnityEngine.dll + + + ..\..\..\..\..\..\..\AKI PROJECT\AKI 2.3.0 17107 Bepin\EscapeFromTarkov_Data\Managed\UnityEngine.CoreModule.dll + + + + diff --git a/README.md b/README.md new file mode 100644 index 0000000..a2fa233 --- /dev/null +++ b/README.md @@ -0,0 +1,7 @@ +# All in One Mods I Maintain +## currently for EFT - SPT-AKI. - 2.3.1 + +### Mods +- Faupi-HideoutArchitect +- Faupi-MunitionsExpert +- KcY-SeeItemValue \ No newline at end of file