Skip to content

Instantly share code, notes, and snippets.

@Geritz
Last active December 13, 2023 20:20
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
// STEP 1: DEFINE YOUR DATABASE
// Define a var as below, assigning the contents of the JSON file as the content of the variable.
// EXAMPLE:
// var npcCoreClassData = [...];
var npcCoreClassData = [];
var npcCoreFeatureData = [];
var npcCoreTemplateData = [];
// STEP 2: Add each of your global variables to their appropriate data sets below, delimited by commas
// EXAMPLE:
//var unusedDataSet = [unusedData,unusedData2];
var npcClassDataSet = [npcCoreClassData]; //Insert the variables of your npcClassJsons here delimited by commas. (e.g. "[set1,set2,set3...setn]")
var npcTemplateDataSet = [npcCoreTemplateData]; //Insert the variables of your npcTemplateJsons here delimited by commas. (e.g. "[set1,set2,set3...setn]")
var npcFeatureDataSet = [npcCoreFeatureData]; //Insert the variables of your npcFeatureJsons here delimited by commas. (e.g. "[set1,set2,set3...setn]")
// STEP 3: Create the following macros and contents in Roll20:
// LcpNpcBuildMenu !lcp_builder_display_class_menu
// LcpCleanToken !lcp_builder_clean
// At this point you do not need any more setup, you're good to go!
// USING THIS TOOL
// Character setup:
// When you are setting up a new character to use with this script you must ensure that
// the character token is connected to a character sheet or this script will not function
// properly! Once connected, the script uses the character sheet abilities to do its thing
// so you should not need to do any further pre work.
// ADD CHARACTER CLASS FIRST BEFORE ANY OTHER OPTIONS
// Make sure you have a token selected! If a token is selected, open the build menu using the
// LcpNpcBuildMenu macro that you built in step 3. Select the tier of the NPC you want to build
// then pick a class and then click assign.
// TO ADD AN ABILITY TO AN EXISTING TOKEN
// follow the steps you would pick for adding a class or template, and then click any of its features
// Contact Geritz in the PilotNet discord if you run into trouble.
//ChatTableFactory
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+")";
}
}
// PC CORE BONUS SEARCH
function FindPcCoreBonus(bonusName){
let pcCoreBonus;
pcCoreBonusDataSet.forEach((element) => {
let cbTarget = element.find((cb) => cb.name.toLowerCase() == bonusName.toLowerCase());
if (typeof cbTarget !== 'undefined'){
pcCoreBonus = cbTarget;
}
});
return pcCoreBonus;
}
// NPC CLASS SEARCH
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;
}
// NPC FEATURE SEARCH
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;
}
// NPC TEMPLATE SEARCH
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;
}
// NPC TEMPLATE DISPLAY FORMATTING
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);
}
// DELETE ALL MACROS FROM SELECTED CHARACTER
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();
})
}
// MODIFY SCAN MACRO WITH ADDITIONAL PROPERTIES
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);
}
}
}
})
}
// FORMAT AND DISPLAY ALL NPC CLASSES
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));
}
// DISPLAY MENU FOR PC OPTIONS
function MenuPcOptionsList(msg){
let output = CTF.addTemplate()
+ CTF.addTableHeader("PC Build Menu")
+ CTF.addTableItem("Frames =" + CTF.addTableButton("FRAMES", "!lncr_stub"))
+ CTF.addTableItem("Weapons =" + CTF.addTableButton("WEAPONS", "!lncr_stub"))
+ CTF.addTableItem("Systems = " + CTF.addTableButton("SYSTEMS", "!lncr_stub"))
+ CTF.addTableItem("Core Bonuses = " + CTF.addTableButton("CORE BONUSES", "!lncr_stub"))
+ CTF.addTableItem("Mods = " + CTF.addTableButton("MODS", "!lncr_stub"))
+ CTF.AddTableItem("Talents = " + CTF.addTableButton("TALENTS", "!lncr_stub"));
sendChat("LCP BUILDER", "/w gm " + output);
}
// DISPLAY MENU FOR NPC CLASSES
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);
}
// ADD A FEATURE MACRO TO AN NPC
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]);
});
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);
}
}
// ADD A TEMPLATE WITH SCAN DATA TO AN NPC
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)
});
}
// ADD CLASS WITH SCAN DATA TO AN NPC
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)
});
}
// CHAT MESSAGE 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 '!debugAppendData':
injectScanData(msg,args[1], args[2],args[3]);
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;
case '!lcp_builder_clean':
CleanTokenOfAllAbilities(msg);
break;
case '!lcp_builder_display_pc_menu':
MenuPcOptionsList(msg);
break;
default:
break;
}
return;
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment