change so clientside is independant of server
@ -1,137 +0,0 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
}
|
@ -1,108 +0,0 @@
|
||||
import type { DependencyContainer } from "tsyringe";
|
||||
import { IPreAkiLoadMod } from "@spt-aki/models/external/IPreAkiLoadMod";
|
||||
import { IPostAkiLoadMod } from "@spt-aki/models/external/IPostAkiLoadMod";
|
||||
import { DynamicRouterModService } from "@spt-aki/services/mod/dynamicRouter/DynamicRouterModService"
|
||||
import { DatabaseServer } from "@spt-aki/servers/DatabaseServer"
|
||||
import { JsonUtil } from "@spt-aki/utils/JsonUtil"
|
||||
import { IDatabaseTables } from "@spt-aki/models/spt/server/IDatabaseTables";
|
||||
import { PreAkiModLoader } from "@spt-aki/loaders/PreAkiModLoader";
|
||||
|
||||
class MunitionsExpert implements IPreAkiLoadMod, IPostAkiLoadMod
|
||||
{
|
||||
private database: DatabaseServer;
|
||||
private router: DynamicRouterModService;
|
||||
private json: JsonUtil;
|
||||
private modLoader: PreAkiModLoader;
|
||||
private table: IDatabaseTables;
|
||||
private globalLocale: { [x: string]: { interface: { [x: string]: any; }; }; };
|
||||
private translations: { [x: string]: any; };
|
||||
private items: { [x: string]: any; };
|
||||
private path: { resolve: (arg0: string) => any; };
|
||||
private cfg: { BulletBackgroundColours: boolean; };
|
||||
|
||||
public preAkiLoad(container: DependencyContainer)
|
||||
{
|
||||
this.router = container.resolve<DynamicRouterModService>("DynamicRouterModService");
|
||||
this.json = container.resolve<JsonUtil>("JsonUtil");
|
||||
this.translations = require("../res/translations.json");
|
||||
this.path = require("path");
|
||||
this.cfg = require("./config.json");
|
||||
this.hookRoutes();
|
||||
}
|
||||
|
||||
public postAkiLoad(container: DependencyContainer)
|
||||
{
|
||||
this.modLoader = container.resolve<PreAkiModLoader>("PreAkiModLoader");
|
||||
this.database = container.resolve<DatabaseServer>("DatabaseServer");
|
||||
this.table = this.database.getTables();
|
||||
this.globalLocale = this.table.locales.global;
|
||||
this.items = this.table.templates.items;
|
||||
this.updateLocalization();
|
||||
this.changeBulletColour();
|
||||
}
|
||||
|
||||
private updateLocalization()
|
||||
{
|
||||
for (const language in this.translations)
|
||||
{
|
||||
if (!(language in this.globalLocale))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
const attrKvPair = this.translations[language];
|
||||
for (const attrKey in attrKvPair)
|
||||
{
|
||||
const attrValue = attrKvPair[attrKey];
|
||||
|
||||
this.globalLocale[language][attrKey] = attrValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private hookRoutes()
|
||||
{
|
||||
this.router.registerDynamicRouter(
|
||||
"MunitionsExpert",
|
||||
[
|
||||
{
|
||||
url: "/MunitionsExpert/GetInfo",
|
||||
action: (url, info, sessionId, output) =>
|
||||
{
|
||||
return this.json.serialize(this.path.resolve(this.modLoader.getModPath("Faupi-MunitionsExpert 1.6.9")));
|
||||
}
|
||||
}
|
||||
],
|
||||
"MunitionsExpert"
|
||||
)
|
||||
}
|
||||
|
||||
changeBulletColour()
|
||||
{
|
||||
if (this.cfg.BulletBackgroundColours === true)
|
||||
{
|
||||
for (const i in this.items)
|
||||
{
|
||||
const item = this.items[i]
|
||||
|
||||
//set baground colour of ammo depending on pen
|
||||
if (item._parent === "5485a8684bdc2da71d8b4567")
|
||||
{
|
||||
const pen = item._props.PenetrationPower
|
||||
let colour = ""
|
||||
|
||||
pen > 60 ? colour = "red" : //SuperHighPen
|
||||
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 = { mod: new MunitionsExpert() };
|
||||
|
Before Width: | Height: | Size: 701 B After Width: | Height: | Size: 701 B |
Before Width: | Height: | Size: 732 B After Width: | Height: | Size: 732 B |
@ -1,11 +1,11 @@
|
||||
{
|
||||
"name": "MunitionsExpert",
|
||||
"author": "Faupi",
|
||||
"version": "1.6.9",
|
||||
"updatedBy": "CWX",
|
||||
"version": "1.7.0",
|
||||
"license": "NCSA Open Source",
|
||||
"main": "./src/MunitionsExpert.js",
|
||||
"akiVersion": "3.5.0",
|
||||
"updatedBy": "CWX",
|
||||
"scripts": {
|
||||
"setup": "npm i",
|
||||
"build": "node ./packageBuild.ts"
|
@ -0,0 +1,58 @@
|
||||
import type { DependencyContainer } from "tsyringe";
|
||||
import { IPostAkiLoadMod } from "@spt-aki/models/external/IPostAkiLoadMod";
|
||||
import { DatabaseServer } from "@spt-aki/servers/DatabaseServer"
|
||||
import { ITemplateItem } from "@spt-aki/models/eft/common/tables/ITemplateItem";
|
||||
|
||||
class MunitionsExpert implements IPostAkiLoadMod
|
||||
{
|
||||
private database: DatabaseServer;
|
||||
private items: Record<string, ITemplateItem>;
|
||||
private cfg: { BulletBackgroundColours: boolean; } = require("./config.json");
|
||||
|
||||
public postAkiLoad(container: DependencyContainer)
|
||||
{
|
||||
this.database = container.resolve<DatabaseServer>("DatabaseServer");
|
||||
this.items = this.database.getTables().templates.items;
|
||||
this.changeBulletColour();
|
||||
}
|
||||
|
||||
private changeBulletColour()
|
||||
{
|
||||
if (this.cfg.BulletBackgroundColours)
|
||||
{
|
||||
for (const i in this.items)
|
||||
{
|
||||
//set background colour of ammo depending on pen
|
||||
if (this.items[i]._parent === "5485a8684bdc2da71d8b4567")
|
||||
{
|
||||
const pen = this.items[i]._props.PenetrationPower;
|
||||
|
||||
switch (true)
|
||||
{
|
||||
case (pen > 60):
|
||||
this.items[i]._props.BackgroundColor = "red";
|
||||
break;
|
||||
case (pen > 50):
|
||||
this.items[i]._props.BackgroundColor = "yellow";
|
||||
break;
|
||||
case (pen > 40):
|
||||
this.items[i]._props.BackgroundColor = "violet";
|
||||
break;
|
||||
case (pen > 30):
|
||||
this.items[i]._props.BackgroundColor = "blue";
|
||||
break;
|
||||
case (pen > 20):
|
||||
this.items[i]._props.BackgroundColor = "green";
|
||||
break;
|
||||
default:
|
||||
this.items[i]._props.BackgroundColor = "grey";
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { mod: new MunitionsExpert() };
|
||||
|
@ -1,26 +0,0 @@
|
||||
using Aki.Reflection.Patching;
|
||||
using EFT.InventoryLogic;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
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<ItemAttributeClass> __result)
|
||||
{
|
||||
if (!__result.Any((ItemAttributeClass a) => (Attributes.ENewItemAttributeId)a.Id == Attributes.ENewItemAttributeId.Damage))
|
||||
{
|
||||
//MunitionsExpert.FormatExistingAttributes(ref __result, __instance);
|
||||
Plugin.AddNewAttributes(ref __result, __instance);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,147 @@
|
||||
using BepInEx;
|
||||
using Comfort.Common;
|
||||
using EFT.InventoryLogic;
|
||||
using MunitionsExpert.Patches;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Networking;
|
||||
|
||||
namespace MunitionsExpert
|
||||
{
|
||||
[BepInPlugin("com.Faupi.MunitionsExpert", "Faupi-MunitionsExpert", "1.7.0")]
|
||||
public class MunitionsExpertPlugin : BaseUnityPlugin
|
||||
{
|
||||
public static Dictionary<Enum, Sprite> iconCache = new Dictionary<Enum, Sprite>();
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
CacheIcons();
|
||||
new CachedAttributesPatch().Enable();
|
||||
new StaticIconsPatch().Enable();
|
||||
}
|
||||
|
||||
public static void CacheIcons()
|
||||
{
|
||||
iconCache.Add(Attributes.ENewItemAttributeId.Damage, Resources.Load<Sprite>("characteristics/icons/icon_info_damage"));
|
||||
iconCache.Add(Attributes.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(Attributes.ENewItemAttributeId.Penetration, Resources.Load<Sprite>("characteristics/icon_info_penetration"));
|
||||
_ = LoadTexture(Attributes.ENewItemAttributeId.ArmorDamage, Path.Combine(Directory.GetCurrentDirectory(), "BepInEx/plugins/Faupi-MunitionsExpert/armorDamage.png"));
|
||||
_ = LoadTexture(Attributes.ENewItemAttributeId.RicochetChance, Path.Combine(Directory.GetCurrentDirectory(), "BepInEx/plugins/Faupi-MunitionsExpert/ricochet.png"));
|
||||
}
|
||||
|
||||
public static async Task LoadTexture(Enum id, string path)
|
||||
{
|
||||
var unityWebRequest = UnityWebRequestTexture.GetTexture(path);
|
||||
if (unityWebRequest != null)
|
||||
{
|
||||
unityWebRequest.SendWebRequest();
|
||||
|
||||
while (!unityWebRequest.isDone)
|
||||
{
|
||||
await Task.Delay(5);
|
||||
}
|
||||
|
||||
if (unityWebRequest.responseCode != 200)
|
||||
{
|
||||
Debug.LogError($"[MunitionsExpert]: Request Error - {unityWebRequest.responseCode}: {unityWebRequest.error}");
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError($"[MunitionsExpert]: Request Success - {id} from {path}");
|
||||
var cachedTexture = DownloadHandlerTexture.GetContent(unityWebRequest);
|
||||
iconCache.Add(id, Sprite.Create(cachedTexture, new Rect(0, 0, cachedTexture.width, cachedTexture.height), new Vector2(0, 0)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void AddNewAttributes(ref List<ItemAttributeClass> attributes, AmmoTemplate template)
|
||||
{
|
||||
var totalDamage = template.Damage * template.ProjectileCount;
|
||||
var damageString = totalDamage.ToString();
|
||||
|
||||
if (template.ProjectileCount > 1)
|
||||
{
|
||||
damageString += $" ({template.Damage} x {template.ProjectileCount})";
|
||||
}
|
||||
|
||||
var damageAttribute = new ItemAttributeClass(Attributes.ENewItemAttributeId.Damage)
|
||||
{
|
||||
Name = Attributes.ENewItemAttributeId.Damage.GetName(),
|
||||
Base = () => totalDamage,
|
||||
StringValue = () => damageString,
|
||||
DisplayType = () => EItemAttributeDisplayType.Compact
|
||||
};
|
||||
attributes.Add(damageAttribute);
|
||||
|
||||
if (template.ArmorDamage > 0)
|
||||
{
|
||||
var armorDamageAttribute = new ItemAttributeClass(Attributes.ENewItemAttributeId.ArmorDamage)
|
||||
{
|
||||
Name = Attributes.ENewItemAttributeId.ArmorDamage.GetName(),
|
||||
Base = () => template.ArmorDamage,
|
||||
StringValue = () => $"{template.ArmorDamage}%",
|
||||
DisplayType = () => EItemAttributeDisplayType.Compact
|
||||
};
|
||||
attributes.Add(armorDamageAttribute);
|
||||
}
|
||||
|
||||
if (template.PenetrationPower > 0)
|
||||
{
|
||||
string GetStringValue()
|
||||
{
|
||||
int ratedClass = 0;
|
||||
|
||||
if (!Singleton<BackendConfigSettingsClass>.Instantiated) { return $"[MunitionsExpert]: CLASS_DATA_MISSING {template.PenetrationPower}"; }
|
||||
var armorClasses = Singleton<BackendConfigSettingsClass>.Instance.Armor.ArmorClass;
|
||||
|
||||
for (var i = 0; i < armorClasses.Length; i++)
|
||||
{
|
||||
if (armorClasses[i].Resistance > template.PenetrationPower) continue;
|
||||
|
||||
ratedClass = Math.Max(ratedClass, i);
|
||||
}
|
||||
|
||||
return $"{(ratedClass > 0 ? $"Class {ratedClass}" : "Unarmored")} ({template.PenetrationPower})";
|
||||
}
|
||||
|
||||
ItemAttributeClass penetrationAttribute = new ItemAttributeClass(Attributes.ENewItemAttributeId.Penetration)
|
||||
{
|
||||
Name = Attributes.ENewItemAttributeId.Penetration.GetName(),
|
||||
Base = () => template.PenetrationPower,
|
||||
StringValue = GetStringValue,
|
||||
DisplayType = () => EItemAttributeDisplayType.Compact
|
||||
};
|
||||
attributes.Add(penetrationAttribute);
|
||||
}
|
||||
|
||||
if (template.FragmentationChance > 0)
|
||||
{
|
||||
ItemAttributeClass fragmentationAttribute = new ItemAttributeClass(Attributes.ENewItemAttributeId.FragmentationChance)
|
||||
{
|
||||
Name = Attributes.ENewItemAttributeId.FragmentationChance.GetName(),
|
||||
Base = () => template.FragmentationChance,
|
||||
StringValue = () => $"{template.FragmentationChance * 100}%",
|
||||
DisplayType = () => EItemAttributeDisplayType.Compact
|
||||
};
|
||||
attributes.Add(fragmentationAttribute);
|
||||
}
|
||||
|
||||
if (template.RicochetChance > 0)
|
||||
{
|
||||
ItemAttributeClass ricochetAttribute = new ItemAttributeClass(Attributes.ENewItemAttributeId.RicochetChance)
|
||||
{
|
||||
Name = Attributes.ENewItemAttributeId.RicochetChance.GetName(),
|
||||
Base = () => template.RicochetChance,
|
||||
StringValue = () => $"{template.RicochetChance * 100}%",
|
||||
DisplayType = () => EItemAttributeDisplayType.Compact
|
||||
};
|
||||
attributes.Add(ricochetAttribute);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
using Aki.Reflection.Patching;
|
||||
using EFT.InventoryLogic;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
namespace MunitionsExpert.Patches
|
||||
{
|
||||
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<ItemAttributeClass> __result)
|
||||
{
|
||||
if (__result.All(a => (Attributes.ENewItemAttributeId)a.Id != Attributes.ENewItemAttributeId.Damage))
|
||||
{
|
||||
MunitionsExpertPlugin.AddNewAttributes(ref __result, __instance);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using Aki.Reflection.Patching;
|
||||
using EFT.UI;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MunitionsExpert.Patches
|
||||
{
|
||||
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 || !MunitionsExpertPlugin.iconCache.ContainsKey(id))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
var sprite = MunitionsExpertPlugin.iconCache[id];
|
||||
|
||||
if (sprite != null)
|
||||
{
|
||||
__result = sprite;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,155 +0,0 @@
|
||||
using Aki.Common.Http;
|
||||
using Aki.Common.Utils;
|
||||
using BepInEx;
|
||||
using Comfort.Common;
|
||||
using EFT.InventoryLogic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Networking;
|
||||
using static MunitionsExpert.Attributes;
|
||||
|
||||
namespace MunitionsExpert
|
||||
{
|
||||
[BepInPlugin("com.Faupi.MunitionsExpert", "Faupi-MunitionsExpert", "1.6.9")]
|
||||
public class Plugin : BaseUnityPlugin
|
||||
{
|
||||
public static Dictionary<Enum, Sprite> iconCache = new Dictionary<Enum, Sprite>();
|
||||
public static List<ItemAttributeClass> penAttributes = new List<ItemAttributeClass>();
|
||||
public static string modPath;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
GetPath();
|
||||
new CachedAttributesPatch().Enable();
|
||||
new StaticIconsPatch().Enable();
|
||||
CacheIcons();
|
||||
}
|
||||
|
||||
private void GetPath()
|
||||
{
|
||||
var mod = RequestHandler.GetJson($"/MunitionsExpert/GetInfo");
|
||||
modPath = Json.Deserialize<string>(mod);
|
||||
}
|
||||
|
||||
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(modPath, "res\\armorDamage.png"));
|
||||
_ = LoadTexture(ENewItemAttributeId.RicochetChance, Path.Combine(modPath, "res\\armorDamage.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<ItemAttributeClass> 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)
|
||||
}
|
||||
|
||||
ItemAttributeClass at_damage = new ItemAttributeClass(ENewItemAttributeId.Damage)
|
||||
{
|
||||
Name = ENewItemAttributeId.Damage.GetName(),
|
||||
Base = () => totalDamage,
|
||||
StringValue = () => damageStr,
|
||||
DisplayType = () => EItemAttributeDisplayType.Compact
|
||||
};
|
||||
attributes.Add(at_damage);
|
||||
|
||||
if (template.ArmorDamage > 0)
|
||||
{
|
||||
ItemAttributeClass at_armordmg = new ItemAttributeClass(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<BackendConfigSettingsClass>.Instantiated) { return $"CLASS_DATA_MISSING {template.PenetrationPower.ToString()}"; }
|
||||
var classes = Singleton<BackendConfigSettingsClass>.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()})";
|
||||
}
|
||||
|
||||
ItemAttributeClass at_pen = new ItemAttributeClass(ENewItemAttributeId.Penetration)
|
||||
{
|
||||
Name = ENewItemAttributeId.Penetration.GetName(),
|
||||
Base = () => template.PenetrationPower,
|
||||
StringValue = getStringValue,
|
||||
DisplayType = () => EItemAttributeDisplayType.Compact
|
||||
};
|
||||
attributes.Add(at_pen);
|
||||
}
|
||||
|
||||
if (template.FragmentationChance > 0)
|
||||
{
|
||||
ItemAttributeClass at_frag = new ItemAttributeClass(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)
|
||||
{
|
||||
ItemAttributeClass at_ricochet = new ItemAttributeClass(ENewItemAttributeId.RicochetChance)
|
||||
{
|
||||
Name = ENewItemAttributeId.RicochetChance.GetName(),
|
||||
Base = () => template.RicochetChance,
|
||||
StringValue = () => $"{(template.RicochetChance * 100).ToString()}%",
|
||||
DisplayType = () => EItemAttributeDisplayType.Compact
|
||||
};
|
||||
attributes.Add(at_ricochet);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,35 +0,0 @@
|
||||
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 || !Plugin.iconCache.ContainsKey(id))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
Sprite sprite = Plugin.iconCache[id];
|
||||
|
||||
if (sprite != null)
|
||||
{
|
||||
__result = sprite;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,11 +1,11 @@
|
||||
{
|
||||
"name": "MunitionsExpert",
|
||||
"author": "Faupi",
|
||||
"version": "1.6.9",
|
||||
"updatedBy": "CWX",
|
||||
"version": "1.7.0",
|
||||
"license": "NCSA Open Source",
|
||||
"main": "./src/MunitionsExpert.js",
|
||||
"akiVersion": "3.5.0",
|
||||
"updatedBy": "CWX",
|
||||
"scripts": {
|
||||
"setup": "npm i",
|
||||
"build": "node ./packageBuild.ts"
|
||||
|
Before Width: | Height: | Size: 701 B |
BIN
Faupi-MunitionsExpert/server/dist/res/ricochet.png
vendored
Before Width: | Height: | Size: 732 B |
@ -1,137 +0,0 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
}
|
@ -1,103 +1,53 @@
|
||||
import type { DependencyContainer } from "tsyringe";
|
||||
import { IPreAkiLoadMod } from "@spt-aki/models/external/IPreAkiLoadMod";
|
||||
import { IPostAkiLoadMod } from "@spt-aki/models/external/IPostAkiLoadMod";
|
||||
import { DynamicRouterModService } from "@spt-aki/services/mod/dynamicRouter/DynamicRouterModService"
|
||||
import { DatabaseServer } from "@spt-aki/servers/DatabaseServer"
|
||||
import { JsonUtil } from "@spt-aki/utils/JsonUtil"
|
||||
import { IDatabaseTables } from "@spt-aki/models/spt/server/IDatabaseTables";
|
||||
import { PreAkiModLoader } from "@spt-aki/loaders/PreAkiModLoader";
|
||||
import { ITemplateItem } from "@spt-aki/models/eft/common/tables/ITemplateItem";
|
||||
|
||||
class MunitionsExpert implements IPreAkiLoadMod, IPostAkiLoadMod
|
||||
class MunitionsExpert implements IPostAkiLoadMod
|
||||
{
|
||||
private database: DatabaseServer;
|
||||
private router: DynamicRouterModService;
|
||||
private json: JsonUtil;
|
||||
private modLoader: PreAkiModLoader;
|
||||
private table: IDatabaseTables;
|
||||
private globalLocale: { [x: string]: { interface: { [x: string]: any; }; }; };
|
||||
private translations: { [x: string]: any; };
|
||||
private items: { [x: string]: any; };
|
||||
private path: { resolve: (arg0: string) => any; };
|
||||
private cfg: { BulletBackgroundColours: boolean; };
|
||||
|
||||
public preAkiLoad(container: DependencyContainer)
|
||||
{
|
||||
this.router = container.resolve<DynamicRouterModService>("DynamicRouterModService");
|
||||
this.json = container.resolve<JsonUtil>("JsonUtil");
|
||||
this.translations = require("../res/translations.json");
|
||||
this.path = require("path");
|
||||
this.cfg = require("./config.json");
|
||||
this.hookRoutes();
|
||||
}
|
||||
private items: Record<string, ITemplateItem>;
|
||||
private cfg: { BulletBackgroundColours: boolean; } = require("./config.json");
|
||||
|
||||
public postAkiLoad(container: DependencyContainer)
|
||||
{
|
||||
this.modLoader = container.resolve<PreAkiModLoader>("PreAkiModLoader");
|
||||
this.database = container.resolve<DatabaseServer>("DatabaseServer");
|
||||
this.table = this.database.getTables();
|
||||
this.globalLocale = this.table.locales.global;
|
||||
this.items = this.table.templates.items;
|
||||
this.updateLocalization();
|
||||
this.items = this.database.getTables().templates.items;
|
||||
this.changeBulletColour();
|
||||
}
|
||||
|
||||
private updateLocalization()
|
||||
private changeBulletColour()
|
||||
{
|
||||
for (const language in this.translations)
|
||||
{
|
||||
if (!(language in this.globalLocale))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
const attrKvPair = this.translations[language];
|
||||
for (const attrKey in attrKvPair)
|
||||
{
|
||||
const attrValue = attrKvPair[attrKey];
|
||||
|
||||
this.globalLocale[language][attrKey] = attrValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private hookRoutes()
|
||||
{
|
||||
this.router.registerDynamicRouter(
|
||||
"MunitionsExpert",
|
||||
[
|
||||
{
|
||||
url: "/MunitionsExpert/GetInfo",
|
||||
action: (url, info, sessionId, output) =>
|
||||
{
|
||||
return this.json.serialize(this.path.resolve(this.modLoader.getModPath("Faupi-MunitionsExpert 1.6.9")));
|
||||
}
|
||||
}
|
||||
],
|
||||
"MunitionsExpert"
|
||||
)
|
||||
}
|
||||
|
||||
changeBulletColour()
|
||||
{
|
||||
if (this.cfg.BulletBackgroundColours === true)
|
||||
if (this.cfg.BulletBackgroundColours)
|
||||
{
|
||||
for (const i in this.items)
|
||||
{
|
||||
const item = this.items[i]
|
||||
|
||||
//set baground colour of ammo depending on pen
|
||||
if (item._parent === "5485a8684bdc2da71d8b4567")
|
||||
//set background colour of ammo depending on pen
|
||||
if (this.items[i]._parent === "5485a8684bdc2da71d8b4567")
|
||||
{
|
||||
const pen = item._props.PenetrationPower
|
||||
let colour = ""
|
||||
const pen = this.items[i]._props.PenetrationPower;
|
||||
|
||||
pen > 60 ? colour = "red" : //SuperHighPen
|
||||
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
|
||||
switch (true)
|
||||
{
|
||||
case (pen > 60):
|
||||
this.items[i]._props.BackgroundColor = "red";
|
||||
break;
|
||||
case (pen > 50):
|
||||
this.items[i]._props.BackgroundColor = "yellow";
|
||||
break;
|
||||
case (pen > 40):
|
||||
this.items[i]._props.BackgroundColor = "violet";
|
||||
break;
|
||||
case (pen > 30):
|
||||
this.items[i]._props.BackgroundColor = "blue";
|
||||
break;
|
||||
case (pen > 20):
|
||||
this.items[i]._props.BackgroundColor = "green";
|
||||
break;
|
||||
default:
|
||||
this.items[i]._props.BackgroundColor = "grey";
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +1,11 @@
|
||||
{
|
||||
"name": "MunitionsExpert",
|
||||
"author": "Faupi",
|
||||
"version": "1.6.9",
|
||||
"updatedBy": "CWX",
|
||||
"version": "1.7.0",
|
||||
"license": "NCSA Open Source",
|
||||
"main": "./src/MunitionsExpert.js",
|
||||
"akiVersion": "3.5.0",
|
||||
"updatedBy": "CWX",
|
||||
"scripts": {
|
||||
"setup": "npm i",
|
||||
"build": "node ./packageBuild.ts"
|
||||
|
Before Width: | Height: | Size: 701 B |
Before Width: | Height: | Size: 732 B |
@ -1,137 +0,0 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
}
|
@ -1,103 +1,53 @@
|
||||
import type { DependencyContainer } from "tsyringe";
|
||||
import { IPreAkiLoadMod } from "@spt-aki/models/external/IPreAkiLoadMod";
|
||||
import { IPostAkiLoadMod } from "@spt-aki/models/external/IPostAkiLoadMod";
|
||||
import { DynamicRouterModService } from "@spt-aki/services/mod/dynamicRouter/DynamicRouterModService"
|
||||
import { DatabaseServer } from "@spt-aki/servers/DatabaseServer"
|
||||
import { JsonUtil } from "@spt-aki/utils/JsonUtil"
|
||||
import { IDatabaseTables } from "@spt-aki/models/spt/server/IDatabaseTables";
|
||||
import { PreAkiModLoader } from "@spt-aki/loaders/PreAkiModLoader";
|
||||
import { ITemplateItem } from "@spt-aki/models/eft/common/tables/ITemplateItem";
|
||||
|
||||
class MunitionsExpert implements IPreAkiLoadMod, IPostAkiLoadMod
|
||||
class MunitionsExpert implements IPostAkiLoadMod
|
||||
{
|
||||
private database: DatabaseServer;
|
||||
private router: DynamicRouterModService;
|
||||
private json: JsonUtil;
|
||||
private modLoader: PreAkiModLoader;
|
||||
private table: IDatabaseTables;
|
||||
private globalLocale: { [x: string]: { interface: { [x: string]: any; }; }; };
|
||||
private translations: { [x: string]: any; };
|
||||
private items: { [x: string]: any; };
|
||||
private path: { resolve: (arg0: string) => any; };
|
||||
private cfg: { BulletBackgroundColours: boolean; };
|
||||
|
||||
public preAkiLoad(container: DependencyContainer)
|
||||
{
|
||||
this.router = container.resolve<DynamicRouterModService>("DynamicRouterModService");
|
||||
this.json = container.resolve<JsonUtil>("JsonUtil");
|
||||
this.translations = require("../res/translations.json");
|
||||
this.path = require("path");
|
||||
this.cfg = require("./config.json");
|
||||
this.hookRoutes();
|
||||
}
|
||||
private items: Record<string, ITemplateItem>;
|
||||
private cfg: { BulletBackgroundColours: boolean; } = require("./config.json");
|
||||
|
||||
public postAkiLoad(container: DependencyContainer)
|
||||
{
|
||||
this.modLoader = container.resolve<PreAkiModLoader>("PreAkiModLoader");
|
||||
this.database = container.resolve<DatabaseServer>("DatabaseServer");
|
||||
this.table = this.database.getTables();
|
||||
this.globalLocale = this.table.locales.global;
|
||||
this.items = this.table.templates.items;
|
||||
this.updateLocalization();
|
||||
this.items = this.database.getTables().templates.items;
|
||||
this.changeBulletColour();
|
||||
}
|
||||
|
||||
private updateLocalization()
|
||||
private changeBulletColour()
|
||||
{
|
||||
for (const language in this.translations)
|
||||
{
|
||||
if (!(language in this.globalLocale))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
const attrKvPair = this.translations[language];
|
||||
for (const attrKey in attrKvPair)
|
||||
{
|
||||
const attrValue = attrKvPair[attrKey];
|
||||
|
||||
this.globalLocale[language][attrKey] = attrValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private hookRoutes()
|
||||
{
|
||||
this.router.registerDynamicRouter(
|
||||
"MunitionsExpert",
|
||||
[
|
||||
{
|
||||
url: "/MunitionsExpert/GetInfo",
|
||||
action: (url, info, sessionId, output) =>
|
||||
{
|
||||
return this.json.serialize(this.path.resolve(this.modLoader.getModPath("Faupi-MunitionsExpert 1.6.9")));
|
||||
}
|
||||
}
|
||||
],
|
||||
"MunitionsExpert"
|
||||
)
|
||||
}
|
||||
|
||||
changeBulletColour()
|
||||
{
|
||||
if (this.cfg.BulletBackgroundColours === true)
|
||||
if (this.cfg.BulletBackgroundColours)
|
||||
{
|
||||
for (const i in this.items)
|
||||
{
|
||||
const item = this.items[i]
|
||||
|
||||
//set baground colour of ammo depending on pen
|
||||
if (item._parent === "5485a8684bdc2da71d8b4567")
|
||||
//set background colour of ammo depending on pen
|
||||
if (this.items[i]._parent === "5485a8684bdc2da71d8b4567")
|
||||
{
|
||||
const pen = item._props.PenetrationPower
|
||||
let colour = ""
|
||||
const pen = this.items[i]._props.PenetrationPower;
|
||||
|
||||
pen > 60 ? colour = "red" : //SuperHighPen
|
||||
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
|
||||
switch (true)
|
||||
{
|
||||
case (pen > 60):
|
||||
this.items[i]._props.BackgroundColor = "red";
|
||||
break;
|
||||
case (pen > 50):
|
||||
this.items[i]._props.BackgroundColor = "yellow";
|
||||
break;
|
||||
case (pen > 40):
|
||||
this.items[i]._props.BackgroundColor = "violet";
|
||||
break;
|
||||
case (pen > 30):
|
||||
this.items[i]._props.BackgroundColor = "blue";
|
||||
break;
|
||||
case (pen > 20):
|
||||
this.items[i]._props.BackgroundColor = "green";
|
||||
break;
|
||||
default:
|
||||
this.items[i]._props.BackgroundColor = "grey";
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|