nyx

The first CODM discrod bot -- cath.exe Template
git clone https://codeberg.org/night0721/nyx
Log | Files | Refs | LICENSE

common.js (27911B)


      1 const data = require("../Data/data.json");
      2 const QuickChart = require("quickchart-js");
      3 const nmDt = require("../Data/aliases.json");
      4 const weaponActualName = nmDt.weaponActualName;
      5 const weaponAlliasName = nmDt.weaponAlliasName;
      6 Object.defineProperty(String.prototype, "Simplify", {
      7   // Function to remove all characters except 0-9 and a-z
      8   // Eg "AK-47" -> "ak47"
      9   value: function Simplify() {
     10     return this.toLowerCase().replace(/[^0-9a-z]/g, "");
     11   },
     12   writable: true,
     13   configurable: true,
     14 });
     15 
     16 Object.defineProperty(Number.prototype, "IsPositive", {
     17   // Function to check the number is positive or not
     18   value: function IsPositive() {
     19     if (this > 0) return true;
     20     else return false;
     21   },
     22   writable: true,
     23   configurable: true,
     24 });
     25 
     26 Object.defineProperty(Number.prototype, "IsNegative", {
     27   // Function to check the number is negative or not
     28   value: function IsNegative() {
     29     if (this < 0) return true;
     30     else return false;
     31   },
     32   writable: true,
     33   configurable: true,
     34 });
     35 
     36 Object.defineProperty(Number.prototype, "ToBool", {
     37   // Function to check the number is one or not
     38   value: function ToBool() {
     39     if (this == 1) return true;
     40     else return false;
     41   },
     42   writable: true,
     43   configurable: true,
     44 });
     45 
     46 Object.defineProperty(Number.prototype, "PlusHL", {
     47   value: function PlusHL() {
     48     if (this.toString()[0] == "-") {
     49       return parseFloat(this.toFixed(2)).toString();
     50     }
     51     return `+${parseFloat(this.toFixed(2)).toString()}`;
     52   },
     53   writable: true,
     54   configurable: true,
     55 });
     56 
     57 /* Function to fix the input statement */
     58 function inpFixer(inpmsg) {
     59   const parts = PartSpliter(inpmsg);
     60   // parts will be an array
     61   //eg: ["fennec", "akimbo, mono"]
     62   nmDt.attachmentAlliasName[0].map((x, i) =>
     63     // x is the content of each index, i is the number of each index
     64     x.map(y => {
     65       if (parts[0].startsWith(y + " ") || parts[0].endsWith(" " + y)) {
     66         inpmsg =
     67           parts[0].replace(y + " ", "").replace(" " + y, "") +
     68           (parts[1] ? ", " : " + ") +
     69           nmDt.attachmentActualName[0][i];
     70       }
     71     })
     72   );
     73   // so it fking only fix akimbo and stopping power wtf
     74   return inpmsg;
     75 }
     76 // Function to split weapon name and the attachments from the input statement
     77 function PartSpliter(inpmsg) {
     78   if (inpmsg.includes(" + ")) {
     79     // If the input statement has multiple attachments joined by "+", split them and output them as an array of strings [0] is the weapon name, [1] is the attachments
     80     // Eg: "M4A1 + Silencer + Flashlight" -> ["M4A1", "Silencer + Flashlight"]
     81     const out = inpmsg
     82       .split(" + ")
     83       .map(x => x.split("+"))
     84       .flat();
     85     return [out.shift(), out.join(", ")];
     86   }
     87   // If there is only one attachment, output it as an array of strings [0] is the weapon name, [1] is the attachment
     88   // Eg: "M4A1 with Flashlight" -> ["M4A1", "Flashlight"]
     89   return inpmsg.split(" with ");
     90 }
     91 
     92 function hasAttachments(inpmsg) {
     93   inpmsg = inpFixer(inpmsg);
     94   // If the input statement has multiple attachments joined by "+" or "with", split them and output them as an array of strings [0] is the weapon name, [1] is the attachments
     95   if (
     96     inpmsg.split(" with ").filter(x => x.Simplify()).length > 1 ||
     97     inpmsg.split(" + ").filter(x => x.Simplify()).length > 1
     98   ) {
     99     return true;
    100   }
    101   return false;
    102 }
    103 
    104 function isolator(inpmsg) {
    105   return PartSpliter(inpFixer(inpmsg));
    106 }
    107 // identifying the weapon
    108 function weaponIdentifier(inpmsg) {
    109   const inpWeaponName = isolator(inpmsg)[0];
    110   // ["ak", "mono"] -> inpWeaponName: "ak"
    111   // if weapon name is too short, return the error
    112   if (inpWeaponName.length < 2) {
    113     return inpmsg.trim().length
    114       ? `The name ${inpmsg.trim()} is too short.`
    115       : "There isn't any weapon name.";
    116   }
    117   let probableWeapons = [];
    118   // Loop through all the weapons to find the probable weapons
    119   // Eg: "ak"
    120   for (let i = 0; i < data.cguns.length; i++) {
    121     if (inpWeaponName.Simplify() == data.cguns[i].gunname.Simplify()) {
    122       // if the simplified name of the weapon is the same as the weapon name in the database, return the only one stats object
    123       return JSON.parse(JSON.stringify(data.cguns[i]));
    124     } else if (
    125       data.cguns[i].gunname.Simplify().includes(inpWeaponName.Simplify())
    126     ) {
    127       // If the weapon name is included in the actual name of the weapon
    128       // push the weapon to the probableWeapons array
    129       probableWeapons.push(i);
    130     }
    131   }
    132   // if there is only one probable weapon, mean the gun has already been identified
    133   if (probableWeapons.length == 1) {
    134     // if there is only one probable weapon, return the only one stats object
    135     return JSON.parse(JSON.stringify(data.cguns[probableWeapons[0]]));
    136   }
    137   // continue loop when there is no identified weapons or there are more than one identfied weaponds
    138   // detecting aliases
    139   // getting total number of weapons that had added aliases
    140   for (let i = 0; i < weaponAlliasName.length; i++) {
    141     // getting the number of aliases of each weapon
    142     for (let j = 0; j < weaponAlliasName[i].length; j++) {
    143       // weaponAliases[i][j] is the each alias of each weapon
    144       // finding if simplified alias is same as input weapon name
    145       if (weaponAlliasName[i][j].Simplify() == inpWeaponName.Simplify()) {
    146         // if simplified alias is same as input weapon name
    147         // eg "mow" == "mow", run the loop
    148         for (let i2 = 0; i2 < data.cguns.length; i2++) {
    149           if (weaponActualName[i] == data.cguns[i2].gunname) {
    150             // use the actual name of the weapon to find the weapon
    151             return JSON.parse(JSON.stringify(data.cguns[i2]));
    152           }
    153         }
    154       }
    155     }
    156   }
    157   // removing duplicates in the array
    158   probableWeapons = [...new Set(probableWeapons)];
    159   // if there is only one probable weapon, return the only one stats object
    160   if (probableWeapons.length == 1)
    161     return JSON.parse(JSON.stringify(data.cguns[probableWeapons[0]]));
    162   else if (probableWeapons.length > 1) {
    163     // reply with the question of probable weapons
    164     return `Did you mean ${probableWeapons
    165       .map(x => data.cguns[x].gunname)
    166       .reduce((out, x, i) =>
    167         [out, x].join(i === probableWeapons.length - 1 ? "` or `" : "`, `")
    168       )}
    169       ?`;
    170   } else return `Couldn't identify the weapon: "${inpWeaponName}"`;
    171 }
    172 // identifying attachments and return array or error
    173 function attachmentsIdentifier(inpmsg, gun) {
    174   if (!hasAttachments(inpmsg)) return [];
    175   // no need for isolator because using slash commands, we get individual attachment
    176   let inputAttachmentsNames = isolator(inpmsg)[1]
    177     .split(/ & |, |,| and /)
    178     .filter(x => x);
    179 
    180   const tooSmall = inputAttachmentsNames.filter(x => x.length < 3);
    181   // filter all elements thats shorter than 2 characters
    182   inputAttachmentsNames = inputAttachmentsNames.filter(x => !(x.length < 3));
    183   let errorMsgs = "",
    184     errors = [],
    185     unidentifined = [];
    186 
    187   if (inputAttachmentsNames.length == 0)
    188     errorMsgs += "\nAttachments are missing!\n";
    189   // if (inputAttachmentsNames.length >= 10) return "Cocaineeeeee"; ?????????
    190 
    191   // Can directly use args[] to return, no need for isolator, partExtractor, inpFixer
    192   const splitAttachmentsDataName = [],
    193     outAttachments = [];
    194 
    195   for (let i = 0; i < gun.aments.length; i++) {
    196     // Eg: "Stippled Grip Tape" -> ["Stippled", "Grip", "Tape"]
    197     splitAttachmentsDataName.push([
    198       ...new Set(
    199         gun.aments[i].name
    200           .split(" ")
    201           .filter(x => x)
    202           .map(x => x.trim())
    203       ),
    204     ]);
    205 
    206     // splitAttachmentsDataName[i] = ["Stippled", "Grip", "Tape"]
    207     for (let j = 0; j < splitAttachmentsDataName[i].length; j++) {
    208       // simplify the attachments name
    209       // Eg: ["Stippled", "Grip", "Tape"] -> ["stippled", "grip", "tape"]
    210       splitAttachmentsDataName[i][j] =
    211         splitAttachmentsDataName[i][j].Simplify();
    212     }
    213   }
    214   // inputAttachmentsNames = [["stippled", "grip", "tape"]
    215   for (let i = 0; i < inputAttachmentsNames.length; i++) {
    216     let probables = [];
    217     // loop through all the input attachments and split them into words
    218     var splitInputAttachmentsName = inputAttachmentsNames[i]
    219       .split(" ")
    220       .filter(x => x);
    221 
    222     function finder() {
    223       //splitInputAttachmentsName = [["stippled", "grip", "tape"], ["545", "ammo"], ["owc","lazer", "tactical"]]
    224       for (let j = 0; j < splitAttachmentsDataName.length; j++) {
    225         for (let i2 = 0; i2 < splitAttachmentsDataName[j].length; i2++) {
    226           for (let i3 = 0; i3 < splitInputAttachmentsName.length; i3++) {
    227             // if simplified input attachment name is included in the real attachments name
    228             if (
    229               splitAttachmentsDataName[j][i2].includes(
    230                 splitInputAttachmentsName[i3].Simplify()
    231               )
    232             ) {
    233               // if probables list doesn't include the attachment, push
    234               let probablePushed = false;
    235               for (let i4 = 0; i4 < probables.length; i4++) {
    236                 // push another attachment that also probable to the probables list to the same array that identified last loop
    237                 // Eg: probables = [ [32]] // as user input mag and first loop it identfified extended mag
    238                 // then as it got more possible, it will push large extended mag to the same array -> [ [32,33] ]
    239                 if (!probables[i4].includes(j)) {
    240                   probables[i4].push(j);
    241                   // make it true so that it doesn't push again in the next condition
    242                   probablePushed = true;
    243                   break;
    244                 }
    245               }
    246               // push if the attachment isn't been identified yet
    247               if (!probablePushed) probables.push([j]);
    248             }
    249           }
    250         }
    251       }
    252     }
    253     finder();
    254     // finding magazines attachments
    255     if (
    256       (inputAttachmentsNames[i].includes(" rounds mag") ||
    257         inputAttachmentsNames[i].includes(" round mag") ||
    258         inputAttachmentsNames[i].includes(" round") ||
    259         inputAttachmentsNames[i].includes(" rounds")) &&
    260       inputAttachmentsNames[i].startsWith(
    261         inputAttachmentsNames[i].replace(/\D/g, "")
    262       )
    263     ) {
    264       var tmp1 = parseInt(inputAttachmentsNames[i]);
    265       // calculating the sum of number of rounds and see if it matches the input number of rounds
    266       const tmp2 = gun.aments.filter(
    267         x =>
    268           x.type === 8 && x.effects[27] + x.effects[28] + gun.stats[17] === tmp1
    269       );
    270       // push if the magazine is found
    271       if (tmp2.length === 1) {
    272         outAttachments.push(tmp2[0]);
    273         continue;
    274       }
    275     }
    276     // if probables is empty or there is more than one identified attachment
    277     if (
    278       probables.length === 0 ||
    279       probables[probables.length - 1].length !== 1 ||
    280       probables.length < splitInputAttachmentsName.length
    281     ) {
    282       // empty probables as can't indentify the attachment
    283       probables = [];
    284       // the splitInputAttachmentsName isn't simplified pls rmb
    285       splitInputAttachmentsName.map((x, i5) => {
    286         // finding aliases
    287         nmDt.attachmentAlliasName[1].map((y, i6) => {
    288           y.map(z => {
    289             if (z.Simplify().includes(x.Simplify())) {
    290               splitInputAttachmentsName[i5] = nmDt.attachmentActualName[1][i6];
    291             }
    292           });
    293         });
    294       });
    295       // simple iteration to make the array again
    296       splitInputAttachmentsName = splitInputAttachmentsName
    297         .join(" ")
    298         .split(" ")
    299         .filter(x => x);
    300       // find one more time as we do aliases already
    301       finder();
    302       if (
    303         probables.length === 0 ||
    304         probables[probables.length - 1].length !== 1 ||
    305         probables.length < splitInputAttachmentsName.length
    306       ) {
    307         probables = [];
    308         splitInputAttachmentsName = inputAttachmentsNames[i]
    309           .split(" ")
    310           .filter(x => x);
    311         finder();
    312       }
    313     }
    314     if (probables.length === 0) {
    315       // push to unidentifined list as can't be identified after serveral times
    316       unidentifined.push(inputAttachmentsNames[i]);
    317       continue;
    318     }
    319     // curr is the most probable attachment
    320     var curr = probables[probables.length - 1];
    321     const temp1 = probables[probables.length - 1].filter(
    322       x => gun.aments[x].name.Simplify() == inputAttachmentsNames[i].Simplify()
    323     );
    324     // see if the length of the array is the same or not
    325     // Eg: splitAttachmentsDataName[x] = ["stippled", "grip", "tape"] and splitInputAttachmentsName = ["stippled", "grip", "tape"]
    326     // then it it equal
    327     const temp2 = probables[probables.length - 1].filter(
    328       x =>
    329         splitAttachmentsDataName[x].length == splitInputAttachmentsName.length
    330     );
    331 
    332     // if found probable, push it
    333     if (temp1.length === 1 && temp2.length !== 1) {
    334       probables.push([temp1]);
    335     } else if (temp1.length !== 1 && temp2.length === 1) {
    336       probables.push([temp2]);
    337     } else if (
    338       temp1.length === 1 &&
    339       temp2.length === 1 &&
    340       temp1[0] == temp2[0]
    341     ) {
    342       probables.push([temp1]);
    343     }
    344     if (
    345       probables[probables.length - 1].length != 1 ||
    346       probables.length < splitInputAttachmentsName.length
    347     ) {
    348       // ask the user if he means xxx = which attachment
    349       errors.push(
    350         "`" +
    351           curr
    352             .map(x => gun.aments[x].name)
    353             .reduce((out, x, i) =>
    354               [out, x].join(i === curr.length - 1 ? "` or `" : "`, `")
    355             ) +
    356           '` by `"' +
    357           inputAttachmentsNames[i] +
    358           '"`'
    359       );
    360     }
    361     // push the attachment to the output list
    362     outAttachments.push(gun.aments[probables[probables.length - 1][0]]);
    363   }
    364 
    365   const outAttachmentsTypes = outAttachments.map(x => x.type - 1),
    366     t1 = outAttachments
    367       .map(x => x.effects[35])
    368       .reduce((t, x) => t + x, 0)
    369       .toString()
    370       .padStart(11, "0")
    371       .toString()
    372       .split("")
    373       .map((x, i) =>
    374         parseInt(x) !== 0 && outAttachmentsTypes.includes(i) ? parseInt(i) : -1
    375       )
    376       .filter(x => x !== -1);
    377 
    378   errorMsgs += t1.length
    379     ? "Can't equip `" +
    380       t1
    381         .map(x => data.attachmentTypes[x])
    382         .reduce((out, x, i, a) =>
    383           [out, x].join(i === a.length - 1 ? "` or `" : "`, `")
    384         ) +
    385       "` with " +
    386       outAttachments
    387         .filter(x => x.effects[35])
    388         .map(x => x.name)
    389         .reduce((out, x, i, a) =>
    390           [out, x].join(i === a.length - 1 ? " and " : ", ")
    391         )
    392     : "";
    393   errorMsgs += errors.length ? `\nDid you mean ${errors.join(";\n")}?\n` : "";
    394   errorMsgs += unidentifined.length
    395     ? `\nCouldn't identify the attachment(${
    396         unidentifined.length === 1 ? "" : "s"
    397       }): \`"${unidentifined.join('"`, `"')}"\`\n`
    398     : "";
    399   errorMsgs +=
    400     outAttachments.length > 5 ? "\nCan't equip more than 5 attachments!\n" : "";
    401   errorMsgs += outAttachments.filter((x, i, a) => a.indexOf(x) !== i).length
    402     ? "\nMultiple of same attachments found!\n"
    403     : "";
    404   errorMsgs += outAttachments
    405     .map(x => x.type)
    406     .filter((x, i, a) => a.indexOf(x) !== i).length
    407     ? "\nMultiple of attachments the same type found!\n"
    408     : "";
    409   errorMsgs += tooSmall.length
    410     ? "\nThe name" +
    411       (tooSmall.length === 1 ? "" : "s") +
    412       ': `"' +
    413       tooSmall.reduce((out, x, i) =>
    414         [out, x].join(i === curr.length - 1 ? '"` and `"' : '"`, `"')
    415       ) +
    416       '"` ' +
    417       (tooSmall.length === 1 ? "is" : "are") +
    418       " too short\n"
    419     : "";
    420   return errorMsgs ? errorMsgs.trim() : outAttachments;
    421 }
    422 // console.log(attachmentsIdentifier("chopper with heavy handle, red sight, granulated", data.cguns[38].aments)); makeError();
    423 // console.log(attachmentsIdentifier("ak + 5mw lazer", data.cguns[0].aments)); makeError();
    424 // console.log(attachmentsIdentifier("117 + 40 round mag", data.cguns[0].aments, data.cguns[0].stats)); makeError();
    425 // console.log(attachmentsIdentifier("117 + rtc muzzle brake, rubberized griptape, tac lazer sight, 40 round mag, no stock", data.cguns[1].aments)); makeError();
    426 // console.log(attachmentsIdentifier("47 + stipplied grip tape", data.cguns[0]));
    427 // makeError();
    428 function damageHandler(
    429   currDmgs,
    430   currRngs,
    431   damageMulti,
    432   hp,
    433   tbs,
    434   tbb,
    435   bib,
    436   pellets
    437 ) {
    438   currDmgs = [...currDmgs];
    439   currRngs = [...currRngs];
    440 
    441   currRngs = currRngs.filter(x => x < 100).map(x => Math.round(x));
    442   currDmgs.length = currRngs.length + 1;
    443   currDmgs = currDmgs.map(x => Math.round(x * damageMulti));
    444   let currSTKs = currDmgs.map(x => stk(x)),
    445     currTTKs = currDmgs.map(x => ttk(x)),
    446     currPDmg = null,
    447     n = Math.max(...currTTKs.map(x => x.toString().length));
    448   n = n < 3 ? 3 : n;
    449   function worker1(inp) {
    450     return inp.map(x => x.toString().padStart(n)).join(" -- ") + "\n";
    451   }
    452   function worker2(inp) {
    453     return (
    454       "".padStart(n + 1) +
    455       inp.map(x => x.toString().padStart(2)).join("".padStart(n + 2)) +
    456       "\n"
    457     );
    458   }
    459   function stk(dmg) {
    460     let out;
    461     if (!pellets) out = Math.ceil(hp / dmg);
    462     else out = Math.ceil(hp / (dmg * pellets));
    463     return out == Infinity ? "∞" : out;
    464   }
    465   function ttk(dmg) {
    466     const stkVal = stk(dmg);
    467     if (stkVal == "∞") return stkVal;
    468     if (!bib) return Math.round((stkVal - 1) * tbs);
    469 
    470     let out = 0;
    471     if (dmg > 0) {
    472       if (stkVal % bib == 0) {
    473         for (var i = 0; i < Math.floor(stkVal / bib) - 1; i++) {
    474           out += tbs * (bib - 1) + tbb;
    475         }
    476         out = out + tbs * (bib - 1);
    477       } else if (stkVal % bib != 0) {
    478         for (var i = 0; i <= Math.floor(stkVal / bib) - 1; i++) {
    479           out += tbs * (bib - 1) + tbb;
    480         }
    481         for (var i = 0; i < (stkVal % bib) - 1; i++) {
    482           out += tbs;
    483         }
    484       }
    485       out = Math.round(out);
    486       if (out == Infinity) {
    487         return "∞";
    488       }
    489     } else {
    490       out = "No";
    491     }
    492     return out;
    493   }
    494   if (pellets) {
    495     currPDmg = currDmgs.map(x => x + "×" + pellets);
    496     n = Math.max(...currPDmg.map(x => x.toString().length));
    497   }
    498   return (
    499     "```swift\n" +
    500     "Damage : " +
    501     worker1(currPDmg || currDmgs) +
    502     (pellets ? "Total : " + worker1(currDmgs.map(x => x * pellets)) : "") +
    503     "STK    : " +
    504     worker1(currSTKs) +
    505     "TTK    : " +
    506     worker1(currTTKs) +
    507     "Range  : " +
    508     (currRngs.length ? worker2(currRngs) : worker1(["∞"])) +
    509     "```"
    510   );
    511 }
    512 // console.log(damageHandler([30, 25, 20], [10, 20], 1, 100, 60000 / 720, 0, 0));  makeError();
    513 // console.log(damageHandler([ 33, 23 ], [ 39 ], 1, 100, 109.0909090909091, 0, 0 )); makeError();
    514 
    515 function recoilHandler(
    516   xRecoil,
    517   yRecoil,
    518   xMultiplier,
    519   yMultiplier,
    520   bulletCount
    521 ) {
    522   if (xRecoil.length != yRecoil.length) return "err";
    523 
    524   const recoilLength = xRecoil.length;
    525   if (recoilLength == 0) return "none";
    526 
    527   const recoilPattern = [
    528     {
    529       x: 0,
    530       y: 0,
    531     },
    532   ];
    533   let recoilObj;
    534   for (let i = 0; i < bulletCount; i++) {
    535     const xContinuationVal =
    536       xRecoil[recoilLength - 1] - xRecoil[recoilLength - 2];
    537     const yContinuationVal =
    538       yRecoil[recoilLength - 1] - yRecoil[recoilLength - 2];
    539     if (i < recoilLength) {
    540       recoilObj = {
    541         x: xRecoil[i] * (1 + xMultiplier / 100),
    542         y: yRecoil[i] * (1 + yMultiplier / 100),
    543       };
    544     } else {
    545       recoilObj = {
    546         x:
    547           (recoilPattern[recoilPattern.length - 1].x + xContinuationVal) *
    548           xMultiplier,
    549         y:
    550           (recoilPattern[recoilPattern.length - 1].y + yContinuationVal) *
    551           yMultiplier,
    552       };
    553     }
    554     recoilPattern.push(recoilObj);
    555   }
    556   const chart = new QuickChart();
    557   chart
    558     .setConfig({
    559       type: "scatter",
    560       data: {
    561         datasets: [
    562           {
    563             data: recoilPattern,
    564             showLine: true,
    565             fill: false,
    566             pointRadius: 3,
    567             backgroundColor: "rgba(056,205,255,1.00)", // "#38CDFF" fully transparent
    568             borderColor: "rgba(056,205,255,0.75)", // "#38CDFF" 75% transparent
    569           },
    570         ],
    571       },
    572       options: {
    573         plugins: {
    574           backgroundImageUrl: "https://i.imgur.com/jFAFaWF.png",
    575         },
    576         legend: {
    577           display: false,
    578         },
    579         scales: {
    580           yAxes: [
    581             {
    582               ticks: {
    583                 display: false,
    584                 min: 0,
    585                 max: 5050,
    586               },
    587             },
    588           ],
    589           xAxes: [
    590             {
    591               ticks: {
    592                 display: false,
    593                 min: -4495,
    594                 max: 4495,
    595               },
    596             },
    597           ],
    598         },
    599       },
    600     })
    601     .setWidth(1780)
    602     .setHeight(1000);
    603 
    604   return chart;
    605 }
    606 
    607 function updateStatswithEffects(inpEffects, inpStats) {
    608   const l = inpStats[18] / inpStats[17];
    609   const outStats = [...inpStats];
    610 
    611   var inpStatsarr = [1, 2, 5, 11, 14, 15, 20, 21, 22, 26, 27, 31];
    612   var inpEfecsarr = [17, 18, 16, 19, 1, 10, 14, 14, 14, 6, 7, 42]; // Efecs is short for Effects
    613   for (let i = 0; i < inpEffects.length; i++) {
    614     if (inpEffects[inpEfecsarr[i]] != 0) {
    615       outStats[inpStatsarr[i]] *= (inpEffects[inpEfecsarr[i]] + 100) / 100;
    616     }
    617   }
    618   var inpStatsarr = [3, 4, 16, 28, 29, 30];
    619   var inpEfecsarr = [20, 38, 0, 39, 40, 41];
    620   for (let i = 0; i < inpEffects.length; i++) {
    621     if (inpEffects[inpEfecsarr[i]] != 0) {
    622       outStats[inpStatsarr[i]] = inpEffects[inpEfecsarr[i]];
    623     }
    624   }
    625   var inpStatsarr = [0, 17, 25];
    626   var inpEfecsarr = [29, 27, 9];
    627   for (let i = 0; i < inpEffects.length; i++) {
    628     if (inpEffects[inpEfecsarr[i]] != 0) {
    629       outStats[inpStatsarr[i]] += inpEffects[inpEfecsarr[i]];
    630     }
    631   }
    632 
    633   if (inpEffects[4] != 0) {
    634     outStats[10] = 11 - (11 - inpStats[10]) * (1 + inpEffects[4] / 100); //
    635   }
    636   if (inpEffects[43] != 0 && inpStats[8] != -1) {
    637     outStats[8] *= (inpEffects[43] + 100) / 100;
    638   }
    639   if (inpEffects[16] != 0) {
    640     outStats[7] *= inpEffects[16] / -100 + 1;
    641   }
    642   outStats[18] = inpStats[17] * l;
    643   return outStats;
    644 }
    645 
    646 function attachmentHandler(currEffects, currStats) {
    647   const pos = [],
    648     neg = [],
    649     atr = [];
    650   if (currEffects[0] > currStats[16]) {
    651     pos.push(
    652       currEffects[0] +
    653         "% zoom (+" +
    654         (currEffects[0] - currStats[16]) +
    655         "% zoom)"
    656     );
    657   } else if (currEffects[0] != 0 && currEffects[0] != currStats[16]) {
    658     neg.push(
    659       currEffects[0] +
    660         "% zoom (-" +
    661         (currStats[16] - currEffects[0]) +
    662         "% zoom)"
    663     );
    664   }
    665   if (currEffects[0] != 0 && currStats[16] <= 110) {
    666     atr.push("Easier to Aim");
    667   }
    668   negGood1(1, "ADS time");
    669   negGood1(2, "Vertical Recoil");
    670   negGood1(3, "Horizontal Recoil");
    671   negGood1(4, "Bullet Spread");
    672   negGood1(5, "Moving Bullet Spread");
    673   posGood1(6, "Mobility");
    674   posGood1(7, "ADS Mobility");
    675   negGood1(8, "Recoil when Crouched or Prone");
    676   posGood1(9, "Sprint Mobility");
    677   negGood1(10, "Sprint to Fire Time");
    678   negGood1(11, "Flinch");
    679   negGood1(12, "Hipfire Spread");
    680   posGood1(13, "Damage Range");
    681   negGood1(14, "Reload Time");
    682   posGood1(15, "Headshot Damage");
    683   posGood1(16, "Rate of Fire");
    684   posGood1(17, "Detonation Range");
    685   posGood1(18, "Explosion Radius");
    686   negGood1(19, "Idle Sway");
    687   if (currEffects[20] > currStats[3]) {
    688     pos.push(
    689       currEffects[20].ToString().Replace(".", " ~ ") + " Explosion Damage"
    690     );
    691   } else if (currEffects[20] != 0 && currEffects[20] != currStats[3]) {
    692     neg.push(
    693       currEffects[20].ToString().Replace(".", " ~ ") + " Explosion Damage"
    694     );
    695   }
    696   atrPush3(21, "Visible Laser when not ADS-ed");
    697   atrPush3(22, "Visible Laser when ADS-ed");
    698   atrPush3(23, "Visible Laser");
    699   atrPush3(24, "Silenced Gunfire");
    700   atrPush3(25, "Hidden Muzzle Flash");
    701   posGood2(27, "Rounds/Mag");
    702   posGood2(28, "Rounds/Tube");
    703   posGood2(29, "Pellets per Shot");
    704   posGood2(30, "Damage Over Time");
    705   atrPush3(32, "Reworked ADS");
    706   atrPush3(33, "Faster Melee QTE");
    707   if (currEffects[35]) {
    708     atr.push(
    709       "Can Not use " +
    710         currEffects[35]
    711           .toString()
    712           .padStart(11, "0")
    713           .toString()
    714           .split("")
    715           .map((x, i) => (parseInt(x) !== 0 ? data.attachmentTypes[i] : 0))
    716           .filter(x => x)
    717     );
    718   }
    719   atrPush3(36, "Can't ADS");
    720   if (currEffects[37] != 0) {
    721     atr.push("New Lethality Profile");
    722   }
    723   if (currEffects[38] != 0 && currEffects[38] < currStats[4]) {
    724     pos.push("Turns to " + data.firingModes[currEffects[38] - 1]);
    725   } else if (currEffects[38] != 0 && currEffects[38] != currStats[4]) {
    726     neg.push("Turns to " + data.firingModes[currEffects[38] - 1]);
    727   }
    728   posGood2(39, "Tick Damage");
    729   posGood2(40, "Ticks");
    730   negGood2(41, "ms Tick Interval");
    731   posGood2(42, "Breath Holding Time");
    732   posGood1(43, "Bullet Speed");
    733   if (currEffects[44] == 1) {
    734     atr.push("Higher Penetraion Damage");
    735   } else if (currEffects[44] == -1) {
    736     atr.push("Lower Penetraion Damage");
    737   }
    738   posGood2(45, `Round ${currEffects[45] - 1 ? "s" : ""} in Reserve`);
    739 
    740   function posGood1(i, ext) {
    741     if (currEffects[i].IsPositive()) {
    742       pos.push(currEffects[i].PlusHL() + "% " + ext);
    743     } else if (currEffects[i].IsNegative()) {
    744       neg.push(currEffects[i].PlusHL() + "% " + ext);
    745     }
    746   }
    747 
    748   function negGood1(i, ext) {
    749     if (currEffects[i].IsNegative()) {
    750       pos.push(currEffects[i].PlusHL() + "% " + ext);
    751     } else if (currEffects[i].IsPositive()) {
    752       neg.push(currEffects[i].PlusHL() + "% " + ext);
    753     }
    754   }
    755 
    756   function posGood2(i, ext) {
    757     if (currEffects[i].IsPositive()) {
    758       pos.push(currEffects[i].PlusHL() + " " + ext);
    759     } else if (currEffects[i].IsNegative()) {
    760       neg.push(currEffects[i].PlusHL() + " " + ext);
    761     }
    762   }
    763 
    764   function negGood2(i, ext) {
    765     if (currEffects[i].IsNegative()) {
    766       pos.push(currEffects[i].PlusHL() + " " + ext);
    767     } else if (currEffects[i].IsPositive()) {
    768       neg.push(currEffects[i].PlusHL() + " " + ext);
    769     }
    770   }
    771 
    772   function atrPush3(i, ext) {
    773     if (currEffects[i].ToBool()) {
    774       atr.push(ext);
    775     }
    776   }
    777   // Return the attributes when there is and use algorithms to join them
    778   return [
    779     pos.length
    780       ? {
    781           name: "**Positives:**",
    782           value: `\`\`\`ini\n[${pos.join("]\n[")}]\n\`\`\``,
    783           inline: true,
    784         }
    785       : 0,
    786     neg.length
    787       ? {
    788           name: "**Negatives:**",
    789           value: `\`\`\`css\n[${neg.join("]\n[")}]\n\`\`\``,
    790           inline: true,
    791         }
    792       : 0,
    793     atr.length
    794       ? {
    795           name: "**Attributes:**",
    796           value: `\`\`\`fix\n[${atr.join("]\n[")}]\n\`\`\``,
    797         }
    798       : 0,
    799   ].filter(x => x);
    800 }
    801 
    802 function interpretioner(inpAttachments) {
    803   return inpAttachments.length
    804     ? " with " + inpAttachments.map(x => x.name).join(", ")
    805     : "";
    806 }
    807 
    808 function totaler(inpAttachments) {
    809   const totalEffects = inpAttachments[0].effects;
    810   for (let j = 1; j < inpAttachments.length; j++) {
    811     for (let i2 = 0; i2 < totalEffects.length; i2++) {
    812       totalEffects[i2] += inpAttachments[j].effects[i2];
    813     }
    814   }
    815   return totalEffects;
    816 }
    817 
    818 function makeError() {
    819   undefined.split("L");
    820 }
    821 
    822 module.exports = {
    823   weaponIdentifier,
    824   attachmentsIdentifier,
    825   recoilHandler,
    826   attachmentHandler,
    827   updateStatswithEffects,
    828   makeError,
    829   interpretioner,
    830   damageHandler,
    831   isolator,
    832   totaler,
    833   hasAttachments,
    834 };