Release v0.1.2 2026-07-11
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import { BULL_CHARGE } from "./bossMechanics";
|
||||
import { CINDER_BREATH } from "./bosses/cindermaw";
|
||||
import { EMBER_MANTIS_SLASH } from "./bosses/emberMantis";
|
||||
import { moveToward, pointOutsideCircle, pointOutsideLane, pointToSegmentDistance } from "./geometry";
|
||||
import type { BossMotionState, MemberId, PartyMember, WorldPosition } from "./types";
|
||||
|
||||
@@ -122,6 +123,42 @@ export const avoidBreathBehavior: PartyBehavior = {
|
||||
},
|
||||
};
|
||||
|
||||
export const evadeSlashLanesBehavior: PartyBehavior = {
|
||||
id: "evade-slash-lanes",
|
||||
decide: ({ memberId, current, formationTarget, bossMotion }) => {
|
||||
if (bossMotion.mode !== "mantis_line_telegraph" && bossMotion.mode !== "mantis_cross_telegraph") return null;
|
||||
if (!bossMotion.slashLanes.length) return null;
|
||||
const unsafe = (position: WorldPosition) => bossMotion.slashLanes.some((lane) =>
|
||||
pointToSegmentDistance(position, lane.start, lane.end) < EMBER_MANTIS_SLASH.aiClearance,
|
||||
);
|
||||
if (!unsafe(current) && !unsafe(formationTarget)) return null;
|
||||
|
||||
const origin = unsafe(formationTarget) ? formationTarget : current;
|
||||
const memberOffset = AI_MEMBER_IDS.indexOf(memberId) * (Math.PI / 4);
|
||||
let best = current;
|
||||
let bestScore = Number.NEGATIVE_INFINITY;
|
||||
for (const radius of [2.2, 3.4]) {
|
||||
for (let index = 0; index < 8; index += 1) {
|
||||
const angle = memberOffset + (index / 8) * Math.PI * 2;
|
||||
const candidate = clampToArena([
|
||||
origin[0] + Math.sin(angle) * radius,
|
||||
origin[1] + Math.cos(angle) * radius,
|
||||
]);
|
||||
const laneDistance = Math.min(...bossMotion.slashLanes.map((lane) =>
|
||||
pointToSegmentDistance(candidate, lane.start, lane.end),
|
||||
));
|
||||
const travelPenalty = Math.hypot(candidate[0] - current[0], candidate[1] - current[1]) * 0.08;
|
||||
const score = laneDistance - travelPenalty;
|
||||
if (score > bestScore) {
|
||||
best = candidate;
|
||||
bestScore = score;
|
||||
}
|
||||
}
|
||||
}
|
||||
return { target: best, speed: EMBER_MANTIS_SLASH.aiEvadeSpeed };
|
||||
},
|
||||
};
|
||||
|
||||
export const avoidCircleHazardsBehavior: PartyBehavior = {
|
||||
id: "avoid-circle-hazards",
|
||||
decide: ({ memberId, current, formationTarget, bossMotion, time }) => {
|
||||
@@ -143,7 +180,7 @@ export const avoidCircleHazardsBehavior: PartyBehavior = {
|
||||
export const maintainFormationBehavior: PartyBehavior = {
|
||||
id: "maintain-formation",
|
||||
decide: ({ formationTarget, bossMotion, memberId }) => {
|
||||
if (!["holding", "telegraph", "tethering", "venom_cast", "skyfall"].includes(bossMotion.mode)) return null;
|
||||
if (!["holding", "telegraph", "tethering", "venom_cast", "skyfall", "mantis_sidestep", "mantis_recover"].includes(bossMotion.mode)) return null;
|
||||
return { target: formationTarget, speed: MOVE_SPEEDS[memberId] };
|
||||
},
|
||||
};
|
||||
@@ -152,6 +189,7 @@ export const DEFAULT_PARTY_BEHAVIORS: readonly PartyBehavior[] = [
|
||||
breakTetherBehavior,
|
||||
stackForPounceBehavior,
|
||||
evadeChargeBehavior,
|
||||
evadeSlashLanesBehavior,
|
||||
avoidBreathBehavior,
|
||||
avoidCircleHazardsBehavior,
|
||||
maintainFormationBehavior,
|
||||
|
||||
Reference in New Issue
Block a user