diff --git a/modules/pegasus-actor.js b/modules/pegasus-actor.js index 794ebe4..1c59edd 100644 --- a/modules/pegasus-actor.js +++ b/modules/pegasus-actor.js @@ -1100,6 +1100,9 @@ export class PegasusActor extends Actor { /* -------------------------------------------- */ async computeNRGHealth() { + if (this.type == "vehicle") { + return + } if (this.isOwner || game.user.isGM) { let updates = {} let phyDiceValue = PegasusUtility.getDiceValue(this.system.statistics.phy.value) + this.system.secondary.health.bonus + this.system.statistics.phy.mod; diff --git a/modules/pegasus-main.js b/modules/pegasus-main.js index f298eee..3ee15df 100644 --- a/modules/pegasus-main.js +++ b/modules/pegasus-main.js @@ -11,6 +11,7 @@ import { PegasusActor } from "./pegasus-actor.js"; import { PegasusItemSheet } from "./pegasus-item-sheet.js"; import { PegasusActorSheet } from "./pegasus-actor-sheet.js"; +import { PegasusVehicleSheet } from "./pegasus-vehicle-sheet.js"; import { PegasusNPCSheet } from "./pegasus-npc-sheet.js"; import { PegasusUtility } from "./pegasus-utility.js"; import { PegasusCombat } from "./pegasus-combat.js"; @@ -62,8 +63,8 @@ Hooks.once("init", async function () { /* -------------------------------------------- */ // Register sheet application classes Actors.unregisterSheet("core", ActorSheet); - Actors.registerSheet("fvtt-pegasus", PegasusActorSheet, { types: ["character"], makeDefault: true }); - //Actors.registerSheet("fvtt-pegasus", PegasusVehicleSheet, { types: ["vehicle"], makeDefault: false }); + Actors.registerSheet("fvtt-pegasus", PegasusActorSheet, { types: ["character"], makeDefault: true }) + Actors.registerSheet("fvtt-pegasus", PegasusVehicleSheet, { types: ["vehicle"], makeDefault: false }) Items.unregisterSheet("core", ItemSheet); Items.registerSheet("fvtt-pegasus", PegasusItemSheet, { makeDefault: true }); diff --git a/modules/pegasus-utility.js b/modules/pegasus-utility.js index 4926d32..f9c5d5d 100644 --- a/modules/pegasus-utility.js +++ b/modules/pegasus-utility.js @@ -447,6 +447,8 @@ export class PegasusUtility { 'systems/fvtt-pegasus-rpg/templates/partial-equipment-effects.html', 'systems/fvtt-pegasus-rpg/templates/partial-actor-stat-block.html', 'systems/fvtt-pegasus-rpg/templates/partial-actor-status.html', + 'systems/fvtt-pegasus-rpg/templates/partial-vehicle-stat-block.html', + 'systems/fvtt-pegasus-rpg/templates/partial-vehicle-arc.html', 'systems/fvtt-pegasus-rpg/templates/partial-item-nav.html', 'systems/fvtt-pegasus-rpg/templates/partial-item-description.html', 'systems/fvtt-pegasus-rpg/templates/partial-actor-equipment.html', diff --git a/modules/pegasus-vehicle-sheet.js b/modules/pegasus-vehicle-sheet.js new file mode 100644 index 0000000..99d39be --- /dev/null +++ b/modules/pegasus-vehicle-sheet.js @@ -0,0 +1,359 @@ +/** + * Extend the basic ActorSheet with some very simple modifications + * @extends {ActorSheet} + */ + +import { PegasusUtility } from "./pegasus-utility.js" +import { PegasusRollDialog } from "./pegasus-roll-dialog.js" + +/* -------------------------------------------- */ +export class PegasusVehicleSheet extends ActorSheet { + + /** @override */ + static get defaultOptions() { + + return mergeObject(super.defaultOptions, { + classes: ["fvtt-pegasus-rpg", "sheet", "actor"], + template: "systems/fvtt-pegasus-rpg/templates/vehicle-sheet.html", + width: 960, + height: 720, + tabs: [{ navSelector: ".sheet-tabs", contentSelector: ".sheet-body", initial: "combat" }], + dragDrop: [{ dragSelector: ".item-list .item", dropSelector: null }], + editScore: true + }); + } + + /* -------------------------------------------- */ + async getData() { + const objectData = this.object + let actorData = duplicate(this.object) + + let formData = { + title: this.title, + id: objectData.id, + type: objectData.type, + img: objectData.img, + name: objectData.name, + editable: this.isEditable, + cssClass: this.isEditable ? "editable" : "locked", + data: actorData.system, + effects: this.object.effects.map(e => foundry.utils.deepClone(e.data)), + limited: this.object.limited, + optionsDiceList: PegasusUtility.getOptionsDiceList(), + optionsLevel: PegasusUtility.getOptionsLevel(), + subActors: duplicate(this.actor.getSubActors()), + effects: duplicate(this.actor.getEffects()), + options: this.options, + owner: this.document.isOwner, + editScore: this.options.editScore, + isGM: game.user.isGM + } + this.formData = formData; + + console.log("VEHICLE : ", formData, this.object); + return formData; + } + + /* -------------------------------------------- */ + async openGenericRoll() { + let rollData = PegasusUtility.initGenericRoll() + rollData.traumaState = this.actor.getTraumaState() + + let rollDialog = await PegasusRollDialog.create( this.actor, rollData); + rollDialog.render( true ); + } + + /* -------------------------------------------- */ + async rollIDR( itemId, diceValue) { + let item = this.actor.items.get( itemId) ?? {name: "Unknown"} + let myRoll = new Roll(diceValue+"x").roll({ async: false }) + await PegasusUtility.showDiceSoNice(myRoll, game.settings.get("core", "rollMode")) + let chatData = { + user: game.user.id, + rollMode: game.settings.get("core", "rollMode"), + whisper: [game.user.id].concat(ChatMessage.getWhisperRecipients('GM')), + content: `${this.actor.name} has roll IDR for ${item.name} : ${myRoll.total}` + } + ChatMessage.create(chatData) + } + + /* -------------------------------------------- */ + /** @override */ + activateListeners(html) { + super.activateListeners(html); + + // Everything below here is only needed if the sheet is editable + if (!this.options.editable) return; + + html.bind("keydown", function(e) { // Ignore Enter in actores sheet + if (e.keyCode === 13) return false; + }); + + // Update Inventory Item + html.find('.item-edit').click(ev => { + const li = $(ev.currentTarget).parents(".item") + let itemId = li.data("item-id") + const item = this.actor.items.get( itemId ); + item.sheet.render(true); + }); + // Delete Inventory Item + html.find('.item-delete').click(ev => { + const li = $(ev.currentTarget).parents(".item") + PegasusUtility.confirmDelete(this, li) + }) + html.find('.item-add').click(ev => { + let dataType = $(ev.currentTarget).data("type") + this.actor.createEmbeddedDocuments('Item', [{ name: "NewItem", type: dataType }], { renderSheet: true }) + }) + + html.find('.spec-group-activate').click(ev => { + const li = $(ev.currentTarget).parents(".item"); + let itemId = li.data("item-id"); + this.actor.specPowerActivate( itemId) + }); + html.find('.spec-group-deactivate').click(ev => { + const li = $(ev.currentTarget).parents(".item"); + let itemId = li.data("item-id"); + this.actor.specPowerDeactivate( itemId) + }); + + html.find('.equip-activate').click(ev => { + const li = $(ev.currentTarget).parents(".item") + let itemId = li.data("item-id") + this.actor.equipActivate( itemId) + }); + html.find('.equip-deactivate').click(ev => { + const li = $(ev.currentTarget).parents(".item") + let itemId = li.data("item-id") + this.actor.equipDeactivate( itemId) + }); + + html.find('.effect-used').click(ev => { + const li = $(ev.currentTarget).parents(".item"); + let itemId = li.data("item-id"); + this.actor.perkEffectUsed( itemId) + }); + + html.find('.perk-status').change(ev => { + const li = $(ev.currentTarget).parents(".item"); + let itemId = li.data("item-id"); + this.actor.updatePerkStatus( itemId, ev.currentTarget.value) + this.render() + }); + + html.find('.power-cost-spent').change(ev => { + const li = $(ev.currentTarget).parents(".item"); + let itemId = li.data("item-id"); + this.actor.updatePowerSpentCost( itemId, ev.currentTarget.value) + }); + + html.find('.power-dmg-roll').click(ev => { + const li = $(ev.currentTarget).parents(".item") + let itemId = li.data("item-id") + this.actor.powerDmgRoll( itemId ) + }) + + html.find('.perk-used').change(ev => { + const li = $(ev.currentTarget).parents(".item") + let itemId = li.data("item-id") + let index = Number($(ev.currentTarget).data("use-index") ) + this.actor.updatePerkUsed( itemId, index, ev.currentTarget.checked ) + }); + + html.find('.subactor-edit').click(ev => { + const li = $(ev.currentTarget).parents(".item"); + let actorId = li.data("actor-id"); + let actor = game.actors.get( actorId ); + actor.sheet.render(true); + }); + + html.find('.subactor-delete').click(ev => { + const li = $(ev.currentTarget).parents(".item"); + let actorId = li.data("actor-id"); + this.actor.delSubActor(actorId); + }); + + html.find('.quantity-minus').click(event => { + const li = $(event.currentTarget).parents(".item"); + this.actor.incDecQuantity( li.data("item-id"), -1 ); + } ) + html.find('.quantity-plus').click(event => { + const li = $(event.currentTarget).parents(".item"); + this.actor.incDecQuantity( li.data("item-id"), +1 ); + } ) + html.find('.current-nrg-minus').click(event => { + this.actor.incDecNRG( -1 ); + } ) + html.find('.current-nrg-plus').click(event => { + this.actor.incDecNRG( 1 ); + } ) + + html.find('.ammo-minus').click(event => { + const li = $(event.currentTarget).parents(".item") + this.actor.incDecAmmo( li.data("item-id"), -1 ); + } ); + html.find('.ammo-plus').click(event => { + const li = $(event.currentTarget).parents(".item") + this.actor.incDecAmmo( li.data("item-id"), +1 ) + } ); + + html.find('.stun-minus').click(event => { + this.actor.modifyStun( -1 ) + } ) + html.find('.stun-plus').click(event => { + this.actor.modifyStun( 1 ) + } ) + + + html.find('.momentum-minus').click(event => { + this.actor.modifyMomentum( -1 ) + } ) + html.find('.momentum-plus').click(event => { + this.actor.modifyMomentum( 1 ) + } ) + + html.find('.unarmed-attack').click((event) => { + this.actor.rollUnarmedAttack(); + }); + html.find('.generic-pool-roll').click((event) => { + this.openGenericRoll() + } ); + html.find('.attack-melee').click((event) => { + this.actor.rollPool( 'com', false, "melee-atk"); + }); + html.find('.attack-ranged').click((event) => { + this.actor.rollPool( 'agi', false, "ranged-atk"); + }); + html.find('.defense-roll').click((event) => { + this.actor.rollPool( 'def', true); + }); + html.find('.damage-melee').click((event) => { + this.actor.rollPool( 'str', false, "melee-dmg"); + }); + html.find('.damage-ranged').click((event) => { + this.actor.rollPool( 'per', false, "ranged-dmg"); + }); + html.find('.damage-resistance').click((event) => { + this.actor.rollPool( 'phy', false, "dmg-res"); + }); + + html.find('.roll-stat').click((event) => { + const statId = $(event.currentTarget).data("stat-key"); + this.actor.rollStat(statId); + }); + html.find('.roll-mr').click((event) => { + this.actor.rollMR(); + }); + html.find('.roll-idr').click((event) => { + const diceValue = $(event.currentTarget).data("dice-value") + const li = $(event.currentTarget).parents(".item") + this.rollIDR( li.data("item-id"), diceValue) + }) + + html.find('.roll-spec').click((event) => { + const li = $(event.currentTarget).parents(".item"); + const specId = li.data("item-id"); + this.actor.rollSpec(specId); + }); + html.find('.power-roll').click((event) => { + const li = $(event.currentTarget).parents(".item"); + const powerId = li.data("item-id"); + this.actor.rollPower(powerId); + }); + html.find('.weapon-roll').click((event) => { + const li = $(event.currentTarget).parents(".item"); + const weaponId = li.data("item-id"); + this.actor.rollWeapon(weaponId); + }); + html.find('.armor-roll').click((event) => { + const li = $(event.currentTarget).parents(".item"); + const armorId = li.data("item-id"); + this.actor.rollArmor(armorId); + }); + + html.find('.weapon-damage-roll').click((event) => { + const li = $(event.currentTarget).parents(".item"); + const weaponId = li.data("item-id"); + this.actor.rollWeapon(weaponId, true); + }); + + html.find('.weapon-damage').click((event) => { + const li = $(event.currentTarget).parents(".item"); + const weapon = this.actor.getOwnedItem(li.data("item-id")); + this.actor.rollDamage(weapon, 'damage'); + }); + + html.find('.lock-unlock-sheet').click((event) => { + this.options.editScore = !this.options.editScore; + this.render(true); + }); + html.find('.item-link a').click((event) => { + const itemId = $(event.currentTarget).data("item-id"); + const item = this.actor.getOwnedItem(itemId); + item.sheet.render(true); + }); + html.find('.item-equip').click(ev => { + const li = $(ev.currentTarget).parents(".item"); + this.actor.equipItem( li.data("item-id") ); + this.render(true); + }); + html.find('.power-activate').click(ev => { + const li = $(ev.currentTarget).parents(".item"); + this.actor.activatePower( li.data("item-id") ); + this.render(true); + }); + html.find('.vice-virtue-activate').click(ev => { + const li = $(ev.currentTarget).parents(".item") + this.actor.activateViceOrVirtue( li.data("item-id") ) + this.render(true); + }) + + html.find('.change-worstfear').change(ev => { + this.actor.manageWorstFear( ev.currentTarget.checked ) + }); + html.find('.change-desires').change(ev => { + this.actor.manageDesires( ev.currentTarget.checked ) + }); + + html.find('.update-field').change(ev => { + const fieldName = $(ev.currentTarget).data("field-name"); + let value = Number(ev.currentTarget.value); + this.actor.update( { [`${fieldName}`]: value } ); + }); + html.find('.perk-active').click(ev => { + const li = $(ev.currentTarget).parents(".item"); + this.actor.activatePerk( li.data("item-id") ); + this.render(true); + }); + + } + + /* -------------------------------------------- */ + /** @override */ + setPosition(options = {}) { + const position = super.setPosition(options); + const sheetBody = this.element.find(".sheet-body"); + const bodyHeight = position.height - 192; + sheetBody.css("height", bodyHeight); + return position; + } + + /* -------------------------------------------- */ + async _onDropItem(event, dragData) { + const item = fromUuidSync(dragData.uuid) + if (item == undefined) { + item = this.actor.items.get( dragData.uuid ) + } + let ret = await this.actor.preprocessItem( event, item, true ) + if ( ret ) { + super._onDropItem(event, dragData) + } + } + + /* -------------------------------------------- */ + /** @override */ + _updateObject(event, formData) { + // Update the Actor + return this.object.update(formData) + } +} diff --git a/styles/simple.css b/styles/simple.css index 9221f64..ebb8569 100644 --- a/styles/simple.css +++ b/styles/simple.css @@ -1376,6 +1376,10 @@ Focus FOC: #ff0084 max-height: 42px; min-height: 36px; } +.item-stat-roll-vehicle { + max-height: 96px; + min-height: 90px; +} .item-stat-roll select, .item-stat-roll input { margin-top: 4px; margin-right: 2px; diff --git a/system.json b/system.json index 3dd2002..0432ffb 100644 --- a/system.json +++ b/system.json @@ -278,9 +278,9 @@ "styles": [ "styles/simple.css" ], - "templateVersion": 109, + "templateVersion": 112, "title": "Pegasus RPG", "url": "https://www.uberwald.me/data/files/fvtt-pegasus-rpg", - "version": "10.0.0", + "version": "10.0.3", "background" : "./images/ui/pegasus_welcome_page.webp" } diff --git a/template.json b/template.json index a675ea5..574b13f 100644 --- a/template.json +++ b/template.json @@ -199,26 +199,30 @@ "description": "" }, "vehicle": { + "subactors": [], "statistics": { "fc": { "label": "FC", "abbrev": "fc", "level": 0, - "currentlevel": 0 + "currentlevel": 0, + "col": 1 }, "man": { - "label": "FC", - "abbrev": "fc", + "label": "MAN", + "abbrev": "man", "dicevalue": "", "level": 0, - "currentlevel": 0 + "currentlevel": 0, + "col": 1 }, "hr": { "label": "HR", "abbrev": "hr", "level": 0, "currentlevel": 0, - "size": 0 + "size": 0, + "col": 2 }, "pc": { "label": "PC", @@ -226,13 +230,15 @@ "level": 0, "currentlevel": 0, "avgnrg": 0, - "curnrg": 0 + "curnrg": 0, + "col": 2 }, "mr": { "label": "MR", "abbrev": "mr", "level": 0, - "currentlevel": 0 + "currentlevel": 0, + "col": 3 }, "ad": { "label": "A/D", @@ -240,7 +246,8 @@ "level": 0, "currentlevel": 0, "accmode": "", - "currentspeed": "" + "currentspeed": "", + "col": 3 } }, "stun": { @@ -275,34 +282,46 @@ }, "arcs": { "frontarc": { + "label": "Fore Arc", "topspeed": "", "shieldlevel": 0, - "armourlevel": 0 + "armourlevel": 0, + "is3D": false }, "reararc": { + "label": "Rear Arc", "topspeed": "", "shieldlevel": 0, - "armourlevel": 0 + "armourlevel": 0, + "is3D": false }, "rightarc": { + "label": "Right Arc", "topspeed": "", "shieldlevel": 0, - "armourlevel": 0 + "armourlevel": 0, + "is3D": false }, "leftarc": { + "label": "Left Arc", "topspeed": "", "shieldlevel": 0, - "armourlevel": 0 + "armourlevel": 0, + "is3D": false }, "toparc": { + "label": "Top Arc", "topspeed": "", "shieldlevel": 0, - "armourlevel": 0 + "armourlevel": 0, + "is3D": true }, "bottomarc": { + "label": "Bottom Arc", "topspeed": "", "shieldlevel": 0, - "armourlevel": 0 + "armourlevel": 0, + "is3D": true } } } diff --git a/templates/item-race-sheet.html b/templates/item-race-sheet.html index 287e286..21d7a27 100644 --- a/templates/item-race-sheet.html +++ b/templates/item-race-sheet.html @@ -54,12 +54,12 @@ diff --git a/templates/partial-vehicle-stat-block.html b/templates/partial-vehicle-stat-block.html new file mode 100644 index 0000000..7c57f2b --- /dev/null +++ b/templates/partial-vehicle-stat-block.html @@ -0,0 +1,28 @@ +
  • +
    +
    + + + + +

    {{stat.abbrev}}

    +
    + +
    +
    + Current level + +
    +
    +
  • \ No newline at end of file diff --git a/templates/vehicle-sheet.html b/templates/vehicle-sheet.html new file mode 100644 index 0000000..05f1917 --- /dev/null +++ b/templates/vehicle-sheet.html @@ -0,0 +1,847 @@ +
    + + {{!-- Sheet Header --}} +
    +
    +

    +
    + +
    + +
    +
    +
      + {{#each data.statistics as |stat key|}} + {{#if (eq stat.col 1)}} + {{> systems/fvtt-pegasus-rpg/templates/partial-vehicle-stat-block.html stat=stat key=key}} + {{/if}} + {{/each}} +
    +
    + +
    +
      + {{#each data.statistics as |stat key|}} + {{#if (eq stat.col 2)}} + {{> systems/fvtt-pegasus-rpg/templates/partial-vehicle-stat-block.html stat=stat key=key}} + {{/if}} + {{/each}} +
    +
    + +
    +
      + {{#each data.statistics as |stat key|}} + {{#if (eq stat.col 3)}} + {{> systems/fvtt-pegasus-rpg/templates/partial-vehicle-stat-block.html stat=stat key=key}} + {{/if}} + {{/each}} +
    +
    + +
    +
    +
    +
    +
    + + {{!-- Sheet Tab Navigation --}} + + + {{!-- Sheet Body --}} +
    + + {{!-- Combat Tab --}} +
    +
    + +
    + + + + + + + + +
    + +

    Stun

    + + + +  - + +
    +
      +
    • + Current + + Threshold + +
    • +
    +
    +
    + +
    + + + + + + + + + + +
    + +
    + + + + + + + {{#if data.secondary.is3Dcombat}} + + + + + + + + + + {{/if}} + +
    + +
    + {{#each data.arcs as |arc idx|}} + {{#if arc.is3D}} + {{#if @root.data.secondary.is3Dcombat}} + {{> systems/fvtt-pegasus-rpg/templates/partial-vehicle-arc.html idx=idx arc=arc}} + {{/if}} + {{else}} + {{> systems/fvtt-pegasus-rpg/templates/partial-vehicle-arc.html idx=idx arc=arc}} + {{/if}} + {{/each}} +
    + +
      +
    • + +

      +
      + + + + + + + + + + + + +
    • + + {{#each effects as |effect key|}} +
    • + + {{effect.name}} + {{effect.system.effectlevel}} + {{upperFirst effect.system.type}} + {{upperFirst effect.datsystema.genre}} + {{upper effect.system.stataffected}} +
       
      +
      + +
      +
    • + {{/each}} +
    +
    +
    + + {{!-- Other Tab --}} +
    + +
      +
    • + +

      +
      + + + + + + + + + +
    • + {{#each specs as |spec key|}} +
    • + + {{spec.name}} + {{upper spec.system.statistic}} + {{spec.system.dice}} + {{#if spec.system.ispowergroup}} + {{#if spec.system.powersactivated}} + Deactivate + {{else}} + Activate + {{/if}} + {{else}} +  -  + {{/if}} +
       
      +
      + +
      +
    • + {{/each}} +
    +
    + + + + + + + {{!-- Cargo Tab --}} +
    + +
    +

    Encumbrance

    + Current : {{encCurrent}} + Capacity : {{encCapacity}} + Hindrance : {{encHindrance}} +
    + +
      +
    • + +

      +
      + + + + + + + + + +
       
      +
      + +
      +
    • + {{#each moneys as |money key|}} +
    • + + {{money.name}} + + + + + + + + {{#if money.system.idrDice}} + {{money.system.idrDice}} + {{else}} +  -  + {{/if}} + + +
       
      +
      + +
      +
    • + {{/each}} +
    + +
      +
    • + +

      +
      + + + + + + + + + + + + + + + + + + + + + + + + +
       
      +
      + +
      +
    • + {{#each weapons as |weapon key|}} +
    • + + {{weapon.name}} + + + + + + {{#if (gt weapon.system.ammomax 0)}} + + {{else}} + + + {{/if}} + + + {{#if (count weapon.system.effects)}} + {{#if weapon.system.activated}} + Deactivate + {{else}} + Activate + {{/if}} + {{else}} +  -  + {{/if}} + + + + + {{#if weapon.system.idrDice}} + {{weapon.system.idrDice}} + {{else}} +  -  + {{/if}} + + +
       
      + +
    • + {{/each}} +
    + +
      +
    • + +

      +
      + + + + + + + + + + + + + + + + + + + +
       
      +
      + +
      +
    • + {{#each armors as |armor key|}} +
    • + + {{armor.name}} + {{upper armor.system.statistic}} + {{armor.system.resistanceDice}} + {{armor.system.locationprotected}} + + + {{#if (count armor.system.effects)}} + {{#if armor.system.activated}} + Deactivate + {{else}} + Activate + {{/if}} + {{else}} +  -  + {{/if}} + + + {{armor.system.weight}} + + {{#if armor.system.idrDice}} + {{armor.system.idrDice}} + {{else}} +  -  + {{/if}} + + +
       
      + +
    • + {{/each}} +
    + +
      +
    • + +

      +
      + + + + + + + + + + + + +
       
      +
      + +
      +
    • + {{#each shields as |shield key|}} +
    • + + {{shield.name}} + {{shield.system.levelDice}} + + + {{#if (count shield.system.effects)}} + {{#if shield.system.activated}} + Deactivate + {{else}} + Activate + {{/if}} + {{else}} +  -  + {{/if}} + + + {{shield.system.weight}} + + {{#if shield.system.idrDice}} + {{shield.system.idrDice}} + {{else}} +  -  + {{/if}} + +
       
      + +
    • + {{/each}} +
    + +
      +
    • + +

      +
      + + + + + + + + + + + + +
       
      +
      + +
      + +
    • + {{#each containersTree as |equip key|}} + {{> systems/fvtt-pegasus-rpg/templates/partial-actor-equipment.html equip=equip level=1}} +
        + {{#each equip.system.contents as |subgear key|}} + {{> systems/fvtt-pegasus-rpg/templates/partial-actor-equipment.html equip=subgear level=2}} + {{/each}} +
      + {{/each}} +
    + +
    + +
    + + {{!-- Details Tab --}} +
    +
    +
    +
      +
    • + + +
    • +
    • + + +
    • +
    • + + +
    • +
    • + + +
    • +
    • + + +
    • +
    +
    +
    +
      +
    • + + +
    • +
    • + + +
    • +
    • + + +
    • +
    • + + + +
      + +
      +
    • +
    • + + + +
      + +
      +
    • +
    +
    +
    + +
    +
      +
    • + +

      +
      + Status +
    • + {{#each virtues as |virtue key|}} +
    • + + {{virtue.name}} + + +
       
      + +
    • + {{/each}} +
    +
    + +
    +
      +
    • + +

      +
      + Status +
    • + {{#each vices as |vice key|}} +
    • + + {{vice.name}} + + + +
       
      + +
    • + {{/each}} +
    +
    + +
      +
    • + +

      +
      +
    • +
    • + + + +
    • +
    • + + + +
    • +
    + +

    Catchphrase :

    +
      +
    • + + +
    • +
    • + + +
    • +
    • + + +
    • +
    + +
      +
    • + +

      +
      +
    • +
    • + + +
    • +
    • + + +
    • +
    • + + +
    • +
    • + + +
    • +
    • + + +
    • +
    • + + +
    • +
    • + + +
    • +
    • + + +
    • +
    + +
    +

    Background :

    +
    + {{editor data.biodata.description target="system.biodata.description" button=true owner=owner + editable=editable}} +
    +
    +

    Notes :

    +
    + {{editor data.biodata.notes target="system.biodata.notes" button=true owner=owner editable=editable}} +
    +
    + +
    + +
    +
    \ No newline at end of file