diff --git a/fonts/middlesaxonytext.ttf b/fonts/middlesaxonytext.ttf new file mode 100755 index 0000000..ceb8483 Binary files /dev/null and b/fonts/middlesaxonytext.ttf differ diff --git a/fonts/zag_bold.otf b/fonts/zag_bold.otf new file mode 100755 index 0000000..4088867 Binary files /dev/null and b/fonts/zag_bold.otf differ diff --git a/fonts/zag_regular.otf b/fonts/zag_regular.otf new file mode 100755 index 0000000..0574594 Binary files /dev/null and b/fonts/zag_regular.otf differ diff --git a/images/dice/cancel_icon.webp b/images/dice/cancel_icon.webp new file mode 100644 index 0000000..186c23f Binary files /dev/null and b/images/dice/cancel_icon.webp differ diff --git a/images/dice/d10-grey.svg b/images/dice/d10-grey.svg new file mode 100644 index 0000000..0777f00 --- /dev/null +++ b/images/dice/d10-grey.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/images/dice/d10black.svg b/images/dice/d10black.svg new file mode 100644 index 0000000..048209d --- /dev/null +++ b/images/dice/d10black.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/images/dice/d6_1.png b/images/dice/d6_1.png new file mode 100644 index 0000000..ff08092 Binary files /dev/null and b/images/dice/d6_1.png differ diff --git a/images/dice/d6_2.png b/images/dice/d6_2.png new file mode 100644 index 0000000..69c2955 Binary files /dev/null and b/images/dice/d6_2.png differ diff --git a/images/dice/d6_3.png b/images/dice/d6_3.png new file mode 100644 index 0000000..66a0fa6 Binary files /dev/null and b/images/dice/d6_3.png differ diff --git a/images/dice/d6_4.png b/images/dice/d6_4.png new file mode 100644 index 0000000..58a8dc9 Binary files /dev/null and b/images/dice/d6_4.png differ diff --git a/images/dice/d6_5.png b/images/dice/d6_5.png new file mode 100644 index 0000000..d2f7543 Binary files /dev/null and b/images/dice/d6_5.png differ diff --git a/images/dice/d6_6.png b/images/dice/d6_6.png new file mode 100644 index 0000000..caaf47e Binary files /dev/null and b/images/dice/d6_6.png differ diff --git a/images/dice/perspective-dice-five.webp b/images/dice/perspective-dice-five.webp new file mode 100644 index 0000000..8e26f2e Binary files /dev/null and b/images/dice/perspective-dice-five.webp differ diff --git a/images/icons/.directory b/images/icons/.directory new file mode 100644 index 0000000..b68f2ff --- /dev/null +++ b/images/icons/.directory @@ -0,0 +1,6 @@ +[Dolphin] +SortRole=creationtime +Timestamp=2021,4,13,9,23,48.267 +Version=4 +ViewMode=1 +VisibleRoles=Details_text,Details_size,Details_modificationtime,Details_creationtime,CustomizedDetails diff --git a/images/icons/Language.webp b/images/icons/Language.webp new file mode 100644 index 0000000..be23cfb Binary files /dev/null and b/images/icons/Language.webp differ diff --git a/images/icons/Mist_Items_And_Services.webp b/images/icons/Mist_Items_And_Services.webp new file mode 100644 index 0000000..13b130c Binary files /dev/null and b/images/icons/Mist_Items_And_Services.webp differ diff --git a/images/icons/Outfit.webp b/images/icons/Outfit.webp new file mode 100644 index 0000000..5fcce0f Binary files /dev/null and b/images/icons/Outfit.webp differ diff --git a/images/icons/Perks.webp b/images/icons/Perks.webp new file mode 100644 index 0000000..d219429 Binary files /dev/null and b/images/icons/Perks.webp differ diff --git a/images/icons/Races.webp b/images/icons/Races.webp new file mode 100644 index 0000000..c7cca36 Binary files /dev/null and b/images/icons/Races.webp differ diff --git a/images/icons/Research.webp b/images/icons/Research.webp new file mode 100644 index 0000000..4da4c67 Binary files /dev/null and b/images/icons/Research.webp differ diff --git a/images/icons/Skills.webp b/images/icons/Skills.webp new file mode 100644 index 0000000..00c840d Binary files /dev/null and b/images/icons/Skills.webp differ diff --git a/images/icons/Trade_Goods.webp b/images/icons/Trade_Goods.webp new file mode 100644 index 0000000..3af15e9 Binary files /dev/null and b/images/icons/Trade_Goods.webp differ diff --git a/images/icons/Traits.webp b/images/icons/Traits.webp new file mode 100644 index 0000000..8844e03 Binary files /dev/null and b/images/icons/Traits.webp differ diff --git a/images/icons/Utility.webp b/images/icons/Utility.webp new file mode 100644 index 0000000..ba186ed Binary files /dev/null and b/images/icons/Utility.webp differ diff --git a/images/icons/Weapon_General.webp b/images/icons/Weapon_General.webp new file mode 100644 index 0000000..4139871 Binary files /dev/null and b/images/icons/Weapon_General.webp differ diff --git a/images/icons/icon_advantage.webp b/images/icons/icon_advantage.webp new file mode 100644 index 0000000..58e8170 Binary files /dev/null and b/images/icons/icon_advantage.webp differ diff --git a/images/icons/icon_armor.webp b/images/icons/icon_armor.webp new file mode 100644 index 0000000..054ecaf Binary files /dev/null and b/images/icons/icon_armor.webp differ diff --git a/images/icons/icon_art.webp b/images/icons/icon_art.webp new file mode 100644 index 0000000..4f31d3c Binary files /dev/null and b/images/icons/icon_art.webp differ diff --git a/images/icons/icon_disadvantage.webp b/images/icons/icon_disadvantage.webp new file mode 100644 index 0000000..2f59f53 Binary files /dev/null and b/images/icons/icon_disadvantage.webp differ diff --git a/images/icons/icon_equipment.webp b/images/icons/icon_equipment.webp new file mode 100644 index 0000000..df10619 Binary files /dev/null and b/images/icons/icon_equipment.webp differ diff --git a/images/icons/icon_skill.webp b/images/icons/icon_skill.webp new file mode 100644 index 0000000..3bb4293 Binary files /dev/null and b/images/icons/icon_skill.webp differ diff --git a/images/icons/icon_skills.webp b/images/icons/icon_skills.webp new file mode 100644 index 0000000..00c840d Binary files /dev/null and b/images/icons/icon_skills.webp differ diff --git a/images/icons/icon_style.webp b/images/icons/icon_style.webp new file mode 100644 index 0000000..2ed656b Binary files /dev/null and b/images/icons/icon_style.webp differ diff --git a/images/icons/icon_technique.webp b/images/icons/icon_technique.webp new file mode 100644 index 0000000..2856d2b Binary files /dev/null and b/images/icons/icon_technique.webp differ diff --git a/images/icons/icon_weapon.webp b/images/icons/icon_weapon.webp new file mode 100644 index 0000000..d7f5d09 Binary files /dev/null and b/images/icons/icon_weapon.webp differ diff --git a/images/icons/image_corporation.webp b/images/icons/image_corporation.webp new file mode 100644 index 0000000..6399faa Binary files /dev/null and b/images/icons/image_corporation.webp differ diff --git a/images/icons/locked.svg b/images/icons/locked.svg new file mode 100644 index 0000000..6033b6c --- /dev/null +++ b/images/icons/locked.svg @@ -0,0 +1,60 @@ + + + + + + image/svg+xml + + + + + + + + + diff --git a/images/icons/unlocked.svg b/images/icons/unlocked.svg new file mode 100644 index 0000000..f86c359 --- /dev/null +++ b/images/icons/unlocked.svg @@ -0,0 +1,64 @@ + + + + + + image/svg+xml + + + + + + + + + diff --git a/images/ui/sheet_background.webp b/images/ui/sheet_background.webp new file mode 100644 index 0000000..6e10040 Binary files /dev/null and b/images/ui/sheet_background.webp differ diff --git a/images/ui/wotg_logo_01.webp b/images/ui/wotg_logo_01.webp new file mode 100644 index 0000000..381194c Binary files /dev/null and b/images/ui/wotg_logo_01.webp differ diff --git a/lang/en.json b/lang/en.json new file mode 100644 index 0000000..077404a --- /dev/null +++ b/lang/en.json @@ -0,0 +1,3 @@ +{ + +} \ No newline at end of file diff --git a/modules/pegasus-actor-sheet.js b/modules/pegasus-actor-sheet.js new file mode 100644 index 0000000..46acc9b --- /dev/null +++ b/modules/pegasus-actor-sheet.js @@ -0,0 +1,193 @@ +/** + * Extend the basic ActorSheet with some very simple modifications + * @extends {ActorSheet} + */ + +import { PegasusUtility } from "./pegasus-utility.js"; +import { PegasusItemSheet } from "./pegasus-item-sheet.js"; + +/* -------------------------------------------- */ +export class PegasusActorSheet extends ActorSheet { + + /** @override */ + static get defaultOptions() { + + return mergeObject(super.defaultOptions, { + classes: ["fvtt-pegasus-rpg", "sheet", "actor"], + template: "systems/fvtt-pegasus-rpg/templates/actor-sheet.html", + width: 640, + height: 720, + tabs: [{ navSelector: ".sheet-tabs", contentSelector: ".sheet-body", initial: "stats" }], + dragDrop: [{ dragSelector: ".item-list .item", dropSelector: null }], + editScore: false + }); + } + + /* -------------------------------------------- */ + async getData() { + const objectData = PegasusUtility.data(this.object); + + let actorData = duplicate(PegasusUtility.templateData(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, + effects: this.object.effects.map(e => foundry.utils.deepClone(e.data)), + limited: this.object.limited, + specs: this.actor.getSpecs( ), + weapons: this.actor.checkAndPrepareWeapons( duplicate(this.actor.getWeapons()) ), + armors: duplicate(this.actor.getArmors()), + shields: duplicate(this.actor.getShields()), + equipments: duplicate(this.actor.getEquipments()), + perks: duplicate(this.actor.getPerks()), + powers: duplicate(this.actor.getPowers()), + subActors: duplicate(this.actor.getSubActors()), + options: this.options, + owner: this.document.isOwner, + editScore: this.options.editScore, + isGM: game.user.isGM + } + this.formData = formData; + + console.log("PC : ", formData, this.object); + return formData; + } + + /* -------------------------------------------- */ + /** @override */ + activateListeners(html) { + super.activateListeners(html); + + // Everything below here is only needed if the sheet is editable + if (!this.options.editable) return; + + // 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('.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('.equipement-moins').click(event => { + const li = $(event.currentTarget).parents(".item"); + this.actor.decrementeQuantite( li.data("item-id") ); + } ); + html.find('.equipement-plus').click(event => { + const li = $(event.currentTarget).parents(".item"); + this.actor.incrementeQuantite( li.data("item-id") ); + } ); + + html.find('.skill-roll').click((event) => { + const li = $(event.currentTarget).parents(".item"); + const skillId = li.data("item-id"); + this.actor.rollSkill(skillId); + }); + html.find('.technique-roll').click((event) => { + const li = $(event.currentTarget).parents(".item"); + const techId = li.data("item-id"); + this.actor.rollTechnique(techId); + }); + html.find('.weapon-roll').click((event) => { + const li = $(event.currentTarget).parents(".item"); + const weaponId = li.data("item-id"); + this.actor.rollWeapon(weaponId); + }); + html.find('.river-flush').click((event) => { + const diceIndex = $(event.currentTarget).data("dice-index"); + this.actor.flushDice(diceIndex); + }); + html.find('.river-use').click((event) => { + const diceIndex = $(event.currentTarget).data("dice-index"); + this.actor.addDice(diceIndex); + }); + + html.find('.weapon-label a').click((event) => { + const li = $(event.currentTarget).parents(".item"); + const armeId = li.data("item-id"); + const statId = li.data("stat-id"); + this.actor.rollWeapon(armeId, statId); + }); + 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('.weapon-damage-critical').click((event) => { + const li = $(event.currentTarget).parents(".item"); + const weapon = this.actor.getOwnedItem(li.data("item-id")); + this.actor.rollDamage(weapon, 'criticaldamage'); + }); + + 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); + }); + + } + + /* -------------------------------------------- */ + /** @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 _onDrop(event) { + let data = event.dataTransfer.getData('text/plain'); + if (data) { + let dataItem = JSON.parse( data); + let npc = game.actors.get( dataItem.id); + if ( npc ) { + this.actor.addSubActor( dataItem.id); + return; + } + } + super._onDrop(event); + } + + /* -------------------------------------------- */ + /** @override */ + _updateObject(event, formData) { + // Update the Actor + return this.object.update(formData); + } +} diff --git a/modules/pegasus-actor.js b/modules/pegasus-actor.js new file mode 100644 index 0000000..cd45e08 --- /dev/null +++ b/modules/pegasus-actor.js @@ -0,0 +1,512 @@ +/* -------------------------------------------- */ +import { PegasusUtility } from "./pegasus-utility.js"; +import { PegasusRollDialog } from "./pegasus-roll-dialog.js"; + +/* -------------------------------------------- */ +const coverBonusTable = { "nocover": 0, "lightcover": 2, "heavycover": 4, "entrenchedcover": 6}; + +/* -------------------------------------------- */ +/* -------------------------------------------- */ +/** + * Extend the base Actor entity by defining a custom roll data structure which is ideal for the Simple system. + * @extends {Actor} + */ +export class PegasusActor extends Actor { + + /* -------------------------------------------- */ + /** + * Override the create() function to provide additional SoS functionality. + * + * This overrided create() function adds initial items + * Namely: Basic skills, money, + * + * @param {Object} data Barebones actor data which this function adds onto. + * @param {Object} options (Unused) Additional options which customize the creation workflow. + * + */ + + static async create(data, options) { + + // Case of compendium global import + if (data instanceof Array) { + return super.create(data, options); + } + // If the created actor has items (only applicable to duplicated actors) bypass the new actor creation logic + if (data.items) { + let actor = super.create(data, options); + return actor; + } + + if ( data.type == 'character') { + const skills = await PegasusUtility.loadCompendium("fvtt-weapons-of-the-gods.skills"); + data.items = skills.map(i => i.toObject()); + } + if ( data.type == 'npc') { + } + + return super.create(data, options); + } + + /* -------------------------------------------- */ + prepareBaseData() { + } + + /* -------------------------------------------- */ + async prepareData() { + + super.prepareData(); + } + + /* -------------------------------------------- */ + prepareDerivedData() { + + if (this.type == 'character') { + let h = 0; + let updates = []; + + for (let key in this.data.data.statistics) { + let attr = this.data.data.statistics[key]; + } + /*if ( h != this.data.data.secondary.health.max) { + this.data.data.secondary.health.max = h; + updates.push( {'data.secondary.health.max': h} ); + }*/ + if ( updates.length > 0 ) { + this.update( updates ); + } + } + + super.prepareDerivedData(); + } + + /* -------------------------------------------- */ + _preUpdate(changed, options, user) { + + super._preUpdate(changed, options, user); + } + + /* -------------------------------------------- */ + getPerks() { + let comp = this.data.items.filter( item => item.type == 'perk'); + return comp; + } + /* -------------------------------------------- */ + getPowers() { + let comp = this.data.items.filter( item => item.type == 'power'); + return comp; + } + /* -------------------------------------------- */ + getArmors() { + let comp = this.data.items.filter( item => item.type == 'armor'); + return comp; + } + /* -------------------------------------------- */ + getShields() { + let comp = this.data.items.filter( item => item.type == 'shield'); + return comp; + } + + /* -------------------------------------------- */ + checkAndPrepareWeapon(item) { + let types=[]; + let specs=[]; + let stats=[]; + item.data.specs = specs; + item.data.stats = stats; + item.data.typeText = types.join('/'); + } + + /* -------------------------------------------- */ + checkAndPrepareWeapons(weapons) { + for ( let item of weapons) { + this.checkAndPrepareWeapon(item); + } + return weapons; + } + + /* -------------------------------------------- */ + getWeapons() { + let comp = this.data.items.filter( item => item.type == 'weapon' ); + return comp; + } + + /* -------------------------------------------- */ + getSpecs() { + let comp = this.data.items.filter( item => item.type == 'specialisation'); + return comp; + } + + /* -------------------------------------------- */ + async equipItem(itemId ) { + let item = this.data.items.find( item => item.id == itemId ); + if (item && item.data.data) { + let update = { _id: item.id, "data.equipped": !item.data.data.equipped }; + await this.updateEmbeddedDocuments('Item', [update]); // Updates one EmbeddedEntity + } + } + + /* -------------------------------------------- */ + compareName( a, b) { + if ( a.name < b.name ) { + return -1; + } + if ( a.name > b.name ) { + return 1; + } + return 0; + } + + /* ------------------------------------------- */ + getEquipments() { + return this.data.items.filter( item => item.type == 'shield' || item.type == 'armor' || item.type == "weapon" || item.type == "equipment"); + } + + /* -------------------------------------------- */ + getActiveEffects(matching = it => true) { + let array = Array.from(this.getEmbeddedCollection("ActiveEffect").values()); + return Array.from(this.getEmbeddedCollection("ActiveEffect").values()).filter(it => matching(it)); + } + /* -------------------------------------------- */ + getEffectByLabel(label) { + return this.getActiveEffects().find(it => it.data.label == label); + } + /* -------------------------------------------- */ + getEffectById(id) { + return this.getActiveEffects().find(it => it.id == id); + } + + /* -------------------------------------------- */ + getAttribute( attrKey ) { + return this.data.data.attributes[attrKey]; + } + /* -------------------------------------------- */ + + async equipGear( equipmentId ) { + let item = this.data.items.find( item => item.id == equipmentId ); + if (item && item.data.data) { + let update = { _id: item.id, "data.equipped": !item.data.data.equipped }; + await this.updateEmbeddedDocuments('Item', [update]); // Updates one EmbeddedEntity + } + } + /* -------------------------------------------- */ + getInitiativeScore( ) { + if ( this.type == 'character') { + // TODO + } + return 0.0; + } + + /* -------------------------------------------- */ + getArmorModifier( ) { + let armors = this.getArmors(); + let modifier = 0; + for (let armor of armors) { + if (armor.data.data.equipped) { + if (armor.data.data.type == 'light') modifier += 5; + if (armor.data.data.type == 'medium') modifier += 10; + if (armor.data.data.type == 'heavy') modifier += 15; + } + } + return modifier; + } + + /* -------------------------------------------- */ + async applyDamageLoss( damage) { + let chatData = { + user: game.user.id, + alias : this.name, + rollMode: game.settings.get("core", "rollMode"), + whisper: [game.user.id].concat( ChatMessage.getWhisperRecipients('GM') ), + }; + //console.log("Apply damage chat", chatData ); + + if (damage > 0 ) { + let health = duplicate(this.data.data.secondary.health); + health.value -= damage; + if (health.value < 0 ) health.value = 0; + this.update( { "data.secondary.health.value": health.value}); + chatData.content = `${this.name} looses ${damage} health. New health value is : ${health.value}` ; + } else { + chatData.content = `No health loss for ${this.name} !`; + } + await ChatMessage.create( chatData ); + } + + + /* -------------------------------------------- */ + processNoDefense( attackRollData) { + let defenseRollData = { + mode : "nodefense", + finalScore: 0, + defenderName: this.name, + attackerName: attackRollData.alias, + armorModifier: this.getArmorModifier(), + actorId: this.id, + alias: this.name, + result: 0, + + } + this.syncRoll( defenseRollData ); + this.processDefenseResult(defenseRollData, attackRollData); + } + + /* -------------------------------------------- */ + async processApplyDamage(defenseRollData, attackRollData) { // Processed by the defender actor + if ( attackRollData && attackRollData ) { + let result = attackRollData.finalScore; + defenseRollData.damageDices = WotGUtility.getDamageDice( result ); + defenseRollData.damageRoll = await this.rollDamage(defenseRollData); + chatData.damages = true; + chatData.damageDices = defenseRollData.damageDices; + + WotGUtility.createChatWithRollMode( this.name, { + content: await renderTemplate(`systems/fvtt-weapons-of-the-gods/templates/chat-opposed-damages.html`, chatData) + }); + } + } + + /* -------------------------------------------- */ + async processDefenseResult(defenseRollData, attackRollData) { // Processed by the defenser + + if ( defenseRollData && attackRollData) { + + let result = attackRollData.finalScore - defenseRollData.finalScore; + defenseRollData.defenderName = this.name, + defenseRollData.attackerName = attackRollData.alias + defenseRollData.result= result + defenseRollData.damages = false + defenseRollData.damageDices = 0; + + if ( result > 0 ) { + defenseRollData.damageDices = WotGUtility.getDamageDice( result ); + defenseRollData.damageRoll = await this.rollDamage(defenseRollData, attackRollData); + defenseRollData.damages = true; + defenseRollData.finalDamage = defenseRollData.damageRoll.total; + WotGUtility.updateRollData( defenseRollData); + console.log("DAMAGE ROLL OBJECT", defenseRollData); + WotGUtility.createChatWithRollMode( this.name, { + content: await renderTemplate(`systems/fvtt-weapons-of-the-gods/templates/chat-opposed-damage.html`, defenseRollData) + }); + } else { + WotGUtility.updateRollData( defenseRollData ); + WotGUtility.createChatWithRollMode( this.name, { + content: await renderTemplate(`systems/fvtt-weapons-of-the-gods/templates/chat-opposed-fail.html`, defenseRollData) + }); + } + } + } + + /* -------------------------------------------- */ + async rollDamage( defenseRollData, attackRollData ) { + let weaponDamage = 0; + if (attackRollData.weapon?.data?.damage) { + weaponDamage = Number(attackRollData.weapon.data.damage); + } + let formula = defenseRollData.damageDices+"d10+"+defenseRollData.armorModifier + "+" + weaponDamage; + console.log("ROLL : ", formula); + let myRoll = new Roll(formula).roll( { async: false} ); + await WotGUtility.showDiceSoNice(myRoll, game.settings.get("core", "rollMode") ); + return myRoll; + } + + /* -------------------------------------------- */ + getSubActors() { + let subActors = []; + for (let id of this.data.data.subactors) { + subActors.push(duplicate(game.actors.get(id))); + } + return subActors; + } + /* -------------------------------------------- */ + async addSubActor( subActorId) { + let subActors = duplicate( this.data.data.subactors); + subActors.push( subActorId); + await this.update( { 'data.subactors': subActors } ); + } + /* -------------------------------------------- */ + async delSubActor( subActorId) { + let newArray = []; + for (let id of this.data.data.subactors) { + if ( id != subActorId) { + newArray.push( id); + } + } + await this.update( { 'data.subactors': newArray } ); + } + + /* -------------------------------------------- */ + setDefenseMode( rollData ) { + console.log("DEFENSE MODE IS SET FOR", this.name); + this.data.defenseRollData = rollData; + this.data.defenseDefenderId = rollData.defenderId; + this.data.defenseAttackerId = rollData.attackerId; + } + /* -------------------------------------------- */ + clearDefenseMode( ) { + this.data.defenseDefenderId = undefined; + this.data.defenseAttackerId = undefined; + this.data.defenseRollData = undefined; + } + + /* -------------------------------------------- */ + syncRoll( rollData ) { + let linkedRollId = WotGUtility.getDefenseState(this.id); + if ( linkedRollId) { + rollData.linkedRollId = linkedRollId; + } + rollData.rollId = randomID(16); + this.lastRollId = rollData.rollId; + WotGUtility.saveRollData( rollData ); + } + + /* -------------------------------------------- */ + async rollSkill( skillId ) { + let skill = this.data.items.find( item => item.type == 'skill' && item.id == skillId); + if (skill) { + let rollData = { + mode: "skill", + alias: this.name, + actorImg: this.img, + actorId: this.id, + img: skill.img, + rollMode: game.settings.get("core", "rollMode"), + armorModifier: this.getArmorModifier(), + title: `Skill ${skill.name} `, + skill: duplicate(skill), + skillAttr: this.getAttribute( skill.data.data.attribute ), + optionsNegative: WotGUtility.getNegativeModifiers(), + optionsPositive: WotGUtility.getPositiveModifiers(), + negativeModifier: 0, + positiveModifier: 0, + specialtiesBonus: 0, + } + + this.syncRoll( rollData); + + let rollDialog = await WotGRollDialog.create( this, rollData); + console.log(rollDialog); + rollDialog.render( true ); + } else { + ui.notifications.warn("Skill not found !"); + } + } + + /* -------------------------------------------- */ + applyTechniqueCost( techId) { + let tech = duplicate(this.data.items.find( item => item.id == techId)); + if (tech ) { + let attr = this.getAttributeFromChiName( tech.data.chicolor); + let chiscore = attr.chiscore - tech.data.chicost; + chiscore = (chiscore < 0) ? 0 : chiscore; + this.update( { [`data.attributes.${attr.key}.chiscore`]: chiscore } ); + } + } + + /* -------------------------------------------- */ + updateWithTarget( rollData) { + let objectDefender + let target = WotGUtility.getTarget(); + if ( !target) { + ui.notifications.warn("You are using a Weapon without a Target."); + } else { + let defenderActor = game.actors.get(target.data.actorId); + objectDefender = WotGUtility.data(defenderActor); + objectDefender = mergeObject(objectDefender, target.data.actorData); + rollData.defender = objectDefender; + rollData.attackerId = this.id; + rollData.defenderId = objectDefender._id; + + console.log("ROLLDATA DEFENDER !!!", rollData); + } + } + + /* -------------------------------------------- */ + async rollTechnique( techId ) { + let technique = this.data.items.find( item => item.type == 'technique' && item.id == techId); + if (technique) { + let usedChi = this.getAttributeFromChiName( technique.data.data.chicolor); + if ( usedChi.chiscore < 0 || technique.data.data.chicost > usedChi.chiscore) { + ui.notifications.warn(`Not enough ${usedChi.chi} to use Technique ${technique.name}`); + return; + } + + let chi = undefined; + let skill = this.data.items.find( item => item.type == 'skill' && item.name == technique.data.data.skillchi); + if ( !skill) { + chi = this.getAttributeFromChiName( technique.data.data.skillchi); + } + let rollData = { + mode: "technique", + alias: this.name, + armorModifier: this.getArmorModifier(), + actorImg: this.img, + actorId: this.id, + img: technique.img, + rollMode: game.settings.get("core", "rollMode"), + title: `Technique ${technique.name} `, + technique: duplicate(technique), + optionsNegative: WotGUtility.getNegativeModifiers(), + optionsPositive: WotGUtility.getPositiveModifiers(), + negativeModifier: 0, + positiveModifier: 0, + specialtiesBonus: 0 + } + if ( skill) { + rollData.skill = duplicate(skill); + rollData.attr = this.getAttribute( skill.data.data.attribute ); + } else { + rollData.attr = chi; + } + + this.updateWithTarget(rollData); + + this.syncRoll( rollData); + + let rollDialog = await WotGRollDialog.create( this, rollData); + console.log(rollDialog); + rollDialog.render( true ); + } else { + ui.notifications.warn("Technique not found !"); + } + } + + /* -------------------------------------------- */ + async rollWeapon( weaponId ) { + let weapon = this.data.items.find( item => item.id == weaponId); + console.log("WEAPON :", weaponId, weapon ); + + if ( weapon ) { + + weapon = duplicate(weapon); + this.checkAndPrepareWeapon( weapon ); + let rollData = { + mode: 'weapon', + actorType: this.type, + alias: this.name, + actorId: this.id, + img: weapon.img, + rollMode: game.settings.get("core", "rollMode"), + armorModifier: this.getArmorModifier(), + title: "Attack : " + weapon.name, + weapon: weapon, + skillKey : 0, + optionsNegative: WotGUtility.getNegativeModifiers(), + optionsPositive: WotGUtility.getPositiveModifiers(), + negativeModifier: 0, + positiveModifier: 0, + specialtiesBonus: 0, + } + + this.updateWithTarget(rollData); + + this.syncRoll( rollData); + + let rollDialog = await WotGRollDialog.create( this, rollData); + console.log("WEAPON ROLL", rollData); + rollDialog.render( true ); + } else { + ui.notifications.warn("Weapon not found !", weaponId); + } + } + +} diff --git a/modules/pegasus-combat.js b/modules/pegasus-combat.js new file mode 100644 index 0000000..cc64c21 --- /dev/null +++ b/modules/pegasus-combat.js @@ -0,0 +1,20 @@ +import { PegasusUtility } from "./pegasus-utility.js"; + +/* -------------------------------------------- */ +export class PegasusCombat extends Combat { + + /* -------------------------------------------- */ + async rollInitiative(ids, formula = undefined, messageOptions = {} ) { + console.log("Initiative is requested !!!"); + + ids = typeof ids === "string" ? [ids] : ids; + const currentId = this.combatant._id; + for (let cId = 0; cId < ids.length; cId++) { + const c = this.getCombatant(ids[cId]); + let initBonus = c.actor ? c.actor.getInitiativeScore() : 0; + await this.updateEmbeddedEntity("Combatant", { _id: c._id, initiative: initBonus }); + } + + return this; + } +} diff --git a/modules/pegasus-item-sheet.js b/modules/pegasus-item-sheet.js new file mode 100644 index 0000000..4f83259 --- /dev/null +++ b/modules/pegasus-item-sheet.js @@ -0,0 +1,176 @@ +import { PegasusUtility } from "./pegasus-utility.js"; + +/** + * Extend the basic ItemSheet with some very simple modifications + * @extends {ItemSheet} + */ +export class PegasusItemSheet extends ItemSheet { + + /** @override */ + static get defaultOptions() { + + return mergeObject(super.defaultOptions, { + classes: ["fvtt-pegasus-rpg", "sheet", "item"], + template: "systems/fvtt-pegasus-rpg/templates/item-sheet.html", + dragDrop: [{dragSelector: null, dropSelector: null}], + width: 620, + height: 550 + //tabs: [{navSelector: ".sheet-tabs", contentSelector: ".sheet-body", initial: "description"}] + }); + } + + /* -------------------------------------------- */ + _getHeaderButtons() { + let buttons = super._getHeaderButtons(); + // Add "Post to chat" button + // We previously restricted this to GM and editable items only. If you ever find this comment because it broke something: eh, sorry! + buttons.unshift( + { + class: "post", + icon: "fas fa-comment", + onclick: ev => {} + }) + return buttons + } + + /* -------------------------------------------- */ + /** @override */ + setPosition(options={}) { + const position = super.setPosition(options); + const sheetBody = this.element.find(".sheet-body"); + const bodyHeight = position.height - 192; + sheetBody.css("height", bodyHeight); + if ( this.item.type.includes('weapon')) { + position.width = 640; + } + return position; + } + + /* -------------------------------------------- */ + async getData() { + const objectData = PegasusUtility.data(this.object); + + let itemData = foundry.utils.deepClone(PegasusUtility.templateData(this.object)); + let formData = { + title: this.title, + id: this.id, + type: objectData.type, + img: objectData.img, + name: objectData.name, + editable: this.isEditable, + cssClass: this.isEditable ? "editable" : "locked", + data: itemData, + limited: this.object.limited, + options: this.options, + owner: this.document.isOwner, + isGM: game.user.isGM + } + + this.options.editable = !(this.object.data.origin == "embeddedItem"); + console.log("ITEM DATA", formData, this); + return formData; + } + + + /* -------------------------------------------- */ + _getHeaderButtons() { + let buttons = super._getHeaderButtons(); + buttons.unshift({ + class: "post", + icon: "fas fa-comment", + onclick: ev => this.postItem() + }); + return buttons + } + + /* -------------------------------------------- */ + postItem() { + console.log(this.item); + let chatData = duplicate(PegasusUtility.data(this.item)); + if (this.actor) { + chatData.actor = { id: this.actor.id }; + } + // Don't post any image for the item (which would leave a large gap) if the default image is used + if (chatData.img.includes("/blank.png")) { + chatData.img = null; + } + // JSON object for easy creation + chatData.jsondata = JSON.stringify( + { + compendium: "postedItem", + payload: chatData, + }); + + renderTemplate('systems/fvtt-pegasus-rpg/templates/post-item.html', chatData).then(html => { + let chatOptions = WotGUtility.chatDataSetup(html); + ChatMessage.create(chatOptions) + }); + } + + /* -------------------------------------------- */ + /** @override */ + activateListeners(html) { + super.activateListeners(html); + + // Everything below here is only needed if the sheet is editable + if (!this.options.editable) return; + + + // Update Inventory Item + html.find('.item-edit').click(ev => { + const li = $(ev.currentTarget).parents(".item"); + const item = this.object.options.actor.getOwnedItem(li.data("item-id")); + item.sheet.render(true); + }); + + // Update Inventory Item + html.find('.item-delete').click(ev => { + const li = $(ev.currentTarget).parents(".item"); + let itemId = li.data("item-id"); + let itemType = li.data("item-type"); + let array = duplicate(this.object.data.data[itemType]); + let newArray = array.filter( item => item._id != itemId); + if ( itemType == 'variations') { + this.object.update( {"data.variations": newArray} ); + } else if (itemType == "modifications") { + this.object.update( { "data.modifications": newArray} ); + } else { + this.object.update( { "data.traits": newArray} ); + } + }); + + html.find('.trait-name').click(ev => { + const li = $(ev.currentTarget).parents(".item"); + let itemId = li.data("item-id"); + this.manageTrait( itemId); + }); + html.find('.variation-name').click(ev => { + const li = $(ev.currentTarget).parents(".item"); + let itemId = li.data("item-id"); + this.manageVariation( itemId); + }); + html.find('.modification-name').click(ev => { + const li = $(ev.currentTarget).parents(".item"); + let itemId = li.data("item-id"); + this.manageModification( itemId); + }); + + } + + /* -------------------------------------------- */ + async _onDrop(event) { + + } + + /* -------------------------------------------- */ + get template() { + let type = this.item.type; + return `systems/fvtt-pegasus-rpg/templates/item-${type}-sheet.html`; + } + + /* -------------------------------------------- */ + /** @override */ + _updateObject(event, formData) { + return this.object.update(formData); + } +} diff --git a/modules/pegasus-item.js b/modules/pegasus-item.js new file mode 100644 index 0000000..5186c36 --- /dev/null +++ b/modules/pegasus-item.js @@ -0,0 +1,28 @@ +import { PegasusUtility } from "./pegasus-utility.js"; + +export const defaultItemImg = { + skill: "systems/fvtt-pegasus-rpg/images/icons/icon_skill.webp", + advantage: "systems/fvtt-pegasus-rpg/images/icons/icon_advantage.webp", + disadvantage: "systems/fvtt-pegasus-rpg/images/icons/icon_disadvantage.webp", + technique: "systems/fvtt-pegasus-rpg/images/icons/icon_technique.webp", + armor: "systems/fvtt-pegasus-rpg/images/icons/icon_armor.webp", + art: "systems/fvtt-pegasus-rpg/icons/images/icon_art.webp", + weapon: "systems/fvtt-pegasus-rpg/images/icons/icon_weapon.webp", + equipment: "systems/fvtt-pegasus-rpg/images/icons/icon_equipment.webp", + style: "systems/fvtt-pegasus-rpg/images/icons/icon_style.webp", +} + +/** + * Extend the basic ItemSheet with some very simple modifications + * @extends {ItemSheet} + */ +export class PegasusItem extends Item { + + constructor(data, context) { + if (!data.img) { + data.img = defaultItemImg[data.type]; + } + super(data, context); + } + +} diff --git a/modules/pegasus-main.js b/modules/pegasus-main.js new file mode 100644 index 0000000..4d28839 --- /dev/null +++ b/modules/pegasus-main.js @@ -0,0 +1,101 @@ +/** + * Pegasus system + * Author: Uberwald + * Software License: Prop + */ + +/* -------------------------------------------- */ + +/* -------------------------------------------- */ +// Import Modules +import { PegasusActor } from "./pegasus-actor.js"; +import { PegasusItemSheet } from "./pegasus-item-sheet.js"; +import { PegasusActorSheet } from "./pegasus-actor-sheet.js"; +import { PegasusNPCSheet } from "./pegasus-npc-sheet.js"; +import { PegasusUtility } from "./pegasus-utility.js"; +import { PegasusCombat } from "./pegasus-combat.js"; +import { PegasusItem } from "./pegasus-item.js"; + +/* -------------------------------------------- */ +/* Foundry VTT Initialization */ +/* -------------------------------------------- */ + +/************************************************************************************/ +Hooks.once("init", async function () { + console.log(`Initializing Pegasus RPG`); + + /* -------------------------------------------- */ + // preload handlebars templates + PegasusUtility.preloadHandlebarsTemplates(); + + /* -------------------------------------------- */ + // Set an initiative formula for the system + CONFIG.Combat.initiative = { + formula: "1d6", + decimals: 1 + }; + + /* -------------------------------------------- */ + game.socket.on("system.fvtt-pegasus-rpg", data => { + PegasusUtility.onSocketMesssage(data); + }); + + /* -------------------------------------------- */ + // Define custom Entity classes + CONFIG.Combat.documentClass = PegasusCombat; + CONFIG.Actor.documentClass = PegasusActor; + CONFIG.Item.documentClass = PegasusItem; + CONFIG.WOTG = { + } + + /* -------------------------------------------- */ + // Register sheet application classes + Actors.unregisterSheet("core", ActorSheet); + Actors.registerSheet("fvtt-pegasus", PegasusActorSheet, { types: ["character"], makeDefault: true }); + Actors.registerSheet("fvtt-pegasus", PegasusNPCSheet, { types: ["npc"], makeDefault: false }); + + Items.unregisterSheet("core", ItemSheet); + Items.registerSheet("fvtt-pegasus", PegasusItemSheet, { makeDefault: true }); + + PegasusUtility.init(); + +}); + +/* -------------------------------------------- */ +function welcomeMessage() { + ChatMessage.create({ + user: game.user.id, + whisper: [game.user.id], + content: `
Welcome !
+ ` }); +} + +/* -------------------------------------------- */ +/* Foundry VTT Initialization */ +/* -------------------------------------------- */ +Hooks.once("ready", function () { + + PegasusUtility.ready(); + // User warning + if (!game.user.isGM && game.user.character == undefined) { + ui.notifications.info("Warning ! No character linked to your user !"); + ChatMessage.create({ + content: "WARNING The player " + game.user.name + " is not linked to a character !", + user: game.user._id + }); + } + + welcomeMessage(); +}); + +/* -------------------------------------------- */ +/* Foundry VTT Initialization */ +/* -------------------------------------------- */ +Hooks.on("chatMessage", (html, content, msg) => { + if (content[0] == '/') { + let regExp = /(\S+)/g; + let commands = content.toLowerCase().match(regExp); + console.log(commands); + } + return true; +}); diff --git a/modules/pegasus-npc-sheet.js b/modules/pegasus-npc-sheet.js new file mode 100644 index 0000000..62ee5b7 --- /dev/null +++ b/modules/pegasus-npc-sheet.js @@ -0,0 +1,139 @@ +/** + * Extend the basic ActorSheet with some very simple modifications + * @extends {ActorSheet} + */ + +import { PegasusUtility } from "./pegasus-utility.js"; +import { PegasusItemSheet } from "./pegasus-item-sheet.js"; + +/* -------------------------------------------- */ +export class PegasusNPCSheet extends ActorSheet { + + /** @override */ + static get defaultOptions() { + + return mergeObject(super.defaultOptions, { + classes: ["pegasus-rpg", "sheet", "actor"], + template: "systems/fvtt-pegasus-rpg/templates/npc-sheet.html", + width: 640, + height: 720, + tabs: [{ navSelector: ".sheet-tabs", contentSelector: ".sheet-body", initial: "stats" }], + dragDrop: [{ dragSelector: ".item-list .item", dropSelector: null }], + editScore: false + }); + } + + /* -------------------------------------------- */ + async getData() { + const objectData = PegasusUtility.data(this.object); + + this.actor.prepareTraitsAttributes(); + let actorData = duplicate(PegasusUtility.templateData(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, + effects: this.object.effects.map(e => foundry.utils.deepClone(e.data)), + limited: this.object.limited, + equipments: this.actor.getEquipments(), + defenseBase: this.actor.getDefenseBase(), + bodyArmourBase: this.actor.getBodyArmour(), + headArmourBase: this.actor.getHeadArmour(), + weapons: this.actor.getWeapons(), + traits: this.actor.getTraits(), + optionsBase: FraggedKingdomUtility.createDirectOptionList(0, 20), + options: this.options, + owner: this.document.isOwner, + editScore: this.options.editScore, + isGM: game.user.isGM + } + this.formData = formData; + + console.log("NPC : ", formData, this.object); + return formData; + } + + /* -------------------------------------------- */ + /** @override */ + activateListeners(html) { + super.activateListeners(html); + + // Everything below here is only needed if the sheet is editable + if (!this.options.editable) return; + + // 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"); + FraggedKingdomUtility.confirmDelete(this, li); + }); + + html.find('.trait-link').click((event) => { + const itemId = $(event.currentTarget).data("item-id"); + const item = this.actor.getOwnedItem(itemId); + item.sheet.render(true); + }); + + html.find('.competence-label a').click((event) => { + const li = $(event.currentTarget).parents(".item"); + const competenceId = li.data("item-id"); + this.actor.rollSkill(competenceId); + }); + html.find('.weapon-label a').click((event) => { + const li = $(event.currentTarget).parents(".item"); + const armeId = li.data("item-id"); + const statId = li.data("stat-id"); + this.actor.rollWeapon(armeId, statId); + }); + html.find('.npc-fight-roll').click((event) => { + this.actor.rollNPCFight(); + }); + html.find('.npc-skill-roll').click((event) => { + this.actor.rollGenericSkill(); + }); + 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); + }); + + } + + /* -------------------------------------------- */ + /** @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; + } + + /* -------------------------------------------- */ + /** @override */ + _updateObject(event, formData) { + // Update the Actor + return this.object.update(formData); + } +} diff --git a/modules/pegasus-roll-dialog.js b/modules/pegasus-roll-dialog.js new file mode 100644 index 0000000..8811b27 --- /dev/null +++ b/modules/pegasus-roll-dialog.js @@ -0,0 +1,84 @@ +import { PegasusUtility } from "./pegasus-utility.js"; + +export class PegasusRollDialog extends Dialog { + + /* -------------------------------------------- */ + static async create(actor, rollData ) { + + let html + let options = { classes: ["WotGdialog"], width: 420, height: 320, 'z-index': 99999 }; + if ( rollData.mode == "skill") { + html = await renderTemplate('systems/fvtt-pegasus-rpg/templates/roll-dialog-skill.html', rollData); + options.height = 360; + } else if (rollData.mode == "chidamage") { + html = await renderTemplate('systems/fvtt-pegasus-rpg/templates/roll-dialog-damage-chi.html', rollData); + options.height = 380; + } else if (rollData.mode == "technique") { + html = await renderTemplate('systems/fvtt-pegasus-rpg/templates/roll-dialog-technique.html', rollData); + options.height = 380; + } else if (rollData.mode == "weapon") { + html = await renderTemplate('systems/fvtt-pegasus-rpg/templates/roll-dialog-weapon.html', rollData); + options.height = 460; + } else { + html = await renderTemplate('systems/fvtt-pegasus-rpg/templates/roll-dialog-skill.html', rollData); + } + return new PegasusRollDialog(actor, rollData, html, options ); + } + + /* -------------------------------------------- */ + constructor(actor, rollData, html, options, close = undefined) { + let conf = { + title: (rollData.mode == "skill") ? "Skill" : "Roll", + content: html, + buttons: { + roll: { + icon: '', + label: "Roll !", + callback: () => { this.roll() } + }, + cancel: { + icon: '', + label: "Cancel", + callback: () => { this.close() } + } }, + default: "roll", + close: close + } + + super(conf, options); + + this.actor = actor; + this.rollData = rollData; + } + + /* -------------------------------------------- */ + roll () { + PegasusUtility.rollWotG( this.rollData ) + } + + /* -------------------------------------------- */ + activateListeners(html) { + super.activateListeners(html); + + var dialog = this; + function onLoad() { + } + $(function () { onLoad(); }); + + html.find('#negativeModifier').change((event) => { + this.rollData.negativeModifier = Number(event.currentTarget.value); + }); + html.find('#positiveModifier').change((event) => { + this.rollData.positiveModifier = Number(event.currentTarget.value); + }); + html.find('#specialtiesBonus').change((event) => { + this.rollData.specialtiesBonus = Number(event.currentTarget.value); + }); + html.find('#selectedChi').change((event) => { + this.rollData.selectedChi = Number(event.currentTarget.value); + }); + html.find('#bonusMalus').change((event) => { + this.rollData.bonusMalus = Number(event.currentTarget.value); + }); + } +} \ No newline at end of file diff --git a/modules/pegasus-utility.js b/modules/pegasus-utility.js new file mode 100644 index 0000000..10e7399 --- /dev/null +++ b/modules/pegasus-utility.js @@ -0,0 +1,593 @@ +/* -------------------------------------------- */ + +/* -------------------------------------------- */ +export class PegasusUtility { + + + /* -------------------------------------------- */ + static async init() { + Hooks.on('renderChatLog', (log, html, data) => PegasusUtility.chatListeners(html)); + this.rollDataStore = {} + this.defenderStore = {} + } + /* -------------------------------------------- */ + static getSpecs( ) { + return this.specs; + } + + /* -------------------------------------------- */ + static async ready() { + const specs = await PegasusUtility.loadCompendium("fvtt-pegasus-rpg.specialisations"); + this.specs = specs.map(i => i.toObject()); + } + + /* -------------------------------------------- */ + static computeAttackDefense(defenseRollId) { + let defenseRollData = this.getRollData(defenseRollId ); + let attackRollData = this.getRollData(defenseRollData.linkedRollId); + let defender = game.actors.get( defenseRollData.actorId); + defender.processDefenseResult(defenseRollData, attackRollData); + } + + /* -------------------------------------------- */ + static applyDamage( defenseRollId) { + let defenseRollData = this.getRollData(defenseRollId ); + let defender = game.actors.get( defenseRollData.actorId); + defender.applyDamageLoss( defenseRollData.finalDamage) ; + } + + /* -------------------------------------------- */ + static applyNoDefense( actorId, attackRollId ) { + let attackRollData = this.getRollData(attackRollId ); + let defender = game.actors.get( actorId ); + defender.processNoDefense( attackRollData ) ; + } + + /* -------------------------------------------- */ + static reduceDamageWithChi( defenseRollId) { + let defenseRollData = this.getRollData(defenseRollId ); + let attackRollData = this.getRollData(defenseRollData.linkedRollId); + let defender = game.actors.get( defenseRollData.actorId); + defender.reduceDamageWithChi(defenseRollData, attackRollData); + } + + /* -------------------------------------------- */ + static async chatListeners(html) { + + html.on("click", '.apply-technique-cost', event => { + const rollId = $(event.currentTarget).data("roll-id"); + const actorId = $(event.currentTarget).data("actor-id"); + const techId = $(event.currentTarget).data("technique-id"); + let actor = game.actors.get( actorId); + actor.applyTechniqueCost(techId); + }); + + html.on("click", '.apply-defense-roll', event => { + const defenseRollId = $(event.currentTarget).data("roll-id"); + this.computeAttackDefense(defenseRollId); + }); + + html.on("click", '.apply-nodefense', event => { + const actorId = $(event.currentTarget).data("actor-id"); + const rollId = $(event.currentTarget).data("roll-id"); + this.applyNoDefense(actorId, rollId); + }); + + html.on("click", '.apply-damage', event => { + const rollId = $(event.currentTarget).data("roll-id"); + this.applyDamage(rollId); + }); + } + + /* -------------------------------------------- */ + static async preloadHandlebarsTemplates() { + + const templatePaths = [ + 'systems/fvtt-pegasus-rpg/templates/editor-notes-gm.html', + 'systems/fvtt-pegasus-rpg/templates/partial-options-statistics.html', + 'systems/fvtt-pegasus-rpg/templates/partial-options-level.html', + 'systems/fvtt-pegasus-rpg/templates/partial-options-equipment-types.html' + ] + return loadTemplates(templatePaths); + } + + /* -------------------------------------------- */ + static templateData(it) { + return PegasusUtility.data(it)?.data ?? {} + } + + /* -------------------------------------------- */ + static data(it) { + if (it instanceof Actor || it instanceof Item || it instanceof Combatant) { + return it.data; + } + return it; + } + /* -------------------------------------------- */ + static getChiList() { + let chi = []; + chi.push( { name: "Jade" }); + chi.push( { name: "Crimson" }); + chi.push( { name: "Gold" }); + chi.push( { name: "White" }); + chi.push( { name: "Silver" }); + return chi; + } + /* -------------------------------------------- */ + static getskillChiList( ) { + let skillsName = []; + let skills = this.getSkills(); + console.log("SKILLS", skills); + for (let skill of skills) { + skillsName.push( { name: skill.name }); + } + skillsName = skillsName.concat( this.getChiList() ); + return skillsName; + } + + /* -------------------------------------------- */ + static createDirectOptionList( min, max) { + let options = {}; + for(let i=min; i<=max; i++) { + options[`${i}`] = `${i}`; + } + return options; + } + + /* -------------------------------------------- */ + static buildListOptions(min, max) { + let options = "" + for (let i = min; i <= max; i++) { + options += `` + } + return options; + } + /* -------------------------------------------- */ + static getNegativeModifiers(min, max) { + let options = "" + options += `` + options += `` + options += `` + return options; + } + /* -------------------------------------------- */ + static getPositiveModifiers(min, max) { + let options = "" + options += `` + options += `` + options += `` + options += `` + options += `` + options += `` + options += `` + options += `` + options += `` + options += `` + return options; + } + + /* -------------------------------------------- */ + static getTarget() { + if (game.user.targets && game.user.targets.size == 1) { + for (let target of game.user.targets) { + return target; + } + } + return undefined; + } + + /* -------------------------------------------- */ + static getDefenseState( actorId) { + return this.defenderStore[actorId]; + } + + /* -------------------------------------------- */ + static async updateDefenseState( defenderId, rollId) { + this.defenderStore[defenderId] = rollId; + if ( game.user.character && game.user.character.id == defenderId ) { + let defender = game.actors.get( defenderId); + let chatData = { + user: game.user.id, + alias : defender.name, + rollMode: game.settings.get("core", "rollMode"), + whisper: [game.user.id].concat( ChatMessage.getWhisperRecipients('GM') ), + content: `
${defender.name} is under attack. He must roll a skill/weapon/technique to defend himself or suffer damages (button below). + u.id); + if (chatData.rollMode === "blindroll") chatData["blind"] = true; + else if (chatData.rollMode === "selfroll") chatData["whisper"] = [game.user]; + + if (forceWhisper) { // Final force ! + chatData["speaker"] = ChatMessage.getSpeaker(); + chatData["whisper"] = ChatMessage.getWhisperRecipients(forceWhisper); + } + + return chatData; + } + + /* -------------------------------------------- */ + static async loadCompendiumData(compendium) { + const pack = game.packs.get(compendium); + return await pack?.getDocuments() ?? []; + } + + /* -------------------------------------------- */ + static async loadCompendium(compendium, filter = item => true) { + let compendiumData = await this.loadCompendiumData(compendium); + //console.log("Compendium", compendiumData); + return compendiumData.filter(filter); + } + + /* -------------------------------------------- */ + static async showDiceSoNice(roll, rollMode) { + if (game.modules.get("dice-so-nice")?.active) { + if (game.dice3d) { + let whisper = null; + let blind = false; + rollMode = rollMode ?? game.settings.get("core", "rollMode"); + switch (rollMode) { + case "blindroll": //GM only + blind = true; + case "gmroll": //GM + rolling player + whisper = this.getUsers(user => user.isGM); + break; + case "roll": //everybody + whisper = this.getUsers(user => user.active); + break; + case "selfroll": + whisper = [game.user.id]; + break; + } + await game.dice3d.showForRoll(roll, game.user, true, whisper, blind); + } + } + } + + + /* -------------------------------------------- */ + static async rollWotG( rollData ) { + + if (rollData.mode == "chidamage" ) { + let defender = game.actors.get( rollData.actorId); + defender.rollChiDamage(rollData); + return; + } + + let nbDice = 0; + if ( rollData.mode == 'skill' || rollData.mode == 'technique') { + nbDice = rollData.skill?.data.level || 0; + } + if ( rollData.mode == 'weapon' ) { + rollData.skill = rollData.weapon.data.skills[rollData.skillKey]; + rollData.skillAttr = rollData.weapon.data.skills[rollData.skillKey].data.attr; + nbDice = rollData.skill?.data.level || 0; + } + if ( rollData.mode == 'technique') { + // Compute number of dice + if (rollData.attr ) { + rollData.skillAttr = rollData.attr; + } + if (rollData.chi ) { + rollData.skillAttr = rollData.chi; + nbDice = rollData.skillAttr.value || 0; + } + } + if ( rollData.skill && rollData.skillAttr.value >= rollData.skill.data.level) { + nbDice++; + } + if ( nbDice == 0) nbDice = 1; + nbDice += rollData.specialtiesBonus; + + // Build dice formula + let diceTab = []; + for(let i=0; i bestScore) bestScore = score; + sortedRoll[dice1.results[0].result] = nbFound; + } + + // Final score and keep data + rollData.nbDice = nbDice; + rollData.bestScore = bestScore; + rollData.diceResults = diceResults; + rollData.finalScore = bestScore + rollData.negativeModifier + rollData.positiveModifier; + console.log("ROLLLL!!!!", rollData); + + let actor = game.actors.get(rollData.actorId); + + this.createChatWithRollMode( rollData.alias, { + content: await renderTemplate(`systems/fvtt-weapons-of-the-gods/templates/chat-generic-result.html`, rollData) + }); + + if ( rollData.defender ) { + this.storeDefenseState( rollData ); + } + this.saveRollData( rollData ); + } + + /* -------------------------------------------- */ + static getDamageDice( result ) { + if ( result < 0) return 0; + return Math.floor(result/5) + 1; + } + + /* -------------------------------------------- */ + static removeDice( rollData, diceIndex) { + + let diceResults = rollData.diceResults; + diceResults.splice( diceIndex, 1); + rollData.diceResults = diceResults; + + rollData.nbDice = diceResults.length; + + this.updateRoll(rollData); + } + + /* -------------------------------------------- */ + static addDice(rollId, diceValue) { + + let rollData = this.getRollData( rollId ); + + let diceResults = rollData.diceResults; + let newResult = duplicate(diceResults[0]); + newResult.result = diceValue; + diceResults.push( newResult); + rollData.diceResults = diceResults; + + rollData.nbDice = diceResults.length; + + this.updateRoll(rollData); + } + + /* ------------------------- ------------------- */ + static async updateRoll( rollData) { + + let diceResults = rollData.diceResults; + let sortedRoll = []; + for (let i=0; i<10; i++) { + sortedRoll[i] = 0; + } + for (let dice of diceResults) { + sortedRoll[dice.result]++; + } + let index = 0; + let bestRoll = 0; + for (let i=0; i<10; i++) { + if ( sortedRoll[i] > bestRoll) { + bestRoll = sortedRoll[i]; + index = i; + } + } + let bestScore = (bestRoll * 10) + index; + rollData.bestScore = bestScore; + rollData.finalScore = bestScore + rollData.negativeModifier + rollData.positiveModifier; + + this.saveRollData(rollData ); + + this.createChatWithRollMode( rollData.alias, { + content: await renderTemplate(`systems/fvtt-weapons-of-the-gods/templates/chat-generic-result.html`, rollData) + }); + } + + /* ------------------------- ------------------- */ + static async rerollDice( actorId, diceIndex = -1 ) { + let actor = game.actors.get(actorId); + let rollData = actor.getRollData( ); + + if ( diceIndex == -1 ) { + rollData.hasWillpower = actor.decrementWillpower(); + rollData.roll = undefined; + } else { + let myRoll = new Roll("1d6").roll( { async: false} ); + await this.showDiceSoNice(myRoll, game.settings.get("core", "rollMode") ); + console.log("Result: ", myRoll); + + rollData.roll.dice[0].results[diceIndex].result = myRoll.total; // Patch + rollData.nbStrongHitUsed++; + } + this.rollFraggedKingdom( rollData ); + } + + /* -------------------------------------------- */ + static getUsers(filter) { + return game.users.filter(filter).map(user => user.data._id); + } + /* -------------------------------------------- */ + static getWhisperRecipients(rollMode, name) { + switch (rollMode) { + case "blindroll": return this.getUsers(user => user.isGM); + case "gmroll": return this.getWhisperRecipientsAndGMs(name); + case "selfroll": return [game.user.id]; + } + return undefined; + } + /* -------------------------------------------- */ + static getWhisperRecipientsAndGMs(name) { + let recep1 = ChatMessage.getWhisperRecipients(name) || []; + return recep1.concat(ChatMessage.getWhisperRecipients('GM')); + } + + /* -------------------------------------------- */ + static blindMessageToGM(chatOptions) { + let chatGM = duplicate(chatOptions); + chatGM.whisper = this.getUsers(user => user.isGM); + chatGM.content = "Blinde message of " + game.user.name + "
" + chatOptions.content; + console.log("blindMessageToGM", chatGM); + game.socket.emit("system.fvtt-weapons-of-the-gods", { msg: "msg_gm_chat_message", data: chatGM }); + } + + /* -------------------------------------------- */ + static split3Columns(data) { + + let array = [ [], [], [] ]; + if (data== undefined) return array; + + let col = 0; + for (let key in data) { + let keyword = data[key]; + keyword.key = key; // Self-reference + array[col].push( keyword); + col++; + if (col == 3) col = 0; + } + return array; + } + + /* -------------------------------------------- */ + static createChatMessage(name, rollMode, chatOptions) { + switch (rollMode) { + case "blindroll": // GM only + if (!game.user.isGM) { + this.blindMessageToGM(chatOptions); + + chatOptions.whisper = [game.user.id]; + chatOptions.content = "Message only to the GM"; + } + else { + chatOptions.whisper = this.getUsers(user => user.isGM); + } + break; + default: + chatOptions.whisper = this.getWhisperRecipients(rollMode, name); + break; + } + chatOptions.alias = chatOptions.alias || name; + ChatMessage.create(chatOptions); + } + + /* -------------------------------------------- */ + static createChatWithRollMode(name, chatOptions) { + this.createChatMessage(name, game.settings.get("core", "rollMode"), chatOptions); + } + + /* -------------------------------------------- */ + static buildDifficultyOptions( ) { + let options = "" + options += `` + options += `` + options += `` + options += `` + options += `` + return options; + + } + + /* -------------------------------------------- */ + static async confirmDelete(actorSheet, li) { + let itemId = li.data("item-id"); + let msgTxt = "

Are you sure to remove this Item ?"; + let buttons = { + delete: { + icon: '', + label: "Yes, remove it", + callback: () => { + actorSheet.actor.deleteEmbeddedDocuments( "Item", [itemId] ); + li.slideUp(200, () => actorSheet.render(false)); + } + }, + cancel: { + icon: '', + label: "Cancel" + } + } + msgTxt += "

"; + let d = new Dialog({ + title: "Confirm removal", + content: msgTxt, + buttons: buttons, + default: "cancel" + }); + d.render(true); + } + +} \ No newline at end of file diff --git a/packs/specialisations.db b/packs/specialisations.db new file mode 100644 index 0000000..e69de29 diff --git a/styles/simple.css b/styles/simple.css new file mode 100644 index 0000000..a795ddb --- /dev/null +++ b/styles/simple.css @@ -0,0 +1,1221 @@ + /* ==================== (A) Fonts ==================== */ + + :root { + /* =================== 1. ACTOR SHEET FONT STYLES =========== */ + --window-header-title-font-size: 1.3rem; + --window-header-title-font-weight: normal; + --window-header-title-color: #f5f5f5; + + --major-button-font-size: 1.05rem; + --major-button-font-weight: normal; + --major-button-color: #dadada; + + --tab-header-font-size: 1.0rem; + --tab-header-font-weight: 700; + --tab-header-color: #403f3e; + --tab-header-color-active: #4a0404; + + --actor-input-font-size: 0.8rem; + --actor-input-font-weight: 500; + --actor-input-color: black; + + --actor-label-font-size: 0.8rem; + --actor-label-font-weight: 700; + --actor-label-color: #464331c4; + + /* =================== 2. DEBUGGING HIGHLIGHTERS ============ */ + --debug-background-color-red: #ff000054; + --debug-background-color-blue: #1d00ff54; + --debug-background-color-green: #54ff0054; + + --debug-box-shadow-red: inset 0 0 2px red; + --debug-box-shadow-blue: inset 0 0 2px blue; + --debug-box-shadow-green: inset 0 0 2px green; + } + +/*@import url("https://fonts.googleapis.com/css2?family=Martel:wght@400;800&family=Roboto:wght@300;400;500&display=swap");*/ +/* Global styles & Font */ +.window-app { + text-align: justify; + font-size: 16px; + letter-spacing: 1px; +} + +/* Fonts */ +.sheet header.sheet-header h1 input, .window-app .window-header, #actors .directory-list, #navigation #scene-list .scene.nav-item { + font-size: 1.0rem; +} /* For title, sidebar character and scene */ +.sheet nav.sheet-tabs { + font-size: 0.8rem; +} /* For nav and title */ +.window-app input, .foundryvtt-vadentis .item-form, .sheet header.sheet-header .flex-group-center.flex-compteurs, .sheet header.sheet-header .flex-group-center.flex-fatigue, select, button, .item-checkbox, #sidebar, #players, #navigation #nav-toggle { + font-size: 0.8rem; +} + +.window-header{ + background: rgba(0,0,0,0.75); +} + +.window-app.sheet .window-content { + margin: 0; + padding: 0; +} +.strong-text{ + font-weight: bold; +} + +.tabs .item.active, .blessures-list li ul li:first-child:hover, a:hover { + text-shadow: 1px 0px 0px #ff6600; +} + +.rollable:hover, .rollable:focus { + color: #000; + text-shadow: 0 0 10px red; + cursor: pointer; +} +input:disabled { + color:#1c2058; +} +select:disabled { + color:#1c2058; +} +table {border: 1px solid #7a7971;} + +.grid, .grid-2col { + display: grid; + grid-column: span 2 / span 2; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 10px; + margin: 10px 0; + padding: 0; +} + +.grid-3col { + grid-column: span 3 / span 3; + grid-template-columns: repeat(3, minmax(0, 1fr)); +} + +.grid-4col { + grid-column: span 4 / span 4; + grid-template-columns: repeat(4, minmax(0, 1fr)); +} + +.grid-5col { + grid-column: span 5 / span 5; + grid-template-columns: repeat(5, minmax(0, 1fr)); +} + +.grid-6col { + grid-column: span 5 / span 5; + grid-template-columns: repeat(5, minmax(0, 1fr)); +} + +.grid-7col { + grid-column: span 7 / span 7; + grid-template-columns: repeat(7, minmax(0, 1fr)); +} + +.grid-8col { + grid-column: span 8 / span 8; + grid-template-columns: repeat(8, minmax(0, 1fr)); +} + +.grid-9col { + grid-column: span 9 / span 9; + grid-template-columns: repeat(9, minmax(0, 1fr)); +} + +.grid-10col { + grid-column: span 10 / span 10; + grid-template-columns: repeat(10, minmax(0, 1fr)); +} + +.grid-11col { + grid-column: span 11 / span 11; + grid-template-columns: repeat(11, minmax(0, 1fr)); +} + +.grid-12col { + grid-column: span 12 / span 12; + grid-template-columns: repeat(12, minmax(0, 1fr)); +} + +.flex-group-center, +.flex-group-left, +.flex-group-right { + -webkit-box-pack: center; + -ms-flex-pack: center; + justify-content: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + text-align: center; + padding: 5px; +} + +.flex-group-left { + -webkit-box-pack: start; + -ms-flex-pack: start; + justify-content: flex-start; + text-align: left; +} + +.flex-group-right { + -webkit-box-pack: end; + -ms-flex-pack: end; + justify-content: flex-end; + text-align: right; +} + +.flex-center { + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + -webkit-box-pack: center; + -ms-flex-pack: center; + justify-content: center; + text-align: center; +} + +.flex-between { + -webkit-box-pack: justify; + -ms-flex-pack: justify; + justify-content: space-between; +} +.flex-shrink { + flex: 'flex-shrink' ; +} + +/* Styles limited to foundryvtt-vadentis sheets */ + +.fvtt-weapons-of-the-gods .sheet-header { + -webkit-box-flex: 0; + -ms-flex: 0 0 210px; + flex: 0 0 210px; + overflow: hidden; + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-orient: horizontal; + -webkit-box-direction: normal; + -ms-flex-direction: row; + flex-direction: row; + -ms-flex-wrap: wrap; + flex-wrap: wrap; + -webkit-box-pack: start; + -ms-flex-pack: start; + justify-content: flex-start; + margin-bottom: 10px; +} + +.fvtt-weapons-of-the-gods .sheet-header .profile-img { + -webkit-box-flex: 0; + -ms-flex: 0 0 128px; + flex: 0 0 128px; + height: 128px; + width: 128px; + margin-right: 10px; + object-fit: cover; + object-position: 50% 0; +} + +.button-img { + vertical-align: baseline; + width: 8%; + height: 8%; + max-height: 48px; + border-width: 0; + border: 1px solid rgba(0, 0, 0, 0); +} +.button-img:hover { + color: rgba(255, 255, 128, 0.7); + border: 1px solid rgba(255, 128, 0, 0.8); + cursor: pointer; +} + +.button-effect-img { + vertical-align: baseline; + width: 16px; + max-height: 16px; + height: 16; + border-width: 0; +} + +.small-button-container { + height: 16px; + width: 16px; + border: 0; + vertical-align: bottom; +} + +.fvtt-weapons-of-the-gods .sheet-header .header-fields { + -webkit-box-flex: 1; + -ms-flex: 1; + flex: 1; +} + +.fvtt-weapons-of-the-gods .sheet-header h1.charname { + height: 50px; + padding: 0px; + margin: 5px 0; + border-bottom: 0; +} + +.fvtt-weapons-of-the-gods .sheet-header h1.charname input { + width: 100%; + height: 100%; + margin: 0; +} + +.fvtt-weapons-of-the-gods .sheet-tabs { + -webkit-box-flex: 0; + -ms-flex: 0; + flex: 0; +} + +.fvtt-weapons-of-the-gods .sheet-body, +.fvtt-weapons-of-the-gods .sheet-body .tab, +.fvtt-weapons-of-the-gods .sheet-body .tab .editor { + height: 100%; + font-size: 0.8rem; +} + +.editor { + border: 2; + height: 300px; + padding: 0 3px; +} + +.medium-editor { + border: 2; + height: 240px; + padding: 0 3px; +} + +.small-editor { + border: 2; + height: 120px; + padding: 0 3px; +} + +.fvtt-weapons-of-the-gods .tox .tox-editor-container { + background: #fff; +} + +.fvtt-weapons-of-the-gods .tox .tox-edit-area { + padding: 0 8px; +} + +.fvtt-weapons-of-the-gods .resource-label { + font-weight: bold; + text-transform: uppercase; +} + +.fvtt-weapons-of-the-gods .tabs { + height: 40px; + border-top: 1px solid #AAA; + border-bottom: 1px solid #AAA; +} + +.fvtt-weapons-of-the-gods .tabs .item { + line-height: 40px; + font-weight: bold; +} + +.fvtt-weapons-of-the-gods .tabs .item.active { + text-decoration: underline; + text-shadow: none; +} + +.fvtt-weapons-of-the-gods .items-list { + list-style: none; + margin: 1px 0; + padding: 0; + overflow-y: auto; +} + +.fvtt-weapons-of-the-gods .items-list .item-header { + font-weight: bold; +} + +.fvtt-weapons-of-the-gods .items-list .item { + height: 30px; + line-height: 24px; + padding: 1px 0; + border-bottom: 1px solid #BBB; +} + +.fvtt-weapons-of-the-gods .items-list .item .item-image { + -webkit-box-flex: 0; + -ms-flex: 0 0 24px; + flex: 0 0 24px; + margin-right: 5px; +} + +.fvtt-weapons-of-the-gods .items-list .item img { + display: block; +} + +.fvtt-weapons-of-the-gods .items-list .item-name { + margin: 0; +} + +.fvtt-weapons-of-the-gods .items-list .item-controls { + -webkit-box-flex: 0; + -ms-flex: 0 0 86px; + flex: 0 0 86px; + text-align: right; +} + + +/* ======================================== */ +/* Sheet */ +.window-app.sheet .window-content .sheet-header{ + color: rgba(168, 139, 139, 0.5); +} +/* background: #011d33 url("../images/ui/fond1.webp") repeat left top;*/ + +.window-app.sheet .window-content .sheet-header input[type="text"], .window-app.sheet .window-content .sheet-header input[type="number"], .window-app.sheet .window-content .sheet-header input[type="password"], .window-app.sheet .window-content .sheet-header input[type="date"], .window-app.sheet .window-content .sheet-header input[type="time"] { + color: rgba(36, 37, 37, 0.75); + background: rgba(255, 255, 255, 0.05); + border: 0 none; + margin-bottom: 0.25rem; +} + +.window-app .window-content, .window-app.sheet .window-content .sheet-body{ + font-size: 0.8rem; +} + +/* background: rgba(245,245,240,0.6) url("../images/ui/sheet_background.webp") left top;*/ + +section.sheet-body{padding: 0.25rem 0.5rem;} + +.sheet header.sheet-header .profile-img { + object-fit: cover; + object-position: 50% 0; + margin: 0.5rem 0 0.5rem 0.5rem; + padding: 0; +} + +.sheet nav.sheet-tabs { + font-size: 0.70rem; + font-weight: bold; + height: 3rem; + flex: 0 0 3rem; + margin: 0; + padding: 0 0 0 0.25rem; + text-align: center; + text-transform: uppercase; + line-height: 1.5rem; + border-top: 0 none; + border-bottom: 0 none; + color: rgba(52, 52, 52, 0.95); +} + +/* background: rgb(245,245,240) url("../images/ui/fond4.webp") repeat left top;*/ + +nav.sheet-tabs .item { + position: relative; + padding: 0 0.25rem; +} + +nav.sheet-tabs .item:after { + content: ""; + position: absolute; + top: 0; + right: 0; + height: 2rem; + width: 1px; + border-right: 1px dashed rgba(52, 52, 52, 0.25); +} + +.sheet .tab[data-tab] { + padding: 0; +} + +section.sheet-body:after { + content: ""; + display: block; + clear: both; +} + +.sheet header.sheet-header .flex-compteurs {text-align: right;} +.sheet header.sheet-header .resource-content {width: 2rem;} + +.select-diff { + display: inline-block; + text-align: left; + width: 50px; +} + +#vie-plus, #vie-moins, #endurance-plus, #endurance-moins, #fatigue-plus, #fatigue-moins, #ptreve-actuel-plus, #ptreve-actuel-moins, .monnaie-plus, .monnaie-moins { + display: inline-block; + width: 1.25rem; + background: rgba(30, 25, 20, 1); + text-align: center; + border: 1px solid rgba(72, 46, 28, 1); + border-radius: 0.25rem; + line-height: 1.25rem; + color: rgba(255, 255, 255, 0.5); +} + + +.window-app.sheet .window-content .tooltip:hover .tooltiptext { + top: 2rem; + left: 2rem; + margin: 0; + padding: 0.25rem; +} + +.window-app.sheet .window-content .carac-value, .window-app.sheet .window-content .competence-xp { + margin: 0.05rem; + flex-basis: 3rem; + text-align: center; +} + +/* ======================================== */ +/* Global UI elements */ + +/* ======================================== */ + +h1, h2, h3, h4 { + font-weight: bold; +} + +ul, ol { + margin: 0; + padding: 0; +} +ul, li { + list-style-type: none; +} + +.sheet li { + margin: 0.010rem; + padding: 0.25rem; +} +.header-fields li { + margin: 0; + padding: 0; +} + +.alterne-list > .list-item:hover { + background: rgba(100, 100, 50, 0.25); +} +.alterne-list > .list-item:nth-child(even) { + background: rgba(80, 60, 0, 0.10); +} +.alterne-list > .list-item:nth-child(odd) { + background: rgb(160, 130, 100, 0.05); +} + +.specialisation-label { + font-size: 0.8rem; +} + +.carac-label, +.attr-label { + font-weight: bold; +} + +.list-item { + margin: 0.125rem; + box-shadow: inset 0px 0px 1px #00000096; + border-radius: 0.25rem; + padding: 0.125rem; + flex: 1 1 5rem; +} +.item-display-show { + display: block; +} +.item-display-hide { + display: none; +} +.conteneur-type { + background: rgb(200, 10, 100, 0.25); +} + +.item-quantite { + margin-left: 0.5rem; +} + +.list-item-margin1 { + margin-left: 1rem; +} +.list-item-margin2 { + margin-left: 2rem; +} +.list-item-margin3 { + margin-left: 3rem; +} +.list-item-margin4 { + margin-left: 4rem; +} + +.sheet-competence-img { + width: 24px; + height: 24px; + flex-grow: 0; + margin-right: 0.25rem; +} +.competence-column { + flex-direction: column; + align-content: flex-start; + justify-content: flex-start; + flex-grow: 0; + flex-basis: 1; +} +.competence-header { + align-content: flex-start; + justify-content: flex-start; + font-weight: bold; + flex-grow: 0; +} +.secondaire-label, +.arme-label, +.generic-label, +.competence-label, +.devotion-label, +.sort-label, +.technique-label, +.stat-label, +.arme-label, +.armure-label, +.equipement-label, +.description-label { + flex-grow: 2; + margin-left: 4px; +} +.roll-dialog-label { + margin: 4px 0; +} + +.short-label { + flex-grow: 1; +} +.keyword-label { + font-size: 0.85rem; +} + +.item-sheet-label { + flex-grow: 1; +} + +.item-text-long-line { + flex-grow: 3; +} + +.score-label { + flex-grow: 2; + align-content: center; +} + +.attribut-value, +.carac-value { + flex-grow: 0; + flex-basis: 64px; + margin-right: 4px; + margin-left: 4px; +} +.sante-value, +.competence-value { + flex-grow: 0; + flex-basis: 2rem; + margin-right: 0.25rem; + margin-left: 0.25rem; +} +.description-value { + flex-grow: 0; + flex-basis: 4rem; + margin-right: 0.25rem; + margin-left: 0.25rem; +} +.competence-xp { + flex-grow: 0; + flex-basis: 2rem; + margin-right: 0.25rem; + margin-left: 0.25rem; +} +.blessures-title { + font-weight: bold; +} +.alchimie-title { + font-weight: bold; +} +.blessure-data { + flex-direction: row; + align-content: flex-start; + justify-content: flex-start; +} +.blessures-soins { + flex-grow: 0; + flex-basis: 32px; + margin-right: 4px; + margin-left: 4px; +} +.blessures-loc { + flex-grow: 0; + flex-basis: 96px; + margin-right: 4px; + margin-left: 4px; +} +.pointsreve-value { + flex-grow: 0; + flex-basis: 64px; + margin-right: 4px; + margin-left: 4px; +} +.input-sante-header, +.stress-style { + flex-grow: 0; + flex-basis: 64px; + margin-right: 4px; + margin-left: 4px; +} + +.padd-right { + margin-right: 8px; +} +.padd-left { + margin-left: 8px; +} + +.stack-left { + align-items:center; + flex-shrink: 1; + flex-grow: 0; +} +.npc-stat-label { + flex-grow: 2; +} + +.packed-left { + white-space: nowrap; + flex-grow: 0; +} + +.input-numeric-short { + width: 40px; + max-width: 40px; + flex-grow: 0; + flex-shrink: 0; + flex-basis: 40px; + margin-right: 0.25rem; + margin-left: 0.25rem; +} + +.stats-table { + align-content: flex-start; +} + +/* ======================================== */ +.tokenhudext { + display: flex; + flex: 0 !important; + font-weight: 600; +} +.tokenhudext.left { + justify-content: flex-start; + flex-direction: column; + position: absolute; + top: 2.75rem; + right: 4rem; +} +.tokenhudext.right { + justify-content: flex-start; + flex-direction: column; + position: absolute; + top: 2.75rem; + left: 4rem; +} +.control-icon.tokenhudicon { + width: fit-content; + height: fit-content; + min-width: 6rem; + flex-basis: auto; + padding: 0; + line-height: 1rem; + margin: 0.25rem; +} +.control-icon.tokenhudicon.right { + margin-left: 8px; +} +#token-hud .status-effects.active{ + z-index: 2; +} +/* ======================================== */ +.item-checkbox { + height: 25px; + border: 1px solid #736953a6; + border-left: none; + font-weight: 500; + font-size: 1rem; + color: black; + padding-top: 5px; + margin-right: 0px; + width: 45px; + position: relative; + left: 0px; + text-align: center; +} + + +.flex-actions-bar { + flex-grow: 2; +} + +/* ======================================== */ +/* Sidebar CSS */ +#sidebar { + font-size: 1rem; + background-position: 100%; + color: rgba(220,220,220,0.75); +} + +/* background: rgb(105,85,65) url("../images/ui/texture_feuille_perso_onglets.webp") no-repeat right bottom;*/ + +#sidebar.collapsed { + height: 430px !important; + position: absolute; +} + +#sidebar-tabs > .collapsed, #chat-controls .chat-control-icon { + color: rgba(220,220,220,0.75); + text-shadow: 1px 1px 0 rgba(0,0,0,0.75); +} + +.sidebar-tab .directory-list .entity { + border-top: 1px dashed rgba(0,0,0,0.25); + border-bottom: 0 none; + padding: 0.25rem 0; +} + +.sidebar-tab .directory-list .entity:hover { + background: rgba(0,0,0,0.05); + cursor: pointer; +} +.chat-message-header { + background: rgba(220,220,210,0.5); + font-size: 1.1rem; + height: 48px; + text-align: center; + vertical-align: middle; + display: flex; + align-items: center; +} + +.chat-message .message-header .flavor-text, .chat-message .message-header .whisper-to { + font-size: 0.9rem; +} +.chat-actor-name { + padding: 4px; +} + +.chat-img { + width: 64px; + height: 64px; +} + +.roll-dialog-header { + height: 52px; +} + +.actor-icon { + float: left; + width: 48px; + height: 48px; + padding: 2px 6px 2px 2px; +} + +.padding-dice { + padding-top: .2rem; + padding-bottom: .2rem; +} + +.dice-image { + box-sizing: border-box; + border: none; + border-radius: 0; + max-width: 100%; +} + +.dice-image-reroll { + background-color:rgba(115, 224, 115, 0.25); + border-color: #011d33; + box-sizing: border-box; + border: 1px; + border-radius: 0%; + max-width: 100%; +} + +.chat-dice { + width: 15%; + height: 15%; + font-size: 15px; + padding: 10px; + padding-bottom: 20px; + padding-top: .2rem; + padding-bottom: .2rem; +} + +.div-river-full { + height: 5rem; + align-items: flex-start; +} + +.div-river { + align-content: center; + margin-left: 8px; + align-content:space-around; + justify-content: space-around; +} + +.div-center { + align-self: center; +} + +.chat-message { + background: rgba(220,220,210,0.5); + font-size: 0.9rem; +} + +.chat-message.whisper { + background: rgba(220,220,210,0.75); + border: 2px solid #545469; +} +.chat-message .chat-icon { + border: 0; + padding: 2px 6px 2px 2px; + float: left; + width: 64px; + height: 64px; +} + +#sidebar-tabs { + flex: 0 0 32px; + box-sizing: border-box; + margin: 0 0 5px; + border-bottom: 1px solid rgba(0,0,0,0); + box-shadow: inset 0 0 2rem rgba(0,0,0,0.5); +} + +#sidebar-tabs > .item.active { + border: 1px solid rgba(114,98,72,1); + background: rgba(30, 25, 20, 0.75); + box-shadow: 0 0 6px inset rgba(114,98,72,1); +} + +#sidebar #sidebar-tabs i{ + width: 25px; + height: 25px; + display: inline-block; + background-position:center; + background-size:cover; + text-shadow: 1px 1px 0 rgba(0,0,0,0.75); + +} + +/*#sidebar #sidebar-tabs i.fa-comments:before, #sidebar #sidebar-tabs i.fa-fist-raised:before, #sidebar #sidebar-tabs i.fa-users:before, #sidebar #sidebar-tabs i.fa-map:before, #sidebar #sidebar-tabs i.fa-suitcase:before, #sidebar #sidebar-tabs i.fa-book-open:before, #sidebar #sidebar-tabs i.fa-th-list:before, #sidebar #sidebar-tabs i.fa-music:before, #sidebar #sidebar-tabs i.fa-atlas:before, #sidebar #sidebar-tabs i.fa-cogs:before {content: "";} +#sidebar #sidebar-tabs i.fa-comments {background: url("img/ui/icon_sidebar_chat.svg") no-repeat;} +#sidebar #sidebar-tabs i.fa-fist-raised {background: url("img/ui/icon_sidebar_fight.svg") no-repeat;} +#sidebar #sidebar-tabs i.fa-users {background: url("img/ui/icon_sidebar_actor.svg") no-repeat;} +#sidebar #sidebar-tabs i.fa-map {background: url("img/ui/icon_sidebar_scene.svg") no-repeat;} +#sidebar #sidebar-tabs i.fa-suitcase {background: url("img/ui/icon_sidebar_item.svg") no-repeat;} +#sidebar #sidebar-tabs i.fa-book-open {background: url("img/ui/icon_sidebar_journal.svg") no-repeat;} +#sidebar #sidebar-tabs i.fa-th-list {background: url("img/ui/icon_sidebar_rolltable.svg") no-repeat;} +#sidebar #sidebar-tabs i.fa-music {background: url("img/ui/icon_sidebar_music.svg") no-repeat;} +#sidebar #sidebar-tabs i.fa-atlas {background: url("img/ui/icon_sidebar_compendium.svg") no-repeat;} +#sidebar #sidebar-tabs i.fa-cogs {background: url("img/ui/icon_sidebar_settings.svg") no-repeat;} + +#combat #combat-controls { + box-shadow: inset 0 0 2rem rgba(0,0,0,0.5); +} +*/ + +/*--------------------------------------------------------------------------*/ +/* Control, Tool, hotbar & navigation */ + +#controls .scene-control, #controls .control-tool { + box-shadow: 0 0 3px #000; + margin: 0 0 8px; + border-radius: 0; + background: rgba(30, 25, 20, 1); + background-origin: padding-box; + border-image: url(img/ui/footer-button.png) 10 repeat; + border-image-width: 4px; + border-image-outset: 0px; +} + +#controls .scene-control.active, #controls .control-tool.active, #controls .scene-control:hover, #controls .control-tool:hover { + background: rgba(72, 46, 28, 1); + background-origin: padding-box; + border-image: url(img/ui/footer-button.png) 10 repeat; + border-image-width: 4px; + border-image-outset: 0px; + box-shadow: 0 0 3px #ff6400; +} + +#hotbar #action-bar #macro-list { + border: 1px solid rgba(72, 46, 28, 1); + box-shadow: 2px 2px 5px #000000; +} + +#hotbar #action-bar .macro { + border-image: url(img/ui/bg_control.jpg) 21 repeat; + border-image-slice: 6 6 6 6 fill; + border-image-width: 6px 6px 6px 6px; + border-image-outset: 0px 0px 0px 0px; + border-radius: 0px; +} + +#hotbar .bar-controls { + background: rgba(30, 25, 20, 1); + border: 1px solid rgba(72, 46, 28, 1); +} + +#players { + border-image: url(img/ui/footer-button.png) 10 repeat; + border-image-width: 4px; + border-image-outset: 0px; + background: rgba(30, 25, 20, 1); +} + +#navigation #scene-list .scene.nav-item.active { + background: rgba(72, 46, 28, 1); +} + +#navigation #scene-list .scene.nav-item { + background: rgba(30, 25, 20, 1); + background-origin: padding-box; + border-image: url(img/ui/footer-button.png) 10 repeat; + border-image-width: 4px; + border-image-outset: 0px; +} + +#navigation #scene-list .scene.view, #navigation #scene-list .scene.context { + background: rgba(72, 46, 28, 1); + background-origin: padding-box; + border-image: url(img/ui/footer-button.png) 10 repeat; + border-image-width: 4px; + border-image-outset: 0px; + box-shadow: 0 0 3px #ff6400; +} + +#navigation #nav-toggle { + background: rgba(30, 25, 20, 1); + background-origin: padding-box; + border-image: url(img/ui/footer-button.png) 10 repeat; + border-image-width: 4px; + border-image-outset: 0px; +} + +/* Tooltip container */ +.tooltip { + position: relative; + display: inline-block; + /*border-bottom: 1px dotted black; /* If you want dots under the hoverable text */ +} + +/* Tooltip text */ +.tooltip .tooltiptext { + text-align: left; + background: rgba(231, 229, 226, 0.9); + width: 150px; + padding: 3px 0; + font-size: 0.9rem; + + /* Position the tooltip text */ + top: 1px; + position: absolute; + z-index: 1; + + /* Fade in tooltip */ + visibility: hidden; + opacity: 0; + transition: opacity 0.3s; +} + +.tooltip .ttt-fatigue{ + width: 360px; + + background: rgba(30, 25, 20, 0.9); + border-image: url(img/ui/bg_control.jpg) 21 repeat; + border-image-slice: 6 6 6 6 fill; + border-image-width: 6px 6px 6px 6px; + border-image-outset: 0px 0px 0px 0px; + border-radius: 0px; + + font-size: 0.8rem; + padding: 3px 0; +} + +.tooltip .ttt-ajustements { + width: 150px; + background: rgba(220,220,210,0.95); + border-radius: 6px; + font-size: 0.9rem; + padding: 3px 0; +} + +.tooltip-nobottom { + border-bottom: unset; /* If you want dots under the hoverable text */ +} +.tooltip .ttt-xp { + width: 250px; + background: rgba(220,220,210,0.95); + border-radius: 6px; + font-size: 0.9rem; + padding: 3px 0; +} + +/* Show the tooltip text when you mouse over the tooltip container */ +.tooltip:hover .tooltiptext { + visibility: visible; + opacity: 1; +} + +.river-button { + box-shadow: inset 0px 1px 0px 0px #a6827e; + background: linear-gradient(to bottom, #21374afc 5%, #152833ab 100%); + background-color: #7d5d3b00; + border-radius: 3px; + border: 2px ridge #846109; + display: inline-block; + cursor: pointer; + color: #ffffff; + font-size: 0.8rem; + padding: 2px 4px 0px 4px; + text-decoration: none; + text-shadow: 0px 1px 0px #4d3534; + position: relative; + margin:4px; +} + +.chat-card-button { + box-shadow: inset 0px 1px 0px 0px #a6827e; + background: linear-gradient(to bottom, #21374afc 5%, #152833ab 100%); + background-color: #7d5d3b00; + border-radius: 3px; + border: 2px ridge #846109; + display: inline-block; + cursor: pointer; + color: #ffffff; + font-size: 0.9rem; + padding: 4px 4px 0px 4px; + text-decoration: none; + text-shadow: 0px 1px 0px #4d3534; + position: relative; + margin:4px; +} + +.plus-moins-button { + box-shadow: inset 0px 1px 0px 0px #a6827e; + background: linear-gradient(to bottom, #21374afc 5%, #152833ab 100%); + background-color: #7d5d3b00; + border-radius: 3px; + border: 2px ridge #846109; + display: inline-block; + cursor: pointer; + color: #ffffff; + padding: 2px 6px 0px 6px; + text-decoration: none; + text-shadow: 0px 1px 0px #4d3534; + position: relative; + margin:3px; +} + +.river-button:hover, +.plus-moins-button:hover, +.chat-card-button:hover { + background: linear-gradient(to bottom, #800000 5%, #3e0101 100%); + background-color: red; +} + +.river-button:active, +.plus-moins-button:active, +.chat-card-button:active { + position:relative; + top:1px; +} + +.plus-moins { + font-size: 0.9rem; + font-weight: bold; +} + +/*************************************************************/ +#pause +{ + font-size: 2rem; +} +#pause > h3 +{ + color: #CCC +} +#pause > img { + content: url(../images/ui/wotg_logo_01.webp); + height: 160px; + width: 256px; + top: -80px; + left: calc(50% - 132px); +} + +#logo { + content : url(../images/ui/wotg_logo_01.webp); + width: 110px; + height: 70px; +} + +.dice-cell { + padding-left: 12px; + padding-right: 12px; + width: 60px; + text-align: center; +} + +.dice-formula, +.dice-total { + height: 54px; + position:relative; +} + +.dice-river-list-river { + position:relative; +} + +.dice-river-list .dice, +.dice-formula .dice, +.dice-total .dice { + margin: 2px; + display: inline-flex; + width: 50px; + height: 50px; +} + +.dice-river-list .dice label, +.dice-formula .dice label, +.dice-total .dice label { + font-weight: bold; + color: white; + position:absolute; + text-align: center; + margin-top: 10px; + margin-left: 20px; + z-index: 1; +} + +.dice-river-list .dice img, +.dice-formula .dice img, +.dice-total .dice img { + position: relative; + width: 48px; + height: 48px; + border: none; +} + +.dice-river-list .modifier, +.dice-formula .modifier, +.dice-total .modifier { + margin: 2px; + display: inline-flex; +} \ No newline at end of file diff --git a/system.json b/system.json new file mode 100644 index 0000000..6ba0911 --- /dev/null +++ b/system.json @@ -0,0 +1,48 @@ +{ + "author": "Uberwald", + "compatibleCoreVersion": "0.8.9", + "description": "Pegasus RPG system for FoundryVTT", + "download": "https://www.uberwald.me/data/files/fvtt-pegasus-rpg/fvtt-pegasus-rpg.zip", + "esmodules": [ + "modules/pegasus-main.js" + ], + "gridDistance": 5, + "gridUnits": "m", + "languages": [ + { + "lang": "en", + "name": "English", + "path": "lang/en.json" + } + ], + "library": false, + "license": "LICENSE.txt", + "manifest": "https://www.uberwald.me/data/files/fvtt-pegasus-rpg/system.json", + "manifestPlusVersion": "1.0.0", + "media": [], + "minimumCoreVersion": "0.8.0", + "name": "fvtt-pegasus-rpg", + "packs": [ + { + "entity": "Item", + "label": "Specialisations", + "name": "specialisations", + "path": "./packs/specialisations.db", + "system": "fvtt-pegasus-rpg", + "tags": [ + "skill" + ] + } + ], + "primaryTokenAttribute": "endurance.endurance", + "secondaryTokenAttribute": "fate.value", + "socket": true, + "styles": [ + "styles/simple.css" + ], + "templateVersion": 4, + "title": "Pegasus RPG", + "url": "https://www.uberwald.me/data/files/fvtt-pegasus-rpg", + "version": "0.0.4'", + "background" : "" +} \ No newline at end of file diff --git a/template.json b/template.json new file mode 100644 index 0000000..8c0b90c --- /dev/null +++ b/template.json @@ -0,0 +1,199 @@ +{ + "Actor": { + "types": ["character", "npc"], + "templates": { + "biodata": { + "biodata": { + "name": "", + "age": 0, + "size": "", + "weight": "", + "hair": "", + "sex": "", + "eyes": "", + "description": "", + "worstfear": "", + "desires": "", + "preferredhand": "", + "catchphrase": "", + "catchphrase_trigger": "", + "character_value": 0, + "level": 0, + "cdp": 0, + "notes": "", + "gmnotes": "" + } + }, + "core": { + "subactors": [], + "statistics": { + "agi":{ + "label": "Agility", + "abbrev": "agi", + "level": 1, + "mod": 0 + }, + "mnd":{ + "label": "Mind", + "abbrev": "mnd", + "level": 1, + "mod": 0 + }, + "soc":{ + "label": "Sociability", + "abbrev": "soc", + "level": 1, + "mod": 0 + }, + "str":{ + "label": "Strength", + "abbrev": "str", + "level": 1, + "mod": 0 + }, + "phy":{ + "label": "Physical", + "abbrev": "phy", + "level": 1, + "mod": 0 + }, + "com":{ + "label": "Communication", + "abbrev": "com", + "level": 1, + "mod": 0 + }, + "def":{ + "label": "Defense", + "abbrev": "def", + "level": 1, + "mod": 0 + }, + "stl":{ + "label": "Stealth", + "abbrev": "stl", + "level": 1, + "mod": 0 + }, + "per":{ + "label": "Perception", + "abbrev": "per", + "level": 1, + "mod": 0 + }, + "foc":{ + "label": "Focus", + "abbrev": "foc", + "level": 1, + "mod": 0 + } + }, + "secondary": { + "health": { + "label": "Health", + "value": 0, + "max": 0 + }, + "delirium": { + "label": "Delirium", + "value": 0, + "max": 0 + }, + "nrg": { + "label": "NRJ", + "value": 0, + "max": 0 + }, + "mr": { + "label": "MR", + "value": 0 + } + } + }, + "npccore": { + "npctype": "", + "description": "" + } + }, + "character": { + "templates": [ "biodata", "core" ] + }, + "npc": { + "templates": [ "npccore" ] + } + }, + "Item": { + "types": [ "race", "role", "ability", "specialisation", "perk", "power" , "armor", "equipment", "weapon"], + "race": { + "description": "", + "environment": "", + "society_culture": "", + "outlook": "", + "abilities": "", + "statistics": "" + }, + "role": { + "description": "", + "abilities": "", + "statistics": "" + }, + "ability": { + "description": "" + }, + "specialisation": { + "statistic": "", + "level": 0, + "description": "" + }, + "perk": { + "description": "", + "upgrades": "", + "rules": "" + }, + "power": { + "rollneeded": false, + "statistic": "", + "cost": 0, + "costtype": "", + "range": "", + "action": "", + "type": "", + "effects": "" + }, + "armor": { + "statistic": "", + "resistance": "", + "weight": 0, + "cost": 0, + "idr": "", + "equipped": false, + "description":"" + }, + "shield": { + "deftype": "", + "level": "", + "weight": 0, + "cost": 0, + "idr": "", + "equipped": false, + "description":"" + }, + "equipment": { + "type": "", + "cost": 0, + "weight": 0, + "idr": "", + "equipped": false, + "description":"" + }, + "weapon": { + "statistic": "", + "damage": "", + "cost": 0, + "weight": 0, + "idr": "", + "equipped": false, + "description": "" + } + } +} diff --git a/templates/actor-sheet.html b/templates/actor-sheet.html new file mode 100644 index 0000000..8d42b92 --- /dev/null +++ b/templates/actor-sheet.html @@ -0,0 +1,296 @@ +
+ + {{!-- Sheet Header --}} +
+
+
+ +

+
+
+
+ + {{!-- Sheet Tab Navigation --}} + + + {{!-- Sheet Body --}} +
+ + {{!-- Carac Tab --}} +
+ + Unlocked/Locked{{#if editScore}}Unlocked{{else}}Locked{{/if}} + + +
+
+
    + {{#each data.statistics as |stat key|}} +
  • +

    {{stat.label}}

    + + +
  • + {{/each}} +
+
+ +
+
    + {{#each data.secondary as |stat2 key|}} +
  • +

    {{stat2.label}}

    + +
  • + {{/each}} +
+
+ +
+ +
+ + + {{!-- Other Tab --}} +
+ +
+

Specialisations

+
    + {{#each specs as |spec key|}} +
  • + + {{spec.name}} +
    + + +
    +
  • + {{/each}} +
+
+ +
+ + {{!-- Combat Tab --}} +
+
+ +
    +
  • +

    Health

    + + / {{data.secondary.health.max}} +
  • +
+ +

Weapons

+ + +

Armor

+ +
+
+ + {{!-- Powers Tab --}} +
+ +
+ +

Powers

+
    + {{#each powers as |power key|}} +
  • + + {{power.name}} +
    + + +
    +
  • + {{/each}} +
+ +
+
+ + {{!-- Equipement Tab --}} +
+ +

Equipment

+ +
+ + {{!-- Biography Tab --}} +
+
+
+
    +
  • + + +
  • +
  • + + +
  • +
  • + + +
  • +
  • + + +
  • +
  • + + +
  • +
+
+
+
    +
  • + + +
  • +
  • + + +
  • +
  • + + +
  • +
  • + + +
  • +
+
+
+
+

Destiny :

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

Goals :

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

History :

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

Notes :

+
+ {{editor content=data.biodata.notes target="data.biodata.notes" button=true owner=owner editable=editable}} +
+
+ +
+ +
+
+ diff --git a/templates/chat-generic-result.html b/templates/chat-generic-result.html new file mode 100644 index 0000000..696dd35 --- /dev/null +++ b/templates/chat-generic-result.html @@ -0,0 +1,61 @@ +
+ {{alias}} +

{{alias}}

+
+ +
+ +
+ {{name}} +

+ {{#if (eq mode "skill")}} + Skill : {{skill.name}} + {{else}} + {{#if (eq mode "technique")}} + Technique : {{technique.name}} + {{else}} + {{#if (eq mode "weapon")}} + Weapon attack : {{weapon.name}} + {{/if}} + {{/if}} + {{/if}} +

+
+ +
+
+ {{#each diceResults as |diceResult key|}} + + {{/each}} +
+ +
+
    + {{#if (eq mode "technique")}} +
  • {{technique.data.chicolor}} Chi cost : {{technique.data.chicost}}
  • +
  • +
  • Effect description : {{technique.data.effectdescription}}
  • + {{/if}} + {{#if (eq mode "weapon")}} +
  • Effect description : {{weapon.data.effectdescription}}
  • + {{/if}} +
  • Modifiers : {{negativeModifier}} / {{positiveModifier}}
  • +
  • Total Roll : {{finalScore}} + + {{#if linkedRollId}} +
  • + {{/if}} + +
+
+ +
diff --git a/templates/chat-opposed-damage.html b/templates/chat-opposed-damage.html new file mode 100644 index 0000000..e6f27a6 --- /dev/null +++ b/templates/chat-opposed-damage.html @@ -0,0 +1,15 @@ +
+ {{alias}} +

{{defenderName}}

+
+ +
+ +
+
    +
  • {{defenderName}} suffer damages from {{attackerName}} !
  • +
  • Damages (inc. armor+weapon) : {{finalDamage}}
  • +
  • +
  • +
+
diff --git a/templates/chat-opposed-fail.html b/templates/chat-opposed-fail.html new file mode 100644 index 0000000..4e71d14 --- /dev/null +++ b/templates/chat-opposed-fail.html @@ -0,0 +1,11 @@ +
+ {{alias}} +

{{defenderName}}

+
+ +
+
+ {{defenderName}} wins the opposition against {{attackerName}} ! +
+ +
diff --git a/templates/editor-notes-gm.html b/templates/editor-notes-gm.html new file mode 100644 index 0000000..f3b3218 --- /dev/null +++ b/templates/editor-notes-gm.html @@ -0,0 +1,6 @@ +{{#if data.isGM}} +

GM Notes :

+
+ {{editor content=data.gmnotes target="data.gmnotes" button=true owner=owner editable=editable}} +
+{{/if}} diff --git a/templates/item-ability-sheet.html b/templates/item-ability-sheet.html new file mode 100644 index 0000000..3e56c21 --- /dev/null +++ b/templates/item-ability-sheet.html @@ -0,0 +1,20 @@ +
+
+ +
+

+
+
+ + {{!-- Sheet Body --}} +
+ +
+ +
+ {{editor content=data.description target="data.description" button=true owner=owner editable=editable}} +
+
+ +
+
diff --git a/templates/item-armor-sheet.html b/templates/item-armor-sheet.html new file mode 100644 index 0000000..02b50f0 --- /dev/null +++ b/templates/item-armor-sheet.html @@ -0,0 +1,46 @@ +
+
+ +
+

+
+
+ + {{!-- Sheet Body --}} +
+ +
+
    +
  • + +
  • +
  • + +
  • + +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • +
    + {{editor content=data.description target="data.description" button=true owner=owner editable=editable}} +
    +
  • +
+
+ +
+
diff --git a/templates/item-equipment-sheet.html b/templates/item-equipment-sheet.html new file mode 100644 index 0000000..daee4ac --- /dev/null +++ b/templates/item-equipment-sheet.html @@ -0,0 +1,42 @@ +
+
+ +
+

+
+
+ + {{!-- Sheet Body --}} +
+ +
+
    +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • +
    + {{editor content=data.description target="data.description" button=true owner=owner editable=editable}} +
    +
  • +
+
+ +
+
diff --git a/templates/item-perk-sheet.html b/templates/item-perk-sheet.html new file mode 100644 index 0000000..865cc0e --- /dev/null +++ b/templates/item-perk-sheet.html @@ -0,0 +1,28 @@ +
+
+ +
+

+
+
+ + {{!-- Sheet Body --}} +
+ +
+ +
+ {{editor content=data.description target="data.description" button=true owner=owner editable=editable}} +
+ +
+ {{editor content=data.upgrades target="data.upgrades" button=true owner=owner editable=editable}} +
+ +
+ {{editor content=data.rules target="data.rules" button=true owner=owner editable=editable}} +
+
+ +
+
diff --git a/templates/item-power-sheet.html b/templates/item-power-sheet.html new file mode 100644 index 0000000..8cfe21d --- /dev/null +++ b/templates/item-power-sheet.html @@ -0,0 +1,66 @@ +
+
+ +
+

+
+
+ + {{!-- Sheet Body --}} +
+ +
+
    +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • +
    + {{editor content=data.effects target="data.effects" button=true owner=owner editable=editable}} +
    +
  • + < +
+ +
+
diff --git a/templates/item-race-sheet.html b/templates/item-race-sheet.html new file mode 100644 index 0000000..9c98723 --- /dev/null +++ b/templates/item-race-sheet.html @@ -0,0 +1,40 @@ +
+
+ +
+

+
+
+ + {{!-- Sheet Body --}} +
+ +
+ +
+ {{editor content=data.description target="data.description" button=true owner=owner editable=editable}} +
+ +
+ {{editor content=data.environment target="data.environment" button=true owner=owner editable=editable}} +
+ +
+ {{editor content=data.society_culture target="data.society_culture" button=true owner=owner editable=editable}} +
+ +
+ {{editor content=data.outlook target="data.outlook" button=true owner=owner editable=editable}} +
+ +
+ {{editor content=data.abilities target="data.abilities" button=true owner=owner editable=editable}} +
+ +
+ {{editor content=data.statistics target="data.statistics" button=true owner=owner editable=editable}} +
+
+ +
+
diff --git a/templates/item-role-sheet.html b/templates/item-role-sheet.html new file mode 100644 index 0000000..e748533 --- /dev/null +++ b/templates/item-role-sheet.html @@ -0,0 +1,28 @@ +
+
+ +
+

+
+
+ + {{!-- Sheet Body --}} +
+ +
+ +
+ {{editor content=data.description target="data.description" button=true owner=owner editable=editable}} +
+ +
+ {{editor content=data.abilities target="data.abilities" button=true owner=owner editable=editable}} +
+ +
+ {{editor content=data.statistics target="data.statistics" button=true owner=owner editable=editable}} +
+
+ +
+
diff --git a/templates/item-shield-sheet.html b/templates/item-shield-sheet.html new file mode 100644 index 0000000..ba58211 --- /dev/null +++ b/templates/item-shield-sheet.html @@ -0,0 +1,48 @@ +
+
+ +
+

+
+
+ + {{!-- Sheet Body --}} +
+ +
+
    +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • +
    + {{editor content=data.description target="data.description" button=true owner=owner editable=editable}} +
    +
  • +
+
+ +
+
diff --git a/templates/item-specialisation-sheet.html b/templates/item-specialisation-sheet.html new file mode 100644 index 0000000..b4e2aa3 --- /dev/null +++ b/templates/item-specialisation-sheet.html @@ -0,0 +1,36 @@ +
+
+ +
+

+
+
+ + {{!-- Sheet Body --}} +
+ +
+
    +
  • + +
  • +
  • + +
  • +
+ +
+ + {{editor content=data.description target="data.description" button=true owner=owner editable=editable}} +
+ +
+
diff --git a/templates/item-weapon-sheet.html b/templates/item-weapon-sheet.html new file mode 100644 index 0000000..20ad3f8 --- /dev/null +++ b/templates/item-weapon-sheet.html @@ -0,0 +1,46 @@ +
+
+ +
+

+
+
+ + {{!-- Sheet Body --}} +
+ +
+
    +
  • + +
  • +
  • + +
  • + +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • +
    + {{editor content=data.description target="data.description" button=true owner=owner editable=editable}} +
    +
  • +
+
+ +
+
diff --git a/templates/npc-sheet.html b/templates/npc-sheet.html new file mode 100644 index 0000000..6bfa5f1 --- /dev/null +++ b/templates/npc-sheet.html @@ -0,0 +1,184 @@ +
+ + {{!-- Sheet Header --}} +
+
+
+ +

+
+
+
+ + {{!-- Sheet Tab Navigation --}} + + + {{!-- Sheet Body --}} +
+ + {{!-- Carac Tab --}} +
+ Unlocked/Locked{{#if editScore}}Unlocked{{else}}Locked{{/if}} + +
+
+ +

Type

+
+ + + +
+

Traits List

+
    + {{#each traits as |trait key|}} +
  • + + {{trait.name}} + {{trait.data.data.type}} +
    + + +
    +
  • + {{/each}} +
+
+ +
+ +
+

Stats & Numbers

+
    + {{#each data.spec as |spec key|}} +
  • + {{spec.label}} + +
  • + {{/each}} +
+
+ +
+
+ + + {{!-- Defence Tab --}} +
+
+ +
+ {{#each data.fight as |fight key|}} +
    +
  • + {{fight.label}} + +
  • + {{#each fight.derivated as |derivated keydev|}} +
  • + {{derivated.label}} + +
  • + {{/each}} +
+ {{/each}} +
+ +

Weapons

+ + +
+
+ + {{!-- Traits Tab --}} +
+ + +
+ + {{!-- Features Tab --}} +
+
+ +

Equipment

+ + +
+
+ + {{!-- Notes Tab --}} +
+
+

Description :

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

Notes :

+
+ {{editor content=data.notes target="data.notes" button=true owner=owner editable=editable}} +
+
+ {{>"systems/fvtt-fragged-kingdom/templates/editor-notes-gm.html"}} +
+
+ +
+
+ diff --git a/templates/partial-options-equipment-types.html b/templates/partial-options-equipment-types.html new file mode 100644 index 0000000..ab60653 --- /dev/null +++ b/templates/partial-options-equipment-types.html @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/templates/partial-options-level.html b/templates/partial-options-level.html new file mode 100644 index 0000000..902313d --- /dev/null +++ b/templates/partial-options-level.html @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/templates/partial-options-statistics.html b/templates/partial-options-statistics.html new file mode 100644 index 0000000..77d236e --- /dev/null +++ b/templates/partial-options-statistics.html @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/templates/post-item.html b/templates/post-item.html new file mode 100644 index 0000000..4680560 --- /dev/null +++ b/templates/post-item.html @@ -0,0 +1,28 @@ +
+

{{name}}

+ {{#if img}} + + {{/if}} + + {{#if (eq type "weapon")}} + {{#each data.weaponstats as |weaponstat rootkey|}} + {{#if weaponstat.deleted}} + {{else}} +

Stats for {{weaponstat.name}}

+ {{> "systems/fvtt-fragged-kingdom/templates/weapon-stats-section-tchat.html" stats=weaponstat.data.statstotal isfinal=false header=false}} + {{/if}} + {{/each}} + {{else}} + + {{#if data.statstotal}} + {{> "systems/fvtt-fragged-kingdom/templates/weapon-stats-section-tchat.html" stats=data.statstotal isfinal=false title="" header=true}} + {{else}} + {{#if data.stats}} + {{> "systems/fvtt-fragged-kingdom/templates/weapon-stats-section-tchat.html" stats=data.stats isfinal=false title="" header=true}} + {{/if}} + {{/if}} + {{/if}} + +

Description :

+

{{{data.description}}}

+
diff --git a/templates/roll-dialog-damage-chi.html b/templates/roll-dialog-damage-chi.html new file mode 100644 index 0000000..facf33f --- /dev/null +++ b/templates/roll-dialog-damage-chi.html @@ -0,0 +1,22 @@ +
+
+ +

{{title}}

+
+ +
+ +
+ Select Chi : + +
+ +
+ +
diff --git a/templates/roll-dialog-skill.html b/templates/roll-dialog-skill.html new file mode 100644 index 0000000..fa7d796 --- /dev/null +++ b/templates/roll-dialog-skill.html @@ -0,0 +1,46 @@ +
+
+ +

{{title}}

+
+ +
+
+ {{skillAttr.label}} : {{skillAttr.value}} + Skill Level : {{skill.data.level}} +
+
+ Specialties reminder : + {{skill.data.specialties}} +
+
+ Specialties Dices Bonus : + +
+
+ Negative Modifier : + +
+
+ Positive Modifier : + +
+ + +
+ +
diff --git a/templates/roll-dialog-weapon.html b/templates/roll-dialog-weapon.html new file mode 100644 index 0000000..2d3f5b4 --- /dev/null +++ b/templates/roll-dialog-weapon.html @@ -0,0 +1,56 @@ +
+ +
+ +

{{title}}

+
+ +
+ +
+ Select Skill : + +
+ +
+ Specialties reminder : + {{skill.data.specialties}} +
+ +
+ Specialties Dices Bonus : + +
+
+ Negative Modifier : + +
+
+ Positive Modifier : + +
+ + +
+ +