Files
Richard W f7e47cf154 New rolltemplate, more responsive with dice in rows
Legacy option for dice in columns option still, can be set as default in game settings.
2025-02-06 08:33:09 +01:00

2460 lines
112 KiB
JavaScript

/* beautify preserve:start */
"use strict";
const silent = {silent:true};
//-------------------------------------------------------------------------------------
// orcsAsync - https://github.com/onyxring/orcsAsync
// Compensates for a defect in the underlying Roll20 system, where it "loses context"
// (forgets which player is active) during asynchronous methods and callbacks,
// resulting in error messages like:
//
// Error: Trying to do getAttrs when no character is active in sandbox.
//
// Include this script at the top of your character sheet's code setion to have
// setTimeout() and setInterval() just start working as expected; no additional setup
// required. Additionally, async-safe versions of the typical attribute functions will
// be available:
//
// getAttrsAsync
// setAttrsAsync
// getSectionIDsAsync
//
function isRunningOnServer() { return self.dispatchEvent == undefined; }
function setActiveCharacterId(charId){
var oldAcid=getActiveCharacterId();
var msg={"id":"0", "type":"setActiveCharacter", "data":charId};
if(isRunningOnServer()==false){ //if in a browser, use "dispatchEvent" to process the message
var ev = new CustomEvent("message");
ev.data=msg;
self.dispatchEvent(ev);
}else{ //otherwise, use the API (server) message processor, "onmessage"
self.onmessage({data:msg});
}
return oldAcid; //return what the value used to be, so calling code can be a little cleaner
}
/*
var _sIn=setInterval;
setInterval=function(callback, timeout){
var acid=getActiveCharacterId();
_sIn(
function(){
var prevAcid=setActiveCharacterId(acid);
callback();
setActiveCharacterId(prevAcid);
}
,timeout);
}
var _sto=setTimeout
setTimeout=function(callback, timeout){
var acid=getActiveCharacterId();
_sto(
function(){
var prevAcid=setActiveCharacterId(acid);
callback();
setActiveCharacterId(prevAcid);
}
,timeout);
}
*/
function getAttrsAsync(props){
var acid=getActiveCharacterId(); //save the current activeCharacterID in case it has changed when the promise runs
var prevAcid=null; //local variable defined here, because it needs to be shared across the promise callbacks defined below
return new Promise((resolve,reject)=>{
prevAcid=setActiveCharacterId(acid); //in case the activeCharacterId has changed, restore it to what we were expecting and save the current value to restore later
try{
getAttrs(props,(values)=>{ resolve(values); });
}
catch{ reject(); }
}).finally(()=>{
setActiveCharacterId(prevAcid); //restore activeCharcterId to what it was when the promise first ran
});
}
//use the same pattern for each of the following...
function setAttrsAsync(propObj, options){
var acid=getActiveCharacterId();
var prevAcid=null;
return new Promise((resolve,reject)=>{
prevAcid=setActiveCharacterId(acid);
try{
setAttrs(propObj,options,(values)=>{ resolve(values); });
}
catch{ reject(); }
}).finally(()=>{
setActiveCharacterId(prevAcid);
});
}
function getSectionIDsAsync(sectionName){
var acid = getActiveCharacterId();
var prevAcid=null;
return new Promise((resolve,reject)=>{
prevAcid = setActiveCharacterId(acid);
try{
getSectionIDs(sectionName,(values)=>{ resolve(values); });
}
catch{ reject(); }
}).finally(()=>{
setActiveCharacterId(prevAcid);
});
}
function getSingleAttrAsync(prop){
var acid=getActiveCharacterId();
var prevAcid=null;
return new Promise((resolve,reject)=>{
prevAcid=setActiveCharacterId(acid);
try{
getAttrs([prop],(values)=>{ resolve(values[prop]); });
}
catch{ reject(); }
}).finally(()=>{
setActiveCharacterId(prevAcid);
});
}
/* END ASYNC FUNCTIONS */
/*
SPINES
*/
const mainAttributesArr = [
{name:"strength",attr:"strength",abbr:"strength-abbr",damage:"damage"},
{name:"strength_monster",attr:"strength_monster",abbr:"strength-abbr",damage:"damage"},
{name:"agility",attr:"agility",abbr:"agility-abbr",damage:"fatigue"},
{name:"wits",attr:"wits",abbr:"wits-abbr",damage:"coonfusion"},
{name:"empathy",attr:"empathy",abbr:"empathy-abbr",damage:"doubt"},
],
extraAttributesArr = [
{name:"instinct",attr:"empathy",abbr:"instinct-abbr",damage:"doubt"},
{name:"servos",attr:"strength",abbr:"servos-abbr",damage:"damage"},
{name:"stability",attr:"agility",abbr:"stability-abbr",damage:"damage"},
{name:"processor",attr:"wits",abbr:"processor-abbr",damage:"damage"},
{name:"network",attr:"empathy",abbr:"network-abbr",damage:"damage"}
],
attributesArr = [...mainAttributesArr, ...extraAttributesArr],
skillsArr = [
{id:"endure",name:"endure",label:"endure",level:"endure",attr_name:"strength",attr:"strength",misc:"endure_skill_misc",gear:"endure_gear_misc"},
{id:"force",name:"force",label:"force",level:"force",attr_name:"strength",attr:"strength",misc:"force_skill_misc",gear:"force_gear_misc"},
{id:"fight",name:"fight",label:"fight",level:"fight",attr_name:"strength",attr:"strength",misc:"fight_skill_misc",gear:"fight_gear_misc"},
{id:"sneak",name:"sneak",label:"sneak",level:"sneak",attr_name:"agility",attr:"agility",misc:"sneak_skill_misc",gear:"sneak_gear_misc"},
{id:"move",name:"move",label:"move",level:"move",attr_name:"agility",attr:"agility",misc:"move_skill_misc",gear:"move_gear_misc"},
{id:"shoot",name:"shoot",label:"shoot",level:"shoot",attr_name:"agility",attr:"agility",misc:"shoot_skill_misc",gear:"shoot_gear_misc"},
{id:"scout",name:"scout",label:"scout",level:"scout",attr_name:"wits",attr:"wits",misc:"scout_skill_misc",gear:"scout_gear_misc"},
{id:"comprehend",name:"comprehend",label:"comprehend",level:"comprehend",attr_name:"wits",attr:"wits",misc:"comprehend_skill_misc",gear:"comprehend_gear_misc"},
{id:"know_the_zone",name:"know the zone",label:"know-the-zone",level:"know_the_zone",attr_name:"wits",attr:"wits",misc:"know_the_zone_skill_misc",gear:"know_the_zone_gear_misc"},
{id:"sense_emotion",name:"sense emotion",label:"sense-emotion",level:"sense_emotion",attr_name:"empathy",attr:"empathy",misc:"sense_emotion_skill_misc",gear:"sense_emotion_gear_misc"},
{id:"manipulate",name:"manipulate",label:"manipulate",level:"manipulate",attr_name:"empathy",attr:"empathy",misc:"manipulate_skill_misc",gear:"manipulate_gear_misc"},
{id:"heal",name:"heal",label:"heal",level:"heal",attr_name:"empathy",attr:"empathy",misc:"heal_skill_misc",gear:"heal_gear_misc"},
{id:"know_nature",name:"know nature",label:"know-nature",level:"know_the_zone",attr_name:"instinct",attr:"empathy",misc:"know_the_zone_skill_misc",gear:"know_the_zone_gear_misc"},
{id:"sense_emotion_animal",name:"sense emotion animal",label:"sense-emotion-animal",level:"sense_emotion",attr_name:"instinct",attr:"empathy",misc:"sense_emotion_skill_misc",gear:"sense_emotion_gear_misc"},
{id:"dominate",name:"dominate",label:"dominate",level:"manipulate",attr_name:"instinct",attr:"empathy",misc:"manipulate_skill_misc",gear:"manipulate_gear_misc"},
{id:"overload",name:"overload",label:"overload",level:"endure",attr_name:"servos",attr:"strength",misc:"endure_skill_misc",gear:"endure_gear_misc"},
{id:"force_robot",name:"force",label:"force-robot",level:"force",attr_name:"servos",attr:"strength",misc:"force_skill_misc",gear:"force_gear_misc"},
{id:"assault",name:"assault",label:"assault",level:"fight",attr_name:"servos",attr:"strength",misc:"fight_skill_misc",gear:"fight_gear_misc"},
{id:"infiltrate",name:"infiltrate",label:"infiltrate",level:"sneak",attr_name:"stability",attr:"agility",misc:"sneak_skill_misc",gear:"sneak_gear_misc"},
{id:"move_robot",name:"move",label:"move-robot",level:"move",attr_name:"stability",attr:"agility",misc:"move_skill_misc",gear:"move_gear_misc"},
{id:"shoot_robot",name:"shoot",label:"shoot-robot",level:"shoot",attr_name:"stability",attr:"agility",misc:"shoot_skill_misc",gear:"shoot_gear_misc"},
{id:"scan",name:"scan",label:"scan",level:"scout",attr_name:"processor",attr:"wits",misc:"scout_skill_misc",gear:"scout_gear_misc"},
{id:"datamine",name:"datamine",label:"datamine",level:"comprehend",attr_name:"processor",attr:"wits",misc:"comprehend_skill_misc",gear:"comprehend_gear_misc"},
{id:"analyze",name:"analyze",label:"analyze",level:"know_the_zone",attr_name:"processor",attr:"wits",misc:"know_the_zone_skill_misc",gear:"know_the_zone_gear_misc"},
{id:"question",name:"question",label:"question",level:"sense_emotion",attr_name:"network",attr:"empathy",misc:"sense_emotion_skill_misc",gear:"sense_emotion_gear_misc"},
{id:"interact",name:"interact",label:"interact",level:"manipulate",attr_name:"network",attr:"empathy",misc:"manipulate_skill_misc",gear:"manipulate_gear_misc"},
{id:"repair",name:"repair",label:"repair",level:"heal",attr_name:"network",attr:"empathy",misc:"heal_skill_misc",gear:"heal_gear_misc"},
];
let race_att_names;
/* START RACE TYPE UPDATES */
//object reference for Race specific attribute names
//Upate sheet labels when chartype attribute changes
on("change:chartype sheet:opened", function(eventInfo) {
var character_types = {
"Mutant":{ strength: getTranslationByKey("strength"),
agility: getTranslationByKey("agility"),
wits: getTranslationByKey("wits"),
empathy: getTranslationByKey("empathy"),
str:getTranslationByKey("strength-abbr"),
agi: getTranslationByKey("agility-abbr"),
wit: getTranslationByKey("wits-abbr"),
emp: getTranslationByKey("empathy-abbr"),
damage: getTranslationByKey("damage"),
fatigue: getTranslationByKey("fatigue"),
confusion: getTranslationByKey("confusion"),
doubt: getTranslationByKey("doubt"),
endure: getTranslationByKey("endure"),
force: getTranslationByKey("force"),
fight: getTranslationByKey("fight"),
sneak: getTranslationByKey("sneak"),
move: getTranslationByKey("move"),
shoot: getTranslationByKey("shoot"),
scout: getTranslationByKey("scout"),
comprehend: getTranslationByKey("comprehend"),
know_the_zone: getTranslationByKey("know-the-zone"),
sense_emotion: getTranslationByKey("sense-emotion"),
manipulate: getTranslationByKey("manipulate"),
heal: getTranslationByKey("heal") },
"Animal":{ strength: getTranslationByKey("strength"),
agility: getTranslationByKey("agility"),
wits: getTranslationByKey("wits"),
empathy: getTranslationByKey("instinct"),
str:getTranslationByKey("strength-abbr"),
agi: getTranslationByKey("agility-abbr"),
wit: getTranslationByKey("wits-abbr"),
emp: getTranslationByKey("instinct-abbr"),
damage: getTranslationByKey("damage"),
fatigue: getTranslationByKey("fatigue"),
confusion: getTranslationByKey("confusion"),
doubt: getTranslationByKey("doubt"),
endure: getTranslationByKey("endure"),
force: getTranslationByKey("force"),
fight: getTranslationByKey("fight"),
sneak: getTranslationByKey("sneak"),
move: getTranslationByKey("move"),
shoot: getTranslationByKey("shoot"),
scout: getTranslationByKey("scout"),
comprehend: getTranslationByKey("comprehend"),
know_the_zone: getTranslationByKey("know-nature"),
sense_emotion: getTranslationByKey("sense-emotion-animal"),
manipulate: getTranslationByKey("dominate"),
heal: getTranslationByKey("heal") },
"Robot":{ strength: getTranslationByKey("servos"),
agility: getTranslationByKey("stability"),
wits: getTranslationByKey("processor"),
empathy: getTranslationByKey("network"),
str: getTranslationByKey("servos-abbr"),
agi: getTranslationByKey("stability-abbr"),
wit: getTranslationByKey("processor-abbr"),
emp: getTranslationByKey("network-abbr"),
damage: getTranslationByKey("damage"),
fatigue: getTranslationByKey("damage"),
confusion: getTranslationByKey("damage"),
doubt: getTranslationByKey("damage"),
endure: getTranslationByKey("overload"),
force: getTranslationByKey("force-robot"),
fight: getTranslationByKey("assault"),
sneak: getTranslationByKey("infiltrate"),
move: getTranslationByKey("move-robot"),
shoot: getTranslationByKey("shoot-robot"),
scout: getTranslationByKey("scan"),
comprehend: getTranslationByKey("datamine"),
know_the_zone: getTranslationByKey("analyze"),
sense_emotion: getTranslationByKey("question"),
manipulate: getTranslationByKey("interact"),
heal: getTranslationByKey("repair") },
"Human":{ strength: getTranslationByKey("strength"),
agility: getTranslationByKey("agility"),
wits: getTranslationByKey("wits"),
empathy: getTranslationByKey("empathy"),
str: getTranslationByKey("strength-abbr"),
agi: getTranslationByKey("agility-abbr"),
wit: getTranslationByKey("wits-abbr"),
emp: getTranslationByKey("empathy-abbr"),
damage: getTranslationByKey("damage"),
fatigue: getTranslationByKey("fatigue"),
confusion: getTranslationByKey("confusion"),
doubt: getTranslationByKey("doubt"),
endure: getTranslationByKey("endure"),
force: getTranslationByKey("force"),
fight: getTranslationByKey("fight"),
sneak: getTranslationByKey("sneak"),
move: getTranslationByKey("move"),
shoot: getTranslationByKey("shoot"),
scout: getTranslationByKey("scout"),
comprehend: getTranslationByKey("comprehend"),
know_the_zone: getTranslationByKey("know-the-zone"),
sense_emotion: getTranslationByKey("sense-emotion"),
manipulate: getTranslationByKey("manipulate"),
heal: getTranslationByKey("heal") },
};
getAttrs(["chartype"], function(values) {
// race_att_names;
if (_.has(character_types, values.chartype) ) {
race_att_names = character_types[values.chartype];
}
else {
race_att_names = character_types["Mutant"];
setAttrs({chartype: "Mutant"});
}
setAttrs({strength_name: race_att_names.strength});
setAttrs({agility_name: race_att_names.agility});
setAttrs({wits_name: race_att_names.wits});
setAttrs({empathy_name: race_att_names.empathy});
setAttrs({strength_abbr: race_att_names.str});
setAttrs({agility_abbr: race_att_names.agi});
setAttrs({wits_abbr: race_att_names.wit});
setAttrs({empathy_abbr: race_att_names.emp});
setAttrs({damage_name: race_att_names.damage});
setAttrs({fatigue_name: race_att_names.fatigue});
setAttrs({confusion_name: race_att_names.confusion});
setAttrs({doubt_name: race_att_names.doubt});
setAttrs({endure_name: race_att_names.endure});
setAttrs({force_name: race_att_names.force});
setAttrs({fight_name: race_att_names.fight});
setAttrs({sneak_name: race_att_names.sneak});
setAttrs({move_name: race_att_names.move});
setAttrs({shoot_name: race_att_names.shoot});
setAttrs({scout_name: race_att_names.scout});
setAttrs({comprehend_name: race_att_names.comprehend});
setAttrs({know_the_zone_name: race_att_names.know_the_zone});
setAttrs({sense_emotion_name: race_att_names.sense_emotion});
setAttrs({manipulate_name: race_att_names.manipulate});
setAttrs({heal_name: race_att_names.heal});
});
});
/* END RACE TYPE UPDATES */
/* Attributre damage const */
const attributes = {
// an Object Literal: damages['strength'] returns 'damage', damages['empathy'] returns 'doubt', etc
strength: "strength_total",
agility: "agility_total",
wits: "wits_total",
empathy: "empathy_total",
};
/* Attributre damage const */
const damages = {
// an Object Literal: damages['strength'] returns 'damage', damages['empathy'] returns 'doubt', etc
strength: "damage",
agility: "fatigue",
wits: "confusion",
empathy: "doubt",
};
/* Skill attributes, misc and gear */
const skills = {
// a javascript Object Literal, containing each skill, and its corresponding skill
endure: "strength_total",
force: "strength_total",
fight: "strength_total",
sneak: "agility_total",
Move: "agility_total",
shoot: "agility_total",
scout: "wits_total",
comprehend: "wits_total",
know_the_zone: "wits_total",
sense_emotion: "empathy_total",
manipulate: "empathy_total",
heal: "empathy_total",
know_nature: "wits_total",
dominate: "empathy_total",
overload: "strength_total",
assault: "strength_total",
infiltrate: "agility_total",
scan: "wits_total",
datamine: "wits_total",
analyze: "wits_total",
question: "empathy_total",
interact: "empathy_total",
repair: "empathy_total",
},
skillAttrs = {
// a javascript Object Literal, containing each skill, and its corresponding attribute
endure: "strength_total",
force: "strength_total",
fight: "strength_total",
sneak: "agility_total",
Move: "agility_total",
shoot: "agility_total",
scout: "wits_total",
comprehend: "wits_total",
know_the_zone: "wits_total",
sense_emotion: "empathy_total",
manipulate: "empathy_total",
heal: "empathy_total",
know_nature: "wits_total",
dominate: "empathy_total",
overload: "strength_total",
assault: "strength_total",
infiltrate: "agility_total",
scan: "wits_total",
datamine: "wits_total",
analyze: "wits_total",
question: "empathy_total",
interact: "empathy_total",
repair: "empathy_total",
},
skillLevels = {
// a javascript Object Literal, containing each skill, and its corresponding attribute
endure: "endure",
force: "force",
fight: "fight",
sneak: "sneak",
move: "move",
shoot: "shoot",
scout: "scout",
comprehend: "comprehend",
know_the_zone: "know_the_zone",
sense_emotion: "sense_emotion",
manipulate: "manipulate",
heal: "heal",
know_nature: "know_the_zone",
dominate: "manipulate",
overload: "endure",
assault: "fight",
infiltrate: "sneak",
scan: "scout",
datamine: "comprehend",
analyze: "know_the_zone",
question: "sense_emotion",
interact: "manipulate",
repair: "heal",
},
skillTotals = {
// a javascript Object Literal, containing each skill, and its corresponding total value
endure: "endure_total",
force: "force_total",
fight: "fight_total",
sneak: "sneak_total",
move: "move_total",
shoot: "shoot_total",
scout: "scout_total",
comprehend: "comprehend_total",
know_the_zone: "know_the_zone_total",
sense_emotion: "sense_emotion_total",
manipulate: "manipulate_total",
heal: "heal_total",
know_nature: "know_the_zone_total",
dominate: "manipulate_total",
overload: "endure_total",
assault: "fight_total",
infiltrate: "sneak_total",
scan: "scout_total",
datamine: "comprehend_total",
analyze: "know_the_zone_total",
question: "sense_emotion_total",
interact: "manipulate_total",
repair: "heal_total",
},
misc = {
// a javascript Object Literal, containing each skill, and its corresponding skill modification
endure: "endure_skill_misc",
force: "force_skill_misc",
fight: "fight_skill_misc",
sneak: "sneak_skill_misc",
Move: "move_skill_misc",
shoot: "shoot_skill_misc",
scout: "scout_skill_misc",
comprehend: "comprehend_skill_misc",
know_the_zone: "know_the_zone_skill_misc",
sense_emotion: "sense_emotion_skill_misc",
manipulate: "manipulate_skill_misc",
heal: "heal_skill_misc",
know_nature: "know_the_zone_skill_misc",
dominate: "manipulate_skill_misc",
overload: "endure_skill_misc",
assault: "fight_skill_misc",
infiltrate: "sneak_skill_misc",
scan: "scout_skill_misc",
datamine: "comprehend_skill_misc",
analyze: "know_the_zone_skill_misc",
question: "sense_emotion_skill_misc",
interact: "manipulate_skill_misc",
repair: "heal_skill_misc",
},
miscGear = {
// a javascript Object Literal, containing each skill, and its corresponding gear modification
endure: "endure_gear_misc",
force: "force_gear_misc",
fight: "fight_gear_misc",
sneak: "sneak_gear_misc",
Move: "move_gear_misc",
shoot: "shoot_gear_misc",
scout: "scout_gear_misc",
comprehend: "comprehend_gear_misc",
know_the_zone: "know_the_zone_gear_misc",
sense_emotion: "sense_emotion_gear_misc",
manipulate: "manipulate_gear_misc",
heal: "heal_gear_misc",
know_nature: "know_the_zone_gear_misc",
dominate: "manipulate_gear_misc",
overload: "endure_gear_misc",
assault: "fight_gear_misc",
infiltrate: "sneak_gear_misc",
scan: "scout_gear_misc",
datamine: "comprehend_gear_misc",
analyze: "know_the_zone_gear_misc",
question: "sense_emotion_gear_misc",
interact: "manipulate_gear_misc",
repair: "heal_gear_misc",
}
/* Monster constant */
const monster_skills = {
"endure monster": "endure_monster",
"fight monster": "fight_monster",
"force monster": "force_monster",
},
monster_misc = {
"endure monster": "endure_skill_misc_monster",
"fight monster": "fight_skill_misc_monster",
"force monster": "force_skill_misc_monster",
},
monster_miscGear = {
"endure monster": "endure_gear_misc_monster",
"fight monster": "fight_gear_misc_monster",
"force monster": "force_gear_misc_monster",
};
/* Switch between tabs. From version 1.12, using buttons.*/
[`character`, `ark`, `monster`, `zonelog`, 'npc', 'settings'].forEach(attr => {
on(`clicked:tab_${attr}`, () => {
setAttrs({tab: attr});
});
});
/* number and log handling */
const int = (score, on_error = 0) => parseInt(score) || on_error;
const float = (score, on_error = 0) => parseFloat(score) || on_error;
const clog = (text, title = "", color = "green", style = "font-size:12px; font-weight:normal;", headerstyle = "font-size:13px; font-weight:bold;") => {
let titleStyle = `color:${color}; ${headerstyle} text-decoration:underline;`;
let textStyle = `color:${color}; ${style}`;
let output = `%c${title} %c${text}`;
if (title) {
console.log(output, titleStyle, textStyle);
} else {
output = `%c${text}`;
console.log(output, textStyle);
}
};
/* version tracking */
const newAnnouncement = async (old_version, new_version, show_announcement) => {
const setter = {};
clog(`Current sheet data version: ${old_version}, Sheet code version: ${new_version}`);
if (new_version > old_version) {
setter.version = new_version;
}
if (show_announcement) {
clog("New Announcements: v" + new_version);
setter.show_announcements = 1;
}
if (setter) {
await setAttrsAsync(setter);
}
};
/*********************
UPGRADE LOGIC
***********************/
on("sheet:opened", () => {
getAttrs(["version"], (v) => {
versionator(float(v.version));
});
});
const versionator = async (version) => {
const getter = {},
setter = {};
var old_version = version;
// set the sheet version you are upgrading sheet to,
// and set show_announcement to 1 if you want to force an announcement to be shown.
// don't set a code_version; we use the switch below for that.
// let new_version = 2.02; // DEPRECATED, using the attribute attr_sheetversion at top of html for this
var new_version = (await getAttrsAsync(["sheetversion"])).sheetversion;
//clog("New version from attr: "+ new_version);
let show_announcement = 1;
// Test if character is new
var new_character = (await getAttrsAsync(["is_new_character"])).is_new_character;
clog("--- New character: "+ new_character);
// Test for upgrade from previous version 1.x (by checking for previous Attributes)
var test = await getAttrsAsync(["chartype", "adv-modifiers", "header-bgimg", "rot_permenant", "rot_permanent", "strength_max", "agility_max", "wits_max", "empathy_max", "ark_culture"]);
//clog("Test prev version: " + JSON.stringify(test));
//clog("Test prev version 2: " + ( ( test.strength_max || test.agility_max || test.wits_max || test.empathy_max ) === undefined));
//clog("Test prev version 3: " + ( test.strength_max === undefined || test.agility_max === undefined || test.wits_max === undefined || test.empathy_max === undefined ));
const prev_version_one = ( test.strength_max === undefined && test.agility_max === undefined && test.wits_max === undefined && test.empathy_max === undefined ) ? 0 : 1;
//clog("prev_version_one: "+prev_version_one);
// Test for upgrade from previous version 2.x (by checking for previous Attributes)
var test2 = await getAttrsAsync(["strength_total", "agility_total", "wits_total", "empathy_total"]);
//clog("Test prev version two: " + JSON.stringify(test2));
//clog("Test prev version two 1: " + (( test2.strength_total && test2.agility_total && test2.wits_total && test2.empathy_total ) === 0));
//clog("Test prev version two 2: " + ( test2.agility_total === 0 && test2.wits_total === 0 && test2.empathy_total === 0 ));
const prev_version_two = ( test2.agility_total === 0 && test2.wits_total === 0 && test2.empathy_total === 0 ) ? 0 : 1;
//clog("prev_version_two: "+prev_version_two);
//clog("old_version 1 = "+old_version);
//if (!old_version) old_version = 2.01; // New sheet
//clog("old_version 2 = "+old_version);
/* VERSION DETECTION */
/* 2025-01-14: Removed callback to versionator() and removed breaks in switch to allow fallback to decide what upgrades to make */
switch (true) {
//add checks for updates here
case new_character == 1 :
old_version = new_version;
await setAttrsAsync({is_new_character: 0, version: new_version});
new_character = (await getAttrsAsync(["is_new_character"])).is_new_character;
clog("----- New character, no sheet upgrades needed");
break;
case old_version < 3.00:
clog("---- Updating to version 3.00");
update_to_3_00(float(2.04), float(3.00));
//await setAttrsAsync({is_new_character: 0});
//new_character = (await getAttrsAsync(["is_new_character"])).is_new_character;
//clog("New character: 0");
show_announcement = 1;
//break;
case old_version < 3.10:
clog("---- Updating to version 3.10");
update_to_3_10(float(3.00), float(3.10));
//await setAttrsAsync({is_new_character: 0});
//new_character = (await getAttrsAsync(["is_new_character"])).is_new_character;
//clog("New character: 0");
show_announcement = 1;
//break;
default:
//clog("----- No sheet update needed, in switch default. ");
// if reach this, all codeversion upgrades are complete
// can run your recalculate function to show announcements
setAttrs({
version: new_version,
});
}
newAnnouncement(old_version, new_version, show_announcement);
};
//add update functions here
/* Upgrade to 3.10 version from previous version 3.00 */
const update_to_3_10 = async (old_version, upgraded_version) => {
clog(`** UPDATING TO V3.10 FROM ${old_version} **`);
const setter = {
whisper : 0,
//whisper_query : 0,
whisper_option : "",
whisper_option_api : "!myz",
whisper_the_roll : 0
};
getAttrs(["api_toggle","whisper_option","whisper_option_api","whisper","whisper_the_roll"], (values) => {
clog(`----- Upgrading from whisper settings: ${JSON.stringify(values)}`);
const option_api = values.whisper_option_api,
option = values.whisper_option,
api = int(values.api_toggle);
if ( ( api === 1 && option_api == "@{translation_whisper_api_macro_query}" ) ||
( api !== 1 && option == "@{translation_whisper_macro_query}" ) ) {
clog(`----- Whisper Upgrade: whisper through QUERY`);
setter.whisper = 2;
setter.whisper_option = "@{translation_whisper_macro_query}";
setter.whisper_option_api = "@{translation_whisper_api_macro_query}";
} else if ( ( api === 1 && option_api == "!wmyz" ) ||
( api !== 1 && option == "/w gm" ) ) {
clog(`----- Whisper Upgrade: whisper ENABLED through SETTING`);
setter.whisper = 1;
setter.whisper_option = "/w gm";
setter.whisper_option_api = "!wmyz";
setter.whisper_the_roll = 1;
} else if ( ( api === 1 && option_api == "!myz" ) ||
( api !== 1 && option == "" ) ) {
clog(`----- Whisper Upgrade: whisper DISABLED through SETTING`);
setter.whisper = 0;
setter.whisper_option = "";
setter.whisper_option_api = "!myz";
setter.whisper_the_roll = 0;
} else {
clog(`----- Whisper Upgrade: roll whisper settings not matched!!!`);
}
//clog(`----- Upgrading to whisper settings: ${JSON.stringify(setter)}`);
setAttrs(setter, { silent: true });
});
}
/* Upgrade to 3.00 version from previous version */
const update_to_3_00 = async (old_version, upgraded_version) => {
const setter = {};
var values = {};
//const charname = await getAttrsAsync(["character_name"]);
clog(`** UPDATING TO V3.00 FROM ${old_version} **`);
battleLvl();
//clog("Correcting battle level.");
//clog("...");
//clog("...");
//clog("...");
//clog("... enabled Custom Roll Parsing for Pushing Rolls in the chat message roll results.");
setAttrs(setter, { silent: true });
};
/* Upgrade to 2.0 version from previous version */
const update_to_2_0 = async (old_version, upgraded_version) => {
var values = {};
const charname = await getAttrsAsync(["character_name"]);
clog(`** UPDATING "${charname.character_name}" TO V2.0 FROM ${old_version} **`);
// On upgrade, set character type to Mutant
if (old_version < 2.01) {
//clog(`Upgrade ${charname.character_name}: Setting character type Mutant.`);
await setAttrsAsync({chartype: "Mutant"});
}
// On upgrade, turn off modifiers
if (old_version < 2.01) {
//clog(`Upgrade ${charname.character_name}: Turning off modifiers.`);
await setAttrsAsync({"adv-modifiers": 0});;
}
// On upgrade, turn on Mutant headers
if (old_version < 2.01) {
//clog(`Upgrade ${charname.character_name}: using image header backgrounds.`);
await setAttrsAsync({"header-bgimg": 1});
}
// On upgrade, change tab according to new tab naming
if (old_version < 2.01) {
const tab = await getAttrsAsync(["tab"]);
//clog(`Upgrading tab, current is ${tab.tab}`);
switch (tab.tab) {
case "log" :
await setAttrsAsync({tab: "zonelog"});
//clog("Setting tab from Log to Zonelog");
break;
case "npc" :
await setAttrsAsync({tab: "monster"});
await setAttrsAsync({"force_show_monsterinfo":1});
//clog("Setting tab from Npc to Monster, and showing legacy npc info.");
break;
default:
//clog("No need to update tab");
}
}
// Upgrade attributes, no need to update damages
// const attributes = { strength: "strength_total", agility: "agility_total", wits: "wits_total", empathy: "empathy_total" };
// const damages = { strength: "damage", agility: "fatigue", wits: "confusion", empathy: "doubt"};
Object.keys(attributes).forEach( async (attr) => {
var attrib = {};
values = await getAttrsAsync([attr, attributes[attr], damages[attr], attr+"_max", "character_name"]);
//charname = values.character_name;
// Set attributes to the attribute_max value from v1
//clog(`Testing attribute ${attr}_total exists (): exists ${(!values[attr+"_total"])} or is zero ${(values[attr+"_total"] == 0)}`);
// if (!values[attr+"_total"] || values[attr+"_total"] == 0) // replaced with old version test
//clog(`Old version test: ${old_version < 2}`);
//clog(`Testing if ${attr}_max exists: ${values[attr+"_max"]}`);
//clog(`Testing is ${attr} is not equal to ${attr}_max: ${values[attr] != values[attr+"_max"]}`);
//clog(`Set ${attr} to ${attr}_max test: ${(old_version < 2 || (values[attr+"_max"] && (values[attr] != values[attr+"_max"])) )}`);
if (old_version < 2 || (values[attr+"_max"] && (values[attr] != values[attr+"_max"])) ) {
attrib[attr] = int(values[attr+"_max"]);
//clog(`Upgrading attribute ${attr} from v1 version ${values[attr+"_max"]} to v2 ${values[attr]}`);
if (attr == "strength") {
// Set strength attributes for PC and Monster to attribute_max value
attrib["strength_pc"] = values["strength_max"];
attrib["strength_monster"] = values["strength_max"];
//clog(`Attrib is Strength, pc = ${attrib["strength_pc"]} and Monster is ${attrib["strength_monster"]}`);
}
} else {
attrib[attr] = int(values[attr]);
}
//clog(`Attributes ${attr} damage is ${int(values[damages[attr]])}`);
attrib[attr+"_total"] = attrib[attr] - int(values[damages[attr]]);
//clog("Attributes attrib: "+JSON.stringify(attrib));
const attr_name = getSkillName(attr);
//clog(`Set ${charname.character_name} attribute ${attr_name} from ${attr_name}_max ${values[attr+"_max"]} to ${attr_name} ${attrib[attr]} with total ${attrib[attr+"_total"]}.`);
await setAttrsAsync(attrib);
});
// Upgrade conditions - no need
// Upgrade apprearance - no need
// Upgrade injuries - non need
// Upgrade rot -
//clog("Pre Upgrade rot points");
const rot = await getAttrsAsync(["rot_permenant", "rot_permanent"]);
//clog("Upgrade rot points test 1, permenant exists : "+ rot.rot_permenant);
//clog("Upgrade rot points test 2, permanent : "+ rot.rot_permanent);
const permenant = int(rot.rot_permenant || 0),
permanent = int(rot.rot_permanent);
//clog("Upgrade rot points test 3, permenant > permanent : "+ (permenant > permanent) );
if (permenant > permanent) {
//clog(`Upgrading ${charname.character_name} permanent rot points from ${permanent} to ${permenant}`);
await setAttrsAsync({rot_permanent: permenant});
}
rot["value"] = await getAttrsAsync(["rot_permanent"]);
//clog(`Post Upgrade ${charname.character_name} permanent rot points ${rot.value.rot_permanent}`);
// Upgrade experience - no need, same attribute
// Upgrade skills - no need
// Upgrade repeating skills - no need, aligned
// Talents - no need - aligned
// Mutations, checking if need upgrade, checking if mutation names exists, then upgrading
var muts_actioned = false;
// Collect new mutaitons, if any
const mutationIds = await getSectionIDsAsync("repeating_mutations");
//clog("Mutation IDs list: "+ JSON.stringify(mutationIds));
//clog("Mutation IDs length: "+ mutationIds.length);
const mutations = [];
mutationIds.forEach( async (mutationId) => {
const muts = await getAttrsAsync(["repeating_mutations_"+mutationId+"_name", "repeating_mutations_"+mutationId+"_description"]);
mutations.push({name: muts["repeating_mutations_"+mutationId+"_name"], description: muts["repeating_mutations_"+mutationId+"_description"]});
//clog("Mutation names: "+JSON.stringify(mutations));
});
// Collect old mutantions, if any
const mutantionIds = await getSectionIDsAsync("repeating_mutantions");
//clog("Mutations list: "+ JSON.stringify(mutationIds));
//clog("Mutations length: "+ mutationIds.length);
//clog("Mutantion IDs list: "+ JSON.stringify(mutantionIds));
//clog("Mutantion IDs list: "+ mutantionIds.length);
if(mutantionIds.length > 0 && mutationIds.length < mutantionIds.length) {
mutantionIds.forEach( async (mutantionsId) => {
values = await getAttrsAsync(["repeating_mutantions_"+mutantionsId+"_mutantion_name", "repeating_mutantions_"+mutantionsId+"_mutantion_description", "character_name"]);
const name = values["repeating_mutantions_"+mutantionsId+"_mutantion_name"],
description = values["repeating_mutantions_"+mutantionsId+"_mutantion_description"],
newrowid = generateRowID();
//clog(`Found ${charname.character_name}'s mutantion ${name}.`);
const found = mutations.find(element => element.name == name);
//clog("Mutantion and mutations doesnt match? "+(found === undefined));
if (!found) {
//clog("Couldnt find match to "+name);
} else {
//clog("Found match to "+name);
}
var asterisk = "";
if ( mutationIds.length > 0) { asterisk = "*"; }
if(found === undefined) { // No match for mutantion in the new mutations list
//clog(`Upgrading ${charname.character_name}'s mutantion ${name}.`);
let newmutation = {};
newmutation["repeating_mutations_"+newrowid+"_name"] = name + asterisk;
newmutation["repeating_mutations_"+newrowid+"_description"] = description;
newmutation["repeating_mutations_"+newrowid+"_rank"] = 1;
newmutation["repeating_mutations_"+newrowid+"_powerlevel"] = 1;
await setAttrsAsync(newmutation);
} else { // Found a match in the new mutation list, skipping
muts_actioned = true;
//clog("mutation already re-added, "+name);
}
});
}
// Mutation points
const mp = await getAttrsAsync(["mutation", "mutationpoints", "character_name"]);
//clog(`${charname.character_name} mutation points mutation: ${mp.mutation}`);
//clog(`${charname.character_name} mutation points mutationpoints: ${mp.mutationpoints}`);
//clog("Testing mutpoints 0: " + old_version );
//clog(`Testing mutpoints 1: ${ old_version < float(2.01) } `);
//clog(`Testing mutpoints 2: ${ int(mp.mutation) != 0 } `);
//clog(`Testing mutpoints 3: ${ int(mp.mutationpoints) == 0 } `);
if (old_version < float(2.01) && int(mp.mutation) != 0 && int(mp.mutationpoints) == 0) {
//charname = mp.character_name;
//clog("Mutations ationed: "+muts_actioned);
//clog("Mutation points upgrade check: "+ (!mutation && !muts_actioned) || !mutationpoints );
//clog(`Upgrading ${charname.character_name} mutation points using v1 mutantion points ${mp.mutation}`);
setAttrs({mutationpoints: mp.mutation});
} else {
//clog("Mutation points not upgraded in version 2+, or when mutantion is 0, or mutationpoints is bigger than 0.");
}
// Upgrade Weapons ASYNC
const weaponIds = await getSectionIDsAsync("repeating_weapons");
weaponIds.forEach( async (weaponId) => {
var values = await getAttrsAsync(["repeating_weapons_"+weaponId+"_weapon_name", "repeating_weapons_"+weaponId+"_weapon_skill", "repeating_weapons_"+weaponId+"_weapon_skill_misc", "repeating_weapons_"+weaponId+"_weapon_bonus", "repeating_weapons_"+weaponId+"_weapon_bonus_max", "repeating_weapons_"+weaponId+"_weapon_damage", "repeating_weapons_"+weaponId+"_weapon_damage_misc", "repeating_weapons_"+weaponId+"_weapon_range", "repeating_weapons_"+weaponId+"_weapon_features", "repeating_weapons_"+weaponId+"_weapon_grip", "repeating_weapons_"+weaponId+"_weapon_weight", "repeating_weapons_"+weaponId+"_weapon_carried", "strength_total", "agility_total", "shoot", "fight", "character_name"]);
//clog(`Upgrade weapon attributes: Weapon ${values["repeating_weapons_"+weaponId+"_weapon_name"]} with bonus ${int(values["repeating_weapons_"+weaponId+"_weapon_bonus"])} and damage ${int(values["repeating_weapons_"+weaponId+"_weapon_damage"])} up to ${values["repeating_weapons_"+weaponId+"_weapon_range"]} distance.`);
//clog("Weapon upgrade json: "+ JSON.stringify(values));
const name = values["repeating_weapons_"+weaponId+"_weapon_name"],
skill = int(values["repeating_weapons_"+weaponId+"_weapon_skill"] || 0),
//skill_misc = int(values["repeating_weapons_"+weaponId+"_weapon_skill_misc"] || 0),
bonus = int(values["repeating_weapons_"+weaponId+"_weapon_bonus"] || 1),
bonus_max = values["repeating_weapons_"+weaponId+"_weapon_bonus_max"] ? int(values["repeating_weapons_"+weaponId+"_weapon_bonus_max"]) : int(values["repeating_weapons_"+weaponId+"_weapon_bonus"] || 1),
//bonus_misc = int(values["repeating_weapons_"+weaponId+"_weapon_bonus_misc" || 0]),
//bonus_tot = bonus + bonus_misc,
//damage = int(values["repeating_weapons_"+weaponId+"_weapon_damage"] || 1),
//damage_misc = int(values["repeating_weapons_"+weaponId+"_weapon_damage_misc"] || 0),
//damage_tot = damage + damage_misc,
//range = values["repeating_weapons_"+weaponId+"_weapon_range"] || "Arm",
//features = values["repeating_weapons_"+weaponId+"_weapon_features"] || "",
//grip = values["repeating_weapons_"+weaponId+"_weapon_grip"] || "1H",
//weight = values["repeating_weapons_"+weaponId+"_weapon_weight"] || 1,
//carried = values["repeating_weapons_"+weaponId+"_weapon_carried"] || 1,
//fight = int(values.fight),
//shoot = int(values.shoot),
base = skill === 0 ? int(values.strength_total) : int(values.agility_total),
skill_lvl = skill === 0 ? int(values.fight) : int(values.shoot);
const max = bonus_max < bonus ? bonus : bonus_max;
//clog(`Upgrade ${charname.character_name}'s weapon Weapon ${name} with ${base} base and ${skill_level} skill, max bonus ${bonus_max} and damage ${damage} up to ${range} distance.`);
let weapon = {};
weapon["repeating_weapons_"+weaponId+"_weapon_name"] = name;
weapon["repeating_weapons_"+weaponId+"_weapon_base_total"] = base;
weapon["repeating_weapons_"+weaponId+"_weapon_skill"] = int(values["repeating_weapons_"+weaponId+"_weapon_skill"] || 0);
weapon["repeating_weapons_"+weaponId+"_weapon_skill_misc"] = int(values["repeating_weapons_"+weaponId+"_weapon_skill_misc"] || 0);
weapon["repeating_weapons_"+weaponId+"_weapon_skill_total"] = skill_lvl + int(values["repeating_weapons_"+weaponId+"_weapon_skill_misc"] || 0);
weapon["repeating_weapons_"+weaponId+"_weapon_bonus"] = int(values["repeating_weapons_"+weaponId+"_weapon_bonus"] || 1);
weapon["repeating_weapons_"+weaponId+"_weapon_bonus_max"] = max;
weapon["repeating_weapons_"+weaponId+"_weapon_bonus_misc"] = int(values["repeating_weapons_"+weaponId+"_weapon_bonus_misc"] || 0);
weapon["repeating_weapons_"+weaponId+"_weapon_bonus_total"] = bonus_max + int(values["repeating_weapons_"+weaponId+"_weapon_bonus_misc"] || 0);
weapon["repeating_weapons_"+weaponId+"_weapon_damage"] = int(values["repeating_weapons_"+weaponId+"_weapon_damage"] || 1);
weapon["repeating_weapons_"+weaponId+"_weapon_damage_misc"] = int(values["repeating_weapons_"+weaponId+"_weapon_damage_misc"] || 0);
weapon["repeating_weapons_"+weaponId+"_weapon_damage_total"] = int(values["repeating_weapons_"+weaponId+"_weapon_damage"] || 1) + int(values["repeating_weapons_"+weaponId+"_weapon_damage_misc"] || 0);
weapon["repeating_weapons_"+weaponId+"_weapon_grip"] = values["repeating_weapons_"+weaponId+"_weapon_grip"] || "1H";
weapon["repeating_weapons_"+weaponId+"_weapon_range"] = values["repeating_weapons_"+weaponId+"_weapon_range"] || "Arm";
weapon["repeating_weapons_"+weaponId+"_weapon_carried"] = values["repeating_weapons_"+weaponId+"_weapon_carried"] || 1;
weapon["repeating_weapons_"+weaponId+"_weapon_weight"] = values["repeating_weapons_"+weaponId+"_weapon_weight"] || 1;
weapon["repeating_weapons_"+weaponId+"_weapon_features"] = values["repeating_weapons_"+weaponId+"_weapon_features"] || "";
//clog("Updating weapon: "+JSON.stringify(weapon));
await setAttrsAsync(weapon);
});
// Upgrade armor to repeating section
var armorIds = await getSectionIDsAsync("repeating_armor");
//clog("New armor IDs: "+ JSON.stringify(armorIds));
const armor_names = [];
var val = "";
armorIds.forEach( async (armorId) => {
val = await getAttrsAsync(["repeating_armor_"+armorId+"_name"]);
armor_names.push(val["repeating_armor_"+armorId+"_name"]);
//clog("Armor names: "+JSON.stringify(armor_names));
});
//clog("Armor names: "+ JSON.stringify(armor_names));
var armors = await getAttrsAsync(["armor1_ar", "armor1_bonus", "armor1_damage", "armor1_equiped", "armor1_name", "armor2_ar", "armor2_bonus", "armor2_damage", "armor2_equiped", "armor2_name", "character_name"]);
var newarmor1 = {};
//clog("Upgrade armor1 name: "+armors.armor1_name);
//clog("Upgrade armor1 bonus: "+armors.armor1_bonus);
//clog("Upgrade armor1 name not found: "+(armor_names.find(element => element == armors.armor1_name) === undefined));
//clog(`Upgrading ${charname.character_name}'s Armor1 ${armors.armor1_name} with bonus ${armors.armor1_bonus} and damage ${armors.armor1_damage}, equipped ${armors.armor1_equiped}`);
if (armors.armor1_name && armors.armor1_bonus && (armor_names.find(element => element == armors.armor1_name) === undefined)) {
//clog("Upgrading Armor1 : " + armors.armor1_name + " with bonus : "+ armors.armor1_bonus +".");
//clog(`Upgrading ${charname.character_name}'s Armor1 ${armors.armor1_name} with bonus ${armors.armor1_bonus} and damage ${armors.armor1_damage}, equipped ${armors.armor1_equiped}`);
const newrowid1 = generateRowID();
const bonus1_actual = int(armors.armor1_bonus) + int(armors.armor1_damage);
const rating1 = bonus1_actual * (armors.armor1_equiped == 0 ? 0 : 1);
//clog("Armor rating calculated: "+rating1);
var asterisk = "";
if ( old_version == float(2.01) ) { asterisk = "*"; }
newarmor1["repeating_armor_" + newrowid1 + "_name"] = armors.armor1_name + asterisk;
newarmor1["repeating_armor_" + newrowid1 + "_rating"] = rating1;
newarmor1["repeating_armor_" + newrowid1 + "_carried"] = 1;
newarmor1["repeating_armor_" + newrowid1 + "_equipped"] = armors.armor1_equiped || 1;
newarmor1["repeating_armor_" + newrowid1 + "_bonus"] = bonus1_actual;
newarmor1["repeating_armor_" + newrowid1 + "_bonus_misc"] = 0;
newarmor1["repeating_armor_" + newrowid1 + "_bonus_max"] = int(armors.armor1_bonus);
newarmor1["repeating_armor_" + newrowid1 + "_weight"] = 1;
newarmor1["repeating_armor_" + newrowid1 + "_features"] = '';
//clog("upgrading armor 1: "+JSON.stringify(newarmor1));
await setAttrsAsync(newarmor1);
} else {
//clog("No need to upgrade Armor_1");
}
var newarmor2 = {};
//clog("Upgrade armor2 name: "+armors.armor2_name);
//clog("Upgrade armor2 bonus: "+armors.armor2_bonus);
//clog("Upgrade armor1 name not found: "+(armor_names.find(element => element == armors.armor1_name) === undefined));
//clog(`Upgrade ${charname.character_name}'s Armor2 ${armors.armor2_name} with bonus ${armors.armor2_bonus} and damage ${armors.armor2_damage}, equipped ${armors.armor2_equiped}`);
if (armors.armor2_bonus && armors.armor2_name && armor_names.find(element => element == armors.armor2_name) === undefined) {
//clog("Upgrading Armor2 : " + armors.armor2_name + " with bonus : "+ armors.armor2_bonus +".");
//clog(`Upgrading ${charname.character_name}'s Armor2 ${armors.armor2_name} with bonus ${armors.armor2_bonus} and damage ${armors.armor2_damage}, equipped ${armors.armor2_equiped}`);
const newrowid2 = generateRowID();
const bonus2_actual = int(armors.armor2_bonus) + int(armors.armor2_damage);
const rating2 = bonus2_actual * (armors.armor2_equiped == 0 ? 0 : 1);
//clog("Armor rating calculated: "+rating2);
newarmor2["repeating_armor_" + newrowid2 + "_name"] = armors.armor2_name;
newarmor2["repeating_armor_" + newrowid2 + "_rating"] = rating2;
newarmor2["repeating_armor_" + newrowid2 + "_carried"] = 1;
newarmor2["repeating_armor_" + newrowid2 + "_equipped"] = armors.armor2_equiped || 1;
newarmor2["repeating_armor_" + newrowid2 + "_bonus"] = bonus2_actual;
newarmor2["repeating_armor_" + newrowid2 + "_bonus_misc"] = 0;
newarmor2["repeating_armor_" + newrowid2 + "_bonus_max"] = int(armors.armor2_bonus);
newarmor2["repeating_armor_" + newrowid2 + "_weight"] = 1;
newarmor2["repeating_armor_" + newrowid2 + "_features"] = '';
//clog("upgrading armor 2: "+JSON.stringify(newarmor2));
await setAttrsAsync(newarmor2);
} else {
//clog("No need to upgrade Armor_2");
}
// Upgrade gear, vehicle and consumables
// Gear - no need
// Vehicle - aligned
// Consumables, aligned
// Upgrade relationships, notes etc
// Aligned
// Upgrade Ark sheet
// Development levels
values = await getAttrsAsync(["ark_food_supply","ark_culture", "ark_technology", "ark_warfare", "ark_dev_food", "ark_dev_cult", "ark_dev_tech", "ark_dev_war", "character_name"]);
const dev_food = int(values.ark_dev_food),
dev_culture = int(values.ark_dev_cult),
dev_tech = int(values.ark_dev_tech),
dev_war = int(values.ark_dev_war),
food = dev_food || int(values.ark_food_supply),
culture = dev_culture || int(values.ark_culture),
tech = dev_tech || int(values.ark_technology),
war = dev_war || int(values.ark_warfare);
//charname = values.character_name;
//clog(`Upgrade ${charname.character_name}'s ark development levels: food ${food}, culture ${culture}, technology ${tech}, and warfare ${war}.`);
//clog("Dev levels json: "+ JSON.stringify(values));
await setAttrsAsync({
ark_dev_food: food,
ark_dev_cult: culture,
ark_dev_tech: tech,
ark_dev_war: war,
});
// Ark details
// Aligned
// UPDATE THIS /
// MUST set the version to a specific value here, the one where the sheet was updated like this.
const setter = {};
//setter.version = upgraded_version;
//clog("Setting upgraded version: "+JSON.stringify(upgraded_version));
setAttrs(setter, { silent: true });
};
/* rename misspelled items
const update_to_1_12 = (old_version, upgraded_version) => {
getAttrs(["armor1_equiped", "armor2_equiped"], (v) => {
const setter = {};
const statswaps = {
armor1_equiped: "armor1_equipped",
armor2_equiped: "armor2_equipped",
};
Object.entries(statswaps).forEach(([key, value]) => {
if (v[key]) {
setter[key] = v[value];
//clog(`Upgrading stat setter[key] ${setter[key]} to v[value] ${v[value]}`);
}
});
// UPDATE THIS /
// MUST set the version to a specific value here, the one where the sheet was updated like this.
//setter.version = upgraded_version;
setAttrs(setter, { silent: true });
//clog("updating misspelled items");
});
};*/
/* ===== PARAMETERS ==========
GiGs 'Super Simple Summarizer'
*/
const repeatingSum = (destination, section, fields, multiplier = 1) => {
if (!Array.isArray(fields)) fields = [fields];
getSectionIDs(`repeating_${section}`, (idArray) => {
const attrArray = idArray.reduce((m, id) => [...m, ...fields.map((field) => `repeating_${section}_${id}_${field}`)], []);
getAttrs(attrArray, (v) => {
//clog("values of v: " + JSON.stringify(v));
// getValue: if not a number, returns 1 if it is 'on' (checkbox), otherwise returns 0..
const getValue = (section, id, field) => float(v[`repeating_${section}_${id}_${field}`]) || (v[`repeating_${section}_${id}_${field}`] === "on" ? 1 : 0);
const sumTotal = idArray.reduce((total, id) => total + fields.reduce((subtotal, field) => subtotal * getValue(section, id, field), 1), 0);
setAttrs({
[destination]: sumTotal * multiplier,
});
});
});
};
/* character encumbrance */
on("change:repeating_gear remove:repeating_gear sheet:opened", function () {
//clog("Change Detected: Character Gear Encumbrance");
repeatingSum("gear_encumbrance", "gear", ["gear_weight", "gear_qty", "gear_carried"]);
});
/* vehicle encumbrance */
on("change:repeating_vehicle remove:repeating_vehicle sheet:opened", function () {
//clog("Change Detected: Vehicle Gear Encumbrance");
repeatingSum("vehiclegear_encumbrance", "vehicle", ["vehiclegear_weight", "vehiclegear_qty", "vehiclegear_carried"]);
});
/* weapons encumbrance */
on("change:repeating_weapons remove:repeating_weapons sheet:opened", function () {
//clog("Change Detected: Character Weapons Encumbrance");
repeatingSum("weapons_encumbrance", "weapons", ["weapon_weight", "weapon_carried"]);
});
/* armor encumbrance */
on("change:repeating_armor remove:repeating_armor sheet:opened", function () {
//clog("Change Detected: Character Armor Encumbrance");
repeatingSum("armor_encumbrance", "armor", ["weight", "carried"]);
});
/* armor rating */
on("change:repeating_armor remove:repeating_armor sheet:opened", function () {
//clog("Change Detected: Character Armor Rating");
repeatingSum("ar_total", "armor", ["rating", "equipped"]);
});
/* Permanent rot */
on("change:rot change:rot_permanent sheet:opened", function () {
//clog("Change Detected: Rot levels");
getAttrs(["rot", "rot_permanent"], (values) => {
var rot = int(values.rot),
rot_permanent = int(values.rot_permanent);
if(rot <= rot_permanent) {
setAttrs ({ rot: rot_permanent });
}
});
});
/* total encumbrance */
on("sheet:opened change:gear_encumbrance change:vehiclegear_encumbrance change:weapons_encumbrance change:armor_encumbrance change:grub change:water change:bullets", function () {
getAttrs(["gear_encumbrance", "vehiclegear_encumbrance", "weapons_encumbrance", "armor_encumbrance", "grub", "water", "bullets"], function (values) {
//clog("Change Detected: Character Encumbrance");
const gear_encumbrance = float(values["gear_encumbrance"]),
vehiclegear_encumbrance = float(values["vehiclegear_encumbrance"]),
weapons_encumbrance = float(values["weapons_encumbrance"]),
armor_encumbrance = float(values["armor_encumbrance"]),
grub = float(values["grub"]),
water = float(values["water"]),
bullets = float(values["bullets"]),
consumables_enc = (grub * 0.25) + (water * 0.25),
bullets_enc = bullets <= 10 ? 0 : (bullets <= 20 ? 0.5 : (bullets <= 40 ? 1 : 2)),
consumables_encumbrance = consumables_enc + bullets_enc,
sum_character_encumbrance = gear_encumbrance + weapons_encumbrance + armor_encumbrance + consumables_encumbrance,
sum_vehicle_encumbrance = vehiclegear_encumbrance;
setAttrs({
weapons_encumbrance: weapons_encumbrance,
armor_encumbrance: armor_encumbrance,
character_encumbrance: sum_character_encumbrance,
consumables_encumbrance: consumables_encumbrance,
vehicle_encumbrance: sum_vehicle_encumbrance,
});
});
});
/* total vehicle encumbrance */
on("sheet:opened change:vehicle_advancedencumbrance change:vehiclegear_encumbrance change:vehicle_occupants change:vehicle_seats change:vehicle_fuel change:vehicle_fuel_cap change:vehicle_maxload", function () {
getAttrs(["vehicle_advancedencumbrance", "vehiclegear_total", "vehicle_carry", "vehiclegear_encumbrance", "vehicle_occupants", "vehicle_occupants_weight", "vehicle_seats", "vehicle_fuel", "vehicle_fuel_cap", "vehicle_fuel_weight", "vehicle_maxload", "vehicle_overencumbered"], function (values) {
//clog("Change Detected: Vehicle Encumbrance");
const adv = int(values["vehicle_advancedencumbrance"]),
gear = float(values["vehiclegear_encumbrance"]),
occupants = int(values["vehicle_occupants"]),
seats = int(values["vehicle_seats"]),
fuel_cap = int(values["vehicle_fuel_cap"]),
fuel_in = float(values["vehicle_fuel"]),
fuel = fuel_in > fuel_cap ? fuel_cap : fuel_in,
fuel_wght = (adv > 0) ? fuel * 2 : 0, // If Adv, A unit of fuel weighs 2 units
maxload = int(values["vehicle_maxload"]),
carry = (adv > 0) ? maxload + (seats * 10) + (fuel_cap * 2) : maxload, // If Adv, Adjusted to accommodate poccupants and fuel
occupants_wght = (adv > 0) ? occupants * 10 : 0, // If Adv, A person weighs 10 units
total = gear + fuel_wght + occupants_wght,
overencumbered = (carry - total) < 0 ? 1 : 0;
setAttrs({
vehiclegear_encumbrance: gear,
vehicle_fuel_weight: fuel_wght,
vehicle_occupants_weight: occupants_wght,
vehiclegear_total: total,
vehicle_carry: carry,
vehicle_overencumbered: overencumbered,
});
});
});
/* Credits total */
on("sheet:opened change:credits change:credits_vehicle change:credits_stash", function () {
getAttrs(["credits", "credits_vehicle", "credits_stash"], function (values) {
//clog("Change Detected: Credits Total");
const credits = float(values["credits"]),
credits_vehicle = float(values["credits_vehicle"]),
credits_stash = float(values["credits_stash"]),
sum_credits = credits + credits_vehicle + credits_stash;
setAttrs({
credits_total: sum_credits,
});
});
});
on("change:powerincrease", (ev) => {
//clog(`Power increase button clicked: ${JSON.stringify(ev)}`);
getAttrs(["powerincrease","energypoints_max","energypoints"], (values) => {
const inc = int(values.powerincrease),
energy = int(values.energypoints),
setter = {},
silent = {silent: true};
if ( inc === 1 ) {
setter.energypoints_max = 12;
clog(`Energy points max increased to 12`);
} else {
setter.energypoints_max = 10;
setter.energypoints = Math.min(energy,10);
clog(`Energy points max set to 10`);
}
setAttrs(setter,silent);
});
});
//Battle Level
// Function also used in upgrade script
const battleLvl = () => {
getAttrs(["ark_dev_war"], (values) => {
//clog(JSON.stringify(values));
//clog(`Battle level at ${int(values.ark_dev_war) || 1}`);
const warfare = (Math.floor(float(values.ark_dev_war) / 10 ))+1 || 1;
setAttrs({
ark_battle_level: warfare,
});
});
}
on("change:ark_dev_war", () => {
battleLvl();
/*
getAttrs(["ark_dev_war"], (values) => {
//clog(JSON.stringify(values));
//clog(`Battle level at ${int(values.ark_dev_war) || 1}`);
const warfare = (Math.floor(float(values.ark_dev_war) / 10 ))+1 || 1;
setAttrs({
ark_battle_level: warfare,
});
});
*/
});
const houses = ['warburg','fortescue','kilgore','morningstar'],
sectors = ['novapaloma','oldkoly','pirius','tindertuft','northolme','calista','hindenburg','mosel','laborum','arcadium','cinderfalls','cogsofhel'],
houseSectors = {},
houseAttrs = {};
houseSectors.warburg = sectors.map( (sector) => sector + '_warburg' );
houseSectors.fortescue = sectors.map( (sector) => sector + '_fortescue' );
houseSectors.kilgore = sectors.map( (sector) => sector + '_kilgore' );
houseSectors.morningstar = sectors.map( (sector) => sector + '_morningstar' );
//clog(`Warburg: ${houseSectors.warburg}`);
//clog(`Fortescue: ${houseSectors.fortescue}`);
//clog(`kilgore: ${houseSectors.kilgore}`);
//clog(`morningstar: ${houseSectors.morningstar}`);
houses.forEach( (house) => {
//clog(`SW for ${house}`);
sectors.forEach( (sector) => {
//clog(`SW for ${house} and ${sector}`);
on(`change:${sector}_${house}`, () => {
updateInfluence(house);
});
});
});
const updateInfluence = (house) => {
//clog(`Updating influence for ${house}`);
getAttrs(houseSectors[`${house}`], (values) => {
//clog(`House influence values: ${JSON.stringify(values)}`);
const houseTotal = (Object.values(values)).reduce( (a,n) => a + int(n), 0);
//clog(`House ${house} influence : ${houseTotal}`);
setAttrs({
[`influence_${house}`] : houseTotal,
});
});
}
on('sheet:opened', () => {
//clog('Updating the influence of the four great houses.');
houses.forEach( (house) => { updateInfluence(house); });
});
/* Monster or PC Strength handling. updates attr_strength depending on sheet tab*/
on("change:tab change:strength_pc change:strength_monster sheet:opened", () => {
getAttrs(["tab", "strength", "strength_pc", "strength_monster"], (v) => {
const settings = {},
tab = (v.tab) ? v.tab : 'character', // sets tab to Character if not recognized - so goes to pc tab;
str = int(v.strength);
let pc = int(v.strength_pc),
monster = int(v.strength_monster),
forceupdate = 0;
//clog(`Found updated strength max in tab ${tab}, checking pc = ${pc} and monster = ${monster} strengths, max is now = ${str}.`)
// check if character existed before strength_pc or _monster stats were introduced.
if (str > 0) {
if (0 === pc || 0 === monster) forceupdate = 1;
if (0 === pc) {
pc = str;
settings.strength_pc = str;
}
if (0 === monster) {
monster = str;
settings.strength_monster = str;
}
}
// if tab is not pc or monster, end the function and leave strength unchanged
if (tab !== 'character' && tab !== 'monster' && !forceupdate) return;
// if tab = 1 or 3, set strength to the appropriate value
settings.strength = tab === 'monster' ? monster : pc;
setAttrs(settings);
});
});
/* ability damage calcs */
Object.keys(damages).forEach((stat) => {
// creates a sheet worker for each stat in damages - first stat = 'strength', then stat = 'agility', etc
// in each worker, stat = the stat (strength, agility, etc) and damages[stat] = the associated damage ('damage'. 'fatigue', etc)
on(`change:${stat} change:${damages[stat]}`, function () {
//clog(`${stat} or ${damages[stat]} has changed.`);
getAttrs([stat, damages[stat]], (values) => {
const stat_score = int(values[stat]),
damage_score = int(values[damages[stat]]);
setAttrs({
[stat + "_total"]: Math.max(0, stat_score - damage_score),
//Math.max returns the highest value from a comma separated list - it's just like roll20's kh1 dice function.
// so you don't need the if( value > 0) test. With math.max, if the value is below 0, it becomes 0.
});
});
});
});
/*** UPDATE ***/
/* skill total calcs */
const getSkill = (label) => (label.split("-").join("_")).split(" ").join("_").toLowerCase(),
getSkillLabel = (skill) => (skill.split("-").join("_")).split(" ").join("_").toLowerCase(),
// a function to change (for example) "Know the Zone" to "know_the_zone"
// needed for a couple of functions below
getSkillKey = (label) => (label.split("_").join("-")).split(" ").join("-").toLowerCase(),
// a function to change (for example) "Know the Zone" to "know-the-zone"
// needed for a couple of functions below
getSkillNameByType = (label) => race_att_names[label],
getSkillName = (label) => getTranslationByKey(label);
// A function to get the name of a skill specific for the selected race/character type.
// Needed for a couple of functions below
// Note, this is joint for all sheets for one logged on user
/* Skill totals calculation */
Object.keys(skills).forEach((skill_name) => {
// creates a sheet worker for each skill in skills - first skill = 'strength', then stat = 'agility', etc
// in each worker, stat = the stat (strength, agility, etc) and damages[stat] = the associated damage ('damage'. 'fatigue', etc)
const skill = getSkill(skill_name);
on(`change:${skill} change:${skills[skill_name]} change:${misc[skill_name]} change:${miscGear[skill_name]}`, function () {
//clog(`${skill} or ${skills[skill_name]} or ${misc[skill_name]} or ${miscGear[skill_name]} has changed.`);
getAttrs([skill, skills[skill_name], misc[skill_name], miscGear[skill_name]], function (values) {
const stat_score = int(values[skills[skill_name]]),
miscSkill_score = int(values[misc[skill_name]]),
miscGear_score = int(values[miscGear[skill_name]]),
skillRank_score = int(values[skill]);
setAttrs({
[skill + "_total"]: stat_score + skillRank_score + miscSkill_score + miscGear_score,
});
});
});
});
/* set profession skill base and skill total FIX courtesy of GiGs*/
on('sheet:opened change:repeating_skills:name change:repeating_skills:attribute change:repeating_skills:skill change:repeating_skills:misc change:repeating_skills:gear change:strength_total change:agility_total change:wits_total change:empathy_total', function () {
//clog('Change Detected: Profession Skill - recalculating Base & Skill totals');
getSectionIDs('repeating_skills', idarray => {
const fields = [];
idarray.forEach(id => fields.push(
`repeating_skills_${id}_name`,
`repeating_skills_${id}_attribute`,
`repeating_skills_${id}_base`,
`repeating_skills_${id}_skill`,
`repeating_skills_${id}_misc`,
`repeating_skills_${id}_gear`,
`repeating_skills_${id}_total`
));
// made the change event specific, so its only fired when needed, and likewise reduced the getAttrs to those only needed
getAttrs([...fields, 'strength_total', 'agility_total', 'wits_total', 'empathy_total'], function (values) {
const output = {};
idarray.forEach(id => {
const name = values[`repeating_skills_${id}_name`];
const attr = values[`repeating_skills_${id}_attribute`];
const skill_skill = int(values[`repeating_skills_${id}_skill`]);
const skill_misc = int(values[`repeating_skills_${id}_misc`]);
const skill_gear = int(values[`repeating_skills_${id}_gear`]);
// separate out the basic values read from the sheet from those calculated within the worker. for clarity
const skill_base = int(values[`${attr}_total`] || 0);
const skill_total = skill_base + skill_skill + skill_misc + skill_gear;
output[`repeating_skills_${id}_base`] = skill_base;
output[`repeating_skills_${id}_total`] = skill_total;
//clog('skill_name: ' + name);
//clog('skill_base: ' + skill_base);
//clog('skill_total: ' + skill_total);
});
setAttrs(output);
});
});
});
/* Monster strength-based skill total calcs */
Object.keys(monster_skills).forEach((skill_name) => {
const monster_skill = getSkill(skill_name);
on(`change:${monster_skill} change:${monster_skills[skill_name]} change:${monster_misc[skill_name]} change:${monster_miscGear[skill_name]}`, function () {
//clog(`${monster_skill} or ${monster_skills[skill_name]} or ${monster_misc[skill_name]} or ${monster_miscGear[skill_name]} has changed.`);
getAttrs([monster_skill, monster_skills[skill_name], monster_misc[skill_name], monster_miscGear[skill_name]], function (values) {
const stat_score = int(values[monster_skills[skill_name]]),
miscSkill_score = int(values[monster_misc[skill_name]]),
miscGear_score = int(values[monster_miscGear[skill_name]]);
setAttrs({
[monster_skill + "_total"]: stat_score + miscSkill_score + miscGear_score,
});
});
});
});
/* set repeating armor rating */
on("change:repeating_armor:bonus change:repeating_armor:bonus_max change:repeating_armor:bonus_misc change:repeating_armor:equipped", function () {
//clog("Change Detected: Repeating Armor Rating - recalculating rating totals");
// made the change event specific, so its only fired when needed, and likewise reduced the getAttrs to those only needed
getAttrs(["repeating_armor_bonus", "repeating_armor_bonus_misc", "repeating_armor_bonus_max", "repeating_armor_rating", "repeating_armor_equipped"], function (values) {
const bonus = int(values.repeating_armor_bonus),
bonus_misc = int(values.repeating_armor_bonus_misc),
bonus_max = int(values.repeating_armor_bonus_max),
equipped = int(values.repeating_armor_equipped);
// separate out the basic values read from the sheet from those calculated within the worker
const bonus_actual = Math.min(bonus, bonus_max);
const rating = (bonus_actual + bonus_misc) * equipped;
//clog("armor calculation: ( bonus " + bonus + " + bonus_misc " + bonus_misc + " ) * " + equipped + " = rating " + rating);
setAttrs({
repeating_armor_bonus: bonus_actual,
repeating_armor_rating: rating,
});
//clog("Armor rating: " + rating);
});
});
/* set weapon base_total and skill_total */
on("change:repeating_weapons:weapon_name change:repeating_weapons:weapon_skill change:repeating_weapons:weapon_skill_misc", function () {
//clog("Change Detected: Weapon Attacks - recalculating Base & Skill totals");
// made the change event specific, so its only fired when needed, and likewise reduced the getAttrs to those only needed
getAttrs(["repeating_weapons_weapon_skill", "repeating_weapons_weapon_skill_value", "repeating_weapons_weapon_skill_misc", "repeating_weapons_weapon_skill_total", "repeating_weapons_weapon_base_total", "strength_total", "agility_total", "shoot", "fight"], function (values) {
const strength = int(values.strength_total),
agility = int(values.agility_total),
fight = int(values.fight),
shoot = int(values.shoot),
weapon_skill = int(values.repeating_weapons_weapon_skill),
weapon_skill_misc = int(values.repeating_weapons_weapon_skill_misc);
// separate out the basic values read from the sheet from those calculated within the worker. for clarity
const attribute = weapon_skill === 0 ? strength : agility,
skill = weapon_skill === 0 ? fight : shoot,
weapon_base_total = attribute,
weapon_skill_value = skill,
weapon_skill_total = skill + weapon_skill_misc;
setAttrs({
repeating_weapons_weapon_base_total: weapon_base_total,
repeating_weapons_weapon_skill_value: weapon_skill_value,
repeating_weapons_weapon_skill_total: weapon_skill_total,
});
//clog(">>>> weapon_base_total: " + weapon_base_total);
//clog(">>>> weapon_skill_value: " + weapon_skill_value);
//clog(">>>> weapon_skill_total: " + weapon_skill_total);
});
});
/* set weapon bonus_total and max */
on("repeating_weapons:weapon_name change:repeating_weapons:weapon_bonus change:repeating_weapons:weapon_bonus_misc change:repeating_weapons:weapon_bonus_max", function () {
//clog("Change Detected: Weapon Attacks - recalculating Bonus totals");
// made the change event specific, so its only fired when needed, and likewise reduced the getAttrs to those only needed
getAttrs(["repeating_weapons_weapon_bonus", "repeating_weapons_weapon_bonus_misc", "repeating_weapons_weapon_bonus_max"], function (values) {
const weapon_bonus_misc = int(values.repeating_weapons_weapon_bonus_misc),
weapon_bonus = int(values.repeating_weapons_weapon_bonus),
weapon_bonus_max = int(values.repeating_weapons_weapon_bonus_max);
// separate out the basic values read from the sheet from those calculated within the worker
//If weapon bonus max is 0, then set it to the value of the weapon bonus
if (weapon_bonus_max === 0) {
//clog("setting weapon bonus max to value of weapon bonus (since no max detected). New max value = "+weapon_bonus);
setAttrs({repeating_weapons_weapon_bonus_max: weapon_bonus});
}
const weapon_bonus_total = Math.min(weapon_bonus, weapon_bonus_max) + weapon_bonus_misc;
setAttrs({
repeating_weapons_weapon_bonus_total: weapon_bonus_total,
});
//clog(">>>> weapon_bonus_total: " + weapon_bonus_total);
});
});
/* set weapon damage totals */
on("change:repeating_weapons:weapon_damage change:repeating_weapons:weapon_damage_misc", function () {
// clog("Change Detected: Weapon Damage - recalculating totals");
getAttrs(["repeating_weapons_weapon_damage", "repeating_weapons_weapon_damage_misc"], function (values) {
const weapon_damage = int(values.repeating_weapons_weapon_damage),
weapon_damage_misc = int(values.repeating_weapons_weapon_damage_misc),
damage = Math.max(weapon_damage + weapon_damage_misc, 0);
setAttrs({
repeating_weapons_weapon_damage_total: damage,
});
//clog(">>>> weapon_damage_total: " + damage);
});
});
/* re-calculate repeating_weapons when outside attributes change */
on("change:strength_total change:agility_total change:fight change:shoot", function () {
getSectionIDs("repeating_weapons", function (ids) {
const fieldnames = [];
ids.forEach((id) => {
fieldnames.push(`repeating_weapons_${id}_weapon_skill`, `repeating_weapons_${id}_weapon_skill_misc`, `repeating_weapons_${id}_weapon_skill_value`, `repeating_weapons_${id}_weapon_bonus`, `repeating_weapons_${id}_weapon_bonus_max`, `repeating_weapons_${id}_weapon_bonus_misc`);
});
getAttrs(["strength_total", "agility_total", "fight", "shoot", ...fieldnames], function (values) {
const settings = {};
const strength = int(values.strength_total),
agility = int(values.agility_total),
fight = int(values.fight),
shoot = int(values.shoot);
ids.forEach((id) => {
const weapon_skill = int(values[`repeating_weapons_${id}_weapon_skill`]),
// calculate attribute total
attribute = weapon_skill === 0 ? strength : agility,
weapon_base_total = attribute;
// calculate skill total
const weapon_skill_misc = int(values[`repeating_weapons_${id}_weapon_skill_misc`]),
skill = weapon_skill === 0 ? fight : shoot,
weapon_skill_total = skill + weapon_skill_misc;
//calculate bonus total
const weapon_bonus = int(values[`repeating_weapons_${id}_weapon_bonus`]),
weapon_bonus_max = int(values[`repeating_weapons_${id}_weapon_bonus_max`]),
weapon_bonus_misc = int(values[`repeating_weapons_${id}_weapon_bonus_misc`]),
weapon_bonus_total = Math.min(weapon_bonus, weapon_bonus_max) + weapon_bonus_misc;
// no need to calculate damage total, its not affected by external factors
settings[`repeating_weapons_${id}_weapon_base_total`] = weapon_base_total;
settings[`repeating_weapons_${id}_weapon_skill_value`] = skill;
settings[`repeating_weapons_${id}_weapon_skill_total`] = weapon_skill_total;
settings[`repeating_weapons_${id}_weapon_bonus_total`] = weapon_bonus_total;
});
setAttrs(settings, { silent: true });
});
});
});
/* set project completion */
on("change:repeating_projects:work_points change:repeating_projects:work_points_max", function () {
//clog("Change Detected: Repeating Project Completion - recalculating completion");
// made the change event specific, so its only fired when needed, and likewise reduced the getAttrs to those only needed
getAttrs(["repeating_projects_work_points", "repeating_projects_work_points_max", "repeating_projects_complete"], function (values) {
var points = int(values.repeating_projects_work_points),
points_max = int(values.repeating_projects_work_points_max),
complete = int(values.repeating_projects_complete);
// separate out the basic values read from the sheet from those calculated within the worker
points = Math.min(points,points_max),
complete = (points === points_max) ? 1 : 0;
const completedStr = (complete === 1) ? "completed" : "not completed";
//clog("project completion: work points " + points + " < required points " + points_max + " => " + completedStr);
setAttrs({
repeating_projects_work_points: points,
repeating_projects_complete: complete,
});
});
});
/* Wes's Dice Pool */
const pools = {
attribute: {
max: 12,
negative: 0,
},
skill: {
max: 10,
negative: 3,
},
gear: {
max: 12,
negative: 0,
},
};
// need to know the max pool size for each type, and whether negative dice should be in the pool.
const numbers = ["", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten", "eleven", "twelve"];
// need to convert numbers 1-12 to text. left index 0 empty, as its not needed.
Object.keys(pools).forEach((which) => {
on(`change:${which}`, function () {
getAttrs([which], function (values) {
let score = Math.min(pools[which].max, int(values[which]));
const settings = {};
// loop through numbers from 1 to the pool's max, and enter a value of 1 if less < pool value, or 0 otherwise.
for (let d = 1; d <= pools[which].max; d++) {
// pools[which].max gets the max value from the pools object.
settings[`${which}_die_${numbers[d]}`] = d <= score ? 1 : 0;
// `${which}_die_${numbers[d]}` creates a name like 'gear_die_three'
// (d <= score) ? 1 : 0; checks current index and assigns value of 1 if less than equal to pool score
// settings[attribute] = value stores the appropriate attribute names and values
}
// need to check negative dice, and compare score
// negative dice are set to zero. They are set to 1 only if pool.negative > 0, score is below zero, and absolute value of score is below 1-3.
for (let n = 1; n <= 3; n++) {
settings[`negative_die_${numbers[n]}`] = pools[which].negative && score < 0 && n <= Math.abs(score) ? 1 : 0;
// clog("Negative dice are being evaluated. |pools.negative value=" + pools[which].negative + " |Skill score=" + score + " |n=" + n + " |abs of score=" + Math.abs(score) + " |include negative die=" + settings[`negative_die_${numbers[n]}`]);
}
setAttrs(settings);
});
});
});
/* Set whiper options for the sheet */
on("change:whisper", (ev) => {
getAttrs(["api_toggle","whisper","whisper_option","whisper_option_api","translation_whisper_api_macro_query","translation_whisper_macro_query"], function (values) {
const whisper = int(values.whisper),
api = int(values.api_toggle),
setter = {};
if ( whisper === 1 ) {
setter.whisper_option = "/w gm";
setter.whisper_option_api = "!wmyz";
setter.whisper_the_roll = 1;
} else if ( whisper === 2 ) {
setter.whisper_option = values.translation_whisper_macro_query;
setter.whisper_option_api = values.translation_whisper_api_macro_query;
setter.whisper_the_roll = 0;
} else {
setter.whisper_option = "";
setter.whisper_option_api = "!myz";
setter.whisper_the_roll = 0;
}
//clog(`Whisper settings: whisper: ${values.whisper}, ${JSON.stringify(setter)}`);
setAttrs(setter,silent);
});
});
on("change:rolltemplate_toggle", function(ev) {
clog(`Rolltemplate type changed: ${JSON.stringify(ev)}`);
getAttrs(["rolltemplate_option"], (values) => {
const option = int(values.rolltemplate_option),
setter = {};
if ( option === 1 ) {
setter.rolltemplate_option = "myz";
} else {
setter.rolltemplate_option = "mutantyearzero";
}
setAttrs(setter,{silent: true});
});
});
/* Sheet worker for skills populating the dice pool */
skillsArr.forEach((skill) => {
//clog(`Creating sheet worker for skill ${skill.label} with Id ${skill['id']}`);
on(`clicked:dice_pool_${skill['id']}`, () => skillDicePoolButtons(skill['id']));
});
const skillDicePoolButtons = (skillId) => {
//clog(`Change Detected: Dice Pool ${skillId} - button clicked`);
const skill = skillsArr.filter((skill) => skill.id === skillId)[0],
rollname = getTranslationByKey(skill.label);
//clog(`Identified skill with Id ${skill.id}: ${JSON.stringify(skill)}`);
getAttrs([ skill.attr, skill.level, skill.misc, skill.gear, "whisper" ], (values) => {
//clog(`skill values: ${JSON.stringify(values)}`);
//clog(`skill name translation: ${getTranslationByKey(skill.label)} `);
const base = int(values[skill.attr]),
whisper = values.whisper === "1" ? 1 : 0;
setAttrs({
attribute: base > 0 ? base : 0 ,
skill: int(values[skill.level]) + int(values[skill.misc]),
gear: int(values[skill.gear]),
current_preset: rollname,
whisper_the_roll: whisper,
pushable: 1,
include_with_roll: `{{skill-base=${base} }} {{skill-skill=${int(values[skill.level])} }} {{skill-bonus-misc-skill=${int(values[skill.misc])}} } {{skill-bonus-misc-gear=${int(values[skill.gear])}} }`
});
});
}
/* DEPRECATED */
/*
Object.keys(skills).forEach((skill) => {
on(`clicked:dice_pool_${getSkill(skill)}`, () => dicePoolButtons(skill));
});
const dicePoolButtons = (which) => {
//clog(`Change Detected: Dice Pool ${which} - button clicked`);
getAttrs([skillAttrs[which], skillLevels[which], skillTotals[which], misc[which], miscGear[which], getSkill(which), getSkillKey(which), getSkillName(which),"whisper"], function (values) {
//clog(`Dice pool button values: ${JSON.stringify(values)}`);
// skillAttrs[which] returns the attribute; e.g skillAttrs['sneak'] gives 'agility_total'
// getSkill(which) converts the skill name to a valid attribute string: getSkill('Know the zone') gives 'know-the-zone'
// getSkillName(which) uses label, string or key to return character type specific translated skill name: getSkillName('Know the zone') gives 'Know nature' for Animals
const stat = int(values[skillAttrs[which]]),
miscSkill_score = Math.max(int(values[misc[which]]), -3),
miscGear_score = int(values[miscGear[which]]),
// stat here first gets skillAttrs['sneak'] which will be, say, 'agility' and
// then values['ability_total'] gives you the score from the character sheet.
skill_attribute_selected = stat > 0 ? stat : 0,
skill_bonus_misc_selected = miscSkill_score,
skill_bonus_misc_gear_selected = miscGear_score,
skill_only_selected = int(values[`${skillLevels[which]}`]),
skill = getSkill(which),
skill_name = getSkillName(skill); // used to get the proper translated character type skill
//clog("Found skill button click, Skill key "+JSON.stringify(which)+" calculated to "+skill+" corresponding to character type "+skill_name);
setAttrs({
attribute: stat > 0 ? stat : 0, // this is a Ternary Operator, it starts with a question (e.g. is stat > 0)
// and if true, returns the bit after the ? and if false returns the bit after the :
// you can often replace simple if/then expressions with a single line ternary operator.
// uses the translation key
gear: miscGear_score,
//skill: int(values[getSkill(which)]) + miscSkill_score, // Using skillTotals instead
skill: int(values[`${skillLevels[which]}`]) + skill_bonus_misc_selected,
skill_attribute_selected: skill_attribute_selected,
skill_bonus_misc_selected: skill_bonus_misc_selected,
skill_bonus_misc_gear_selected: skill_bonus_misc_gear_selected,
skill_only_selected: skill_only_selected,
//skill_notes_selected: skill_notes_selected,
current_preset: skill_name,
whisper_the_roll: int(values.whisper),
include_with_roll: `{{skill-base=${skill_attribute_selected}}} {{skill-skill=${skill_only_selected}}} {{skill-bonus-misc-skill=${skill_bonus_misc_selected}}} {{skill-bonus-misc-gear=${skill_bonus_misc_gear_selected}}}}`, // {{skill-notes=${skill_notes_selected}}
});
});
};
*/
/* */
Object.keys(monster_skills).forEach((monster_skill) => {
on(`clicked:dice_pool_${getSkill(monster_skill)}`, () => monster_dicePoolButtons(monster_skill));
});
/* Monster strength-based skill dice pool */
const monster_dicePoolButtons = (which) => {
//clog(`Change Detected: Dice Pool ${which} - button clicked`);
getAttrs([monster_skills[which], monster_misc[which], monster_miscGear[which], getSkill(which), getSkillKey(which), "whisper"], function (values) {
const stat = int(values[monster_skills[which]]),
miscSkill_score = Math.max(int(values[monster_misc[which]]), -3),
miscGear_score = int(values[monster_miscGear[which]]);
let whichKey = getTranslationByKey(getSkillKey(which));
setAttrs({
attribute: stat > 0 ? stat : 0,
gear: miscGear_score,
skill: miscSkill_score,
current_preset: whichKey,
whisper_the_roll: int(values.whisper),
include_with_roll: "",
});
});
};
/* RESET DICE POOL */
on("sheet:opened clicked:dice_pool_clear", function () {
getAttrs(["whisper"], (values) => {
//clog(`Change Detected: Clear Dice Pool - button clicked or sheet loaded (whisper is ${values.whisper})`);
//const whisper = (int(values.whisper) === 1) ? 1 : 0;
setAttrs({
attribute: 0,
skill: 0,
gear: 0,
current_preset: getTranslationByKey('custom-roll'),
whisper_the_roll: int(values.whisper),
include_with_roll: "",
pushable: 1,
push_latest: "",
showattrextra: "0",
showgearextra: "0",
});
});
});
on("clicked:add_die_attr clicked:add_die_gear", function(ev) {
clog(`clicked add die: ${JSON.stringify(ev)}`);
getAttrs(["","attribute","gear"], (values) => {
const base = int(values.attribute),
gear = int(values.gear),
setter = {},
silent = {silent: true};
if( base <= 12 ) { setter.attribute = 13; }
else { setter.attribute = base + 1; }
clog(`clicked add die, setter: ${JSON.stringify(setter)}`);
setAttrs(setter,silent);
});
});
/* api dice toggle
on("clicked:api_dice_toggle", function () {
//clog("Change Detected: API Dice Toggle - button clicked");
getAttrs(["api_toggle"], function (values) {
const api_toggle = int(values.api_toggle);
let buttonState = api_toggle;
if (buttonState !== 0) {
buttonState = 0;
} else {
buttonState = 1;
}
setAttrs({
api_toggle: buttonState,
},{silent:true});
});
}); */
/* Update the Dice Pool indicator */
on(
"clicked:dice_pool_clear clicked:strength_roll clicked:agility_roll cliecked:wits_roll clicked:empathy_roll clicked:rot_roll clicked:repeating_skills:skill-roll clicked:dice_pool_force_monster clicked:dice_pool_endure_monster clicked:dice_pool_fight_monster clicked:dice_pool_force clicked:dice_pool_endure clicked:dice_pool_fight clicked:dice_pool_sneak clicked:dice_pool_know_the_zone clicked:dice_pool_move clicked:dice_pool_shoot clicked:dice_pool_scout clicked:dice_pool_comprehend clicked:dice_pool_manipulate clicked:dice_pool_sense_emotion clicked:dice_pool_heal clicked:reputation-roll clicked:repeating_weapons:attack-roll clicked:repeating_weapons:attack-parry-roll clicked:repeating_monster:monster-features-roll clicked:repeating_spells:spell-roll clicked:armor-roll",
function () {
//clog("Change Detected: Update Dice Pool");
getAttrs(["roll_the_dice_flag"], function (values) {
const flag = int(values.roll_the_dice_flag);
setAttrs({
roll_the_dice_flag: 1 - flag,
});
});
}
);
on("sheet:opened change:roll_the_dice_flag", function (event) {
//clog("Change Detected: resetting roll_the_dice_flag to zero");
const flag = int(event.newValue) || 0; // you don't need to include the ,10
if (flag === 1)
setAttrs(
{
roll_the_dice_flag: 0,
},
{
silent: true,
}
);
});
/* translate query text */
on("sheet:opened", function () {
//clog("Translation of query text complete.");
setAttrs({
translation_push_query: getTranslationByKey("push-query"),
translation_whisper_macro_query: "?{" + getTranslationByKey("whisper-macro") + "|" + getTranslationByKey("no") + ",|" + getTranslationByKey("yes") + ",/w gm |" + "}",
translation_whisper_api_macro_query: "?{" + getTranslationByKey("whisper-macro") + "|" + getTranslationByKey("no") + ",!myz |" + getTranslationByKey("yes") + ",!wmyz |" + "}",
});
});
const rollEscape = {
chars: {
'"': '%quot;',
',': '%comma;',
':': '%colon;',
'}': '%rcub;',
'{': '%lcub;',
},
escape(str) {
str = (typeof(str) === 'object') ? JSON.stringify(str) : (typeof(str) === 'string') ? str : null;
return (str) ? `${str}`.replace(new RegExp(`[${Object.keys(this.chars)}]`, 'g'), (r) => this.chars[r]) : null;
},
unescape(str) {
str = `${str}`.replace(new RegExp(`(${Object.values(this.chars).join('|')})`, 'g'), (r) => Object.entries(this.chars).find(e=>e[1]===r)[0]);
return JSON.parse(str);
}
}
const expandDice = (dice,name) => {
console.log("Expand dice: "+JSON.stringify(dice));
var diceResults = dice.dice;
var output = [];
console.log("Output pre: "+ JSON.stringify(output));
for(let i = 0; i < diceResults.length; i++) {
output[`${name}-dice-${i}`] = `{"result": ${diceResults[i]},"dice": [${diceResults[i]}],"expression": "1D6","rolls": [{"sides": 6,"dice": 1,"results": [${diceResults[i]}] } ] }`;
}
console.log("Output post: "+ JSON.stringify(output));
return output;
}
// Array to map number to literal numbers, e.g. 1 to "one", 2 to "two" by using litnum[1] and litnum[2] respectively
const litnum = [ "zero","one","two","three","four","five","six","seven","eight","nine","ten","eleven","twelve" ];
on('clicked:push', async (ev) => {
//clog("Starting push roll!");
//clog(JSON.stringify(ev));
const reroll = rollEscape.unescape(ev.originalRollId);
//clog(`Reroll object: ${JSON.stringify(reroll)}`);
const attributes = reroll.attributes,
roll = reroll.roll;
executeRoll({ev,attributes,roll});
});
on("clicked:roll_the_dice", () => {
getAttrs( ["character_name", "character_id", "chartype","attribute", "skill", "gear", "current_preset", "pushable","include_with_roll", "whisper_option", "whisper_the_roll","translation_whisper_macro_query"], async (values) => {
const trigger = {name : values.current_preset},
attributes = {
character_name : values.character_name||"Unknown",
character_id : values.character_id||0,
chartype_id : values.chartype === 'Mutant' ? 1 : ( values.chartype === 'Animal' ? 2 : ( values.chartype === 'Robot' ? 3 : ( values.chartype === 'Human' ? 4 : 1 ))),
},
roll = {
name: values.current_preset||'Roll',
attribute: int(values.attribute),
skill: int(values.skill),
gear: int(values.gear),
totalDice: int(values.attribute)+Math.abs(int(values.skill))+int(values.gear),
whisper: ( int(values.whisper_the_roll) === 1 ) ?
"/w gm" : ( int(values.whisper_the_roll) === 2 ) ?
values.translation_whisper_macro_query : "",
pushable: int(values.pushable),
pushroll: 0,
include: values.include_with_roll,
};
executeRoll({trigger,attributes,roll});
});
});
const executeRoll = function({trigger,attributes,roll}) {
var attributeStr = "",
skillStr = "",
gearStr = "",
rollStr = "";
//clog(`TEST: Roll object: ${JSON.stringify(roll)}`);
//clog(`TEST: Roll pushRollStr: ${!!roll.pushRollStr}`);
if (roll.pushRollStr) {
rollStr = roll.pushRollStr;
} else {
for(let i = 1; i <= roll.attribute; i++) { attributeStr += `{{attribute-die-${litnum[i]}=[[1]] }} {{attribute-roll-${litnum[i]}= [[1d6cf<2cs>5]] }} `; }
if( roll.skill >= 0 ) {
for(let i = 1; i <= roll.skill; i++) { skillStr += `{{skill-die-${litnum[i]}= [[1]] }} {{skill-roll-${litnum[i]}= [[1d6cf<0cs>5]] }} `; }
} else if ( roll.skill < 0) {
for(let i = 1; i <= Math.abs(roll.skill); i++) { skillStr += `{{negative-die-${litnum[i]}= [[1]] }} {{negative-roll-${litnum[i]}= [[1d6cf<0cs>5]] }} `; }
}
for(let i = 1; i <= roll.gear; i++) { gearStr += `{{gear-die-${litnum[i]}= [[1]] }} {{gear-roll-${litnum[i]}= [[1d6cf<2cs>5]] }} `; }
rollStr = `${roll.whisper} &{template:@{rolltemplate_option}} {{character_id=${attributes['character_id']}}} {{character_name=${attributes['character_name']}}} {{chartype_id=[[${attributes['chartype_id']}]]}} {{name=${roll.name}}} {{successes=[[0]]}} {{push=[[${roll.pushable}]]}} {{pushroll=[[${roll.pushroll}]]}} ${attributeStr} ${skillStr} ${gearStr}`;
}
clog("Roll string; "+ rollStr);
startRoll(rollStr, (results) => {
//clog("Roll results: "+ JSON.stringify(results));
var pushAttributeStr = "",
pushSkillStr = "",
pushGearStr = "",
successes = 0,
failures = 0,
gearfails = 0;
roll.pushroll = roll.pushroll + 1;
for(let i = 1; i <= roll.attribute; i++) {
let r = (results.results[`attribute-roll-${litnum[i]}`]).result;
//clog(`results attribute ${i} : ${r}`);
if ( r == '6' || r == 6 ) {
pushAttributeStr += `{{attribute-die-${litnum[i]}=[[1]] }} {{attribute-roll-${litnum[i]}=[[6cs>5]]}} `;
successes++;
//clog(`results attribute die ${i} is 6, total ${successes} successes`);
} else if ( r == '1' || r == 1 ) {
pushAttributeStr += `{{attribute-die-${litnum[i]}=[[1]] }} {{attribute-roll-${litnum[i]}=[[1cf<2]]}} `;
failures++;
//clog(`results attribute die ${i} is 0, total ${failures} failures`);
} else {
pushAttributeStr += `{{attribute-die-${litnum[i]}=[[1]] }} {{attribute-roll-${litnum[i]}=[[1d6cf<2cs>5]]}} `;
//clog(`results attribute die ${i} is ${r}, total ${successes} successes and ${failures} failures`);
}
}
if ( roll.skill >= 0 ) {
for(let i = 1; i <= roll.skill; i++) {
let r = (results.results[`skill-roll-${litnum[i]}`]).result;
//clog(`results skill ${i} : ${r}`);
if ( r == '6' || r == 6 ) {
pushSkillStr += `{{skill-die-${litnum[i]}=[[1]]}} {{skill-roll-${litnum[i]}=[[6cs>5]]}} `;
successes++;
//clog(`results skill die ${i} is 6, total ${successes} successes`);
} else {
pushSkillStr += `{{skill-die-${litnum[i]}=[[1]]}} {{skill-roll-${litnum[i]}=[[1d6cs>5]]}} `;
//clog(`results skill die ${i} is ${r}, total ${successes} successes and ${failures} failures`);
}
}
} else if ( roll.skill < 0 ) {
for(let i = 1; i <= Math.abs(roll.skill); i++) {
let r = (results.results[`negative-roll-${litnum[i]}`]).result;
//clog(`negative skill ${i} : ${r}`);
if ( r == '6' || r == 6 ) {
pushSkillStr += `{{negative-die-${litnum[i]}=[[1]]}} {{negative-roll-${litnum[i]}=[[6cs>5]]}} `;
successes++;
//clog(`results negative skill die ${i} is 6, total ${successes} successes (decreased)`);
} else {
pushSkillStr += `{{negative-die-${litnum[i]}=[[1]]}} {{negative-roll-${litnum[i]}=[[1d6cs>5]]}} `;
//clog(`results skill die ${i} is ${r}, total ${successes} successes and ${failures} failures`);
}
}
}
for(let i = 1; i <= roll.gear; i++) {
let r = (results.results[`gear-roll-${litnum[i]}`]).result;
//clog(`results gear ${i} : ${r}`);
if ( r == '6' || r == 6 ) {
pushGearStr += `{{gear-die-${litnum[i]}=[[1]]}} {{gear-roll-${litnum[i]}=[[6cs>5]]}} `;
successes++;
//clog(`results gear die ${i} is 6, total ${successes} successes`);
} else if ( r == '1' || r == 1 ) {
pushGearStr += `{{gear-die-${litnum[i]}=[[1]]}} {{gear-roll-${litnum[i]}=[[1cf<2]]}} `;
failures++;
gearfails++;
//clog(`results gear die ${i} is 0, total ${failures} failures and ${gearfails} from gear`);
} else {
pushGearStr += `{{gear-die-${litnum[i]}=[[1]]}} {{gear-roll-${litnum[i]}=[[1d6cf<2cs>5]]}} `;
//clog(`results gear die ${i} is ${r}, total ${successes} successes and ${failures} failures`);
}
}
// NB: Successes and failures counts number fo dice which cannot be rerolled.
// In case of negative skill dice, the actual successes may be lower
if ( (successes + failures) == roll.totalDice ) {
roll.pushable = 0;
}
roll.pushRollStr = `${roll.whisper} &{template:@{rolltemplate_option}} {{character_id=${attributes['character_id']}}} {{character_name=${attributes['character_name']}}} {{chartype_id=[[${attributes['chartype_id']}]]}} {{name=${roll.name}}} {{push=[[${roll.pushable}]]}} {{pushroll=[[${roll.pushroll}]]}} {{successes=[[${successes}]]}} {{failures=[[${failures}]]}} {{gearfails=[[${gearfails}]]}} ${pushAttributeStr} ${pushSkillStr} ${pushGearStr}`;
const pushRoll = {
attributes: attributes,
roll: roll,
successes: successes,
failures: failures,
gearfails: gearfails,
totalDice: roll.totalDice,
whisper: roll.whisper,
include: roll.include,
};
setAttrs({
push_latest: pushRoll,
pushable: 1,
});
//clog("Push data: "+ JSON.stringify(pushRoll));
finishRoll(results.rollId, {
pushroll: rollEscape.escape(pushRoll),
push: roll.pushable,
});
});
}
/* set weapon dice pool */
on("clicked:repeating_weapons:attack-roll", function () {
//clog("Change Detected: Weapon Attack - button clicked");
getAttrs( ["repeating_weapons_weapon_name", "repeating_weapons_weapon_skill", "repeating_weapons_weapon_base_total", "repeating_weapons_weapon_skill_misc", "repeating_weapons_weapon_skill_total", "repeating_weapons_weapon_bonus", "repeating_weapons_weapon_bonus_max", "repeating_weapons_weapon_bonus_misc", "repeating_weapons_weapon_bonus_total", "repeating_weapons_weapon_damage", "repeating_weapons_weapon_damage_misc", "repeating_weapons_weapon_damage_total", "repeating_weapons_weapon_range", "repeating_weapons_weapon_features", "strength_total", "agility_total", "shoot", "fight", "whisper"],
function (values) {
const weapon_name = values.repeating_weapons_weapon_name,
weapon_base_total = int(values.repeating_weapons_weapon_base_total),
weapon_bonus = int(values.repeating_weapons_weapon_bonus),
weapon_bonus_misc = int(values.repeating_weapons_weapon_bonus_misc),
weapon_bonus_max = int(values.repeating_weapons_weapon_bonus_max),
weapon_bonus_total = int(values.repeating_weapons_weapon_bonus_total),
weapon_skill_total = int(values.repeating_weapons_weapon_skill_total),
weapon_damage = int(values.repeating_weapons_weapon_damage),
weapon_damage_misc = int(values.repeating_weapons_weapon_damage_misc),
weapon_range = values.repeating_weapons_weapon_range,
weapon_features = values.repeating_weapons_weapon_features;
setAttrs({
attribute: weapon_base_total,
gear: weapon_bonus_total,
skill: weapon_skill_total,
current_preset: getTranslationByKey(`attack`) + ` - ${weapon_name}`,
whisper_the_roll: int(values.whisper),
include_with_roll: `{{weapon-bonus=${weapon_bonus}}} {{weapon-bonus-max=${weapon_bonus_max}}} {{weapon-bonus-misc=${weapon_bonus_misc}}} {{weapon-damage=${weapon_damage}}} {{weapon-damage-misc=${weapon_damage_misc}}} {{weapon-range=${weapon_range}}} {{weapon-features=${weapon_features}}}`,
});
}
);
});
/* set weapon parry dice pool */
on("clicked:repeating_weapons:attack-parry-roll", function () {
//clog("Change Detected: Weapon Parry - button clicked");
getAttrs( ["repeating_weapons_weapon_name", "repeating_weapons_weapon_skill", "repeating_weapons_weapon_base_total", "repeating_weapons_weapon_skill_misc", "repeating_weapons_weapon_skill_total", "repeating_weapons_weapon_bonus", "repeating_weapons_weapon_bonus_max", "repeating_weapons_weapon_bonus_misc", "repeating_weapons_weapon_bonus_total", "repeating_weapons_weapon_damage", "repeating_weapons_weapon_damage_misc", "repeating_weapons_weapon_damage_total", "repeating_weapons_weapon_range", "repeating_weapons_weapon_features", "strength_total", "agility_total", "shoot", "fight","whisper"],
function (values) {
const weapon_name = values.repeating_weapons_weapon_name,
weapon_base_total = int(values.repeating_weapons_weapon_base_total),
weapon_bonus = int(values.repeating_weapons_weapon_bonus),
weapon_bonus_max = int(values.repeating_weapons_weapon_bonus_max),
weapon_bonus_misc = int(values.repeating_weapons_weapon_bonus_misc),
weapon_bonus_total = int(values.repeating_weapons_weapon_bonus_total),
weapon_skill_total = int(values.repeating_weapons_weapon_skill_total),
weapon_damage = int(values.repeating_weapons_weapon_damage),
weapon_damage_misc = int(values.repeating_weapons_weapon_damage_misc),
weapon_features = values.repeating_weapons_weapon_features;
setAttrs({
attribute: weapon_base_total,
gear: weapon_bonus_total,
skill: weapon_skill_total,
current_preset: getTranslationByKey(`parry`) + ` - ${weapon_name}`,
whisper_the_roll: int(values.whisper),
include_with_roll: `{{weapon-bonus=${weapon_bonus}}} {{weapon-bonus-max=${weapon_bonus_max}}} {{weapon-bonus-misc=${weapon_bonus_misc}}} {{weapon-damage=${weapon_damage}}} {{weapon-damage-misc=${weapon_damage_misc}}} {{weapon-range=^{arms-length-abbr}}} {{weapon-features=${weapon_features}}}`,
});
}
);
});
/* Monster attacks dice pool */
on("clicked:repeating_monster:monster-features-roll", function () {
//clog("Change Detected: Monster Attack - button clicked");
getAttrs(["repeating_monster_attack_name", "repeating_monster_attack_dice", "repeating_monster_attack_damage", "repeating_monster_attack_features", "repeating_monster_attack_range", "repeating_monster_attack_description", "whisper"], function (values) {
const monster_attack_name = values.repeating_monster_attack_name,
monster_attack_dice = int(values.repeating_monster_attack_dice),
monster_attack_damage = int(values.repeating_monster_attack_damage),
monster_attack_features = values.repeating_monster_attack_features,
monster_attack_range = values.repeating_monster_attack_range,
monster_attack_description = values.repeating_monster_attack_description,
attribute = monster_attack_dice;
setAttrs({
attribute: attribute,
skill: 0,
gear: 0,
current_preset: monster_attack_name,
whisper_the_roll: int(values.whisper),
include_with_roll: `{{weapon-damage=${monster_attack_damage}}} {{attack-features=${monster_attack_features}}} {{weapon-range=${monster_attack_range}}} {{attack-description=${monster_attack_description}}}`,
});
});
});
/* set prof skill dice pool */
on("clicked:repeating_skills", function () {
//clog("Change Detected: Profession Skill - button clicked");
getAttrs(["repeating_skills_name", "repeating_skills_base", "repeating_skills_skill", "repeating_skills_misc", "repeating_skills_gear","whisper"], function (values) {
const skill_name = values.repeating_skills_name,
skill_base = int(values.repeating_skills_base),
skill_level = int(values.repeating_skills_skill),
skill_misc = int(values.repeating_skills_misc),
skill_gear = int(values.repeating_skills_gear),
skill_skill = skill_level + skill_misc;
setAttrs({
attribute: skill_base,
skill: skill_skill,
gear: skill_gear,
current_preset: `${skill_name}`,
whisper_the_roll: int(values.whisper),
include_with_roll: `{{skill-base=${skill_base}}} {{skill-level=${skill_level}}} {{skill-bonus-misc=${skill_misc}}} {{skill-bonus-gear=${skill_gear}}}`,
});
});
});
/* set dice pool for Powers action button - NOT WORKING (buttons are hidden) */
const powers = ["mutations","modules","animalpowers","contacts","whisper"];
powers.forEach(pow => {
on(`clicked:repeating_${pow}:powerroll`, (eventInfo) => {
//clog(JSON.stringify(eventInfo.sourceAttribute));
var getAttrsString = [`repeating_${pow}_name`,`repeating_${pow}_powerlevel`];
getAttrs(getAttrsString, function(values) {
const name = values[`repeating_${pow}_name`],
level = int(values[`repeating_${pow}_powerlevel`]),
setAttrObj = {
attribute: level,
skill: 0,
gear: 0,
current_preset: getTranslationByKey(`power`) + ` - ${name}`,
whisper_the_roll: int(values.whisper),
include_with_roll: ``,
};
//clog(name);
//clog(level);
//clog(JSON.stringify(setAttrObj));
SetAttrs(setAttrObj);/**/
});
});
});
/* set spell dice pool
on("clicked:repeating_spells:spell-roll", function () {
//clog("Change Detected: Spell - button clicked");
getAttrs(["repeating_spells_spell_name", "repeating_spells_spell_rank", "repeating_spells_spell_range", "repeating_spells_spell_duration", "repeating_spells_spell_ingredients", "repeating_spells_spell_description", "repeating_spells_spell_powerpoint", "powerpoint", "whisper"], function (values) {
const spell_name = values.repeating_spells_spell_name,
spell_rank = int(values.repeating_spells_spell_rank),
spell_powerpoint = int(values.repeating_spells_spell_powerpoint),
spell_range = values.repeating_spells_spell_range,
spell_duration = values.repeating_spells_spell_duration,
spell_ingredients = values.repeating_spells_spell_ingredients,
spell_description = values.repeating_spells_spell_description,
powerpoint = int(values.powerpoint);
setAttrs({
attribute: spell_powerpoint,
skill: 0,
gear: 0,
current_preset: getTranslationByKey(`spell`) + ` - ${spell_name}`,
whisper_the_roll: int(values.whisper),
include_with_roll: `{{powerpoint-total=${powerpoint}}} {{spell-rank=${spell_rank}}} {{spell-powerpoint=${spell_powerpoint}}} {{spell-range=${spell_range}}} {{spell-duration=${spell_duration}}} {{spell-ingredients=${spell_ingredients}}} {{spell-description=${spell_description}}}`,
});
});
}); */
/* set armor dice pool */
on("clicked:armor-roll", function () {
//clog("Change Detected: Armor - button clicked");
getAttrs(["ar_total","whisper"], function (values) {
const ar_total = int(values.ar_total);
setAttrs({
attribute: 0,
gear: ar_total,
skill: 0,
current_preset: getTranslationByKey(`armor-roll`),
whisper_the_roll: int(values.whisper),
include_with_roll: `{{armor-rating=[[ ${ar_total} ]]}}`,
pushable: 0,
});
}
);
});
/* set rot dice pool */
on("clicked:rot_roll", function () {
//clog("Change Detected: Rot - button clicked");
getAttrs(["rot", "rot_permanent","whisper"], function (values) {
const rot = int(values.rot),
rot_permanent = int(values.rot_permanent);
setAttrs({
attribute: rot,
gear: 0,
skill: 0,
current_preset: getTranslationByKey(`rot-roll`),
whisper_the_roll: int(values.whisper),
include_with_roll: `{{rot-level=[[ ${rot} ]]}} {{rot-permanent=[[ ${rot_permanent} ]]}}`,
pushable: 0,
});
}
);
});
/* vehicle/Beast Carry calcs */
const carry = (strength, vehicleed) => (strength * 4) / (vehicleed + 1);
on("change:vehicleed change:vehicle_strength", function () {
//clog("Change Detected: vehicleed/Unvehicleed");
getAttrs(["vehicleed", "vehicle_strength"], function (values) {
const vehicleed = int(values["vehicleed"]),
strength = int(values["vehicle_strength"]),
Carry = carry(strength, vehicleed);
setAttrs({
vehicle_carry: Carry,
});
});
});
on("change:repeating_beasts", function () {
//clog("Change Detected: Beasts Check Carry Calculation");
getAttrs(["repeating_beasts_beast_vehicleed", "repeating_beasts_beast_strength"], function (values) {
const vehicleed = int(values["repeating_beasts_beast_vehicleed"]),
strength = int(values["repeating_beasts_beast_strength"]),
Carry = carry(strength, vehicleed);
setAttrs({
repeating_beasts_beast_carry: Carry,
});
});
});
/* Broken Indicator flags*/
// first create an array listing all 4 stats
const stats = ["strength_pc", "strength_monster", "agility", "wits", "empathy"];
// attribute and skill maximum
// function which takes an attribute name, its score, and its max, and checks the score is valid.
// if not, resets its value.
const checkMax = (stat, score, max) => {
const settings = {};
if (score > max) {
settings[stat] = max;
} else if (score < 0) {
settings[stat] = 0;
}
return settings;
};
stats.forEach((stat) => {
on(`change:${stat} change:tab`, function () {
getAttrs([stat, "tab"], function (values) {
const total = int(values[stat]);
const tab = values.tab;
//clog("Attribute "+stat+" on tab "+tab+" is "+total);
//clog("tab === 'monster' = "+(tab === 3 || tab === 'monster'));
var max = tab === 'monster' ? 12 : 6;
// const max attribute = 12 for monsters;
//clog("tab === 'monster' && stat === strength_monster = "+tab === 'monster' && stat === 'strength_monster');
max = tab === 'monster' && stat === 'strength_monster' ? 40 : 12;
// const max strength = 40 for monsters;
const settings = checkMax(stat, total, max);
if (settings) setAttrs(settings);
});
});
});
/* Duplicate
Object.keys(attributes).forEach((stat) => {
on(`change:${stat}`, function () {
getAttrs([stat], function (values) {
const total = int(values[stat]);
const max = pools.attribute.max;
const settings = checkMax(stat, total, max);
if (settings) setAttrs(settings);
});
});
});
*/
/* Update attributes total max
on("sheet:opened change:strength_total", function () {
//clog("Change Detected: strength_total has changed");
getAttrs(["strength", "strength_total"], function (values) {
const strength = int(values.strength),
strength_total = int(values.strength_total);
setAttrs({
strength: strength,
strength_total: strength_total,
});
});
});
on("sheet:opened change:agility_total", function () {
//clog("Change Detected: agility_total has changed");
getAttrs(["agility", "agility_total"], function (values) {
const agility = int(values.agility),
agility_total = int(values.agility_total);
setAttrs({
agility: agility,
agility_total: agility_total,
});
});
});
on("sheet:opened change:wits_total", function () {
//clog("Change Detected: wits_total has changed");
getAttrs(["wits", "wits_total"], function (values) {
const wits = int(values.wits),
wits_total = int(values.wits_total);
setAttrs({
wits: wits,
wits_total: wits_total,
});
});
});
on("sheet:opened change:empathy_total", function () {
//clog("Change Detected: empathy_total has changed");
getAttrs(["empathy", "empathy_total"], function (values) {
const empathy = int(values.empathy),
empathy_total = int(values.empathy_total);
setAttrs({
empathy: empathy,
empathy_total: empathy_total,
});
});
}); */
mainAttributesArr.forEach( (attrb) => {
on(`sheet:opened change:${attrb.name}_total`, function () {
//clog(`Change Detected: ${attrb.name}_total has changed`);
getAttrs([`${attrb.name}`, `${attrb.name}_total`], function (values) {
const attr = int(values[`${attrb.name}`]),
attr_total = int(values[`${attrb.name}_total`]);
setAttrs({
[`${attrb.name}`]: attr,
[`${attrb.name}_total`]: attr_total,
});
});
});
});
attributesArr.forEach( (attrb) => {
on(`clicked:${attrb.name}_roll`, function (ev) {
//clog(`Change Detected: ${attrb.name} - button clicked`);
//clog(`Event : ${JSON.stringify(ev)}`);
getAttrs([`${attrb.attr}_total`, "whisper"], function (values) {
const total = int(values[`${attrb.attr}_total`]),
name = getTranslationByKey(attrb.name);
//clog(name + " : " + total);
setAttrs({
attribute: total,
skill: 0,
gear: 0,
current_preset: name,
whisper_the_roll: int(values.whisper),
include_with_roll: "",
});
});
});
});
/* set strength dice pool
on("clicked:strength_roll", function () {
//clog("Change Detected: Strength - button clicked");
getAttrs(["strength_total", "strength_name"], function (values) {
const strength_total = int(values.strength_total),
strength_name = values.strength_name;
//clog(strength_name + " : " + strength);
setAttrs({
attribute: strength_total,
skill: 0,
gear: 0,
current_preset: strength_name,
include_with_roll: "",
});
});
}); */
/* set agility dice pool
on("clicked:agility_roll", function () {
//clog("Change Detected: agility - button clicked");
getAttrs(["agility_total", "agility_name"], function (values) {
const agility_total = int(values.agility_total),
agility_name = values.agility_name;
//clog(agility_name + " : " + agility_total);
setAttrs({
attribute: agility_total,
skill: 0,
gear: 0,
current_preset: agility_name,
include_with_roll: "",
});
});
}); */
/* set wits dice pool
on("clicked:wits_roll", function () {
//clog("Change Detected: wits - button clicked");
getAttrs(["wits_total","wits_name"], function (values) {
const wits_total = int(values.wits_total),
wits_name = values.wits_name;
//clog(wits_name + " : " + wits_total);
setAttrs({
attribute: wits_total,
skill: 0,
gear: 0,
current_preset: wits_name,
include_with_roll: "",
});
});
}); */
/* set empathy dice pool
on("clicked:empathy_roll", function () {
//clog("Change Detected: empathy - button clicked");
getAttrs(["empathy_total", "empathy_name"], function (values) {
const empathy_total = int(values.empathy_total),
empathy_name = values.empathy_name;
//clog(empathy_name + " : " + empathy_total);
setAttrs({
attribute: empathy_total,
skill: 0,
gear: 0,
current_preset: empathy_name,
include_with_roll: "",
});
});
}); */
/* beautify preserve:end */