Add mana+AP reset buttons and conditions

This commit is contained in:
2026-03-10 17:56:13 +01:00
parent 7944345c82
commit bf9ad37d24
16 changed files with 668 additions and 2 deletions
+180
View File
@@ -2163,6 +2163,116 @@ i.prismrpg {
.prismrpg .racial-ability-content input[type="checkbox"]:checked::after {
color: rgba(0, 0, 0, 0.1);
}
.prismrpg .ability-content {
font-family: var(--font-primary);
font-size: calc(var(--font-size-standard) * 1);
color: var(--color-dark-1);
background-image: var(--background-image-base);
background-repeat: no-repeat;
background-size: 100% 100%;
overflow: auto;
}
.prismrpg .ability-content nav.tabs [data-tab] {
color: #636060;
}
.prismrpg .ability-content nav.tabs [data-tab].active {
color: #252424;
}
.prismrpg .ability-content input:disabled,
.prismrpg .ability-content select:disabled {
background-color: rgba(0, 0, 0, 0.2);
border-color: transparent;
color: var(--color-dark-3);
}
.prismrpg .ability-content input,
.prismrpg .ability-content select {
height: 1.5rem;
background-color: rgba(0, 0, 0, 0.1);
border-color: var(--color-dark-6);
color: var(--color-dark-2);
}
.prismrpg .ability-content input[name="name"] {
height: 2.5rem;
margin-right: 4px;
font-family: var(--font-secondary);
font-size: calc(var(--font-size-standard) * 1.2);
font-weight: bold;
border: none;
}
.prismrpg .ability-content fieldset {
margin-bottom: 4px;
border-radius: 4px;
}
.prismrpg .ability-content .form-fields input,
.prismrpg .ability-content .form-fields select {
text-align: center;
font-size: calc(var(--font-size-standard) * 1);
}
.prismrpg .ability-content .form-fields select {
font-family: var(--font-secondary);
font-size: calc(var(--font-size-standard) * 1);
}
.prismrpg .ability-content legend {
font-family: var(--font-secondary);
font-size: calc(var(--font-size-standard) * 1.2);
font-weight: bold;
letter-spacing: 1px;
}
.prismrpg .ability-content .form-fields {
padding-top: 4px;
}
.prismrpg .ability-content .form-group {
display: flex;
flex: 1;
flex-direction: row;
}
.prismrpg .ability-content .form-group label {
align-content: center;
min-width: 10rem;
max-width: 10rem;
}
.prismrpg .ability-content .form-group select,
.prismrpg .ability-content .form-group input {
text-align: left;
min-width: 12rem;
max-width: 12rem;
}
.prismrpg .ability-content .form-group input[type="checkbox"] {
min-width: 1.2rem;
max-width: 1.2rem;
margin-right: 0.5rem;
}
.prismrpg .ability-content label {
font-family: var(--font-secondary);
font-size: calc(var(--font-size-standard) * 1);
flex: 50%;
}
.prismrpg .ability-content .align-top {
align-self: flex-start;
padding: 0.1rem;
margin-right: 0.2rem;
}
.prismrpg .ability-content .shift-right {
margin-left: 2rem;
}
.prismrpg .ability-content .header {
display: flex;
}
.prismrpg .ability-content .header img {
width: 50px;
height: 50px;
}
.prismrpg .ability-content input[type="checkbox"] {
font-size: var(--font-size-14);
width: 20px;
padding-top: 0;
}
.prismrpg .ability-content input[type="checkbox"]:checked {
background-color: rgba(0, 0, 0, 0.1);
}
.prismrpg .ability-content input[type="checkbox"]:checked::after {
color: rgba(0, 0, 0, 0.1);
}
.prismrpg .weapon-content {
font-family: var(--font-primary);
font-size: calc(var(--font-size-standard) * 1);
@@ -4317,6 +4427,76 @@ i.prismrpg {
.chat-log .message-content .prismrpg-item-chat-card.equipment-item .item-header {
background: linear-gradient(135deg, #f39c12 0%, #b8730f 100%);
}
.new-round-message .chat-title .new-round-label {
font-size: var(--font-size-11);
font-style: italic;
color: #8a7a5a;
margin-top: 1px;
}
.new-round-message .new-round-actors {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 6px;
padding: 6px 0 2px;
}
.new-round-message .new-round-actors .new-round-restore-btn {
display: flex;
align-items: center;
gap: 6px;
width: calc(50% - 3px);
padding: 4px 8px 4px 6px;
border: 1px solid #7a6a45;
border-radius: 4px;
background: linear-gradient(135deg, #f5e6c8 0%, #e8d5a0 100%);
cursor: pointer;
font-size: var(--font-size-13);
color: #3a2e1a;
overflow: hidden;
}
.new-round-message .new-round-actors .new-round-restore-btn span {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
min-width: 0;
}
.new-round-message .new-round-actors .new-round-restore-btn img {
flex-shrink: 0;
width: 24px;
height: 24px;
border: none;
border-radius: 3px;
object-fit: cover;
}
.new-round-message .new-round-actors .new-round-restore-btn:hover:not(:disabled) {
background: linear-gradient(135deg, #fdf3dc 0%, #f0e0b0 100%);
border-color: #a08040;
}
.new-round-message .new-round-actors .new-round-restore-btn:disabled,
.new-round-message .new-round-actors .new-round-restore-btn.restored {
opacity: 0.45;
cursor: default;
}
.new-round-message .new-round-actors .new-round-all-btn {
width: 100%;
background: linear-gradient(135deg, #c8dff5 0%, #a0c0e8 100%);
border-color: #4a6a8a;
color: #1a2e3a;
font-weight: bold;
}
.new-round-message .new-round-actors .new-round-all-btn:hover:not(:disabled) {
background: linear-gradient(135deg, #dcedfc 0%, #b0d0f0 100%);
border-color: #3a5a7a;
}
.palette.status-effects .status-separator {
grid-column: 1 / -1;
width: 100%;
height: 2px;
border: none;
border-top: 2px solid rgba(255, 255, 255, 0.35);
margin: 5px 0;
position: relative;
}
.application.dialog.prismrpg {
color: var(--color-dark-1);
}
+83
View File
@@ -382,6 +382,88 @@
}
}
},
"Ability": {
"FIELDS": {
"description": {
"label": "Description"
}
}
},
"Combat": {
"newRound": "New Round —",
"restoreAP": "Reset AP & +1 Mana",
"allPC": "All PC"
},
"Status": {
"Aided": "Aided",
"Alert": "Alert",
"Alkalized": "Alkalized",
"Anchored": "Anchored",
"Banished": "Banished",
"Bestowed": "Bestowed",
"Blessed": "Blessed",
"Bleed": "Bleed",
"Blind": "Blind",
"Burning": "Burning",
"Chilled": "Chilled",
"Comatose": "Comatose",
"Compulsed": "Compulsed",
"Concealed": "Concealed",
"Corroded": "Corroded",
"Cursed": "Cursed",
"Dazed": "Dazed",
"Deaf": "Deaf",
"Diseased": "Diseased",
"Distracted": "Distracted",
"Enchanted": "Enchanted",
"Enhance": "Enhance",
"Exhaustion": "Exhaustion",
"Fatigue": "Fatigue",
"Frightened": "Frightened",
"Fury": "Fury",
"Haste": "Haste",
"Heroism": "Heroism",
"Horror": "Horror",
"Inspired": "Inspired",
"Invisible": "Invisible",
"Keen": "Keen",
"LifeDrain": "Life Drain",
"Locked": "Locked",
"Madness": "Madness",
"ManaDrain": "Mana Drain",
"Marked": "Marked",
"Mute": "Mute",
"Necrosis": "Necrosis (Elemental)",
"Numbed": "Numbed",
"Paralyzed": "Paralyzed",
"Petrified": "Petrified",
"Plagued": "Plagued",
"Poison": "Poison",
"Prepared": "Prepared",
"Prone": "Prone",
"Radiated": "Radiated (Elemental)",
"Rage": "Rage",
"Regeneration": "Regeneration",
"Reinforced": "Reinforced",
"Renewed": "Renewed",
"Saturated": "Saturated",
"Sealed": "Sealed",
"Seep": "Seep",
"Shattered": "Shattered",
"Shocked": "Shocked (Elemental)",
"Sightless": "Sightless",
"Silenced": "Silenced",
"Soundless": "Soundless",
"Staggered": "Staggered",
"Stunned": "Stunned",
"Supplied": "Supplied",
"Surged": "Surged",
"Taunt": "Taunt",
"Trance": "Trance",
"Unconscious": "Unconscious",
"Warded": "Warded",
"Wounded": "Wounded"
},
"Label": {
"agility": "Dexterity",
"gotoToken": "Go to token",
@@ -1412,6 +1494,7 @@
"armor": "Armor",
"equipment": "Equipment",
"racial-ability": "Racial Ability",
"ability": "Ability",
"miracle": "Miracle",
"save": "Save",
"shield": "Shield",
+1
View File
@@ -3,6 +3,7 @@ export { default as PrismRPGMonsterSheet } from "./sheets/monster-sheet.mjs"
export { default as PrismRPGWeaponSheet } from "./sheets/weapon-sheet.mjs"
export { default as PrismRPGSkillSheet } from "./sheets/skill-sheet.mjs"
export { default as PrismRPGRacialAbilitySheet } from "./sheets/racial-ability-sheet.mjs"
export { default as PrismRPGAbilitySheet } from "./sheets/ability-sheet.mjs"
export { default as PrismRPGVulnerabilitySheet } from "./sheets/vulnerability-sheet.mjs"
export { default as PrismRPGArmorSheet } from "./sheets/armor-sheet.mjs"
export { default as PrismRPGSpellSheet } from "./sheets/spell-sheet.mjs"
@@ -0,0 +1,50 @@
import PrismRPGItemSheet from "./base-item-sheet.mjs"
export default class PrismRPGAbilitySheet extends PrismRPGItemSheet {
/** @override */
static DEFAULT_OPTIONS = {
classes: ["ability"],
position: {
width: 600,
},
window: {
contentClasses: ["ability-content"],
},
}
/** @override */
static PARTS = {
main: {
template: "systems/fvtt-prism-rpg/templates/ability.hbs",
},
}
/** @override */
tabGroups = {
primary: "description",
}
/**
* Prepare an array of form header tabs.
* @returns {Record<string, Partial<ApplicationTab>>}
*/
#getTabs() {
const tabs = {
description: { id: "description", group: "primary", label: "PRISMRPG.Label.description" },
effects: { id: "effects", group: "primary", label: "PRISMRPG.Label.effects" },
}
for (const v of Object.values(tabs)) {
v.active = this.tabGroups[v.group] === v.id
v.cssClass = v.active ? "active" : ""
}
return tabs
}
/** @override */
async _prepareContext() {
const context = await super._prepareContext()
context.tabs = this.#getTabs()
return context
}
}
+89
View File
@@ -0,0 +1,89 @@
/**
* Afflictions — negative status effects (Mundane & Magic)
* @type {StatusEffectConfig[]}
*/
export const AFFLICTIONS = [
// ── Mundane ────────────────────────────────────────────────────────────────
{ id: "aff-alkalized", name: "PRISMRPG.Status.Alkalized", icon: "icons/svg/acid.svg", category: "affliction", typing: "mundane" },
{ id: "aff-bleed", name: "PRISMRPG.Status.Bleed", icon: "icons/svg/blood.svg", category: "affliction", typing: "mundane" },
{ id: "aff-blind", name: "PRISMRPG.Status.Blind", icon: "icons/svg/blind.svg", category: "affliction", typing: "mundane" },
{ id: "aff-deaf", name: "PRISMRPG.Status.Deaf", icon: "icons/svg/deaf.svg", category: "affliction", typing: "mundane" },
{ id: "aff-diseased", name: "PRISMRPG.Status.Diseased", icon: "icons/svg/biohazard.svg", category: "affliction", typing: "mundane" },
{ id: "aff-distracted", name: "PRISMRPG.Status.Distracted", icon: "icons/svg/daze.svg", category: "affliction", typing: "mundane" },
{ id: "aff-exhaustion", name: "PRISMRPG.Status.Exhaustion", icon: "icons/svg/sleep.svg", category: "affliction", typing: "mundane" },
{ id: "aff-frightened", name: "PRISMRPG.Status.Frightened", icon: "icons/svg/terror.svg", category: "affliction", typing: "mundane" },
{ id: "aff-marked", name: "PRISMRPG.Status.Marked", icon: "icons/svg/target.svg", category: "affliction", typing: "both" },
{ id: "aff-mute", name: "PRISMRPG.Status.Mute", icon: "icons/svg/silenced.svg", category: "affliction", typing: "mundane" },
{ id: "aff-paralyzed", name: "PRISMRPG.Status.Paralyzed", icon: "icons/svg/paralysis.svg", category: "affliction", typing: "mundane" },
{ id: "aff-petrified", name: "PRISMRPG.Status.Petrified", icon: "icons/svg/frozen.svg", category: "affliction", typing: "mundane" },
{ id: "aff-poison", name: "PRISMRPG.Status.Poison", icon: "icons/svg/poison.svg", category: "affliction", typing: "mundane" },
{ id: "aff-prone", name: "PRISMRPG.Status.Prone", icon: "icons/svg/falling.svg", category: "affliction", typing: "mundane" },
{ id: "aff-rage", name: "PRISMRPG.Status.Rage", icon: "icons/svg/fire.svg", category: "affliction", typing: "mundane" },
{ id: "aff-sealed", name: "PRISMRPG.Status.Sealed", icon: "icons/svg/net.svg", category: "affliction", typing: "mundane" },
{ id: "aff-staggered", name: "PRISMRPG.Status.Staggered", icon: "icons/svg/daze.svg", category: "affliction", typing: "mundane" },
{ id: "aff-stunned", name: "PRISMRPG.Status.Stunned", icon: "icons/svg/stun.svg", category: "affliction", typing: "mundane" },
{ id: "aff-taunt", name: "PRISMRPG.Status.Taunt", icon: "icons/svg/eye.svg", category: "affliction", typing: "mundane" },
{ id: "aff-unconscious",name: "PRISMRPG.Status.Unconscious",icon: "icons/svg/unconscious.svg", category: "affliction", typing: "mundane" },
{ id: "aff-wounded", name: "PRISMRPG.Status.Wounded", icon: "icons/svg/degen.svg", category: "affliction", typing: "mundane" },
// ── Magic ──────────────────────────────────────────────────────────────────
{ id: "aff-banished", name: "PRISMRPG.Status.Banished", icon: "icons/svg/wing.svg", category: "affliction", typing: "magic" },
{ id: "aff-seep", name: "PRISMRPG.Status.Seep", icon: "icons/svg/acid.svg", category: "affliction", typing: "magic" },
{ id: "aff-sightless", name: "PRISMRPG.Status.Sightless", icon: "icons/svg/blind.svg", category: "affliction", typing: "magic" },
{ id: "aff-cursed", name: "PRISMRPG.Status.Cursed", icon: "icons/svg/sun.svg", category: "affliction", typing: "magic" },
{ id: "aff-soundless", name: "PRISMRPG.Status.Soundless", icon: "icons/svg/deaf.svg", category: "affliction", typing: "magic" },
{ id: "aff-plagued", name: "PRISMRPG.Status.Plagued", icon: "icons/svg/biohazard.svg", category: "affliction", typing: "magic" },
{ id: "aff-compulsed", name: "PRISMRPG.Status.Compulsed", icon: "icons/svg/eye.svg", category: "affliction", typing: "magic" },
{ id: "aff-fatigue", name: "PRISMRPG.Status.Fatigue", icon: "icons/svg/sleep.svg", category: "affliction", typing: "magic" },
{ id: "aff-horror", name: "PRISMRPG.Status.Horror", icon: "icons/svg/terror.svg", category: "affliction", typing: "magic" },
{ id: "aff-madness", name: "PRISMRPG.Status.Madness", icon: "icons/svg/daze.svg", category: "affliction", typing: "magic" },
{ id: "aff-silenced", name: "PRISMRPG.Status.Silenced", icon: "icons/svg/silenced.svg", category: "affliction", typing: "magic" },
{ id: "aff-locked", name: "PRISMRPG.Status.Locked", icon: "icons/svg/net.svg", category: "affliction", typing: "magic" },
{ id: "aff-dazed", name: "PRISMRPG.Status.Dazed", icon: "icons/svg/daze.svg", category: "affliction", typing: "magic" },
{ id: "aff-numbed", name: "PRISMRPG.Status.Numbed", icon: "icons/svg/frozen.svg", category: "affliction", typing: "magic" },
{ id: "aff-comatose", name: "PRISMRPG.Status.Comatose", icon: "icons/svg/unconscious.svg", category: "affliction", typing: "magic" },
{ id: "aff-shattered", name: "PRISMRPG.Status.Shattered", icon: "icons/svg/blood.svg", category: "affliction", typing: "magic" },
// ── Elemental (Magic) ──────────────────────────────────────────────────────
{ id: "aff-burning", name: "PRISMRPG.Status.Burning", icon: "icons/svg/fire.svg", category: "affliction", typing: "magic" },
{ id: "aff-chilled", name: "PRISMRPG.Status.Chilled", icon: "icons/svg/frozen.svg", category: "affliction", typing: "magic" },
{ id: "aff-corroded", name: "PRISMRPG.Status.Corroded", icon: "icons/svg/acid.svg", category: "affliction", typing: "magic" },
{ id: "aff-necrosis", name: "PRISMRPG.Status.Necrosis", icon: "icons/svg/degen.svg", category: "affliction", typing: "magic" },
{ id: "aff-radiated", name: "PRISMRPG.Status.Radiated", icon: "icons/svg/lightning.svg", category: "affliction", typing: "magic" },
{ id: "aff-shocked", name: "PRISMRPG.Status.Shocked", icon: "icons/svg/lightning.svg", category: "affliction", typing: "magic" },
]
/**
* Imbuements — positive status effects (Mundane & Magic)
* @type {StatusEffectConfig[]}
*/
export const IMBUEMENTS = [
// ── Mundane ────────────────────────────────────────────────────────────────
{ id: "imb-aided", name: "PRISMRPG.Status.Aided", icon: "icons/svg/upgrade.svg", category: "imbuement", typing: "mundane" },
{ id: "imb-alert", name: "PRISMRPG.Status.Alert", icon: "icons/svg/eye.svg", category: "imbuement", typing: "mundane" },
{ id: "imb-alkalized", name: "PRISMRPG.Status.Alkalized", icon: "icons/svg/acid.svg", category: "imbuement", typing: "mundane" },
{ id: "imb-bestowed", name: "PRISMRPG.Status.Bestowed", icon: "icons/svg/angel.svg", category: "imbuement", typing: "both" },
{ id: "imb-concealed", name: "PRISMRPG.Status.Concealed", icon: "icons/svg/invisible.svg", category: "imbuement", typing: "mundane" },
{ id: "imb-enhance", name: "PRISMRPG.Status.Enhance", icon: "icons/svg/upgrade.svg", category: "imbuement", typing: "mundane" },
{ id: "imb-inspired", name: "PRISMRPG.Status.Inspired", icon: "icons/svg/regen.svg", category: "imbuement", typing: "mundane" },
{ id: "imb-keen", name: "PRISMRPG.Status.Keen", icon: "icons/svg/eye.svg", category: "imbuement", typing: "mundane" },
{ id: "imb-life-drain", name: "PRISMRPG.Status.LifeDrain", icon: "icons/svg/regen.svg", category: "imbuement", typing: "mundane" },
{ id: "imb-madness", name: "PRISMRPG.Status.Madness", icon: "icons/svg/daze.svg", category: "imbuement", typing: "mundane" },
{ id: "imb-prepared", name: "PRISMRPG.Status.Prepared", icon: "icons/svg/upgrade.svg", category: "imbuement", typing: "mundane" },
{ id: "imb-rage", name: "PRISMRPG.Status.Rage", icon: "icons/svg/fire.svg", category: "imbuement", typing: "mundane" },
{ id: "imb-reinforced", name: "PRISMRPG.Status.Reinforced", icon: "icons/svg/mage-shield.svg", category: "imbuement", typing: "mundane" },
{ id: "imb-renewed", name: "PRISMRPG.Status.Renewed", icon: "icons/svg/regen.svg", category: "imbuement", typing: "mundane" },
{ id: "imb-supplied", name: "PRISMRPG.Status.Supplied", icon: "icons/svg/upgrade.svg", category: "imbuement", typing: "mundane" },
{ id: "imb-surged", name: "PRISMRPG.Status.Surged", icon: "icons/svg/lightning.svg", category: "imbuement", typing: "mundane" },
{ id: "imb-trance", name: "PRISMRPG.Status.Trance", icon: "icons/svg/sleep.svg", category: "imbuement", typing: "mundane" },
// ── Magic ──────────────────────────────────────────────────────────────────
{ id: "imb-blessed", name: "PRISMRPG.Status.Blessed", icon: "icons/svg/angel.svg", category: "imbuement", typing: "magic" },
{ id: "imb-anchored", name: "PRISMRPG.Status.Anchored", icon: "icons/svg/net.svg", category: "imbuement", typing: "magic" },
{ id: "imb-saturated", name: "PRISMRPG.Status.Saturated", icon: "icons/svg/regen.svg", category: "imbuement", typing: "magic" },
{ id: "imb-invisible", name: "PRISMRPG.Status.Invisible", icon: "icons/svg/invisible.svg", category: "imbuement", typing: "magic" },
{ id: "imb-enchanted", name: "PRISMRPG.Status.Enchanted", icon: "icons/svg/mage-shield.svg", category: "imbuement", typing: "magic" },
{ id: "imb-heroism", name: "PRISMRPG.Status.Heroism", icon: "icons/svg/upgrade.svg", category: "imbuement", typing: "magic" },
{ id: "imb-mana-drain", name: "PRISMRPG.Status.ManaDrain", icon: "icons/svg/regen.svg", category: "imbuement", typing: "magic" },
{ id: "imb-fury", name: "PRISMRPG.Status.Fury", icon: "icons/svg/fire.svg", category: "imbuement", typing: "magic" },
{ id: "imb-warded", name: "PRISMRPG.Status.Warded", icon: "icons/svg/holy-shield.svg", category: "imbuement", typing: "magic" },
{ id: "imb-regeneration", name: "PRISMRPG.Status.Regeneration", icon: "icons/svg/regen.svg", category: "imbuement", typing: "magic" },
{ id: "imb-haste", name: "PRISMRPG.Status.Haste", icon: "icons/svg/wingfoot.svg", category: "imbuement", typing: "magic" },
]
+1 -2
View File
@@ -36,8 +36,7 @@ export default class PrismRPGActor extends Actor {
if (this.type === "character") {
Object.assign(prototypeToken, {
sight: { enabled: true },
actorLink: true,
disposition: CONST.TOKEN_DISPOSITIONS.FRIENDLY,
actorLink: false
})
this.updateSource({ prototypeToken })
}
+1
View File
@@ -6,6 +6,7 @@ export { default as PrismRPGSkill } from "./skill.mjs"
export { default as PrismRPGArmor } from "./armor.mjs"
export { default as PrismRPGShield } from "./shield.mjs"
export { default as PrismRPGRacialAbility } from "./racial-ability.mjs"
export { default as PrismRPGAbility } from "./ability.mjs"
export { default as PrismRPGEquipment } from "./equipment.mjs"
export { default as PrismRPGRace } from "./race.mjs"
export { default as PrismRPGClass } from "./class.mjs"
+14
View File
@@ -0,0 +1,14 @@
export default class PrismRPGAbility extends foundry.abstract.TypeDataModel {
static defineSchema() {
const fields = foundry.data.fields
const schema = {}
schema.description = new fields.HTMLField({ required: true, textSearch: true })
return schema
}
/** @override */
static LOCALIZATION_PREFIXES = ["PRISMRPG.Ability"]
}
+1
View File
@@ -265,6 +265,7 @@ export default class PrismRPGUtils {
const templatePaths = [
'systems/fvtt-prism-rpg/templates/partial-item-effects.hbs',
'systems/fvtt-prism-rpg/templates/weapon-types-config.hbs',
'systems/fvtt-prism-rpg/templates/chat-new-round.hbs',
]
return foundry.applications.handlebars.loadTemplates(templatePaths)
}
+75
View File
@@ -4,6 +4,7 @@
*/
import { SYSTEM } from "./module/config/system.mjs"
import { AFFLICTIONS, IMBUEMENTS } from "./module/config/effects.mjs"
globalThis.SYSTEM = SYSTEM // Expose the SYSTEM object to the global scope
// Import modules
@@ -52,6 +53,7 @@ Hooks.once("init", function () {
CONFIG.Item.dataModels = {
skill: models.PrismRPGSkill,
"racial-ability": models.PrismRPGRacialAbility,
ability: models.PrismRPGAbility,
weapon: models.PrismRPGWeapon,
armor: models.PrismRPGArmor,
shield: models.PrismRPGShield,
@@ -70,6 +72,7 @@ Hooks.once("init", function () {
foundry.documents.collections.Items.unregisterSheet("core", foundry.appv1.sheets.ActorSheet)
foundry.documents.collections.Items.registerSheet("prismRPG", applications.PrismRPGSkillSheet, { types: ["skill"], makeDefault: true })
foundry.documents.collections.Items.registerSheet("prismRPG", applications.PrismRPGRacialAbilitySheet, { types: ["racial-ability"], makeDefault: true })
foundry.documents.collections.Items.registerSheet("prismRPG", applications.PrismRPGAbilitySheet, { types: ["ability"], makeDefault: true })
foundry.documents.collections.Items.registerSheet("prismRPG", applications.PrismRPGWeaponSheet, { types: ["weapon"], makeDefault: true })
foundry.documents.collections.Items.registerSheet("prismRPG", applications.PrismRPGSpellSheet, { types: ["spell"], makeDefault: true })
foundry.documents.collections.Items.registerSheet("prismRPG", applications.PrismRPGArmorSheet, { types: ["armor"], makeDefault: true })
@@ -79,6 +82,13 @@ Hooks.once("init", function () {
foundry.documents.collections.Items.registerSheet("prismRPG", applications.PrismRPGClassSheet, { types: ["class"], makeDefault: true })
foundry.documents.collections.Items.registerSheet("prismRPG", applications.PrismRPGCharacterPathSheet, { types: ["character-path"], makeDefault: true })
// Status Effects — Afflictions & Imbuements
CONFIG.statusEffects = [
{ id: "dead", name: "EFFECT.StatusDead", icon: "icons/svg/skull.svg" },
...AFFLICTIONS,
...IMBUEMENTS,
]
// Other Document Configuration
CONFIG.ChatMessage.documentClass = documents.PrismRPGChatMessage
@@ -196,6 +206,32 @@ Hooks.on(hookName, (message, html, data) => {
}
}
// Handle new round AP/mana restore buttons (GM only)
if (typeMessage === "newRound") {
$html.find(".new-round-restore-btn").click(async (event) => {
const btn = event.currentTarget
const actorId = btn.dataset.actorId
const targets = actorId === "all"
? $html.find(".new-round-restore-btn[data-actor-id!='all']").toArray().map(b => game.actors.get(b.dataset.actorId)).filter(Boolean)
: [game.actors.get(actorId)].filter(Boolean)
for (const actor of targets) {
await actor.update({
"system.actionPoints.value": actor.system.actionPoints.max,
"system.manaPoints.value": Math.min(actor.system.manaPoints.value + 1, actor.system.manaPoints.max)
})
}
if (actorId === "all") {
$html.find(".new-round-restore-btn").prop("disabled", true).addClass("restored")
} else {
btn.disabled = true
btn.classList.add("restored")
}
})
}
// Handle Roll Damage button click in weapon attack messages
$html.find(".roll-damage-button").click(async (event) => {
const btn = event.currentTarget
@@ -211,6 +247,45 @@ Hooks.on(hookName, (message, html, data) => {
await actor.prepareRoll("weapon-damage-medium", weaponId)
})
})
/**
* Send a GM-only chat message with restore buttons at the start of each new round
*/
Hooks.on("updateCombat", async (combat, change, _options, _userId) => {
if (!game.user.isGM) return
if (change.round === undefined || change.round <= 1) return
// Deduplicated character-type actors from the active combat
const seen = new Set()
const playerActors = combat.combatants.contents
.filter(c => c.actor?.type === "character" && !seen.has(c.actor.id) && seen.add(c.actor.id))
.map(c => ({ id: c.actor.id, name: c.actor.name, img: c.actor.img }))
if (playerActors.length === 0) return
const content = await foundry.applications.handlebars.renderTemplate(
"systems/fvtt-prism-rpg/templates/chat-new-round.hbs",
{ actors: playerActors, round: change.round }
)
await ChatMessage.create({
content,
whisper: ChatMessage.getWhisperRecipients("GM"),
flags: { prismRPG: { typeMessage: "newRound" } }
})
})
/**
* Inject a visual separator between Afflictions and Imbuements in the Token HUD status tray
*/
Hooks.on("renderTokenHUD", (_app, html) => {
const tray = html.querySelector(".status-effects")
if (!tray) return
const firstImb = tray.querySelector("[data-status-id^='imb-']")
if (!firstImb) return
const sep = document.createElement("div")
sep.className = "status-separator"
firstImb.before(sep)
})
/**
* Create a macro when dropping an entity on the hotbar
* Item - open roll dialog
+26
View File
@@ -0,0 +1,26 @@
.ability-content {
.sheet-common();
.item-sheet-common();
.header {
display: flex;
img {
width: 50px;
height: 50px;
}
}
input[type="checkbox"] {
font-size: var(--font-size-14);
width: 20px;
padding-top: 0;
}
input[type="checkbox"]:checked {
background-color: rgba(0, 0, 0, 0.1);
}
input[type="checkbox"]:checked::after {
color: rgba(0, 0, 0, 0.1);
}
}
+87
View File
@@ -548,3 +548,90 @@
}
}
}
// New round message
.new-round-message {
.chat-title .new-round-label {
font-size: var(--font-size-11);
font-style: italic;
color: #8a7a5a;
margin-top: 1px;
}
.new-round-actors {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 6px;
padding: 6px 0 2px;
.new-round-restore-btn {
display: flex;
align-items: center;
gap: 6px;
width: calc(50% - 3px);
padding: 4px 8px 4px 6px;
border: 1px solid #7a6a45;
border-radius: 4px;
background: linear-gradient(135deg, #f5e6c8 0%, #e8d5a0 100%);
cursor: pointer;
font-size: var(--font-size-13);
color: #3a2e1a;
overflow: hidden;
span {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
min-width: 0;
}
img {
flex-shrink: 0;
width: 24px;
height: 24px;
border: none;
border-radius: 3px;
object-fit: cover;
}
&:hover:not(:disabled) {
background: linear-gradient(135deg, #fdf3dc 0%, #f0e0b0 100%);
border-color: #a08040;
}
&:disabled,
&.restored {
opacity: 0.45;
cursor: default;
}
}
.new-round-all-btn {
width: 100%;
background: linear-gradient(135deg, #c8dff5 0%, #a0c0e8 100%);
border-color: #4a6a8a;
color: #1a2e3a;
font-weight: bold;
&:hover:not(:disabled) {
background: linear-gradient(135deg, #dcedfc 0%, #b0d0f0 100%);
border-color: #3a5a7a;
}
}
}
}
// Token HUD — separator between Afflictions and Imbuements
.palette.status-effects {
.status-separator {
grid-column: 1 / -1;
width: 100%;
height: 2px;
border: none;
border-top: 2px solid rgba(255, 255, 255, 0.35);
margin: 5px 0;
position: relative;
}
}
+1
View File
@@ -9,6 +9,7 @@
@import "monster.less";
@import "skill.less";
@import "racial-ability.less";
@import "ability.less";
@import "weapon.less";
@import "armor.less";
@import "spell.less";
+1
View File
@@ -34,6 +34,7 @@
"Item": {
"skill": { "htmlFields": ["description"] },
"racial-ability": { "htmlFields": ["description"] },
"ability": { "htmlFields": ["description"] },
"weapon": { "htmlFields": ["description"] },
"armor": { "htmlFields": ["description"] },
"shield": { "htmlFields": ["description"] },
+38
View File
@@ -0,0 +1,38 @@
<section>
<div class="header">
<img
class="item-img"
src="{{item.img}}"
data-edit="img"
data-action="editImage"
data-tooltip="{{item.name}}"
/>
{{formInput fields.name value=source.name}}
</div>
{{! Navigation des onglets }}
<nav class="sheet-tabs tabs" data-group="primary">
<a class="item {{tabs.description.cssClass}}" data-tab="description">{{localize "PRISMRPG.Label.description"}}</a>
<a class="item {{tabs.effects.cssClass}}" data-tab="effects">{{localize "PRISMRPG.Label.effects"}}</a>
</nav>
{{! Onglet Description }}
<div class="tab {{tabs.description.cssClass}}" data-group="primary" data-tab="description">
<fieldset>
<legend>{{localize "PRISMRPG.Label.description"}}</legend>
{{formInput
systemFields.description
enriched=enrichedDescription
value=system.description
name="system.description"
toggled=true
}}
</fieldset>
</div>
{{! Onglet Effects }}
<div class="tab {{tabs.effects.cssClass}}" data-group="primary" data-tab="effects">
{{> systems/fvtt-prism-rpg/templates/partial-item-effects.hbs}}
</div>
</section>
+20
View File
@@ -0,0 +1,20 @@
<div class="new-round-message prismrpg-chat-card">
<div class="chat-header">
<div class="chat-title">
<div class="roll-name">{{localize "PRISMRPG.Combat.newRound"}} {{round}}</div>
<div class="new-round-label">{{localize "PRISMRPG.Combat.restoreAP"}}</div>
</div>
</div>
<div class="new-round-actors">
<button class="new-round-restore-btn new-round-all-btn" type="button" data-actor-id="all" data-tooltip="{{localize "PRISMRPG.Combat.allPC"}}">
<i class="fas fa-users"></i>
<span>{{localize "PRISMRPG.Combat.allPC"}}</span>
</button>
{{#each actors}}
<button class="new-round-restore-btn" type="button" data-actor-id="{{id}}" data-tooltip="{{name}}">
<img src="{{img}}" width="24" height="24" />
<span>{{name}}</span>
</button>
{{/each}}
</div>
</div>