Skip to content

Commit

Permalink
0.1.36
Browse files Browse the repository at this point in the history
  • Loading branch information
gambit07 committed May 6, 2024
1 parent e2c25da commit 7fb2dd3
Show file tree
Hide file tree
Showing 7 changed files with 435 additions and 97 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:
id: set-release-notes-github
run: |
echo "- Updates:" >> release_notes.txt
echo " - Poetry in Misery: Added the ability to use character sheet initiations of Ability Checks and Saving Throws outside of combat." >> release_notes.txt
echo " - Poetry in Misery: Optimized split between combat and non combat triggers." >> release_notes.txt
echo "release-notes-github<<EOF" >> $GITHUB_ENV
cat release_notes.txt >> $GITHUB_ENV
echo "EOF" >> $GITHUB_ENV
Expand Down
2 changes: 1 addition & 1 deletion module.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"name": "Gambit"
}
],
"version": "0.1.35",
"version": "0.1.36",
"compatibility": {
"minimum": "11",
"verified": "11",
Expand Down
15 changes: 11 additions & 4 deletions scripts/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,9 +85,16 @@ export async function handleDialogPromises(userDialogPromise, gmDialogPromise) {
});
}

export function findValidTokens({token, target, itemName, itemType, itemChecked, reactionCheck, sightCheck, rangeCheck, rangeTotal, dispositionCheck, dispositionCheckType, workflowType}) {
let validTokens = game.combat.combatants.map(combatant => canvas.tokens.get(combatant.tokenId)).filter(t => {
export function findValidTokens({token, target, itemName, itemType, itemChecked, reactionCheck, sightCheck, rangeCheck, rangeTotal, dispositionCheck, dispositionCheckType, workflowType, workflowCombat}) {
let validTokens;

if (workflowCombat === false) {
validTokens = canvas.tokens.placeables.filter(t => filterToken(t));
} else {
validTokens = game.combat.combatants.map(combatant => canvas.tokens.get(combatant.tokenId)).filter(t => filterToken(t));
}

function filterToken(t) {
// Check if invalid token on the canvas
if (!t.actor) return;

Expand Down Expand Up @@ -170,7 +177,7 @@ export function findValidTokens({token, target, itemName, itemType, itemChecked,
}

return t;
});
};

return validTokens;
return validTokens;
}
330 changes: 330 additions & 0 deletions scripts/macros/broochOfTheElements.js

Large diffs are not rendered by default.

152 changes: 76 additions & 76 deletions scripts/macros/cuttingWords.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,87 +75,87 @@ export async function cuttingWords({workflowData,workflowType}) {
}

if(workflowType === "damage") {
if (workflow.token.document.disposition === validTokenPrimary.document.disposition) return;
let damageTypes = workflow.damageRolls.map(roll => roll.options.type);
let damageTotals = workflow.damageRolls.map(roll => roll.total);
if (workflow.token.document.disposition === validTokenPrimary.document.disposition) return;
let damageTypes = workflow.damageRolls.map(roll => roll.options.type);
let damageTotals = workflow.damageRolls.map(roll => roll.total);

let result;
let result;

if (MidiQOL.safeGetGameSetting('gambits-premades', 'Mirror 3rd Party Dialog for GMs') && browserUser.id !== game.users?.activeGM.id) {
let userDialogPromise = socket.executeAsUser("showCuttingWordsDialog", browserUser.id, originTokenUuidPrimary, actorUuidPrimary, validTokenPrimary.document.uuid, dialogTitlePrimary, originTokenUuidPrimary, "damage", damageTypes, `cuttingwords_${browserUser.id}`, 'user', damageTotals).then(res => ({...res, source: "user", type: "multiDialog"}));
let gmDialogPromise = socket.executeAsGM("showCuttingWordsDialog", originTokenUuidPrimary, actorUuidPrimary, validTokenPrimary.document.uuid, dialogTitleGM, originTokenUuidPrimary, "damage", damageTypes, `cuttingwords_${game.users?.activeGM.id}`, 'gm', damageTotals).then(res => ({...res, source: "gm", type: "multiDialog"}));
result = await socket.executeAsGM("handleDialogPromises", userDialogPromise, gmDialogPromise);
} else {
result = await socket.executeAsUser("showCuttingWordsDialog", browserUser.id, originTokenUuidPrimary, actorUuidPrimary, validTokenPrimary.document.uuid, dialogTitlePrimary, originTokenUuidPrimary, "damage", damageTypes, null, null, damageTotals).then(res => ({...res, source: browserUser.isGM ? "gm" : "user", type: "singleDialog"}));
}
const { cuttingWordsDecision, damageChosen, source, type } = result;
if (MidiQOL.safeGetGameSetting('gambits-premades', 'Mirror 3rd Party Dialog for GMs') && browserUser.id !== game.users?.activeGM.id) {
let userDialogPromise = socket.executeAsUser("showCuttingWordsDialog", browserUser.id, originTokenUuidPrimary, actorUuidPrimary, validTokenPrimary.document.uuid, dialogTitlePrimary, originTokenUuidPrimary, "damage", damageTypes, `cuttingwords_${browserUser.id}`, 'user', damageTotals).then(res => ({...res, source: "user", type: "multiDialog"}));
let gmDialogPromise = socket.executeAsGM("showCuttingWordsDialog", originTokenUuidPrimary, actorUuidPrimary, validTokenPrimary.document.uuid, dialogTitleGM, originTokenUuidPrimary, "damage", damageTypes, `cuttingwords_${game.users?.activeGM.id}`, 'gm', damageTotals).then(res => ({...res, source: "gm", type: "multiDialog"}));

result = await socket.executeAsGM("handleDialogPromises", userDialogPromise, gmDialogPromise);
} else {
result = await socket.executeAsUser("showCuttingWordsDialog", browserUser.id, originTokenUuidPrimary, actorUuidPrimary, validTokenPrimary.document.uuid, dialogTitlePrimary, originTokenUuidPrimary, "damage", damageTypes, null, null, damageTotals).then(res => ({...res, source: browserUser.isGM ? "gm" : "user", type: "singleDialog"}));
}

const { cuttingWordsDecision, damageChosen, source, type } = result;

if (cuttingWordsDecision === false || !cuttingWordsDecision) {
if(source && source === "user" && type === "multiDialog") await socket.executeAsGM("closeDialogById", { dialogId: `cuttingwords_${game.users?.activeGM.id}` });
if(source && source === "gm" && type === "multiDialog") await socket.executeAsUser("closeDialogById", browserUser.id, { dialogId: `cuttingwords_${browserUser.id}` });
continue;
}
if (cuttingWordsDecision === true) {
const saveSetting = workflow.options.noOnUseMacro;
workflow.options.noOnUseMacro = true;
let reroll;
if(source && source === "user") reroll = await socket.executeAsUser("rollAsUser", browserUser.id, { rollParams: `1${bardicDie}`, type: workflowType });
if(source && source === "gm") reroll = await socket.executeAsGM("rollAsUser", { rollParams: `1${bardicDie}`, type: workflowType });

let remainingReduction = reroll.total;
let updatedRolls = [];
let processedRolls = new Set();

for (const priority of damageChosen) {
let rollFound = workflow.damageRolls.find(roll => roll.options.type === priority);

if (rollFound) {
processedRolls.add(rollFound);
let rollTotal = rollFound.total;
if (rollTotal >= remainingReduction) {
let modifiedRoll = await new Roll(`${rollTotal} - ${remainingReduction}`).evaluate({async: true});
modifiedRoll.options.type = priority;
updatedRolls.push(modifiedRoll);
remainingReduction = 0;
break;
} else {
remainingReduction -= rollTotal;
let zeroRoll = await new Roll(`${rollTotal} - ${rollTotal}`).evaluate({async: true});
zeroRoll.options.type = priority;
updatedRolls.push(zeroRoll);
}
}
}

workflow.damageRolls.forEach(roll => {
if (!processedRolls.has(roll)) {
updatedRolls.push(roll);
if (cuttingWordsDecision === false || !cuttingWordsDecision) {
if(source && source === "user" && type === "multiDialog") await socket.executeAsGM("closeDialogById", { dialogId: `cuttingwords_${game.users?.activeGM.id}` });
if(source && source === "gm" && type === "multiDialog") await socket.executeAsUser("closeDialogById", browserUser.id, { dialogId: `cuttingwords_${browserUser.id}` });
continue;
}
if (cuttingWordsDecision === true) {
const saveSetting = workflow.options.noOnUseMacro;
workflow.options.noOnUseMacro = true;
let reroll;
if(source && source === "user") reroll = await socket.executeAsUser("rollAsUser", browserUser.id, { rollParams: `1${bardicDie}`, type: workflowType });
if(source && source === "gm") reroll = await socket.executeAsGM("rollAsUser", { rollParams: `1${bardicDie}`, type: workflowType });

let remainingReduction = reroll.total;
let updatedRolls = [];
let processedRolls = new Set();

for (const priority of damageChosen) {
let rollFound = workflow.damageRolls.find(roll => roll.options.type === priority);

if (rollFound) {
processedRolls.add(rollFound);
let rollTotal = rollFound.total;
if (rollTotal >= remainingReduction) {
let modifiedRoll = await new Roll(`${rollTotal} - ${remainingReduction}`).evaluate({async: true});
modifiedRoll.options.type = priority;
updatedRolls.push(modifiedRoll);
remainingReduction = 0;
break;
} else {
remainingReduction -= rollTotal;
let zeroRoll = await new Roll(`${rollTotal} - ${rollTotal}`).evaluate({async: true});
zeroRoll.options.type = priority;
updatedRolls.push(zeroRoll);
}
}
});

await workflow.setDamageRolls(updatedRolls);

workflow.options.noOnUseMacro = saveSetting;

let chatList = [];

chatList = `<span style='text-wrap: wrap;'>The creature takes a cutting word, and their damage is reduced by ${reroll.total}. <img src="${workflow.token.actor.img}" width="30" height="30" style="border:0px"></span>`;
}

workflow.damageRolls.forEach(roll => {
if (!processedRolls.has(roll)) {
updatedRolls.push(roll);
}
});

let msgHistory = [];
game.messages.reduce((list, message) => {
if (message.flags["midi-qol"]?.itemId === spellData._id && message.speaker.token === validTokenPrimary.id) msgHistory.push(message.id);
}, msgHistory);
let itemCard = msgHistory[msgHistory.length - 1];
let chatMessage = await game.messages.get(itemCard);
let content = await duplicate(chatMessage.content);
let insertPosition = content.indexOf('<div class="end-midi-qol-attack-roll"></div>');
if (insertPosition !== -1) {
content = content.slice(0, insertPosition) + chatList + content.slice(insertPosition);
}
await chatMessage.update({ content: content });
}
await workflow.setDamageRolls(updatedRolls);

workflow.options.noOnUseMacro = saveSetting;

let chatList = [];

chatList = `<span style='text-wrap: wrap;'>The creature takes a cutting word, and their damage is reduced by ${reroll.total}. <img src="${workflow.token.actor.img}" width="30" height="30" style="border:0px"></span>`;

let msgHistory = [];
game.messages.reduce((list, message) => {
if (message.flags["midi-qol"]?.itemId === spellData._id && message.speaker.token === validTokenPrimary.id) msgHistory.push(message.id);
}, msgHistory);
let itemCard = msgHistory[msgHistory.length - 1];
let chatMessage = await game.messages.get(itemCard);
let content = await duplicate(chatMessage.content);
let insertPosition = content.indexOf('<div class="end-midi-qol-attack-roll"></div>');
if (insertPosition !== -1) {
content = content.slice(0, insertPosition) + chatList + content.slice(insertPosition);
}
await chatMessage.update({ content: content });
}
}

if(workflowType === "attack") {
Expand Down
7 changes: 4 additions & 3 deletions scripts/macros/poetryInMisery.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
export async function poetryInMisery({workflowData,workflowType}) {
export async function poetryInMisery({workflowData,workflowType,workflowCombat}) {
const module = await import('../module.js');
const helpers = await import('../helpers.js');
const socket = module.socket;
const workflowUuid = workflowData;
const workflow = await MidiQOL.Workflow.getWorkflow(workflowUuid);
console.log(workflowCombat)
let itemName = "poetry in misery";
if(!workflow && !workflowData.actor) return;
if(workflow?.item.name.toLowerCase() === itemName) return;
Expand All @@ -15,7 +16,7 @@ export async function poetryInMisery({workflowData,workflowType}) {
// Check if Opportunity Attack is initiating the workflow
if(workflow?.item.name === "Opportunity Attack") return;

let findPoetryInMiseryTokens = helpers.findValidTokens({token: initiatingToken, target: initiatingToken, itemName: itemName, itemType: null, itemChecked: null, reactionCheck: true, sightCheck: false, rangeCheck: true, rangeTotal: 30, dispositionCheck: false, dispositionCheckType: "ally", workflowType: workflowType});
let findPoetryInMiseryTokens = helpers.findValidTokens({token: initiatingToken, target: initiatingToken, itemName: itemName, itemType: null, itemChecked: null, reactionCheck: true, sightCheck: false, rangeCheck: true, rangeTotal: 30, dispositionCheck: false, dispositionCheckType: "ally", workflowType: workflowType, workflowCombat: workflowCombat});

let browserUser;

Expand Down Expand Up @@ -97,7 +98,7 @@ export async function poetryInMisery({workflowData,workflowType}) {
}

let content;
if (workflowType === "attack" || workflowType === "ability") content = `<span style='text-wrap: wrap;'>You use Poetry In Misery to soliloquize over ${initiatingToken.actor.name}'s nat 1 attack roll and regain a use of Bardic Inspiration.<br/><img src="${initiatingToken.actor.img}" width="30" height="30" style="border:0px"></span>`;
if (workflowType === "attack") content = `<span style='text-wrap: wrap;'>You use Poetry In Misery to soliloquize over ${initiatingToken.actor.name}'s nat 1 attack roll and regain a use of Bardic Inspiration.<br/><img src="${initiatingToken.actor.img}" width="30" height="30" style="border:0px"></span>`;
else if (workflowType === "ability") content = `<span style='text-wrap: wrap;'>You use Poetry In Misery to soliloquize over ${initiatingToken.actor.name}'s nat 1 ability check and regain a use of Bardic Inspiration.<br/><img src="${initiatingToken.actor.img}" width="30" height="30" style="border:0px"></span>`;
else if (workflowType === "save") content = `<span style='text-wrap: wrap;'>You use Poetry In Misery to soliloquize over ${chatActor.name}'s nat 1 saving throw and regain a use of Bardic Inspiration.<br/><img src="${chatActor.img}" width="30" height="30" style="border:0px"></span>`;
let actorPlayer = MidiQOL.playerForActor(validTokenPrimary.actor);
Expand Down
Loading

0 comments on commit 7fb2dd3

Please sign in to comment.