9 Commits

Author SHA1 Message Date
uberwald 7abea8e9d4 Add class adancement 2026-02-09 22:46:44 +01:00
uberwald 31573bd522 Fix initiative 2026-01-26 17:47:53 +01:00
uberwald abea77906d Add spells rolls and enhance CSS styling
- Add spell roll functionality to character sheets
- Enhance CSS and LESS styling for better visual presentation
- Update character templates and models
- Remove old backup files (roll-old.mjs, roll.mjs.backup)
- Improve character combat and equipment templates
- Update utility functions and actor documents

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-01-21 13:56:09 +01:00
uberwald 7283f5f15b Update skill sheet 2026-01-14 14:16:31 +01:00
uberwald 56492c40a0 Add spells rolls and enhance CSS styling 2026-01-12 10:48:54 +01:00
uberwald 3634e9da9e Add spells rolls and enhance CSS styling 2026-01-12 10:46:16 +01:00
uberwald 0bc6b43ffe Add spells rolls and enhance CSS styling 2026-01-12 10:45:20 +01:00
uberwald e75824cd20 First PC sheet, WIP 2025-12-20 17:20:01 +01:00
uberwald 189b03ca91 Update actor sheet 2025-12-20 00:09:42 +01:00
52 changed files with 6265 additions and 3089 deletions
+10
View File
@@ -0,0 +1,10 @@
# IDE
.idea/
.vs/
styles/*.css
# Node Modules
node_modules/
.history
-122
View File
@@ -1,122 +0,0 @@
# Configuration des Types d'Armes - PRISM RPG
## Aperçu
Ce système permet de configurer et d'ajouter des types d'armes et des groupes d'armes personnalisés via les Settings de Foundry VTT.
## Fichiers Créés
### 1. `/module/settings.mjs`
- Enregistre les settings pour les types d'armes personnalisés
- Enregistre les settings pour les groupes d'armes personnalisés
- Crée le menu de configuration dans les Settings
### 2. `/module/applications/weapon-types-config.mjs`
- Application FormApplication pour éditer les types et groupes d'armes
- Interface avec onglets (Types d'Armes / Groupes d'Armes)
- Fonctionnalités d'ajout, édition et suppression
- Bouton de réinitialisation aux valeurs par défaut
### 3. `/templates/weapon-types-config.hbs`
- Template Handlebars pour l'interface de configuration
- Affichage en onglets
- Formulaires pour chaque type/groupe d'arme
### 4. `/styles/weapon-types-config.less`
- Styles CSS pour l'interface de configuration
- Design cohérent avec le système PRISM RPG
## Fichiers Modifiés
### 1. `/module/config/weapon.mjs`
- Conversion des constantes `TYPE` et `WEAPON_GROUP` en Proxies dynamiques
- Ajout de fonctions `getWeaponTypes()` et `getWeaponGroups()`
- Les types/groupes sont maintenant chargés depuis les settings
- Rétrocompatibilité maintenue
### 2. `/module/models/weapon.mjs`
- Import des fonctions `getWeaponTypeChoices()` et `getWeaponGroupChoices()`
- Utilisation de fonctions dynamiques au lieu de constantes statiques
- Les choix sont mis à jour automatiquement depuis les settings
### 3. `/prism-rpg.mjs`
- Import du module `settings.mjs`
- Appel de `registerSettings()` dans le hook `init`
### 4. `/module/applications/_module.mjs`
- Export de `WeaponTypesConfig`
### 5. `/module/utils.mjs`
- Ajout du template `weapon-types-config.hbs` dans les templates préchargés
### 6. `/styles/fvtt-prism-rpg.less`
- Import du fichier `weapon-types-config.less`
### 7. `/lang/en.json`
- Ajout de toutes les clés de traduction pour les settings
- Section `Settings` avec toutes les chaînes nécessaires
## Utilisation
### Pour les Game Masters
1. Ouvrir les **Settings** de Foundry VTT
2. Aller dans **Game Settings**
3. Chercher **Configure Weapons** dans la section PRISM RPG
4. Cliquer sur le bouton pour ouvrir l'interface de configuration
### Interface de Configuration
#### Onglet "Weapon Types"
- **ID**: Identifiant unique (non modifiable pour les types par défaut)
- **Label**: Nom affiché du type d'arme
- **APC**: Coût en points d'action
- **Hands**: Nombre de mains requises (0, 1, ou 2)
#### Onglet "Weapon Groups"
- **ID**: Identifiant unique (non modifiable pour les groupes par défaut)
- **Label**: Nom affiché du groupe
- **Passive ID**: Identifiant de la passive
- **Passive Label**: Nom de la passive
- **Passive Description**: Description de l'effet de la passive
### Ajout d'un Type/Groupe d'Arme
1. Cliquer sur le bouton **+** dans l'onglet approprié
2. Un nouvel ID unique sera généré automatiquement
3. Remplir les champs
4. Cliquer sur **Save Changes**
### Suppression d'un Type/Groupe d'Arme
1. Cliquer sur l'icône **poubelle** à côté du type/groupe
2. L'entrée sera supprimée
3. Cliquer sur **Save Changes**
### Réinitialisation
Le bouton **Reset to Defaults** permet de revenir aux valeurs par défaut du système.
## Types d'Armes par Défaut
- **Light** (Légère) - 1 APC, 1 main
- **One-Handed** (Une main) - 2 APC, 1 main
- **Heavy** (Lourde) - 3 APC, 2 mains
- **Projectile** - Variable APC, 2 mains
## Groupes d'Armes par Défaut
1. **Longsword** - Passive: Turning Edge
2. **Warhammer** - Passive: Puncturing Blows
3. **Battleaxe** - Passive: Shield Eater
4. **Dagger** - Passive: Balancing Stance
5. **Crossbow** - Passive: Boltlock
6. **Longbow** - Passive: Volley Fire
## Remarques Techniques
- Les modifications sont sauvegardées au niveau du **monde** (scope: world)
- Un rechargement de la page est déclenché après sauvegarde
- Les valeurs par défaut restent toujours disponibles
- Les types/groupes personnalisés sont fusionnés avec les valeurs par défaut
- Utilisation de Proxies JavaScript pour une compatibilité maximale
+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

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 MiB

+1683 -59
View File
File diff suppressed because it is too large Load Diff
+102 -26
View File
@@ -78,36 +78,96 @@
"cha": {
"label": "Charisma"
},
"prowess": {
"label": "Prowess",
"description": "Physical power and combat capability (STR+DEX)"
},
"vigor": {
"label": "Vigor",
"description": "Endurance and physical resilience (STR+CON)"
},
"competence": {
"label": "Competence",
"description": "Practical application of knowledge (STR+INT)"
},
"authority": {
"label": "Authority",
"description": "Command and leadership presence (STR+WIS)"
},
"presence": {
"label": "Presence",
"description": "Physical charisma and intimidation (STR+CHA)"
},
"stamina": {
"label": "Stamina",
"description": "Athletic endurance (DEX+CON)"
},
"initiative": {
"label": "Initiative",
"description": "Reaction speed and alertness (DEX+INT)"
},
"wit": {
"label": "Wit",
"description": "Mental agility and cleverness (DEX+WIS)"
},
"grace": {
"label": "Grace",
"description": "Social finesse and charm (DEX+CHA)"
},
"tenacity": {
"label": "Tenacity",
"description": "Mental and physical toughness (CON+INT)"
},
"willpower": {
"label": "Willpower",
"description": "Inner strength and determination (CON+WIS)"
},
"resilience": {
"label": "Resilience",
"description": "Ability to endure hardship (CON+CHA)"
},
"cunning": {
"label": "Cunning",
"description": "Strategic thinking (INT+WIS)"
},
"guile": {
"label": "Guile",
"description": "Deceptive intellect (INT+CHA)"
},
"sovereignty": {
"label": "Sovereignty",
"description": "Diplomatic wisdom and influence (WIS+CHA)"
},
"FIELDS": {
"moneys": {
"tinbit": {
"label": "Tin Bits",
"coppercoin": {
"label": "Copper Coin",
"value": {
"label": "Tin Bits"
"label": "Copper Coin"
}
},
"silver": {
"label": "Silver",
"silvercoin": {
"label": "Silver Coin",
"value": {
"label": "Silver"
"label": "Silver Coin"
}
},
"copper": {
"label": "Copper",
"goldcoin": {
"label": "Gold Coin",
"value": {
"label": "Copper"
"label": "Gold Coin"
}
},
"gold": {
"label": "Gold",
"note": {
"label": "Note",
"value": {
"label": "Gold"
"label": "Note"
}
},
"platinum": {
"label": "Platinum",
"steam": {
"label": "Steam",
"value": {
"label": "Platinum"
"label": "Steam"
}
}
},
@@ -355,7 +415,7 @@
"rollProgressionDice": "Roll progression/Lethargy dice",
"earned": "Earned",
"divinityPoints": "Divinity points",
"aetherPoints": "Aether points",
"manaPoints": "Mana points",
"attacks": "Attacks",
"monster": "Monster",
"Resist" :"Resist",
@@ -454,6 +514,10 @@
"skill": "Skill",
"skillBonus": "Skill bonus",
"skills": "Skills",
"sub-attribute": "Sub-Attribute",
"subattributes": "Sub-Attributes",
"subAttribute1": "Sub-Attribute 1",
"subAttribute2": "Sub-Attribute 2",
"spells": "Spells",
"str": "STR",
"titleChallenge": "Challenge",
@@ -526,7 +590,8 @@
"piercing": "Piercing (P)",
"bludgeoning": "Bludgeoning (B)",
"slashing": "Slashing (S)",
"isProjectile": "Is Projectile?",
"shortRange": "Short Range",
"longRange": "Long Range",
"range": "Range",
"reloadAPC": "Reload APC",
"bonuses": "Bonuses",
@@ -623,7 +688,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",
@@ -738,12 +809,17 @@
"coreSkills": "List of all 18 available Core Skills in Prism RPG",
"advancedChecks": "Advanced checks are only available for your Core Skill",
"addEquipment": "New equipment",
"addSpell": "New spells",
"addSpell": "Add new spell",
"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"
},
"RollSavingThrow": "Roll Saving Throw",
"Message": {
"selectCoreSkill": "You must select a Core Skill for your character. Each character chooses one Core Skill at creation."
"selectCoreSkill": "You must select a Core Skill for your character. Each character chooses one Core Skill at creation.",
"dropRace": "Drag and drop a Race item here",
"dropClass": "Drag and drop a Class item here"
},
"Miracle": {
"FIELDS": {
@@ -812,11 +888,11 @@
}
},
"Money": {
"Coppers": "Copper",
"Golds": "Gold",
"Platinums": "Platinum",
"Silvers": "Silver",
"Tinbits": "Tin Bits"
"CopperCoin": "Copper Coin",
"SilverCoin": "Silver Coin",
"GoldCoin": "Gold Coin",
"Note": "Note",
"Steam": "Steam"
},
"Notifications": {
"rollFromWeapon": "Roll from an equipped weapon",
@@ -842,7 +918,7 @@
"save": "Save roll {save}",
"success": "Success",
"visibility": "Visibility",
"favorDisfavor": "Favor/Disfavor"
"advantageDisadvantage": "Advantage/Disadvantage"
},
"Save": {
"FIELDS": {
-191
View File
@@ -1,104 +1,6 @@
/* -------------------------------------------- */
export class PrismRPGCombatTracker extends foundry.applications.sidebar.tabs.CombatTracker {
static PARTS = {
"header": {
"template": "systems/fvtt-prism-rpg/templates/combat-tracker-header-v2.hbs"
},
"tracker": {
"template": "systems/fvtt-prism-rpg/templates/combat-tracker-v2.hbs"
},
"footer": {
"template": "systems/fvtt-prism-rpg/templates/combat-tracker-footer-v2.hbs"
}
}
static DEFAULT_OPTIONS = foundry.utils.mergeObject(super.DEFAULT_OPTIONS, {
actions: {
initiativePlus: PrismRPGCombatTracker.#initiativePlus,
initiativeMinus: PrismRPGCombatTracker.#initiativeMinus,
},
});
async _prepareContext(options) {
let data = await super._prepareContext(options);
console?.log("Combat Tracker Data", data);
/*for (let u of data.turns) {
let c = game.combat.combatants.get(u.id);
u.progressionCount = c.system.progressionCount
u.isMonster = c.actor.type === "monster"
}
console.log("Combat Data", data);*/
return data;
}
static #initiativePlus(ev) {
ev.preventDefault();
let cId = ev.target.closest(".combatant").dataset.combatantId;
let c = game.combat.combatants.get(cId);
c.update({ 'initiative': c.initiative + 1 });
console.log("Initiative Plus");
}
static #initiativeMinus(ev) {
ev.preventDefault();
let cId = ev.target.closest(".combatant").dataset.combatantId;
let c = game.combat.combatants.get(cId);
let newInit = Math.max(c.initiative - 1, 0);
c.update({ 'initiative': newInit });
}
activateListeners(html) {
super.activateListeners(html);
// Display Combat settings
html.find(".initiative-plus").click(ev => {
ev.preventDefault();
let cId = ev.currentTarget.closest(".combatant").dataset.combatantId;
let c = game.combat.combatants.get(cId);
c.update({ 'initiative': c.initiative + 1 });
});
html.find(".initiative-minus").click(ev => {
ev.preventDefault();
let cId = ev.currentTarget.closest(".combatant").dataset.combatantId;
let c = game.combat.combatants.get(cId);
c.update({ 'initiative': c.initiative - 1 });
console.log("Initiative Minus");
});
}
/* -------------------------------------------- */
static get defaultOptions() {
let path = "systems/fvtt-prism-rpg/templates/combat-tracker.hbs";
return foundry.utils.mergeObject(super.defaultOptions, {
template: path,
});
}
}
export class PrismRPGCombat extends Combat {
/**
* Return the Array of combatants sorted into initiative order, breaking ties alphabetically by name.
* @returns {Combatant[]}
*/
setupTurns() {
console?.log("Setup Turns....");
this.turns ||= [];
// Determine the turn order and the current turn
const turns = this.combatants.contents.sort(this.sortCombatantsLF);
if (this.turn !== null) this.turn = Math.clamp(this.turn, 0, turns.length - 1);
// Update state tracking
let c = turns[this.turn];
this.current = this._getCurrentState(c);
if (!this.previous) this.previous = this.current;
// Return the array of prepared turns
return this.turns = turns;
}
async rollInitiative(ids, options) {
console.log("%%%%%%%%% Roll Initiative", ids, options);
@@ -123,98 +25,5 @@ export class PrismRPGCombat extends Combat {
return this;
}
resetProgression(cId) {
let c = this.combatants.get(cId);
c.update({ 'system.progressionCount': 0 });
}
setCasting(cId) {
let c = this.combatants.get(cId);
c.setFlag(SYSTEM.id, "casting", true);
}
resetCasting(cId) {
let c = this.combatants.get(cId);
c.setFlag(SYSTEM.id, "casting", false);
}
isCasting(cId) {
let c = this.combatants.get(cId);
return c.getFlag(SYSTEM.id, "casting");
}
async nextTurn() {
console.log("NEXT TURN");
let turn = this.turn ?? -1;
let skipDefeated = this.settings.skipDefeated;
// Determine the next turn number
let next = null;
for (let [i, t] of this.turns.entries()) {
console.log("Turn", t);
if (i <= turn) continue;
if (skipDefeated && t.isDefeated) continue;
next = i;
break;
}
// Maybe advance to the next round
let round = this.round;
if ((this.round === 0) || (next === null) || (next >= this.turns.length)) {
return this.nextRound();
}
// Update the document, passing data through a hook first
const updateData = { round, turn: next };
const updateOptions = { advanceTime: CONFIG.time.turnTime, direction: 1 };
Hooks.callAll("combatTurn", this, updateData, updateOptions);
return this.update(updateData, updateOptions);
}
async nextRound() {
this.turnsDone = false
let turn = this.turn === null ? null : 0; // Preserve the fact that it's no-one's turn currently.
console.log("ROUND", this);
let advanceTime = Math.max(this.turns.length - this.turn, 0) * CONFIG.time.turnTime;
advanceTime += CONFIG.time.roundTime;
let nextRound = this.round + 1;
let initOK = true;
for (let c of this.combatants) {
if (c.initiative === null) {
initOK = false;
break;
}
}
if (!initOK) {
ui.notifications.error("All combatants must have initiative rolled before the round can advance.");
return this;
}
for (let c of this.combatants) {
if (nextRound >= c.initiative) {
let user = game.users.find(u => u.active && u.character && u.character.id === c.actor.id);
if (user?.hasPlayerOwner) {
game.socket.emit(`system.${SYSTEM.id}`, { type: "rollProgressionDice", progressionCount: c.system.progressionCount + 1, actorId: c.actor.id, combatId: this.id, combatantId: c.id });
} else {
user = game.users.find(u => u.active && u.isGM);
c.actor.system.rollProgressionDice(this.id, c.id);
}
}
}
// Update the document, passing data through a hook first
const updateData = { round: nextRound, turn };
const updateOptions = { advanceTime, direction: 1 };
Hooks.callAll("combatRound", this, updateData, updateOptions);
return this.update(updateData, updateOptions);
}
sortCombatantsLF(a, b) {
return a.initiative - b.initiative;
}
}
+223 -62
View File
@@ -1,12 +1,13 @@
import PrismRPGActorSheet from "./base-actor-sheet.mjs"
import PrismRPGRoll from "../../documents/roll.mjs"
import { SYSTEM } from "../../config/system.mjs"
export default class PrismRPGCharacterSheet extends PrismRPGActorSheet {
/** @override */
static DEFAULT_OPTIONS = {
classes: ["character"],
position: {
width: 972,
width: 780,
height: 780,
},
window: {
@@ -14,14 +15,18 @@ export default class PrismRPGCharacterSheet extends PrismRPGActorSheet {
},
actions: {
createEquipment: PrismRPGCharacterSheet.#onCreateEquipment,
rangedAttackDefense: PrismRPGCharacterSheet.#onRangedAttackDefense,
rollInitiative: PrismRPGCharacterSheet.#onRollInitiative,
armorHitPointsPlus: PrismRPGCharacterSheet.#onArmorHitPointsPlus,
armorHitPointsMinus: PrismRPGCharacterSheet.#onArmorHitPointsMinus,
divinityPointsPlus: PrismRPGCharacterSheet.#onDivinityPointsPlus,
divinityPointsMinus: PrismRPGCharacterSheet.#onDivinityPointsMinus,
aetherPointsPlus: PrismRPGCharacterSheet.#onAetherPointsPlus,
aetherPointsMinus: PrismRPGCharacterSheet.#onAetherPointsMinus,
armorPointsPlus: PrismRPGCharacterSheet.#onArmorPointsPlus,
armorPointsMinus: PrismRPGCharacterSheet.#onArmorPointsMinus,
actionPointsPlus: PrismRPGCharacterSheet.#onActionPointsPlus,
actionPointsMinus: PrismRPGCharacterSheet.#onActionPointsMinus,
manaPointsPlus: PrismRPGCharacterSheet.#onManaPointsPlus,
manaPointsMinus: PrismRPGCharacterSheet.#onManaPointsMinus,
hpPlus: PrismRPGCharacterSheet.#onHpPlus,
hpMinus: PrismRPGCharacterSheet.#onHpMinus,
postItemToChat: PrismRPGCharacterSheet.#onPostItemToChat,
},
}
@@ -36,6 +41,9 @@ export default class PrismRPGCharacterSheet extends PrismRPGActorSheet {
skills: {
template: "systems/fvtt-prism-rpg/templates/character-skills.hbs",
},
subattributes: {
template: "systems/fvtt-prism-rpg/templates/character-subattributes.hbs",
},
combat: {
template: "systems/fvtt-prism-rpg/templates/character-combat.hbs",
},
@@ -45,11 +53,6 @@ export default class PrismRPGCharacterSheet extends PrismRPGActorSheet {
spells: {
template: "systems/fvtt-prism-rpg/templates/character-spells.hbs",
},
/* Miracles disabled - Legacy from Lethal Fantasy
miracles: {
template: "systems/fvtt-prism-rpg/templates/character-miracles.hbs",
},
*/
biography: {
template: "systems/fvtt-prism-rpg/templates/character-biography.hbs",
},
@@ -67,6 +70,7 @@ export default class PrismRPGCharacterSheet extends PrismRPGActorSheet {
#getTabs() {
let tabs = {
skills: { id: "skills", group: "sheet", icon: "fa-solid fa-shapes", label: "PRISMRPG.Label.skills" },
subattributes: { id: "subattributes", group: "sheet", icon: "fa-solid fa-diagram-project", label: "PRISMRPG.Label.subattributes" },
combat: { id: "combat", group: "sheet", icon: "fa-solid fa-swords", label: "PRISMRPG.Label.combat" },
equipment: { id: "equipment", group: "sheet", icon: "fa-solid fa-backpack", label: "PRISMRPG.Label.equipment" },
biography: { id: "biography", group: "sheet", icon: "fa-solid fa-book", label: "PRISMRPG.Label.biography" },
@@ -74,11 +78,6 @@ export default class PrismRPGCharacterSheet extends PrismRPGActorSheet {
if (this.actor.system.biodata.magicUser) {
tabs.spells = { id: "spells", group: "sheet", icon: "fa-sharp-duotone fa-solid fa-wand-magic-sparkles", label: "PRISMRPG.Label.spells" }
}
/* Miracles disabled - Legacy from Lethal Fantasy
if (this.actor.system.biodata.clericUser) {
tabs.miracles = { id: "miracles", group: "sheet", icon: "fa-sharp-duotone fa-solid fa-hands-praying", label: "PRISMRPG.Label.miracles" }
}
*/
for (const v of Object.values(tabs)) {
v.active = this.tabGroups[v.group] === v.id
v.cssClass = v.active ? "active" : ""
@@ -90,6 +89,7 @@ export default class PrismRPGCharacterSheet extends PrismRPGActorSheet {
async _prepareContext() {
const context = await super._prepareContext()
context.tabs = this.#getTabs()
context.config = SYSTEM
return context
}
@@ -99,6 +99,14 @@ export default class PrismRPGCharacterSheet extends PrismRPGActorSheet {
const doc = this.document
switch (partId) {
case "main":
context.race = doc.itemTypes.race?.[0] || null
const classes = doc.itemTypes.class || []
// Create 3 class slots
context.classSlots = [
classes[0] || null,
classes[1] || null,
classes[2] || null
]
break
case "skills":
context.tab = context.tabs.skills
@@ -106,18 +114,14 @@ export default class PrismRPGCharacterSheet extends PrismRPGActorSheet {
context.racialAbilities = doc.itemTypes["racial-ability"]
context.vulnerabilities = doc.itemTypes.vulnerability
break
case "subattributes":
context.tab = context.tabs.subattributes
break
case "spells":
context.tab = context.tabs.spells
context.spells = doc.itemTypes.spell
context.hasSpells = context.spells.length > 0
break
/* Miracles disabled - Legacy from Lethal Fantasy
case "miracles":
context.tab = context.tabs.miracles
context.miracles = doc.itemTypes.miracle
context.hasMiracles = context.miracles.length > 0
break
*/
case "combat":
context.tab = context.tabs.combat
context.weapons = doc.itemTypes.weapon
@@ -155,64 +159,219 @@ export default class PrismRPGCharacterSheet extends PrismRPGActorSheet {
}
}
static async #onRangedAttackDefense(event, target) {
// Future use : const hasTarget = false
let roll = await PrismRPGRoll.promptRangedDefense({
actorId: this.actor.id,
actorName: this.actor.name,
actorImage: this.actor.img,
})
if (!roll) return null
await roll.toMessage({}, { rollMode: roll.options.rollMode })
}
static async #onRollInitiative(event, target) {
await this.document.system.rollInitiative()
}
static #onArmorHitPointsPlus(event, target) {
static async #onArmorHitPointsPlus(event, target) {
let armorHP = this.actor.system.combat.armorHitPoints
armorHP += 1
this.actor.update({ "system.combat.armorHitPoints": armorHP })
}
static #onArmorHitPointsMinus(event, target) {
static async #onArmorHitPointsMinus(event, target) {
let armorHP = this.actor.system.combat.armorHitPoints
armorHP -= 1
this.actor.update({ "system.combat.armorHitPoints": Math.max(armorHP, 0) })
}
static #onDivinityPointsPlus(event, target) {
let points = this.actor.system.divinityPoints.value
points += 1
points = Math.min(points, this.actor.system.divinityPoints.max)
this.actor.update({ "system.divinityPoints.value": points })
static async #onManaPointsPlus(event, target) {
let mana = this.actor.system.manaPoints.value
mana += 1
this.actor.update({ "system.manaPoints.value": Math.min(mana, this.actor.system.manaPoints.max) })
}
static #onDivinityPointsMinus(event, target) {
let points = this.actor.system.divinityPoints.value
points -= 1
points = Math.max(points, 0)
this.actor.update({ "system.divinityPoints.value": points })
static async #onManaPointsMinus(event, target) {
let mana = this.actor.system.manaPoints.value
mana -= 1
this.actor.update({ "system.manaPoints.value": Math.max(mana, 0) })
}
static #onAetherPointsPlus(event, target) {
let points = this.actor.system.aetherPoints.value
points += 1
points = Math.min(points, this.actor.system.aetherPoints.max)
this.actor.update({ "system.aetherPoints.value": points })
static async #onArmorPointsPlus(event, target) {
let armor = this.actor.system.armorPoints.value
armor += 1
this.actor.update({ "system.armorPoints.value": Math.min(armor, this.actor.system.armorPoints.max) })
}
static #onAetherPointsMinus(event, target) {
let points = this.actor.system.aetherPoints.value
points -= 1
points = Math.max(points, 0)
this.actor.update({ "system.aetherPoints.value": points })
static async #onArmorPointsMinus(event, target) {
let armor = this.actor.system.armorPoints.value
armor -= 1
this.actor.update({ "system.armorPoints.value": Math.max(armor, 0) })
}
static #onCreateEquipment(event, target) {
static async#onActionPointsPlus(event, target) {
let actionPoints = this.actor.system.actionPoints.value
actionPoints += 1
this.actor.update({ "system.actionPoints.value": Math.min(actionPoints, this.actor.system.actionPoints.max) })
}
static async#onActionPointsMinus(event, target) {
let actionPoints = this.actor.system.actionPoints.value
actionPoints -= 1
this.actor.update({ "system.actionPoints.value": Math.max(actionPoints, 0) })
}
static async#onHpPlus(event, target) {
let hp = this.actor.system.hp.value
hp += 1
this.actor.update({ "system.hp.value": Math.min(hp, this.actor.system.hp.max) })
}
static async#onHpMinus(event, target) {
let hp = this.actor.system.hp.value
hp -= 1
this.actor.update({ "system.hp.value": Math.max(hp, 0) })
}
static async #onCreateEquipment(event, target) {
}
static async #onPostItemToChat(event, target) {
console.log("PRISM RPG | PostItemToChat action triggered", { event: event, target: target })
// Try to find the item element from the clicked target or its parents
let itemElement = null
// First try with the target (the actual clicked element)
if (event.target) {
itemElement = event.target.closest('[data-item-id]')
}
// If not found, try with currentTarget (the element with the action)
if (!itemElement && event.currentTarget) {
itemElement = event.currentTarget.closest('[data-item-id]')
}
// If still not found, try with the target parameter
if (!itemElement && target) {
itemElement = target.closest('[data-item-id]')
}
console.log("PRISM RPG | Found item element", { itemElement: itemElement })
if (!itemElement) {
console.warn("PRISM RPG | Could not find item element for posting to chat")
return
}
const itemId = itemElement.dataset.itemId
if (!itemId) {
console.warn("PRISM RPG | Item ID not found for posting to chat")
return
}
const item = this.actor.items.get(itemId)
if (!item) {
console.warn("PRISM RPG | Item not found for posting to chat", { itemId: itemId })
return
}
// Create a chat message with the item data
const speaker = ChatMessage.getSpeaker({ actor: this.actor })
const content = await this.formatItemForChat(item)
await ChatMessage.create({
content: content,
speaker: speaker,
})
}
async formatItemForChat(item) {
// Format the item data for chat display
const itemTypeClass = `${item.type}-item`
let htmlContent = `
<div class="prismrpg-item-chat-card ${itemTypeClass}">
<div class="item-header">
<h3>${item.name}</h3>
<span class="item-type">${game.i18n.localize(`TYPES.Item.${item.type}`) || item.type}</span>
</div>
`
// Add item image if available
if (item.img && !item.img.includes('icons/svg/mystery-man.svg')) {
htmlContent += `<div class="item-image"><img src="${item.img}" alt="${item.name}"></div>`
}
// Add item description if available
if (item.system.description && item.system.description.trim() !== '') {
const enrichedDescription = await foundry.applications.ux.TextEditor.implementation.enrichHTML(item.system.description, { async: true })
htmlContent += `<div class="item-description">${enrichedDescription}</div>`
}
// Add specific item data based on item type
htmlContent += `<div class="item-details">`
switch (item.type) {
case 'weapon':
htmlContent += `
<div class="item-detail"><strong>Type:</strong> ${item.system.weaponType || 'Unknown'}</div>
<div class="item-detail"><strong>Damage:</strong> ${item.system.damage || 'N/A'}</div>
<div class="item-detail"><strong>APC:</strong> ${item.system.apc || 0}</div>
`
if (item.system.damageType) {
const damageTypes = []
if (item.system.damageType.piercing) damageTypes.push('Piercing')
if (item.system.damageType.bludgeoning) damageTypes.push('Bludgeoning')
if (item.system.damageType.slashing) damageTypes.push('Slashing')
if (damageTypes.length > 0) {
htmlContent += `<div class="item-detail"><strong>Damage Type:</strong> ${damageTypes.join('/')}</div>`
}
}
break
case 'armor':
htmlContent += `
<div class="item-detail"><strong>Armor Type:</strong> ${item.system.armorType || 'Unknown'}</div>
<div class="item-detail"><strong>Armor Points:</strong> ${item.system.armorPoints || 0}</div>
<div class="item-detail"><strong>APC:</strong> ${item.system.apc || 0}</div>
`
break
case 'shield':
htmlContent += `
<div class="item-detail"><strong>Shield Type:</strong> ${item.system.shieldType || 'Unknown'}</div>
<div class="item-detail"><strong>Armor Points:</strong> ${item.system.armorPoints || 0}</div>
<div class="item-detail"><strong>APC:</strong> ${item.system.apc || 0}</div>
`
break
case 'skill':
htmlContent += `
<div class="item-detail"><strong>Modifier:</strong> ${item.system.modifier || 0}</div>
<div class="item-detail"><strong>Core Skill:</strong> ${item.system.isCoreSkill ? 'Yes' : 'No'}</div>
`
break
case 'spell':
htmlContent += `
<div class="item-detail"><strong>Level:</strong> ${item.system.level || 'Unknown'}</div>
<div class="item-detail"><strong>Mana Cost:</strong> ${item.system.manaCost || 0}</div>
<div class="item-detail"><strong>Casting Time:</strong> ${item.system.castingTime || 'N/A'}</div>
`
break
case 'miracle':
htmlContent += `
<div class="item-detail"><strong>Prayer Time:</strong> ${item.system.prayerTime || 'N/A'}</div>
`
break
case 'equipment':
htmlContent += `
<div class="item-detail"><strong>Weight:</strong> ${item.system.weight || 'N/A'}</div>
`
break
default:
// For other item types, just show basic info
htmlContent += `<div class="item-detail"><strong>Item Type:</strong> ${item.type}</div>`
}
htmlContent += `</div></div>`
return htmlContent
}
_onRender(context, options) {
@@ -252,9 +411,11 @@ export default class PrismRPGCharacterSheet extends PrismRPGActorSheet {
async _onRoll(event, target) {
if (this.isEditMode) return
const rollType = event.target.dataset.rollType
let rollKey = event.target.dataset.rollKey;
let rollDice = event.target.dataset?.rollDice;
// Use closest to find the rollable element in case user clicked on a child
const rollableElement = event.target.closest('.rollable') || event.target
const rollType = rollableElement.dataset.rollType
let rollKey = rollableElement.dataset.rollKey;
let rollDice = rollableElement.dataset?.rollDice;
this.actor.prepareRoll(rollType, rollKey, rollDice)
+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)
}
}
+5 -3
View File
@@ -162,9 +162,11 @@ export default class PrismRPGMonsterSheet extends PrismRPGActorSheet {
async _onRoll(event, target) {
if (this.isEditMode) return
const rollType = event.target.dataset.rollType
let rollKey = event.target.dataset.rollKey
let rollDice = event.target.dataset?.rollDice || "0"
// Use closest to find the rollable element in case user clicked on a child
const rollableElement = event.target.closest('.rollable') || event.target
const rollType = rollableElement.dataset.rollType
let rollKey = rollableElement.dataset.rollKey
let rollDice = rollableElement.dataset?.rollDice || "0"
this.actor.system.prepareMonsterRoll(rollType, rollKey, rollDice)
}
}
+18 -18
View File
@@ -135,28 +135,28 @@ export const CHALLENGES = Object.freeze({
})
export const SAVES = Object.freeze({
will: {
id: "will",
label: "PRISMRPG.Character.will.label"
str: {
id: "str",
label: "PRISMRPG.Character.str.label"
},
dodge: {
id: "dodge",
label: "PRISMRPG.Character.dodge.label"
dex: {
id: "dex",
label: "PRISMRPG.Character.dex.label"
},
toughness: {
id: "toughness",
label: "PRISMRPG.Character.toughness.label"
con: {
id: "con",
label: "PRISMRPG.Character.con.label"
},
contagion: {
id: "contagion",
label: "PRISMRPG.Character.contagion.label"
int: {
id: "int",
label: "PRISMRPG.Character.int.label"
},
poison: {
id: "poison",
label: "PRISMRPG.Character.poison.label"
wis: {
id: "wis",
label: "PRISMRPG.Character.wis.label"
},
pain: {
id: "pain",
label: "PRISMRPG.Character.pain.label"
cha: {
id: "cha",
label: "PRISMRPG.Character.cha.label"
}
})
+75 -75
View File
@@ -9,7 +9,7 @@ export const TABLES = {
"damage": -7,
"attack": -4,
"challenge": -9,
"aether_points": -20,
"mana_points": -20,
"hp": -3,
"encumbered": 1,
"lift": 3,
@@ -20,7 +20,7 @@ export const TABLES = {
"damage": -6,
"attack": -4,
"challenge": -8,
"aether_points": -20,
"mana_points": -20,
"hp": -2,
"encumbered": 1,
"lift": 4,
@@ -31,7 +31,7 @@ export const TABLES = {
"damage": -5,
"attack": -3,
"challenge": -7,
"aether_points": -20,
"mana_points": -20,
"hp": -1,
"encumbered": 1,
"lift": 5,
@@ -42,7 +42,7 @@ export const TABLES = {
"damage": -4,
"attack": -3,
"challenge": -6,
"aether_points": -20,
"mana_points": -20,
"hp": -1,
"encumbered": 2,
"lift": 6,
@@ -53,7 +53,7 @@ export const TABLES = {
"damage": -3,
"attack": -2,
"challenge": -5,
"aether_points": -20,
"mana_points": -20,
"hp": 0,
"encumbered": 2,
"lift": 7,
@@ -64,7 +64,7 @@ export const TABLES = {
"damage": -2,
"attack": -1,
"challenge": -4,
"aether_points": -10,
"mana_points": -10,
"hp": 0,
"encumbered": 3,
"lift": 8,
@@ -75,7 +75,7 @@ export const TABLES = {
"damage": -2,
"attack": 0,
"challenge": -3,
"aether_points": -10,
"mana_points": -10,
"hp": 0,
"encumbered": 3,
"lift": 9,
@@ -86,7 +86,7 @@ export const TABLES = {
"damage": -1,
"attack": 0,
"challenge": -2,
"aether_points": 0,
"mana_points": 0,
"hp": 0,
"encumbered": 4,
"lift": 11,
@@ -97,7 +97,7 @@ export const TABLES = {
"damage": -1,
"attack": 0,
"challenge": -1,
"aether_points": 0,
"mana_points": 0,
"hp": 0,
"encumbered": 5,
"lift": 12,
@@ -108,7 +108,7 @@ export const TABLES = {
"damage": 0,
"attack": 0,
"challenge": 0,
"aether_points": 0,
"mana_points": 0,
"hp": 0,
"encumbered": 6,
"lift": 13,
@@ -119,7 +119,7 @@ export const TABLES = {
"damage": 0,
"attack": 0,
"challenge": 0,
"aether_points": 0,
"mana_points": 0,
"hp": 0,
"encumbered": 7,
"lift": 15,
@@ -130,7 +130,7 @@ export const TABLES = {
"damage": 1,
"attack": 0,
"challenge": 1,
"aether_points": 0,
"mana_points": 0,
"hp": 0,
"encumbered": 8,
"lift": 17,
@@ -141,7 +141,7 @@ export const TABLES = {
"damage": 1,
"attack": 0,
"challenge": 2,
"aether_points": 0,
"mana_points": 0,
"hp": 0,
"encumbered": 9,
"lift": 20,
@@ -152,7 +152,7 @@ export const TABLES = {
"damage": 2,
"attack": 1,
"challenge": 3,
"aether_points": 0,
"mana_points": 0,
"hp": 1,
"encumbered": 10,
"lift": 22,
@@ -163,7 +163,7 @@ export const TABLES = {
"damage": 3,
"attack": 1,
"challenge": 4,
"aether_points": 0,
"mana_points": 0,
"hp": 2,
"encumbered": 11,
"lift": 24,
@@ -174,7 +174,7 @@ export const TABLES = {
"damage": 4,
"attack": 2,
"challenge": 5,
"aether_points": 0,
"mana_points": 0,
"hp": 3,
"encumbered": 12,
"lift": 26,
@@ -185,7 +185,7 @@ export const TABLES = {
"damage": 5,
"attack": 2,
"challenge": 6,
"aether_points": 10,
"mana_points": 10,
"hp": 4,
"encumbered": 13,
"lift": 28,
@@ -196,7 +196,7 @@ export const TABLES = {
"damage": 6,
"attack": 3,
"challenge": 7,
"aether_points": 20,
"mana_points": 20,
"hp": 5,
"encumbered": 14,
"lift": 30,
@@ -207,7 +207,7 @@ export const TABLES = {
"damage": 7,
"attack": 3,
"challenge": 8,
"aether_points": 20,
"mana_points": 20,
"hp": 6,
"encumbered": 15,
"lift": 31,
@@ -218,7 +218,7 @@ export const TABLES = {
"damage": 8,
"attack": 4,
"challenge": 9,
"aether_points": 30,
"mana_points": 30,
"hp": 7,
"encumbered": 15,
"lift": 32,
@@ -229,7 +229,7 @@ export const TABLES = {
"damage": 9,
"attack": 4,
"challenge": 10,
"aether_points": 30,
"mana_points": 30,
"hp": 8,
"encumbered": 16,
"lift": 33,
@@ -240,7 +240,7 @@ export const TABLES = {
"damage": 10,
"attack": 5,
"challenge": 11,
"aether_points": 40,
"mana_points": 40,
"hp": 9,
"encumbered": 16,
"lift": 34,
@@ -251,7 +251,7 @@ export const TABLES = {
"damage": 12,
"attack": 5,
"challenge": 12,
"aether_points": 40,
"mana_points": 40,
"hp": 10,
"encumbered": 17,
"lift": 35,
@@ -262,7 +262,7 @@ export const TABLES = {
"damage": 14,
"attack": 5,
"challenge": 13,
"aether_points": 50,
"mana_points": 50,
"hp": 11,
"encumbered": 18,
"lift": 36,
@@ -273,7 +273,7 @@ export const TABLES = {
"damage": 16,
"attack": 6,
"challenge": 14,
"aether_points": 60,
"mana_points": 60,
"hp": 12,
"encumbered": 19,
"lift": 38,
@@ -286,7 +286,7 @@ export const TABLES = {
"attack": -5,
"defense": -3,
"development_points": 0,
"aether": -50,
"mana": -50,
"spell_cognition": 0,
"arkane_casting_mod": -4
},
@@ -295,7 +295,7 @@ export const TABLES = {
"attack": -4,
"defense": -3,
"development_points": 0,
"aether": -50,
"mana": -50,
"spell_cognition": 0,
"arkane_casting_mod": -4
},
@@ -304,7 +304,7 @@ export const TABLES = {
"attack": -3,
"defense": -3,
"development_points": 0,
"aether": -50,
"mana": -50,
"spell_cognition": 0.01,
"arkane_casting_mod": -3
},
@@ -313,7 +313,7 @@ export const TABLES = {
"attack": -2,
"defense": -2,
"development_points": 0,
"aether": -45,
"mana": -45,
"spell_cognition": 0.05,
"arkane_casting_mod": -3
},
@@ -322,7 +322,7 @@ export const TABLES = {
"attack": -2,
"defense": -2,
"development_points": 0,
"aether": -45,
"mana": -45,
"spell_cognition": 0.1,
"arkane_casting_mod": -2
},
@@ -331,7 +331,7 @@ export const TABLES = {
"attack": -2,
"defense": -2,
"development_points": 0,
"aether": -40,
"mana": -40,
"spell_cognition": 0.15,
"arkane_casting_mod": -2
},
@@ -340,7 +340,7 @@ export const TABLES = {
"attack": -1,
"defense": -1,
"development_points": 0,
"aether": -40,
"mana": -40,
"spell_cognition": 0.2,
"arkane_casting_mod": -1
},
@@ -349,7 +349,7 @@ export const TABLES = {
"attack": -1,
"defense": 0,
"development_points": 0,
"aether": -30,
"mana": -30,
"spell_cognition": 0.25,
"arkane_casting_mod": -1
},
@@ -358,7 +358,7 @@ export const TABLES = {
"attack": -1,
"defense": 0,
"development_points": 0,
"aether": -30,
"mana": -30,
"spell_cognition": 0.3,
"arkane_casting_mod": 0
},
@@ -367,7 +367,7 @@ export const TABLES = {
"attack": 0,
"defense": 0,
"development_points": 0,
"aether": -20,
"mana": -20,
"spell_cognition": 0.35,
"arkane_casting_mod": 0
},
@@ -376,7 +376,7 @@ export const TABLES = {
"attack": 0,
"defense": 0,
"development_points": 1,
"aether": -10,
"mana": -10,
"spell_cognition": 0.45,
"arkane_casting_mod": 0
},
@@ -385,7 +385,7 @@ export const TABLES = {
"attack": 1,
"defense": 0,
"development_points": 2,
"aether": 0,
"mana": 0,
"spell_cognition": 0.5,
"arkane_casting_mod": 1
},
@@ -394,7 +394,7 @@ export const TABLES = {
"attack": 1,
"defense": 0,
"development_points": 3,
"aether": 0,
"mana": 0,
"spell_cognition": 0.6,
"arkane_casting_mod": 1
},
@@ -403,7 +403,7 @@ export const TABLES = {
"attack": 1,
"defense": 1,
"development_points": 4,
"aether": 10,
"mana": 10,
"spell_cognition": 0.65,
"arkane_casting_mod": 2
},
@@ -412,7 +412,7 @@ export const TABLES = {
"attack": 2,
"defense": 1,
"development_points": 5,
"aether": 20,
"mana": 20,
"spell_cognition": 0.75,
"arkane_casting_mod": 2
},
@@ -421,7 +421,7 @@ export const TABLES = {
"attack": 2,
"defense": 1,
"development_points": 7,
"aether": 30,
"mana": 30,
"spell_cognition": 0.8,
"arkane_casting_mod": 3
},
@@ -430,7 +430,7 @@ export const TABLES = {
"attack": 2,
"defense": 1,
"development_points": 9,
"aether": 40,
"mana": 40,
"spell_cognition": 0.85,
"arkane_casting_mod": 3
},
@@ -439,7 +439,7 @@ export const TABLES = {
"attack": 3,
"defense": 2,
"development_points": 11,
"aether": 50,
"mana": 50,
"spell_cognition": 0.9,
"arkane_casting_mod": 4
},
@@ -448,7 +448,7 @@ export const TABLES = {
"attack": 3,
"defense": 2,
"development_points": 13,
"aether": 60,
"mana": 60,
"spell_cognition": 0.92,
"arkane_casting_mod": 5
},
@@ -457,7 +457,7 @@ export const TABLES = {
"attack": 3,
"defense": 2,
"development_points": 15,
"aether": 70,
"mana": 70,
"spell_cognition": 0.94,
"arkane_casting_mod": 6
},
@@ -466,7 +466,7 @@ export const TABLES = {
"attack": 4,
"defense": 2,
"development_points": 18,
"aether": 80,
"mana": 80,
"spell_cognition": 0.95,
"arkane_casting_mod": 7
},
@@ -475,7 +475,7 @@ export const TABLES = {
"attack": 4,
"defense": 3,
"development_points": 21,
"aether": 90,
"mana": 90,
"spell_cognition": 0.96,
"arkane_casting_mod": 7
},
@@ -484,7 +484,7 @@ export const TABLES = {
"attack": 4,
"defense": 3,
"development_points": 24,
"aether": 100,
"mana": 100,
"spell_cognition": 0.97,
"arkane_casting_mod": 8
},
@@ -493,7 +493,7 @@ export const TABLES = {
"attack": 5,
"defense": 3,
"development_points": 27,
"aether": 110,
"mana": 110,
"spell_cognition": 0.98,
"arkane_casting_mod": 8
},
@@ -502,7 +502,7 @@ export const TABLES = {
"attack": 5,
"defense": 4,
"development_points": 30,
"aether": 125,
"mana": 125,
"spell_cognition": 0.99,
"arkane_casting_mod": 9
}
@@ -688,7 +688,7 @@ export const TABLES = {
{
"value": 1,
"hp ": 1,
"aether_points": -50,
"mana_points": -50,
"pain_save": 1,
"toughness_save": -5,
"stabilization_dice": "D6",
@@ -698,7 +698,7 @@ export const TABLES = {
"value": 2,
"hp ": 2,
"aether_points": -40,
"mana_points": -40,
"pain_save": 2,
"toughness_saave": -4,
"stabilization_dice": "D6",
@@ -708,7 +708,7 @@ export const TABLES = {
"value": 3,
"hp ": 3,
"aether_points": -35,
"mana_points": -35,
"pain_save": 2,
"toughness_save": -3,
"stabilization_dice": "D6",
@@ -718,7 +718,7 @@ export const TABLES = {
"value": 4,
"hp ": 4,
"aether_points": -30,
"mana_points": -30,
"pain_save": 2,
"toughness_save": -3,
"stabilization_dice": "D6",
@@ -728,7 +728,7 @@ export const TABLES = {
"value": 5,
"hp ": 5,
"aether_points": -25,
"mana_points": -25,
"pain_save": 3,
"toughness_save": -2,
"stabilization_dice": "D6",
@@ -737,7 +737,7 @@ export const TABLES = {
{
"value": 6,
"hp ": 6,
"aether_points": -20,
"mana_points": -20,
"pain_save": 3,
"toughness_save": -2,
"stabilization_dice": "D6",
@@ -746,7 +746,7 @@ export const TABLES = {
{
"value": 7,
"hp ": 7,
"aether_points": -15,
"mana_points": -15,
"pain_save": 3,
"toughness_save": -1,
"stabilization_dice": "D6",
@@ -756,7 +756,7 @@ export const TABLES = {
"value": 8,
"hp ": 8,
"aether_points": -10,
"mana_points": -10,
"pain_save": 4,
"toughness_save": -1,
"stabilization_dice": "D8",
@@ -766,7 +766,7 @@ export const TABLES = {
"value": 9,
"hp ": 9,
"aether_points": -5,
"mana_points": -5,
"pain_save": 4,
"toughness_save": 0,
"stabilization_dice": "D8",
@@ -776,7 +776,7 @@ export const TABLES = {
"value": 10,
"hp ": 10,
"aether_points": 0,
"mana_points": 0,
"pain_save": 5,
"toughness_save": 0,
"stabilization_dice": "D8",
@@ -786,7 +786,7 @@ export const TABLES = {
"value": 11,
"hp ": 11,
"aether_points": 0,
"mana_points": 0,
"pain_save": 5,
"toughness_save": 0,
"stabilization_dice": "D8",
@@ -796,7 +796,7 @@ export const TABLES = {
"value": 12,
"hp ": 12,
"aether_points": 5,
"mana_points": 5,
"pain_save": 6,
"toughness_save": 0,
"stabilization_dice": "D10",
@@ -806,7 +806,7 @@ export const TABLES = {
"value": 13,
"hp ": 13,
"aether_points": 10,
"mana_points": 10,
"pain_save": 7,
"toughness_save": 1,
"stabilization_dice": "D10",
@@ -816,7 +816,7 @@ export const TABLES = {
"value": 14,
"hp ": 14,
"aether_points": 20,
"mana_points": 20,
"pain_save": 7,
"toughness_save": 2,
"stabilization_dice": "D10",
@@ -826,7 +826,7 @@ export const TABLES = {
"value": 15,
"hp ": 15,
"aether_points": 30,
"mana_points": 30,
"pain_save": 8,
"toughness_save": 3,
"stabilization_dice": "D12",
@@ -836,7 +836,7 @@ export const TABLES = {
"value": 16,
"hp ": 16,
"aether_points": 40,
"mana_points": 40,
"pain_save": 8,
"toughness_save": 4,
"stabilization_dice": "D12",
@@ -846,7 +846,7 @@ export const TABLES = {
"value": 17,
"hp ": 17,
"aether_points": 50,
"mana_points": 50,
"pain_save": 9,
"toughness_save": 5,
"stabilization_dice": "D12",
@@ -856,7 +856,7 @@ export const TABLES = {
"value": 18,
"hp ": 18,
"aether_points": 60,
"mana_points": 60,
"pain_save": 9,
"toughness_save": 6,
"stabilization_dice": "D12",
@@ -866,7 +866,7 @@ export const TABLES = {
"value": 19,
"hp ": 19,
"aether_points": 70,
"mana_points": 70,
"pain_save": 10,
"toughness_save": 7,
"stabilization_dice": "D12",
@@ -876,7 +876,7 @@ export const TABLES = {
"value": 20,
"hp ": 20,
"aether_points": 80,
"mana_points": 80,
"pain_save": 10,
"toughness_save": 8,
"stabilization_dice": "D12",
@@ -886,7 +886,7 @@ export const TABLES = {
"value": 21,
"hp ": 21,
"aether_points": 90,
"mana_points": 90,
"pain_save": 11,
"toughness_save": 9,
"stabilization_dice": "D20",
@@ -896,7 +896,7 @@ export const TABLES = {
"value": 22,
"hp ": 22,
"aether_points": 100,
"mana_points": 100,
"pain_save": 11,
"toughness_save": 10,
"stabilization_dice": "D20",
@@ -906,7 +906,7 @@ export const TABLES = {
"value": 23,
"hp ": 23,
"aether_points": 110,
"mana_points": 110,
"pain_save": 12,
"toughness_save": 11,
"stabilization_dice": "D20",
@@ -916,7 +916,7 @@ export const TABLES = {
"value": 24,
"hp ": 24,
"aether_points": 120,
"mana_points": 120,
"pain_save": 12,
"toughness_save": 12,
"stabilization_dice": "D20",
@@ -925,7 +925,7 @@ export const TABLES = {
{
"value": 25,
"hp ": 25,
"aether_points": 130,
"mana_points": 130,
"pain_save": 13,
"toughness_save": 13,
"stabilization_dice": "D20",
+50 -36
View File
@@ -14,35 +14,35 @@ export const SYSTEM_ID = "fvtt-prism-rpg"
export const DEV_MODE = false
export const MONEY = {
tinbit: {
id: "tinbit",
abbrev: "tb",
label: "PRISMRPG.Money.Tinbits",
coppercoin: {
id: "coppercoin",
abbrev: "cc",
label: "PRISMRPG.Money.CopperCoin",
valuetb: 1
},
copper: {
id: "copper",
abbrev: "cp",
label: "PRISMRPG.Money.Coppers",
silvercoin: {
id: "silvercoin",
abbrev: "sc",
label: "PRISMRPG.Money.SilverCoin",
valuetb: 5
},
goldcoin: {
id: "goldcoin",
abbrev: "gc",
label: "PRISMRPG.Money.GoldCoin",
valuetb: 10
},
silver: {
id: "silver",
abbrev: "sp",
label: "PRISMRPG.Money.Silvers",
note: {
id: "note",
abbrev: "nt",
label: "PRISMRPG.Money.Note",
valuetb: 100
},
gold: {
id: "gold",
abbrev: "gp",
label: "PRISMRPG.Money.Golds",
steam: {
id: "steam",
abbrev: "st",
label: "PRISMRPG.Money.Steam",
valuetb: 1000
},
platinum: {
id: "platinum",
abbrev: "pp",
label: "PRISMRPG.Money.Platinums",
valuetb: 10000
}
}
@@ -56,10 +56,10 @@ export const MORTAL_CHOICES = {
"halflings": { label: "Halfling", id: "halflings", defenseBonus: 2 }
}
export const FAVOR_CHOICES = {
export const ADVANTAGE_CHOICES = {
"none": { label: "None", value: "none" },
"favor": { label: "Favor", value: "favor" },
"disfavor": { label: "Disfavor", value: "disfavor" }
"advantage": { label: "Advantage", value: "advantage" },
"disadvantage": { label: "Disadvantage", value: "disadvantage" }
}
export const MOVEMENT_CHOICES = {
@@ -156,7 +156,7 @@ export const INITIATIVE_DICE_CHOICES_PER_CLASS = {
{ "name": "Aware and know exactly where the enemy is (1D4)", "value": "1D4" }*/
],
"magicuser": [
{ "name": "Sleeping to recover Aether Points (2D20)", "value": "2D20" },
{ "name": "Sleeping to recover Mana Points (2D20)", "value": "2D20" },
{ "name": "Asleep or totally distracted (1D20)", "value": "1D20" },
{ "name": "Awake but unsuspecting (1D12)", "value": "1D12" },
{ "name": "Declared Ready on Alert (1)", "value": "1" },
@@ -261,15 +261,29 @@ export const CHOICE_MODIFIERS = {
}
export const ASCII = `
······················································································································
: :
:@@@ @@@@@@@@ @@@@@@@ @@@ @@@ @@@@@@ @@@ @@@@@@@@ @@@@@@ @@@ @@@ @@@@@@@ @@@@@@ @@@@@@ @@@ @@@ :
:@@! @@! @!! @@! @@@ @@! @@@ @@! @@! @@! @@@ @@!@!@@@ @!! @@! @@@ !@@ @@! !@@ :
:@!! @!!!:! @!! @!@!@!@! @!@!@!@! @!! @!!!:! @!@!@!@! @!@@!!@! @!! @!@!@!@! !@@!! !@!@! :
:!!: !!: !!: !!: !!! !!: !!! !!: !!: !!: !!! !!: !!! !!: !!: !!! !:! !!: :
:: ::.: : : :: :: : : : : : : : : ::.: : : : : : :: : : : : : ::.: : .: :
: :
······················································································································
8888888b. 8888888b. 8888888 .d8888b. 888b d888
888 Y88b 888 Y88b 888 d88P Y88b 8888b d8888
888 888 888 888 888 Y88b. 88888b.d88888
888 d88P 888 d88P 888 "Y888b. 888Y88888P888
8888888P" 8888888P" 888 "Y88b. 888 Y888P 888
888 888 T88b 888 "888 888 Y8P 888
888 888 T88b 888 Y88b d88P 888 " 888
888 888 T88b 8888888 "Y8888P" 888 888
8888888b. 8888888b. .d8888b.
888 Y88b 888 Y88b d88P Y88b
888 888 888 888 888 888
888 d88P 888 d88P 888
8888888P" 8888888P" 888 88888
888 T88b 888 888 888
888 T88b 888 Y88b d88P
888 T88b 888 "Y8888P88
`
/**
@@ -327,7 +341,7 @@ export const SYSTEM = {
MOVE_DIRECTION_CHOICES,
SIZE_CHOICES,
RANGE_CHOICES,
FAVOR_CHOICES,
ADVANTAGE_CHOICES,
ATTACKER_AIM_CHOICES,
MORTAL_CHOICES,
MIRACLE_TYPES,
+82 -83
View File
@@ -1,4 +1,6 @@
import PrismRPGUtils from "../utils.mjs"
import PrismRPGRoll from "./roll.mjs"
export default class PrismRPGActor extends Actor {
static async create(data, options) {
@@ -42,31 +44,12 @@ export default class PrismRPGActor extends Actor {
}
/* *************************************************/
// This method is no longer needed in D&D 5e style system
// Weapon proficiency is handled through character class/race features
getBestWeaponClassSkill(skills, rollType, multiplier = 1.0) {
let maxValue = 0
let goodSkill = skills[0]
for (let s of skills) {
if (rollType === "weapon-attack") {
if (s.system.weaponBonus.attack > maxValue) {
maxValue = Number(s.system.weaponBonus.attack)
goodSkill = s
}
}
if (rollType === "weapon-defense") {
if (s.system.weaponBonus.defense > maxValue) {
maxValue = Number(s.system.weaponBonus.defense)
goodSkill = s
}
}
if (rollType.includes("weapon-damage")) {
if (s.system.weaponBonus.damage > maxValue) {
maxValue = Number(s.system.weaponBonus.damage)
goodSkill = s
}
}
}
goodSkill.weaponSkillModifier = maxValue * multiplier
return goodSkill
// In D&D 5e, we don't need weapon skills with bonuses
// Just return the first skill (or could be removed entirely)
return skills[0]
}
/* *************************************************/
@@ -79,17 +62,39 @@ export default class PrismRPGActor extends Actor {
}
/* *************************************************/
async prepareRoll(rollType, rollKey, rollDice ) {
async prepareRoll(rollType, rollKey, rollDice) {
console.log("Preparing roll", rollType, rollKey, rollDice)
let rollTarget
switch (rollType) {
case "characteristic":
if (!this.system.characteristics || !this.system.characteristics[rollKey]) {
ui.notifications.error(`Characteristic ${rollKey} not found`)
return
}
rollTarget = {
...foundry.utils.duplicate(this.system.characteristics[rollKey]),
rollKey: rollKey,
name: game.i18n.localize(`PRISMRPG.Label.${rollKey}`)
}
break
case "sub-attribute":
if (!this.system.subAttributes || !this.system.subAttributes[rollKey]) {
ui.notifications.error(`Sub-attribute ${rollKey} not found`)
return
}
rollTarget = {
...foundry.utils.duplicate(this.system.subAttributes[rollKey]),
rollKey: rollKey,
name: game.i18n.localize(SYSTEM.SUB_ATTRIBUTES[rollKey].label)
}
break
case "granted":
rollTarget = {
name: rollKey,
formula: foundry.utils.duplicate(this.system.granted[rollKey]),
rollKey: rollKey
}
if ( rollTarget.formula === "" || rollTarget.formula === undefined) {
if (rollTarget.formula === "" || rollTarget.formula === undefined) {
rollTarget.formula = 0
}
break;
@@ -101,6 +106,10 @@ export default class PrismRPGActor extends Actor {
rollTarget = foundry.utils.duplicate(this.system.saves[rollKey])
rollTarget.rollKey = rollKey
rollTarget.rollDice = rollDice
// Pass the characteristic value for D&D 5e modifier calculation
rollTarget.characteristicValue = this.system.characteristics[rollKey].value
// The save bonus is the proficiency modifier (value stored in saves)
rollTarget.saveBonus = this.system.saves[rollKey].value
break
case "spell":
rollTarget = this.items.find((i) => i.type === "spell" && i.id === rollKey)
@@ -110,86 +119,76 @@ export default class PrismRPGActor extends Actor {
rollTarget = this.items.find((i) => i.type === "miracle" && i.id === rollKey)
rollTarget.rollKey = rollKey
break
case "skill":
case "skill": {
rollTarget = this.items.find((i) => i.type === "skill" && i.id === rollKey)
rollTarget.rollKey = rollKey
if (rollTarget.system.category === "weapon") {
ui.notifications.warn(game.i18n.localize("PRISMRPG.Notifications.rollFromWeapon"))
return
}
// Get the two sub-attributes for this skill
const subAttr1 = rollTarget.system.subAttribute1 || "prowess"
const subAttr2 = rollTarget.system.subAttribute2 || "initiative"
// Store both sub-attribute values for the dialog to choose from
rollTarget.subAttribute1 = subAttr1
rollTarget.subAttribute2 = subAttr2
rollTarget.subAttribute1Value = this.system.subAttributes?.[subAttr1]?.value || 0
rollTarget.subAttribute2Value = this.system.subAttributes?.[subAttr2]?.value || 0
rollTarget.subAttribute1Label = game.i18n.localize(SYSTEM.SUB_ATTRIBUTES?.[subAttr1]?.label || subAttr1)
rollTarget.subAttribute2Label = game.i18n.localize(SYSTEM.SUB_ATTRIBUTES?.[subAttr2]?.label || subAttr2)
rollTarget.proficiencyBonus = rollTarget.system.modifier
break
}
case "spell-attack":
case "spell-power":
case "spell-cast":
case "miracle-attack":
case "miracle-power":
rollTarget = this.items.find((i) => (i.type === "miracle" || i.type == "spell") && i.id === rollKey)
rollTarget = this.items.find((i) => (i.type === "miracle" || i.type === "spell") && i.id === rollKey)
rollTarget.rollKey = rollKey
break
case "shield-roll": {
rollTarget = this.items.find((i) => i.type === "shield" && i.id === rollKey)
let shieldSkill = this.items.find((i) => i.type === "skill" && i.name.toLowerCase() === rollTarget.name.toLowerCase())
rollTarget.skill = shieldSkill
rollTarget.rollKey = rollKey
}
rollTarget = this.items.find((i) => i.type === "shield" && i.id === rollKey)
let shieldSkill = this.items.find((i) => i.type === "skill" && i.name.toLowerCase() === rollTarget.name.toLowerCase())
rollTarget.skill = shieldSkill
rollTarget.rollKey = rollKey
}
break;
case "weapon-damage-small":
case "weapon-damage-medium":
case "weapon-attack":
case "weapon-defense": {
let weapon = this.items.find((i) => i.type === "weapon" && i.id === rollKey)
let skill
let skills = this.items.filter((i) => i.type === "skill" && i.name.toLowerCase() === weapon.name.toLowerCase())
if (skills.length > 0) {
skill = this.getBestWeaponClassSkill(skills, rollType, 1.0)
} else {
skills = this.items.filter((i) => i.type === "skill" && i.name.toLowerCase().replace(" skill", "") === weapon.name.toLowerCase())
if (skills.length > 0) {
skill = this.getBestWeaponClassSkill(skills, rollType, 1.0)
} else {
skills = this.items.filter((i) => i.type === "skill" && i.system.weaponClass === weapon.system.weaponClass)
if (skills.length > 0) {
skill = this.getBestWeaponClassSkill(skills, rollType, 0.5)
} else {
skills = this.items.filter((i) => i.type === "skill" && i.system.weaponClass.includes(SYSTEM.WEAPON_CATEGORIES[weapon.system.weaponClass]))
if (skills.length > 0) {
skill = this.getBestWeaponClassSkill(skills, rollType, 0.25)
} else {
ui.notifications.warn(game.i18n.localize("PRISMRPG.Notifications.skillNotFound"))
return
}
}
}
}
if (!weapon || !skill) {
console.error("Weapon or skill not found", weapon, skill)
ui.notifications.warn(game.i18n.localize("PRISMRPG.Notifications.skillNotFound"))
return
}
rollTarget = skill
rollTarget.weapon = weapon
rollTarget.weaponSkillModifier = skill.weaponSkillModifier
rollTarget.rollKey = rollKey
rollTarget.combat = foundry.utils.duplicate(this.system.combat)
if ( rollType === "weapon-damage-small" || rollType === "weapon-damage-medium") {
rollTarget.grantedDice = this.system.granted.damageDice
}
if ( rollType === "weapon-attack") {
rollTarget.grantedDice = this.system.granted.attackDice
}
if ( rollType === "weapon-defense") {
rollTarget.grantedDice = this.system.granted.defenseDice
}
case "weapon-attack": {
let weapon = this.items.find((i) => i.type === "weapon" && i.id === rollKey)
if (!weapon) {
console.error("Weapon not found", weapon, skill)
ui.notifications.warn(game.i18n.localize("PRISMRPG.Notifications.skillNotFound"))
return
}
// Create a plain object for rollTarget to ensure weapon data is preserved
rollTarget = {
weapon: weapon.toObject(),
rollKey: rollKey,
combat: foundry.utils.duplicate(this.system.combat),
strMod: PrismRPGRoll.getAbilityModifier(this.system.characteristics.str.value),
dexMod: PrismRPGRoll.getAbilityModifier(this.system.characteristics.dex.value)
}
}
break
default:
ui.notifications.error(game.i18n.localize("PRISMRPG.Notifications.rollTypeNotFound") + String(rollType))
break
return
}
// In all cases
rollTarget.magicUser = this.system.biodata.magicUser
rollTarget.actorModifiers = foundry.utils.duplicate(this.system.modifiers)
rollTarget.actorLevel = this.system.biodata.level
// In all cases - verify rollTarget exists
if (!rollTarget) {
console.error("Roll target is undefined for rollType:", rollType)
return
}
rollTarget.magicUser = this.system.biodata?.magicUser || false
rollTarget.actorModifiers = this.system.modifiers ? foundry.utils.duplicate(this.system.modifiers) : {}
rollTarget.actorLevel = this.system.biodata?.level || 1
await this.system.roll(rollType, rollTarget)
}
+327 -981
View File
File diff suppressed because it is too large Load Diff
-2
View File
@@ -6,9 +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 PrismRPGVulnerability } from "./vulnerability.mjs"
export { default as PrismRPGEquipment } from "./equipment.mjs"
export { default as PrismRPGMiracle } from "./miracle.mjs"
export { default as PrismRPGRace } from "./race.mjs"
export { default as PrismRPGClass } from "./class.mjs"
export { default as PrismRPGCharacterPath } from "./character-path.mjs"
+26 -1
View File
@@ -24,7 +24,7 @@ export default class PrismRPGArmor extends foundry.abstract.TypeDataModel {
schema.isHelmet = new fields.BooleanField({ required: true, initial: false })
schema.cost = new fields.NumberField({ required: true, initial: 0, min: 0 })
schema.money = new fields.StringField({ required: true, initial: "tinbit", choices: SYSTEM.MONEY })
schema.money = new fields.StringField({ required: true, initial: "coppercoin", choices: SYSTEM.MONEY })
return schema
}
@@ -32,4 +32,29 @@ export default class PrismRPGArmor extends foundry.abstract.TypeDataModel {
/** @override */
static LOCALIZATION_PREFIXES = ["PRISMRPG.Armor"]
static migrateData(data) {
// Migrate old money types to new ones
if (data?.money) {
const moneyMigration = {
"tinbit": "coppercoin",
"copper": "coppercoin",
"silver": "silvercoin",
"gold": "goldcoin",
"platinum": "note"
}
if (moneyMigration[data.money]) {
data.money = moneyMigration[data.money]
}
// If still invalid, default to coppercoin
if (!SYSTEM.MONEY[data.money]) {
console.warn(`Prism RPG | Migrate armor: Invalid money type "${data.money}", defaulting to coppercoin`)
data.money = "coppercoin"
}
}
return super.migrateData(data)
}
}
+82 -143
View File
@@ -43,6 +43,20 @@ export default class PrismRPGCharacter extends foundry.abstract.TypeDataModel {
}, {}),
)
// Sub-Attributes (derived from two parent characteristics)
const subAttributeField = (label) => {
const schema = {
value: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 })
}
return new fields.SchemaField(schema, { label })
}
schema.subAttributes = new fields.SchemaField(
Object.values(SYSTEM.SUB_ATTRIBUTES).reduce((obj, subAttr) => {
obj[subAttr.id] = subAttributeField(subAttr.label)
return obj
}, {}),
)
// Challenges
const challengeField = (label) => {
const schema = {
@@ -67,50 +81,21 @@ export default class PrismRPGCharacter extends foundry.abstract.TypeDataModel {
schema.hp = new fields.SchemaField({
value: new fields.NumberField({ ...requiredInteger, initial: 1, min: 0 }),
max: new fields.NumberField({ ...requiredInteger, initial: 1, min: 0 }),
painDamage: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
wounds: new fields.ArrayField(new fields.SchemaField(woundFieldSchema), {
initial: [{ description: "", value: 0, duration: 0 }, { description: "", value: 0, duration: 0 },
{ description: "", value: 0, duration: 0 }, { description: "", value: 0, duration: 0 }, { description: "", value: 0, duration: 0 }, { description: "", value: 0, duration: 0 },
{ description: "", value: 0, duration: 0 }, { description: "", value: 0, duration: 0 }], min: 8
}),
damageResistance: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 })
temp: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 })
})
schema.perception = new fields.SchemaField({
schema.armorPoints = new fields.SchemaField({
value: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
bonus: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 })
})
schema.grit = new fields.SchemaField({
starting: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
earned: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
current: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 })
})
schema.luck = new fields.SchemaField({
earned: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
current: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 })
})
schema.granted = new fields.SchemaField({
attackDice: new fields.StringField({ required: true, nullable: false, initial: "0", choices: SYSTEM.GRANTED_DICE_CHOICES }),
defenseDice: new fields.StringField({ required: true, nullable: false, initial: "0", choices: SYSTEM.GRANTED_DICE_CHOICES }),
damageDice: new fields.StringField({ required: true, nullable: false, initial: "0", choices: SYSTEM.GRANTED_DICE_CHOICES })
max: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 })
})
schema.movement = new fields.SchemaField({
walk: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
jog: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
sprint: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
run: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
armorAdjust: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
})
schema.jump = new fields.SchemaField({
broad: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
running: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
vertical: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
schema.actionPoints = new fields.SchemaField({
value: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
max: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 })
})
schema.biodata = new fields.SchemaField({
class: new fields.StringField({ required: true, initial: "untrained", choices: SYSTEM.CHAR_CLASSES }),
level: new fields.NumberField({ ...requiredInteger, initial: 1, min: 1 }),
mortal: new fields.StringField({ required: true, initial: "mankind", choices: SYSTEM.MORTAL_CHOICES }),
alignment: new fields.StringField({ required: true, nullable: false, initial: "" }),
age: new fields.NumberField({ ...requiredInteger, initial: 15, min: 6 }),
height: new fields.NumberField({ ...requiredInteger, initial: 170, min: 10 }),
@@ -118,31 +103,14 @@ export default class PrismRPGCharacter extends foundry.abstract.TypeDataModel {
eyes: new fields.StringField({ required: true, nullable: false, initial: "" }),
hair: new fields.StringField({ required: true, nullable: false, initial: "" }),
magicUser: new fields.BooleanField({ initial: false }),
clericUser: new fields.BooleanField({ initial: false }),
hpPerLevel: new fields.StringField({ required: true, nullable: false, initial: "" }),
})
schema.modifiers = new fields.SchemaField({
levelSpellModifier: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
saveModifier: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
levelMiracleModifier: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
intSpellModifier: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
chaMiracleModifier: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
clericUser: new fields.BooleanField({ initial: false })
})
schema.developmentPoints = new fields.SchemaField({
total: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
remaining: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 })
})
schema.spellMiraclePoints = new fields.SchemaField({
total: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
used: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 })
})
schema.aetherPoints = new fields.SchemaField({
max: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
value: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 })
})
schema.divinityPoints = new fields.SchemaField({
schema.manaPoints = new fields.SchemaField({
max: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
value: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 })
})
@@ -168,21 +136,6 @@ export default class PrismRPGCharacter extends foundry.abstract.TypeDataModel {
}, {}),
)
// Core Skill system (Prism RPG)
schema.coreSkill = new fields.SchemaField({
skill: new fields.StringField({
required: true,
initial: "",
choices: SYSTEM.CORE_SKILLS_CHOICES,
label: "Selected Core Skill"
}),
attributeChoice: new fields.StringField({
required: true,
initial: "",
label: "Attribute Choice for +2 Bonus"
})
})
return schema
}
@@ -216,43 +169,49 @@ export default class PrismRPGCharacter extends foundry.abstract.TypeDataModel {
prepareDerivedData() {
super.prepareDerivedData();
let grit = 0
for (let c in this.characteristics) {
if (SYSTEM.CHARACTERISTICS_MAJOR[c.id]) {
grit += this.characteristics[c].value
}
// Calculate action points max based on level
const level = this.biodata.level
let actionPointsMax = 4
if (level >= 3 && level <= 5) {
actionPointsMax = 5
} else if (level >= 6 && level <= 8) {
actionPointsMax = 6
} else if (level >= 9 && level <= 10) {
actionPointsMax = 7
}
// Set max action points (but don't override if already set to a higher value)
if (this.actionPoints.max < actionPointsMax) {
this.actionPoints.max = actionPointsMax
}
this.modifiers.saveModifier = Math.floor((Number(this.biodata.level) / 5))
this.modifiers.levelSpellModifier = Math.floor((Number(this.biodata.level) / 5))
this.modifiers.levelMiracleModifier = Math.floor((Number(this.biodata.level) / 5))
// Calculate sub-attributes from parent characteristics
// Sub-attribute = lowest ability modifier between the two parent characteristics
for (let subAttrKey in SYSTEM.SUB_ATTRIBUTES) {
const subAttr = SYSTEM.SUB_ATTRIBUTES[subAttrKey]
const parent1Value = this.characteristics[subAttr.parents[0]].value
const parent2Value = this.characteristics[subAttr.parents[1]].value
// Calculate D&D 5e style ability modifiers: (ability - 10) / 2
const parent1Bonus = Math.floor((parent1Value - 10) / 2)
const parent2Bonus = Math.floor((parent2Value - 10) / 2)
// Take the lowest modifier
this.subAttributes[subAttrKey].value = Math.min(parent1Bonus, parent2Bonus)
}
this.grit.starting = Math.round(grit / 6)
// Calculate save modifier locally (not stored)
const saveModifier = Math.floor((Number(this.biodata.level) / 5))
let strDef = SYSTEM.CHARACTERISTICS_TABLES.str.find(s => s.value === this.characteristics.str.value)
this.challenges.str.value = strDef.challenge
let intDef = SYSTEM.CHARACTERISTICS_TABLES.int.find(s => s.value === this.characteristics.int.value)
this.modifiers.intSpellModifier = intDef.arkane_casting_mod
let dexDef = SYSTEM.CHARACTERISTICS_TABLES.dex.find(s => s.value === this.characteristics.dex.value)
this.challenges.agility.value = dexDef.challenge
this.saves.dodge.value = dexDef.dodge + this.modifiers.saveModifier
let wisDef = SYSTEM.CHARACTERISTICS_TABLES.wis.find(s => s.value === this.characteristics.wis.value)
this.saves.will.value = wisDef.willpower_save + this.modifiers.saveModifier
let chaDef = SYSTEM.CHARACTERISTICS_TABLES.cha.find(s => s.value === this.characteristics.cha.value)
this.modifiers.chaMiracleModifier = chaDef.divine_miracle_bonus
let conDef = SYSTEM.CHARACTERISTICS_TABLES.con.find(s => s.value === this.characteristics.con.value)
this.saves.pain.value = conDef.pain_save + this.modifiers.saveModifier
this.saves.toughness.value = conDef.toughness_save + this.modifiers.saveModifier
this.challenges.dying.value = conDef.stabilization_dice
this.saves.contagion.value = this.characteristics.con.value;// + this.modifiers.saveModifier
this.saves.poison.value = this.characteristics.con.value; // + this.modifiers.saveModifier
this.combat.attackModifier = 0
for (let chaKey of SYSTEM.CHARACTERISTIC_ATTACK) {
let chaDef = SYSTEM.CHARACTERISTICS_TABLES[chaKey].find(s => s.value === this.characteristics[chaKey].value)
@@ -302,66 +261,46 @@ export default class PrismRPGCharacter extends foundry.abstract.TypeDataModel {
await roll.toMessage({}, { rollMode: roll.options.rollMode })
}
/**
* Rolls initiative for the character: 1d20 + initiative modifier
* @param {string} combatId - Optional combat ID to update
* @param {string} combatantId - Optional combatant ID to update
* @returns {Promise<Roll|null>} The initiative roll or null if cancelled
*/
async rollInitiative(combatId = undefined, combatantId = undefined) {
const hasTarget = false
let actorClass = this.biodata.class;
// Get the initiative sub-attribute modifier
const initiativeModifier = this.subAttributes.initiative.value
let wisDef = SYSTEM.CHARACTERISTICS_TABLES.wis.find((c) => c.value === this.characteristics.wis.value)
let maxInit = Number(wisDef.init_cap) || 1000
// Create the roll formula: 1d20 + initiative modifier
const formula = `1d20 + ${initiativeModifier}`
let roll = await PrismRPGRoll.promptInitiative({
actorId: this.parent.id,
actorName: this.parent.name,
actorImage: this.parent.img,
combatId,
combatantId,
actorClass,
maxInit,
// Roll the initiative
let initRoll = new Roll(formula)
await initRoll.evaluate()
// Create the chat message
let msg = await initRoll.toMessage({
flavor: `${game.i18n.localize("PRISMRPG.Label.initiative")} - ${this.parent.name}`,
speaker: ChatMessage.getSpeaker({ actor: this.parent })
})
if (!roll) return null
await roll.toMessage({}, { rollMode: roll.options.rollMode })
}
async rollProgressionDice(combatId, combatantId, rollProgressionCount) {
// Get all weapons from the actor
let weapons = this.parent.items.filter(i => i.type === "weapon" && i.system.weaponType === "melee")
let weaponsChoices = weapons.map(w => { return { id: w.id, name: `${w.name} (${w.system.combatProgressionDice.toUpperCase()})`, combatProgressionDice: w.system.combatProgressionDice.toUpperCase() } })
let rangeWeapons = this.parent.items.filter(i => i.type === "weapon" && i.system.weaponType === "ranged")
for (let w of rangeWeapons) {
weaponsChoices.push({ id: `${w.id}simpleAim`, name: `${w.name} (Simple Aim: ${w.system.speed.simpleAim.toUpperCase()})`, combatProgressionDice: w.system.speed.simpleAim.toUpperCase() })
weaponsChoices.push({ id: `${w.id}carefulAim`, name: `${w.name} (Careful Aim: ${w.system.speed.carefulAim.toUpperCase()})`, combatProgressionDice: w.system.speed.carefulAim.toUpperCase() })
weaponsChoices.push({ id: `${w.id}focusedAim`, name: `${w.name} (Focused Aim: ${w.system.speed.focusedAim.toUpperCase()})`, combatProgressionDice: w.system.speed.focusedAim.toUpperCase() })
// Wait for 3D dice animation if enabled
if (game?.dice3d) {
await game.dice3d.waitFor3DAnimationByMessageID(msg.id)
}
if (this.biodata.magicUser || this.biodata.clericUser) {
let spells = this.parent.items.filter(i => i.type === "spell" || i.type === "miracle")
for (let s of spells) {
let title = ""
let formula = ""
if (s.type === "spell") {
let dice = PrismRPGUtils.getLethargyDice(s.system.level)
title = `${s.name} (Casting time: ${s.system.castingTime}, Lethargy: ${dice})`
formula = `${s.system.castingTime}+${dice}`
} else {
title = `${s.name} (Prayer time: ${s.system.prayerTime})`
formula = `${s.system.prayerTime}`
}
weaponsChoices.push({ id: s.id, name: title, combatProgressionDice: formula })
// Update the combatant's initiative if in combat
if (combatId && combatantId) {
let combat = game.combats.get(combatId)
if (combat) {
await combat.updateEmbeddedDocuments("Combatant", [{
_id: combatantId,
initiative: initRoll.total
}])
}
}
let roll = await PrismRPGRoll.promptCombatAction({
actorId: this.parent.id,
actorName: this.parent.name,
actorImage: this.parent.img,
weaponsChoices,
combatId,
combatantId,
rollProgressionCount,
type: "progression",
})
return initRoll
}
}
+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}`] || []
}
}
+27 -2
View File
@@ -7,11 +7,11 @@ export default class PrismRPGEquipment extends foundry.abstract.TypeDataModel {
const requiredInteger = { required: true, nullable: false, integer: true }
schema.description = new fields.HTMLField({ required: true, textSearch: true })
schema.category = new fields.StringField({ required: true, initial: "tinbit", choices: SYSTEM.EQUIPMENT_CATEGORIES })
schema.category = new fields.StringField({ required: true, initial: "coppercoin", choices: SYSTEM.EQUIPMENT_CATEGORIES })
schema.encLoad = new fields.NumberField({ required: true, initial: 0, min: 0 })
schema.cost = new fields.NumberField({ ...requiredInteger, required: true, initial: 0, min: 0 })
schema.money = new fields.StringField({ required: true, initial: "tinbit", choices: SYSTEM.MONEY })
schema.money = new fields.StringField({ required: true, initial: "coppercoin", choices: SYSTEM.MONEY })
// Kit properties
schema.isKit = new fields.BooleanField({
@@ -54,4 +54,29 @@ export default class PrismRPGEquipment extends foundry.abstract.TypeDataModel {
/** @override */
static LOCALIZATION_PREFIXES = ["PRISMRPG.Equipment"]
static migrateData(data) {
// Migrate old money types to new ones
if (data?.money) {
const moneyMigration = {
"tinbit": "coppercoin",
"copper": "coppercoin",
"silver": "silvercoin",
"gold": "goldcoin",
"platinum": "note"
}
if (moneyMigration[data.money]) {
data.money = moneyMigration[data.money]
}
// If still invalid, default to coppercoin
if (!SYSTEM.MONEY[data.money]) {
console.warn(`Prism RPG | Migrate equipment: Invalid money type "${data.money}", defaulting to coppercoin`)
data.money = "coppercoin"
}
}
return super.migrateData(data)
}
}
-124
View File
@@ -1,124 +0,0 @@
import { SYSTEM } from "../config/system.mjs"
export default class PrismRPGMiracle extends foundry.abstract.TypeDataModel {
static defineSchema() {
const fields = foundry.data.fields
const requiredInteger = { required: true, nullable: false, integer: true }
const schema = {}
schema.description = new fields.HTMLField({
required: false,
blank: true,
initial: "",
textSearch: true,
})
// Miracle level (1-7+)
schema.level = new fields.NumberField({
...requiredInteger,
initial: 1,
min: 1,
max: 25,
})
// Miracle type
schema.miracleType = new fields.StringField({
required: true,
initial: "combat",
choices: SYSTEM.MIRACLE_TYPES
})
// APC to pray
schema.apc = new fields.NumberField({
...requiredInteger,
required: true,
initial: 1,
min: 0,
label: "Action Point Cost"
})
// Faith cost (if applicable in Prism RPG)
schema.faithCost = new fields.NumberField({
...requiredInteger,
required: true,
initial: 0,
min: 0,
label: "Faith Cost"
})
// Divine favor required
schema.divineFavor = new fields.StringField({
required: true,
initial: "",
label: "Divine Favor"
})
// Components (Miracles have 'religious' component)
schema.components = new fields.SchemaField({
verbal: new fields.BooleanField({ initial: false }),
somatic: new fields.BooleanField({ initial: false }),
material: new fields.BooleanField({ initial: false }),
catalyst: new fields.BooleanField({ initial: false }),
religious: new fields.BooleanField({ initial: true })
})
schema.materialComponent = new fields.StringField({
required: true,
initial: ""
})
schema.catalyst = new fields.StringField({
required: true,
initial: ""
})
// Prayer parameters
schema.prayerTime = new fields.StringField({
required: true,
initial: "1 action"
})
schema.miracleRange = new fields.StringField({
required: true,
initial: "Touch"
})
schema.areaAffected = new fields.StringField({
required: true,
initial: "Single target"
})
schema.duration = new fields.StringField({
required: true,
initial: "Instantaneous"
})
schema.savingThrow = new fields.StringField({
required: true,
initial: ""
})
// Keywords
schema.keywords = new fields.ArrayField(
new fields.StringField()
)
// Miracle augment (if applicable)
schema.augment = new fields.StringField({
required: true,
initial: "",
label: "Miracle Augment"
})
schema.augmentDescription = new fields.HTMLField({
required: true,
initial: "",
label: "Augment Description"
})
return schema
}
/** @override */
static LOCALIZATION_PREFIXES = ["PRISMRPG.Miracle"]
}
+26 -1
View File
@@ -61,7 +61,7 @@ export default class PrismRPGShield extends foundry.abstract.TypeDataModel {
// Equipment properties
schema.encLoad = new fields.NumberField({ required: true, initial: 0, min: 0 })
schema.cost = new fields.NumberField({ required: true, initial: 0, min: 0 })
schema.money = new fields.StringField({ required: true, initial: "tinbit", choices: SYSTEM.MONEY })
schema.money = new fields.StringField({ required: true, initial: "coppercoin", choices: SYSTEM.MONEY })
schema.equipped = new fields.BooleanField({ required: true, initial: false })
return schema
@@ -69,4 +69,29 @@ export default class PrismRPGShield extends foundry.abstract.TypeDataModel {
/** @override */
static LOCALIZATION_PREFIXES = ["PRISMRPG.Shield"]
static migrateData(data) {
// Migrate old money types to new ones
if (data?.money) {
const moneyMigration = {
"tinbit": "coppercoin",
"copper": "coppercoin",
"silver": "silvercoin",
"gold": "goldcoin",
"platinum": "note"
}
if (moneyMigration[data.money]) {
data.money = moneyMigration[data.money]
}
// If still invalid, default to coppercoin
if (!SYSTEM.MONEY[data.money]) {
console.warn(`Prism RPG | Migrate shield: Invalid money type "${data.money}", defaulting to coppercoin`)
data.money = "coppercoin"
}
}
return super.migrateData(data)
}
}
+33 -19
View File
@@ -1,4 +1,5 @@
import { CORE_SKILLS_CHOICES, CORE_SKILL_BONUS, CORE_SKILLS } from "../config/skill.mjs"
import { SUB_ATTRIBUTES } from "../config/character.mjs"
/**
* Core Skill data model for Prism RPG
@@ -21,14 +22,6 @@ export default class PrismRPGSkill extends foundry.abstract.TypeDataModel {
initial: ""
})
// Core Skill type (from the 18 available Core Skills)
schema.coreSkill = new fields.StringField({
required: true,
initial: "acrobatics",
choices: CORE_SKILLS_CHOICES,
label: "Core Skill"
})
// Is this the character's chosen Core Skill?
schema.isCoreSkill = new fields.BooleanField({
required: true,
@@ -36,11 +29,18 @@ export default class PrismRPGSkill extends foundry.abstract.TypeDataModel {
label: "Is Core Skill"
})
// If Core Skill, which attribute receives the +2 bonus?
schema.attributeBonus = new fields.StringField({
// First sub-attribute for this skill
schema.subAttribute1 = new fields.StringField({
required: true,
initial: "",
label: "Attribute Bonus"
initial: "prowess",
label: "Sub-Attribute 1"
})
// Second sub-attribute for this skill
schema.subAttribute2 = new fields.StringField({
required: true,
initial: "initiative",
label: "Sub-Attribute 2"
})
// Skill modifier (includes Core Skill bonus if applicable)
@@ -107,9 +107,9 @@ export default class PrismRPGSkill extends foundry.abstract.TypeDataModel {
prepareDerivedData() {
super.prepareDerivedData()
// If this is the character's Core Skill, apply bonuses
// Core Skill gives +5 proficiency bonus
if (this.isCoreSkill) {
this.modifier = CORE_SKILL_BONUS?.basic || 5
this.modifier = 5
this.canAdvancedCheck = true
} else {
this.modifier = 0
@@ -119,16 +119,30 @@ export default class PrismRPGSkill extends foundry.abstract.TypeDataModel {
/**
* Calculate skill check bonus
* @param {string} attributeKey The attribute to use (str, dex, con, int, wis, cha)
* @param {string} subAttributeKey The sub-attribute to use (prowess, vigor, etc.)
* @returns {number} Total skill check bonus
*/
getSkillCheckBonus(attributeKey) {
getSkillCheckBonus(subAttributeKey) {
let actor = this.parent?.actor
if (!actor) return this.modifier
const attribute = actor.system.characteristics?.[attributeKey]
const attributeMod = attribute?.mod || 0
const subAttribute = actor.system.subAttributes?.[subAttributeKey]
const subAttributeMod = subAttribute?.value || 0
return attributeMod + this.modifier
return subAttributeMod + this.modifier
}
/**
* Get the available sub-attribute choices for this skill
*/
get subAttributeChoices() {
const choices = {}
if (this.subAttribute1) {
choices[this.subAttribute1] = SUB_ATTRIBUTES[this.subAttribute1]?.label || this.subAttribute1
}
if (this.subAttribute2) {
choices[this.subAttribute2] = SUB_ATTRIBUTES[this.subAttribute2]?.label || this.subAttribute2
}
return choices
}
}
-17
View File
@@ -1,17 +0,0 @@
export default class PrismRPGVulnerability extends foundry.abstract.TypeDataModel {
static defineSchema() {
const fields = foundry.data.fields
const requiredInteger = { required: true, nullable: false, integer: true }
const schema = {}
schema.description = new fields.HTMLField({ required: true, textSearch: true })
schema.cost = new fields.NumberField({ ...requiredInteger, required: true, initial: 0, min: 0 })
schema.gainedPoints = new fields.NumberField({ ...requiredInteger, required: true, initial: 0, min: 0 })
return schema
}
/** @override */
static LOCALIZATION_PREFIXES = ["PRISMRPG.Vulnerability"]
}
+35 -9
View File
@@ -96,16 +96,17 @@ export default class PrismRPGWeapon extends foundry.abstract.TypeDataModel {
initial: ""
})
// Projectile-specific properties
schema.isProjectile = new fields.BooleanField({
required: true,
initial: false
// Range properties
schema.shortRange = new fields.NumberField({
...requiredInteger,
initial: 0,
min: 0
})
schema.range = new fields.SchemaField({
short: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
medium: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
long: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 })
schema.longRange = new fields.NumberField({
...requiredInteger,
initial: 0,
min: 0
})
schema.reloadAPC = new fields.NumberField({
@@ -123,7 +124,7 @@ export default class PrismRPGWeapon extends foundry.abstract.TypeDataModel {
schema.encLoad = new fields.NumberField({ required: true, initial: 0, min: 0 })
schema.cost = new fields.NumberField({ ...requiredInteger, required: true, initial: 0, min: 0 })
schema.money = new fields.StringField({ required: true, initial: "tinbit", choices: SYSTEM.MONEY })
schema.money = new fields.StringField({ required: true, initial: "coppercoin", choices: SYSTEM.MONEY })
schema.equipped = new fields.BooleanField({ required: true, initial: false })
schema.isImplement = new fields.BooleanField({ required: true, initial: false })
@@ -132,4 +133,29 @@ export default class PrismRPGWeapon extends foundry.abstract.TypeDataModel {
/** @override */
static LOCALIZATION_PREFIXES = ["PRISMRPG.Weapon"]
static migrateData(data) {
// Migrate old money types to new ones
if (data?.money) {
const moneyMigration = {
"tinbit": "coppercoin",
"copper": "coppercoin",
"silver": "silvercoin",
"gold": "goldcoin",
"platinum": "note"
}
if (moneyMigration[data.money]) {
data.money = moneyMigration[data.money]
}
// If still invalid, default to coppercoin
if (!SYSTEM.MONEY[data.money]) {
console.warn(`Prism RPG | Migrate weapon: Invalid money type "${data.money}", defaulting to coppercoin`)
data.money = "coppercoin"
}
}
return super.migrateData(data)
}
}
+11 -5
View File
@@ -13,11 +13,6 @@ export default class PrismRPGUtils {
return compendiumData.filter(filter)
}
/* -------------------------------------------- */
static pushCombatOptions(html, options) {
options.push({ name: "Reset Progression", condition: true, icon: '<i class="fas fa-rotate-right"></i>', callback: target => { game.combat.resetProgression(target.data('combatant-id')); } })
}
/* -------------------------------------------- */
static setHookListeners() {
@@ -170,6 +165,17 @@ export default class PrismRPGUtils {
Handlebars.registerHelper('countKeys', function (obj) {
return Object.keys(obj).length;
})
Handlebars.registerHelper('entries', function (obj) {
return Object.entries(obj);
})
Handlebars.registerHelper('uppercase', function (str) {
return str ? str.toUpperCase() : '';
})
Handlebars.registerHelper('replace', function (str, search, replacement) {
if (!str) return '';
return str.replace(search, replacement);
})
Handlebars.registerHelper('isEnabled', function (configKey) {
return game.settings.get("bol", configKey);
+30 -15
View File
@@ -11,7 +11,7 @@ import * as models from "./module/models/_module.mjs"
import * as documents from "./module/documents/_module.mjs"
import * as applications from "./module/applications/_module.mjs"
import { PrismRPGCombatTracker, PrismRPGCombat } from "./module/applications/combat.mjs"
import { PrismRPGCombat } from "./module/applications/combat.mjs"
import { Macros } from "./module/macros.mjs"
import { setupTextEnrichers } from "./module/enrichers.mjs"
import { default as PrismRPGUtils } from "./module/utils.mjs"
@@ -36,8 +36,11 @@ Hooks.once("init", function () {
documents,
}
CONFIG.ui.combat = PrismRPGCombatTracker
CONFIG.Combat.documentClass = PrismRPGCombat;
CONFIG.Combat.documentClass = PrismRPGCombat
CONFIG.Combat.initiative = {
formula: "1d20 + @subAttributes.initiative.value",
decimals: 2
}
CONFIG.Actor.documentClass = documents.PrismRPGActor
CONFIG.Actor.dataModels = {
@@ -53,9 +56,7 @@ Hooks.once("init", function () {
armor: models.PrismRPGArmor,
shield: models.PrismRPGShield,
spell: models.PrismRPGSpell,
// Vulnerability: models.PrismRPGVulnerability, // Disabled - Legacy from Lethal Fantasy
equipment: models.PrismRPGEquipment,
// Miracle: models.PrismRPGMiracle // Disabled - Legacy from Lethal Fantasy, PRISM uses Divine class features instead
race: models.PrismRPGRace,
class: models.PrismRPGClass,
"character-path": models.PrismRPGCharacterPath,
@@ -69,7 +70,6 @@ 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.PrismRPGVulnerabilitySheet, { types: ["vulnerability"], makeDefault: true }) // Disabled - Legacy from Lethal Fantasy
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 })
@@ -78,7 +78,6 @@ Hooks.once("init", function () {
foundry.documents.collections.Items.registerSheet("prismRPG", applications.PrismRPGRaceSheet, { types: ["race"], makeDefault: true })
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 })
// Foundry.documents.collections.Items.registerSheet("prismRPG", applications.PrismRPGMiracleSheet, { types: ["miracle"], makeDefault: true }) // Disabled - Legacy from Lethal Fantasy
// Other Document Configuration
CONFIG.ChatMessage.documentClass = documents.PrismRPGChatMessage
@@ -133,9 +132,11 @@ function preLocalizeConfig() {
for (const choice of choicesToLocalize) {
if (CONFIG.PRISMRPG[choice]) {
const localized = {}
for (const [key, label] of Object.entries(CONFIG.PRISMRPG[choice])) {
CONFIG.PRISMRPG[choice][key] = game.i18n.localize(label)
localized[key] = game.i18n.localize(label)
}
CONFIG.PRISMRPG[choice] = localized
}
}
}
@@ -171,15 +172,19 @@ if (foundry.utils.isNewerVersion(game.version, "12.0",)) {
}
Hooks.on(hookName, (message, html, data) => {
const typeMessage = data.message.flags.prismRPG?.typeMessage
// Convertir html en jQuery pour compatibilité avec le code existant si nécessaire
const $html = html instanceof jQuery ? html : $(html)
// Message de demande de jet de dés
if (typeMessage === "askRoll") {
// Affichage des boutons de jet de dés uniquement pour les joueurs
if (game.user.isGM) {
html.find(".ask-roll-dice").each((i, btn) => {
$html.find(".ask-roll-dice").each((i, btn) => {
btn.style.display = "none"
})
} else {
html.find(".ask-roll-dice").click((event) => {
$html.find(".ask-roll-dice").click((event) => {
const btn = $(event.currentTarget)
const type = btn.data("type")
const value = btn.data("value")
@@ -190,12 +195,22 @@ Hooks.on(hookName, (message, html, data) => {
})
}
}
// Handle Roll Damage button click in weapon attack messages
$html.find(".roll-damage-button").click(async (event) => {
const btn = event.currentTarget
const actorId = btn.dataset.actorId
const weaponId = btn.dataset.weaponId
const actor = game.actors.get(actorId)
if (!actor) {
ui.notifications.error("Actor not found")
return
}
await actor.prepareRoll("weapon-damage-medium", weaponId)
})
})
Hooks.on("getCombatTrackerEntryContext", (html, options) => {
PrismRPGUtils.pushCombatOptions(html, options);
});
/**
* Create a macro when dropping an entity on the hotbar
* Item - open roll dialog
+479
View File
@@ -0,0 +1,479 @@
// Character Main Sheet V2 - Based on PNG character sheet design
.character-main-v2 {
.sheet-common();
padding: 0;
margin: 0;
.character-sheet-wrapper {
background-image: url("../assets/ui/prism_rpg_background.webp");
background-size: cover;
background-position: center;
padding: 2px 4px;
min-height: auto;
}
// Character Header with Banner
.character-header {
position: relative;
margin-bottom: 3px;
.character-name-banner {
background-image: url("../assets/sheet/banner-bg.png");
background-size: contain;
background-repeat: no-repeat;
background-position: center;
height: 42px;
display: flex;
align-items: center;
justify-content: center;
input {
background: transparent;
border: none;
text-align: center;
font-family: "Cinzel", serif;
font-size: 20px;
font-weight: bold;
color: #2c2c2c;
width: 500px;
text-shadow: 1px 1px 2px rgba(255, 255, 255, 0.5);
padding: 2px 0;
}
}
.character-toggle-controls {
position: absolute;
top: 6px;
right: 10px;
}
}
// Main Grid Layout
.character-main-grid {
display: grid;
grid-template-columns: 1fr 300px;
gap: 12px;
align-items: start;
}
// Left Column - Portrait, Attributes, HP
.character-left-column {
display: flex;
flex-direction: row;
gap: 12px;
align-items: flex-start;
.portrait-hp-column {
display: flex;
flex-direction: column;
gap: 12px;
width: 200px;
flex-shrink: 0;
}
.character-portrait {
width: 200px;
height: 200px;
border: 3px solid #6b6b6b;
border-radius: 8px;
overflow: hidden;
background: #d4d4d4;
img {
width: 100%;
height: 100%;
object-fit: cover;
}
}
// HP Shields - below portrait, same width
.hp-shields-section {
width: 200px;
.hp-shields {
display: flex;
flex-direction: column;
gap: 6px;
.hp-item {
display: flex;
flex-direction: row;
align-items: center;
gap: 8px;
.hp-label {
font-family: "Cinzel", serif;
font-size: 12px;
font-weight: bold;
color: #2c2c2c;
text-transform: uppercase;
width: 55px;
flex-shrink: 0;
}
.hp-value {
input {
width: 40px;
height: 28px;
text-align: center;
font-size: 14px;
font-weight: bold;
background: rgba(255, 255, 255, 0.9);
border: 2px solid #6b6b6b;
border-radius: 4px;
}
}
.hp-separator {
font-family: "Cinzel", serif;
font-size: 14px;
font-weight: bold;
color: #2c2c2c;
}
.hp-max {
input {
width: 40px;
height: 28px;
text-align: center;
font-size: 14px;
font-weight: bold;
background: rgba(200, 220, 255, 0.5);
border: 2px solid #6b6b6b;
border-radius: 4px;
}
}
}
}
}
.character-attributes {
display: flex;
flex-direction: column;
gap: 6px;
flex: 1;
min-width: 0;
max-width: 280px;
.attribute-shield {
position: relative;
display: flex;
flex-direction: row;
align-items: center;
gap: 8px;
padding: 6px 12px;
background: rgba(255, 255, 255, 0.3);
border: 2px solid #6b6b6b;
border-radius: 4px;
height: auto;
.attribute-label {
font-family: "Cinzel", serif;
font-size: 11px;
font-weight: bold;
color: #2c2c2c;
text-transform: uppercase;
margin: 0;
min-width: 40px;
display: flex;
align-items: center;
a.rollable {
display: flex;
align-items: center;
gap: 4px;
color: #2c2c2c;
text-decoration: none;
cursor: pointer;
transition: all 0.2s;
i {
font-size: 12px;
color: #6b6b6b;
}
&:hover {
color: #000;
text-shadow: 0 0 4px rgba(0, 0, 0, 0.3);
i {
color: #2c2c2c;
}
}
}
}
.attribute-value {
display: flex;
align-items: center;
input {
width: 45px;
height: 32px;
text-align: center;
font-size: 16px;
font-weight: bold;
background: rgba(255, 255, 255, 0.9);
border: 2px solid #6b6b6b;
border-radius: 4px;
}
}
.attribute-save {
margin-left: auto;
display: flex;
align-items: center;
a.save-rollable {
display: contents;
cursor: pointer;
i {
margin-right: 6px;
}
&:hover input {
background: rgba(200, 220, 255, 0.8);
border-color: #4a4a4a;
}
}
input {
width: 45px;
height: 32px;
text-align: center;
font-size: 14px;
font-weight: bold;
background: rgba(200, 220, 255, 0.5);
border: 2px solid #6b6b6b;
border-radius: 4px;
color: #2c2c2c;
cursor: pointer;
transition: all 0.2s;
}
}
}
}
}
// Right Column - Race, Classes
.character-right-column {
display: flex;
flex-direction: column;
gap: 15px;
.section-title {
font-family: "Cinzel", serif;
font-size: 14px;
font-weight: bold;
color: #2c2c2c;
text-transform: uppercase;
margin-bottom: 8px;
padding: 5px 10px;
background: rgba(255, 255, 255, 0.6);
border: 2px solid #6b6b6b;
border-radius: 4px;
text-align: center;
}
.race-section {
.race-box {
padding: 6px;
background: rgba(255, 255, 255, 0.5);
border: 2px solid #6b6b6b;
border-radius: 6px;
min-height: 50px;
.race-label {
font-family: "Cinzel", serif;
font-size: 10px;
color: #6b6b6b;
text-align: center;
margin-bottom: 3px;
}
.section-title {
font-family: "Cinzel", serif;
font-size: 11px;
font-weight: bold;
color: #2c2c2c;
text-transform: uppercase;
margin: 0 0 4px 0;
padding: 3px;
background: rgba(255, 255, 255, 0.6);
border: 2px solid #6b6b6b;
border-radius: 4px;
text-align: center;
}
.race-item {
display: flex;
align-items: center;
gap: 6px;
.item-img {
width: 30px;
height: 30px;
border: 2px solid #6b6b6b;
border-radius: 4px;
object-fit: cover;
cursor: pointer;
}
.race-name {
flex: 1;
font-family: "Cinzel", serif;
font-size: 12px;
font-weight: bold;
color: #2c2c2c;
text-align: center;
}
.controls {
display: flex;
gap: 6px;
a {
color: #6b6b6b;
cursor: pointer;
transition: color 0.2s;
&:hover {
color: #2c2c2c;
}
i {
font-size: 14px;
}
}
}
}
.no-race {
text-align: center;
font-family: "Crimson Text", serif;
font-size: 11px;
color: #6b6b6b;
font-style: italic;
padding: 5px;
}
input {
width: 100%;
background: transparent;
border: none;
font-family: "Cinzel", serif;
font-size: 14px;
text-align: center;
}
}
}
.classes-section {
display: flex;
flex-direction: column;
gap: 8px;
.class-box {
padding: 6px;
background: rgba(255, 255, 255, 0.5);
border: 2px solid #6b6b6b;
border-radius: 6px;
.class-label {
font-family: "Cinzel", serif;
font-size: 10px;
color: #6b6b6b;
text-align: center;
margin-bottom: 3px;
}
.class-content {
.class-item {
display: flex;
align-items: center;
gap: 6px;
.item-img {
width: 28px;
height: 28px;
border: 2px solid #6b6b6b;
border-radius: 4px;
object-fit: cover;
cursor: pointer;
}
.class-name {
flex: 1;
font-family: "Cinzel", serif;
font-size: 11px;
font-weight: bold;
color: #2c2c2c;
text-align: center;
}
.controls {
display: flex;
gap: 6px;
a {
color: #6b6b6b;
cursor: pointer;
transition: color 0.2s;
&:hover {
color: #2c2c2c;
}
i {
font-size: 12px;
}
}
}
}
.no-class {
text-align: center;
font-family: "Crimson Text", serif;
font-size: 11px;
color: #6b6b6b;
font-style: italic;
padding: 5px;
}
}
.class-input {
input {
width: 100%;
background: transparent;
border: none;
font-family: "Cinzel", serif;
font-size: 14px;
text-align: center;
}
}
}
}
.origin-section {
.origin-box {
padding: 15px;
background: rgba(255, 255, 255, 0.5);
border: 3px solid #6b6b6b;
border-radius: 8px;
min-height: 350px;
textarea {
width: 100%;
min-height: 320px;
background: transparent;
border: none;
font-family: "Crimson Text", serif;
font-size: 13px;
line-height: 1.6;
resize: vertical;
}
}
}
}
}
+79
View File
@@ -0,0 +1,79 @@
// Sub-attributes tab styling
.character-subattributes.tab {
.subattributes-content {
padding: 1rem;
.subattributes-list {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 0.5rem;
}
.subattribute-item {
background: rgba(0, 0, 0, 0.1);
border: 1px solid var(--color-border-dark-secondary);
border-radius: 4px;
padding: 0.4rem;
display: flex;
flex-direction: column;
align-items: center;
gap: 0.15rem;
text-align: center;
cursor: pointer;
text-decoration: none;
.subattribute-header {
display: flex;
align-items: center;
gap: 0.3rem;
i.fa-dice-d20 {
font-size: 1em;
color: var(--color-text-dark-primary);
opacity: 0.7;
}
}
&:hover {
background: rgba(0, 0, 0, 0.2);
border-color: var(--color-text-light-primary);
transform: translateY(-1px);
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
.subattribute-header i.fa-dice-d20 {
opacity: 1;
color: var(--color-text-dark-primary);
}
}
.subattribute-name {
font-weight: bold;
font-size: 0.75em;
color: var(--color-text-dark-primary);
line-height: 1.2;
}
.subattribute-value {
font-size: 1em;
font-weight: bold;
color: var(--color-text-dark-primary);
min-width: 2em;
padding: 0.15rem 0.3rem;
background: rgba(255, 255, 255, 0.1);
border-radius: 3px;
}
}
}
}
// Responsive adjustments
@media (max-width: 768px) {
.character-subattributes.tab {
.subattributes-content {
.subattributes-list {
grid-template-columns: repeat(3, 1fr);
}
}
}
}
+312 -23
View File
@@ -1,7 +1,107 @@
.character-content {
.sheet-common();
.character-sheet-common();
font-family: var(--font-primary);
font-size: calc(var(--font-size-standard) * 1);
color: var(--color-dark-1);
background-image: url("../assets/ui/prism_rpg_background.webp");
background-size: cover;
background-position: center;
background-repeat: no-repeat;
overflow: scroll;
nav.tabs {
background-image: url("../assets/ui/prism_rpg_background.webp");
background-size: cover;
background-position: center;
background-repeat: no-repeat;
padding: 2px 0;
}
nav.tabs [data-tab] {
color: #636060;
font-size: calc(var(--font-size-standard) * 1.1);
font-family: var(--font-secondary);
font-weight: bold;
padding: 3px 16px;
background-color: rgba(255, 255, 255, 0.3);
border-radius: 4px;
text-shadow: 1px 1px 2px rgba(255, 255, 255, 0.8);
transition: all 0.2s ease;
cursor: pointer;
border: 2px solid transparent;
&:hover {
background-color: rgba(255, 255, 255, 0.7);
color: #1a1a1a;
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3);
border-color: rgba(107, 107, 107, 0.3);
}
}
nav.tabs [data-tab].active {
color: #000000;
background-color: rgba(255, 255, 255, 0.85);
text-shadow: 1px 1px 3px rgba(255, 255, 255, 1);
border: 2px solid #6b6b6b;
box-shadow: 0 3px 6px rgba(0, 0, 0, 0.25);
&:hover {
background-color: rgba(255, 255, 255, 0.95);
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.35);
transform: translateY(-2px);
border-color: #4a4a4a;
}
}
input:disabled,
select:disabled {
background-color: rgba(0, 0, 0, 0.2);
border-color: transparent;
color: var(--color-dark-3);
}
input,
select {
height: 1.5rem;
background-color: rgba(0, 0, 0, 0.1);
border-color: var(--color-dark-6);
color: var(--color-dark-2);
}
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;
}
fieldset {
margin-bottom: 4px;
border-radius: 4px;
}
.form-fields {
input,
select {
text-align: center;
font-size: calc(var(--font-size-standard) * 1);
}
select {
font-family: var(--font-secondary);
font-size: calc(var(--font-size-standard) * 1);
}
}
legend {
font-family: var(--font-secondary);
font-size: calc(var(--font-size-standard) * 1.2);
font-weight: bold;
letter-spacing: 1px;
}
.character-sheet-common();
}
.character-main {
@@ -223,6 +323,13 @@
}
.tab.character-biography .main-div {
background-image: url("../assets/ui/prism_rpg_background.webp");
background-size: cover;
background-position: center;
background-repeat: no-repeat;
padding: 8px 10px;
min-height: 100%;
.biodata {
display: grid;
grid-template-columns: repeat(4, 1fr);
@@ -253,6 +360,13 @@
}
.tab.character-skills .main-div {
background-image: url("../assets/ui/prism_rpg_background.webp");
background-size: cover;
background-position: center;
background-repeat: no-repeat;
padding: 8px 10px;
min-height: 100%;
display: grid;
grid-template-columns: 1fr;
legend {
@@ -263,58 +377,212 @@
}
.skills {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 4px;
grid-template-columns: repeat(2, 1fr);
gap: 6px;
.skill {
display: flex;
align-items: center;
gap: 4px;
.item-img {
width: 24px;
height: 24px;
gap: 8px;
padding: 6px 10px;
background: rgba(255, 255, 255, 0.3);
border: 2px solid #6b6b6b;
border-radius: 6px;
transition: all 0.2s;
&:hover {
background: rgba(255, 255, 255, 0.5);
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15);
}
&.is-core-skill {
background: rgba(255, 235, 180, 0.4);
border-color: #d4a017;
&:hover {
background: rgba(255, 235, 180, 0.6);
}
}
.item-img {
width: 32px;
height: 32px;
border: 2px solid #6b6b6b;
border-radius: 4px;
object-fit: cover;
flex-shrink: 0;
}
.name {
min-width: 12rem;
flex: 1;
min-width: 0;
a {
font-family: "Cinzel", serif;
font-size: 14px;
font-weight: 600;
color: #2c2c2c;
i {
margin-right: 6px;
color: #6b6b6b;
}
&:hover {
color: #000;
}
}
}
.score {
font-family: "Cinzel", serif;
font-size: 16px;
font-weight: bold;
color: #2c2c2c;
min-width: 50px;
text-align: center;
.advanced-icon {
color: #d4a017;
font-size: 18px;
margin-left: 4px;
}
}
.controls {
display: flex;
gap: 8px;
flex-shrink: 0;
a {
color: #6b6b6b;
font-size: 14px;
transition: color 0.2s;
&:hover {
color: #2c2c2c;
}
}
}
}
}
.racial-abilities {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 4px;
grid-template-columns: repeat(2, 1fr);
gap: 6px;
.racial-ability {
display: flex;
align-items: center;
gap: 4px;
.item-img {
width: 24px;
height: 24px;
gap: 8px;
padding: 6px 10px;
background: rgba(200, 255, 200, 0.2);
border: 2px solid #6b9b6b;
border-radius: 6px;
transition: all 0.2s;
&:hover {
background: rgba(200, 255, 200, 0.4);
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15);
}
.item-img {
width: 32px;
height: 32px;
border: 2px solid #6b9b6b;
border-radius: 4px;
object-fit: cover;
flex-shrink: 0;
}
.name {
min-width: 12rem;
flex: 1;
min-width: 0;
font-family: "Cinzel", serif;
font-size: 14px;
font-weight: 600;
color: #2c2c2c;
}
.controls {
display: flex;
gap: 8px;
flex-shrink: 0;
a {
color: #6b9b6b;
font-size: 14px;
transition: color 0.2s;
&:hover {
color: #3c6b3c;
}
}
}
}
}
.vulnerabilities {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 4px;
grid-template-columns: repeat(2, 1fr);
gap: 6px;
.vulnerability {
display: flex;
align-items: center;
gap: 4px;
.item-img {
width: 24px;
height: 24px;
gap: 8px;
padding: 6px 10px;
background: rgba(255, 200, 200, 0.2);
border: 2px solid #9b6b6b;
border-radius: 6px;
transition: all 0.2s;
&:hover {
background: rgba(255, 200, 200, 0.4);
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15);
}
.item-img {
width: 32px;
height: 32px;
border: 2px solid #9b6b6b;
border-radius: 4px;
object-fit: cover;
flex-shrink: 0;
}
.name {
min-width: 12rem;
flex: 1;
min-width: 0;
font-family: "Cinzel", serif;
font-size: 14px;
font-weight: 600;
color: #2c2c2c;
}
.controls {
display: flex;
gap: 8px;
flex-shrink: 0;
a {
color: #9b6b6b;
font-size: 14px;
transition: color 0.2s;
&:hover {
color: #6b3c3c;
}
}
}
}
}
}
.tab.character-equipment .main-div {
background-image: url("../assets/ui/prism_rpg_background.webp");
background-size: cover;
background-position: center;
background-repeat: no-repeat;
padding: 8px 10px;
min-height: 100%;
display: grid;
grid-template-columns: 1fr;
legend {
@@ -354,6 +622,13 @@
}
.tab.character-combat .main-div {
background-image: url("../assets/ui/prism_rpg_background.webp");
background-size: cover;
background-position: center;
background-repeat: no-repeat;
padding: 8px 10px;
min-height: 100%;
display: grid;
grid-template-columns: 1fr;
legend {
@@ -504,6 +779,13 @@
}
.tab.character-spells .main-div {
background-image: url("../assets/ui/prism_rpg_background.webp");
background-size: cover;
background-position: center;
background-repeat: no-repeat;
padding: 8px 10px;
min-height: 100%;
display: grid;
grid-template-columns: 1fr;
legend {
@@ -565,6 +847,13 @@
}
.tab.character-miracles .main-div {
background-image: url("../assets/ui/prism_rpg_background.webp");
background-size: cover;
background-position: center;
background-repeat: no-repeat;
padding: 8px 10px;
min-height: 100%;
display: grid;
grid-template-columns: 1fr;
legend {
+534 -24
View File
@@ -1,33 +1,406 @@
&.fortune {
img {
border: 0px;
// Chat Message Card Styles - Applied globally for chat messages
.chat-log .message-content {
.prismrpg-chat-card {
font-family: var(--font-primary);
border-radius: 6px;
overflow: hidden;
background: linear-gradient(135deg, rgba(0,0,0,0.05) 0%, rgba(0,0,0,0.02) 100%);
border: 1px solid rgba(0,0,0,0.2);
margin: 2px 0;
.chat-header {
display: flex;
gap: 8px;
padding: 6px 8px;
background: linear-gradient(135deg, #2c2c2c 0%, #1a1a1a 100%);
border-bottom: 1px solid #444;
.chat-portrait {
flex-shrink: 0;
width: 36px;
height: 36px;
border-radius: 50%;
overflow: hidden;
border: 2px solid #666;
box-shadow: 0 1px 4px rgba(0,0,0,0.4);
img {
width: 100%;
height: 100%;
object-fit: cover;
}
.intro-chat {
border-radius: 20px;
display: flex;
flex-direction: row;
.intro-img {
padding: 5px;
width: 80px;
align-self: center;
}
.chat-title {
flex: 1;
display: flex;
flex-direction: column;
justify-content: center;
gap: 2px;
.actor-name {
font-weight: bold;
font-size: 0.95em;
color: #e0e0e0;
text-shadow: 1px 1px 2px rgba(0,0,0,0.5);
line-height: 1.1;
}
.roll-name {
font-size: 0.8em;
color: #aaa;
line-height: 1.1;
}
.roll-type-badge {
display: inline-block;
padding: 1px 6px;
border-radius: 10px;
font-size: 0.7em;
font-weight: bold;
text-transform: uppercase;
margin-top: 2px;
width: fit-content;
&.attack {
background: linear-gradient(135deg, #c41e3a 0%, #8b0000 100%);
color: white;
box-shadow: 0 1px 2px rgba(196, 30, 58, 0.4);
}
.intro-right {
}
.bad-result {
color: #ff6b6b;
font-size: 0.75em;
margin-top: 1px;
}
}
}
.chat-content {
padding: 6px 8px;
display: flex;
flex-direction: column;
gap: 6px;
.weapon-info-card,
.spell-info-card {
background: rgba(255, 255, 255, 0.05);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 4px;
padding: 6px;
.weapon-header,
.spell-header {
display: flex;
align-items: center;
gap: 6px;
margin-bottom: 4px;
padding-bottom: 4px;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
.weapon-name,
.spell-name {
font-size: 0.95em;
color: #d4af37;
}
.badge {
padding: 1px 4px;
border-radius: 3px;
font-size: 0.65em;
font-weight: bold;
text-transform: uppercase;
&.implement {
background: #4a5cf7;
color: white;
}
&.upcast {
background: #9b59b6;
color: white;
}
}
.attribute-used {
font-size: 0.75em;
color: #999;
font-style: italic;
}
}
.weapon-stats,
.spell-stats {
display: flex;
flex-wrap: wrap;
gap: 4px;
.stat-item {
display: flex;
flex-direction: column;
.introText {
font-family: var(--font-secondary);
font-size: calc(var(--font-size-standard) * 1.2);
width: 210px;
text-align: center;
align-items: center;
gap: 3px;
padding: 2px 6px;
background: rgba(0, 0, 0, 0.2);
border-radius: 3px;
font-size: 0.75em;
i {
color: #888;
font-size: 0.85em;
}
&.apc {
background: rgba(255, 193, 7, 0.2);
border: 1px solid rgba(255, 193, 7, 0.4);
i { color: #ffc107; }
}
&.damage {
background: rgba(244, 67, 54, 0.2);
border: 1px solid rgba(244, 67, 54, 0.4);
i { color: #f44336; }
}
&.range {
background: rgba(76, 175, 80, 0.2);
border: 1px solid rgba(76, 175, 80, 0.4);
i { color: #4caf50; }
}
&.reload {
background: rgba(255, 152, 0, 0.2);
border: 1px solid rgba(255, 152, 0, 0.4);
i { color: #ff9800; }
}
&.mana {
background: rgba(33, 150, 243, 0.2);
border: 1px solid rgba(33, 150, 243, 0.4);
i { color: #2196f3; }
}
&.upkeep {
background: rgba(156, 39, 176, 0.2);
border: 1px solid rgba(156, 39, 176, 0.4);
i { color: #9c27b0; }
}
&.characteristic {
background: rgba(103, 58, 183, 0.2);
border: 1px solid rgba(103, 58, 183, 0.4);
i { color: #673ab7; }
}
}
}
.button.control, .fortune-accepted {
display: flex;
justify-content: center;
align-items: center;
font-size: calc(var(--font-size-standard) * 1.3);
}
.special-badge {
display: inline-block;
padding: 2px 6px;
background: linear-gradient(135deg, #ff6b6b 0%, #c92a2a 100%);
color: white;
border-radius: 3px;
font-size: 0.75em;
font-weight: bold;
width: fit-content;
box-shadow: 0 1px 2px rgba(255, 107, 107, 0.4);
}
.aiming-info {
display: flex;
align-items: center;
gap: 4px;
padding: 3px 6px;
background: rgba(76, 175, 80, 0.1);
border-left: 2px solid #4caf50;
border-radius: 3px;
font-size: 0.8em;
i {
color: #4caf50;
}
}
.formula-display {
display: flex;
align-items: center;
gap: 6px;
padding: 4px 8px;
background: rgba(0, 0, 0, 0.3);
border-radius: 3px;
font-family: 'Courier New', monospace;
font-size: 0.85em;
border: 1px dashed rgba(255, 255, 255, 0.2);
i {
color: #888;
}
}
.modifier-info {
display: flex;
gap: 8px;
padding: 3px 6px;
background: rgba(255, 255, 255, 0.05);
border-radius: 3px;
font-size: 0.75em;
span {
color: #aaa;
}
}
.dice-breakdown {
display: flex;
align-items: center;
gap: 4px;
padding: 2px 6px;
background: rgba(255, 255, 255, 0.05);
border-radius: 3px;
font-size: 0.8em;
i {
color: #888;
}
}
.roll-damage-button {
padding: 4px 10px;
background: linear-gradient(135deg, #c41e3a 0%, #8b0000 100%);
color: white;
border: none;
border-radius: 4px;
font-weight: bold;
font-size: 0.85em;
cursor: pointer;
display: flex;
align-items: center;
gap: 6px;
justify-content: center;
transition: all 0.2s;
box-shadow: 0 1px 3px rgba(196, 30, 58, 0.4);
margin-top: 4px;
&:hover {
transform: translateY(-1px);
box-shadow: 0 2px 4px rgba(196, 30, 58, 0.6);
}
i {
font-size: 1em;
}
}
}
.roll-result {
padding: 8px;
background: linear-gradient(135deg, rgba(212, 175, 55, 0.2) 0%, rgba(212, 175, 55, 0.1) 100%);
border-top: 1px solid rgba(212, 175, 55, 0.5);
.result-total {
display: flex;
align-items: center;
justify-content: space-between;
gap: 10px;
.total-label {
font-size: 0.85em;
color: #aaa;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.total-value {
font-size: 1.8em;
font-weight: bold;
color: #d4af37;
text-shadow: 0 1px 3px rgba(212, 175, 55, 0.5);
line-height: 1;
}
}
.d30-result {
margin-top: 4px;
padding: 3px 6px;
background: rgba(0, 0, 0, 0.2);
border-radius: 3px;
font-size: 0.8em;
text-align: center;
i {
color: #d4af37;
margin-right: 4px;
}
}
}
.result-badge {
padding: 6px;
text-align: center;
font-size: 0.9em;
font-weight: bold;
border-top: 1px solid;
&.success {
background: linear-gradient(135deg, rgba(76, 175, 80, 0.3) 0%, rgba(76, 175, 80, 0.1) 100%);
border-color: #4caf50;
color: #4caf50;
}
&.failure {
background: linear-gradient(135deg, rgba(244, 67, 54, 0.3) 0%, rgba(244, 67, 54, 0.1) 100%);
border-color: #f44336;
color: #f44336;
}
i {
margin-right: 6px;
font-size: 1em;
}
}
.damage-info {
padding: 6px;
background: rgba(255, 255, 255, 0.05);
border-radius: 3px;
font-size: 0.8em;
color: #aaa;
}
}
// Old fortune and ask-roll styles
&.fortune {
img {
border: 0px;
}
.intro-chat {
border-radius: 20px;
display: flex;
flex-direction: row;
.intro-img {
padding: 5px;
width: 80px;
align-self: center;
}
.intro-right {
display: flex;
flex-direction: column;
.introText {
font-family: var(--font-secondary);
font-size: calc(var(--font-size-standard) * 1.2);
width: 210px;
text-align: center;
}
}
}
.button.control,
.fortune-accepted {
display: flex;
justify-content: center;
align-items: center;
font-size: calc(var(--font-size-standard) * 1.3);
}
}
&.ask-roll {
@@ -37,4 +410,141 @@
justify-content: center;
font-family: var(--font-secondary);
font-size: calc(var(--font-size-standard) * 1.2);
}
}
// Item Chat Card Styles
.prismrpg-item-chat-card {
font-family: var(--font-primary);
border-radius: 4px;
overflow: hidden;
background: linear-gradient(135deg, rgba(0,0,0,0.05) 0%, rgba(0,0,0,0.02) 100%);
border: 1px solid rgba(0,0,0,0.2);
margin: 2px 0;
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
.item-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 4px 8px;
background: linear-gradient(135deg, #2c2c2c 0%, #1a1a1a 100%);
border-bottom: 1px solid #444;
h3 {
margin: 0;
font-size: 0.95em;
font-weight: bold;
color: #d4af37;
text-shadow: 1px 1px 2px rgba(0,0,0,0.5);
font-family: var(--font-secondary);
}
.item-type {
padding: 1px 6px;
border-radius: 10px;
font-size: 0.65em;
font-weight: bold;
text-transform: uppercase;
background: rgba(255, 255, 255, 0.15);
color: #fff;
border: 1px solid rgba(255, 255, 255, 0.2);
letter-spacing: 0.3px;
}
}
.item-image {
display: flex;
justify-content: center;
align-items: center;
padding: 6px;
background: rgba(0, 0, 0, 0.1);
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
img {
max-width: 50px;
max-height: 50px;
border-radius: 3px;
border: 1px solid rgba(212, 175, 55, 0.3);
box-shadow: 0 1px 3px rgba(0,0,0,0.3);
transition: transform 0.2s ease;
&:hover {
transform: scale(1.05);
}
}
}
.item-description {
padding: 6px 8px;
color: #000;
font-size: 0.8em;
line-height: 1.3;
background: rgba(255, 255, 255, 0.8);
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
font-style: italic;
}
.item-details {
padding: 6px 8px;
display: flex;
flex-direction: column;
gap: 3px;
.item-detail {
display: flex;
align-items: center;
padding: 2px 6px;
background: rgba(255, 255, 255, 0.03);
border-left: 2px solid rgba(212, 175, 55, 0.5);
border-radius: 2px;
font-size: 0.8em;
strong {
color: #d4af37;
margin-right: 6px;
min-width: 90px;
font-weight: bold;
}
&:nth-child(even) {
background: rgba(0, 0, 0, 0.05);
}
}
}
// Special styling for weapon items
&.weapon-item {
.item-header {
background: linear-gradient(135deg, #c41e3a 0%, #8b0000 100%);
}
}
// Special styling for armor items
&.armor-item {
.item-header {
background: linear-gradient(135deg, #4a5cf7 0%, #2c3e9e 100%);
}
}
// Special styling for spell items
&.spell-item {
.item-header {
background: linear-gradient(135deg, #9b59b6 0%, #6c3483 100%);
}
}
// Special styling for skill items
&.skill-item {
.item-header {
background: linear-gradient(135deg, #16a085 0%, #0e6655 100%);
}
}
// Special styling for equipment items
&.equipment-item {
.item-header {
background: linear-gradient(135deg, #f39c12 0%, #b8730f 100%);
}
}
}
}
+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;
}
}
}
}
+4 -1
View File
@@ -4,6 +4,8 @@
.prismrpg {
@import "mixins.less";
@import "character.less";
@import "character-main-v2.less";
@import "character-subattributes.less";
@import "monster.less";
@import "skill.less";
@import "racial-ability.less";
@@ -11,7 +13,6 @@
@import "armor.less";
@import "spell.less";
@import "vulnerability.less";
@import "chat.less";
@import "equipment.less";
@import "shield.less";
@import "miracle.less";
@@ -22,5 +23,7 @@
@import "weapon-types-config.less";
}
@import "chat.less";
@import "roll.less";
@import "roll-dialog-modern.less";
@import "hud.less";
+305
View File
@@ -0,0 +1,305 @@
// Modern Roll Dialog Styling
// Matches the new chat message card design
.prismrpg-roll-dialog-modern {
font-family: var(--font-primary);
// Header section
.dialog-header {
background: linear-gradient(135deg, rgba(33, 33, 33, 0.95) 0%, rgba(66, 66, 66, 0.95) 100%);
border-bottom: 2px solid #d4af37;
padding: 8px 10px;
margin: -8px -8px 8px -8px;
border-radius: 4px 4px 0 0;
.character-info {
display: flex;
flex-direction: column;
gap: 4px;
.character-name {
font-size: 1em;
font-weight: bold;
color: #d4af37;
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.5);
}
.item-name {
display: flex;
align-items: center;
gap: 4px;
font-size: 0.9em;
color: #e0e0e0;
i {
color: #d4af37;
font-size: 0.85em;
}
&.weapon i {
color: #f44336;
}
&.spell i {
color: #9c27b0;
}
}
}
}
// Content area
.dialog-content {
display: flex;
flex-direction: column;
gap: 8px;
}
// Section styling
.option-section {
background: rgba(0, 0, 0, 0.1);
border: 1px solid rgba(0, 0, 0, 0.2);
border-radius: 4px;
padding: 6px 8px;
&.weapon-section {
border-left: 3px solid #f44336;
}
&.spell-section {
border-left: 3px solid #9c27b0;
}
.section-title {
display: flex;
align-items: center;
gap: 4px;
font-weight: bold;
font-size: 0.9em;
color: var(--color-dark-1);
margin-bottom: 6px;
padding-bottom: 4px;
border-bottom: 1px solid rgba(0, 0, 0, 0.15);
i {
color: #d4af37;
font-size: 0.85em;
}
}
}
// Info displays (ranges, spell stats)
.info-display {
display: flex;
align-items: center;
gap: 4px;
padding: 4px 6px;
background: rgba(0, 0, 0, 0.15);
border-radius: 3px;
margin-bottom: 6px;
font-size: 0.8em;
color: #666;
i {
color: #4caf50;
font-size: 0.85em;
}
.info-text {
flex: 1;
}
}
// Spell info badges (like in chat)
.spell-info-display {
display: flex;
flex-wrap: wrap;
gap: 4px;
margin-bottom: 6px;
.info-badge {
display: flex;
align-items: center;
gap: 3px;
padding: 3px 6px;
border-radius: 3px;
font-size: 0.75em;
font-weight: 500;
i {
font-size: 0.85em;
}
&.characteristic {
background: rgba(103, 58, 183, 0.2);
border: 1px solid rgba(103, 58, 183, 0.4);
color: #673ab7;
i { color: #673ab7; }
}
&.mana {
background: rgba(33, 150, 243, 0.2);
border: 1px solid rgba(33, 150, 243, 0.4);
color: #2196f3;
i { color: #2196f3; }
}
&.apc {
background: rgba(255, 193, 7, 0.2);
border: 1px solid rgba(255, 193, 7, 0.4);
color: #ffc107;
i { color: #ffc107; }
}
&.upkeep {
background: rgba(156, 39, 176, 0.2);
border: 1px solid rgba(156, 39, 176, 0.4);
color: #9c27b0;
i { color: #9c27b0; }
}
}
}
// Option rows (label + control)
.option-row {
display: flex;
align-items: center;
gap: 6px;
margin-bottom: 4px;
&:last-child {
margin-bottom: 0;
}
label {
font-size: 0.85em;
font-weight: 500;
color: var(--color-dark-2);
min-width: 90px;
}
select {
flex: 1;
}
}
// Styled selects
.styled-select {
background: rgba(255, 255, 255, 0.9);
border: 1px solid rgba(0, 0, 0, 0.2);
border-radius: 3px;
padding: 4px 8px;
font-size: 0.85em;
color: var(--color-dark-1);
cursor: pointer;
transition: all 0.2s ease;
&:hover {
border-color: #d4af37;
box-shadow: 0 0 4px rgba(212, 175, 55, 0.3);
}
&:focus {
outline: none;
border-color: #d4af37;
box-shadow: 0 0 6px rgba(212, 175, 55, 0.5);
}
&.advantage-select,
&.modifier-select {
text-align: center;
font-weight: 500;
}
}
// Checkbox group
.checkbox-group {
display: flex;
flex-direction: column;
gap: 3px;
margin-bottom: 6px;
.checkbox-label {
display: flex;
align-items: center;
cursor: pointer;
padding: 3px 4px;
border-radius: 3px;
transition: background 0.2s ease;
&:hover {
background: rgba(0, 0, 0, 0.05);
}
input[type="checkbox"] {
margin-right: 6px;
cursor: pointer;
width: 14px;
height: 14px;
accent-color: #d4af37;
}
.checkbox-text {
display: flex;
align-items: center;
gap: 4px;
font-size: 0.8em;
color: var(--color-dark-2);
i {
color: #888;
font-size: 0.85em;
}
}
input[type="checkbox"]:checked ~ .checkbox-text {
color: var(--color-dark-1);
font-weight: 500;
i {
color: #d4af37;
}
}
}
}
}
// Dialog application styling
.application.dialog.prismrpg {
.window-content {
background: linear-gradient(135deg, #f5f5f5 0%, #e0e0e0 100%);
padding: 8px;
}
// Make buttons match the modern style
.dialog-buttons {
padding: 6px 8px;
gap: 6px;
button {
background: linear-gradient(135deg, #4a4a4a 0%, #6a6a6a 100%);
border: 1px solid #3a3a3a;
color: white;
font-weight: 600;
padding: 6px 12px;
border-radius: 4px;
transition: all 0.2s ease;
&:hover {
background: linear-gradient(135deg, #5a5a5a 0%, #7a7a7a 100%);
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
transform: translateY(-1px);
}
&.default,
&[data-button="roll"] {
background: linear-gradient(135deg, #d4af37 0%, #f4cf67 100%);
border-color: #b49030;
color: #2a2a2a;
&:hover {
background: linear-gradient(135deg, #e4bf47 0%, #ffdf77 100%);
}
}
}
}
}
+54
View File
@@ -25,6 +25,30 @@
border-radius: 4px;
padding: 0.5rem;
}
.dialog-weapon-options {
margin-top: 8px;
.dialog-save {
margin: 4px 0;
label {
display: flex;
align-items: center;
cursor: pointer;
input[type="checkbox"] {
margin-right: 8px;
cursor: pointer;
}
}
select {
margin-left: 8px;
min-width: 10rem;
}
}
}
}
.prismrpg-range-defense-dialog {
@@ -156,4 +180,34 @@
font-size: calc(var(--font-size-standard) * 1);
text-shadow: 0 0 10px var(--color-shadow-primary);
}
// Roll Damage button in weapon attack messages
.damage-roll-button {
margin-top: 0.5em;
margin-bottom: 0.25em;
.roll-damage-button {
background: linear-gradient(135deg, #8b0000 0%, #dc143c 100%);
color: white;
border: 1px solid #6b0000;
border-radius: 3px;
padding: 4px 10px;
font-weight: 600;
cursor: pointer;
font-size: 0.85em;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
transition: all 0.2s ease;
&:hover {
background: linear-gradient(135deg, #a00000 0%, #ff1744 100%);
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
transform: translateY(-1px);
}
i {
margin-right: 4px;
font-size: 0.9em;
}
}
}
}
+1 -1
View File
@@ -6,7 +6,7 @@
"download": "#{DOWNLOAD}#",
"url": "#{URL}#",
"license": "LICENSE",
"version": "13.0.0",
"version": "13.0.1",
"authors": [
{
"name": "Uberwald",
+91 -89
View File
@@ -1,97 +1,99 @@
<section class="tab character-{{tab.id}} {{tab.cssClass}}" data-tab="{{tab.id}}" data-group="{{tab.group}}">
<section
class="tab character-{{tab.id}} {{tab.cssClass}}"
data-tab="{{tab.id}}"
data-group="{{tab.group}}"
>
<div class="main-div">
<fieldset>
<legend>{{localize "PRISMRPG.Label.biodata"}}</legend>
<div class="biodata">
<div class="biodata-elem">
<span class="name">Class</span>
{{formInput systemFields.biodata.fields.class value=system.biodata.class }}
</div>
<div class="biodata-elem">
<span class="name">Level</span>
{{formInput systemFields.biodata.fields.level value=system.biodata.level }}
</div>
<div class="biodata-elem">
<span class="name">Mortal</span>
{{formInput systemFields.biodata.fields.mortal value=system.biodata.mortal }}
</div>
<div class="biodata-elem">
<span class="name">Alignment</span>
{{formInput systemFields.biodata.fields.alignment value=system.biodata.alignment }}
</div>
<div class="biodata-elem">
<span class="name">Age</span>
{{formInput systemFields.biodata.fields.age value=system.biodata.age }}
</div>
<div class="biodata-elem">
<span class="name">Height</span>
{{formInput systemFields.biodata.fields.height value=system.biodata.height }}
</div>
<div class="biodata-elem">
<span class="name">Weight</span>
{{formInput systemFields.biodata.fields.weight value=system.biodata.weight }}
</div>
<div class="biodata-elem">
<span class="name">Eyes</span>
{{formInput systemFields.biodata.fields.eyes value=system.biodata.eyes }}
</div>
<div class="biodata-elem">
<span class="name">Hair</span>
{{formInput systemFields.biodata.fields.hair value=system.biodata.hair }}
</div>
<div class="biodata-elem">
<span class="name">Dev. Points (Total)</span>
{{formInput systemFields.developmentPoints.fields.total value=system.developmentPoints.total }}
</div>
<div class="biodata-elem">
<span class="name">Dev. Points (Rem.)</span>
{{formInput systemFields.developmentPoints.fields.remaining value=system.developmentPoints.remaining }}
</div>
<div class="biodata-elem">
<span class="name">Magic User</span>
{{formInput systemFields.biodata.fields.magicUser value=system.biodata.magicUser }}
</div>
<div class="biodata-elem">
<span class="name">Cleric User</span>
{{formInput systemFields.biodata.fields.clericUser value=system.biodata.clericUser }}
</div>
<div class="biodata-elem">
<span class="name">Save bonus (1/5levels)</span>
{{formInput systemFields.modifiers.fields.saveModifier value=system.modifiers.saveModifier disabled=true}}
</div>
<fieldset>
<legend>{{localize "PRISMRPG.Label.biodata"}}</legend>
<div class="biodata">
{{!-- Class and Mortal fields removed - don't exist in DataModel --}}
<div class="biodata-elem">
<span class="name">Level</span>
{{formInput
systemFields.biodata.fields.level
value=system.biodata.level
}}
</div>
<div class="biodata-elem">
<span class="name">Age</span>
{{formInput systemFields.biodata.fields.age value=system.biodata.age}}
</div>
<div class="biodata-elem">
<span class="name">Height</span>
{{formInput
systemFields.biodata.fields.height
value=system.biodata.height
}}
</div>
<div class="biodata-elem">
<span class="name">Weight</span>
{{formInput
systemFields.biodata.fields.weight
value=system.biodata.weight
}}
</div>
<div class="biodata-elem">
<span class="name">Eyes</span>
{{formInput
systemFields.biodata.fields.eyes
value=system.biodata.eyes
}}
</div>
<div class="biodata-elem">
<span class="name">Hair</span>
{{formInput
systemFields.biodata.fields.hair
value=system.biodata.hair
}}
</div>
<div class="biodata-elem">
<span class="name">Dev. Points (Total)</span>
{{formInput
systemFields.developmentPoints.fields.total
value=system.developmentPoints.total
}}
</div>
<div class="biodata-elem">
<span class="name">Dev. Points (Rem.)</span>
{{formInput
systemFields.developmentPoints.fields.remaining
value=system.developmentPoints.remaining
}}
</div>
<div class="biodata-elem">
<span class="name">Magic User</span>
{{formInput
systemFields.biodata.fields.magicUser
value=system.biodata.magicUser
}}
</div>
{{!-- Cleric User, modifiers, and hpPerLevel fields removed - don't exist in DataModel --}}
{{#if system.biodata.magicUser}}
<div class="biodata-elem">
<span class="name">Spell bonus (1/5levels)</span>
{{formInput systemFields.modifiers.fields.levelSpellModifier value=system.modifiers.levelSpellModifier disabled=true}}
</div>
{{/if}}
{{#if system.biodata.clericUser}}
<div class="biodata-elem">
<span class="name">Miracle bonus (1/5levels)</span>
{{formInput systemFields.modifiers.fields.levelMiracleModifier value=system.modifiers.levelMiracleModifier disabled=true}}
</div>
{{/if}}
</fieldset>
<div class="biodata-elem">
<span class="name">Last HD roll</span>
{{formInput systemFields.biodata.fields.hpPerLevel value=system.biodata.hpPerLevel disabled=true}}
</div>
<fieldset>
<legend>{{localize "PRISMRPG.Label.description"}}</legend>
{{formInput
systemFields.description
enriched=enrichedDescription
value=system.description
name="system.description"
toggled=true
}}
</fieldset>
</div>
</fieldset>
<fieldset>
<legend>{{localize "PRISMRPG.Label.description"}}</legend>
{{formInput systemFields.description enriched=enrichedDescription value=system.description name="system.description"
toggled=true}}
</fieldset>
<fieldset>
<legend>{{localize "PRISMRPG.Label.notes"}}</legend>
{{formInput systemFields.notes enriched=enrichedNotes value=system.notes name="system.notes" toggled=true}}
</fieldset>
<fieldset>
<legend>{{localize "PRISMRPG.Label.notes"}}</legend>
{{formInput
systemFields.notes
enriched=enrichedNotes
value=system.notes
name="system.notes"
toggled=true
}}
</fieldset>
</div>
</section>
+145 -130
View File
@@ -1,108 +1,77 @@
<section class="tab character-{{tab.id}} {{tab.cssClass}}" data-tab="combat" data-group="sheet">
<section
class="tab character-{{tab.id}} {{tab.cssClass}}"
data-tab="combat"
data-group="sheet"
>
<div class="main-div">
<fieldset>
<legend>{{localize "PRISMRPG.Label.combatDetails"}}</legend>
<div class="combat-details">
<div class="combat-detail">
<button class="action ranged-attack-button" data-action="rangedAttackDefense">
{{localize "PRISMRPG.Label.rangedAttackDefense"}}
</button>
<button class="action ranged-attack-button" data-action="rollInitiative">
{{localize "PRISMRPG.Label.rollInitiative"}}
</button>
<div class="flexrow armor-hp">
<span class="name">{{localize "PRISMRPG.Label.armorHitPoints"}}</span>
{{formInput systemFields.combat.fields.armorHitPoints value=system.combat.armorHitPoints localize=true }}
<a data-action="armorHitPointsPlus"><i class="fa-solid fa-hexagon-plus"></i></a>
<a data-action="armorHitPointsMinus"><i class="fa-solid fa-hexagon-minus"></i></a>
</div>
<div class="flexrow granted">
<span class="">{{localize
"PRISMRPG.Label.grantedAttackDice"}}</a></span>
{{formInput systemFields.granted.fields.attackDice value=system.granted.attackDice disabled=isPlayMode }}
</div>
<div class="flexrow granted ">
<span class="">{{localize
"PRISMRPG.Label.grantedDefenseDice"}}</a></span>
{{formInput systemFields.granted.fields.defenseDice value=system.granted.defenseDice disabled=isPlayMode }}
</div>
<div class="flexrow granted">
<span class="">{{localize
"PRISMRPG.Label.grantedDamageDice"}}</a></span>
{{formInput systemFields.granted.fields.damageDice value=system.granted.damageDice disabled=isPlayMode }}
</div>
</div>
</div>
</fieldset>
<fieldset>
<legend>{{localize "PRISMRPG.Label.wounds"}}</legend>
<div class="wounds">
{{#each system.hp.wounds as |wound idx|}}
<div class="wound">
Name:<input class="wound-description wound-data" type="text" data-type="String" data-index="{{@index}}"
value="{{wound.description}}" data-name="description">
Duration:<input class="wound-duration wound-data" type="text" data-type="Number" data-index="{{@index}}"
value="{{wound.duration}}" data-name="duration">
HP:<input class="wound-value wound-data" type="text" data-type="Number" data-index="{{@index}}"
value="{{wound.value}}" data-name="value">
</div>
{{/each}}
</div>
</fieldset>
<fieldset>
<legend>{{localize "PRISMRPG.Label.weapons"}}</legend>
<div class="weapons">
{{#each weapons as |item|}}
<div class="weapon" data-item-id="{{item.id}}" data-item-uuid="{{item.uuid}}" data-drag="true"
data-drag-type="damage">
{{#if (ne item.img "icons/svg/item-bag.svg")}}
<img class="item-img" src="{{item.img}}" data-tooltip="{{item.name}}" />
{{/if}}
<div class="name" data-tooltip="{{item.system.description}}">
{{item.name}}
<div
class="weapon"
data-item-id="{{item.id}}"
data-item-uuid="{{item.uuid}}"
data-drag="true"
data-drag-type="damage"
>
{{#if (ne item.img "icons/svg/item-bag.svg")}}
<img
class="item-img"
src="{{item.img}}"
data-tooltip="{{item.name}}"
data-action="postItemToChat"
/>
{{/if}}
<div class="name" data-tooltip="{{item.system.description}}">
{{item.name}}
</div>
<div class="attack-icons">
<a
class="rollable"
data-roll-type="weapon-attack"
data-roll-key="{{item.id}}"
data-tooltip="Roll Attack"
>
<i
class="lf-roll-small fa-solid fa-swords"
data-roll-type="weapon-attack"
data-roll-key="{{item.id}}"
></i>
</a>
<a
class="rollable"
data-roll-type="weapon-damage-medium"
data-roll-key="{{item.id}}"
data-tooltip="Roll Damage"
>
<i
class="fa-regular fa-face-head-bandage"
data-roll-type="weapon-damage-medium"
data-roll-key="{{item.id}}"
></i>
</a>
</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>
<div class="attack-icons">
<a class="rollable" data-roll-type="weapon-attack" data-roll-key="{{item.id}}" data-tooltip="Roll Attack">
<i class="lf-roll-small fa-solid fa-swords" data-roll-type="weapon-attack"
data-roll-key="{{item.id}}"></i>
</a>
<a class="rollable" data-roll-type="weapon-defense" data-roll-key="{{item.id}}" data-tooltip="Roll Defense">
<i class="fa-solid fa-shield-halved" data-roll-type="weapon-defense" data-roll-key="{{item.id}}"></i>
</a>
<a class="rollable" data-roll-type="weapon-damage-small" data-roll-key="{{item.id}}"
data-tooltip="Roll Damage (Small)">
<i class="fa-regular fa-face-head-bandage" data-roll-type="weapon-damage-small"
data-roll-key="{{item.id}}"></i>S
</a>
<a class="rollable" data-roll-type="weapon-damage-medium" data-roll-key="{{item.id}}"
data-tooltip="Roll Damage (Medium)">
<i class="fa-regular fa-face-head-bandage" data-roll-type="weapon-damage-medium"
data-roll-key="{{item.id}}"></i>M
</a>
</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>
@@ -111,22 +80,48 @@
<legend>{{localize "PRISMRPG.Label.armors"}}</legend>
<div class="armors">
{{#each armors as |item|}}
<div class="armor" data-item-id="{{item.id}}" data-item-uuid="{{item.uuid}}">
<img class="item-img" src="{{item.img}}" data-tooltip="{{item.name}}" />
<div class="name" data-tooltip="{{item.system.description}}">
{{item.name}}
<div
class="armor"
data-item-id="{{item.id}}"
data-item-uuid="{{item.uuid}}"
>
<img
class="item-img"
src="{{item.img}}"
data-tooltip="{{item.name}}"
data-action="postItemToChat"
/>
<div class="name" data-tooltip="{{item.system.description}}">
{{item.name}}
</div>
<div
class="item-detail"
data-tooltip="Defense"
>{{item.system.defense}}</div>
<div
class="item-detail"
data-tooltip="Maximum movement"
>{{item.system.maximumMovement}}</div>
<div class="item-detail" data-tooltip="HP">{{item.system.hp}}</div>
<div
class="item-detail"
data-tooltip="Damage Reduction"
>{{item.system.damageReduction}}</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>
<div class="item-detail" data-tooltip="Defense">{{item.system.defense}}</div>
<div class="item-detail" data-tooltip="Maximum movement">{{item.system.maximumMovement}}</div>
<div class="item-detail" data-tooltip="HP">{{item.system.hp}}</div>
<div class="item-detail" data-tooltip="Damage Reduction">{{item.system.damageReduction}}</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>
@@ -135,25 +130,45 @@
<legend>{{localize "PRISMRPG.Label.shields"}}</legend>
<div class="shields">
{{#each shields as |item|}}
<div class="shield" data-item-id="{{item.id}}" data-item-uuid="{{item.uuid}}">
<img class="item-img" src="{{item.img}}" data-tooltip="{{item.name}}" />
<div class="name" data-tooltip="{{item.system.description}}">
{{item.name}}
</div>
<div class="item-detail" data-tooltip="Block APC">
<i class="fa-solid fa-hand"></i> {{item.system.apc}} APC
</div>
<div class="item-detail" data-tooltip="Shield Rating">
<i class="fa-solid fa-shield"></i> {{item.system.sr}}
</div>
<div
class="shield"
data-item-id="{{item.id}}"
data-item-uuid="{{item.uuid}}"
>
<img
class="item-img"
src="{{item.img}}"
data-tooltip="{{item.name}}"
data-action="postItemToChat"
/>
<div class="name" data-tooltip="{{item.system.description}}">
{{item.name}}
</div>
<div class="item-detail" data-tooltip="Block APC">
<i class="fa-solid fa-hand"></i>
{{item.system.apc}}
APC
</div>
<div class="item-detail" data-tooltip="Shield Rating">
<i class="fa-solid fa-shield"></i>
{{item.system.sr}}
</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 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>
</div>
{{/each}}
</div>
</fieldset>
+6 -6
View File
@@ -4,11 +4,11 @@
<fieldset>
<legend>{{localize "PRISMRPG.Label.money"}}</legend>
<div class="moneys">
{{formField systemFields.moneys.fields.tinbit.fields.value value=system.moneys.tinbit.value localize=true}}
{{formField systemFields.moneys.fields.copper.fields.value value=system.moneys.copper.value localize=true}}
{{formField systemFields.moneys.fields.silver.fields.value value=system.moneys.silver.value localize=true}}
{{formField systemFields.moneys.fields.gold.fields.value value=system.moneys.gold.value localize=true}}
{{formField systemFields.moneys.fields.platinum.fields.value value=system.moneys.platinum.value localize=true}}
{{formField systemFields.moneys.fields.coppercoin.fields.value value=system.moneys.coppercoin.value localize=true}}
{{formField systemFields.moneys.fields.silvercoin.fields.value value=system.moneys.silvercoin.value localize=true}}
{{formField systemFields.moneys.fields.goldcoin.fields.value value=system.moneys.goldcoin.value localize=true}}
{{formField systemFields.moneys.fields.note.fields.value value=system.moneys.note.value localize=true}}
{{formField systemFields.moneys.fields.steam.fields.value value=system.moneys.steam.value localize=true}}
</div>
</fieldset>
@@ -17,7 +17,7 @@
<div class="equipments">
{{#each equipments as |item|}}
<div class="equipment" data-item-id="{{item.id}}" data-item-uuid="{{item.uuid}}">
<img class="item-img" src="{{item.img}}" data-tooltip="{{item.name}}" />
<img class="item-img" src="{{item.img}}" data-tooltip="{{item.name}}" data-action="postItemToChat" />
<div class="name" data-tooltip="{{{item.system.description}}}">
{{item.name}}
</div>
+346 -450
View File
@@ -1,461 +1,357 @@
<section
class="character-main character-main-{{ifThen isPlayMode 'play' 'edit'}}"
>
{{log "character-main" this}}
<fieldset>
<legend>{{localize "PRISMRPG.Label.pc"}}</legend>
<div class="character-pc character-pc-{{ifThen isPlayMode 'play' 'edit'}}">
<div class="character-left">
<div class="character-left-image">
<img
class="character-img"
src="{{actor.img}}"
data-edit="img"
data-action="editImage"
data-tooltip="{{actor.name}}"
/>
</div>
<fieldset class="">
<div class="flexrow character-hp">
<span class="name">{{localize "PRISMRPG.Label.HP"}}</span>
{{formInput
systemFields.hp.fields.value
value=system.hp.value
disabled=isPlayMode
classes="character-hp-value"
}}
&nbsp;/&nbsp;
{{formInput
systemFields.hp.fields.max
value=system.hp.max
disabled=isPlayMode
classes="character-hp-value"
}}
</div>
<div class="flexrow character-hp">
<span class="name">{{localize "PRISMRPG.Label.grit"}}</span>
{{formInput
systemFields.grit.fields.current
value=system.grit.current
disabled=isPlayMode
classes="character-hp"
}}
<span class="name">{{localize "PRISMRPG.Label.earned"}}</span>
{{formInput
systemFields.grit.fields.earned
value=system.grit.earned
disabled=isPlayMode
classes="character-hp"
}}
</div>
<div class="flexrow character-hp">
<span class="name">{{localize "PRISMRPG.Label.luck"}}</span>
{{formInput
systemFields.luck.fields.current
value=system.luck.current
disabled=isPlayMode
classes="character-hp"
}}
<span class="name">{{localize "PRISMRPG.Label.earned"}}</span>
{{formInput
systemFields.luck.fields.earned
value=system.luck.earned
disabled=isPlayMode
classes="character-hp"
}}
</div>
<div class="flexrow">
<span class="">{{localize
"PRISMRPG.Label.damageResistanceShort"
}}</span>
{{formInput
systemFields.hp.fields.damageResistance
value=system.hp.fields.damageResistance
disabled=isPlayMode
classes="character-hp"
}}
</div>
</fieldset>
<section class="character-main-v2">
{{log "character-main-v2" this}}
<div class="character-sheet-wrapper">
{{! Character Header with Name }}
<div class="character-header">
<div class="character-name-banner">
{{formInput
fields.name
value=source.name
rootId=partId
disabled=isPlayMode
placeholder="Character Name"
}}
</div>
<div class="character-right">
<div class="character-name">
{{formInput
fields.name
value=source.name
rootId=partId
disabled=isPlayMode
}}
<a
class="control"
data-action="toggleSheet"
data-tooltip="PRISMRPG.ToggleSheet"
data-tooltip-direction="UP"
>
<i class="fa-solid fa-user-{{ifThen isPlayMode 'lock' 'pen'}}"></i>
</a>
</div>
<fieldset
class="character-characteristics character-characteristics-{{ifThen
isPlayMode
'play'
'edit'
}}"
<div class="character-toggle-controls">
<a
class="control"
data-action="toggleSheet"
data-tooltip="PRISMRPG.ToggleSheet"
data-tooltip-direction="UP"
>
<legend>{{localize "PRISMRPG.Label.Saves"}}</legend>
<div class="character-saves">
<div class="character-save">
<span class="name"><a
class="rollable"
data-roll-type="save"
data-roll-key="will"
><i class="lf-roll-small fa-solid fa-dice-d20"></i>
{{localize "PRISMRPG.Label.saves.will"}}
</a></span>
{{formField
systemFields.saves.fields.will.fields.value
value=system.saves.will.value
disabled=true
}}
<span class="name">
<a
class="rollable"
data-roll-type="save"
data-roll-key="dodge"
><i class="lf-roll-small fa-solid fa-dice-d20"></i>
{{localize "PRISMRPG.Label.saves.dodge"}}
</a>
</span>
{{formField
systemFields.saves.fields.dodge.fields.value
value=system.saves.dodge.value
disabled=true
}}
<span class="name">
<a
class="rollable"
data-roll-type="save"
data-roll-key="toughness"
><i class="lf-roll-small fa-solid fa-dice-d20"></i>
{{localize "PRISMRPG.Label.saves.toughness"}}
</a>
</span>
{{formField
systemFields.saves.fields.toughness.fields.value
value=system.saves.toughness.value
disabled=true
}}
</div>
<div class="character-save">
<span class="name">
<a
class="rollable"
data-roll-type="save"
data-roll-key="contagion"
><i class="lf-roll-small fa-solid fa-dice-d20"></i>
{{localize "PRISMRPG.Label.saves.contagion"}}
</a>
</span>
{{formField
systemFields.saves.fields.contagion.fields.value
value=system.saves.contagion.value
disabled=true
}}
<span class="name">
<a
class="rollable"
data-roll-type="save"
data-roll-key="poison"
><i class="lf-roll-small fa-solid fa-dice-d20"></i>
{{localize "PRISMRPG.Label.saves.poison"}}
</a>
</span>
{{formField
systemFields.saves.fields.poison.fields.value
value=system.saves.poison.value
disabled=true
}}
<!--
<span class="name-pain">
<a class="rollable" data-roll-type="save" data-roll-key="pain" data-roll-dice="D12"><i
class="lf-roll-small fa-solid fa-dice-d12"></i>
{{localize "PRISMRPG.Label.saves.pain"}}
</a>
</span>
<span class="name-pain">
<a class="rollable" data-roll-type="save" data-roll-key="pain" data-roll-dice="D20"><i
class="lf-roll-small fa-solid fa-dice-d20"></i>
{{localize "PRISMRPG.Label.saves.pain"}}
</a>
</span>
{{formField systemFields.saves.fields.pain.fields.value value=system.saves.pain.value disabled=true}}
<span data-tooltip="Pain save if wound exceeds">
{{formField systemFields.hp.fields.painDamage value=system.hp.painDamage disabled=isPlayMode
tooltip="Pain Damage"}}
</span>
-->
</div>
</div>
</fieldset>
<fieldset
class="character-characteristics character-characteristics-{{ifThen
isPlayMode
'play'
'edit'
}}"
>
<legend>{{localize "PRISMRPG.Label.Challenges"}}</legend>
<div class="character-challenges">
<div class="character-challenge">
<span class="name"><a
class="rollable"
data-roll-type="challenge"
data-roll-key="str"
><i class="lf-roll-small fa-solid fa-dice-d20"></i>{{localize
"PRISMRPG.Label.challenges.strength"
}}</a></span>
{{formField
systemFields.challenges.fields.str.fields.value
value=system.challenges.str.value
disabled=true
}}
<span class="name"><a
class="rollable"
data-roll-type="challenge"
data-roll-key="agility"
><i class="lf-roll-small fa-solid fa-dice-d20"></i>{{localize
"PRISMRPG.Label.challenges.agility"
}}</a></span>
{{formField
systemFields.challenges.fields.agility.fields.value
value=system.challenges.agility.value
disabled=true
}}
<span class="name"><a
class="rollable"
data-roll-type="challenge"
data-roll-key="dying"
><i class="lf-roll-small fa-solid fa-dice-d20"></i>{{localize
"PRISMRPG.Label.challenges.dying"
}}</a></span>
{{formField
systemFields.challenges.fields.dying.fields.value
value=system.challenges.dying.value
disabled=true
}}
</div>
</div>
</fieldset>
<fieldset
class="character-characteristics character-characteristics-{{ifThen
isPlayMode
'play'
'edit'
}}"
>
<legend>{{localize "PRISMRPG.Label.Movement"}}</legend>
<div class="character-movements">
<div class="character-movement">
<span class="name">{{localize
"PRISMRPG.Label.movement.walk"
}}</span>
{{formField
systemFields.movement.fields.walk
value=system.movement.walk
disabled=isPlayMode
}}
<span class="name">{{localize
"PRISMRPG.Label.movement.jog"
}}</span>
{{formField
systemFields.movement.fields.jog
value=system.movement.jog
disabled=isPlayMode
}}
<span class="name">{{localize
"PRISMRPG.Label.movement.run"
}}</span>
{{formField
systemFields.movement.fields.run
value=system.movement.run
disabled=isPlayMode
}}
<span class="name">{{localize
"PRISMRPG.Label.movement.sprint"
}}</span>
{{formField
systemFields.movement.fields.sprint
value=system.movement.sprint
disabled=isPlayMode
}}
</div>
<div class="character-movement">
<span class="name">{{localize
"PRISMRPG.Label.movement.jumpBroad"
}}</span>
{{formField
systemFields.jump.fields.broad
value=system.jump.broad
disabled=isPlayMode
}}
<span class="name">{{localize
"PRISMRPG.Label.movement.jumpRunning"
}}</span>
{{formField
systemFields.jump.fields.running
value=system.jump.running
disabled=isPlayMode
}}
<span class="name">{{localize
"PRISMRPG.Label.movement.jumpVertical"
}}</span>
{{formField
systemFields.jump.fields.vertical
value=system.jump.vertical
disabled=isPlayMode
}}
</div>
</div>
</fieldset>
<i class="fa-solid fa-user-{{ifThen isPlayMode 'lock' 'pen'}}"></i>
</a>
</div>
</div>
</fieldset>
<fieldset
class="character-characteristics character-characteristics-{{ifThen
isPlayMode
'play'
'edit'
}}"
>
<legend>{{localize "PRISMRPG.Label.characteristics"}}</legend>
<div class="character-characteristic">
<span>{{localize "PRISMRPG.Label.str"}}</span>
{{formField
systemFields.characteristics.fields.str.fields.value
value=system.characteristics.str.value
disabled=isPlayMode
data-char-id="str"
}}
{{formField
systemFields.characteristics.fields.str.fields.percent
value=system.characteristics.str.percent
disabled=isPlayMode
type="number"
}}
</div>
<div class="character-characteristic">
<span>{{localize "PRISMRPG.Label.int"}}</span>
{{formField
systemFields.characteristics.fields.int.fields.value
value=system.characteristics.int.value
disabled=isPlayMode
data-char-id="int"
}}
<div class="character-main-grid">
{{! Left Column - Portrait, Attributes & HP }}
<div class="character-left-column">
{{! Portrait + HP column }}
<div class="portrait-hp-column">
{{! Portrait }}
<div class="character-portrait">
<img
class="character-img"
src="{{actor.img}}"
data-edit="img"
data-action="editImage"
data-tooltip="{{actor.name}}"
/>
</div>
{{formField
systemFields.characteristics.fields.int.fields.percent
value=system.characteristics.int.percent
disabled=isPlayMode
type="number"
}}
</div>
<div class="character-characteristic">
<span>{{localize "PRISMRPG.Label.wis"}}</span>
{{formField
systemFields.characteristics.fields.wis.fields.value
value=system.characteristics.wis.value
disabled=isPlayMode
data-char-id="wis"
}}
{{formField
systemFields.characteristics.fields.wis.fields.percent
value=system.characteristics.wis.percent
disabled=isPlayMode
type="number"
}}
</div>
<div class="character-characteristic">
<span>{{localize "PRISMRPG.Label.dex"}}</span>
{{formField
systemFields.characteristics.fields.dex.fields.value
value=system.characteristics.dex.value
disabled=isPlayMode
data-char-id="wis"
}}
{{formField
systemFields.characteristics.fields.dex.fields.percent
value=system.characteristics.dex.percent
disabled=isPlayMode
type="number"
}}
</div>
<div class="character-characteristic">
<span>{{localize "PRISMRPG.Label.con"}}</span>
{{formField
systemFields.characteristics.fields.con.fields.value
value=system.characteristics.con.value
disabled=isPlayMode
data-char-id="con"
}}
{{formField
systemFields.characteristics.fields.con.fields.percent
value=system.characteristics.con.percent
disabled=isPlayMode
type="number"
}}
</div>
<div class="character-characteristic">
<span>{{localize "PRISMRPG.Label.cha"}}</span>
{{formField
systemFields.characteristics.fields.cha.fields.value
value=system.characteristics.cha.value
disabled=isPlayMode
data-char-id="cha"
}}
{{formField
systemFields.characteristics.fields.cha.fields.percent
value=system.characteristics.cha.percent
disabled=isPlayMode
type="number"
}}
</div>
</fieldset>
{{!-- Sub-Attributes (Prism RPG) --}}
<fieldset
class="character-subattributes character-subattributes-{{ifThen
isPlayMode
'play'
'edit'
}}"
>
<legend>{{localize "PRISMRPG.Label.subAttributes"}}</legend>
<div class="subattributes-grid">
{{#each config.SUB_ATTRIBUTES as |subAttr|}}
<div class="character-subattribute" data-tooltip="{{localize subAttr.description}}">
<span class="subattr-label">{{localize subAttr.label}}</span>
<span class="subattr-value">{{lookup (lookup ../system.subAttributes subAttr.id) 'value'}}</span>
<span class="subattr-parents">({{#each subAttr.parents}}{{localize (concat "PRISMRPG.Label." this)}}{{#unless @last}}/{{/unless}}{{/each}})</span>
{{! HP Shields (3 shields) - Below portrait }}
<div class="hp-shields-section">
<div class="hp-shields">
<div class="hp-item">
<div class="hp-label">HP</div>
<a data-action="hpMinus"><i class="fa-solid fa-minus"></i></a>
<div class="hp-value">
{{formInput
systemFields.hp.fields.value
value=system.hp.value
disabled=isPlayMode
}}
</div>
<a data-action="hpPlus"><i class="fa-solid fa-plus"></i></a>
<div class="hp-separator">/</div>
<div class="hp-max">
{{formInput
systemFields.hp.fields.max
value=system.hp.max
disabled=isPlayMode
}}
</div>
</div>
<div class="hp-item">
<div class="hp-label">MANA</div>
<a data-action="manaPointsMinus"><i class="fa-solid fa-minus"></i></a>
<div class="hp-value">
{{formInput
systemFields.manaPoints.fields.value
value=system.manaPoints.value
disabled=isPlayMode
}}
</div>
<a data-action="manaPointsPlus"><i class="fa-solid fa-plus"></i></a>
<div class="hp-separator">/</div>
<div class="hp-max">
{{formInput
systemFields.manaPoints.fields.max
value=system.manaPoints.max
disabled=isPlayMode
}}
</div>
</div>
<div class="hp-item">
<div class="hp-label">ARMOR</div>
<a data-action="armorPointsMinus"><i class="fa-solid fa-minus"></i></a>
<div class="hp-value">
{{formInput
systemFields.armorPoints.fields.value
value=system.armorPoints.value
disabled=isPlayMode
}}
</div>
<a data-action="armorPointsPlus"><i class="fa-solid fa-plus"></i></a>
<div class="hp-separator">/</div>
<div class="hp-max">
{{formInput
systemFields.armorPoints.fields.max
value=system.armorPoints.max
disabled=isPlayMode
}}
</div>
</div>
<div class="hp-item">
<div class="hp-label" data-tooltip="Action Points">AP</div>
<a data-action="actionPointsMinus"><i class="fa-solid fa-minus"></i></a>
<div class="hp-value">
{{formInput
systemFields.actionPoints.fields.value
value=system.actionPoints.value
disabled=isPlayMode
}}
</div>
<a data-action="actionPointsPlus"><i class="fa-solid fa-plus"></i></a>
<div class="hp-separator">/</div>
<div class="hp-max">
{{formInput
systemFields.actionPoints.fields.max
value=system.actionPoints.max
disabled=isPlayMode
}}
</div>
</div>
</div>
</div>
</div>
{{/each}}
{{! Core Attributes (STR, DEX, CON, INT, WIS, CHA) }}
<div class="character-attributes">
<div class="attribute-shield">
<div class="attribute-label">
<a class="rollable" data-roll-type="characteristic" data-roll-key="str">
<i class="fa-duotone fa-solid fa-dice-d20"></i>
STR
</a>
</div>
<div class="attribute-value">
{{formInput
systemFields.characteristics.fields.str.fields.value
value=system.characteristics.str.value
disabled=isPlayMode
}}
</div>
<div class="attribute-save">
<a class="rollable save-rollable" data-roll-type="save" data-roll-key="str" data-tooltip="{{localize 'PRISMRPG.RollSavingThrow'}}">
<i class="fa-duotone fa-solid fa-dice-d20"></i>
</a>
{{formInput
systemFields.saves.fields.str.fields.value
value=system.saves.str.value
disabled=isPlayMode
}}
</div>
</div>
<div class="attribute-shield">
<div class="attribute-label">
<a class="rollable" data-roll-type="characteristic" data-roll-key="dex">
<i class="fa-duotone fa-solid fa-dice-d20"></i>
DEX
</a>
</div>
<div class="attribute-value">
{{formInput
systemFields.characteristics.fields.dex.fields.value
value=system.characteristics.dex.value
disabled=isPlayMode
}}
</div>
<div class="attribute-save">
<a class="rollable save-rollable" data-roll-type="save" data-roll-key="dex" data-tooltip="{{localize 'PRISMRPG.RollSavingThrow'}}">
<i class="fa-duotone fa-solid fa-dice-d20"></i>
</a>
{{formInput
systemFields.saves.fields.dex.fields.value
value=system.saves.dex.value
disabled=isPlayMode
}}
</div>
</div>
<div class="attribute-shield">
<div class="attribute-label">
<a class="rollable" data-roll-type="characteristic" data-roll-key="con">
<i class="fa-duotone fa-solid fa-dice-d20"></i>
CON
</a>
</div>
<div class="attribute-value">
{{formInput
systemFields.characteristics.fields.con.fields.value
value=system.characteristics.con.value
disabled=isPlayMode
}}
</div>
<div class="attribute-save">
<a class="rollable save-rollable" data-roll-type="save" data-roll-key="con" data-tooltip="{{localize 'PRISMRPG.RollSavingThrow'}}">
<i class="fa-duotone fa-solid fa-dice-d20"></i>
</a>
{{formInput
systemFields.saves.fields.con.fields.value
value=system.saves.con.value
disabled=isPlayMode
}}
</div>
</div>
<div class="attribute-shield">
<div class="attribute-label">
<a class="rollable" data-roll-type="characteristic" data-roll-key="int">
<i class="fa-duotone fa-solid fa-dice-d20"></i>
INT
</a>
</div>
<div class="attribute-value">
{{formInput
systemFields.characteristics.fields.int.fields.value
value=system.characteristics.int.value
disabled=isPlayMode
}}
</div>
<div class="attribute-save">
<a class="rollable save-rollable" data-roll-type="save" data-roll-key="int" data-tooltip="{{localize 'PRISMRPG.RollSavingThrow'}}">
<i class="fa-duotone fa-solid fa-dice-d20"></i>
</a>
{{formInput
systemFields.saves.fields.int.fields.value
value=system.saves.int.value
disabled=isPlayMode
}}
</div>
</div>
<div class="attribute-shield">
<div class="attribute-label">
<a class="rollable" data-roll-type="characteristic" data-roll-key="wis">
<i class="fa-duotone fa-solid fa-dice-d20"></i>
WIS
</a>
</div>
<div class="attribute-value">
{{formInput
systemFields.characteristics.fields.wis.fields.value
value=system.characteristics.wis.value
disabled=isPlayMode
}}
</div>
<div class="attribute-save">
<a class="rollable save-rollable" data-roll-type="save" data-roll-key="wis" data-tooltip="{{localize 'PRISMRPG.RollSavingThrow'}}">
<i class="fa-duotone fa-solid fa-dice-d20"></i>
</a>
{{formInput
systemFields.saves.fields.wis.fields.value
value=system.saves.wis.value
disabled=isPlayMode
}}
</div>
</div>
<div class="attribute-shield">
<div class="attribute-label">
<a class="rollable" data-roll-type="characteristic" data-roll-key="cha">
<i class="fa-duotone fa-solid fa-dice-d20"></i>
CHA
</a>
</div>
<div class="attribute-value">
{{formInput
systemFields.characteristics.fields.cha.fields.value
value=system.characteristics.cha.value
disabled=isPlayMode
}}
</div>
<div class="attribute-save">
<a class="rollable save-rollable" data-roll-type="save" data-roll-key="cha" data-tooltip="{{localize 'PRISMRPG.RollSavingThrow'}}">
<i class="fa-duotone fa-solid fa-dice-d20"></i>
</a>
{{formInput
systemFields.saves.fields.cha.fields.value
value=system.saves.cha.value
disabled=isPlayMode
}}
</div>
</div>
</div>
</div>
{{! Right Column - Race, Classes }}
<div class="character-right-column">
{{! Race }}
<div class="race-section">
<div class="race-box">
<h4 class="race-label">Race</h4>
<div class="race-content">
{{#if race}}
<div class="race-item" data-item-id="{{race.id}}" data-item-uuid="{{race.uuid}}">
<img class="item-img" src="{{race.img}}" data-tooltip="{{race.name}}" data-action="postItemToChat" />
<div class="race-name">{{race.name}}</div>
<div class="controls">
<a data-tooltip="{{localize 'PRISMRPG.Edit'}}" data-action="edit" data-item-id="{{race.id}}"
data-item-uuid="{{race.uuid}}"><i class="fas fa-edit"></i></a>
<a data-tooltip="{{localize 'PRISMRPG.Delete'}}" data-action="delete" data-item-id="{{race.id}}"
data-item-uuid="{{race.uuid}}"><i class="fas fa-trash"></i></a>
</div>
</div>
{{else}}
<div class="no-race">
<p>{{localize "PRISMRPG.Message.dropRace"}}</p>
</div>
{{/if}}
</div>
</div>
</div>
{{! Classes (Three boxes) }}
<div class="classes-section">
{{#each classSlots as |classItem index|}}
<div class="class-box">
<h4 class="class-label">Class {{add index 1}}</h4>
<div class="class-content">
{{#if classItem}}
<div class="class-item" data-item-id="{{classItem.id}}" data-item-uuid="{{classItem.uuid}}">
<img class="item-img" src="{{classItem.img}}" data-tooltip="{{classItem.name}}" data-action="postItemToChat" />
<div class="class-name">{{classItem.name}}</div>
<div class="controls">
<a data-tooltip="{{localize 'PRISMRPG.Edit'}}" data-action="edit" data-item-id="{{classItem.id}}"
data-item-uuid="{{classItem.uuid}}"><i class="fas fa-edit"></i></a>
<a data-tooltip="{{localize 'PRISMRPG.Delete'}}" data-action="delete" data-item-id="{{classItem.id}}"
data-item-uuid="{{classItem.uuid}}"><i class="fas fa-trash"></i></a>
</div>
</div>
{{else}}
<div class="no-class">
<p>{{localize "PRISMRPG.Message.dropClass"}}</p>
</div>
{{/if}}
</div>
</div>
{{/each}}
{{! Alignment }}
<div class="class-box">
<h4 class="class-label">Alignment</h4>
<div class="class-content">
<div class="alignment-value">
{{formInput
systemFields.biodata.fields.alignment
value=system.biodata.alignment
disabled=isPlayMode
}}
</div>
</div>
</div>
</div>
</div>
</div>
</fieldset>
</div>
</section>
-52
View File
@@ -1,52 +0,0 @@
<section class="tab character-{{tab.id}} {{tab.cssClass}}" data-tab="{{tab.id}}" data-group="{{tab.group}}">
<div class="main-div">
<fieldset>
<legend>{{localize "PRISMRPG.Label.divinityPoints"}}</legend>
<div class="miracle-details">
<div class="miracle-detail">
<span>Current</span>
{{formField systemFields.divinityPoints.fields.value value=system.divinityPoints.value localize=true}}
<a data-action="divinityPointsPlus"><i class="fa-solid fa-hexagon-plus"></i></a>
<a data-action="divinityPointsMinus"><i class="fa-solid fa-hexagon-minus"></i></a>
<span>Max</span>
{{formField systemFields.divinityPoints.fields.max value=system.divinityPoints.max localize=true
disabled=isPlayMode}}
</div>
</div>
</fieldset>
<fieldset>
<legend>{{localize "PRISMRPG.Label.miracles"}}{{#if isEditMode}}<a class="action" data-tooltip="{{localize "
PRISMRPG.Tooltip.addMiracle"}}" data-tooltip-direction="UP"><i class="fas fa-plus"
data-action="createMiracle"></i></a>{{/if}}</legend>
<div class="miracles">
{{#each miracles as |item|}}
<div class="miracle" data-item-id="{{item.id}}" data-item-uuid="{{item.uuid}}" data-drag="true">
<img class="item-img" src="{{item.img}}" data-tooltip="{{item.name}}" />
<div class="name">
{{item.name}}
</div>
<a class="rollable" data-roll-type="miracle-attack" data-roll-key="{{item.id}}" data-tooltip="Miracle Attack">
<i class="lf-roll-small fa-solid fa-swords" data-roll-type="miracle-attack" data-roll-key="{{item.id}}"></i>
</a>
<a class="rollable" data-roll-type="miracle-power" data-roll-key="{{item.id}}" data-tooltip="Miracle Power">
<i class="fa-duotone fa-solid fa-stars" data-roll-type="miracle-power" data-roll-key="{{item.id}}"></i>
</a>
<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>
+5 -85
View File
@@ -1,76 +1,18 @@
<section class="tab character-{{tab.id}} {{tab.cssClass}}" data-tab="skills" data-group="sheet">
<div class="main-div">
{{!-- Core Skill Selection (Prism RPG) --}}
<fieldset class="core-skill-selection">
<legend data-tooltip="{{localize 'PRISMRPG.Tooltip.coreSkill'}}" data-tooltip-direction="UP">
{{localize "PRISMRPG.Label.coreSkill"}}
</legend>
<div class="core-skill-info">
{{#if system.coreSkill.skill}}
<div class="selected-core-skill">
<span class="core-skill-name">{{localize (concat "PRISMRPG.CoreSkill." system.coreSkill.skill)}}</span>
<span class="core-skill-bonus">+5 {{localize "PRISMRPG.Label.basicChecks"}}</span>
{{#if system.coreSkill.attributeChoice}}
<span class="attribute-bonus">+2 {{localize (concat "PRISMRPG.Label." system.coreSkill.attributeChoice)}}</span>
{{/if}}
<span class="advanced-checks">{{localize "PRISMRPG.Label.advancedChecksEnabled"}}</span>
</div>
{{else}}
<div class="no-core-skill">
<p>{{localize "PRISMRPG.Message.selectCoreSkill"}}</p>
<select name="system.coreSkill.skill" {{#if isPlayMode}}disabled{{/if}}>
<option value="">{{localize "PRISMRPG.Label.chooseSkill"}}</option>
{{#each config.CORE_SKILLS as |skill skillId|}}
<option value="{{skillId}}">{{localize skill.label}}</option>
{{/each}}
</select>
</div>
{{/if}}
</div>
</fieldset>
{{!-- Available Core Skills Reference --}}
<fieldset class="core-skills-list">
<legend data-tooltip="{{localize 'PRISMRPG.Tooltip.coreSkills'}}" data-tooltip-direction="UP">
{{localize "PRISMRPG.Label.availableCoreSkills"}}
</legend>
<div class="skills-grid">
{{#each config.CORE_SKILLS as |skill skillId|}}
<div class="core-skill-item {{#if (eq ../system.coreSkill.skill skillId)}}selected{{/if}}"
data-skill-id="{{skillId}}">
<div class="skill-header">
<span class="skill-name">{{localize skill.label}}</span>
{{#if (eq ../system.coreSkill.skill skillId)}}
<span class="badge-core">{{localize "PRISMRPG.Label.yourCoreSkill"}}</span>
{{/if}}
</div>
<div class="skill-attributes">
<span class="attribute-choices-label">{{localize "PRISMRPG.Label.attributeChoices"}}:</span>
{{#each skill.attributeChoices as |attr|}}
<span class="attribute-choice {{#if (eq ../../system.coreSkill.attributeChoice attr)}}chosen{{/if}}">
{{localize (concat "PRISMRPG.Label." attr)}}
</span>
{{#unless @last}}/{{/unless}}
{{/each}}
</div>
</div>
{{/each}}
</div>
</fieldset>
{{!-- Skills Items (if any) --}}
{{!-- Skills Items --}}
<fieldset>
<legend data-tooltip="{{localize 'PRISMRPG.Tooltip.skills'}}" data-tooltip-direction="UP">
{{localize "PRISMRPG.Label.customSkills"}}
{{localize "PRISMRPG.Label.skills"}}
</legend>
<div class="skills">
{{#each skills as |item|}}
<div class="skill {{#if item.system.isCoreSkill}}is-core-skill{{/if}}"
data-item-id="{{item.id}}"
data-item-uuid="{{item.uuid}}">
<img class="item-img" src="{{item.img}}" data-tooltip="{{item.name}}" />
<div class="name">
<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.system.description}}">
<a class="rollable" data-roll-type="skill" data-roll-key="{{item.id}}">
<i class="lf-roll-small fa-duotone fa-solid fa-dice-d10"></i>
{{item.name}}
@@ -104,29 +46,7 @@
<div class="racial-abilities">
{{#each racialAbilities 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}}" />
<div class="name" data-tooltip="{{{item.description}}}<br><br>{{item.path}}" 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>
<fieldset>
<legend data-tooltip="{{localize 'PRISMRPG.Tooltip.vulnerabilities'}}" data-tooltip-direction="UP">
{{localize "PRISMRPG.Label.vulnerabilities"}}
</legend>
<div class="vulnerabilities">
{{#each vulnerabilities as |item|}}
<div class="vulnerability " data-item-id="{{item.id}}" data-item-uuid="{{item.uuid}}">
<img class="item-img" src="{{item.img}}" data-tooltip="{{item.name}}" />
<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}}}<br><br>{{item.path}}" data-tooltip-direction="UP">
{{item.name}}
</div>
+3 -23
View File
@@ -1,22 +1,6 @@
<section class="tab character-{{tab.id}} {{tab.cssClass}}" data-tab="{{tab.id}}" data-group="{{tab.group}}">
<div class="main-div">
<fieldset>
<legend>{{localize "PRISMRPG.Label.aetherPoints"}}</legend>
<div class="spell-details">
<div class="spell-detail">
<span>Current</span>
{{formField systemFields.aetherPoints.fields.value value=system.aetherPoints.value localize=true}}
<a data-action="aetherPointsPlus"><i class="fa-solid fa-hexagon-plus"></i></a>
<a data-action="aetherPointsMinus"><i class="fa-solid fa-hexagon-minus"></i></a>
<span>Max</span>
{{formField systemFields.aetherPoints.fields.max value=system.aetherPoints.max localize=true
disabled=isPlayMode}}
</div>
</div>
</fieldset>
<fieldset>
<legend>{{localize "PRISMRPG.Label.spells"}}{{#if isEditMode}}<a class="action" data-tooltip="{{localize "
PRISMRPG.Tooltip.addSpell"}}" data-tooltip-direction="UP"><i class="fas fa-plus"
@@ -24,17 +8,13 @@
<div class="spells">
{{#each spells as |item|}}
<div class="spell" data-item-id="{{item.id}}" data-item-uuid="{{item.uuid}}" data-drag="true">
<img class="item-img" src="{{item.img}}" data-tooltip="{{item.name}}" />
<img class="item-img" src="{{item.img}}" data-tooltip="{{item.name}}" data-action="postItemToChat" />
<div class="name">
{{item.name}}
</div>
<a class="rollable" data-roll-type="spell-attack" data-roll-key="{{item.id}}" data-tooltip="Spell Attack">
<i class="lf-roll-small fa-solid fa-swords" data-roll-type="spell-attack" data-roll-key="{{item.id}}"></i>
</a>
<a class="rollable" data-roll-type="spell-power" data-roll-key="{{item.id}}" data-tooltip="Spell Power">
<i class="fa-duotone fa-solid fa-stars" data-roll-type="spell-power" data-roll-key="{{item.id}}"></i>
<a class="rollable" data-roll-type="spell-cast" data-roll-key="{{item.id}}" data-tooltip="Cast Spell">
<i class="fa-duotone fa-solid fa-wand-magic-sparkles" data-roll-type="spell-cast" data-roll-key="{{item.id}}"></i>
</a>
<div class="controls">
+25
View File
@@ -0,0 +1,25 @@
<section class="character-subattributes tab" data-group="sheet" data-tab="subattributes">
<div class="subattributes-content">
<div class="subattributes-list">
{{#each (entries config.SUB_ATTRIBUTES) as |entry|}}
{{#with entry.[1] as |subAttr|}}
<a class="rollable subattribute-item" data-roll-type="sub-attribute" data-roll-key="{{subAttr.id}}" title="{{#each subAttr.parents as |parentKey|}}{{uppercase parentKey}}{{#unless @last}}/{{/unless}}{{/each}}">
<div class="subattribute-header">
<i class="fa-duotone fa-solid fa-dice-d20"></i>
<div class="subattribute-name">{{localize subAttr.label}}</div>
</div>
<div class="subattribute-value">
{{#with (lookup ../../system.subAttributes subAttr.id) as |subAttrData|}}
{{#if (gt subAttrData.value 0)}}
+{{subAttrData.value}}
{{else}}
{{subAttrData.value}}
{{/if}}
{{/with}}
</div>
</a>
{{/with}}
{{/each}}
</div>
</div>
</section>
+206 -73
View File
@@ -1,88 +1,221 @@
{{!log 'chat-message' this}}
<div class="{{cssClass}}">
<div class="intro-chat">
<div class="intro-img">
{{!log "chat-message" this}}
{{!log "rollTarget" rollTarget}}
{{!log "rollData" rollData}}
<div class="{{cssClass}} prismrpg-chat-card">
<div class="chat-header">
<div class="chat-portrait">
<img src="{{actingCharImg}}" data-tooltip="{{actingCharName}}" />
</div>
<div class="intro-right">
<span><STRONG>{{actingCharName}} - {{upperFirst rollName}}</STRONG></span>
<div class="chat-title">
<div class="actor-name">{{actingCharName}}</div>
<div class="roll-name">{{upperFirst rollName}}</div>
{{#if (match rollType "attack")}}
<span>Attack roll !</span>
{{/if}}
{{#if (match rollType "defense")}}
<span>Defense roll !</span>
{{/if}}
{{#if (eq rollData.favor "favor")}}
<span><strong>Favor roll</strong></span>
{{/if}}
{{#if (eq rollData.favor "disfavor")}}
<span><strong>Disfavor roll</strong></span>
<div class="roll-type-badge attack">Attack Roll</div>
{{/if}}
{{#if badResult}}
<span><strong>{{localize "PRISMRPG.Label.otherResult"}}</strong> : {{badResult}}</span>
<div class="bad-result">{{localize "PRISMRPG.Label.otherResult"}}: {{badResult}}</div>
{{/if}}
{{#if rollTarget.weapon}}
<span>{{rollTarget.weapon.name}}</span>
{{/if}}
{{#if rollData.letItFly}}
<span>Let It Fly attack ! </span>
{{/if}}
{{#if rollData.pointBlank}}
<span>Point Blank Range Attack !</span>
{{/if}}
{{#if rollData.beyondSkill}}
<span>Beyond Skill Range Attack !</span>
{{/if}}
<span><strong>Formula</strong> : {{titleFormula}}</span>
{{#each diceResults as |result|}}
<span>{{result.dice}} : {{result.value}}</span>
{{/each}}
</div>
</div>
{{#if isSave}}
<div class="result">
{{#if (eq resultType "success")}}
{{#if isPrivate}}?{{else}}{{localize "PRISMRPG.Roll.success"}}{{/if}}
{{else}}
{{#if isPrivate}}?{{else}}{{localize "PRISMRPG.Roll.failure"}}{{/if}}
<div class="chat-content">
{{#if rollTarget.weapon}}
<div class="weapon-info-card">
<div class="weapon-header">
<strong class="weapon-name">{{rollTarget.weapon.name}}</strong>
{{#if rollTarget.weapon.system.isImplement}}
<span class="badge implement">Implement</span>
{{/if}}
{{#if rollData.attackAttribute}}
<span class="attribute-used">
({{#if (eq rollData.attackAttribute "str")}}Strength{{else}}Dexterity{{/if}})
</span>
{{/if}}
</div>
<div class="weapon-stats">
{{#if rollTarget.weapon.system.weaponGroup}}
<div class="stat-item">
<i class="fas fa-shield-halved"></i>
<span>{{rollTarget.weapon.system.weaponGroup}}</span>
</div>
{{/if}}
{{#if rollTarget.weapon.system.apc}}
<div class="stat-item apc">
<i class="fas fa-bolt"></i>
<span>{{rollTarget.weapon.system.apc}} APC</span>
</div>
{{/if}}
{{#if rollTarget.weapon.system.damage}}
<div class="stat-item damage">
<i class="fas fa-dice-d20"></i>
<span>{{rollTarget.weapon.system.damage}}</span>
</div>
{{/if}}
{{#if rollTarget.weapon.system.damageType}}
<div class="stat-item">
<i class="fas fa-burst"></i>
<span>
{{#if rollTarget.weapon.system.damageType.piercing}}P{{/if}}{{#if rollTarget.weapon.system.damageType.bludgeoning}}{{#if rollTarget.weapon.system.damageType.piercing}}/{{/if}}B{{/if}}{{#if rollTarget.weapon.system.damageType.slashing}}{{#if (or rollTarget.weapon.system.damageType.piercing rollTarget.weapon.system.damageType.bludgeoning)}}/{{/if}}S{{/if}}
</span>
</div>
{{/if}}
{{#if (or (gt rollTarget.weapon.system.shortRange 0) (gt rollTarget.weapon.system.longRange 0))}}
<div class="stat-item range">
<i class="fas fa-bow-arrow"></i>
<span>
{{#if (gt rollTarget.weapon.system.shortRange 0)}}{{rollTarget.weapon.system.shortRange}}{{/if}}{{#if (and (gt rollTarget.weapon.system.shortRange 0) (gt rollTarget.weapon.system.longRange 0))}}/{{/if}}{{#if (gt rollTarget.weapon.system.longRange 0)}}{{rollTarget.weapon.system.longRange}}{{/if}}ft
</span>
</div>
{{/if}}
{{#if (gt rollTarget.weapon.system.reloadAPC 0)}}
<div class="stat-item reload">
<i class="fas fa-rotate"></i>
<span>Reload {{rollTarget.weapon.system.reloadAPC}}</span>
</div>
{{/if}}
</div>
</div>
{{/if}}
{{#if (eq rollType "spell-cast")}}
<div class="spell-info-card">
<div class="spell-header">
<strong class="spell-name">Spell Cast</strong>
{{#if (gt rollData.upcastLevel 0)}}
<span class="badge upcast">+{{rollData.upcastLevel}} Levels</span>
{{/if}}
</div>
<div class="spell-stats">
{{#if rollData.mentalCharacteristic}}
<div class="stat-item characteristic">
<i class="fas fa-brain"></i>
<span>{{rollData.mentalCharacteristic}} ({{rollData.mentalCharValue}})</span>
</div>
{{/if}}
<div class="stat-item mana">
<i class="fas fa-flask"></i>
<span>{{rollData.totalManaCost}} Mana</span>
</div>
<div class="stat-item apc">
<i class="fas fa-bolt"></i>
<span>{{rollData.totalAPC}} APC</span>
</div>
{{#if (gt rollData.manaUpkeep 0)}}
<div class="stat-item upkeep">
<i class="fas fa-repeat"></i>
<span>{{rollData.manaUpkeep}}/round</span>
</div>
{{/if}}
</div>
</div>
{{/if}}
{{#if (eq rollType "skill")}}
{{#if rollData.skillSubAttributeLabel}}
<div class="skill-info-card">
<div class="skill-header">
<strong class="skill-name">Skill Check</strong>
</div>
<div class="skill-stats">
<div class="stat-item subattribute">
<i class="fas fa-diagram-project"></i>
<span>{{rollData.skillSubAttributeLabel}} (+{{rollData.skillSubAttributeValue}})</span>
</div>
</div>
</div>
{{/if}}
{{/if}}
{{#if rollData.letItFly}}
<div class="special-badge">Let It Fly!</div>
{{/if}}
{{#if rollData.pointBlank}}
<div class="special-badge">Point Blank</div>
{{/if}}
{{#if rollData.beyondSkill}}
<div class="special-badge">Beyond Skill Range</div>
{{/if}}
{{#if (and rollData.attackerAim (ne rollData.attackerAim "0"))}}
<div class="aiming-info">
<i class="fas fa-crosshairs"></i> Aiming: {{rollData.attackerAim}}
</div>
{{/if}}
<div class="formula-display">
<i class="fas fa-calculator"></i> {{titleFormula}}
</div>
{{#if (eq rollType "save")}}
{{#if rollTarget.abilityModifier}}
<div class="modifier-info">
<span>Ability Mod: {{#if (gt rollTarget.abilityModifier 0)}}+{{/if}}{{rollTarget.abilityModifier}}</span>
<span>Save Bonus: {{#if (gt rollTarget.saveProficiency 0)}}+{{/if}}{{rollTarget.saveProficiency}}</span>
</div>
{{/if}}
{{/if}}
{{#each diceResults as |result|}}
<div class="dice-breakdown">
<i class="fas fa-dice"></i> {{result.dice}}: {{result.value}}
</div>
{{/each}}
{{#if (eq rollType "weapon-attack")}}
<button class="roll-damage-button" type="button" data-actor-id="{{actorId}}" data-weapon-id="{{rollTarget.weapon._id}}">
<i class="fa-regular fa-face-head-bandage"></i>
Roll Damage
</button>
{{/if}}
</div>
{{/if}}
{{#if isResource}}
<div class="result">
{{#if (eq resultType "success")}}
{{#if isPrivate}}?{{else}}{{localize "PRISMRPG.Roll.success"}}{{/if}}
{{else}}
{{#if isPrivate}}?{{else}}{{localize "PRISMRPG.Roll.failure"}}{{#if isFailure}} ({{localize
"PRISMRPG.Roll.resourceLost"}}){{/if}}{{/if}}
{{/if}}
</div>
{{/if}}
{{#if isDamage}}
<div>
{{#if (and isGM hasTarget)}}
{{{localize "PRISMRPG.Roll.displayArmor" targetName=targetName targetArmor=targetArmor realDamage=realDamage}}}
{{/if}}
</div>
{{/if}}
{{#unless isPrivate}}
<div class="dice-result">
<h4 class="dice-total">{{total}}</h4>
</div>
{{#if D30result}}
<div class="dice-result">
<h4 class="dice-total">D30 result: {{D30result}}</h4>
</div>
<div class="roll-result">
<div class="result-total">
<span class="total-label">Result</span>
<span class="total-value">{{total}}</span>
</div>
{{#if D30result}}
<div class="d30-result">
<i class="fas fa-dice-d20"></i> D30: {{D30result}}
</div>
{{/if}}
</div>
{{/unless}}
{{#if isSave}}
<div class="result-badge {{resultType}}">
{{#if isPrivate}}
?
{{else}}
{{#if (eq resultType "success")}}
<i class="fas fa-check-circle"></i> {{localize "PRISMRPG.Roll.success"}}
{{else}}
<i class="fas fa-times-circle"></i> {{localize "PRISMRPG.Roll.failure"}}
{{/if}}
{{/if}}
</div>
{{/if}}
{{/unless}}
{{#if isResource}}
<div class="result-badge {{resultType}}">
{{#if isPrivate}}
?
{{else}}
{{#if (eq resultType "success")}}
<i class="fas fa-check-circle"></i> {{localize "PRISMRPG.Roll.success"}}
{{else}}
<i class="fas fa-times-circle"></i> {{localize "PRISMRPG.Roll.failure"}}{{#if isFailure}} ({{localize "PRISMRPG.Roll.resourceLost"}}){{/if}}
{{/if}}
{{/if}}
</div>
{{/if}}
{{#if isDamage}}
{{#if (and isGM hasTarget)}}
<div class="damage-info">
{{{localize "PRISMRPG.Roll.displayArmor" targetName=targetName targetArmor=targetArmor realDamage=realDamage}}}
</div>
{{/if}}
{{/if}}
</div>
+55 -1
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>
@@ -61,7 +62,7 @@
<legend>{{localize "PRISMRPG.Label.classFeatures"}}</legend>
{{#each system.features as |feature level|}}
<label>{{localize "PRISMRPG.Label.level"}} {{inc @key}}</label>
<label>{{localize "PRISMRPG.Label.level"}} {{replace @key "level" ""}}</label>
{{formInput
(lookup ../systemFields.features.fields @key)
enriched=(lookup ../enrichedFeatures @key)
@@ -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>
+218
View File
@@ -0,0 +1,218 @@
<div class="prismrpg-roll-dialog-modern">
{{! Header with character info }}
<div class="dialog-header">
<div class="character-info">
<div class="character-name">{{actorName}}</div>
{{#if rollTarget.weapon}}
<div class="item-name weapon">
<i class="fas fa-sword"></i>
<strong>{{rollTarget.weapon.name}}</strong>
</div>
{{/if}}
{{#if rollTarget.type}}
{{#if (eq rollTarget.type "spell")}}
<div class="item-name spell">
<i class="fas fa-magic"></i>
<strong>{{rollTarget.name}}</strong>
</div>
{{/if}}
{{/if}}
</div>
</div>
<div class="dialog-content">
{{! Weapon Options }}
{{#if rollTarget.weapon}}
<div class="option-section weapon-section">
<div class="section-title">
<i class="fas fa-crosshairs"></i>
<span>Weapon Options</span>
</div>
{{! Display weapon ranges }}
{{#if (or (gt rollTarget.weapon.system.shortRange 0) (gt rollTarget.weapon.system.longRange 0))}}
<div class="info-display">
<i class="fas fa-bullseye"></i>
<span class="info-text">
{{#if (gt rollTarget.weapon.system.shortRange 0)}}
Short: {{rollTarget.weapon.system.shortRange}} ft
{{/if}}
{{#if (gt rollTarget.weapon.system.longRange 0)}}
{{#if (gt rollTarget.weapon.system.shortRange 0)}}{{/if}}
Long: {{rollTarget.weapon.system.longRange}} ft
{{/if}}
</span>
</div>
{{/if}}
{{! STR or DEX choice }}
{{#if (or (eq rollType "weapon-attack") (eq rollType "weapon-damage-small") (eq rollType "weapon-damage-medium"))}}
<div class="option-row">
<label>Attack with:</label>
<select name="attackAttribute" class="styled-select">
<option value="str" {{#if (eq rollTarget.weapon.system.weaponType "melee")}}selected{{/if}}>
<i class="fas fa-dumbbell"></i> Strength (+{{rollTarget.strMod}})
</option>
<option value="dex" {{#if (eq rollTarget.weapon.system.weaponType "ranged")}}selected{{/if}}>
<i class="fas fa-running"></i> Dexterity (+{{rollTarget.dexMod}})
</option>
</select>
</div>
{{/if}}
{{! Ranged weapon checkboxes }}
{{#if (eq rollTarget.weapon.system.weaponType "ranged")}}
{{#if (or (eq rollType "weapon-attack") (eq rollType "monster-attack"))}}
<div class="checkbox-group">
<label class="checkbox-label">
<input type="checkbox" name="pointBlank" data-action="selectPointBlank" />
<span class="checkbox-text">
<i class="fas fa-bullseye-arrow"></i>
Point Blank Range
</span>
</label>
<label class="checkbox-label">
<input type="checkbox" name="beyondSkill" data-action="selectBeyondSkill" />
<span class="checkbox-text">
<i class="fas fa-exclamation-triangle"></i>
Beyond Skill Range
</span>
</label>
<label class="checkbox-label">
<input type="checkbox" name="letItFly" data-action="selectLetItFly" />
<span class="checkbox-text">
<i class="fas fa-dice-d20"></i>
Let it Fly (Pure D20E)
</span>
</label>
</div>
<div class="option-row">
<label>Aiming:</label>
<select name="attackerAim" class="styled-select">
{{selectOptions attackerAimChoices selected="simple"}}
</select>
</div>
{{/if}}
{{/if}}
</div>
{{/if}}
{{! Spell Options }}
{{#if rollTarget.type}}
{{#if (eq rollTarget.type "spell")}}
<div class="option-section spell-section">
<div class="section-title">
<i class="fas fa-wand-magic"></i>
<span>Spell Options</span>
</div>
{{! Spell info display }}
<div class="spell-info-display">
<div class="info-badge characteristic">
<i class="fas fa-brain"></i>
<span>{{rollTarget.mentalCharacteristic}} ({{rollTarget.mentalCharValue}}, +{{rollTarget.value}})</span>
</div>
<div class="info-badge mana">
<i class="fas fa-flask"></i>
<span>{{rollTarget.system.manaCost}} Mana</span>
</div>
<div class="info-badge apc">
<i class="fas fa-bolt"></i>
<span>{{rollTarget.system.apc}} APC</span>
</div>
{{#if (gt rollTarget.system.manaUpkeep 0)}}
<div class="info-badge upkeep">
<i class="fas fa-repeat"></i>
<span>{{rollTarget.system.manaUpkeep}}/round</span>
</div>
{{/if}}
</div>
{{! Upcast option }}
{{#if rollTarget.system.canAscend}}
<div class="option-row">
<label>Upcast Level:</label>
<select name="upcastLevel" class="styled-select">
<option value="0">Base Level ({{rollTarget.system.level}})</option>
<option value="1">+1 Level (+1 Mana, +1 APC)</option>
<option value="2">+2 Levels (+2 Mana, +2 APC)</option>
<option value="3">+3 Levels (+3 Mana, +3 APC)</option>
<option value="4">+4 Levels (+4 Mana, +4 APC)</option>
<option value="5">+5 Levels (+5 Mana, +5 APC)</option>
<option value="6">+6 Levels (+6 Mana, +6 APC)</option>
<option value="7">+7 Levels (+7 Mana, +7 APC)</option>
</select>
</div>
{{/if}}
</div>
{{/if}}
{{/if}}
{{! Skill Options }}
{{#if (eq rollType "skill")}}
<div class="option-section skill-section">
<div class="section-title">
<i class="fas fa-dice-d20"></i>
<span>Skill Options</span>
</div>
{{! Choose which sub-attribute to use }}
<div class="option-row">
<label>Use Sub-Attribute:</label>
<select name="skillSubAttribute" class="styled-select">
<option value="{{rollTarget.subAttribute1}}" selected>
{{rollTarget.subAttribute1Label}} (+{{rollTarget.subAttribute1Value}})
</option>
<option value="{{rollTarget.subAttribute2}}">
{{rollTarget.subAttribute2Label}} (+{{rollTarget.subAttribute2Value}})
</option>
</select>
</div>
</div>
{{/if}}
{{! Advantage/Disadvantage }}
{{#if hasAdvantage}}
<div class="option-section">
<div class="section-title">
<i class="fas fa-balance-scale"></i>
<span>{{localize "PRISMRPG.Roll.advantageDisadvantage"}}</span>
</div>
<div class="option-row">
<select name="advantage" class="styled-select advantage-select">
{{selectOptions choiceAdvantage selected=advantage}}
</select>
</div>
</div>
{{/if}}
{{! Modifier }}
{{#if hasModifier}}
<div class="option-section">
<div class="section-title">
<i class="fas fa-plus-minus"></i>
<span>{{localize "PRISMRPG.Roll.modifierBonusMalus"}}</span>
</div>
<div class="option-row">
<select name="modifier" class="styled-select modifier-select">
{{selectOptions choiceModifier selected=modifier}}
</select>
</div>
</div>
{{/if}}
{{! Visibility }}
<div class="option-section">
<div class="section-title">
<i class="fas fa-eye"></i>
<span>{{localize "PRISMRPG.Roll.visibility"}}</span>
</div>
<div class="option-row">
<select name="visibility" class="styled-select">
{{selectOptions rollModes selected=visibility localize=true}}
</select>
</div>
</div>
</div>
</div>
+150 -101
View File
@@ -1,111 +1,162 @@
<div class="prismrpg-roll-dialog">
<fieldSet>
<legend>{{actorName}}</legend>
<fieldSet class="">
<legend>{{localize (concat "PRISMRPG.Label." rollType)}} - {{actorName}}</legend>
{{#if rollTarget.tokenId}}
<div class="dialog-save">
<a class="goto-token-button" data-action="gotoToken" data-token-id="{{rollTarget.tokenId}}">{{localize
"PRISMRPG.Label.gotoToken"}} </a>
</div>
{{/if}}
{{#if (match rollType "attack")}}
<div class="dialog-save">Attack roll ! - {{rollTarget.name}}</div>
{{/if}}
{{#if (match rollType "defense")}}
<div class="dialog-save">Defense roll ! - {{rollTarget.name}}</div>
{{/if}}
{{#if hasModifier}}
<div class="dialog-save">{{upperFirst rollName}} : {{baseFormula}} + {{baseValue}}</div>
{{else}}
<div class="dialog-save">{{upperFirst rollName}} : {{baseFormula}}</div>
{{/if}}
{{#if rollTarget.weapon}}
<div class="dialog-save">{{localize "PRISMRPG.Label.baseModifier"}} : {{rollTarget.charModifier}}</div>
<div class="dialog-save">{{localize "PRISMRPG.Label.weapon"}} : {{rollTarget.weapon.name}}</div>
<div class="dialog-save">{{localize "PRISMRPG.Label.skill"}} : {{rollTarget.name}}</div>
<div class="dialog-save">{{localize "PRISMRPG.Label.skillBonus"}} : {{rollTarget.weaponSkillModifier}}</div>
<div class="dialog-save">
<strong>{{rollTarget.weapon.name}}</strong>
</div>
{{/if}}
</fieldSet>
{{#if (match rollType "attack")}}
<div class="dialog-save">Add Granted Attack Dice
<input type="checkbox" data-action="selectGranted" name="granted">
</div>
{{#if rollTarget.weapon}}
{{#if (eq rollTarget.weapon.system.weaponType "melee")}}
{{else}}
<div class="dialog-save">Point Blank Range Attack
<input type="checkbox" data-action="selectPointBlank" name="pointBlankV">
</div>
<div class="dialog-save">Beyond Skill Range Attack
<input type="checkbox" data-action="selectBeyondSkill" name="beyondSkillV">
</div>
<div class="dialog-save">Let it Fly (Pure D20E)
<input type="checkbox" data-action="selectLetItFly" name="letItFlyV">
</div>
<div class="dialog-save">Aiming
<select name="attackerAim" data-tooltip-direction="UP">
{{selectOptions attackerAimChoices selected=attackerAim}}
{{#if rollTarget.weapon}}
{{! Weapon-specific options }}
<fieldSet class="dialog-weapon-options">
<legend>Weapon Options</legend>
{{! Display weapon ranges if set }}
{{#if (or (gt rollTarget.weapon.system.shortRange 0) (gt rollTarget.weapon.system.longRange 0))}}
<div class="dialog-save" style="font-size: 0.9em; color: #666; margin-bottom: 0.5em;">
{{#if (gt rollTarget.weapon.system.shortRange 0)}}
<span>Short Range: {{rollTarget.weapon.system.shortRange}} ft</span>
{{/if}}
{{#if (gt rollTarget.weapon.system.longRange 0)}}
{{#if (gt rollTarget.weapon.system.shortRange 0)}}{{/if}}
<span>Long Range: {{rollTarget.weapon.system.longRange}} ft</span>
{{/if}}
</div>
{{/if}}
{{! Choose STR or DEX for attack/damage rolls }}
{{#if (or (eq rollType "weapon-attack") (eq rollType "weapon-damage-small") (eq rollType "weapon-damage-medium"))}}
<div class="dialog-save">
<label>Attack with:</label>
<select name="attackAttribute" data-tooltip-direction="UP">
<option value="str" {{#if (eq rollTarget.weapon.system.weaponType "melee")}}selected{{/if}}>Strength (+{{rollTarget.strMod}})</option>
<option value="dex" {{#if (eq rollTarget.weapon.system.weaponType "ranged")}}selected{{/if}}>Dexterity (+{{rollTarget.dexMod}})</option>
</select>
</div>
{{/if}}
{{! Ranged weapon specific options }}
{{#if (eq rollTarget.weapon.system.weaponType "ranged")}}
{{#if
(or (eq rollType "weapon-attack") (eq rollType "monster-attack"))
}}
<div class="dialog-save">
<label>
<input
type="checkbox"
name="pointBlank"
data-action="selectPointBlank"
/>
Point Blank Range Attack
</label>
</div>
<div class="dialog-save">
<label>
<input
type="checkbox"
name="beyondSkill"
data-action="selectBeyondSkill"
/>
Beyond Skill Range Attack
</label>
</div>
<div class="dialog-save">
<label>
<input
type="checkbox"
name="letItFly"
data-action="selectLetItFly"
/>
Let it Fly (Pure D20E)
</label>
</div>
<div class="dialog-save">
<label>Aiming:</label>
<select name="attackerAim" data-tooltip-direction="UP">
{{selectOptions attackerAimChoices selected="simple"}}
</select>
</div>
{{/if}}
{{/if}}
</fieldSet>
{{/if}}
{{#if rollTarget.type}}
{{#if (eq rollTarget.type "spell")}}
{{! Spell-specific options }}
<fieldSet class="dialog-spell-options">
<legend>Spell Options</legend>
<div class="dialog-save" style="font-size: 0.9em; color: #666; margin-bottom: 0.5em;">
<span>Casting with: {{rollTarget.mentalCharacteristic}} ({{rollTarget.mentalCharValue}}, +{{rollTarget.value}})</span>
<br><span>Base Cost: {{rollTarget.system.manaCost}} Mana, {{rollTarget.system.apc}} APC</span>
{{#if (gt rollTarget.system.manaUpkeep 0)}}
<br><span>Upkeep: {{rollTarget.system.manaUpkeep}} Mana/round</span>
{{/if}}
</div>
{{#if rollTarget.system.canAscend}}
<div class="dialog-save">
<label>Upcast Level:</label>
<select name="upcastLevel" data-tooltip-direction="UP">
<option value="0">Base Level ({{rollTarget.system.level}})</option>
<option value="1">+1 Level (+1 Mana, +1 APC)</option>
<option value="2">+2 Levels (+2 Mana, +2 APC)</option>
<option value="3">+3 Levels (+3 Mana, +3 APC)</option>
<option value="4">+4 Levels (+4 Mana, +4 APC)</option>
<option value="5">+5 Levels (+5 Mana, +5 APC)</option>
<option value="6">+6 Levels (+6 Mana, +6 APC)</option>
<option value="7">+7 Levels (+7 Mana, +7 APC)</option>
</select>
</div>
{{/if}}
</fieldSet>
{{/if}}
{{/if}}
{{! Skill Options }}
{{#if (eq rollType "skill")}}
<fieldSet class="dialog-skill-options">
<legend>Skill Options</legend>
{{! Choose which sub-attribute to use }}
<div class="dialog-save">
<label>Use Sub-Attribute:</label>
<select name="skillSubAttribute" data-tooltip-direction="UP">
<option value="{{rollTarget.subAttribute1}}" selected>
{{rollTarget.subAttribute1Label}} (+{{rollTarget.subAttribute1Value}})
</option>
<option value="{{rollTarget.subAttribute2}}">
{{rollTarget.subAttribute2Label}} (+{{rollTarget.subAttribute2Value}})
</option>
</select>
</div>
</fieldSet>
{{/if}}
{{#if hasAdvantage}}
<fieldSet class="dialog-advantage">
<legend>{{localize "PRISMRPG.Roll.advantageDisadvantage"}}</legend>
<select
name="advantage"
class="advantage-choice"
data-tooltip-direction="UP"
>
{{selectOptions choiceAdvantage selected=advantage}}
</select>
</div>
{{/if}}
{{/if}}
{{/if}}
{{#if (match rollType "defense")}}
<div class="dialog-save">Add Granted Defense Dice
<input type="checkbox" data-action="selectGranted" name="granted">
</div>
{{/if}}
{{#if (match rollType "damage")}}
<div class="dialog-save">Add Granted Damage Dice
<input type="checkbox" data-action="selectGranted" name="granted">
</div>
{{/if}}
{{#if rollTarget.staticModifier}}
<div class="dialog-save">Static modifier : +{{rollTarget.staticModifier}}</div>
{{/if}}
</fieldSet>
{{#if hasFavor}}
<fieldSet class="dialog-favor">
<legend>{{localize "PRISMRPG.Roll.favorDisfavor"}}</legend>
<select name="favor" class="favor-choice" data-tooltip-direction="UP">
{{selectOptions choiceFavor selected=favor}}
</select>
</fieldSet>
</fieldSet>
{{/if}}
{{#if hasModifier}}
<fieldSet class="dialog-modifier">
<legend>{{localize "PRISMRPG.Roll.modifierBonusMalus"}}</legend>
<select name="modifier" data-tooltip-direction="UP">
{{selectOptions choiceModifier selected=modifier}}
</select>
{{#if (eq rollType "save")}}
{{#if rollTarget.magicUser}}
<div>
<span>Save against spell (+{{rollTarget.actorModifiers.saveModifier}}) ?</span>
<input type="checkbox" name="saveSpellCheck" data-action="saveSpellCheck">
</div>
{{/if}}
{{/if}}
</fieldSet>
{{/if}}
{{#if hasChangeDice}}
<fieldSet class="dialog-modifier">
<legend>{{localize "PRISMRPG.Roll.changeDice"}}</legend>
<select name="changeDice" data-tooltip-direction="UP">
{{selectOptions choiceDice selected=changeDice}}
</select>
</fieldSet>
<fieldSet class="dialog-modifier">
<legend>{{localize "PRISMRPG.Roll.modifierBonusMalus"}}</legend>
<select name="modifier" data-tooltip-direction="UP">
{{selectOptions choiceModifier selected=modifier}}
</select>
</fieldSet>
{{/if}}
<fieldSet>
@@ -114,6 +165,4 @@
{{selectOptions rollModes selected=visibility localize=true}}
</select>
</fieldSet>
</div>
+24 -15
View File
@@ -11,22 +11,31 @@
<p class="hint">{{localize "PRISMRPG.Hint.isCoreSkill"}}</p>
</div>
{{#if system.isCoreSkill}}
{{!-- Attribute Bonus Selection --}}
<div class="form-group">
<label>{{localize "PRISMRPG.Label.attributeBonusChoice"}}</label>
<select name="system.attributeBonus">
<option value="">{{localize "PRISMRPG.Label.selectAttribute"}}</option>
{{#each config.CHARACTERISTICS}}
<option value="{{@key}}" {{#if (eq ../system.attributeBonus @key)}}selected{{/if}}>
{{localize this.label}}
</option>
{{/each}}
</select>
<p class="hint">{{localize "PRISMRPG.Hint.attributeBonus"}}</p>
</div>
{{!-- First Sub-Attribute for Skill Checks --}}
<div class="form-group">
<label>{{localize "PRISMRPG.Label.subAttribute1"}}</label>
<select name="system.subAttribute1">
{{#each config.SUB_ATTRIBUTES}}
<option value="{{@key}}" {{#if (eq ../system.subAttribute1 @key)}}selected{{/if}}>
{{localize this.label}}
</option>
{{/each}}
</select>
<p class="hint">First sub-attribute used for skill checks</p>
</div>
{{/if}}
{{!-- Second Sub-Attribute for Skill Checks --}}
<div class="form-group">
<label>{{localize "PRISMRPG.Label.subAttribute2"}}</label>
<select name="system.subAttribute2">
{{#each config.SUB_ATTRIBUTES}}
<option value="{{@key}}" {{#if (eq ../system.subAttribute2 @key)}}selected{{/if}}>
{{localize this.label}}
</option>
{{/each}}
</select>
<p class="hint">Second sub-attribute used for skill checks</p>
</div>
{{!-- Notes --}}
<fieldset>
+20 -21
View File
@@ -71,27 +71,26 @@
</div>
<div class="align-top">
{{! Prism RPG: Projectile Properties }}
{{#if system.isProjectile}}
{{formField
systemFields.isProjectile
value=system.isProjectile
localize=true
label="PRISMRPG.Label.isProjectile"
}}
{{formField
systemFields.range
value=system.range
localize=true
label="PRISMRPG.Label.range"
}}
{{formField
systemFields.reloadAPC
value=system.reloadAPC
localize=true
label="PRISMRPG.Label.reloadAPC"
}}
{{/if}}
{{formField
systemFields.shortRange
value=system.shortRange
localize=true
label="PRISMRPG.Label.shortRange"
}}
{{formField
systemFields.longRange
value=system.longRange
localize=true
label="PRISMRPG.Label.longRange"
}}
{{formField
systemFields.reloadAPC
value=system.reloadAPC
localize=true
label="PRISMRPG.Label.reloadAPC"
}}
{{formField
systemFields.encLoad