initial commit

This commit is contained in:
CWX 2022-05-14 20:06:24 +01:00
commit 3de0e8a993
73 changed files with 2974 additions and 0 deletions

30
.gitignore vendored Normal file
View File

@ -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

View File

@ -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.

View File

@ -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.*/
}

View File

@ -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"
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 553 B

View File

@ -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"
}
}

View File

@ -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();

View File

@ -0,0 +1,135 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{21B1900E-2DFA-4C18-A1BD-E10907620FE3}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>HideoutArchitect</RootNamespace>
<AssemblyName>HideoutArchitect</AssemblyName>
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<Deterministic>true</Deterministic>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup>
<SignAssembly>true</SignAssembly>
</PropertyGroup>
<PropertyGroup>
<AssemblyOriginatorKeyFile>
</AssemblyOriginatorKeyFile>
</PropertyGroup>
<ItemGroup>
<Compile Include="src\client\HideoutItemViewPanel.cs" />
<Compile Include="src\client\Main.cs" />
<Compile Include="src\client\ModConfiguration.cs" />
<Compile Include="src\client\ModInformation.cs" />
<Compile Include="src\client\Patches.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="src\client\Resources.cs" />
<Compile Include="src\client\Utils.cs" />
</ItemGroup>
<ItemGroup>
<None Include="package.json" />
<None Include="res\translations.json" />
</ItemGroup>
<ItemGroup>
<Content Include="res\icon_neededforhideout_small.png" />
<Content Include="src\server\hideoutarchitect.js" />
</ItemGroup>
<ItemGroup>
<Reference Include="Aki.Common, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\..\..\..\..\..\AKI PROJECT\AKI 2.3.0 17107 Bepin\EscapeFromTarkov_Data\Managed\Aki.Common.dll</HintPath>
</Reference>
<Reference Include="Aki.Reflection, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\..\..\..\..\..\AKI PROJECT\AKI 2.3.0 17107 Bepin\EscapeFromTarkov_Data\Managed\Aki.Reflection.dll</HintPath>
</Reference>
<Reference Include="Assembly-CSharp, Version=0.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\..\..\..\..\..\AKI PROJECT\AKI 2.3.0 17107 Bepin\EscapeFromTarkov_Data\Managed\Assembly-CSharp.dll</HintPath>
</Reference>
<Reference Include="BepInEx">
<HintPath>..\..\..\..\..\..\..\AKI PROJECT\AKI 2.3.0 17107 Bepin\BepInEx\core\BepInEx.dll</HintPath>
</Reference>
<Reference Include="Comfort, Version=1.0.0.4, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\..\..\..\..\..\AKI PROJECT\AKI 2.3.0 17107 Bepin\EscapeFromTarkov_Data\Managed\Comfort.dll</HintPath>
</Reference>
<Reference Include="Newtonsoft.Json, Version=9.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\..\..\..\..\..\AKI PROJECT\AKI 2.3.0 17107 Bepin\EscapeFromTarkov_Data\Managed\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="Sirenix.Serialization, Version=3.0.4.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\..\..\..\..\..\AKI PROJECT\AKI 2.3.0 17107 Bepin\EscapeFromTarkov_Data\Managed\Sirenix.Serialization.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Drawing" />
<Reference Include="Unity.TextMeshPro, Version=0.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\..\..\..\..\..\AKI PROJECT\AKI 2.3.0 17107 Bepin\EscapeFromTarkov_Data\Managed\Unity.TextMeshPro.dll</HintPath>
</Reference>
<Reference Include="UnityEngine, Version=0.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\..\..\..\..\..\AKI PROJECT\AKI 2.3.0 17107 Bepin\EscapeFromTarkov_Data\Managed\UnityEngine.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.CoreModule, Version=0.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\..\..\..\..\..\AKI PROJECT\AKI 2.3.0 17107 Bepin\EscapeFromTarkov_Data\Managed\UnityEngine.CoreModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.UI, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\..\..\..\..\..\AKI PROJECT\AKI 2.3.0 17107 Bepin\EscapeFromTarkov_Data\Managed\UnityEngine.UI.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.UnityWebRequestModule, Version=0.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\..\..\..\..\..\AKI PROJECT\AKI 2.3.0 17107 Bepin\EscapeFromTarkov_Data\Managed\UnityEngine.UnityWebRequestModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.UnityWebRequestTextureModule, Version=0.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\..\..\..\..\..\AKI PROJECT\AKI 2.3.0 17107 Bepin\EscapeFromTarkov_Data\Managed\UnityEngine.UnityWebRequestTextureModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.UnityWebRequestWWWModule, Version=0.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\..\..\..\..\..\AKI PROJECT\AKI 2.3.0 17107 Bepin\EscapeFromTarkov_Data\Managed\UnityEngine.UnityWebRequestWWWModule.dll</HintPath>
</Reference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<PropertyGroup>
<PostBuildEvent>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"</PostBuildEvent>
</PropertyGroup>
<PropertyGroup>
<PlatformTarget>x64</PlatformTarget>
</PropertyGroup>
<ImportGroup Label=".NET DllExport">
<Import Project="../packages/UnmanagedExports.1.2.6/tools/RGiesecke.DllExport.targets" Condition="Exists('../packages/UnmanagedExports.1.2.6/tools/RGiesecke.DllExport.targets')" />
<Import Project="../packages/UnmanagedExports.1.2.7/tools/RGiesecke.DllExport.targets" Condition="Exists('../packages/UnmanagedExports.1.2.7/tools/RGiesecke.DllExport.targets')" />
</ImportGroup>
</Project>

View File

@ -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

View File

@ -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.

View File

@ -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")]

View File

@ -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"
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 553 B

View File

@ -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"
}
}

View File

@ -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();

View File

@ -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"
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 553 B

View File

@ -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"
}
}

View File

@ -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<HoverTrigger>();
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<string> parts = new List<string>() { $"<color={HideoutArchitect.ModConfig.TooltipHeaderColor}><b>{"NEEDED FOR HIDEOUT".Localized().ToSentenceCase()}:</b></color>" };
List<AreaData> areasToUpgrade = HideoutArchitect.GetApplicableUpgrades(this.itemView.Item);
if (areasToUpgrade == null || areasToUpgrade.Count < 1) return;
areasToUpgrade.ForEach(a =>
{
try
{
parts.Add($"<color=white>{a.Template.Name.ToSentenceCase() /*Already localized*/}:</color> {"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;
}
}
}
}

View File

@ -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<AreaData> GetApplicableUpgrades(Item item)
{
List<AreaData> areas = Singleton<Hideout>.Instance.AreaDatas.Where(area =>
{
bool areaActive = area.Status != EAreaStatus.NotSet && area.Template.Enabled == true;
List<Requirement> targetedRequirements;
switch (ModConfig.NeededForHideoutDefinition)
{
case ENeededDefinition.NextLevel:
case ENeededDefinition.NextLevelReady:
targetedRequirements = area.NextStage.Requirements.Value as List<Requirement>;
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<AreaData> data = GetApplicableUpgrades(item);
return data != null && data.Count > 0;
}
}
}

View File

@ -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<ModConfiguration>(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<ModConfiguration>(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;
}
}
}

View File

@ -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<int>("status") == 0);
ModInfo = response["data"].ToObject<ModInformation>();
}
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;
}
}
}

View File

@ -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<ModulePatch>
{
new ItemViewPatches.GridItemViewUpdateInfoPatch(),
new ItemViewPatches.ItemViewInitPatch(),
new ItemViewPatches.NewGridItemViewPatch()
};
}
public void RunPatches()
{
foreach (ModulePatch patch in this._patches)
{
patch.Enable();
}
}
private readonly List<ModulePatch> _patches;
}
public static class ItemViewPatches
{
public static Dictionary<ItemView, HideoutItemViewPanel> hideoutPanels = new Dictionary<ItemView, HideoutItemViewPanel>();
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();
}
}
}
}

View File

@ -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<string, Sprite> iconCache = new Dictionary<string, Sprite>();
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<QuestItemViewPanel>(original);
GameObject newObject = clone.gameObject;
clone.transform.parent = HideoutArchitect.GameObjectStorage;
newObject.name = "HideoutItem";
HideoutItemViewPanel result = newObject.AddComponent<HideoutItemViewPanel>();
//Copy fields over
result.CopyFieldsFromQuestView(clone);
//Set custom sprite
if(result.iconImage != null)
{
result.iconImage.sprite = iconCache["neededforhideout"] ?? UnityEngine.Resources.Load<Sprite>("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)));
}
}
}
}
}

View File

@ -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;
}
}
}

View File

@ -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();

View File

@ -0,0 +1,4 @@
# HideoutArchitect
## Faupi-HideoutArchitect for EFT - SPT-AKI
### CURRENT AKI VERSION: 2.2.1
### GAMEVERSION: 0.12.12.15.16584

View File

@ -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.

View File

@ -0,0 +1,113 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{C5889536-4EE7-40BC-A8FD-7F1A80C3F3E5}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>MunitionsExpert</RootNamespace>
<AssemblyName>Faupi-MunitionsExpert</AssemblyName>
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<Deterministic>true</Deterministic>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE;/noentry</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup>
<SignAssembly>true</SignAssembly>
</PropertyGroup>
<PropertyGroup>
<AssemblyOriginatorKeyFile>
</AssemblyOriginatorKeyFile>
</PropertyGroup>
<ItemGroup>
<Compile Include="src\client\StaticIconsPatch.cs" />
<Compile Include="src\client\CachedAttributesPatch.cs" />
<Compile Include="src\client\ModInformation.cs" />
<Compile Include="src\client\Attributes.cs" />
<Compile Include="src\client\MunitionsExpert.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<Content Include="src\server\ammostats.js" />
<Content Include="res\armorDamage.png" />
<Content Include="res\ricochet.png" />
</ItemGroup>
<ItemGroup>
<None Include="LICENSE" />
<None Include="package.json" />
<None Include="res\translations.json" />
</ItemGroup>
<ItemGroup>
<Reference Include="0Harmony">
<HintPath>..\..\..\..\..\..\..\AKI PROJECT\AKI 2.3.0 17107 Bepin\BepInEx\core\0Harmony.dll</HintPath>
</Reference>
<Reference Include="Aki.Common">
<HintPath>..\..\..\..\..\..\..\AKI PROJECT\AKI 2.3.0 17107 Bepin\EscapeFromTarkov_Data\Managed\Aki.Common.dll</HintPath>
</Reference>
<Reference Include="Aki.Reflection">
<HintPath>..\..\..\..\..\..\..\AKI PROJECT\AKI 2.3.0 17107 Bepin\EscapeFromTarkov_Data\Managed\Aki.Reflection.dll</HintPath>
</Reference>
<Reference Include="Assembly-CSharp">
<HintPath>..\..\..\..\..\..\..\AKI PROJECT\AKI 2.3.0 17107 Bepin\EscapeFromTarkov_Data\Managed\Assembly-CSharp.dll</HintPath>
</Reference>
<Reference Include="BepInEx">
<HintPath>..\..\..\..\..\..\..\AKI PROJECT\AKI 2.3.0 17107 Bepin\BepInEx\core\BepInEx.dll</HintPath>
</Reference>
<Reference Include="Comfort">
<HintPath>..\..\..\..\..\..\..\AKI PROJECT\AKI 2.3.0 17107 Bepin\EscapeFromTarkov_Data\Managed\Comfort.dll</HintPath>
</Reference>
<Reference Include="Microsoft.CSharp" />
<Reference Include="Newtonsoft.Json, Version=9.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\References\EFT-Managed\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="UnityEngine">
<HintPath>..\..\..\..\..\..\..\AKI PROJECT\AKI 2.3.0 17107 Bepin\EscapeFromTarkov_Data\Managed\UnityEngine.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.CoreModule">
<HintPath>..\..\..\..\..\..\..\AKI PROJECT\AKI 2.3.0 17107 Bepin\EscapeFromTarkov_Data\Managed\UnityEngine.CoreModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.UnityWebRequestModule">
<HintPath>..\..\..\..\..\..\..\AKI PROJECT\AKI 2.3.0 17107 Bepin\EscapeFromTarkov_Data\Managed\UnityEngine.UnityWebRequestModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.UnityWebRequestTextureModule">
<HintPath>..\..\..\..\..\..\..\AKI PROJECT\AKI 2.3.0 17107 Bepin\EscapeFromTarkov_Data\Managed\UnityEngine.UnityWebRequestTextureModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.UnityWebRequestWWWModule">
<HintPath>..\..\..\..\..\..\..\AKI PROJECT\AKI 2.3.0 17107 Bepin\EscapeFromTarkov_Data\Managed\UnityEngine.UnityWebRequestWWWModule.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<Folder Include="_Build\Faupi-MunitionsExpert\" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<PropertyGroup>
<PostBuildEvent>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"</PostBuildEvent>
</PropertyGroup>
</Project>

View File

@ -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

View File

@ -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")]

View File

@ -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.

View File

@ -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"
}

View File

@ -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"
}
}

View File

@ -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();

Binary file not shown.

After

Width:  |  Height:  |  Size: 701 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 732 B

View File

@ -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"
}
}

View File

@ -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();
}
}
}
}

View File

@ -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<ItemAttributes> __result)
{
if (!__result.Any((ItemAttributes a) => (Attributes.ENewItemAttributeId)a.Id == Attributes.ENewItemAttributeId.Damage))
{
//MunitionsExpert.FormatExistingAttributes(ref __result, __instance);
MunitionsExpert.AddNewAttributes(ref __result, __instance);
}
}
}
}

View File

@ -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<int>("status") == 0);
ModInfo = response["data"].ToObject<ModInformation>();
}
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;
}
}
}

View File

@ -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<Enum, Sprite> iconCache = new Dictionary<Enum, Sprite>();
public static List<ItemAttribute> penAttributes = new List<ItemAttribute>(); // For refreshing armor class rating
public static string modName = ModInfo.name;
public static void CacheIcons()
{
iconCache.Add(ENewItemAttributeId.Damage, Resources.Load<Sprite>("characteristics/icons/icon_info_damage"));
iconCache.Add(ENewItemAttributeId.FragmentationChance, Resources.Load<Sprite>("characteristics/icons/icon_info_shrapnelcount"));
iconCache.Add(EItemAttributeId.LightBleedingDelta, Resources.Load<Sprite>("characteristics/icons/icon_info_bloodloss"));
iconCache.Add(EItemAttributeId.HeavyBleedingDelta, Resources.Load<Sprite>("characteristics/icon_info_hydration"));
iconCache.Add(ENewItemAttributeId.Penetration, Resources.Load<Sprite>("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<ItemAttribute> 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<ServerSettings>.Instantiated) { return $"CLASS_DATA_MISSING {template.PenetrationPower.ToString()}"; }
ServerSettings.GClass1148.GClass1149[] classes = Singleton<ServerSettings>.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);
}
}
}
}

View File

@ -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;
}
}
}

View File

@ -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();

View File

@ -0,0 +1,4 @@
# Faupi-MunitionsExpert
## Faupi-MunitionsExpert for EFT - SPT-AKI
### CURRENT AKI VERSION: 2.3.0
### GAMEVERSION: 0.12.12.15.17107

View File

@ -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.

View File

@ -0,0 +1,2 @@
const { Mod } = require("./src/mod.js");
module.exports.mod = new Mod();

View File

@ -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"
}

View File

@ -0,0 +1,4 @@
{
"TraderPrice": false,
"TraderMultiplier": 0.54
}

View File

@ -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;

View File

@ -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

View File

@ -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);
}
}
}

View File

@ -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();
}
}
}

View File

@ -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.

View File

@ -0,0 +1,2 @@
const { Mod } = require("./src/mod.js");
module.exports.mod = new Mod();

Binary file not shown.

View File

@ -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"
}

View File

@ -0,0 +1,4 @@
{
"TraderPrice": false,
"TraderMultiplier": 0.54
}

View File

@ -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;

View File

@ -0,0 +1,7 @@
//namespace itemValueMod
//{
// public struct itemGetter
// {
// public double sPrice { get; set; }
// }
//}

View File

@ -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

View File

@ -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<T>(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<ItemAttribute>();
atts.AddRange(__instance.Attributes);
__instance.Attributes = atts;
ItemAttribute attr = new ItemAttribute(EItemAttributeId.MoneySum)
{
StringValue = new Func<string>(__instance.ValueStr), // ₽
Name = "RUB ₽", //new Func<string>(ValueExtension.ValueTrName).ToString(),
DisplayType = new Func<EItemAttributeDisplayType>(() => 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<int>(json);
var medKit = item.GetItemComponent<MedKitComponent>();
if (medKit != null)
{
_price *= medKit.HpResource / medKit.MaxHpResource;
}
var repair = item.GetItemComponent<RepairableComponent>();
if (repair != null)
{
_price *= repair.Durability / repair.MaxDurability;
}
var dogtag = item.GetItemComponent<DogtagComponent>();
if (dogtag != null)
{
_price *= dogtag.Level;
}
//_price *= item.StackObjectsCount;
return _price;
}
public static string ValueStr(this Item item)
{
return Math.Round(item.Value()).ToString();
}
}
}

View File

@ -0,0 +1,42 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net472</TargetFramework>
<Version>1.2.0</Version>
<AssemblyName>KcY-SeeItemValue</AssemblyName>
</PropertyGroup>
<ItemGroup>
<Reference Include="0Harmony">
<HintPath>..\..\..\..\..\..\..\AKI PROJECT\AKI 2.3.0 17107 Bepin\BepInEx\core\0Harmony.dll</HintPath>
</Reference>
<Reference Include="Aki.Common">
<HintPath>..\..\..\..\..\..\..\AKI PROJECT\AKI 2.3.0 17107 Bepin\EscapeFromTarkov_Data\Managed\Aki.Common.dll</HintPath>
</Reference>
<Reference Include="Aki.Reflection">
<HintPath>..\..\..\..\..\..\..\AKI PROJECT\AKI 2.3.0 17107 Bepin\EscapeFromTarkov_Data\Managed\Aki.Reflection.dll</HintPath>
</Reference>
<Reference Include="Assembly-CSharp">
<HintPath>..\..\..\..\..\..\..\AKI PROJECT\AKI 2.3.0 17107 Bepin\EscapeFromTarkov_Data\Managed\Assembly-CSharp.dll</HintPath>
</Reference>
<Reference Include="BepInEx">
<HintPath>..\..\..\..\..\..\..\AKI PROJECT\AKI 2.3.0 17107 Bepin\BepInEx\core\BepInEx.dll</HintPath>
</Reference>
<Reference Include="Comfort">
<HintPath>..\..\..\..\..\..\..\AKI PROJECT\AKI 2.3.0 17107 Bepin\EscapeFromTarkov_Data\Managed\Comfort.dll</HintPath>
</Reference>
<Reference Include="ItemComponent.Types">
<HintPath>..\..\..\..\..\..\..\AKI PROJECT\AKI 2.3.0 17107 Bepin\EscapeFromTarkov_Data\Managed\ItemComponent.Types.dll</HintPath>
</Reference>
<Reference Include="Newtonsoft.Json">
<HintPath>..\..\..\..\..\..\..\AKI PROJECT\AKI 2.3.0 17107 Bepin\EscapeFromTarkov_Data\Managed\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="UnityEngine">
<HintPath>..\..\..\..\..\..\..\AKI PROJECT\AKI 2.3.0 17107 Bepin\EscapeFromTarkov_Data\Managed\UnityEngine.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.CoreModule">
<HintPath>..\..\..\..\..\..\..\AKI PROJECT\AKI 2.3.0 17107 Bepin\EscapeFromTarkov_Data\Managed\UnityEngine.CoreModule.dll</HintPath>
</Reference>
</ItemGroup>
</Project>

7
README.md Normal file
View File

@ -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