First iteration over items

This commit is contained in:
2025-11-06 23:30:37 +01:00
parent 6b883f8126
commit 4a9f026a2a
109 changed files with 1535 additions and 715 deletions
+3 -2
View File
@@ -2,11 +2,12 @@ export { default as PrismRPGCharacterSheet } from "./sheets/character-sheet.mjs"
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 PrismRPGGiftSheet } from "./sheets/gift-sheet.mjs"
export { default as PrismRPGRacialAbilitySheet } from "./sheets/racial-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"
export { default as PrismRPGEquipmentSheet } from "./sheets/equipment-sheet.mjs"
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"
@@ -24,6 +24,8 @@ export default class PrismRPGArmorSheet extends PrismRPGItemSheet {
async _prepareContext() {
const context = await super._prepareContext()
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 })
return context
}
@@ -45,9 +45,11 @@ 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",
},
@@ -72,9 +74,11 @@ 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" : ""
@@ -99,7 +103,7 @@ export default class PrismRPGCharacterSheet extends PrismRPGActorSheet {
case "skills":
context.tab = context.tabs.skills
context.skills = doc.itemTypes.skill
context.gifts = doc.itemTypes.gift
context.racialAbilities = doc.itemTypes["racial-ability"]
context.vulnerabilities = doc.itemTypes.vulnerability
break
case "spells":
@@ -107,11 +111,13 @@ export default class PrismRPGCharacterSheet extends PrismRPGActorSheet {
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
@@ -0,0 +1,39 @@
import PrismRPGItemSheet from "./base-item-sheet.mjs"
export default class PrismRPGClassSheet extends PrismRPGItemSheet {
/** @override */
static DEFAULT_OPTIONS = {
classes: ["class"],
position: {
width: 700,
height: 800,
},
window: {
contentClasses: ["class-content"],
},
}
/** @override */
static PARTS = {
main: {
template: "systems/fvtt-prism-rpg/templates/class.hbs",
},
}
/** @override */
async _prepareContext() {
const context = await super._prepareContext()
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 })
// Enrich all feature levels
context.enrichedFeatures = {}
for (let i = 1; i <= 10; i++) {
const key = `level${i}`
context.enrichedFeatures[key] = await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.document.system.features[key], { async: true })
}
return context
}
}
+31
View File
@@ -0,0 +1,31 @@
import PrismRPGItemSheet from "./base-item-sheet.mjs"
export default class PrismRPGRaceSheet extends PrismRPGItemSheet {
/** @override */
static DEFAULT_OPTIONS = {
classes: ["race"],
position: {
width: 650,
},
window: {
contentClasses: ["race-content"],
},
}
/** @override */
static PARTS = {
main: {
template: "systems/fvtt-prism-rpg/templates/race.hbs",
},
}
/** @override */
async _prepareContext() {
const context = await super._prepareContext()
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 })
context.enrichedNotes = await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.document.system.notes, { async: true })
return context
}
}
@@ -1,21 +1,21 @@
import PrismRPGItemSheet from "./base-item-sheet.mjs"
export default class PrismRPGGiftSheet extends PrismRPGItemSheet {
export default class PrismRPGRacialAbilitySheet extends PrismRPGItemSheet {
/** @override */
static DEFAULT_OPTIONS = {
classes: ["gift"],
classes: ["racial-ability"],
position: {
width: 600,
},
window: {
contentClasses: ["gift-content"],
contentClasses: ["racial-ability-content"],
},
}
/** @override */
static PARTS = {
main: {
template: "systems/fvtt-prism-rpg/templates/gift.hbs",
template: "systems/fvtt-prism-rpg/templates/racial-ability.hbs",
},
}
@@ -23,6 +23,7 @@ export default class PrismRPGShieldSheet extends PrismRPGItemSheet {
async _prepareContext() {
const context = await super._prepareContext()
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
}
}
@@ -1,4 +1,5 @@
import PrismRPGItemSheet from "./base-item-sheet.mjs"
import { SYSTEM } from "../../config/system.mjs"
export default class PrismRPGSkillSheet extends PrismRPGItemSheet {
/** @override */
@@ -22,7 +23,9 @@ export default class PrismRPGSkillSheet extends PrismRPGItemSheet {
/** @override */
async _prepareContext() {
const context = await super._prepareContext()
context.config = SYSTEM
context.enrichedDescription = await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.document.system.description, { async: true })
context.enrichedNotes = await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.document.system.notes, { async: true })
return context
}
}
@@ -23,6 +23,8 @@ export default class PrismRPGSpellSheet extends PrismRPGItemSheet {
async _prepareContext() {
const context = await super._prepareContext()
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 })
return context
}
@@ -23,6 +23,8 @@ export default class PrismRPGWeaponSheet extends PrismRPGItemSheet {
async _prepareContext() {
const context = await super._prepareContext()
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.enrichedManeuverDescription = await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.document.system.maneuverDescription, { async: true })
return context
}
+20
View File
@@ -0,0 +1,20 @@
/**
* Class configuration for Prism RPG
*/
export const ARCHETYPE = {
faith: "PRISMRPG.Class.Archetype.faith",
mana: "PRISMRPG.Class.Archetype.mana",
wanderer: "PRISMRPG.Class.Archetype.wanderer",
warrior: "PRISMRPG.Class.Archetype.warrior"
}
export const CLASS_TYPE = {
core: "PRISMRPG.Class.ClassType.core",
continental: "PRISMRPG.Class.ClassType.continental"
}
export const SPELLCASTING_TYPE = {
mana: "PRISMRPG.Class.SpellcastingType.mana",
faith: "PRISMRPG.Class.SpellcastingType.faith"
}
+15
View File
@@ -0,0 +1,15 @@
/**
* Race configuration for Prism RPG
*/
export const SIZE = {
small: "PRISMRPG.Race.Size.small",
medium: "PRISMRPG.Race.Size.medium",
large: "PRISMRPG.Race.Size.large"
}
export const AGE_CATEGORY = {
short: "PRISMRPG.Race.AgeCategory.short",
medium: "PRISMRPG.Race.AgeCategory.medium",
long: "PRISMRPG.Race.AgeCategory.long"
}
+9
View File
@@ -38,6 +38,15 @@ export const TYPE = Object.freeze({
}
});
/**
* Simplified Shield 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])
)
);
/**
* Block augment descriptions for reference
* These are activated when using the Block action with the shield
+24
View File
@@ -99,6 +99,30 @@ export const CORE_SKILLS = Object.freeze({
}
});
/**
* Simplified Core Skills object for form choices (label-only format)
*/
export const CORE_SKILLS_CHOICES = {
acrobatics: "PRISMRPG.Skill.CoreSkill.acrobatics",
animalHandling: "PRISMRPG.Skill.CoreSkill.animalHandling",
arcana: "PRISMRPG.Skill.CoreSkill.arcana",
athletics: "PRISMRPG.Skill.CoreSkill.athletics",
deception: "PRISMRPG.Skill.CoreSkill.deception",
history: "PRISMRPG.Skill.CoreSkill.history",
insight: "PRISMRPG.Skill.CoreSkill.insight",
intimidate: "PRISMRPG.Skill.CoreSkill.intimidate",
investigation: "PRISMRPG.Skill.CoreSkill.investigation",
medicine: "PRISMRPG.Skill.CoreSkill.medicine",
nature: "PRISMRPG.Skill.CoreSkill.nature",
perception: "PRISMRPG.Skill.CoreSkill.perception",
performance: "PRISMRPG.Skill.CoreSkill.performance",
persuasion: "PRISMRPG.Skill.CoreSkill.persuasion",
religion: "PRISMRPG.Skill.CoreSkill.religion",
sleightOfHand: "PRISMRPG.Skill.CoreSkill.sleightOfHand",
stealth: "PRISMRPG.Skill.CoreSkill.stealth",
survival: "PRISMRPG.Skill.CoreSkill.survival"
};
/**
* Core Skill bonus values
*/
+9
View File
@@ -40,6 +40,15 @@ export const COLORS = Object.freeze({
}
});
/**
* Simplified Spell Colors object for form choices (label-only format)
*/
export const COLORS_CHOICES = Object.freeze(
Object.fromEntries(
Object.entries(COLORS).map(([key, value]) => [key, value.label])
)
);
/**
* Spell ranges (legacy - to be replaced with Prism system)
*/
+13 -12
View File
@@ -7,6 +7,8 @@ import * as SKILL from "./skill.mjs"
import * as EQUIPMENT from "./equipment.mjs"
import * as CHARACTERISTICS from "./characteristic-tables.mjs"
import * as MONSTER from "./monster.mjs"
import * as RACE from "./race.mjs"
import * as CLASS from "./class.mjs"
export const SYSTEM_ID = "fvtt-prism-rpg"
export const DEV_MODE = false
@@ -220,15 +222,6 @@ export const MIRACLE_TYPES = {
"ritualfaith": "Ritual of Faith"
}
export const SPELL_CRITICAL = {
"none": "None",
"electric": "Electric",
"fire": "Fire",
"cold": "Cold",
"force": "Force",
"acid": "Acid"
}
export const CHOICE_MODIFIERS = {
"-9": "-9",
"-8": "-8",
@@ -296,6 +289,7 @@ export const SYSTEM = {
CHALLENGES: CHARACTER.CHALLENGES,
SKILL_CATEGORY: SKILL.CATEGORY,
CORE_SKILLS: SKILL.CORE_SKILLS,
CORE_SKILLS_CHOICES: SKILL.CORE_SKILLS_CHOICES,
CORE_SKILL_BONUS: SKILL.CORE_SKILL_BONUS,
ARMOR_TYPE: ARMOR.TYPE,
ARMOR_BASE_AC: ARMOR.BASE_AC,
@@ -303,16 +297,19 @@ export const SYSTEM = {
ARMOR_WITHSTAND_APC: ARMOR.WITHSTAND_APC,
ARMOR_SET_REQUIREMENTS: ARMOR.SET_REQUIREMENTS,
SHIELD_TYPE: SHIELD.TYPE,
SHIELD_TYPE_CHOICES: SHIELD.TYPE_CHOICES,
SHIELD_BLOCK_AUGMENTS: SHIELD.BLOCK_AUGMENTS,
EQUIPMENT_CATEGORY: EQUIPMENT.CATEGORY,
SPELL_RANGE: SPELL.RANGE,
SPELL_COLORS: SPELL.COLORS,
SPELL_COLORS_CHOICES: SPELL.COLORS_CHOICES,
WEAPON_TYPE: WEAPON.TYPE,
WEAPON_TYPE_CHOICES: WEAPON.TYPE_CHOICES,
WEAPON_GROUP: WEAPON.WEAPON_GROUP,
WEAPON_GROUP_CHOICES: WEAPON.WEAPON_GROUP_CHOICES,
WEAPON_DAMAGE_TYPE: WEAPON.DAMAGE_TYPE,
WEAPON_CLASS: WEAPON.WEAPON_CLASS,
COMBAT_PROGRESSION_DICE: DICE_VALUES,
SHIELD_DEFENSE_DICE: DEFENSE_DICE_VALUES,
WEAPON_CATEGORIES: WEAPON.WEAPON_CATEGORIES,
CHARACTERISTIC_ATTACK,
CHARACTERISTIC_RANGED_ATTACK,
@@ -333,8 +330,12 @@ export const SYSTEM = {
FAVOR_CHOICES,
ATTACKER_AIM_CHOICES,
MORTAL_CHOICES,
SPELL_CRITICAL,
MIRACLE_TYPES,
SPELL_LETHARGY_DICE,
GRANTED_DICE_CHOICES
GRANTED_DICE_CHOICES,
RACE_SIZE: RACE.SIZE,
RACE_AGE_CATEGORY: RACE.AGE_CATEGORY,
CLASS_ARCHETYPE: CLASS.ARCHETYPE,
CLASS_TYPE: CLASS.CLASS_TYPE,
CLASS_SPELLCASTING_TYPE: CLASS.SPELLCASTING_TYPE
}
+24 -6
View File
@@ -29,6 +29,15 @@ export const TYPE = Object.freeze({
}
});
/**
* 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])
)
);
/**
* Weapon groups and their associated passives
* Each weapon belongs to a group and possesses its passive while wielded
@@ -36,48 +45,57 @@ export const TYPE = Object.freeze({
export const WEAPON_GROUP = Object.freeze({
longsword: {
id: "longsword",
label: "PRISMRPG.Weapon.WeaponGroup.longsword",
label: "PRISMRPG.WeaponGroup.longsword",
passive: "turningEdge",
passiveLabel: "PRISMRPG.Weapon.Passive.turningEdge",
passiveDescription: "PRISMRPG.Weapon.PassiveDescription.turningEdge"
},
warhammer: {
id: "warhammer",
label: "PRISMRPG.Weapon.WeaponGroup.warhammer",
label: "PRISMRPG.WeaponGroup.warhammer",
passive: "puncturingBlows",
passiveLabel: "PRISMRPG.Weapon.Passive.puncturingBlows",
passiveDescription: "PRISMRPG.Weapon.PassiveDescription.puncturingBlows"
},
battleaxe: {
id: "battleaxe",
label: "PRISMRPG.Weapon.WeaponGroup.battleaxe",
label: "PRISMRPG.WeaponGroup.battleaxe",
passive: "shieldEater",
passiveLabel: "PRISMRPG.Weapon.Passive.shieldEater",
passiveDescription: "PRISMRPG.Weapon.PassiveDescription.shieldEater"
},
dagger: {
id: "dagger",
label: "PRISMRPG.Weapon.WeaponGroup.dagger",
label: "PRISMRPG.WeaponGroup.dagger",
passive: "balancingStance",
passiveLabel: "PRISMRPG.Weapon.Passive.balancingStance",
passiveDescription: "PRISMRPG.Weapon.PassiveDescription.balancingStance"
},
crossbow: {
id: "crossbow",
label: "PRISMRPG.Weapon.WeaponGroup.crossbow",
label: "PRISMRPG.WeaponGroup.crossbow",
passive: "boltlock",
passiveLabel: "PRISMRPG.Weapon.Passive.boltlock",
passiveDescription: "PRISMRPG.Weapon.PassiveDescription.boltlock"
},
longbow: {
id: "longbow",
label: "PRISMRPG.Weapon.WeaponGroup.longbow",
label: "PRISMRPG.WeaponGroup.longbow",
passive: "volleyFire",
passiveLabel: "PRISMRPG.Weapon.Passive.volleyFire",
passiveDescription: "PRISMRPG.Weapon.PassiveDescription.volleyFire"
}
});
/**
* 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])
)
);
/**
* Damage types for weapons
*/
+3 -3
View File
@@ -3,11 +3,11 @@ export const defaultItemImg = {
armor: "systems/fvtt-prism-rpg/assets/icons/icon_armor.webp",
equipment: "systems/fvtt-prism-rpg/assets/icons/icon_equipment.webp",
skill: "systems/fvtt-prism-rpg/assets/icons/icon_skill.webp",
gift: "systems/fvtt-prism-rpg/assets/icons/icon_gift.webp",
vulnerability: "systems/fvtt-prism-rpg/assets/icons/icon_vulnerability.webp",
"racial-ability": "systems/fvtt-prism-rpg/assets/icons/icon_racial-ability.webp",
shield: "systems/fvtt-prism-rpg/assets/icons/icon_shield.webp",
spell: "systems/fvtt-prism-rpg/assets/icons/icon_spell.webp",
miracle: "systems/fvtt-prism-rpg/assets/icons/icon_miracle.webp"
race: "systems/fvtt-prism-rpg/assets/icons/icon_race.webp",
class: "systems/fvtt-prism-rpg/assets/icons/icon_class.webp"
}
export default class PrismRPGItem extends Item {
+7 -4
View File
@@ -247,11 +247,13 @@ export default class PrismRPGRoll extends Roll {
}
} else if (options.rollType === "shield-roll") {
// Legacy Lethal Fantasy - Shield Defense Roll (not used in PRISM RPG)
// In PRISM, shields use Block action with Shield Rating (SR)
hasD30 = false
options.rollName = "Shield Defense"
dice = options.rollTarget.system.defense.toUpperCase()
options.rollName = "Shield Block"
dice = "1d20" // Placeholder - actual Block mechanic handled elsewhere
baseFormula = dice
hasModifier = true
hasModifier = false
hasChangeDice = false
hasMaxValue = false
hasExplode = false
@@ -261,7 +263,8 @@ export default class PrismRPGRoll extends Roll {
options.rollName = options.rollTarget.name
hasModifier = true
hasChangeDice = false
let damageBonus = (options.rollTarget.weapon.system.applyStrengthDamageBonus) ? options.rollTarget.combat.damageModifier : 0
// In PRISM, all weapons apply STR damage bonus
let damageBonus = options.rollTarget.combat.damageModifier
options.rollTarget.value = damageBonus + options.rollTarget.weaponSkillModifier + options.rollTarget.weapon.system.bonuses.damageBonus
options.rollTarget.charModifier = damageBonus
if (options.rollType.includes("small")) {
+3 -1
View File
@@ -5,7 +5,9 @@ export { default as PrismRPGSpell } from "./spell.mjs"
export { default as PrismRPGSkill } from "./skill.mjs"
export { default as PrismRPGArmor } from "./armor.mjs"
export { default as PrismRPGShield } from "./shield.mjs"
export { default as PrismRPGGift } from "./gift.mjs"
export { default as PrismRPGRacialAbility } from "./racial-ability.mjs"
export { default as PrismRPGVulnerability } from "./vulnerability.mjs"
export { default as PrismRPGEquipment } from "./equipment.mjs"
export { default as PrismRPGMiracle } from "./miracle.mjs"
export { default as PrismRPGRace } from "./race.mjs"
export { default as PrismRPGClass } from "./class.mjs"
-6
View File
@@ -19,12 +19,6 @@ export default class PrismRPGArmor extends foundry.abstract.TypeDataModel {
schema.augment = new fields.StringField({ required: false, initial: "", label: "Armor Augment" })
schema.augmentDescription = new fields.HTMLField({ required: false, textSearch: true, label: "Augment Description" })
// Legacy fields for compatibility
schema.defense = new fields.NumberField({ ...requiredInteger, required: true, initial: 0, min: -50 })
schema.maximumMovement = new fields.StringField({ required: false, initial: "" })
schema.hp = new fields.NumberField({ ...requiredInteger, required: true, initial: 0, min: 0 })
schema.damageReduction = new fields.NumberField({ ...requiredInteger, required: true, initial: 0, min: 0 })
schema.encLoad = new fields.NumberField({ required: true, initial: 0, min: 0 })
schema.equipped = new fields.BooleanField({ required: true, initial: false })
schema.isHelmet = new fields.BooleanField({ required: true, initial: false })
+1 -1
View File
@@ -173,7 +173,7 @@ export default class PrismRPGCharacter extends foundry.abstract.TypeDataModel {
skill: new fields.StringField({
required: true,
initial: "",
choices: Object.keys(SYSTEM.CORE_SKILLS || {}),
choices: SYSTEM.CORE_SKILLS_CHOICES,
label: "Selected Core Skill"
}),
attributeChoice: new fields.StringField({
+159
View File
@@ -0,0 +1,159 @@
/**
* Class data model for Prism RPG
*
* Classes in Prism provide:
* - Archetype classification (Faith, Mana, Wanderer, Warrior)
* - Class features gained at different levels
* - Proficiencies and abilities
* - Characters can have up to 3 classes simultaneously
*/
import { SYSTEM } from "../config/system.mjs"
export default class PrismRPGClass extends foundry.abstract.TypeDataModel {
static defineSchema() {
const fields = foundry.data.fields
const schema = {}
schema.description = new fields.HTMLField({
required: true,
textSearch: true,
initial: ""
})
// Archetype (Faith, Mana, Wanderer, Warrior)
schema.archetype = new fields.StringField({
required: true,
initial: "wanderer",
choices: SYSTEM.CLASS_ARCHETYPE,
label: "Archetype"
})
// Is this a Core Class or Continental Class?
schema.classType = new fields.StringField({
required: true,
initial: "core",
choices: SYSTEM.CLASS_TYPE,
label: "Class Type"
})
// For Continental Classes, specify the continent
schema.continent = new fields.StringField({
required: false,
initial: "",
label: "Continent"
})
// Current level in this class (for tracking progression)
schema.level = new fields.NumberField({
required: true,
nullable: false,
integer: true,
initial: 1,
min: 1,
max: 10,
label: "Class Level"
})
// Class Features (structured data for features gained at each level)
schema.features = new fields.SchemaField({
level1: new fields.HTMLField({ initial: "" }),
level2: new fields.HTMLField({ initial: "" }),
level3: new fields.HTMLField({ initial: "" }),
level4: new fields.HTMLField({ initial: "" }),
level5: new fields.HTMLField({ initial: "" }),
level6: new fields.HTMLField({ initial: "" }),
level7: new fields.HTMLField({ initial: "" }),
level8: new fields.HTMLField({ initial: "" }),
level9: new fields.HTMLField({ initial: "" }),
level10: new fields.HTMLField({ initial: "" })
})
// Proficiencies granted by this class
schema.weaponProficiencies = new fields.StringField({
required: true,
initial: "",
label: "Weapon Proficiencies"
})
schema.armorProficiencies = new fields.StringField({
required: true,
initial: "",
label: "Armor Proficiencies"
})
// Special abilities
schema.spellcasting = new fields.BooleanField({
required: true,
initial: false,
label: "Has Spellcasting"
})
schema.spellcastingType = new fields.StringField({
required: false,
nullable: true,
initial: null,
blank: true,
choices: SYSTEM.CLASS_SPELLCASTING_TYPE,
label: "Spellcasting Type"
})
// Attribute bonuses
schema.attributeBonuses = new fields.HTMLField({
required: true,
initial: "",
label: "Attribute Bonuses"
})
// Additional notes
schema.notes = new fields.HTMLField({
required: true,
initial: "",
label: "Notes"
})
return schema
}
/** @override */
static LOCALIZATION_PREFIXES = ["PRISMRPG.Class"]
/**
* Clean up data before validation
* @override
*/
static cleanData(source = {}, options = {}) {
// Convert empty string to null for spellcastingType
if (source.spellcastingType === "") {
source.spellcastingType = null
}
return super.cleanData(source, options)
}
/**
* Get the localized archetype label
*/
get archetypeLabel() {
return game.i18n.localize(`PRISMRPG.Class.Archetype.${this.archetype}`)
}
/**
* Get the current level's features
*/
get currentLevelFeatures() {
return this.features[`level${this.level}`] || ""
}
/**
* Get all features up to current level
*/
get allFeaturesUpToLevel() {
const features = []
for (let i = 1; i <= this.level; i++) {
const feature = this.features[`level${i}`]
if (feature) {
features.push({ level: i, description: feature })
}
}
return features
}
}
-3
View File
@@ -10,9 +10,6 @@ export default class PrismRPGEquipment extends foundry.abstract.TypeDataModel {
schema.category = new fields.StringField({ required: true, initial: "tinbit", choices: SYSTEM.EQUIPMENT_CATEGORIES })
schema.encLoad = new fields.NumberField({ required: true, initial: 0, min: 0 })
schema.hi = new fields.NumberField({ ...requiredInteger, required: true, initial: 0, min: 0 })
schema.medium = new fields.NumberField({ ...requiredInteger, required: true, initial: 0, min: 0 })
schema.lo = new fields.NumberField({ ...requiredInteger, required: true, initial: 0, min: 0 })
schema.cost = new fields.NumberField({ ...requiredInteger, required: true, initial: 0, min: 0 })
schema.money = new fields.StringField({ required: true, initial: "tinbit", choices: SYSTEM.MONEY })
-16
View File
@@ -1,16 +0,0 @@
export default class PrismRPGGift 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({ required: true, initial: 0, min: 0 })
return schema
}
/** @override */
static LOCALIZATION_PREFIXES = ["PRISMRPG.Gift"]
}
-11
View File
@@ -98,17 +98,6 @@ export default class PrismRPGMiracle extends foundry.abstract.TypeDataModel {
initial: ""
})
// Attack and power
schema.attackRoll = new fields.StringField({
required: true,
initial: ""
})
schema.powerRoll = new fields.StringField({
required: true,
initial: ""
})
// Keywords
schema.keywords = new fields.ArrayField(
new fields.StringField()
+107
View File
@@ -0,0 +1,107 @@
/**
* Race data model for Prism RPG
*
* Races provide:
* - Racial Passive: Always-on ability
* - Sub-race selection: Specific racial ability based on sub-race
* - Basic information: Senses, size, age, language
*/
import { SYSTEM } from "../config/system.mjs"
export default class PrismRPGRace extends foundry.abstract.TypeDataModel {
static defineSchema() {
const fields = foundry.data.fields
const schema = {}
schema.description = new fields.HTMLField({
required: true,
textSearch: true,
initial: ""
})
// Basic Information
schema.senses = new fields.StringField({
required: true,
initial: "standard",
label: "Senses"
})
schema.size = new fields.StringField({
required: true,
initial: "medium",
choices: SYSTEM.RACE_SIZE,
label: "Size"
})
schema.ageCategory = new fields.StringField({
required: true,
initial: "short",
choices: SYSTEM.RACE_AGE_CATEGORY,
label: "Age Category"
})
schema.language = new fields.StringField({
required: true,
initial: "",
label: "Language"
})
// Racial Passive
schema.racialPassive = new fields.StringField({
required: true,
initial: "",
label: "Racial Passive Name"
})
schema.racialPassiveDescription = new fields.HTMLField({
required: true,
initial: "",
label: "Racial Passive Description"
})
// Sub-race
schema.subrace = new fields.StringField({
required: true,
initial: "",
label: "Sub-race"
})
schema.subraceAbility = new fields.StringField({
required: true,
initial: "",
label: "Sub-race Ability Name"
})
schema.subraceAbilityDescription = new fields.HTMLField({
required: true,
initial: "",
label: "Sub-race Ability Description"
})
// Additional notes
schema.notes = new fields.HTMLField({
required: true,
initial: "",
label: "Notes"
})
return schema
}
/** @override */
static LOCALIZATION_PREFIXES = ["PRISMRPG.Race"]
/**
* Get the localized size label
*/
get sizeLabel() {
return game.i18n.localize(`PRISMRPG.Race.Size.${this.size}`)
}
/**
* Get the localized age category label
*/
get ageCategoryLabel() {
return game.i18n.localize(`PRISMRPG.Race.AgeCategory.${this.ageCategory}`)
}
}
+14
View File
@@ -0,0 +1,14 @@
export default class PrismRPGRacialAbility 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.RacialAbility"]
}
+2 -42
View File
@@ -12,7 +12,7 @@ export default class PrismRPGShield extends foundry.abstract.TypeDataModel {
schema.shieldType = new fields.StringField({
required: true,
initial: "buckler",
choices: Object.keys(SYSTEM.SHIELD_TYPE || {})
choices: SYSTEM.SHIELD_TYPE_CHOICES
})
// APC (Action Point Cost) for Block action
@@ -58,47 +58,7 @@ export default class PrismRPGShield extends foundry.abstract.TypeDataModel {
label: "Augment Description"
})
// Legacy properties (kept for backward compatibility)
schema.defense = new fields.StringField({
required: true,
initial: "d4",
choices: SYSTEM.SHIELD_DEFENSE_DICE
})
schema.movementreduction = new fields.NumberField({
...requiredInteger,
required: true,
initial: 0,
min: 0
})
schema.hascover = new fields.BooleanField({
required: true,
initial: false
})
schema.standing = new fields.SchemaField({
min: new fields.NumberField({ ...requiredInteger, required: true, initial: 0, min: 0 }),
max: new fields.NumberField({ ...requiredInteger, required: true, initial: 0, min: 0 })
})
schema.crouching = new fields.SchemaField({
min: new fields.NumberField({ ...requiredInteger, required: true, initial: 0, min: 0 }),
max: new fields.NumberField({ ...requiredInteger, required: true, initial: 0, min: 0 })
})
schema.destruction = new fields.SchemaField({
bashing: new fields.NumberField({ ...requiredInteger, required: true, initial: 0, min: 0 }),
slashing: new fields.NumberField({ ...requiredInteger, required: true, initial: 0, min: 0 }),
piercing: new fields.NumberField({ ...requiredInteger, required: true, initial: 0, min: 0 })
})
schema.autodestruction = new fields.SchemaField({
bashing: new fields.NumberField({ ...requiredInteger, required: true, initial: 0, min: 0 }),
slashing: new fields.NumberField({ ...requiredInteger, required: true, initial: 0, min: 0 }),
piercing: new fields.NumberField({ ...requiredInteger, required: true, initial: 0, min: 0 })
})
// 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 })
+4 -4
View File
@@ -1,4 +1,4 @@
import { SYSTEM } from "../config/system.mjs"
import { CORE_SKILLS_CHOICES, CORE_SKILL_BONUS, CORE_SKILLS } from "../config/skill.mjs"
/**
* Core Skill data model for Prism RPG
@@ -25,7 +25,7 @@ export default class PrismRPGSkill extends foundry.abstract.TypeDataModel {
schema.coreSkill = new fields.StringField({
required: true,
initial: "acrobatics",
choices: Object.keys(SYSTEM.CORE_SKILLS || {}),
choices: CORE_SKILLS_CHOICES,
label: "Core Skill"
})
@@ -82,7 +82,7 @@ export default class PrismRPGSkill extends foundry.abstract.TypeDataModel {
* Get the Core Skill definition from SYSTEM
*/
get coreSkillDefinition() {
return SYSTEM.CORE_SKILLS?.[this.coreSkill] || null
return CORE_SKILLS?.[this.coreSkill] || null
}
/**
@@ -109,7 +109,7 @@ export default class PrismRPGSkill extends foundry.abstract.TypeDataModel {
// If this is the character's Core Skill, apply bonuses
if (this.isCoreSkill) {
this.modifier = SYSTEM.CORE_SKILL_BONUS?.basic || 5
this.modifier = CORE_SKILL_BONUS?.basic || 5
this.canAdvancedCheck = true
} else {
this.modifier = 0
+13 -45
View File
@@ -52,7 +52,7 @@ export default class PrismRPGSpell extends foundry.abstract.TypeDataModel {
schema.color = new fields.StringField({
required: true,
initial: "violet",
choices: Object.keys(SYSTEM.SPELL_COLORS || {}),
choices: SYSTEM.SPELL_COLORS_CHOICES,
label: "Spell Color"
})
@@ -82,24 +82,6 @@ export default class PrismRPGSpell extends foundry.abstract.TypeDataModel {
initial: false
})
// Components
schema.components = new fields.SchemaField({
verbal: new fields.BooleanField({ initial: false }),
somatic: new fields.BooleanField({ initial: false }),
catalyst: new fields.BooleanField({ initial: false }),
material: new fields.BooleanField({ initial: false }),
})
schema.materialComponent = new fields.StringField({
required: true,
initial: ""
})
schema.catalyst = new fields.StringField({
required: true,
initial: ""
})
// Casting parameters
schema.castingTime = new fields.StringField({
required: true,
@@ -126,39 +108,25 @@ export default class PrismRPGSpell extends foundry.abstract.TypeDataModel {
initial: ""
})
// Attack and damage
schema.attackRoll = new fields.StringField({
// Keywords (text field with comma-separated values)
schema.keywords = new fields.StringField({
required: true,
initial: ""
initial: "",
label: "Keywords"
})
schema.powerRoll = new fields.StringField({
// Prism RPG: Targets
schema.targets = new fields.StringField({
required: true,
initial: ""
initial: "Single enemy",
label: "Targets"
})
// Keywords
schema.keywords = new fields.ArrayField(
new fields.StringField()
)
// Legacy fields
schema.cost = new fields.NumberField({
...requiredInteger,
// Prism RPG: Resolve method (Attack, Saving Throw, None)
schema.resolve = new fields.StringField({
required: true,
initial: 0,
min: 0
})
schema.extraAetherPoints = new fields.StringField({
required: true,
initial: ""
})
schema.criticalType = new fields.StringField({
required: true,
initial: "electric",
choices: SYSTEM.SPELL_CRITICAL
initial: "Attack",
label: "Resolve"
})
return schema
+3 -33
View File
@@ -12,13 +12,13 @@ export default class PrismRPGWeapon extends foundry.abstract.TypeDataModel {
schema.weaponType = new fields.StringField({
required: true,
initial: "light",
choices: Object.keys(SYSTEM.WEAPON_TYPE || {})
choices: SYSTEM.WEAPON_TYPE_CHOICES
})
schema.weaponGroup = new fields.StringField({
required: true,
initial: "longsword",
choices: Object.keys(SYSTEM.WEAPON_GROUP || {})
choices: SYSTEM.WEAPON_GROUP_CHOICES
})
// APC (Action Point Cost) - determined by weapon type
@@ -105,37 +105,7 @@ export default class PrismRPGWeapon extends foundry.abstract.TypeDataModel {
min: 0
})
// Legacy properties (kept for backward compatibility)
schema.hands = new fields.StringField({
required: true,
initial: "1",
choices: { "1": "1", "2": "2" }
})
schema.isAgile = new fields.BooleanField({
required: true,
initial: false
})
schema.applyStrengthDamageBonus = new fields.BooleanField({
required: true,
initial: true
})
schema.defenseMax = new fields.NumberField({
...requiredInteger,
required: true,
initial: 0,
min: 0
})
schema.defense = new fields.NumberField({
...requiredInteger,
required: true,
initial: 0,
min: 0
})
// Bonuses (from magical enhancements, etc.)
schema.bonuses = new fields.SchemaField({
attackBonus: new fields.NumberField({ ...requiredInteger, required: true, initial: 0, min: 0 }),
damageBonus: new fields.NumberField({ ...requiredInteger, required: true, initial: 0, min: 0 }),
+41 -36
View File
@@ -22,43 +22,43 @@ export default class PrismRPGUtils {
static setHookListeners() {
Hooks.on('renderTokenHUD', async (hud, html, token) => {
const lossHPButton = await foundry.applications.handlebars.renderTemplate('systems/fvtt-prism-rpg/templates/loss-hp-hud.hbs', {} )
const lossHPButton = await foundry.applications.handlebars.renderTemplate('systems/fvtt-prism-rpg/templates/loss-hp-hud.hbs', {})
$(html).find('div.left').append(lossHPButton);
$(html).find('img.prism-hp-loss-hud').click((event) => {
event.preventDefault();
let hpMenu = $(html).find('.hp-loss-wrap')[0]
if (hpMenu.classList.contains("hp-loss-hud-disabled")) {
$(html).find('.hp-loss-wrap')[0].classList.add('hp-loss-hud-active');
$(html).find('.hp-loss-wrap')[0].classList.remove('hp-loss-hud-disabled');
$(html).find('.hp-loss-wrap')[1].classList.add('hp-loss-hud-active');
$(html).find('.hp-loss-wrap')[1].classList.remove('hp-loss-hud-disabled');
$(html).find('.hp-loss-wrap')[2].classList.add('hp-loss-hud-active');
$(html).find('.hp-loss-wrap')[2].classList.remove('hp-loss-hud-disabled');
} else {
$(html).find('.hp-loss-wrap')[0].classList.remove('hp-loss-hud-active');
$(html).find('.hp-loss-wrap')[0].classList.add('hp-loss-hud-disabled');
$(html).find('.hp-loss-wrap')[1].classList.remove('hp-loss-hud-active');
$(html).find('.hp-loss-wrap')[1].classList.add('hp-loss-hud-disabled');
$(html).find('.hp-loss-wrap')[2].classList.remove('hp-loss-hud-active');
$(html).find('.hp-loss-wrap')[2].classList.add('hp-loss-hud-disabled');
}
})
$(html).find('.loss-hp-hud-click').click((event) => {
event.preventDefault();
let hpLoss = event.currentTarget.dataset.hpValue;
if (token) {
let tokenFull = canvas.tokens.placeables.find( t => t.id === token._id);
console.log(tokenFull, token)
let actor = tokenFull.actor;
actor.applyDamage(Number(hpLoss));
$(html).find('.hp-loss-wrap')[0].classList.remove('hp-loss-hud-active');
$(html).find('.hp-loss-wrap')[0].classList.add('hp-loss-hud-disabled');
$(html).find('.hp-loss-wrap')[1].classList.remove('hp-loss-hud-active');
$(html).find('.hp-loss-wrap')[1].classList.add('hp-loss-hud-disabled');
$(html).find('.hp-loss-wrap')[2].classList.remove('hp-loss-hud-active');
$(html).find('.hp-loss-wrap')[2].classList.add('hp-loss-hud-disabled');
}
})
$(html).find('img.prism-hp-loss-hud').click((event) => {
event.preventDefault();
let hpMenu = $(html).find('.hp-loss-wrap')[0]
if (hpMenu.classList.contains("hp-loss-hud-disabled")) {
$(html).find('.hp-loss-wrap')[0].classList.add('hp-loss-hud-active');
$(html).find('.hp-loss-wrap')[0].classList.remove('hp-loss-hud-disabled');
$(html).find('.hp-loss-wrap')[1].classList.add('hp-loss-hud-active');
$(html).find('.hp-loss-wrap')[1].classList.remove('hp-loss-hud-disabled');
$(html).find('.hp-loss-wrap')[2].classList.add('hp-loss-hud-active');
$(html).find('.hp-loss-wrap')[2].classList.remove('hp-loss-hud-disabled');
} else {
$(html).find('.hp-loss-wrap')[0].classList.remove('hp-loss-hud-active');
$(html).find('.hp-loss-wrap')[0].classList.add('hp-loss-hud-disabled');
$(html).find('.hp-loss-wrap')[1].classList.remove('hp-loss-hud-active');
$(html).find('.hp-loss-wrap')[1].classList.add('hp-loss-hud-disabled');
$(html).find('.hp-loss-wrap')[2].classList.remove('hp-loss-hud-active');
$(html).find('.hp-loss-wrap')[2].classList.add('hp-loss-hud-disabled');
}
})
$(html).find('.loss-hp-hud-click').click((event) => {
event.preventDefault();
let hpLoss = event.currentTarget.dataset.hpValue;
if (token) {
let tokenFull = canvas.tokens.placeables.find(t => t.id === token._id);
console.log(tokenFull, token)
let actor = tokenFull.actor;
actor.applyDamage(Number(hpLoss));
$(html).find('.hp-loss-wrap')[0].classList.remove('hp-loss-hud-active');
$(html).find('.hp-loss-wrap')[0].classList.add('hp-loss-hud-disabled');
$(html).find('.hp-loss-wrap')[1].classList.remove('hp-loss-hud-active');
$(html).find('.hp-loss-wrap')[1].classList.add('hp-loss-hud-disabled');
$(html).find('.hp-loss-wrap')[2].classList.remove('hp-loss-hud-active');
$(html).find('.hp-loss-wrap')[2].classList.add('hp-loss-hud-disabled');
}
})
})
}
@@ -159,6 +159,11 @@ export default class PrismRPGUtils {
Handlebars.registerHelper('not', function (cond) {
return !cond;
})
Handlebars.registerHelper('inc', function (value) {
// Extract number from string like "level1" or just parse number
const num = typeof value === 'string' ? parseInt(value.replace(/\D/g, '')) : parseInt(value);
return isNaN(num) ? value : num + 1;
})
Handlebars.registerHelper('count', function (list) {
return list.length;
})