First adaptation pass

This commit is contained in:
2025-11-06 00:01:59 +01:00
parent 5b1fd847c2
commit 6b883f8126
112 changed files with 44142 additions and 953 deletions
+41 -1
View File
@@ -1,5 +1,45 @@
export const TYPE = Object.freeze({
light: { id: "light", label: "PRISMRPG.Armor.Category.light" },
medium: { id: "medium", label: "PRISMRPG.Armor.Category.medium" },
heavy: { id: "medium", label: "PRISMRPG.Armor.Category.heavy" }
heavy: { id: "heavy", label: "PRISMRPG.Armor.Category.heavy" }
})
// Base AC values for armor proficiency levels
export const BASE_AC = Object.freeze({
light: 12, // +Prowess, Competence, or Grace
medium: 14, // +Authority, Cunning, or Resilience
heavy: 16 // +Presence, Tenacity, or Willpower
})
// AC bonus sub-attributes for each armor type
export const AC_BONUS_ATTRIBUTES = Object.freeze({
light: ["prowess", "competence", "grace"],
medium: ["authority", "cunning", "resilience"],
heavy: ["presence", "tenacity", "willpower"]
})
// Withstand action APC cost for each armor type
export const WITHSTAND_APC = Object.freeze({
light: 1,
medium: 2,
heavy: 3
})
// Armor set composition requirements
export const SET_REQUIREMENTS = Object.freeze({
light: {
light: 2,
medium: 0,
heavy: 0
},
medium: {
light: 3,
medium: 1,
heavy: 0
},
heavy: {
light: 3,
medium: 2,
heavy: 1
}
})
+101 -15
View File
@@ -1,15 +1,7 @@
export const CHARACTERISTICS = Object.freeze({
str: {
id: "str",
label: "PRISMRPG.Label.str"
},
int: {
id: "int",
label: "PRISMRPG.Character.int.label"
},
wis: {
id: "wis",
label: "PRISMRPG.Character.wis.label"
label: "PRISMRPG.Character.str.label"
},
dex: {
id: "dex",
@@ -19,18 +11,112 @@ export const CHARACTERISTICS = Object.freeze({
id: "con",
label: "PRISMRPG.Character.con.label"
},
int: {
id: "int",
label: "PRISMRPG.Character.int.label"
},
wis: {
id: "wis",
label: "PRISMRPG.Character.wis.label"
},
cha: {
id: "cha",
label: "PRISMRPG.Character.cha.label"
}
})
// Sub-Attributes derived from combinations of two main attributes
export const SUB_ATTRIBUTES = Object.freeze({
prowess: {
id: "prowess",
label: "PRISMRPG.Character.prowess.label",
parents: ["str", "dex"],
description: "PRISMRPG.Character.prowess.description"
},
luc: {
id: "luc",
label: "PRISMRPG.Character.luc.label"
vigor: {
id: "vigor",
label: "PRISMRPG.Character.vigor.label",
parents: ["str", "con"],
description: "PRISMRPG.Character.vigor.description"
},
app: {
id: "app",
label: "PRISMRPG.Character.app.label"
competence: {
id: "competence",
label: "PRISMRPG.Character.competence.label",
parents: ["str", "int"],
description: "PRISMRPG.Character.competence.description"
},
authority: {
id: "authority",
label: "PRISMRPG.Character.authority.label",
parents: ["str", "wis"],
description: "PRISMRPG.Character.authority.description"
},
presence: {
id: "presence",
label: "PRISMRPG.Character.presence.label",
parents: ["str", "cha"],
description: "PRISMRPG.Character.presence.description"
},
stamina: {
id: "stamina",
label: "PRISMRPG.Character.stamina.label",
parents: ["dex", "con"],
description: "PRISMRPG.Character.stamina.description"
},
initiative: {
id: "initiative",
label: "PRISMRPG.Character.initiative.label",
parents: ["dex", "int"],
description: "PRISMRPG.Character.initiative.description"
},
wit: {
id: "wit",
label: "PRISMRPG.Character.wit.label",
parents: ["dex", "wis"],
description: "PRISMRPG.Character.wit.description"
},
grace: {
id: "grace",
label: "PRISMRPG.Character.grace.label",
parents: ["dex", "cha"],
description: "PRISMRPG.Character.grace.description"
},
tenacity: {
id: "tenacity",
label: "PRISMRPG.Character.tenacity.label",
parents: ["con", "int"],
description: "PRISMRPG.Character.tenacity.description"
},
willpower: {
id: "willpower",
label: "PRISMRPG.Character.willpower.label",
parents: ["con", "wis"],
description: "PRISMRPG.Character.willpower.description"
},
resilience: {
id: "resilience",
label: "PRISMRPG.Character.resilience.label",
parents: ["con", "cha"],
description: "PRISMRPG.Character.resilience.description"
},
cunning: {
id: "cunning",
label: "PRISMRPG.Character.cunning.label",
parents: ["int", "wis"],
description: "PRISMRPG.Character.cunning.description"
},
guile: {
id: "guile",
label: "PRISMRPG.Character.guile.label",
parents: ["int", "cha"],
description: "PRISMRPG.Character.guile.description"
},
sovereignty: {
id: "sovereignty",
label: "PRISMRPG.Character.sovereignty.label",
parents: ["wis", "cha"],
description: "PRISMRPG.Character.sovereignty.description"
}
})
export const CHALLENGES = Object.freeze({
+50
View File
@@ -0,0 +1,50 @@
/**
* Shield types based on Prism RPG rules
* Shields prevent damage equal to their Shield Rating (SR)
* Each shield has a Block APC and a Block Augment passive ability
*/
export const TYPE = Object.freeze({
buckler: {
id: "buckler",
label: "PRISMRPG.Shield.Type.buckler",
apc: 1,
sr: "1d4",
blockAugment: "PRISMRPG.Shield.BlockAugment.buckler",
blockAugmentDescription: "PRISMRPG.Shield.BlockAugmentDescription.buckler"
},
light: {
id: "light",
label: "PRISMRPG.Shield.Type.light",
apc: 2,
sr: "1d8",
blockAugment: "PRISMRPG.Shield.BlockAugment.light",
blockAugmentDescription: "PRISMRPG.Shield.BlockAugmentDescription.light"
},
heavy: {
id: "heavy",
label: "PRISMRPG.Shield.Type.heavy",
apc: 3,
sr: "1d12",
blockAugment: "PRISMRPG.Shield.BlockAugment.heavy",
blockAugmentDescription: "PRISMRPG.Shield.BlockAugmentDescription.heavy"
},
tower: {
id: "tower",
label: "PRISMRPG.Shield.Type.tower",
apc: 4,
sr: "3d6",
blockAugment: "PRISMRPG.Shield.BlockAugment.tower",
blockAugmentDescription: "PRISMRPG.Shield.BlockAugmentDescription.tower"
}
});
/**
* Block augment descriptions for reference
* These are activated when using the Block action with the shield
*/
export const BLOCK_AUGMENTS = Object.freeze({
buckler: "If the character should successfully block damage with this shield, the character reduces the cost of the next Parry action by an amount equal to their Prowess.",
light: "If the character would successfully block damage with this shield, the character may move an amount equal to their movement rating.",
heavy: "If the character would successfully block damage with this shield, they reduce the APC of the next attack they make by an amount equal to their Vigor or Prowess.",
tower: "If the character would successfully block damage with this shield, for 1 round they are considered cover for themselves and allies that are adjacent to them."
});
+112
View File
@@ -1,3 +1,115 @@
/**
* Core Skills in Prism RPG
* Characters choose 1 Core Skill which gives:
* - +5 bonus to basic skill checks
* - Access to advanced skill checks
* - Access to a Core Skill Class (based on archetype)
* - +2 to one of 3 associated attributes
*/
export const CORE_SKILLS = Object.freeze({
acrobatics: {
id: "acrobatics",
label: "PRISMRPG.Skill.CoreSkill.acrobatics",
attributeChoices: ["dex", "wis", "con"]
},
animalHandling: {
id: "animalHandling",
label: "PRISMRPG.Skill.CoreSkill.animalHandling",
attributeChoices: ["str", "con", "dex"]
},
arcana: {
id: "arcana",
label: "PRISMRPG.Skill.CoreSkill.arcana",
attributeChoices: ["str", "int", "wis"]
},
athletics: {
id: "athletics",
label: "PRISMRPG.Skill.CoreSkill.athletics",
attributeChoices: ["str", "dex", "con"]
},
deception: {
id: "deception",
label: "PRISMRPG.Skill.CoreSkill.deception",
attributeChoices: ["int", "wis", "cha"]
},
history: {
id: "history",
label: "PRISMRPG.Skill.CoreSkill.history",
attributeChoices: ["str", "wis", "con"]
},
insight: {
id: "insight",
label: "PRISMRPG.Skill.CoreSkill.insight",
attributeChoices: ["int", "cha", "wis"]
},
intimidate: {
id: "intimidate",
label: "PRISMRPG.Skill.CoreSkill.intimidate",
attributeChoices: ["str", "cha", "wis"]
},
investigation: {
id: "investigation",
label: "PRISMRPG.Skill.CoreSkill.investigation",
attributeChoices: ["int", "wis", "con"]
},
medicine: {
id: "medicine",
label: "PRISMRPG.Skill.CoreSkill.medicine",
attributeChoices: ["con", "wis", "int"]
},
nature: {
id: "nature",
label: "PRISMRPG.Skill.CoreSkill.nature",
attributeChoices: ["str", "wis", "int"]
},
perception: {
id: "perception",
label: "PRISMRPG.Skill.CoreSkill.perception",
attributeChoices: ["dex", "wis", "cha"]
},
performance: {
id: "performance",
label: "PRISMRPG.Skill.CoreSkill.performance",
attributeChoices: ["str", "cha", "wis"]
},
persuasion: {
id: "persuasion",
label: "PRISMRPG.Skill.CoreSkill.persuasion",
attributeChoices: ["cha", "dex", "int"]
},
religion: {
id: "religion",
label: "PRISMRPG.Skill.CoreSkill.religion",
attributeChoices: ["str", "wis", "cha"]
},
sleightOfHand: {
id: "sleightOfHand",
label: "PRISMRPG.Skill.CoreSkill.sleightOfHand",
attributeChoices: ["dex", "wis", "int"]
},
stealth: {
id: "stealth",
label: "PRISMRPG.Skill.CoreSkill.stealth",
attributeChoices: ["int", "dex", "cha"]
},
survival: {
id: "survival",
label: "PRISMRPG.Skill.CoreSkill.survival",
attributeChoices: ["int", "con", "cha"]
}
});
/**
* Core Skill bonus values
*/
export const CORE_SKILL_BONUS = Object.freeze({
basic: 5, // +5 to basic skill checks
attributeBonus: 2 // +2 to chosen attribute
});
/**
* Legacy skill categories (may be deprecated)
*/
export const CATEGORY = Object.freeze({
layperson: {
id: "layperson",
+45
View File
@@ -1,3 +1,48 @@
/**
* Spell colors from the Prism
* Each color gives a different effect to spells
*/
export const COLORS = Object.freeze({
indigo: {
id: "indigo",
label: "PRISMRPG.Spell.Color.indigo",
description: "PRISMRPG.Spell.ColorEffect.indigo"
},
blue: {
id: "blue",
label: "PRISMRPG.Spell.Color.blue",
description: "PRISMRPG.Spell.ColorEffect.blue"
},
green: {
id: "green",
label: "PRISMRPG.Spell.Color.green",
description: "PRISMRPG.Spell.ColorEffect.green"
},
yellow: {
id: "yellow",
label: "PRISMRPG.Spell.Color.yellow",
description: "PRISMRPG.Spell.ColorEffect.yellow"
},
orange: {
id: "orange",
label: "PRISMRPG.Spell.Color.orange",
description: "PRISMRPG.Spell.ColorEffect.orange"
},
red: {
id: "red",
label: "PRISMRPG.Spell.Color.red",
description: "PRISMRPG.Spell.ColorEffect.red"
},
violet: {
id: "violet",
label: "PRISMRPG.Spell.Color.violet",
description: "PRISMRPG.Spell.ColorEffect.violet"
}
});
/**
* Spell ranges (legacy - to be replaced with Prism system)
*/
export const RANGE = Object.freeze({
na: {
id: "na",
+14 -1
View File
@@ -1,6 +1,7 @@
import * as CHARACTER from "./character.mjs"
import * as WEAPON from "./weapon.mjs"
import * as ARMOR from "./armor.mjs"
import * as SHIELD from "./shield.mjs"
import * as SPELL from "./spell.mjs"
import * as SKILL from "./skill.mjs"
import * as EQUIPMENT from "./equipment.mjs"
@@ -285,6 +286,7 @@ export const ASCII = `
export const SYSTEM = {
id: SYSTEM_ID,
CHARACTERISTICS: CHARACTER.CHARACTERISTICS,
SUB_ATTRIBUTES: CHARACTER.SUB_ATTRIBUTES,
CHARACTERISTICS_TABLES: CHARACTERISTICS.TABLES,
CHARACTERISTICS_MAJOR: CHARACTERISTICS.MAJOR,
MONSTER_CHARACTERISTICS: MONSTER.MONSTER_CHARACTERISTICS,
@@ -293,10 +295,21 @@ export const SYSTEM = {
SAVES: CHARACTER.SAVES,
CHALLENGES: CHARACTER.CHALLENGES,
SKILL_CATEGORY: SKILL.CATEGORY,
CORE_SKILLS: SKILL.CORE_SKILLS,
CORE_SKILL_BONUS: SKILL.CORE_SKILL_BONUS,
ARMOR_TYPE: ARMOR.TYPE,
ARMOR_BASE_AC: ARMOR.BASE_AC,
ARMOR_AC_BONUS_ATTRIBUTES: ARMOR.AC_BONUS_ATTRIBUTES,
ARMOR_WITHSTAND_APC: ARMOR.WITHSTAND_APC,
ARMOR_SET_REQUIREMENTS: ARMOR.SET_REQUIREMENTS,
SHIELD_TYPE: SHIELD.TYPE,
SHIELD_BLOCK_AUGMENTS: SHIELD.BLOCK_AUGMENTS,
EQUIPMENT_CATEGORY: EQUIPMENT.CATEGORY,
SPELL_RANGE: SPELL.RANGE,
WEAPON_TYPE: WEAPON.WEAPON_TYPE,
SPELL_COLORS: SPELL.COLORS,
WEAPON_TYPE: WEAPON.TYPE,
WEAPON_GROUP: WEAPON.WEAPON_GROUP,
WEAPON_DAMAGE_TYPE: WEAPON.DAMAGE_TYPE,
WEAPON_CLASS: WEAPON.WEAPON_CLASS,
COMBAT_PROGRESSION_DICE: DICE_VALUES,
SHIELD_DEFENSE_DICE: DEFENSE_DICE_VALUES,
+102 -2
View File
@@ -1,4 +1,105 @@
/**
* 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({
light: {
id: "light",
label: "PRISMRPG.Weapon.Type.light",
apc: 1,
hands: 1
},
oneHanded: {
id: "oneHanded",
label: "PRISMRPG.Weapon.Type.oneHanded",
apc: 2,
hands: 1
},
heavy: {
id: "heavy",
label: "PRISMRPG.Weapon.Type.heavy",
apc: 3,
hands: 2
},
projectile: {
id: "projectile",
label: "PRISMRPG.Weapon.Type.projectile",
apc: 0, // Variable based on specific weapon
hands: 2
}
});
/**
* Weapon groups and their associated passives
* Each weapon belongs to a group and possesses its passive while wielded
*/
export const WEAPON_GROUP = Object.freeze({
longsword: {
id: "longsword",
label: "PRISMRPG.Weapon.WeaponGroup.longsword",
passive: "turningEdge",
passiveLabel: "PRISMRPG.Weapon.Passive.turningEdge",
passiveDescription: "PRISMRPG.Weapon.PassiveDescription.turningEdge"
},
warhammer: {
id: "warhammer",
label: "PRISMRPG.Weapon.WeaponGroup.warhammer",
passive: "puncturingBlows",
passiveLabel: "PRISMRPG.Weapon.Passive.puncturingBlows",
passiveDescription: "PRISMRPG.Weapon.PassiveDescription.puncturingBlows"
},
battleaxe: {
id: "battleaxe",
label: "PRISMRPG.Weapon.WeaponGroup.battleaxe",
passive: "shieldEater",
passiveLabel: "PRISMRPG.Weapon.Passive.shieldEater",
passiveDescription: "PRISMRPG.Weapon.PassiveDescription.shieldEater"
},
dagger: {
id: "dagger",
label: "PRISMRPG.Weapon.WeaponGroup.dagger",
passive: "balancingStance",
passiveLabel: "PRISMRPG.Weapon.Passive.balancingStance",
passiveDescription: "PRISMRPG.Weapon.PassiveDescription.balancingStance"
},
crossbow: {
id: "crossbow",
label: "PRISMRPG.Weapon.WeaponGroup.crossbow",
passive: "boltlock",
passiveLabel: "PRISMRPG.Weapon.Passive.boltlock",
passiveDescription: "PRISMRPG.Weapon.PassiveDescription.boltlock"
},
longbow: {
id: "longbow",
label: "PRISMRPG.Weapon.WeaponGroup.longbow",
passive: "volleyFire",
passiveLabel: "PRISMRPG.Weapon.Passive.volleyFire",
passiveDescription: "PRISMRPG.Weapon.PassiveDescription.volleyFire"
}
});
/**
* Damage types for weapons
*/
export const DAMAGE_TYPE = Object.freeze({
piercing: {
id: "piercing",
label: "PRISMRPG.Weapon.DamageType.piercing",
abbreviation: "P"
},
bludgeoning: {
id: "bludgeoning",
label: "PRISMRPG.Weapon.DamageType.bludgeoning",
abbreviation: "B"
},
slashing: {
id: "slashing",
label: "PRISMRPG.Weapon.DamageType.slashing",
abbreviation: "S"
}
});
// Legacy exports for backward compatibility (to be removed later)
export const WEAPON_TYPE = {
"melee": "PRISMRPG.Weapon.WeaponType.melee",
"ranged": "PRISMRPG.Weapon.WeaponType.ranged"
@@ -16,7 +117,7 @@ export const WEAPON_CLASS = {
"sling": "PRISMRPG.Weapon.WeaponClass.sling",
"thrown": "PRISMRPG.Weapon.WeaponClass.thrown",
"polearm": "PRISMRPG.Weapon.WeaponClass.polearm",
"unarmed" : "PRISMRPG.Weapon.WeaponClass.unarmed"
"unarmed": "PRISMRPG.Weapon.WeaponClass.unarmed"
}
export const WEAPON_CATEGORIES = {
@@ -32,4 +133,3 @@ export const WEAPON_CATEGORIES = {
"thrown": ["axe", "hammer", "mace", "flail", "bow", "sling", "polearm"],
"polearm": ["axe", "hammer", "mace", "flail", "bow", "sling", "thrown"]
}
+15 -2
View File
@@ -7,8 +7,21 @@ export default class PrismRPGArmor extends foundry.abstract.TypeDataModel {
schema.description = new fields.HTMLField({ required: true, textSearch: true })
schema.armorType = new fields.StringField({ required: true, initial: "light", choices: SYSTEM.ARMOR_TYPE })
// MRR - Movement Rating Reduction (reduced by Vigor sub-attribute)
schema.mrr = new fields.NumberField({ ...requiredInteger, required: true, initial: 0, min: 0, label: "Movement Rating Reduction" })
// Armor Passive - the passive ability of this armor piece
schema.passive = new fields.StringField({ required: false, initial: "", label: "Armor Passive" })
schema.passiveDescription = new fields.HTMLField({ required: false, textSearch: true, label: "Passive Description" })
// Armor Augment - the Withstand action effect (requires augment from class feature)
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({ ...requiredInteger, required: true, initial: "" })
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 })
@@ -18,7 +31,7 @@ export default class PrismRPGArmor extends foundry.abstract.TypeDataModel {
schema.cost = new fields.NumberField({ required: true, initial: 0, min: 0 })
schema.money = new fields.StringField({ required: true, initial: "tinbit", choices: SYSTEM.MONEY })
return schema
}
+15
View File
@@ -168,6 +168,21 @@ 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: Object.keys(SYSTEM.CORE_SKILLS || {}),
label: "Selected Core Skill"
}),
attributeChoice: new fields.StringField({
required: true,
initial: "",
label: "Attribute Choice for +2 Bonus"
})
})
return schema
}
+108 -17
View File
@@ -1,4 +1,5 @@
import { SYSTEM } from "../config/system.mjs"
export default class PrismRPGMiracle extends foundry.abstract.TypeDataModel {
static defineSchema() {
const fields = foundry.data.fields
@@ -11,30 +12,120 @@ export default class PrismRPGMiracle extends foundry.abstract.TypeDataModel {
initial: "",
textSearch: true,
})
// Miracle level (1-7+)
schema.level = new fields.NumberField({
...requiredInteger,
initial: 1,
min: 1,
max: 25,
})
schema.components = new fields.SchemaField({
verbal: new fields.BooleanField(),
somatic: new fields.BooleanField(),
material: new fields.BooleanField(),
catalyst: new fields.BooleanField(),
religious: new fields.BooleanField()
})
schema.prayerTime = new fields.StringField({ required: true, initial: "" })
schema.miracleRange = new fields.StringField({ required: true, initial: "" })
schema.areaAffected = new fields.StringField({ required: true, initial: "" })
schema.duration = new fields.StringField({ required: true, initial: "" })
schema.savingThrow = new fields.StringField({ required: true, initial: "" })
schema.materialComponent = new fields.StringField({ required: true, initial: "" })
schema.catalyst = new fields.StringField({ required: true, initial: "" })
schema.miracleType = new fields.StringField({ required: true, initial: "combat", choices: SYSTEM.MIRACLE_TYPES })
schema.attackRoll = new fields.StringField({ required: true, initial: "" })
schema.powerRoll = new fields.StringField({ required: true, initial: "" })
// 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: ""
})
// 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()
)
// 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
}
+72 -4
View File
@@ -1,4 +1,5 @@
import { SYSTEM } from "../config/system.mjs"
export default class PrismRPGShield extends foundry.abstract.TypeDataModel {
static defineSchema() {
const fields = foundry.data.fields
@@ -6,14 +7,81 @@ export default class PrismRPGShield extends foundry.abstract.TypeDataModel {
const requiredInteger = { required: true, nullable: false, integer: true }
schema.description = new fields.HTMLField({ required: true, textSearch: true })
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 })
// Prism RPG shield properties
schema.shieldType = new fields.StringField({
required: true,
initial: "buckler",
choices: Object.keys(SYSTEM.SHIELD_TYPE || {})
})
// APC (Action Point Cost) for Block action
schema.apc = new fields.NumberField({
...requiredInteger,
required: true,
initial: 1,
min: 1,
max: 4,
label: "Block APC"
})
// Shield Rating (SR) - damage prevention dice (e.g., "1d4", "1d8", "1d12", "3d6")
schema.sr = new fields.StringField({
required: true,
initial: "1d4",
label: "Shield Rating (SR)"
})
// Block Augment - passive ability when using Block action
schema.blockAugment = new fields.StringField({
required: true,
initial: "",
label: "Block Augment"
})
schema.blockAugmentDescription = new fields.HTMLField({
required: true,
initial: "",
label: "Block Augment Description"
})
// Augment effects (for equipment progression)
schema.augment = new fields.StringField({
required: true,
initial: "",
label: "Shield Augment"
})
schema.augmentDescription = new fields.HTMLField({
required: true,
initial: "",
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 })
@@ -24,6 +92,7 @@ export default class PrismRPGShield extends foundry.abstract.TypeDataModel {
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 }),
@@ -40,5 +109,4 @@ export default class PrismRPGShield extends foundry.abstract.TypeDataModel {
/** @override */
static LOCALIZATION_PREFIXES = ["PRISMRPG.Shield"]
}
+106 -86
View File
@@ -1,29 +1,75 @@
import { SYSTEM } from "../config/system.mjs"
import { CATEGORY } from "../config/skill.mjs"
/**
* Core Skill data model for Prism RPG
*
* Core Skills are skills in which the character is particularly proficient.
* - Basic skill checks: +5 modifier
* - Advanced skill checks: Only accessible with Core Skill
* - Core Skill Class: Gives access to a class based on archetype
* - Attribute Bonus: +2 to one of 3 associated attributes
*/
export default class PrismRPGSkill extends foundry.abstract.TypeDataModel {
static defineSchema() {
const fields = foundry.data.fields
const schema = {}
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: "layperson", choices: SYSTEM.SKILL_CATEGORY })
schema.base = new fields.StringField({ required: true, initial: "WIS" })
schema.bonus = new fields.NumberField({ ...requiredInteger, required: true, initial: 0, min: 0 })
schema.classesCost = new fields.SchemaField(
Object.values(SYSTEM.CHAR_CLASSES_DEFINES).reduce((obj, pcClass) => {
obj[pcClass.id] = new fields.NumberField({ ...requiredInteger, required: true, initial: 0, min: 0 })
return obj
}, {}),
)
schema.cost = new fields.NumberField({ ...requiredInteger, required: true, initial: 0, min: 0 })
schema.description = new fields.HTMLField({
required: true,
textSearch: true,
initial: ""
})
schema.weaponClass = new fields.StringField({ required: true, initial: "shortblade", choices: SYSTEM.WEAPON_CLASS })
schema.weaponBonus = new fields.SchemaField({
attack: new fields.NumberField({ ...requiredInteger, required: true, initial: 0, min: 0 }),
defense: new fields.NumberField({ ...requiredInteger, required: true, initial: 0, min: 0 }),
damage: new fields.NumberField({ ...requiredInteger, required: true, initial: 0, min: 0 })
// Core Skill type (from the 18 available Core Skills)
schema.coreSkill = new fields.StringField({
required: true,
initial: "acrobatics",
choices: Object.keys(SYSTEM.CORE_SKILLS || {}),
label: "Core Skill"
})
// Is this the character's chosen Core Skill?
schema.isCoreSkill = new fields.BooleanField({
required: true,
initial: false,
label: "Is Core Skill"
})
// If Core Skill, which attribute receives the +2 bonus?
schema.attributeBonus = new fields.StringField({
required: true,
initial: "",
label: "Attribute Bonus"
})
// Skill modifier (includes Core Skill bonus if applicable)
schema.modifier = new fields.NumberField({
...requiredInteger,
required: true,
initial: 0,
label: "Skill Modifier"
})
// Can perform advanced checks
schema.canAdvancedCheck = new fields.BooleanField({
required: true,
initial: false,
label: "Can Perform Advanced Checks"
})
// Associated Core Skill Class (if any)
schema.coreSkillClass = new fields.StringField({
required: true,
initial: "",
label: "Core Skill Class"
})
// Notes/Custom description
schema.notes = new fields.HTMLField({
required: true,
initial: "",
label: "Notes"
})
return schema
@@ -32,83 +78,57 @@ export default class PrismRPGSkill extends foundry.abstract.TypeDataModel {
/** @override */
static LOCALIZATION_PREFIXES = ["PRISMRPG.Skill"]
get skillCategory() {
return game.i18n.localize(CATEGORY[this.category].label)
/**
* Get the Core Skill definition from SYSTEM
*/
get coreSkillDefinition() {
return SYSTEM.CORE_SKILLS?.[this.coreSkill] || null
}
validate(options) {
let isError = super.validate(options)
let bonus = this._source.weaponBonus.attack + this._source.weaponBonus.defense + this._source.weaponBonus.damage
if (bonus > Math.floor(this._source.skillTotal / 10)) {
ui.notifications.error(game.i18n.localize("PRISMRPG.Skill.error.weaponBonus"))
isError = true
}
return isError
/**
* Get the localized Core Skill name
*/
get coreSkillLabel() {
const definition = this.coreSkillDefinition
return definition ? game.i18n.localize(definition.label) : this.coreSkill
}
/**
* Get the available attribute choices for this Core Skill
*/
get attributeChoices() {
const definition = this.coreSkillDefinition
return definition?.attributeChoices || []
}
/**
* Prepare derived data
*/
prepareDerivedData() {
super.prepareDerivedData();
this.skillTotal = this.computeBase();
if (this.category === "weapon") {
this.totalBonus = this.weaponBonus.attack + this.weaponBonus.defense + this.weaponBonus.damage;
if (Number(this.skillTotal)) {
this.availableBonus = Math.max(Math.floor(this.skillTotal / 10) - 1, 0)
} else {
this.availableBonus = "N/A"
}
super.prepareDerivedData()
// If this is the character's Core Skill, apply bonuses
if (this.isCoreSkill) {
this.modifier = SYSTEM.CORE_SKILL_BONUS?.basic || 5
this.canAdvancedCheck = true
} else {
this.modifier = 0
this.canAdvancedCheck = false
}
}
computeBase() {
let actor = this.parent?.actor;
if (!actor) {
return `${this.base} + ${String(this.bonus)}`;
}
/**
* Calculate skill check bonus
* @param {string} attributeKey The attribute to use (str, dex, con, int, wis, cha)
* @returns {number} Total skill check bonus
*/
getSkillCheckBonus(attributeKey) {
let actor = this.parent?.actor
if (!actor) return this.modifier
if (this.base === "N/A" || this.base === "None") {
return this.bonus
}
const attribute = actor.system.characteristics?.[attributeKey]
const attributeMod = attribute?.mod || 0
// Split the base value per stat : WIS,DEX,STR,INT,CHA (example)
let base = this.base;
// Fix errors in the base value
base.replace("CHARISMA", "CHA");
if (base.match(/OR/)) {
let baseSplit = base.split("OR");
let baseSplitLength = baseSplit.length;
if (baseSplitLength > 0) {
// Select the max stat value from the parent actor
let maxStat = 0;
for (let i = 0; i < baseSplitLength; i++) {
const stat = baseSplit[i].trim();
const statValue = actor.system.characteristics[stat.toLowerCase()]?.value || 0;
if (statValue > maxStat) {
maxStat = statValue;
}
}
return maxStat + this.bonus
}
} else {
if (base.match(/\+/)) {
// Split with + calculate the total
let baseSplit = base.split("+");
let baseSplitLength = baseSplit.length;
if (baseSplitLength > 0) {
let total = 0;
for (let i = 0; i < baseSplitLength; i++) {
const stat = baseSplit[i].trim();
const statValue = actor.system.characteristics[stat.toLowerCase()]?.value || 0;
total += statValue;
}
return total + this.bonus
}
} else {
// Single stat
const statValue = actor.system.characteristics[base.trim().toLowerCase()]?.value || 0;
return statValue + this.bonus
}
}
return `${this.base} + ${String(this.bonus)}`;
return attributeMod + this.modifier
}
}
+141 -20
View File
@@ -1,4 +1,5 @@
import { SYSTEM } from "../config/system.mjs"
export default class PrismRPGSpell extends foundry.abstract.TypeDataModel {
static defineSchema() {
const fields = foundry.data.fields
@@ -11,6 +12,8 @@ export default class PrismRPGSpell extends foundry.abstract.TypeDataModel {
initial: "",
textSearch: true,
})
// Spell level (1-7+)
schema.level = new fields.NumberField({
...requiredInteger,
initial: 1,
@@ -18,28 +21,146 @@ export default class PrismRPGSpell extends foundry.abstract.TypeDataModel {
max: 25,
})
schema.cost = new fields.NumberField({ ...requiredInteger, required: true, initial: 0, min: 0 })
schema.memorized = new fields.BooleanField({ required: true, initial: false })
schema.components = new fields.SchemaField({
verbal: new fields.BooleanField(),
somatic: new fields.BooleanField(),
catalyst: new fields.BooleanField(),
material: new fields.BooleanField(),
// Mana cost - base cost of the spell
schema.manaCost = new fields.NumberField({
...requiredInteger,
required: true,
initial: 1,
min: 0,
label: "Mana Cost"
})
// Mana upkeep cost (for sustained spells)
schema.manaUpkeep = new fields.NumberField({
...requiredInteger,
required: true,
initial: 0,
min: 0,
label: "Mana Upkeep"
})
// APC to cast
schema.apc = new fields.NumberField({
...requiredInteger,
required: true,
initial: 1,
min: 0,
label: "Action Point Cost"
})
// Color/Hue of the spell (from the Prism)
schema.color = new fields.StringField({
required: true,
initial: "violet",
choices: Object.keys(SYSTEM.SPELL_COLORS || {}),
label: "Spell Color"
})
// Color effect description
schema.colorEffect = new fields.HTMLField({
required: true,
initial: "",
label: "Color Effect"
})
// Spell Ascension - can be upcast
schema.canAscend = new fields.BooleanField({
required: true,
initial: true,
label: "Can Ascend"
})
schema.ascensionEffect = new fields.HTMLField({
required: true,
initial: "",
label: "Ascension Effect"
})
// Memorized (prepared)
schema.memorized = new fields.BooleanField({
required: true,
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,
initial: "1 action"
})
schema.spellRange = new fields.StringField({
required: true,
initial: "30ft"
})
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: ""
})
// Attack and damage
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()
)
// Legacy fields
schema.cost = new fields.NumberField({
...requiredInteger,
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
})
schema.castingTime = new fields.StringField({ required: true, initial: "" })
schema.spellRange = new fields.StringField({ required: true, initial: "" })
schema.areaAffected = new fields.StringField({ required: true, initial: "" })
schema.duration = new fields.StringField({ required: true, initial: "" })
schema.savingThrow = new fields.StringField({ required: true, initial: "" })
schema.extraAetherPoints = new fields.StringField({ required: true, initial: "" })
schema.materialComponent = new fields.StringField({ required: true, initial: "" })
schema.catalyst = new fields.StringField({ required: true, initial: "" })
schema.criticalType = new fields.StringField({ required: true, initial: "electric", choices : SYSTEM.SPELL_CRITICAL })
schema.attackRoll = new fields.StringField({ required: true, initial: "" })
schema.powerRoll = new fields.StringField({ required: true, initial: "" })
return schema
}
+124 -37
View File
@@ -1,47 +1,139 @@
import { SYSTEM } from "../config/system.mjs"
export default class PrismRPGSkill extends foundry.abstract.TypeDataModel {
export default class PrismRPGWeapon extends foundry.abstract.TypeDataModel {
static defineSchema() {
const fields = foundry.data.fields
const schema = {}
const requiredInteger = { required: true, nullable: false, integer: true }
schema.description = new fields.HTMLField({ required: true, textSearch: true })
schema.weaponType = new fields.StringField({ required: true, initial: "melee", choices: SYSTEM.WEAPON_TYPE })
schema.weaponClass = new fields.StringField({ required: true, initial: "shortblade", choices: SYSTEM.WEAPON_CLASS })
// Prism RPG weapon properties
schema.weaponType = new fields.StringField({
required: true,
initial: "light",
choices: Object.keys(SYSTEM.WEAPON_TYPE || {})
})
schema.weaponGroup = new fields.StringField({
required: true,
initial: "longsword",
choices: Object.keys(SYSTEM.WEAPON_GROUP || {})
})
// APC (Action Point Cost) - determined by weapon type
schema.apc = new fields.NumberField({
...requiredInteger,
required: true,
initial: 1,
min: 0,
max: 3
})
// Damage dice (e.g., "1d8", "2d6", "1d12")
schema.damage = new fields.StringField({
required: true,
initial: "1d8"
})
// Damage type(s)
schema.damageType = new fields.SchemaField({
typeP: new fields.BooleanField(),
typeB: new fields.BooleanField(),
typeS: new fields.BooleanField()
})
schema.damage = new fields.SchemaField({
damageS: new fields.StringField({required: true, initial: ""}),
damageM: new fields.StringField({required: true, initial: ""})
})
schema.applyStrengthDamageBonus = new fields.BooleanField({ required: true, initial: true })
schema.hands = new fields.StringField({ required: true, initial: "1", choices: {"1": "1", "2": "2"} })
schema.isAgile = new fields.BooleanField({ required: true, initial: false })
schema.defenseMax = new fields.NumberField({ ...requiredInteger, required: true, initial: 0, min: 0 })
schema.secondsToAttack = new fields.StringField({required: true, initial: ""})
schema.combatProgressionDice = new fields.StringField({required: true, initial: "d4", choices: SYSTEM.COMBAT_PROGRESSION_DICE})
schema.speed = new fields.SchemaField({
simpleAim: new fields.StringField({required: true, initial: ""}),
carefulAim: new fields.StringField({required: true, initial: ""}),
focusedAim: new fields.StringField({required: true, initial: ""})
piercing: new fields.BooleanField({ initial: false }),
bludgeoning: new fields.BooleanField({ initial: false }),
slashing: new fields.BooleanField({ initial: false })
})
schema.defense = new fields.NumberField({ ...requiredInteger, required: true, initial: 0, min: 0 })
schema.weaponRange = new fields.SchemaField({
pointBlank: new fields.NumberField({ ...requiredInteger, required: true, initial: 0, min: 0 }),
short: new fields.NumberField({ ...requiredInteger, required: true, initial: 0, min: 0 }),
medium: new fields.NumberField({ ...requiredInteger, required: true, initial: 0, min: 0 }),
long: new fields.NumberField({ ...requiredInteger, required: true, initial: 0, min: 0 }),
extreme: new fields.NumberField({ ...requiredInteger, required: true, initial: 0, min: 0 }),
outOfSkill: new fields.NumberField({ ...requiredInteger, required: true, initial: 0, min: 0 })
// Group passive (inherited from weapon group)
schema.groupPassive = new fields.StringField({
required: true,
initial: ""
})
schema.groupPassiveDescription = new fields.HTMLField({
required: true,
initial: ""
})
// Weapon-specific passive ability
schema.passive = new fields.StringField({
required: true,
initial: ""
})
schema.passiveDescription = new fields.HTMLField({
required: true,
initial: ""
})
// Maneuver(s) available with this weapon
schema.maneuver = new fields.StringField({
required: true,
initial: ""
})
schema.maneuverDescription = new fields.HTMLField({
required: true,
initial: ""
})
// Augment effects (for equipment progression)
schema.augment = new fields.StringField({
required: true,
initial: ""
})
schema.augmentDescription = new fields.HTMLField({
required: true,
initial: ""
})
// Projectile-specific properties
schema.isProjectile = new fields.BooleanField({
required: true,
initial: false
})
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.reloadAPC = new fields.NumberField({
...requiredInteger,
initial: 0,
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
})
schema.bonuses = new fields.SchemaField({
@@ -60,9 +152,4 @@ export default class PrismRPGSkill extends foundry.abstract.TypeDataModel {
/** @override */
static LOCALIZATION_PREFIXES = ["PRISMRPG.Weapon"]
get weaponCategory() {
return game.i18n.localize(CATEGORY[this.weaponType].label)
}
}