12 Commits

Author SHA1 Message Date
uberwald 68e3d35af1 Add abilities sectioon on sheet 2026-03-18 17:12:32 +01:00
uberwald 58fdc3482a Add HP TEMP 2026-03-10 19:54:36 +01:00
uberwald 236d752406 Update gitignore 2026-03-10 18:02:02 +01:00
uberwald bf9ad37d24 Add mana+AP reset buttons and conditions 2026-03-10 18:02:02 +01:00
uberwald 7944345c82 Actualiser README.md 2026-02-10 08:09:02 +01:00
uberwald 7abea8e9d4 Add class adancement 2026-02-09 22:46:44 +01:00
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
56 changed files with 4465 additions and 3810 deletions
+13
View File
@@ -0,0 +1,13 @@
# IDE
.idea/
.vs/
styles/*.css
# Node Modules
node_modules/
.history
# GitHub/Copilot config
.github/
+1 -13
View File
@@ -1,20 +1,8 @@
## Prism RPG RPG for Foundry Virtual TableTop ## Prism RPG RPG for Foundry Virtual TableTop
The Official game system for playing Prism RPG TTRPG: The Role Playing Game on FoundryVTT. This fully functional system is the foundational framework to build your game. The Official game system for playing Prism RPG TTRPG: The Role Playing Game on FoundryVTT. This fully functional system is the foundational framework to build your game.
This product's format, programming code, and presentation is copyrighted by Prism RPG Games LLC. This product's format, programming code, and presentation is copyrighted by Prism RPG Games LLC.
This system & product are used with permission granted as part of the partnership agreement between Foundry Gaming LLC and Prism RPG Games LLC. It uses the following trademarks and/or copyrights: This system & product are used with permission granted as part of the partnership agreement between Foundry Gaming LLC and Prism RPG Games LLC.
© 2025 Prism RPG Games. Content copyright Ted McClintock, Prism RPG Games LLC. All Rights Reserved. Prism RPG® is a Registered Trademark of Prism RPG Games LLC. All Rights Reserved.
Prism RPG Games is ©2025 Prism RPG Games, LLC. All rights reserved. Prism RPG, Prism RPG Games, and their associated logos are trademarks of Prism RPG Games, LLC. https://lethalfantasy.com/
For inquiries on developing content for this ruleset please contact Lethalted@lethalfantasy.com
## Community
Please join our Discord server Prism RPG games https://discord.gg/UDvnnyvreV
It's the place to ask questions on how to use the system, make feature request and follow the development of the system.
-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

+1137 -38
View File
File diff suppressed because it is too large Load Diff
+119 -24
View File
@@ -140,34 +140,34 @@
}, },
"FIELDS": { "FIELDS": {
"moneys": { "moneys": {
"tinbit": { "coppercoin": {
"label": "Tin Bits", "label": "Copper Coin",
"value": { "value": {
"label": "Tin Bits" "label": "Copper Coin"
} }
}, },
"silver": { "silvercoin": {
"label": "Silver", "label": "Silver Coin",
"value": { "value": {
"label": "Silver" "label": "Silver Coin"
} }
}, },
"copper": { "goldcoin": {
"label": "Copper", "label": "Gold Coin",
"value": { "value": {
"label": "Copper" "label": "Gold Coin"
} }
}, },
"gold": { "note": {
"label": "Gold", "label": "Note",
"value": { "value": {
"label": "Gold" "label": "Note"
} }
}, },
"platinum": { "steam": {
"label": "Platinum", "label": "Steam",
"value": { "value": {
"label": "Platinum" "label": "Steam"
} }
} }
}, },
@@ -382,6 +382,88 @@
} }
} }
}, },
"Ability": {
"FIELDS": {
"description": {
"label": "Description"
}
}
},
"Combat": {
"newRound": "New Round —",
"restoreAP": "Reset AP & +1 Mana",
"allPC": "All PC"
},
"Status": {
"Aided": "Aided",
"Alert": "Alert",
"Alkalized": "Alkalized",
"Anchored": "Anchored",
"Banished": "Banished",
"Bestowed": "Bestowed",
"Blessed": "Blessed",
"Bleed": "Bleed",
"Blind": "Blind",
"Burning": "Burning",
"Chilled": "Chilled",
"Comatose": "Comatose",
"Compulsed": "Compulsed",
"Concealed": "Concealed",
"Corroded": "Corroded",
"Cursed": "Cursed",
"Dazed": "Dazed",
"Deaf": "Deaf",
"Diseased": "Diseased",
"Distracted": "Distracted",
"Enchanted": "Enchanted",
"Enhance": "Enhance",
"Exhaustion": "Exhaustion",
"Fatigue": "Fatigue",
"Frightened": "Frightened",
"Fury": "Fury",
"Haste": "Haste",
"Heroism": "Heroism",
"Horror": "Horror",
"Inspired": "Inspired",
"Invisible": "Invisible",
"Keen": "Keen",
"LifeDrain": "Life Drain",
"Locked": "Locked",
"Madness": "Madness",
"ManaDrain": "Mana Drain",
"Marked": "Marked",
"Mute": "Mute",
"Necrosis": "Necrosis (Elemental)",
"Numbed": "Numbed",
"Paralyzed": "Paralyzed",
"Petrified": "Petrified",
"Plagued": "Plagued",
"Poison": "Poison",
"Prepared": "Prepared",
"Prone": "Prone",
"Radiated": "Radiated (Elemental)",
"Rage": "Rage",
"Regeneration": "Regeneration",
"Reinforced": "Reinforced",
"Renewed": "Renewed",
"Saturated": "Saturated",
"Sealed": "Sealed",
"Seep": "Seep",
"Shattered": "Shattered",
"Shocked": "Shocked (Elemental)",
"Sightless": "Sightless",
"Silenced": "Silenced",
"Soundless": "Soundless",
"Staggered": "Staggered",
"Stunned": "Stunned",
"Supplied": "Supplied",
"Surged": "Surged",
"Taunt": "Taunt",
"Trance": "Trance",
"Unconscious": "Unconscious",
"Warded": "Warded",
"Wounded": "Wounded"
},
"Label": { "Label": {
"agility": "Dexterity", "agility": "Dexterity",
"gotoToken": "Go to token", "gotoToken": "Go to token",
@@ -415,7 +497,7 @@
"rollProgressionDice": "Roll progression/Lethargy dice", "rollProgressionDice": "Roll progression/Lethargy dice",
"earned": "Earned", "earned": "Earned",
"divinityPoints": "Divinity points", "divinityPoints": "Divinity points",
"aetherPoints": "Aether points", "manaPoints": "Mana points",
"attacks": "Attacks", "attacks": "Attacks",
"monster": "Monster", "monster": "Monster",
"Resist" :"Resist", "Resist" :"Resist",
@@ -440,6 +522,7 @@
"combatDetails": "Combat details", "combatDetails": "Combat details",
"Challenges": "Challenges", "Challenges": "Challenges",
"HP": "HP", "HP": "HP",
"HPTemp": "Temporary Hit Points",
"Movement": "Movement", "Movement": "Movement",
"Saves": "Saves", "Saves": "Saves",
"app": "APP", "app": "APP",
@@ -471,6 +554,7 @@
"equipment": "Equipment", "equipment": "Equipment",
"experience": "Experience", "experience": "Experience",
"racialAbilities": "Racial Abilities", "racialAbilities": "Racial Abilities",
"abilities": "Abilities",
"grit": "Grit", "grit": "Grit",
"gritEarned": "Grit earned", "gritEarned": "Grit earned",
"int": "INT", "int": "INT",
@@ -516,6 +600,8 @@
"skills": "Skills", "skills": "Skills",
"sub-attribute": "Sub-Attribute", "sub-attribute": "Sub-Attribute",
"subattributes": "Sub-Attributes", "subattributes": "Sub-Attributes",
"subAttribute1": "Sub-Attribute 1",
"subAttribute2": "Sub-Attribute 2",
"spells": "Spells", "spells": "Spells",
"str": "STR", "str": "STR",
"titleChallenge": "Challenge", "titleChallenge": "Challenge",
@@ -588,7 +674,8 @@
"piercing": "Piercing (P)", "piercing": "Piercing (P)",
"bludgeoning": "Bludgeoning (B)", "bludgeoning": "Bludgeoning (B)",
"slashing": "Slashing (S)", "slashing": "Slashing (S)",
"isProjectile": "Is Projectile?", "shortRange": "Short Range",
"longRange": "Long Range",
"range": "Range", "range": "Range",
"reloadAPC": "Reload APC", "reloadAPC": "Reload APC",
"bonuses": "Bonuses", "bonuses": "Bonuses",
@@ -685,7 +772,13 @@
"spellcastingTypeMana": "Mana", "spellcastingTypeMana": "Mana",
"spellcastingTypeFaith": "Faith", "spellcastingTypeFaith": "Faith",
"attributeBonuses": "Attribute Bonuses", "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": { "CoreSkill": {
"acrobatics": "Acrobatics", "acrobatics": "Acrobatics",
@@ -804,7 +897,8 @@
"addMiracle": "Add new miracle", "addMiracle": "Add new miracle",
"skill": "Skills list", "skill": "Skills list",
"skills": "Skills - Your character's skills and abilities", "skills": "Skills - Your character's skills and abilities",
"racialAbilities": "Racial Abilities from your character's race and sub-race" "racialAbilities": "Racial Abilities from your character's race and sub-race",
"abilities": "Abilities acquired through class, paths, or other sources"
}, },
"RollSavingThrow": "Roll Saving Throw", "RollSavingThrow": "Roll Saving Throw",
"Message": { "Message": {
@@ -879,11 +973,11 @@
} }
}, },
"Money": { "Money": {
"Coppers": "Copper", "CopperCoin": "Copper Coin",
"Golds": "Gold", "SilverCoin": "Silver Coin",
"Platinums": "Platinum", "GoldCoin": "Gold Coin",
"Silvers": "Silver", "Note": "Note",
"Tinbits": "Tin Bits" "Steam": "Steam"
}, },
"Notifications": { "Notifications": {
"rollFromWeapon": "Roll from an equipped weapon", "rollFromWeapon": "Roll from an equipped weapon",
@@ -1403,6 +1497,7 @@
"armor": "Armor", "armor": "Armor",
"equipment": "Equipment", "equipment": "Equipment",
"racial-ability": "Racial Ability", "racial-ability": "Racial Ability",
"ability": "Ability",
"miracle": "Miracle", "miracle": "Miracle",
"save": "Save", "save": "Save",
"shield": "Shield", "shield": "Shield",
+1
View File
@@ -3,6 +3,7 @@ export { default as PrismRPGMonsterSheet } from "./sheets/monster-sheet.mjs"
export { default as PrismRPGWeaponSheet } from "./sheets/weapon-sheet.mjs" export { default as PrismRPGWeaponSheet } from "./sheets/weapon-sheet.mjs"
export { default as PrismRPGSkillSheet } from "./sheets/skill-sheet.mjs" export { default as PrismRPGSkillSheet } from "./sheets/skill-sheet.mjs"
export { default as PrismRPGRacialAbilitySheet } from "./sheets/racial-ability-sheet.mjs" export { default as PrismRPGRacialAbilitySheet } from "./sheets/racial-ability-sheet.mjs"
export { default as PrismRPGAbilitySheet } from "./sheets/ability-sheet.mjs"
export { default as PrismRPGVulnerabilitySheet } from "./sheets/vulnerability-sheet.mjs" export { default as PrismRPGVulnerabilitySheet } from "./sheets/vulnerability-sheet.mjs"
export { default as PrismRPGArmorSheet } from "./sheets/armor-sheet.mjs" export { default as PrismRPGArmorSheet } from "./sheets/armor-sheet.mjs"
export { default as PrismRPGSpellSheet } from "./sheets/spell-sheet.mjs" export { default as PrismRPGSpellSheet } from "./sheets/spell-sheet.mjs"
-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 { 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) { async rollInitiative(ids, options) {
console.log("%%%%%%%%% Roll Initiative", ids, options); console.log("%%%%%%%%% Roll Initiative", ids, options);
@@ -123,98 +25,5 @@ export class PrismRPGCombat extends Combat {
return this; 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;
}
} }
@@ -0,0 +1,50 @@
import PrismRPGItemSheet from "./base-item-sheet.mjs"
export default class PrismRPGAbilitySheet extends PrismRPGItemSheet {
/** @override */
static DEFAULT_OPTIONS = {
classes: ["ability"],
position: {
width: 600,
},
window: {
contentClasses: ["ability-content"],
},
}
/** @override */
static PARTS = {
main: {
template: "systems/fvtt-prism-rpg/templates/ability.hbs",
},
}
/** @override */
tabGroups = {
primary: "description",
}
/**
* Prepare an array of form header tabs.
* @returns {Record<string, Partial<ApplicationTab>>}
*/
#getTabs() {
const tabs = {
description: { id: "description", group: "primary", label: "PRISMRPG.Label.description" },
effects: { id: "effects", group: "primary", label: "PRISMRPG.Label.effects" },
}
for (const v of Object.values(tabs)) {
v.active = this.tabGroups[v.group] === v.id
v.cssClass = v.active ? "active" : ""
}
return tabs
}
/** @override */
async _prepareContext() {
const context = await super._prepareContext()
context.tabs = this.#getTabs()
return context
}
}
+221 -3
View File
@@ -18,6 +18,17 @@ export default class PrismRPGCharacterSheet extends PrismRPGActorSheet {
rollInitiative: PrismRPGCharacterSheet.#onRollInitiative, rollInitiative: PrismRPGCharacterSheet.#onRollInitiative,
armorHitPointsPlus: PrismRPGCharacterSheet.#onArmorHitPointsPlus, armorHitPointsPlus: PrismRPGCharacterSheet.#onArmorHitPointsPlus,
armorHitPointsMinus: PrismRPGCharacterSheet.#onArmorHitPointsMinus, armorHitPointsMinus: PrismRPGCharacterSheet.#onArmorHitPointsMinus,
armorPointsPlus: PrismRPGCharacterSheet.#onArmorPointsPlus,
armorPointsMinus: PrismRPGCharacterSheet.#onArmorPointsMinus,
actionPointsPlus: PrismRPGCharacterSheet.#onActionPointsPlus,
actionPointsMinus: PrismRPGCharacterSheet.#onActionPointsMinus,
manaPointsPlus: PrismRPGCharacterSheet.#onManaPointsPlus,
manaPointsMinus: PrismRPGCharacterSheet.#onManaPointsMinus,
hpPlus: PrismRPGCharacterSheet.#onHpPlus,
hpMinus: PrismRPGCharacterSheet.#onHpMinus,
hpTempPlus: PrismRPGCharacterSheet.#onHpTempPlus,
hpTempMinus: PrismRPGCharacterSheet.#onHpTempMinus,
postItemToChat: PrismRPGCharacterSheet.#onPostItemToChat,
}, },
} }
@@ -103,6 +114,7 @@ export default class PrismRPGCharacterSheet extends PrismRPGActorSheet {
context.tab = context.tabs.skills context.tab = context.tabs.skills
context.skills = doc.itemTypes.skill context.skills = doc.itemTypes.skill
context.racialAbilities = doc.itemTypes["racial-ability"] context.racialAbilities = doc.itemTypes["racial-ability"]
context.abilities = doc.itemTypes["ability"]
context.vulnerabilities = doc.itemTypes.vulnerability context.vulnerabilities = doc.itemTypes.vulnerability
break break
case "subattributes": case "subattributes":
@@ -154,19 +166,225 @@ export default class PrismRPGCharacterSheet extends PrismRPGActorSheet {
await this.document.system.rollInitiative() await this.document.system.rollInitiative()
} }
static #onArmorHitPointsPlus(event, target) { static async #onArmorHitPointsPlus(event, target) {
let armorHP = this.actor.system.combat.armorHitPoints let armorHP = this.actor.system.combat.armorHitPoints
armorHP += 1 armorHP += 1
this.actor.update({ "system.combat.armorHitPoints": armorHP }) this.actor.update({ "system.combat.armorHitPoints": armorHP })
} }
static #onArmorHitPointsMinus(event, target) { static async #onArmorHitPointsMinus(event, target) {
let armorHP = this.actor.system.combat.armorHitPoints let armorHP = this.actor.system.combat.armorHitPoints
armorHP -= 1 armorHP -= 1
this.actor.update({ "system.combat.armorHitPoints": Math.max(armorHP, 0) }) this.actor.update({ "system.combat.armorHitPoints": Math.max(armorHP, 0) })
} }
static #onCreateEquipment(event, target) { 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 async #onManaPointsMinus(event, target) {
let mana = this.actor.system.manaPoints.value
mana -= 1
this.actor.update({ "system.manaPoints.value": Math.max(mana, 0) })
}
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 async #onArmorPointsMinus(event, target) {
let armor = this.actor.system.armorPoints.value
armor -= 1
this.actor.update({ "system.armorPoints.value": Math.max(armor, 0) })
}
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#onHpTempPlus(event, target) {
const temp = this.actor.system.hp.temp
this.actor.update({ "system.hp.temp": temp + 1 })
}
static async#onHpTempMinus(event, target) {
const temp = this.actor.system.hp.temp
this.actor.update({ "system.hp.temp": Math.max(temp - 1, 0) })
}
static async #onCreateEquipment(event, target) {
}
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) { _onRender(context, options) {
+137
View File
@@ -32,6 +32,7 @@ export default class PrismRPGClassSheet extends PrismRPGItemSheet {
#getTabs() { #getTabs() {
const tabs = { const tabs = {
details: { id: "details", group: "primary", label: "PRISMRPG.Label.details" }, 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" }, description: { id: "description", group: "primary", label: "PRISMRPG.Label.description" },
} }
for (const v of Object.values(tabs)) { 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 }) 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 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)
}
} }
+75 -75
View File
@@ -9,7 +9,7 @@ export const TABLES = {
"damage": -7, "damage": -7,
"attack": -4, "attack": -4,
"challenge": -9, "challenge": -9,
"aether_points": -20, "mana_points": -20,
"hp": -3, "hp": -3,
"encumbered": 1, "encumbered": 1,
"lift": 3, "lift": 3,
@@ -20,7 +20,7 @@ export const TABLES = {
"damage": -6, "damage": -6,
"attack": -4, "attack": -4,
"challenge": -8, "challenge": -8,
"aether_points": -20, "mana_points": -20,
"hp": -2, "hp": -2,
"encumbered": 1, "encumbered": 1,
"lift": 4, "lift": 4,
@@ -31,7 +31,7 @@ export const TABLES = {
"damage": -5, "damage": -5,
"attack": -3, "attack": -3,
"challenge": -7, "challenge": -7,
"aether_points": -20, "mana_points": -20,
"hp": -1, "hp": -1,
"encumbered": 1, "encumbered": 1,
"lift": 5, "lift": 5,
@@ -42,7 +42,7 @@ export const TABLES = {
"damage": -4, "damage": -4,
"attack": -3, "attack": -3,
"challenge": -6, "challenge": -6,
"aether_points": -20, "mana_points": -20,
"hp": -1, "hp": -1,
"encumbered": 2, "encumbered": 2,
"lift": 6, "lift": 6,
@@ -53,7 +53,7 @@ export const TABLES = {
"damage": -3, "damage": -3,
"attack": -2, "attack": -2,
"challenge": -5, "challenge": -5,
"aether_points": -20, "mana_points": -20,
"hp": 0, "hp": 0,
"encumbered": 2, "encumbered": 2,
"lift": 7, "lift": 7,
@@ -64,7 +64,7 @@ export const TABLES = {
"damage": -2, "damage": -2,
"attack": -1, "attack": -1,
"challenge": -4, "challenge": -4,
"aether_points": -10, "mana_points": -10,
"hp": 0, "hp": 0,
"encumbered": 3, "encumbered": 3,
"lift": 8, "lift": 8,
@@ -75,7 +75,7 @@ export const TABLES = {
"damage": -2, "damage": -2,
"attack": 0, "attack": 0,
"challenge": -3, "challenge": -3,
"aether_points": -10, "mana_points": -10,
"hp": 0, "hp": 0,
"encumbered": 3, "encumbered": 3,
"lift": 9, "lift": 9,
@@ -86,7 +86,7 @@ export const TABLES = {
"damage": -1, "damage": -1,
"attack": 0, "attack": 0,
"challenge": -2, "challenge": -2,
"aether_points": 0, "mana_points": 0,
"hp": 0, "hp": 0,
"encumbered": 4, "encumbered": 4,
"lift": 11, "lift": 11,
@@ -97,7 +97,7 @@ export const TABLES = {
"damage": -1, "damage": -1,
"attack": 0, "attack": 0,
"challenge": -1, "challenge": -1,
"aether_points": 0, "mana_points": 0,
"hp": 0, "hp": 0,
"encumbered": 5, "encumbered": 5,
"lift": 12, "lift": 12,
@@ -108,7 +108,7 @@ export const TABLES = {
"damage": 0, "damage": 0,
"attack": 0, "attack": 0,
"challenge": 0, "challenge": 0,
"aether_points": 0, "mana_points": 0,
"hp": 0, "hp": 0,
"encumbered": 6, "encumbered": 6,
"lift": 13, "lift": 13,
@@ -119,7 +119,7 @@ export const TABLES = {
"damage": 0, "damage": 0,
"attack": 0, "attack": 0,
"challenge": 0, "challenge": 0,
"aether_points": 0, "mana_points": 0,
"hp": 0, "hp": 0,
"encumbered": 7, "encumbered": 7,
"lift": 15, "lift": 15,
@@ -130,7 +130,7 @@ export const TABLES = {
"damage": 1, "damage": 1,
"attack": 0, "attack": 0,
"challenge": 1, "challenge": 1,
"aether_points": 0, "mana_points": 0,
"hp": 0, "hp": 0,
"encumbered": 8, "encumbered": 8,
"lift": 17, "lift": 17,
@@ -141,7 +141,7 @@ export const TABLES = {
"damage": 1, "damage": 1,
"attack": 0, "attack": 0,
"challenge": 2, "challenge": 2,
"aether_points": 0, "mana_points": 0,
"hp": 0, "hp": 0,
"encumbered": 9, "encumbered": 9,
"lift": 20, "lift": 20,
@@ -152,7 +152,7 @@ export const TABLES = {
"damage": 2, "damage": 2,
"attack": 1, "attack": 1,
"challenge": 3, "challenge": 3,
"aether_points": 0, "mana_points": 0,
"hp": 1, "hp": 1,
"encumbered": 10, "encumbered": 10,
"lift": 22, "lift": 22,
@@ -163,7 +163,7 @@ export const TABLES = {
"damage": 3, "damage": 3,
"attack": 1, "attack": 1,
"challenge": 4, "challenge": 4,
"aether_points": 0, "mana_points": 0,
"hp": 2, "hp": 2,
"encumbered": 11, "encumbered": 11,
"lift": 24, "lift": 24,
@@ -174,7 +174,7 @@ export const TABLES = {
"damage": 4, "damage": 4,
"attack": 2, "attack": 2,
"challenge": 5, "challenge": 5,
"aether_points": 0, "mana_points": 0,
"hp": 3, "hp": 3,
"encumbered": 12, "encumbered": 12,
"lift": 26, "lift": 26,
@@ -185,7 +185,7 @@ export const TABLES = {
"damage": 5, "damage": 5,
"attack": 2, "attack": 2,
"challenge": 6, "challenge": 6,
"aether_points": 10, "mana_points": 10,
"hp": 4, "hp": 4,
"encumbered": 13, "encumbered": 13,
"lift": 28, "lift": 28,
@@ -196,7 +196,7 @@ export const TABLES = {
"damage": 6, "damage": 6,
"attack": 3, "attack": 3,
"challenge": 7, "challenge": 7,
"aether_points": 20, "mana_points": 20,
"hp": 5, "hp": 5,
"encumbered": 14, "encumbered": 14,
"lift": 30, "lift": 30,
@@ -207,7 +207,7 @@ export const TABLES = {
"damage": 7, "damage": 7,
"attack": 3, "attack": 3,
"challenge": 8, "challenge": 8,
"aether_points": 20, "mana_points": 20,
"hp": 6, "hp": 6,
"encumbered": 15, "encumbered": 15,
"lift": 31, "lift": 31,
@@ -218,7 +218,7 @@ export const TABLES = {
"damage": 8, "damage": 8,
"attack": 4, "attack": 4,
"challenge": 9, "challenge": 9,
"aether_points": 30, "mana_points": 30,
"hp": 7, "hp": 7,
"encumbered": 15, "encumbered": 15,
"lift": 32, "lift": 32,
@@ -229,7 +229,7 @@ export const TABLES = {
"damage": 9, "damage": 9,
"attack": 4, "attack": 4,
"challenge": 10, "challenge": 10,
"aether_points": 30, "mana_points": 30,
"hp": 8, "hp": 8,
"encumbered": 16, "encumbered": 16,
"lift": 33, "lift": 33,
@@ -240,7 +240,7 @@ export const TABLES = {
"damage": 10, "damage": 10,
"attack": 5, "attack": 5,
"challenge": 11, "challenge": 11,
"aether_points": 40, "mana_points": 40,
"hp": 9, "hp": 9,
"encumbered": 16, "encumbered": 16,
"lift": 34, "lift": 34,
@@ -251,7 +251,7 @@ export const TABLES = {
"damage": 12, "damage": 12,
"attack": 5, "attack": 5,
"challenge": 12, "challenge": 12,
"aether_points": 40, "mana_points": 40,
"hp": 10, "hp": 10,
"encumbered": 17, "encumbered": 17,
"lift": 35, "lift": 35,
@@ -262,7 +262,7 @@ export const TABLES = {
"damage": 14, "damage": 14,
"attack": 5, "attack": 5,
"challenge": 13, "challenge": 13,
"aether_points": 50, "mana_points": 50,
"hp": 11, "hp": 11,
"encumbered": 18, "encumbered": 18,
"lift": 36, "lift": 36,
@@ -273,7 +273,7 @@ export const TABLES = {
"damage": 16, "damage": 16,
"attack": 6, "attack": 6,
"challenge": 14, "challenge": 14,
"aether_points": 60, "mana_points": 60,
"hp": 12, "hp": 12,
"encumbered": 19, "encumbered": 19,
"lift": 38, "lift": 38,
@@ -286,7 +286,7 @@ export const TABLES = {
"attack": -5, "attack": -5,
"defense": -3, "defense": -3,
"development_points": 0, "development_points": 0,
"aether": -50, "mana": -50,
"spell_cognition": 0, "spell_cognition": 0,
"arkane_casting_mod": -4 "arkane_casting_mod": -4
}, },
@@ -295,7 +295,7 @@ export const TABLES = {
"attack": -4, "attack": -4,
"defense": -3, "defense": -3,
"development_points": 0, "development_points": 0,
"aether": -50, "mana": -50,
"spell_cognition": 0, "spell_cognition": 0,
"arkane_casting_mod": -4 "arkane_casting_mod": -4
}, },
@@ -304,7 +304,7 @@ export const TABLES = {
"attack": -3, "attack": -3,
"defense": -3, "defense": -3,
"development_points": 0, "development_points": 0,
"aether": -50, "mana": -50,
"spell_cognition": 0.01, "spell_cognition": 0.01,
"arkane_casting_mod": -3 "arkane_casting_mod": -3
}, },
@@ -313,7 +313,7 @@ export const TABLES = {
"attack": -2, "attack": -2,
"defense": -2, "defense": -2,
"development_points": 0, "development_points": 0,
"aether": -45, "mana": -45,
"spell_cognition": 0.05, "spell_cognition": 0.05,
"arkane_casting_mod": -3 "arkane_casting_mod": -3
}, },
@@ -322,7 +322,7 @@ export const TABLES = {
"attack": -2, "attack": -2,
"defense": -2, "defense": -2,
"development_points": 0, "development_points": 0,
"aether": -45, "mana": -45,
"spell_cognition": 0.1, "spell_cognition": 0.1,
"arkane_casting_mod": -2 "arkane_casting_mod": -2
}, },
@@ -331,7 +331,7 @@ export const TABLES = {
"attack": -2, "attack": -2,
"defense": -2, "defense": -2,
"development_points": 0, "development_points": 0,
"aether": -40, "mana": -40,
"spell_cognition": 0.15, "spell_cognition": 0.15,
"arkane_casting_mod": -2 "arkane_casting_mod": -2
}, },
@@ -340,7 +340,7 @@ export const TABLES = {
"attack": -1, "attack": -1,
"defense": -1, "defense": -1,
"development_points": 0, "development_points": 0,
"aether": -40, "mana": -40,
"spell_cognition": 0.2, "spell_cognition": 0.2,
"arkane_casting_mod": -1 "arkane_casting_mod": -1
}, },
@@ -349,7 +349,7 @@ export const TABLES = {
"attack": -1, "attack": -1,
"defense": 0, "defense": 0,
"development_points": 0, "development_points": 0,
"aether": -30, "mana": -30,
"spell_cognition": 0.25, "spell_cognition": 0.25,
"arkane_casting_mod": -1 "arkane_casting_mod": -1
}, },
@@ -358,7 +358,7 @@ export const TABLES = {
"attack": -1, "attack": -1,
"defense": 0, "defense": 0,
"development_points": 0, "development_points": 0,
"aether": -30, "mana": -30,
"spell_cognition": 0.3, "spell_cognition": 0.3,
"arkane_casting_mod": 0 "arkane_casting_mod": 0
}, },
@@ -367,7 +367,7 @@ export const TABLES = {
"attack": 0, "attack": 0,
"defense": 0, "defense": 0,
"development_points": 0, "development_points": 0,
"aether": -20, "mana": -20,
"spell_cognition": 0.35, "spell_cognition": 0.35,
"arkane_casting_mod": 0 "arkane_casting_mod": 0
}, },
@@ -376,7 +376,7 @@ export const TABLES = {
"attack": 0, "attack": 0,
"defense": 0, "defense": 0,
"development_points": 1, "development_points": 1,
"aether": -10, "mana": -10,
"spell_cognition": 0.45, "spell_cognition": 0.45,
"arkane_casting_mod": 0 "arkane_casting_mod": 0
}, },
@@ -385,7 +385,7 @@ export const TABLES = {
"attack": 1, "attack": 1,
"defense": 0, "defense": 0,
"development_points": 2, "development_points": 2,
"aether": 0, "mana": 0,
"spell_cognition": 0.5, "spell_cognition": 0.5,
"arkane_casting_mod": 1 "arkane_casting_mod": 1
}, },
@@ -394,7 +394,7 @@ export const TABLES = {
"attack": 1, "attack": 1,
"defense": 0, "defense": 0,
"development_points": 3, "development_points": 3,
"aether": 0, "mana": 0,
"spell_cognition": 0.6, "spell_cognition": 0.6,
"arkane_casting_mod": 1 "arkane_casting_mod": 1
}, },
@@ -403,7 +403,7 @@ export const TABLES = {
"attack": 1, "attack": 1,
"defense": 1, "defense": 1,
"development_points": 4, "development_points": 4,
"aether": 10, "mana": 10,
"spell_cognition": 0.65, "spell_cognition": 0.65,
"arkane_casting_mod": 2 "arkane_casting_mod": 2
}, },
@@ -412,7 +412,7 @@ export const TABLES = {
"attack": 2, "attack": 2,
"defense": 1, "defense": 1,
"development_points": 5, "development_points": 5,
"aether": 20, "mana": 20,
"spell_cognition": 0.75, "spell_cognition": 0.75,
"arkane_casting_mod": 2 "arkane_casting_mod": 2
}, },
@@ -421,7 +421,7 @@ export const TABLES = {
"attack": 2, "attack": 2,
"defense": 1, "defense": 1,
"development_points": 7, "development_points": 7,
"aether": 30, "mana": 30,
"spell_cognition": 0.8, "spell_cognition": 0.8,
"arkane_casting_mod": 3 "arkane_casting_mod": 3
}, },
@@ -430,7 +430,7 @@ export const TABLES = {
"attack": 2, "attack": 2,
"defense": 1, "defense": 1,
"development_points": 9, "development_points": 9,
"aether": 40, "mana": 40,
"spell_cognition": 0.85, "spell_cognition": 0.85,
"arkane_casting_mod": 3 "arkane_casting_mod": 3
}, },
@@ -439,7 +439,7 @@ export const TABLES = {
"attack": 3, "attack": 3,
"defense": 2, "defense": 2,
"development_points": 11, "development_points": 11,
"aether": 50, "mana": 50,
"spell_cognition": 0.9, "spell_cognition": 0.9,
"arkane_casting_mod": 4 "arkane_casting_mod": 4
}, },
@@ -448,7 +448,7 @@ export const TABLES = {
"attack": 3, "attack": 3,
"defense": 2, "defense": 2,
"development_points": 13, "development_points": 13,
"aether": 60, "mana": 60,
"spell_cognition": 0.92, "spell_cognition": 0.92,
"arkane_casting_mod": 5 "arkane_casting_mod": 5
}, },
@@ -457,7 +457,7 @@ export const TABLES = {
"attack": 3, "attack": 3,
"defense": 2, "defense": 2,
"development_points": 15, "development_points": 15,
"aether": 70, "mana": 70,
"spell_cognition": 0.94, "spell_cognition": 0.94,
"arkane_casting_mod": 6 "arkane_casting_mod": 6
}, },
@@ -466,7 +466,7 @@ export const TABLES = {
"attack": 4, "attack": 4,
"defense": 2, "defense": 2,
"development_points": 18, "development_points": 18,
"aether": 80, "mana": 80,
"spell_cognition": 0.95, "spell_cognition": 0.95,
"arkane_casting_mod": 7 "arkane_casting_mod": 7
}, },
@@ -475,7 +475,7 @@ export const TABLES = {
"attack": 4, "attack": 4,
"defense": 3, "defense": 3,
"development_points": 21, "development_points": 21,
"aether": 90, "mana": 90,
"spell_cognition": 0.96, "spell_cognition": 0.96,
"arkane_casting_mod": 7 "arkane_casting_mod": 7
}, },
@@ -484,7 +484,7 @@ export const TABLES = {
"attack": 4, "attack": 4,
"defense": 3, "defense": 3,
"development_points": 24, "development_points": 24,
"aether": 100, "mana": 100,
"spell_cognition": 0.97, "spell_cognition": 0.97,
"arkane_casting_mod": 8 "arkane_casting_mod": 8
}, },
@@ -493,7 +493,7 @@ export const TABLES = {
"attack": 5, "attack": 5,
"defense": 3, "defense": 3,
"development_points": 27, "development_points": 27,
"aether": 110, "mana": 110,
"spell_cognition": 0.98, "spell_cognition": 0.98,
"arkane_casting_mod": 8 "arkane_casting_mod": 8
}, },
@@ -502,7 +502,7 @@ export const TABLES = {
"attack": 5, "attack": 5,
"defense": 4, "defense": 4,
"development_points": 30, "development_points": 30,
"aether": 125, "mana": 125,
"spell_cognition": 0.99, "spell_cognition": 0.99,
"arkane_casting_mod": 9 "arkane_casting_mod": 9
} }
@@ -688,7 +688,7 @@ export const TABLES = {
{ {
"value": 1, "value": 1,
"hp ": 1, "hp ": 1,
"aether_points": -50, "mana_points": -50,
"pain_save": 1, "pain_save": 1,
"toughness_save": -5, "toughness_save": -5,
"stabilization_dice": "D6", "stabilization_dice": "D6",
@@ -698,7 +698,7 @@ export const TABLES = {
"value": 2, "value": 2,
"hp ": 2, "hp ": 2,
"aether_points": -40, "mana_points": -40,
"pain_save": 2, "pain_save": 2,
"toughness_saave": -4, "toughness_saave": -4,
"stabilization_dice": "D6", "stabilization_dice": "D6",
@@ -708,7 +708,7 @@ export const TABLES = {
"value": 3, "value": 3,
"hp ": 3, "hp ": 3,
"aether_points": -35, "mana_points": -35,
"pain_save": 2, "pain_save": 2,
"toughness_save": -3, "toughness_save": -3,
"stabilization_dice": "D6", "stabilization_dice": "D6",
@@ -718,7 +718,7 @@ export const TABLES = {
"value": 4, "value": 4,
"hp ": 4, "hp ": 4,
"aether_points": -30, "mana_points": -30,
"pain_save": 2, "pain_save": 2,
"toughness_save": -3, "toughness_save": -3,
"stabilization_dice": "D6", "stabilization_dice": "D6",
@@ -728,7 +728,7 @@ export const TABLES = {
"value": 5, "value": 5,
"hp ": 5, "hp ": 5,
"aether_points": -25, "mana_points": -25,
"pain_save": 3, "pain_save": 3,
"toughness_save": -2, "toughness_save": -2,
"stabilization_dice": "D6", "stabilization_dice": "D6",
@@ -737,7 +737,7 @@ export const TABLES = {
{ {
"value": 6, "value": 6,
"hp ": 6, "hp ": 6,
"aether_points": -20, "mana_points": -20,
"pain_save": 3, "pain_save": 3,
"toughness_save": -2, "toughness_save": -2,
"stabilization_dice": "D6", "stabilization_dice": "D6",
@@ -746,7 +746,7 @@ export const TABLES = {
{ {
"value": 7, "value": 7,
"hp ": 7, "hp ": 7,
"aether_points": -15, "mana_points": -15,
"pain_save": 3, "pain_save": 3,
"toughness_save": -1, "toughness_save": -1,
"stabilization_dice": "D6", "stabilization_dice": "D6",
@@ -756,7 +756,7 @@ export const TABLES = {
"value": 8, "value": 8,
"hp ": 8, "hp ": 8,
"aether_points": -10, "mana_points": -10,
"pain_save": 4, "pain_save": 4,
"toughness_save": -1, "toughness_save": -1,
"stabilization_dice": "D8", "stabilization_dice": "D8",
@@ -766,7 +766,7 @@ export const TABLES = {
"value": 9, "value": 9,
"hp ": 9, "hp ": 9,
"aether_points": -5, "mana_points": -5,
"pain_save": 4, "pain_save": 4,
"toughness_save": 0, "toughness_save": 0,
"stabilization_dice": "D8", "stabilization_dice": "D8",
@@ -776,7 +776,7 @@ export const TABLES = {
"value": 10, "value": 10,
"hp ": 10, "hp ": 10,
"aether_points": 0, "mana_points": 0,
"pain_save": 5, "pain_save": 5,
"toughness_save": 0, "toughness_save": 0,
"stabilization_dice": "D8", "stabilization_dice": "D8",
@@ -786,7 +786,7 @@ export const TABLES = {
"value": 11, "value": 11,
"hp ": 11, "hp ": 11,
"aether_points": 0, "mana_points": 0,
"pain_save": 5, "pain_save": 5,
"toughness_save": 0, "toughness_save": 0,
"stabilization_dice": "D8", "stabilization_dice": "D8",
@@ -796,7 +796,7 @@ export const TABLES = {
"value": 12, "value": 12,
"hp ": 12, "hp ": 12,
"aether_points": 5, "mana_points": 5,
"pain_save": 6, "pain_save": 6,
"toughness_save": 0, "toughness_save": 0,
"stabilization_dice": "D10", "stabilization_dice": "D10",
@@ -806,7 +806,7 @@ export const TABLES = {
"value": 13, "value": 13,
"hp ": 13, "hp ": 13,
"aether_points": 10, "mana_points": 10,
"pain_save": 7, "pain_save": 7,
"toughness_save": 1, "toughness_save": 1,
"stabilization_dice": "D10", "stabilization_dice": "D10",
@@ -816,7 +816,7 @@ export const TABLES = {
"value": 14, "value": 14,
"hp ": 14, "hp ": 14,
"aether_points": 20, "mana_points": 20,
"pain_save": 7, "pain_save": 7,
"toughness_save": 2, "toughness_save": 2,
"stabilization_dice": "D10", "stabilization_dice": "D10",
@@ -826,7 +826,7 @@ export const TABLES = {
"value": 15, "value": 15,
"hp ": 15, "hp ": 15,
"aether_points": 30, "mana_points": 30,
"pain_save": 8, "pain_save": 8,
"toughness_save": 3, "toughness_save": 3,
"stabilization_dice": "D12", "stabilization_dice": "D12",
@@ -836,7 +836,7 @@ export const TABLES = {
"value": 16, "value": 16,
"hp ": 16, "hp ": 16,
"aether_points": 40, "mana_points": 40,
"pain_save": 8, "pain_save": 8,
"toughness_save": 4, "toughness_save": 4,
"stabilization_dice": "D12", "stabilization_dice": "D12",
@@ -846,7 +846,7 @@ export const TABLES = {
"value": 17, "value": 17,
"hp ": 17, "hp ": 17,
"aether_points": 50, "mana_points": 50,
"pain_save": 9, "pain_save": 9,
"toughness_save": 5, "toughness_save": 5,
"stabilization_dice": "D12", "stabilization_dice": "D12",
@@ -856,7 +856,7 @@ export const TABLES = {
"value": 18, "value": 18,
"hp ": 18, "hp ": 18,
"aether_points": 60, "mana_points": 60,
"pain_save": 9, "pain_save": 9,
"toughness_save": 6, "toughness_save": 6,
"stabilization_dice": "D12", "stabilization_dice": "D12",
@@ -866,7 +866,7 @@ export const TABLES = {
"value": 19, "value": 19,
"hp ": 19, "hp ": 19,
"aether_points": 70, "mana_points": 70,
"pain_save": 10, "pain_save": 10,
"toughness_save": 7, "toughness_save": 7,
"stabilization_dice": "D12", "stabilization_dice": "D12",
@@ -876,7 +876,7 @@ export const TABLES = {
"value": 20, "value": 20,
"hp ": 20, "hp ": 20,
"aether_points": 80, "mana_points": 80,
"pain_save": 10, "pain_save": 10,
"toughness_save": 8, "toughness_save": 8,
"stabilization_dice": "D12", "stabilization_dice": "D12",
@@ -886,7 +886,7 @@ export const TABLES = {
"value": 21, "value": 21,
"hp ": 21, "hp ": 21,
"aether_points": 90, "mana_points": 90,
"pain_save": 11, "pain_save": 11,
"toughness_save": 9, "toughness_save": 9,
"stabilization_dice": "D20", "stabilization_dice": "D20",
@@ -896,7 +896,7 @@ export const TABLES = {
"value": 22, "value": 22,
"hp ": 22, "hp ": 22,
"aether_points": 100, "mana_points": 100,
"pain_save": 11, "pain_save": 11,
"toughness_save": 10, "toughness_save": 10,
"stabilization_dice": "D20", "stabilization_dice": "D20",
@@ -906,7 +906,7 @@ export const TABLES = {
"value": 23, "value": 23,
"hp ": 23, "hp ": 23,
"aether_points": 110, "mana_points": 110,
"pain_save": 12, "pain_save": 12,
"toughness_save": 11, "toughness_save": 11,
"stabilization_dice": "D20", "stabilization_dice": "D20",
@@ -916,7 +916,7 @@ export const TABLES = {
"value": 24, "value": 24,
"hp ": 24, "hp ": 24,
"aether_points": 120, "mana_points": 120,
"pain_save": 12, "pain_save": 12,
"toughness_save": 12, "toughness_save": 12,
"stabilization_dice": "D20", "stabilization_dice": "D20",
@@ -925,7 +925,7 @@ export const TABLES = {
{ {
"value": 25, "value": 25,
"hp ": 25, "hp ": 25,
"aether_points": 130, "mana_points": 130,
"pain_save": 13, "pain_save": 13,
"toughness_save": 13, "toughness_save": 13,
"stabilization_dice": "D20", "stabilization_dice": "D20",
+89
View File
@@ -0,0 +1,89 @@
/**
* Afflictions — negative status effects (Mundane & Magic)
* @type {StatusEffectConfig[]}
*/
export const AFFLICTIONS = [
// ── Mundane ────────────────────────────────────────────────────────────────
{ id: "aff-alkalized", name: "PRISMRPG.Status.Alkalized", icon: "icons/svg/acid.svg", category: "affliction", typing: "mundane" },
{ id: "aff-bleed", name: "PRISMRPG.Status.Bleed", icon: "icons/svg/blood.svg", category: "affliction", typing: "mundane" },
{ id: "aff-blind", name: "PRISMRPG.Status.Blind", icon: "icons/svg/blind.svg", category: "affliction", typing: "mundane" },
{ id: "aff-deaf", name: "PRISMRPG.Status.Deaf", icon: "icons/svg/deaf.svg", category: "affliction", typing: "mundane" },
{ id: "aff-diseased", name: "PRISMRPG.Status.Diseased", icon: "icons/svg/biohazard.svg", category: "affliction", typing: "mundane" },
{ id: "aff-distracted", name: "PRISMRPG.Status.Distracted", icon: "icons/svg/daze.svg", category: "affliction", typing: "mundane" },
{ id: "aff-exhaustion", name: "PRISMRPG.Status.Exhaustion", icon: "icons/svg/sleep.svg", category: "affliction", typing: "mundane" },
{ id: "aff-frightened", name: "PRISMRPG.Status.Frightened", icon: "icons/svg/terror.svg", category: "affliction", typing: "mundane" },
{ id: "aff-marked", name: "PRISMRPG.Status.Marked", icon: "icons/svg/target.svg", category: "affliction", typing: "both" },
{ id: "aff-mute", name: "PRISMRPG.Status.Mute", icon: "icons/svg/silenced.svg", category: "affliction", typing: "mundane" },
{ id: "aff-paralyzed", name: "PRISMRPG.Status.Paralyzed", icon: "icons/svg/paralysis.svg", category: "affliction", typing: "mundane" },
{ id: "aff-petrified", name: "PRISMRPG.Status.Petrified", icon: "icons/svg/frozen.svg", category: "affliction", typing: "mundane" },
{ id: "aff-poison", name: "PRISMRPG.Status.Poison", icon: "icons/svg/poison.svg", category: "affliction", typing: "mundane" },
{ id: "aff-prone", name: "PRISMRPG.Status.Prone", icon: "icons/svg/falling.svg", category: "affliction", typing: "mundane" },
{ id: "aff-rage", name: "PRISMRPG.Status.Rage", icon: "icons/svg/fire.svg", category: "affliction", typing: "mundane" },
{ id: "aff-sealed", name: "PRISMRPG.Status.Sealed", icon: "icons/svg/net.svg", category: "affliction", typing: "mundane" },
{ id: "aff-staggered", name: "PRISMRPG.Status.Staggered", icon: "icons/svg/daze.svg", category: "affliction", typing: "mundane" },
{ id: "aff-stunned", name: "PRISMRPG.Status.Stunned", icon: "icons/svg/stun.svg", category: "affliction", typing: "mundane" },
{ id: "aff-taunt", name: "PRISMRPG.Status.Taunt", icon: "icons/svg/eye.svg", category: "affliction", typing: "mundane" },
{ id: "aff-unconscious",name: "PRISMRPG.Status.Unconscious",icon: "icons/svg/unconscious.svg", category: "affliction", typing: "mundane" },
{ id: "aff-wounded", name: "PRISMRPG.Status.Wounded", icon: "icons/svg/degen.svg", category: "affliction", typing: "mundane" },
// ── Magic ──────────────────────────────────────────────────────────────────
{ id: "aff-banished", name: "PRISMRPG.Status.Banished", icon: "icons/svg/wing.svg", category: "affliction", typing: "magic" },
{ id: "aff-seep", name: "PRISMRPG.Status.Seep", icon: "icons/svg/acid.svg", category: "affliction", typing: "magic" },
{ id: "aff-sightless", name: "PRISMRPG.Status.Sightless", icon: "icons/svg/blind.svg", category: "affliction", typing: "magic" },
{ id: "aff-cursed", name: "PRISMRPG.Status.Cursed", icon: "icons/svg/sun.svg", category: "affliction", typing: "magic" },
{ id: "aff-soundless", name: "PRISMRPG.Status.Soundless", icon: "icons/svg/deaf.svg", category: "affliction", typing: "magic" },
{ id: "aff-plagued", name: "PRISMRPG.Status.Plagued", icon: "icons/svg/biohazard.svg", category: "affliction", typing: "magic" },
{ id: "aff-compulsed", name: "PRISMRPG.Status.Compulsed", icon: "icons/svg/eye.svg", category: "affliction", typing: "magic" },
{ id: "aff-fatigue", name: "PRISMRPG.Status.Fatigue", icon: "icons/svg/sleep.svg", category: "affliction", typing: "magic" },
{ id: "aff-horror", name: "PRISMRPG.Status.Horror", icon: "icons/svg/terror.svg", category: "affliction", typing: "magic" },
{ id: "aff-madness", name: "PRISMRPG.Status.Madness", icon: "icons/svg/daze.svg", category: "affliction", typing: "magic" },
{ id: "aff-silenced", name: "PRISMRPG.Status.Silenced", icon: "icons/svg/silenced.svg", category: "affliction", typing: "magic" },
{ id: "aff-locked", name: "PRISMRPG.Status.Locked", icon: "icons/svg/net.svg", category: "affliction", typing: "magic" },
{ id: "aff-dazed", name: "PRISMRPG.Status.Dazed", icon: "icons/svg/daze.svg", category: "affliction", typing: "magic" },
{ id: "aff-numbed", name: "PRISMRPG.Status.Numbed", icon: "icons/svg/frozen.svg", category: "affliction", typing: "magic" },
{ id: "aff-comatose", name: "PRISMRPG.Status.Comatose", icon: "icons/svg/unconscious.svg", category: "affliction", typing: "magic" },
{ id: "aff-shattered", name: "PRISMRPG.Status.Shattered", icon: "icons/svg/blood.svg", category: "affliction", typing: "magic" },
// ── Elemental (Magic) ──────────────────────────────────────────────────────
{ id: "aff-burning", name: "PRISMRPG.Status.Burning", icon: "icons/svg/fire.svg", category: "affliction", typing: "magic" },
{ id: "aff-chilled", name: "PRISMRPG.Status.Chilled", icon: "icons/svg/frozen.svg", category: "affliction", typing: "magic" },
{ id: "aff-corroded", name: "PRISMRPG.Status.Corroded", icon: "icons/svg/acid.svg", category: "affliction", typing: "magic" },
{ id: "aff-necrosis", name: "PRISMRPG.Status.Necrosis", icon: "icons/svg/degen.svg", category: "affliction", typing: "magic" },
{ id: "aff-radiated", name: "PRISMRPG.Status.Radiated", icon: "icons/svg/lightning.svg", category: "affliction", typing: "magic" },
{ id: "aff-shocked", name: "PRISMRPG.Status.Shocked", icon: "icons/svg/lightning.svg", category: "affliction", typing: "magic" },
]
/**
* Imbuements — positive status effects (Mundane & Magic)
* @type {StatusEffectConfig[]}
*/
export const IMBUEMENTS = [
// ── Mundane ────────────────────────────────────────────────────────────────
{ id: "imb-aided", name: "PRISMRPG.Status.Aided", icon: "icons/svg/upgrade.svg", category: "imbuement", typing: "mundane" },
{ id: "imb-alert", name: "PRISMRPG.Status.Alert", icon: "icons/svg/eye.svg", category: "imbuement", typing: "mundane" },
{ id: "imb-alkalized", name: "PRISMRPG.Status.Alkalized", icon: "icons/svg/acid.svg", category: "imbuement", typing: "mundane" },
{ id: "imb-bestowed", name: "PRISMRPG.Status.Bestowed", icon: "icons/svg/angel.svg", category: "imbuement", typing: "both" },
{ id: "imb-concealed", name: "PRISMRPG.Status.Concealed", icon: "icons/svg/invisible.svg", category: "imbuement", typing: "mundane" },
{ id: "imb-enhance", name: "PRISMRPG.Status.Enhance", icon: "icons/svg/upgrade.svg", category: "imbuement", typing: "mundane" },
{ id: "imb-inspired", name: "PRISMRPG.Status.Inspired", icon: "icons/svg/regen.svg", category: "imbuement", typing: "mundane" },
{ id: "imb-keen", name: "PRISMRPG.Status.Keen", icon: "icons/svg/eye.svg", category: "imbuement", typing: "mundane" },
{ id: "imb-life-drain", name: "PRISMRPG.Status.LifeDrain", icon: "icons/svg/regen.svg", category: "imbuement", typing: "mundane" },
{ id: "imb-madness", name: "PRISMRPG.Status.Madness", icon: "icons/svg/daze.svg", category: "imbuement", typing: "mundane" },
{ id: "imb-prepared", name: "PRISMRPG.Status.Prepared", icon: "icons/svg/upgrade.svg", category: "imbuement", typing: "mundane" },
{ id: "imb-rage", name: "PRISMRPG.Status.Rage", icon: "icons/svg/fire.svg", category: "imbuement", typing: "mundane" },
{ id: "imb-reinforced", name: "PRISMRPG.Status.Reinforced", icon: "icons/svg/mage-shield.svg", category: "imbuement", typing: "mundane" },
{ id: "imb-renewed", name: "PRISMRPG.Status.Renewed", icon: "icons/svg/regen.svg", category: "imbuement", typing: "mundane" },
{ id: "imb-supplied", name: "PRISMRPG.Status.Supplied", icon: "icons/svg/upgrade.svg", category: "imbuement", typing: "mundane" },
{ id: "imb-surged", name: "PRISMRPG.Status.Surged", icon: "icons/svg/lightning.svg", category: "imbuement", typing: "mundane" },
{ id: "imb-trance", name: "PRISMRPG.Status.Trance", icon: "icons/svg/sleep.svg", category: "imbuement", typing: "mundane" },
// ── Magic ──────────────────────────────────────────────────────────────────
{ id: "imb-blessed", name: "PRISMRPG.Status.Blessed", icon: "icons/svg/angel.svg", category: "imbuement", typing: "magic" },
{ id: "imb-anchored", name: "PRISMRPG.Status.Anchored", icon: "icons/svg/net.svg", category: "imbuement", typing: "magic" },
{ id: "imb-saturated", name: "PRISMRPG.Status.Saturated", icon: "icons/svg/regen.svg", category: "imbuement", typing: "magic" },
{ id: "imb-invisible", name: "PRISMRPG.Status.Invisible", icon: "icons/svg/invisible.svg", category: "imbuement", typing: "magic" },
{ id: "imb-enchanted", name: "PRISMRPG.Status.Enchanted", icon: "icons/svg/mage-shield.svg", category: "imbuement", typing: "magic" },
{ id: "imb-heroism", name: "PRISMRPG.Status.Heroism", icon: "icons/svg/upgrade.svg", category: "imbuement", typing: "magic" },
{ id: "imb-mana-drain", name: "PRISMRPG.Status.ManaDrain", icon: "icons/svg/regen.svg", category: "imbuement", typing: "magic" },
{ id: "imb-fury", name: "PRISMRPG.Status.Fury", icon: "icons/svg/fire.svg", category: "imbuement", typing: "magic" },
{ id: "imb-warded", name: "PRISMRPG.Status.Warded", icon: "icons/svg/holy-shield.svg", category: "imbuement", typing: "magic" },
{ id: "imb-regeneration", name: "PRISMRPG.Status.Regeneration", icon: "icons/svg/regen.svg", category: "imbuement", typing: "magic" },
{ id: "imb-haste", name: "PRISMRPG.Status.Haste", icon: "icons/svg/wingfoot.svg", category: "imbuement", typing: "magic" },
]
+46 -32
View File
@@ -14,35 +14,35 @@ export const SYSTEM_ID = "fvtt-prism-rpg"
export const DEV_MODE = false export const DEV_MODE = false
export const MONEY = { export const MONEY = {
tinbit: { coppercoin: {
id: "tinbit", id: "coppercoin",
abbrev: "tb", abbrev: "cc",
label: "PRISMRPG.Money.Tinbits", label: "PRISMRPG.Money.CopperCoin",
valuetb: 1 valuetb: 1
}, },
copper: { silvercoin: {
id: "copper", id: "silvercoin",
abbrev: "cp", abbrev: "sc",
label: "PRISMRPG.Money.Coppers", label: "PRISMRPG.Money.SilverCoin",
valuetb: 5
},
goldcoin: {
id: "goldcoin",
abbrev: "gc",
label: "PRISMRPG.Money.GoldCoin",
valuetb: 10 valuetb: 10
}, },
silver: { note: {
id: "silver", id: "note",
abbrev: "sp", abbrev: "nt",
label: "PRISMRPG.Money.Silvers", label: "PRISMRPG.Money.Note",
valuetb: 100 valuetb: 100
}, },
gold: { steam: {
id: "gold", id: "steam",
abbrev: "gp", abbrev: "st",
label: "PRISMRPG.Money.Golds", label: "PRISMRPG.Money.Steam",
valuetb: 1000 valuetb: 1000
},
platinum: {
id: "platinum",
abbrev: "pp",
label: "PRISMRPG.Money.Platinums",
valuetb: 10000
} }
} }
@@ -156,7 +156,7 @@ export const INITIATIVE_DICE_CHOICES_PER_CLASS = {
{ "name": "Aware and know exactly where the enemy is (1D4)", "value": "1D4" }*/ { "name": "Aware and know exactly where the enemy is (1D4)", "value": "1D4" }*/
], ],
"magicuser": [ "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": "Asleep or totally distracted (1D20)", "value": "1D20" },
{ "name": "Awake but unsuspecting (1D12)", "value": "1D12" }, { "name": "Awake but unsuspecting (1D12)", "value": "1D12" },
{ "name": "Declared Ready on Alert (1)", "value": "1" }, { "name": "Declared Ready on Alert (1)", "value": "1" },
@@ -261,15 +261,29 @@ export const CHOICE_MODIFIERS = {
} }
export const ASCII = ` 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 │
└─────────────────────────────────────────────────────┘
` `
/** /**
+33 -70
View File
@@ -1,4 +1,6 @@
import PrismRPGUtils from "../utils.mjs" import PrismRPGUtils from "../utils.mjs"
import PrismRPGRoll from "./roll.mjs"
export default class PrismRPGActor extends Actor { export default class PrismRPGActor extends Actor {
static async create(data, options) { static async create(data, options) {
@@ -34,39 +36,19 @@ export default class PrismRPGActor extends Actor {
if (this.type === "character") { if (this.type === "character") {
Object.assign(prototypeToken, { Object.assign(prototypeToken, {
sight: { enabled: true }, sight: { enabled: true },
actorLink: true, actorLink: false
disposition: CONST.TOKEN_DISPOSITIONS.FRIENDLY,
}) })
this.updateSource({ prototypeToken }) this.updateSource({ prototypeToken })
} }
} }
/* *************************************************/ /* *************************************************/
// 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) { getBestWeaponClassSkill(skills, rollType, multiplier = 1.0) {
let maxValue = 0 // In D&D 5e, we don't need weapon skills with bonuses
let goodSkill = skills[0] // Just return the first skill (or could be removed entirely)
for (let s of skills) { return skills[0]
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
} }
/* *************************************************/ /* *************************************************/
@@ -136,20 +118,30 @@ export default class PrismRPGActor extends Actor {
rollTarget = this.items.find((i) => i.type === "miracle" && i.id === rollKey) rollTarget = this.items.find((i) => i.type === "miracle" && i.id === rollKey)
rollTarget.rollKey = rollKey rollTarget.rollKey = rollKey
break break
case "skill": case "skill": {
rollTarget = this.items.find((i) => i.type === "skill" && i.id === rollKey) rollTarget = this.items.find((i) => i.type === "skill" && i.id === rollKey)
rollTarget.rollKey = rollKey rollTarget.rollKey = rollKey
if (rollTarget.system.category === "weapon") { if (rollTarget.system.category === "weapon") {
ui.notifications.warn(game.i18n.localize("PRISMRPG.Notifications.rollFromWeapon")) ui.notifications.warn(game.i18n.localize("PRISMRPG.Notifications.rollFromWeapon"))
return return
} }
// Get the primary attribute for D&D 5e style rolls // Get the two sub-attributes for this skill
const attrKey = rollTarget.system.primaryAttribute || "dex" const subAttr1 = rollTarget.system.subAttribute1 || "prowess"
rollTarget.characteristicValue = this.system.characteristics[attrKey].value 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 rollTarget.proficiencyBonus = rollTarget.system.modifier
break break
}
case "spell-attack": case "spell-attack":
case "spell-power": case "spell-power":
case "spell-cast":
case "miracle-attack": case "miracle-attack":
case "miracle-power": 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)
@@ -164,50 +156,21 @@ export default class PrismRPGActor extends Actor {
break; break;
case "weapon-damage-small": case "weapon-damage-small":
case "weapon-damage-medium": case "weapon-damage-medium":
case "weapon-attack": case "weapon-attack": {
case "weapon-defense": {
let weapon = this.items.find((i) => i.type === "weapon" && i.id === rollKey) let weapon = this.items.find((i) => i.type === "weapon" && i.id === rollKey)
let skill if (!weapon) {
let skills = this.items.filter((i) => i.type === "skill" && i.name.toLowerCase() === weapon.name.toLowerCase()) console.error("Weapon not found", weapon, skill)
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")) ui.notifications.warn(game.i18n.localize("PRISMRPG.Notifications.skillNotFound"))
return return
} }
rollTarget = skill
rollTarget.weapon = weapon // Create a plain object for rollTarget to ensure weapon data is preserved
rollTarget.weaponSkillModifier = skill.weaponSkillModifier rollTarget = {
rollTarget.rollKey = rollKey weapon: weapon.toObject(),
rollTarget.combat = foundry.utils.duplicate(this.system.combat) rollKey: rollKey,
if (rollType === "weapon-damage-small" || rollType === "weapon-damage-medium") { combat: foundry.utils.duplicate(this.system.combat),
rollTarget.grantedDice = this.system.granted.damageDice strMod: PrismRPGRoll.getAbilityModifier(this.system.characteristics.str.value),
} dexMod: PrismRPGRoll.getAbilityModifier(this.system.characteristics.dex.value)
if (rollType === "weapon-attack") {
rollTarget.grantedDice = this.system.granted.attackDice
}
if (rollType === "weapon-defense") {
rollTarget.grantedDice = this.system.granted.defenseDice
} }
} }
break break
File diff suppressed because it is too large Load Diff
+194 -32
View File
@@ -127,39 +127,48 @@ export default class PrismRPGRoll extends Roll {
case "skill": case "skill":
options.rollName = options.rollTarget.name options.rollName = options.rollTarget.name
// D&D 5e style: ability modifier + proficiency bonus // D&D 5e style: sub-attribute modifier + proficiency bonus
const skillCharValue = options.rollTarget.characteristicValue // Default to first sub-attribute, will be recalculated if player chooses different one
const skillAbilityMod = this.getAbilityModifier(skillCharValue)
const proficiency = options.rollTarget.proficiencyBonus || 0 const proficiency = options.rollTarget.proficiencyBonus || 0
options.rollTarget.value = skillAbilityMod + proficiency options.rollTarget.value = options.rollTarget.subAttribute1Value + proficiency
break break
case "weapon-attack": case "weapon-attack":
options.rollName = options.rollTarget.name options.rollName = options.rollTarget.name
// Default to STR for melee, DEX for ranged (will be updated by dialog choice)
if (options.rollTarget.weapon.system.weaponType === "melee") { if (options.rollTarget.weapon.system.weaponType === "melee") {
options.rollTarget.value = options.rollTarget.combat.attackModifier + options.rollTarget.value = options.rollTarget.strMod +
options.rollTarget.weaponSkillModifier +
options.rollTarget.weapon.system.bonuses.attackBonus options.rollTarget.weapon.system.bonuses.attackBonus
} else { } else {
options.rollTarget.value = options.rollTarget.combat.rangedAttackModifier + options.rollTarget.value = options.rollTarget.dexMod +
options.rollTarget.weaponSkillModifier +
options.rollTarget.weapon.system.bonuses.attackBonus options.rollTarget.weapon.system.bonuses.attackBonus
} }
break break
case "weapon-defense":
options.rollName = options.rollTarget.name
options.rollTarget.value = options.rollTarget.combat.defenseModifier +
options.rollTarget.weaponSkillModifier +
options.rollTarget.weapon.system.bonuses.defenseBonus
break
case "spell": case "spell":
case "spell-attack": case "spell-attack":
case "spell-power": case "spell-power":
case "spell-cast":
options.rollName = options.rollTarget.name options.rollName = options.rollTarget.name
options.rollTarget.value = options.rollTarget.actorModifiers.levelSpellModifier + // Find best mental characteristic (INT, WIS, CHA)
options.rollTarget.actorModifiers.intSpellModifier const actor = game.actors.get(options.actorId)
const intMod = this.getAbilityModifier(actor.system.characteristics.int.value)
const wisMod = this.getAbilityModifier(actor.system.characteristics.wis.value)
const chaMod = this.getAbilityModifier(actor.system.characteristics.cha.value)
const bestMentalMod = Math.max(intMod, wisMod, chaMod)
options.rollTarget.value = bestMentalMod
// Store which characteristic is being used
if (bestMentalMod === intMod) {
options.rollTarget.mentalCharacteristic = "INT"
options.rollTarget.mentalCharValue = actor.system.characteristics.int.value
} else if (bestMentalMod === wisMod) {
options.rollTarget.mentalCharacteristic = "WIS"
options.rollTarget.mentalCharValue = actor.system.characteristics.wis.value
} else {
options.rollTarget.mentalCharacteristic = "CHA"
options.rollTarget.mentalCharValue = actor.system.characteristics.cha.value
}
break break
case "miracle": case "miracle":
@@ -188,17 +197,18 @@ export default class PrismRPGRoll extends Roll {
if (options.rollType.includes("weapon-damage")) { if (options.rollType.includes("weapon-damage")) {
isDamageRoll = true isDamageRoll = true
hasAdvantage = false hasAdvantage = false
options.rollName = options.rollTarget.name options.rollName = options.rollTarget.weapon.name
let damageBonus = options.rollTarget.combat.damageModifier // Default to STR for melee, DEX for ranged (will be updated by dialog choice)
options.rollTarget.value = damageBonus + if (options.rollTarget.weapon.system.weaponType === "melee") {
options.rollTarget.weaponSkillModifier + options.rollTarget.value = options.rollTarget.strMod +
options.rollTarget.weapon.system.bonuses.damageBonus options.rollTarget.weapon.system.bonuses.damageBonus
if (options.rollType.includes("small")) {
dice = options.rollTarget.weapon.system.damage.damageS
} else { } else {
dice = options.rollTarget.weapon.system.damage.damageM options.rollTarget.value = options.rollTarget.dexMod +
options.rollTarget.weapon.system.bonuses.damageBonus
} }
// Use the weapon's damage dice
dice = options.rollTarget.weapon.system.damage || "1d6"
dice = dice.replace(/E/gi, "") dice = dice.replace(/E/gi, "")
} else if (options.rollType.includes("monster-damage")) { } else if (options.rollType.includes("monster-damage")) {
isDamageRoll = true isDamageRoll = true
@@ -218,6 +228,44 @@ export default class PrismRPGRoll extends Roll {
const rollModes = foundry.utils.duplicate(CONFIG.Dice.rollModes) const rollModes = foundry.utils.duplicate(CONFIG.Dice.rollModes)
const choiceModifier = SYSTEM.CHOICE_MODIFIERS const choiceModifier = SYSTEM.CHOICE_MODIFIERS
const choiceAdvantage = SYSTEM.ADVANTAGE_CHOICES const choiceAdvantage = SYSTEM.ADVANTAGE_CHOICES
const attackerAimChoices = SYSTEM.ATTACKER_AIM_CHOICES
// For weapon damage rolls, skip dialog and roll directly
if (options.rollType.includes("weapon-damage")) {
// Just roll the weapon's damage dice, no modifiers
const finalFormula = dice
const rollData = {
type: options.rollType,
rollType: options.rollType,
target: options.rollTarget,
rollName: options.rollName,
actorId: options.actorId,
actorName: options.actorName,
actorImage: options.actorImage,
rollMode: "publicroll",
hasTarget: options.hasTarget,
titleFormula: finalFormula
}
if (Hooks.call("fvtt-prism-rpg.preRoll", options, rollData) === false) return
// Execute the roll
let roll = new this(finalFormula, options.data, rollData)
await roll.evaluate()
// Store results
const duplicatedRollTarget = foundry.utils.duplicate(options.rollTarget)
roll.options.resultType = "success"
roll.options.rollTotal = roll.total
roll.options.rollTarget = duplicatedRollTarget
roll.options.titleFormula = finalFormula
roll.options.rollData = foundry.utils.duplicate(rollData)
if (Hooks.call("fvtt-prism-rpg.Roll", options, rollData, roll) === false) return
return roll
}
let dialogContext = { let dialogContext = {
rollType: options.rollType, rollType: options.rollType,
@@ -232,13 +280,15 @@ export default class PrismRPGRoll extends Roll {
dice, dice,
choiceModifier, choiceModifier,
choiceAdvantage, choiceAdvantage,
attackerAimChoices,
hasTarget: options.hasTarget, hasTarget: options.hasTarget,
modifier: "+0", modifier: "+0",
advantage: "none" advantage: "none",
config: SYSTEM
} }
const content = await foundry.applications.handlebars.renderTemplate( const content = await foundry.applications.handlebars.renderTemplate(
"systems/fvtt-prism-rpg/templates/roll-dialog.hbs", "systems/fvtt-prism-rpg/templates/roll-dialog-v2.hbs",
dialogContext dialogContext
) )
@@ -255,7 +305,13 @@ export default class PrismRPGRoll extends Roll {
callback: (event, button, dialog) => { callback: (event, button, dialog) => {
game.user.setFlag(SYSTEM.id, "roll-dialog-pos", foundry.utils.duplicate(dialog.position)) game.user.setFlag(SYSTEM.id, "roll-dialog-pos", foundry.utils.duplicate(dialog.position))
const output = Array.from(button.form.elements).reduce((obj, input) => { const output = Array.from(button.form.elements).reduce((obj, input) => {
if (input.name) obj[input.name] = input.value if (input.name) {
if (input.type === "checkbox") {
obj[input.name] = input.checked
} else {
obj[input.name] = input.value
}
}
return obj return obj
}, {}) }, {})
return output return output
@@ -272,9 +328,38 @@ export default class PrismRPGRoll extends Roll {
if (hasModifier) { if (hasModifier) {
let bonus = Number(options.rollTarget.value) || 0 let bonus = Number(options.rollTarget.value) || 0
let extraModifier = rollContext.modifier === "" ? 0 : parseInt(rollContext.modifier, 10)
// Recalculate bonus if player chose different attribute for weapon attack/damage
if (rollContext.attackAttribute && options.rollTarget.weapon) {
const chosenMod = rollContext.attackAttribute === "str" ? options.rollTarget.strMod : options.rollTarget.dexMod
const weaponBonus = options.rollTarget.weapon.system.bonuses.attackBonus || 0
const damageBonus = options.rollTarget.weapon.system.bonuses.damageBonus || 0
if (options.rollType === "weapon-attack") {
bonus = chosenMod + weaponBonus
} else if (options.rollType.includes("weapon-damage")) {
bonus = chosenMod + damageBonus
}
}
// Recalculate bonus if player chose different sub-attribute for skill
if (rollContext.skillSubAttribute && options.rollType === "skill") {
const chosenSubAttrValue = rollContext.skillSubAttribute === options.rollTarget.subAttribute1 ?
options.rollTarget.subAttribute1Value :
options.rollTarget.subAttribute2Value
const proficiencyBonus = options.rollTarget.proficiencyBonus || 0
bonus = chosenSubAttrValue + proficiencyBonus
}
let extraModifier = rollContext.modifier === "" ? 0 : Number.parseInt(rollContext.modifier, 10)
totalModifier = bonus + extraModifier totalModifier = bonus + extraModifier
// Apply aiming modifier for ranged attacks
if (rollContext.attackerAim && rollContext.attackerAim !== "0") {
const aimModifier = Number.parseInt(rollContext.attackerAim, 10)
totalModifier += aimModifier
}
if (totalModifier !== 0) { if (totalModifier !== 0) {
finalFormula = totalModifier > 0 ? finalFormula = totalModifier > 0 ?
`${dice} + ${totalModifier}` : `${dice} + ${totalModifier}` :
@@ -289,6 +374,50 @@ export default class PrismRPGRoll extends Roll {
finalFormula = finalFormula.replace(dice, `2${dice}kl`) finalFormula = finalFormula.replace(dice, `2${dice}kl`)
} }
// Special ranged weapon modifiers
if (rollContext.letItFly) {
// Let it Fly: Pure D20E (replace with 1d20 if it was modified)
finalFormula = finalFormula.replace(/2d20k[hl]/, "1d20")
}
if (rollContext.pointBlank) {
// Point Blank: Add special advantage or bonus (implement based on your rules)
// This could add advantage or a flat bonus
}
// Handle spell upcast
let upcastLevel = 0
let totalManaCost = 0
let totalAPC = 0
let manaUpkeep = 0
let mentalCharacteristic = null
let mentalCharValue = null
if (options.rollType === "spell-cast") {
upcastLevel = rollContext.upcastLevel ? Number.parseInt(rollContext.upcastLevel, 10) : 0
totalManaCost = options.rollTarget.system.manaCost + upcastLevel
totalAPC = options.rollTarget.system.apc + upcastLevel
manaUpkeep = options.rollTarget.system.manaUpkeep
// Get mental characteristic info from rollTarget
mentalCharacteristic = options.rollTarget.mentalCharacteristic
mentalCharValue = options.rollTarget.mentalCharValue
}
// Store skill sub-attribute information
let skillSubAttribute = null
let skillSubAttributeLabel = null
let skillSubAttributeValue = null
if (options.rollType === "skill" && rollContext.skillSubAttribute) {
skillSubAttribute = rollContext.skillSubAttribute
const subAttrConfig = SYSTEM.SUB_ATTRIBUTES?.[skillSubAttribute]
if (subAttrConfig) {
skillSubAttributeLabel = game.i18n.localize(subAttrConfig.label)
}
skillSubAttributeValue = rollContext.skillSubAttribute === options.rollTarget.subAttribute1 ?
options.rollTarget.subAttribute1Value :
options.rollTarget.subAttribute2Value
}
const rollData = { const rollData = {
type: options.rollType, type: options.rollType,
rollType: options.rollType, rollType: options.rollType,
@@ -300,19 +429,52 @@ export default class PrismRPGRoll extends Roll {
rollMode: rollContext.visibility, rollMode: rollContext.visibility,
hasTarget: options.hasTarget, hasTarget: options.hasTarget,
titleFormula: finalFormula, titleFormula: finalFormula,
upcastLevel,
totalManaCost,
totalAPC,
manaUpkeep,
mentalCharacteristic,
mentalCharValue,
skillSubAttribute,
skillSubAttributeLabel,
skillSubAttributeValue,
...rollContext, ...rollContext,
} }
if (Hooks.call("fvtt-prism-rpg.preRoll", options, rollData) === false) return if (Hooks.call("fvtt-prism-rpg.preRoll", options, rollData) === false) return
// Handle mana spending for spell-cast
if (options.rollType === "spell-cast" && totalManaCost > 0) {
const actor = game.actors.get(options.actorId)
const currentMana = actor.system.manaPoints.value
// Check if enough mana
if (currentMana < totalManaCost) {
ui.notifications.error(
`Not enough Mana! Need ${totalManaCost}, but only have ${currentMana} Mana points.`
)
return null
}
// Spend mana
await actor.update({
"system.manaPoints.value": currentMana - totalManaCost
})
ui.notifications.info(
`Spent ${totalManaCost} Mana (${currentMana}${currentMana - totalManaCost})`
)
}
// Execute the roll // Execute the roll
let roll = new this(finalFormula, options.data, rollData) let roll = new this(finalFormula, options.data, rollData)
await roll.evaluate() await roll.evaluate()
// Store results // Store results - duplicate rollTarget to properly serialize weapon Item
const duplicatedRollTarget = foundry.utils.duplicate(options.rollTarget)
roll.options.resultType = "success" roll.options.resultType = "success"
roll.options.rollTotal = roll.total roll.options.rollTotal = roll.total
roll.options.rollTarget = options.rollTarget roll.options.rollTarget = duplicatedRollTarget
roll.options.titleFormula = finalFormula roll.options.titleFormula = finalFormula
roll.options.rollData = foundry.utils.duplicate(rollData) roll.options.rollData = foundry.utils.duplicate(rollData)
File diff suppressed because it is too large Load Diff
+1 -2
View File
@@ -6,9 +6,8 @@ export { default as PrismRPGSkill } from "./skill.mjs"
export { default as PrismRPGArmor } from "./armor.mjs" export { default as PrismRPGArmor } from "./armor.mjs"
export { default as PrismRPGShield } from "./shield.mjs" export { default as PrismRPGShield } from "./shield.mjs"
export { default as PrismRPGRacialAbility } from "./racial-ability.mjs" export { default as PrismRPGRacialAbility } from "./racial-ability.mjs"
export { default as PrismRPGVulnerability } from "./vulnerability.mjs" export { default as PrismRPGAbility } from "./ability.mjs"
export { default as PrismRPGEquipment } from "./equipment.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 PrismRPGRace } from "./race.mjs"
export { default as PrismRPGClass } from "./class.mjs" export { default as PrismRPGClass } from "./class.mjs"
export { default as PrismRPGCharacterPath } from "./character-path.mjs" export { default as PrismRPGCharacterPath } from "./character-path.mjs"
+14
View File
@@ -0,0 +1,14 @@
export default class PrismRPGAbility extends foundry.abstract.TypeDataModel {
static defineSchema() {
const fields = foundry.data.fields
const schema = {}
schema.description = new fields.HTMLField({ required: true, textSearch: true })
return schema
}
/** @override */
static LOCALIZATION_PREFIXES = ["PRISMRPG.Ability"]
}
+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.isHelmet = new fields.BooleanField({ required: true, initial: false })
schema.cost = 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 })
return schema return schema
} }
@@ -32,4 +32,29 @@ export default class PrismRPGArmor extends foundry.abstract.TypeDataModel {
/** @override */ /** @override */
static LOCALIZATION_PREFIXES = ["PRISMRPG.Armor"] 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)
}
} }
+49 -62
View File
@@ -84,12 +84,12 @@ export default class PrismRPGCharacter extends foundry.abstract.TypeDataModel {
temp: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }) temp: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 })
}) })
schema.magicPoints = new fields.SchemaField({ schema.armorPoints = new fields.SchemaField({
value: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }), value: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
max: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }) max: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 })
}) })
schema.armorPoints = new fields.SchemaField({ schema.actionPoints = new fields.SchemaField({
value: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }), value: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
max: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }) max: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 })
}) })
@@ -110,15 +110,7 @@ export default class PrismRPGCharacter extends foundry.abstract.TypeDataModel {
total: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }), total: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
remaining: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }) remaining: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 })
}) })
schema.spellMiraclePoints = new fields.SchemaField({ schema.manaPoints = 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({
max: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }), max: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
value: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }) value: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 })
}) })
@@ -178,6 +170,21 @@ export default class PrismRPGCharacter extends foundry.abstract.TypeDataModel {
prepareDerivedData() { prepareDerivedData() {
super.prepareDerivedData(); super.prepareDerivedData();
// 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
}
// Calculate sub-attributes from parent characteristics // Calculate sub-attributes from parent characteristics
// Sub-attribute = lowest ability modifier between the two parent characteristics // Sub-attribute = lowest ability modifier between the two parent characteristics
for (let subAttrKey in SYSTEM.SUB_ATTRIBUTES) { for (let subAttrKey in SYSTEM.SUB_ATTRIBUTES) {
@@ -254,66 +261,46 @@ export default class PrismRPGCharacter extends foundry.abstract.TypeDataModel {
await roll.toMessage({}, { rollMode: roll.options.rollMode }) 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) { async rollInitiative(combatId = undefined, combatantId = undefined) {
const hasTarget = false // Get the initiative sub-attribute modifier
let actorClass = this.biodata.class; const initiativeModifier = this.subAttributes.initiative.value
let wisDef = SYSTEM.CHARACTERISTICS_TABLES.wis.find((c) => c.value === this.characteristics.wis.value) // Create the roll formula: 1d20 + initiative modifier
let maxInit = Number(wisDef.init_cap) || 1000 const formula = `1d20 + ${initiativeModifier}`
let roll = await PrismRPGRoll.promptInitiative({ // Roll the initiative
actorId: this.parent.id, let initRoll = new Roll(formula)
actorName: this.parent.name, await initRoll.evaluate()
actorImage: this.parent.img,
combatId, // Create the chat message
combatantId, let msg = await initRoll.toMessage({
actorClass, flavor: `${game.i18n.localize("PRISMRPG.Label.initiative")} - ${this.parent.name}`,
maxInit, speaker: ChatMessage.getSpeaker({ actor: this.parent })
}) })
if (!roll) return null
await roll.toMessage({}, { rollMode: roll.options.rollMode }) // Wait for 3D dice animation if enabled
} if (game?.dice3d) {
await game.dice3d.waitFor3DAnimationByMessageID(msg.id)
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() })
} }
if (this.biodata.magicUser || this.biodata.clericUser) {
let spells = this.parent.items.filter(i => i.type === "spell" || i.type === "miracle") // Update the combatant's initiative if in combat
for (let s of spells) { if (combatId && combatantId) {
let title = "" let combat = game.combats.get(combatId)
let formula = "" if (combat) {
if (s.type === "spell") { await combat.updateEmbeddedDocuments("Combatant", [{
let dice = PrismRPGUtils.getLethargyDice(s.system.level) _id: combatantId,
title = `${s.name} (Casting time: ${s.system.castingTime}, Lethargy: ${dice})` initiative: initRoll.total
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 })
} }
} }
let roll = await PrismRPGRoll.promptCombatAction({ return initRoll
actorId: this.parent.id,
actorName: this.parent.name,
actorImage: this.parent.img,
weaponsChoices,
combatId,
combatantId,
rollProgressionCount,
type: "progression",
})
} }
} }
+67
View File
@@ -68,6 +68,41 @@ export default class PrismRPGClass extends foundry.abstract.TypeDataModel {
level10: new fields.HTMLField({ initial: "" }) 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 // Proficiencies granted by this class
schema.weaponProficiencies = new fields.StringField({ schema.weaponProficiencies = new fields.StringField({
required: true, required: true,
@@ -156,4 +191,36 @@ export default class PrismRPGClass extends foundry.abstract.TypeDataModel {
} }
return features 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 } const requiredInteger = { required: true, nullable: false, integer: true }
schema.description = new fields.HTMLField({ required: true, textSearch: 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.encLoad = new fields.NumberField({ required: true, initial: 0, min: 0 })
schema.cost = new fields.NumberField({ ...requiredInteger, 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 // Kit properties
schema.isKit = new fields.BooleanField({ schema.isKit = new fields.BooleanField({
@@ -54,4 +54,29 @@ export default class PrismRPGEquipment extends foundry.abstract.TypeDataModel {
/** @override */ /** @override */
static LOCALIZATION_PREFIXES = ["PRISMRPG.Equipment"] 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 // Equipment properties
schema.encLoad = new fields.NumberField({ required: true, initial: 0, min: 0 }) schema.encLoad = new fields.NumberField({ required: true, initial: 0, min: 0 })
schema.cost = 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 }) schema.equipped = new fields.BooleanField({ required: true, initial: false })
return schema return schema
@@ -69,4 +69,29 @@ export default class PrismRPGShield extends foundry.abstract.TypeDataModel {
/** @override */ /** @override */
static LOCALIZATION_PREFIXES = ["PRISMRPG.Shield"] 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)
}
} }
+30 -23
View File
@@ -1,4 +1,5 @@
import { CORE_SKILLS_CHOICES, CORE_SKILL_BONUS, CORE_SKILLS } from "../config/skill.mjs" 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 * Core Skill data model for Prism RPG
@@ -21,14 +22,6 @@ export default class PrismRPGSkill extends foundry.abstract.TypeDataModel {
initial: "" 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? // Is this the character's chosen Core Skill?
schema.isCoreSkill = new fields.BooleanField({ schema.isCoreSkill = new fields.BooleanField({
required: true, required: true,
@@ -36,18 +29,18 @@ export default class PrismRPGSkill extends foundry.abstract.TypeDataModel {
label: "Is Core Skill" label: "Is Core Skill"
}) })
// Primary attribute for this skill (str, dex, con, int, wis, cha) // First sub-attribute for this skill
schema.primaryAttribute = new fields.StringField({ schema.subAttribute1 = new fields.StringField({
required: true, required: true,
initial: "dex", initial: "prowess",
label: "Primary Attribute" label: "Sub-Attribute 1"
}) })
// If Core Skill, which attribute receives the +2 bonus? // Second sub-attribute for this skill
schema.attributeBonus = new fields.StringField({ schema.subAttribute2 = new fields.StringField({
required: true, required: true,
initial: "", initial: "initiative",
label: "Attribute Bonus" label: "Sub-Attribute 2"
}) })
// Skill modifier (includes Core Skill bonus if applicable) // Skill modifier (includes Core Skill bonus if applicable)
@@ -114,9 +107,9 @@ export default class PrismRPGSkill extends foundry.abstract.TypeDataModel {
prepareDerivedData() { prepareDerivedData() {
super.prepareDerivedData() super.prepareDerivedData()
// D&D 5e style: Core Skill gives +2 proficiency bonus // Core Skill gives +5 proficiency bonus
if (this.isCoreSkill) { if (this.isCoreSkill) {
this.modifier = 2 this.modifier = 5
this.canAdvancedCheck = true this.canAdvancedCheck = true
} else { } else {
this.modifier = 0 this.modifier = 0
@@ -126,16 +119,30 @@ export default class PrismRPGSkill extends foundry.abstract.TypeDataModel {
/** /**
* Calculate skill check bonus * 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 * @returns {number} Total skill check bonus
*/ */
getSkillCheckBonus(attributeKey) { getSkillCheckBonus(subAttributeKey) {
let actor = this.parent?.actor let actor = this.parent?.actor
if (!actor) return this.modifier if (!actor) return this.modifier
const attribute = actor.system.characteristics?.[attributeKey] const subAttribute = actor.system.subAttributes?.[subAttributeKey]
const attributeMod = attribute?.mod || 0 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: "" initial: ""
}) })
// Projectile-specific properties // Range properties
schema.isProjectile = new fields.BooleanField({ schema.shortRange = new fields.NumberField({
required: true, ...requiredInteger,
initial: false initial: 0,
min: 0
}) })
schema.range = new fields.SchemaField({ schema.longRange = new fields.NumberField({
short: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }), ...requiredInteger,
medium: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }), initial: 0,
long: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }) min: 0
}) })
schema.reloadAPC = new fields.NumberField({ 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.encLoad = new fields.NumberField({ required: true, initial: 0, min: 0 })
schema.cost = new fields.NumberField({ ...requiredInteger, 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.equipped = new fields.BooleanField({ required: true, initial: false })
schema.isImplement = 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 */ /** @override */
static LOCALIZATION_PREFIXES = ["PRISMRPG.Weapon"] 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)
}
} }
+6 -5
View File
@@ -13,11 +13,6 @@ export default class PrismRPGUtils {
return compendiumData.filter(filter) 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() { static setHookListeners() {
@@ -177,6 +172,11 @@ export default class PrismRPGUtils {
return str ? str.toUpperCase() : ''; return str ? str.toUpperCase() : '';
}) })
Handlebars.registerHelper('replace', function (str, search, replacement) {
if (!str) return '';
return str.replace(search, replacement);
})
Handlebars.registerHelper('isEnabled', function (configKey) { Handlebars.registerHelper('isEnabled', function (configKey) {
return game.settings.get("bol", configKey); return game.settings.get("bol", configKey);
}) })
@@ -265,6 +265,7 @@ export default class PrismRPGUtils {
const templatePaths = [ const templatePaths = [
'systems/fvtt-prism-rpg/templates/partial-item-effects.hbs', 'systems/fvtt-prism-rpg/templates/partial-item-effects.hbs',
'systems/fvtt-prism-rpg/templates/weapon-types-config.hbs', 'systems/fvtt-prism-rpg/templates/weapon-types-config.hbs',
'systems/fvtt-prism-rpg/templates/chat-new-round.hbs',
] ]
return foundry.applications.handlebars.loadTemplates(templatePaths) return foundry.applications.handlebars.loadTemplates(templatePaths)
} }
+103 -13
View File
@@ -4,6 +4,7 @@
*/ */
import { SYSTEM } from "./module/config/system.mjs" import { SYSTEM } from "./module/config/system.mjs"
import { AFFLICTIONS, IMBUEMENTS } from "./module/config/effects.mjs"
globalThis.SYSTEM = SYSTEM // Expose the SYSTEM object to the global scope globalThis.SYSTEM = SYSTEM // Expose the SYSTEM object to the global scope
// Import modules // Import modules
@@ -11,7 +12,7 @@ import * as models from "./module/models/_module.mjs"
import * as documents from "./module/documents/_module.mjs" import * as documents from "./module/documents/_module.mjs"
import * as applications from "./module/applications/_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 { Macros } from "./module/macros.mjs"
import { setupTextEnrichers } from "./module/enrichers.mjs" import { setupTextEnrichers } from "./module/enrichers.mjs"
import { default as PrismRPGUtils } from "./module/utils.mjs" import { default as PrismRPGUtils } from "./module/utils.mjs"
@@ -36,8 +37,11 @@ Hooks.once("init", function () {
documents, 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.documentClass = documents.PrismRPGActor
CONFIG.Actor.dataModels = { CONFIG.Actor.dataModels = {
@@ -49,13 +53,12 @@ Hooks.once("init", function () {
CONFIG.Item.dataModels = { CONFIG.Item.dataModels = {
skill: models.PrismRPGSkill, skill: models.PrismRPGSkill,
"racial-ability": models.PrismRPGRacialAbility, "racial-ability": models.PrismRPGRacialAbility,
ability: models.PrismRPGAbility,
weapon: models.PrismRPGWeapon, weapon: models.PrismRPGWeapon,
armor: models.PrismRPGArmor, armor: models.PrismRPGArmor,
shield: models.PrismRPGShield, shield: models.PrismRPGShield,
spell: models.PrismRPGSpell, spell: models.PrismRPGSpell,
// Vulnerability: models.PrismRPGVulnerability, // Disabled - Legacy from Lethal Fantasy
equipment: models.PrismRPGEquipment, equipment: models.PrismRPGEquipment,
// Miracle: models.PrismRPGMiracle // Disabled - Legacy from Lethal Fantasy, PRISM uses Divine class features instead
race: models.PrismRPGRace, race: models.PrismRPGRace,
class: models.PrismRPGClass, class: models.PrismRPGClass,
"character-path": models.PrismRPGCharacterPath, "character-path": models.PrismRPGCharacterPath,
@@ -69,7 +72,7 @@ Hooks.once("init", function () {
foundry.documents.collections.Items.unregisterSheet("core", foundry.appv1.sheets.ActorSheet) 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.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.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.PrismRPGAbilitySheet, { types: ["ability"], makeDefault: true })
foundry.documents.collections.Items.registerSheet("prismRPG", applications.PrismRPGWeaponSheet, { types: ["weapon"], makeDefault: true }) foundry.documents.collections.Items.registerSheet("prismRPG", applications.PrismRPGWeaponSheet, { types: ["weapon"], makeDefault: true })
foundry.documents.collections.Items.registerSheet("prismRPG", applications.PrismRPGSpellSheet, { types: ["spell"], makeDefault: true }) foundry.documents.collections.Items.registerSheet("prismRPG", applications.PrismRPGSpellSheet, { types: ["spell"], makeDefault: true })
foundry.documents.collections.Items.registerSheet("prismRPG", applications.PrismRPGArmorSheet, { types: ["armor"], makeDefault: true }) foundry.documents.collections.Items.registerSheet("prismRPG", applications.PrismRPGArmorSheet, { types: ["armor"], makeDefault: true })
@@ -78,7 +81,13 @@ Hooks.once("init", function () {
foundry.documents.collections.Items.registerSheet("prismRPG", applications.PrismRPGRaceSheet, { types: ["race"], makeDefault: true }) 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.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.PrismRPGCharacterPathSheet, { types: ["character-path"], makeDefault: true })
// Foundry.documents.collections.Items.registerSheet("prismRPG", applications.PrismRPGMiracleSheet, { types: ["miracle"], makeDefault: true }) // Disabled - Legacy from Lethal Fantasy
// Status Effects — Afflictions & Imbuements
CONFIG.statusEffects = [
{ id: "dead", name: "EFFECT.StatusDead", icon: "icons/svg/skull.svg" },
...AFFLICTIONS,
...IMBUEMENTS,
]
// Other Document Configuration // Other Document Configuration
CONFIG.ChatMessage.documentClass = documents.PrismRPGChatMessage CONFIG.ChatMessage.documentClass = documents.PrismRPGChatMessage
@@ -133,9 +142,11 @@ function preLocalizeConfig() {
for (const choice of choicesToLocalize) { for (const choice of choicesToLocalize) {
if (CONFIG.PRISMRPG[choice]) { if (CONFIG.PRISMRPG[choice]) {
const localized = {}
for (const [key, label] of Object.entries(CONFIG.PRISMRPG[choice])) { 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 +182,19 @@ if (foundry.utils.isNewerVersion(game.version, "12.0",)) {
} }
Hooks.on(hookName, (message, html, data) => { Hooks.on(hookName, (message, html, data) => {
const typeMessage = data.message.flags.prismRPG?.typeMessage 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 // Message de demande de jet de dés
if (typeMessage === "askRoll") { if (typeMessage === "askRoll") {
// Affichage des boutons de jet de dés uniquement pour les joueurs // Affichage des boutons de jet de dés uniquement pour les joueurs
if (game.user.isGM) { if (game.user.isGM) {
html.find(".ask-roll-dice").each((i, btn) => { $html.find(".ask-roll-dice").each((i, btn) => {
btn.style.display = "none" btn.style.display = "none"
}) })
} else { } else {
html.find(".ask-roll-dice").click((event) => { $html.find(".ask-roll-dice").click((event) => {
const btn = $(event.currentTarget) const btn = $(event.currentTarget)
const type = btn.data("type") const type = btn.data("type")
const value = btn.data("value") const value = btn.data("value")
@@ -190,11 +205,86 @@ Hooks.on(hookName, (message, html, data) => {
}) })
} }
} }
// Handle new round AP/mana restore buttons (GM only)
if (typeMessage === "newRound") {
$html.find(".new-round-restore-btn").click(async (event) => {
const btn = event.currentTarget
const actorId = btn.dataset.actorId
const targets = actorId === "all"
? $html.find(".new-round-restore-btn[data-actor-id!='all']").toArray().map(b => game.actors.get(b.dataset.actorId)).filter(Boolean)
: [game.actors.get(actorId)].filter(Boolean)
for (const actor of targets) {
await actor.update({
"system.actionPoints.value": actor.system.actionPoints.max,
"system.manaPoints.value": Math.min(actor.system.manaPoints.value + 1, actor.system.manaPoints.max)
})
}
if (actorId === "all") {
$html.find(".new-round-restore-btn").prop("disabled", true).addClass("restored")
} else {
btn.disabled = true
btn.classList.add("restored")
}
})
}
// Handle Roll Damage button click in weapon attack messages
$html.find(".roll-damage-button").click(async (event) => {
const btn = event.currentTarget
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)
})
})
/**
* Send a GM-only chat message with restore buttons at the start of each new round
*/
Hooks.on("updateCombat", async (combat, change, _options, _userId) => {
if (!game.user.isGM) return
if (change.round === undefined || change.round <= 1) return
// Deduplicated character-type actors from the active combat
const seen = new Set()
const playerActors = combat.combatants.contents
.filter(c => c.actor?.type === "character" && !seen.has(c.actor.id) && seen.add(c.actor.id))
.map(c => ({ id: c.actor.id, name: c.actor.name, img: c.actor.img }))
if (playerActors.length === 0) return
const content = await foundry.applications.handlebars.renderTemplate(
"systems/fvtt-prism-rpg/templates/chat-new-round.hbs",
{ actors: playerActors, round: change.round }
)
await ChatMessage.create({
content,
whisper: ChatMessage.getWhisperRecipients("GM"),
flags: { prismRPG: { typeMessage: "newRound" } }
})
}) })
Hooks.on("getCombatTrackerEntryContext", (html, options) => { /**
PrismRPGUtils.pushCombatOptions(html, options); * Inject a visual separator between Afflictions and Imbuements in the Token HUD status tray
}); */
Hooks.on("renderTokenHUD", (_app, html) => {
const tray = html.querySelector(".status-effects")
if (!tray) return
const firstImb = tray.querySelector("[data-status-id^='imb-']")
if (!firstImb) return
const sep = document.createElement("div")
sep.className = "status-separator"
firstImb.before(sep)
})
/** /**
* Create a macro when dropping an entity on the hotbar * Create a macro when dropping an entity on the hotbar
+26
View File
@@ -0,0 +1,26 @@
.ability-content {
.sheet-common();
.item-sheet-common();
.header {
display: flex;
img {
width: 50px;
height: 50px;
}
}
input[type="checkbox"] {
font-size: var(--font-size-14);
width: 20px;
padding-top: 0;
}
input[type="checkbox"]:checked {
background-color: rgba(0, 0, 0, 0.1);
}
input[type="checkbox"]:checked::after {
color: rgba(0, 0, 0, 0.1);
}
}
+7
View File
@@ -143,6 +143,13 @@
border-radius: 4px; border-radius: 4px;
} }
} }
&.hp-temp-item {
.hp-value input {
background: rgba(255, 230, 160, 0.85);
border-color: #b8860b;
}
}
} }
} }
} }
+620 -23
View File
@@ -1,33 +1,406 @@
&.fortune { // Chat Message Card Styles - Applied globally for chat messages
img { .chat-log .message-content {
border: 0px; .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; .chat-title {
flex-direction: row; flex: 1;
.intro-img { display: flex;
padding: 5px; flex-direction: column;
width: 80px; justify-content: center;
align-self: 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; display: flex;
flex-direction: column; align-items: center;
.introText { gap: 3px;
font-family: var(--font-secondary); padding: 2px 6px;
font-size: calc(var(--font-size-standard) * 1.2); background: rgba(0, 0, 0, 0.2);
width: 210px; border-radius: 3px;
text-align: center; 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; .special-badge {
align-items: center; display: inline-block;
font-size: calc(var(--font-size-standard) * 1.3); 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 { &.ask-roll {
@@ -38,3 +411,227 @@
font-family: var(--font-secondary); font-family: var(--font-secondary);
font-size: calc(var(--font-size-standard) * 1.2); 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%);
}
}
}
}
// New round message
.new-round-message {
.chat-title .new-round-label {
font-size: var(--font-size-11);
font-style: italic;
color: #8a7a5a;
margin-top: 1px;
}
.new-round-actors {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 6px;
padding: 6px 0 2px;
.new-round-restore-btn {
display: flex;
align-items: center;
gap: 6px;
width: calc(50% - 3px);
padding: 4px 8px 4px 6px;
border: 1px solid #7a6a45;
border-radius: 4px;
background: linear-gradient(135deg, #f5e6c8 0%, #e8d5a0 100%);
cursor: pointer;
font-size: var(--font-size-13);
color: #3a2e1a;
overflow: hidden;
span {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
min-width: 0;
}
img {
flex-shrink: 0;
width: 24px;
height: 24px;
border: none;
border-radius: 3px;
object-fit: cover;
}
&:hover:not(:disabled) {
background: linear-gradient(135deg, #fdf3dc 0%, #f0e0b0 100%);
border-color: #a08040;
}
&:disabled,
&.restored {
opacity: 0.45;
cursor: default;
}
}
.new-round-all-btn {
width: 100%;
background: linear-gradient(135deg, #c8dff5 0%, #a0c0e8 100%);
border-color: #4a6a8a;
color: #1a2e3a;
font-weight: bold;
&:hover:not(:disabled) {
background: linear-gradient(135deg, #dcedfc 0%, #b0d0f0 100%);
border-color: #3a5a7a;
}
}
}
}
// Token HUD — separator between Afflictions and Imbuements
.palette.status-effects {
.status-separator {
grid-column: 1 / -1;
width: 100%;
height: 2px;
border: none;
border-top: 2px solid rgba(255, 255, 255, 0.35);
margin: 5px 0;
position: relative;
}
}
+135
View File
@@ -13,4 +13,139 @@
label { label {
flex: 10%; 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;
}
}
}
} }
+3 -1
View File
@@ -9,11 +9,11 @@
@import "monster.less"; @import "monster.less";
@import "skill.less"; @import "skill.less";
@import "racial-ability.less"; @import "racial-ability.less";
@import "ability.less";
@import "weapon.less"; @import "weapon.less";
@import "armor.less"; @import "armor.less";
@import "spell.less"; @import "spell.less";
@import "vulnerability.less"; @import "vulnerability.less";
@import "chat.less";
@import "equipment.less"; @import "equipment.less";
@import "shield.less"; @import "shield.less";
@import "miracle.less"; @import "miracle.less";
@@ -24,5 +24,7 @@
@import "weapon-types-config.less"; @import "weapon-types-config.less";
} }
@import "chat.less";
@import "roll.less"; @import "roll.less";
@import "roll-dialog-modern.less";
@import "hud.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; border-radius: 4px;
padding: 0.5rem; 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 { .prismrpg-range-defense-dialog {
@@ -156,4 +180,34 @@
font-size: calc(var(--font-size-standard) * 1); font-size: calc(var(--font-size-standard) * 1);
text-shadow: 0 0 10px var(--color-shadow-primary); 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;
}
}
}
} }
+2 -1
View File
@@ -6,7 +6,7 @@
"download": "#{DOWNLOAD}#", "download": "#{DOWNLOAD}#",
"url": "#{URL}#", "url": "#{URL}#",
"license": "LICENSE", "license": "LICENSE",
"version": "13.0.0", "version": "13.0.1",
"authors": [ "authors": [
{ {
"name": "Uberwald", "name": "Uberwald",
@@ -34,6 +34,7 @@
"Item": { "Item": {
"skill": { "htmlFields": ["description"] }, "skill": { "htmlFields": ["description"] },
"racial-ability": { "htmlFields": ["description"] }, "racial-ability": { "htmlFields": ["description"] },
"ability": { "htmlFields": ["description"] },
"weapon": { "htmlFields": ["description"] }, "weapon": { "htmlFields": ["description"] },
"armor": { "htmlFields": ["description"] }, "armor": { "htmlFields": ["description"] },
"shield": { "htmlFields": ["description"] }, "shield": { "htmlFields": ["description"] },
+38
View File
@@ -0,0 +1,38 @@
<section>
<div class="header">
<img
class="item-img"
src="{{item.img}}"
data-edit="img"
data-action="editImage"
data-tooltip="{{item.name}}"
/>
{{formInput fields.name value=source.name}}
</div>
{{! Navigation des onglets }}
<nav class="sheet-tabs tabs" data-group="primary">
<a class="item {{tabs.description.cssClass}}" data-tab="description">{{localize "PRISMRPG.Label.description"}}</a>
<a class="item {{tabs.effects.cssClass}}" data-tab="effects">{{localize "PRISMRPG.Label.effects"}}</a>
</nav>
{{! Onglet Description }}
<div class="tab {{tabs.description.cssClass}}" data-group="primary" data-tab="description">
<fieldset>
<legend>{{localize "PRISMRPG.Label.description"}}</legend>
{{formInput
systemFields.description
enriched=enrichedDescription
value=system.description
name="system.description"
toggled=true
}}
</fieldset>
</div>
{{! Onglet Effects }}
<div class="tab {{tabs.effects.cssClass}}" data-group="primary" data-tab="effects">
{{> systems/fvtt-prism-rpg/templates/partial-item-effects.hbs}}
</div>
</section>
+2 -59
View File
@@ -8,13 +8,7 @@
<fieldset> <fieldset>
<legend>{{localize "PRISMRPG.Label.biodata"}}</legend> <legend>{{localize "PRISMRPG.Label.biodata"}}</legend>
<div class="biodata"> <div class="biodata">
<div class="biodata-elem"> {{!-- Class and Mortal fields removed - don't exist in DataModel --}}
<span class="name">Class</span>
{{formInput
systemFields.biodata.fields.class
value=system.biodata.class
}}
</div>
<div class="biodata-elem"> <div class="biodata-elem">
<span class="name">Level</span> <span class="name">Level</span>
{{formInput {{formInput
@@ -22,13 +16,6 @@
value=system.biodata.level value=system.biodata.level
}} }}
</div> </div>
<div class="biodata-elem">
<span class="name">Mortal</span>
{{formInput
systemFields.biodata.fields.mortal
value=system.biodata.mortal
}}
</div>
<div class="biodata-elem"> <div class="biodata-elem">
<span class="name">Age</span> <span class="name">Age</span>
{{formInput systemFields.biodata.fields.age value=system.biodata.age}} {{formInput systemFields.biodata.fields.age value=system.biodata.age}}
@@ -82,51 +69,7 @@
value=system.biodata.magicUser value=system.biodata.magicUser
}} }}
</div> </div>
<div class="biodata-elem"> {{!-- Cleric User, modifiers, and hpPerLevel fields removed - don't exist in DataModel --}}
<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>
{{#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}}
<div class="biodata-elem">
<span class="name">Last HD roll</span>
{{formInput
systemFields.biodata.fields.hpPerLevel
value=system.biodata.hpPerLevel
disabled=true
}}
</div>
</div> </div>
</fieldset> </fieldset>
+5 -28
View File
@@ -21,6 +21,7 @@
class="item-img" class="item-img"
src="{{item.img}}" src="{{item.img}}"
data-tooltip="{{item.name}}" data-tooltip="{{item.name}}"
data-action="postItemToChat"
/> />
{{/if}} {{/if}}
<div class="name" data-tooltip="{{item.system.description}}"> <div class="name" data-tooltip="{{item.system.description}}">
@@ -41,43 +42,17 @@
></i> ></i>
</a> </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 <a
class="rollable" class="rollable"
data-roll-type="weapon-damage-medium" data-roll-type="weapon-damage-medium"
data-roll-key="{{item.id}}" data-roll-key="{{item.id}}"
data-tooltip="Roll Damage (Medium)" data-tooltip="Roll Damage"
> >
<i <i
class="fa-regular fa-face-head-bandage" class="fa-regular fa-face-head-bandage"
data-roll-type="weapon-damage-medium" data-roll-type="weapon-damage-medium"
data-roll-key="{{item.id}}" data-roll-key="{{item.id}}"
></i>M ></i>
</a> </a>
</div> </div>
@@ -114,6 +89,7 @@
class="item-img" class="item-img"
src="{{item.img}}" src="{{item.img}}"
data-tooltip="{{item.name}}" data-tooltip="{{item.name}}"
data-action="postItemToChat"
/> />
<div class="name" data-tooltip="{{item.system.description}}"> <div class="name" data-tooltip="{{item.system.description}}">
{{item.name}} {{item.name}}
@@ -163,6 +139,7 @@
class="item-img" class="item-img"
src="{{item.img}}" src="{{item.img}}"
data-tooltip="{{item.name}}" data-tooltip="{{item.name}}"
data-action="postItemToChat"
/> />
<div class="name" data-tooltip="{{item.system.description}}"> <div class="name" data-tooltip="{{item.system.description}}">
{{item.name}} {{item.name}}
+6 -6
View File
@@ -4,11 +4,11 @@
<fieldset> <fieldset>
<legend>{{localize "PRISMRPG.Label.money"}}</legend> <legend>{{localize "PRISMRPG.Label.money"}}</legend>
<div class="moneys"> <div class="moneys">
{{formField systemFields.moneys.fields.tinbit.fields.value value=system.moneys.tinbit.value localize=true}} {{formField systemFields.moneys.fields.coppercoin.fields.value value=system.moneys.coppercoin.value localize=true}}
{{formField systemFields.moneys.fields.copper.fields.value value=system.moneys.copper.value localize=true}} {{formField systemFields.moneys.fields.silvercoin.fields.value value=system.moneys.silvercoin.value localize=true}}
{{formField systemFields.moneys.fields.silver.fields.value value=system.moneys.silver.value localize=true}} {{formField systemFields.moneys.fields.goldcoin.fields.value value=system.moneys.goldcoin.value localize=true}}
{{formField systemFields.moneys.fields.gold.fields.value value=system.moneys.gold.value localize=true}} {{formField systemFields.moneys.fields.note.fields.value value=system.moneys.note.value localize=true}}
{{formField systemFields.moneys.fields.platinum.fields.value value=system.moneys.platinum.value localize=true}} {{formField systemFields.moneys.fields.steam.fields.value value=system.moneys.steam.value localize=true}}
</div> </div>
</fieldset> </fieldset>
@@ -17,7 +17,7 @@
<div class="equipments"> <div class="equipments">
{{#each equipments as |item|}} {{#each equipments as |item|}}
<div class="equipment" data-item-id="{{item.id}}" data-item-uuid="{{item.uuid}}"> <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}}}"> <div class="name" data-tooltip="{{{item.system.description}}}">
{{item.name}} {{item.name}}
</div> </div>
+46 -9
View File
@@ -46,6 +46,7 @@
<div class="hp-shields"> <div class="hp-shields">
<div class="hp-item"> <div class="hp-item">
<div class="hp-label">HP</div> <div class="hp-label">HP</div>
<a data-action="hpMinus"><i class="fa-solid fa-minus"></i></a>
<div class="hp-value"> <div class="hp-value">
{{formInput {{formInput
systemFields.hp.fields.value systemFields.hp.fields.value
@@ -53,6 +54,7 @@
disabled=isPlayMode disabled=isPlayMode
}} }}
</div> </div>
<a data-action="hpPlus"><i class="fa-solid fa-plus"></i></a>
<div class="hp-separator">/</div> <div class="hp-separator">/</div>
<div class="hp-max"> <div class="hp-max">
{{formInput {{formInput
@@ -62,26 +64,41 @@
}} }}
</div> </div>
</div> </div>
<div class="hp-item"> <div class="hp-item hp-temp-item">
<div class="hp-label">MAGIC</div> <div class="hp-label" data-tooltip="{{localize 'PRISMRPG.Label.HPTemp'}}">HP Tmp</div>
<a data-action="hpTempMinus"><i class="fa-solid fa-minus"></i></a>
<div class="hp-value"> <div class="hp-value">
{{formInput {{formInput
systemFields.magicPoints.fields.value systemFields.hp.fields.temp
value=system.magicPoints.value value=system.hp.temp
disabled=isPlayMode disabled=isPlayMode
}} }}
</div> </div>
<a data-action="hpTempPlus"><i class="fa-solid fa-plus"></i></a>
</div>
<div class="hp-item">
<div class="hp-label">MANA</div>
<a data-action="manaPointsMinus"><i class="fa-solid fa-minus"></i></a>
<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-separator">/</div>
<div class="hp-max"> <div class="hp-max">
{{formInput {{formInput
systemFields.magicPoints.fields.max systemFields.manaPoints.fields.max
value=system.magicPoints.max value=system.manaPoints.max
disabled=isPlayMode disabled=isPlayMode
}} }}
</div> </div>
</div> </div>
<div class="hp-item"> <div class="hp-item">
<div class="hp-label">ARMOR</div> <div class="hp-label">ARMOR</div>
<a data-action="armorPointsMinus"><i class="fa-solid fa-minus"></i></a>
<div class="hp-value"> <div class="hp-value">
{{formInput {{formInput
systemFields.armorPoints.fields.value systemFields.armorPoints.fields.value
@@ -89,6 +106,7 @@
disabled=isPlayMode disabled=isPlayMode
}} }}
</div> </div>
<a data-action="armorPointsPlus"><i class="fa-solid fa-plus"></i></a>
<div class="hp-separator">/</div> <div class="hp-separator">/</div>
<div class="hp-max"> <div class="hp-max">
{{formInput {{formInput
@@ -98,6 +116,26 @@
}} }}
</div> </div>
</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> </div>
</div> </div>
@@ -266,7 +304,7 @@
<div class="race-content"> <div class="race-content">
{{#if race}} {{#if race}}
<div class="race-item" data-item-id="{{race.id}}" data-item-uuid="{{race.uuid}}"> <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}}" /> <img class="item-img" src="{{race.img}}" data-tooltip="{{race.name}}" data-action="postItemToChat" />
<div class="race-name">{{race.name}}</div> <div class="race-name">{{race.name}}</div>
<div class="controls"> <div class="controls">
<a data-tooltip="{{localize 'PRISMRPG.Edit'}}" data-action="edit" data-item-id="{{race.id}}" <a data-tooltip="{{localize 'PRISMRPG.Edit'}}" data-action="edit" data-item-id="{{race.id}}"
@@ -292,7 +330,7 @@
<div class="class-content"> <div class="class-content">
{{#if classItem}} {{#if classItem}}
<div class="class-item" data-item-id="{{classItem.id}}" data-item-uuid="{{classItem.uuid}}"> <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}}" /> <img class="item-img" src="{{classItem.img}}" data-tooltip="{{classItem.name}}" data-action="postItemToChat" />
<div class="class-name">{{classItem.name}}</div> <div class="class-name">{{classItem.name}}</div>
<div class="controls"> <div class="controls">
<a data-tooltip="{{localize 'PRISMRPG.Edit'}}" data-action="edit" data-item-id="{{classItem.id}}" <a data-tooltip="{{localize 'PRISMRPG.Edit'}}" data-action="edit" data-item-id="{{classItem.id}}"
@@ -327,6 +365,5 @@
</div> </div>
</div> </div>
</div> </div>
</div>
</section> </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>
+25 -3
View File
@@ -11,8 +11,8 @@
<div class="skill {{#if item.system.isCoreSkill}}is-core-skill{{/if}}" <div class="skill {{#if item.system.isCoreSkill}}is-core-skill{{/if}}"
data-item-id="{{item.id}}" data-item-id="{{item.id}}"
data-item-uuid="{{item.uuid}}"> 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"> <div class="name" data-tooltip="{{item.system.description}}">
<a class="rollable" data-roll-type="skill" data-roll-key="{{item.id}}"> <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> <i class="lf-roll-small fa-duotone fa-solid fa-dice-d10"></i>
{{item.name}} {{item.name}}
@@ -46,7 +46,7 @@
<div class="racial-abilities"> <div class="racial-abilities">
{{#each racialAbilities as |item|}} {{#each racialAbilities as |item|}}
<div class="racial-ability " data-item-id="{{item.id}}" data-item-uuid="{{item.uuid}}"> <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}}" /> <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"> <div class="name" data-tooltip="{{{item.description}}}<br><br>{{item.path}}" data-tooltip-direction="UP">
{{item.name}} {{item.name}}
</div> </div>
@@ -60,5 +60,27 @@
{{/each}} {{/each}}
</div> </div>
</fieldset> </fieldset>
<fieldset>
<legend data-tooltip="{{localize 'PRISMRPG.Tooltip.abilities'}}" data-tooltip-direction="UP">
{{localize "PRISMRPG.Label.abilities"}}
</legend>
<div class="racial-abilities">
{{#each abilities as |item|}}
<div class="racial-ability" data-item-id="{{item.id}}" data-item-uuid="{{item.uuid}}">
<img class="item-img" src="{{item.img}}" data-tooltip="{{item.name}}\nClick to post to chat" data-action="postItemToChat" />
<div class="name" data-tooltip="{{{item.description}}}" data-tooltip-direction="UP">
{{item.name}}
</div>
<div class="controls">
<a data-tooltip="{{localize 'PRISMRPG.Edit'}}" data-action="edit" data-item-id="{{item.id}}"
data-item-uuid="{{item.uuid}}"><i class="fas fa-edit"></i></a>
<a data-tooltip="{{localize 'PRISMRPG.Delete'}}" data-action="delete" data-item-id="{{item.id}}"
data-item-uuid="{{item.uuid}}"><i class="fas fa-trash"></i></a>
</div>
</div>
{{/each}}
</div>
</fieldset>
</div> </div>
</section> </section>
+3 -23
View File
@@ -1,22 +1,6 @@
<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"> <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> <fieldset>
<legend>{{localize "PRISMRPG.Label.spells"}}{{#if isEditMode}}<a class="action" data-tooltip="{{localize " <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" PRISMRPG.Tooltip.addSpell"}}" data-tooltip-direction="UP"><i class="fas fa-plus"
@@ -24,17 +8,13 @@
<div class="spells"> <div class="spells">
{{#each spells as |item|}} {{#each spells as |item|}}
<div class="spell" data-item-id="{{item.id}}" data-item-uuid="{{item.uuid}}" data-drag="true"> <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"> <div class="name">
{{item.name}} {{item.name}}
</div> </div>
<a class="rollable" data-roll-type="spell-attack" data-roll-key="{{item.id}}" data-tooltip="Spell Attack"> <a class="rollable" data-roll-type="spell-cast" data-roll-key="{{item.id}}" data-tooltip="Cast Spell">
<i class="lf-roll-small fa-solid fa-swords" data-roll-type="spell-attack" data-roll-key="{{item.id}}"></i> <i class="fa-duotone fa-solid fa-wand-magic-sparkles" data-roll-type="spell-cast" 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> </a>
<div class="controls"> <div class="controls">
-257
View File
@@ -1,257 +0,0 @@
<section class="character-subattributes tab" data-group="sheet" data-tab="subattributes">
{{log "character-subattributes" this}}
<div class="subattributes-content">
<h2 class="section-header">
<i class="fa-solid fa-diagram-project"></i>
Sub-Attributes
</h2>
<p class="section-description">
Sub-attributes are derived from the average of two primary characteristics.
</p>
<div class="subattributes-list">
{{#each (entries config.SUB_ATTRIBUTES) as |entry|}}
{{#with entry.1 as |subAttr|}}
<div class="subattribute-item">
<div class="subattribute-header">
<div class="subattribute-name">
<i class="fa-solid fa-circle-nodes"></i>
<span>{{localize subAttr.label}}</span>
</div>
<div class="subattribute-value">
<input
type="text"
value="{{lookup ../system.subAttributes subAttr.id 'value'}}"
disabled
readonly
/>
</div>
</div>
<div class="subattribute-details">
<div class="subattribute-parents">
<span class="parent-label">From:</span>
{{#each subAttr.parents as |parentKey|}}
<span class="parent-char">
{{uppercase parentKey}}
({{lookup ../../system.characteristics parentKey 'value'}})
</span>
{{/each}}
</div>
<div class="subattribute-description">
{{localize subAttr.description}}
</div>
</div>
</div>
{{/with}}
{{/each}}
</div>
</div>
</section>
<div class="subattributes-wrapper">
{{! Character Header with Age, Length, Weight, Sex, Skin, Hair }}
<div class="character-bio-header">
<div class="bio-row">
<div class="bio-field">
<label>Age</label>
{{formInput
systemFields.bio.fields.age
value=system.bio.age
disabled=isPlayMode
}}
</div>
<div class="bio-field">
<label>Length</label>
{{formInput
systemFields.bio.fields.length
value=system.bio.length
disabled=isPlayMode
}}
</div>
<div class="bio-field">
<label>Weight</label>
{{formInput
systemFields.bio.fields.weight
value=system.bio.weight
disabled=isPlayMode
}}
</div>
</div>
<div class="bio-row">
<div class="bio-field">
<label>Sex</label>
{{formInput
systemFields.bio.fields.sex
value=system.bio.sex
disabled=isPlayMode
}}
</div>
<div class="bio-field">
<label>Skin</label>
{{formInput
systemFields.bio.fields.skin
value=system.bio.skin
disabled=isPlayMode
}}
</div>
<div class="bio-field">
<label>Hair</label>
{{formInput
systemFields.bio.fields.hair
value=system.bio.hair
disabled=isPlayMode
}}
</div>
</div>
</div>
{{! Sub-Attributes Table }}
<div class="subattributes-section">
<h3 class="section-title">Sub-Attribute</h3>
<div class="subattributes-table">
<div class="subattr-column">
<div class="subattr-header">Prowess</div>
<div class="subattr-cell">
{{formInput
systemFields.subattributes.fields.prowess
value=system.subattributes.prowess
disabled=isPlayMode
}}
</div>
<div class="subattr-header">Vigor</div>
<div class="subattr-cell">
{{formInput
systemFields.subattributes.fields.vigor
value=system.subattributes.vigor
disabled=isPlayMode
}}
</div>
<div class="subattr-header">Competence</div>
<div class="subattr-cell">
{{formInput
systemFields.subattributes.fields.competence
value=system.subattributes.competence
disabled=isPlayMode
}}
</div>
<div class="subattr-header">Authority</div>
<div class="subattr-cell">
{{formInput
systemFields.subattributes.fields.authority
value=system.subattributes.authority
disabled=isPlayMode
}}
</div>
<div class="subattr-header">Presence</div>
<div class="subattr-cell">
{{formInput
systemFields.subattributes.fields.presence
value=system.subattributes.presence
disabled=isPlayMode
}}
</div>
</div>
<div class="subattr-column">
<div class="subattr-header">Willpower</div>
<div class="subattr-cell">
{{formInput
systemFields.subattributes.fields.willpower
value=system.subattributes.willpower
disabled=isPlayMode
}}
</div>
<div class="subattr-header">Resilience</div>
<div class="subattr-cell">
{{formInput
systemFields.subattributes.fields.resilience
value=system.subattributes.resilience
disabled=isPlayMode
}}
</div>
<div class="subattr-header">Cunning</div>
<div class="subattr-cell">
{{formInput
systemFields.subattributes.fields.cunning
value=system.subattributes.cunning
disabled=isPlayMode
}}
</div>
<div class="subattr-header">Guile</div>
<div class="subattr-cell">
{{formInput
systemFields.subattributes.fields.guile
value=system.subattributes.guile
disabled=isPlayMode
}}
</div>
<div class="subattr-header">Sovereignty</div>
<div class="subattr-cell">
{{formInput
systemFields.subattributes.fields.sovereignty
value=system.subattributes.sovereignty
disabled=isPlayMode
}}
</div>
</div>
<div class="subattr-column">
<div class="subattr-header">Stamina</div>
<div class="subattr-cell">
{{formInput
systemFields.subattributes.fields.stamina
value=system.subattributes.stamina
disabled=isPlayMode
}}
</div>
<div class="subattr-header">Initiative</div>
<div class="subattr-cell">
{{formInput
systemFields.subattributes.fields.initiative
value=system.subattributes.initiative
disabled=isPlayMode
}}
</div>
<div class="subattr-header">Wit</div>
<div class="subattr-cell">
{{formInput
systemFields.subattributes.fields.wit
value=system.subattributes.wit
disabled=isPlayMode
}}
</div>
<div class="subattr-header">Grace</div>
<div class="subattr-cell">
{{formInput
systemFields.subattributes.fields.grace
value=system.subattributes.grace
disabled=isPlayMode
}}
</div>
<div class="subattr-header">Tenacity</div>
<div class="subattr-cell">
{{formInput
systemFields.subattributes.fields.tenacity
value=system.subattributes.tenacity
disabled=isPlayMode
}}
</div>
</div>
</div>
</div>
{{! Proficiencies Section }}
<div class="proficiencies-section">
<h3 class="section-title">Proficiencies</h3>
<div class="proficiencies-box">
{{formInput
systemFields.proficiencies
value=system.proficiencies
disabled=isPlayMode
type="textarea"
}}
</div>
</div>
</div>
</section>
+207 -96
View File
@@ -1,110 +1,221 @@
{{!log 'chat-message' this}} {{!log "chat-message" this}}
<div class="{{cssClass}}"> {{!log "rollTarget" rollTarget}}
<div class="intro-chat"> {{!log "rollData" rollData}}
<div class="intro-img"> <div class="{{cssClass}} prismrpg-chat-card">
<div class="chat-header">
<div class="chat-portrait">
<img src="{{actingCharImg}}" data-tooltip="{{actingCharName}}" /> <img src="{{actingCharImg}}" data-tooltip="{{actingCharName}}" />
</div> </div>
<div class="chat-title">
<div class="intro-right"> <div class="actor-name">{{actingCharName}}</div>
<span><STRONG>{{actingCharName}} - {{upperFirst rollName}}</STRONG></span> <div class="roll-name">{{upperFirst rollName}}</div>
{{#if (match rollType "attack")}} {{#if (match rollType "attack")}}
<span>Attack roll !</span> <div class="roll-type-badge attack">Attack Roll</div>
{{/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>
{{/if}} {{/if}}
{{#if badResult}} {{#if badResult}}
<span><strong>{{localize "PRISMRPG.Label.otherResult"}}</strong> <div class="bad-result">{{localize "PRISMRPG.Label.otherResult"}}: {{badResult}}</div>
:
{{badResult}}</span>
{{/if}} {{/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>
{{#if (eq rollType "save")}}
{{#if rollTarget.abilityModifier}}
<span style="font-size: 0.9em;">
(Ability Mod:
{{#if
(gt rollTarget.abilityModifier 0)
}}+{{/if}}{{rollTarget.abilityModifier}}, Save Bonus:
{{#if
(gt rollTarget.saveProficiency 0)
}}+{{/if}}{{rollTarget.saveProficiency}})
</span>
{{/if}}
{{/if}}
{{#each diceResults as |result|}}
<span>{{result.dice}} : {{result.value}}</span>
{{/each}}
</div> </div>
</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}}
{{/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="chat-content">
<div class="dice-result"> {{#if rollTarget.weapon}}
<h4 class="dice-total">{{total}}</h4> <div class="weapon-info-card">
</div> <div class="weapon-header">
{{#if D30result}} <strong class="weapon-name">{{rollTarget.weapon.name}}</strong>
<div class="dice-result"> {{#if rollTarget.weapon.system.isImplement}}
<h4 class="dice-total">D30 result: {{D30result}}</h4> <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> </div>
{{/if}} {{/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>
{{#unless isPrivate}}
<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}} {{/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}}
{{#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> </div>
+20
View File
@@ -0,0 +1,20 @@
<div class="new-round-message prismrpg-chat-card">
<div class="chat-header">
<div class="chat-title">
<div class="roll-name">{{localize "PRISMRPG.Combat.newRound"}} {{round}}</div>
<div class="new-round-label">{{localize "PRISMRPG.Combat.restoreAP"}}</div>
</div>
</div>
<div class="new-round-actors">
<button class="new-round-restore-btn new-round-all-btn" type="button" data-actor-id="all" data-tooltip="{{localize "PRISMRPG.Combat.allPC"}}">
<i class="fas fa-users"></i>
<span>{{localize "PRISMRPG.Combat.allPC"}}</span>
</button>
{{#each actors}}
<button class="new-round-restore-btn" type="button" data-actor-id="{{id}}" data-tooltip="{{name}}">
<img src="{{img}}" width="24" height="24" />
<span>{{name}}</span>
</button>
{{/each}}
</div>
</div>
+55 -1
View File
@@ -7,6 +7,7 @@
{{! Navigation des onglets }} {{! Navigation des onglets }}
<nav class="sheet-tabs tabs" data-group="primary"> <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.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> <a class="item {{tabs.description.cssClass}}" data-tab="description">{{localize "PRISMRPG.Label.description"}}</a>
</nav> </nav>
@@ -61,7 +62,7 @@
<legend>{{localize "PRISMRPG.Label.classFeatures"}}</legend> <legend>{{localize "PRISMRPG.Label.classFeatures"}}</legend>
{{#each system.features as |feature level|}} {{#each system.features as |feature level|}}
<label>{{localize "PRISMRPG.Label.level"}} {{inc @key}}</label> <label>{{localize "PRISMRPG.Label.level"}} {{replace @key "level" ""}}</label>
{{formInput {{formInput
(lookup ../systemFields.features.fields @key) (lookup ../systemFields.features.fields @key)
enriched=(lookup ../enrichedFeatures @key) enriched=(lookup ../enrichedFeatures @key)
@@ -73,6 +74,59 @@
</fieldset> </fieldset>
</div> </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 }} {{! Onglet Description }}
<div class="tab {{tabs.description.cssClass}}" data-group="primary" data-tab="description"> <div class="tab {{tabs.description.cssClass}}" data-group="primary" data-tab="description">
<fieldset> <fieldset>
-119
View File
@@ -1,119 +0,0 @@
<div class="prismrpg-roll-dialog">
<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>
{{/if}}
{{#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}}
</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>
{{/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>
{{/if}}
<fieldSet>
<legend>{{localize "PRISMRPG.Roll.visibility"}}</legend>
<select name="visibility">
{{selectOptions rollModes selected=visibility localize=true}}
</select>
</fieldSet>
</div>
+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>
+130 -39
View File
@@ -1,51 +1,142 @@
<div class="prismrpg-roll-dialog"> <div class="prismrpg-roll-dialog">
<fieldSet> <fieldSet>
<legend>{{localize (concat "PRISMRPG.Label." rollType)}} <legend>{{actorName}}</legend>
-
{{actorName}}</legend>
{{#if hasModifier}}
<div class="dialog-save">
<strong>{{upperFirst rollName}}</strong>
:
{{dice}}
+
{{baseValue}}
</div>
{{#if (eq rollType "save")}}
<div class="dialog-save" style="font-size: 0.9em; color: #666;">
(Ability Mod:
{{#if
(gt rollTarget.abilityModifier 0)
}}+{{/if}}{{rollTarget.abilityModifier}}
+ Save Bonus:
{{#if
(gt rollTarget.saveProficiency 0)
}}+{{/if}}{{rollTarget.saveProficiency}})
</div>
{{/if}}
{{else}}
<div class="dialog-save">
<strong>{{upperFirst rollName}}</strong>
:
{{dice}}
</div>
{{/if}}
{{#if rollTarget.weapon}} {{#if rollTarget.weapon}}
<div class="dialog-save"> <div class="dialog-save">
{{localize "PRISMRPG.Label.weapon"}} <strong>{{rollTarget.weapon.name}}</strong>
:
{{rollTarget.weapon.name}}
</div>
<div class="dialog-save">
{{localize "PRISMRPG.Label.skill"}}
:
{{rollTarget.name}}
</div> </div>
{{/if}} {{/if}}
</fieldSet> </fieldSet>
{{#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}} {{#if hasAdvantage}}
<fieldSet class="dialog-advantage"> <fieldSet class="dialog-advantage">
<legend>{{localize "PRISMRPG.Roll.advantageDisadvantage"}}</legend> <legend>{{localize "PRISMRPG.Roll.advantageDisadvantage"}}</legend>
+18 -22
View File
@@ -11,35 +11,31 @@
<p class="hint">{{localize "PRISMRPG.Hint.isCoreSkill"}}</p> <p class="hint">{{localize "PRISMRPG.Hint.isCoreSkill"}}</p>
</div> </div>
{{!-- Primary Attribute for Skill Checks --}} {{!-- First Sub-Attribute for Skill Checks --}}
<div class="form-group"> <div class="form-group">
<label>{{localize "PRISMRPG.Label.primaryAttribute"}}</label> <label>{{localize "PRISMRPG.Label.subAttribute1"}}</label>
<select name="system.primaryAttribute"> <select name="system.subAttribute1">
{{#each config.CHARACTERISTICS}} {{#each config.SUB_ATTRIBUTES}}
<option value="{{@key}}" {{#if (eq ../system.primaryAttribute @key)}}selected{{/if}}> <option value="{{@key}}" {{#if (eq ../system.subAttribute1 @key)}}selected{{/if}}>
{{localize this.label}} {{localize this.label}}
</option> </option>
{{/each}} {{/each}}
</select> </select>
<p class="hint">Primary attribute used for skill checks (D&D 5e style: ability modifier + proficiency)</p> <p class="hint">First sub-attribute used for skill checks</p>
</div> </div>
{{#if system.isCoreSkill}} {{!-- Second Sub-Attribute for Skill Checks --}}
{{!-- Attribute Bonus Selection --}} <div class="form-group">
<div class="form-group"> <label>{{localize "PRISMRPG.Label.subAttribute2"}}</label>
<label>{{localize "PRISMRPG.Label.attributeBonusChoice"}}</label> <select name="system.subAttribute2">
<select name="system.attributeBonus"> {{#each config.SUB_ATTRIBUTES}}
<option value="">{{localize "PRISMRPG.Label.selectAttribute"}}</option> <option value="{{@key}}" {{#if (eq ../system.subAttribute2 @key)}}selected{{/if}}>
{{#each config.CHARACTERISTICS}} {{localize this.label}}
<option value="{{@key}}" {{#if (eq ../system.attributeBonus @key)}}selected{{/if}}> </option>
{{localize this.label}} {{/each}}
</option> </select>
{{/each}} <p class="hint">Second sub-attribute used for skill checks</p>
</select> </div>
<p class="hint">{{localize "PRISMRPG.Hint.attributeBonus"}}</p>
</div>
{{/if}}
{{!-- Notes --}} {{!-- Notes --}}
<fieldset> <fieldset>
+20 -21
View File
@@ -71,27 +71,26 @@
</div> </div>
<div class="align-top"> <div class="align-top">
{{! Prism RPG: Projectile Properties }} {{formField
{{#if system.isProjectile}} systemFields.shortRange
{{formField value=system.shortRange
systemFields.isProjectile localize=true
value=system.isProjectile label="PRISMRPG.Label.shortRange"
localize=true }}
label="PRISMRPG.Label.isProjectile"
}} {{formField
{{formField systemFields.longRange
systemFields.range value=system.longRange
value=system.range localize=true
localize=true label="PRISMRPG.Label.longRange"
label="PRISMRPG.Label.range" }}
}}
{{formField {{formField
systemFields.reloadAPC systemFields.reloadAPC
value=system.reloadAPC value=system.reloadAPC
localize=true localize=true
label="PRISMRPG.Label.reloadAPC" label="PRISMRPG.Label.reloadAPC"
}} }}
{{/if}}
{{formField {{formField
systemFields.encLoad systemFields.encLoad