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",
|
"name": "MunitionsExpert",
|
||||||
"author": "Faupi",
|
"author": "Faupi",
|
||||||
"version": "1.6.9",
|
"updatedBy": "CWX",
|
||||||
|
"version": "1.7.0",
|
||||||
"license": "NCSA Open Source",
|
"license": "NCSA Open Source",
|
||||||
"main": "./src/MunitionsExpert.js",
|
"main": "./src/MunitionsExpert.js",
|
||||||
"akiVersion": "3.5.0",
|
"akiVersion": "3.5.0",
|
||||||
"updatedBy": "CWX",
|
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"setup": "npm i",
|
"setup": "npm i",
|
||||||
"build": "node ./packageBuild.ts"
|
"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",
|
"name": "MunitionsExpert",
|
||||||
"author": "Faupi",
|
"author": "Faupi",
|
||||||
"version": "1.6.9",
|
"updatedBy": "CWX",
|
||||||
|
"version": "1.7.0",
|
||||||
"license": "NCSA Open Source",
|
"license": "NCSA Open Source",
|
||||||
"main": "./src/MunitionsExpert.js",
|
"main": "./src/MunitionsExpert.js",
|
||||||
"akiVersion": "3.5.0",
|
"akiVersion": "3.5.0",
|
||||||
"updatedBy": "CWX",
|
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"setup": "npm i",
|
"setup": "npm i",
|
||||||
"build": "node ./packageBuild.ts"
|
"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 type { DependencyContainer } from "tsyringe";
|
||||||
import { IPreAkiLoadMod } from "@spt-aki/models/external/IPreAkiLoadMod";
|
|
||||||
import { IPostAkiLoadMod } from "@spt-aki/models/external/IPostAkiLoadMod";
|
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 { DatabaseServer } from "@spt-aki/servers/DatabaseServer"
|
||||||
import { JsonUtil } from "@spt-aki/utils/JsonUtil"
|
import { ITemplateItem } from "@spt-aki/models/eft/common/tables/ITemplateItem";
|
||||||
import { IDatabaseTables } from "@spt-aki/models/spt/server/IDatabaseTables";
|
|
||||||
import { PreAkiModLoader } from "@spt-aki/loaders/PreAkiModLoader";
|
|
||||||
|
|
||||||
class MunitionsExpert implements IPreAkiLoadMod, IPostAkiLoadMod
|
class MunitionsExpert implements IPostAkiLoadMod
|
||||||
{
|
{
|
||||||
private database: DatabaseServer;
|
private database: DatabaseServer;
|
||||||
private router: DynamicRouterModService;
|
private items: Record<string, ITemplateItem>;
|
||||||
private json: JsonUtil;
|
private cfg: { BulletBackgroundColours: boolean; } = require("./config.json");
|
||||||
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)
|
public postAkiLoad(container: DependencyContainer)
|
||||||
{
|
{
|
||||||
this.modLoader = container.resolve<PreAkiModLoader>("PreAkiModLoader");
|
|
||||||
this.database = container.resolve<DatabaseServer>("DatabaseServer");
|
this.database = container.resolve<DatabaseServer>("DatabaseServer");
|
||||||
this.table = this.database.getTables();
|
this.items = this.database.getTables().templates.items;
|
||||||
this.globalLocale = this.table.locales.global;
|
|
||||||
this.items = this.table.templates.items;
|
|
||||||
this.updateLocalization();
|
|
||||||
this.changeBulletColour();
|
this.changeBulletColour();
|
||||||
}
|
}
|
||||||
|
|
||||||
private updateLocalization()
|
private changeBulletColour()
|
||||||
{
|
{
|
||||||
for (const language in this.translations)
|
if (this.cfg.BulletBackgroundColours)
|
||||||
{
|
|
||||||
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)
|
for (const i in this.items)
|
||||||
{
|
{
|
||||||
const item = this.items[i]
|
//set background colour of ammo depending on pen
|
||||||
|
if (this.items[i]._parent === "5485a8684bdc2da71d8b4567")
|
||||||
//set baground colour of ammo depending on pen
|
|
||||||
if (item._parent === "5485a8684bdc2da71d8b4567")
|
|
||||||
{
|
{
|
||||||
const pen = item._props.PenetrationPower
|
const pen = this.items[i]._props.PenetrationPower;
|
||||||
let colour = ""
|
|
||||||
|
switch (true)
|
||||||
pen > 60 ? colour = "red" : //SuperHighPen
|
{
|
||||||
pen > 50 ? colour = "yellow" : //HighPen
|
case (pen > 60):
|
||||||
pen > 40 ? colour = "violet" : //MedHighPen
|
this.items[i]._props.BackgroundColor = "red";
|
||||||
pen > 30 ? colour = "blue" : //MedPen
|
break;
|
||||||
pen > 20 ? colour = "green" : //LowMedPen
|
case (pen > 50):
|
||||||
colour = "grey" //LowPen
|
this.items[i]._props.BackgroundColor = "yellow";
|
||||||
item._props.BackgroundColor = colour
|
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",
|
"name": "MunitionsExpert",
|
||||||
"author": "Faupi",
|
"author": "Faupi",
|
||||||
"version": "1.6.9",
|
"updatedBy": "CWX",
|
||||||
|
"version": "1.7.0",
|
||||||
"license": "NCSA Open Source",
|
"license": "NCSA Open Source",
|
||||||
"main": "./src/MunitionsExpert.js",
|
"main": "./src/MunitionsExpert.js",
|
||||||
"akiVersion": "3.5.0",
|
"akiVersion": "3.5.0",
|
||||||
"updatedBy": "CWX",
|
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"setup": "npm i",
|
"setup": "npm i",
|
||||||
"build": "node ./packageBuild.ts"
|
"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 type { DependencyContainer } from "tsyringe";
|
||||||
import { IPreAkiLoadMod } from "@spt-aki/models/external/IPreAkiLoadMod";
|
|
||||||
import { IPostAkiLoadMod } from "@spt-aki/models/external/IPostAkiLoadMod";
|
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 { DatabaseServer } from "@spt-aki/servers/DatabaseServer"
|
||||||
import { JsonUtil } from "@spt-aki/utils/JsonUtil"
|
import { ITemplateItem } from "@spt-aki/models/eft/common/tables/ITemplateItem";
|
||||||
import { IDatabaseTables } from "@spt-aki/models/spt/server/IDatabaseTables";
|
|
||||||
import { PreAkiModLoader } from "@spt-aki/loaders/PreAkiModLoader";
|
|
||||||
|
|
||||||
class MunitionsExpert implements IPreAkiLoadMod, IPostAkiLoadMod
|
class MunitionsExpert implements IPostAkiLoadMod
|
||||||
{
|
{
|
||||||
private database: DatabaseServer;
|
private database: DatabaseServer;
|
||||||
private router: DynamicRouterModService;
|
private items: Record<string, ITemplateItem>;
|
||||||
private json: JsonUtil;
|
private cfg: { BulletBackgroundColours: boolean; } = require("./config.json");
|
||||||
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)
|
public postAkiLoad(container: DependencyContainer)
|
||||||
{
|
{
|
||||||
this.modLoader = container.resolve<PreAkiModLoader>("PreAkiModLoader");
|
|
||||||
this.database = container.resolve<DatabaseServer>("DatabaseServer");
|
this.database = container.resolve<DatabaseServer>("DatabaseServer");
|
||||||
this.table = this.database.getTables();
|
this.items = this.database.getTables().templates.items;
|
||||||
this.globalLocale = this.table.locales.global;
|
|
||||||
this.items = this.table.templates.items;
|
|
||||||
this.updateLocalization();
|
|
||||||
this.changeBulletColour();
|
this.changeBulletColour();
|
||||||
}
|
}
|
||||||
|
|
||||||
private updateLocalization()
|
private changeBulletColour()
|
||||||
{
|
{
|
||||||
for (const language in this.translations)
|
if (this.cfg.BulletBackgroundColours)
|
||||||
{
|
|
||||||
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)
|
for (const i in this.items)
|
||||||
{
|
{
|
||||||
const item = this.items[i]
|
//set background colour of ammo depending on pen
|
||||||
|
if (this.items[i]._parent === "5485a8684bdc2da71d8b4567")
|
||||||
//set baground colour of ammo depending on pen
|
|
||||||
if (item._parent === "5485a8684bdc2da71d8b4567")
|
|
||||||
{
|
{
|
||||||
const pen = item._props.PenetrationPower
|
const pen = this.items[i]._props.PenetrationPower;
|
||||||
let colour = ""
|
|
||||||
|
switch (true)
|
||||||
pen > 60 ? colour = "red" : //SuperHighPen
|
{
|
||||||
pen > 50 ? colour = "yellow" : //HighPen
|
case (pen > 60):
|
||||||
pen > 40 ? colour = "violet" : //MedHighPen
|
this.items[i]._props.BackgroundColor = "red";
|
||||||
pen > 30 ? colour = "blue" : //MedPen
|
break;
|
||||||
pen > 20 ? colour = "green" : //LowMedPen
|
case (pen > 50):
|
||||||
colour = "grey" //LowPen
|
this.items[i]._props.BackgroundColor = "yellow";
|
||||||
item._props.BackgroundColor = colour
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|