|
27 | 27 | #include "../../../Include/xrRender/Kinematics.h" |
28 | 28 | #include "../../../xrServerEntities/character_info.h" |
29 | 29 | #include "../../actor.h" |
| 30 | +#include "../xrEngine/CameraBase.h" |
30 | 31 | #include "../../relation_registry.h" |
31 | 32 | #include "../../stalker_animation_manager.h" |
32 | 33 | #include "../../stalker_planner.h" |
@@ -91,6 +92,10 @@ CAI_Stalker::CAI_Stalker () : |
91 | 92 | m_dbg_hud_draw = false; |
92 | 93 | #endif // DEBUG |
93 | 94 | m_registered_in_combat_on_migration = false; |
| 95 | + |
| 96 | + savedOrientation = { 0.f, 0.f, 0.f }; |
| 97 | + dTimeFSeen = Device.dwTimeGlobal + 1000; |
| 98 | + dTimeNfSeen = Device.dwTimeGlobal + 1000; |
94 | 99 | } |
95 | 100 |
|
96 | 101 | CAI_Stalker::~CAI_Stalker () |
@@ -502,6 +507,84 @@ void CAI_Stalker::Load (LPCSTR section) |
502 | 507 | m_can_select_items = !!pSettings->r_bool(section,"can_select_items"); |
503 | 508 | } |
504 | 509 |
|
| 510 | +void CAI_Stalker::BoneCallback(CBoneInstance* B) { |
| 511 | + CAI_Stalker* this_class = static_cast<CAI_Stalker*>(B->callback_param()); |
| 512 | + |
| 513 | + this_class->LookAtActor(B); |
| 514 | + R_ASSERT2(_valid(B->mTransform), "CAI_Stalker::BoneCallback"); |
| 515 | +} |
| 516 | + |
| 517 | +void CAI_Stalker::LookAtActor(CBoneInstance* headBone) { |
| 518 | + if (!g_Alive()) |
| 519 | + return; |
| 520 | + |
| 521 | + if (!Actor()) |
| 522 | + return; |
| 523 | + |
| 524 | + if (wounded()) |
| 525 | + return; |
| 526 | + |
| 527 | + auto adjustHeadOrientation = [&](float targetPitch, float targetYaw, float targetRoll) { |
| 528 | + savedOrientation.x = angle_inertion(savedOrientation.x, targetPitch, angle_difference(savedOrientation.x, targetPitch), PI_MUL_2, Device.fTimeDelta); |
| 529 | + savedOrientation.y = angle_inertion(savedOrientation.y, targetYaw, angle_difference(savedOrientation.y, targetYaw), PI_MUL_2, Device.fTimeDelta); |
| 530 | + savedOrientation.z = angle_inertion(savedOrientation.z, targetRoll, angle_difference(savedOrientation.z, targetRoll), PI_MUL_2, Device.fTimeDelta); |
| 531 | + }; |
| 532 | + |
| 533 | + if (Actor()->Position().distance_to(Position()) > 4.f) { |
| 534 | + adjustHeadOrientation(0.f, 0.f, 0.f); |
| 535 | + Fmatrix M; |
| 536 | + M.setHPB(VPUSH(savedOrientation)); |
| 537 | + headBone->mTransform.mulB_43(M); |
| 538 | + return; |
| 539 | + } |
| 540 | + |
| 541 | + if (memory().visual().visible_right_now(Actor())) { |
| 542 | + Fmatrix actorHead; |
| 543 | + smart_cast<IKinematics*>(Actor()->Visual())->Bone_GetAnimPos(actorHead, u16(Actor()->m_head), u8(-1), false); |
| 544 | + actorHead.mulA_43(Actor()->XFORM()); |
| 545 | + |
| 546 | + Fmatrix myHead = headBone->mTransform; |
| 547 | + myHead.mulA_43(XFORM()); |
| 548 | + myHead.c.mad(myHead.i, .15f); |
| 549 | + |
| 550 | + Fvector dir, cam_pos = Actor()->HUDview() ? Actor()->cam_FirstEye()->Position() : actorHead.c; |
| 551 | + dir.sub(cam_pos, myHead.c).normalize(); |
| 552 | + |
| 553 | + Fmatrix target_matrix; |
| 554 | + target_matrix.identity(); |
| 555 | + target_matrix.k.set(dir); |
| 556 | + Fvector::generate_orthonormal_basis_normalized(target_matrix.k, target_matrix.i, target_matrix.j); |
| 557 | + target_matrix.j.invert(); |
| 558 | + target_matrix.mulA_43(Fmatrix(headBone->mTransform).mulA_43(XFORM()).invert()); |
| 559 | + |
| 560 | + float yaw, pitch, roll; |
| 561 | + target_matrix.getHPB(pitch, yaw, roll); |
| 562 | + |
| 563 | + clamp(pitch, -0.75f, 0.7f); |
| 564 | + clamp(yaw, -1.0f, 1.0f); |
| 565 | + clamp(roll, -0.4f, 0.4f); |
| 566 | + |
| 567 | + bool inRange = (pitch > -0.7f && pitch < 0.65f) && (yaw > -0.9f && yaw < 0.9f) && (roll > -0.35f && roll < 0.35f); |
| 568 | + if (inRange && dTimeNfSeen < Device.dwTimeGlobal) { |
| 569 | + dTimeFSeen = Device.dwTimeGlobal + 1000; |
| 570 | + adjustHeadOrientation(pitch, yaw, roll); |
| 571 | + } |
| 572 | + |
| 573 | + bool outOfRange = !(pitch > -0.75f && pitch < 0.7f) && !(yaw > -1.2f && yaw < 1.2f) && !(roll > -0.5f && roll < 0.5f); |
| 574 | + if (outOfRange && dTimeFSeen < Device.dwTimeGlobal) { |
| 575 | + dTimeNfSeen = Device.dwTimeGlobal + 1000; |
| 576 | + adjustHeadOrientation(0.f, 0.f, 0.f); |
| 577 | + } |
| 578 | + } |
| 579 | + else { |
| 580 | + adjustHeadOrientation(0.f, 0.f, 0.f); // Reset if actor not visible |
| 581 | + } |
| 582 | + |
| 583 | + Fmatrix M; |
| 584 | + M.setHPB(VPUSH(savedOrientation)); |
| 585 | + headBone->mTransform.mulB_43(M); |
| 586 | +} |
| 587 | + |
505 | 588 | BOOL CAI_Stalker::net_Spawn (CSE_Abstract* DC) |
506 | 589 | { |
507 | 590 | CSE_Abstract *e = (CSE_Abstract*)(DC); |
@@ -609,7 +692,13 @@ BOOL CAI_Stalker::net_Spawn (CSE_Abstract* DC) |
609 | 692 |
|
610 | 693 | sight().update (); |
611 | 694 | Exec_Look (.001f); |
612 | | - |
| 695 | + |
| 696 | + if (EngineExternal()[EEngineExternalGame::EnableNPCLookAtActor]) { |
| 697 | + CBoneInstance* bone_head = &smart_cast<IKinematics*>(Visual())->LL_GetBoneInstance( |
| 698 | + smart_cast<IKinematics*>(Visual())->LL_BoneID("bip01_head")); |
| 699 | + bone_head->set_callback(bctCustom, BoneCallback, this); |
| 700 | + } |
| 701 | + |
613 | 702 | m_pPhysics_support->in_NetSpawn (e); |
614 | 703 |
|
615 | 704 | return (TRUE); |
|
0 commit comments