updating buff and debuff icons. separated COA classes from WoW classes on the character creation screen
This commit is contained in:
1 parent
b5c9bce1aa
commit
c00f3211ea
39 files changed
+2037
-208
No files matched your search
@@ -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();
|
||||
|
||||
Reference in new issue
Block a user