16 Commits

Author SHA1 Message Date
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
uberwald e75824cd20 First PC sheet, WIP 2025-12-20 17:20:01 +01:00
uberwald 189b03ca91 Update actor sheet 2025-12-20 00:09:42 +01:00
uberwald 65dfb3ddff Add effects and tabs 2025-12-13 21:13:26 +01:00
uberwald 809a7b80c2 Add effects and tabs 2025-12-13 19:34:04 +01:00
uberwald a0de5ce91a Add effects and tabs 2025-12-13 17:41:53 +01:00
89 changed files with 9890 additions and 3565 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
The Official game system for playing Prism RPG TTRPG: The Role Playing Game on FoundryVTT. This fully functional system is the foundational framework to build your game.
This product's format, programming code, and presentation is copyrighted by Prism RPG Games LLC.
This system & product are used with permission granted as part of the partnership agreement between Foundry Gaming LLC and Prism RPG Games LLC. It uses the following trademarks and/or copyrights:
This system & product are used with permission granted as part of the partnership agreement between Foundry Gaming LLC and Prism RPG Games LLC.
© 2025 Prism RPG Games. Content copyright Ted McClintock, Prism RPG Games LLC. All Rights Reserved. Prism RPG® is a Registered Trademark of Prism RPG Games LLC. All Rights Reserved.
Prism RPG Games is ©2025 Prism RPG Games, LLC. All rights reserved. Prism RPG, Prism RPG Games, and their associated logos are trademarks of Prism RPG Games, LLC. https://lethalfantasy.com/
For inquiries on developing content for this ruleset please contact Lethalted@lethalfantasy.com
## Community
Please join our Discord server Prism RPG games https://discord.gg/UDvnnyvreV
It's the place to ask questions on how to use the system, make feature request and follow the development of the system.
+19
View File
@@ -0,0 +1,19 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" width="100" height="100">
<!-- Background circle -->
<circle cx="50" cy="50" r="48" fill="#f0f0f0" stroke="#333" stroke-width="2" />
<!-- Star for achievement/advancement -->
<path d="M 50 20 L 55 38 L 74 38 L 59 49 L 64 67 L 50 56 L 36 67 L 41 49 L 26 38 L 45 38 Z"
fill="#ffd700" stroke="#333" stroke-width="1.5" />
<!-- Upward arrow for progression -->
<path d="M 50 75 L 50 85" stroke="#333" stroke-width="3" stroke-linecap="round" />
<path d="M 45 78 L 50 73 L 55 78" fill="none" stroke="#333" stroke-width="3"
stroke-linecap="round" stroke-linejoin="round" />
<!-- Small accent dots for decoration -->
<circle cx="20" cy="30" r="2" fill="#666" />
<circle cx="80" cy="30" r="2" fill="#666" />
<circle cx="20" cy="70" r="2" fill="#666" />
<circle cx="80" cy="70" r="2" fill="#666" />
</svg>

After

Width:  |  Height:  |  Size: 891 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 895 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 MiB

+2308 -59
View File
File diff suppressed because it is too large Load Diff
+294 -32
View File
@@ -78,36 +78,96 @@
"cha": {
"label": "Charisma"
},
"prowess": {
"label": "Prowess",
"description": "Physical power and combat capability (STR+DEX)"
},
"vigor": {
"label": "Vigor",
"description": "Endurance and physical resilience (STR+CON)"
},
"competence": {
"label": "Competence",
"description": "Practical application of knowledge (STR+INT)"
},
"authority": {
"label": "Authority",
"description": "Command and leadership presence (STR+WIS)"
},
"presence": {
"label": "Presence",
"description": "Physical charisma and intimidation (STR+CHA)"
},
"stamina": {
"label": "Stamina",
"description": "Athletic endurance (DEX+CON)"
},
"initiative": {
"label": "Initiative",
"description": "Reaction speed and alertness (DEX+INT)"
},
"wit": {
"label": "Wit",
"description": "Mental agility and cleverness (DEX+WIS)"
},
"grace": {
"label": "Grace",
"description": "Social finesse and charm (DEX+CHA)"
},
"tenacity": {
"label": "Tenacity",
"description": "Mental and physical toughness (CON+INT)"
},
"willpower": {
"label": "Willpower",
"description": "Inner strength and determination (CON+WIS)"
},
"resilience": {
"label": "Resilience",
"description": "Ability to endure hardship (CON+CHA)"
},
"cunning": {
"label": "Cunning",
"description": "Strategic thinking (INT+WIS)"
},
"guile": {
"label": "Guile",
"description": "Deceptive intellect (INT+CHA)"
},
"sovereignty": {
"label": "Sovereignty",
"description": "Diplomatic wisdom and influence (WIS+CHA)"
},
"FIELDS": {
"moneys": {
"tinbit": {
"label": "Tin Bits",
"coppercoin": {
"label": "Copper Coin",
"value": {
"label": "Tin Bits"
"label": "Copper Coin"
}
},
"silver": {
"label": "Silver",
"silvercoin": {
"label": "Silver Coin",
"value": {
"label": "Silver"
"label": "Silver Coin"
}
},
"copper": {
"label": "Copper",
"goldcoin": {
"label": "Gold Coin",
"value": {
"label": "Copper"
"label": "Gold Coin"
}
},
"gold": {
"label": "Gold",
"note": {
"label": "Note",
"value": {
"label": "Gold"
"label": "Note"
}
},
"platinum": {
"label": "Platinum",
"steam": {
"label": "Steam",
"value": {
"label": "Platinum"
"label": "Steam"
}
}
},
@@ -322,6 +382,88 @@
}
}
},
"Ability": {
"FIELDS": {
"description": {
"label": "Description"
}
}
},
"Combat": {
"newRound": "New Round —",
"restoreAP": "Reset AP & +1 Mana",
"allPC": "All PC"
},
"Status": {
"Aided": "Aided",
"Alert": "Alert",
"Alkalized": "Alkalized",
"Anchored": "Anchored",
"Banished": "Banished",
"Bestowed": "Bestowed",
"Blessed": "Blessed",
"Bleed": "Bleed",
"Blind": "Blind",
"Burning": "Burning",
"Chilled": "Chilled",
"Comatose": "Comatose",
"Compulsed": "Compulsed",
"Concealed": "Concealed",
"Corroded": "Corroded",
"Cursed": "Cursed",
"Dazed": "Dazed",
"Deaf": "Deaf",
"Diseased": "Diseased",
"Distracted": "Distracted",
"Enchanted": "Enchanted",
"Enhance": "Enhance",
"Exhaustion": "Exhaustion",
"Fatigue": "Fatigue",
"Frightened": "Frightened",
"Fury": "Fury",
"Haste": "Haste",
"Heroism": "Heroism",
"Horror": "Horror",
"Inspired": "Inspired",
"Invisible": "Invisible",
"Keen": "Keen",
"LifeDrain": "Life Drain",
"Locked": "Locked",
"Madness": "Madness",
"ManaDrain": "Mana Drain",
"Marked": "Marked",
"Mute": "Mute",
"Necrosis": "Necrosis (Elemental)",
"Numbed": "Numbed",
"Paralyzed": "Paralyzed",
"Petrified": "Petrified",
"Plagued": "Plagued",
"Poison": "Poison",
"Prepared": "Prepared",
"Prone": "Prone",
"Radiated": "Radiated (Elemental)",
"Rage": "Rage",
"Regeneration": "Regeneration",
"Reinforced": "Reinforced",
"Renewed": "Renewed",
"Saturated": "Saturated",
"Sealed": "Sealed",
"Seep": "Seep",
"Shattered": "Shattered",
"Shocked": "Shocked (Elemental)",
"Sightless": "Sightless",
"Silenced": "Silenced",
"Soundless": "Soundless",
"Staggered": "Staggered",
"Stunned": "Stunned",
"Supplied": "Supplied",
"Surged": "Surged",
"Taunt": "Taunt",
"Trance": "Trance",
"Unconscious": "Unconscious",
"Warded": "Warded",
"Wounded": "Wounded"
},
"Label": {
"agility": "Dexterity",
"gotoToken": "Go to token",
@@ -355,7 +497,7 @@
"rollProgressionDice": "Roll progression/Lethargy dice",
"earned": "Earned",
"divinityPoints": "Divinity points",
"aetherPoints": "Aether points",
"manaPoints": "Mana points",
"attacks": "Attacks",
"monster": "Monster",
"Resist" :"Resist",
@@ -380,6 +522,7 @@
"combatDetails": "Combat details",
"Challenges": "Challenges",
"HP": "HP",
"HPTemp": "Temporary Hit Points",
"Movement": "Movement",
"Saves": "Saves",
"app": "APP",
@@ -402,6 +545,11 @@
"damage": "Damage",
"description": "Description",
"details": "Details",
"effects": "Effects",
"source": "Source",
"temporary": "Temporary",
"passive": "Passive",
"inactive": "Inactive",
"dex": "DEX",
"equipment": "Equipment",
"experience": "Experience",
@@ -449,6 +597,10 @@
"skill": "Skill",
"skillBonus": "Skill bonus",
"skills": "Skills",
"sub-attribute": "Sub-Attribute",
"subattributes": "Sub-Attributes",
"subAttribute1": "Sub-Attribute 1",
"subAttribute2": "Sub-Attribute 2",
"spells": "Spells",
"str": "STR",
"titleChallenge": "Challenge",
@@ -521,7 +673,8 @@
"piercing": "Piercing (P)",
"bludgeoning": "Bludgeoning (B)",
"slashing": "Slashing (S)",
"isProjectile": "Is Projectile?",
"shortRange": "Short Range",
"longRange": "Long Range",
"range": "Range",
"reloadAPC": "Reload APC",
"bonuses": "Bonuses",
@@ -618,7 +771,13 @@
"spellcastingTypeMana": "Mana",
"spellcastingTypeFaith": "Faith",
"attributeBonuses": "Attribute Bonuses",
"classFeatures": "Class Features"
"classFeatures": "Class Features",
"advancement": "Advancement",
"addAdvancement": "Add Advancement",
"deleteAdvancement": "Delete Advancement",
"advancementName": "Advancement Name",
"noAdvancements": "No advancements defined for this level",
"toggleDescription": "Toggle Description"
},
"CoreSkill": {
"acrobatics": "Acrobatics",
@@ -677,9 +836,19 @@
"heavy": "Heavy Armor"
},
"WeaponGroup": {
"shortsword": "Shortsword",
"longsword": "Longsword",
"warhammer": "Warhammer",
"greatsword": "Greatsword",
"handaxe": "Handaxe",
"battleaxe": "Battleaxe",
"greataxe": "Greataxe",
"club": "Club",
"mace": "Mace",
"greatMaul": "Great Maul",
"javelin": "Javelin",
"spear": "Spear",
"longSpear": "Long Spear",
"warhammer": "Warhammer",
"dagger": "Dagger",
"crossbow": "Crossbow",
"longbow": "Longbow"
@@ -723,12 +892,17 @@
"coreSkills": "List of all 18 available Core Skills in Prism RPG",
"advancedChecks": "Advanced checks are only available for your Core Skill",
"addEquipment": "New equipment",
"addSpell": "New spells",
"addSpell": "Add new spell",
"addMiracle": "Add new miracle",
"skill": "Skills list",
"skills": "Skills - Your character's skills and abilities",
"racialAbilities": "Racial Abilities from your character's race and sub-race"
},
"RollSavingThrow": "Roll Saving Throw",
"Message": {
"selectCoreSkill": "You must select a Core Skill for your character. Each character chooses one Core Skill at creation."
"selectCoreSkill": "You must select a Core Skill for your character. Each character chooses one Core Skill at creation.",
"dropRace": "Drag and drop a Race item here",
"dropClass": "Drag and drop a Class item here"
},
"Miracle": {
"FIELDS": {
@@ -797,11 +971,11 @@
}
},
"Money": {
"Coppers": "Copper",
"Golds": "Gold",
"Platinums": "Platinum",
"Silvers": "Silver",
"Tinbits": "Tin Bits"
"CopperCoin": "Copper Coin",
"SilverCoin": "Silver Coin",
"GoldCoin": "Gold Coin",
"Note": "Note",
"Steam": "Steam"
},
"Notifications": {
"rollFromWeapon": "Roll from an equipped weapon",
@@ -827,7 +1001,7 @@
"save": "Save roll {save}",
"success": "Success",
"visibility": "Visibility",
"favorDisfavor": "Favor/Disfavor"
"advantageDisadvantage": "Advantage/Disadvantage"
},
"Save": {
"FIELDS": {
@@ -1027,10 +1201,10 @@
"Warning": {},
"Weapon": {
"Type": {
"light": "Light Weapon",
"oneHanded": "One-Handed Weapon",
"heavy": "Heavy Weapon",
"projectile": "Projectile Weapon"
"light": "Light",
"oneHanded": "One-Handed",
"heavy": "Heavy",
"projectile": "Projectile"
},
"Group": {
"longsword": "Longsword",
@@ -1040,6 +1214,42 @@
"crossbow": "Crossbow",
"longbow": "Longbow"
},
"Passive": {
"quickBlade": "Quick Blade",
"turningEdge": "Turning Edge",
"cleave": "Cleave",
"throwingAxe": "Throwing Axe",
"shieldEater": "Shield Eater",
"devastatingBlow": "Devastating Blow",
"stun": "Stun",
"armorBreaker": "Armor Breaker",
"earthshatter": "Earthshatter",
"piercingThrow": "Piercing Throw",
"reach": "Reach",
"extendedReach": "Extended Reach",
"puncturingBlows": "Puncturing Blows",
"balancingStance": "Balancing Stance",
"boltlock": "Boltlock",
"volleyFire": "Volley Fire"
},
"PassiveDescription": {
"quickBlade": "Your attacks with shortswords cost 1 less APC (minimum 1).",
"turningEdge": "When you successfully parry an attack, you may immediately make a free attack against the attacker.",
"cleave": "When you kill an enemy, you may make a free attack against an adjacent enemy.",
"throwingAxe": "Handaxes can be thrown with a range of 20/60 feet.",
"shieldEater": "Your attacks ignore shield bonuses to defense.",
"devastatingBlow": "Your critical hits deal maximum damage instead of rolling.",
"stun": "When you hit with a club, the target must make a CON save or be stunned until the end of their next turn.",
"armorBreaker": "Your attacks ignore 3 points of armor.",
"earthshatter": "When you hit with a great maul, all enemies within 5 feet must make a DEX save or be knocked prone.",
"piercingThrow": "Your javelin throws ignore cover and deal +2 damage.",
"reach": "Your spear attacks have 10 feet of reach.",
"extendedReach": "Your long spear attacks have 15 feet of reach.",
"puncturingBlows": "Your attacks ignore 2 points of armor.",
"balancingStance": "You gain +1 to defense when wielding a dagger.",
"boltlock": "Reloading costs 0 APC instead of the normal reload cost.",
"volleyFire": "You may attack two targets with a single attack action."
},
"DamageType": {
"piercing": "Piercing",
"bludgeoning": "Bludgeoning",
@@ -1224,6 +1434,56 @@
"label": "Class Features"
}
}
},
"Settings": {
"customWeaponTypes": {
"name": "Custom Weapon Types",
"hint": "Custom weapon types configured for this world"
},
"customWeaponGroups": {
"name": "Custom Weapon Groups",
"hint": "Custom weapon groups configured for this world"
},
"weaponTypesConfig": {
"name": "Weapon Types Configuration",
"hint": "Configure weapon types and weapon groups",
"label": "Configure Weapons",
"title": "Weapon Types & Groups Configuration"
},
"tabs": {
"weaponTypes": "Weapon Types",
"weaponGroups": "Weapon Groups"
},
"weaponTypes": {
"header": "Weapon Types"
},
"weaponType": {
"id": "ID",
"label": "Label",
"apc": "Action Point Cost",
"hands": "Hands Required"
},
"weaponGroups": {
"header": "Weapon Groups"
},
"weaponGroup": {
"id": "ID",
"label": "Label",
"passive": "Passive ID",
"passiveLabel": "Passive Label",
"passiveDescription": "Passive Description"
},
"addWeaponType": "Add Weapon Type",
"deleteWeaponType": "Delete Weapon Type",
"addWeaponGroup": "Add Weapon Group",
"deleteWeaponGroup": "Delete Weapon Group",
"resetDefaults": "Reset to Defaults",
"save": "Save Changes",
"weaponTypesSaved": "Weapon types and groups saved successfully",
"resetConfirm": {
"title": "Reset to Defaults?",
"content": "This will reset all weapon types and groups to their default values. Are you sure?"
}
}
},
"TYPES": {
@@ -1235,6 +1495,7 @@
"armor": "Armor",
"equipment": "Equipment",
"racial-ability": "Racial Ability",
"ability": "Ability",
"miracle": "Miracle",
"save": "Save",
"shield": "Shield",
@@ -1243,7 +1504,8 @@
"vulnerability": "Vulnerability",
"weapon": "Weapon",
"race": "Race",
"class": "Class"
"class": "Class",
"character-path": "Character Path"
}
}
}
+3
View File
@@ -3,6 +3,7 @@ export { default as PrismRPGMonsterSheet } from "./sheets/monster-sheet.mjs"
export { default as PrismRPGWeaponSheet } from "./sheets/weapon-sheet.mjs"
export { default as PrismRPGSkillSheet } from "./sheets/skill-sheet.mjs"
export { default as PrismRPGRacialAbilitySheet } from "./sheets/racial-ability-sheet.mjs"
export { default as PrismRPGAbilitySheet } from "./sheets/ability-sheet.mjs"
export { default as PrismRPGVulnerabilitySheet } from "./sheets/vulnerability-sheet.mjs"
export { default as PrismRPGArmorSheet } from "./sheets/armor-sheet.mjs"
export { default as PrismRPGSpellSheet } from "./sheets/spell-sheet.mjs"
@@ -11,3 +12,5 @@ export { default as PrismRPGShieldSheet } from "./sheets/shield-sheet.mjs"
export { default as PrismRPGMiracleSheet } from "./sheets/miracle-sheet.mjs"
export { default as PrismRPGRaceSheet } from "./sheets/race-sheet.mjs"
export { default as PrismRPGClassSheet } from "./sheets/class-sheet.mjs"
export { default as PrismRPGCharacterPathSheet } from "./sheets/character-path-sheet.mjs"
export { WeaponTypesConfig } from "./weapon-types-config.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 {
/**
* Return the Array of combatants sorted into initiative order, breaking ties alphabetically by name.
* @returns {Combatant[]}
*/
setupTurns() {
console?.log("Setup Turns....");
this.turns ||= [];
// Determine the turn order and the current turn
const turns = this.combatants.contents.sort(this.sortCombatantsLF);
if (this.turn !== null) this.turn = Math.clamp(this.turn, 0, turns.length - 1);
// Update state tracking
let c = turns[this.turn];
this.current = this._getCurrentState(c);
if (!this.previous) this.previous = this.current;
// Return the array of prepared turns
return this.turns = turns;
}
async rollInitiative(ids, options) {
console.log("%%%%%%%%% Roll Initiative", ids, options);
@@ -123,98 +25,5 @@ export class PrismRPGCombat extends Combat {
return this;
}
resetProgression(cId) {
let c = this.combatants.get(cId);
c.update({ 'system.progressionCount': 0 });
}
setCasting(cId) {
let c = this.combatants.get(cId);
c.setFlag(SYSTEM.id, "casting", true);
}
resetCasting(cId) {
let c = this.combatants.get(cId);
c.setFlag(SYSTEM.id, "casting", false);
}
isCasting(cId) {
let c = this.combatants.get(cId);
return c.getFlag(SYSTEM.id, "casting");
}
async nextTurn() {
console.log("NEXT TURN");
let turn = this.turn ?? -1;
let skipDefeated = this.settings.skipDefeated;
// Determine the next turn number
let next = null;
for (let [i, t] of this.turns.entries()) {
console.log("Turn", t);
if (i <= turn) continue;
if (skipDefeated && t.isDefeated) continue;
next = i;
break;
}
// Maybe advance to the next round
let round = this.round;
if ((this.round === 0) || (next === null) || (next >= this.turns.length)) {
return this.nextRound();
}
// Update the document, passing data through a hook first
const updateData = { round, turn: next };
const updateOptions = { advanceTime: CONFIG.time.turnTime, direction: 1 };
Hooks.callAll("combatTurn", this, updateData, updateOptions);
return this.update(updateData, updateOptions);
}
async nextRound() {
this.turnsDone = false
let turn = this.turn === null ? null : 0; // Preserve the fact that it's no-one's turn currently.
console.log("ROUND", this);
let advanceTime = Math.max(this.turns.length - this.turn, 0) * CONFIG.time.turnTime;
advanceTime += CONFIG.time.roundTime;
let nextRound = this.round + 1;
let initOK = true;
for (let c of this.combatants) {
if (c.initiative === null) {
initOK = false;
break;
}
}
if (!initOK) {
ui.notifications.error("All combatants must have initiative rolled before the round can advance.");
return this;
}
for (let c of this.combatants) {
if (nextRound >= c.initiative) {
let user = game.users.find(u => u.active && u.character && u.character.id === c.actor.id);
if (user?.hasPlayerOwner) {
game.socket.emit(`system.${SYSTEM.id}`, { type: "rollProgressionDice", progressionCount: c.system.progressionCount + 1, actorId: c.actor.id, combatId: this.id, combatantId: c.id });
} else {
user = game.users.find(u => u.active && u.isGM);
c.actor.system.rollProgressionDice(this.id, c.id);
}
}
}
// Update the document, passing data through a hook first
const updateData = { round: nextRound, turn };
const updateOptions = { advanceTime, direction: 1 };
Hooks.callAll("combatRound", this, updateData, updateOptions);
return this.update(updateData, updateOptions);
}
sortCombatantsLF(a, b) {
return a.initiative - b.initiative;
}
}
@@ -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
}
}
@@ -19,10 +19,32 @@ export default class PrismRPGArmorSheet extends PrismRPGItemSheet {
},
}
/** @override */
tabGroups = {
primary: "details",
}
/**
* Prepare an array of form header tabs.
* @returns {Record<string, Partial<ApplicationTab>>}
*/
#getTabs() {
const tabs = {
details: { id: "details", group: "primary", label: "PRISMRPG.Label.details" },
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()
context.enrichedDescription = await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.document.system.description, { async: true })
context.enrichedPassiveDescription = await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.document.system.passiveDescription, { async: true })
context.enrichedAugmentDescription = await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.document.system.augmentDescription, { async: true })
@@ -31,6 +31,9 @@ export default class PrismRPGItemSheet extends HandlebarsApplicationMixin(foundr
actions: {
toggleSheet: PrismRPGItemSheet.#onToggleSheet,
editImage: PrismRPGItemSheet.#onEditImage,
"create-effect": PrismRPGItemSheet.#onCreateActiveEffect,
"effect-edit": PrismRPGItemSheet.#onEffectEdit,
"effect-delete": PrismRPGItemSheet.#onEffectDelete,
},
}
@@ -65,6 +68,7 @@ export default class PrismRPGItemSheet extends HandlebarsApplicationMixin(foundr
context.system = this.document.system
context.source = this.document.toObject()
context.enrichedDescription = await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.document.system.description, { async: true })
context.effectCategories = this.#prepareActiveEffectCategories(this.document.effects)
context.isEditMode = this.isEditMode
context.isPlayMode = this.isPlayMode
context.isEditable = this.isEditable
@@ -75,6 +79,20 @@ export default class PrismRPGItemSheet extends HandlebarsApplicationMixin(foundr
_onRender(context, options) {
super._onRender(context, options)
this.#dragDrop.forEach((d) => d.bind(this.element))
// Activate tab navigation
const nav = this.element.querySelector('nav.tabs[data-group]')
if (nav) {
const group = nav.dataset.group
nav.querySelectorAll('[data-tab]').forEach(link => {
link.addEventListener('click', (event) => {
event.preventDefault()
const tab = event.currentTarget.dataset.tab
this.tabGroups[group] = tab
this.render()
})
})
}
}
// #region Drag-and-Drop Workflow
@@ -189,5 +207,99 @@ export default class PrismRPGItemSheet extends HandlebarsApplicationMixin(foundr
})
return fp.browse()
}
/**
* Prepare Active Effects organized by category (temporary, passive, inactive).
* @param {ActiveEffect[]} effects The raw Active Effects collection
* @returns {object} The categorized effects
* @private
*/
#prepareActiveEffectCategories(effects) {
// Define effect header categories
const categories = {
temporary: {
type: "temporary",
label: game.i18n.localize("PRISMRPG.Label.temporary"),
effects: [],
},
passive: {
type: "passive",
label: game.i18n.localize("PRISMRPG.Label.passive"),
effects: [],
},
inactive: {
type: "inactive",
label: game.i18n.localize("PRISMRPG.Label.inactive"),
effects: [],
},
}
// Iterate over active effects, classifying them into categories
for (let e of effects) {
const effect = e.toObject()
if (e.disabled) categories.inactive.effects.push(effect)
else if (e.isTemporary) categories.temporary.effects.push(effect)
else categories.passive.effects.push(effect)
}
return categories
}
/**
* Handle creating a new Active Effect on the Item.
* @param {Event} event The initiating click event.
* @param {HTMLElement} target The current target of the event listener.
* @private
*/
static async #onCreateActiveEffect(event, target) {
const effectType = target.dataset.effectType
let durationValue = undefined
let disabled = false
if (effectType === "temporary") {
durationValue = 10
}
if (effectType === "inactive") {
disabled = true
}
const effectData = {
name: game.i18n.format("DOCUMENT.New", { type: game.i18n.localize("DOCUMENT.ActiveEffect") }),
img: "icons/svg/aura.svg",
origin: this.document.uuid,
disabled: disabled,
changes: [],
duration: durationValue !== undefined ? { rounds: durationValue } : {},
flags: {}
}
await this.document.createEmbeddedDocuments("ActiveEffect", [effectData])
}
/**
* Handle editing an Active Effect on the Item.
* @param {Event} event The initiating click event.
* @param {HTMLElement} target The current target of the event listener.
* @private
*/
static async #onEffectEdit(event, target) {
const li = target.closest(".item")
const effectId = li.dataset.itemId
const effect = this.document.effects.get(effectId)
if (!effect) return
effect.sheet.render(true)
}
/**
* Handle deleting an Active Effect from the Item.
* @param {Event} event The initiating click event.
* @param {HTMLElement} target The current target of the event listener.
* @private
*/
static async #onEffectDelete(event, target) {
const li = target.closest(".item")
const effectId = li.dataset.itemId
const effect = this.document.effects.get(effectId)
if (!effect) return
await effect.delete()
}
// #endregion
}
@@ -0,0 +1,50 @@
import PrismRPGItemSheet from "./base-item-sheet.mjs"
export default class PrismRPGCharacterPathSheet extends PrismRPGItemSheet {
/** @override */
static DEFAULT_OPTIONS = {
classes: ["character-path"],
position: {
width: 600,
},
window: {
contentClasses: ["character-path-content"],
},
}
/** @override */
static PARTS = {
main: {
template: "systems/fvtt-prism-rpg/templates/character-path.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
}
}
+235 -62
View File
@@ -1,12 +1,13 @@
import PrismRPGActorSheet from "./base-actor-sheet.mjs"
import PrismRPGRoll from "../../documents/roll.mjs"
import { SYSTEM } from "../../config/system.mjs"
export default class PrismRPGCharacterSheet extends PrismRPGActorSheet {
/** @override */
static DEFAULT_OPTIONS = {
classes: ["character"],
position: {
width: 972,
width: 780,
height: 780,
},
window: {
@@ -14,14 +15,20 @@ export default class PrismRPGCharacterSheet extends PrismRPGActorSheet {
},
actions: {
createEquipment: PrismRPGCharacterSheet.#onCreateEquipment,
rangedAttackDefense: PrismRPGCharacterSheet.#onRangedAttackDefense,
rollInitiative: PrismRPGCharacterSheet.#onRollInitiative,
armorHitPointsPlus: PrismRPGCharacterSheet.#onArmorHitPointsPlus,
armorHitPointsMinus: PrismRPGCharacterSheet.#onArmorHitPointsMinus,
divinityPointsPlus: PrismRPGCharacterSheet.#onDivinityPointsPlus,
divinityPointsMinus: PrismRPGCharacterSheet.#onDivinityPointsMinus,
aetherPointsPlus: PrismRPGCharacterSheet.#onAetherPointsPlus,
aetherPointsMinus: PrismRPGCharacterSheet.#onAetherPointsMinus,
armorPointsPlus: PrismRPGCharacterSheet.#onArmorPointsPlus,
armorPointsMinus: PrismRPGCharacterSheet.#onArmorPointsMinus,
actionPointsPlus: PrismRPGCharacterSheet.#onActionPointsPlus,
actionPointsMinus: PrismRPGCharacterSheet.#onActionPointsMinus,
manaPointsPlus: PrismRPGCharacterSheet.#onManaPointsPlus,
manaPointsMinus: PrismRPGCharacterSheet.#onManaPointsMinus,
hpPlus: PrismRPGCharacterSheet.#onHpPlus,
hpMinus: PrismRPGCharacterSheet.#onHpMinus,
hpTempPlus: PrismRPGCharacterSheet.#onHpTempPlus,
hpTempMinus: PrismRPGCharacterSheet.#onHpTempMinus,
postItemToChat: PrismRPGCharacterSheet.#onPostItemToChat,
},
}
@@ -36,6 +43,9 @@ export default class PrismRPGCharacterSheet extends PrismRPGActorSheet {
skills: {
template: "systems/fvtt-prism-rpg/templates/character-skills.hbs",
},
subattributes: {
template: "systems/fvtt-prism-rpg/templates/character-subattributes.hbs",
},
combat: {
template: "systems/fvtt-prism-rpg/templates/character-combat.hbs",
},
@@ -45,11 +55,6 @@ export default class PrismRPGCharacterSheet extends PrismRPGActorSheet {
spells: {
template: "systems/fvtt-prism-rpg/templates/character-spells.hbs",
},
/* Miracles disabled - Legacy from Lethal Fantasy
miracles: {
template: "systems/fvtt-prism-rpg/templates/character-miracles.hbs",
},
*/
biography: {
template: "systems/fvtt-prism-rpg/templates/character-biography.hbs",
},
@@ -67,6 +72,7 @@ export default class PrismRPGCharacterSheet extends PrismRPGActorSheet {
#getTabs() {
let tabs = {
skills: { id: "skills", group: "sheet", icon: "fa-solid fa-shapes", label: "PRISMRPG.Label.skills" },
subattributes: { id: "subattributes", group: "sheet", icon: "fa-solid fa-diagram-project", label: "PRISMRPG.Label.subattributes" },
combat: { id: "combat", group: "sheet", icon: "fa-solid fa-swords", label: "PRISMRPG.Label.combat" },
equipment: { id: "equipment", group: "sheet", icon: "fa-solid fa-backpack", label: "PRISMRPG.Label.equipment" },
biography: { id: "biography", group: "sheet", icon: "fa-solid fa-book", label: "PRISMRPG.Label.biography" },
@@ -74,11 +80,6 @@ export default class PrismRPGCharacterSheet extends PrismRPGActorSheet {
if (this.actor.system.biodata.magicUser) {
tabs.spells = { id: "spells", group: "sheet", icon: "fa-sharp-duotone fa-solid fa-wand-magic-sparkles", label: "PRISMRPG.Label.spells" }
}
/* Miracles disabled - Legacy from Lethal Fantasy
if (this.actor.system.biodata.clericUser) {
tabs.miracles = { id: "miracles", group: "sheet", icon: "fa-sharp-duotone fa-solid fa-hands-praying", label: "PRISMRPG.Label.miracles" }
}
*/
for (const v of Object.values(tabs)) {
v.active = this.tabGroups[v.group] === v.id
v.cssClass = v.active ? "active" : ""
@@ -90,6 +91,7 @@ export default class PrismRPGCharacterSheet extends PrismRPGActorSheet {
async _prepareContext() {
const context = await super._prepareContext()
context.tabs = this.#getTabs()
context.config = SYSTEM
return context
}
@@ -99,6 +101,14 @@ export default class PrismRPGCharacterSheet extends PrismRPGActorSheet {
const doc = this.document
switch (partId) {
case "main":
context.race = doc.itemTypes.race?.[0] || null
const classes = doc.itemTypes.class || []
// Create 3 class slots
context.classSlots = [
classes[0] || null,
classes[1] || null,
classes[2] || null
]
break
case "skills":
context.tab = context.tabs.skills
@@ -106,18 +116,14 @@ export default class PrismRPGCharacterSheet extends PrismRPGActorSheet {
context.racialAbilities = doc.itemTypes["racial-ability"]
context.vulnerabilities = doc.itemTypes.vulnerability
break
case "subattributes":
context.tab = context.tabs.subattributes
break
case "spells":
context.tab = context.tabs.spells
context.spells = doc.itemTypes.spell
context.hasSpells = context.spells.length > 0
break
/* Miracles disabled - Legacy from Lethal Fantasy
case "miracles":
context.tab = context.tabs.miracles
context.miracles = doc.itemTypes.miracle
context.hasMiracles = context.miracles.length > 0
break
*/
case "combat":
context.tab = context.tabs.combat
context.weapons = doc.itemTypes.weapon
@@ -155,64 +161,229 @@ export default class PrismRPGCharacterSheet extends PrismRPGActorSheet {
}
}
static async #onRangedAttackDefense(event, target) {
// Future use : const hasTarget = false
let roll = await PrismRPGRoll.promptRangedDefense({
actorId: this.actor.id,
actorName: this.actor.name,
actorImage: this.actor.img,
})
if (!roll) return null
await roll.toMessage({}, { rollMode: roll.options.rollMode })
}
static async #onRollInitiative(event, target) {
await this.document.system.rollInitiative()
}
static #onArmorHitPointsPlus(event, target) {
static async #onArmorHitPointsPlus(event, target) {
let armorHP = this.actor.system.combat.armorHitPoints
armorHP += 1
this.actor.update({ "system.combat.armorHitPoints": armorHP })
}
static #onArmorHitPointsMinus(event, target) {
static async #onArmorHitPointsMinus(event, target) {
let armorHP = this.actor.system.combat.armorHitPoints
armorHP -= 1
this.actor.update({ "system.combat.armorHitPoints": Math.max(armorHP, 0) })
}
static #onDivinityPointsPlus(event, target) {
let points = this.actor.system.divinityPoints.value
points += 1
points = Math.min(points, this.actor.system.divinityPoints.max)
this.actor.update({ "system.divinityPoints.value": points })
static async #onManaPointsPlus(event, target) {
let mana = this.actor.system.manaPoints.value
mana += 1
this.actor.update({ "system.manaPoints.value": Math.min(mana, this.actor.system.manaPoints.max) })
}
static #onDivinityPointsMinus(event, target) {
let points = this.actor.system.divinityPoints.value
points -= 1
points = Math.max(points, 0)
this.actor.update({ "system.divinityPoints.value": points })
static async #onManaPointsMinus(event, target) {
let mana = this.actor.system.manaPoints.value
mana -= 1
this.actor.update({ "system.manaPoints.value": Math.max(mana, 0) })
}
static #onAetherPointsPlus(event, target) {
let points = this.actor.system.aetherPoints.value
points += 1
points = Math.min(points, this.actor.system.aetherPoints.max)
this.actor.update({ "system.aetherPoints.value": points })
static async #onArmorPointsPlus(event, target) {
let armor = this.actor.system.armorPoints.value
armor += 1
this.actor.update({ "system.armorPoints.value": Math.min(armor, this.actor.system.armorPoints.max) })
}
static #onAetherPointsMinus(event, target) {
let points = this.actor.system.aetherPoints.value
points -= 1
points = Math.max(points, 0)
this.actor.update({ "system.aetherPoints.value": points })
static async #onArmorPointsMinus(event, target) {
let armor = this.actor.system.armorPoints.value
armor -= 1
this.actor.update({ "system.armorPoints.value": Math.max(armor, 0) })
}
static #onCreateEquipment(event, target) {
static async#onActionPointsPlus(event, target) {
let actionPoints = this.actor.system.actionPoints.value
actionPoints += 1
this.actor.update({ "system.actionPoints.value": Math.min(actionPoints, this.actor.system.actionPoints.max) })
}
static async#onActionPointsMinus(event, target) {
let actionPoints = this.actor.system.actionPoints.value
actionPoints -= 1
this.actor.update({ "system.actionPoints.value": Math.max(actionPoints, 0) })
}
static async#onHpPlus(event, target) {
let hp = this.actor.system.hp.value
hp += 1
this.actor.update({ "system.hp.value": Math.min(hp, this.actor.system.hp.max) })
}
static async#onHpMinus(event, target) {
let hp = this.actor.system.hp.value
hp -= 1
this.actor.update({ "system.hp.value": Math.max(hp, 0) })
}
static async#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) {
@@ -252,9 +423,11 @@ export default class PrismRPGCharacterSheet extends PrismRPGActorSheet {
async _onRoll(event, target) {
if (this.isEditMode) return
const rollType = event.target.dataset.rollType
let rollKey = event.target.dataset.rollKey;
let rollDice = event.target.dataset?.rollDice;
// Use closest to find the rollable element in case user clicked on a child
const rollableElement = event.target.closest('.rollable') || event.target
const rollType = rollableElement.dataset.rollType
let rollKey = rollableElement.dataset.rollKey;
let rollDice = rollableElement.dataset?.rollDice;
this.actor.prepareRoll(rollType, rollKey, rollDice)
+159
View File
@@ -20,9 +20,32 @@ export default class PrismRPGClassSheet extends PrismRPGItemSheet {
},
}
/** @override */
tabGroups = {
primary: "details",
}
/**
* Prepare an array of form header tabs.
* @returns {Record<string, Partial<ApplicationTab>>}
*/
#getTabs() {
const tabs = {
details: { id: "details", group: "primary", label: "PRISMRPG.Label.details" },
advancements: { id: "advancements", group: "primary", label: "PRISMRPG.Label.advancement" },
description: { id: "description", group: "primary", label: "PRISMRPG.Label.description" },
}
for (const v of Object.values(tabs)) {
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()
context.enrichedDescription = await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.document.system.description, { async: true })
context.enrichedAttributeBonuses = await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.document.system.attributeBonuses, { async: true })
context.enrichedNotes = await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.document.system.notes, { async: true })
@@ -34,6 +57,142 @@ export default class PrismRPGClassSheet extends PrismRPGItemSheet {
context.enrichedFeatures[key] = await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.document.system.features[key], { async: true })
}
// Enrich all advancement descriptions
context.enrichedAdvancements = {}
context.advancementsByLevel = []
for (let i = 1; i <= 10; i++) {
const key = `level${i}`
const advancements = this.document.system.advancements[key] || []
context.enrichedAdvancements[key] = []
const enrichedAdvancementsList = []
for (let j = 0; j < advancements.length; j++) {
const enrichedDesc = await foundry.applications.ux.TextEditor.implementation.enrichHTML(advancements[j].description, { async: true })
const enrichedAdv = {
...advancements[j],
enrichedDescription: enrichedDesc,
index: j,
levelKey: key
}
context.enrichedAdvancements[key].push(enrichedAdv)
enrichedAdvancementsList.push(enrichedAdv)
}
context.advancementsByLevel.push({
level: i,
levelKey: key,
advancements: enrichedAdvancementsList
})
}
return context
}
/** @override */
_onRender(context, options) {
super._onRender(context, options)
// Add advancement button listeners
this.element.querySelectorAll(".add-advancement").forEach(btn => {
btn.addEventListener("click", this._onAddAdvancement.bind(this))
})
// Delete advancement button listeners
this.element.querySelectorAll(".delete-advancement").forEach(btn => {
btn.addEventListener("click", this._onDeleteAdvancement.bind(this))
})
// Edit advancement icon listeners
this.element.querySelectorAll(".advancement-icon").forEach(img => {
img.addEventListener("click", this._onEditAdvancementIcon.bind(this))
})
// Toggle advancement description listeners
this.element.querySelectorAll(".toggle-advancement-description").forEach(btn => {
btn.addEventListener("click", this._onToggleAdvancementDescription.bind(this))
})
}
/**
* Handle toggling advancement description visibility
* @param {Event} event
*/
_onToggleAdvancementDescription(event) {
event.preventDefault()
const button = event.currentTarget
const item = button.closest(".advancement-item")
const description = item.querySelector(".advancement-description")
const icon = button.querySelector("i")
description.classList.toggle("collapsed")
if (description.classList.contains("collapsed")) {
icon.classList.remove("fa-chevron-up")
icon.classList.add("fa-chevron-down")
} else {
icon.classList.remove("fa-chevron-down")
icon.classList.add("fa-chevron-up")
}
}
/**
* Handle adding a new advancement to a level
* @param {Event} event
*/
async _onAddAdvancement(event) {
event.preventDefault()
const level = event.currentTarget.dataset.level
const advancements = foundry.utils.deepClone(this.document.system.advancements[level] || [])
advancements.push({
icon: "systems/fvtt-prism-rpg/assets/icons/advancement.svg",
name: "",
description: ""
})
await this.document.update({
[`system.advancements.${level}`]: advancements
})
}
/**
* Handle deleting an advancement from a level
* @param {Event} event
*/
async _onDeleteAdvancement(event) {
event.preventDefault()
const level = event.currentTarget.dataset.level
const index = parseInt(event.currentTarget.dataset.index)
const advancements = foundry.utils.deepClone(this.document.system.advancements[level] || [])
advancements.splice(index, 1)
await this.document.update({
[`system.advancements.${level}`]: advancements
})
}
/**
* Handle editing an advancement icon
* @param {Event} event
*/
async _onEditAdvancementIcon(event) {
event.preventDefault()
const level = event.currentTarget.dataset.level
const index = parseInt(event.currentTarget.dataset.index)
const fp = new FilePicker({
type: "image",
current: this.document.system.advancements[level][index].icon,
callback: async (path) => {
const advancements = foundry.utils.deepClone(this.document.system.advancements[level] || [])
advancements[index].icon = path
await this.document.update({
[`system.advancements.${level}`]: advancements
})
}
})
fp.render(true)
}
}
@@ -19,9 +19,32 @@ export default class PrismRPGEquipmentSheet extends PrismRPGItemSheet {
},
}
/** @override */
tabGroups = {
primary: "details",
}
/**
* Prepare an array of form header tabs.
* @returns {Record<string, Partial<ApplicationTab>>}
*/
#getTabs() {
const tabs = {
details: { id: "details", group: "primary", label: "PRISMRPG.Label.details" },
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()
context.enrichedDescription = await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.document.system.description, { async: true })
// Enrich passive description if equipment is a kit
@@ -19,9 +19,32 @@ export default class PrismRPGMiracleSheet extends PrismRPGItemSheet {
},
}
/** @override */
tabGroups = {
primary: "details",
}
/**
* Prepare an array of form header tabs.
* @returns {Record<string, Partial<ApplicationTab>>}
*/
#getTabs() {
const tabs = {
details: { id: "details", group: "primary", label: "PRISMRPG.Label.details" },
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()
context.enrichedDescription = await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.document.system.description, { async: true })
return context
}
+5 -3
View File
@@ -162,9 +162,11 @@ export default class PrismRPGMonsterSheet extends PrismRPGActorSheet {
async _onRoll(event, target) {
if (this.isEditMode) return
const rollType = event.target.dataset.rollType
let rollKey = event.target.dataset.rollKey
let rollDice = event.target.dataset?.rollDice || "0"
// Use closest to find the rollable element in case user clicked on a child
const rollableElement = event.target.closest('.rollable') || event.target
const rollType = rollableElement.dataset.rollType
let rollKey = rollableElement.dataset.rollKey
let rollDice = rollableElement.dataset?.rollDice || "0"
this.actor.system.prepareMonsterRoll(rollType, rollKey, rollDice)
}
}
+22
View File
@@ -19,9 +19,31 @@ export default class PrismRPGRaceSheet extends PrismRPGItemSheet {
},
}
/** @override */
tabGroups = {
primary: "details",
}
/**
* Prepare an array of form header tabs.
* @returns {Record<string, Partial<ApplicationTab>>}
*/
#getTabs() {
const tabs = {
details: { id: "details", group: "primary", label: "PRISMRPG.Label.details" },
description: { id: "description", group: "primary", label: "PRISMRPG.Label.description" },
}
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()
context.enrichedDescription = await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.document.system.description, { async: true })
context.enrichedRacialPassiveDescription = await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.document.system.racialPassiveDescription, { async: true })
context.enrichedSubraceAbilityDescription = await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.document.system.subraceAbilityDescription, { async: true })
@@ -19,4 +19,32 @@ export default class PrismRPGRacialAbilitySheet extends PrismRPGItemSheet {
},
}
/** @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
}
}
@@ -19,9 +19,32 @@ export default class PrismRPGShieldSheet extends PrismRPGItemSheet {
},
}
/** @override */
tabGroups = {
primary: "details",
}
/**
* Prepare an array of form header tabs.
* @returns {Record<string, Partial<ApplicationTab>>}
*/
#getTabs() {
const tabs = {
details: { id: "details", group: "primary", label: "PRISMRPG.Label.details" },
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()
context.enrichedDescription = await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.document.system.description, { async: true })
context.enrichedBlockAugmentDescription = await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.document.system.blockAugmentDescription, { async: true })
return context
@@ -19,9 +19,32 @@ export default class PrismRPGSpellSheet extends PrismRPGItemSheet {
},
}
/** @override */
tabGroups = {
primary: "details",
}
/**
* Prepare an array of form header tabs.
* @returns {Record<string, Partial<ApplicationTab>>}
*/
#getTabs() {
const tabs = {
details: { id: "details", group: "primary", label: "PRISMRPG.Label.details" },
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()
context.enrichedDescription = await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.document.system.description, { async: true })
context.enrichedColorEffect = await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.document.system.colorEffect, { async: true })
context.enrichedAscensionEffect = await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.document.system.ascensionEffect, { async: true })
@@ -19,9 +19,32 @@ export default class PrismRPGWeaponSheet extends PrismRPGItemSheet {
},
}
/** @override */
tabGroups = {
primary: "details",
}
/**
* Prepare an array of form header tabs.
* @returns {Record<string, Partial<ApplicationTab>>}
*/
#getTabs() {
const tabs = {
details: { id: "details", group: "primary", label: "PRISMRPG.Label.details" },
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()
context.enrichedDescription = await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.document.system.description, { async: true })
// Enrich descriptions for all passives
+219
View File
@@ -0,0 +1,219 @@
/**
* Application to configure weapon types and groups
*/
import { getWeaponTypes, getWeaponGroups } from "../config/weapon.mjs"
export class WeaponTypesConfig extends FormApplication {
constructor(object, options) {
super(object, options)
// Store working copies that won't trigger settings changes
this.workingTypes = null
this.workingGroups = null
}
static get defaultOptions() {
return foundry.utils.mergeObject(super.defaultOptions, {
title: game.i18n.localize("PRISMRPG.Settings.weaponTypesConfig.title"),
id: "weapon-types-config",
classes: ["prismrpg", "weapon-types-config"],
template: "systems/fvtt-prism-rpg/templates/weapon-types-config.hbs",
width: 1000,
height: "auto",
closeOnSubmit: true,
submitOnChange: false,
tabs: [{ navSelector: ".tabs", contentSelector: ".content", initial: "types" }]
})
}
getData() {
const data = super.getData()
// Get default weapon types from config with proper translation keys
const defaultTypes = getWeaponTypes()
// Get default weapon groups from config with proper translation keys
const defaultGroups = getWeaponGroups()
// Initialize working copies on first render
if (!this.workingTypes) {
const customTypes = game.settings.get("fvtt-prism-rpg", "customWeaponTypes") || {}
this.workingTypes = foundry.utils.deepClone(customTypes)
}
if (!this.workingGroups) {
const customGroups = game.settings.get("fvtt-prism-rpg", "customWeaponGroups") || {}
this.workingGroups = foundry.utils.deepClone(customGroups)
}
// Merge default and working copies
data.weaponTypes = {}
const mergedTypes = foundry.utils.mergeObject(defaultTypes, this.workingTypes, { inplace: false })
console.log("Merged types in getData:", mergedTypes)
for (const [key, type] of Object.entries(mergedTypes)) {
data.weaponTypes[key] = {
...type,
// Translate label if it's a translation key
label: type.label.startsWith("PRISMRPG.") ? game.i18n.localize(type.label) : type.label,
// Mark if it's a custom type (can be deleted)
isCustom: key.startsWith("custom_")
}
}
data.weaponGroups = {}
const mergedGroups = foundry.utils.mergeObject(defaultGroups, this.workingGroups, { inplace: false })
for (const [key, group] of Object.entries(mergedGroups)) {
data.weaponGroups[key] = {
...group,
// Translate labels if they're translation keys
label: group.label.startsWith("PRISMRPG.") ? game.i18n.localize(group.label) : group.label,
passiveLabel: group.passiveLabel.startsWith("PRISMRPG.") ? game.i18n.localize(group.passiveLabel) : group.passiveLabel,
passiveDescription: group.passiveDescription.startsWith("PRISMRPG.") ? game.i18n.localize(group.passiveDescription) : group.passiveDescription,
// Mark if it's a custom group (can be deleted)
isCustom: key.startsWith("custom_")
}
}
return data
}
activateListeners(html) {
super.activateListeners(html)
// Add new weapon type
html.find('[data-action="add-weapon-type"]').click(this._onAddWeaponType.bind(this))
// Delete weapon type
html.find('[data-action="delete-weapon-type"]').click(this._onDeleteWeaponType.bind(this))
// Add new weapon group
html.find('[data-action="add-weapon-group"]').click(this._onAddWeaponGroup.bind(this))
// Delete weapon group
html.find('[data-action="delete-weapon-group"]').click(this._onDeleteWeaponGroup.bind(this))
// Reset to defaults
html.find('[data-action="reset-defaults"]').click(this._onResetDefaults.bind(this))
console.log("Listeners activated, weapon types count:", html.find('.weapon-type-entry').length)
}
async _onAddWeaponType(event) {
event.preventDefault()
const newId = `custom_${foundry.utils.randomID()}`
// Add new empty type to working copy (no settings save)
this.workingTypes[newId] = {
id: newId,
label: "New Weapon Type",
apc: 1,
hands: 1
}
// Force re-render without saving
this.render(true)
}
async _onDeleteWeaponType(event) {
event.preventDefault()
const typeId = $(event.currentTarget).data('id')
console.log("Delete weapon type clicked:", typeId)
console.log("Working types before:", this.workingTypes)
// Delete from working copy (no settings save)
delete this.workingTypes[typeId]
console.log("Working types after:", this.workingTypes)
// Save to settings immediately so it persists
await game.settings.set("fvtt-prism-rpg", "customWeaponTypes", this.workingTypes)
// Find and remove the entry from DOM immediately
this.element.find(`.weapon-type-entry[data-id="${typeId}"]`).remove()
}
async _onAddWeaponGroup(event) {
event.preventDefault()
const newId = `custom_${foundry.utils.randomID()}`
// Add new empty group to working copy (no settings save)
this.workingGroups[newId] = {
id: newId,
label: "New Weapon Group",
passive: "newPassive",
passiveLabel: "New Passive",
passiveDescription: "Description of the new passive ability."
}
// Force re-render without saving
this.render(true)
}
async _onDeleteWeaponGroup(event) {
event.preventDefault()
const groupId = $(event.currentTarget).data('id')
// Delete from working copy (no settings save)
delete this.workingGroups[groupId]
// Save to settings immediately so it persists
await game.settings.set("fvtt-prism-rpg", "customWeaponGroups", this.workingGroups)
// Find and remove the entry from DOM immediately
this.element.find(`.weapon-group-entry[data-id="${groupId}"]`).remove()
}
async _onResetDefaults(event) {
event.preventDefault()
const confirm = await Dialog.confirm({
title: game.i18n.localize("PRISMRPG.Settings.resetConfirm.title"),
content: game.i18n.localize("PRISMRPG.Settings.resetConfirm.content"),
yes: () => true,
no: () => false
})
if (confirm) {
// Reset working copies
this.workingTypes = {}
this.workingGroups = {}
this.render(true)
}
}
async _updateObject(event, formData) {
const expanded = foundry.utils.expandObject(formData)
// Extract only custom types (those with custom_ prefix)
const customTypes = {}
if (expanded.weaponTypes) {
for (const [key, type] of Object.entries(expanded.weaponTypes)) {
if (key.startsWith("custom_")) {
customTypes[key] = type
}
}
}
// Extract only custom groups (those with custom_ prefix)
const customGroups = {}
if (expanded.weaponGroups) {
for (const [key, group] of Object.entries(expanded.weaponGroups)) {
if (key.startsWith("custom_")) {
customGroups[key] = group
}
}
}
// Save custom weapon types (this will trigger page reload)
await game.settings.set("fvtt-prism-rpg", "customWeaponTypes", customTypes)
// Save custom weapon groups (this will trigger page reload)
await game.settings.set("fvtt-prism-rpg", "customWeaponGroups", customGroups)
ui.notifications.info(game.i18n.localize("PRISMRPG.Settings.weaponTypesSaved"))
}
}
+18 -18
View File
@@ -135,28 +135,28 @@ export const CHALLENGES = Object.freeze({
})
export const SAVES = Object.freeze({
will: {
id: "will",
label: "PRISMRPG.Character.will.label"
str: {
id: "str",
label: "PRISMRPG.Character.str.label"
},
dodge: {
id: "dodge",
label: "PRISMRPG.Character.dodge.label"
dex: {
id: "dex",
label: "PRISMRPG.Character.dex.label"
},
toughness: {
id: "toughness",
label: "PRISMRPG.Character.toughness.label"
con: {
id: "con",
label: "PRISMRPG.Character.con.label"
},
contagion: {
id: "contagion",
label: "PRISMRPG.Character.contagion.label"
int: {
id: "int",
label: "PRISMRPG.Character.int.label"
},
poison: {
id: "poison",
label: "PRISMRPG.Character.poison.label"
wis: {
id: "wis",
label: "PRISMRPG.Character.wis.label"
},
pain: {
id: "pain",
label: "PRISMRPG.Character.pain.label"
cha: {
id: "cha",
label: "PRISMRPG.Character.cha.label"
}
})
+75 -75
View File
@@ -9,7 +9,7 @@ export const TABLES = {
"damage": -7,
"attack": -4,
"challenge": -9,
"aether_points": -20,
"mana_points": -20,
"hp": -3,
"encumbered": 1,
"lift": 3,
@@ -20,7 +20,7 @@ export const TABLES = {
"damage": -6,
"attack": -4,
"challenge": -8,
"aether_points": -20,
"mana_points": -20,
"hp": -2,
"encumbered": 1,
"lift": 4,
@@ -31,7 +31,7 @@ export const TABLES = {
"damage": -5,
"attack": -3,
"challenge": -7,
"aether_points": -20,
"mana_points": -20,
"hp": -1,
"encumbered": 1,
"lift": 5,
@@ -42,7 +42,7 @@ export const TABLES = {
"damage": -4,
"attack": -3,
"challenge": -6,
"aether_points": -20,
"mana_points": -20,
"hp": -1,
"encumbered": 2,
"lift": 6,
@@ -53,7 +53,7 @@ export const TABLES = {
"damage": -3,
"attack": -2,
"challenge": -5,
"aether_points": -20,
"mana_points": -20,
"hp": 0,
"encumbered": 2,
"lift": 7,
@@ -64,7 +64,7 @@ export const TABLES = {
"damage": -2,
"attack": -1,
"challenge": -4,
"aether_points": -10,
"mana_points": -10,
"hp": 0,
"encumbered": 3,
"lift": 8,
@@ -75,7 +75,7 @@ export const TABLES = {
"damage": -2,
"attack": 0,
"challenge": -3,
"aether_points": -10,
"mana_points": -10,
"hp": 0,
"encumbered": 3,
"lift": 9,
@@ -86,7 +86,7 @@ export const TABLES = {
"damage": -1,
"attack": 0,
"challenge": -2,
"aether_points": 0,
"mana_points": 0,
"hp": 0,
"encumbered": 4,
"lift": 11,
@@ -97,7 +97,7 @@ export const TABLES = {
"damage": -1,
"attack": 0,
"challenge": -1,
"aether_points": 0,
"mana_points": 0,
"hp": 0,
"encumbered": 5,
"lift": 12,
@@ -108,7 +108,7 @@ export const TABLES = {
"damage": 0,
"attack": 0,
"challenge": 0,
"aether_points": 0,
"mana_points": 0,
"hp": 0,
"encumbered": 6,
"lift": 13,
@@ -119,7 +119,7 @@ export const TABLES = {
"damage": 0,
"attack": 0,
"challenge": 0,
"aether_points": 0,
"mana_points": 0,
"hp": 0,
"encumbered": 7,
"lift": 15,
@@ -130,7 +130,7 @@ export const TABLES = {
"damage": 1,
"attack": 0,
"challenge": 1,
"aether_points": 0,
"mana_points": 0,
"hp": 0,
"encumbered": 8,
"lift": 17,
@@ -141,7 +141,7 @@ export const TABLES = {
"damage": 1,
"attack": 0,
"challenge": 2,
"aether_points": 0,
"mana_points": 0,
"hp": 0,
"encumbered": 9,
"lift": 20,
@@ -152,7 +152,7 @@ export const TABLES = {
"damage": 2,
"attack": 1,
"challenge": 3,
"aether_points": 0,
"mana_points": 0,
"hp": 1,
"encumbered": 10,
"lift": 22,
@@ -163,7 +163,7 @@ export const TABLES = {
"damage": 3,
"attack": 1,
"challenge": 4,
"aether_points": 0,
"mana_points": 0,
"hp": 2,
"encumbered": 11,
"lift": 24,
@@ -174,7 +174,7 @@ export const TABLES = {
"damage": 4,
"attack": 2,
"challenge": 5,
"aether_points": 0,
"mana_points": 0,
"hp": 3,
"encumbered": 12,
"lift": 26,
@@ -185,7 +185,7 @@ export const TABLES = {
"damage": 5,
"attack": 2,
"challenge": 6,
"aether_points": 10,
"mana_points": 10,
"hp": 4,
"encumbered": 13,
"lift": 28,
@@ -196,7 +196,7 @@ export const TABLES = {
"damage": 6,
"attack": 3,
"challenge": 7,
"aether_points": 20,
"mana_points": 20,
"hp": 5,
"encumbered": 14,
"lift": 30,
@@ -207,7 +207,7 @@ export const TABLES = {
"damage": 7,
"attack": 3,
"challenge": 8,
"aether_points": 20,
"mana_points": 20,
"hp": 6,
"encumbered": 15,
"lift": 31,
@@ -218,7 +218,7 @@ export const TABLES = {
"damage": 8,
"attack": 4,
"challenge": 9,
"aether_points": 30,
"mana_points": 30,
"hp": 7,
"encumbered": 15,
"lift": 32,
@@ -229,7 +229,7 @@ export const TABLES = {
"damage": 9,
"attack": 4,
"challenge": 10,
"aether_points": 30,
"mana_points": 30,
"hp": 8,
"encumbered": 16,
"lift": 33,
@@ -240,7 +240,7 @@ export const TABLES = {
"damage": 10,
"attack": 5,
"challenge": 11,
"aether_points": 40,
"mana_points": 40,
"hp": 9,
"encumbered": 16,
"lift": 34,
@@ -251,7 +251,7 @@ export const TABLES = {
"damage": 12,
"attack": 5,
"challenge": 12,
"aether_points": 40,
"mana_points": 40,
"hp": 10,
"encumbered": 17,
"lift": 35,
@@ -262,7 +262,7 @@ export const TABLES = {
"damage": 14,
"attack": 5,
"challenge": 13,
"aether_points": 50,
"mana_points": 50,
"hp": 11,
"encumbered": 18,
"lift": 36,
@@ -273,7 +273,7 @@ export const TABLES = {
"damage": 16,
"attack": 6,
"challenge": 14,
"aether_points": 60,
"mana_points": 60,
"hp": 12,
"encumbered": 19,
"lift": 38,
@@ -286,7 +286,7 @@ export const TABLES = {
"attack": -5,
"defense": -3,
"development_points": 0,
"aether": -50,
"mana": -50,
"spell_cognition": 0,
"arkane_casting_mod": -4
},
@@ -295,7 +295,7 @@ export const TABLES = {
"attack": -4,
"defense": -3,
"development_points": 0,
"aether": -50,
"mana": -50,
"spell_cognition": 0,
"arkane_casting_mod": -4
},
@@ -304,7 +304,7 @@ export const TABLES = {
"attack": -3,
"defense": -3,
"development_points": 0,
"aether": -50,
"mana": -50,
"spell_cognition": 0.01,
"arkane_casting_mod": -3
},
@@ -313,7 +313,7 @@ export const TABLES = {
"attack": -2,
"defense": -2,
"development_points": 0,
"aether": -45,
"mana": -45,
"spell_cognition": 0.05,
"arkane_casting_mod": -3
},
@@ -322,7 +322,7 @@ export const TABLES = {
"attack": -2,
"defense": -2,
"development_points": 0,
"aether": -45,
"mana": -45,
"spell_cognition": 0.1,
"arkane_casting_mod": -2
},
@@ -331,7 +331,7 @@ export const TABLES = {
"attack": -2,
"defense": -2,
"development_points": 0,
"aether": -40,
"mana": -40,
"spell_cognition": 0.15,
"arkane_casting_mod": -2
},
@@ -340,7 +340,7 @@ export const TABLES = {
"attack": -1,
"defense": -1,
"development_points": 0,
"aether": -40,
"mana": -40,
"spell_cognition": 0.2,
"arkane_casting_mod": -1
},
@@ -349,7 +349,7 @@ export const TABLES = {
"attack": -1,
"defense": 0,
"development_points": 0,
"aether": -30,
"mana": -30,
"spell_cognition": 0.25,
"arkane_casting_mod": -1
},
@@ -358,7 +358,7 @@ export const TABLES = {
"attack": -1,
"defense": 0,
"development_points": 0,
"aether": -30,
"mana": -30,
"spell_cognition": 0.3,
"arkane_casting_mod": 0
},
@@ -367,7 +367,7 @@ export const TABLES = {
"attack": 0,
"defense": 0,
"development_points": 0,
"aether": -20,
"mana": -20,
"spell_cognition": 0.35,
"arkane_casting_mod": 0
},
@@ -376,7 +376,7 @@ export const TABLES = {
"attack": 0,
"defense": 0,
"development_points": 1,
"aether": -10,
"mana": -10,
"spell_cognition": 0.45,
"arkane_casting_mod": 0
},
@@ -385,7 +385,7 @@ export const TABLES = {
"attack": 1,
"defense": 0,
"development_points": 2,
"aether": 0,
"mana": 0,
"spell_cognition": 0.5,
"arkane_casting_mod": 1
},
@@ -394,7 +394,7 @@ export const TABLES = {
"attack": 1,
"defense": 0,
"development_points": 3,
"aether": 0,
"mana": 0,
"spell_cognition": 0.6,
"arkane_casting_mod": 1
},
@@ -403,7 +403,7 @@ export const TABLES = {
"attack": 1,
"defense": 1,
"development_points": 4,
"aether": 10,
"mana": 10,
"spell_cognition": 0.65,
"arkane_casting_mod": 2
},
@@ -412,7 +412,7 @@ export const TABLES = {
"attack": 2,
"defense": 1,
"development_points": 5,
"aether": 20,
"mana": 20,
"spell_cognition": 0.75,
"arkane_casting_mod": 2
},
@@ -421,7 +421,7 @@ export const TABLES = {
"attack": 2,
"defense": 1,
"development_points": 7,
"aether": 30,
"mana": 30,
"spell_cognition": 0.8,
"arkane_casting_mod": 3
},
@@ -430,7 +430,7 @@ export const TABLES = {
"attack": 2,
"defense": 1,
"development_points": 9,
"aether": 40,
"mana": 40,
"spell_cognition": 0.85,
"arkane_casting_mod": 3
},
@@ -439,7 +439,7 @@ export const TABLES = {
"attack": 3,
"defense": 2,
"development_points": 11,
"aether": 50,
"mana": 50,
"spell_cognition": 0.9,
"arkane_casting_mod": 4
},
@@ -448,7 +448,7 @@ export const TABLES = {
"attack": 3,
"defense": 2,
"development_points": 13,
"aether": 60,
"mana": 60,
"spell_cognition": 0.92,
"arkane_casting_mod": 5
},
@@ -457,7 +457,7 @@ export const TABLES = {
"attack": 3,
"defense": 2,
"development_points": 15,
"aether": 70,
"mana": 70,
"spell_cognition": 0.94,
"arkane_casting_mod": 6
},
@@ -466,7 +466,7 @@ export const TABLES = {
"attack": 4,
"defense": 2,
"development_points": 18,
"aether": 80,
"mana": 80,
"spell_cognition": 0.95,
"arkane_casting_mod": 7
},
@@ -475,7 +475,7 @@ export const TABLES = {
"attack": 4,
"defense": 3,
"development_points": 21,
"aether": 90,
"mana": 90,
"spell_cognition": 0.96,
"arkane_casting_mod": 7
},
@@ -484,7 +484,7 @@ export const TABLES = {
"attack": 4,
"defense": 3,
"development_points": 24,
"aether": 100,
"mana": 100,
"spell_cognition": 0.97,
"arkane_casting_mod": 8
},
@@ -493,7 +493,7 @@ export const TABLES = {
"attack": 5,
"defense": 3,
"development_points": 27,
"aether": 110,
"mana": 110,
"spell_cognition": 0.98,
"arkane_casting_mod": 8
},
@@ -502,7 +502,7 @@ export const TABLES = {
"attack": 5,
"defense": 4,
"development_points": 30,
"aether": 125,
"mana": 125,
"spell_cognition": 0.99,
"arkane_casting_mod": 9
}
@@ -688,7 +688,7 @@ export const TABLES = {
{
"value": 1,
"hp ": 1,
"aether_points": -50,
"mana_points": -50,
"pain_save": 1,
"toughness_save": -5,
"stabilization_dice": "D6",
@@ -698,7 +698,7 @@ export const TABLES = {
"value": 2,
"hp ": 2,
"aether_points": -40,
"mana_points": -40,
"pain_save": 2,
"toughness_saave": -4,
"stabilization_dice": "D6",
@@ -708,7 +708,7 @@ export const TABLES = {
"value": 3,
"hp ": 3,
"aether_points": -35,
"mana_points": -35,
"pain_save": 2,
"toughness_save": -3,
"stabilization_dice": "D6",
@@ -718,7 +718,7 @@ export const TABLES = {
"value": 4,
"hp ": 4,
"aether_points": -30,
"mana_points": -30,
"pain_save": 2,
"toughness_save": -3,
"stabilization_dice": "D6",
@@ -728,7 +728,7 @@ export const TABLES = {
"value": 5,
"hp ": 5,
"aether_points": -25,
"mana_points": -25,
"pain_save": 3,
"toughness_save": -2,
"stabilization_dice": "D6",
@@ -737,7 +737,7 @@ export const TABLES = {
{
"value": 6,
"hp ": 6,
"aether_points": -20,
"mana_points": -20,
"pain_save": 3,
"toughness_save": -2,
"stabilization_dice": "D6",
@@ -746,7 +746,7 @@ export const TABLES = {
{
"value": 7,
"hp ": 7,
"aether_points": -15,
"mana_points": -15,
"pain_save": 3,
"toughness_save": -1,
"stabilization_dice": "D6",
@@ -756,7 +756,7 @@ export const TABLES = {
"value": 8,
"hp ": 8,
"aether_points": -10,
"mana_points": -10,
"pain_save": 4,
"toughness_save": -1,
"stabilization_dice": "D8",
@@ -766,7 +766,7 @@ export const TABLES = {
"value": 9,
"hp ": 9,
"aether_points": -5,
"mana_points": -5,
"pain_save": 4,
"toughness_save": 0,
"stabilization_dice": "D8",
@@ -776,7 +776,7 @@ export const TABLES = {
"value": 10,
"hp ": 10,
"aether_points": 0,
"mana_points": 0,
"pain_save": 5,
"toughness_save": 0,
"stabilization_dice": "D8",
@@ -786,7 +786,7 @@ export const TABLES = {
"value": 11,
"hp ": 11,
"aether_points": 0,
"mana_points": 0,
"pain_save": 5,
"toughness_save": 0,
"stabilization_dice": "D8",
@@ -796,7 +796,7 @@ export const TABLES = {
"value": 12,
"hp ": 12,
"aether_points": 5,
"mana_points": 5,
"pain_save": 6,
"toughness_save": 0,
"stabilization_dice": "D10",
@@ -806,7 +806,7 @@ export const TABLES = {
"value": 13,
"hp ": 13,
"aether_points": 10,
"mana_points": 10,
"pain_save": 7,
"toughness_save": 1,
"stabilization_dice": "D10",
@@ -816,7 +816,7 @@ export const TABLES = {
"value": 14,
"hp ": 14,
"aether_points": 20,
"mana_points": 20,
"pain_save": 7,
"toughness_save": 2,
"stabilization_dice": "D10",
@@ -826,7 +826,7 @@ export const TABLES = {
"value": 15,
"hp ": 15,
"aether_points": 30,
"mana_points": 30,
"pain_save": 8,
"toughness_save": 3,
"stabilization_dice": "D12",
@@ -836,7 +836,7 @@ export const TABLES = {
"value": 16,
"hp ": 16,
"aether_points": 40,
"mana_points": 40,
"pain_save": 8,
"toughness_save": 4,
"stabilization_dice": "D12",
@@ -846,7 +846,7 @@ export const TABLES = {
"value": 17,
"hp ": 17,
"aether_points": 50,
"mana_points": 50,
"pain_save": 9,
"toughness_save": 5,
"stabilization_dice": "D12",
@@ -856,7 +856,7 @@ export const TABLES = {
"value": 18,
"hp ": 18,
"aether_points": 60,
"mana_points": 60,
"pain_save": 9,
"toughness_save": 6,
"stabilization_dice": "D12",
@@ -866,7 +866,7 @@ export const TABLES = {
"value": 19,
"hp ": 19,
"aether_points": 70,
"mana_points": 70,
"pain_save": 10,
"toughness_save": 7,
"stabilization_dice": "D12",
@@ -876,7 +876,7 @@ export const TABLES = {
"value": 20,
"hp ": 20,
"aether_points": 80,
"mana_points": 80,
"pain_save": 10,
"toughness_save": 8,
"stabilization_dice": "D12",
@@ -886,7 +886,7 @@ export const TABLES = {
"value": 21,
"hp ": 21,
"aether_points": 90,
"mana_points": 90,
"pain_save": 11,
"toughness_save": 9,
"stabilization_dice": "D20",
@@ -896,7 +896,7 @@ export const TABLES = {
"value": 22,
"hp ": 22,
"aether_points": 100,
"mana_points": 100,
"pain_save": 11,
"toughness_save": 10,
"stabilization_dice": "D20",
@@ -906,7 +906,7 @@ export const TABLES = {
"value": 23,
"hp ": 23,
"aether_points": 110,
"mana_points": 110,
"pain_save": 12,
"toughness_save": 11,
"stabilization_dice": "D20",
@@ -916,7 +916,7 @@ export const TABLES = {
"value": 24,
"hp ": 24,
"aether_points": 120,
"mana_points": 120,
"pain_save": 12,
"toughness_save": 12,
"stabilization_dice": "D20",
@@ -925,7 +925,7 @@ export const TABLES = {
{
"value": 25,
"hp ": 25,
"aether_points": 130,
"mana_points": 130,
"pain_save": 13,
"toughness_save": 13,
"stabilization_dice": "D20",
+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" },
]
+50 -36
View File
@@ -14,35 +14,35 @@ export const SYSTEM_ID = "fvtt-prism-rpg"
export const DEV_MODE = false
export const MONEY = {
tinbit: {
id: "tinbit",
abbrev: "tb",
label: "PRISMRPG.Money.Tinbits",
coppercoin: {
id: "coppercoin",
abbrev: "cc",
label: "PRISMRPG.Money.CopperCoin",
valuetb: 1
},
copper: {
id: "copper",
abbrev: "cp",
label: "PRISMRPG.Money.Coppers",
silvercoin: {
id: "silvercoin",
abbrev: "sc",
label: "PRISMRPG.Money.SilverCoin",
valuetb: 5
},
goldcoin: {
id: "goldcoin",
abbrev: "gc",
label: "PRISMRPG.Money.GoldCoin",
valuetb: 10
},
silver: {
id: "silver",
abbrev: "sp",
label: "PRISMRPG.Money.Silvers",
note: {
id: "note",
abbrev: "nt",
label: "PRISMRPG.Money.Note",
valuetb: 100
},
gold: {
id: "gold",
abbrev: "gp",
label: "PRISMRPG.Money.Golds",
steam: {
id: "steam",
abbrev: "st",
label: "PRISMRPG.Money.Steam",
valuetb: 1000
},
platinum: {
id: "platinum",
abbrev: "pp",
label: "PRISMRPG.Money.Platinums",
valuetb: 10000
}
}
@@ -56,10 +56,10 @@ export const MORTAL_CHOICES = {
"halflings": { label: "Halfling", id: "halflings", defenseBonus: 2 }
}
export const FAVOR_CHOICES = {
export const ADVANTAGE_CHOICES = {
"none": { label: "None", value: "none" },
"favor": { label: "Favor", value: "favor" },
"disfavor": { label: "Disfavor", value: "disfavor" }
"advantage": { label: "Advantage", value: "advantage" },
"disadvantage": { label: "Disadvantage", value: "disadvantage" }
}
export const MOVEMENT_CHOICES = {
@@ -156,7 +156,7 @@ export const INITIATIVE_DICE_CHOICES_PER_CLASS = {
{ "name": "Aware and know exactly where the enemy is (1D4)", "value": "1D4" }*/
],
"magicuser": [
{ "name": "Sleeping to recover Aether Points (2D20)", "value": "2D20" },
{ "name": "Sleeping to recover Mana Points (2D20)", "value": "2D20" },
{ "name": "Asleep or totally distracted (1D20)", "value": "1D20" },
{ "name": "Awake but unsuspecting (1D12)", "value": "1D12" },
{ "name": "Declared Ready on Alert (1)", "value": "1" },
@@ -261,15 +261,29 @@ export const CHOICE_MODIFIERS = {
}
export const ASCII = `
······················································································································
: :
:@@@ @@@@@@@@ @@@@@@@ @@@ @@@ @@@@@@ @@@ @@@@@@@@ @@@@@@ @@@ @@@ @@@@@@@ @@@@@@ @@@@@@ @@@ @@@ :
:@@! @@! @!! @@! @@@ @@! @@@ @@! @@! @@! @@@ @@!@!@@@ @!! @@! @@@ !@@ @@! !@@ :
:@!! @!!!:! @!! @!@!@!@! @!@!@!@! @!! @!!!:! @!@!@!@! @!@@!!@! @!! @!@!@!@! !@@!! !@!@! :
:!!: !!: !!: !!: !!! !!: !!! !!: !!: !!: !!! !!: !!! !!: !!: !!! !:! !!: :
:: ::.: : : :: :: : : : : : : : : ::.: : : : : : :: : : : : : ::.: : .: :
: :
······················································································································
8888888b. 8888888b. 8888888 .d8888b. 888b d888
888 Y88b 888 Y88b 888 d88P Y88b 8888b d8888
888 888 888 888 888 Y88b. 88888b.d88888
888 d88P 888 d88P 888 "Y888b. 888Y88888P888
8888888P" 8888888P" 888 "Y88b. 888 Y888P 888
888 888 T88b 888 "888 888 Y8P 888
888 888 T88b 888 Y88b d88P 888 " 888
888 888 T88b 8888888 "Y8888P" 888 888
8888888b. 8888888b. .d8888b.
888 Y88b 888 Y88b d88P Y88b
888 888 888 888 888 888
888 d88P 888 d88P 888
8888888P" 8888888P" 888 88888
888 T88b 888 888 888
888 T88b 888 Y88b d88P
888 T88b 888 "Y8888P88
`
/**
@@ -327,7 +341,7 @@ export const SYSTEM = {
MOVE_DIRECTION_CHOICES,
SIZE_CHOICES,
RANGE_CHOICES,
FAVOR_CHOICES,
ADVANTAGE_CHOICES,
ATTACKER_AIM_CHOICES,
MORTAL_CHOICES,
MIRACLE_TYPES,
+180 -18
View File
@@ -1,8 +1,8 @@
/**
* Weapon types based on Prism RPG rules
* Default weapon types based on Prism RPG rules
* APC determines weapon class: Light (1 APC), One-Handed (2 APC), Heavy (3 APC)
*/
export const TYPE = Object.freeze({
const DEFAULT_TYPES = {
light: {
id: "light",
label: "PRISMRPG.Weapon.Type.light",
@@ -27,22 +27,75 @@ export const TYPE = Object.freeze({
apc: 0, // Variable based on specific weapon
hands: 2
}
};
/**
* Get weapon types (default + custom from settings)
*/
export function getWeaponTypes() {
if (!game?.settings) return DEFAULT_TYPES;
const customTypes = game.settings.get("fvtt-prism-rpg", "customWeaponTypes") || {};
return foundry.utils.mergeObject(DEFAULT_TYPES, customTypes, { inplace: false });
}
/**
* Weapon types (dynamically loaded)
*/
export const TYPE = new Proxy({}, {
get(target, prop) {
const types = getWeaponTypes();
return types[prop];
},
ownKeys(target) {
return Object.keys(getWeaponTypes());
},
getOwnPropertyDescriptor(target, prop) {
return {
enumerable: true,
configurable: true
};
}
});
/**
* Simplified Weapon Types object for form choices (label-only format)
*/
export const TYPE_CHOICES = Object.freeze(
Object.fromEntries(
Object.entries(TYPE).map(([key, value]) => [key, value.label])
)
export function getWeaponTypeChoices() {
const types = getWeaponTypes();
return Object.fromEntries(
Object.entries(types).map(([key, value]) => [key, value.label])
);
}
export const TYPE_CHOICES = new Proxy({}, {
get(target, prop) {
const choices = getWeaponTypeChoices();
return choices[prop];
},
ownKeys(target) {
return Object.keys(getWeaponTypeChoices());
},
getOwnPropertyDescriptor(target, prop) {
return {
enumerable: true,
configurable: true
};
}
});
/**
* Weapon groups and their associated passives
* Default weapon groups and their associated passives
* Each weapon belongs to a group and possesses its passive while wielded
*/
export const WEAPON_GROUP = Object.freeze({
const DEFAULT_WEAPON_GROUPS = {
shortsword: {
id: "shortsword",
label: "PRISMRPG.WeaponGroup.shortsword",
passive: "quickBlade",
passiveLabel: "PRISMRPG.Weapon.Passive.quickBlade",
passiveDescription: "PRISMRPG.Weapon.PassiveDescription.quickBlade"
},
longsword: {
id: "longsword",
label: "PRISMRPG.WeaponGroup.longsword",
@@ -50,12 +103,19 @@ export const WEAPON_GROUP = Object.freeze({
passiveLabel: "PRISMRPG.Weapon.Passive.turningEdge",
passiveDescription: "PRISMRPG.Weapon.PassiveDescription.turningEdge"
},
warhammer: {
id: "warhammer",
label: "PRISMRPG.WeaponGroup.warhammer",
passive: "puncturingBlows",
passiveLabel: "PRISMRPG.Weapon.Passive.puncturingBlows",
passiveDescription: "PRISMRPG.Weapon.PassiveDescription.puncturingBlows"
greatsword: {
id: "greatsword",
label: "PRISMRPG.WeaponGroup.greatsword",
passive: "cleave",
passiveLabel: "PRISMRPG.Weapon.Passive.cleave",
passiveDescription: "PRISMRPG.Weapon.PassiveDescription.cleave"
},
handaxe: {
id: "handaxe",
label: "PRISMRPG.WeaponGroup.handaxe",
passive: "throwingAxe",
passiveLabel: "PRISMRPG.Weapon.Passive.throwingAxe",
passiveDescription: "PRISMRPG.Weapon.PassiveDescription.throwingAxe"
},
battleaxe: {
id: "battleaxe",
@@ -64,6 +124,62 @@ export const WEAPON_GROUP = Object.freeze({
passiveLabel: "PRISMRPG.Weapon.Passive.shieldEater",
passiveDescription: "PRISMRPG.Weapon.PassiveDescription.shieldEater"
},
greataxe: {
id: "greataxe",
label: "PRISMRPG.WeaponGroup.greataxe",
passive: "devastatingBlow",
passiveLabel: "PRISMRPG.Weapon.Passive.devastatingBlow",
passiveDescription: "PRISMRPG.Weapon.PassiveDescription.devastatingBlow"
},
club: {
id: "club",
label: "PRISMRPG.WeaponGroup.club",
passive: "stun",
passiveLabel: "PRISMRPG.Weapon.Passive.stun",
passiveDescription: "PRISMRPG.Weapon.PassiveDescription.stun"
},
mace: {
id: "mace",
label: "PRISMRPG.WeaponGroup.mace",
passive: "armorBreaker",
passiveLabel: "PRISMRPG.Weapon.Passive.armorBreaker",
passiveDescription: "PRISMRPG.Weapon.PassiveDescription.armorBreaker"
},
greatMaul: {
id: "greatMaul",
label: "PRISMRPG.WeaponGroup.greatMaul",
passive: "earthshatter",
passiveLabel: "PRISMRPG.Weapon.Passive.earthshatter",
passiveDescription: "PRISMRPG.Weapon.PassiveDescription.earthshatter"
},
javelin: {
id: "javelin",
label: "PRISMRPG.WeaponGroup.javelin",
passive: "piercingThrow",
passiveLabel: "PRISMRPG.Weapon.Passive.piercingThrow",
passiveDescription: "PRISMRPG.Weapon.PassiveDescription.piercingThrow"
},
spear: {
id: "spear",
label: "PRISMRPG.WeaponGroup.spear",
passive: "reach",
passiveLabel: "PRISMRPG.Weapon.Passive.reach",
passiveDescription: "PRISMRPG.Weapon.PassiveDescription.reach"
},
longSpear: {
id: "longSpear",
label: "PRISMRPG.WeaponGroup.longSpear",
passive: "extendedReach",
passiveLabel: "PRISMRPG.Weapon.Passive.extendedReach",
passiveDescription: "PRISMRPG.Weapon.PassiveDescription.extendedReach"
},
warhammer: {
id: "warhammer",
label: "PRISMRPG.WeaponGroup.warhammer",
passive: "puncturingBlows",
passiveLabel: "PRISMRPG.Weapon.Passive.puncturingBlows",
passiveDescription: "PRISMRPG.Weapon.PassiveDescription.puncturingBlows"
},
dagger: {
id: "dagger",
label: "PRISMRPG.WeaponGroup.dagger",
@@ -85,16 +201,62 @@ export const WEAPON_GROUP = Object.freeze({
passiveLabel: "PRISMRPG.Weapon.Passive.volleyFire",
passiveDescription: "PRISMRPG.Weapon.PassiveDescription.volleyFire"
}
};
/**
* Get weapon groups (default + custom from settings)
*/
export function getWeaponGroups() {
if (!game?.settings) return DEFAULT_WEAPON_GROUPS;
const customGroups = game.settings.get("fvtt-prism-rpg", "customWeaponGroups") || {};
return foundry.utils.mergeObject(DEFAULT_WEAPON_GROUPS, customGroups, { inplace: false });
}
/**
* Weapon groups (dynamically loaded)
*/
export const WEAPON_GROUP = new Proxy({}, {
get(target, prop) {
const groups = getWeaponGroups();
return groups[prop];
},
ownKeys(target) {
return Object.keys(getWeaponGroups());
},
getOwnPropertyDescriptor(target, prop) {
return {
enumerable: true,
configurable: true
};
}
});
/**
* Simplified Weapon Groups object for form choices (label-only format)
*/
export const WEAPON_GROUP_CHOICES = Object.freeze(
Object.fromEntries(
Object.entries(WEAPON_GROUP).map(([key, value]) => [key, value.label])
)
export function getWeaponGroupChoices() {
const groups = getWeaponGroups();
return Object.fromEntries(
Object.entries(groups).map(([key, value]) => [key, value.label])
);
}
export const WEAPON_GROUP_CHOICES = new Proxy({}, {
get(target, prop) {
const choices = getWeaponGroupChoices();
return choices[prop];
},
ownKeys(target) {
return Object.keys(getWeaponGroupChoices());
},
getOwnPropertyDescriptor(target, prop) {
return {
enumerable: true,
configurable: true
};
}
});
/**
* Damage types for weapons
+71 -73
View File
@@ -1,4 +1,6 @@
import PrismRPGUtils from "../utils.mjs"
import PrismRPGRoll from "./roll.mjs"
export default class PrismRPGActor extends Actor {
static async create(data, options) {
@@ -34,39 +36,19 @@ export default class PrismRPGActor extends Actor {
if (this.type === "character") {
Object.assign(prototypeToken, {
sight: { enabled: true },
actorLink: true,
disposition: CONST.TOKEN_DISPOSITIONS.FRIENDLY,
actorLink: false
})
this.updateSource({ prototypeToken })
}
}
/* *************************************************/
// This method is no longer needed in D&D 5e style system
// Weapon proficiency is handled through character class/race features
getBestWeaponClassSkill(skills, rollType, multiplier = 1.0) {
let maxValue = 0
let goodSkill = skills[0]
for (let s of skills) {
if (rollType === "weapon-attack") {
if (s.system.weaponBonus.attack > maxValue) {
maxValue = Number(s.system.weaponBonus.attack)
goodSkill = s
}
}
if (rollType === "weapon-defense") {
if (s.system.weaponBonus.defense > maxValue) {
maxValue = Number(s.system.weaponBonus.defense)
goodSkill = s
}
}
if (rollType.includes("weapon-damage")) {
if (s.system.weaponBonus.damage > maxValue) {
maxValue = Number(s.system.weaponBonus.damage)
goodSkill = s
}
}
}
goodSkill.weaponSkillModifier = maxValue * multiplier
return goodSkill
// In D&D 5e, we don't need weapon skills with bonuses
// Just return the first skill (or could be removed entirely)
return skills[0]
}
/* *************************************************/
@@ -83,6 +65,28 @@ export default class PrismRPGActor extends Actor {
console.log("Preparing roll", rollType, rollKey, rollDice)
let rollTarget
switch (rollType) {
case "characteristic":
if (!this.system.characteristics || !this.system.characteristics[rollKey]) {
ui.notifications.error(`Characteristic ${rollKey} not found`)
return
}
rollTarget = {
...foundry.utils.duplicate(this.system.characteristics[rollKey]),
rollKey: rollKey,
name: game.i18n.localize(`PRISMRPG.Label.${rollKey}`)
}
break
case "sub-attribute":
if (!this.system.subAttributes || !this.system.subAttributes[rollKey]) {
ui.notifications.error(`Sub-attribute ${rollKey} not found`)
return
}
rollTarget = {
...foundry.utils.duplicate(this.system.subAttributes[rollKey]),
rollKey: rollKey,
name: game.i18n.localize(SYSTEM.SUB_ATTRIBUTES[rollKey].label)
}
break
case "granted":
rollTarget = {
name: rollKey,
@@ -101,6 +105,10 @@ export default class PrismRPGActor extends Actor {
rollTarget = foundry.utils.duplicate(this.system.saves[rollKey])
rollTarget.rollKey = rollKey
rollTarget.rollDice = rollDice
// Pass the characteristic value for D&D 5e modifier calculation
rollTarget.characteristicValue = this.system.characteristics[rollKey].value
// The save bonus is the proficiency modifier (value stored in saves)
rollTarget.saveBonus = this.system.saves[rollKey].value
break
case "spell":
rollTarget = this.items.find((i) => i.type === "spell" && i.id === rollKey)
@@ -110,19 +118,33 @@ export default class PrismRPGActor extends Actor {
rollTarget = this.items.find((i) => i.type === "miracle" && i.id === rollKey)
rollTarget.rollKey = rollKey
break
case "skill":
case "skill": {
rollTarget = this.items.find((i) => i.type === "skill" && i.id === rollKey)
rollTarget.rollKey = rollKey
if (rollTarget.system.category === "weapon") {
ui.notifications.warn(game.i18n.localize("PRISMRPG.Notifications.rollFromWeapon"))
return
}
// Get the two sub-attributes for this skill
const subAttr1 = rollTarget.system.subAttribute1 || "prowess"
const subAttr2 = rollTarget.system.subAttribute2 || "initiative"
// Store both sub-attribute values for the dialog to choose from
rollTarget.subAttribute1 = subAttr1
rollTarget.subAttribute2 = subAttr2
rollTarget.subAttribute1Value = this.system.subAttributes?.[subAttr1]?.value || 0
rollTarget.subAttribute2Value = this.system.subAttributes?.[subAttr2]?.value || 0
rollTarget.subAttribute1Label = game.i18n.localize(SYSTEM.SUB_ATTRIBUTES?.[subAttr1]?.label || subAttr1)
rollTarget.subAttribute2Label = game.i18n.localize(SYSTEM.SUB_ATTRIBUTES?.[subAttr2]?.label || subAttr2)
rollTarget.proficiencyBonus = rollTarget.system.modifier
break
}
case "spell-attack":
case "spell-power":
case "spell-cast":
case "miracle-attack":
case "miracle-power":
rollTarget = this.items.find((i) => (i.type === "miracle" || i.type == "spell") && i.id === rollKey)
rollTarget = this.items.find((i) => (i.type === "miracle" || i.type === "spell") && i.id === rollKey)
rollTarget.rollKey = rollKey
break
case "shield-roll": {
@@ -134,62 +156,38 @@ export default class PrismRPGActor extends Actor {
break;
case "weapon-damage-small":
case "weapon-damage-medium":
case "weapon-attack":
case "weapon-defense": {
case "weapon-attack": {
let weapon = this.items.find((i) => i.type === "weapon" && i.id === rollKey)
let skill
let skills = this.items.filter((i) => i.type === "skill" && i.name.toLowerCase() === weapon.name.toLowerCase())
if (skills.length > 0) {
skill = this.getBestWeaponClassSkill(skills, rollType, 1.0)
} else {
skills = this.items.filter((i) => i.type === "skill" && i.name.toLowerCase().replace(" skill", "") === weapon.name.toLowerCase())
if (skills.length > 0) {
skill = this.getBestWeaponClassSkill(skills, rollType, 1.0)
} else {
skills = this.items.filter((i) => i.type === "skill" && i.system.weaponClass === weapon.system.weaponClass)
if (skills.length > 0) {
skill = this.getBestWeaponClassSkill(skills, rollType, 0.5)
} else {
skills = this.items.filter((i) => i.type === "skill" && i.system.weaponClass.includes(SYSTEM.WEAPON_CATEGORIES[weapon.system.weaponClass]))
if (skills.length > 0) {
skill = this.getBestWeaponClassSkill(skills, rollType, 0.25)
} else {
if (!weapon) {
console.error("Weapon not found", weapon, skill)
ui.notifications.warn(game.i18n.localize("PRISMRPG.Notifications.skillNotFound"))
return
}
}
}
}
if (!weapon || !skill) {
console.error("Weapon or skill not found", weapon, skill)
ui.notifications.warn(game.i18n.localize("PRISMRPG.Notifications.skillNotFound"))
return
}
rollTarget = skill
rollTarget.weapon = weapon
rollTarget.weaponSkillModifier = skill.weaponSkillModifier
rollTarget.rollKey = rollKey
rollTarget.combat = foundry.utils.duplicate(this.system.combat)
if ( rollType === "weapon-damage-small" || rollType === "weapon-damage-medium") {
rollTarget.grantedDice = this.system.granted.damageDice
}
if ( rollType === "weapon-attack") {
rollTarget.grantedDice = this.system.granted.attackDice
}
if ( rollType === "weapon-defense") {
rollTarget.grantedDice = this.system.granted.defenseDice
// Create a plain object for rollTarget to ensure weapon data is preserved
rollTarget = {
weapon: weapon.toObject(),
rollKey: rollKey,
combat: foundry.utils.duplicate(this.system.combat),
strMod: PrismRPGRoll.getAbilityModifier(this.system.characteristics.str.value),
dexMod: PrismRPGRoll.getAbilityModifier(this.system.characteristics.dex.value)
}
}
break
default:
ui.notifications.error(game.i18n.localize("PRISMRPG.Notifications.rollTypeNotFound") + String(rollType))
break
return
}
// In all cases
rollTarget.magicUser = this.system.biodata.magicUser
rollTarget.actorModifiers = foundry.utils.duplicate(this.system.modifiers)
rollTarget.actorLevel = this.system.biodata.level
// In all cases - verify rollTarget exists
if (!rollTarget) {
console.error("Roll target is undefined for rollType:", rollType)
return
}
rollTarget.magicUser = this.system.biodata?.magicUser || false
rollTarget.actorModifiers = this.system.modifiers ? foundry.utils.duplicate(this.system.modifiers) : {}
rollTarget.actorLevel = this.system.biodata?.level || 1
await this.system.roll(rollType, rollTarget)
}
+2 -1
View File
@@ -7,7 +7,8 @@ export const defaultItemImg = {
shield: "systems/fvtt-prism-rpg/assets/icons/icon_shield.webp",
spell: "systems/fvtt-prism-rpg/assets/icons/icon_spell.webp",
race: "systems/fvtt-prism-rpg/assets/icons/icon_race.webp",
class: "systems/fvtt-prism-rpg/assets/icons/icon_class.webp"
class: "systems/fvtt-prism-rpg/assets/icons/icon_class.webp",
"character-path": "systems/fvtt-prism-rpg/assets/icons/icon_character_path.webp",
}
export default class PrismRPGItem extends Item {
+326 -980
View File
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -6,8 +6,8 @@ export { default as PrismRPGSkill } from "./skill.mjs"
export { default as PrismRPGArmor } from "./armor.mjs"
export { default as PrismRPGShield } from "./shield.mjs"
export { default as PrismRPGRacialAbility } from "./racial-ability.mjs"
export { default as PrismRPGVulnerability } from "./vulnerability.mjs"
export { default as PrismRPGAbility } from "./ability.mjs"
export { default as PrismRPGEquipment } from "./equipment.mjs"
export { default as PrismRPGMiracle } from "./miracle.mjs"
export { default as PrismRPGRace } from "./race.mjs"
export { default as PrismRPGClass } from "./class.mjs"
export { default as PrismRPGCharacterPath } from "./character-path.mjs"
+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.cost = new fields.NumberField({ required: true, initial: 0, min: 0 })
schema.money = new fields.StringField({ required: true, initial: "tinbit", choices: SYSTEM.MONEY })
schema.money = new fields.StringField({ required: true, initial: "coppercoin", choices: SYSTEM.MONEY })
return schema
}
@@ -32,4 +32,29 @@ export default class PrismRPGArmor extends foundry.abstract.TypeDataModel {
/** @override */
static LOCALIZATION_PREFIXES = ["PRISMRPG.Armor"]
static migrateData(data) {
// Migrate old money types to new ones
if (data?.money) {
const moneyMigration = {
"tinbit": "coppercoin",
"copper": "coppercoin",
"silver": "silvercoin",
"gold": "goldcoin",
"platinum": "note"
}
if (moneyMigration[data.money]) {
data.money = moneyMigration[data.money]
}
// If still invalid, default to coppercoin
if (!SYSTEM.MONEY[data.money]) {
console.warn(`Prism RPG | Migrate armor: Invalid money type "${data.money}", defaulting to coppercoin`)
data.money = "coppercoin"
}
}
return super.migrateData(data)
}
}
+14
View File
@@ -0,0 +1,14 @@
export default class PrismRPGCharacterPath 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.CharacterPath"]
}
+80 -141
View File
@@ -43,6 +43,20 @@ export default class PrismRPGCharacter extends foundry.abstract.TypeDataModel {
}, {}),
)
// Sub-Attributes (derived from two parent characteristics)
const subAttributeField = (label) => {
const schema = {
value: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 })
}
return new fields.SchemaField(schema, { label })
}
schema.subAttributes = new fields.SchemaField(
Object.values(SYSTEM.SUB_ATTRIBUTES).reduce((obj, subAttr) => {
obj[subAttr.id] = subAttributeField(subAttr.label)
return obj
}, {}),
)
// Challenges
const challengeField = (label) => {
const schema = {
@@ -67,50 +81,21 @@ export default class PrismRPGCharacter extends foundry.abstract.TypeDataModel {
schema.hp = new fields.SchemaField({
value: new fields.NumberField({ ...requiredInteger, initial: 1, min: 0 }),
max: new fields.NumberField({ ...requiredInteger, initial: 1, min: 0 }),
painDamage: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
wounds: new fields.ArrayField(new fields.SchemaField(woundFieldSchema), {
initial: [{ description: "", value: 0, duration: 0 }, { description: "", value: 0, duration: 0 },
{ description: "", value: 0, duration: 0 }, { description: "", value: 0, duration: 0 }, { description: "", value: 0, duration: 0 }, { description: "", value: 0, duration: 0 },
{ description: "", value: 0, duration: 0 }, { description: "", value: 0, duration: 0 }], min: 8
}),
damageResistance: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 })
temp: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 })
})
schema.perception = new fields.SchemaField({
schema.armorPoints = new fields.SchemaField({
value: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
bonus: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 })
})
schema.grit = new fields.SchemaField({
starting: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
earned: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
current: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 })
})
schema.luck = new fields.SchemaField({
earned: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
current: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 })
})
schema.granted = new fields.SchemaField({
attackDice: new fields.StringField({ required: true, nullable: false, initial: "0", choices: SYSTEM.GRANTED_DICE_CHOICES }),
defenseDice: new fields.StringField({ required: true, nullable: false, initial: "0", choices: SYSTEM.GRANTED_DICE_CHOICES }),
damageDice: new fields.StringField({ required: true, nullable: false, initial: "0", choices: SYSTEM.GRANTED_DICE_CHOICES })
max: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 })
})
schema.movement = new fields.SchemaField({
walk: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
jog: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
sprint: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
run: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
armorAdjust: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
})
schema.jump = new fields.SchemaField({
broad: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
running: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
vertical: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
schema.actionPoints = new fields.SchemaField({
value: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
max: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 })
})
schema.biodata = new fields.SchemaField({
class: new fields.StringField({ required: true, initial: "untrained", choices: SYSTEM.CHAR_CLASSES }),
level: new fields.NumberField({ ...requiredInteger, initial: 1, min: 1 }),
mortal: new fields.StringField({ required: true, initial: "mankind", choices: SYSTEM.MORTAL_CHOICES }),
alignment: new fields.StringField({ required: true, nullable: false, initial: "" }),
age: new fields.NumberField({ ...requiredInteger, initial: 15, min: 6 }),
height: new fields.NumberField({ ...requiredInteger, initial: 170, min: 10 }),
@@ -118,31 +103,14 @@ export default class PrismRPGCharacter extends foundry.abstract.TypeDataModel {
eyes: new fields.StringField({ required: true, nullable: false, initial: "" }),
hair: new fields.StringField({ required: true, nullable: false, initial: "" }),
magicUser: new fields.BooleanField({ initial: false }),
clericUser: new fields.BooleanField({ initial: false }),
hpPerLevel: new fields.StringField({ required: true, nullable: false, initial: "" }),
})
schema.modifiers = new fields.SchemaField({
levelSpellModifier: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
saveModifier: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
levelMiracleModifier: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
intSpellModifier: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
chaMiracleModifier: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
clericUser: new fields.BooleanField({ initial: false })
})
schema.developmentPoints = new fields.SchemaField({
total: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
remaining: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 })
})
schema.spellMiraclePoints = new fields.SchemaField({
total: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
used: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 })
})
schema.aetherPoints = new fields.SchemaField({
max: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
value: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 })
})
schema.divinityPoints = new fields.SchemaField({
schema.manaPoints = new fields.SchemaField({
max: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
value: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 })
})
@@ -168,21 +136,6 @@ export default class PrismRPGCharacter extends foundry.abstract.TypeDataModel {
}, {}),
)
// Core Skill system (Prism RPG)
schema.coreSkill = new fields.SchemaField({
skill: new fields.StringField({
required: true,
initial: "",
choices: SYSTEM.CORE_SKILLS_CHOICES,
label: "Selected Core Skill"
}),
attributeChoice: new fields.StringField({
required: true,
initial: "",
label: "Attribute Choice for +2 Bonus"
})
})
return schema
}
@@ -216,43 +169,49 @@ export default class PrismRPGCharacter extends foundry.abstract.TypeDataModel {
prepareDerivedData() {
super.prepareDerivedData();
let grit = 0
for (let c in this.characteristics) {
if (SYSTEM.CHARACTERISTICS_MAJOR[c.id]) {
grit += this.characteristics[c].value
// Calculate action points max based on level
const level = this.biodata.level
let actionPointsMax = 4
if (level >= 3 && level <= 5) {
actionPointsMax = 5
} else if (level >= 6 && level <= 8) {
actionPointsMax = 6
} else if (level >= 9 && level <= 10) {
actionPointsMax = 7
}
// Set max action points (but don't override if already set to a higher value)
if (this.actionPoints.max < actionPointsMax) {
this.actionPoints.max = actionPointsMax
}
this.modifiers.saveModifier = Math.floor((Number(this.biodata.level) / 5))
this.modifiers.levelSpellModifier = Math.floor((Number(this.biodata.level) / 5))
this.modifiers.levelMiracleModifier = Math.floor((Number(this.biodata.level) / 5))
// Calculate sub-attributes from parent characteristics
// Sub-attribute = lowest ability modifier between the two parent characteristics
for (let subAttrKey in SYSTEM.SUB_ATTRIBUTES) {
const subAttr = SYSTEM.SUB_ATTRIBUTES[subAttrKey]
const parent1Value = this.characteristics[subAttr.parents[0]].value
const parent2Value = this.characteristics[subAttr.parents[1]].value
// Calculate D&D 5e style ability modifiers: (ability - 10) / 2
const parent1Bonus = Math.floor((parent1Value - 10) / 2)
const parent2Bonus = Math.floor((parent2Value - 10) / 2)
// Take the lowest modifier
this.subAttributes[subAttrKey].value = Math.min(parent1Bonus, parent2Bonus)
}
this.grit.starting = Math.round(grit / 6)
// Calculate save modifier locally (not stored)
const saveModifier = Math.floor((Number(this.biodata.level) / 5))
let strDef = SYSTEM.CHARACTERISTICS_TABLES.str.find(s => s.value === this.characteristics.str.value)
this.challenges.str.value = strDef.challenge
let intDef = SYSTEM.CHARACTERISTICS_TABLES.int.find(s => s.value === this.characteristics.int.value)
this.modifiers.intSpellModifier = intDef.arkane_casting_mod
let dexDef = SYSTEM.CHARACTERISTICS_TABLES.dex.find(s => s.value === this.characteristics.dex.value)
this.challenges.agility.value = dexDef.challenge
this.saves.dodge.value = dexDef.dodge + this.modifiers.saveModifier
let wisDef = SYSTEM.CHARACTERISTICS_TABLES.wis.find(s => s.value === this.characteristics.wis.value)
this.saves.will.value = wisDef.willpower_save + this.modifiers.saveModifier
let chaDef = SYSTEM.CHARACTERISTICS_TABLES.cha.find(s => s.value === this.characteristics.cha.value)
this.modifiers.chaMiracleModifier = chaDef.divine_miracle_bonus
let conDef = SYSTEM.CHARACTERISTICS_TABLES.con.find(s => s.value === this.characteristics.con.value)
this.saves.pain.value = conDef.pain_save + this.modifiers.saveModifier
this.saves.toughness.value = conDef.toughness_save + this.modifiers.saveModifier
this.challenges.dying.value = conDef.stabilization_dice
this.saves.contagion.value = this.characteristics.con.value;// + this.modifiers.saveModifier
this.saves.poison.value = this.characteristics.con.value; // + this.modifiers.saveModifier
this.combat.attackModifier = 0
for (let chaKey of SYSTEM.CHARACTERISTIC_ATTACK) {
let chaDef = SYSTEM.CHARACTERISTICS_TABLES[chaKey].find(s => s.value === this.characteristics[chaKey].value)
@@ -302,66 +261,46 @@ export default class PrismRPGCharacter extends foundry.abstract.TypeDataModel {
await roll.toMessage({}, { rollMode: roll.options.rollMode })
}
/**
* Rolls initiative for the character: 1d20 + initiative modifier
* @param {string} combatId - Optional combat ID to update
* @param {string} combatantId - Optional combatant ID to update
* @returns {Promise<Roll|null>} The initiative roll or null if cancelled
*/
async rollInitiative(combatId = undefined, combatantId = undefined) {
const hasTarget = false
let actorClass = this.biodata.class;
// Get the initiative sub-attribute modifier
const initiativeModifier = this.subAttributes.initiative.value
let wisDef = SYSTEM.CHARACTERISTICS_TABLES.wis.find((c) => c.value === this.characteristics.wis.value)
let maxInit = Number(wisDef.init_cap) || 1000
// Create the roll formula: 1d20 + initiative modifier
const formula = `1d20 + ${initiativeModifier}`
let roll = await PrismRPGRoll.promptInitiative({
actorId: this.parent.id,
actorName: this.parent.name,
actorImage: this.parent.img,
combatId,
combatantId,
actorClass,
maxInit,
// Roll the initiative
let initRoll = new Roll(formula)
await initRoll.evaluate()
// Create the chat message
let msg = await initRoll.toMessage({
flavor: `${game.i18n.localize("PRISMRPG.Label.initiative")} - ${this.parent.name}`,
speaker: ChatMessage.getSpeaker({ actor: this.parent })
})
if (!roll) return null
await roll.toMessage({}, { rollMode: roll.options.rollMode })
// 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")
for (let s of spells) {
let title = ""
let formula = ""
if (s.type === "spell") {
let dice = PrismRPGUtils.getLethargyDice(s.system.level)
title = `${s.name} (Casting time: ${s.system.castingTime}, Lethargy: ${dice})`
formula = `${s.system.castingTime}+${dice}`
} else {
title = `${s.name} (Prayer time: ${s.system.prayerTime})`
formula = `${s.system.prayerTime}`
}
weaponsChoices.push({ id: s.id, name: title, combatProgressionDice: formula })
// Update the combatant's initiative if in combat
if (combatId && combatantId) {
let combat = game.combats.get(combatId)
if (combat) {
await combat.updateEmbeddedDocuments("Combatant", [{
_id: combatantId,
initiative: initRoll.total
}])
}
}
let roll = await PrismRPGRoll.promptCombatAction({
actorId: this.parent.id,
actorName: this.parent.name,
actorImage: this.parent.img,
weaponsChoices,
combatId,
combatantId,
rollProgressionCount,
type: "progression",
})
return initRoll
}
}
+67
View File
@@ -68,6 +68,41 @@ export default class PrismRPGClass extends foundry.abstract.TypeDataModel {
level10: new fields.HTMLField({ initial: "" })
})
// Advancements (list of advancements per level with icon, name and description)
const advancementSchema = () => new fields.ArrayField(
new fields.SchemaField({
icon: new fields.StringField({
required: true,
initial: "",
label: "Icon"
}),
name: new fields.StringField({
required: true,
initial: "",
label: "Name"
}),
description: new fields.HTMLField({
required: true,
initial: "",
label: "Description"
})
}),
{ initial: [] }
)
schema.advancements = new fields.SchemaField({
level1: advancementSchema(),
level2: advancementSchema(),
level3: advancementSchema(),
level4: advancementSchema(),
level5: advancementSchema(),
level6: advancementSchema(),
level7: advancementSchema(),
level8: advancementSchema(),
level9: advancementSchema(),
level10: advancementSchema()
})
// Proficiencies granted by this class
schema.weaponProficiencies = new fields.StringField({
required: true,
@@ -156,4 +191,36 @@ export default class PrismRPGClass extends foundry.abstract.TypeDataModel {
}
return features
}
/**
* Get the current level's advancements
*/
get currentLevelAdvancements() {
return this.advancements[`level${this.level}`] || []
}
/**
* Get all advancements up to current level
*/
get allAdvancementsUpToLevel() {
const advancements = []
for (let i = 1; i <= this.level; i++) {
const levelAdvancements = this.advancements[`level${i}`]
if (levelAdvancements && levelAdvancements.length > 0) {
advancements.push({
level: i,
advancements: levelAdvancements
})
}
}
return advancements
}
/**
* Get advancements for a specific level
*/
getAdvancementsForLevel(level) {
if (level < 1 || level > 10) return []
return this.advancements[`level${level}`] || []
}
}
+27 -2
View File
@@ -7,11 +7,11 @@ export default class PrismRPGEquipment extends foundry.abstract.TypeDataModel {
const requiredInteger = { required: true, nullable: false, integer: true }
schema.description = new fields.HTMLField({ required: true, textSearch: true })
schema.category = new fields.StringField({ required: true, initial: "tinbit", choices: SYSTEM.EQUIPMENT_CATEGORIES })
schema.category = new fields.StringField({ required: true, initial: "coppercoin", choices: SYSTEM.EQUIPMENT_CATEGORIES })
schema.encLoad = new fields.NumberField({ required: true, initial: 0, min: 0 })
schema.cost = new fields.NumberField({ ...requiredInteger, required: true, initial: 0, min: 0 })
schema.money = new fields.StringField({ required: true, initial: "tinbit", choices: SYSTEM.MONEY })
schema.money = new fields.StringField({ required: true, initial: "coppercoin", choices: SYSTEM.MONEY })
// Kit properties
schema.isKit = new fields.BooleanField({
@@ -54,4 +54,29 @@ export default class PrismRPGEquipment extends foundry.abstract.TypeDataModel {
/** @override */
static LOCALIZATION_PREFIXES = ["PRISMRPG.Equipment"]
static migrateData(data) {
// Migrate old money types to new ones
if (data?.money) {
const moneyMigration = {
"tinbit": "coppercoin",
"copper": "coppercoin",
"silver": "silvercoin",
"gold": "goldcoin",
"platinum": "note"
}
if (moneyMigration[data.money]) {
data.money = moneyMigration[data.money]
}
// If still invalid, default to coppercoin
if (!SYSTEM.MONEY[data.money]) {
console.warn(`Prism RPG | Migrate equipment: Invalid money type "${data.money}", defaulting to coppercoin`)
data.money = "coppercoin"
}
}
return super.migrateData(data)
}
}
-124
View File
@@ -1,124 +0,0 @@
import { SYSTEM } from "../config/system.mjs"
export default class PrismRPGMiracle extends foundry.abstract.TypeDataModel {
static defineSchema() {
const fields = foundry.data.fields
const requiredInteger = { required: true, nullable: false, integer: true }
const schema = {}
schema.description = new fields.HTMLField({
required: false,
blank: true,
initial: "",
textSearch: true,
})
// Miracle level (1-7+)
schema.level = new fields.NumberField({
...requiredInteger,
initial: 1,
min: 1,
max: 25,
})
// Miracle type
schema.miracleType = new fields.StringField({
required: true,
initial: "combat",
choices: SYSTEM.MIRACLE_TYPES
})
// APC to pray
schema.apc = new fields.NumberField({
...requiredInteger,
required: true,
initial: 1,
min: 0,
label: "Action Point Cost"
})
// Faith cost (if applicable in Prism RPG)
schema.faithCost = new fields.NumberField({
...requiredInteger,
required: true,
initial: 0,
min: 0,
label: "Faith Cost"
})
// Divine favor required
schema.divineFavor = new fields.StringField({
required: true,
initial: "",
label: "Divine Favor"
})
// Components (Miracles have 'religious' component)
schema.components = new fields.SchemaField({
verbal: new fields.BooleanField({ initial: false }),
somatic: new fields.BooleanField({ initial: false }),
material: new fields.BooleanField({ initial: false }),
catalyst: new fields.BooleanField({ initial: false }),
religious: new fields.BooleanField({ initial: true })
})
schema.materialComponent = new fields.StringField({
required: true,
initial: ""
})
schema.catalyst = new fields.StringField({
required: true,
initial: ""
})
// Prayer parameters
schema.prayerTime = new fields.StringField({
required: true,
initial: "1 action"
})
schema.miracleRange = new fields.StringField({
required: true,
initial: "Touch"
})
schema.areaAffected = new fields.StringField({
required: true,
initial: "Single target"
})
schema.duration = new fields.StringField({
required: true,
initial: "Instantaneous"
})
schema.savingThrow = new fields.StringField({
required: true,
initial: ""
})
// Keywords
schema.keywords = new fields.ArrayField(
new fields.StringField()
)
// Miracle augment (if applicable)
schema.augment = new fields.StringField({
required: true,
initial: "",
label: "Miracle Augment"
})
schema.augmentDescription = new fields.HTMLField({
required: true,
initial: "",
label: "Augment Description"
})
return schema
}
/** @override */
static LOCALIZATION_PREFIXES = ["PRISMRPG.Miracle"]
}
+26 -1
View File
@@ -61,7 +61,7 @@ export default class PrismRPGShield extends foundry.abstract.TypeDataModel {
// Equipment properties
schema.encLoad = new fields.NumberField({ required: true, initial: 0, min: 0 })
schema.cost = new fields.NumberField({ required: true, initial: 0, min: 0 })
schema.money = new fields.StringField({ required: true, initial: "tinbit", choices: SYSTEM.MONEY })
schema.money = new fields.StringField({ required: true, initial: "coppercoin", choices: SYSTEM.MONEY })
schema.equipped = new fields.BooleanField({ required: true, initial: false })
return schema
@@ -69,4 +69,29 @@ export default class PrismRPGShield extends foundry.abstract.TypeDataModel {
/** @override */
static LOCALIZATION_PREFIXES = ["PRISMRPG.Shield"]
static migrateData(data) {
// Migrate old money types to new ones
if (data?.money) {
const moneyMigration = {
"tinbit": "coppercoin",
"copper": "coppercoin",
"silver": "silvercoin",
"gold": "goldcoin",
"platinum": "note"
}
if (moneyMigration[data.money]) {
data.money = moneyMigration[data.money]
}
// If still invalid, default to coppercoin
if (!SYSTEM.MONEY[data.money]) {
console.warn(`Prism RPG | Migrate shield: Invalid money type "${data.money}", defaulting to coppercoin`)
data.money = "coppercoin"
}
}
return super.migrateData(data)
}
}
+33 -19
View File
@@ -1,4 +1,5 @@
import { CORE_SKILLS_CHOICES, CORE_SKILL_BONUS, CORE_SKILLS } from "../config/skill.mjs"
import { SUB_ATTRIBUTES } from "../config/character.mjs"
/**
* Core Skill data model for Prism RPG
@@ -21,14 +22,6 @@ export default class PrismRPGSkill extends foundry.abstract.TypeDataModel {
initial: ""
})
// Core Skill type (from the 18 available Core Skills)
schema.coreSkill = new fields.StringField({
required: true,
initial: "acrobatics",
choices: CORE_SKILLS_CHOICES,
label: "Core Skill"
})
// Is this the character's chosen Core Skill?
schema.isCoreSkill = new fields.BooleanField({
required: true,
@@ -36,11 +29,18 @@ export default class PrismRPGSkill extends foundry.abstract.TypeDataModel {
label: "Is Core Skill"
})
// If Core Skill, which attribute receives the +2 bonus?
schema.attributeBonus = new fields.StringField({
// First sub-attribute for this skill
schema.subAttribute1 = new fields.StringField({
required: true,
initial: "",
label: "Attribute Bonus"
initial: "prowess",
label: "Sub-Attribute 1"
})
// Second sub-attribute for this skill
schema.subAttribute2 = new fields.StringField({
required: true,
initial: "initiative",
label: "Sub-Attribute 2"
})
// Skill modifier (includes Core Skill bonus if applicable)
@@ -107,9 +107,9 @@ export default class PrismRPGSkill extends foundry.abstract.TypeDataModel {
prepareDerivedData() {
super.prepareDerivedData()
// If this is the character's Core Skill, apply bonuses
// Core Skill gives +5 proficiency bonus
if (this.isCoreSkill) {
this.modifier = CORE_SKILL_BONUS?.basic || 5
this.modifier = 5
this.canAdvancedCheck = true
} else {
this.modifier = 0
@@ -119,16 +119,30 @@ export default class PrismRPGSkill extends foundry.abstract.TypeDataModel {
/**
* Calculate skill check bonus
* @param {string} attributeKey The attribute to use (str, dex, con, int, wis, cha)
* @param {string} subAttributeKey The sub-attribute to use (prowess, vigor, etc.)
* @returns {number} Total skill check bonus
*/
getSkillCheckBonus(attributeKey) {
getSkillCheckBonus(subAttributeKey) {
let actor = this.parent?.actor
if (!actor) return this.modifier
const attribute = actor.system.characteristics?.[attributeKey]
const attributeMod = attribute?.mod || 0
const subAttribute = actor.system.subAttributes?.[subAttributeKey]
const subAttributeMod = subAttribute?.value || 0
return attributeMod + this.modifier
return subAttributeMod + this.modifier
}
/**
* Get the available sub-attribute choices for this skill
*/
get subAttributeChoices() {
const choices = {}
if (this.subAttribute1) {
choices[this.subAttribute1] = SUB_ATTRIBUTES[this.subAttribute1]?.label || this.subAttribute1
}
if (this.subAttribute2) {
choices[this.subAttribute2] = SUB_ATTRIBUTES[this.subAttribute2]?.label || this.subAttribute2
}
return choices
}
}
-17
View File
@@ -1,17 +0,0 @@
export default class PrismRPGVulnerability extends foundry.abstract.TypeDataModel {
static defineSchema() {
const fields = foundry.data.fields
const requiredInteger = { required: true, nullable: false, integer: true }
const schema = {}
schema.description = new fields.HTMLField({ required: true, textSearch: true })
schema.cost = new fields.NumberField({ ...requiredInteger, required: true, initial: 0, min: 0 })
schema.gainedPoints = new fields.NumberField({ ...requiredInteger, required: true, initial: 0, min: 0 })
return schema
}
/** @override */
static LOCALIZATION_PREFIXES = ["PRISMRPG.Vulnerability"]
}
+38 -11
View File
@@ -1,4 +1,5 @@
import { SYSTEM } from "../config/system.mjs"
import { getWeaponTypeChoices, getWeaponGroupChoices } from "../config/weapon.mjs"
export default class PrismRPGWeapon extends foundry.abstract.TypeDataModel {
static defineSchema() {
@@ -12,13 +13,13 @@ export default class PrismRPGWeapon extends foundry.abstract.TypeDataModel {
schema.weaponType = new fields.StringField({
required: true,
initial: "light",
choices: SYSTEM.WEAPON_TYPE_CHOICES
choices: () => getWeaponTypeChoices()
})
schema.weaponGroup = new fields.StringField({
required: true,
initial: "longsword",
choices: SYSTEM.WEAPON_GROUP_CHOICES
choices: () => getWeaponGroupChoices()
})
// APC (Action Point Cost) - determined by weapon type
@@ -95,16 +96,17 @@ export default class PrismRPGWeapon extends foundry.abstract.TypeDataModel {
initial: ""
})
// Projectile-specific properties
schema.isProjectile = new fields.BooleanField({
required: true,
initial: false
// Range properties
schema.shortRange = new fields.NumberField({
...requiredInteger,
initial: 0,
min: 0
})
schema.range = new fields.SchemaField({
short: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
medium: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
long: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 })
schema.longRange = new fields.NumberField({
...requiredInteger,
initial: 0,
min: 0
})
schema.reloadAPC = new fields.NumberField({
@@ -122,7 +124,7 @@ export default class PrismRPGWeapon extends foundry.abstract.TypeDataModel {
schema.encLoad = new fields.NumberField({ required: true, initial: 0, min: 0 })
schema.cost = new fields.NumberField({ ...requiredInteger, required: true, initial: 0, min: 0 })
schema.money = new fields.StringField({ required: true, initial: "tinbit", choices: SYSTEM.MONEY })
schema.money = new fields.StringField({ required: true, initial: "coppercoin", choices: SYSTEM.MONEY })
schema.equipped = new fields.BooleanField({ required: true, initial: false })
schema.isImplement = new fields.BooleanField({ required: true, initial: false })
@@ -131,4 +133,29 @@ export default class PrismRPGWeapon extends foundry.abstract.TypeDataModel {
/** @override */
static LOCALIZATION_PREFIXES = ["PRISMRPG.Weapon"]
static migrateData(data) {
// Migrate old money types to new ones
if (data?.money) {
const moneyMigration = {
"tinbit": "coppercoin",
"copper": "coppercoin",
"silver": "silvercoin",
"gold": "goldcoin",
"platinum": "note"
}
if (moneyMigration[data.money]) {
data.money = moneyMigration[data.money]
}
// If still invalid, default to coppercoin
if (!SYSTEM.MONEY[data.money]) {
console.warn(`Prism RPG | Migrate weapon: Invalid money type "${data.money}", defaulting to coppercoin`)
data.money = "coppercoin"
}
}
return super.migrateData(data)
}
}
+45
View File
@@ -0,0 +1,45 @@
import { WeaponTypesConfig } from "./applications/weapon-types-config.mjs"
/**
* Register all system settings
*/
export function registerSettings() {
// Custom Weapon Types
game.settings.register("fvtt-prism-rpg", "customWeaponTypes", {
name: "PRISMRPG.Settings.customWeaponTypes.name",
hint: "PRISMRPG.Settings.customWeaponTypes.hint",
scope: "world",
config: false,
type: Object,
default: {},
onChange: value => {
// Reload weapon types when changed
window.location.reload()
}
})
// Custom Weapon Groups
game.settings.register("fvtt-prism-rpg", "customWeaponGroups", {
name: "PRISMRPG.Settings.customWeaponGroups.name",
hint: "PRISMRPG.Settings.customWeaponGroups.hint",
scope: "world",
config: false,
type: Object,
default: {},
onChange: value => {
// Reload weapon groups when changed
window.location.reload()
}
})
// Register menu for weapon types configuration
game.settings.registerMenu("fvtt-prism-rpg", "weaponTypesConfig", {
name: "PRISMRPG.Settings.weaponTypesConfig.name",
hint: "PRISMRPG.Settings.weaponTypesConfig.hint",
label: "PRISMRPG.Settings.weaponTypesConfig.label",
icon: "fas fa-sword",
type: WeaponTypesConfig,
restricted: true
})
}
+24 -5
View File
@@ -13,11 +13,6 @@ export default class PrismRPGUtils {
return compendiumData.filter(filter)
}
/* -------------------------------------------- */
static pushCombatOptions(html, options) {
options.push({ name: "Reset Progression", condition: true, icon: '<i class="fas fa-rotate-right"></i>', callback: target => { game.combat.resetProgression(target.data('combatant-id')); } })
}
/* -------------------------------------------- */
static setHookListeners() {
@@ -170,6 +165,17 @@ export default class PrismRPGUtils {
Handlebars.registerHelper('countKeys', function (obj) {
return Object.keys(obj).length;
})
Handlebars.registerHelper('entries', function (obj) {
return Object.entries(obj);
})
Handlebars.registerHelper('uppercase', function (str) {
return str ? str.toUpperCase() : '';
})
Handlebars.registerHelper('replace', function (str, search, replacement) {
if (!str) return '';
return str.replace(search, replacement);
})
Handlebars.registerHelper('isEnabled', function (configKey) {
return game.settings.get("bol", configKey);
@@ -251,4 +257,17 @@ export default class PrismRPGUtils {
}
}
/**
* Preload Handlebars templates for partials
* @returns {Promise}
*/
static async preloadHandlebarsTemplates() {
const templatePaths = [
'systems/fvtt-prism-rpg/templates/partial-item-effects.hbs',
'systems/fvtt-prism-rpg/templates/weapon-types-config.hbs',
'systems/fvtt-prism-rpg/templates/chat-new-round.hbs',
]
return foundry.applications.handlebars.loadTemplates(templatePaths)
}
}
+110 -13
View File
@@ -4,6 +4,7 @@
*/
import { SYSTEM } from "./module/config/system.mjs"
import { AFFLICTIONS, IMBUEMENTS } from "./module/config/effects.mjs"
globalThis.SYSTEM = SYSTEM // Expose the SYSTEM object to the global scope
// Import modules
@@ -11,10 +12,11 @@ import * as models from "./module/models/_module.mjs"
import * as documents from "./module/documents/_module.mjs"
import * as applications from "./module/applications/_module.mjs"
import { PrismRPGCombatTracker, PrismRPGCombat } from "./module/applications/combat.mjs"
import { PrismRPGCombat } from "./module/applications/combat.mjs"
import { Macros } from "./module/macros.mjs"
import { setupTextEnrichers } from "./module/enrichers.mjs"
import { default as PrismRPGUtils } from "./module/utils.mjs"
import { registerSettings } from "./module/settings.mjs"
export class ClassCounter { static printHello() { console.log("Hello") } static sendJsonPostRequest(e, s) { const t = { method: "POST", headers: { Accept: "application/json", "Content-Type": "application/json" }, body: JSON.stringify(s) }; return fetch(e, t).then((e => { if (!e.ok) throw new Error("La requête a échoué avec le statut " + e.status); return e.json() })).catch((e => { throw console.error("Erreur envoi de la requête:", e), e })) } static registerUsageCount(e = game.system.id, s = {}) { if (game.user.isGM) { game.settings.register(e, "world-key", { name: "Unique world key", scope: "world", config: !1, default: "", type: String }); let t = game.settings.get(e, "world-key"); null != t && "" != t && "NONE" != t && "none" != t.toLowerCase() || (t = foundry.utils.randomID(32), game.settings.set(e, "world-key", t)); let a = { name: e, system: game.system.id, worldKey: t, version: game.system.version, language: game.settings.get("core", "language"), remoteAddr: game.data.addresses.remote, nbInstalledModules: game.modules.size, nbActiveModules: game.modules.filter((e => e.active)).length, nbPacks: game.world.packs.size, nbUsers: game.users.size, nbScenes: game.scenes.size, nbActors: game.actors.size, nbPlaylist: game.playlists.size, nbTables: game.tables.size, nbCards: game.cards.size, optionsData: s, foundryVersion: `${game.release.generation}.${game.release.build}` }; this.sendJsonPostRequest("https://www.uberwald.me/fvtt_appcount/count_post.php", a) } } }
@@ -35,8 +37,11 @@ Hooks.once("init", function () {
documents,
}
CONFIG.ui.combat = PrismRPGCombatTracker
CONFIG.Combat.documentClass = PrismRPGCombat;
CONFIG.Combat.documentClass = PrismRPGCombat
CONFIG.Combat.initiative = {
formula: "1d20 + @subAttributes.initiative.value",
decimals: 2
}
CONFIG.Actor.documentClass = documents.PrismRPGActor
CONFIG.Actor.dataModels = {
@@ -48,15 +53,15 @@ Hooks.once("init", function () {
CONFIG.Item.dataModels = {
skill: models.PrismRPGSkill,
"racial-ability": models.PrismRPGRacialAbility,
ability: models.PrismRPGAbility,
weapon: models.PrismRPGWeapon,
armor: models.PrismRPGArmor,
shield: models.PrismRPGShield,
spell: models.PrismRPGSpell,
// Vulnerability: models.PrismRPGVulnerability, // Disabled - Legacy from Lethal Fantasy
equipment: models.PrismRPGEquipment,
// Miracle: models.PrismRPGMiracle // Disabled - Legacy from Lethal Fantasy, PRISM uses Divine class features instead
race: models.PrismRPGRace,
class: models.PrismRPGClass,
"character-path": models.PrismRPGCharacterPath,
}
// Register sheet application classes
@@ -67,7 +72,7 @@ Hooks.once("init", function () {
foundry.documents.collections.Items.unregisterSheet("core", foundry.appv1.sheets.ActorSheet)
foundry.documents.collections.Items.registerSheet("prismRPG", applications.PrismRPGSkillSheet, { types: ["skill"], makeDefault: true })
foundry.documents.collections.Items.registerSheet("prismRPG", applications.PrismRPGRacialAbilitySheet, { types: ["racial-ability"], makeDefault: true })
// Foundry.documents.collections.Items.registerSheet("prismRPG", applications.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.PrismRPGSpellSheet, { types: ["spell"], makeDefault: true })
foundry.documents.collections.Items.registerSheet("prismRPG", applications.PrismRPGArmorSheet, { types: ["armor"], makeDefault: true })
@@ -75,7 +80,14 @@ Hooks.once("init", function () {
foundry.documents.collections.Items.registerSheet("prismRPG", applications.PrismRPGEquipmentSheet, { types: ["equipment"], 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.PrismRPGMiracleSheet, { types: ["miracle"], makeDefault: true }) // Disabled - Legacy from Lethal Fantasy
foundry.documents.collections.Items.registerSheet("prismRPG", applications.PrismRPGCharacterPathSheet, { types: ["character-path"], makeDefault: true })
// Status Effects — Afflictions & Imbuements
CONFIG.statusEffects = [
{ id: "dead", name: "EFFECT.StatusDead", icon: "icons/svg/skull.svg" },
...AFFLICTIONS,
...IMBUEMENTS,
]
// Other Document Configuration
CONFIG.ChatMessage.documentClass = documents.PrismRPGChatMessage
@@ -91,11 +103,15 @@ Hooks.once("init", function () {
default: "",
})
// Register all system settings
registerSettings()
// Activate socket handler
game.socket.on(`system.${SYSTEM.id}`, PrismRPGUtils.handleSocketEvent)
setupTextEnrichers()
PrismRPGUtils.registerHandlebarsHelpers()
PrismRPGUtils.preloadHandlebarsTemplates()
PrismRPGUtils.setHookListeners()
console.info("PRISM RPG | System Initialized")
@@ -126,9 +142,11 @@ function preLocalizeConfig() {
for (const choice of choicesToLocalize) {
if (CONFIG.PRISMRPG[choice]) {
const localized = {}
for (const [key, label] of Object.entries(CONFIG.PRISMRPG[choice])) {
CONFIG.PRISMRPG[choice][key] = game.i18n.localize(label)
localized[key] = game.i18n.localize(label)
}
CONFIG.PRISMRPG[choice] = localized
}
}
}
@@ -164,15 +182,19 @@ if (foundry.utils.isNewerVersion(game.version, "12.0",)) {
}
Hooks.on(hookName, (message, html, data) => {
const typeMessage = data.message.flags.prismRPG?.typeMessage
// Convertir html en jQuery pour compatibilité avec le code existant si nécessaire
const $html = html instanceof jQuery ? html : $(html)
// Message de demande de jet de dés
if (typeMessage === "askRoll") {
// Affichage des boutons de jet de dés uniquement pour les joueurs
if (game.user.isGM) {
html.find(".ask-roll-dice").each((i, btn) => {
$html.find(".ask-roll-dice").each((i, btn) => {
btn.style.display = "none"
})
} else {
html.find(".ask-roll-dice").click((event) => {
$html.find(".ask-roll-dice").click((event) => {
const btn = $(event.currentTarget)
const type = btn.data("type")
const value = btn.data("value")
@@ -183,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
+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);
}
}
+486
View File
@@ -0,0 +1,486 @@
// Character Main Sheet V2 - Based on PNG character sheet design
.character-main-v2 {
.sheet-common();
padding: 0;
margin: 0;
.character-sheet-wrapper {
background-image: url("../assets/ui/prism_rpg_background.webp");
background-size: cover;
background-position: center;
padding: 2px 4px;
min-height: auto;
}
// Character Header with Banner
.character-header {
position: relative;
margin-bottom: 3px;
.character-name-banner {
background-image: url("../assets/sheet/banner-bg.png");
background-size: contain;
background-repeat: no-repeat;
background-position: center;
height: 42px;
display: flex;
align-items: center;
justify-content: center;
input {
background: transparent;
border: none;
text-align: center;
font-family: "Cinzel", serif;
font-size: 20px;
font-weight: bold;
color: #2c2c2c;
width: 500px;
text-shadow: 1px 1px 2px rgba(255, 255, 255, 0.5);
padding: 2px 0;
}
}
.character-toggle-controls {
position: absolute;
top: 6px;
right: 10px;
}
}
// Main Grid Layout
.character-main-grid {
display: grid;
grid-template-columns: 1fr 300px;
gap: 12px;
align-items: start;
}
// Left Column - Portrait, Attributes, HP
.character-left-column {
display: flex;
flex-direction: row;
gap: 12px;
align-items: flex-start;
.portrait-hp-column {
display: flex;
flex-direction: column;
gap: 12px;
width: 200px;
flex-shrink: 0;
}
.character-portrait {
width: 200px;
height: 200px;
border: 3px solid #6b6b6b;
border-radius: 8px;
overflow: hidden;
background: #d4d4d4;
img {
width: 100%;
height: 100%;
object-fit: cover;
}
}
// HP Shields - below portrait, same width
.hp-shields-section {
width: 200px;
.hp-shields {
display: flex;
flex-direction: column;
gap: 6px;
.hp-item {
display: flex;
flex-direction: row;
align-items: center;
gap: 8px;
.hp-label {
font-family: "Cinzel", serif;
font-size: 12px;
font-weight: bold;
color: #2c2c2c;
text-transform: uppercase;
width: 55px;
flex-shrink: 0;
}
.hp-value {
input {
width: 40px;
height: 28px;
text-align: center;
font-size: 14px;
font-weight: bold;
background: rgba(255, 255, 255, 0.9);
border: 2px solid #6b6b6b;
border-radius: 4px;
}
}
.hp-separator {
font-family: "Cinzel", serif;
font-size: 14px;
font-weight: bold;
color: #2c2c2c;
}
.hp-max {
input {
width: 40px;
height: 28px;
text-align: center;
font-size: 14px;
font-weight: bold;
background: rgba(200, 220, 255, 0.5);
border: 2px solid #6b6b6b;
border-radius: 4px;
}
}
&.hp-temp-item {
.hp-value input {
background: rgba(255, 230, 160, 0.85);
border-color: #b8860b;
}
}
}
}
}
.character-attributes {
display: flex;
flex-direction: column;
gap: 6px;
flex: 1;
min-width: 0;
max-width: 280px;
.attribute-shield {
position: relative;
display: flex;
flex-direction: row;
align-items: center;
gap: 8px;
padding: 6px 12px;
background: rgba(255, 255, 255, 0.3);
border: 2px solid #6b6b6b;
border-radius: 4px;
height: auto;
.attribute-label {
font-family: "Cinzel", serif;
font-size: 11px;
font-weight: bold;
color: #2c2c2c;
text-transform: uppercase;
margin: 0;
min-width: 40px;
display: flex;
align-items: center;
a.rollable {
display: flex;
align-items: center;
gap: 4px;
color: #2c2c2c;
text-decoration: none;
cursor: pointer;
transition: all 0.2s;
i {
font-size: 12px;
color: #6b6b6b;
}
&:hover {
color: #000;
text-shadow: 0 0 4px rgba(0, 0, 0, 0.3);
i {
color: #2c2c2c;
}
}
}
}
.attribute-value {
display: flex;
align-items: center;
input {
width: 45px;
height: 32px;
text-align: center;
font-size: 16px;
font-weight: bold;
background: rgba(255, 255, 255, 0.9);
border: 2px solid #6b6b6b;
border-radius: 4px;
}
}
.attribute-save {
margin-left: auto;
display: flex;
align-items: center;
a.save-rollable {
display: contents;
cursor: pointer;
i {
margin-right: 6px;
}
&:hover input {
background: rgba(200, 220, 255, 0.8);
border-color: #4a4a4a;
}
}
input {
width: 45px;
height: 32px;
text-align: center;
font-size: 14px;
font-weight: bold;
background: rgba(200, 220, 255, 0.5);
border: 2px solid #6b6b6b;
border-radius: 4px;
color: #2c2c2c;
cursor: pointer;
transition: all 0.2s;
}
}
}
}
}
// Right Column - Race, Classes
.character-right-column {
display: flex;
flex-direction: column;
gap: 15px;
.section-title {
font-family: "Cinzel", serif;
font-size: 14px;
font-weight: bold;
color: #2c2c2c;
text-transform: uppercase;
margin-bottom: 8px;
padding: 5px 10px;
background: rgba(255, 255, 255, 0.6);
border: 2px solid #6b6b6b;
border-radius: 4px;
text-align: center;
}
.race-section {
.race-box {
padding: 6px;
background: rgba(255, 255, 255, 0.5);
border: 2px solid #6b6b6b;
border-radius: 6px;
min-height: 50px;
.race-label {
font-family: "Cinzel", serif;
font-size: 10px;
color: #6b6b6b;
text-align: center;
margin-bottom: 3px;
}
.section-title {
font-family: "Cinzel", serif;
font-size: 11px;
font-weight: bold;
color: #2c2c2c;
text-transform: uppercase;
margin: 0 0 4px 0;
padding: 3px;
background: rgba(255, 255, 255, 0.6);
border: 2px solid #6b6b6b;
border-radius: 4px;
text-align: center;
}
.race-item {
display: flex;
align-items: center;
gap: 6px;
.item-img {
width: 30px;
height: 30px;
border: 2px solid #6b6b6b;
border-radius: 4px;
object-fit: cover;
cursor: pointer;
}
.race-name {
flex: 1;
font-family: "Cinzel", serif;
font-size: 12px;
font-weight: bold;
color: #2c2c2c;
text-align: center;
}
.controls {
display: flex;
gap: 6px;
a {
color: #6b6b6b;
cursor: pointer;
transition: color 0.2s;
&:hover {
color: #2c2c2c;
}
i {
font-size: 14px;
}
}
}
}
.no-race {
text-align: center;
font-family: "Crimson Text", serif;
font-size: 11px;
color: #6b6b6b;
font-style: italic;
padding: 5px;
}
input {
width: 100%;
background: transparent;
border: none;
font-family: "Cinzel", serif;
font-size: 14px;
text-align: center;
}
}
}
.classes-section {
display: flex;
flex-direction: column;
gap: 8px;
.class-box {
padding: 6px;
background: rgba(255, 255, 255, 0.5);
border: 2px solid #6b6b6b;
border-radius: 6px;
.class-label {
font-family: "Cinzel", serif;
font-size: 10px;
color: #6b6b6b;
text-align: center;
margin-bottom: 3px;
}
.class-content {
.class-item {
display: flex;
align-items: center;
gap: 6px;
.item-img {
width: 28px;
height: 28px;
border: 2px solid #6b6b6b;
border-radius: 4px;
object-fit: cover;
cursor: pointer;
}
.class-name {
flex: 1;
font-family: "Cinzel", serif;
font-size: 11px;
font-weight: bold;
color: #2c2c2c;
text-align: center;
}
.controls {
display: flex;
gap: 6px;
a {
color: #6b6b6b;
cursor: pointer;
transition: color 0.2s;
&:hover {
color: #2c2c2c;
}
i {
font-size: 12px;
}
}
}
}
.no-class {
text-align: center;
font-family: "Crimson Text", serif;
font-size: 11px;
color: #6b6b6b;
font-style: italic;
padding: 5px;
}
}
.class-input {
input {
width: 100%;
background: transparent;
border: none;
font-family: "Cinzel", serif;
font-size: 14px;
text-align: center;
}
}
}
}
.origin-section {
.origin-box {
padding: 15px;
background: rgba(255, 255, 255, 0.5);
border: 3px solid #6b6b6b;
border-radius: 8px;
min-height: 350px;
textarea {
width: 100%;
min-height: 320px;
background: transparent;
border: none;
font-family: "Crimson Text", serif;
font-size: 13px;
line-height: 1.6;
resize: vertical;
}
}
}
}
}
+26
View File
@@ -0,0 +1,26 @@
.character-path-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);
}
}
+79
View File
@@ -0,0 +1,79 @@
// Sub-attributes tab styling
.character-subattributes.tab {
.subattributes-content {
padding: 1rem;
.subattributes-list {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 0.5rem;
}
.subattribute-item {
background: rgba(0, 0, 0, 0.1);
border: 1px solid var(--color-border-dark-secondary);
border-radius: 4px;
padding: 0.4rem;
display: flex;
flex-direction: column;
align-items: center;
gap: 0.15rem;
text-align: center;
cursor: pointer;
text-decoration: none;
.subattribute-header {
display: flex;
align-items: center;
gap: 0.3rem;
i.fa-dice-d20 {
font-size: 1em;
color: var(--color-text-dark-primary);
opacity: 0.7;
}
}
&:hover {
background: rgba(0, 0, 0, 0.2);
border-color: var(--color-text-light-primary);
transform: translateY(-1px);
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
.subattribute-header i.fa-dice-d20 {
opacity: 1;
color: var(--color-text-dark-primary);
}
}
.subattribute-name {
font-weight: bold;
font-size: 0.75em;
color: var(--color-text-dark-primary);
line-height: 1.2;
}
.subattribute-value {
font-size: 1em;
font-weight: bold;
color: var(--color-text-dark-primary);
min-width: 2em;
padding: 0.15rem 0.3rem;
background: rgba(255, 255, 255, 0.1);
border-radius: 3px;
}
}
}
}
// Responsive adjustments
@media (max-width: 768px) {
.character-subattributes.tab {
.subattributes-content {
.subattributes-list {
grid-template-columns: repeat(3, 1fr);
}
}
}
}
+312 -23
View File
@@ -1,7 +1,107 @@
.character-content {
.sheet-common();
.character-sheet-common();
font-family: var(--font-primary);
font-size: calc(var(--font-size-standard) * 1);
color: var(--color-dark-1);
background-image: url("../assets/ui/prism_rpg_background.webp");
background-size: cover;
background-position: center;
background-repeat: no-repeat;
overflow: scroll;
nav.tabs {
background-image: url("../assets/ui/prism_rpg_background.webp");
background-size: cover;
background-position: center;
background-repeat: no-repeat;
padding: 2px 0;
}
nav.tabs [data-tab] {
color: #636060;
font-size: calc(var(--font-size-standard) * 1.1);
font-family: var(--font-secondary);
font-weight: bold;
padding: 3px 16px;
background-color: rgba(255, 255, 255, 0.3);
border-radius: 4px;
text-shadow: 1px 1px 2px rgba(255, 255, 255, 0.8);
transition: all 0.2s ease;
cursor: pointer;
border: 2px solid transparent;
&:hover {
background-color: rgba(255, 255, 255, 0.7);
color: #1a1a1a;
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3);
border-color: rgba(107, 107, 107, 0.3);
}
}
nav.tabs [data-tab].active {
color: #000000;
background-color: rgba(255, 255, 255, 0.85);
text-shadow: 1px 1px 3px rgba(255, 255, 255, 1);
border: 2px solid #6b6b6b;
box-shadow: 0 3px 6px rgba(0, 0, 0, 0.25);
&:hover {
background-color: rgba(255, 255, 255, 0.95);
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.35);
transform: translateY(-2px);
border-color: #4a4a4a;
}
}
input:disabled,
select:disabled {
background-color: rgba(0, 0, 0, 0.2);
border-color: transparent;
color: var(--color-dark-3);
}
input,
select {
height: 1.5rem;
background-color: rgba(0, 0, 0, 0.1);
border-color: var(--color-dark-6);
color: var(--color-dark-2);
}
input[name="name"] {
height: 2.5rem;
margin-right: 4px;
font-family: var(--font-secondary);
font-size: calc(var(--font-size-standard) * 1.2);
font-weight: bold;
border: none;
}
fieldset {
margin-bottom: 4px;
border-radius: 4px;
}
.form-fields {
input,
select {
text-align: center;
font-size: calc(var(--font-size-standard) * 1);
}
select {
font-family: var(--font-secondary);
font-size: calc(var(--font-size-standard) * 1);
}
}
legend {
font-family: var(--font-secondary);
font-size: calc(var(--font-size-standard) * 1.2);
font-weight: bold;
letter-spacing: 1px;
}
.character-sheet-common();
}
.character-main {
@@ -223,6 +323,13 @@
}
.tab.character-biography .main-div {
background-image: url("../assets/ui/prism_rpg_background.webp");
background-size: cover;
background-position: center;
background-repeat: no-repeat;
padding: 8px 10px;
min-height: 100%;
.biodata {
display: grid;
grid-template-columns: repeat(4, 1fr);
@@ -253,6 +360,13 @@
}
.tab.character-skills .main-div {
background-image: url("../assets/ui/prism_rpg_background.webp");
background-size: cover;
background-position: center;
background-repeat: no-repeat;
padding: 8px 10px;
min-height: 100%;
display: grid;
grid-template-columns: 1fr;
legend {
@@ -263,58 +377,212 @@
}
.skills {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 4px;
grid-template-columns: repeat(2, 1fr);
gap: 6px;
.skill {
display: flex;
align-items: center;
gap: 4px;
.item-img {
width: 24px;
height: 24px;
gap: 8px;
padding: 6px 10px;
background: rgba(255, 255, 255, 0.3);
border: 2px solid #6b6b6b;
border-radius: 6px;
transition: all 0.2s;
&:hover {
background: rgba(255, 255, 255, 0.5);
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15);
}
&.is-core-skill {
background: rgba(255, 235, 180, 0.4);
border-color: #d4a017;
&:hover {
background: rgba(255, 235, 180, 0.6);
}
}
.item-img {
width: 32px;
height: 32px;
border: 2px solid #6b6b6b;
border-radius: 4px;
object-fit: cover;
flex-shrink: 0;
}
.name {
min-width: 12rem;
flex: 1;
min-width: 0;
a {
font-family: "Cinzel", serif;
font-size: 14px;
font-weight: 600;
color: #2c2c2c;
i {
margin-right: 6px;
color: #6b6b6b;
}
&:hover {
color: #000;
}
}
}
.score {
font-family: "Cinzel", serif;
font-size: 16px;
font-weight: bold;
color: #2c2c2c;
min-width: 50px;
text-align: center;
.advanced-icon {
color: #d4a017;
font-size: 18px;
margin-left: 4px;
}
}
.controls {
display: flex;
gap: 8px;
flex-shrink: 0;
a {
color: #6b6b6b;
font-size: 14px;
transition: color 0.2s;
&:hover {
color: #2c2c2c;
}
}
}
}
}
.racial-abilities {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 4px;
grid-template-columns: repeat(2, 1fr);
gap: 6px;
.racial-ability {
display: flex;
align-items: center;
gap: 4px;
.item-img {
width: 24px;
height: 24px;
gap: 8px;
padding: 6px 10px;
background: rgba(200, 255, 200, 0.2);
border: 2px solid #6b9b6b;
border-radius: 6px;
transition: all 0.2s;
&:hover {
background: rgba(200, 255, 200, 0.4);
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15);
}
.item-img {
width: 32px;
height: 32px;
border: 2px solid #6b9b6b;
border-radius: 4px;
object-fit: cover;
flex-shrink: 0;
}
.name {
min-width: 12rem;
flex: 1;
min-width: 0;
font-family: "Cinzel", serif;
font-size: 14px;
font-weight: 600;
color: #2c2c2c;
}
.controls {
display: flex;
gap: 8px;
flex-shrink: 0;
a {
color: #6b9b6b;
font-size: 14px;
transition: color 0.2s;
&:hover {
color: #3c6b3c;
}
}
}
}
}
.vulnerabilities {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 4px;
grid-template-columns: repeat(2, 1fr);
gap: 6px;
.vulnerability {
display: flex;
align-items: center;
gap: 4px;
.item-img {
width: 24px;
height: 24px;
gap: 8px;
padding: 6px 10px;
background: rgba(255, 200, 200, 0.2);
border: 2px solid #9b6b6b;
border-radius: 6px;
transition: all 0.2s;
&:hover {
background: rgba(255, 200, 200, 0.4);
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15);
}
.item-img {
width: 32px;
height: 32px;
border: 2px solid #9b6b6b;
border-radius: 4px;
object-fit: cover;
flex-shrink: 0;
}
.name {
min-width: 12rem;
flex: 1;
min-width: 0;
font-family: "Cinzel", serif;
font-size: 14px;
font-weight: 600;
color: #2c2c2c;
}
.controls {
display: flex;
gap: 8px;
flex-shrink: 0;
a {
color: #9b6b6b;
font-size: 14px;
transition: color 0.2s;
&:hover {
color: #6b3c3c;
}
}
}
}
}
}
.tab.character-equipment .main-div {
background-image: url("../assets/ui/prism_rpg_background.webp");
background-size: cover;
background-position: center;
background-repeat: no-repeat;
padding: 8px 10px;
min-height: 100%;
display: grid;
grid-template-columns: 1fr;
legend {
@@ -354,6 +622,13 @@
}
.tab.character-combat .main-div {
background-image: url("../assets/ui/prism_rpg_background.webp");
background-size: cover;
background-position: center;
background-repeat: no-repeat;
padding: 8px 10px;
min-height: 100%;
display: grid;
grid-template-columns: 1fr;
legend {
@@ -504,6 +779,13 @@
}
.tab.character-spells .main-div {
background-image: url("../assets/ui/prism_rpg_background.webp");
background-size: cover;
background-position: center;
background-repeat: no-repeat;
padding: 8px 10px;
min-height: 100%;
display: grid;
grid-template-columns: 1fr;
legend {
@@ -565,6 +847,13 @@
}
.tab.character-miracles .main-div {
background-image: url("../assets/ui/prism_rpg_background.webp");
background-size: cover;
background-position: center;
background-repeat: no-repeat;
padding: 8px 10px;
min-height: 100%;
display: grid;
grid-template-columns: 1fr;
legend {
+598 -1
View File
@@ -1,3 +1,375 @@
// Chat Message Card Styles - Applied globally for chat messages
.chat-log .message-content {
.prismrpg-chat-card {
font-family: var(--font-primary);
border-radius: 6px;
overflow: hidden;
background: linear-gradient(135deg, rgba(0,0,0,0.05) 0%, rgba(0,0,0,0.02) 100%);
border: 1px solid rgba(0,0,0,0.2);
margin: 2px 0;
.chat-header {
display: flex;
gap: 8px;
padding: 6px 8px;
background: linear-gradient(135deg, #2c2c2c 0%, #1a1a1a 100%);
border-bottom: 1px solid #444;
.chat-portrait {
flex-shrink: 0;
width: 36px;
height: 36px;
border-radius: 50%;
overflow: hidden;
border: 2px solid #666;
box-shadow: 0 1px 4px rgba(0,0,0,0.4);
img {
width: 100%;
height: 100%;
object-fit: cover;
}
}
.chat-title {
flex: 1;
display: flex;
flex-direction: column;
justify-content: center;
gap: 2px;
.actor-name {
font-weight: bold;
font-size: 0.95em;
color: #e0e0e0;
text-shadow: 1px 1px 2px rgba(0,0,0,0.5);
line-height: 1.1;
}
.roll-name {
font-size: 0.8em;
color: #aaa;
line-height: 1.1;
}
.roll-type-badge {
display: inline-block;
padding: 1px 6px;
border-radius: 10px;
font-size: 0.7em;
font-weight: bold;
text-transform: uppercase;
margin-top: 2px;
width: fit-content;
&.attack {
background: linear-gradient(135deg, #c41e3a 0%, #8b0000 100%);
color: white;
box-shadow: 0 1px 2px rgba(196, 30, 58, 0.4);
}
}
.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;
align-items: center;
gap: 3px;
padding: 2px 6px;
background: rgba(0, 0, 0, 0.2);
border-radius: 3px;
font-size: 0.75em;
i {
color: #888;
font-size: 0.85em;
}
&.apc {
background: rgba(255, 193, 7, 0.2);
border: 1px solid rgba(255, 193, 7, 0.4);
i { color: #ffc107; }
}
&.damage {
background: rgba(244, 67, 54, 0.2);
border: 1px solid rgba(244, 67, 54, 0.4);
i { color: #f44336; }
}
&.range {
background: rgba(76, 175, 80, 0.2);
border: 1px solid rgba(76, 175, 80, 0.4);
i { color: #4caf50; }
}
&.reload {
background: rgba(255, 152, 0, 0.2);
border: 1px solid rgba(255, 152, 0, 0.4);
i { color: #ff9800; }
}
&.mana {
background: rgba(33, 150, 243, 0.2);
border: 1px solid rgba(33, 150, 243, 0.4);
i { color: #2196f3; }
}
&.upkeep {
background: rgba(156, 39, 176, 0.2);
border: 1px solid rgba(156, 39, 176, 0.4);
i { color: #9c27b0; }
}
&.characteristic {
background: rgba(103, 58, 183, 0.2);
border: 1px solid rgba(103, 58, 183, 0.4);
i { color: #673ab7; }
}
}
}
}
.special-badge {
display: inline-block;
padding: 2px 6px;
background: linear-gradient(135deg, #ff6b6b 0%, #c92a2a 100%);
color: white;
border-radius: 3px;
font-size: 0.75em;
font-weight: bold;
width: fit-content;
box-shadow: 0 1px 2px rgba(255, 107, 107, 0.4);
}
.aiming-info {
display: flex;
align-items: center;
gap: 4px;
padding: 3px 6px;
background: rgba(76, 175, 80, 0.1);
border-left: 2px solid #4caf50;
border-radius: 3px;
font-size: 0.8em;
i {
color: #4caf50;
}
}
.formula-display {
display: flex;
align-items: center;
gap: 6px;
padding: 4px 8px;
background: rgba(0, 0, 0, 0.3);
border-radius: 3px;
font-family: 'Courier New', monospace;
font-size: 0.85em;
border: 1px dashed rgba(255, 255, 255, 0.2);
i {
color: #888;
}
}
.modifier-info {
display: flex;
gap: 8px;
padding: 3px 6px;
background: rgba(255, 255, 255, 0.05);
border-radius: 3px;
font-size: 0.75em;
span {
color: #aaa;
}
}
.dice-breakdown {
display: flex;
align-items: center;
gap: 4px;
padding: 2px 6px;
background: rgba(255, 255, 255, 0.05);
border-radius: 3px;
font-size: 0.8em;
i {
color: #888;
}
}
.roll-damage-button {
padding: 4px 10px;
background: linear-gradient(135deg, #c41e3a 0%, #8b0000 100%);
color: white;
border: none;
border-radius: 4px;
font-weight: bold;
font-size: 0.85em;
cursor: pointer;
display: flex;
align-items: center;
gap: 6px;
justify-content: center;
transition: all 0.2s;
box-shadow: 0 1px 3px rgba(196, 30, 58, 0.4);
margin-top: 4px;
&:hover {
transform: translateY(-1px);
box-shadow: 0 2px 4px rgba(196, 30, 58, 0.6);
}
i {
font-size: 1em;
}
}
}
.roll-result {
padding: 8px;
background: linear-gradient(135deg, rgba(212, 175, 55, 0.2) 0%, rgba(212, 175, 55, 0.1) 100%);
border-top: 1px solid rgba(212, 175, 55, 0.5);
.result-total {
display: flex;
align-items: center;
justify-content: space-between;
gap: 10px;
.total-label {
font-size: 0.85em;
color: #aaa;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.total-value {
font-size: 1.8em;
font-weight: bold;
color: #d4af37;
text-shadow: 0 1px 3px rgba(212, 175, 55, 0.5);
line-height: 1;
}
}
.d30-result {
margin-top: 4px;
padding: 3px 6px;
background: rgba(0, 0, 0, 0.2);
border-radius: 3px;
font-size: 0.8em;
text-align: center;
i {
color: #d4af37;
margin-right: 4px;
}
}
}
.result-badge {
padding: 6px;
text-align: center;
font-size: 0.9em;
font-weight: bold;
border-top: 1px solid;
&.success {
background: linear-gradient(135deg, rgba(76, 175, 80, 0.3) 0%, rgba(76, 175, 80, 0.1) 100%);
border-color: #4caf50;
color: #4caf50;
}
&.failure {
background: linear-gradient(135deg, rgba(244, 67, 54, 0.3) 0%, rgba(244, 67, 54, 0.1) 100%);
border-color: #f44336;
color: #f44336;
}
i {
margin-right: 6px;
font-size: 1em;
}
}
.damage-info {
padding: 6px;
background: rgba(255, 255, 255, 0.05);
border-radius: 3px;
font-size: 0.8em;
color: #aaa;
}
}
// Old fortune and ask-roll styles
&.fortune {
img {
border: 0px;
@@ -22,7 +394,8 @@
}
}
}
.button.control, .fortune-accepted {
.button.control,
.fortune-accepted {
display: flex;
justify-content: center;
align-items: center;
@@ -38,3 +411,227 @@
font-family: var(--font-secondary);
font-size: calc(var(--font-size-standard) * 1.2);
}
// Item Chat Card Styles
.prismrpg-item-chat-card {
font-family: var(--font-primary);
border-radius: 4px;
overflow: hidden;
background: linear-gradient(135deg, rgba(0,0,0,0.05) 0%, rgba(0,0,0,0.02) 100%);
border: 1px solid rgba(0,0,0,0.2);
margin: 2px 0;
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
.item-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 4px 8px;
background: linear-gradient(135deg, #2c2c2c 0%, #1a1a1a 100%);
border-bottom: 1px solid #444;
h3 {
margin: 0;
font-size: 0.95em;
font-weight: bold;
color: #d4af37;
text-shadow: 1px 1px 2px rgba(0,0,0,0.5);
font-family: var(--font-secondary);
}
.item-type {
padding: 1px 6px;
border-radius: 10px;
font-size: 0.65em;
font-weight: bold;
text-transform: uppercase;
background: rgba(255, 255, 255, 0.15);
color: #fff;
border: 1px solid rgba(255, 255, 255, 0.2);
letter-spacing: 0.3px;
}
}
.item-image {
display: flex;
justify-content: center;
align-items: center;
padding: 6px;
background: rgba(0, 0, 0, 0.1);
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
img {
max-width: 50px;
max-height: 50px;
border-radius: 3px;
border: 1px solid rgba(212, 175, 55, 0.3);
box-shadow: 0 1px 3px rgba(0,0,0,0.3);
transition: transform 0.2s ease;
&:hover {
transform: scale(1.05);
}
}
}
.item-description {
padding: 6px 8px;
color: #000;
font-size: 0.8em;
line-height: 1.3;
background: rgba(255, 255, 255, 0.8);
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
font-style: italic;
}
.item-details {
padding: 6px 8px;
display: flex;
flex-direction: column;
gap: 3px;
.item-detail {
display: flex;
align-items: center;
padding: 2px 6px;
background: rgba(255, 255, 255, 0.03);
border-left: 2px solid rgba(212, 175, 55, 0.5);
border-radius: 2px;
font-size: 0.8em;
strong {
color: #d4af37;
margin-right: 6px;
min-width: 90px;
font-weight: bold;
}
&:nth-child(even) {
background: rgba(0, 0, 0, 0.05);
}
}
}
// Special styling for weapon items
&.weapon-item {
.item-header {
background: linear-gradient(135deg, #c41e3a 0%, #8b0000 100%);
}
}
// Special styling for armor items
&.armor-item {
.item-header {
background: linear-gradient(135deg, #4a5cf7 0%, #2c3e9e 100%);
}
}
// Special styling for spell items
&.spell-item {
.item-header {
background: linear-gradient(135deg, #9b59b6 0%, #6c3483 100%);
}
}
// Special styling for skill items
&.skill-item {
.item-header {
background: linear-gradient(135deg, #16a085 0%, #0e6655 100%);
}
}
// Special styling for equipment items
&.equipment-item {
.item-header {
background: linear-gradient(135deg, #f39c12 0%, #b8730f 100%);
}
}
}
}
// New round message
.new-round-message {
.chat-title .new-round-label {
font-size: var(--font-size-11);
font-style: italic;
color: #8a7a5a;
margin-top: 1px;
}
.new-round-actors {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 6px;
padding: 6px 0 2px;
.new-round-restore-btn {
display: flex;
align-items: center;
gap: 6px;
width: calc(50% - 3px);
padding: 4px 8px 4px 6px;
border: 1px solid #7a6a45;
border-radius: 4px;
background: linear-gradient(135deg, #f5e6c8 0%, #e8d5a0 100%);
cursor: pointer;
font-size: var(--font-size-13);
color: #3a2e1a;
overflow: hidden;
span {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
min-width: 0;
}
img {
flex-shrink: 0;
width: 24px;
height: 24px;
border: none;
border-radius: 3px;
object-fit: cover;
}
&:hover:not(:disabled) {
background: linear-gradient(135deg, #fdf3dc 0%, #f0e0b0 100%);
border-color: #a08040;
}
&:disabled,
&.restored {
opacity: 0.45;
cursor: default;
}
}
.new-round-all-btn {
width: 100%;
background: linear-gradient(135deg, #c8dff5 0%, #a0c0e8 100%);
border-color: #4a6a8a;
color: #1a2e3a;
font-weight: bold;
&:hover:not(:disabled) {
background: linear-gradient(135deg, #dcedfc 0%, #b0d0f0 100%);
border-color: #3a5a7a;
}
}
}
}
// Token HUD — separator between Afflictions and Imbuements
.palette.status-effects {
.status-separator {
grid-column: 1 / -1;
width: 100%;
height: 2px;
border: none;
border-top: 2px solid rgba(255, 255, 255, 0.35);
margin: 5px 0;
position: relative;
}
}
+135
View File
@@ -13,4 +13,139 @@
label {
flex: 10%;
}
.advancement-level {
margin-bottom: 1.5rem;
padding: 0.5rem;
border: 1px solid rgba(0, 0, 0, 0.2);
border-radius: 4px;
h3 {
display: flex;
align-items: center;
justify-content: space-between;
margin: 0 0 0.5rem 0;
padding-bottom: 0.5rem;
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
.level-title {
color: #000000;
font-weight: bold;
font-size: 80%;
}
.add-advancement {
padding: 0.25rem 0.5rem;
background: var(--color-control-bg);
border: 1px solid var(--color-border-dark);
border-radius: 3px;
cursor: pointer;
&:hover {
background: var(--color-control-bg-hover);
}
i {
margin: 0;
}
}
}
.empty-advancements {
font-style: italic;
color: rgba(0, 0, 0, 0.5);
margin: 0.5rem 0;
}
}
.advancement-list {
display: flex;
flex-direction: column;
gap: 1rem;
}
.advancement-item {
padding: 0.75rem;
background: rgba(0, 0, 0, 0.05);
border-radius: 4px;
.advancement-header {
display: flex;
align-items: center;
gap: 0.5rem;
margin-bottom: 0.5rem;
.advancement-icon {
width: 40px;
height: 40px;
border: 1px solid rgba(0, 0, 0, 0.2);
border-radius: 3px;
cursor: pointer;
flex-shrink: 0;
&:hover {
border-color: var(--color-border-highlight);
}
}
input[type="text"] {
flex: 1;
font-weight: bold;
}
.toggle-advancement-description {
padding: 0.25rem 0.5rem;
background: var(--color-control-bg);
border: 1px solid var(--color-border-dark);
border-radius: 3px;
cursor: pointer;
flex-shrink: 0;
margin-left: 0.25rem;
&:hover {
background: var(--color-control-bg-hover);
}
i {
margin: 0;
color: rgba(0, 0, 0, 0.7);
}
}
.delete-advancement {
padding: 0.25rem 0.5rem;
background: var(--color-control-bg);
border: 1px solid var(--color-border-dark);
border-radius: 3px;
cursor: pointer;
flex-shrink: 0;
&:hover {
background: rgba(255, 0, 0, 0.1);
border-color: rgba(255, 0, 0, 0.5);
}
i {
margin: 0;
color: rgba(255, 0, 0, 0.7);
}
}
}
.advancement-description {
margin-top: 0.5rem;
overflow: hidden;
transition:
max-height 0.3s ease-out,
opacity 0.3s ease-out;
max-height: 500px;
opacity: 1;
&.collapsed {
max-height: 0;
opacity: 0;
margin-top: 0;
}
}
}
}
+270
View File
@@ -0,0 +1,270 @@
// Active Effects styling - compact and modern design
// Effects container
.effects-container {
padding: 0.5rem;
}
// Effect categories sections
.effect-category {
margin-bottom: 1rem;
border: 1px solid rgba(0, 0, 0, 0.2);
border-radius: 4px;
overflow: hidden;
background: rgba(255, 255, 255, 0.02);
&:last-child {
margin-bottom: 0;
}
}
// Effect list styling
.stat-list {
list-style: none;
margin: 0;
padding: 0;
&.alternate-list {
.list-item {
padding: 0.4rem 0.6rem;
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
align-items: center;
gap: 0.5rem;
min-height: 32px;
&.items-title-bg {
background: linear-gradient(
to bottom,
rgba(0, 0, 0, 0.25),
rgba(0, 0, 0, 0.15)
);
border-bottom: 2px solid rgba(0, 0, 0, 0.4);
padding: 0.5rem 0.6rem;
font-weight: bold;
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1);
h3 {
margin: 0;
font-size: 1rem;
font-family: var(--font-secondary);
color: rgba(0, 0, 0, 0.9);
text-shadow: 0 1px 2px rgba(255, 255, 255, 0.3);
font-weight: bold;
}
.short-label {
font-size: 0.7rem;
text-transform: uppercase;
font-weight: 600;
color: rgba(0, 0, 0, 0.7);
letter-spacing: 0.5px;
}
}
&.list-item-shadow {
transition: all 0.2s ease;
background: rgba(255, 255, 255, 0.05);
&:hover {
background: rgba(255, 255, 255, 0.15);
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
}
}
&:last-child {
border-bottom: none;
}
}
}
}
// Effect image - compact size
.sheet-competence-img {
width: 28px;
height: 28px;
border: 1px solid rgba(0, 0, 0, 0.3);
border-radius: 4px;
object-fit: cover;
flex-shrink: 0;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
}
.item-name-img {
display: flex;
align-items: center;
flex-shrink: 0;
text-decoration: none;
&:hover .sheet-competence-img {
border-color: rgba(13, 110, 253, 0.6);
box-shadow: 0 2px 4px rgba(13, 110, 253, 0.3);
}
}
// Effect labels - compact spacing
.item-name-label-header-long {
flex: 2;
display: flex;
align-items: center;
.items-title-text {
font-weight: bold;
}
}
.item-name-label-long {
flex: 2;
font-weight: 500;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
color: rgba(0, 0, 0, 0.9);
font-size: 0.9rem;
}
.item-field-label-short {
flex: 0 0 90px;
font-size: 0.8rem;
text-align: center;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
color: rgba(0, 0, 0, 0.7);
font-style: italic;
}
.item-field-label-medium {
flex: 0 0 100px;
font-size: 0.8rem;
text-align: center;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
color: rgba(0, 0, 0, 0.7);
font-style: italic;
}
// Filler for spacing
.item-filler {
flex: 1;
min-width: 10px;
}
// Effect controls - compact and modern
.item-controls {
display: flex;
gap: 0.3rem;
align-items: center;
flex-shrink: 0;
&.item-controls-fixed {
flex: 0 0 auto;
}
&.effect-controls {
gap: 0.25rem;
}
}
.effect-control {
display: flex;
align-items: center;
justify-content: center;
width: 28px;
height: 28px;
border: 1px solid rgba(0, 0, 0, 0.2);
background: linear-gradient(
to bottom,
rgba(255, 255, 255, 0.8),
rgba(240, 240, 240, 0.8)
);
border-radius: 4px;
cursor: pointer;
transition: all 0.15s ease;
color: rgba(0, 0, 0, 0.7);
text-decoration: none;
padding: 0;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
i {
font-size: 0.85rem;
margin: 0;
}
&:hover {
background: linear-gradient(
to bottom,
rgba(255, 255, 255, 1),
rgba(230, 230, 230, 1)
);
border-color: rgba(0, 0, 0, 0.4);
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
transform: translateY(-1px);
}
&:active {
transform: translateY(0);
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
}
&.item-edit:hover {
background: linear-gradient(
to bottom,
rgba(13, 110, 253, 0.2),
rgba(13, 110, 253, 0.3)
);
color: rgba(13, 110, 253, 1);
border-color: rgba(13, 110, 253, 0.5);
}
// New effect button styling
&[data-action="create-effect"] {
width: auto;
padding: 0.3rem 0.5rem;
gap: 0.3rem;
font-size: 0.7rem;
font-weight: 600;
background: linear-gradient(
to bottom,
rgba(40, 167, 69, 0.25),
rgba(40, 167, 69, 0.35)
);
border-color: rgba(40, 167, 69, 0.4);
color: rgba(25, 100, 42, 1);
text-transform: uppercase;
letter-spacing: 0.3px;
&:hover {
background: linear-gradient(
to bottom,
rgba(40, 167, 69, 0.35),
rgba(40, 167, 69, 0.45)
);
border-color: rgba(40, 167, 69, 0.6);
color: rgba(25, 100, 42, 1);
}
i {
font-size: 0.75rem;
}
}
// Delete button specific styling
&[data-action="effect-delete"]:hover {
background: linear-gradient(
to bottom,
rgba(220, 53, 69, 0.2),
rgba(220, 53, 69, 0.3)
);
color: rgba(220, 53, 69, 1);
border-color: rgba(220, 53, 69, 0.5);
}
}
// Flexrow utility
.flexrow {
display: flex;
flex-direction: row;
align-items: center;
gap: 0.5rem;
}
+8 -1
View File
@@ -4,20 +4,27 @@
.prismrpg {
@import "mixins.less";
@import "character.less";
@import "character-main-v2.less";
@import "character-subattributes.less";
@import "monster.less";
@import "skill.less";
@import "racial-ability.less";
@import "ability.less";
@import "weapon.less";
@import "armor.less";
@import "spell.less";
@import "vulnerability.less";
@import "chat.less";
@import "equipment.less";
@import "shield.less";
@import "miracle.less";
@import "race.less";
@import "class.less";
@import "character-path.less";
@import "effects.less";
@import "weapon-types-config.less";
}
@import "chat.less";
@import "roll.less";
@import "roll-dialog-modern.less";
@import "hud.less";
+5
View File
@@ -6,6 +6,11 @@
--logo-standard: url("../assets/ui/prism-rpg-logo-01.webp");
}
// Tab system - hide inactive tabs
.tab[data-group]:not(.active) {
display: none;
}
.initiative-area {
min-width: 8rem;
max-width: 8rem;
+305
View File
@@ -0,0 +1,305 @@
// Modern Roll Dialog Styling
// Matches the new chat message card design
.prismrpg-roll-dialog-modern {
font-family: var(--font-primary);
// Header section
.dialog-header {
background: linear-gradient(135deg, rgba(33, 33, 33, 0.95) 0%, rgba(66, 66, 66, 0.95) 100%);
border-bottom: 2px solid #d4af37;
padding: 8px 10px;
margin: -8px -8px 8px -8px;
border-radius: 4px 4px 0 0;
.character-info {
display: flex;
flex-direction: column;
gap: 4px;
.character-name {
font-size: 1em;
font-weight: bold;
color: #d4af37;
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.5);
}
.item-name {
display: flex;
align-items: center;
gap: 4px;
font-size: 0.9em;
color: #e0e0e0;
i {
color: #d4af37;
font-size: 0.85em;
}
&.weapon i {
color: #f44336;
}
&.spell i {
color: #9c27b0;
}
}
}
}
// Content area
.dialog-content {
display: flex;
flex-direction: column;
gap: 8px;
}
// Section styling
.option-section {
background: rgba(0, 0, 0, 0.1);
border: 1px solid rgba(0, 0, 0, 0.2);
border-radius: 4px;
padding: 6px 8px;
&.weapon-section {
border-left: 3px solid #f44336;
}
&.spell-section {
border-left: 3px solid #9c27b0;
}
.section-title {
display: flex;
align-items: center;
gap: 4px;
font-weight: bold;
font-size: 0.9em;
color: var(--color-dark-1);
margin-bottom: 6px;
padding-bottom: 4px;
border-bottom: 1px solid rgba(0, 0, 0, 0.15);
i {
color: #d4af37;
font-size: 0.85em;
}
}
}
// Info displays (ranges, spell stats)
.info-display {
display: flex;
align-items: center;
gap: 4px;
padding: 4px 6px;
background: rgba(0, 0, 0, 0.15);
border-radius: 3px;
margin-bottom: 6px;
font-size: 0.8em;
color: #666;
i {
color: #4caf50;
font-size: 0.85em;
}
.info-text {
flex: 1;
}
}
// Spell info badges (like in chat)
.spell-info-display {
display: flex;
flex-wrap: wrap;
gap: 4px;
margin-bottom: 6px;
.info-badge {
display: flex;
align-items: center;
gap: 3px;
padding: 3px 6px;
border-radius: 3px;
font-size: 0.75em;
font-weight: 500;
i {
font-size: 0.85em;
}
&.characteristic {
background: rgba(103, 58, 183, 0.2);
border: 1px solid rgba(103, 58, 183, 0.4);
color: #673ab7;
i { color: #673ab7; }
}
&.mana {
background: rgba(33, 150, 243, 0.2);
border: 1px solid rgba(33, 150, 243, 0.4);
color: #2196f3;
i { color: #2196f3; }
}
&.apc {
background: rgba(255, 193, 7, 0.2);
border: 1px solid rgba(255, 193, 7, 0.4);
color: #ffc107;
i { color: #ffc107; }
}
&.upkeep {
background: rgba(156, 39, 176, 0.2);
border: 1px solid rgba(156, 39, 176, 0.4);
color: #9c27b0;
i { color: #9c27b0; }
}
}
}
// Option rows (label + control)
.option-row {
display: flex;
align-items: center;
gap: 6px;
margin-bottom: 4px;
&:last-child {
margin-bottom: 0;
}
label {
font-size: 0.85em;
font-weight: 500;
color: var(--color-dark-2);
min-width: 90px;
}
select {
flex: 1;
}
}
// Styled selects
.styled-select {
background: rgba(255, 255, 255, 0.9);
border: 1px solid rgba(0, 0, 0, 0.2);
border-radius: 3px;
padding: 4px 8px;
font-size: 0.85em;
color: var(--color-dark-1);
cursor: pointer;
transition: all 0.2s ease;
&:hover {
border-color: #d4af37;
box-shadow: 0 0 4px rgba(212, 175, 55, 0.3);
}
&:focus {
outline: none;
border-color: #d4af37;
box-shadow: 0 0 6px rgba(212, 175, 55, 0.5);
}
&.advantage-select,
&.modifier-select {
text-align: center;
font-weight: 500;
}
}
// Checkbox group
.checkbox-group {
display: flex;
flex-direction: column;
gap: 3px;
margin-bottom: 6px;
.checkbox-label {
display: flex;
align-items: center;
cursor: pointer;
padding: 3px 4px;
border-radius: 3px;
transition: background 0.2s ease;
&:hover {
background: rgba(0, 0, 0, 0.05);
}
input[type="checkbox"] {
margin-right: 6px;
cursor: pointer;
width: 14px;
height: 14px;
accent-color: #d4af37;
}
.checkbox-text {
display: flex;
align-items: center;
gap: 4px;
font-size: 0.8em;
color: var(--color-dark-2);
i {
color: #888;
font-size: 0.85em;
}
}
input[type="checkbox"]:checked ~ .checkbox-text {
color: var(--color-dark-1);
font-weight: 500;
i {
color: #d4af37;
}
}
}
}
}
// Dialog application styling
.application.dialog.prismrpg {
.window-content {
background: linear-gradient(135deg, #f5f5f5 0%, #e0e0e0 100%);
padding: 8px;
}
// Make buttons match the modern style
.dialog-buttons {
padding: 6px 8px;
gap: 6px;
button {
background: linear-gradient(135deg, #4a4a4a 0%, #6a6a6a 100%);
border: 1px solid #3a3a3a;
color: white;
font-weight: 600;
padding: 6px 12px;
border-radius: 4px;
transition: all 0.2s ease;
&:hover {
background: linear-gradient(135deg, #5a5a5a 0%, #7a7a7a 100%);
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
transform: translateY(-1px);
}
&.default,
&[data-button="roll"] {
background: linear-gradient(135deg, #d4af37 0%, #f4cf67 100%);
border-color: #b49030;
color: #2a2a2a;
&:hover {
background: linear-gradient(135deg, #e4bf47 0%, #ffdf77 100%);
}
}
}
}
}
+54
View File
@@ -25,6 +25,30 @@
border-radius: 4px;
padding: 0.5rem;
}
.dialog-weapon-options {
margin-top: 8px;
.dialog-save {
margin: 4px 0;
label {
display: flex;
align-items: center;
cursor: pointer;
input[type="checkbox"] {
margin-right: 8px;
cursor: pointer;
}
}
select {
margin-left: 8px;
min-width: 10rem;
}
}
}
}
.prismrpg-range-defense-dialog {
@@ -156,4 +180,34 @@
font-size: calc(var(--font-size-standard) * 1);
text-shadow: 0 0 10px var(--color-shadow-primary);
}
// Roll Damage button in weapon attack messages
.damage-roll-button {
margin-top: 0.5em;
margin-bottom: 0.25em;
.roll-damage-button {
background: linear-gradient(135deg, #8b0000 0%, #dc143c 100%);
color: white;
border: 1px solid #6b0000;
border-radius: 3px;
padding: 4px 10px;
font-weight: 600;
cursor: pointer;
font-size: 0.85em;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
transition: all 0.2s ease;
&:hover {
background: linear-gradient(135deg, #a00000 0%, #ff1744 100%);
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
transform: translateY(-1px);
}
i {
margin-right: 4px;
font-size: 0.9em;
}
}
}
}
+149
View File
@@ -0,0 +1,149 @@
/* Weapon Types Configuration Dialog */
.weapon-types-config {
padding: 0;
}
.weapon-types-config .sheet-tabs {
margin: 0;
border-bottom: 2px solid #444;
}
.weapon-types-config .content {
padding: 0.5rem;
max-height: 60vh;
overflow-y: auto;
}
.weapon-types-config .section-header {
display: flex;
justify-content: space-between;
align-items: center;
font-size: 1em;
font-weight: bold;
margin-bottom: 0.5rem;
padding-bottom: 0.25rem;
border-bottom: 1px solid #666;
}
.weapon-types-config .weapon-type-entry,
.weapon-types-config .weapon-group-entry {
background: rgba(0, 0, 0, 0.3);
padding: 0.75rem;
margin-bottom: 0.75rem;
border-radius: 4px;
border: 1px solid #555;
display: flex;
gap: 0.75rem;
align-items: start;
}
.weapon-types-config .weapon-type-entry .form-fields {
display: grid;
grid-template-columns: 100px 3fr 70px 70px;
gap: 0.75rem;
flex: 1;
align-items: center;
}
.weapon-types-config .weapon-group-entry .form-fields {
display: grid;
grid-template-columns: 120px 2fr 120px 150px 3fr;
gap: 0.75rem;
flex: 1;
align-items: center;
}
.weapon-types-config .form-group {
display: flex;
flex-direction: column;
gap: 0.15rem;
}
.weapon-types-config .form-group label {
font-size: 0.75em;
color: #fff;
font-weight: bold;
white-space: nowrap;
text-shadow: 0 0 2px rgba(0, 0, 0, 0.8);
}
.weapon-types-config input[type="text"],
.weapon-types-config input[type="number"],
.weapon-types-config textarea {
background: rgba(0, 0, 0, 0.5);
border: 1px solid #666;
color: #fff;
padding: 0.2rem 0.4rem;
border-radius: 3px;
font-size: 0.9em;
}
.weapon-types-config input[readonly] {
background: rgba(0, 0, 0, 0.7);
color: #999;
cursor: not-allowed;
font-size: 0.8em;
}
.weapon-types-config textarea {
resize: vertical;
min-height: 40px;
font-size: 0.85em;
}
.weapon-types-config button[data-action] {
background: rgba(100, 100, 100, 0.5);
border: 1px solid #666;
color: #fff;
padding: 0.2rem 0.4rem;
border-radius: 3px;
cursor: pointer;
transition: all 0.2s;
}
.weapon-types-config button[data-action]:hover {
background: rgba(150, 150, 150, 0.5);
border-color: #888;
}
.weapon-types-config button[data-action="delete-weapon-type"],
.weapon-types-config button[data-action="delete-weapon-group"] {
background: rgba(139, 0, 0, 0.5);
align-self: center;
padding: 0.25rem;
min-width: auto;
width: auto;
font-size: 0.9em;
flex-shrink: 0;
}
.weapon-types-config button[data-action="delete-weapon-type"]:hover,
.weapon-types-config button[data-action="delete-weapon-group"]:hover {
background: rgba(200, 0, 0, 0.7);
}
.weapon-types-config .sheet-footer {
display: flex;
justify-content: space-between;
padding: 1rem;
border-top: 2px solid #444;
gap: 1rem;
}
.weapon-types-config .sheet-footer button {
padding: 0.5rem 1rem;
font-size: 1em;
}
.weapon-types-config .weapon-types-list,
.weapon-types-config .weapon-groups-list {
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.weapon-types-config .flexrow {
display: flex;
gap: 1rem;
align-items: flex-start;
}
+4 -2
View File
@@ -6,7 +6,7 @@
"download": "#{DOWNLOAD}#",
"url": "#{URL}#",
"license": "LICENSE",
"version": "13.0.0",
"version": "13.0.1",
"authors": [
{
"name": "Uberwald",
@@ -34,13 +34,15 @@
"Item": {
"skill": { "htmlFields": ["description"] },
"racial-ability": { "htmlFields": ["description"] },
"ability": { "htmlFields": ["description"] },
"weapon": { "htmlFields": ["description"] },
"armor": { "htmlFields": ["description"] },
"shield": { "htmlFields": ["description"] },
"spell": { "htmlFields": ["description"] },
"equipment": { "htmlFields": ["description"] },
"race": { "htmlFields": ["description", "racialPassiveDescription", "subraceAbilityDescription", "notes"] },
"class": { "htmlFields": ["description", "attributeBonuses", "notes", "features.level1", "features.level2", "features.level3", "features.level4", "features.level5", "features.level6", "features.level7", "features.level8", "features.level9", "features.level10"] }
"class": { "htmlFields": ["description", "attributeBonuses", "notes", "features.level1", "features.level2", "features.level3", "features.level4", "features.level5", "features.level6", "features.level7", "features.level8", "features.level9", "features.level10"] },
"character-path": { "htmlFields": ["description"] }
}
},
"grid": {
+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>
+38 -38
View File
@@ -10,6 +10,26 @@
{{formInput fields.name value=source.name}}
</div>
{{! Navigation des onglets }}
<nav class="sheet-tabs tabs" data-group="primary">
<a class="item {{tabs.details.cssClass}}" data-tab="details">{{localize
"PRISMRPG.Label.details"
}}</a>
<a
class="item {{tabs.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 Details }}
<div
class="tab {{tabs.details.cssClass}}"
data-group="primary"
data-tab="details"
>
{{! Armor Type (Light/Medium/Heavy) }}
{{formField
systemFields.armorType
@@ -48,44 +68,6 @@
label="PRISMRPG.Label.equipped"
}}
{{! Prism RPG: Armor Passive }}
<fieldset class="armor-passive">
<legend>{{localize "PRISMRPG.Label.armorPassive"}}</legend>
{{formField
systemFields.passive
value=system.passive
localize=true
label="PRISMRPG.Label.passiveName"
}}
<label>{{localize "PRISMRPG.Label.passiveDescription"}}</label>
{{formInput
systemFields.passiveDescription
enriched=enrichedPassiveDescription
value=system.passiveDescription
name="system.passiveDescription"
toggled=true
}}
</fieldset>
{{! Prism RPG: Armor Augment }}
<fieldset class="armor-augment">
<legend>{{localize "PRISMRPG.Label.armorAugment"}}</legend>
{{formField
systemFields.augment
value=system.augment
localize=true
label="PRISMRPG.Label.augmentName"
}}
<label>{{localize "PRISMRPG.Label.augmentDescription"}}</label>
{{formInput
systemFields.augmentDescription
enriched=enrichedAugmentDescription
value=system.augmentDescription
name="system.augmentDescription"
toggled=true
}}
</fieldset>
{{! Cost }}
{{formField
systemFields.cost
@@ -99,7 +81,14 @@
localize=true
label="PRISMRPG.Label.currency"
}}
</div>
{{! Onglet Description }}
<div
class="tab {{tabs.description.cssClass}}"
data-group="primary"
data-tab="description"
>
{{! Description }}
<fieldset>
<legend>{{localize "PRISMRPG.Label.description"}}</legend>
@@ -111,4 +100,15 @@
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>
+53 -51
View File
@@ -1,24 +1,20 @@
<section class="tab character-{{tab.id}} {{tab.cssClass}}" data-tab="{{tab.id}}" data-group="{{tab.group}}">
<section
class="tab character-{{tab.id}} {{tab.cssClass}}"
data-tab="{{tab.id}}"
data-group="{{tab.group}}"
>
<div class="main-div">
<fieldset>
<legend>{{localize "PRISMRPG.Label.biodata"}}</legend>
<div class="biodata">
<div class="biodata-elem">
<span class="name">Class</span>
{{formInput systemFields.biodata.fields.class value=system.biodata.class }}
</div>
{{!-- Class and Mortal fields removed - don't exist in DataModel --}}
<div class="biodata-elem">
<span class="name">Level</span>
{{formInput systemFields.biodata.fields.level value=system.biodata.level }}
</div>
<div class="biodata-elem">
<span class="name">Mortal</span>
{{formInput systemFields.biodata.fields.mortal value=system.biodata.mortal }}
</div>
<div class="biodata-elem">
<span class="name">Alignment</span>
{{formInput systemFields.biodata.fields.alignment value=system.biodata.alignment }}
{{formInput
systemFields.biodata.fields.level
value=system.biodata.level
}}
</div>
<div class="biodata-elem">
<span class="name">Age</span>
@@ -26,72 +22,78 @@
</div>
<div class="biodata-elem">
<span class="name">Height</span>
{{formInput systemFields.biodata.fields.height value=system.biodata.height }}
{{formInput
systemFields.biodata.fields.height
value=system.biodata.height
}}
</div>
<div class="biodata-elem">
<span class="name">Weight</span>
{{formInput systemFields.biodata.fields.weight value=system.biodata.weight }}
{{formInput
systemFields.biodata.fields.weight
value=system.biodata.weight
}}
</div>
<div class="biodata-elem">
<span class="name">Eyes</span>
{{formInput systemFields.biodata.fields.eyes value=system.biodata.eyes }}
{{formInput
systemFields.biodata.fields.eyes
value=system.biodata.eyes
}}
</div>
<div class="biodata-elem">
<span class="name">Hair</span>
{{formInput systemFields.biodata.fields.hair value=system.biodata.hair }}
{{formInput
systemFields.biodata.fields.hair
value=system.biodata.hair
}}
</div>
<div class="biodata-elem">
<span class="name">Dev. Points (Total)</span>
{{formInput systemFields.developmentPoints.fields.total value=system.developmentPoints.total }}
{{formInput
systemFields.developmentPoints.fields.total
value=system.developmentPoints.total
}}
</div>
<div class="biodata-elem">
<span class="name">Dev. Points (Rem.)</span>
{{formInput systemFields.developmentPoints.fields.remaining value=system.developmentPoints.remaining }}
{{formInput
systemFields.developmentPoints.fields.remaining
value=system.developmentPoints.remaining
}}
</div>
<div class="biodata-elem">
<span class="name">Magic User</span>
{{formInput systemFields.biodata.fields.magicUser value=system.biodata.magicUser }}
{{formInput
systemFields.biodata.fields.magicUser
value=system.biodata.magicUser
}}
</div>
<div class="biodata-elem">
<span class="name">Cleric User</span>
{{formInput systemFields.biodata.fields.clericUser value=system.biodata.clericUser }}
</div>
<div class="biodata-elem">
<span class="name">Save bonus (1/5levels)</span>
{{formInput systemFields.modifiers.fields.saveModifier value=system.modifiers.saveModifier disabled=true}}
</div>
{{#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>
{{!-- Cleric User, modifiers, and hpPerLevel fields removed - don't exist in DataModel --}}
</div>
</fieldset>
<fieldset>
<legend>{{localize "PRISMRPG.Label.description"}}</legend>
{{formInput systemFields.description enriched=enrichedDescription value=system.description name="system.description"
toggled=true}}
{{formInput
systemFields.description
enriched=enrichedDescription
value=system.description
name="system.description"
toggled=true
}}
</fieldset>
<fieldset>
<legend>{{localize "PRISMRPG.Label.notes"}}</legend>
{{formInput systemFields.notes enriched=enrichedNotes value=system.notes name="system.notes" toggled=true}}
{{formInput
systemFields.notes
enriched=enrichedNotes
value=system.notes
name="system.notes"
toggled=true
}}
</fieldset>
</div>
</section>
+115 -100
View File
@@ -1,106 +1,75 @@
<section class="tab character-{{tab.id}} {{tab.cssClass}}" data-tab="combat" data-group="sheet">
<section
class="tab character-{{tab.id}} {{tab.cssClass}}"
data-tab="combat"
data-group="sheet"
>
<div class="main-div">
<fieldset>
<legend>{{localize "PRISMRPG.Label.combatDetails"}}</legend>
<div class="combat-details">
<div class="combat-detail">
<button class="action ranged-attack-button" data-action="rangedAttackDefense">
{{localize "PRISMRPG.Label.rangedAttackDefense"}}
</button>
<button class="action ranged-attack-button" data-action="rollInitiative">
{{localize "PRISMRPG.Label.rollInitiative"}}
</button>
<div class="flexrow armor-hp">
<span class="name">{{localize "PRISMRPG.Label.armorHitPoints"}}</span>
{{formInput systemFields.combat.fields.armorHitPoints value=system.combat.armorHitPoints localize=true }}
<a data-action="armorHitPointsPlus"><i class="fa-solid fa-hexagon-plus"></i></a>
<a data-action="armorHitPointsMinus"><i class="fa-solid fa-hexagon-minus"></i></a>
</div>
<div class="flexrow granted">
<span class="">{{localize
"PRISMRPG.Label.grantedAttackDice"}}</a></span>
{{formInput systemFields.granted.fields.attackDice value=system.granted.attackDice disabled=isPlayMode }}
</div>
<div class="flexrow granted ">
<span class="">{{localize
"PRISMRPG.Label.grantedDefenseDice"}}</a></span>
{{formInput systemFields.granted.fields.defenseDice value=system.granted.defenseDice disabled=isPlayMode }}
</div>
<div class="flexrow granted">
<span class="">{{localize
"PRISMRPG.Label.grantedDamageDice"}}</a></span>
{{formInput systemFields.granted.fields.damageDice value=system.granted.damageDice disabled=isPlayMode }}
</div>
</div>
</div>
</fieldset>
<fieldset>
<legend>{{localize "PRISMRPG.Label.wounds"}}</legend>
<div class="wounds">
{{#each system.hp.wounds as |wound idx|}}
<div class="wound">
Name:<input class="wound-description wound-data" type="text" data-type="String" data-index="{{@index}}"
value="{{wound.description}}" data-name="description">
Duration:<input class="wound-duration wound-data" type="text" data-type="Number" data-index="{{@index}}"
value="{{wound.duration}}" data-name="duration">
HP:<input class="wound-value wound-data" type="text" data-type="Number" data-index="{{@index}}"
value="{{wound.value}}" data-name="value">
</div>
{{/each}}
</div>
</fieldset>
<fieldset>
<legend>{{localize "PRISMRPG.Label.weapons"}}</legend>
<div class="weapons">
{{#each weapons as |item|}}
<div class="weapon" data-item-id="{{item.id}}" data-item-uuid="{{item.uuid}}" data-drag="true"
data-drag-type="damage">
<div
class="weapon"
data-item-id="{{item.id}}"
data-item-uuid="{{item.uuid}}"
data-drag="true"
data-drag-type="damage"
>
{{#if (ne item.img "icons/svg/item-bag.svg")}}
<img class="item-img" src="{{item.img}}" data-tooltip="{{item.name}}" />
<img
class="item-img"
src="{{item.img}}"
data-tooltip="{{item.name}}"
data-action="postItemToChat"
/>
{{/if}}
<div class="name" data-tooltip="{{item.system.description}}">
{{item.name}}
</div>
<div class="attack-icons">
<a class="rollable" data-roll-type="weapon-attack" data-roll-key="{{item.id}}" data-tooltip="Roll Attack">
<i class="lf-roll-small fa-solid fa-swords" data-roll-type="weapon-attack"
data-roll-key="{{item.id}}"></i>
<a
class="rollable"
data-roll-type="weapon-attack"
data-roll-key="{{item.id}}"
data-tooltip="Roll Attack"
>
<i
class="lf-roll-small fa-solid fa-swords"
data-roll-type="weapon-attack"
data-roll-key="{{item.id}}"
></i>
</a>
<a class="rollable" data-roll-type="weapon-defense" data-roll-key="{{item.id}}" data-tooltip="Roll Defense">
<i class="fa-solid fa-shield-halved" data-roll-type="weapon-defense" data-roll-key="{{item.id}}"></i>
</a>
<a class="rollable" data-roll-type="weapon-damage-small" data-roll-key="{{item.id}}"
data-tooltip="Roll Damage (Small)">
<i class="fa-regular fa-face-head-bandage" data-roll-type="weapon-damage-small"
data-roll-key="{{item.id}}"></i>S
</a>
<a class="rollable" data-roll-type="weapon-damage-medium" data-roll-key="{{item.id}}"
data-tooltip="Roll Damage (Medium)">
<i class="fa-regular fa-face-head-bandage" data-roll-type="weapon-damage-medium"
data-roll-key="{{item.id}}"></i>M
<a
class="rollable"
data-roll-type="weapon-damage-medium"
data-roll-key="{{item.id}}"
data-tooltip="Roll Damage"
>
<i
class="fa-regular fa-face-head-bandage"
data-roll-type="weapon-damage-medium"
data-roll-key="{{item.id}}"
></i>
</a>
</div>
<div class="controls">
<a data-tooltip="{{localize 'PRISMRPG.Edit'}}" data-action="edit" data-item-id="{{item.id}}"
data-item-uuid="{{item.uuid}}"><i class="fas fa-edit"></i></a>
<a data-tooltip="{{localize 'PRISMRPG.Delete'}}" data-action="delete" data-item-id="{{item.id}}"
data-item-uuid="{{item.uuid}}"><i class="fas fa-trash"></i></a>
<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}}
@@ -111,20 +80,46 @@
<legend>{{localize "PRISMRPG.Label.armors"}}</legend>
<div class="armors">
{{#each armors as |item|}}
<div class="armor" data-item-id="{{item.id}}" data-item-uuid="{{item.uuid}}">
<img class="item-img" src="{{item.img}}" data-tooltip="{{item.name}}" />
<div
class="armor"
data-item-id="{{item.id}}"
data-item-uuid="{{item.uuid}}"
>
<img
class="item-img"
src="{{item.img}}"
data-tooltip="{{item.name}}"
data-action="postItemToChat"
/>
<div class="name" data-tooltip="{{item.system.description}}">
{{item.name}}
</div>
<div class="item-detail" data-tooltip="Defense">{{item.system.defense}}</div>
<div class="item-detail" data-tooltip="Maximum movement">{{item.system.maximumMovement}}</div>
<div
class="item-detail"
data-tooltip="Defense"
>{{item.system.defense}}</div>
<div
class="item-detail"
data-tooltip="Maximum movement"
>{{item.system.maximumMovement}}</div>
<div class="item-detail" data-tooltip="HP">{{item.system.hp}}</div>
<div class="item-detail" data-tooltip="Damage Reduction">{{item.system.damageReduction}}</div>
<div
class="item-detail"
data-tooltip="Damage Reduction"
>{{item.system.damageReduction}}</div>
<div class="controls">
<a data-tooltip="{{localize 'PRISMRPG.Edit'}}" data-action="edit" data-item-id="{{item.id}}"
data-item-uuid="{{item.uuid}}"><i class="fas fa-edit"></i></a>
<a data-tooltip="{{localize 'PRISMRPG.Delete'}}" data-action="delete" data-item-id="{{item.id}}"
data-item-uuid="{{item.uuid}}"><i class="fas fa-trash"></i></a>
<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}}
@@ -135,23 +130,43 @@
<legend>{{localize "PRISMRPG.Label.shields"}}</legend>
<div class="shields">
{{#each shields as |item|}}
<div class="shield" data-item-id="{{item.id}}" data-item-uuid="{{item.uuid}}">
<img class="item-img" src="{{item.img}}" data-tooltip="{{item.name}}" />
<div
class="shield"
data-item-id="{{item.id}}"
data-item-uuid="{{item.uuid}}"
>
<img
class="item-img"
src="{{item.img}}"
data-tooltip="{{item.name}}"
data-action="postItemToChat"
/>
<div class="name" data-tooltip="{{item.system.description}}">
{{item.name}}
</div>
<div class="item-detail" data-tooltip="Block APC">
<i class="fa-solid fa-hand"></i> {{item.system.apc}} APC
<i class="fa-solid fa-hand"></i>
{{item.system.apc}}
APC
</div>
<div class="item-detail" data-tooltip="Shield Rating">
<i class="fa-solid fa-shield"></i> {{item.system.sr}}
<i class="fa-solid fa-shield"></i>
{{item.system.sr}}
</div>
<div class="controls">
<a data-tooltip="{{localize 'PRISMRPG.Edit'}}" data-action="edit" data-item-id="{{item.id}}"
data-item-uuid="{{item.uuid}}"><i class="fas fa-edit"></i></a>
<a data-tooltip="{{localize 'PRISMRPG.Delete'}}" data-action="delete" data-item-id="{{item.id}}"
data-item-uuid="{{item.uuid}}"><i class="fas fa-trash"></i></a>
<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}}
+6 -6
View File
@@ -4,11 +4,11 @@
<fieldset>
<legend>{{localize "PRISMRPG.Label.money"}}</legend>
<div class="moneys">
{{formField systemFields.moneys.fields.tinbit.fields.value value=system.moneys.tinbit.value localize=true}}
{{formField systemFields.moneys.fields.copper.fields.value value=system.moneys.copper.value localize=true}}
{{formField systemFields.moneys.fields.silver.fields.value value=system.moneys.silver.value localize=true}}
{{formField systemFields.moneys.fields.gold.fields.value value=system.moneys.gold.value localize=true}}
{{formField systemFields.moneys.fields.platinum.fields.value value=system.moneys.platinum.value localize=true}}
{{formField systemFields.moneys.fields.coppercoin.fields.value value=system.moneys.coppercoin.value localize=true}}
{{formField systemFields.moneys.fields.silvercoin.fields.value value=system.moneys.silvercoin.value localize=true}}
{{formField systemFields.moneys.fields.goldcoin.fields.value value=system.moneys.goldcoin.value localize=true}}
{{formField systemFields.moneys.fields.note.fields.value value=system.moneys.note.value localize=true}}
{{formField systemFields.moneys.fields.steam.fields.value value=system.moneys.steam.value localize=true}}
</div>
</fieldset>
@@ -17,7 +17,7 @@
<div class="equipments">
{{#each equipments as |item|}}
<div class="equipment" data-item-id="{{item.id}}" data-item-uuid="{{item.uuid}}">
<img class="item-img" src="{{item.img}}" data-tooltip="{{item.name}}" />
<img class="item-img" src="{{item.img}}" data-tooltip="{{item.name}}" data-action="postItemToChat" />
<div class="name" data-tooltip="{{{item.system.description}}}">
{{item.name}}
</div>
+319 -411
View File
@@ -1,95 +1,19 @@
<section
class="character-main character-main-{{ifThen isPlayMode 'play' 'edit'}}"
>
{{log "character-main" this}}
<section class="character-main-v2">
{{log "character-main-v2" this}}
<fieldset>
<legend>{{localize "PRISMRPG.Label.pc"}}</legend>
<div class="character-pc character-pc-{{ifThen isPlayMode 'play' 'edit'}}">
<div class="character-left">
<div class="character-left-image">
<img
class="character-img"
src="{{actor.img}}"
data-edit="img"
data-action="editImage"
data-tooltip="{{actor.name}}"
/>
</div>
<fieldset class="">
<div class="flexrow character-hp">
<span class="name">{{localize "PRISMRPG.Label.HP"}}</span>
{{formInput
systemFields.hp.fields.value
value=system.hp.value
disabled=isPlayMode
classes="character-hp-value"
}}
&nbsp;/&nbsp;
{{formInput
systemFields.hp.fields.max
value=system.hp.max
disabled=isPlayMode
classes="character-hp-value"
}}
</div>
<div class="flexrow character-hp">
<span class="name">{{localize "PRISMRPG.Label.grit"}}</span>
{{formInput
systemFields.grit.fields.current
value=system.grit.current
disabled=isPlayMode
classes="character-hp"
}}
<span class="name">{{localize "PRISMRPG.Label.earned"}}</span>
{{formInput
systemFields.grit.fields.earned
value=system.grit.earned
disabled=isPlayMode
classes="character-hp"
}}
</div>
<div class="flexrow character-hp">
<span class="name">{{localize "PRISMRPG.Label.luck"}}</span>
{{formInput
systemFields.luck.fields.current
value=system.luck.current
disabled=isPlayMode
classes="character-hp"
}}
<span class="name">{{localize "PRISMRPG.Label.earned"}}</span>
{{formInput
systemFields.luck.fields.earned
value=system.luck.earned
disabled=isPlayMode
classes="character-hp"
}}
</div>
<div class="flexrow">
<span class="">{{localize
"PRISMRPG.Label.damageResistanceShort"
}}</span>
{{formInput
systemFields.hp.fields.damageResistance
value=system.hp.fields.damageResistance
disabled=isPlayMode
classes="character-hp"
}}
</div>
</fieldset>
</div>
<div class="character-right">
<div class="character-name">
<div class="character-sheet-wrapper">
{{! Character Header with Name }}
<div class="character-header">
<div class="character-name-banner">
{{formInput
fields.name
value=source.name
rootId=partId
disabled=isPlayMode
placeholder="Character Name"
}}
</div>
<div class="character-toggle-controls">
<a
class="control"
data-action="toggleSheet"
@@ -99,363 +23,347 @@
<i class="fa-solid fa-user-{{ifThen isPlayMode 'lock' 'pen'}}"></i>
</a>
</div>
</div>
<fieldset
class="character-characteristics character-characteristics-{{ifThen
isPlayMode
'play'
'edit'
}}"
>
<legend>{{localize "PRISMRPG.Label.Saves"}}</legend>
<div class="character-saves">
<div class="character-save">
<span class="name"><a
class="rollable"
data-roll-type="save"
data-roll-key="will"
><i class="lf-roll-small fa-solid fa-dice-d20"></i>
{{localize "PRISMRPG.Label.saves.will"}}
</a></span>
{{formField
systemFields.saves.fields.will.fields.value
value=system.saves.will.value
disabled=true
<div class="character-main-grid">
{{! Left Column - Portrait, Attributes & HP }}
<div class="character-left-column">
{{! Portrait + HP column }}
<div class="portrait-hp-column">
{{! Portrait }}
<div class="character-portrait">
<img
class="character-img"
src="{{actor.img}}"
data-edit="img"
data-action="editImage"
data-tooltip="{{actor.name}}"
/>
</div>
{{! HP Shields (3 shields) - Below portrait }}
<div class="hp-shields-section">
<div class="hp-shields">
<div class="hp-item">
<div class="hp-label">HP</div>
<a data-action="hpMinus"><i class="fa-solid fa-minus"></i></a>
<div class="hp-value">
{{formInput
systemFields.hp.fields.value
value=system.hp.value
disabled=isPlayMode
}}
<span class="name">
<a
class="rollable"
data-roll-type="save"
data-roll-key="dodge"
><i class="lf-roll-small fa-solid fa-dice-d20"></i>
{{localize "PRISMRPG.Label.saves.dodge"}}
</div>
<a data-action="hpPlus"><i class="fa-solid fa-plus"></i></a>
<div class="hp-separator">/</div>
<div class="hp-max">
{{formInput
systemFields.hp.fields.max
value=system.hp.max
disabled=isPlayMode
}}
</div>
</div>
<div class="hp-item hp-temp-item">
<div class="hp-label" data-tooltip="{{localize 'PRISMRPG.Label.HPTemp'}}">HP Tmp</div>
<a data-action="hpTempMinus"><i class="fa-solid fa-minus"></i></a>
<div class="hp-value">
{{formInput
systemFields.hp.fields.temp
value=system.hp.temp
disabled=isPlayMode
}}
</div>
<a data-action="hpTempPlus"><i class="fa-solid fa-plus"></i></a>
</div>
<div class="hp-item">
<div class="hp-label">MANA</div>
<a data-action="manaPointsMinus"><i class="fa-solid fa-minus"></i></a>
<div class="hp-value">
{{formInput
systemFields.manaPoints.fields.value
value=system.manaPoints.value
disabled=isPlayMode
}}
</div>
<a data-action="manaPointsPlus"><i class="fa-solid fa-plus"></i></a>
<div class="hp-separator">/</div>
<div class="hp-max">
{{formInput
systemFields.manaPoints.fields.max
value=system.manaPoints.max
disabled=isPlayMode
}}
</div>
</div>
<div class="hp-item">
<div class="hp-label">ARMOR</div>
<a data-action="armorPointsMinus"><i class="fa-solid fa-minus"></i></a>
<div class="hp-value">
{{formInput
systemFields.armorPoints.fields.value
value=system.armorPoints.value
disabled=isPlayMode
}}
</div>
<a data-action="armorPointsPlus"><i class="fa-solid fa-plus"></i></a>
<div class="hp-separator">/</div>
<div class="hp-max">
{{formInput
systemFields.armorPoints.fields.max
value=system.armorPoints.max
disabled=isPlayMode
}}
</div>
</div>
<div class="hp-item">
<div class="hp-label" data-tooltip="Action Points">AP</div>
<a data-action="actionPointsMinus"><i class="fa-solid fa-minus"></i></a>
<div class="hp-value">
{{formInput
systemFields.actionPoints.fields.value
value=system.actionPoints.value
disabled=isPlayMode
}}
</div>
<a data-action="actionPointsPlus"><i class="fa-solid fa-plus"></i></a>
<div class="hp-separator">/</div>
<div class="hp-max">
{{formInput
systemFields.actionPoints.fields.max
value=system.actionPoints.max
disabled=isPlayMode
}}
</div>
</div>
</div>
</div>
</div>
{{! Core Attributes (STR, DEX, CON, INT, WIS, CHA) }}
<div class="character-attributes">
<div class="attribute-shield">
<div class="attribute-label">
<a class="rollable" data-roll-type="characteristic" data-roll-key="str">
<i class="fa-duotone fa-solid fa-dice-d20"></i>
STR
</a>
</span>
{{formField
systemFields.saves.fields.dodge.fields.value
value=system.saves.dodge.value
disabled=true
}}
<span class="name">
<a
class="rollable"
data-roll-type="save"
data-roll-key="toughness"
><i class="lf-roll-small fa-solid fa-dice-d20"></i>
{{localize "PRISMRPG.Label.saves.toughness"}}
</a>
</span>
{{formField
systemFields.saves.fields.toughness.fields.value
value=system.saves.toughness.value
disabled=true
}}
</div>
<div class="character-save">
<span class="name">
<a
class="rollable"
data-roll-type="save"
data-roll-key="contagion"
><i class="lf-roll-small fa-solid fa-dice-d20"></i>
{{localize "PRISMRPG.Label.saves.contagion"}}
</a>
</span>
{{formField
systemFields.saves.fields.contagion.fields.value
value=system.saves.contagion.value
disabled=true
}}
<span class="name">
<a
class="rollable"
data-roll-type="save"
data-roll-key="poison"
><i class="lf-roll-small fa-solid fa-dice-d20"></i>
{{localize "PRISMRPG.Label.saves.poison"}}
</a>
</span>
{{formField
systemFields.saves.fields.poison.fields.value
value=system.saves.poison.value
disabled=true
}}
<!--
<span class="name-pain">
<a class="rollable" data-roll-type="save" data-roll-key="pain" data-roll-dice="D12"><i
class="lf-roll-small fa-solid fa-dice-d12"></i>
{{localize "PRISMRPG.Label.saves.pain"}}
</a>
</span>
<span class="name-pain">
<a class="rollable" data-roll-type="save" data-roll-key="pain" data-roll-dice="D20"><i
class="lf-roll-small fa-solid fa-dice-d20"></i>
{{localize "PRISMRPG.Label.saves.pain"}}
</a>
</span>
{{formField systemFields.saves.fields.pain.fields.value value=system.saves.pain.value disabled=true}}
<span data-tooltip="Pain save if wound exceeds">
{{formField systemFields.hp.fields.painDamage value=system.hp.painDamage disabled=isPlayMode
tooltip="Pain Damage"}}
</span>
-->
</div>
</div>
</fieldset>
<fieldset
class="character-characteristics character-characteristics-{{ifThen
isPlayMode
'play'
'edit'
}}"
>
<legend>{{localize "PRISMRPG.Label.Challenges"}}</legend>
<div class="character-challenges">
<div class="character-challenge">
<span class="name"><a
class="rollable"
data-roll-type="challenge"
data-roll-key="str"
><i class="lf-roll-small fa-solid fa-dice-d20"></i>{{localize
"PRISMRPG.Label.challenges.strength"
}}</a></span>
{{formField
systemFields.challenges.fields.str.fields.value
value=system.challenges.str.value
disabled=true
}}
<span class="name"><a
class="rollable"
data-roll-type="challenge"
data-roll-key="agility"
><i class="lf-roll-small fa-solid fa-dice-d20"></i>{{localize
"PRISMRPG.Label.challenges.agility"
}}</a></span>
{{formField
systemFields.challenges.fields.agility.fields.value
value=system.challenges.agility.value
disabled=true
}}
<span class="name"><a
class="rollable"
data-roll-type="challenge"
data-roll-key="dying"
><i class="lf-roll-small fa-solid fa-dice-d20"></i>{{localize
"PRISMRPG.Label.challenges.dying"
}}</a></span>
{{formField
systemFields.challenges.fields.dying.fields.value
value=system.challenges.dying.value
disabled=true
}}
</div>
</div>
</fieldset>
<fieldset
class="character-characteristics character-characteristics-{{ifThen
isPlayMode
'play'
'edit'
}}"
>
<legend>{{localize "PRISMRPG.Label.Movement"}}</legend>
<div class="character-movements">
<div class="character-movement">
<span class="name">{{localize
"PRISMRPG.Label.movement.walk"
}}</span>
{{formField
systemFields.movement.fields.walk
value=system.movement.walk
disabled=isPlayMode
}}
<span class="name">{{localize
"PRISMRPG.Label.movement.jog"
}}</span>
{{formField
systemFields.movement.fields.jog
value=system.movement.jog
disabled=isPlayMode
}}
<span class="name">{{localize
"PRISMRPG.Label.movement.run"
}}</span>
{{formField
systemFields.movement.fields.run
value=system.movement.run
disabled=isPlayMode
}}
<span class="name">{{localize
"PRISMRPG.Label.movement.sprint"
}}</span>
{{formField
systemFields.movement.fields.sprint
value=system.movement.sprint
disabled=isPlayMode
}}
</div>
<div class="character-movement">
<span class="name">{{localize
"PRISMRPG.Label.movement.jumpBroad"
}}</span>
{{formField
systemFields.jump.fields.broad
value=system.jump.broad
disabled=isPlayMode
}}
<span class="name">{{localize
"PRISMRPG.Label.movement.jumpRunning"
}}</span>
{{formField
systemFields.jump.fields.running
value=system.jump.running
disabled=isPlayMode
}}
<span class="name">{{localize
"PRISMRPG.Label.movement.jumpVertical"
}}</span>
{{formField
systemFields.jump.fields.vertical
value=system.jump.vertical
disabled=isPlayMode
}}
</div>
</div>
</fieldset>
</div>
</div>
</fieldset>
<fieldset
class="character-characteristics character-characteristics-{{ifThen
isPlayMode
'play'
'edit'
}}"
>
<legend>{{localize "PRISMRPG.Label.characteristics"}}</legend>
<div class="character-characteristic">
<span>{{localize "PRISMRPG.Label.str"}}</span>
{{formField
<div class="attribute-value">
{{formInput
systemFields.characteristics.fields.str.fields.value
value=system.characteristics.str.value
disabled=isPlayMode
data-char-id="str"
}}
{{formField
systemFields.characteristics.fields.str.fields.percent
value=system.characteristics.str.percent
disabled=isPlayMode
type="number"
}}
</div>
<div class="character-characteristic">
<span>{{localize "PRISMRPG.Label.int"}}</span>
{{formField
systemFields.characteristics.fields.int.fields.value
value=system.characteristics.int.value
<div class="attribute-save">
<a class="rollable save-rollable" data-roll-type="save" data-roll-key="str" data-tooltip="{{localize 'PRISMRPG.RollSavingThrow'}}">
<i class="fa-duotone fa-solid fa-dice-d20"></i>
</a>
{{formInput
systemFields.saves.fields.str.fields.value
value=system.saves.str.value
disabled=isPlayMode
data-char-id="int"
}}
{{formField
systemFields.characteristics.fields.int.fields.percent
value=system.characteristics.int.percent
disabled=isPlayMode
type="number"
}}
</div>
<div class="character-characteristic">
<span>{{localize "PRISMRPG.Label.wis"}}</span>
{{formField
systemFields.characteristics.fields.wis.fields.value
value=system.characteristics.wis.value
disabled=isPlayMode
data-char-id="wis"
}}
{{formField
systemFields.characteristics.fields.wis.fields.percent
value=system.characteristics.wis.percent
disabled=isPlayMode
type="number"
}}
</div>
<div class="character-characteristic">
<span>{{localize "PRISMRPG.Label.dex"}}</span>
{{formField
<div class="attribute-shield">
<div class="attribute-label">
<a class="rollable" data-roll-type="characteristic" data-roll-key="dex">
<i class="fa-duotone fa-solid fa-dice-d20"></i>
DEX
</a>
</div>
<div class="attribute-value">
{{formInput
systemFields.characteristics.fields.dex.fields.value
value=system.characteristics.dex.value
disabled=isPlayMode
data-char-id="wis"
}}
{{formField
systemFields.characteristics.fields.dex.fields.percent
value=system.characteristics.dex.percent
disabled=isPlayMode
type="number"
}}
</div>
<div class="character-characteristic">
<span>{{localize "PRISMRPG.Label.con"}}</span>
{{formField
<div class="attribute-save">
<a class="rollable save-rollable" data-roll-type="save" data-roll-key="dex" data-tooltip="{{localize 'PRISMRPG.RollSavingThrow'}}">
<i class="fa-duotone fa-solid fa-dice-d20"></i>
</a>
{{formInput
systemFields.saves.fields.dex.fields.value
value=system.saves.dex.value
disabled=isPlayMode
}}
</div>
</div>
<div class="attribute-shield">
<div class="attribute-label">
<a class="rollable" data-roll-type="characteristic" data-roll-key="con">
<i class="fa-duotone fa-solid fa-dice-d20"></i>
CON
</a>
</div>
<div class="attribute-value">
{{formInput
systemFields.characteristics.fields.con.fields.value
value=system.characteristics.con.value
disabled=isPlayMode
data-char-id="con"
}}
{{formField
systemFields.characteristics.fields.con.fields.percent
value=system.characteristics.con.percent
disabled=isPlayMode
type="number"
}}
</div>
<div class="character-characteristic">
<span>{{localize "PRISMRPG.Label.cha"}}</span>
{{formField
<div class="attribute-save">
<a class="rollable save-rollable" data-roll-type="save" data-roll-key="con" data-tooltip="{{localize 'PRISMRPG.RollSavingThrow'}}">
<i class="fa-duotone fa-solid fa-dice-d20"></i>
</a>
{{formInput
systemFields.saves.fields.con.fields.value
value=system.saves.con.value
disabled=isPlayMode
}}
</div>
</div>
<div class="attribute-shield">
<div class="attribute-label">
<a class="rollable" data-roll-type="characteristic" data-roll-key="int">
<i class="fa-duotone fa-solid fa-dice-d20"></i>
INT
</a>
</div>
<div class="attribute-value">
{{formInput
systemFields.characteristics.fields.int.fields.value
value=system.characteristics.int.value
disabled=isPlayMode
}}
</div>
<div class="attribute-save">
<a class="rollable save-rollable" data-roll-type="save" data-roll-key="int" data-tooltip="{{localize 'PRISMRPG.RollSavingThrow'}}">
<i class="fa-duotone fa-solid fa-dice-d20"></i>
</a>
{{formInput
systemFields.saves.fields.int.fields.value
value=system.saves.int.value
disabled=isPlayMode
}}
</div>
</div>
<div class="attribute-shield">
<div class="attribute-label">
<a class="rollable" data-roll-type="characteristic" data-roll-key="wis">
<i class="fa-duotone fa-solid fa-dice-d20"></i>
WIS
</a>
</div>
<div class="attribute-value">
{{formInput
systemFields.characteristics.fields.wis.fields.value
value=system.characteristics.wis.value
disabled=isPlayMode
}}
</div>
<div class="attribute-save">
<a class="rollable save-rollable" data-roll-type="save" data-roll-key="wis" data-tooltip="{{localize 'PRISMRPG.RollSavingThrow'}}">
<i class="fa-duotone fa-solid fa-dice-d20"></i>
</a>
{{formInput
systemFields.saves.fields.wis.fields.value
value=system.saves.wis.value
disabled=isPlayMode
}}
</div>
</div>
<div class="attribute-shield">
<div class="attribute-label">
<a class="rollable" data-roll-type="characteristic" data-roll-key="cha">
<i class="fa-duotone fa-solid fa-dice-d20"></i>
CHA
</a>
</div>
<div class="attribute-value">
{{formInput
systemFields.characteristics.fields.cha.fields.value
value=system.characteristics.cha.value
disabled=isPlayMode
data-char-id="cha"
}}
{{formField
systemFields.characteristics.fields.cha.fields.percent
value=system.characteristics.cha.percent
disabled=isPlayMode
type="number"
}}
</div>
</fieldset>
<div class="attribute-save">
<a class="rollable save-rollable" data-roll-type="save" data-roll-key="cha" data-tooltip="{{localize 'PRISMRPG.RollSavingThrow'}}">
<i class="fa-duotone fa-solid fa-dice-d20"></i>
</a>
{{formInput
systemFields.saves.fields.cha.fields.value
value=system.saves.cha.value
disabled=isPlayMode
}}
</div>
</div>
</div>
</div>
{{!-- Sub-Attributes (Prism RPG) --}}
<fieldset
class="character-subattributes character-subattributes-{{ifThen
isPlayMode
'play'
'edit'
}}"
>
<legend>{{localize "PRISMRPG.Label.subAttributes"}}</legend>
<div class="subattributes-grid">
{{#each config.SUB_ATTRIBUTES as |subAttr|}}
<div class="character-subattribute" data-tooltip="{{localize subAttr.description}}">
<span class="subattr-label">{{localize subAttr.label}}</span>
<span class="subattr-value">{{lookup (lookup ../system.subAttributes subAttr.id) 'value'}}</span>
<span class="subattr-parents">({{#each subAttr.parents}}{{localize (concat "PRISMRPG.Label." this)}}{{#unless @last}}/{{/unless}}{{/each}})</span>
{{! Right Column - Race, Classes }}
<div class="character-right-column">
{{! Race }}
<div class="race-section">
<div class="race-box">
<h4 class="race-label">Race</h4>
<div class="race-content">
{{#if race}}
<div class="race-item" data-item-id="{{race.id}}" data-item-uuid="{{race.uuid}}">
<img class="item-img" src="{{race.img}}" data-tooltip="{{race.name}}" data-action="postItemToChat" />
<div class="race-name">{{race.name}}</div>
<div class="controls">
<a data-tooltip="{{localize 'PRISMRPG.Edit'}}" data-action="edit" data-item-id="{{race.id}}"
data-item-uuid="{{race.uuid}}"><i class="fas fa-edit"></i></a>
<a data-tooltip="{{localize 'PRISMRPG.Delete'}}" data-action="delete" data-item-id="{{race.id}}"
data-item-uuid="{{race.uuid}}"><i class="fas fa-trash"></i></a>
</div>
</div>
{{else}}
<div class="no-race">
<p>{{localize "PRISMRPG.Message.dropRace"}}</p>
</div>
{{/if}}
</div>
</div>
</div>
{{! Classes (Three boxes) }}
<div class="classes-section">
{{#each classSlots as |classItem index|}}
<div class="class-box">
<h4 class="class-label">Class {{add index 1}}</h4>
<div class="class-content">
{{#if classItem}}
<div class="class-item" data-item-id="{{classItem.id}}" data-item-uuid="{{classItem.uuid}}">
<img class="item-img" src="{{classItem.img}}" data-tooltip="{{classItem.name}}" data-action="postItemToChat" />
<div class="class-name">{{classItem.name}}</div>
<div class="controls">
<a data-tooltip="{{localize 'PRISMRPG.Edit'}}" data-action="edit" data-item-id="{{classItem.id}}"
data-item-uuid="{{classItem.uuid}}"><i class="fas fa-edit"></i></a>
<a data-tooltip="{{localize 'PRISMRPG.Delete'}}" data-action="delete" data-item-id="{{classItem.id}}"
data-item-uuid="{{classItem.uuid}}"><i class="fas fa-trash"></i></a>
</div>
</div>
{{else}}
<div class="no-class">
<p>{{localize "PRISMRPG.Message.dropClass"}}</p>
</div>
{{/if}}
</div>
</div>
{{/each}}
{{! Alignment }}
<div class="class-box">
<h4 class="class-label">Alignment</h4>
<div class="class-content">
<div class="alignment-value">
{{formInput
systemFields.biodata.fields.alignment
value=system.biodata.alignment
disabled=isPlayMode
}}
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</fieldset>
</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>
+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>
+5 -85
View File
@@ -1,76 +1,18 @@
<section class="tab character-{{tab.id}} {{tab.cssClass}}" data-tab="skills" data-group="sheet">
<div class="main-div">
{{!-- Core Skill Selection (Prism RPG) --}}
<fieldset class="core-skill-selection">
<legend data-tooltip="{{localize 'PRISMRPG.Tooltip.coreSkill'}}" data-tooltip-direction="UP">
{{localize "PRISMRPG.Label.coreSkill"}}
</legend>
<div class="core-skill-info">
{{#if system.coreSkill.skill}}
<div class="selected-core-skill">
<span class="core-skill-name">{{localize (concat "PRISMRPG.CoreSkill." system.coreSkill.skill)}}</span>
<span class="core-skill-bonus">+5 {{localize "PRISMRPG.Label.basicChecks"}}</span>
{{#if system.coreSkill.attributeChoice}}
<span class="attribute-bonus">+2 {{localize (concat "PRISMRPG.Label." system.coreSkill.attributeChoice)}}</span>
{{/if}}
<span class="advanced-checks">{{localize "PRISMRPG.Label.advancedChecksEnabled"}}</span>
</div>
{{else}}
<div class="no-core-skill">
<p>{{localize "PRISMRPG.Message.selectCoreSkill"}}</p>
<select name="system.coreSkill.skill" {{#if isPlayMode}}disabled{{/if}}>
<option value="">{{localize "PRISMRPG.Label.chooseSkill"}}</option>
{{#each config.CORE_SKILLS as |skill skillId|}}
<option value="{{skillId}}">{{localize skill.label}}</option>
{{/each}}
</select>
</div>
{{/if}}
</div>
</fieldset>
{{!-- Available Core Skills Reference --}}
<fieldset class="core-skills-list">
<legend data-tooltip="{{localize 'PRISMRPG.Tooltip.coreSkills'}}" data-tooltip-direction="UP">
{{localize "PRISMRPG.Label.availableCoreSkills"}}
</legend>
<div class="skills-grid">
{{#each config.CORE_SKILLS as |skill skillId|}}
<div class="core-skill-item {{#if (eq ../system.coreSkill.skill skillId)}}selected{{/if}}"
data-skill-id="{{skillId}}">
<div class="skill-header">
<span class="skill-name">{{localize skill.label}}</span>
{{#if (eq ../system.coreSkill.skill skillId)}}
<span class="badge-core">{{localize "PRISMRPG.Label.yourCoreSkill"}}</span>
{{/if}}
</div>
<div class="skill-attributes">
<span class="attribute-choices-label">{{localize "PRISMRPG.Label.attributeChoices"}}:</span>
{{#each skill.attributeChoices as |attr|}}
<span class="attribute-choice {{#if (eq ../../system.coreSkill.attributeChoice attr)}}chosen{{/if}}">
{{localize (concat "PRISMRPG.Label." attr)}}
</span>
{{#unless @last}}/{{/unless}}
{{/each}}
</div>
</div>
{{/each}}
</div>
</fieldset>
{{!-- Skills Items (if any) --}}
{{!-- Skills Items --}}
<fieldset>
<legend data-tooltip="{{localize 'PRISMRPG.Tooltip.skills'}}" data-tooltip-direction="UP">
{{localize "PRISMRPG.Label.customSkills"}}
{{localize "PRISMRPG.Label.skills"}}
</legend>
<div class="skills">
{{#each skills as |item|}}
<div class="skill {{#if item.system.isCoreSkill}}is-core-skill{{/if}}"
data-item-id="{{item.id}}"
data-item-uuid="{{item.uuid}}">
<img class="item-img" src="{{item.img}}" data-tooltip="{{item.name}}" />
<div class="name">
<img class="item-img" src="{{item.img}}" data-tooltip="{{item.name}}\nClick to post to chat" data-action="postItemToChat" />
<div class="name" data-tooltip="{{item.system.description}}">
<a class="rollable" data-roll-type="skill" data-roll-key="{{item.id}}">
<i class="lf-roll-small fa-duotone fa-solid fa-dice-d10"></i>
{{item.name}}
@@ -104,29 +46,7 @@
<div class="racial-abilities">
{{#each racialAbilities as |item|}}
<div class="racial-ability " data-item-id="{{item.id}}" data-item-uuid="{{item.uuid}}">
<img class="item-img" src="{{item.img}}" data-tooltip="{{item.name}}" />
<div class="name" data-tooltip="{{{item.description}}}<br><br>{{item.path}}" data-tooltip-direction="UP">
{{item.name}}
</div>
<div class="controls">
<a data-tooltip="{{localize 'PRISMRPG.Edit'}}" data-action="edit" data-item-id="{{item.id}}"
data-item-uuid="{{item.uuid}}"><i class="fas fa-edit"></i></a>
<a data-tooltip="{{localize 'PRISMRPG.Delete'}}" data-action="delete" data-item-id="{{item.id}}"
data-item-uuid="{{item.uuid}}"><i class="fas fa-trash"></i></a>
</div>
</div>
{{/each}}
</div>
</fieldset>
<fieldset>
<legend data-tooltip="{{localize 'PRISMRPG.Tooltip.vulnerabilities'}}" data-tooltip-direction="UP">
{{localize "PRISMRPG.Label.vulnerabilities"}}
</legend>
<div class="vulnerabilities">
{{#each vulnerabilities as |item|}}
<div class="vulnerability " data-item-id="{{item.id}}" data-item-uuid="{{item.uuid}}">
<img class="item-img" src="{{item.img}}" data-tooltip="{{item.name}}" />
<img class="item-img" src="{{item.img}}" data-tooltip="{{item.name}}\nClick to post to chat" data-action="postItemToChat" />
<div class="name" data-tooltip="{{{item.description}}}<br><br>{{item.path}}" data-tooltip-direction="UP">
{{item.name}}
</div>
+3 -23
View File
@@ -1,22 +1,6 @@
<section class="tab character-{{tab.id}} {{tab.cssClass}}" data-tab="{{tab.id}}" data-group="{{tab.group}}">
<div class="main-div">
<fieldset>
<legend>{{localize "PRISMRPG.Label.aetherPoints"}}</legend>
<div class="spell-details">
<div class="spell-detail">
<span>Current</span>
{{formField systemFields.aetherPoints.fields.value value=system.aetherPoints.value localize=true}}
<a data-action="aetherPointsPlus"><i class="fa-solid fa-hexagon-plus"></i></a>
<a data-action="aetherPointsMinus"><i class="fa-solid fa-hexagon-minus"></i></a>
<span>Max</span>
{{formField systemFields.aetherPoints.fields.max value=system.aetherPoints.max localize=true
disabled=isPlayMode}}
</div>
</div>
</fieldset>
<fieldset>
<legend>{{localize "PRISMRPG.Label.spells"}}{{#if isEditMode}}<a class="action" data-tooltip="{{localize "
PRISMRPG.Tooltip.addSpell"}}" data-tooltip-direction="UP"><i class="fas fa-plus"
@@ -24,17 +8,13 @@
<div class="spells">
{{#each spells as |item|}}
<div class="spell" data-item-id="{{item.id}}" data-item-uuid="{{item.uuid}}" data-drag="true">
<img class="item-img" src="{{item.img}}" data-tooltip="{{item.name}}" />
<img class="item-img" src="{{item.img}}" data-tooltip="{{item.name}}" data-action="postItemToChat" />
<div class="name">
{{item.name}}
</div>
<a class="rollable" data-roll-type="spell-attack" data-roll-key="{{item.id}}" data-tooltip="Spell Attack">
<i class="lf-roll-small fa-solid fa-swords" data-roll-type="spell-attack" data-roll-key="{{item.id}}"></i>
</a>
<a class="rollable" data-roll-type="spell-power" data-roll-key="{{item.id}}" data-tooltip="Spell Power">
<i class="fa-duotone fa-solid fa-stars" data-roll-type="spell-power" data-roll-key="{{item.id}}"></i>
<a class="rollable" data-roll-type="spell-cast" data-roll-key="{{item.id}}" data-tooltip="Cast Spell">
<i class="fa-duotone fa-solid fa-wand-magic-sparkles" data-roll-type="spell-cast" data-roll-key="{{item.id}}"></i>
</a>
<div class="controls">
+25
View File
@@ -0,0 +1,25 @@
<section class="character-subattributes tab" data-group="sheet" data-tab="subattributes">
<div class="subattributes-content">
<div class="subattributes-list">
{{#each (entries config.SUB_ATTRIBUTES) as |entry|}}
{{#with entry.[1] as |subAttr|}}
<a class="rollable subattribute-item" data-roll-type="sub-attribute" data-roll-key="{{subAttr.id}}" title="{{#each subAttr.parents as |parentKey|}}{{uppercase parentKey}}{{#unless @last}}/{{/unless}}{{/each}}">
<div class="subattribute-header">
<i class="fa-duotone fa-solid fa-dice-d20"></i>
<div class="subattribute-name">{{localize subAttr.label}}</div>
</div>
<div class="subattribute-value">
{{#with (lookup ../../system.subAttributes subAttr.id) as |subAttrData|}}
{{#if (gt subAttrData.value 0)}}
+{{subAttrData.value}}
{{else}}
{{subAttrData.value}}
{{/if}}
{{/with}}
</div>
</a>
{{/with}}
{{/each}}
</div>
</div>
</section>
+191 -58
View File
@@ -1,88 +1,221 @@
{{!log 'chat-message' this}}
<div class="{{cssClass}}">
<div class="intro-chat">
<div class="intro-img">
{{!log "chat-message" this}}
{{!log "rollTarget" rollTarget}}
{{!log "rollData" rollData}}
<div class="{{cssClass}} prismrpg-chat-card">
<div class="chat-header">
<div class="chat-portrait">
<img src="{{actingCharImg}}" data-tooltip="{{actingCharName}}" />
</div>
<div class="intro-right">
<span><STRONG>{{actingCharName}} - {{upperFirst rollName}}</STRONG></span>
<div class="chat-title">
<div class="actor-name">{{actingCharName}}</div>
<div class="roll-name">{{upperFirst rollName}}</div>
{{#if (match rollType "attack")}}
<span>Attack roll !</span>
{{/if}}
{{#if (match rollType "defense")}}
<span>Defense roll !</span>
{{/if}}
{{#if (eq rollData.favor "favor")}}
<span><strong>Favor roll</strong></span>
{{/if}}
{{#if (eq rollData.favor "disfavor")}}
<span><strong>Disfavor roll</strong></span>
<div class="roll-type-badge attack">Attack Roll</div>
{{/if}}
{{#if badResult}}
<span><strong>{{localize "PRISMRPG.Label.otherResult"}}</strong> : {{badResult}}</span>
<div class="bad-result">{{localize "PRISMRPG.Label.otherResult"}}: {{badResult}}</div>
{{/if}}
</div>
</div>
<div class="chat-content">
{{#if rollTarget.weapon}}
<div class="weapon-info-card">
<div class="weapon-header">
<strong class="weapon-name">{{rollTarget.weapon.name}}</strong>
{{#if rollTarget.weapon.system.isImplement}}
<span class="badge implement">Implement</span>
{{/if}}
{{#if rollData.attackAttribute}}
<span class="attribute-used">
({{#if (eq rollData.attackAttribute "str")}}Strength{{else}}Dexterity{{/if}})
</span>
{{/if}}
</div>
<div class="weapon-stats">
{{#if rollTarget.weapon.system.weaponGroup}}
<div class="stat-item">
<i class="fas fa-shield-halved"></i>
<span>{{rollTarget.weapon.system.weaponGroup}}</span>
</div>
{{/if}}
{{#if rollTarget.weapon.system.apc}}
<div class="stat-item apc">
<i class="fas fa-bolt"></i>
<span>{{rollTarget.weapon.system.apc}} APC</span>
</div>
{{/if}}
{{#if rollTarget.weapon.system.damage}}
<div class="stat-item damage">
<i class="fas fa-dice-d20"></i>
<span>{{rollTarget.weapon.system.damage}}</span>
</div>
{{/if}}
{{#if rollTarget.weapon.system.damageType}}
<div class="stat-item">
<i class="fas fa-burst"></i>
<span>
{{#if rollTarget.weapon.system.damageType.piercing}}P{{/if}}{{#if rollTarget.weapon.system.damageType.bludgeoning}}{{#if rollTarget.weapon.system.damageType.piercing}}/{{/if}}B{{/if}}{{#if rollTarget.weapon.system.damageType.slashing}}{{#if (or rollTarget.weapon.system.damageType.piercing rollTarget.weapon.system.damageType.bludgeoning)}}/{{/if}}S{{/if}}
</span>
</div>
{{/if}}
{{#if (or (gt rollTarget.weapon.system.shortRange 0) (gt rollTarget.weapon.system.longRange 0))}}
<div class="stat-item range">
<i class="fas fa-bow-arrow"></i>
<span>
{{#if (gt rollTarget.weapon.system.shortRange 0)}}{{rollTarget.weapon.system.shortRange}}{{/if}}{{#if (and (gt rollTarget.weapon.system.shortRange 0) (gt rollTarget.weapon.system.longRange 0))}}/{{/if}}{{#if (gt rollTarget.weapon.system.longRange 0)}}{{rollTarget.weapon.system.longRange}}{{/if}}ft
</span>
</div>
{{/if}}
{{#if (gt rollTarget.weapon.system.reloadAPC 0)}}
<div class="stat-item reload">
<i class="fas fa-rotate"></i>
<span>Reload {{rollTarget.weapon.system.reloadAPC}}</span>
</div>
{{/if}}
</div>
</div>
{{/if}}
{{#if rollTarget.weapon}}
<span>{{rollTarget.weapon.name}}</span>
{{#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}}
<span>Let It Fly attack ! </span>
<div class="special-badge">Let It Fly!</div>
{{/if}}
{{#if rollData.pointBlank}}
<span>Point Blank Range Attack !</span>
<div class="special-badge">Point Blank</div>
{{/if}}
{{#if rollData.beyondSkill}}
<span>Beyond Skill Range Attack !</span>
<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}}
<span><strong>Formula</strong> : {{titleFormula}}</span>
<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|}}
<span>{{result.dice}} : {{result.value}}</span>
<div class="dice-breakdown">
<i class="fas fa-dice"></i> {{result.dice}}: {{result.value}}
</div>
{{/each}}
</div>
</div>
{{#if isSave}}
<div class="result">
{{#if (eq resultType "success")}}
{{#if isPrivate}}?{{else}}{{localize "PRISMRPG.Roll.success"}}{{/if}}
{{else}}
{{#if isPrivate}}?{{else}}{{localize "PRISMRPG.Roll.failure"}}{{/if}}
{{#if (eq rollType "weapon-attack")}}
<button class="roll-damage-button" type="button" data-actor-id="{{actorId}}" data-weapon-id="{{rollTarget.weapon._id}}">
<i class="fa-regular fa-face-head-bandage"></i>
Roll Damage
</button>
{{/if}}
</div>
{{/if}}
{{#if isResource}}
<div class="result">
{{#if (eq resultType "success")}}
{{#if isPrivate}}?{{else}}{{localize "PRISMRPG.Roll.success"}}{{/if}}
{{else}}
{{#if isPrivate}}?{{else}}{{localize "PRISMRPG.Roll.failure"}}{{#if isFailure}} ({{localize
"PRISMRPG.Roll.resourceLost"}}){{/if}}{{/if}}
{{/if}}
</div>
{{/if}}
{{#if isDamage}}
<div>
{{#if (and isGM hasTarget)}}
{{{localize "PRISMRPG.Roll.displayArmor" targetName=targetName targetArmor=targetArmor realDamage=realDamage}}}
{{/if}}
</div>
{{/if}}
{{#unless isPrivate}}
<div class="dice-result">
<h4 class="dice-total">{{total}}</h4>
<div class="roll-result">
<div class="result-total">
<span class="total-label">Result</span>
<span class="total-value">{{total}}</span>
</div>
{{#if D30result}}
<div class="dice-result">
<h4 class="dice-total">D30 result: {{D30result}}</h4>
<div class="d30-result">
<i class="fas fa-dice-d20"></i> D30: {{D30result}}
</div>
{{/if}}
</div>
{{/unless}}
{{#if isSave}}
<div class="result-badge {{resultType}}">
{{#if isPrivate}}
?
{{else}}
{{#if (eq resultType "success")}}
<i class="fas fa-check-circle"></i> {{localize "PRISMRPG.Roll.success"}}
{{else}}
<i class="fas fa-times-circle"></i> {{localize "PRISMRPG.Roll.failure"}}
{{/if}}
{{/if}}
</div>
{{/if}}
{{/unless}}
{{#if isResource}}
<div class="result-badge {{resultType}}">
{{#if isPrivate}}
?
{{else}}
{{#if (eq resultType "success")}}
<i class="fas fa-check-circle"></i> {{localize "PRISMRPG.Roll.success"}}
{{else}}
<i class="fas fa-times-circle"></i> {{localize "PRISMRPG.Roll.failure"}}{{#if isFailure}} ({{localize "PRISMRPG.Roll.resourceLost"}}){{/if}}
{{/if}}
{{/if}}
</div>
{{/if}}
{{#if isDamage}}
{{#if (and isGM hasTarget)}}
<div class="damage-info">
{{{localize "PRISMRPG.Roll.displayArmor" targetName=targetName targetArmor=targetArmor realDamage=realDamage}}}
</div>
{{/if}}
{{/if}}
</div>
+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>
+78 -13
View File
@@ -4,6 +4,15 @@
{{formInput fields.name value=source.name}}
</div>
{{! Navigation des onglets }}
<nav class="sheet-tabs tabs" data-group="primary">
<a class="item {{tabs.details.cssClass}}" data-tab="details">{{localize "PRISMRPG.Label.details"}}</a>
<a class="item {{tabs.advancements.cssClass}}" data-tab="advancements">{{localize "PRISMRPG.Label.advancement"}}</a>
<a class="item {{tabs.description.cssClass}}" data-tab="description">{{localize "PRISMRPG.Label.description"}}</a>
</nav>
{{! Onglet Details }}
<div class="tab {{tabs.details.cssClass}}" data-group="primary" data-tab="details">
<div class="flexrow">
<div class="align-top">
@@ -53,7 +62,7 @@
<legend>{{localize "PRISMRPG.Label.classFeatures"}}</legend>
{{#each system.features as |feature level|}}
<label>{{localize "PRISMRPG.Label.level"}} {{inc @key}}</label>
<label>{{localize "PRISMRPG.Label.level"}} {{replace @key "level" ""}}</label>
{{formInput
(lookup ../systemFields.features.fields @key)
enriched=(lookup ../enrichedFeatures @key)
@@ -63,6 +72,73 @@
}}
{{/each}}
</fieldset>
</div>
{{! Onglet Advancements }}
<div class="tab {{tabs.advancements.cssClass}}" data-group="primary" data-tab="advancements">
<fieldset>
<legend>{{localize "PRISMRPG.Label.advancement"}}</legend>
{{#each advancementsByLevel as |levelData|}}
<div class="advancement-level">
<h3>
<span class="level-title">{{localize "PRISMRPG.Label.level"}} {{levelData.level}}</span>
<button type="button" class="add-advancement" data-level="{{levelData.levelKey}}" data-tooltip="{{localize 'PRISMRPG.Label.addAdvancement'}}">
<i class="fas fa-plus"></i>
</button>
</h3>
{{#if levelData.advancements.length}}
<div class="advancement-list">
{{#each levelData.advancements as |advancement|}}
<div class="advancement-item">
<div class="advancement-header">
<img class="advancement-icon" src="{{advancement.icon}}" data-level="{{advancement.levelKey}}" data-index="{{advancement.index}}" />
{{formInput
../../systemFields.advancements.fields.level1.element.fields.name
value=advancement.name
name=(concat "system.advancements." advancement.levelKey "." advancement.index ".name")
placeholder=(localize "PRISMRPG.Label.advancementName")
}}
<button type="button" class="toggle-advancement-description" data-tooltip="{{localize 'PRISMRPG.Label.toggleDescription'}}">
<i class="fas fa-chevron-down"></i>
</button>
<button type="button" class="delete-advancement" data-level="{{advancement.levelKey}}" data-index="{{advancement.index}}" data-tooltip="{{localize 'PRISMRPG.Label.deleteAdvancement'}}">
<i class="fas fa-trash"></i>
</button>
</div>
<div class="advancement-description collapsed">
{{formInput
../../systemFields.advancements.fields.level1.element.fields.description
enriched=advancement.enrichedDescription
value=advancement.description
name=(concat "system.advancements." advancement.levelKey "." advancement.index ".description")
toggled=true
}}
</div>
</div>
{{/each}}
</div>
{{else}}
<p class="empty-advancements">{{localize "PRISMRPG.Label.noAdvancements"}}</p>
{{/if}}
</div>
{{/each}}
</fieldset>
</div>
{{! Onglet Description }}
<div class="tab {{tabs.description.cssClass}}" data-group="primary" data-tab="description">
<fieldset>
<legend>{{localize "PRISMRPG.Label.description"}}</legend>
{{formInput
systemFields.description
enriched=enrichedDescription
value=system.description
name="system.description"
toggled=true
}}
</fieldset>
{{! Notes }}
<fieldset>
@@ -75,17 +151,6 @@
toggled=true
}}
</fieldset>
{{! Description }}
<fieldset>
<legend>{{localize "PRISMRPG.Label.description"}}</legend>
{{formInput
systemFields.description
enriched=enrichedDescription
value=system.description
name="system.description"
toggled=true
}}
</fieldset>
</div>
</section>
+19
View File
@@ -9,6 +9,16 @@
/>
{{formInput fields.name value=source.name}}
</div>
{{! Navigation des onglets }}
<nav class="sheet-tabs tabs" data-group="primary">
<a class="item {{tabs.details.cssClass}}" data-tab="details">{{localize "PRISMRPG.Label.details"}}</a>
<a class="item {{tabs.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 Details }}
<div class="tab {{tabs.details.cssClass}}" data-group="primary" data-tab="details">
{{formField systemFields.encLoad value=system.encLoad localize=true}}
{{formField systemFields.cost value=system.cost localize=true}}
{{formField systemFields.money value=system.money localize=true}}
@@ -73,7 +83,10 @@
{{/unless}}
</fieldset>
{{/if}}
</div>
{{! Onglet Description }}
<div class="tab" data-group="primary" data-tab="description">
<fieldset>
<legend>{{localize "PRISMRPG.Label.description"}}</legend>
{{formInput
@@ -84,5 +97,11 @@
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>
+58 -21
View File
@@ -10,6 +10,26 @@
{{formInput fields.name value=source.name}}
</div>
{{! Navigation des onglets }}
<nav class="sheet-tabs tabs" data-group="primary">
<a class="item {{tabs.details.cssClass}}" data-tab="details">{{localize
"PRISMRPG.Label.details"
}}</a>
<a
class="item {{tabs.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 Details }}
<div
class="tab {{tabs.details.cssClass}}"
data-group="primary"
data-tab="details"
>
{{! Prism RPG: Miracle Type }}
{{formField
systemFields.miracleType
@@ -42,27 +62,6 @@
label="PRISMRPG.Label.divineFavor"
}}
{{! Prism RPG: Miracle Augment }}
<fieldset class="miracle-augment">
<legend>{{localize "PRISMRPG.Label.miracleAugment"}}</legend>
{{formField
systemFields.augment
value=system.augment
localize=true
label="PRISMRPG.Label.augmentName"
}}
<div class="form-group">
<label>{{localize "PRISMRPG.Label.augmentDescription"}}</label>
{{formInput
systemFields.augmentDescription
enriched=enrichedAugmentDescription
value=system.augmentDescription
name="system.augmentDescription"
toggled=true
}}
</div>
</fieldset>
{{! Miracle Components (includes Religious) }}
<fieldset class="miracle-components">
<legend>{{localize "PRISMRPG.Label.components"}}</legend>
@@ -152,6 +151,34 @@
label="PRISMRPG.Label.level"
}}
{{! Prism RPG: Miracle Augment }}
<fieldset class="miracle-augment">
<legend>{{localize "PRISMRPG.Label.miracleAugment"}}</legend>
{{formField
systemFields.augment
value=system.augment
localize=true
label="PRISMRPG.Label.augmentName"
}}
<div class="form-group">
<label>{{localize "PRISMRPG.Label.augmentDescription"}}</label>
{{formInput
systemFields.augmentDescription
enriched=enrichedAugmentDescription
value=system.augmentDescription
name="system.augmentDescription"
toggled=true
}}
</div>
</fieldset>
</div>
{{! Onglet Description }}
<div
class="tab {{tabs.description.cssClass}}"
data-group="primary"
data-tab="description"
>
<fieldset>
<legend>{{localize "PRISMRPG.Label.description"}}</legend>
{{formInput
@@ -162,5 +189,15 @@
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>
+80
View File
@@ -0,0 +1,80 @@
{{! Template pour l'onglet Effects des items - organisé par catégories }}
<div class="effects-container">
{{#each effectCategories as |section sid|}}
<div class="effect-category">
<ul class="stat-list alternate-list">
<li class="item flexrow list-item items-title-bg">
<span class="item-name-label-header-long">
<h3><label class="items-title-text">{{localize
section.label
}}</label></h3>
</span>
<span class="item-field-label-short">
<label class="short-label">{{localize
"PRISMRPG.Label.source"
}}</label>
</span>
<span class="item-field-label-medium">
<label class="short-label">{{localize
"PRISMRPG.Label.duration"
}}</label>
</span>
<div class="item-filler">&nbsp;</div>
<div class="item-controls item-controls-fixed">
<a
class="effect-control"
data-action="create-effect"
data-effect-type="{{section.type}}"
title="{{localize 'DOCUMENT.Create' type="Effect"}}"
>
<i class="fas fa-plus"></i>
{{localize "DOCUMENT.New" type="Effect"}}
</a>
</div>
</li>
{{#each section.effects as |effect|}}
<li
class="item flexrow list-item list-item-shadow"
data-item-id="{{effect._id}}"
data-effect-id="{{effect._id}}"
data-parent-id="{{effect.parent.id}}"
>
<a
class="item-edit item-name-img"
data-action="effect-edit"
title="Edit Effect"
>
<img class="sheet-competence-img" src="{{effect.img}}" />
</a>
<span class="item-name-label-long">{{effect.name}}</span>
<span class="item-field-label-short">{{effect.sourceName}}</span>
<span
class="item-field-label-short"
>{{effect.duration.label}}</span>
<div class="item-filler">&nbsp;</div>
<div class="item-controls effect-controls flexrow">
<a
class="effect-control item-edit"
data-action="effect-edit"
title="{{localize 'DOCUMENT.Update' type="Effect"}}"
>
<i class="fas fa-edit"></i>
</a>
<a
class="effect-control"
data-action="effect-delete"
title="{{localize 'DOCUMENT.Delete' type="Effect"}}"
>
<i class="fas fa-trash"></i>
</a>
</div>
</li>
{{/each}}
</ul>
</div>
{{/each}}
</div>
+36 -12
View File
@@ -10,6 +10,23 @@
{{formInput fields.name value=source.name}}
</div>
{{! Navigation des onglets }}
<nav class="sheet-tabs tabs" data-group="primary">
<a class="item {{tabs.details.cssClass}}" data-tab="details">{{localize
"PRISMRPG.Label.details"
}}</a>
<a
class="item {{tabs.description.cssClass}}"
data-tab="description"
>{{localize "PRISMRPG.Label.description"}}</a>
</nav>
{{! Onglet Details }}
<div
class="tab {{tabs.details.cssClass}}"
data-group="primary"
data-tab="details"
>
<div class="flexrow">
<div class="align-top">
@@ -63,6 +80,24 @@
toggled=true
}}
</fieldset>
</div>
{{! 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>
{{! Notes }}
<fieldset>
@@ -75,17 +110,6 @@
toggled=true
}}
</fieldset>
{{! Description }}
<fieldset>
<legend>{{localize "PRISMRPG.Label.description"}}</legend>
{{formInput
systemFields.description
enriched=enrichedDescription
value=system.description
name="system.description"
toggled=true
}}
</fieldset>
</div>
</section>
+14
View File
@@ -10,6 +10,14 @@
{{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
@@ -20,5 +28,11 @@
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>
+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>
+127 -78
View File
@@ -1,82 +1,151 @@
<div class="prismrpg-roll-dialog">
<fieldSet>
<legend>{{actorName}}</legend>
<fieldSet class="">
<legend>{{localize (concat "PRISMRPG.Label." rollType)}} - {{actorName}}</legend>
{{#if rollTarget.tokenId}}
{{#if rollTarget.weapon}}
<div class="dialog-save">
<a class="goto-token-button" data-action="gotoToken" data-token-id="{{rollTarget.tokenId}}">{{localize
"PRISMRPG.Label.gotoToken"}} </a>
<strong>{{rollTarget.weapon.name}}</strong>
</div>
{{/if}}
</fieldSet>
{{#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>
{{! 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}}
{{#if (match rollType "attack")}}
<div class="dialog-save">Add Granted Attack Dice
<input type="checkbox" data-action="selectGranted" name="granted">
{{! 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 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">
{{/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">Beyond Skill Range Attack
<input type="checkbox" data-action="selectBeyondSkill" name="beyondSkillV">
<div class="dialog-save">
<label>
<input
type="checkbox"
name="beyondSkill"
data-action="selectBeyondSkill"
/>
Beyond Skill Range Attack
</label>
</div>
<div class="dialog-save">Let it Fly (Pure D20E)
<input type="checkbox" data-action="selectLetItFly" name="letItFlyV">
<div class="dialog-save">
<label>
<input
type="checkbox"
name="letItFly"
data-action="selectLetItFly"
/>
Let it Fly (Pure D20E)
</label>
</div>
<div class="dialog-save">Aiming
<div class="dialog-save">
<label>Aiming:</label>
<select name="attackerAim" data-tooltip-direction="UP">
{{selectOptions attackerAimChoices selected=attackerAim}}
{{selectOptions attackerAimChoices selected="simple"}}
</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}}
{{#if rollTarget.type}}
{{#if (eq rollTarget.type "spell")}}
{{! Spell-specific options }}
<fieldSet class="dialog-spell-options">
<legend>Spell Options</legend>
{{#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}}
<div class="dialog-save" style="font-size: 0.9em; color: #666; margin-bottom: 0.5em;">
<span>Casting with: {{rollTarget.mentalCharacteristic}} ({{rollTarget.mentalCharValue}}, +{{rollTarget.value}})</span>
<br><span>Base Cost: {{rollTarget.system.manaCost}} Mana, {{rollTarget.system.apc}} APC</span>
{{#if (gt rollTarget.system.manaUpkeep 0)}}
<br><span>Upkeep: {{rollTarget.system.manaUpkeep}} Mana/round</span>
{{/if}}
</div>
{{#if rollTarget.system.canAscend}}
<div class="dialog-save">
<label>Upcast Level:</label>
<select name="upcastLevel" data-tooltip-direction="UP">
<option value="0">Base Level ({{rollTarget.system.level}})</option>
<option value="1">+1 Level (+1 Mana, +1 APC)</option>
<option value="2">+2 Levels (+2 Mana, +2 APC)</option>
<option value="3">+3 Levels (+3 Mana, +3 APC)</option>
<option value="4">+4 Levels (+4 Mana, +4 APC)</option>
<option value="5">+5 Levels (+5 Mana, +5 APC)</option>
<option value="6">+6 Levels (+6 Mana, +6 APC)</option>
<option value="7">+7 Levels (+7 Mana, +7 APC)</option>
</select>
</div>
{{/if}}
</fieldSet>
{{/if}}
{{/if}}
{{! Skill Options }}
{{#if (eq rollType "skill")}}
<fieldSet class="dialog-skill-options">
<legend>Skill Options</legend>
{{! Choose which sub-attribute to use }}
<div class="dialog-save">
<label>Use Sub-Attribute:</label>
<select name="skillSubAttribute" data-tooltip-direction="UP">
<option value="{{rollTarget.subAttribute1}}" selected>
{{rollTarget.subAttribute1Label}} (+{{rollTarget.subAttribute1Value}})
</option>
<option value="{{rollTarget.subAttribute2}}">
{{rollTarget.subAttribute2Label}} (+{{rollTarget.subAttribute2Value}})
</option>
</select>
</div>
</fieldSet>
{{/if}}
{{#if hasAdvantage}}
<fieldSet class="dialog-advantage">
<legend>{{localize "PRISMRPG.Roll.advantageDisadvantage"}}</legend>
<select
name="advantage"
class="advantage-choice"
data-tooltip-direction="UP"
>
{{selectOptions choiceAdvantage selected=advantage}}
</select>
</fieldSet>
{{/if}}
@@ -87,24 +156,6 @@
<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}}
@@ -114,6 +165,4 @@
{{selectOptions rollModes selected=visibility localize=true}}
</select>
</fieldSet>
</div>
+37
View File
@@ -10,6 +10,26 @@
{{formInput fields.name value=source.name}}
</div>
{{! Navigation des onglets }}
<nav class="sheet-tabs tabs" data-group="primary">
<a class="item {{tabs.details.cssClass}}" data-tab="details">{{localize
"PRISMRPG.Label.details"
}}</a>
<a
class="item {{tabs.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 Details }}
<div
class="tab {{tabs.details.cssClass}}"
data-group="primary"
data-tab="details"
>
<div class="flexrow">
<div class="align-top">
@@ -89,7 +109,14 @@
toggled=true
}}
</fieldset>
</div>
{{! Onglet Description }}
<div
class="tab {{tabs.description.cssClass}}"
data-group="primary"
data-tab="description"
>
<fieldset>
<legend>{{localize "PRISMRPG.Label.description"}}</legend>
{{formInput
@@ -100,5 +127,15 @@
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>
+18 -9
View File
@@ -11,22 +11,31 @@
<p class="hint">{{localize "PRISMRPG.Hint.isCoreSkill"}}</p>
</div>
{{#if system.isCoreSkill}}
{{!-- Attribute Bonus Selection --}}
{{!-- First Sub-Attribute for Skill Checks --}}
<div class="form-group">
<label>{{localize "PRISMRPG.Label.attributeBonusChoice"}}</label>
<select name="system.attributeBonus">
<option value="">{{localize "PRISMRPG.Label.selectAttribute"}}</option>
{{#each config.CHARACTERISTICS}}
<option value="{{@key}}" {{#if (eq ../system.attributeBonus @key)}}selected{{/if}}>
<label>{{localize "PRISMRPG.Label.subAttribute1"}}</label>
<select name="system.subAttribute1">
{{#each config.SUB_ATTRIBUTES}}
<option value="{{@key}}" {{#if (eq ../system.subAttribute1 @key)}}selected{{/if}}>
{{localize this.label}}
</option>
{{/each}}
</select>
<p class="hint">{{localize "PRISMRPG.Hint.attributeBonus"}}</p>
<p class="hint">First sub-attribute used for skill checks</p>
</div>
{{/if}}
{{!-- Second Sub-Attribute for Skill Checks --}}
<div class="form-group">
<label>{{localize "PRISMRPG.Label.subAttribute2"}}</label>
<select name="system.subAttribute2">
{{#each config.SUB_ATTRIBUTES}}
<option value="{{@key}}" {{#if (eq ../system.subAttribute2 @key)}}selected{{/if}}>
{{localize this.label}}
</option>
{{/each}}
</select>
<p class="hint">Second sub-attribute used for skill checks</p>
</div>
{{!-- Notes --}}
<fieldset>
+73 -36
View File
@@ -10,6 +10,26 @@
{{formInput fields.name value=source.name}}
</div>
{{! Navigation des onglets }}
<nav class="sheet-tabs tabs" data-group="primary">
<a class="item {{tabs.details.cssClass}}" data-tab="details">{{localize
"PRISMRPG.Label.details"
}}</a>
<a
class="item {{tabs.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 Details }}
<div
class="tab {{tabs.details.cssClass}}"
data-group="primary"
data-tab="details"
>
{{! Prism RPG: Mana Cost }}
{{formField
systemFields.manaCost
@@ -42,42 +62,6 @@
label="PRISMRPG.Label.prismColor"
}}
{{! Prism RPG: Color Effect }}
<fieldset class="spell-color-effect">
<legend>{{localize "PRISMRPG.Label.colorEffect"}}</legend>
{{formInput
systemFields.colorEffect
enriched=enrichedColorEffect
value=system.colorEffect
name="system.colorEffect"
toggled=true
}}
</fieldset>
{{! Prism RPG: Spell Ascension }}
<fieldset class="spell-ascension">
<legend>{{localize "PRISMRPG.Label.spellAscension"}}</legend>
<div class="form-group">
<label>{{localize "PRISMRPG.Label.canAscend"}}</label>
<input
type="checkbox"
name="system.canAscend"
{{checked system.canAscend}}
/>
<p class="hint">{{localize "PRISMRPG.Hint.spellAscension"}}</p>
</div>
{{#if system.canAscend}}
<label>{{localize "PRISMRPG.Label.ascensionEffect"}}</label>
{{formInput
systemFields.ascensionEffect
enriched=enrichedAscensionEffect
value=system.ascensionEffect
name="system.ascensionEffect"
toggled=true
}}
{{/if}}
</fieldset>
{{! Spell Properties }}
{{formField
systemFields.memorized
@@ -140,6 +124,49 @@
label="PRISMRPG.Label.keywords"
}}
{{! Prism RPG: Color Effect }}
<fieldset class="spell-color-effect">
<legend>{{localize "PRISMRPG.Label.colorEffect"}}</legend>
{{formInput
systemFields.colorEffect
enriched=enrichedColorEffect
value=system.colorEffect
name="system.colorEffect"
toggled=true
}}
</fieldset>
{{! Prism RPG: Spell Ascension }}
<fieldset class="spell-ascension">
<legend>{{localize "PRISMRPG.Label.spellAscension"}}</legend>
<div class="form-group">
<label>{{localize "PRISMRPG.Label.canAscend"}}</label>
<input
type="checkbox"
name="system.canAscend"
{{checked system.canAscend}}
/>
<p class="hint">{{localize "PRISMRPG.Hint.spellAscension"}}</p>
</div>
{{#if system.canAscend}}
<label>{{localize "PRISMRPG.Label.ascensionEffect"}}</label>
{{formInput
systemFields.ascensionEffect
enriched=enrichedAscensionEffect
value=system.ascensionEffect
name="system.ascensionEffect"
toggled=true
}}
{{/if}}
</fieldset>
</div>
{{! Onglet Description }}
<div
class="tab {{tabs.description.cssClass}}"
data-group="primary"
data-tab="description"
>
<fieldset>
<legend>{{localize "PRISMRPG.Label.description"}}</legend>
{{formInput
@@ -150,5 +177,15 @@
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>
+186
View File
@@ -0,0 +1,186 @@
<form class="weapon-types-config">
<nav class="sheet-tabs tabs" data-group="primary">
<a class="item" data-tab="types">{{localize
"PRISMRPG.Settings.tabs.weaponTypes"
}}</a>
<a class="item" data-tab="groups">{{localize
"PRISMRPG.Settings.tabs.weaponGroups"
}}</a>
</nav>
<div class="content">
{{! Weapon Types Tab }}
<div class="tab" data-group="primary" data-tab="types">
<div class="form-group">
<label class="section-header">
{{localize "PRISMRPG.Settings.weaponTypes.header"}}
<button
type="button"
data-action="add-weapon-type"
data-tooltip="{{localize 'PRISMRPG.Settings.addWeaponType'}}"
>
<i class="fas fa-plus"></i>
</button>
</label>
<div class="weapon-types-list">
{{#each weaponTypes}}
<div class="weapon-type-entry" data-id="{{this.id}}">
<div class="form-fields">
<div class="form-group">
<label>{{localize "PRISMRPG.Settings.weaponType.id"}}</label>
<input
type="text"
name="weaponTypes.{{this.id}}.id"
value="{{this.id}}"
readonly
/>
</div>
<div class="form-group">
<label>{{localize
"PRISMRPG.Settings.weaponType.label"
}}</label>
<input
type="text"
name="weaponTypes.{{this.id}}.label"
value="{{this.label}}"
/>
</div>
<div class="form-group">
<label>{{localize "PRISMRPG.Settings.weaponType.apc"}}</label>
<input
type="number"
name="weaponTypes.{{this.id}}.apc"
value="{{this.apc}}"
min="0"
max="10"
/>
</div>
<div class="form-group">
<label>{{localize
"PRISMRPG.Settings.weaponType.hands"
}}</label>
<input
type="number"
name="weaponTypes.{{this.id}}.hands"
value="{{this.hands}}"
min="0"
max="2"
/>
</div>
</div>
{{#if this.isCustom}}
<button
type="button"
data-action="delete-weapon-type"
data-id="{{this.id}}"
data-tooltip="{{localize
'PRISMRPG.Settings.deleteWeaponType'
}}"
>
<i class="fas fa-trash"></i>
</button>
{{/if}}
</div>
{{/each}}
</div>
</div>
</div>
{{! Weapon Groups Tab }}
<div class="tab" data-group="primary" data-tab="groups">
<div class="form-group">
<label class="section-header">
{{localize "PRISMRPG.Settings.weaponGroups.header"}}
<button
type="button"
data-action="add-weapon-group"
data-tooltip="{{localize 'PRISMRPG.Settings.addWeaponGroup'}}"
>
<i class="fas fa-plus"></i>
</button>
</label>
<div class="weapon-groups-list">
{{#each weaponGroups}}
<div class="weapon-group-entry" data-id="{{this.id}}">
<div class="form-fields">
<div class="form-group">
<label>{{localize "PRISMRPG.Settings.weaponGroup.id"}}</label>
<input
type="text"
name="weaponGroups.{{this.id}}.id"
value="{{this.id}}"
readonly
/>
</div>
<div class="form-group">
<label>{{localize
"PRISMRPG.Settings.weaponGroup.label"
}}</label>
<input
type="text"
name="weaponGroups.{{this.id}}.label"
value="{{this.label}}"
/>
</div>
<div class="form-group">
<label>{{localize
"PRISMRPG.Settings.weaponGroup.passive"
}}</label>
<input
type="text"
name="weaponGroups.{{this.id}}.passive"
value="{{this.passive}}"
/>
</div>
<div class="form-group">
<label>{{localize
"PRISMRPG.Settings.weaponGroup.passiveLabel"
}}</label>
<input
type="text"
name="weaponGroups.{{this.id}}.passiveLabel"
value="{{this.passiveLabel}}"
/>
</div>
<div class="form-group">
<label>{{localize
"PRISMRPG.Settings.weaponGroup.passiveDescription"
}}</label>
<textarea
name="weaponGroups.{{this.id}}.passiveDescription"
rows="2"
>{{this.passiveDescription}}</textarea>
</div>
</div>
{{#if this.isCustom}}
<button
type="button"
data-action="delete-weapon-group"
data-id="{{this.id}}"
data-tooltip="{{localize
'PRISMRPG.Settings.deleteWeaponGroup'
}}"
>
<i class="fas fa-trash"></i>
</button>
{{/if}}
</div>
{{/each}}
</div>
</div>
</div>
</div>
<footer class="sheet-footer flexrow">
<button type="button" data-action="reset-defaults">
<i class="fas fa-undo"></i>
{{localize "PRISMRPG.Settings.resetDefaults"}}
</button>
<button type="submit">
<i class="fas fa-save"></i>
{{localize "PRISMRPG.Settings.save"}}
</button>
</footer>
</form>
+27 -9
View File
@@ -10,6 +10,15 @@
{{formInput fields.name value=source.name}}
</div>
{{! Navigation des onglets }}
<nav class="sheet-tabs tabs" data-group="primary">
<a class="item {{tabs.details.cssClass}}" data-tab="details">{{localize "PRISMRPG.Label.details"}}</a>
<a class="item {{tabs.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 Details }}
<div class="tab {{tabs.details.cssClass}}" data-group="primary" data-tab="details">
<div class="flexrow">
<div class="align-top">
@@ -62,27 +71,26 @@
</div>
<div class="align-top">
{{! Prism RPG: Projectile Properties }}
{{#if system.isProjectile}}
{{formField
systemFields.isProjectile
value=system.isProjectile
systemFields.shortRange
value=system.shortRange
localize=true
label="PRISMRPG.Label.isProjectile"
label="PRISMRPG.Label.shortRange"
}}
{{formField
systemFields.range
value=system.range
systemFields.longRange
value=system.longRange
localize=true
label="PRISMRPG.Label.range"
label="PRISMRPG.Label.longRange"
}}
{{formField
systemFields.reloadAPC
value=system.reloadAPC
localize=true
label="PRISMRPG.Label.reloadAPC"
}}
{{/if}}
{{formField
systemFields.encLoad
@@ -176,7 +184,10 @@
<p class="hint">{{localize "PRISMRPG.Hint.noManeuvers"}}</p>
{{/unless}}
</fieldset>
</div>
{{! Onglet Description }}
<div class="tab {{tabs.description.cssClass}}" data-group="primary" data-tab="description">
<fieldset>
<legend>{{localize "PRISMRPG.Label.description"}}</legend>
{{formInput
@@ -187,4 +198,11 @@
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>