|
|
|
@ -21,8 +21,96 @@ Hooks.on("createChatMessage", async (chatData, options, userId) => {
|
|
|
|
if (chatData.speaker?.alias === 'AI DM') return
|
|
|
|
if (chatData.speaker?.alias === 'AI DM') return
|
|
|
|
if(chatData.isRoll){
|
|
|
|
if(chatData.isRoll){
|
|
|
|
console.log(chatData);
|
|
|
|
console.log(chatData);
|
|
|
|
|
|
|
|
const parts = [];
|
|
|
|
|
|
|
|
const speaker = chatData.speaker?.alias || "Someone";
|
|
|
|
|
|
|
|
const flavor = chatData.flavor || "";
|
|
|
|
|
|
|
|
const pf2eFlags = chatData.flags?.pf2e || {};
|
|
|
|
|
|
|
|
const pf2eContext = pf2eFlags.context || {};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Who rolled
|
|
|
|
|
|
|
|
parts.push(`${speaker}`);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// What type of roll (PF2E-specific context)
|
|
|
|
|
|
|
|
// pf2eContext.type can be: "skill-check", "attack-roll", "damage-roll",
|
|
|
|
|
|
|
|
// "saving-throw", "perception-check", "flat-check", "initiative", etc.
|
|
|
|
|
|
|
|
if (pf2eContext.type) {
|
|
|
|
|
|
|
|
parts.push(`made a ${pf2eContext.type.replace(/-/g, " ")}`);
|
|
|
|
|
|
|
|
} else if (flavor) {
|
|
|
|
|
|
|
|
parts.push(`rolled ${flavor.replace(/<[^>]*>/g, "")}`); // strip HTML tags
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
parts.push("rolled");
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// What action/item triggered the roll (e.g. "Longsword", "Perception", "Fireball")
|
|
|
|
|
|
|
|
const origin = pf2eFlags.origin;
|
|
|
|
|
|
|
|
if (origin?.type && origin?.sourceId) {
|
|
|
|
|
|
|
|
parts.push(`using ${origin.type}`);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// The strike info if it's an attack
|
|
|
|
|
|
|
|
if (pf2eFlags.strike) {
|
|
|
|
|
|
|
|
const strike = pf2eFlags.strike;
|
|
|
|
|
|
|
|
if (strike.name) parts.push(`(${strike.name})`);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Outcome for checks (PF2E stores degree of success)
|
|
|
|
|
|
|
|
// pf2eContext.outcome can be: "criticalSuccess", "success", "failure", "criticalFailure"
|
|
|
|
|
|
|
|
if (pf2eContext.outcome) {
|
|
|
|
|
|
|
|
const outcomeMap = {
|
|
|
|
|
|
|
|
criticalSuccess: "Critical Success",
|
|
|
|
|
|
|
|
success: "Success",
|
|
|
|
|
|
|
|
failure: "Failure",
|
|
|
|
|
|
|
|
criticalFailure: "Critical Failure"
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
parts.push(`— ${outcomeMap[pf2eContext.outcome] || pf2eContext.outcome}`);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// The actual roll numbers
|
|
|
|
|
|
|
|
const rollDetails = chatData.rolls.map(r => {
|
|
|
|
|
|
|
|
let detail = `${r.formula} = ${r.total}`;
|
|
|
|
|
|
|
|
// For damage rolls, try to extract damage types from the terms
|
|
|
|
|
|
|
|
if (pf2eContext.type === "damage-roll" && r.options?.damage?.categories) {
|
|
|
|
|
|
|
|
// Damage categories might be available
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return detail;
|
|
|
|
|
|
|
|
}).join("; ");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
parts.push(`[${rollDetails}]`);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// DC if present (PF2E often includes the target DC)
|
|
|
|
|
|
|
|
if (pf2eContext.dc !== undefined && pf2eContext.dc !== null) {
|
|
|
|
|
|
|
|
const dc = typeof pf2eContext.dc === "object" ? pf2eContext.dc.value : pf2eContext.dc;
|
|
|
|
|
|
|
|
if (dc) parts.push(`against DC ${dc}`);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Target info
|
|
|
|
|
|
|
|
if (pf2eContext.target) {
|
|
|
|
|
|
|
|
const targetName = pf2eContext.target?.token?.name
|
|
|
|
|
|
|
|
|| pf2eContext.target?.actor?.name
|
|
|
|
|
|
|
|
|| null;
|
|
|
|
|
|
|
|
if (targetName) parts.push(`targeting ${targetName}`);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Traits (e.g. "fire", "mental", "incapacitation")
|
|
|
|
|
|
|
|
if (pf2eContext.traits?.length > 0) {
|
|
|
|
|
|
|
|
parts.push(`(traits: ${pf2eContext.traits.join(", ")})`);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Modifiers/notes from the flavor text (often contains the breakdown)
|
|
|
|
|
|
|
|
// The flavor field in PF2E often has rich HTML with modifier breakdowns
|
|
|
|
|
|
|
|
// We'll grab a clean text version as supplemental info
|
|
|
|
|
|
|
|
if (flavor) {
|
|
|
|
|
|
|
|
const cleanFlavor = flavor.replace(/<[^>]*>/g, " ").replace(/\s{2,}/g, '. ').trim();
|
|
|
|
|
|
|
|
if (cleanFlavor && !parts.some(p => p.includes(cleanFlavor))) {
|
|
|
|
|
|
|
|
parts.push(`| ${cleanFlavor}`);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const rollString = `[ROLL] ${parts.join(" ")}`;
|
|
|
|
|
|
|
|
console.log(rollString);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
Hooks.on("chatMessage", async (chatLog, message, chatData) => {
|
|
|
|
Hooks.on("chatMessage", async (chatLog, message, chatData) => {
|
|
|
|
|
|
|
|
|
|
|
|
const formattedContent = chatData.speaker.actor ? `${chatData.speaker.alias} says, "${message}"` : message
|
|
|
|
const formattedContent = chatData.speaker.actor ? `${chatData.speaker.alias} says, "${message}"` : message
|
|
|
|
|