updating buff and debuff icons. separated COA classes from WoW classes on the character creation screen

This commit is contained in:
phenom committed 2026-08-16 11:16:51 -04:00
1 parent b5c9bce1aa
commit c00f3211ea
39 files changed
+2037 -208

No files matched your search

+98
View File
@@ -8,10 +8,17 @@ import {
partyCombatMovementGoal,
partyFormationPosition,
partyMadeStuckProgress,
partyMovementProfile,
partyNaturalFollowPosition,
partyPersonalizedCombatMovementGoal,
partyPresentationY,
partySeparationGoal,
partyShouldRecallForStuck,
partyShouldMoveToComfortTarget,
personalizedPartyCombatRangeBand,
resetPartyBreadcrumbTrail,
stepPartyPosition,
synchronizePartyBreadcrumbCursor,
} from "./partyMovement";
describe("party world movement", () => {
@@ -38,6 +45,89 @@ describe("party world movement", () => {
expect(out).toEqual([12, 2, 17]);
});
it("builds stable and distinct bounded movement personalities", () => {
const first = partyMovementProfile("member-a", 0);
const repeat = partyMovementProfile("member-a", 0);
const second = partyMovementProfile("member-b", 1);
expect(first).toEqual(repeat);
expect(second).not.toEqual(first);
for (const profile of [first, second]) {
expect(Math.abs(profile.followDistanceJitter)).toBeLessThanOrEqual(0.35);
expect(Math.abs(profile.lateralOffset)).toBeLessThanOrEqual(0.65);
expect(profile.comfortSlack).toBeGreaterThanOrEqual(0.35);
expect(profile.comfortSlack).toBeLessThanOrEqual(0.85);
expect(profile.speedMultiplier).toBeGreaterThanOrEqual(0.94);
expect(profile.speedMultiplier).toBeLessThanOrEqual(1.06);
expect(Math.abs(profile.combatAngleOffset)).toBeLessThanOrEqual(50 * Math.PI / 180);
}
});
it("places natural followers inside personal spacing and movement bands", () => {
const profile = partyMovementProfile("member-c", 2);
const out: [number, number, number] = [0, 0, 0];
partyNaturalFollowPosition([10, 2, 20], 0, 1, 3.75, profile, 5_000, out);
expect(out[1]).toBe(2);
expect(Math.abs(out[0] - 10)).toBeLessThanOrEqual(0.83);
expect(20 - out[2]).toBeGreaterThanOrEqual(3.4);
expect(20 - out[2]).toBeLessThanOrEqual(4.1);
expect(partyShouldMoveToComfortTarget(out, out, false, profile.comfortSlack)).toBe(false);
expect(partyShouldMoveToComfortTarget([0, 0, 0], out, false, profile.comfortSlack)).toBe(true);
});
it("keeps personalized combat bands inside role-safe bounds", () => {
const profiles = [
partyMovementProfile("ranged-a", 1),
partyMovementProfile("ranged-b", 2),
];
for (const profile of profiles) {
const ranged = personalizedPartyCombatRangeBand({
minimum: 10,
preferred: 14.5,
maximum: 18.1,
}, profile);
expect(ranged.minimum).toBeGreaterThanOrEqual(9);
expect(ranged.maximum).toBeLessThanOrEqual(18.1);
expect(ranged.preferred).toBeGreaterThan(ranged.minimum);
expect(ranged.preferred).toBeLessThan(ranged.maximum);
const melee = personalizedPartyCombatRangeBand({
minimum: 0,
preferred: 3.3,
maximum: 3.55,
}, profile);
expect(melee.minimum).toBe(0);
expect(melee.maximum).toBeLessThanOrEqual(3.55);
expect(melee.preferred).toBeLessThan(melee.maximum);
}
});
it("uses a stable combat bearing and separates overlapping allies", () => {
const out: [number, number, number] = [0, 0, 0];
expect(partyPersonalizedCombatMovementGoal(
[20, 0, 0],
[0, 0, 0],
true,
{ minimum: 10, preferred: 14, maximum: 18 },
Math.PI / 2,
false,
out,
)).toBe("approach");
expect(out[0]).toBeCloseTo(14);
expect(out[1]).toBe(0);
expect(out[2]).toBeCloseTo(0);
const separated: [number, number, number] = [0, 0, 0];
expect(partySeparationGoal(
[1, 0, 1],
[2, 0, 2],
[[1, 0, 1]],
Math.PI / 2,
separated,
)).toBe(true);
expect(separated[0]).toBeGreaterThan(2);
expect(separated[2]).toBeCloseTo(2);
});
it("recalls only distant allies that should be moving and stopped making progress", () => {
expect(partyMadeStuckProgress([0, 0, 0], [0.45, 4, 0])).toBe(true);
expect(partyMadeStuckProgress([0, 0, 0], [0.44, 0, 0])).toBe(false);
@@ -168,6 +258,14 @@ describe("party world movement", () => {
expect(out).toEqual([20, 3, 4]);
});
it("synchronizes direct followers to the matching safe trail point", () => {
const trail = createPartyBreadcrumbTrail();
for (let x = 0; x <= 6; x += 1) appendPartyBreadcrumb(trail, [x, 0, 0], 0.1, 20);
const cursor = createPartyBreadcrumbCursor();
synchronizePartyBreadcrumbCursor(trail, cursor, 2);
expect(cursor.breadcrumbId).toBe(trail.points.find((point) => point.position[0] === 4)?.id);
});
it("lets a distant follower rejoin a trail before it is long enough for formation spacing", () => {
const trail = createPartyBreadcrumbTrail();
const cursor = createPartyBreadcrumbCursor();