From 000057b50e3003e44f3e858fbbb546142ef434e3 Mon Sep 17 00:00:00 2001 From: openhands Date: Tue, 28 Jan 2025 16:14:19 +0000 Subject: [PATCH] Fix issue #337: Displacement --- app/src/libs/combat/tags.ts | 81 ++++++++++++++++++++++++++++++++++++ app/src/libs/combat/types.ts | 10 +++++ app/src/libs/combat/util.ts | 1 + 3 files changed, 92 insertions(+) diff --git a/app/src/libs/combat/tags.ts b/app/src/libs/combat/tags.ts index 39d55a86..f79c7c5f 100644 --- a/app/src/libs/combat/tags.ts +++ b/app/src/libs/combat/tags.ts @@ -1136,6 +1136,87 @@ export const lifesteal = ( * 2. Add user to any new ground effect * 3. Move user */ +export const displacement = ( + effect: UserEffect, + usersEffects: UserEffect[], + usersState: BattleUserState[], + groundEffects: GroundEffect[], +) => { + const target = usersState.find((u) => u.userId === effect.targetId); + const user = usersState.find((u) => u.userId === effect.creatorId); + let info: ActionEffect | undefined = undefined; + + if (target && user) { + // Prevent? + const { pass } = preventCheck(usersEffects, "moveprevent", target); + if (!pass) return preventResponse(effect, target, "resisted being displaced"); + + // Find an empty ground position + const occupiedPositions = new Set( + usersState.map((u) => `${u.latitude},${u.longitude}`) + ); + + // Try positions in a spiral pattern around the target + const dx = [0, 1, 0, -1]; // right, down, left, up + const dy = [1, 0, -1, 0]; + let x = target.longitude; + let y = target.latitude; + let layer = 1; + let found = false; + + while (layer <= 5 && !found) { // Try up to 5 layers out + for (let direction = 0; direction < 4; direction++) { + for (let step = 0; step < layer; step++) { + x += dx[direction]; + y += dy[direction]; + + if (!occupiedPositions.has(`${y},${x}`)) { + // Found an empty spot + // Update movement information + info = { + txt: `${user.username} displaces ${target.username} to [${y}, ${x}]`, + color: "blue", + }; + + // Handle ground effects + groundEffects.forEach((g) => { + if (g.timeTracker && target.userId in g.timeTracker) { + delete g.timeTracker[target.userId]; + } + }); + + groundEffects.forEach((g) => { + if ( + g.timeTracker && + g.longitude === x && + g.latitude === y + ) { + g.timeTracker[target.userId] = effect.createdRound; + } + }); + + // Update target location + target.longitude = x; + target.latitude = y; + found = true; + break; + } + } + if (found) break; + } + layer++; + } + + if (!found) { + info = { + txt: `${user.username} failed to displace ${target.username} - no empty ground found`, + color: "red", + }; + } + } + return info; +}; + export const move = ( effect: GroundEffect, usersEffects: UserEffect[], diff --git a/app/src/libs/combat/types.ts b/app/src/libs/combat/types.ts index ca4cf0d8..ddd9f5b3 100644 --- a/app/src/libs/combat/types.ts +++ b/app/src/libs/combat/types.ts @@ -507,6 +507,15 @@ export const MoveTag = z.object({ export type MoveTagType = z.infer; +export const DisplacementTag = z.object({ + ...BaseAttributes, + ...PowerAttributes, + type: z.literal("displacement").default("displacement"), + description: msg("Move target to an empty ground"), +}); + +export type DisplacementTagType = z.infer; + export const MovePreventTag = z.object({ ...BaseAttributes, ...PowerAttributes, @@ -702,6 +711,7 @@ export const AllTags = z.union([ LifeStealTag.default({}), MoveTag.default({}), MovePreventTag.default({}), + DisplacementTag.default({}), OneHitKillPreventTag.default({}), OneHitKillTag.default({}), PierceTag.default({}), diff --git a/app/src/libs/combat/util.ts b/app/src/libs/combat/util.ts index 1191d10f..65835c44 100644 --- a/app/src/libs/combat/util.ts +++ b/app/src/libs/combat/util.ts @@ -296,6 +296,7 @@ export const sortEffects = ( "increaseheal", // End-modifiers "move", + "displacement", "visual", ]; if (ordered.includes(a.type) && ordered.includes(b.type)) {