Skip to content

Cone-type projectiles fired by the player aim above the crosshair in third person (free aim and target lock) #6

Description

@zspatter

Description

Long-range cone-type projectiles (BGSProjectile type 16 / "Cone") fired by the player in third person do not fly toward the crosshair. They launch with the character's body pitch instead of the camera/aim pitch, so they typically hit above the aim point, with the error growing with distance. In first person, or with TDM removed, the same spells aim correctly.

Vanilla barely exposes this because vanilla player-castable cone projectiles are short-range sprays (Flames ~600 units). But several spell mods use long-range cone projectiles specifically because cones penetrate actors, e.g.:

  • Astral Magic 2 - the Astral Spear line (_A_AstralSpearProj1/2/3, cone, speed 6000, range 90000, cone spread 0.01)
  • Piercing Expert Spells - all its projectiles (cone, range 10000-12000)

With TDM installed these spells are only usable at close range in third person.

Root cause (from reading master)

The engine computes launch direction for player-fired arrow/missile/beam projectiles from the camera/crosshair, so they are unaffected by TDM's camera/character pitch decoupling. Cone projectiles instead launch using the caster's aim/body pitch.

In vanilla third person with a weapon drawn, character pitch is coupled to camera pitch, so the two sources agree. TDM's free camera stores pitch in thirdPersonState->freeRotation.y and leaves the character's data.angle.x unsynced, and:

  1. DirectionalMovementHandler::UpdateFacingCrosshair() transfers only yaw (freeRotation.x) into _desiredAngle when casting - pitch is never synced to the character.
  2. ProjectileHook::ProjectileAimSupport() only handles FormType::ProjectileArrow and FormType::ProjectileMissile (default case = kFreeAim), and the GetLinearVelocity hooks are installed on the Projectile, ArrowProjectile, MissileProjectile vtables only - ConeProjectile overrides are never touched. Func183 covers beams. So cones get no correction under target lock either.
  3. UpdateRotationLockedCam() early-outs when _bIsAiming is true, so while actually casting under target lock the body is not pitched toward the target, and (2) means nothing corrects the projectile afterwards.

Suggested fix

Either (or both):

  • Free aim: on cone projectile launch (e.g. the same InitProjectile call-site hook, or a write_vfunc on RE::VTABLE_ConeProjectile[0]), when the shooter is the player and the camera is third person, re-aim the projectile along the camera-crosshair raycast - the exact math already exists in ProjectileHook::InitProjectile for the mounted-aiming path (project camera→player onto the view ray, raycast, recompute data.angle + node rotation + linearVelocity).
  • Target lock: add case RE::FormType::ProjectileCone: (mapping to Settings::uTargetLockMissileAimType) in ProjectileAimSupport's switch and install the GetLinearVelocity hook on RE::VTABLE_ConeProjectile[0] as well. The velocity-redirect logic should work for cones as-is since it just rewrites linearVelocity and the projectile's angles.

Reproduction

  1. Install Astral Magic 2 (or any spell using a long-range cone projectile) + TDM.
  2. In third person, free aim or target lock an enemy ~2000+ units away on flat ground and cast Astral Spear at the reticle.
  3. The projectile passes visibly above the reticle/target; switching to first person and repeating hits the reticle.

Observed on TDM 2.2.5 in-game (Skyrim SE 1.6.1170); the code references above are from current master (2.2.7), where the relevant logic is unchanged, confirmed not reproducible with TDM's requirements alone (Address Library etc.), appears with TDM alone added.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions