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: `
`,
+ 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);
+}