diff --git a/css/fvtt-prism-rpg.css b/css/fvtt-prism-rpg.css index af7dec9..9c60a5a 100644 --- a/css/fvtt-prism-rpg.css +++ b/css/fvtt-prism-rpg.css @@ -1550,6 +1550,51 @@ i.prismrpg { .prismrpg .weapon-content label { flex: 10%; } +.prismrpg .weapon-content .weapon-passives legend { + display: flex; + justify-content: space-between; + align-items: center; + width: 100%; +} +.prismrpg .weapon-content .weapon-passives legend button { + padding: 2px 8px; + font-size: 12px; +} +.prismrpg .weapon-content .weapon-passives .passive-item { + margin-bottom: 16px; + padding: 12px; + border: 1px solid var(--color-border-light-primary); + border-radius: 4px; + background: rgba(0, 0, 0, 0.05); +} +.prismrpg .weapon-content .weapon-passives .passive-item:last-child { + margin-bottom: 0; +} +.prismrpg .weapon-content .weapon-passives .passive-header { + display: flex; + gap: 8px; + align-items: center; + margin-bottom: 8px; +} +.prismrpg .weapon-content .weapon-passives .passive-header input[type="text"] { + flex: 1; +} +.prismrpg .weapon-content .weapon-passives .passive-header button { + padding: 4px 8px; + font-size: 12px; +} +.prismrpg .weapon-content .weapon-passives .passive-header button[data-action="delete-passive"] { + color: var(--color-text-danger, #c00); +} +.prismrpg .weapon-content .weapon-passives .passive-header button[data-action="delete-passive"]:hover { + background: var(--color-bg-danger, rgba(200, 0, 0, 0.1)); +} +.prismrpg .weapon-content .weapon-passives .hint { + font-style: italic; + color: var(--color-text-light-secondary); + text-align: center; + padding: 12px; +} .prismrpg .weapon-content .weapon-maneuvers legend { display: flex; justify-content: space-between; @@ -2049,6 +2094,54 @@ i.prismrpg { .prismrpg .equipment-content label { flex: 10%; } +.prismrpg .equipment-content .kit-passive { + margin-bottom: 16px; +} +.prismrpg .equipment-content .special-activations legend { + display: flex; + justify-content: space-between; + align-items: center; + width: 100%; +} +.prismrpg .equipment-content .special-activations legend button { + padding: 2px 8px; + font-size: 12px; +} +.prismrpg .equipment-content .special-activations .activation-item { + margin-bottom: 16px; + padding: 12px; + border: 1px solid var(--color-border-light-primary); + border-radius: 4px; + background: rgba(0, 0, 0, 0.05); +} +.prismrpg .equipment-content .special-activations .activation-item:last-child { + margin-bottom: 0; +} +.prismrpg .equipment-content .special-activations .activation-header { + display: flex; + gap: 8px; + align-items: center; + margin-bottom: 8px; +} +.prismrpg .equipment-content .special-activations .activation-header input[type="text"] { + flex: 1; +} +.prismrpg .equipment-content .special-activations .activation-header button { + padding: 4px 8px; + font-size: 12px; +} +.prismrpg .equipment-content .special-activations .activation-header button[data-action="delete-special-activation"] { + color: var(--color-text-danger, #c00); +} +.prismrpg .equipment-content .special-activations .activation-header button[data-action="delete-special-activation"]:hover { + background: var(--color-bg-danger, rgba(200, 0, 0, 0.1)); +} +.prismrpg .equipment-content .special-activations .hint { + font-style: italic; + color: var(--color-text-light-secondary); + text-align: center; + padding: 12px; +} .prismrpg .shield-content { font-family: var(--font-primary); font-size: calc(var(--font-size-standard) * 1); diff --git a/lang/en.json b/lang/en.json index ae74889..a4cae03 100644 --- a/lang/en.json +++ b/lang/en.json @@ -300,6 +300,18 @@ }, "money": { "label": "Money unit" + }, + "isKit": { + "label": "Is Kit?" + }, + "passive": { + "label": "Passive Name" + }, + "passiveDescription": { + "label": "Passive Description" + }, + "specialActivations": { + "label": "Special Activations" } } }, @@ -493,6 +505,7 @@ "encumbranceLoad": "Encumbrance Load", "isHelmet": "Is Helmet?", "equipped": "Equipped?", + "isImplement": "Is Implement?", "armorPassive": "Armor Passive", "passiveName": "Passive Name", "passiveDescription": "Passive Description", @@ -517,13 +530,22 @@ "hands": "Hands Required", "groupPassive": "Weapon Group Passive", "groupPassiveName": "Group Passive Name", - "weaponPassive": "Weapon Passive", + "weaponPassives": "Weapon Passives", + "addPassive": "Add Passive", + "deletePassive": "Delete Passive", "weaponManeuvers": "Weapon Maneuvers", "addManeuver": "Add Maneuver", "deleteManeuver": "Delete Maneuver", "maneuverName": "Maneuver Name", "maneuverDescription": "Maneuver Description", "weaponAugment": "Weapon Augment", + "isKit": "Is Kit?", + "kitPassive": "Kit Passive", + "specialActivations": "Special Activations", + "addSpecialActivation": "Add Special Activation", + "deleteSpecialActivation": "Delete Special Activation", + "activationName": "Activation Name", + "activationDescription": "Activation Description", "shieldType": "Shield Type", "blockAPC": "Block APC", "shieldRating": "Shield Rating (SR)", @@ -686,6 +708,8 @@ "violet": "Violet" }, "Hint": { + "noPassives": "No passives defined. Click the + button to add one.", + "noSpecialActivations": "No special activations defined. Click the + button to add one.", "noManeuvers": "No maneuvers defined. Click the + button to add one.", "isCoreSkill": "Check this if this is your character's chosen Core Skill", "attributeBonus": "Choose which attribute receives the +2 bonus", diff --git a/module/applications/sheets/equipment-sheet.mjs b/module/applications/sheets/equipment-sheet.mjs index 10eb1d0..5162776 100644 --- a/module/applications/sheets/equipment-sheet.mjs +++ b/module/applications/sheets/equipment-sheet.mjs @@ -23,6 +23,48 @@ export default class PrismRPGEquipmentSheet extends PrismRPGItemSheet { async _prepareContext() { const context = await super._prepareContext() context.enrichedDescription = await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.document.system.description, { async: true }) + + // Enrich passive description if equipment is a kit + if (this.document.system.isKit && this.document.system.passiveDescription) { + context.enrichedPassiveDescription = await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.document.system.passiveDescription, { async: true }) + } + + // Enrich descriptions for all special activations + context.enrichedSpecialActivations = await Promise.all( + this.document.system.specialActivations.map(async (activation) => ({ + ...activation, + enrichedDescription: await foundry.applications.ux.TextEditor.implementation.enrichHTML(activation.description, { async: true }) + })) + ) + return context } + + /** @override */ + _onRender(context, options) { + super._onRender(context, options) + + // Add event listeners for special activation management + this.element.querySelectorAll('[data-action="add-special-activation"]').forEach(el => { + el.addEventListener("click", this._onAddSpecialActivation.bind(this)) + }) + + this.element.querySelectorAll('[data-action="delete-special-activation"]').forEach(el => { + el.addEventListener("click", this._onDeleteSpecialActivation.bind(this)) + }) + } + + async _onAddSpecialActivation(event) { + event.preventDefault() + const specialActivations = [...this.document.system.specialActivations] + specialActivations.push({ name: "", description: "" }) + await this.document.update({ "system.specialActivations": specialActivations }) + } + + async _onDeleteSpecialActivation(event) { + event.preventDefault() + const index = parseInt(event.currentTarget.closest("[data-activation-index]").dataset.activationIndex) + const specialActivations = this.document.system.specialActivations.filter((_, i) => i !== index) + await this.document.update({ "system.specialActivations": specialActivations }) + } } diff --git a/module/applications/sheets/weapon-sheet.mjs b/module/applications/sheets/weapon-sheet.mjs index 9da0905..bf83612 100644 --- a/module/applications/sheets/weapon-sheet.mjs +++ b/module/applications/sheets/weapon-sheet.mjs @@ -23,7 +23,14 @@ 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 }) + + // Enrich descriptions for all passives + context.enrichedPassives = await Promise.all( + this.document.system.passives.map(async (passive) => ({ + ...passive, + enrichedDescription: await foundry.applications.ux.TextEditor.implementation.enrichHTML(passive.description, { async: true }) + })) + ) // Enrich descriptions for all maneuvers context.enrichedManeuvers = await Promise.all( @@ -40,6 +47,15 @@ export default class PrismRPGWeaponSheet extends PrismRPGItemSheet { _onRender(context, options) { super._onRender(context, options) + // Add event listeners for passive management + this.element.querySelectorAll('[data-action="add-passive"]').forEach(el => { + el.addEventListener("click", this._onAddPassive.bind(this)) + }) + + this.element.querySelectorAll('[data-action="delete-passive"]').forEach(el => { + el.addEventListener("click", this._onDeletePassive.bind(this)) + }) + // Add event listeners for maneuver management this.element.querySelectorAll('[data-action="add-maneuver"]').forEach(el => { el.addEventListener("click", this._onAddManeuver.bind(this)) @@ -50,6 +66,20 @@ export default class PrismRPGWeaponSheet extends PrismRPGItemSheet { }) } + async _onAddPassive(event) { + event.preventDefault() + const passives = [...this.document.system.passives] + passives.push({ name: "", description: "" }) + await this.document.update({ "system.passives": passives }) + } + + async _onDeletePassive(event) { + event.preventDefault() + const index = parseInt(event.currentTarget.closest("[data-passive-index]").dataset.passiveIndex) + const passives = this.document.system.passives.filter((_, i) => i !== index) + await this.document.update({ "system.passives": passives }) + } + async _onAddManeuver(event) { event.preventDefault() const maneuvers = [...this.document.system.maneuvers] diff --git a/module/models/equipment.mjs b/module/models/equipment.mjs index 6f1d335..d6957a2 100644 --- a/module/models/equipment.mjs +++ b/module/models/equipment.mjs @@ -13,6 +13,41 @@ export default class PrismRPGEquipment extends foundry.abstract.TypeDataModel { schema.cost = new fields.NumberField({ ...requiredInteger, required: true, initial: 0, min: 0 }) schema.money = new fields.StringField({ required: true, initial: "tinbit", choices: SYSTEM.MONEY }) + // Kit properties + schema.isKit = new fields.BooleanField({ + required: true, + initial: false, + label: "Is Kit" + }) + + // Kit passive (only applies when isKit is true) + schema.passive = new fields.StringField({ + required: false, + initial: "", + label: "Passive Name" + }) + + schema.passiveDescription = new fields.HTMLField({ + required: false, + initial: "", + label: "Passive Description" + }) + + // Special Activations (only applies when isKit is true) + schema.specialActivations = new fields.ArrayField(new fields.SchemaField({ + name: new fields.StringField({ + required: true, + initial: "" + }), + description: new fields.HTMLField({ + required: true, + initial: "" + }) + }), { + required: true, + initial: [] + }) + return schema } diff --git a/module/models/weapon.mjs b/module/models/weapon.mjs index cb2be58..151b419 100644 --- a/module/models/weapon.mjs +++ b/module/models/weapon.mjs @@ -54,15 +54,19 @@ export default class PrismRPGWeapon extends foundry.abstract.TypeDataModel { initial: "" }) - // Weapon-specific passive ability - schema.passive = new fields.StringField({ + // Weapon-specific passive abilities + schema.passives = new fields.ArrayField(new fields.SchemaField({ + name: new fields.StringField({ + required: true, + initial: "" + }), + description: new fields.HTMLField({ + required: true, + initial: "" + }) + }), { required: true, - initial: "" - }) - - schema.passiveDescription = new fields.HTMLField({ - required: true, - initial: "" + initial: [] }) // Maneuver(s) available with this weapon @@ -120,6 +124,7 @@ export default class PrismRPGWeapon extends foundry.abstract.TypeDataModel { 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.equipped = new fields.BooleanField({ required: true, initial: false }) + schema.isImplement = new fields.BooleanField({ required: true, initial: false }) return schema } diff --git a/styles/equipment.less b/styles/equipment.less index a2db33b..1a3af4b 100644 --- a/styles/equipment.less +++ b/styles/equipment.less @@ -9,8 +9,69 @@ height: 50px; } } - + label { flex: 10%; } + + .kit-passive { + margin-bottom: 16px; + } + + .special-activations { + legend { + display: flex; + justify-content: space-between; + align-items: center; + width: 100%; + + button { + padding: 2px 8px; + font-size: 12px; + } + } + + .activation-item { + margin-bottom: 16px; + padding: 12px; + border: 1px solid var(--color-border-light-primary); + border-radius: 4px; + background: rgba(0, 0, 0, 0.05); + + &:last-child { + margin-bottom: 0; + } + } + + .activation-header { + display: flex; + gap: 8px; + align-items: center; + margin-bottom: 8px; + + input[type="text"] { + flex: 1; + } + + button { + padding: 4px 8px; + font-size: 12px; + + &[data-action="delete-special-activation"] { + color: var(--color-text-danger, #c00); + + &:hover { + background: var(--color-bg-danger, rgba(200, 0, 0, 0.1)); + } + } + } + } + + .hint { + font-style: italic; + color: var(--color-text-light-secondary); + text-align: center; + padding: 12px; + } + } } diff --git a/styles/weapon.less b/styles/weapon.less index 4a19e4b..2acabfb 100644 --- a/styles/weapon.less +++ b/styles/weapon.less @@ -14,6 +14,63 @@ flex: 10%; } + .weapon-passives { + legend { + display: flex; + justify-content: space-between; + align-items: center; + width: 100%; + + button { + padding: 2px 8px; + font-size: 12px; + } + } + + .passive-item { + margin-bottom: 16px; + padding: 12px; + border: 1px solid var(--color-border-light-primary); + border-radius: 4px; + background: rgba(0, 0, 0, 0.05); + + &:last-child { + margin-bottom: 0; + } + } + + .passive-header { + display: flex; + gap: 8px; + align-items: center; + margin-bottom: 8px; + + input[type="text"] { + flex: 1; + } + + button { + padding: 4px 8px; + font-size: 12px; + + &[data-action="delete-passive"] { + color: var(--color-text-danger, #c00); + + &:hover { + background: var(--color-bg-danger, rgba(200, 0, 0, 0.1)); + } + } + } + } + + .hint { + font-style: italic; + color: var(--color-text-light-secondary); + text-align: center; + padding: 12px; + } + } + .weapon-maneuvers { legend { display: flex; diff --git a/templates/equipment.hbs b/templates/equipment.hbs index c088e1d..8b97cd5 100644 --- a/templates/equipment.hbs +++ b/templates/equipment.hbs @@ -13,6 +13,67 @@ {{formField systemFields.cost value=system.cost localize=true}} {{formField systemFields.money value=system.money localize=true}} + {{formField systemFields.isKit value=system.isKit localize=true label="PRISMRPG.Label.isKit"}} + + {{#if system.isKit}} + {{! Kit Passive }} +
+ + {{! Special Activations }} + + {{/if}} +