|
import { i18n } from "./util.js";
|
|
import { Avatar, ActorAvatar, TokenAvatar, CombatantAvatar } from "./consts.js";
|
|
|
|
const rgb2hex = (rgb) =>
|
|
`#${rgb
|
|
.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/)
|
|
.slice(1)
|
|
.map((n) => parseInt(n, 10).toString(16).padStart(2, "0"))
|
|
.join("")}`;
|
|
|
|
// second return value is whether the first value can be styled as pf2e-icon
|
|
function getActionGlyph(actionCost) {
|
|
if (actionCost === "1 to 3") return ["1 / 2 / 3", true];
|
|
if (actionCost === "1 or 2") return ["1 / 2", true];
|
|
if (actionCost === "2 or 3") return ["2 / 3", true];
|
|
if (actionCost.type === "action") {
|
|
return [actionCost.value, true];
|
|
} else if (actionCost.type === "reaction") return ["R", true];
|
|
else if (actionCost.type === "free") return ["F", true];
|
|
else if (actionCost.length === 1) return [actionCost, true];
|
|
else return [actionCost, false];
|
|
}
|
|
// Chat cards
|
|
Hooks.on("renderChatMessage", (chatMessage, html, messageData) => {
|
|
const isNarratorToolsMessage = chatMessage.flags["narrator-tools"];
|
|
const isMLDRoundMarker = chatMessage.flags["monks-little-details"]?.roundmarker;
|
|
const isMCDRoundMarker = chatMessage.flags["monks-combat-details"]?.roundmarker;
|
|
const isRoundMarker = isMLDRoundMarker || isMCDRoundMarker;
|
|
if (isNarratorToolsMessage || isRoundMarker) {
|
|
return;
|
|
}
|
|
|
|
if (game.settings.get("pf2e-dorako-ux", "moving.restructure-card-info")) {
|
|
let uuid = chatMessage?.flags?.pf2e?.origin?.uuid;
|
|
if (uuid) {
|
|
try {
|
|
let origin = fromUuidSync(uuid);
|
|
let actionCost = origin?.actionCost;
|
|
if (actionCost) injectActionCost(html, actionCost);
|
|
if (origin?.type === "spell") injectSpellInfo(html, origin);
|
|
} catch (error) {
|
|
// An error is thrown if the UUID is a reference to something that is not loaded, like an actor in a compendium.
|
|
}
|
|
}
|
|
}
|
|
|
|
injectSenderWrapper(html, messageData);
|
|
injectMessageTag(html, messageData);
|
|
injectWhisperParticipants(html, messageData);
|
|
injectAuthorName(html, messageData);
|
|
|
|
if (
|
|
(game.settings.get("pf2e-dorako-ux", "avatar.hide-when-token-hidden") &&
|
|
chatMessage.getFlag("pf2e-dorako-ux", "wasTokenHidden")) ||
|
|
(game.settings.get("pf2e-dorako-ux", "avatar.hide-gm-avatar-when-secret") && !chatMessage.isContentVisible)
|
|
) {
|
|
} else {
|
|
injectAvatar(html, getAvatar(chatMessage));
|
|
}
|
|
moveFlavorTextToContents(html);
|
|
});
|
|
|
|
// Is damage roll
|
|
Hooks.on("renderChatMessage", (chatMessage, html, messageData) => {
|
|
if (!game.settings.get("pf2e-dorako-ux", "hiding.remove-attack-info-from-damage-roll-messages")) return;
|
|
|
|
if (chatMessage?.isDamageRoll && chatMessage?.item?.type !== "spell") {
|
|
html[0].classList.add("dorako-damage-roll");
|
|
let flavor = html.find(".flavor-text");
|
|
flavor.each(function () {
|
|
$(this).contents().eq(1).wrap("<span/>");
|
|
});
|
|
}
|
|
});
|
|
|
|
// Is check roll
|
|
Hooks.on("renderChatMessage", (chatMessage, html, messageData) => {
|
|
if (!game.settings.get("pf2e-dorako-ux", "hiding.remove-attack-info-from-damage-roll-messages")) return;
|
|
|
|
if (chatMessage?.isCheckRoll) {
|
|
html.addClass("dorako-check-roll");
|
|
}
|
|
});
|
|
|
|
// "Be last" magic trick. Should ensure that any other modules that modify, say, who spoke the message, have done so before you add the flags.
|
|
Hooks.once("ready", () => {
|
|
Hooks.on("preCreateChatMessage", (message) => {
|
|
addAvatarsToFlags(message);
|
|
message.updateSource({
|
|
"flags.pf2e-dorako-ux.wasTokenHidden": message?.token?.hidden,
|
|
});
|
|
});
|
|
Hooks.on("updateChatMessage", (message) => {
|
|
addAvatarsToFlags(message);
|
|
});
|
|
|
|
Hooks.on("renderChatMessage", (app, html, data) => {
|
|
const isKoboldWorksTurnAnnouncerMessage = app.flags["koboldworks-turn-announcer"];
|
|
if (!isKoboldWorksTurnAnnouncerMessage) return;
|
|
|
|
const avatar = html.find(".portrait");
|
|
avatar.css("transform", `scale(${app.flags["pf2e-dorako-ux"]?.tokenAvatar.scale})`);
|
|
avatar.css("flex", `0px 0px var(--avatar-size)`);
|
|
avatar.css("height", `var(--avatar-size)`);
|
|
avatar.css("width", `var(--avatar-size)`);
|
|
});
|
|
});
|
|
|
|
function moveFlavorTextToContents(html) {
|
|
let flavor = html.find(".flavor-text")[0];
|
|
let contents = html.find(".message-content")[0];
|
|
if (flavor) contents.prepend(flavor);
|
|
}
|
|
|
|
function injectSenderWrapper(html, messageData) {
|
|
if (messageData.author === undefined) return;
|
|
var target = html.find(".message-sender")[0];
|
|
var wrapper = document.createElement("div");
|
|
wrapper.classList.add("sender-wrapper");
|
|
target.parentNode.insertBefore(wrapper, target);
|
|
wrapper.appendChild(target);
|
|
}
|
|
|
|
function injectAvatar(html, avatar) {
|
|
if (!avatar) return;
|
|
let messageHeader = html.find(".message-header")[0];
|
|
let portraitAndName = document.createElement("div");
|
|
portraitAndName.classList.add("portrait-and-name");
|
|
messageHeader.prepend(portraitAndName);
|
|
let wrapper = document.createElement("div");
|
|
wrapper.classList.add("portrait-wrapper");
|
|
let portrait = document.createElement("img");
|
|
portrait.classList.add("avatar");
|
|
portrait.classList.add("portrait");
|
|
wrapper.append(portrait);
|
|
let senderWrapper = html.find(".sender-wrapper")[0];
|
|
portraitAndName.append(senderWrapper);
|
|
portraitAndName.prepend(wrapper);
|
|
}
|
|
|
|
function injectActionCost(html, actionCost) {
|
|
if (!actionCost) return;
|
|
const [actionGlyph, shouldBeStyled] = getActionGlyph(actionCost);
|
|
if (!actionGlyph) return;
|
|
|
|
// console.log("Injecting actionGlyph %s", actionGlyph);
|
|
let messageHeader = html.find(".card-header")[0];
|
|
let actionGlyphText = document.createElement("h3");
|
|
html.find(".action-glyph")?.get(0).remove(); // Remove action-glyph added by system
|
|
if (shouldBeStyled) actionGlyphText.classList.add("pf2-icon");
|
|
actionGlyphText.textContent = actionGlyph;
|
|
messageHeader.append(actionGlyphText);
|
|
}
|
|
|
|
function localizeComponent(componentKey) {
|
|
if (componentKey === "focus") return i18n("PF2E.SpellComponentF");
|
|
if (componentKey === "material") return i18n("PF2E.SpellComponentM");
|
|
if (componentKey === "somatic") return i18n("PF2E.SpellComponentS");
|
|
if (componentKey === "verbal") return i18n("PF2E.SpellComponentV");
|
|
}
|
|
|
|
function spellComponentsToText(components) {
|
|
// console.log(components);
|
|
const asArray = Object.entries(components);
|
|
// console.log(asArray);
|
|
const filtered = asArray.filter(([key, value]) => value);
|
|
// console.log(filtered);
|
|
const localized = filtered.map(([key, value]) => localizeComponent(key));
|
|
// console.log(localized);
|
|
const combined = localized.join(", ");
|
|
return " " + combined.toLowerCase();
|
|
}
|
|
|
|
function injectSpellInfo(html, spell) {
|
|
if (!spell) return;
|
|
let messageHeader = html.find(".card-content")[0];
|
|
let spellInfo = document.createElement("div");
|
|
spellInfo.classList.add("spell-info");
|
|
// console.log(spell);
|
|
|
|
// Cast time + components
|
|
let time = spell?.system?.time?.value;
|
|
let components = spell?.system?.components;
|
|
let elem = document.createElement("p");
|
|
let castInfoLabel = document.createElement("strong");
|
|
castInfoLabel.textContent = i18n("PF2E.CastLabel") + " ";
|
|
let castTime = document.createElement("span");
|
|
const [actionCost, shouldBeGlyph] = getActionGlyph(time);
|
|
castTime.textContent = actionCost;
|
|
if (shouldBeGlyph) castTime.classList.add("pf2-icon");
|
|
let castComponents = document.createElement("span");
|
|
// castComponents.textContent = spellComponentsToText(components);
|
|
elem.append(castInfoLabel);
|
|
elem.append(castTime);
|
|
// elem.append(castComponents);
|
|
spellInfo.append(elem);
|
|
|
|
// Cost info (note: not cast time, material cost)
|
|
let cost = spell?.system?.cost?.value;
|
|
if (cost) {
|
|
let elem = document.createElement("p");
|
|
let label = document.createElement("strong");
|
|
label.textContent = i18n("PF2E.SpellCostLabel") + " ";
|
|
let value = document.createElement("span");
|
|
value.textContent = cost;
|
|
elem.append(label);
|
|
elem.append(value);
|
|
spellInfo.append(elem);
|
|
}
|
|
|
|
let secondarycasters = spell?.system?.secondarycasters?.value;
|
|
if (secondarycasters) {
|
|
let info = document.createElement("p");
|
|
let label = document.createElement("strong");
|
|
label.textContent = i18n("PF2E.SpellSecondaryCasters") + " ";
|
|
let value = document.createElement("span");
|
|
value.textContent = secondarycasters;
|
|
info.append(label);
|
|
info.append(value);
|
|
spellInfo.append(info);
|
|
}
|
|
|
|
let primarycheck = spell?.system?.primarycheck?.value;
|
|
if (primarycheck) {
|
|
let info = document.createElement("p");
|
|
let label = document.createElement("strong");
|
|
label.textContent = i18n("PF2E.SpellPrimaryCheckLabel") + " ";
|
|
let value = document.createElement("span");
|
|
value.textContent = primarycheck;
|
|
info.append(label);
|
|
info.append(value);
|
|
spellInfo.append(info);
|
|
}
|
|
|
|
let secondarycheck = spell?.system?.secondarycheck?.value;
|
|
if (secondarycheck) {
|
|
let info = document.createElement("p");
|
|
let label = document.createElement("strong");
|
|
label.textContent = i18n("PF2E.SpellSecondaryChecksLabel") + " ";
|
|
let value = document.createElement("span");
|
|
value.textContent = secondarycheck;
|
|
info.append(label);
|
|
info.append(value);
|
|
spellInfo.append(info);
|
|
}
|
|
|
|
// Target info
|
|
let target = spell?.system?.target?.value;
|
|
if (target) {
|
|
// console.log(target);
|
|
let targetInfo = document.createElement("p");
|
|
let targetInfoLabel = document.createElement("strong");
|
|
targetInfoLabel.textContent = i18n("PF2E.SpellTargetLabel") + " ";
|
|
let targetValue = document.createElement("span");
|
|
targetValue.textContent = target;
|
|
targetInfo.append(targetInfoLabel);
|
|
targetInfo.append(targetValue);
|
|
spellInfo.append(targetInfo);
|
|
}
|
|
|
|
// Range info
|
|
let range = spell?.system?.range?.value;
|
|
if (range) {
|
|
// console.log(range);
|
|
let rangeInfo = document.createElement("p");
|
|
let rangeInfoLabel = document.createElement("strong");
|
|
rangeInfoLabel.textContent = i18n("PF2E.SpellRangeLabel") + " ";
|
|
let rangeValue = document.createElement("span");
|
|
rangeValue.textContent = range;
|
|
rangeInfo.append(rangeInfoLabel);
|
|
rangeInfo.append(rangeValue);
|
|
spellInfo.append(rangeInfo);
|
|
}
|
|
|
|
// Area info
|
|
let area = spell?.system?.area?.value;
|
|
if (area) {
|
|
// console.log(area);
|
|
let areaInfo = document.createElement("p");
|
|
let areaInfoLabel = document.createElement("strong");
|
|
areaInfoLabel.textContent = i18n("PF2E.AreaLabel") + " ";
|
|
let areaType = spell?.system?.area?.type;
|
|
let areaTypeLabel = areaType
|
|
? i18n("PF2E.AreaType" + areaType.charAt(0).toUpperCase() + areaType.slice(1)).toLowerCase()
|
|
: "";
|
|
let areaValue = document.createElement("span");
|
|
areaValue.textContent = area + " " + i18n("PF2E.Foot").toLowerCase() + " " + areaTypeLabel;
|
|
areaInfo.append(areaInfoLabel);
|
|
areaInfo.append(areaValue);
|
|
spellInfo.append(areaInfo);
|
|
}
|
|
|
|
// Duration info
|
|
let duration = spell?.system?.duration?.value;
|
|
if (duration) {
|
|
// console.log(duration);
|
|
let durationInfo = document.createElement("p");
|
|
let durationInfoLabel = document.createElement("strong");
|
|
durationInfoLabel.textContent = i18n("PF2E.SpellDurationLabel") + " ";
|
|
let durationValue = document.createElement("span");
|
|
durationValue.textContent = duration;
|
|
durationInfo.append(durationInfoLabel);
|
|
durationInfo.append(durationValue);
|
|
spellInfo.append(durationInfo);
|
|
}
|
|
|
|
let hr = document.createElement("hr");
|
|
|
|
// Heightening info
|
|
let spellRightInfo = html.find(".card-header").find("h4")[0];
|
|
let originalText = spellRightInfo.textContent;
|
|
const [_, spellType, parsedLevel] = originalText.split(/(.*) (\d+)/);
|
|
|
|
const baseLevel = spell?.baseLevel;
|
|
const actualLevel = spell?.level;
|
|
if (baseLevel != parsedLevel) {
|
|
let heighteningInfo = document.createElement("h4");
|
|
let spellTypeSpan = document.createElement("span");
|
|
spellTypeSpan.textContent = spellType + " ";
|
|
|
|
let originalLevel = document.createElement("s");
|
|
originalLevel.textContent = baseLevel;
|
|
|
|
let heightenedLevel = document.createElement("span");
|
|
heightenedLevel.classList.add("heightened");
|
|
heightenedLevel.textContent = " " + parsedLevel;
|
|
|
|
heighteningInfo.append(spellTypeSpan);
|
|
heighteningInfo.append(originalLevel);
|
|
heighteningInfo.append(heightenedLevel);
|
|
|
|
spellRightInfo.parentNode.replaceChild(heighteningInfo, spellRightInfo);
|
|
}
|
|
|
|
// Footer
|
|
let footer = html.find(".card-footer")[0];
|
|
if (footer) footer.classList.add("dorako-display-none");
|
|
|
|
messageHeader.prepend(hr);
|
|
messageHeader.prepend(spellInfo);
|
|
}
|
|
|
|
function injectAuthorName(html, messageData) {
|
|
if (messageData.author === undefined) return;
|
|
if (game.settings.get("pf2e-dorako-ux", "other.enable-player-tags")) {
|
|
const messageSenderElem = html.find(".sender-wrapper");
|
|
const playerName = messageData.author.name;
|
|
const playerNameElem = document.createElement("span");
|
|
playerNameElem.appendChild(document.createTextNode(playerName));
|
|
playerNameElem.classList.add("player-name");
|
|
playerNameElem.classList.add("header-meta");
|
|
if (playerName === messageData.alias) {
|
|
html.find(".message-sender").addClass("dorako-display-none");
|
|
}
|
|
messageSenderElem.append(playerNameElem);
|
|
}
|
|
}
|
|
|
|
function injectMessageTag(html, messageData) {
|
|
const setting = game.settings.get("pf2e-dorako-ux", "other.enable-rolltype-indication");
|
|
if (setting == false) {
|
|
return;
|
|
}
|
|
const messageMetadata = html.find(".message-metadata");
|
|
|
|
const rolltype = $("<span>");
|
|
rolltype.addClass("rolltype");
|
|
rolltype.addClass("header-meta");
|
|
|
|
const whisperTargets = messageData.message.whisper;
|
|
|
|
const isBlind = messageData.message.blind;
|
|
if (isBlind) rolltype.addClass("blind");
|
|
const isWhisper = whisperTargets?.length > 0;
|
|
if (isWhisper && !isBlind) rolltype.addClass("whisper");
|
|
const isSelf = isWhisper && whisperTargets.length === 1 && whisperTargets[0] === messageData.message.user;
|
|
const isRoll = messageData.message.rolls !== undefined;
|
|
|
|
if (isBlind) {
|
|
rolltype.text(i18n("pf2e-dorako-ux.text.secret"));
|
|
messageMetadata.prepend(rolltype);
|
|
} else if (isSelf && whisperTargets[0]) {
|
|
rolltype.text(i18n("pf2e-dorako-ux.text.self-roll"));
|
|
messageMetadata.prepend(rolltype);
|
|
} else if (isRoll && isWhisper) {
|
|
rolltype.text(i18n("pf2e-dorako-ux.text.gm-only"));
|
|
messageMetadata.prepend(rolltype);
|
|
} else if (isWhisper) {
|
|
rolltype.text(i18n("pf2e-dorako-ux.text.whisper"));
|
|
messageMetadata.prepend(rolltype);
|
|
}
|
|
|
|
if (game.settings.get("pf2e-dorako-ux", "moving.animate-messages")) {
|
|
// Draw attention to direct whispers from players to GM
|
|
const isGmSpeaker = game.users.get(messageData.message.user)?.isGM;
|
|
const isGmTarget = game.users.get(whisperTargets?.[0])?.isGM;
|
|
if (!(isBlind || isSelf) && isWhisper && !isGmSpeaker && isGmTarget) {
|
|
html[0].classList.add("attention");
|
|
}
|
|
}
|
|
}
|
|
|
|
function injectWhisperParticipants(html, messageData) {
|
|
const alias = messageData.alias;
|
|
const author = messageData.author;
|
|
const whisperTargets = messageData.message.whisper;
|
|
const whisperTargetString = messageData.whisperTo;
|
|
const whisperTargetIds = messageData.message.whisper;
|
|
const isWhisper = whisperTargetIds?.length > 0 || false;
|
|
const isRoll = messageData.message.rolls !== undefined;
|
|
const isSelf =
|
|
(isWhisper && whisperTargets.length === 1 && whisperTargets[0] === messageData.message.user) ||
|
|
(isWhisper &&
|
|
whisperTargets.length === 2 &&
|
|
whisperTargets[0] === "null" &&
|
|
whisperTargets[1] === messageData.message.user);
|
|
|
|
const authorId = messageData.message.user;
|
|
const userId = game.user.id;
|
|
|
|
if (!isWhisper) return;
|
|
if (userId !== authorId && !whisperTargetIds.includes(userId)) return;
|
|
|
|
// remove the old whisper to content, if it exists
|
|
html.find(".whisper-to").detach();
|
|
|
|
// if this is a roll
|
|
if (isRoll || isSelf) return;
|
|
|
|
const messageHeader = html.find(".message-header");
|
|
|
|
const whisperParticipants = $("<span>");
|
|
whisperParticipants.addClass("whisper-to");
|
|
|
|
const whisperFrom = $("<span>");
|
|
const fromText = titleCase(i18n("pf2e-dorako-ux.text.from"));
|
|
whisperFrom.text(`${fromText}: ${alias}`);
|
|
whisperFrom.addClass("header-meta");
|
|
|
|
const whisperTo = $("<span>");
|
|
const toText = titleCase(i18n("pf2e-dorako-ux.text.to")).toLowerCase();
|
|
whisperTo.text(`${toText}: ${whisperTargetString}`);
|
|
whisperTo.addClass("header-meta");
|
|
|
|
whisperParticipants.append(whisperFrom);
|
|
whisperParticipants.append(whisperTo);
|
|
messageHeader.append(whisperParticipants);
|
|
}
|
|
|
|
function addAvatarsToFlags(message) {
|
|
let combatantImg =
|
|
game.modules.get("combat-tracker-images")?.active && message.actor
|
|
? message.actor.getFlag("combat-tracker-images", "trackerImage")
|
|
: null;
|
|
let speaker = message.speaker;
|
|
const token = game.scenes.get(speaker.scene)?.tokens.get(speaker.token);
|
|
let tokenImg = token?.texture.src;
|
|
const actor = game.actors.get(speaker.actor);
|
|
let actorImg = actor?.img;
|
|
let userImg = message.user?.avatar;
|
|
|
|
let userAvatar = new Avatar(message.speaker.alias, userImg);
|
|
|
|
let combatantAvatar = combatantImg ? new CombatantAvatar(message.speaker.alias, combatantImg) : null;
|
|
|
|
let actorAvatar = actorImg ? new ActorAvatar(message.speaker.alias, actorImg) : null;
|
|
|
|
let tokenAvatar = null;
|
|
if (tokenImg) {
|
|
tokenAvatar = new TokenAvatar(message.speaker.alias, tokenImg, token.texture.scaleX, actor.size == "sm");
|
|
}
|
|
|
|
message.updateSource({
|
|
"flags.pf2e-dorako-ux.userAvatar": userAvatar,
|
|
"flags.pf2e-dorako-ux.combatantAvatar": combatantAvatar,
|
|
"flags.pf2e-dorako-ux.tokenAvatar": tokenAvatar,
|
|
"flags.pf2e-dorako-ux.actorAvatar": actorAvatar,
|
|
});
|
|
}
|
|
|
|
function getAvatar(message) {
|
|
const source = game.settings.get("pf2e-dorako-ux", "avatar.source");
|
|
if (source == "none") {
|
|
return null;
|
|
}
|
|
|
|
let combatantAvatar = message.getFlag("pf2e-dorako-ux", "combatantAvatar");
|
|
let tokenAvatar = message.getFlag("pf2e-dorako-ux", "tokenAvatar");
|
|
let actorAvatar = message.getFlag("pf2e-dorako-ux", "actorAvatar");
|
|
let userAvatar = game.settings.get("pf2e-dorako-ux", "avatar.use-user-avatar")
|
|
? message.getFlag("pf2e-dorako-ux", "userAvatar")
|
|
: null;
|
|
|
|
if (combatantAvatar) return combatantAvatar;
|
|
|
|
if (
|
|
game.settings.get("pf2e-dorako-ux", "avatar.hide-when-token-hidden") &&
|
|
message.getFlag("pf2e-dorako-ux", "wasTokenHidden")
|
|
) {
|
|
return null;
|
|
}
|
|
|
|
return source == "token" ? tokenAvatar || actorAvatar || userAvatar : actorAvatar || tokenAvatar || userAvatar;
|
|
}
|
|
|
|
// Add avatar if message contains avatar data
|
|
Hooks.on("renderChatMessage", (message, b) => {
|
|
let avatar = getAvatar(message);
|
|
if (!avatar) return;
|
|
let html = b[0];
|
|
|
|
let avatarElem = html.getElementsByClassName("avatar")[0];
|
|
if (!avatarElem) return;
|
|
|
|
avatarElem.src = avatar.image;
|
|
|
|
if (avatar.type == "token") {
|
|
const smallScale = game.settings.get("pf2e-dorako-ux", "avatar.small-creature-token-avatar-size");
|
|
let smallCorrection = avatar.isSmall ? 1.25 * smallScale : 1;
|
|
avatarElem?.setAttribute("style", "transform: scale(" + Math.abs(avatar.scale) * smallCorrection + ")");
|
|
}
|
|
|
|
const portraitDegreeSetting = game.settings.get("pf2e-dorako-ux", "avatar.reacts-to-degree-of-success");
|
|
|
|
if (portraitDegreeSetting && message.isContentVisible) {
|
|
let outcome = message?.flags?.pf2e?.context?.outcome;
|
|
if (outcome === undefined) return;
|
|
if (outcome === "criticalFailure") {
|
|
let wrapper = html.getElementsByClassName("portrait-wrapper")[0];
|
|
wrapper?.setAttribute("style", "filter: saturate(0.2) drop-shadow(0px 0px 6px black)");
|
|
} else if (outcome === "criticalSuccess") {
|
|
let wrapper = html.getElementsByClassName("portrait-wrapper")[0];
|
|
wrapper?.setAttribute("style", "filter: drop-shadow(0px 0px 6px lightgreen)");
|
|
}
|
|
}
|
|
});
|
|
|
|
// Add .spell to spells
|
|
Hooks.on("renderChatMessage", (app, html, data) => {
|
|
const item = app?.item;
|
|
if (!item) return;
|
|
if (!item.constructor.name.includes("SpellPF2e")) return;
|
|
html[0].classList.add("spell");
|
|
});
|