Skip to content

Instantly share code, notes, and snippets.

@Geritz
Last active October 19, 2024 21:42
Show Gist options
  • Save Geritz/978cdd2ade053b5c357318306909c588 to your computer and use it in GitHub Desktop.
Save Geritz/978cdd2ade053b5c357318306909c588 to your computer and use it in GitHub Desktop.
LCP Json parser for Roll20
// LCP PC BUILDER by Spencer Moran
// AUTHORS NOTE: This script relies on a separate variables file called
// "ScriptData.js" which contains the datasets used by this script.
// YOU NEED BOTH FILES FOR THIS SCRIPT TO WORK. Due to load order restrictions
// The dataset will not register in this script until after you have first
// saved the script in Roll20. And then subsequently saved this script:
// STEP 1: Save ScriptData.js
// STEP 2: Save lcp_pc_builder.js
// You will need to do this each time you make a change to the datasets.
// CHAT TABLE FACTORY
var CTF = {
'addTemplate' : function(template="default"){
return "&{template:"+template+"}";
},
'addTableHeader' : function(name){
return "{{name="+name+"}}";
},
'addTableItem' : function(content){
return "{{"+content+"}}";
},
'addTableButton' : function(content,link="!lncr_stub"){
return "["+content+"]("+link+")";
}
}
// FUNCTIONS
// HELPER FUNCTIONS
function p_lncr_roll20objcontains(value,type='macro'){
try {
let test = findObjs({
_type: type,
name: value
});
return ((test.length <= 0)? false : true);
} catch (e){
sendChat("LANCER CORE HELPER", "Something went wrong when attempting to do a contains test:" + e);
}
}
// FIND LCP ENTRY BY NAME
function FindPcFrameByName(frameName){
let frame;
frame_data_set.forEach((element) => {
if (typeof frame != 'undefined'){
return frame;
}
frame = element.find((obj) => obj.name.toLowerCase() == frameName.toLowerCase());
});
if (typeof frame == 'undefined'){
sendChat("LCP BUILD ERROR", "could not find a frame called \"" + frameName + "\" in stored LCP files");
}
return frame;
}
function FindPcSystemByName(systemName){
let system;
system_data_set.forEach((element) => {
if (typeof system != 'undefined'){
return system;
}
system = element.find((obj) => obj.name.toLowerCase() == systemName.toLowerCase());
});
if (typeof system == 'undefined'){
sendChat("LCP BUILD ERROR", "could not find a system called \"" + systemName + "\" in stored LCP files");
}
return system;
}
function FindPcWeaponByName(weaponName){
let weapon;
weapon_data_set.forEach((element) => {
if (typeof weapon != 'undefined'){
return weapon;
}
weapon = element.find((obj) => obj.name.toLowerCase() == weaponName.toLowerCase());
});
if (typeof weapon == 'undefined'){
sendChat("LCP BUILD ERROR", "could not find a weapon called \"" + weaponName + "\" in stored LCP files");
}
return weapon;
}
function FindPcModByName(modName){
let mod;
mod_data_set.forEach((element) => {
log(element);
if (typeof mod !== 'undefined'){
return mod;
}
mod = element.find((obj) => obj.name.toLowerCase() == modName.toLowerCase());
});
if (typeof mod == 'undefined'){
sendChat("LCP BUILD ERROR", "could not find a mod called \"" + modName + "\" in stored LCP files");
}
return mod;
}
function FindPcTalentByName(talentName){
let talent;
talent_data_set.forEach((element) => {
if (typeof talent != 'undefined'){
return talent;
}
talent = element.find((obj) => obj.name.toLowerCase() == talentName.toLowerCase());
});
if (typeof talent == 'undefined'){
sendChat("LCP BUILD ERROR", "could not find a talent called \"" + talentName + "\" in stored LCP files");
}
return talent;
}
function FindPcCoreBonusByName(cbName){
let cb;
core_bonus_data_set.forEach((element) => {
if (typeof cb != 'undefined'){
return cb;
}
cb = element.find((obj) => obj.name.toLowerCase() == cbName.toLowerCase());
});
if (typeof cb == 'undefined'){
sendChat("LCP BUILD ERROR", "could not find a core bonus called \"" + cbName + "\" in stored LCP files");
}
return cb;
}
// FIND LCP ENTRY BY ID
function FindPcFrameById(frameId){
let frame;
frame_data_set.forEach((element) => {
if (typeof frame != 'undefined'){
return frame;
}
frame = element.find((obj) => obj.id.toLowerCase() == frameId.toLowerCase());
});
if (typeof frame == 'undefined'){
sendChat("LCP BUILD ERROR", "could not find a frame called \"" + frameId + "\" in stored LCP files");
}
return frame;
}
function FindPcSystemById(systemId){
let system;
system_data_set.forEach((element) => {
if (typeof system != 'undefined'){
return system;
}
system = element.find((obj) => obj.id.toLowerCase() == systemId.toLowerCase());
});
if (typeof system == 'undefined'){
sendChat("LCP BUILD ERROR", "could not find a system called \"" + systemId + "\" in stored LCP files");
}
return system;
}
function FindPcWeaponById(weaponId){
let weapon;
weapon_data_set.forEach((element) => {
if (typeof weapon != 'undefined'){
return weapon;
}
weapon = element.find((obj) => obj.id.toLowerCase() == weaponId.toLowerCase());
});
if (typeof weapon == 'undefined'){
sendChat("LCP BUILD ERROR", "could not find a weapon called \"" + weaponId + "\" in stored LCP files");
}
return weapon;
}
function FindPcModById(modId){
let mod;
mod_data_set.forEach((element) => {
if (typeof mod != 'undefined'){
return mod;
}
mod = element.find((obj) => obj.id.toLowerCase() == modId.toLowerCase());
});
if (typeof mod == 'undefined'){
sendChat("LCP BUILD ERROR", "could not find a mod called \"" + modId + "\" in stored LCP files");
}
return mod;
}
function FindPcTalentById(talentId){
let talent;
talent_data_set.forEach((element) => {
if (typeof talent != 'undefined'){
return talent;
}
talent = element.find((obj) => obj.id.toLowerCase() == talentId.toLowerCase());
});
if (typeof talent == 'undefined'){
sendChat("LCP BUILD ERROR", "could not find a talent called \"" + talentId + "\" in stored LCP files");
}
return talent;
}
function FindPcCoreBonusById(cbId){
let cb;
core_bonus_data_set.forEach((element) => {
if (typeof cb != 'undefined'){
return cb;
}
cb = element.find((obj) => obj.id.toLowerCase() == cbId.toLowerCase());
});
if (typeof cb == 'undefined'){
sendChat("LCP BUILD ERROR", "could not find a core bonus called \"" + cbId + "\" in stored LCP files");
}
return cb;
}
function FindSystemTagById(tagId){
let tag;
tag_data_set.forEach((element) => {
if (typeof tag != 'undefined'){
return tag;
}
tag = element.find((obj) => obj.id.toLowerCase() == tagId.toLowerCase());
});
if (typeof tag == 'undefined'){
sendChat("LCP BUILD ERROR", "could not find a tag called \"" + tagId + "\" in stored LCP files");
}
return tag;
}
// FIND NPC THINGS
function FindNpcClass(className){
let npcClass;
npcClassDataSet.forEach((element) => {
let classTarget = element.find((npc) => npc.name.toLowerCase() == className.toLowerCase()); // Retrieve the class element by name;
if (typeof classTarget !== 'undefined'){
npcClass = classTarget;
}
});
return npcClass;
}
function FindNpcFeature(id){
let desiredFeature;
npcFeatureDataSet.forEach((element) => {
let featureTarget = element.find((feature) => feature.id.toLowerCase() == id.toLowerCase());
if (typeof featureTarget !== 'undefined'){
desiredFeature=featureTarget;
}
});
return desiredFeature;
}
function FindNpcTemplate(templateName){
let npcTemplate;
npcTemplateDataSet.forEach((element) => {
let templateTarget = element.find((feature) => feature.name.toLowerCase() == templateName.toLowerCase());
if (typeof templateTarget !== 'undefined'){
npcTemplate=templateTarget;
}
});
return npcTemplate;
}
// FIND FROM ANY
function FindPcFeatureById(id, type){
switch(type){
case 'frame':
return FindPcFrameById(id);
case 'system':
return FindPcSystemById(id);
case 'weapon':
return FindPcWeaponById(id);
case 'mod':
return FindPcModById(id);
case 'talent':
return FindPcTalentById(id);
case 'corebonus':
return FindPcCoreBonusById(id);
case 'tag':
return FindSystemTagById(id);
default:
sendChat("ERROR", "Invalid type: " + type);
break;
}
}
// SYSTEM MENUS
function DisplayPcItemList(msg, type, displayOnly = false){
let itemList = "";
switch (type){
case 'frame':
frame_data_set.forEach((element) => {
element.forEach((item) => {
if (item.name != "ERR: DATA NOT FOUND") {
itemList += CTF.addTableButton(item.name, "!lcp_builder_pc_add_macro " + item.id + " frame true");
};
});
});
break;
case 'weapon':
weapon_data_set.forEach((element) => {
element.forEach((item) => {
if (item.name != "ERR: DATA NOT FOUND") {
itemList += CTF.addTableButton(item.name, "!lcp_builder_pc_add_macro " + item.id + " weapon true");
};
});
});
break;
case 'system':
system_data_set.forEach((element) => {
element.forEach((item) => {
if (item.name != "ERR: DATA NOT FOUND") {
itemList += CTF.addTableButton(item.name, "!lcp_builder_pc_add_macro " + item.id + " system true");
};
});
});
break;
case 'corebonus':
core_bonus_data_set.forEach((element) => {
element.forEach((item) => {
if (item.name != "ERR: DATA NOT FOUND") {
itemList += CTF.addTableButton(item.name, "!lcp_builder_pc_add_macro " + item.id + " corebonus true");
};
});
});
break;
case 'mod':
mod_data_set.forEach((element) => {
element.forEach((item) => {
if (item.name != "ERR: DATA NOT FOUND") {
itemList += CTF.addTableButton(item.name, "!lcp_builder_pc_add_macro " + item.id + " mod true");
};
});
});
break;
case 'talent':
talent_data_set.forEach((element) => {
element.forEach((item) => {
if (item.name != "ERR: DATA NOT FOUND") {
itemList += CTF.addTableButton(item.name, "!lcp_builder_pc_add_macro " + item.id + " talent true");
};
});
});
break;
case 'tag':
tag_data_set.forEach((element) => {
element.forEach((item) => {
if (item.name != "ERR: DATA NOT FOUND") {
itemList += CTF.addTableButton(item.name, "!lcp_builder_pc_add_macro " + item.id + " tag true");
};
});
});
break;
default:
break;
}
let output = CTF.addTemplate() + CTF.addTableHeader(type.toUpperCase() +" LIST") + CTF.addTableItem(itemList);
sendChat("LCP BUILDER", "/w gm " + output);
}
// NPC MENUS
function DisplayTemplateList(msg, tier, displayOnly = false){
let TemplateItems = "";
npcTemplateDataSet.forEach((element) => {
element.forEach((npcTemplate) => {
TemplateItems += CTF.addTableButton(npcTemplate.name, "!lcp_builder_add_npc_template " + npcTemplate.name + " " + tier + " true");
});
});
let output = CTF.addTemplate() + CTF.addTableHeader("Template List Tier " + tier) + CTF.addTableItem(TemplateItems);
sendChat("LCP BUILDER", "/w gm " + output);
}
function DisplayClassList(msg, tier, displayOnly = false){
let ArtilleryList = "";
let BiologicalList = "";
let ControllerList = "";
let DefenderList = "";
let StrikerList = "";
let SupportList = "";
let OtherList = "";
npcClassDataSet.forEach((element) => {
element.forEach((npcClass) => {
if (npcClass.role == "artillery"){
ArtilleryList += CTF.addTableButton(npcClass.name,"!lcp_builder_add_npc_class " + npcClass.name + " " + tier + " true");
}
else if (npcClass.role == "biological"){
BiologicalList += CTF.addTableButton(npcClass.name, "!lcp_builder_add_npc_class " + npcClass.name + " " + tier + " true");
}
else if (npcClass.role == "controller"){
ControllerList += CTF.addTableButton(npcClass.name,"!lcp_builder_add_npc_class " + npcClass.name + " " + tier + " true");
}
else if (npcClass.role == "defender"){
DefenderList += CTF.addTableButton(npcClass.name,"!lcp_builder_add_npc_class " + npcClass.name + " " + tier + " true");
}
else if (npcClass.role == "striker"){
StrikerList += CTF.addTableButton(npcClass.name,"!lcp_builder_add_npc_class " + npcClass.name + " " + tier + " true");
}
else if (npcClass.role == "support"){
SupportList += CTF.addTableButton(npcClass.name,"!lcp_builder_add_npc_class " + npcClass.name + " " + tier + " true");
}
else {
OtherList += CTF.addTableButton(npcClass.name,"!lcp_builder_add_npc_class " + npcClass.name + " " + tier + " true");
}
});
});
sendChat("LCP BUILDER", "/w gm " + CTF.addTemplate()
+ CTF.addTableHeader("Class List Tier " + tier)
+ CTF.addTableItem("Artillery =" + ArtilleryList)
+ CTF.addTableItem("Biological =" + BiologicalList)
+ CTF.addTableItem("Controller =" + ControllerList)
+ CTF.addTableItem("Defender =" + DefenderList)
+ CTF.addTableItem("Striker =" + StrikerList)
+ CTF.addTableItem("Support =" + SupportList)
+ CTF.addTableItem("Other =" + OtherList));
}
function MenuNpcClassTier(msg){
let output = CTF.addTemplate()
+ CTF.addTableHeader("Select NPC Tier")
+ CTF.addTableItem("Classes =" + CTF.addTableButton("Tier 1", "!lcp_builder_display_class_list 1") + CTF.addTableButton("Tier 2", "!lcp_builder_display_class_list 2") + CTF.addTableButton("Tier 3", "!lcp_builder_display_class_list 3"))
+ CTF.addTableItem("Templates =" + CTF.addTableButton("Tier 1", "!lcp_builder_display_template_list 1") + CTF.addTableButton("Tier 2", "!lcp_builder_display_template_list 2") + CTF.addTableButton("Tier 3", "!lcp_builder_display_template_list 3"));
sendChat("LCP BUILDER", "/w gm " + output);
}
// DISPLAY MENU FOR PC OPTIONS
function MenuPcOptionsList(msg){
let output = CTF.addTemplate()
+ CTF.addTableHeader("PC Build Menu -- [IMPORT CHARACTER](!lncr_stub)")
+ CTF.addTableItem("Frames =" + CTF.addTableButton("FRAMES", "!lcp_builder_displaylist frame true"))
+ CTF.addTableItem("Weapons =" + CTF.addTableButton("WEAPONS", "!lcp_builder_displaylist weapon true"))
+ CTF.addTableItem("Systems = " + CTF.addTableButton("SYSTEMS", "!lcp_builder_displaylist system true"))
+ CTF.addTableItem("Core Bonuses = " + CTF.addTableButton("CORE BONUSES", "!lcp_builder_displaylist corebonus true"))
+ CTF.addTableItem("Mods = " + CTF.addTableButton("MODS", "!lcp_builder_displaylist mod true"))
+ CTF.addTableItem("Talents = " + CTF.addTableButton("TALENTS", "!lcp_builder_displaylist talent true"));
+ CTF.addTableItem("Tags = " + CTF.addTableButton("TAGS", "!lcp_builder_displaylist tags true"));
sendChat("LCP BUILDER", "/w gm " + output);
}
// MACRO && SHEET MANIPULATION
// ADD A FEATURE MACRO TO A CHARACTER SHEET
function AddPcFeature(msg, id, type, displayOnly = false){
if (typeof msg.selected === 'undefined') {
sendChat("LANCER CORE HELPER", "&{template:default}{{name=NO TOKEN SELECTED}}{{You must select a token for this macro}}");
return;
}
let obj = getObj(msg.selected[0]._type,msg.selected[0]._id);
let characterID = obj.get('represents');
let tokobj = getObj('graphic', msg.selected[0]._id);
let tokenName = tokobj.get('name').toLowerCase();
if (!displayOnly) { sendChat("LCP BUILDER", "Adding " + id + " to " + tokenName); }
let desiredFeature = FindPcFeatureById(id, type);
if (typeof desiredFeature === 'undefined'){
sendChat("LCP BUILDER", "Feature Not Found in Data Set. ID: " + id);
return;
}
let fAction;
let fName;
let fId;
let fSource;
let fMounts;
let fStats;
let fTraits;
let core_system;
let subList;
let fTypesAllowed;
let fEffect;
let fActions;
let fTags;
switch(type){
case 'frame':
fId = desiredFeature["id"];
fSource = desiredFeature["source"];
fName = desiredFeature["name"];
fMounts = desiredFeature["mounts"];
fStats = desiredFeature["stats"];
fTraits = desiredFeature["traits"];
core_system = desiredFeature["core_system"];
subList = "";
// MECH NAME
fAction = CTF.addTemplate()
+ CTF.addTableHeader(fSource + " " + fName + " " + CTF.addTableButton("Add To Sheet", "!lcp_builder_pc_add_macro " + fId + " " + type));
// MECH MOUNTS
fMounts.forEach((mount) => { subList += CTF.addTableButton(mount, "!lncr_stub"); });
fAction += CTF.addTableItem("Mounts = " + subList);
subList = "";
// MECH STATS
fAction += CTF.addTableItem("Stats = "
+ CTF.addTableButton("SIZE " + fStats.size)
+ CTF.addTableButton("STRUCTURE " + fStats.structure)
+ CTF.addTableButton("CORE STRESS " + fStats.stress)
+ CTF.addTableButton("ARMOR " + fStats.armor)
+ CTF.addTableButton("HP " + fStats.hp)
+ CTF.addTableButton("EVASION " + fStats.evasion)
+ CTF.addTableButton("E-DEFENSE " + fStats.edef)
+ CTF.addTableButton("HEAT CAP " + fStats.heatcap)
+ CTF.addTableButton("REPAIRS " + fStats.repcap)
+ CTF.addTableButton("SENSORS " + fStats.sensor_range)
+ CTF.addTableButton("TECH ATTACK " + fStats.tech_attack)
+ CTF.addTableButton("SAVE " + fStats.save)
+ CTF.addTableButton("SPEED " + fStats.speed)
+ CTF.addTableButton("SP " + fStats.sp)
);
// MECH TRAITS
fAction += CTF.addTableItem("TRAITS");
fTraits.forEach((trait) => { fAction += CTF.addTableItem(trait.name + " = " + trait['description'])});
// MECH CORE SYSTEM
fAction += CTF.addTableItem("CORE SYSTEM = **" + core_system.name + "**");
// MECH PASSIVE
if (typeof core_system['passive_name'] !== 'undefined'){
fAction += CTF.addTableItem("PASSIVE = " + core_system.passive_name + " - " + core_system.passive_effect);
}
if (typeof core_system['passive_actions'] !== 'undefined')
{
core_system.passive_actions.forEach((passive) => {
let passiveName = passive.name;
let passiveActivation = passive.activation;
let passiveFrequency = passive.frequency;
let passiveTrigger = passive.trigger;
let passiveDetail = passive.detail;
fAction += CTF.addTableItem(""
+ ((typeof passiveActivation !== 'undefined') ? CTF.addTableButton(passive.activation, "!lncr_stub") : "")
+ ((typeof passiveFrequency !== 'undefined') ? CTF.addTableButton(passiveFrequency, "!lncr_stub") : "")
+ " = "
+ ((typeof passiveName !== 'undefined') ? "**" + passiveName + "** - " : "")
+ ((typeof passiveTrigger !== 'undefined') ? "<br>**Trigger** - " + passiveTrigger : "")
+ ((typeof passiveDetail !== 'undefined') ? "<br><br>" + passiveDetail : "")
);
})
}
// MECH ACTIVE
fAction += CTF.addTableItem( CTF.addTableButton(core_system.activation,"!lncr_stub") + " = " + "**" + core_system.active_name + "** - " + core_system.active_effect);
if (typeof core_system['active_actions'] !== 'undefined')
{
core_system.active_actions.forEach((action) => {
let actionName = action.name;
let actionActivation = action.activation;
let actionFrequency = action.frequency;
let actionTrigger = action.trigger;
let actionDetail = action.detail;
fAction += CTF.addTableItem(""
+ ((typeof actionActivation !== 'undefined') ? CTF.addTableButton(action.activation, "!lncr_stub") : "")
+ ((typeof actionFrequency !== 'undefined') ? CTF.addTableButton(actionFrequency, "!lncr_stub") : "")
+ " = "
+ ((typeof actionName !== 'undefined') ? "**" + actionName + "** - " : "")
+ ((typeof actionTrigger !== 'undefined') ? "<br>**Trigger** - " + actionTrigger : "")
+ ((typeof actionDetail !== 'undefined') ? "<br><br>" + actionDetail : "")
);
})
}
if (typeof core_system['deployables'] !== 'undefined'){
fAction += CTF.addTableItem("DEPLOYABLES");
core_system.deployables.forEach((deployable) => {
let deployName = deployable.name;
let deployType = deployable.type;
let deployDetail = deployable.detail;
let deployActivation = deployable.activation;
let deployRecall = deployable.recall;
let deployRedeploy = deployable.redeploy;
let deploySize = deployable.size;
let deployHp = deployable.hp;
fAction += CTF.addTableItem(""
+ ((typeof deployName !== 'undefined') ? "**" + deployName + "** <br> " : "")
+ ((typeof deployActivation !== 'undefined') ? CTF.addTableButton(deployActivation, "!lncr_stub") : "")
+ ((typeof deployRecall !== 'undefined') ? CTF.addTableButton("RECALL - " + deployRecall, "!lncr_stub") : "")
+ ((typeof deployRedeploy !== 'undefined') ? CTF.addTableButton("REDEPLOY - "+ deployRecall, "!lncr_stub") : "")
+ " = "
+ ((typeof deploySize !== 'undefined') ? " - **Size: " + deploySize + "** " : "")
+ ((typeof deployHp !== 'undefined') ? "**HP: " + deployHp + "**": "")
+ ((typeof deployDetail !== 'undefined') ? "<br>" + deployDetail : "")
);
})
}
if (typeof core_system['integrated'] !== 'undefined'){
fAction += CTF.addTableItem("INTEGRATED EQUIPMENT");
core_system.integrated.forEach((equipment) => {
let integratedItem = FindPcFeatureById(equipment, 'weapon');
fAction += CTF.addTableItem(integratedItem.name + " = " + CTF.addTableButton(integratedItem.name, "!lcp_builder_pc_add_macro " + equipment + " weapon"));
})
}
break;
case 'mod':
fId = desiredFeature["id"];
fName = desiredFeature["name"];
fSource = desiredFeature["source"];
fTypesAllowed = desiredFeature["allowed_types"];
fEffect = desiredFeature["effect"];
fActions = desiredFeature["actions"];
fTags = desiredFeature["tags"];
subList = "";
fAction = CTF.addTemplate()
+ CTF.addTableHeader(fName + " " + CTF.addTableButton("Add To Sheet", "!lcp_builder_pc_add_macro " + fId + " " + type))
+ CTF.addTableItem("Combatible Types = " + fTypesAllowed)
+ CTF.addTableItem("Effect = " + fEffect);
if (typeof fActions !== 'undefined'){
fActions.forEach((action) =>{
fAction += CTF.addTableItem(action.name + "<br>" + CTF.addTableButton(action.activation) + " = " + action.detail);
})
}
if (typeof fTags !== 'undefined'){
sublist = "";
fTags.forEach((tag) => {
let tagInfo = FindPcFeatureById(tag.id,'tag');
sublist += CTF.addTableButton(((tag.val !== 'undefined') ? tagInfo.name.replace("{VAL}", tag.val) : tagInfo.name), "!lcp_builder_pc_add_macro " + tag.id + " tag false");
})
fAction += CTF.addTableItem("Tags = " + sublist);
}
break;
case 'system':
fId = desiredFeature["id"];
fName = desiredFeature["name"];
fSource = desiredFeature["source"];
fEffect = desiredFeature["effect"];
fActions = desiredFeature["actions"];
fTags = desiredFeature["tags"];
subList = "";
fAction = CTF.addTemplate()
+ CTF.addTableHeader(fName + " " + CTF.addTableButton("Add To Sheet", "!lcp_builder_pc_add_macro " + fId + " " + type))
+ ((typeof fEffect !== 'undefined') ? CTF.addTableItem("Effect = " + fEffect) : "");
if (typeof fActions !== 'undefined'){
fActions.forEach((action) =>{
fAction += CTF.addTableItem(((typeof action.name !== 'undefined') ? action.name + "<br>" : "") + CTF.addTableButton(action.activation) + " = " + action.detail);
})
}
if (typeof fTags !== 'undefined'){
sublist = "";
fTags.forEach((tag) => {
let tagInfo = FindPcFeatureById(tag.id,'tag');
sublist += CTF.addTableButton(((tag.val !== 'undefined') ? tagInfo.name.replace("{VAL}", tag.val) : tagInfo.name), "!lcp_builder_pc_add_macro " + tag.id + " tag false");
})
fAction += CTF.addTableItem("Tags = " + sublist);
}
break;
case 'weapon':
let wepid = desiredFeature['id'];
let wepdamage = desiredFeature['damage'];
let wepmount = desiredFeature['mount'];
let wepname = desiredFeature['name'];
fName = desiredFeature['name'];
let weprange = desiredFeature['range'];
let webdescription = desiredFeature['description'];
let weplicense = desiredFeature['license'];
let wepsource = desiredFeature['source'];
let weptags = desiredFeature['tags'];
let weptype = desiredFeature['type'];
let weponcrit = desiredFeature['on_crit'];
let wepeffect = desiredFeature['effect'];
let wepactions = desiredFeature['actions'];
let wepprofiles = desiredFeature['profiles'];
let weponhit = desiredFeature['on_hit'];
let weponattack = desiredFeature['on_attack'];
fAction = CTF.addTemplate()
+ CTF.addTableHeader(wepname + " " + CTF.addTableButton("Add To Sheet", "!lcp_builder_pc_add_macro " + wepid + " " + type))
+ CTF.addTableItem("Type = **" + wepmount + " " + weptype + "**")
+ CTF.addTableItem("Attack Roll = [[1d20 + @{selected|grit}]] *ACC/DIFF** [[1d6]][[1d6]][[1d6]][[1d6]][[1d6]][[1d6]]");
if (typeof wepdamage !== 'undefined'){
sublist = "";
wepdamage.forEach((damtype) => {
sublist += damtype.type + " [[" + damtype.val + "]]<br>";
})
fAction += CTF.addTableItem("Damage = " + sublist);
}
if (typeof weprange !== 'undefined'){
sublist = "";
weprange.forEach((range) => {
sublist += range.type + " " + range.val + "<br>";
})
fAction += CTF.addTableItem("Range = " + sublist);
}
if (typeof weponcrit !== 'undefined'){
fAction += CTF.addTableItem("On Crit = " + weponcrit);
}
if (typeof weponhit !== 'undefined'){
fAction += CTF.addTableItem("On Hit = " + weponhit);
}
if (typeof weponattack !== 'undefined'){
fAction += CTF.addTableItem("On Attack = " + weponattack);
}
if (typeof wepeffect !== 'undefined'){
fAction += CTF.addTableItem("Effect = " + wepeffect);
}
if (typeof wepprofiles !== 'undefined'){
sublist = "";
wepprofiles.forEach((profile) => {
fAction += CTF.addTableItem(profile.name + " Profile = ----------" );
if(typeof profile.on_attack !== 'undefined'){
fAction += CTF.addTableItem(profile.name + ' On Attack = ' + profile.on_attack);
}
if(typeof profile.range !== 'undefined'){
sublist = "";
profile.range.forEach((rangetype)=>{
sublist += rangetype.type + " **" + rangetype.val + "**<br>";
})
fAction += CTF.addTableItem(profile.name + " Range = " + sublist);
}
if(typeof profile.damage !== 'undefined'){
profile.damage.forEach((damtype)=>{
// WEIRD BARBAROSSA WORKAROUND
fAction += CTF.addTableItem(profile.name + " Damage = " + ((typeof damtype.type !== 'undefined') ? damtype.type : "") + ((damtype.val !== "N/A") ? " [[" + damtype.val + "]]" : "N/A"));
})
}
if (typeof profile.effect !== 'undefined'){
fAction += CTF.addTableItem(profile.name + " Effect = " + profile.effect);
}
if (typeof profile.actions !== 'undefined'){
profile.actions.forEach((profileaction) => {
fAction += CTF.addTableItem(profileaction.name + "<br>" + CTF.addTableButton(profileaction.activation) + " = " + ((typeof profileaction.trigger !== 'undefined') ? "**Trigger** - " + profileaction.trigger + "<br>" : "") + profileaction.detail);
if (typeof profileaction.effect !== 'undefined'){
fAction += CTF.addTableItem("Effect = " + profileaction.effect);
}
});
}
if (typeof profile['tags'] !== 'undefined'){
sublist = "";
profile['tags'].forEach((profiletag)=>{
let tagInfo = FindPcFeatureById(profiletag.id,'tag');
sublist += CTF.addTableButton(((typeof profiletag.val !== 'undefined') ? tagInfo.name.replace("{VAL}", profiletag.val) : tagInfo.name));
})
fAction += CTF.addTableItem(profile.name + " Tags =" + sublist);
sublist = "";
}
})
}
if (typeof wepactions !== 'undefined'){
wepactions.forEach((action) =>{
fAction += CTF.addTableItem(action.name + "<br>" + CTF.addTableButton(action.activation) + " = " + action.detail);
})
}
if (typeof weptags !== 'undefined'){
sublist = "";
weptags.forEach((tag) => {
let tagInfo = FindPcFeatureById(tag.id,'tag');
sublist += CTF.addTableButton(((typeof tag.val !== 'undefined') ? tagInfo.name.replace("{VAL}", tag.val) : tagInfo.name), "!lcp_builder_pc_add_macro " + tag.id + " tag false");
})
fAction += CTF.addTableItem("Tags = " + sublist);
}
break;
case 'talent':
fId = desiredFeature["id"];
fName = desiredFeature["name"];
fEffect = desiredFeature["effect"];
fActions = desiredFeature["actions"];
fTags = desiredFeature["tags"];
let talentRanks = desiredFeature["ranks"];
subList = "";
fAction = CTF.addTemplate()
+ CTF.addTableHeader(fName + " " + CTF.addTableButton("Add To Sheet", "!lcp_builder_pc_add_macro " + fId + " " + type))
+ ((typeof fEffect !== 'undefined') ? CTF.addTableItem("Effect = " + fEffect) : "");
if(typeof talentRanks !== 'undefined'){
sublist = "";
let talentitr = 0;
talentRanks.forEach((rank) => {
talentitr++;
fAction += CTF.addTableItem("Rank " + talentitr + "<br>" + rank.name + " = --------------------<br>" + rank['description']);
if (typeof rank.actions !== 'undefined'){
rank.actions.forEach((action)=>{
if (typeof action.activation != 'undefined'){
sublist += "<br>"
+ CTF.addTableButton(action.activation)
+ ((typeof action.frequency !== 'undefined') ? "<br>" + CTF.addTableButton(action.frequency) : "")
+ " = "
+ ((typeof action.trigger !== 'undefined') ? "**Trigger** - " + action.trigger + "<br>": "")
+ ((typeof action.detail !== 'undefined') ? action.detail : "")
}
fAction += CTF.addTableItem(action.name + sublist);
sublist = "";
})
}
})
}
break;
case 'corebonus':
fId = desiredFeature["id"];
fName = desiredFeature["name"];
fSource = desiredFeature["source"];
fEffect = desiredFeature["effect"];
fActions = desiredFeature["actions"];
fTags = desiredFeature["tags"];
subList = "";
fAction = CTF.addTemplate()
+ CTF.addTableHeader(fName + " " + CTF.addTableButton("Add To Sheet", "!lcp_builder_pc_add_macro " + fId + " " + type))
+ ((typeof fEffect !== 'undefined') ? CTF.addTableItem("Effect = " + fEffect) : "");
if (typeof fActions !== 'undefined'){
fActions.forEach((action) =>{
let actiontrigger = action.trigger;
fAction += CTF.addTableItem(((typeof action.name !== 'undefined') ? action.name + "<br>" : "") + CTF.addTableButton(action.activation) + " = " + ((typeof actiontrigger !== 'undefined') ? "**Trigger** - " + actiontrigger + "<br>" : "") + action.detail);
})
}
if (typeof fTags !== 'undefined'){
sublist = "";
fTags.forEach((tag) => {
let tagInfo = FindPcFeatureById(tag.id,'tag');
sublist += CTF.addTableButton(((tag.val !== 'undefined') ? tagInfo.name.replace("{VAL}", tag.val) : tagInfo.name), "!lcp_builder_pc_add_macro " + tag.id + " tag false");
})
fAction += CTF.addTableItem("Tags = " + sublist);
}
break;
break;
default:
break;
}
if(displayOnly){
sendChat("LCP BUILDER", fAction);
}
else{
createObj("ability", {
name: fName,
characterid: characterID,
description: "a description",
action: fAction,
istokenaction: true
});
}
}
function CleanTokenOfAllAbilities(msg){
if (typeof msg.selected === 'undefined'){
log("selected was undefined.");
return;
}
let obj = getObj(msg.selected[0]._type,msg.selected[0]._id);
let characterID = obj.get('represents');
let charAbilities = findObjs({_characterid: characterID, _type: 'ability'});
charAbilities.forEach((ability) => {
ability.remove();
})
}
function ImportPcCharacter(msg, rawstring){
// First, Sanitize inputs
sendChat("IMPORT ACTION", "**PROCESSING RAW INPUT**")
var jsonObj = {};
// First, pull in the mech name from the headline
log("IMPORTING MECH, BEGINNING PULL");
let mechIndex = rawstring.indexOf(" ", 3)+1;
jsonObj.parsedMech = rawstring.substring(mechIndex, rawstring.indexOf(" @"));
// Pull in core bonuses
let bonusIndex = rawstring.indexOf("[ core bonuses ]");
let bonusOffset = 16+1;
let parsedBonuses = rawstring.substring(bonusIndex+bonusOffset, rawstring.indexOf(" [ ", bonusIndex+bonusOffset));
jsonObj.parsedBonuses = parsedBonuses.split(', ');
// Pull in Talents
let talentsIndex = rawstring.indexOf(" [ talents ]");
let talentsOffset = 11+2;
let parsedTalents = rawstring.substring(talentsIndex+talentsOffset, rawstring.indexOf(" [ ", talentsIndex+talentsOffset));
parsedTalents = parsedTalents.replaceAll(/ \d/g,"");
jsonObj.parsedTalents = parsedTalents.split(', ');
// Pull in Weapons
let weaponsIndex = rawstring.indexOf("[ weapons ]");
let weaponsOffset = 11+1;
let parsedWeapons = rawstring.substring(weaponsIndex+weaponsOffset, rawstring.indexOf(" [ ", weaponsIndex+weaponsOffset));
let tempWeapons = parsedWeapons;
while (tempWeapons.includes('main mount: ')){
tempWeapons = tempWeapons.replaceAll(/main mount: /g, ',');
}
//log("Removed integrated mounts and replaced with commas. STRING:" + tempWeapons);
while (tempWeapons.includes('integrated: ')){
tempWeapons = tempWeapons.replace('integrated: ', ',');
}
while (tempWeapons.includes('integrated weapon: ')){
tempWeapons = tempWeapons.replace('integrated weapon: ', ',');
}
while (tempWeapons.includes('aux/aux mount: ')){
tempWeapons = tempWeapons.replace('aux/aux mount: ', ',');
}
//log("Removed aux/aux mounts and replaced with commas. STRING:" + tempWeapons);
while (tempWeapons.includes('main/aux mount: ')){
tempWeapons = tempWeapons.replace('main/aux mount: ', ',');
}
//log("Removed main/aux mounts and replaced with commas. STRING:" + tempWeapons);
while (tempWeapons.includes('flex mount: ')){
tempWeapons = tempWeapons.replace('flex mount: ', ',');
}
//log("Removed flex mounts and replaced with commas. STRING:" + tempWeapons);
while (tempWeapons.includes('heavy mount: ')){
tempWeapons = tempWeapons.replace('heavy mount: ', ',');
}
//log("Removed heavy mounts and replaced with commas. STRING:" + tempWeapons);
while (tempWeapons.includes(' / ')){
tempWeapons = tempWeapons.replace(' / ', ' ,');
}
//log("Removed Split mounts and replaced with commas. STRING:" + tempWeapons);
//Remove leading comma
tempWeapons = tempWeapons.substring(1);
let activeItem = "";
let tempArry = tempWeapons.split(" ,");
jsonObj.parsedMods = "";
log("JSON prior to weapon fix:" + tempArry);
for (weapon in tempArry){
log("PARSING WEAPON:" + tempArry[weapon]);
activeItem = weapon;
//Note and remove mods
//Erase any equipped core bonuses. We don't need that info.
if (tempArry[weapon].includes(' // ')){
activeItem = tempArry[weapon].substring(0,tempArry[weapon].indexOf(' // '));
log("WEAPON W/O CORE BONUSES: " + activeItem);
}
//Take note of equipped mods
if (tempArry[weapon].includes(' (')){
log("ENTERED MOD LOGIC");
let mod = "";
// Carveout for these silly things
if (tempArry[weapon].includes('(hunter-killer)') || tempArry[weapon].includes('(light)')){
log("CARVEOUT gms nexus");
log(activeItem);
let tempstring = activeItem.substring(activeItem.indexOf(')')+1);
let actualObject = activeItem.substring(0,activeItem.indexOf(')')+1);
log(tempstring);
log("ACTUAL WEAPON:"+actualObject);
if (tempstring.includes('(') )
{
mod = tempstring.substring(tempstring.indexOf('(')+1,tempstring.indexOf(')'));
log(mod);
if (!jsonObj.parsedMods.includes(mod)){
jsonObj.parsedMods = jsonObj.parsedMods + "," + mod;
}
}
tempArry[weapon] = actualObject;
} else {
mod = tempArry[weapon].substring(tempArry[weapon].indexOf('(')+1,tempArry[weapon].indexOf(')'));
let wep = tempArry[weapon].substring(0,tempArry[weapon].indexOf(' ('));
log(wep);
tempArry[weapon] = wep;
if (!jsonObj.parsedMods.includes(mod)){
jsonObj.parsedMods = jsonObj.parsedMods + "," + mod;
}
}
} else {
// No mods were found. Add to list.
activeItem = weapon;
log("WEAPON NO FRILLS:"+activeItem);
}
//Fix the mods if mods were found
}
// If mods were found, we should have a list of mods by now.
log("PARSED MODS:");
log(jsonObj.parsedMods);
if (jsonObj.parsedMods !== ""){
log("FIXING MODS");
jsonObj.parsedMods = jsonObj.parsedMods.substring(1);
// convert to array of strings
jsonObj.parsedMods = jsonObj.parsedMods.split(',');
log(jsonObj.parsedMods);
log("FIXED MODS");
}
//Finally, copy each entry into the json obj, ignoring duplicates.
log("REMOVING DUPLICATE WEAPONS");
jsonObj.parsedWeapons = [];
for (weapon in tempArry){
if(!jsonObj.parsedWeapons.includes(tempArry[weapon])){
jsonObj.parsedWeapons.push(tempArry[weapon]);
}
}
log("PARSING SYSTEMS");
let systemsIndex = rawstring.indexOf("[ systems ]");
let systemsOffset = 11+1;
let parsedSystems = rawstring.substring(systemsIndex+systemsOffset);
jsonObj.parsedSystems = parsedSystems.split(", ");
log("SYSTEMS:" + jsonObj.parsedSystems);
// The object is now prepped. Now start adding features to the character sheet.
log("ADDING FRAME IF ANY");
if (typeof jsonObj.parsedMech !== 'undefined'){
log("DEBUG FRAME: " + jsonObj.parsedMech);
let searchFrame = FindPcFrameByName(jsonObj.parsedMech)
if (typeof searchFrame !== 'undefined'){
AddPcFeature(msg, searchFrame.id, 'frame', false);
}
}
log("ADDING CORE BONUSES IF ANY");
if (typeof jsonObj.parsedBonuses !== 'undefined'){
log("DEBUG BONUSES: " + jsonObj.parsedBonuses);
let searchBonus = "";
jsonObj.parsedBonuses.forEach((bonus)=>{
searchBonus = FindPcCoreBonusByName(bonus);
if (typeof searchBonus !== 'undefined'){
AddPcFeature(msg, searchBonus.id, 'corebonus', false);
}
});
}
log("ADDING TALENTS IF ANY");
if (typeof jsonObj.parsedTalents !== 'undefined'){
let searchTalent = "";
jsonObj.parsedTalents.forEach((talent)=>{
searchTalent = FindPcTalentByName(talent);
if (typeof searchTalent !== 'undefined'){
AddPcFeature(msg, searchTalent.id, 'talent', false);
}
});
}
log("ADDING WEAPONS IF ANY");
if (typeof jsonObj.parsedWeapons !== 'undefined'){
log("CURRENT WEAPONS:"+jsonObj.parsedWeapons);
let searchWeapon = "";
jsonObj.parsedWeapons.forEach((weapon)=>{
searchWeapon = FindPcWeaponByName(weapon);
log(searchWeapon);
if(typeof searchWeapon !== 'undefined'){
AddPcFeature(msg,searchWeapon.id, 'weapon', false);
}
});
}
log("ADDING MODS IF ANY");
if (typeof jsonObj.parsedMods !== 'undefined'){
let searchMod = "";
//log("ADDING MODS TO SHEET");
//log(jsonObj.parsedMods);
jsonObj.parsedMods.forEach((mod)=>{
searchMod = FindPcModByName(mod);
//log(searchMod);
if(typeof searchMod !== 'undefined'){
AddPcFeature(msg,searchMod.id, 'mod', false);
}
});
}
log("ADDING SYSTEMS IF ANY");
if (typeof jsonObj.parsedSystems !== 'undefined'){
let searchSystem = "";
jsonObj.parsedSystems.forEach((system)=>{
searchSystem = FindPcSystemByName(system);
if(typeof searchSystem !== 'undefined'){
AddPcFeature(msg, searchSystem.id, 'system', false);
}
});
}
//Concat the strings that matter. while converting them into the format for the changeloadout call.
}
function injectScanData(msg, newabilityid, featuretype, tier){
if (typeof msg.selected === 'undefined'){
log("selected was undefined.");
return;
}
let obj = getObj(msg.selected[0]._type,msg.selected[0]._id);
let characterID = obj.get('represents');
let charAbilities = findObjs({_characterid: characterID, _type: 'ability'});
charAbilities.forEach((ability) => {
log("CHECK IF SCAN DATA");
if (ability.get('name') === 'scan-data'){
log("ENTERED SCAN DATA");
let abilityaction = ability.get('action');
log(abilityaction);
let of = /Optional Features = /;
let bf = /Base Features = /;
let tf = /Template Features = /;
let bfMatch = abilityaction.match(bf);
let ofMatch = abilityaction.match(of);
let tfMatch = abilityaction.match(tf);
log(newabilityid);
let featuretoadd = FindNpcFeature(newabilityid);
log(featuretoadd);
if (typeof featuretoadd === 'undefined'){
log("INVALID FEATURE");
return;
}
if (featuretoadd["origin"]["base"] == true){
if (bfMatch === null) { // if no Optional on scan-data, create
abilityaction += CTF.addTableItem("Base Features = " + CTF.addTableButton(featuretoadd.name,"!lcp_builder_display_npc_feature " + newabilityid + " " + tier));
ability.set('action', abilityaction);
} else {
let actionSplit = abilityaction.split(bf);
let reassembledstring = actionSplit[0]
+ "Base Features = " + CTF.addTableButton(featuretoadd.name,"!lcp_builder_display_npc_feature " + newabilityid + " " + tier)
+ actionSplit[1];
ability.set('action', reassembledstring);
}
}
if (featuretoadd["origin"]["base"] == false){
if (ofMatch === null) { // if no Optional on scan-data, create
abilityaction += CTF.addTableItem("Optional Features = " + CTF.addTableButton(featuretoadd.name,"!lcp_builder_display_npc_feature " + newabilityid + " " + tier));
ability.set('action', abilityaction);
} else { // else, inject the new feature at the start.
let actionSplit = abilityaction.split(of);
let reassembledstring = actionSplit[0]
+ "Optional Features = " + CTF.addTableButton(featuretoadd.name,"!lcp_builder_display_npc_feature " + newabilityid + " " + tier)
+ actionSplit[1];
ability.set('action', reassembledstring);
}
}
if (featuretype == "Template"){
if (tfMatch === null) { // if no Template on scan-data, create
abilityaction += CTF.addTableItem("Template Features = " + CTF.addTableButton(featuretoadd.name,"!lcp_builder_display_npc_feature " + newabilityid + " " + tier));
ability.set('action', abilityaction);
} else { // else, inject the new feature at the start.
let actionSplit = abilityaction.split(tf);
let reassembledstring = actionSplit[0]
+ "Template Features = " + CTF.addTableButton(featuretoadd.name,"!lcp_builder_display_npc_feature " + newabilityid + " " + tier)
+ actionSplit[1];
ability.set('action', reassembledstring);
}
}
}
})
}
function AddNpcClassFeature(msg, id, tier, displayOnly = false){
let adjustedTier = tier-1;
if (typeof msg.selected === 'undefined') {
sendChat("LANCER CORE HELPER", "&{template:default}{{name=NO TOKEN SELECTED}}{{You must select a token for this macro}}");
return;
}
let obj = getObj(msg.selected[0]._type,msg.selected[0]._id);
let characterID = obj.get('represents');
let tokobj = getObj('graphic', msg.selected[0]._id);
let tokenName = tokobj.get('name').toLowerCase();
if (!displayOnly) { sendChat("LCP BUILDER", "Adding " + id + " to " + tokenName); }
let desiredFeature = FindNpcFeature(id, tier);
if (typeof desiredFeature === 'undefined'){
sendChat("LCP BUILDER", "Feature Not Found in Data Set. ID: "+id);
return;
}
let fName = desiredFeature["name"];
let fType = desiredFeature["type"];
let fOrigin = desiredFeature["origin"]["name"];
let fEffect = desiredFeature["effect"];
let fTags = desiredFeature["tags"];
log("Name: "+ fName + " Type: " + fType + " Origin: " + " Effect: " + fEffect + " Tags: " + fTags);
let fAction = CTF.addTemplate()
+ CTF.addTableHeader(fName)
+ CTF.addTableItem("Origin = " + fOrigin)
+ CTF.addTableItem("Type = " + fType);
if (fType.toLowerCase() === "reaction"){
fAction += CTF.addTableItem("Trigger = " + desiredFeature["trigger"]);
}
if (fType.toLowerCase() === "weapon"){
fAction += CTF.addTableItem("Mount =" + desiredFeature["weapon_type"]);
desiredFeature["range"].forEach((rangetype) => {
fAction += CTF.addTableItem(rangetype.type + "=" + rangetype.val);
});
log("ATTACK BONUS");
let fAttackBonus = desiredFeature["attack_bonus"][adjustedTier];
if (!(typeof desiredFeature["accuracy"] === 'undefined')){
fAction += CTF.addTableItem("Accuracy = " + desiredFeature["accuracy"][adjustedTier]);
}
fAction += CTF.addTableItem("Attack Roll = **[[1d20+" + fAttackBonus + "]] | ACC/DIFF [[1d6]][[1d6]][[1d6]][[1d6]][[1d6]][[1d6]]**");
let damstring = "";
desiredFeature["damage"].forEach((damtype) => {
damstring += CTF.addTableButton(damtype.type + " " + damtype.damage[adjustedTier], "!lcp_chart_harm " + damtype.damage[adjustedTier] + " " + damtype.type);
});
fAction += CTF.addTableItem("Damage =" + damstring);
}
if (fType.toLowerCase() === "tech"){
if (!(typeof desiredFeature["attack_bonus"] === 'undefined')){
let fAttackBonus = desiredFeature["attack_bonus"][adjustedTier];
fAction += CTF.addTableItem("Attack Roll = **[[1d20+" + fAttackBonus + "]] | ACC/DIFF [[1d6]][[1d6]][[1d6]][[1d6]][[1d6]][[1d6]]**");
}
}
if (!(typeof fEffect === 'undefined')){
fAction += CTF.addTableItem("Effect = " + fEffect);
}
let onhit = "";
if (!(typeof desiredFeature["on_hit"] === 'undefined')){
fAction += CTF.addTableItem("On Hit = " + desiredFeature["on_hit"]);
}
log("TAGS");
tagstring = "";
if (!(typeof fTags === 'undefined')){
fTags.forEach((tag) => {
log(tag);
if (tag.id == "tg_round" || tag.id == "tg_turn" || tag.id == "tg_scene"){
tagstring += CTF.addTableButton(tag.val+"/"+tag.id.substring(3));
}
else if (tag.id == "tg_recharge"){
tagstring += CTF.addTableButton(tag.id.substring(3) + " " + tag.val + "+");
}
else {
let tagvalue = (typeof tag.val === 'undefined') ? "" : " " + tag.val;
tagstring += CTF.addTableButton(tag.id.substring(3) + tagvalue);
}
});
}
fAction += CTF.addTableItem("Tags = " + tagstring);
if(displayOnly){
sendChat("LCP BUILDER", fAction);
}
else{
createObj("ability", {
name: fName,
characterid: characterID,
description: "a description",
action: fAction,
istokenaction: true
});
injectScanData(msg, id, adjustedTier);
}
}
function BuildTemplate(msg, templateName, tier, displayTemplate = false){
let adjustedTier = tier-1;
if (typeof msg.selected === 'undefined') {
sendChat("LCP BUILDER", CTF.addTemplate()+CTF.addHeader("Error adding Template")+CTF.addTableItem("You must select a token to add or view a template."));
return;
}
let obj = getObj(msg.selected[0]._type,msg.selected[0]._id);
let characterID = obj.get('represents');
let npcTemplate = FindNpcTemplate(templateName);
if (typeof npcTemplate === 'undefined'){
sendChat("LCP BUILDER", "Could not find template named "+ templateName + " in npcClassDataSet. Searched " + npcTemplateDataSet.length + " Sets.");
return;
}
// Get the token name
let tokobj = getObj('graphic', msg.selected[0]._id);
let tokenName = tokobj.get('name').toLowerCase();
let basefeatures = npcTemplate["base_features"];
let accumulatedBaseFeatures = "";
basefeatures.forEach((element) => {
let tempFeature = FindNpcFeature(element, adjustedTier);
let addOrDisplay = (!displayTemplate) ? "!lcp_builder_display_npc_feature" : "!lcp_builder_add_npc_feature";
accumulatedBaseFeatures += CTF.addTableButton(tempFeature["name"], addOrDisplay + " " + element + " " + tier);
});
let optionalfeatures = npcTemplate["optional_features"];
let accumulatedOptionalFeatures = "";
optionalfeatures.forEach((element) => {
let tempFeature = FindNpcFeature(element, adjustedTier);
let addOrDisplay = (!displayTemplate) ? "!lcp_builder_display_npc_feature" : "!lcp_builder_add_npc_feature";
accumulatedOptionalFeatures += CTF.addTableButton(tempFeature["name"], addOrDisplay + " " + element + " " + tier);
});
// Define the card action for the character sheet ability
let MechCardAction = CTF.addTemplate();
MechCardAction += (displayTemplate) ? CTF.addTableHeader(" Template Info " + CTF.addTableButton("ASSIGN","!lcp_builder_add_npc_template " + templateName + " " + tier)) : CTF.addTableHeader("scan-data");
MechCardAction += CTF.addTableItem("Name = "+ templateName);
if(displayTemplate){
MechCardAction += CTF.addTableItem("Base Features = "+ accumulatedBaseFeatures);
MechCardAction += CTF.addTableItem("Optional Features = "+ accumulatedOptionalFeatures);
}
if(displayTemplate){
sendChat("LCP BUILDER", "/w gm " + MechCardAction);
return;
}
// else continue and attach all abilities from the base class.
basefeatures.forEach((element) => {
AddNpcClassFeature(msg, element, tier)
});
}
function BuildClass(msg, className, tier, displayClass = false){
// Get Selected Character
let adjustedTier = tier-1;
if (typeof msg.selected === 'undefined' && !displayClass) {
sendChat("LANCER CORE HELPER", "&{template:default}{{name=NO TOKEN SELECTED}}{{You must select a token for this macro}}");
return;
}
let obj = getObj(msg.selected[0]._type,msg.selected[0]._id);
let characterID = obj.get('represents');
let npcClass = FindNpcClass(className);
if (typeof npcClass === 'undefined'){
sendChat("LCP BUILDER", "Could not find class named "+ className + " in npcClassDataSet. Searched " + npcClassDataSet.length + " Sets.");
return;
}
// Get the token name
let tokobj = getObj('graphic', msg.selected[0]._id);
let tokenName = tokobj.get('name').toLowerCase();
let basefeatures = npcClass["base_features"];
let accumulatedBaseFeatures = "";
basefeatures.forEach((element) => {
let tempFeature = FindNpcFeature(element, adjustedTier);
let addOrDisplay = (!displayClass) ? "!lcp_builder_display_npc_feature" : "!lcp_builder_add_npc_feature";
accumulatedBaseFeatures += CTF.addTableButton(tempFeature["name"], addOrDisplay + " " + element + " " + tier);
});
let optionalfeatures = npcClass["optional_features"];
let accumulatedOptionalFeatures = "";
optionalfeatures.forEach((element) => {
let tempFeature = FindNpcFeature(element, adjustedTier);
let addOrDisplay = (!displayClass) ? "!lcp_builder_display_npc_feature" : "!lcp_builder_add_npc_feature";
accumulatedOptionalFeatures += CTF.addTableButton(tempFeature["name"], addOrDisplay + " " + element + " " + tier);
});
// Define the card action for the character sheet ability
let MechCardAction = CTF.addTemplate();
MechCardAction += (displayClass) ? CTF.addTableHeader("Class Info " + CTF.addTableButton("ASSIGN","!lcp_builder_add_npc_class " + className + " " + tier)) : CTF.addTableHeader("scan-data");
MechCardAction += (displayClass) ? "" : CTF.addTableItem("Name = "+ tokenName);
MechCardAction += CTF.addTableItem("Class = "+ npcClass["name"] + " TIER " + tier)
+ CTF.addTableItem("Vitals = "+ CTF.addTableButton("HP: " + "@{selected|bar1} / " + npcClass["stats"]["hp"][adjustedTier])
+ CTF.addTableButton("HEAT: " + "@{selected|bar3} / " + npcClass["stats"]["heatcap"][adjustedTier])
+ CTF.addTableButton("ARMOR: " + npcClass["stats"]["armor"][adjustedTier]))
+ CTF.addTableItem("Stats = "+ CTF.addTableButton("EVADE: " + npcClass["stats"]["evade"][adjustedTier])
+ CTF.addTableButton("EDEF: " + npcClass["stats"]["edef"][adjustedTier])
+ CTF.addTableButton("HASE: "
+ npcClass["stats"]["hull"][adjustedTier] + " / "
+ npcClass["stats"]["agility"][adjustedTier] + " / "
+ npcClass["stats"]["systems"][adjustedTier] + " / "
+ npcClass["stats"]["engineering"][adjustedTier])
+ CTF.addTableButton("SAVE: " + npcClass["stats"]["save"][adjustedTier])
+ CTF.addTableButton("SENSOR: " + npcClass["stats"]["sensor"][adjustedTier])
+ CTF.addTableButton("SPEED: " + npcClass["stats"]["speed"][adjustedTier])
+ CTF.addTableButton("SIZE: " + npcClass["stats"]["size"][adjustedTier]))
if(displayClass){
MechCardAction += CTF.addTableItem("Base Features = "+ accumulatedBaseFeatures);
MechCardAction += CTF.addTableItem("Optional Features = "+ accumulatedOptionalFeatures);
}
if(displayClass){
sendChat("LCP BUILDER", "/w gm " + MechCardAction);
return;
}
// else continue and attach all abilities from the base class.
createObj("ability", {
name: "scan-data",
characterid: characterID,
description: "a description",
action: MechCardAction,
istokenaction: true
});
basefeatures.forEach((element) => {
AddNpcClassFeature(msg, element, tier)
});
}
// DAMAGE CALCULATION AND APPLICATION
function ChartHarm(msg, harm, type){
var message = CTF.addTemplate() + CTF.addTableHeader("INCOMING HARM - " + type.toUpperCase());
let initialHarm = parseInt(harm);
let exposedHarm = parseInt(harm) * 2;
let ZeroComparator = function(left,right){
return (left+right > 0) ? left+right : 0;
}
message += CTF.addTableItem("Raw Damage = "
+ CTF.addTableButton("Raw " + initialHarm, "!lcp_do_harm " + initialHarm + " " + type)
+ CTF.addTableButton("Exposed " + exposedHarm, "!lcp_do_harm " + exposedHarm + " " + type)
+ CTF.addTableButton("Resisted " + Math.ceil(initialHarm/2), "!lcp_do_harm " + Math.ceil(initialHarm/2) + " " + type));
message += CTF.addTableItem("ARMOR = **1 | 2 | 3 | 4 | 5 | 6**")
message += CTF.addTableItem("Armored = "
+ CTF.addTableButton( (ZeroComparator(initialHarm,-1)), "!lcp_do_harm " + (ZeroComparator(initialHarm,-1)) + " " + type)
+ CTF.addTableButton( (ZeroComparator(initialHarm,-2)), "!lcp_do_harm " + (ZeroComparator(initialHarm,-2)) + " " + type)
+ CTF.addTableButton( (ZeroComparator(initialHarm,-3)), "!lcp_do_harm " + (ZeroComparator(initialHarm,-3)) + " " + type)
+ CTF.addTableButton( (ZeroComparator(initialHarm,-4)), "!lcp_do_harm " + (ZeroComparator(initialHarm,-4)) + " " + type)
+ CTF.addTableButton( (ZeroComparator(initialHarm,-5)), "!lcp_do_harm " + (ZeroComparator(initialHarm,-5)) + " " + type)
+ CTF.addTableButton( (ZeroComparator(initialHarm,-6)), "!lcp_do_harm " + (ZeroComparator(initialHarm,-6)) + " " + type));
message += CTF.addTableItem("Armored + Resisted = "
+ CTF.addTableButton( Math.ceil(((ZeroComparator(initialHarm,-1)) / 2)), "!lcp_do_harm " + Math.ceil((ZeroComparator(initialHarm,-1))/2) + " " + type)
+ CTF.addTableButton( Math.ceil(((ZeroComparator(initialHarm,-2)) / 2)), "!lcp_do_harm " + Math.ceil((ZeroComparator(initialHarm,-2))/2) + " " + type)
+ CTF.addTableButton( Math.ceil(((ZeroComparator(initialHarm,-3)) / 2)), "!lcp_do_harm " + Math.ceil((ZeroComparator(initialHarm,-3))/2) + " " + type)
+ CTF.addTableButton( Math.ceil(((ZeroComparator(initialHarm,-4)) / 2)), "!lcp_do_harm " + Math.ceil((ZeroComparator(initialHarm,-4))/2) + " " + type)
+ CTF.addTableButton( Math.ceil(((ZeroComparator(initialHarm,-5)) / 2)), "!lcp_do_harm " + Math.ceil((ZeroComparator(initialHarm,-5))/2) + " " + type)
+ CTF.addTableButton( Math.ceil(((ZeroComparator(initialHarm,-6)) / 2)), "!lcp_do_harm " + Math.ceil((ZeroComparator(initialHarm,-6))/2) + " " + type));
message += CTF.addTableItem("Armored + Exposed = "
+ CTF.addTableButton( (ZeroComparator(exposedHarm,-1)), "!lcp_do_harm " + (ZeroComparator(exposedHarm,-1)) + " " + type)
+ CTF.addTableButton( (ZeroComparator(exposedHarm,-2)), "!lcp_do_harm " + (ZeroComparator(exposedHarm,-2)) + " " + type)
+ CTF.addTableButton( (ZeroComparator(exposedHarm,-3)), "!lcp_do_harm " + (ZeroComparator(exposedHarm,-3)) + " " + type)
+ CTF.addTableButton( (ZeroComparator(exposedHarm,-4)), "!lcp_do_harm " + (ZeroComparator(exposedHarm,-4)) + " " + type)
+ CTF.addTableButton( (ZeroComparator(exposedHarm,-5)), "!lcp_do_harm " + (ZeroComparator(exposedHarm,-5)) + " " + type)
+ CTF.addTableButton( (ZeroComparator(exposedHarm,-6)), "!lcp_do_harm " + (ZeroComparator(exposedHarm,-6)) + " " + type));
message += CTF.addTableItem("Armored + Exposed + Resisted = "
+ CTF.addTableButton( Math.ceil(((ZeroComparator(exposedHarm,-1))/2)), "!lcp_do_harm " + Math.ceil((ZeroComparator(exposedHarm,-1))/2) + " " + type)
+ CTF.addTableButton( Math.ceil(((ZeroComparator(exposedHarm,-2))/2)), "!lcp_do_harm " + Math.ceil((ZeroComparator(exposedHarm,-2))/2) + " " + type)
+ CTF.addTableButton( Math.ceil(((ZeroComparator(exposedHarm,-3))/2)), "!lcp_do_harm " + Math.ceil((ZeroComparator(exposedHarm,-3))/2) + " " + type)
+ CTF.addTableButton( Math.ceil(((ZeroComparator(exposedHarm,-4))/2)), "!lcp_do_harm " + Math.ceil((ZeroComparator(exposedHarm,-4))/2) + " " + type)
+ CTF.addTableButton( Math.ceil(((ZeroComparator(exposedHarm,-5))/2)), "!lcp_do_harm " + Math.ceil((ZeroComparator(exposedHarm,-5))/2) + " " + type)
+ CTF.addTableButton( Math.ceil(((ZeroComparator(exposedHarm,-6))/2)), "!lcp_do_harm " + Math.ceil((ZeroComparator(exposedHarm,-6))/2) + " " + type));
sendChat("LANCER CORE HELPER", message);
}
function DoHarm(msg, amount, type){
// Harm is either physical or heat. Burn is physical harm with extra properties so not needed.
let actualType = "";
if (type.toLowerCase() === "energy"
|| type.toLowerCase() === "explosive"
|| type.toLowerCase() === "kinetic"
|| type.toLowerCase() === "burn"){
actualType = "physical";
}
else {
actualType = "heat";
}
try{
let obj = getObj(msg.selected[0]._type,msg.selected[0]._id);
if (typeof obj === 'undefined'){
sendChat("LANCER CORE HELPER","ERROR: No Character attached to Token.");
return;
}
let bar1_max = parseInt(obj.get('bar1_max'));
let bar1_current = parseInt(obj.get('bar1_value'));
let bar2_max = parseInt(obj.get('bar2_max'));
let bar2_current = parseInt(obj.get('bar2_value'));
let bar3_max = parseInt(obj.get('bar3_max'));
let bar3_current = parseInt(obj.get('bar3_value'));
log("\nbar1: " + bar1_current + " | " + bar1_max + "\n"
+ "bar2: " + bar2_current + " | " + bar2_max + "\n"
+ "bar3: " + bar3_current + " | " + bar3_max + "\n");
let damagecounter = parseInt(amount);
let timesover = 0;
if (actualType === "physical"){
// if there is a value for overshield, get rid of it first.
if (bar2_current !== "" || bar2_current > 0){
// more overshield than damage? subtract damage and stop.
if (bar2_current > damagecounter){
bar2_current -= damagecounter;
obj.set('bar2_value', bar2_current);
} else {
damagecounter -= bar2_current;
obj.set('bar2_value', 0); // zero out the field.
}
}
// Next affect actual hitpoints. Keep track of how many times it flips
bar1_current -= damagecounter;
if (bar1_current <= 0){
while (bar1_current <= 0 ) {
bar1_current += bar1_max;
timesover++;
}
}
obj.set('bar1_value', bar1_current); // Set the new value;
let message = CTF.addTemplate()
+ CTF.addTableHeader(obj.get('name').toUpperCase() + " DAMAGED")
+ CTF.addTableItem("SUFFERED " + amount + " " + type.toUpperCase())
+ CTF.addTableItem("STRUCTURED " + timesover + " TIMES");
if (timesover > 0){
message += CTF.addTableItem(CTF.addTableButton("ROLL STRUCTURE", "!lcp_structure_table"));
}
sendChat("LANCER CORE HELPER", message);
} else {
// Damage is heat, use the other bar
bar3_current += damagecounter;
while (bar3_current > bar3_max){
bar3_current -= bar3_max;
timesover++;
}
obj.set('bar3_value', bar3_current); // Set the new value;
let message = CTF.addTemplate()
+ CTF.addTableHeader(obj.get('name').toUpperCase() + " DAMAGED")
+ CTF.addTableItem("SUFFERED " + amount + " " + type.toUpperCase())
+ CTF.addTableItem("OVERHEATED " + timesover + " TIMES");
if (timesover > 0) {
message += CTF.addTableItem(CTF.addTableButton("ROLL OVERHEAT", "!lcp_overheat_table"));
}
sendChat("LANCER CORE HELPER", message);
}
} catch (e) {
sendChat("LANCER CORE HELPER", "ERROR: Something went while calculating damage:" + e);
}
}
function RollStructureTable(msg){
let message = CTF.addTemplate()
+ CTF.addTableHeader("STRUCTURE DAMAGE")
+ CTF.addTableItem("ROLL = [[1d6]][[1d6]][[1d6]][[1d6]][[1d6]][[1d6]]")
+ CTF.addTableItem("5 OR 6 = GLANCING BLOW<br>Emergency systems kick in and stabilize your mech. However, your mech is impaired until the end of your next turn.")
+ CTF.addTableItem("2, 3, OR 4 = SYSTEM TRAUMA<br>Parts of your mech are torn off by the damage. Roll a d6 [[1d6]]. <br>On a 1-3, all the weapons on one mount are destroyed. <br>On a 4-6, a system is destroyed. <br>You choose what’s destroyed, but systems or weapons with the limited tag and no charges left are not valid. <br>If there’s nothing left of one result, it becomes the other. <br>If there’s absolutely nothing left to destroy, this result becomes DIRECT HIT instead.")
+ CTF.addTableItem("1 = DIRECT HIT<br>This result has different outcomes depending on how much structure your mech has remaining.<ul><li>3+ - Your mech is stunned until the end of your next turn.</li><li>2 - Your mech must pass a hull check or be destroyed. Even on a successful check, your mech is stunned until the end of your next turn.</li><li>1 or lower - Your mech is destroyed.</li></ul>")
+ CTF.addTableItem("MULTIPLE 1'S = CRUSHING BLOW <br> Your mech is damaged beyond repair and is destroyed. You can still exit it as normal.");
sendChat("LANCER CORE HELPER", message);
}
function RollOverheatTable(msg){
let message = CTF.addTemplate()
+ CTF.addTableHeader("OVERHEATING")
+ CTF.addTableItem("ROLL = [[1d6]][[1d6]][[1d6]][[1d6]][[1d6]][[1d6]]")
+ CTF.addTableItem("5 OR 6 = EMERGENCY SHUNT<br>Cooling systems recover and manage to contain the peaking heat levels. However, your mech is impaired until the end of your next turn.")
+ CTF.addTableItem("2, 3, OR 4 = POWER PLANT DESTABILIZE<br>Your mech’s power plant becomes unstable, ejecting jets of plasma. Your mech suffers from the exposed status (taking double damage) until it takes action to remove it.")
+ CTF.addTableItem("1 = MELTDOWN<br>This result has different outcomes depending on how much reactor stress your mech has remaining.<ul><li>3+ - Your mech becomes EXPOSED</li><li>2 - Roll an ENGINEERING check. On a success, your mech is EXPOSED; on a failure, it suffers a reactor meltdown after 1d6 of your turns (rolled by the GM). A reactor meltdown can be prevented by retrying the ENGINEERING check as a full action.</li><li>1 or lower - Your mech suffers a reactor meltdown at the end of your next turn.</li></ul>")
+ CTF.addTableItem("MULTIPLE 1'S = IRREVERSIBLE MELTDOWN <br> Your reactor goes critical. Your mech will suffer a reactor meltdown at the end of your next turn.");
sendChat("LANCER CORE HELPER", message);
}
// AUTOMATED MACRO CREATION
function LcpCreateMacros(msg){
try {
let chatstring = "/w " + msg.who + " " + CTF.addTemplate() + CTF.addTableHeader("Generating Lancer Core Helper Macros");
let numMacros = 0;
if (!playerIsGM(msg.playerid)){
return;
}
if (!p_lncr_roll20objcontains("PC-Build-Menu")){
createObj('macro', {
_playerid: msg.playerid,
name: "PC-Build-Menu",
action: "!lcp_builder_pc_menu",
visibleto: 'all',
istokenaction: true
});
chatstring += "{{PC-Build-Menu}}";
numMacros++;
}
if (!p_lncr_roll20objcontains('Clean-Selected-Token')){
createObj('macro', {
_playerid: msg.playerid,
name: "Clean-Selected-Token",
action: "!lcp_builder_clean",
visibleto: '',
istokenaction: true
});
chatstring += CTF.addTableItem("Clean-Selected-Token");
numMacros++;
}
if (!p_lncr_roll20objcontains('NPC-Build-Menu')){
createObj('macro', {
_playerid: msg.playerid,
name: "NPC-Build-Menu",
action: "!lcp_builder_display_class_menu",
visibleto: '',
istokenaction: false
});
chatstring += "{{NPC-Build-Menu}}";
numMacros++;
}
if (!p_lncr_roll20objcontains('NPC-Attack/Save')){
createObj('macro', {
_playerid: msg.playerid,
name: "NPC-Attack/Save",
action: "&{template:default}{{name=NPC-Attack/Save}}{{Attack/Save = [[1d20]]}}{{ACC/DIFF = [[1d6]][[1d6]][[1d6]][[1d6]][[1d6]][[1d6]][[1d6]]}}",
visibleto: '',
istokenaction: false
});
chatstring += "{{NPC-Attack/Save}}";
numMacros++;
}
if (numMacros == 0) {
chatstring += "{{All macros are already defined}}";
}
sendChat("LANCER CORE HELPER",chatstring);
}catch(e){
sendChat("LANCER CORE HELPER", "Something went wrong while initializing.");
}
}
// COMMAND HANDLER
on('chat:message', function(msg){
"use strict";
if('api' !== msg.type) {
return;
}
var args = msg.content.split(/\s+/);
let stragg = function(startpos=1){
let aggstring = ""
for (let i = startpos; i < args.length; i++)
{
aggstring += args[i]
if (i+1 !== args.length){
aggstring += " ";
}
}
return aggstring.toLowerCase();
};
switch (args[0]){
case '!lcp_builder_pc_menu':
MenuPcOptionsList(msg);
break;
case '!lcp_builder_displaylist':
DisplayPcItemList(msg, args[1], args[2]);
break;
case '!lcp_builder_pc_add_macro':
AddPcFeature(msg, args[1], args[2], args[3]);
break;
case '!lcp_builder_clean':
sendChat("LCP BUILDER:", "Cleaning Token");
CleanTokenOfAllAbilities(msg);
break;
case '!lcp_import_char':
sendChat("LCP IMPORTER", "Attepting to import character.");
let contents = stragg();
ImportPcCharacter(msg, contents);
break;
case '!lcp_chart_harm':
ChartHarm(msg, args[1], args[2]);
break;
case '!lcp_do_harm':
DoHarm(msg, args[1], args[2]);
break;
case '!lcp_structure_table':
RollStructureTable(msg);
break;
case '!lcp_overheat_table':
RollOverheatTable(msg);
break;
case '!lcp_builder_display_class_menu':
MenuNpcClassTier(msg);
break;
case '!lcp_builder_add_npc_feature':
sendChat("LCP BUILDER", "Adding feature to selected token");
AddNpcClassFeature(msg,args[1], parseInt(args[2]));
break;
case '!lcp_builder_display_npc_feature':
AddNpcClassFeature(msg,args[1], parseInt(args[2]), true);
break;
case '!lcp_builder_add_npc_class':
BuildClass(msg,args[1],parseInt(args[2]), args[3]);
break;
case '!lcp_builder_add_npc_template':
BuildTemplate(msg,args[1],parseInt(args[2]), args[3]);
break;
case '!lcp_builder_display_class_list':
sendChat("LCP BUILDER", "Attempting to parse JSON data from Classes LCP");
DisplayClassList(msg, parseInt(args[1]), true);
break;
case '!lcp_builder_display_template_list':
sendChat("LCP BUILDER", "Attempting to parse JSON data from Templates LCP");
DisplayTemplateList(msg, parseInt(args[1]), true);
break;
default:
break;
}
return;
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment