import { Hero6Utility } from "./hero6-utility.js"; /* -------------------------------------------- */ export class Hero6CombatTracker extends CombatTracker { /* -------------------------------------------- */ static get defaultOptions() { let path = "systems/fvtt-hero-system-6/templates/apps/combat-tracker.hbs"; return foundry.utils.mergeObject(super.defaultOptions, { template: path, }); } } /* -------------------------------------------- */ export class Hero6Combat extends Combat { /* -------------------------------------------- */ static ready() { Hooks.on("getCombatTrackerEntryContext", (html, options) => { Hero6Combat.pushMenuOptions(html, options); }); game.combat.settings.resource = "characteristics.spd.value"; } /* -------------------------------------------- */ static pushMenuOptions(html, options) { let newOpt for (let i = 0; i < options.length; i++) { let option = options[i]; if (option.name == 'COMBAT.CombatantReroll') { // Replace ! option.name = "Hold/Unhold action"; option.condition = true; option.icon = ''; option.callback = target => { let id = target.data('combatant-id') let c = game.combat.combatants.get(id) c.actor.holdAction() } //newOpt = duplicate(option) } } //options.push(newOpt) } /* -------------------------------------------- */ holdAction(combatantId) { this.rebuildInitiative() //console.log("Rebuilding.....") } /* -------------------------------------------- */ abortAction(actorId, abortState) { this.rebuildInitiative() } /* -------------------------------------------- */ constructor(data, context) { data.flags = { world: { turnData: { turnNumber: 0, segmentNumber: 12} } } super(data, context); this.turnNumber = 0; this.segmentNumber = 12; } /* -------------------------------------------- */ async startCombat() { game.combat.settings.resource = "characteristics.spd.value"; let updList = [] for (let c of this.combatants) { this.computeInitiative(c, updList) await c.actor.cleanCombat() } if (updList.length > 0) { await this.updateEmbeddedDocuments("Combatant", updList); } super.startCombat(); } /* -------------------------------------------- */ forceHold(actor, isHold) { if (game.user.isGM) { let updList = [] let c = this.combatants.find(c => c.actor._id == actor.id) let name = actor.name + ((isHold) ? " (H)" : "") console.log("ForceHold!!", c, actor) updList.push({ _id: c.id || c._id, name: name, initiative: actor.getBaseInit(this.segmentNumber) }) this.updateEmbeddedDocuments("Combatant", updList) } else { game.socket.emit("system.fvtt-hero-system-6", { name: "msg_force_hold", data: { actorId: actor.id, isHold: isHold } }); } } /* -------------------------------------------- */ forceAbort(actor, isAbort) { if (game.user.isGM) { let updList = [] let c = this.combatants.find(c => c.actor._id == actor.id) let name = actor.name + ((isAbort) ? " (A)" : "") updList.push({ _id: c.id || c._id, name: name, initiative: actor.getBaseInit(this.segmentNumber) }) this.updateEmbeddedDocuments("Combatant", updList) } else { game.socket.emit("system.fvtt-hero-system-6", { name: "msg_force_abort", data: { actorId: actor.id, isAbort: isAbort } }); } } /* -------------------------------------------- */ computeInitiative(c, updList) { let id = c._id || c.id let hasSegment = c.actor.hasPhase(this.segmentNumber) let isOnHold = c.actor.getHoldAction() let isOnAbort = c.actor.getAbortAction() let name = c.actor.name if (hasSegment || isOnHold || isOnAbort) { let baseInit = c.actor ? c.actor.getBaseInit(this.segmentNumber) : 0; if (isOnHold) { if (hasSegment) { // On hold + current segment -> auto-disable on hold c.actor.disableHoldAction() } else { name = c.actor.name + " (H)" } } if (isOnAbort) { name = c.actor.name + " (A)" if (c.actor.incAbortActionCount()) { c.actor.disableAbortAction() } } updList.push({ _id: id, name: name, initiative: baseInit, holdAction: c.holdAction }) } else { updList.push({ _id: id, name: name, initiative: 0, holdAction: c.holdAction }) } } /* -------------------------------------------- */ async rollInitiative(ids, formula = undefined, messageOptions = {}) { ids = typeof ids === "string" ? [ids] : ids; let updList = [] for (let cId = 0; cId < ids.length; cId++) { const c = this.combatants.get(ids[cId]) this.computeInitiative(c, updList) } if (updList.length > 0) { await this.updateEmbeddedDocuments("Combatant", updList); } return this; } /* -------------------------------------------- */ async rebuildInitiative() { let updList = [] for (let c of this.combatants) { this.computeInitiative(c, updList) } if (updList.length > 0) { await this.updateEmbeddedDocuments("Combatant", updList); //console.log("Rebuild INIT", updList) for (let c of updList) { if (c.initiative != 0) { return true } } } return false } /* -------------------------------------------- */ nextTurn() { let nbC = this.combatants.filter(c => c.initiative > 0).length //console.log("Next turn called....", this.turn, nbC) if (this.turn < nbC - 1) { super.nextTurn() } else { this.nextRound() } } /* -------------------------------------------- */ async previousRound() { let hasCombatants = false let nextRound = this.round let advanceTime = 0 let turn = this.turn === null ? null : 0; // Preserve the fact that it's no-one's turn currently. let turnData = this.getFlag("world", "turnData") //console.log("Next round called....", nextRound, turnData) while (!hasCombatants) { if (this.settings.skipDefeated && (turn !== null)) { turn = this.turns.findIndex(t => !t.isDefeated); if (turn === -1) { ui.notifications.warn("COMBAT.NoneRemaining", { localize: true }); turn = 0; } } advanceTime = -1 * (Math.max(this.turns.length - this.turn, 0) * CONFIG.time.turnTime); advanceTime -= CONFIG.time.roundTime; nextRound = nextRound -1 //console.log("Next round called....2", nextRound, turnData) turnData = this.getFlag("world", "turnData") if (!turnData) { turnData = { turnNumber: 0, segmentNumber: 12 } this.setFlag("world", "turnData", turnData) } turnData = duplicate(turnData) turnData.segmentNumber -= 1 if (turnData.segmentNumber <= 0) { turnData.segmentNumber = 12 turnData.turnNumber-- } await this.setFlag("world", "turnData", turnData) this.turnNumber = turnData.turnNumber; this.segmentNumber = turnData.segmentNumber; //console.log("Next round called....3", nextRound, turnData) // Re-compute init of actors hasCombatants = await this.rebuildInitiative() //console.log("Going round....", nextRound, hasCombatants) } // Update the document, passing data through a hook first const updateData = { round: nextRound, turn }; const updateOptions = { advanceTime, direction: -1 }; Hooks.callAll("combatRound", this, updateData, updateOptions); console.log(this) return this.update(updateData, updateOptions); } /* -------------------------------------------- */ async nextRound() { let hasCombatants = false let nextRound = this.round let advanceTime = 0 let turn = this.turn === null ? null : 0; // Preserve the fact that it's no-one's turn currently. let turnData = this.getFlag("world", "turnData") //console.log("Next round called....", nextRound, turnData) while (!hasCombatants) { if (this.settings.skipDefeated && (turn !== null)) { turn = this.turns.findIndex(t => !t.isDefeated); if (turn === -1) { ui.notifications.warn("COMBAT.NoneRemaining", { localize: true }); turn = 0; } } advanceTime = Math.max(this.turns.length - this.turn, 0) * CONFIG.time.turnTime; advanceTime += CONFIG.time.roundTime; nextRound = nextRound + 1; //console.log("Next round called....2", nextRound, turnData) turnData = this.getFlag("world", "turnData") if (!turnData) { turnData = { turnNumber: 0, segmentNumber: 12 } this.setFlag("world", "turnData", turnData) } turnData = duplicate(turnData) turnData.segmentNumber += 1 if (turnData.segmentNumber > 12) { turnData.segmentNumber = 1 turnData.turnNumber++ ChatMessage.create({ content: "Complete Post-Segment 12 Recoveries." }) } await this.setFlag("world", "turnData", turnData) this.turnNumber = turnData.turnNumber; this.segmentNumber = turnData.segmentNumber; //console.log("Next round called....3", nextRound, turnData) // Re-compute init of actors hasCombatants = await this.rebuildInitiative() //console.log("Going round....", nextRound, hasCombatants) } // Update the document, passing data through a hook first const updateData = { round: nextRound, turn }; const updateOptions = { advanceTime, direction: 1 }; Hooks.callAll("combatRound", this, updateData, updateOptions); console.log(this) return this.update(updateData, updateOptions); } /* -------------------------------------------- */ async _onCreateDescendantDocuments(type, documents, result, options, userId) { //console.log("Added...") if (game.user.isGM) { await super._onCreateEmbeddedDocuments(type, documents, result, options, userId) await this.rebuildInitiative() } } /* -------------------------------------------- _onUpdate(changed, options, userId) { }*/ /* -------------------------------------------- */ static async checkTurnPosition() { while (game.combat.turn > 0) { await game.combat.previousTurn() } } }