/* -------------------------------------------- */ 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); } } }