/* -------------------------------------------- */ import { PegasusUtility } from "./pegasus-utility.js"; import { PegasusRollDialog } from "./pegasus-roll-dialog.js"; /* -------------------------------------------- */ const coverBonusTable = { "nocover": 0, "lightcover": 2, "heavycover": 4, "entrenchedcover": 6 }; const statThreatLevel = ["agi", "str", "phy", "com", "def", "per"] const __subkey2title = { "melee-dmg": "Melee Damage", "melee-atk": "Melee Attack", "ranged-atk": "Ranged Attack", "ranged-dmg": "Ranged Damage", "defence": "Defence", "dmg-res": "Damare Resistance" } const __statBuild = [ { modules: ["vehiclehull"], field: "hr", itemfield: "hr" }, { modules: ["vehiclehull", "vehiclemodule"], field: "hr", itemfield: "size", subfield: "size" }, //{ modules: ["vehiclehull"], field: "pc", itemfield: "vms", subfield: "avgnrg" }, //{ modules: ["powercoremodule"], field: "pc", itemfield: "nrg", subfield: "avgnrg" }, { modules: ["vehiclehull", "mobilitymodule"], itemfield: "man", field: "man" }, { modules: ["powercoremodule"], field: "pc", itemfield: "pc", additionnal1: "curnrg", additionnal2: "maxnrg" }, { modules: ["mobilitymodule"], field: "mr", itemfield: "mr" }, { modules: ["propulsionmodule"], field: "ad", itemfield: "ad" }, { modules: ["combatmodule"], field: "fc", itemfield: "fc" }, ] const __isVehicleUnique = { vehiclehull: 1, powercoremodule: 1, mobilitymodule: 1, propulsionmodule: 1, combatmodule: 1 } const __speed2Num = { fullstop: 0, crawling: 1, slow: 2, average: 3, fast: 4, extfast: 5 } const __num2speed = ["fullstop", "crawling", "slow", "average", "fast", "extfast"] const __isVehicle = { vehiclehull: 1, powercoremodule: 1, mobilitymodule: 1, combatmodule: 1, propulsionmodule: 1, vehiclemodule: 1, vehicleweaponmodule: 1, effect: 1 } const __bonusEffect = { name: "Crawling MAN Bonus", type: "effect", img: "systems/fvtt-pegasus-rpg/images/icons/icon_effect.webp", system: { type: "physical", genre: "positive", effectlevel: 3, reducedicevalue: false, stataffected: "man", specaffected: [], statdice: false, bonusdice: true, weapondamage: false, hindrance: false, resistedby: "notapplicable", recoveryroll: false, recoveryrollstat: "", recoveryrollspec: [], effectstatlevel: false, effectstat: "", oneuse: false, ignorehealthpenalty: false, isthispossible: "", mentaldisruption: false, physicaldisruption: false, mentalimmunity: false, physicalimmunity: false, nobonusdice: false, noperksallowed: false, description: "", otherdice: false } } /* -------------------------------------------- */ /** * 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') { } if (data.type == 'npc') { } return super.create(data, options); } /* -------------------------------------------- */ prepareBaseData() { } /* -------------------------------------------- */ async prepareData() { super.prepareData(); } /* -------------------------------------------- */ prepareDerivedData() { if (!this.traumaState) { this.traumaState = "none" } if (this.type == 'character') { this.computeNRGHealth(); this.system.encCapacity = this.getEncumbranceCapacity() this.buildContainerTree() } if (this.type == 'vehicle') { this.computeVehicleStats(); } super.prepareDerivedData(); } /* -------------------------------------------- */ _preUpdate(changed, options, user) { super._preUpdate(changed, options, user); } /* -------------------------------------------- */ getEncumbranceCapacity() { return this.system.statistics.str.value * 25 } /* -------------------------------------------- */ getActivePerks() { let perks = this.items.filter(item => item.type == 'perk' && item.system.active); return perks; } /* -------------------------------------------- */ getAbilities() { let ab = this.items.filter(item => item.type == 'ability'); return ab; } /* -------------------------------------------- */ getPerks() { let comp = this.items.filter(item => item.type == 'perk'); return comp; } /* -------------------------------------------- */ getEffects() { let comp = this.items.filter(item => item.type == 'effect'); return comp; } /* -------------------------------------------- */ getCombatModules() { let comp = this.items.filter(item => item.type == 'combatmodule'); return comp; } /* -------------------------------------------- */ getVehicleHull() { let comp = this.items.filter(item => item.type == 'vehiclehull'); return comp; } /* -------------------------------------------- */ getPowercoreModules() { let comp = this.items.filter(item => item.type == 'powercoremodule'); return comp; } /* -------------------------------------------- */ getMobilityModules() { let comp = this.items.filter(item => item.type == 'mobilitymodule'); return comp; } /* -------------------------------------------- */ getPropulsionModules() { let comp = this.items.filter(item => item.type == 'propulsionmodule'); return comp; } /* -------------------------------------------- */ getVehicleModules() { let comp = this.items.filter(item => item.type == 'vehiclemodule'); return comp; } /* -------------------------------------------- */ getVehicleWeaponModules() { let comp = this.items.filter(item => item.type == 'vehicleweaponmodule'); return comp; } /* -------------------------------------------- */ getPowers() { let comp = this.items.filter(item => item.type == 'power'); return comp; } /* -------------------------------------------- */ getMoneys() { let comp = this.items.filter(item => item.type == 'money'); return comp; } /* -------------------------------------------- */ getVirtues() { let comp = this.items.filter(item => item.type == 'virtue'); return comp; } /* -------------------------------------------- */ getVices() { let comp = this.items.filter(item => item.type == 'vice'); return comp; } /* -------------------------------------------- */ getArmors() { let comp = duplicate(this.items.filter(item => item.type == 'armor') || []); return comp; } /* -------------------------------------------- */ getShields() { let comp = this.items.filter(item => item.type == 'shield') return comp; } getRace() { let race = this.items.filter(item => item.type == 'race') return race[0] ?? []; } getRole() { let role = this.items.filter(item => item.type == 'role') return role[0] ?? []; } /* -------------------------------------------- */ checkAndPrepareEquipment(item) { if (item.system.resistance) { item.system.resistanceDice = PegasusUtility.getDiceFromLevel(item.system.resistance) } if (item.system.idr) { item.system.idrDice = PegasusUtility.getDiceFromLevel(item.system.idr) } if (item.system.damage) { item.system.damageDice = PegasusUtility.getDiceFromLevel(item.system.damage) } if (item.system.level) { item.system.levelDice = PegasusUtility.getDiceFromLevel(item.system.level) } } /* -------------------------------------------- */ checkAndPrepareEquipments(listItem) { for (let item of listItem) { this.checkAndPrepareEquipment(item) } return listItem } /* -------------------------------------------- */ getWeapons() { let comp = duplicate(this.items.filter(item => item.type == 'weapon') || []); return comp; } /* -------------------------------------------- */ getItemById(id) { let item = this.items.find(item => item.id == id); if (item) { item = duplicate(item) if (item.type == 'specialisation') { item.system.dice = PegasusUtility.getDiceFromLevel(item.system.level); } } return item; } /* -------------------------------------------- */ getSpecs() { let comp = duplicate(this.items.filter(item => item.type == 'specialisation') || []); for (let c of comp) { c.system.dice = PegasusUtility.getDiceFromLevel(c.system.level); } return comp; } /* -------------------------------------------- */ async manageWorstFear(flag) { if (flag) { let effect = await PegasusUtility.getEffectFromCompendium("Worst Fear") effect.system.worstfear = true this.createEmbeddedDocuments('Item', [effect]) } else { let effect = this.items.find(item => item.type == "effect" && item.system.worstfear) if (effect) { this.deleteEmbeddedDocuments('Item', [effect.id]) } } } /* -------------------------------------------- */ async manageDesires(flag) { if (flag) { let effect = await PegasusUtility.getEffectFromCompendium("Desire") //console.log("EFFECT", effect) effect.system.desires = true this.createEmbeddedDocuments('Item', [effect]) } else { let effect = this.items.find(item => item.type == "effect" && item.system.desires) if (effect) { this.deleteEmbeddedDocuments('Item', [effect.id]) } } } /* -------------------------------------------- */ getRelevantSpec(statKey) { let comp = duplicate(this.items.filter(item => item.type == 'specialisation' && item.system.statistic == statKey) || []); for (let c of comp) { c.system.dice = PegasusUtility.getDiceFromLevel(c.system.level); } return comp; } /* -------------------------------------------- */ async activatePerk(perkId) { let item = this.items.find(item => item.id == perkId); if (item && item.system) { let update = { _id: item.id, "data.active": !item.system.active }; await this.updateEmbeddedDocuments('Item', [update]); // Updates one EmbeddedEntity } } /* -------------------------------------------- */ async activateViceOrVirtue(itemId) { let item = this.items.find(item => item.id == itemId) if (item && item.system) { let nrg = duplicate(this.system.nrg) if (!item.system.activated) { // Current value let effects = [] for (let effect of item.system.effectsgained) { effect.system.powerId = itemId // Link to the perk, in order to dynamically remove them effects.push(effect) } if (effects.length) { await this.createEmbeddedDocuments('Item', effects) } } else { let toRem = [] for (let item of this.items) { if (item.type == 'effect' && item.system.powerId == itemId) { toRem.push(item.id) } } if (toRem.length) { await this.deleteEmbeddedDocuments('Item', toRem) } } let update = { _id: item.id, "data.activated": !item.system.activated } await this.updateEmbeddedDocuments('Item', [update]) // Updates one EmbeddedEntity } } /* -------------------------------------------- */ async activatePower(itemId) { let item = this.items.find(item => item.id == itemId) if (item && item.system) { let nrg = duplicate(this.system.nrg) if (!item.system.activated) { // Current value if (item.system.costspent > nrg.value || item.system.costspent > nrg.max) { return ui.notifications.warn("Not enough NRG to activate the Power " + item.name) } nrg.activated += item.system.costspent nrg.value -= item.system.costspent nrg.max -= item.system.costspent await this.update({ 'system.nrg': nrg }) let effects = [] for (let effect of item.system.effectsgained) { effect.system.powerId = itemId // Link to the perk, in order to dynamically remove them effects.push(effect) } if (effects.length) { await this.createEmbeddedDocuments('Item', effects) } if (item.system.activatedtext.length > 0) { ChatMessage.create({ content: `Power ${item.name} activated : ${item.system.activatedtext}` }) } } else { nrg.activated -= item.system.costspent nrg.max += item.system.costspent await this.update({ 'system.nrg': nrg }) let toRem = [] for (let item of this.items) { if (item.type == 'effect' && item.system.powerId == itemId) { toRem.push(item.id) } } if (toRem.length) { await this.deleteEmbeddedDocuments('Item', toRem) } if (item.system.deactivatedtext.length > 0) { ChatMessage.create({ content: `Power ${item.name} deactivated : ${item.system.deactivatedtext}` }) } } let update = { _id: item.id, "data.activated": !item.system.activated } await this.updateEmbeddedDocuments('Item', [update]) // Updates one EmbeddedEntity } } /* -------------------------------------------- */ async equipItem(itemId) { let item = this.items.find(item => item.id == itemId); if (item && item.system) { let update = { _id: item.id, "data.equipped": !item.system.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.items.filter(item => item.type == 'shield' || item.type == 'armor' || item.type == "weapon" || item.type == "equipment"); } /* ------------------------------------------- */ getEquipmentsOnly() { return duplicate(this.items.filter(item => item.type == "equipment") || []) } /* ------------------------------------------- */ computeThreatLevel() { let tl = 0 for (let key of statThreatLevel) { // Init with concerned stats tl += PegasusUtility.getDiceValue(this.system.statistics[key].value) } let powers = duplicate(this.getPowers() || []) if (powers.length > 0) { // Then add some mental ones of powers tl += PegasusUtility.getDiceValue(this.system.statistics.foc.value) tl += PegasusUtility.getDiceValue(this.system.statistics.mnd.value) } tl += PegasusUtility.getDiceValue(this.system.mr.value) let specThreat = this.items.filter(it => it.type == "specialisation" && it.system.isthreatlevel) || [] for (let spec of specThreat) { tl += PegasusUtility.getDiceValue(spec.system.level) } tl += this.system.nrg.absolutemax + this.system.secondary.health.max + this.system.secondary.delirium.max tl += this.getPerks().length * 5 let weapons = this.getWeapons() for (let weapon of weapons) { tl += PegasusUtility.getDiceValue(weapon.system.damage) } let armors = this.getArmors() for (let armor of armors) { tl += PegasusUtility.getDiceValue(armor.system.resistance) } let shields = duplicate(this.getShields()) for (let shield of shields) { tl += PegasusUtility.getDiceValue(shield.system.level) } let abilities = duplicate(this.getAbilities()) for (let ability of abilities) { tl += ability.system.threatlevel } let equipments = this.getEquipmentsOnly() for (let equip of equipments) { tl += equip.system.threatlevel } if (tl != this.system.biodata.threatlevel) { this.update({ 'system.biodata.threatlevel': tl }) } } /* ------------------------------------------- */ async buildContainerTree() { let equipments = duplicate(this.items.filter(item => item.type == "equipment") || []) for (let equip1 of equipments) { if (equip1.system.iscontainer) { equip1.system.contents = [] equip1.system.contentsEnc = 0 for (let equip2 of equipments) { if (equip1._id != equip2._id && equip2.system.containerid == equip1._id) { equip1.system.contents.push(equip2) let q = equip2.system.quantity ?? 1 equip1.system.contentsEnc += q * equip2.system.weight } } } } // Compute whole enc let enc = 0 for (let item of equipments) { item.system.idrDice = PegasusUtility.getDiceFromLevel(Number(item.system.idr)) if (item.system.equipped) { if (item.system.iscontainer) { enc += item.system.contentsEnc } else if (item.system.containerid == "") { let q = item.system.quantity ?? 1 enc += q * item.system.weight } } } for (let item of this.items) { // Process items/shields/armors if ((item.type == "weapon" || item.type == "shield" || item.type == "armor") && item.system.equipped) { let q = item.system.quantity ?? 1 enc += q * item.system.weight } } // Store local values this.encCurrent = enc this.containersTree = equipments.filter(item => item.system.containerid == "") // Returns the root of equipements without container // Manages slow effect let overCapacity = Math.floor(this.encCurrent / this.getEncumbranceCapacity()) this.encHindrance = Math.floor(this.encCurrent / this.getEncumbranceCapacity()) //console.log("Capacity", overCapacity, this.encCurrent / this.getEncumbranceCapacity() ) let effect = this.items.find(item => item.type == "effect" && item.system.slow) if (overCapacity >= 4) { if (!effect) { effect = await PegasusUtility.getEffectFromCompendium("Slowed") effect.system.slow = true this.createEmbeddedDocuments('Item', [effect]) } } else { if (effect) { this.deleteEmbeddedDocuments('Item', [effect.id]) } } } /* -------------------------------------------- */ modifyStun(incDec) { let combat = duplicate(this.system.combat) combat.stunlevel += incDec if (combat.stunlevel >= 0) { this.update({ 'system.combat': combat }) let chatData = { user: game.user.id, rollMode: game.settings.get("core", "rollMode"), whisper: [game.user.id].concat(ChatMessage.getWhisperRecipients('GM')) } if (incDec > 0) { chatData.content = `
${this.name} suffered a Stun level.${this.name} recovered a Stun level. 0) { ChatMessage.create({ content: `${this.name} Stun threshold has been exceeded.` }) } if (incDec > 0 && stunAbove > 0) { let delirium = duplicate(this.system.secondary.delirium) delirium.value -= incDec this.update({ 'system.secondary.delirium': delirium }) } } /* -------------------------------------------- */ modifyMomentum(incDec) { let momentum = duplicate(this.system.momentum) momentum.value += incDec this.update({ 'system.momentum': momentum }) let chatData = { user: game.user.id, rollMode: game.settings.get("core", "rollMode"), whisper: [game.user.id].concat(ChatMessage.getWhisperRecipients('GM')) } if (incDec > 0) { chatData.content = `
${this.name} has gained a Momentum${this.name} has used a Momentum= 0) { PegasusUtility.showMomentumDialog(this.id) } } /* -------------------------------------------- */ 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.system.label == label); } /* -------------------------------------------- */ getEffectById(id) { return this.getActiveEffects().find(it => it.id == id); } /* -------------------------------------------- */ getAttribute(attrKey) { return this.system.attributes[attrKey]; } /* -------------------------------------------- */ async addObjectToContainer(itemId, containerId) { let container = this.items.find(item => item.id == containerId && item.system.iscontainer) let object = this.items.find(item => item.id == itemId) if (container) { if (object.system.iscontainer) { ui.notifications.warn("Only 1 level of container allowed") return } let alreadyInside = this.items.filter(item => item.system.containerid && item.system.containerid == containerId); if (alreadyInside.length >= container.system.containercapacity) { ui.notifications.warn("Container is already full !") return } else { await this.updateEmbeddedDocuments("Item", [{ _id: object.id, 'system.containerid': containerId }]) } } else if (object && object.system.containerid) { // remove from container console.log("Removeing: ", object) await this.updateEmbeddedDocuments("Item", [{ _id: object.id, 'system.containerid': "" }]); } } /* -------------------------------------------- */ checkVirtue(virtue) { let vices = this.getVices() for (let vice of vices) { let nonVirtues = vice.system.unavailablevirtue for (let blockedVirtue of nonVirtues) { if (blockedVirtue.name.toLowerCase() == virtue.name.toLowerCase()) { return false } } } return true } /* -------------------------------------------- */ checkVice(vice) { let virtues = this.getVirtues() for (let virtue of virtues) { let nonVices = virtue.system.unavailablevice for (let blockedVice of nonVices) { if (blockedVice.name.toLowerCase() == vice.name.toLowerCase()) { return false } } } return true } /* -------------------------------------------- */ async preprocessItem(event, item, onDrop = false) { console.log("Pre-process", item) if (item.type != "effect" && __isVehicle[item.type]) { ui.notifications.warn("You can't drop Vehicles item over a character sheet.") return } // Pre-filter effects if (item.type == 'effect') { if (this.checkMentalDisruption() && item.system.type == "mental" && item.system.genre == "positive") { ChatMessage.create({ content: "Effects of this type cannot be applied while Disruption is applied, Use a Soft Action to remove Disruption" }) return } if (this.checkPhysicalDisruption() && item.system.type == "physical" && item.system.genre == "positive") { ChatMessage.create({ content: "Effects of this type cannot be applied while Disruption is applied, Use a Soft Action to remove Disruption" }) return } if (this.checkMentalImmunity() && item.system.type == "mental" && item.system.genre == "negative") { ChatMessage.create({ content: "Effects of this type cannot be applied while Immunity is applied" }) return } if (this.checkPhysicalImmunity() && item.system.type == "physical" && item.system.genre == "negative") { ChatMessage.create({ content: "Effects of this type cannot be applied while Immunity is applied" }) return } } if (item.type == 'race') { this.applyRace(item) } else if (item.type == 'role') { this.applyRole(item) } else if (item.type == 'ability') { this.applyAbility(item, [], true) if (!onDrop) { await this.createEmbeddedDocuments('Item', [item]) } } else { if (!onDrop) { await this.createEmbeddedDocuments('Item', [item]) } } // Check virtue/vice validity if (item.type == "virtue") { if (!this.checkVirtue(item)) { ui.notifications.info("Virtue is not allowed due to Vice.") return false } } if (item.type == "vice") { if (!this.checkVice(item)) { ui.notifications.info("Vice is not allowed due to Virtue.") return false } } if (item.type == "power" && item.system.purchasedtext.length > 0) { ChatMessage.create({ content: `Power ${item.name} puchased : ${item.system.purchasedtext}` }) } let dropID = $(event.target).parents(".item").attr("data-item-id") // Only relevant if container drop let objectID = item.id || item._id this.addObjectToContainer(objectID, dropID) return true } /* -------------------------------------------- */ async equipGear(equipmentId) { let item = this.items.find(item => item.id == equipmentId); if (item && item.system) { let update = { _id: item.id, "data.equipped": !item.system.equipped }; await this.updateEmbeddedDocuments('Item', [update]); // Updates one EmbeddedEntity } } /* -------------------------------------------- */ getInitiativeScore(combatId, combatantId) { if (this.type == 'character') { this.rollMR(true, combatId, combatantId) } console.log("Init required !!!!") return -1; } /* -------------------------------------------- */ getSubActors() { let subActors = []; for (let id of this.system.subactors) { subActors.push(duplicate(game.actors.get(id))) } return subActors; } /* -------------------------------------------- */ async addSubActor(subActorId) { let subActors = duplicate(this.system.subactors); subActors.push(subActorId); await this.update({ 'system.subactors': subActors }); } /* -------------------------------------------- */ async delSubActor(subActorId) { let newArray = []; for (let id of this.system.subactors) { if (id != subActorId) { newArray.push(id); } } await this.update({ 'system.subactors': newArray }); } /* -------------------------------------------- */ syncRoll(rollData) { let linkedRollId = PegasusUtility.getDefenseState(this.id); if (linkedRollId) { rollData.linkedRollId = linkedRollId; } this.lastRollId = rollData.rollId; PegasusUtility.saveRollData(rollData); } /* -------------------------------------------- */ getStat(statKey) { let stat if (this.type == "character" && statKey == 'mr') { stat = duplicate(this.system.mr); } else { stat = duplicate(this.system.statistics[statKey]); } stat.dice = PegasusUtility.getDiceFromLevel(stat.value || stat.level); return stat; } /* -------------------------------------------- */ getOneSpec(specId) { let spec = this.items.find(item => item.type == 'specialisation' && item.id == specId) if (spec) { spec = duplicate(spec); spec.system.dice = PegasusUtility.getDiceFromLevel(spec.system.level); } return spec; } /* -------------------------------------------- */ specPowerActivate(specId) { let spec = this.getOneSpec(specId) if (spec) { let powers = [] for (let power of spec.system.powers) { if (power.data) { power.system = power.data } power.system.specId = specId powers.push(power) } if (powers.length > 0) { this.createEmbeddedDocuments('Item', powers) } this.updateEmbeddedDocuments('Item', [{ _id: specId, 'system.powersactivated': true }]) } } /* -------------------------------------------- */ specPowerDeactivate(specId) { let toRem = [] for (let power of this.items) { if (power.type == "power" && power.system.specId && power.system.specId == specId) { toRem.push(power.id) } } if (toRem.length > 0) { this.deleteEmbeddedDocuments('Item', toRem) } this.updateEmbeddedDocuments('Item', [{ _id: specId, 'system.powersactivated': false }]) } /* -------------------------------------------- */ equipActivate(itemId) { let item = this.items.get(itemId) if (item) { let effects = [] for (let effect of item.system.effects) { effect.system.itemId = itemId // Keep link effects.push(effect) } if (effects.length > 0) { this.createEmbeddedDocuments('Item', effects) } this.updateEmbeddedDocuments('Item', [{ _id: itemId, 'system.activated': true }]) } } /* -------------------------------------------- */ equipDeactivate(itemId) { let toRem = [] for (let item of this.items) { if (item.system.itemId && item.system.itemId == itemId) { toRem.push(item.id) } } if (toRem.length > 0) { this.deleteEmbeddedDocuments('Item', toRem) } this.updateEmbeddedDocuments('Item', [{ _id: itemId, 'system.activated': false }]) } /* -------------------------------------------- */ async perkEffectUsed(itemId) { let effect = this.items.get(itemId) if (effect) { PegasusUtility.createChatWithRollMode(effect.name, { content: await renderTemplate(`systems/fvtt-pegasus-rpg/templates/chat-effect-used.html`, effect.data) }); this.deleteEmbeddedDocuments('Item', [effect.id]) } } /* -------------------------------------------- */ disableWeaverPerk(perk) { if (perk.system.isweaver) { for (let spec of this.items) { if (spec.type == 'specialisation' && spec.system.ispowergroup) { this.specPowerDeactivate(spec.id) } } } } /* -------------------------------------------- */ enableWeaverPerk(perk) { if (perk.system.isweaver) { for (let spec of this.items) { if (spec.type == 'specialisation' && spec.system.ispowergroup) { this.specPowerActivate(spec.id) } } } } /* -------------------------------------------- */ async cleanPerkEffects(itemId) { let effects = [] for (let item of this.items) { if (item.type == "effect" && item.system.perkId == itemId) { effects.push(item.id) } } if (effects.length > 0) { console.log("DELET!!!!", effects, this) await this.deleteEmbeddedDocuments('Item', effects) } } /* -------------------------------------------- */ async updatePerkUsed(itemId, index, checked) { let item = this.items.get(itemId) if (item && index) { let key = "data.used" + index await this.updateEmbeddedDocuments('Item', [{ _id: itemId, [`${key}`]: checked }]) item = this.items.get(itemId) // Refresh if (item.system.nbuse == "next1action" && item.system.used1) { this.cleanPerkEffects(itemId) } if (item.system.nbuse == "next2action" && item.system.used1 && item.system.used2) { this.cleanPerkEffects(itemId) } if (item.system.nbuse == "next3action" && item.system.used1 && item.system.used2 && item.system.used3) { this.cleanPerkEffects(itemId) } } } /* -------------------------------------------- */ async updatePowerSpentCost(itemId, value) { let item = this.items.get(itemId) if (item && value) { value = Number(value) || 0 await this.updateEmbeddedDocuments('Item', [{ _id: itemId, 'system.costspent': value }]) } } /* -------------------------------------------- */ async cleanupPerksIfTrauma() { if (this.getTraumaState() == "severetrauma") { for (let perk of this.items) { if (perk.type == "perk") { this.cleanPerkEffects(perk.id) this.updateEmbeddedDocuments('Item', [{ _id: perk.id, 'system.status': "ready", 'system.used1': false, 'system.used2': false, 'system.used3': false }]) ChatMessage.create({ content: `Perk ${perk.name} has been deactivated due to Severe Trauma state !` }) } } } } /* -------------------------------------------- */ incDecNRG(value) { if (this.type == "character") { let nrg = duplicate(this.system.nrg) nrg.value += value if (nrg.value >= 0 && nrg.value <= nrg.max) { this.update({ 'system.nrg': nrg }) } } else { let pc = duplicate(this.system.statistics.pc) pc.curnrg += value if (pc.curnrg >= 0 && pc.curnrg <= pc.maxnrg) { this.update({ 'system.statistics.pc': pc }) } } } /* -------------------------------------------- */ async updatePerkStatus(itemId, status) { let item = this.items.get(itemId) if (item) { if (item.system.status == status) return;// Ensure we are really changing the status if (this.checkNoPerksAllowed()) { await this.updateEmbeddedDocuments('Item', [{ _id: item.id, 'system.status': "ready" }]) ChatMessage.create({ content: "No perks activation allowed due to effect !" }) return } // Severe Trauma management if (this.getTraumaState() == "severetrauma") { if (!this.severeTraumaMessage) { let chatData = { user: game.user.id, rollMode: game.settings.get("core", "rollMode"), whisper: [game.user.id].concat(ChatMessage.getWhisperRecipients('GM')) } chatData.content = `
${this.name} is suffering from Severe Trauma and cannot use Perks at this time.= item.system.features.nrgcost.value) && (this.system.nrg.max >= item.system.features.nrgcost.value)) { let nrg = duplicate(this.system.nrg) nrg.activated += item.system.features.nrgcost.value nrg.value -= item.system.features.nrgcost.value nrg.max -= item.system.features.nrgcost.value await this.update({ 'system.nrg': nrg }) } else { updateOK = false ui.notifications.warn("Not enough NRG to activate the Perk " + item.name) } } if (item.system.features.bonushealth.flag) { let health = duplicate(this.system.secondary.health) health.value += Number(item.system.features.bonushealth.value) || 0 health.max += Number(item.system.features.bonushealth.value) || 0 await this.update({ 'system.secondary.health': health }) } if (item.system.features.bonusdelirium.flag) { let delirium = duplicate(this.system.secondary.delirium) delirium.value += Number(item.system.features.bonusdelirium.value) || 0 delirium.max += Number(item.system.features.bonusdelirium.value) || 0 await this.update({ 'system.secondary.delirium': delirium }) } if (item.system.features.bonusnrg.flag) { let nrg = duplicate(this.system.nrg) nrg.value += Number(item.system.features.bonusnrg.value) || 0 nrg.max += Number(item.system.features.bonusnrg.value) || 0 await this.update({ 'system.nrg': nrg }) } PegasusUtility.createChatWithRollMode(item.name, { content: await renderTemplate(`systems/fvtt-pegasus-rpg/templates/chat-perk-activated.html`, { name: this.name, perk: duplicate(item) }) }) this.enableWeaverPerk(item) } if (updateOK) { await this.updateEmbeddedDocuments('Item', [{ _id: item.id, 'system.status': status }]) } } } /* -------------------------------------------- */ async deleteAllItemsByType(itemType) { let items = this.items.filter(item => item.type == itemType); await this.deleteEmbeddedDocuments('Item', items); } /* -------------------------------------------- */ async addItemWithoutDuplicate(newItem) { let item = this.items.find(item => item.type == newItem.type && item.name.toLowerCase() == newItem.name.toLowerCase()) if (!item) { await this.createEmbeddedDocuments('Item', [newItem]); } } /* -------------------------------------------- */ getTraumaState() { this.traumaState = "none" if (this.type == "character") { let negDelirium = -Math.floor((this.system.secondary.delirium.max + 1) / 2) if (this.type == "character") { if (this.system.secondary.delirium.value <= 0 && this.system.secondary.delirium.value >= negDelirium) { this.traumaState = "trauma" } if (this.system.secondary.delirium.value < negDelirium) { this.traumaState = "severetrauma" } } } return this.traumaState } /* -------------------------------------------- */ getLevelRemaining() { return this.system.biodata.currentlevelremaining } /* -------------------------------------------- */ modifyHeroLevelRemaining(incDec) { let biodata = duplicate(this.system.biodata) biodata.currentlevelremaining = Math.max(biodata.currentlevelremaining + incDec, 0) this.update({ "system.biodata": biodata }) ChatMessage.create({ content: `${this.name} has used a Hero Level to reroll !` }) return biodata.currentlevelremaining } /* -------------------------------------------- */ checkIgnoreHealth() { for (let effect of this.items) { if (effect.type == "effect" && effect.system.ignorehealthpenalty) { return true } } return false } /* -------------------------------------------- */ checkMentalDisruption() { for (let effect of this.items) { if (effect.type == "effect" && effect.system.mentaldisruption) { return true } } return false } /* -------------------------------------------- */ checkPhysicalDisruption() { for (let effect of this.items) { if (effect.type == "effect" && effect.system.physicaldisruption) { return true } } return false } /* -------------------------------------------- */ checkMentalImmunity() { for (let effect of this.items) { if (effect.type == "effect" && effect.system.mentalimmunity) { return true } } return false } /* -------------------------------------------- */ checkPhysicalImmunity() { for (let effect of this.items) { if (effect.type == "effect" && effect.system.physicalimmunity) { return true } } return false } /* -------------------------------------------- */ checkNoBonusDice() { for (let effect of this.items) { if (effect.type == "effect" && effect.system.nobonusdice) { return true } } return false } /* -------------------------------------------- */ checkNoPerksAllowed() { for (let effect of this.items) { if (effect.type == "effect" && effect.system.noperksallowed) { return true } } return false } /* -------------------------------------------- */ checkIfPossible() { for (let effect of this.items) { if (effect.type == "effect" && effect.system.isthispossible.length > 0) { ChatMessage.create({ content: effect.system.isthispossible }) } } } /* -------------------------------------------- */ async computeNRGHealth() { if (this.isOwner || game.user.isGM) { let updates = {} let phyDiceValue = PegasusUtility.getDiceValue(this.system.statistics.phy.value) + this.system.secondary.health.bonus + this.system.statistics.phy.mod; if (phyDiceValue != this.system.secondary.health.max) { updates['system.secondary.health.max'] = phyDiceValue } if (this.computeValue) { updates['system.secondary.health.value'] = phyDiceValue } let mndDiceValue = PegasusUtility.getDiceValue(this.system.statistics.mnd.value) + this.system.secondary.delirium.bonus + this.system.statistics.mnd.mod; if (mndDiceValue != this.system.secondary.delirium.max) { updates['system.secondary.delirium.max'] = mndDiceValue } if (this.computeValue) { updates['system.secondary.delirium.value'] = mndDiceValue } let stlDiceValue = PegasusUtility.getDiceValue(this.system.statistics.stl.value) + this.system.secondary.stealthhealth.bonus + this.system.statistics.stl.mod; if (stlDiceValue != this.system.secondary.stealthhealth.max) { updates['system.secondary.stealthhealth.max'] = stlDiceValue } if (this.computeValue) { updates['system.secondary.stealthhealth.value'] = stlDiceValue } let socDiceValue = PegasusUtility.getDiceValue(this.system.statistics.soc.value) + this.system.secondary.socialhealth.bonus + this.system.statistics.soc.mod; if (socDiceValue != this.system.secondary.socialhealth.max) { updates['system.secondary.socialhealth.max'] = socDiceValue } if (this.computeValue) { updates['system.secondary.socialhealth.value'] = socDiceValue } let nrgValue = PegasusUtility.getDiceValue(this.system.statistics.foc.value) + this.system.nrg.mod + this.system.statistics.foc.mod if (nrgValue != this.system.nrg.absolutemax) { updates['system.nrg.absolutemax'] = nrgValue } if (this.computeValue) { updates['system.nrg.max'] = nrgValue updates['system.nrg.value'] = nrgValue } let stunth = PegasusUtility.getDiceValue(this.system.statistics.phy.value) + PegasusUtility.getDiceValue(this.system.statistics.mnd.value) + PegasusUtility.getDiceValue(this.system.statistics.foc.value) + this.system.statistics.mnd.mod + this.system.statistics.phy.mod + this.system.statistics.foc.mod if (stunth != this.system.combat.stunthreshold) { updates['system.combat.stunthreshold'] = stunth } let momentum = this.system.statistics.foc.value + this.system.statistics.foc.mod if (momentum != this.system.momentum.max) { updates['system.momentum.value'] = 0 updates['system.momentum.max'] = momentum } let mrLevel = (this.system.statistics.agi.value + this.system.statistics.str.value) - this.system.statistics.phy.value mrLevel = (mrLevel < 1) ? 1 : mrLevel; if (mrLevel != this.system.mr.value) { updates['system.mr.value'] = mrLevel } let moralitythreshold = - (Number(PegasusUtility.getDiceValue(this.system.statistics.foc.value)) + Number(this.system.statistics.foc.mod)) if (moralitythreshold != this.system.biodata.moralitythreshold) { updates['system.biodata.moralitythreshold'] = moralitythreshold } if (!this.isToken) { if (this.warnMorality != this.system.biodata.morality && this.system.biodata.morality < 0) { console.log("CHAR", this) ChatMessage.create({ content: "WARNING: Your character is dangerously close to becoming corrupted and defeated. Start on a path of redemption!" }) } if (this.warnMorality != this.system.biodata.morality) { this.warnMorality = this.system.biodata.morality } } let race = this.getRace() if (race && race.name && (race.name != this.system.biodata.racename)) { updates['system.biodata.racename'] = race.name } let role = this.getRole() if (role && role.name && (role.name != this.system.biodata.rolename)) { updates['system.biodata.rolename'] = role.name } if (Object.entries(updates).length > 0) { await this.update(updates) } this.computeThreatLevel() } if (this.isOwner || game.user.isGM) { // Update current hindrance level let hindrance = this.system.combat.hindrancedice if (!this.checkIgnoreHealth()) { if (this.system.secondary.health.value < 0) { if (this.system.secondary.health.value < -Math.floor((this.system.secondary.health.max + 1) / 2)) { // Severe wounded hindrance += 3 } else { hindrance += 1 } } } this.system.combat.hindrancedice = hindrance this.getTraumaState() this.cleanupPerksIfTrauma() } } /* -------------------------------------------- */ async modStat(key, inc = 1) { let stat = duplicate(this.system.statistics[key]) stat.mod += parseInt(inc) await this.update({ [`data.statistics.${key}`]: stat }) } /* -------------------------------------------- */ async valueStat(key, inc = 1) { key = key.toLowerCase() let stat = duplicate(this.system.statistics[key]) stat.value += parseInt(inc) await this.update({ [`data.statistics.${key}`]: stat }) } /* -------------------------------------------- */ async addIncSpec(spec, inc = 1) { console.log("Using spec : ", spec, inc) let specExist = this.items.find(item => item.type == 'specialisation' && item.name.toLowerCase() == spec.name.toLowerCase()) if (specExist) { specExist = duplicate(specExist) specExist.system.level += inc; let update = { _id: specExist._id, "data.level": specExist.system.level }; await this.updateEmbeddedDocuments('Item', [update]); } else { spec.system.level += inc; await this.createEmbeddedDocuments('Item', [spec]); } } /* -------------------------------------------- */ async incDecQuantity(objetId, incDec = 0) { let objetQ = this.items.get(objetId) if (objetQ) { let newQ = objetQ.system.quantity + incDec if (newQ >= 0) { const updated = await this.updateEmbeddedDocuments('Item', [{ _id: objetQ.id, 'system.quantity': newQ }]) // pdates one EmbeddedEntity } } } /* -------------------------------------------- */ async incDecAmmo(objetId, incDec = 0) { let objetQ = this.items.get(objetId) if (objetQ) { let newQ = objetQ.system.ammocurrent + incDec; if (newQ >= 0 && newQ <= objetQ.system.ammomax) { const updated = await this.updateEmbeddedDocuments('Item', [{ _id: objetQ.id, 'system.ammocurrent': newQ }]); // pdates one EmbeddedEntity } } } /* -------------------------------------------- */ async applyAbility(ability, updates = [], directUpdate = false) { // manage stat bonus if (ability.system.affectedstat != "notapplicable") { let stat = duplicate(this.system.statistics[ability.system.affectedstat]) stat.mod += Number(ability.system.statmodifier) updates[`system.statistics.${ability.system.affectedstat}`] = stat } // manage status bonus if (ability.system.statusaffected != "notapplicable") { if (ability.system.statusaffected == 'nrg') { let nrg = duplicate(this.system.nrg) nrg.mod += Number(ability.system.statusmodifier) updates[`system.nrg`] = nrg } if (ability.system.statusaffected == 'health') { let health = duplicate(this.system.secondary.health) health.bonus += Number(ability.system.statusmodifier) updates[`system.secondary.health`] = health } if (ability.system.statusaffected == 'delirium') { let delirium = duplicate(this.system.secondary.delirium) delirium.bonus += Number(ability.system.statusmodifier) updates[`system.secondary.delirium`] = delirium } } if (directUpdate) { await this.update(updates) } let newItems = [] if (ability.system.effectsgained) { for (let effect of ability.system.effectsgained) { if (!effect.system) effect.system = effect.data newItems.push(effect); } } if (ability.system.powersgained) { for (let power of ability.system.powersgained) { if (!power.system) power.system = power.data newItems.push(power); } } if (ability.system.specialisations) { for (let spec of ability.system.specialisations) { if (!spec.system) spec.system = spec.data newItems.push(spec); } } if (ability.system.attackgained) { for (let weapon of ability.system.attackgained) { if (!weapon.system) weapon.system = weapon.data newItems.push(weapon); } } if (ability.system.armorgained) { for (let armor of ability.system.armorgained) { if (!armor.system) armor.system = armor.data newItems.push(armor); } } console.log("Ability : adding", newItems) await this.createEmbeddedDocuments('Item', newItems) } /* -------------------------------------------- */ async applyRace(race) { let updates = { 'system.biodata.racename': race.name } let newItems = [] await this.deleteAllItemsByType('race') newItems.push(race); console.log("DROPPED RACE", race) for (let ability of race.system.abilities) { if (!ability.system) ability.system = ability.data newItems.push(ability) this.applyAbility(ability, updates) } if (race.system.perksgained) { for (let power of race.system.perks) { if (!power.system) power.system = power.data newItems.push(power); } } await this.update(updates) await this.createEmbeddedDocuments('Item', newItems) console.log("Updates", updates, newItems) console.log("Updated actor", this) } /* -------------------------------------------- */ getIncreaseStatValue(updates, statKey) { let stat = duplicate(this.system.statistics[statKey]) stat.value += 1; updates[`data.statistics.${statKey}`] = stat } /* -------------------------------------------- */ async applyRole(role) { console.log("ROLE", role) let updates = { 'system.biodata.rolename': role.name } let newItems = [] await this.deleteAllItemsByType('role') newItems.push(role) this.getIncreaseStatValue(updates, role.system.statincrease1) this.getIncreaseStatValue(updates, role.system.statincrease2) if (role.system.specialability.length > 0) { //console.log("Adding ability", role.system.specialability) newItems = newItems.concat(duplicate(role.system.specialability)) // Add new ability this.applyAbility(role.system.specialability[0], newItems) } await this.update(updates) await this.createEmbeddedDocuments('Item', newItems) } /* -------------------------------------------- */ addHindrancesList(effectsList) { if (this.type == "character") { if (this.system.combat.stunlevel > 0) { effectsList.push({ label: "Stun Hindrance", type: "hindrance", foreign: true, actorId: this.id, applied: false, value: 2 }) } if (this.system.combat.hindrancedice > 0) { effectsList.push({ label: "Wounds Hindrance", type: "hindrance", foreign: true, actorId: this.id, applied: false, value: this.system.combat.hindrancedice }) } let overCapacity = Math.floor(this.encCurrent / this.getEncumbranceCapacity()) if (overCapacity > 0) { effectsList.push({ label: "Encumbrance Hindrance", type: "hindrance", foreign: true, actorId: this.id, applied: false, value: overCapacity }) } if (this.system.biodata.morality <= 0) { effectsList.push({ label: "Morality Hindrance", type: "hindrance", foreign: true, actorId: this.id, applied: false, value: 3 }) } let effects = this.items.filter(item => item.type == 'effect') for (let effect of effects) { effect = duplicate(effect) if (effect.system.hindrance) { effectsList.push({ label: effect.name, type: "effect", foreign: true, actorId: this.id, applied: false, effect: effect, value: effect.system.effectlevel }) } } } if (this.type == "vehicle") { if (this.system.stun.value > 0) { effectsList.push({ label: "Stun Hindrance", type: "hindrance", foreign: true, actorId: this.id, applied: false, value: 2 }) } if (this.isVehicleCrawling()) { effectsList.push({ label: "Crawling Hindrance", type: "hindrance", foreign: true, actorId: this.id, applied: false, value: 3 }) } if (this.isVehicleSlow()) { effectsList.push({ label: "Slow Hindrance", type: "hindrance", foreign: true, actorId: this.id, applied: false, value: 1 }) } if (this.isVehicleAverage()) { effectsList.push({ label: "Average Hindrance", type: "hindrance", foreign: true, actorId: this.id, applied: false, value: 1 }) } if (this.isVehicleFast()) { effectsList.push({ label: "Fast Hindrance", type: "hindrance", foreign: true, actorId: this.id, applied: false, value: 3 }) } if (this.isVehicleExFast()) { effectsList.push({ label: "Ext. Fast Hindrance", type: "hindrance", foreign: true, actorId: this.id, applied: false, value: 5 }) } } } /* -------------------------------------------- */ /* ROLL SECTION /* -------------------------------------------- */ pushEffect(rollData, effect) { if (this.getTraumaState() == "none" && !this.checkNoBonusDice()) { rollData.effectsList.push({ label: effect.name, type: "effect", applied: false, effect: effect, value: effect.system.effectlevel }) } else { if (!effect.system.bonusdice) { // Do not push bonus dice effect when TraumaState is activated rollData.effectsList.push({ label: effect.name, type: "effect", applied: false, effect: effect, value: effect.system.effectlevel }) } } } /* -------------------------------------------- */ addEffects(rollData, isInit = false, isPower = false, isPowerDmg = false) { let effects = this.items.filter(item => item.type == 'effect') for (let effect of effects) { effect = duplicate(effect) if (!effect.system.hindrance && (effect.system.stataffected != "notapplicable" || effect.system.specaffected.length > 0) && effect.system.stataffected != "special" && effect.system.stataffected != "powerroll" && effect.system.stataffected != "powerdmgroll") { if (effect.system.effectstatlevel) { effect.system.effectlevel = this.system.statistics[effect.system.effectstat].value } this.pushEffect(rollData, effect) } if (isPower && effect.system.stataffected == "powerroll") { this.pushEffect(rollData, effect) } if (isPowerDmg && effect.system.stataffected == "powerdmgroll") { this.pushEffect(rollData, effect) } } } /* -------------------------------------------- */ addArmorsShields(rollData, statKey = "none", useShield = false) { if (statKey == 'phy') { let armors = this.getArmors() for (let armor of armors) { rollData.armorsList.push({ label: `Armor ${armor.name}`, type: "armor", applied: false, value: armor.system.resistance }) } } if (useShield) { let shields = this.items.filter(item => item.type == "shield" && item.system.equipped) for (let sh of shields) { rollData.armorsList.push({ label: `Shield ${sh.name}`, type: "shield", applied: false, value: sh.system.level }) } } } addWeapons(rollData, statKey) { let weapons = this.getWeapons() for (let weapon of weapons) { if (weapon.system.equipped && weapon.system.statistic == statKey) { rollData.weaponsList.push({ label: `Attack ${weapon.name}`, type: "attack", applied: false, weapon: weapon, value: 0, damageDice: PegasusUtility.getDiceFromLevel(0) }) } if (weapon.system.equipped && weapon.system.canbethrown && statKey == "agi") { rollData.weaponsList.push({ label: `Attack ${weapon.name}`, type: "attack", applied: false, weapon: weapon, value: 0, damageDice: PegasusUtility.getDiceFromLevel(0) }) } if (weapon.system.equipped && weapon.system.enhanced && weapon.system.enhancedstat == statKey) { rollData.weaponsList.push({ label: `Enhanced Attack ${weapon.name}`, type: "enhanced", applied: false, weapon: weapon, value: weapon.system.enhancedlevel, damageDice: PegasusUtility.getDiceFromLevel(weapon.system.enhancedlevel) }) } if (weapon.system.equipped && weapon.system.damagestatistic == statKey) { rollData.weaponsList.push({ label: `Damage ${weapon.name}`, type: "damage", applied: false, weapon: weapon, value: weapon.system.damage, damageDice: PegasusUtility.getDiceFromLevel(weapon.system.damage) }) } } } addEquipments(rollData, statKey) { let equipments = this.getEquipmentsOnly() for (let equip of equipments) { if (equip.system.equipped && equip.system.stataffected == statKey) { rollData.equipmentsList.push({ label: `Item ${equip.name}`, type: "item", applied: false, equip: equip, value: equip.system.level }) } } } addVehicleWeapons(rollData, vehicle) { if (vehicle) { let modules = vehicle.items.filter(vehicle => vehicle.type == "vehicleweaponmodule") if (modules && modules.length > 0) { for (let module of modules) { rollData.vehicleWeapons.push({ label: `Weapon ${module.name}`, type: "item", applied: false, weapon: module, value: module.system.damagedicevalue }) } } } } /* -------------------------------------------- */ getCommonRollData(statKey = undefined, useShield = false, isInit = false, isPower = false, subKey = "", vehicle = undefined) { let rollData = PegasusUtility.getBasicRollData(isInit) rollData.alias = this.name rollData.actorImg = this.img rollData.actorId = this.id rollData.img = this.img rollData.traumaState = this.getTraumaState() rollData.levelRemaining = this.getLevelRemaining() rollData.activePerks = duplicate(this.getActivePerks()) rollData.diceList = PegasusUtility.getDiceList() rollData.noBonusDice = this.checkNoBonusDice() rollData.dicePool = [] if (subKey == "melee-dmg" || subKey == "ranged-dmg" || subKey == "power-dmg") { rollData.isDamage = true } if (statKey) { rollData.statKey = statKey rollData.stat = this.getStat(statKey) rollData.statDicesLevel = rollData.stat.value || rollData.stat.level rollData.statMod = rollData.stat.mod if (vehicle) { rollData.vehicle = duplicate(vehicle) if (subKey == "melee-dmg") { if (vehicle.isVehicleFullStop()) { ui.notifications.warn("MR not added to Melee Damage due to Full Stop.") } else { rollData.statVehicle = vehicle.system.statistics.mr } this.addVehicleWeapons(rollData, vehicle) } if (subKey == "ranged-atk") { rollData.statVehicle = vehicle.system.statistics.fc } if (subKey == "ranged-dmg") { this.addVehicleWeapons(rollData, vehicle) } if (subKey == "defense") { if (vehicle.isVehicleFullStop()) { ui.notifications.warn("MAN not added to Defense due to Full Stop.") } else { rollData.statVehicle = vehicle.system.statistics.man } } //this.addVehiculeHindrances(rollData.effectsList, vehicle) //this.addVehicleBonus(rollData, vehicle) } rollData.specList = this.getRelevantSpec(statKey) rollData.selectedSpec = "0" if (statKey.toLowerCase() == "mr") { rollData.img = "systems/fvtt-pegasus-rpg/images/icons/MR.webp" } else { rollData.img = `systems/fvtt-pegasus-rpg/images/icons/${rollData.stat.abbrev}.webp` } rollData.dicePool = rollData.dicePool.concat(PegasusUtility.buildDicePool("stat", rollData.statDicesLevel, rollData.stat.mod)) if (rollData.statVehicle) { rollData.dicePool = rollData.dicePool.concat(PegasusUtility.buildDicePool("statvehicle", rollData.statVehicle.currentlevel, 0)) } } this.addEffects(rollData, isInit, isPower, subKey == "power-dmg") this.addArmorsShields(rollData, statKey, useShield) this.addWeapons(rollData, statKey, useShield) this.addEquipments(rollData, statKey) console.log("ROLLDATA", rollData) return rollData } /* -------------------------------------------- */ getLevelRemainingList() { let options = [] for (let i = 0; i <= this.system.biodata.maxlevelremaining; i++) { options.push(``) } return options.join("\n") } /* -------------------------------------------- */ getMaxLevelRemainingList() { let options = [] for (let i = 0; i <= 12; i++) { options.push(``) } return options.join("\n") } /* -------------------------------------------- */ async startRoll(rollData) { this.syncRoll(rollData); //console.log("ROLL DATA", rollData) let rollDialog = await PegasusRollDialog.create(this, rollData) console.log(rollDialog) rollDialog.render(true); } /* -------------------------------------------- */ powerDmgRoll(itemId) { let power = this.items.get(itemId) if (power) { power = duplicate(power) this.rollPool(power.system.dmgstatistic, false, "power-dmg") } } /* -------------------------------------------- */ rollPool(statKey, useShield = false, subKey = "none", vehicle = undefined) { let stat = this.getStat(statKey) if (stat) { let rollData = this.getCommonRollData(statKey, useShield, false, false, subKey, vehicle) rollData.mode = "stat" rollData.subKey = subKey let def = stat.label if (subKey) { def = __subkey2title[subKey] } rollData.title = `Roll : ${def} ` rollData.img = "icons/dice/d12black.svg" this.startRoll(rollData) } else { ui.notifications.warn("Statistic not found !"); } } /* -------------------------------------------- */ rollUnarmedAttack() { let stat = this.getStat('com') if (stat) { let rollData = this.getCommonRollData(statKey) rollData.mode = "stat" rollData.title = `Unarmed Attack`; rollData.damages = this.getStat('str'); this.startRoll(rollData); } else { ui.notifications.warn("Statistic not found !"); } } /*-------------------------------------------- */ rollStat(statKey) { let stat = this.getStat(statKey); if (stat) { let rollData = this.getCommonRollData(statKey) rollData.mode = "stat" rollData.title = `Stat ${stat.label}`; this.startRoll(rollData) } else { ui.notifications.warn("Statistic not found !"); } } /* -------------------------------------------- */ async rollSpec(specId) { let spec = this.getOneSpec(specId) if (spec) { let rollData = this.getCommonRollData(spec.system.statistic) rollData.mode = "spec" rollData.title = `Spec. : ${spec.name} ` rollData.specList = [spec] rollData.selectedSpec = spec._id rollData.specName = spec.name rollData.img = spec.img rollData.specDicesLevel = spec.system.level PegasusUtility.updateSpecDicePool(rollData) this.startRoll(rollData) } else { ui.notifications.warn("Specialisation not found !"); } } /* -------------------------------------------- */ async rollMR(isInit = false, combatId = 0, combatantId = 0) { let mr = duplicate(this.system.mr) if (mr) { mr.dice = PegasusUtility.getDiceFromLevel(mr.value); let rollData = this.getCommonRollData("mr", false, isInit) rollData.mode = "MR" rollData.img = "systems/fvtt-pegasus-rpg/images/icons/MR.webp" rollData.isInit = isInit rollData.combatId = combatId rollData.combatantId = combatantId console.log("MR ROLL", rollData) if (isInit) { rollData.title = "MR / Initiative" } this.startRoll(rollData); } else { ui.notifications.warn("MR not found !"); } } /* -------------------------------------------- */ async rollArmor(armorId) { let armor = this.items.get(armorId) if (armor) { let rollData = this.getCommonRollData(armor.system.statistic) armor = duplicate(armor); this.checkAndPrepareEquipment(armor); rollData.mode = "armor" rollData.armor = armor rollData.title = `Armor : ${armor.name}` rollData.isResistance = true; rollData.img = armor.img rollData.damageDiceLevel = armor.system.resistance this.startRoll(rollData); } else { ui.notifications.warn("Armor not found !", weaponId); } } /* -------------------------------------------- */ async rollPower(powerId) { let power = this.items.get(powerId) if (power) { power = duplicate(power) let rollData = this.getCommonRollData(power.system.statistic, false, false, true) rollData.mode = "power" rollData.power = power rollData.title = `Power : ${power.name}` rollData.img = power.img this.startRoll(rollData); } else { ui.notifications.warn("Power not found !", powerId); } } /* -------------------------------------------- */ /* VEHICLE STUFF */ manageCurrentSpeed(speed) { if (speed == "fullstop") { this.update({ 'system.secondary.moverange': "nomovement" }) } if (speed == "crawling") { this.update({ 'system.secondary.moverange': "threatzone" }) } if (speed == "slow") { this.update({ 'system.secondary.moverange': "close" }) } if (speed == "average") { this.update({ 'system.secondary.moverange': "medium" }) } if (speed == "fast") { this.update({ 'system.secondary.moverange': "long" }) } if (speed == "extfast") { this.update({ 'system.secondary.moverange': "extreme" }) } } /* -------------------------------------------- */ modifyVehicleStun(incDec) { let stun = this.system.stun.value + incDec this.update({ 'stun.value': stun }) } /* -------------------------------------------- */ addTopSpeedBonus(topspeed, bonus) { let num = __speed2Num[topspeed] + Number(bonus) num = Math.max(0, num) num = Math.min(num, __num2speed.length - 1) return __num2speed[num] } /* -------------------------------------------- */ manageVehicleSpeedBonus(speed, name, stat, level) { if (this.system.statistics.ad.currentspeed == speed) { if (!this.items.find(effect => effect.system.isspeed == speed)) { let effect = duplicate(__bonusEffect) effect.name = name effect.system.stataffected = stat effect.system.effectlevel = level effect.system.isspeed = speed this.createEmbeddedDocuments("Item", [effect]) } } else { let effect = this.items.find(effect => effect.system.isspeed == speed) if (effect) { this.deleteEmbeddedDocuments("Item", [effect.id]) } } } /* -------------------------------------------- */ async computeVehicleStats() { if (this.type == "vehicle") { for (let statDef of __statBuild) { let sum = 0 let list = [] for (let moduleType of statDef.modules) { list = list.concat(this.items.filter(item => item.type == moduleType)) } if (list && list.length > 0) { sum = list.reduce((value, item2) => value + Number(item2.system[statDef.itemfield]), 0) } //console.log("Processing", statDef.field, this.system.statistics[statDef.field].level, list, sum) if (statDef.subfield) { if (sum != Number(this.system.statistics[statDef.field][statDef.subfield])) { //console.log("Update", statDef.field, statDef.subfield, sum, this.system.statistics[statDef.field][statDef.subfield]) this.update({ [`system.statistics.${statDef.field}.${statDef.subfield}`]: sum }) } } else { if (sum != Number(this.system.statistics[statDef.field].level)) { this.update({ [`system.statistics.${statDef.field}.level`]: sum, [`system.statistics.${statDef.field}.currentlevel`]: sum }) if (statDef.additionnal1) { if (sum != Number(this.system.statistics[statDef.field][statDef.additionnal1])) { this.update({ [`system.statistics.${statDef.field}.${statDef.additionnal1}`]: sum }) } } if (statDef.additionnal2) { if (sum != Number(this.system.statistics[statDef.field][statDef.additionnal2])) { this.update({ [`system.statistics.${statDef.field}.${statDef.additionnal2}`]: sum }) } } } } } // Top speed management let mobility = this.items.find(item => item.type == "mobilitymodule") let arcs = duplicate(this.system.arcs) if (mobility) { let propulsion = this.items.find(item => item.type == "propulsionmodule") let bonus = (propulsion) ? propulsion.system.topspeed : 0 arcs.frontarc.topspeed = this.addTopSpeedBonus(mobility.system.ts_f, bonus) arcs.rightarc.topspeed = mobility.system.ts_s arcs.leftarc.topspeed = mobility.system.ts_s arcs.toparc.topspeed = mobility.system.ts_s arcs.bottomarc.topspeed = mobility.system.ts_s arcs.reararc.topspeed = mobility.system.ts_r } else { arcs.frontarc.topspeed = "fullstop" arcs.rightarc.topspeed = "fullstop" arcs.leftarc.topspeed = "fullstop" arcs.toparc.topspeed = "fullstop" arcs.bottomarc.topspeed = "fullstop" arcs.reararc.topspeed = "fullstop" } for (let key in this.system.arcs) { if (this.system.arcs[key].topspeed != arcs[key].topspeed) { this.update({ 'system.arcs': arcs }) } } // VMS management let hull = this.items.find(item => item.type == "vehiclehull") let modules = duplicate(this.system.modules) if (hull) { modules.totalvms = Number(hull.system.vms) } else { modules.totalvms = 0 } let spaceList = this.items.filter(item => item.type == "vehiclemodule") || [] spaceList = spaceList.concat(this.items.filter(item => item.type == "vehicleweaponmodule") || []) let space = 0 if (spaceList && spaceList.length > 0) { space = spaceList.reduce((value, item2) => value + Number(item2.system.space), 0) } modules.usedvms = space if (modules.totalvms != this.system.modules.totalvms || modules.usedvms != this.system.modules.usedvms) { this.update({ 'system.modules': modules }) } if (modules.usedvms > modules.totalvms) { ui.notifications.warn("Warning! No more space available in cargo !!") } } // Speed effect management this.manageVehicleSpeedBonus("crawling", "Crawling MAN Bonus", "man", 3) this.manageVehicleSpeedBonus("slow", "Slow MAN Bonus", "man", 1) this.manageVehicleSpeedBonus("average", "Avoid attack Bonus", "all", 1) this.manageVehicleSpeedBonus("fast", "Avoid attack Bonus", "all", 3) this.manageVehicleSpeedBonus("extfast", "Avoid attack Bonus", "all", 5) } /* -------------------------------------------- */ getTotalCost() { let sumCost = 0 for (let item of this.items) { if (__isVehicle[item.type]) { if (item.system.cost) { sumCost += Number(item.system.cost) } } } return sumCost } /* -------------------------------------------- */ async preprocessItemVehicle(event, item, onDrop = false) { if (item.type != "effect" && !__isVehicle[item.type]) { ui.notifications.warn("You can't drop Character items over a vehicle sheet.") return } //console.log(">>>>> item", item.type, __isVehicleUnique[item.type]) if (__isVehicleUnique[item.type]) { let toDelList = [] for (let toDel of this.items) { if (toDel.type == item.type) { toDelList.push(toDel.id) } } //console.log("TODEL : ", toDelList) if (toDelList.length > 0) { await this.deleteEmbeddedDocuments('Item', toDelList) } } // Check size if (item.type == "vehiclemodule" || item.type == "vehicleweaponmodule") { item.system.space = item.system.space || 0 if (this.system.modules.usedvms + Number(item.system.space) > this.system.modules.totalvms) { ChatMessage.create({ content: `No more room available to host module ${item.name}. Module is not added to the vehicle.` }) return false } } return true } /* -------------------------------------------- */ getCrewList() { let crew = [] for (let actorDef of this.system.crew) { let actor = game.actors.get(actorDef.id) if (actor) { crew.push({ name: actor.name, img: actor.img, id: actor.id }) } } return crew } /* -------------------------------------------- */ addCrew(actorId) { if ( this.system.crew.length >= this.system.crewmax) { ui.notifications.warn("Vehicle crew is already full.") return } let crewList = duplicate(this.system.crew.filter(actorDef => actorDef.id != actorId) || []) crewList.push({ id: actorId }) this.update({ 'system.crew': crewList }) } /* -------------------------------------------- */ delCrew(actorId) { let crewList = duplicate(this.system.crew.filter(actorDef => actorDef.id != actorId) || []) this.update({ 'system.crew': crewList }) } /* -------------------------------------------- */ isVehicleFullStop() { return this.system.statistics.ad.currentspeed == "fullstop" } isVehicleCrawling() { return this.system.statistics.ad.currentspeed == "crawling" } isVehicleSlow() { return this.system.statistics.ad.currentspeed == "slow" } isVehicleAverage() { return this.system.statistics.ad.currentspeed == "average" } isVehicleFast() { return this.system.statistics.ad.currentspeed == "fast" } isVehicleExFast() { return this.system.statistics.ad.currentspeed == "extfast" } /* -------------------------------------------- */ isValidActor() { // Find relevant actor let actor for (let actorDef of this.system.crew) { let actorTest = game.actors.get(actorDef.id) if (actorTest.testUserPermission(game.user, "OWNER")) { return actorTest } } if (!actor) { ui.notifications.warn("You do no own any actors in the crew of this vehicle.") return } } /* -------------------------------------------- */ rollPoolFromVehicle(statKey, useShield = false, subKey = "none") { let actor = this.isValidActor() if (actor) { actor.rollPool(statKey, useShield, subKey, this) } } /* -------------------------------------------- */ addVehicleShields(rollData) { let shields = this.items.filter( shield => shield.type == "vehiclemodule" && shield.system.activated && shield.system.shielddicevalue > 0) || [] for (let shield of shields) { rollData.vehicleShieldList.push({ label: `${shield.name} (${shield.system.arccoverage})`, type: "vehicleshield", applied: false, value: shield.system.shielddicevalue }) } } /* -------------------------------------------- */ rollVehicleDamageResistance() { let actor = this.isValidActor() if (actor) { let stat = this.getStat("hr") let rollData = this.getCommonRollData("hr") rollData.mode = "stat" rollData.title = `Stat ${stat.label}`; this.addVehicleShields(rollData) this.startRoll(rollData) } } /* -------------------------------------------- */ activateVehicleModule(itemId) { let mod = this.items.get(itemId) if (mod) { this.updateEmbeddedDocuments('Item', [{ _id: mod.id, 'system.activated': !mod.system.activated }]) } } }