diff --git a/eslint.config.mjs b/eslint.config.mjs index 3730a978..46e8e101 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -74,6 +74,7 @@ export default [ OutlineOverlayFilter: "readonly", DocumentSheetConfig: "readonly", ActiveEffectConfig: "readonly", + Scene: "readonly", }, }, }, diff --git a/module/herosystem6e.mjs b/module/herosystem6e.mjs index c031b043..9ddfdd91 100644 --- a/module/herosystem6e.mjs +++ b/module/herosystem6e.mjs @@ -37,6 +37,7 @@ import "./utility/chat-dice.mjs"; import "./testing/testing-main.mjs"; import { EffectsPanel } from "./effects-panel.mjs"; import { HeroSystemActiveEffectConfig } from "./actor/active-effect-config.mjs"; +import { HeroSystem6eEndToEndTest } from "./testing/end-to-end.mjs"; Hooks.once("init", async function () { // Custom HeroSystem VisionMode @@ -457,6 +458,9 @@ Hooks.once("ready", async function () { // Update lastMigration await game.settings.set(game.system.id, "lastMigration", game.system.version.replace("-alpha", "")); } + + // Testing + window.HeroSystem6eEndToEndTest = new HeroSystem6eEndToEndTest(); }); // New Actor Dialog diff --git a/module/testing/end-to-end.mjs b/module/testing/end-to-end.mjs new file mode 100644 index 00000000..2d51a6e2 --- /dev/null +++ b/module/testing/end-to-end.mjs @@ -0,0 +1,247 @@ +import { HeroSystem6eItem } from "../item/item.mjs"; + +export class HeroSystem6eEndToEndTest { + sceneName = "EndToEndTest"; + //scene; + actor5Name = "_EndToEndTest5"; + actor6Name = "_EndToEndTest6"; + // actor5; + // actor6; + // orderedListElement; + + delay = (ms) => new Promise((res) => setTimeout(res, ms || 100)); + + async start() { + await new Dialog({ + title: `End To End Testing`, + content: `
    +
  1. Init
  2. +
`, + buttons: { + ok: { + label: "OK", + }, + }, + }).render(true); + + for (let i = 0; i < 50; i++) { + if ((this.orderedListElement = $(".dialog .end-to-end-testing")).length > 0) break; + await this.delay(); + } + this.orderedListElement.closest(".dialog").addClass("end-to-end-testing"); + //$(".dialog.end-to-end-testing").css("left", 5); + + await this.performTests(); + } + + async performTests() { + await this.createTestScene(); + await this.createTestActors(); + await this.testAid(this.token6); + await this.testAid(this.token5); + } + + log(text, css) { + console.log(text); + this.orderedListElement.append(`
  • ${text}
  • `); + } + + async createTestScene() { + this.scene = game.scenes.find((s) => s.name === this.sceneName); + if (this.scene) { + this.log(`Deleting Scene '${this.sceneName}'`); + this.scene.delete(); + } + this.scene = await Scene.create({ + name: "EndToEndTest", + width: 1000, + height: 500, + }); + this.log(`Created scene: ${this.sceneName}`); + + await this.scene.view(); + + // Remove all tokens + // for (const token of this.scene.tokens) { + // await token.delete(); + // } + } + + async createTestActors() { + // Scene Center + const x = Math.floor(this.scene.dimensions.width / 200) * 100; + const y = Math.floor(this.scene.dimensions.height / 200) * 100; + + // 5e + for (const actor of game.actors.filter((a) => a.name === this.actor5Name)) { + this.log(`Deleting ${actor.name}`); + await actor.delete(); + } + this.log(`Creating ${this.actor5Name}`); + this.actor5 = await Actor.create({ + name: this.actor5Name, + type: "npc", + }); + await this.actor5.update({ "system.is5e": true }); + await TokenDocument.create( + { + name: this.actor5.name.replace("_", ""), + actorId: this.actor5.id, + x: x - 100, + y, + displayName: foundry.CONST.TOKEN_DISPLAY_MODES.ALWAYS, + }, + { parent: this.scene }, + ); + this.token5 = this.actor5.getActiveTokens()[0]; + + // 6e + for (const actor of game.actors.filter((a) => a.name === this.actor6Name)) { + this.log(`Deleting ${actor.name}`); + await actor.delete(); + } + this.log(`Creating ${this.actor6Name}`); + this.actor6 = await Actor.create({ + name: this.actor6Name, + type: "pc", + system: { + is5e: false, + }, + }); + await TokenDocument.create( + { + name: this.actor6.name.replace("_", ""), + actorId: this.actor6.id, + x: x + 100, + y, + displayName: foundry.CONST.TOKEN_DISPLAY_MODES.ALWAYS, + }, + { parent: this.scene }, + ); + this.token6 = this.actor6.getActiveTokens()[0]; + } + + async testAid(token) { + // Create AID + const xml = ` + + + `; + const itemData = HeroSystem6eItem.itemDataFromXml(xml, token.actor); + const aidItem = await HeroSystem6eItem.create(itemData, { parent: token.actor }); + await aidItem._postUpload(); + this.log(`Added ${aidItem.name} to ${aidItem.actor.name}`); + + // Target ourselves + await game.user.updateTokenTargets([token.id]); + // await game.user.broadcastActivity({ + // targets: [this.token6.id], + // }); + + // Roll + await aidItem.roll(); + + // Wait for application to render then Click "Roll to Hit" + let button; + for (let i = 0; i < 50; i++) { + if ((button = $("#item-attack-form-application button")).length === 1) break; + await this.delay(); + } + button.click(); + this.log(`${aidItem.name} toHit`); + + // Wait for chat chard then Roll AID + for (let i = 0; i < 50; i++) { + if ( + (button = $(`ol#chat-log .chat-message:last-child button.roll-damage[data-item-id='${aidItem.uuid}']`)) + .length === 1 + ) + break; + await this.delay(); + } + button.click(); + this.log(`Roll ${aidItem.name}`); + + // Wait for chat chard then Apply AID + for (let i = 0; i < 50; i++) { + if ( + (button = $( + `ol#chat-log .chat-message:last-child button.apply-damage[data-item-id='${aidItem.uuid}']:last-child`, + )).length === 1 + ) + break; + await this.delay(); + } + button.click(); + this.log(`Apply ${aidItem.name}`); + + // Get AID Active Effect + let aidActiveEffect; + for (let i = 0; i < 50; i++) { + if ((aidActiveEffect = token.actor.temporaryEffects?.[0])) break; + await this.delay(); + } + this.log(`Active Effect: ${aidActiveEffect.changes[0].key} ${aidActiveEffect.changes[0].value}`); + + // Confirm STR has been aided + let aidValue = parseInt(aidActiveEffect.changes[0].value); + let actorStrValue = 10 + aidValue; + if (token.actor.system.characteristics.str.value !== actorStrValue) { + this.log(`FAIL`, "color:red"); + return; + } + + if (aidActiveEffect.changes.length !== 1) { + this.log(`FAIL: AE has more than 1 change`, "color:red"); + return; + } + + const saveWorldTime = game.time.worldTime; + + while (aidValue !== 0) { + // Advance world time 12 seconds + + this.log(`worldTime + 12 seconds`); + await game.time.advance(12); + + // Wait for AE to update/fade + for (let i = 0; i < 50; i++) { + if (token.actor.system.characteristics.str.value !== actorStrValue) break; + await this.delay(); + } + aidActiveEffect = token.actor.temporaryEffects?.[0]; // Make sure we have latest updates + + if (aidActiveEffect && aidActiveEffect.changes.length !== 1) { + this.log(`FAIL: AE has more than 1 change`, "color:red"); + return; + } + + // Check for fade + this.log(`Active Effect: ${aidActiveEffect?.changes[0].key} ${aidActiveEffect?.changes[0].value}`); + const aidNewValue = Math.max(0, aidValue - 5); + const actorNewStrValue = Math.max(10, actorStrValue - 5); + if ( + (!aidActiveEffect && aidNewValue === 0) || + token.actor.system.characteristics.str.value === actorNewStrValue + ) { + this.log(`Fade from ${aidValue} to ${aidNewValue} was successful. STR=${actorNewStrValue}`); + } else { + this.log( + `Excpected actor STR=${actorNewStrValue} got ${token.actor.system.characteristics.str.value}`, + "color:red", + ); + this.log(`Excpected change.value=${aidNewValue} got ${aidActiveEffect?.changes[0].value}`, "color:red"); + + // await this.delay(); + // this.log(`Active Effect: ${aidActiveEffect.changes[0].key} ${aidActiveEffect.changes[0].value}`); + + return; + } + + // fade again? + aidValue = parseInt(aidActiveEffect?.changes[0].value || 0); + actorStrValue = 10 + aidValue; + } + this.log(`Active Effect: Fade completed succesfully`); + } +} diff --git a/module/utility/adjustment.mjs b/module/utility/adjustment.mjs index 6680795b..48da4349 100644 --- a/module/utility/adjustment.mjs +++ b/module/utility/adjustment.mjs @@ -305,7 +305,7 @@ function _createNewAdjustmentEffect( targetPower?.name || potentialCharacteristic // TODO: This will need to change for multiple effects }`, img: item.img, - //changes: [_createAEChangeBlock(potentialCharacteristic, targetSystem)], + changes: [_createAEChangeBlock(potentialCharacteristic, targetSystem)], duration: { seconds: _determineEffectDurationInSeconds(item, rawActivePointsDamage), }, @@ -495,13 +495,13 @@ export async function performAdjustment( } // Update AE active points by summing changes - if (activeEffect.flags.changes) { - const _ap = activeEffect.flags.changes?.reduce((partialSum, a) => partialSum + (a.activePoints || 0), 0); - if (_ap !== 0 && activeEffect.flags.adjustmentActivePoints != _ap) { - console.log("New AP calc"); - activeEffect.flags.adjustmentActivePoints = _ap; - } - } + // if (activeEffect.flags.changes) { + // const _ap = activeEffect.flags.changes?.reduce((partialSum, a) => partialSum + (a.activePoints || 0), 0); + // if (_ap !== 0 && activeEffect.flags.adjustmentActivePoints != _ap) { + // console.log("New AP calc"); + // activeEffect.flags.adjustmentActivePoints = _ap; + // } + // } // Healing is not cumulative but all else is. Healing cannot harm when lower than an existing effect. let thisAttackEffectiveAdjustmentActivePoints = isHealing @@ -595,20 +595,20 @@ export async function performAdjustment( // Calculate the effect's change to the maximum. Only healing does not change the maximum. if (!isOnlyToStartingValues) { - //activeEffect.changes[0].value = parseInt(activeEffect.changes[0].value) - totalActivePointAffectedDifference; - const _key = - targetSystem.system.characteristics?.[potentialCharacteristic.toLowerCase()] != null - ? `system.characteristics.${potentialCharacteristic.toLowerCase()}.max` - : "system.max"; - const _seconds = _determineEffectDurationInSeconds(item, thisAttackRawActivePointsDamage); - _createAEChange( - activeEffect, - _key, - -totalActivePointAffectedDifference, - _seconds, - item.uuid, - -totalAdjustmentNewActivePoints, - ); + activeEffect.changes[0].value = parseInt(activeEffect.changes[0].value) - totalActivePointAffectedDifference; + // const _key = + // targetSystem.system.characteristics?.[potentialCharacteristic.toLowerCase()] != null + // ? `system.characteristics.${potentialCharacteristic.toLowerCase()}.max` + // : "system.max"; + // const _seconds = _determineEffectDurationInSeconds(item, thisAttackRawActivePointsDamage); + // _createAEChange( + // activeEffect, + // _key, + // -totalActivePointAffectedDifference, + // _seconds, + // item.uuid, + // -totalAdjustmentNewActivePoints, + // ); } // If this is 5e then some characteristics are calculated (not figured) based on diff --git a/scss/components/_junk.scss b/scss/components/_junk.scss index f18539e5..2788a989 100644 --- a/scss/components/_junk.scss +++ b/scss/components/_junk.scss @@ -222,3 +222,11 @@ li.directory-item.child-item { .characteristic-roll { max-width: 40px; } + +// .dialog.end-to-end-testing { +// //background-color: rgba (255 0 0 /0.1); +// } + +.dialog.end-to-end-testing .window-content { + background: rgba(255, 255, 255, 0.9); +}