Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
97 changes: 97 additions & 0 deletions src/Hooks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -948,6 +948,9 @@ namespace Hooks
case RE::FormType::ProjectileMissile:
aimType = Settings::uTargetLockMissileAimType;
break;
case RE::FormType::ProjectileCone:
aimType = Settings::uTargetLockMissileAimType;
break;
default:
aimType = TargetLockProjectileAimType::kFreeAim;
}
Expand Down Expand Up @@ -1070,6 +1073,13 @@ namespace Hooks
ProjectileAimSupport(a_this);
}

void ProjectileHook::GetLinearVelocityCone(RE::Projectile* a_this, RE::NiPoint3& a_outVelocity)
{
_GetLinearVelocityCone(a_this, a_outVelocity);

ProjectileAimSupport(a_this);
}

bool ProjectileHook::Func183(RE::Projectile* a_this)
{
// player only, 0x100000 == player
Expand Down Expand Up @@ -1214,6 +1224,93 @@ namespace Hooks
RE::NiPoint3 rightVector = direction.Cross(upVector);
direction = RotateAngleAxis(direction, AngleToRadian(*g_f3PArrowTiltUpAngle), rightVector);

linearVelocity = direction * velocityScalar;
}
} else if (a_this->formType.get() == RE::FormType::ProjectileCone && directionalMovementHandler->IsFreeCamera() &&
(!directionalMovementHandler->HasTargetLocked() || Settings::uTargetLockMissileAimType == TargetLockProjectileAimType::kFreeAim)) {
// The game launches cone projectiles with the shooter's body pitch instead of the camera aim pitch,
// so they miss the crosshair while the third person free camera is active (the character's pitch is
// not synced to the camera). Re-aim them along the camera ray. Only relevant for long range cone
// projectiles used by some spell mods, e.g. Astral Magic 2.
auto playerCamera = RE::PlayerCamera::GetSingleton();
if (playerCamera->currentState && playerCamera->currentState->id == RE::CameraState::kThirdPerson) {
constexpr RE::NiPoint3 forwardVector{ 0.f, 1.f, 0.f };
auto thirdPersonState = static_cast<RE::ThirdPersonState*>(playerCamera->currentState.get());
RE::NiQuaternion cameraRotation;
thirdPersonState->GetRotation(cameraRotation);

auto cameraForwardVector = RotateVector(forwardVector, cameraRotation);

cameraForwardVector.Unitize();

auto cameraPos = playerCamera->cameraRoot->world.translate;

RE::NiPoint3 rayStart = cameraPos;
RE::NiPoint3 rayEnd = cameraPos + cameraForwardVector * 5000.f;
RE::NiPoint3 hitPos = rayEnd;

RE::NiPoint3 cameraToPlayer = playerCamera->cameraTarget.get()->GetPosition() - cameraPos;
RE::NiPoint3 cameraToTarget = rayEnd - cameraPos;
RE::NiPoint3 projected = Project(cameraToPlayer, cameraToTarget);
RE::NiPoint3 projectedPos = RE::NiPoint3(projected.x + cameraPos.x, projected.y + cameraPos.y, projected.z + cameraPos.z);
rayStart = projectedPos;

uint16_t playerCollisionGroup = 0;

auto playerCharacter = RE::PlayerCharacter::GetSingleton();
if (auto playerBody = playerCharacter->Get3D()) {
if (auto collisionObject = playerBody->GetCollisionObject()) {
if (auto rigidBody = collisionObject->GetRigidBody()) {
playerCollisionGroup = static_cast<RE::hkpEntity*>(rigidBody->referencedObject.get())->collidable.broadPhaseHandle.collisionFilterInfo >> 16;
}
}
}

float bhkWorldScale = *g_worldScale;

collector.Reset();
RE::hkpWorldRayCastInput raycastInput;
raycastInput.filterInfo = ((uint32_t)playerCollisionGroup << 16) | 0x28;
raycastInput.from.quad = _mm_setr_ps(rayStart.x * bhkWorldScale, rayStart.y * bhkWorldScale, rayStart.z * bhkWorldScale, 0.f);
raycastInput.to.quad = _mm_setr_ps(rayEnd.x * bhkWorldScale, rayEnd.y * bhkWorldScale, rayEnd.z * bhkWorldScale, 0.f);
auto world = playerCharacter->parentCell->GetbhkWorld();
world->worldLock.LockForRead();
CastRay(world->GetWorld2(), raycastInput, collector);
world->worldLock.UnlockForRead();
if (collector.doesHitExist) {
auto distance = rayEnd - rayStart;
hitPos = rayStart + (distance * collector.closestHitInfo.hitFraction);
}

RE::NiPoint3 direction = hitPos - a_this->data.location;
direction.Unitize();

float rotationX, rotationZ;

rotationX = atan2(-direction.z, std::sqrtf(direction.x * direction.x + direction.y * direction.y));
rotationZ = atan2(direction.x, direction.y);

if (rotationZ < 0.0) {
rotationZ += PI;
}

if (direction.x < 0.0) {
rotationZ += PI;
}

a_this->data.angle.x = rotationX;
a_this->data.angle.z = rotationZ;

auto projectileNode = a_this->Get3D();
if (projectileNode) {
SetRotationMatrix(projectileNode->local.rotate, direction.x, direction.y, direction.z);
}

// no f3PArrowTiltUpAngle tilt here: unlike arrows, cone projectiles get no engine side
// crosshair correction that would compensate for it, so the exact ray direction is what we want
auto& linearVelocity = a_this->GetProjectileRuntimeData().linearVelocity;
float velocityScalar = linearVelocity.Length();

linearVelocity = direction * velocityScalar;
}
}
Expand Down
4 changes: 4 additions & 0 deletions src/Hooks.h
Original file line number Diff line number Diff line change
Expand Up @@ -243,9 +243,11 @@ namespace Hooks
REL::Relocation<std::uintptr_t> ArrowProjectileVtbl{ RE::VTABLE_ArrowProjectile[0] }; // 1676318
REL::Relocation<std::uintptr_t> MissileProjectileVtbl{ RE::VTABLE_MissileProjectile[0] }; // 167AE78
REL::Relocation<std::uintptr_t> BeamProjectileVtbl{ RE::VTABLE_BeamProjectile[0] }; // 1677660
REL::Relocation<std::uintptr_t> ConeProjectileVtbl{ RE::VTABLE_ConeProjectile[0] };
_GetLinearVelocityProjectile = ProjectileVtbl.write_vfunc(0x86, GetLinearVelocityProjectile);
_GetLinearVelocityArrow = ArrowProjectileVtbl.write_vfunc(0x86, GetLinearVelocityArrow);
_GetLinearVelocityMissile = MissileProjectileVtbl.write_vfunc(0x86, GetLinearVelocityMissile);
_GetLinearVelocityCone = ConeProjectileVtbl.write_vfunc(0x86, GetLinearVelocityCone);

auto& trampoline = SKSE::GetTrampoline();
REL::Relocation<uintptr_t> hook{ RELOCATION_ID(43030, 44222) }; // 754820, 7821A0
Expand All @@ -258,12 +260,14 @@ namespace Hooks
static void GetLinearVelocityProjectile(RE::Projectile* a_this, RE::NiPoint3& a_outVelocity);
static void GetLinearVelocityArrow(RE::Projectile* a_this, RE::NiPoint3& a_outVelocity);
static void GetLinearVelocityMissile(RE::Projectile* a_this, RE::NiPoint3& a_outVelocity);
static void GetLinearVelocityCone(RE::Projectile* a_this, RE::NiPoint3& a_outVelocity);
static bool Func183(RE::Projectile* a_this);
static void InitProjectile(RE::Projectile* a_this);

static inline REL::Relocation<decltype(GetLinearVelocityProjectile)> _GetLinearVelocityProjectile;
static inline REL::Relocation<decltype(GetLinearVelocityArrow)> _GetLinearVelocityArrow;
static inline REL::Relocation<decltype(GetLinearVelocityMissile)> _GetLinearVelocityMissile;
static inline REL::Relocation<decltype(GetLinearVelocityCone)> _GetLinearVelocityCone;
static inline REL::Relocation<decltype(InitProjectile)> _InitProjectile;
};

Expand Down