Name
Info:
+
Name
Info:
Location
@@ -4448,7 +4110,7 @@
-
+
@@ -4473,17 +4135,9 @@
Current Issues
Gear Weapon TN bonus only updates when changing domains
-
- To be added: Personal Armor list.
-
-
+
-
-
-
-
-
@@ -4491,9 +4145,423 @@
Version Changes:
Ver:
-
Wiki:https://wiki.roll20.net/Heavy_Gear_4th_Edition
+
+ Wiki: https://wiki.roll20.net/Heavy_Gear_4th_Edition
@@ -4654,7 +4722,7 @@ buttonlist.forEach(button => {
});
-const domains = ['athletics','awareness','business','craft','culture','electronicwarfare','electronics','gunnery','hardscience','influence',
+const domains = ['','athletics','awareness','business','craft','culture','electronicwarfare','electronics','gunnery','hardscience','influence',
'institutions','investigation','mechanics','medicine','melee','pilot','showmanship','socialscience','survival','tactics'];
const gearSkills = ['gunnery','pilot','electronicwarfare'];
const gearWeaponTraits = ['ammo','advanced','aoe','ap','apex','attachment','auto','backup','blast','bomb','brace','brawler','burst','corrosion','demo',
@@ -4662,7 +4730,7 @@ const gearWeaponTraits = ['ammo','advanced','aoe','ap','apex','attachment','auto
const gearWeaponTraitsX = ['aoe','ap','brawler','burst','demo','fire','reach'];
const archetypes = ['Caretaker','Commander','Duelist','Fixer','Grunt','Pilot','Scout','Techie'];
const ranks = [1,2,3,4,5,6];
-const infWeaponTraits = ['ammo','advanced','aoe','ap','blast','brace','burst','corrosion','demo','fire','frag','haywire','indirect','missile','musclepowered','nonlethal','poison','placed','precise','reach','silent','slashing','shit','smoke','spray','stun','thrown','see_desc'];
+const infWeaponTraits = ['ammo','advanced','aoe','ap','blast','brace','burst','corrosion','demo','fire','frag','haywire','indirect','missile','musclepowered','nonlethal','poison','placed','precise','reach','silent','slashing','shot','smoke','spray','stun','thrown','see_desc'];
const infWeaponTraitsX = ['aoe','ap','burst','demo','fire'];
//when archetypes change, update archeytpe_select_x
@@ -5060,6 +5128,102 @@ on('change:blitz_check', function() {
});
});
});
+
+const findDomainUpper = (domain = 0) => {
+ console.log(domain+" domain");
+ if (domain == 1) {
+ domain = "Athletics";
+ } else if (domain == 2){
+ domain = "Awareness";
+ } else if (domain == 3){
+ domain = "Business";
+ } else if (domain == 4){
+ domain = "Craft";
+ } else if (domain == 5){
+ domain = "Culture";
+ } else if (domain == 6){
+ domain = "Electronic Warfare";
+ } else if (domain == 7){
+ domain = "Electronics";
+ } else if (domain == 8){
+ domain = "Gunnery";
+ } else if (domain == 9){
+ domain = "Hard Science";
+ } else if (domain == 10){
+ domain = "Influence";
+ } else if (domain == 11){
+ domain = "Institutions";
+ } else if (domain == 12){
+ domain = "Investigation";
+ } else if (domain == 13){
+ domain = "Mechanics";
+ } else if (domain == 14){
+ domain = "Medicine";
+ } else if (domain == 15){
+ domain = "Melee";
+ } else if (domain == 16){
+ domain = "Pilot";
+ } else if (domain == 17){
+ domain = "Showmanship";
+ } else if (domain == 18){
+ domain = "Social Science";
+ } else if (domain == 19){
+ domain = "Survival";
+ } else if (domain == 20){
+ domain = "Tactics";
+ } else {
+ domain = domain;
+ };
+ return domain;
+};
+const findDomainLower = (domain = 0) => {
+ console.log(domain+" domain");
+ if (domain == 1) {
+ domain = "athletics";
+ } else if (domain == 2){
+ domain = 'awareness';
+ } else if (domain == 3){
+ domain = 'business';
+ } else if (domain == 4){
+ domain = 'craft';
+ } else if (domain == 5){
+ domain = 'culture';
+ } else if (domain == 6){
+ domain = 'electronicwarfare';
+ } else if (domain == 7){
+ domain = 'electronics';
+ } else if (domain == 8){
+ domain = 'gunnery';
+ } else if (domain == 9){
+ domain = 'hardscience';
+ } else if (domain == 10){
+ domain = 'influence';
+ } else if (domain == 11){
+ domain = 'institutions';
+ } else if (domain == 12){
+ domain = 'investigation';
+ } else if (domain == 13){
+ domain = 'mechanics';
+ } else if (domain == 14){
+ domain = 'medicine';
+ } else if (domain == 15){
+ domain = 'melee';
+ } else if (domain == 16){
+ domain = 'pilot';
+ } else if (domain == 17){
+ domain = 'showmanship';
+ } else if (domain == 18){
+ domain = 'socialscience';
+ } else if (domain == 19){
+ domain = 'survival';
+ } else if (domain == 20){
+ domain = 'tactics';
+ } else {
+ domain = domain;
+ };
+ return domain;
+};
+
//when stat button pushed, lookup weapon attributes
const statGearWeapon = (input = "",arm = 0,lvl = 2, underslungWeapon = 0) => {
let gearweapon = gearWeaponLookup(input);
@@ -5419,8 +5583,7 @@ const gearWeaponLookup = (string) => {
gearweapon = {type:"Bomb", range :"-",size: sizeletter, l:7,m:8, h:9, traits:["AoE:15/20/25","Bomb","Limited Ammo"], mode:"D", slots:0, aux:0, location:"-", ammunition:1, pl:1, desc:""};
break;
case "CB":
- gearweapon = {type:"Chain Bayonet", range :"3 (0in)",size: sizeletter, l:5,m:6, h:7, traits:["Attachment","Demo:2","Brawler:1","See Desc"], mode:"M", slots:1, aux:0, location:"-", ammunition:0, pl:4,
- desc:"Swapping to or from a weapon with a bayonet counts as an additional action"};
+ gearweapon = {type:"Chain Bayonet", range :"3 (0in)",size: sizeletter, l:5,m:6, h:7, traits:["Attachment","Demo:2","Brawler:1","See Desc"], mode:"M", slots:1, aux:0, location:"-", ammunition:0, pl:4, desc:"Swapping to or from a weapon with a bayonet counts as an additional action"};
break;
case "CBL":
gearweapon = {type:"Chain Blade", range :"6 (1in)",size: sizeletter, l:7,m:8, h:9, traits:["Demo:2/4/6","Brawler:1"], mode:"M", slots:2, aux:0, location:"Manip", ammunition:0, pl:4, desc:""};
@@ -5444,8 +5607,7 @@ const gearWeaponLookup = (string) => {
gearweapon = {type:"Field Mortar", range :"120-360/720",size: sizeletter, l:8,m:9, h:10, traits:["AoE:20","Blast"], mode:"I", slots:3, aux:2, location:"Shoulder,Torso", ammunition:1, pl:4, desc:""};
break;
case "FS":
- gearweapon = {type:"Fist", range :"0 (0in)",size: " ", l:0,m:0, h:0, traits:["See Desc"], mode:"M", slots:0, aux:0, location:"-", ammunition:0, pl:0,
- desc:"A Gear or Strider with the Hands equipment that has an open hand is counted as having a fist. This is not counted as a weapon for any rules interaction. The Damage of a Fist is equal to the Arm - 2 of the vehicle."};
+ gearweapon = {type:"Fist", range :"0 (0in)",size: " ", l:0,m:0, h:0, traits:["See Desc"], mode:"M", slots:0, aux:0, location:"-", ammunition:0, pl:0, desc:"A Gear or Strider with the Hands equipment that has an open hand is counted as having a fist. This is not counted as a weapon for any rules interaction. The Damage of a Fist is equal to the Arm - 2 of the vehicle."};
break;
case "FAI":
gearweapon = {type:"Flail", range :"5 (1in)",size: sizeletter, l:6,m:7, h:8, traits:["See Desc"], mode:"M", slots:2, aux:0, location:"Manip", ammunition:0, pl:2, desc:"Attacks from flails do not allow the effects of shields or bucklers."};
@@ -5460,11 +5622,7 @@ const gearWeaponLookup = (string) => {
gearweapon = {type:"Frag Grenades", range :"0-60/90",size: sizeletter, l:3,m:4, h:5, traits:["AoE:15","Blast","Frag","Limited Ammo"], mode:"D,I", slots:1, aux:0, location:"Manip", ammunition:4, pl:1, desc:""};
break;
case "GL":
- gearweapon = {type:"Grenade Launcher", range :"60-120/240",size: sizeletter, l:0,m:0, h:0, traits:["Burst:1","See Desc"], mode:"D,I", slots:2, aux:1, location:"Manip", ammunition:4, pl:1,
- desc:"Grenade Launchers may fire Anti-Vehicle, Frag, Incendiary, Haywire, White Sand and Smoke Grenades using those Grenade’s damage but the Grenade Launcher’s \
- stats for everything else (this includes range and Ammo. Attacks with the Grenade and Grenade Launcher use the combined traits of both. When selecting the weapon,\
- the user must select 2 of the available grenade types to be available to use (these are still restricted by PL). The user may only use those 2 types when attacking.\
- A Grenade Launcher may fire grenades the same size as it or lower (A MGL can fire Medium or Light grenades). Ignore the LA:X trait on any Grenades used."};
+ gearweapon = {type:"Grenade Launcher", range :"60-120/240",size: sizeletter, l:0,m:0, h:0, traits:["Burst:1","See Desc"], mode:"D,I", slots:2, aux:1, location:"Manip", ammunition:4, pl:1, desc:"Grenade Launchers may fire Anti-Vehicle, Frag, Incendiary, Haywire, White Sand and Smoke Grenades using those Grenade’s damage but the Grenade Launcher’s stats for everything else (this includes range and Ammo. Attacks with the Grenade and Grenade Launcher use the combined traits of both. When selecting the weapon, the user must select 2 of the available grenade types to be available to use (these are still restricted by PL). The user may only use those 2 types when attacking. A Grenade Launcher may fire grenades the same size as it or lower (A MGL can fire Medium or Light grenades). Ignore the LA:X trait on any Grenades used."};
break;
case "GM":
gearweapon = {type:"Guided Mortar", range :"120-360/720",size: sizeletter, l:7,m:8, h:9, traits:["AoE:15","Blast","Guided"], mode:"I", slots:3, aux:2, location:"Shoulder,Torso", ammunition:1, pl:5, desc:""};
@@ -5500,8 +5658,7 @@ const gearWeaponLookup = (string) => {
gearweapon = {type:"Morningstar", range :"5 (1in)",size: sizeletter, l:9,m:10, h:11, traits:[""], mode:"M", slots:3, aux:0, location:"Manip", ammunition:0, pl:2, desc:""};
break;
case "PZ":
- gearweapon = {type:"Panzerfaust", range :"30-60/120",size: sizeletter, l:7,m:8, h:9, traits:["AP:2/3/4","Limited Ammo","See Desc"], mode:"D", slots:1, aux:0, location:"Manip", ammunition:2, pl:2,
- desc:"Panzerfausts are always assumed to be stored on the torso if they are listed on a vehicle’s profile"};
+ gearweapon = {type:"Panzerfaust", range :"30-60/120",size: sizeletter, l:7,m:8, h:9, traits:["AP:2/3/4","Limited Ammo","See Desc"], mode:"D", slots:1, aux:0, location:"Manip", ammunition:2, pl:2, desc:"Panzerfausts are always assumed to be stored on the torso if they are listed on a vehicle’s profile"};
break;
case "PA":
gearweapon = {type:"Particle Accelerator", range :"30-240/480",size: sizeletter, l:7,m:8, h:9, traits:["Haywire","Precise","Advanced"], mode:"D", slots:3, aux:0, location:"Manip", ammunition:0, pl:6, desc:""};
@@ -5525,8 +5682,7 @@ const gearWeaponLookup = (string) => {
gearweapon = {type:"Rifle", range :"60-360/720",size: sizeletter, l:6,m:7, h:8, traits:["Precise"], mode:"D", slots:2, aux:1, location:"Manip", ammunition:5, pl:2, desc:""};
break;
case "RP":
- gearweapon = {type:"Rocket Pack", range :"60-180/360",size: sizeletter, l:7, m:8, h:9, traits:["AoE:15","AP:1","Limited Ammo","Salvo"], mode:"D,I", slots:2, aux:0, location:"Shoulder,Torso", ammunition:4, pl:3,
- desc:"When reloaded at a base or supply vehicle, a rocket pack can be loaded with torpedos. These work exactly like a rocket pack but can only fire at targets in the water."};
+ gearweapon = {type:"Rocket Pack", range :"60-180/360",size: sizeletter, l:7, m:8, h:9, traits:["AoE:15","AP:1","Limited Ammo","Salvo"], mode:"D,I", slots:2, aux:0, location:"Shoulder,Torso", ammunition:4, pl:3, desc:"When reloaded at a base or supply vehicle, a rocket pack can be loaded with torpedos. These work exactly like a rocket pack but can only fire at targets in the water."};
break;
case "RC":
gearweapon = {type:"Rotary Cannon", range :"60-180/360",size: sizeletter, l:6, m:7, h:8, traits:["Burst:2"], mode:"D", slots:3, aux:1, location:"Manip", ammunition:5, pl:2, desc:""};
@@ -5702,11 +5858,13 @@ const parseIntArray = (array) => {
};
//function that capitalises first letter of a string and replaces _ with spaces
const capitalise = (string) => {
- //console.log("capitalise "+string);
+ console.log("capitalise "+string);
let output = string.charAt(0).toUpperCase() + string.slice(1);
output = output.replace(/_/g, " ");
output = output.replace("Aoe:", "AoE:");
output = output.replace("Ap:", "AP:");
+ output = output.replace("Musclepowered", "Muscle Powered");
+ output = output.replace("Nonlethal", "Non-Lethal");
return output;
};
const deCapitalise = (array) => {
@@ -5717,11 +5875,13 @@ const deCapitalise = (array) => {
console.log("item "+item);
if (typeof item === 'string') { // check if item is a string
let newItem = item.toLowerCase();
+ newItem = newItem.replace("muscle powered", "musclepowered"); // replace Muscle Powered with musclepowered
newItem = newItem.replace(/ /g, "_"); // replace spaces with underscores
newItem = newItem.replace(":_", ":"); //remove space after :
newItem = newItem.replace(/,/g, "");//remove ,
newItem = newItem.replace("(", "");//remove brackets
newItem = newItem.replace(")", "");
+ newItem = newItem.replace("n-", "n");// makes non-lethal into nonlethal
//console.log("newItem :"+newItem+".");
//remove space at begining of string
if (newItem.charAt(0) == "_") {
@@ -5798,156 +5958,65 @@ const damageLookup = (input) => {
switch (input) {
case "Anger":
damage = {name:"Anger",type:"SHELL",
- penalty: "Past trauma, an injury, or slights (real or imagined) have\
- caused the character to lose their cool. They suffer a -1d6\
- penalty to actions that require them to think before acting\
- but will only face half the penalty (round down) when\
- committing a violent act.",
+ penalty: "Past trauma, an injury, or slights (real or imagined) have caused the character to lose their cool. They suffer a -1d6 penalty to actions that require them to think before acting but will only face half the penalty (round down) when committing a violent act.",
deterioration:"The character suffers an additional -1d6 each time the same stimulus happens.",
- fix:" Removing the cause of the anger will take away -1d6 immediately. A character can also take a\
- round to try and calm themselves down and make\
- an Endependent Roll at Difficulty 4, but failure will\
- add another -1d6. For disagreements the character\
- may need to have a third-party help calm things\
- down. Other characters can “talk them down”, with\
- a successful skill roll Numbing -1d6 of penalty\
- for that game session. Generally, any anger that\
- reaches -3d6 or more will not be solved simply\
- by stepping out of the room and regaining their\
- composure, and will require some catalyst to solve\
- it such as an apology, mediation, or counseling.\
- Lashing out at the target will Numb the damage\
- entirely for the remainder of that scene and the\
- next, but if the same stimulus happens again\
- without some kind of intervention the character\
- immediately returns to their highest anger damage result."};
+ fix:" Removing the cause of the anger will take away -1d6 immediately. A character can also take a round to try and calm themselves down and make an Independent Roll at Difficulty 4, but failure will add another -1d6. For disagreements the character may need to have a third-party help calm things down. Other characters can “talk them down”, with a successful skill roll Numbing -1d6 of penalty for that game session. Generally, any anger that reaches -3d6 or more will not be solved simply by stepping out of the room and regaining their composure, and will require some catalyst to solve it such as an apology, mediation, or counseling. Lashing out at the target will Numb the damage entirely for the remainder of that scene and the next, but if the same stimulus happens again without some kind of intervention the character immediately returns to their highest anger damage result."};
break;
case "Bleeding":
damage = {name:"Bleeding",type:"SYSTEM",
- penalty: "The character will feel weak and drained. The action\
- penalty applies to phyiscal actions or motion.",
- deterioration:"The damage result increases by 1 for\
- every 30 minutes this is not treated",
- fix:" Any charater can apply pressure to the wound,\
- so long as this is applied the Deteroration will not\
- occur."};
+ penalty: "The character will feel weak and drained. The action penalty applies to phyiscal actions or motion.",
+ deterioration:"The damage result increases by 1 for every 30 minutes this is not treated",
+ fix:" Any charater can apply pressure to the wound, so long as this is applied the Deteroration will not occur."};
break;
case "Blinded":
damage = {name:"Blinded",type:"SYSTEM",
- penalty: "A partially blinded character suffers the action penalty\
- to anything that requires sight, and cannot attempt\
- some skills at all. One advantage of Datagloves and HUI\
- systems is that they allow users to make gesture-based\
- commands, so it may still be possible for a character to\
- take some actions while “fighting blind”.",
+ penalty: "A partially blinded character suffers the action penalty to anything that requires sight, and cannot attempt some skills at all. One advantage of Datagloves and HUI systems is that they allow users to make gesture-based commands, so it may still be possible for a character to take some actions while “fighting blind”.",
deterioration:"",
- fix:"characters blinded by bright lights (be it a\
- spotlight, or a laser aimed at them while using an\
- IR visor) will have to remove the stimulus or shield\
- their eyes. After this they will recover -1d6 of this\
- result per round. A character blinded by chemicals,\
- dirt, or sand, will have to wash or wipe their eyes\
- clean before recovering. Characters blinded by\
- damage to their eyes will have to seek medical\
- help. Permanent blinding can be healed in a genetic\
- therapy clinic but will take a full season."};
+ fix:"characters blinded by bright lights (be it a spotlight, or a laser aimed at them while using an IR visor) will have to remove the stimulus or shield their eyes. After this they will recover -1d6 of this result per round. A character blinded by chemicals, dirt, or sand, will have to wash or wipe their eyes clean before recovering. Characters blinded by damage to their eyes will have to seek medical help. Permanent blinding can be healed in a genetic therapy clinic but will take a full season."};
break;
case "Brain Injury":
damage = {name:"Brain Injury*",type:"SYSTEM",
- penalty: "The character suffers the damage result to all skill tests\
- involving thinking or problem solving. A penalty of 3d6\
- is applied to all motion related actions in addition to\
- this. This result cannot be healed during an intermission\
- action.",
- deterioration:"If not treated, the action penalty and\
- penatly to motion increase by 1d6 every hour",
- fix:" Brain injuries are not easy to treat. A [W] is not\
- sufficent to address a brain injury; a surgeon and\
- hospital is required. Even then it will take months\
- or years to fully recover. The rate of recovery is 1d6\
- per season"};
+ penalty: "The character suffers the damage result to all skill tests involving thinking or problem solving. A penalty of 3d6 is applied to all motion related actions in addition to this. This result cannot be healed during an intermission action.",
+ deterioration:"If not treated, the action penalty and penatly to motion increase by 1d6 every hour",
+ fix:" Brain injuries are not easy to treat. A [W] is not sufficent to address a brain injury; a surgeon and hospital is required. Even then it will take months or years to fully recover. The rate of recovery is 1d6 per season"};
break;
case "Burned":
damage = {name:"Burned*",type:"SYSTEM",
- penalty: "The character suffers the damage results to all skill\
- tests involving motion. Multiple instances of this result\
- combine for a single large damage result",
- deterioration:" -1d6 or -2d6 burns will Deteriorate\
- in 24 hours. -3d6 and above will deteriorate hourly\
- without dressings",
- fix:" Dressings, antiseptics, and pain medication.\
- For -2d6 or -3d6 burns, skin grafts will be needed\
- for healing. Burns will heal in a hospital at a rate\
- of five days per d6 of burn for each dice removed,\
- so a -2d6 burn requires ten days to be reduced to\
- -1d6, and five more to be healed entirely. Outside of\
- hospital conditions, a character may be left scarred\
- by burns and could even gain the Ugly flaw if the\
- damage is bad enough."};
+ penalty: "The character suffers the damage results to all skill tests involving motion. Multiple instances of this result combine for a single large damage result",
+ deterioration:" -1d6 or -2d6 burns will Deteriorate in 24 hours. -3d6 and above will deteriorate hourly without dressings",
+ fix:" Dressings, antiseptics, and pain medication. For -2d6 or -3d6 burns, skin grafts will be needed for healing. Burns will heal in a hospital at a rate of five days per d6 of burn for each dice removed, so a -2d6 burn requires ten days to be reduced to -1d6, and five more to be healed entirely. Outside of hospital conditions, a character may be left scarred by burns and could even gain the Ugly flaw if the damage is bad enough."};
break;
case "Combat Fatigue":
damage = {name:"Combat Fatigue",type:"SHELL",
- penalty: "The character gains an adversity called Combat Fatigue.\
- It is applied to every skill test and has a penalty of 3d6\
- instead of the normal 1d6 for an adversity. The character\
- is extemely irritable, depressed, and generally seems less\
- alert overall.",
+ penalty: "The character gains an adversity called Combat Fatigue. It is applied to every skill test and has a penalty of 3d6 instead of the normal 1d6 for an adversity. The character is extemely irritable, depressed, and generally seems less alert overall.",
deterioration:"",
- fix:"Combat fatigue will improve by 1d6 every 2\
- weeks with rest and removal from the situation."};
+ fix:"Combat fatigue will improve by 1d6 every 2 weeks with rest and removal from the situation."};
break;
case "Concussion":
damage = {name:"Concussion",type:"SYSTEM",
- penalty: "The character suffers an action penalty to their thought\
- processes and any skill tests that requires knowlege or\
- memory. Muscle memory actions such as attacking are\
- unafffected.",
+ penalty: "The character suffers an action penalty to their thought processes and any skill tests that requires knowlege or memory. Muscle memory actions such as attacking are unafffected.",
deterioration:"",
- fix:"Concussions will go away after a week per\
- action penalty."};
+ fix:"Concussions will go away after a week per action penalty."};
break;
-
case "Deafened":
damage = {name:"Deafened",type:"SYSTEM",
- penalty: "The character suffers a -1d6 penalty to all actions that\
- benefit from sound such as seeking a hidden foe, treating\
- a patient, or haggling with a trader. actions that are entirely\
- dependent on hearing, such as radio conversations,\
- are impossible. While they won’t suffer a penalty when\
- fighting, they do suffer a loss of combat awareness and\
- will take the deafened action penalty to their initiative.",
+ penalty: "The character suffers a -1d6 penalty to all actions that benefit from sound such as seeking a hidden foe, treating a patient, or haggling with a trader. actions that are entirely dependent on hearing, such as radio conversations, are impossible. While they won’t suffer a penalty when fighting, they do suffer a loss of combat awareness and will take the deafened action penalty to their initiative.",
deterioration:"",
- fix:"A character temporarily deafened - such as\
- from a flash bang grenade - will have their hearing\
- come back in 1 minute per MoS of the action that\
- deafened them. Permanent hearing damage will\
- require care in a hospital and a week of regenerative\
- therapy to remove."};
+ fix:"A character temporarily deafened - such as from a flash bang grenade - will have their hearing come back in 1 minute per MoS of the action that deafened them. Permanent hearing damage will require care in a hospital and a week of regenerative therapy to remove."};
break;
case "Dehydrated":
damage = {name:"Dehydrated",type:"SYSTEM",
- penalty: "The character suffers -1d6 when they fail a survival roll\
- in high heat conditions or are denied access to water\
- for a prolonged period. People can become dehydrated\
- even in wet environments or when drinking water “if\
- they overexert themselves.. A well-made desert suit will\
- prevent this result, but someone foolish with their water\
- can still get into trouble in the harsh sands.",
- deterioration:"Each additional 24 hours dehydration\
- is not fixed, the character will suffer another -1d6\
- penalty.",
- fix:"Hydrate with water or other liquid. Each liter\
- taken (slowly) will remove one dice of penalty. At\
- -4d6 the character will need intravenous fluids to\
- recover."};
+ penalty: "The character suffers -1d6 when they fail a survival roll in high heat conditions or are denied access to water for a prolonged period. People can become dehydrated even in wet environments or when drinking water “if they overexert themselves.. A well-made desert suit will prevent this result, but someone foolish with their water can still get into trouble in the harsh sands.",
+ deterioration:"Each additional 24 hours dehydration is not fixed, the character will suffer another -1d6 penalty.",
+ fix:"Hydrate with water or other liquid. Each liter taken (slowly) will remove one dice of penalty. At -4d6 the character will need intravenous fluids to recover."};
break;
case "ENCUMBURED":
@@ -6013,6 +6082,120 @@ domains.forEach(function (domains) { //calculates expertise when improving or de
});
});
+on('change:repeating_infweapon:inf_weapon_domain', function(eventInfo) {//change infweapon_expertise to correct domain
+ console.log(eventInfo);
+ getAttrs(["repeating_infweapon_inf_weapon_domain","expertise_athletics","expertise_awareness","expertise_business","expertise_craft","expertise_culture","expertise_electronicwarfare","expertise_electronics","expertise_gunnery","expertise_hardscience","expertise_influence","expertise_institutions","expertise_investigation","expertise_mechanics","expertise_medicine","expertise_melee","expertise_pilot","expertise_showmanship","expertise_socialscience","expertise_survival","expertise_tactics"], function(v) {
+ let domain = findDomainLower(v["repeating_infweapon_inf_weapon_domain"]);
+ console.log("domain "+domain);
+ let expertise = v["expertise_"+domain];
+ console.log("expertise "+expertise);
+ setAttrs({
+ repeating_infweapon_inf_weapon_expertise: expertise,
+ });
+ });
+});
+
+
+
+infWeaponTraits.forEach(function (trait) {
+ on('change:repeating_infweapon:inf_weapon_traits_'+trait+' change:repeating_infweapon:inf_weapon_traits_'+trait+'_selector \
+ change:repeating_infweapon:inf_weapon_traits_'+trait+'_selector_2 change:repeating_infweapon:inf_weapon_traits_'+trait+'_selector_3', function() {
+ //console.log("change:repeating_infweapon:inf_weapon_traits_"+trait);
+ let traitList = {};
+ let selectList = {};
+ let numCallbacks = 0;
+ let traitString = "";
+ let select1 = "";
+ let select2 = "";
+ let select3 = "";
+ infWeaponTraits.forEach(function(trait) {
+ traitList[trait] = 0;
+ selectList[trait] = 0;
+ getAttrs([`repeating_infweapon_inf_weapon_traits_${trait}`,"repeating_infweapon_inf_weapon_traits_"+trait+"_selector"], function(v) {
+ numCallbacks++;
+ if (v[`repeating_infweapon_inf_weapon_traits_${trait}`] !== undefined) {
+ traitList[trait] = parseInt(v["repeating_infweapon_inf_weapon_traits_"+trait]);
+ } else {
+ traitList[trait] = 0;
+ }
+ if (v["repeating_infweapon_inf_weapon_traits_"+trait+"_selector"] !== undefined) {
+ selectList[trait] = parseInt(v["repeating_infweapon_inf_weapon_traits_"+trait+"_selector"]);
+ } else {
+ selectList[trait] = 0;
+ }
+ if (traitList[trait] == 1 && !infWeaponTraitsX.includes(trait)) {//if trait is 1 and not a trait with x, make it trait
+ traitString += capitalise(`${trait}, `);
+ if (selectList[trait] == 1 && trait !='ammo') {
+ select1 += capitalise(`${trait}, `);
+ } else if (selectList[trait] == 2 && trait !='ammo') {
+ select2 += capitalise(`${trait}, `);
+ } else if (selectList[trait] == 3 && trait !='ammo') {
+ select3 += capitalise(`${trait}, `);
+ } else if (traitList[trait] == 1 && trait =='ammo') {
+ //select1 += capitalise(`ammo, `);
+ }
+
+ } else if (traitList[trait] == 1 && infWeaponTraitsX.includes(trait)) {//if trait is 1 and is a trait with x, make it trait:1
+ traitString += capitalise(`${trait}:${traitList[trait]}, `);
+ if (selectList[trait] == 1 ) {
+ select1 += capitalise(`${trait}:${traitList[trait]}, `);
+ } else if (selectList[trait] == 2) {
+ select2 += capitalise(`${trait}:${traitList[trait]}, `);
+ } else if (selectList[trait] == 3) {
+ select3 += capitalise(`${trait}:${traitList[trait]}, `);
+ }
+ if (trait == 'aoe') {//change , to m,
+ traitString = traitString.replace(/,/g, "m, ");
+ };
+ } else if (traitList[trait] > 1) {// if triat is greater than 1, make it trait:x
+ traitString += capitalise(`${trait}:${traitList[trait]}, `);
+ if (selectList[trait] == 1) {
+ select1 += capitalise(`${trait}:${traitList[trait]}, `);
+ } else if (selectList[trait] == 2) {
+ select2 += capitalise(`${trait}:${traitList[trait]}, `);
+ } else if (selectList[trait] == 3) {
+ select3 += capitalise(`${trait}:${traitList[trait]}, `);
+ }
+ if (trait == 'aoe') {//change , to m,
+ traitString = traitString.replace(/,/g, "m, ");
+ };
+ } else if (traitList[trait] < 0) {// if triat is less than 0, make it trait:-x
+ traitString += capitalise(`${trait}:${traitList[trait]}, `);
+ if (selectList[trait] == 1) {
+ select1 += capitalise(`${trait}:${traitList[trait]}, `);
+ } else if (selectList[trait] == 2) {
+ select2 += capitalise(`${trait}:${traitList[trait]}, `);
+ } else if (selectList[trait] == 3) {
+ select3 += capitalise(`${trait}:${traitList[trait]}, `);
+ }
+ if (trait == 'aoe') {//change , to m,
+ traitString = traitString.replace(/,/g, "m, ");
+ };
+ }
+ //console.log("select 1 "+select1);
+ if (select1 == "") {
+ select1 = " ";
+ }
+ //commented out becasue it would remove all commas
+ //select1 = removeLastComma(select1);
+ //select2 = removeLastComma(select2);
+ //select3 = removeLastComma(select3);
+ setAttrs({
+ repeating_infweapon_inf_weapon_traits: select1,
+ repeating_infweapon_inf_weapon_traits_selector_2: select2,
+ repeating_infweapon_inf_weapon_traits_selector_3: select3,
+ });
+ });
+
+ });
+
+ });
+});
+
+
+
+
+
//changes gear weapon traits to a string of traits that each gear weapon
gearWeaponTraits.forEach(function (trait) {
on('change:repeating_gearweapon:gear_weapon_traits_'+trait+' change:repeating_gearweapon:gear_weapon_traits_'+trait+'_selector \
@@ -6109,6 +6292,8 @@ gearWeaponTraits.forEach(function (trait) {
});
});
+
+
// --- Damage Calulations ---
on('change:repeating_damage remove:repeating_damage sheet:opened', function() { //calculates total damage
repeatingSum("damage_shell","damage",["damage_shell_check", "damage_level"],'multiplier:damage_shell_check|-1','damage_shell_max');
@@ -6121,7 +6306,7 @@ on('change:repeating_gear-damage remove:repeating_gear-damage sheet:opened', fun
// ----- Burden Calculations -----
-on('change:repeating_weapon_domain sheet:opened', function() { //calculates domains to expertise used in personal weapons
+on('change:repeating_infweapon_domain sheet:opened', function() { //calculates domains to expertise used in personal weapons
repeatingSum("weapon_burden_total","weapon",["weapon_burden"]); //calculate weapon burden
});
@@ -6189,35 +6374,79 @@ gearSkills.forEach(function (gearSkills) { // roll buttons for gear skills NPC
});
});
+on("clicked:repeating_infweapon:tumble", function () {//roller for inf weapons
+ console.log("clicked:repeating_infweapon:tumble");
+ getAttrs(["whisper","repeating_infweapon_inf_weapon_frame_check","repeating_infweapon_frame_weapon_type","repeating_infweapon_inf_weapon_name","repeating_infweapon_inf_weapon_domain",
+ "repeating_infweapon_inf_skill_level","bonus_dice","tn","tn_bonus","expertise_check",
+ "reliable","repeating_infweapon_inf_weapon_skill_familiar", "repeating_infweapon_inf_weapon_damage",
+ "repeating_infweapon_inf_weapon_traits", "repeating_infweapon_inf_weapon_traits_ammo_selector", "repeating_infweapon_inf_weapon_traits_selector_2" ,
+ "repeating_infweapon_inf_weapon_traits_selector_3","repeating_infweapon_inf_weapon_traits_ammo","repeating_infweapon_inf_weapon_ammo","repeating_infweapon_inf_weapon_ammo_max",
+ "expertise_athletics","expertise_awareness","expertise_business","expertise_craft","expertise_culture","expertise_electronicwarfare","expertise_electronics","expertise_gunnery","expertise_hardscience","expertise_influence","expertise_institutions","expertise_investigation","expertise_mechanics","expertise_medicine","expertise_melee","expertise_pilot","expertise_showmanship","expertise_socialscience","expertise_survival","expertise_tactics"], (values) => {
+
+ let framecheck = parseInt(values["repeating_infweapon_inf_weapon_frame_check"]);
+ console.log("framecheck:"+framecheck);
+ console.log(values["repeating_infweapon_inf_weapon_name"]+values["repeating_infweapon_frame_weapon_type"]);
+ let infweapondomain = parseInt(values["repeating_infweapon_inf_weapon_domain"]);
+ let expertise = 0;
+ console.log("infweapondomain:" + infweapondomain);
+
+ let domain = findDomainLower(values["repeating_infweapon_inf_weapon_domain"]);
+ expertise = values["expertise_"+domain];
+ console.log("expertise "+expertise);
-on("clicked:repeating_weapon:tumble", function () { //roll for personal weapons
- getAttrs(["whisper","repeating_weapon_weapon_name","repeating_weapon_weapon_domain","repeating_weapon_skill_level",
- "repeating_weapon_weapon_expertise","expertise_check" ,"bonus_dice","tn","tn_bonus","reliable","repeating_weapon_skill_familiar"], (values) => {
- let whisper = values["whisper"];
- let names = [values["repeating_weapon_weapon_name"],values["repeating_weapon_weapon_domain"]];
- let expertise = parseInt(values["repeating_weapon_weapon_expertise"]);
+
+
let expertiseCheck = parseInt(values["expertise_check"]);
if (expertiseCheck != 1) {
expertise = 0;
}
- let dice = parseIntArray([values["repeating_weapon_skill_level"],values["bonus_dice"]]);
- let tn = parseIntArray([values["tn"],values["tn_bonus"]]);
- let reliabledice = parseIntArray([values["reliable"],values["repeating_weapon_skill_familiar"]]) ;
- let traits = [];
- rollArray(whisper,names,expertise,dice,tn,reliabledice,traits);
+ let whisper = values["whisper"];
+ let names = [values["repeating_infweapon_inf_weapon_name"],values["repeating_infweapon_inf_weapon_domain"]];
+ if (framecheck == 1){
+ names = [values["repeating_infweapon_frame_weapon_type"],values["repeating_infweapon_inf_weapon_domain"]];
+ } else {
+ names = [values["repeating_infweapon_inf_weapon_name"],values["repeating_infweapon_inf_weapon_domain"]];
+ }
+ let dice = parseIntArray([values["repeating_infweapon_inf_skill_level"],values["bonus_dice"]]);
+ let tn = parseIntArray([values["tn"], 0 ,values["tn_bonus"]]);
+ let reliabledice = parseIntArray([values["reliable"],values["repeating_infweapon_inf_weapon_skill_familiar"]]) ;
+ let damage = parseInt([values["repeating_infweapon_inf_weapon_damage"]]);
+ let traits = [values["repeating_infweapon_inf_weapon_traits_ammo_selector"],values["repeating_infweapon_inf_weapon_traits"],values["repeating_infweapon_inf_weapon_traits_selector_2"],
+ values["repeating_infweapon_inf_weapon_traits_selector_3"],values["repeating_infweapon_inf_weapon_traits_ammo"]];
+ let ammunition = [parseInt(values["repeating_infweapon_inf_weapon_ammo"]), parseInt(values["repeating_infweapon_inf_weapon_ammo_max"])] ;
+ if (ammunition[1] > 0){//weapon requires ammo
+ if (ammunition[0] > 0 ){//weapon has ammo
+ console.log("Firing weapon"+ammunition);;
+ } else {
+ startRoll("/w gm &{template:custom} {{title=@{character_name}}} {{desc=You have no ammo left for "+names[0]+"}}", (diceroll) => {
+ finishRoll(diceroll.rollId, {
+ });
+ });
+ return;
+ }
+ }
+ console.log(whisper,names,expertise,dice,tn,reliabledice,damage,ammunition,traits);
+ rollArrayInfWeapon(whisper,names,expertise,dice,tn,reliabledice,damage,ammunition,traits);
+
});
});
+
+
on("clicked:repeating_gearweapon:tumble", function () {//roller for gear weapons
- getAttrs(["whisper","repeating_gearweapon_gear_weapon_type","repeating_gearweapon_gear_weapon_domain",
+ getAttrs(["whisper","repeating_gearweapon_gear_weapon_type","repeating_gearweapon_gear_weapon_domain", "repeating_gearweapon_gear_weapon_gear_domain_tn",
"repeating_gearweapon_gear_skill_level","bonus_dice","tn","tn_bonus","expertise_check",
"reliable","repeating_gearweapon_gear_weapon_skill_familiar", "repeating_gearweapon_gear_weapon_damage",
"repeating_gearweapon_gear_weapon_traits", "repeating_gearweapon_gear_weapon_traits_ammo_selector", "repeating_gearweapon_gear_weapon_traits_selector_2" ,
"repeating_gearweapon_gear_weapon_traits_selector_3","repeating_gearweapon_gear_weapon_traits_ammo","repeating_gearweapon_gear_weapon_ammo","repeating_gearweapon_gear_weapon_ammo_max",
- "expertise_gunnery", "expertise_pilot","expertise_electronicwarfare","gear_gunnery", "gear_pilot","gear_electronicwarfare"], (values) => {
+ "expertise_athletics","expertise_awareness","expertise_business","expertise_craft","expertise_culture","expertise_electronicwarfare","expertise_electronics","expertise_gunnery","expertise_hardscience","expertise_influence","expertise_institutions","expertise_investigation","expertise_mechanics","expertise_medicine","expertise_melee","expertise_pilot","expertise_showmanship","expertise_socialscience","expertise_survival","expertise_tactics","gear_gunnery", "gear_pilot","gear_electronicwarfare"], (values) => {
- let gearweapondomain = parseInt(values["repeating_gearweapon_gear_weapon_domain"]);
+ let domain = findDomainLower(values["repeating_gearweapon_gear_weapon_domain"]);
+
+ let gearweapondomain = parseInt(values["repeating_gearweapon_gear_weapon_gear_domain_tn"]);
let expertise = 0;
+ expertise = values["expertise_"+domain];
+ console.log("expertise CHECK"+expertise);
let geartn = 0;
console.log("gearweapondomain:"+gearweapondomain);
if (gearweapondomain == 1) {
@@ -6235,7 +6464,7 @@ on("clicked:repeating_gearweapon:tumble", function () {//roller for gear weapons
expertise = 0;
}
let whisper = values["whisper"];
- let names = [values["repeating_gearweapon_gear_weapon_type"],values["repeating_gearweapon_gear_weapon_domain"]];
+ let names = [values["repeating_gearweapon_gear_weapon_type"],values["repeating_gearweapon_gear_weapon_domain"],values["repeating_gearweapon_gear_weapon_gear_domain_tn"]];
let dice = parseIntArray([values["repeating_gearweapon_gear_skill_level"],values["bonus_dice"]]);
let tn = parseIntArray([values["tn"], geartn ,values["tn_bonus"]]);
let reliabledice = parseIntArray([values["reliable"],values["repeating_gearweapon_gear_weapon_skill_familiar"]]) ;
@@ -6254,7 +6483,7 @@ on("clicked:repeating_gearweapon:tumble", function () {//roller for gear weapons
return;
}
}
- console.log(whisper,names,expertise,dice,tn,reliabledice,damage,ammunition,traits);
+ console.log("rollArrayGearWeapon:",whisper,names,expertise,dice,tn,reliabledice,damage,ammunition,traits);
rollArrayGearWeapon(whisper,names,expertise,dice,tn,reliabledice,damage,ammunition,traits);
});
@@ -6331,49 +6560,7 @@ const rollArray = (whisper = "" ,names,expertise,dice,tnArray,reliabledice, ...e
//domain = capitalise(domain);
- if (domain == 1) {
- domain = "Athletics";
- } else if (domain == 2){
- domain = "Awareness";
- } else if (domain == 3){
- domain = "Business";
- } else if (domain == 4){
- domain = "Craft";
- } else if (domain == 5){
- domain = "Culture";
- } else if (domain == 6){
- domain = "Electronic Warfare";
- } else if (domain == 7){
- domain = "Electronics";
- } else if (domain == 8){
- domain = "Gunnery";
- } else if (domain == 9){
- domain = "Hard Science";
- } else if (domain == 10){
- domain = "Influence";
- } else if (domain == 11){
- domain = "Institutions";
- } else if (domain == 12){
- domain = "Investigation";
- } else if (domain == 13){
- domain = "Mechanics";
- } else if (domain == 14){
- domain = "Medicine";
- } else if (domain == 15){
- domain = "Melee";
- } else if (domain == 16){
- domain = "Pilot";
- } else if (domain == 17){
- domain = "Showmanship";
- } else if (domain == 18){
- domain = "Social Science";
- } else if (domain == 19){
- domain = "Survival";
- } else if (domain == 20){
- domain = "Tactics";
- } else {
- domain = domain;
- };
+ domain = findDomainUpper(domain);
console.log(skillname, domain, "lv:"+ skilllevel, "Ex:"+ expertise,bonus,"tn:"+ tn,tnBonus,"Rel:"+ reliable);
startRoll(whisper+"&{template:diceroll} {{title=@{character_name} using "+skillname+"}} \
@@ -6505,8 +6692,8 @@ const rollArray = (whisper = "" ,names,expertise,dice,tnArray,reliabledice, ...e
//damage is a number
//ammo is an array [ammo,ammomax] as values
//traits is an array [ammo,,selector1,selector2,selector3,ammo check] as number, string, string, string, number
-const rollArrayGearWeapon = (whisper = "" ,names,expertise,dice,tnArray,reliabledice,damage= 0 ,ammunition = [0,0],traits = [1,"","","",0], ...extra) => {
- console.log("rollArrayGearWeapon");
+const rollArrayInfWeapon = (whisper = "" ,names,expertise,dice,tnArray,reliabledice,damage= 0 ,ammunition = [0,0],traits = [1,"","","",0], ...extra) => {
+ console.log("rollArrayInfWeapon");
console.log(whisper,names,expertise,dice,tnArray,reliabledice,damage,ammunition,traits, ...extra)
console.log(" names"+names," expertise"+expertise," dice"+dice," tnArray"+tnArray," reliabledice"+reliabledice," damage"+damage," traits"+traits," ammunition"+ammunition);
let weaponname = names[0];
@@ -6522,14 +6709,606 @@ const rollArrayGearWeapon = (whisper = "" ,names,expertise,dice,tnArray,reliable
let traittext = "";
let reroll = 0;
- if (domain == 1){
- domain = "Gunnery";
- } else if (domain == 2){
- domain = "Pilot";
- } else if (domain == 3){
- domain = "Electronic Warfare";
+
+ domain = findDomainUpper(domain);
+
+ let ammunitionString = "";
+ let currentAmmunition = 0;
+ if (ammunition[0]==0) {
+ ammunitionString = "";
+ } else {
+ ammunitionString = "Ammo:("+ammunition[0]+"/"+ammunition[1]+")";
+ currentAmmunition = (ammunition[0]-1);
+
}
+ //'ammo'
+ console.log("ammo:"+traits[0],"traits:"+traits[1],"selector2:"+traits[2],"selector3:"+traits[3],"ammo check:"+traits[4]);
+ if (traits[0] == 2 && traits[4] == 1) {
+ console.log("ammo2:"+traits[2]);
+ traitstring = traitstring.concat(", Ammo(",removeLastComma(traits[2]),")") ;
+ traitcalcs = traitcalcs.concat(", ",removeLastComma(traits[2])) ;
+ } else if (traits[0] == 3 && traits[4] == 1){
+ console.log("ammo3:"+traits[3]);
+ traitstring = traitstring.concat(", Ammo(",removeLastComma(traits[3]),")") ;
+ traitcalcs = traitcalcs.concat(", ",removeLastComma(traits[3])) ;
+ }
+ traitstring = removeLastComma(traitstring);
+ console.log("traitstring:"+traitstring);
+ console.log("traitcalcs:"+traitcalcs);
+
+ //Trait calculations
+ //blast is needed in other calcs, so done first
+ let blastindex = traitcalcs.indexOf("Blast");//'blast', calculate cover from primary target
+ let blast = 0;
+ if (blastindex > 0) {
+ blast = 1;
+ traittext = traittext.concat("Calculate cover from primary target\n") ;
+ } else {
+ blast = 0;
+ }
+ let advancedindex = traitcalcs.indexOf("Advanced");//'advanced', cannot pickup and used
+ let advanced = 0;
+ if (advancedindex > 0) {
+ advanced = 1;
+ traittext = traittext.concat("Advanced: This weapon but must be connected to a power source, usually the vehicle’s engine. If not connected it cannot be fired. This connection takes 1 hour of time working on the vehicle and is not something that can be done in combat.\n") ;
+ } else {
+ advanced = 0;
+ }
+
+ let aoeindex = traitcalcs.indexOf("AoE");//'aoe', remind to roll for secondary targets at -1d6, check blast
+ let aoe = 0;
+ let aoequestion = "";
+ let aoeexpression = "";
+ let aoeroll = "";
+ let aoetargetquestion = "";
+ if (aoeindex > 0) {
+ aoeindex += 4;
+ aoe = parseInt(traitcalcs.substring(aoeindex));
+ console.log("aoe:"+aoe);
+ aoequestion = "?{AoE("+aoe+"m) Primary Target|Yes,0|No,-1}";
+ aoetargetquestion = "?{AoE("+aoe+"m) Additional Secondary Targets|0}";
+ aoeexpression = "+("+aoequestion+")(AoE) ";
+ aoeroll = "+"+aoequestion;
+ if (blast == 1){
+ traittext = traittext.concat("AoE:"+aoe+" Hits all targets within "+aoe+"m/ "+Math.floor(aoe/5)+"in. Roll secondary targets individually. Calculate cover from target's perspective.\n") ;
+ } else {
+ traittext = traittext.concat("AoE:"+aoe+" Hits all targets within "+aoe+"m/ "+Math.floor(aoe/5)+"in. Roll secondary targets individually. Calculate cover from firer's perspective. Targets in full medium or heavy cover are not hit.\n") ;
+
+ }
+ } else {
+ aoe = 0;
+ aoequestion = "";
+ }
+
+ let apindex = traitcalcs.indexOf("AP");//'ap', may choose to apply MOS as damage instead of normal damage calculation, min 1
+ let ap = 0;
+ if (apindex > 0) {
+ apindex += 3;
+ ap =parseInt(traitcalcs.substring(apindex));
+ traittext = traittext.concat("AP:"+ap+" May choose to apply MOS up to "+ap+" as damage instead of normal damage calculation, min 1\n") ;
+ } else {
+ ap = 0;
+ }
+
+
+ let burstindex = traitcalcs.indexOf("Burst");//'burst', adds xd6 or x-1d6, but get second attack roll
+ let burst = 0;
+ let burstquestion = "";
+ let burstexpression = "";
+ let burstroll = "";
+ let bursttargetquestion = "";
+ if (burstindex > 0) {
+ burstindex += 6;
+ burst =parseInt(traitcalcs.substring(burstindex));
+ burstquestion = "?{Burst("+burst+") Attacking|Only one target,0|Two Targets ,1|Multi-Target Tracking,"+burst+"}";
+ burstexpression = "+ ("+burst+"-"+burstquestion+")(Burst) ";
+ burstroll = "+"+burst+"-"+burstquestion;
+ bursttargetquestion = burstquestion;
+ } else {
+ burst = 0;
+ }
+ //brace
+ let braceindex = traitcalcs.indexOf("Brace");//'brace', free aim if Stationary, Dug in, or Braced
+ let brace = 0;
+ let bracequestion = "";
+ let braceexpression = "";
+ let braceroll = "";
+ if (braceindex > 0) {
+ brace = 1;
+ traittext = traittext.concat("Brace: Free aim if Stationary, Dug in, or Braced\n") ;
+ } else {
+ brace = 0;
+ }
+
+ let corrosionindex = traitcalcs.indexOf("Corrosion");//'corrosion', targets gain corrosion condition, take 1 damage at end of turn
+ let corrosion = 0;
+ if (corrosionindex > 0) {
+ corrosion = 1;
+ traittext = traittext.concat("Corrosion: Target gains Corrosion condition and takes 1 damage at end of each turn until removed\n") ;
+ } else {
+ corrosion = 0;
+ }
+
+ let demoindex = traitcalcs.indexOf("Demo");//'demo', can do x damage to buildings instead of normal damage
+ let demo = 0;
+ if (demoindex > 0) {
+ demoindex += 5;
+ demo =parseInt(traitcalcs.substring(demoindex));
+ traittext = traittext.concat("Demo:"+demo+" Can do "+demo+" damage to buildings instead of normal damage\n") ;
+ } else {
+ demo = 0;
+ demoquestion = "";
+ }
+
+ let fireindex = traitcalcs.indexOf("Fire");//'fire', The first damage result this weapon deals is either Burned or On Fire. In addition, if the attack hit, roll Xd6 after the attack is resolved. For each 4+ the target sustains 1 additional damage.
+ let fire = 0;
+ let fireroll = "-1";
+ if (fireindex > 0) {
+ fireindex += 5;
+ console.log("fireindex:"+traitcalcs.charAt(fireindex));
+ fire =parseInt(traitcalcs.substring(fireindex));
+ console.log("fire:"+fire);
+ fireroll = fire+"d6>4";
+ traittext = traittext.concat("Fire:"+fire+" First damage result is either Burned or On Fire. Deals additional damage on hit.\n") ;
+ } else {
+ fire = 0;
+ }
+
+
+
+ //'forearm', not used
+ let fragindex = traitcalcs.indexOf("Frag");//'frag', The weapon gains +2d6 when attacking targets in optimal range. This weapon gains an additional +1d6 when attacking Swarms can deal full damage to Swarms.
+ let fragquestion = "";
+ let fragquestion1 = "";
+ let fragquestion2 = "";
+ let fragexpression = "";
+ let fragroll = "";
+ if (fragindex > 0) {
+ frag = 1;
+ fragquestion1 = "("+optimalquestion+"*2)";
+ optimalrequired = 1;
+ fragquestion2 = "?{Frag: Attacking Swarms|No,0|Yes,1}";
+ fragquestion = fragquestion1+"+"+fragquestion2;
+ fragexpression = "("+fragquestion+"(Frag)) ";
+ fragroll = "+"+fragquestion1+"+"+fragquestion2;
+ traittext = traittext.concat("Frag: Deals full damage when attacking Swarms\n") ;
+ } else {
+ frag = 0;
+ }
+
+
+ let haywireindex = traitcalcs.indexOf("Haywire");//'haywire', targets gain haywire condition, take -1 gu pi ew
+ let haywire = 0;
+ if (haywireindex > 0) {
+ haywire = 1;
+ traittext = traittext.concat("Haywire: Target gains Haywire condition and takes -1 GU PI EW\n") ;
+ } else {
+ haywire = 0;
+ }
+
+ ///''indirect'
+ let indirectindex = traitcalcs.indexOf("Indirect");//'indirect', can fire over cover, but cannot aim
+ let indirect = 0;
+ if (indirectindex > 0) {
+ indirect = 1;
+ traittext = traittext.concat("Indirect: This weapon may be used to make indirect attacks. See Chapter 11 for more information on indirect attacks.\n") ;
+ } else {
+ indirect = 0;
+ }
+
+ //'missile'
+ let missileindex = traitcalcs.indexOf("Missile");//'missile', can be fired at targets out of line of sight, but cannot aim
+ let missile = 0;
+ if (missileindex > 0) {
+ missile = 1;
+ traittext = traittext.concat("Missile: Unless it hits something, this weapon will continue along its trajectory for up to 1km before self-destructing (though it will likely be well off target by then). You may suffer TB:1 to double the range bands of this weapon (this stacks with Aiming bonuses). When benefiting from Guided, these weapons will travel 4km each round seeking their target. They will continue in this way out to 12km.\n") ;
+ } else {
+ missile = 0;
+ }
+
+ //'musclepowered',
+ let musclepoweredindex = traitcalcs.indexOf("Muscle Powered");//'musclepowered', cannot pickup and used
+ let musclepowered = 0;
+ if (musclepoweredindex > 0) {
+ musclepowered = 1;
+ traittext = traittext.concat("Muscle Powered: These weapons require the strength of the user to do damage. They will gain bonus damage (or suffer a penalty to damage) from certain perks and flaws or character advantages.\n") ;
+ } else {
+ musclepowered = 0;
+ }
+
+ //'nonlethal',
+ let nonlethalindex = traitcalcs.indexOf("Non-Lethal");//'nonlethal', cannot kill
+ let nonlethal = 0;
+ if (nonlethalindex > 0) {
+ nonlethal = 1;
+ traittext = traittext.concat("Non-Lethal: These weapons use less lethal ammunition and will only kill at Director discretion (such as if all the defenders dice result in 1s). For guns firing non-lethal rounds, halve effective damage (down to a minimum of 1). Conditions applied should represent bruises, disorientation, broken bones, or unconsciousness. Note: these rounds can still kill if targeted at the head of unarmored people.\n") ;
+ } else {
+ nonlethal = 0;
+ }
+
+ //'poison','
+ let poisonindex = traitcalcs.indexOf("Poison");//'poison', targets gain poison condition, take 1 damage at end of turn
+ let poison = 0;
+ if (poisonindex > 0) {
+ poison = 1;
+ traittext = traittext.concat("Poison: These weapons are usually coated with poison. See the Poisoned damage result (Chapter 3). The dart itself remains non-lethal, even if the poison is. If a number is listed in damage, assume this is for the weapon rather than the poison itself\n") ;
+ } else {
+ poison = 0;
+ }
+
+ //placed',
+ let placedindex = traitcalcs.indexOf("Placed");//'placed', cannot pickup and used
+ let placed = 0;
+ if (placedindex > 0) {
+ placed = 1;
+ traittext = traittext.concat("Placed: If used in a melee attack, this weapon suffers -1d6. It can be placed at the cost of an action and later detonated, which will suffer no penalty. When placing the charge, usea relevant demolitions skill instead of melee to determine how good the placement is. Apply damage when the charge is detonated\n") ;
+ } else {
+ placed = 0;
+ }
+
+ //shock
+ let shockindex = traitcalcs.indexOf("Shock");//'shock', targets gain shock condition, take -1d6
+ let shock = 0;
+ if (shockindex > 0) {
+ shock = 1;
+ traittext = traittext.concat("Shock: Add the damage of the weapon to the MoS. The target must then make an independent roll at 3d6 against this MoS+damage or suffer the Stunned damage result for a number of rounds equal to the MoF. An MoF of 6 or more causes them to suffer the Knocked Out condition. If struck over successive rounds, add +1 to the initial MoS for each round\n") ;
+ } else {
+ shock = 0;
+ }
+
+ //'slashing', This weapon relies on exposed flesh to wound. It doubles the value of any armor an opponent has.
+ let slashingindex = traitcalcs.indexOf("Slashing");//'slashing', This weapon relies on exposed flesh to wound. It doubles the value of any armor an opponent has.
+ let slashing = 0;
+ if (slashingindex > 0) {
+ slashing = 1;
+ traittext = traittext.concat("Slashing: This weapon relies on exposed flesh to wound. It doubles the value of any armor an opponent has.\n") ;
+ } else {
+ slashing = 0;
+ }
+ //'shot'
+ let shotindex = traitcalcs.indexOf("Shot");//'shot', can be fired at targets out of line of sight, but cannot aim
+ let shot = 0;
+ if (shotindex > 0) {
+ shot = 1;
+ traittext = traittext.concat("Shot:These weapons have poor impact against armored targets. Double the target’s armor when using them.\n") ;
+ } else {
+ shot = 0;
+ }
+
+ //'stun',
+ let stunindex = traitcalcs.indexOf("Stun");//'stun', targets gain stun condition, take -1d6
+ let stun = 0;
+ if (stunindex > 0) {
+ stun = 1;
+ traittext = traittext.concat("Stun: This weapon is effective at stunning or disorienting its target. Instead of causing damage this weapon will cause Rattled, Deafened, Blinded, and Stunned conditions, in that order, with each MoS representing one condition.\n") ;
+ } else {
+ stun = 0;
+ }
+
+ //'thrown'
+ let thrownindex = traitcalcs.indexOf("Thrown");//'thrown', can be thrown, but cannot aim
+ let thrown = 0;
+ if (thrownindex > 0) {
+ thrown = 1;
+ traittext = traittext.concat("Thrown: This weapon can be thrown. The optimum range for a throw is up to 10m for each point of toughness. Additionally, you may add 10 meters if you have an appropriate aptitude. The maximum effective range of thrown weapons is 2x the optimum range. Aiming does not increase the range of thrown weapons.\n") ;
+ } else {
+ thrown = 0;
+ }
+
+
+ let preciseindex = traitcalcs.indexOf("Precise");//'precise', improves MOS/MOF by 1 when in optimal range
+ let precisequestion = "";
+ let preciseexpression = "";
+ let preciseroll = "";
+ if (preciseindex > 0) {
+ precise = 1;
+ precisequestion = optimalquestion;
+ optimalrequired = 1;
+ preciseexpression = "+ Precise:"+precisequestion;
+ preciseroll = "+"+precisequestion;
+ } else {
+ precise = 0;
+ }
+
+
+ //'reach',
+ let reachindex = traitcalcs.indexOf("Reach");//'reach',fighting in melee against an opponent without reach, you may impose TP:1 to their defense roll
+ let reach = 0;
+ if (reachindex > 0) {
+ reach = 1;
+ traittext = traittext.concat("Reach: Fighting in melee against an opponent without reach, you may impose TP:1 to their defense roll\n") ;
+ } else {
+ reach = 0;
+ }
+ //'silent'
+ let silentindex = traitcalcs.indexOf("Silent");//'silent', no sound, no damage, no smoke
+ let silent = 0;
+ if (silentindex > 0) {
+ silent = 1;
+ damage = 0;
+ traittext = traittext.concat("Silent: Suppressed noise\n") ;
+ } else {
+ silent = 0;
+ }
+
+ let smokeindex = traitcalcs.indexOf("Smoke");//'smoke', creates smoke
+ let smoke = 0;
+ if (smokeindex > 0) {
+ smoke = 1;
+ traittext = traittext.concat("Smoke: Creates Smoke in AoE"+aoe+"m, gives +1d6 to defense in area\n") ;
+ } else {
+ smoke = 0;
+ }
+
+ let sprayindex = traitcalcs.indexOf("Spray");//'spray', ignores cover, cannot aim
+ let spray = 0;
+ if (sprayindex > 0) {
+ spray = 1;
+ traittext = traittext.concat("Spray: Ignores cover, cannot aim\n") ;
+ } else {
+ spray = 0;
+ }
+ let salvoindex = traitcalcs.indexOf("Salvo");//'salvo'
+ let salvoexpression = "";
+ let salvoquestion = "";
+ let salvoroll = "";
+
+ let salvo = 0;
+ if (salvoindex > 0) {
+ salvo = 1;
+ salvoquestion = "?{Rocket Salvo: Use extra ammo?\nAmmo Remaining "+currentAmmunition+"|0}";
+ salvoexpression = "+"+salvoquestion+"(Salvo)";
+ salvoroll = "+"+salvoquestion;
+ traittext = traittext.concat("Salvo: When attacking with this weapon, the user may increase the ammo used on the attack by 1 to \
+ add +1d6 to the attack roll against a single target. This may be done multiple times, but can only be used once per target.\n") ;
+ } else {
+ salvo = 0;
+ }
+
+//'see_desc'
+ let seedescindex = traitcalcs.indexOf("See desc");//'see_desc', not used\
+ console.log("seedescindex:"+seedescindex);
+ let seedesc = 0;
+ if (seedescindex > 0) {
+ seedesc = 1;
+ traittext = traittext.concat("See Description for more details.\n") ;
+ } else {
+ seedesc = 0;
+ }
+ // end of inf traits calc
+
+
+
+ //calculate bonus dice as all dice[] exept the first
+ let bonus = 0;
+ for (i=1;i
6) { //tn over 6 is converted to bonus dice at 1:2
+ bonus = bonus - Math.floor((tn - 6)/2);
+ tn = 6;
+ console.log("tn reduced due to tn over 6. bonus:", bonus, tn);
+ };
+
+ if (skilllevel == 0) {
+ bonus--;
+ console.log("bonus reduced due to skill level 0",bonus,skilllevel);
+ };
+
+ if (skilllevel + bonus < 0) {// work out if dice pool is keeping or dropping dice
+ console.log("skilllevel + bonus < 1",skilllevel,bonus);
+ rollexpression = "d6kl1s";// d6 keep lowest 1, sort ascending
+ } else {
+ rollexpression = "d6k1s"; // d6 keep highest 1, sort ascending
+ };
+
+
+
+ if (optimalrequired == 1) {
+ optimalroll = optimalquestion;
+ } else {
+ optimalroll = "";
+ }
+
+ let traitroll = reliableRoll+aoeroll+burstroll+fragroll;
+ let traitexpression = aoeexpression+burstexpression+fragexpression+preciseexpression;
+
+ console.log("startRollInfWeap", weaponname, domain, "lv:"+ skilllevel, "Ex:"+ expertise,bonus,"tn:"+ tn,tnBonus,"Rel:"+ reliable);
+ startRoll(whisper+"&{template:weapondiceroll} {{title=@{character_name} using "+weaponname+" }} \
+ {{subtitle= Expertise ("+domain+"):"+expertise+" TN("+tn+") "+ammunitionString+"\n ("+skilllevel+"(Skill) +"+bonus+"(Bonus))+ "+reliable+"(Reliable)"+traitexpression+"+1(Base) \n Traits="+traitstring+" }} \
+ {{roll=[[("+diceRoll+"+"+traitroll+"+1)"+rollexpression+""+preciseroll+"]] }} {{MOS=[[0]]}} {{doubles=[["+expertise+"]]}} {{reliable=[["+reliable+"]]}} \
+ {{six=[[6]]}} {{five=[[5]]}} {{four=[[4]]}} {{three=[[3]]}} {{two=[[2]]}} {{one=[[1]]}} \
+ {{changedsix=[[6]]}} {{changedfive=[[5]]}} {{changedfour=[[4]]}} {{changedthree=[[3]]}} {{changedtwo=[[2]]}} {{changedone=[[1]]}} \
+ {{damageroll=[["+damage+"]]}} {{firedamage=[["+fireroll+"]]}} {{precice=[["+preciseroll+"+0]]}} {{AP=[["+ap+"]]}} {{traits="+traittext+"}} \
+ }}" , (diceroll) => {
+
+ let total = diceroll.results.roll.result;
+ console.log("total:"+total);
+ let dice = diceroll.results.roll.dice;
+ let MOS = 0;
+ let doubles = 0;
+ let damageroll = diceroll.results.damageroll.result;
+ let firedamage = diceroll.results.firedamage.result;
+ let precicerolldice = diceroll.results.precice.result;
+
+ console.log("firedamage:"+firedamage);
+ console.log("damageroll:"+damageroll);
+ console.log("precicerolldice:"+precicerolldice);
+
+ const total6s = dice.filter(d => d === 6).length;
+ const total5s = dice.filter(d => d === 5).length;
+ const total4s = dice.filter(d => d === 4).length;
+ const total3s = dice.filter(d => d === 3).length;
+ const total2s = dice.filter(d => d === 2).length;
+ const total1s = dice.filter(d => d === 1).length;
+
+
+ const targetArray = [0,total1s,total2s,total3s,total4s,total5s,total6s];
+ const reliableArray = [reliable,0,0,0,0,0,0];//records reliable dice
+ console.log(targetArray,total);
+ let tempArray = targetArray;
+
+ //remove x reliable dice
+ //if reliable is greater than 1, remove x lowest
+ if (reliableArray[0] > 0) {
+ console.log("reliable is greater than 1");
+ while (reliableArray[0] > 0) {
+ for (i=1;i<7;i++) {
+ if (tempArray[i] > 0) {
+ tempArray[i]--;
+ reliableArray[i]--;
+ reliableArray[0]--;
+ i = 7;
+ };
+ };
+ };
+ };
+
+ //if reliable is less than 1, remove x highest
+ if (reliableArray[0] < 0) {
+ console.log("reliable is less than 1");
+ while (reliableArray[0] < 0) {
+ for (i=6;i>0;i--) {
+ if (tempArray[i] > 0) {
+ tempArray[i]--;
+ reliableArray[i]--;
+ reliableArray[0]++;
+ i = 0;
+ };
+ };
+ };
+ };
+ //calculate total
+ //if (skilllevel + bonus) is >0 find highest number
+ if ((skilllevel + bonus) > 0) {
+ for (i=6;i>0;i--) {
+ if (tempArray[i] > 0) {
+ console.log("calculate high total :"+i);
+ total = i;
+ i = 0;
+ };
+ };
+ };
+ //if (skilllevel + bonus) is <1 find lowest number
+ if ((skilllevel + bonus) < 1) {
+ for (i=1;i<7;i++) {
+ if (tempArray[i] > 0) {
+ console.log("calculate low total :"+i);
+ total = i;
+ i = 7;
+ };
+ };
+ };
+ tempArray[total]--;//take out the total from the array
+ if (total < tn) { //tn is bigger, calculate MOF
+ } else { //tn is smaller, calculate MOS
+ for (i=tn;i<=total;i++) {
+ MOS = MOS + tempArray[i];
+ };
+ };
+ if (expertise > 1) {
+ for (i=1;i 1){
+ tempArray[i] = tempArray[i] - 2;
+ doubles = i;
+ total++;
+ i = tn;
+
+ console.log("doubles:"+doubles+" added " + tempArray);
+ };
+ };
+ };
+ total = total + MOS + precicerolldice;//precise is only in optimalrange
+ MOS = total - tn;
+ console.log("temp:"+tempArray,"reliable+:"+reliableArray);
+
+ finishRoll(diceroll.rollId, {
+ roll: total,
+ MOS: MOS,
+ six: total6s,
+ five: total5s,
+ four: total4s,
+ three: total3s,
+ two: total2s,
+ one: total1s,
+ changedsix: reliableArray[6],
+ changedfive: reliableArray[5],
+ changedfour: reliableArray[4],
+ changedthree: reliableArray[3],
+ changedtwo: reliableArray[2],
+ changedone: reliableArray[1],
+ doubles: doubles,
+ damageroll: damageroll,
+ firedamage: firedamage
+ });
+ //Option: multiple rolls for AoE??
+ });
+};
+
+
+//function that takes in arrays of names, dicepool, tn and reliable and outputs the sums of each
+//names[] is an array [skillname,domain]
+//expertise is a number
+//dicepool[] is an array [skilllevel,bonusdice....] as values
+//tn[] is an array [tn,tnbonus....] as values
+//reliable[] is an array [reliable,reliablebonus....] as values
+//damage is a number
+//ammo is an array [ammo,ammomax] as values
+//traits is an array [ammo,,selector1,selector2,selector3,ammo check] as number, string, string, string, number
+const rollArrayGearWeapon = (whisper = "" ,names,expertise,dice,tnArray,reliabledice,damage= 0 ,ammunition = [0,0],traits = [1,"","","",0], ...extra) => {
+ console.log("rollArrayGearWeapon");
+ console.log(whisper,names,expertise,dice,tnArray,reliabledice,damage,ammunition,traits, ...extra)
+ console.log(" names"+names," expertise"+expertise," dice"+dice," tnArray"+tnArray," reliabledice"+reliabledice," damage"+damage," traits"+traits," ammunition"+ammunition);
+ let weaponname = names[0];
+ let skilllevel = parseInt(dice[0]);
+ let domain = names[1];
+ let gearTNname = parseInt(names[2]);
+ let tn = parseInt(tnArray[0]);
+ let rollexpression = "d6kl1";
+ let traitstring = traits[1];
+ let traitcalcs = traits[1];
+ let optimalquestion = "?{Attacking targets in optimal range|Yes,1|No,0}";
+ let optimalrequired = 0;
+ let optimalroll = "";
+ let traittext = "";
+ let reroll = 0;
+
+ domain = findDomainUpper(domain);
+ console.log("gearTNname:"+gearTNname);
+ if (gearTNname == 1){
+ gearTNname = "Gunnery";
+ } else if (gearTNname == 2){
+ gearTNname = "Pilot";
+ } else if (gearTNname == 3){
+ gearTNname = "Electronic Warfare";
+ }
+ console.log("gearTNname:"+gearTNname);
let ammunitionString = "";
let currentAmmunition = 0;
if (ammunition[0]==0) {
@@ -6841,7 +7620,7 @@ const rollArrayGearWeapon = (whisper = "" ,names,expertise,dice,tnArray,reliable
} else {
seedesc = 0;
}
- // end of gear traits calc //salvo is See Desc of Artillery Rockets and Rocket Pods
+ // end of gear traits calc
@@ -6904,9 +7683,9 @@ const rollArrayGearWeapon = (whisper = "" ,names,expertise,dice,tnArray,reliable
let traitroll = reliableRoll+aoeroll+burstroll+flakroll+fragroll+guidedroll+linkroll+salvoroll+brawlerroll;
let traitexpression = aoeexpression+brawlerexpression+burstexpression+flakexpression+fragexpression+linkexpression+salvoexpression+preciseexpression;
- console.log(weaponname, domain, "lv:"+ skilllevel, "Ex:"+ expertise,bonus,"tn:"+ tn,tnBonus,"Rel:"+ reliable);
+ console.log("startRollGearWeap",weaponname, domain, "lv:"+ skilllevel, "Ex:"+ expertise,bonus,"tn:"+ tn,tnBonus,"Rel:"+ reliable);
startRoll(whisper+"&{template:weapondiceroll} {{title=@{character_name} using "+weaponname+" }} \
- {{subtitle= Expertise ("+domain+"):"+expertise+" TN("+tn+") "+ammunitionString+"\n ("+skilllevel+"(Skill) +"+bonus+"(Bonus))+ "+reliable+"(Reliable)"+traitexpression+"+1(Base) \n Traits="+traitstring+" }} \
+ {{subtitle= Expertise ("+domain+"):"+expertise+" TN("+tn+")"+gearTNname+" "+ammunitionString+"\n ("+skilllevel+"(Skill) +"+bonus+"(Bonus))+ "+reliable+"(Reliable)"+traitexpression+"+1(Base) \n Traits="+traitstring+" }} \
{{roll=[[("+diceRoll+"+"+traitroll+"+1)"+rollexpression+""+preciseroll+"]] }} {{MOS=[[0]]}} {{doubles=[["+expertise+"]]}} {{reliable=[["+reliable+"]]}} \
{{six=[[6]]}} {{five=[[5]]}} {{four=[[4]]}} {{three=[[3]]}} {{two=[[2]]}} {{one=[[1]]}} \
{{changedsix=[[6]]}} {{changedfive=[[5]]}} {{changedfour=[[4]]}} {{changedthree=[[3]]}} {{changedtwo=[[2]]}} {{changedone=[[1]]}} \
@@ -7032,7 +7811,7 @@ const rollArrayGearWeapon = (whisper = "" ,names,expertise,dice,tnArray,reliable
damageroll: damageroll,
firedamage: firedamage
});
- //multiple rolls for AoE
+ ////Option: multiple rolls for AoE??
});
};
@@ -7102,11 +7881,12 @@ const multistats = {
'domain_expertise_influence','domain_expertise_institutions','domain_expertise_investigation','domain_expertise_mechanics','domain_expertise_medicine',
'domain_expertise_melee','domain_expertise_pilot','domain_expertise_showmanship','domain_expertise_socialscience','domain_expertise_survival','domain_expertise_tactics']},
- '$weapon_expertise': {rule: 'pick_from_list', attributes: ['$weapon_domain', 'expertise_athletics','expertise_awareness','expertise_business','expertise_craft',
+ '$infweapon_expertise': {rule: 'pick_from_list', attributes: ['$weapon_domain', 'expertise_athletics','expertise_awareness','expertise_business','expertise_craft',
'expertise_culture','expertise_electronicwarfare','expertise_electronics','expertise_gunnery','expertise_hardscience','expertise_influence','expertise_institutions','expertise_investigation',
- 'expertise_mechanics','expertise_medicine','expertise_melee','expertise_pilot','expertise_showmanship','expertise_socialscience','expertise_survival','expertise_tactics'], section: 'weapon'},
+ 'expertise_mechanics','expertise_medicine','expertise_melee','expertise_pilot','expertise_showmanship','expertise_socialscience','expertise_survival','expertise_tactics'], section: 'infweapon'},
'$gear_weapon_ammunition_show':{rule: 'ammo_find', attributes:['$gear_weapon_ammo_max'], section: 'gearweapon'},
+ '$inf_weapon_ammunition_show':{rule: 'ammo_find', attributes:['$inf_weapon_ammo_max'], section: 'infweapon'},
};
domains.forEach(domains => {
multistats[`expertise_${domains}`] = {rule: 'calc_expertise', attributes: [`domain_expertise_${domains}`], values: -1};