diff --git a/packages/core/src/math/index.ts b/packages/core/src/math/index.ts index b8a2089a0..1200fe9e1 100644 --- a/packages/core/src/math/index.ts +++ b/packages/core/src/math/index.ts @@ -3,6 +3,7 @@ export * from "./fisherYates"; export * from "./formatCompact"; export * from "./isNumber"; export * from "./NaNFallback"; +export * from "./normalizeAngleDeg"; export * from "./normalizeAngleRad"; export * from "./normalToEuler"; export * from "./numberToRomanNumeral"; diff --git a/packages/core/src/math/normalizeAngleDeg.ts b/packages/core/src/math/normalizeAngleDeg.ts new file mode 100644 index 000000000..ec6e54e81 --- /dev/null +++ b/packages/core/src/math/normalizeAngleDeg.ts @@ -0,0 +1,6 @@ +export function normalizeAngleDeg(angle: number) { + const newAngle = angle % 360; + if (newAngle > 360) return newAngle - 360; + if (newAngle < -360) return newAngle + 360; + return newAngle; +} diff --git a/packages/website-ue/src/core/tankopedia/applyPitchYawLimits.ts b/packages/website-ue/src/core/tankopedia/applyPitchYawLimits.ts new file mode 100644 index 000000000..73c63f70b --- /dev/null +++ b/packages/website-ue/src/core/tankopedia/applyPitchYawLimits.ts @@ -0,0 +1,102 @@ +import { normalizeAngleDeg } from "@blitzkit/core"; +import type { PitchLimit } from "@protos/blitz_static_tank_upgrade_single_stage"; +import { clamp, degToRad, lerp, radToDeg } from "three/src/math/MathUtils.js"; + +export function applyPitchYawLimits( + givenPitch: number, + givenYaw: number, + pitchLimitsUp: PitchLimit[], + pitchLimitsDown: PitchLimit[] +) { + const givenYawDeg = normalizeAngleDeg(radToDeg(givenYaw)); + const givenPitchDeg = normalizeAngleDeg(radToDeg(givenPitch)); + + const yawDeg = givenYawDeg; + const yaw = degToRad(givenYawDeg); + + let leftLimitUp: PitchLimit | undefined; + let rightLimitUp: PitchLimit | undefined; + let leftLimitDown: PitchLimit | undefined; + let rightLimitDown: PitchLimit | undefined; + + for (const limit of pitchLimitsUp) { + const normalizedAngle = normalizeAngleDeg(limit.angle); + + if (normalizedAngle <= yawDeg) { + if ( + !leftLimitUp || + normalizeAngleDeg(yawDeg - normalizedAngle) < + normalizeAngleDeg(yawDeg - leftLimitUp.angle) + ) { + leftLimitUp = limit; + } + } + + if (normalizedAngle >= yawDeg) { + if ( + !rightLimitUp || + normalizeAngleDeg(normalizedAngle - yawDeg) < + normalizeAngleDeg(rightLimitUp.angle - yawDeg) + ) { + rightLimitUp = limit; + } + } + } + + for (const limit of pitchLimitsDown) { + const normalizedAngle = normalizeAngleDeg(limit.angle); + + if (normalizedAngle <= yawDeg) { + if ( + !leftLimitDown || + normalizeAngleDeg(yawDeg - normalizedAngle) < + normalizeAngleDeg(yawDeg - leftLimitDown.angle) + ) { + leftLimitDown = limit; + } + } + + if (normalizedAngle >= yawDeg) { + if ( + !rightLimitDown || + normalizeAngleDeg(normalizedAngle - yawDeg) < + normalizeAngleDeg(rightLimitDown.angle - yawDeg) + ) { + rightLimitDown = limit; + } + } + } + + if (!leftLimitUp || !rightLimitUp || !leftLimitDown || !rightLimitDown) { + throw new Error("Pitch limits not found"); + } + + const spanUp = normalizeAngleDeg(rightLimitUp.angle - leftLimitUp.angle); + const spanDown = normalizeAngleDeg( + rightLimitDown.angle - leftLimitDown.angle + ); + + let limitUp: number; + let limitDown: number; + + if (spanUp === 0) { + limitUp = leftLimitUp.limit; + } else { + const delta = normalizeAngleDeg(givenYawDeg - leftLimitUp.angle); + const t = delta / spanUp; + limitUp = lerp(leftLimitUp.limit, rightLimitUp.limit, t); + } + + if (spanDown === 0) { + limitDown = leftLimitDown.limit; + } else { + const delta = normalizeAngleDeg(givenYawDeg - leftLimitDown.angle); + const t = delta / spanDown; + limitDown = lerp(leftLimitDown.limit, rightLimitDown.limit, t); + } + + const pitchDeg = clamp(givenPitchDeg, limitUp, -limitDown); + const pitch = degToRad(pitchDeg); + + return { pitch, yaw }; +} diff --git a/packages/website-ue/src/core/tankopedia/characteristics.ts b/packages/website-ue/src/core/tankopedia/characteristics.ts index 630c63dca..5866b5d8d 100644 --- a/packages/website-ue/src/core/tankopedia/characteristics.ts +++ b/packages/website-ue/src/core/tankopedia/characteristics.ts @@ -4,6 +4,8 @@ import { StageParameters, TankAttributeChange_AttributeName, } from "@protos/blitz_static_tank_upgrade_single_stage"; +import { radToDeg } from "three/src/math/MathUtils.js"; +import { applyPitchYawLimits } from "./applyPitchYawLimits"; import type { TankState } from "./tankState"; export type CharacteristicOutput = number | null; @@ -213,23 +215,25 @@ export const characteristics = { }, gun_depression({ parameters }) { - const pitchLimit = parameters.pitch_limits_down.find( - (pitchLimit) => pitchLimit.angle === 0 + return radToDeg( + applyPitchYawLimits( + -Math.PI / 2, + 0, + parameters.pitch_limits_up, + parameters.pitch_limits_down + ).pitch ); - - if (!pitchLimit) throw new Error("No angle 0 depression pitch limit found"); - - return pitchLimit.limit; }, gun_elevation({ parameters }) { - const pitchLimit = parameters.pitch_limits_up.find( - (pitchLimit) => pitchLimit.angle === 0 + return radToDeg( + applyPitchYawLimits( + Math.PI / 2, + 0, + parameters.pitch_limits_up, + parameters.pitch_limits_down + ).pitch ); - - if (!pitchLimit) throw new Error("No angle 0 elevation pitch limit found"); - - return pitchLimit.limit; }, engine_power({ attribute }) { diff --git a/packages/website-ue/src/core/tankopedia/characteristicsOrder.ts b/packages/website-ue/src/core/tankopedia/characteristicsOrder.ts index 7697e5b51..e30be15f1 100644 --- a/packages/website-ue/src/core/tankopedia/characteristicsOrder.ts +++ b/packages/website-ue/src/core/tankopedia/characteristicsOrder.ts @@ -50,8 +50,8 @@ export const characteristicsOrder: CharacteristicsOrder = [ { name: "dispersion", decimals: 3, units: "m" }, - { name: "gun_depression", units: "deg" }, - { name: "gun_elevation", units: "deg" }, + { name: "gun_depression", decimals: 0, units: "deg" }, + { name: "gun_elevation", decimals: 0, units: "deg" }, { toy: "flexibility" }, ],