6 Commits

Author SHA1 Message Date
uberwald 68e3d35af1 Add abilities sectioon on sheet 2026-03-18 17:12:32 +01:00
uberwald 58fdc3482a Add HP TEMP 2026-03-10 19:54:36 +01:00
uberwald 236d752406 Update gitignore 2026-03-10 18:02:02 +01:00
uberwald bf9ad37d24 Add mana+AP reset buttons and conditions 2026-03-10 18:02:02 +01:00
uberwald 7944345c82 Actualiser README.md 2026-02-10 08:09:02 +01:00
uberwald 7abea8e9d4 Add class adancement 2026-02-09 22:46:44 +01:00
27 changed files with 1266 additions and 18 deletions
+3
View File
@@ -8,3 +8,6 @@ node_modules/
.history
# GitHub/Copilot config
.github/
+1 -13
View File
@@ -1,20 +1,8 @@
## Prism RPG RPG for Foundry Virtual TableTop
The Official game system for playing Prism RPG TTRPG: The Role Playing Game on FoundryVTT. This fully functional system is the foundational framework to build your game.
This product's format, programming code, and presentation is copyrighted by Prism RPG Games LLC.
This system & product are used with permission granted as part of the partnership agreement between Foundry Gaming LLC and Prism RPG Games LLC. It uses the following trademarks and/or copyrights:
This system & product are used with permission granted as part of the partnership agreement between Foundry Gaming LLC and Prism RPG Games LLC.
© 2025 Prism RPG Games. Content copyright Ted McClintock, Prism RPG Games LLC. All Rights Reserved. Prism RPG® is a Registered Trademark of Prism RPG Games LLC. All Rights Reserved.
Prism RPG Games is ©2025 Prism RPG Games, LLC. All rights reserved. Prism RPG, Prism RPG Games, and their associated logos are trademarks of Prism RPG Games, LLC. https://lethalfantasy.com/
For inquiries on developing content for this ruleset please contact Lethalted@lethalfantasy.com
## Community
Please join our Discord server Prism RPG games https://discord.gg/UDvnnyvreV
It's the place to ask questions on how to use the system, make feature request and follow the development of the system.
+19
View File
@@ -0,0 +1,19 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" width="100" height="100">
<!-- Background circle -->
<circle cx="50" cy="50" r="48" fill="#f0f0f0" stroke="#333" stroke-width="2" />
<!-- Star for achievement/advancement -->
<path d="M 50 20 L 55 38 L 74 38 L 59 49 L 64 67 L 50 56 L 36 67 L 41 49 L 26 38 L 45 38 Z"
fill="#ffd700" stroke="#333" stroke-width="1.5" />
<!-- Upward arrow for progression -->
<path d="M 50 75 L 50 85" stroke="#333" stroke-width="3" stroke-linecap="round" />
<path d="M 45 78 L 50 73 L 55 78" fill="none" stroke="#333" stroke-width="3"
stroke-linecap="round" stroke-linejoin="round" />
<!-- Small accent dots for decoration -->
<circle cx="20" cy="30" r="2" fill="#666" />
<circle cx="80" cy="30" r="2" fill="#666" />
<circle cx="20" cy="70" r="2" fill="#666" />
<circle cx="80" cy="70" r="2" fill="#666" />
</svg>

After

Width:  |  Height:  |  Size: 891 B

+296
View File
@@ -1126,6 +1126,10 @@ i.prismrpg {
border: 2px solid #6b6b6b;
border-radius: 4px;
}
.prismrpg .character-main-v2 .character-left-column .hp-shields-section .hp-shields .hp-item.hp-temp-item .hp-value input {
background: rgba(255, 230, 160, 0.85);
border-color: #b8860b;
}
.prismrpg .character-main-v2 .character-left-column .character-attributes {
display: flex;
flex-direction: column;
@@ -2163,6 +2167,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);
@@ -3227,6 +3341,118 @@ i.prismrpg {
.prismrpg .class-content label {
flex: 10%;
}
.prismrpg .class-content .advancement-level {
margin-bottom: 1.5rem;
padding: 0.5rem;
border: 1px solid rgba(0, 0, 0, 0.2);
border-radius: 4px;
}
.prismrpg .class-content .advancement-level h3 {
display: flex;
align-items: center;
justify-content: space-between;
margin: 0 0 0.5rem 0;
padding-bottom: 0.5rem;
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
}
.prismrpg .class-content .advancement-level h3 .level-title {
color: #000000;
font-weight: bold;
font-size: 80%;
}
.prismrpg .class-content .advancement-level h3 .add-advancement {
padding: 0.25rem 0.5rem;
background: var(--color-control-bg);
border: 1px solid var(--color-border-dark);
border-radius: 3px;
cursor: pointer;
}
.prismrpg .class-content .advancement-level h3 .add-advancement:hover {
background: var(--color-control-bg-hover);
}
.prismrpg .class-content .advancement-level h3 .add-advancement i {
margin: 0;
}
.prismrpg .class-content .advancement-level .empty-advancements {
font-style: italic;
color: rgba(0, 0, 0, 0.5);
margin: 0.5rem 0;
}
.prismrpg .class-content .advancement-list {
display: flex;
flex-direction: column;
gap: 1rem;
}
.prismrpg .class-content .advancement-item {
padding: 0.75rem;
background: rgba(0, 0, 0, 0.05);
border-radius: 4px;
}
.prismrpg .class-content .advancement-item .advancement-header {
display: flex;
align-items: center;
gap: 0.5rem;
margin-bottom: 0.5rem;
}
.prismrpg .class-content .advancement-item .advancement-header .advancement-icon {
width: 40px;
height: 40px;
border: 1px solid rgba(0, 0, 0, 0.2);
border-radius: 3px;
cursor: pointer;
flex-shrink: 0;
}
.prismrpg .class-content .advancement-item .advancement-header .advancement-icon:hover {
border-color: var(--color-border-highlight);
}
.prismrpg .class-content .advancement-item .advancement-header input[type="text"] {
flex: 1;
font-weight: bold;
}
.prismrpg .class-content .advancement-item .advancement-header .toggle-advancement-description {
padding: 0.25rem 0.5rem;
background: var(--color-control-bg);
border: 1px solid var(--color-border-dark);
border-radius: 3px;
cursor: pointer;
flex-shrink: 0;
margin-left: 0.25rem;
}
.prismrpg .class-content .advancement-item .advancement-header .toggle-advancement-description:hover {
background: var(--color-control-bg-hover);
}
.prismrpg .class-content .advancement-item .advancement-header .toggle-advancement-description i {
margin: 0;
color: rgba(0, 0, 0, 0.7);
}
.prismrpg .class-content .advancement-item .advancement-header .delete-advancement {
padding: 0.25rem 0.5rem;
background: var(--color-control-bg);
border: 1px solid var(--color-border-dark);
border-radius: 3px;
cursor: pointer;
flex-shrink: 0;
}
.prismrpg .class-content .advancement-item .advancement-header .delete-advancement:hover {
background: rgba(255, 0, 0, 0.1);
border-color: rgba(255, 0, 0, 0.5);
}
.prismrpg .class-content .advancement-item .advancement-header .delete-advancement i {
margin: 0;
color: rgba(255, 0, 0, 0.7);
}
.prismrpg .class-content .advancement-item .advancement-description {
margin-top: 0.5rem;
overflow: hidden;
transition: max-height 0.3s ease-out, opacity 0.3s ease-out;
max-height: 500px;
opacity: 1;
}
.prismrpg .class-content .advancement-item .advancement-description.collapsed {
max-height: 0;
opacity: 0;
margin-top: 0;
}
.prismrpg .character-path-content {
font-family: var(--font-primary);
font-size: calc(var(--font-size-standard) * 1);
@@ -4205,6 +4431,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);
}
+94 -2
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",
@@ -440,6 +522,7 @@
"combatDetails": "Combat details",
"Challenges": "Challenges",
"HP": "HP",
"HPTemp": "Temporary Hit Points",
"Movement": "Movement",
"Saves": "Saves",
"app": "APP",
@@ -471,6 +554,7 @@
"equipment": "Equipment",
"experience": "Experience",
"racialAbilities": "Racial Abilities",
"abilities": "Abilities",
"grit": "Grit",
"gritEarned": "Grit earned",
"int": "INT",
@@ -688,7 +772,13 @@
"spellcastingTypeMana": "Mana",
"spellcastingTypeFaith": "Faith",
"attributeBonuses": "Attribute Bonuses",
"classFeatures": "Class Features"
"classFeatures": "Class Features",
"advancement": "Advancement",
"addAdvancement": "Add Advancement",
"deleteAdvancement": "Delete Advancement",
"advancementName": "Advancement Name",
"noAdvancements": "No advancements defined for this level",
"toggleDescription": "Toggle Description"
},
"CoreSkill": {
"acrobatics": "Acrobatics",
@@ -807,7 +897,8 @@
"addMiracle": "Add new miracle",
"skill": "Skills list",
"skills": "Skills - Your character's skills and abilities",
"racialAbilities": "Racial Abilities from your character's race and sub-race"
"racialAbilities": "Racial Abilities from your character's race and sub-race",
"abilities": "Abilities acquired through class, paths, or other sources"
},
"RollSavingThrow": "Roll Saving Throw",
"Message": {
@@ -1406,6 +1497,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
}
}
@@ -26,6 +26,8 @@ export default class PrismRPGCharacterSheet extends PrismRPGActorSheet {
manaPointsMinus: PrismRPGCharacterSheet.#onManaPointsMinus,
hpPlus: PrismRPGCharacterSheet.#onHpPlus,
hpMinus: PrismRPGCharacterSheet.#onHpMinus,
hpTempPlus: PrismRPGCharacterSheet.#onHpTempPlus,
hpTempMinus: PrismRPGCharacterSheet.#onHpTempMinus,
postItemToChat: PrismRPGCharacterSheet.#onPostItemToChat,
},
}
@@ -112,6 +114,7 @@ export default class PrismRPGCharacterSheet extends PrismRPGActorSheet {
context.tab = context.tabs.skills
context.skills = doc.itemTypes.skill
context.racialAbilities = doc.itemTypes["racial-ability"]
context.abilities = doc.itemTypes["ability"]
context.vulnerabilities = doc.itemTypes.vulnerability
break
case "subattributes":
@@ -223,6 +226,16 @@ export default class PrismRPGCharacterSheet extends PrismRPGActorSheet {
this.actor.update({ "system.hp.value": Math.max(hp, 0) })
}
static async#onHpTempPlus(event, target) {
const temp = this.actor.system.hp.temp
this.actor.update({ "system.hp.temp": temp + 1 })
}
static async#onHpTempMinus(event, target) {
const temp = this.actor.system.hp.temp
this.actor.update({ "system.hp.temp": Math.max(temp - 1, 0) })
}
static async #onCreateEquipment(event, target) {
}
+137
View File
@@ -32,6 +32,7 @@ export default class PrismRPGClassSheet extends PrismRPGItemSheet {
#getTabs() {
const tabs = {
details: { id: "details", group: "primary", label: "PRISMRPG.Label.details" },
advancements: { id: "advancements", group: "primary", label: "PRISMRPG.Label.advancement" },
description: { id: "description", group: "primary", label: "PRISMRPG.Label.description" },
}
for (const v of Object.values(tabs)) {
@@ -56,6 +57,142 @@ export default class PrismRPGClassSheet extends PrismRPGItemSheet {
context.enrichedFeatures[key] = await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.document.system.features[key], { async: true })
}
// Enrich all advancement descriptions
context.enrichedAdvancements = {}
context.advancementsByLevel = []
for (let i = 1; i <= 10; i++) {
const key = `level${i}`
const advancements = this.document.system.advancements[key] || []
context.enrichedAdvancements[key] = []
const enrichedAdvancementsList = []
for (let j = 0; j < advancements.length; j++) {
const enrichedDesc = await foundry.applications.ux.TextEditor.implementation.enrichHTML(advancements[j].description, { async: true })
const enrichedAdv = {
...advancements[j],
enrichedDescription: enrichedDesc,
index: j,
levelKey: key
}
context.enrichedAdvancements[key].push(enrichedAdv)
enrichedAdvancementsList.push(enrichedAdv)
}
context.advancementsByLevel.push({
level: i,
levelKey: key,
advancements: enrichedAdvancementsList
})
}
return context
}
/** @override */
_onRender(context, options) {
super._onRender(context, options)
// Add advancement button listeners
this.element.querySelectorAll(".add-advancement").forEach(btn => {
btn.addEventListener("click", this._onAddAdvancement.bind(this))
})
// Delete advancement button listeners
this.element.querySelectorAll(".delete-advancement").forEach(btn => {
btn.addEventListener("click", this._onDeleteAdvancement.bind(this))
})
// Edit advancement icon listeners
this.element.querySelectorAll(".advancement-icon").forEach(img => {
img.addEventListener("click", this._onEditAdvancementIcon.bind(this))
})
// Toggle advancement description listeners
this.element.querySelectorAll(".toggle-advancement-description").forEach(btn => {
btn.addEventListener("click", this._onToggleAdvancementDescription.bind(this))
})
}
/**
* Handle toggling advancement description visibility
* @param {Event} event
*/
_onToggleAdvancementDescription(event) {
event.preventDefault()
const button = event.currentTarget
const item = button.closest(".advancement-item")
const description = item.querySelector(".advancement-description")
const icon = button.querySelector("i")
description.classList.toggle("collapsed")
if (description.classList.contains("collapsed")) {
icon.classList.remove("fa-chevron-up")
icon.classList.add("fa-chevron-down")
} else {
icon.classList.remove("fa-chevron-down")
icon.classList.add("fa-chevron-up")
}
}
/**
* Handle adding a new advancement to a level
* @param {Event} event
*/
async _onAddAdvancement(event) {
event.preventDefault()
const level = event.currentTarget.dataset.level
const advancements = foundry.utils.deepClone(this.document.system.advancements[level] || [])
advancements.push({
icon: "systems/fvtt-prism-rpg/assets/icons/advancement.svg",
name: "",
description: ""
})
await this.document.update({
[`system.advancements.${level}`]: advancements
})
}
/**
* Handle deleting an advancement from a level
* @param {Event} event
*/
async _onDeleteAdvancement(event) {
event.preventDefault()
const level = event.currentTarget.dataset.level
const index = parseInt(event.currentTarget.dataset.index)
const advancements = foundry.utils.deepClone(this.document.system.advancements[level] || [])
advancements.splice(index, 1)
await this.document.update({
[`system.advancements.${level}`]: advancements
})
}
/**
* Handle editing an advancement icon
* @param {Event} event
*/
async _onEditAdvancementIcon(event) {
event.preventDefault()
const level = event.currentTarget.dataset.level
const index = parseInt(event.currentTarget.dataset.index)
const fp = new FilePicker({
type: "image",
current: this.document.system.advancements[level][index].icon,
callback: async (path) => {
const advancements = foundry.utils.deepClone(this.document.system.advancements[level] || [])
advancements[index].icon = path
await this.document.update({
[`system.advancements.${level}`]: advancements
})
}
})
fp.render(true)
}
}
+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"]
}
+67
View File
@@ -68,6 +68,41 @@ export default class PrismRPGClass extends foundry.abstract.TypeDataModel {
level10: new fields.HTMLField({ initial: "" })
})
// Advancements (list of advancements per level with icon, name and description)
const advancementSchema = () => new fields.ArrayField(
new fields.SchemaField({
icon: new fields.StringField({
required: true,
initial: "",
label: "Icon"
}),
name: new fields.StringField({
required: true,
initial: "",
label: "Name"
}),
description: new fields.HTMLField({
required: true,
initial: "",
label: "Description"
})
}),
{ initial: [] }
)
schema.advancements = new fields.SchemaField({
level1: advancementSchema(),
level2: advancementSchema(),
level3: advancementSchema(),
level4: advancementSchema(),
level5: advancementSchema(),
level6: advancementSchema(),
level7: advancementSchema(),
level8: advancementSchema(),
level9: advancementSchema(),
level10: advancementSchema()
})
// Proficiencies granted by this class
schema.weaponProficiencies = new fields.StringField({
required: true,
@@ -156,4 +191,36 @@ export default class PrismRPGClass extends foundry.abstract.TypeDataModel {
}
return features
}
/**
* Get the current level's advancements
*/
get currentLevelAdvancements() {
return this.advancements[`level${this.level}`] || []
}
/**
* Get all advancements up to current level
*/
get allAdvancementsUpToLevel() {
const advancements = []
for (let i = 1; i <= this.level; i++) {
const levelAdvancements = this.advancements[`level${i}`]
if (levelAdvancements && levelAdvancements.length > 0) {
advancements.push({
level: i,
advancements: levelAdvancements
})
}
}
return advancements
}
/**
* Get advancements for a specific level
*/
getAdvancementsForLevel(level) {
if (level < 1 || level > 10) return []
return this.advancements[`level${level}`] || []
}
}
+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);
}
}
+7
View File
@@ -143,6 +143,13 @@
border-radius: 4px;
}
}
&.hp-temp-item {
.hp-value input {
background: rgba(255, 230, 160, 0.85);
border-color: #b8860b;
}
}
}
}
}
+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;
}
}
+135
View File
@@ -13,4 +13,139 @@
label {
flex: 10%;
}
.advancement-level {
margin-bottom: 1.5rem;
padding: 0.5rem;
border: 1px solid rgba(0, 0, 0, 0.2);
border-radius: 4px;
h3 {
display: flex;
align-items: center;
justify-content: space-between;
margin: 0 0 0.5rem 0;
padding-bottom: 0.5rem;
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
.level-title {
color: #000000;
font-weight: bold;
font-size: 80%;
}
.add-advancement {
padding: 0.25rem 0.5rem;
background: var(--color-control-bg);
border: 1px solid var(--color-border-dark);
border-radius: 3px;
cursor: pointer;
&:hover {
background: var(--color-control-bg-hover);
}
i {
margin: 0;
}
}
}
.empty-advancements {
font-style: italic;
color: rgba(0, 0, 0, 0.5);
margin: 0.5rem 0;
}
}
.advancement-list {
display: flex;
flex-direction: column;
gap: 1rem;
}
.advancement-item {
padding: 0.75rem;
background: rgba(0, 0, 0, 0.05);
border-radius: 4px;
.advancement-header {
display: flex;
align-items: center;
gap: 0.5rem;
margin-bottom: 0.5rem;
.advancement-icon {
width: 40px;
height: 40px;
border: 1px solid rgba(0, 0, 0, 0.2);
border-radius: 3px;
cursor: pointer;
flex-shrink: 0;
&:hover {
border-color: var(--color-border-highlight);
}
}
input[type="text"] {
flex: 1;
font-weight: bold;
}
.toggle-advancement-description {
padding: 0.25rem 0.5rem;
background: var(--color-control-bg);
border: 1px solid var(--color-border-dark);
border-radius: 3px;
cursor: pointer;
flex-shrink: 0;
margin-left: 0.25rem;
&:hover {
background: var(--color-control-bg-hover);
}
i {
margin: 0;
color: rgba(0, 0, 0, 0.7);
}
}
.delete-advancement {
padding: 0.25rem 0.5rem;
background: var(--color-control-bg);
border: 1px solid var(--color-border-dark);
border-radius: 3px;
cursor: pointer;
flex-shrink: 0;
&:hover {
background: rgba(255, 0, 0, 0.1);
border-color: rgba(255, 0, 0, 0.5);
}
i {
margin: 0;
color: rgba(255, 0, 0, 0.7);
}
}
}
.advancement-description {
margin-top: 0.5rem;
overflow: hidden;
transition:
max-height 0.3s ease-out,
opacity 0.3s ease-out;
max-height: 500px;
opacity: 1;
&.collapsed {
max-height: 0;
opacity: 0;
margin-top: 0;
}
}
}
}
+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";
+2 -1
View File
@@ -6,7 +6,7 @@
"download": "#{DOWNLOAD}#",
"url": "#{URL}#",
"license": "LICENSE",
"version": "13.0.0",
"version": "13.0.1",
"authors": [
{
"name": "Uberwald",
@@ -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>
+12
View File
@@ -64,6 +64,18 @@
}}
</div>
</div>
<div class="hp-item hp-temp-item">
<div class="hp-label" data-tooltip="{{localize 'PRISMRPG.Label.HPTemp'}}">HP Tmp</div>
<a data-action="hpTempMinus"><i class="fa-solid fa-minus"></i></a>
<div class="hp-value">
{{formInput
systemFields.hp.fields.temp
value=system.hp.temp
disabled=isPlayMode
}}
</div>
<a data-action="hpTempPlus"><i class="fa-solid fa-plus"></i></a>
</div>
<div class="hp-item">
<div class="hp-label">MANA</div>
<a data-action="manaPointsMinus"><i class="fa-solid fa-minus"></i></a>
+22
View File
@@ -60,5 +60,27 @@
{{/each}}
</div>
</fieldset>
<fieldset>
<legend data-tooltip="{{localize 'PRISMRPG.Tooltip.abilities'}}" data-tooltip-direction="UP">
{{localize "PRISMRPG.Label.abilities"}}
</legend>
<div class="racial-abilities">
{{#each abilities as |item|}}
<div class="racial-ability" data-item-id="{{item.id}}" data-item-uuid="{{item.uuid}}">
<img class="item-img" src="{{item.img}}" data-tooltip="{{item.name}}\nClick to post to chat" data-action="postItemToChat" />
<div class="name" data-tooltip="{{{item.description}}}" data-tooltip-direction="UP">
{{item.name}}
</div>
<div class="controls">
<a data-tooltip="{{localize 'PRISMRPG.Edit'}}" data-action="edit" data-item-id="{{item.id}}"
data-item-uuid="{{item.uuid}}"><i class="fas fa-edit"></i></a>
<a data-tooltip="{{localize 'PRISMRPG.Delete'}}" data-action="delete" data-item-id="{{item.id}}"
data-item-uuid="{{item.uuid}}"><i class="fas fa-trash"></i></a>
</div>
</div>
{{/each}}
</div>
</fieldset>
</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>
+54
View File
@@ -7,6 +7,7 @@
{{! Navigation des onglets }}
<nav class="sheet-tabs tabs" data-group="primary">
<a class="item {{tabs.details.cssClass}}" data-tab="details">{{localize "PRISMRPG.Label.details"}}</a>
<a class="item {{tabs.advancements.cssClass}}" data-tab="advancements">{{localize "PRISMRPG.Label.advancement"}}</a>
<a class="item {{tabs.description.cssClass}}" data-tab="description">{{localize "PRISMRPG.Label.description"}}</a>
</nav>
@@ -73,6 +74,59 @@
</fieldset>
</div>
{{! Onglet Advancements }}
<div class="tab {{tabs.advancements.cssClass}}" data-group="primary" data-tab="advancements">
<fieldset>
<legend>{{localize "PRISMRPG.Label.advancement"}}</legend>
{{#each advancementsByLevel as |levelData|}}
<div class="advancement-level">
<h3>
<span class="level-title">{{localize "PRISMRPG.Label.level"}} {{levelData.level}}</span>
<button type="button" class="add-advancement" data-level="{{levelData.levelKey}}" data-tooltip="{{localize 'PRISMRPG.Label.addAdvancement'}}">
<i class="fas fa-plus"></i>
</button>
</h3>
{{#if levelData.advancements.length}}
<div class="advancement-list">
{{#each levelData.advancements as |advancement|}}
<div class="advancement-item">
<div class="advancement-header">
<img class="advancement-icon" src="{{advancement.icon}}" data-level="{{advancement.levelKey}}" data-index="{{advancement.index}}" />
{{formInput
../../systemFields.advancements.fields.level1.element.fields.name
value=advancement.name
name=(concat "system.advancements." advancement.levelKey "." advancement.index ".name")
placeholder=(localize "PRISMRPG.Label.advancementName")
}}
<button type="button" class="toggle-advancement-description" data-tooltip="{{localize 'PRISMRPG.Label.toggleDescription'}}">
<i class="fas fa-chevron-down"></i>
</button>
<button type="button" class="delete-advancement" data-level="{{advancement.levelKey}}" data-index="{{advancement.index}}" data-tooltip="{{localize 'PRISMRPG.Label.deleteAdvancement'}}">
<i class="fas fa-trash"></i>
</button>
</div>
<div class="advancement-description collapsed">
{{formInput
../../systemFields.advancements.fields.level1.element.fields.description
enriched=advancement.enrichedDescription
value=advancement.description
name=(concat "system.advancements." advancement.levelKey "." advancement.index ".description")
toggled=true
}}
</div>
</div>
{{/each}}
</div>
{{else}}
<p class="empty-advancements">{{localize "PRISMRPG.Label.noAdvancements"}}</p>
{{/if}}
</div>
{{/each}}
</fieldset>
</div>
{{! Onglet Description }}
<div class="tab {{tabs.description.cssClass}}" data-group="primary" data-tab="description">
<fieldset>