diff --git a/Runtime/MP1/World/CMagdolite.cpp b/Runtime/MP1/World/CMagdolite.cpp index 400e3dba4..f1636ac1b 100644 --- a/Runtime/MP1/World/CMagdolite.cpp +++ b/Runtime/MP1/World/CMagdolite.cpp @@ -1,22 +1,43 @@ #include "Runtime/MP1/World/CMagdolite.hpp" +#include "Runtime/Collision/CCollisionActor.hpp" +#include "Runtime/Collision/CCollisionActorManager.hpp" +#include "Runtime/Collision/CJointCollisionDescription.hpp" +#include "Runtime/Graphics/CSkinnedModel.hpp" +#include "Runtime/Weapon/CFlameThrower.hpp" +#include "Runtime/World/CPlayer.hpp" +#include "Runtime/World/CScriptWater.hpp" +#include "Runtime/World/CScriptWaypoint.hpp" +#include "Runtime/GameGlobalObjects.hpp" +#include "Runtime/CSimplePool.hpp" +#include "Runtime/CStateManager.hpp" +#include "TCastTo.hpp" // Generated file, do not modify include path + +#include + namespace urde::MP1 { -CMagdolite::CMagdoliteData::CMagdoliteData(CInputStream& in) -: x0_propertyCount(in.readUint32Big()) -, x4_(in.readUint32Big()) -, x8_(in) -, xc_(in.readUint32Big()) -, x10_(in.readFloatBig()) -, x18_(in.readFloatBig()) -, x1c_(in.readFloatBig()) {} +namespace { +constexpr std::array skSpineJoints{{ + {"spine1", 0.75f}, + {"spine3", 0.75f}, + {"spine5", 0.75f}, + {"spine7", 0.75f}, + {"spine9", 0.75f}, +}}; + +constexpr std::array skHeadJoints{{ + {"head", "Top_LCTR", {1.f, 0.15f, 0.5f}}, + {"head", "Bottom_LCTR", {0.75f, 0.15f, 0.25f}}, +}}; +} // namespace CMagdolite::CMagdolite(TUniqueId uid, std::string_view name, const CEntityInfo& info, const zeus::CTransform& xf, CModelData&& mData, const CPatternedInfo& pInfo, const CActorParameters& actParms, float f1, float f2, const CDamageInfo& dInfo1, const CDamageInfo& dInfo2, const CDamageVulnerability& dVuln1, const CDamageVulnerability& dVuln2, CAssetId modelId, - CAssetId skinId, float f3, float f4, float f5, float f6, - const CMagdolite::CMagdoliteData& magData, float f7, float f8, float f9) + CAssetId skinId, float f3, float f4, float f5, float f6, const CFlameInfo& magData, float f7, + float f8, float f9) : CPatterned(ECharacter::Magdolite, uid, name, EFlavorType::Zero, info, xf, std::move(mData), pInfo, EMovementType::Flyer, EColliderType::One, EBodyType::BiPedal, actParms, EKnockBackVariant::Large) , x568_(f4) @@ -26,28 +47,600 @@ CMagdolite::CMagdolite(TUniqueId uid, std::string_view name, const CEntityInfo& , x578_(std::cos(zeus::degToRad(f2))) , x57c_(f1) , x584_boneTracker(*GetModelData()->GetAnimationData(), "head"sv, zeus::degToRad(f1), zeus::degToRad(90.f), - EBoneTrackingFlags::ParentIk) { - + EBoneTrackingFlags::ParentIk) +, x5bc_(dVuln1) +, x624_(dVuln2) +, x690_headlessModel( + CToken(TObjOwnerDerivedFromIObj::GetNewDerivedObject(std::make_unique( + g_SimplePool->GetObj({SBIG('CMDL'), modelId}), g_SimplePool->GetObj({SBIG('CSKR'), skinId}), + x64_modelData->GetAnimationData()->GetModelData()->GetLayoutInfo(), 1, 1)))) +, x6a8_(magData) +, x6cc_(g_SimplePool->GetObj("FlameThrower"sv)) +, x6d4_(dInfo1) +, x6f0_(dInfo2) +, x71c_(xf.origin) +, x744_(f7) +, x748_(f8) +, x74c_(f9) { + x460_knockBackController.SetAutoResetImpulse(false); + x460_knockBackController.SetEnableBurn(false); } void CMagdolite::AcceptScriptMsg(EScriptObjectMessage msg, TUniqueId uid, CStateManager& mgr) { switch (msg) { + case EScriptObjectMessage::Touched: + ApplyContactDamage(uid, mgr); + break; case EScriptObjectMessage::Damage: case EScriptObjectMessage::InvulnDamage: - /* TODO Implement */ + if (TCastToConstPtr proj = mgr.GetObjectById(uid)) { + if (proj->GetOwnerId() == mgr.GetPlayer().GetUniqueId() && + (GetBodyController()->GetPercentageFrozen() <= 0.f || + x5bc_.GetVulnerability(proj->GetDamageInfo().GetWeaponMode(), false) == EVulnerability::Deflect)) { + if (x70c_ - HealthInfo(mgr)->GetHP() <= x574_ && + x624_.GetVulnerability(proj->GetDamageInfo().GetWeaponMode(), false) != EVulnerability::Deflect) { + x400_24_hitByPlayerProjectile = true; + } else { + x70c_ = HealthInfo(mgr)->GetHP(); + x754_24_ = true; + } + } else if (x400_24_hitByPlayerProjectile) { + x754_26_ = true; + x401_30_pendingDeath = true; + } + } return; - case EScriptObjectMessage::Registered: + case EScriptObjectMessage::SuspendedMove: { + if (x580_) + x580_->SetMovable(mgr, false); + break; + } + case EScriptObjectMessage::Registered: { x450_bodyController->Activate(mgr); RemoveMaterial(EMaterialTypes::Solid, mgr); AddMaterial(EMaterialTypes::NonSolidDamageable, mgr); x584_boneTracker.SetActive(false); CreateShadow(false); - /* TODO Finish */ + const zeus::CVector3f boxExtents = GetBoundingBox().extents(); + SetBoundingBox({-boxExtents, boxExtents}); + SetupCollisionActors(mgr); + x330_stateMachineState.SetDelay(0.f); + CreateFlameThrower(mgr); + x70c_ = GetHealthInfo(mgr)->GetHP(); + break; + } + case EScriptObjectMessage::Deleted: + x580_->Destroy(mgr); + if (x6c8_flameThrowerId != kInvalidUniqueId) { + mgr.FreeScriptObject(x6c8_flameThrowerId); + x6c8_flameThrowerId = kInvalidUniqueId; + } break; default: break; } CPatterned::AcceptScriptMsg(msg, uid, mgr); } + +void CMagdolite::ApplyContactDamage(TUniqueId uid, CStateManager& mgr) { + if (TCastToConstPtr colAct = mgr.GetObjectById(uid)) { + if (!IsAlive()) + return; + CDamageInfo dInfo = GetContactDamage(); + + for (TUniqueId testId : x69c_) { + if (testId == colAct->GetUniqueId()) { + dInfo = x6f0_; + break; + } + } + + if (colAct->GetLastTouchedObject() == mgr.GetPlayer().GetUniqueId() && x420_curDamageRemTime <= 0.f) { + mgr.ApplyDamage(GetUniqueId(), mgr.GetPlayer().GetUniqueId(), GetUniqueId(), dInfo, + CMaterialFilter::MakeIncludeExclude({EMaterialTypes::Solid}, {}), {}); + x420_curDamageRemTime = x424_damageWaitTime; + } + } +} + +void CMagdolite::SetupCollisionActors(CStateManager& mgr) { + std::vector joints; + joints.reserve(8); + const CAnimData* animData = GetModelData()->GetAnimationData(); + for (const auto& info : skSpineJoints) { + joints.push_back(CJointCollisionDescription::SphereCollision(animData->GetLocatorSegId(info.name), info.radius, + info.name, 200.f)); + } + + for (const auto& info : skHeadJoints) { + joints.push_back(CJointCollisionDescription::OBBAutoSizeCollision( + animData->GetLocatorSegId(info.from), animData->GetLocatorSegId(info.to), info.bounds, + CJointCollisionDescription::EOrientationType::One, info.to, 200.f)); + } + x580_.reset(new CCollisionActorManager(mgr, GetUniqueId(), GetAreaIdAlways(), joints, GetActive())); + + for (int i = 0; i < x580_->GetNumCollisionActors(); ++i) { + const auto& desc = x580_->GetCollisionDescFromIndex(i); + if (TCastToPtr colAct = mgr.ObjectById(desc.GetCollisionActorId())) { + colAct->AddMaterial(EMaterialTypes::AIJoint, mgr); + } + } + + for (int i = 0; i < x580_->GetNumCollisionActors(); ++i) { + const auto& desc = x580_->GetCollisionDescFromIndex(i); + if (desc.GetName().compare("Top_LCTR"sv) != 0 && desc.GetName().compare("Bottom_LCTR"sv) != 0) { + if (TCastToPtr colAct = mgr.ObjectById(desc.GetCollisionActorId())) { + colAct->AddMaterial(EMaterialTypes::ProjectilePassthrough, mgr); + x69c_.push_back(colAct->GetUniqueId()); + } + } + } +} + +void CMagdolite::CreateFlameThrower(CStateManager& mgr) { + if (x6c8_flameThrowerId != kInvalidUniqueId) + return; + + x6c8_flameThrowerId = mgr.AllocateUniqueId(); + mgr.AddObject(new CFlameThrower(x6cc_, "Magdolite_Flame"sv, EWeaponType::Plasma, x6a8_, {}, + EMaterialTypes::CollisionActor, x6d4_, x6c8_flameThrowerId, GetAreaIdAlways(), + GetUniqueId(), EProjectileAttrib::None, CAssetId(), -1, CAssetId())); +} + +void CMagdolite::LaunchFlameThrower(CStateManager& mgr, bool fire) { + if ((!fire || x754_30_) && IsAlive()) { + if (CFlameThrower* fl = static_cast(mgr.ObjectById(x6c8_flameThrowerId))) { + if (!fire) + fl->Reset(mgr, false); + else + fl->Fire(GetTransform(), mgr, false); + } + x754_27_ = fire; + } +} + +void CMagdolite::Think(float dt, CStateManager& mgr) { + CPatterned::Think(dt, mgr); + if (!GetActive()) + return; + + x758_ += dt; + if (GetBodyController()->GetPercentageFrozen() > 0.f && x754_27_) { + LaunchFlameThrower(mgr, false); + } + + if (!IsAlive()) { + if (CFlameThrower* fl = static_cast(mgr.ObjectById(x6c8_flameThrowerId))) { + fl->SetTransform(GetLctrTransform("LCTR_MAGMOUTH"sv), dt); + } + } + + zeus::CVector3f plPos = mgr.GetPlayer().GetTranslation(); + zeus::CVector3f aimPos = mgr.GetPlayer().GetAimPosition(mgr, 0.f); + zeus::CTransform xf = GetLctrTransform("LCTR_MAGMOUTH"sv); + zeus::CVector3f aimDir = (aimPos - xf.origin).normalized(); + float angleDiff = zeus::CVector3f::getAngleDiff(xf.basis[1], aimDir); + float dVar7 = std::min(angleDiff, zeus::degToRad(x57c_)); + if (xf.basis[2].dot(aimDir) < 0.f) { + dVar7 = -dVar7; + } + + float dVar8 = dVar7 + zeus::degToRad(x57c_) / 2.f * zeus::degToRad(x57c_); + float dVar2 = 1.f - dVar8; + x710_ = plPos * dVar2 + (aimPos * dVar8); + + if (GetActive()) { // lol tautologies + if (IsAlive()) { + GetModelData()->GetAnimationData()->PreRender(); + x584_boneTracker.Update(dt); + x584_boneTracker.PreRender(mgr, *GetModelData()->GetAnimationData(), GetTransform(), GetModelData()->GetScale(), + *GetBodyController()); + + if (CFlameThrower* fl = static_cast(mgr.ObjectById(x6c8_flameThrowerId))) { + fl->SetTransform(GetLctrTransform("LCTR_MAGMOUTH"sv), dt); + } + } + x580_->Update(dt, mgr, CCollisionActorManager::EUpdateOptions::ObjectSpace); + zeus::CTransform headXf = GetLocatorTransform("head"sv); + MoveCollisionPrimitive(headXf.rotate(GetModelData()->GetScale() * headXf.origin)); + xe4_27_notInSortedLists = true; + } + + if (x750_ == 2) { + if (x738_ * 0.5f <= x734_) + x740_ -= x73c_ * dt; + else + x740_ += x73c_ * dt; + x734_ += x740_ * dt; + SetTranslation(GetTranslation() - zeus::CVector3f{0.f, 0.f, x740_ * dt}); + if (GetTranslation().z() < x728_.z()) { + SetTranslation(x728_); + x750_ = 0; + } + } else if (x750_ == 1) { + if (x738_ * 0.5f <= x734_) + x740_ -= x73c_ * dt; + else + x740_ += x73c_ * dt; + x734_ += x740_ * dt; + + SetTranslation(GetTranslation() + zeus::CVector3f{0.f, 0.f, x740_ * dt}); + if (GetTranslation().z() > x728_.z()) { + SetTranslation(x728_); + x750_ = 0; + } + } +} +void CMagdolite::DoUserAnimEvent(CStateManager& mgr, const CInt32POINode& node, EUserEventType type, float dt) { + + if (type == EUserEventType::DamageOff) { + LaunchFlameThrower(mgr, false); + } else if (type == EUserEventType::DamageOn) { + LaunchFlameThrower(mgr, true); + } else if (type == EUserEventType::BreakLockOn) { + RemoveMaterial(EMaterialTypes::Target, EMaterialTypes::Orbit, mgr); + mgr.GetPlayer().SetOrbitRequestForTarget(GetUniqueId(), CPlayer::EPlayerOrbitRequest::ActivateOrbitSource, mgr); + return; + } + CPatterned::DoUserAnimEvent(mgr, node, type, dt); +} +void CMagdolite::Death(CStateManager& mgr, const zeus::CVector3f& direction, EScriptObjectState state) { + if (IsAlive()) + return; + zeus::CTransform tmpXf = GetTransform(); + SetTransform(GetLctrTransform("head"sv)); + SendScriptMsgs(EScriptObjectState::MassiveDeath, mgr, EScriptObjectMessage::None); + GenerateDeathExplosion(mgr); + SetTransform(tmpXf); + GetModelData()->GetAnimationData()->SubstituteModelData(x690_headlessModel); + x460_knockBackController.SetEnableFreeze(false); + GetBodyController()->UnFreeze(); + if (!x754_26_) + x460_knockBackController.SetSeverity(pas::ESeverity::Two); + else + GetModelData()->GetAnimationData()->SubstituteModelData(x690_headlessModel); + + CPatterned::Death(mgr, direction, state); +} + +void CMagdolite::FluidFXThink(EFluidState state, CScriptWater& water, CStateManager& mgr) { + if (x754_28_ && state == EFluidState::InFluid) { + CFluidPlaneManager* flMgr = mgr.GetFluidPlaneManager(); + if (flMgr->GetLastRippleDeltaTime(GetUniqueId()) >= 2.3f) { + zeus::CVector3f center = GetTranslation(); + center.z() = float(water.GetTriggerBoundsWR().max.z()); + water.GetFluidPlane().AddRipple(3.f, GetUniqueId(), center, water, mgr); + } + } +} + +void CMagdolite::SelectTarget(CStateManager& mgr, EStateMsg msg, float arg) { + if (msg == EStateMsg::Update) { + TUniqueId targetId = FindSuitableTarget(mgr, EScriptObjectState::Attack, EScriptObjectMessage::Follow); + if (targetId != kInvalidUniqueId) { + if (TCastToConstPtr wp = mgr.GetObjectById(targetId)) { + SetTransform(wp->GetTransform()); + x71c_ = GetTranslation(); + } + } + } else if (msg == EStateMsg::Deactivate) { + UpdateOrientation(mgr); + } +} +void CMagdolite::Generate(CStateManager& mgr, EStateMsg msg, float arg) { + if (msg == EStateMsg::Activate) { + x32c_animState = EAnimState::Ready; + x754_24_ = false; + } else if (msg == EStateMsg::Update) { + TryCommand(mgr, pas::EAnimationState::Generate, &CPatterned::TryGenerateNoXf, 0); + if (x32c_animState == EAnimState::Repeat) + GetBodyController()->SetLocomotionType(pas::ELocomotionType::Relaxed); + } else if (msg == EStateMsg::Deactivate) { + x32c_animState = EAnimState::NotReady; + } +} +void CMagdolite::Deactivate(CStateManager& mgr, EStateMsg msg, float arg) { + if (msg == EStateMsg::Activate) { + x32c_animState = EAnimState::Ready; + x754_24_ = false; + x584_boneTracker.SetActive(false); + } else if (msg == EStateMsg::Update) { + TryCommand(mgr, pas::EAnimationState::Step, &CPatterned::TryStep, int(pas::EStepDirection::Down)); + if (x32c_animState == EAnimState::Repeat) + GetBodyController()->SetLocomotionType(pas::ELocomotionType::Internal7); + } else if (msg == EStateMsg::Deactivate) { + x32c_animState = EAnimState::NotReady; + } +} +void CMagdolite::Attack(CStateManager& mgr, EStateMsg msg, float arg) { + if (msg == EStateMsg::Activate) { + x32c_animState = EAnimState::Ready; + x584_boneTracker.SetActive(false); + zeus::CVector3f plPos = mgr.GetPlayer().GetTranslation(); + zeus::CTransform headXf = GetLctrTransform("head"sv); + float zDiff = headXf.origin.z() - plPos.z(); + + float fVar1 = (zDiff - x74c_); + float fVar2 = 0.f; + if (fVar1 >= 0.f) { + fVar2 = fVar1; + if (x748_ < fVar1) + fVar2 = x748_; + } + x728_ = x71c_ - zeus::CVector3f(0.f, 0.f, fVar1 - fVar2); + x740_ = 0.f; + x734_ = 0.f; + x738_ = fVar2; + x73c_ = (2.f * x738_) / (x744_ * x744_); + if (GetTranslation().z() <= x728_.z()) + x750_ = 1; + else + x750_ = 2; + x754_30_ = true; + } else if (msg == EStateMsg::Update) { + TryCommand(mgr, pas::EAnimationState::MeleeAttack, &CPatterned::TryMeleeAttack, 1); + zeus::CVector3f direction = (mgr.GetPlayer().GetTranslation().toVec2f() - GetTranslation().toVec2f()).normalized(); + float angle = zeus::CVector3f::getAngleDiff(GetTransform().basis[1], direction); + if (GetTransform().basis[0].dot(direction) > 0.f) + angle *= -1.f; + + if ((57.29f * angle) <= x3b8_turnSpeed && (57.29f * angle) < -x3b8_turnSpeed) { + angle = zeus::degToRad(-x3b8_turnSpeed); + } else { + angle = zeus::degToRad(x3b8_turnSpeed); + } + + RotateInOneFrameOR(zeus::CQuaternion::fromAxisAngle({0.f, 0.f, 1.f}, angle), arg); + } else if (msg == EStateMsg::Deactivate) { + x32c_animState = EAnimState::NotReady; + LaunchFlameThrower(mgr, false); + x750_ = 1; + x728_ = x71c_; + x740_ = 0.f; + x744_ = 0.f; + x738_ = x728_.z() - GetTranslation().z(); + x73c_ = (2.f * x738_) / (x744_ * x744_); + x754_30_ = false; + } +} +void CMagdolite::Active(CStateManager& mgr, EStateMsg msg, float arg) { + if (msg == EStateMsg::Activate) { + GetBodyController()->SetLocomotionType(pas::ELocomotionType::Lurk); + x584_boneTracker.SetActive(true); + x584_boneTracker.SetTarget(mgr.GetPlayer().GetUniqueId()); + x400_24_hitByPlayerProjectile = false; + x754_29_ = false; + x330_stateMachineState.SetDelay(x568_); + } else if (msg == EStateMsg::Update) { + zeus::CVector3f posDiff = (mgr.GetPlayer().GetTranslation().toVec2f() - GetTranslation().toVec2f()); + if (posDiff.canBeNormalized()) { + posDiff.normalize(); + if (GetTransform().basis[1].dot(posDiff) < x578_) + GetBodyController()->GetCommandMgr().DeliverCmd(CBCLocomotionCmd({}, posDiff, 1.f)); + } + } else if (msg == EStateMsg::Deactivate) { + x330_stateMachineState.SetDelay(((x570_ - x56c_) * mgr.GetActiveRandom()->Float()) + x56c_); + } +} + +void CMagdolite::InActive(CStateManager& mgr, EStateMsg msg, float arg) { + if (msg == EStateMsg::Activate) { + xe7_27_enableRender = false; + GetBodyController()->SetLocomotionType(pas::ELocomotionType::Internal7); + RemoveMaterial(EMaterialTypes::Orbit, EMaterialTypes::Target, mgr); + } else if (msg == EStateMsg::Deactivate) { + AddMaterial(EMaterialTypes::Orbit, EMaterialTypes::Target, mgr); + x754_25_ = false; + UpdateOrientation(mgr); + xe7_27_enableRender = true; + } +} +void CMagdolite::GetUp(CStateManager& mgr, EStateMsg msg, float arg) { + if (msg == EStateMsg::Activate) { + x32c_animState = EAnimState::Ready; + x754_24_ = false; + x754_28_ = true; + AddMaterial(EMaterialTypes::Orbit, EMaterialTypes::Target, mgr); + } else if (msg == EStateMsg::Update) { + if (!x754_25_) + TryCommand(mgr, pas::EAnimationState::Step, &CPatterned::TryStep, int(pas::EStepDirection::Forward)); + else + TryCommand(mgr, pas::EAnimationState::Step, &CPatterned::TryStep, int(pas::EStepDirection::Up)); + if (x32c_animState == EAnimState::Repeat) + GetBodyController()->SetLocomotionType(pas::ELocomotionType::Lurk); + } else if (msg == EStateMsg::Deactivate) { + x32c_animState = EAnimState::NotReady; + x754_28_ = false; + } +} + +void CMagdolite::Taunt(CStateManager& mgr, EStateMsg msg, float arg) { + if (msg == EStateMsg::Activate) { + x32c_animState = EAnimState::Ready; + x584_boneTracker.SetActive(true); + } else if (msg == EStateMsg::Update) { + TryCommand(mgr, pas::EAnimationState::Taunt, &CPatterned::TryTaunt, 1); + } else if (msg == EStateMsg::Deactivate) { + x32c_animState = EAnimState::NotReady; + x584_boneTracker.SetActive(false); + } +} + +void CMagdolite::Lurk(CStateManager& mgr, EStateMsg msg, float arg) { + if (msg == EStateMsg::Activate) { + GetBodyController()->SetLocomotionType(pas::ELocomotionType::Relaxed); + x584_boneTracker.SetActive(false); + x330_stateMachineState.SetDelay(0.f); + AddMaterial(EMaterialTypes::Target, EMaterialTypes::Orbit, mgr); + } else if (msg == EStateMsg::Deactivate) { + x754_25_ = true; + } +} +void CMagdolite::ProjectileAttack(CStateManager& mgr, EStateMsg msg, float arg) { + if (msg == EStateMsg::Activate) { + x32c_animState = EAnimState::Ready; + LaunchFlameThrower(mgr, true); + x584_boneTracker.SetActive(true); + x584_boneTracker.SetNoHorizontalAim(true); + x584_boneTracker.SetTarget(kInvalidUniqueId); + x754_30_ = true; + } else if (msg == EStateMsg::Update) { + if (TooClose(mgr, 0.f)) { + TryCommand(mgr, pas::EAnimationState::ProjectileAttack, &CPatterned::TryProjectileAttack, 0); + } else { + TryCommand(mgr, pas::EAnimationState::ProjectileAttack, &CPatterned::TryProjectileAttack, 1); + } + x584_boneTracker.SetTargetPosition(x710_); + } else if (msg == EStateMsg::Deactivate) { + x32c_animState = EAnimState::NotReady; + LaunchFlameThrower(mgr, false); + x584_boneTracker.SetActive(false); + x584_boneTracker.SetNoHorizontalAim(false); + x754_30_ = false; + x584_boneTracker.SetTarget(mgr.GetPlayer().GetUniqueId()); + } +} + +void CMagdolite::Flinch(CStateManager& mgr, EStateMsg msg, float arg) { + if (msg == EStateMsg::Activate) { + x32c_animState = EAnimState::Ready; + x400_24_hitByPlayerProjectile = false; + x584_boneTracker.SetActive(false); + } else if (msg == EStateMsg::Update) { + TryCommand(mgr, pas::EAnimationState::KnockBack, &CPatterned::TryKnockBack, 0); + } else if (msg == EStateMsg::Deactivate) { + x32c_animState = EAnimState::NotReady; + } +} + +void CMagdolite::Retreat(CStateManager& mgr, EStateMsg msg, float arg) { + if (msg == EStateMsg::Activate) { + x32c_animState = EAnimState::Ready; + x754_24_ = false; + x584_boneTracker.SetActive(false); + GetBodyController()->GetCommandMgr().DeliverCmd(CBodyStateCmd(EBodyStateCmd::NextState)); + x754_28_ = true; + } else if (msg == EStateMsg::Update) { + TryCommand(mgr, pas::EAnimationState::Generate, &CPatterned::TryGenerate, 1); + if (x32c_animState == EAnimState::Repeat) + GetBodyController()->SetLocomotionType(pas::ELocomotionType::Internal7); + } else if (msg == EStateMsg::Deactivate) { + x754_28_ = false; + x32c_animState = EAnimState::NotReady; + } +} + +bool CMagdolite::InAttackPosition(CStateManager& mgr, float arg) { + zeus::CTransform xf = GetLctrTransform("head"sv); + zeus::CVector3f plAimPos = mgr.GetPlayer().GetAimPosition(mgr, 0.f); + zeus::CVector3f diff = (plAimPos - GetTranslation()); + if (GetTranslation().z() < plAimPos.z() && plAimPos.z() < xf.origin.z()) + diff.z() = 0.f; + + if (std::fabs(diff.magnitude()) >= FLT_EPSILON) { + printf("InAttackPosition: %08X 1\n", GetEditorId().id); + return ((1.f / diff.magnitude()) * diff).dot(GetTransform().basis[1]) < x578_; + } + + printf("InAttackPosition: %08X 0\n", GetEditorId().id); + return false; +} + +bool CMagdolite::Leash(CStateManager& mgr, float arg) { + float dist = (GetTranslation() - mgr.GetPlayer().GetTranslation()).magSquared(); + bool ret = dist < x3c8_leashRadius * x3c8_leashRadius; + printf("Leash: %08X %i %f\n", GetEditorId().id, ret, dist); + return ret; +} + +bool CMagdolite::HasAttackPattern(CStateManager& mgr, float arg) { + bool ret = FindSuitableTarget(mgr, EScriptObjectState::Attack, EScriptObjectMessage::Follow) != kInvalidUniqueId; + printf("LineOfSight: %08X %i\n", GetEditorId().id, ret); + return ret; +} +bool CMagdolite::LineOfSight(CStateManager& mgr, float arg) { + zeus::CTransform mouthXf = GetLctrTransform("LCTR_MAGMOUTH"sv); + zeus::CVector3f diff = x710_ - mouthXf.origin; + if (diff.canBeNormalized()) { + diff.normalize(); + if (diff.dot(GetTransform().basis[1]) < x578_) + return false; + } + + bool ret = mgr.RayCollideWorld( + mouthXf.origin, x710_, + CMaterialFilter::MakeIncludeExclude({EMaterialTypes::Solid, EMaterialTypes::Character}, + {EMaterialTypes::Player, EMaterialTypes::ProjectilePassthrough}), + this); + printf("LineOfSight: %08X %i\n", GetEditorId().id, ret); + return ret; +} +bool CMagdolite::ShouldRetreat(CStateManager& mgr, float arg) { + printf("ShouldRetreat: %08X %i\n", GetEditorId().id, x754_24_); + return x754_24_; +} +void CMagdolite::UpdateOrientation(CStateManager& mgr) { + zeus::CVector3f plDiff = (mgr.GetPlayer().GetTranslation().toVec2f() - GetTranslation().toVec2f()); + plDiff = plDiff.normalized(); + float angle = zeus::CVector3f::getAngleDiff(GetTransform().basis[1], plDiff); + if (GetTransform().basis[0].dot(plDiff) > 0.f) + angle *= -1.f; + zeus::CQuaternion q = GetTransform().basis; + q.rotateZ(angle); + SetTransform(q.toTransform(GetTranslation())); +} + +TUniqueId CMagdolite::FindSuitableTarget(CStateManager& mgr, EScriptObjectState state, EScriptObjectMessage msg) { + float lastDistance = FLT_MAX; + int wpCount = 0; + float maxDistanceSq = + x754_29_ ? x3bc_detectionRange * x3bc_detectionRange : x300_maxAttackRange * x300_maxAttackRange; + + TUniqueId tmpId = kInvalidUniqueId; + for (const auto& conn : x20_conns) { + if (conn.x0_state != state || conn.x4_msg != msg) + continue; + TUniqueId uid = mgr.GetIdForScript(conn.x8_objId); + if (const CEntity* ent = mgr.GetObjectById(uid)) { + if (!ent->GetActive()) + continue; + + if (TCastToConstPtr wp = ent) { + ++wpCount; + const float dist = (wp->GetTranslation() - mgr.GetPlayer().GetTranslation()).magSquared(); + if (dist < maxDistanceSq) { + if (dist < lastDistance) { + lastDistance = dist; + tmpId = uid; + } + } + } + } + } + + if (!x754_29_) { + int skipCount = mgr.GetActiveRandom()->Next(); + skipCount = skipCount - (skipCount / wpCount) * wpCount; + for (const auto& conn : x20_conns) { + if (conn.x0_state != state || conn.x4_msg != msg) + continue; + TUniqueId uid = mgr.GetIdForScript(conn.x8_objId); + if (const CEntity* ent = mgr.GetObjectById(uid)) { + if (!ent->GetActive()) + continue; + + if (TCastToConstPtr wp = ent) { + tmpId = uid; + if (skipCount == 0) + break; + --skipCount; + } + } + } + } + + return tmpId; +} + } // namespace urde::MP1 diff --git a/Runtime/MP1/World/CMagdolite.hpp b/Runtime/MP1/World/CMagdolite.hpp index 9346e6027..380828026 100644 --- a/Runtime/MP1/World/CMagdolite.hpp +++ b/Runtime/MP1/World/CMagdolite.hpp @@ -1,9 +1,14 @@ #pragma once #include "Runtime/Character/CBoneTracking.hpp" +#include "Runtime/Weapon/CFlameInfo.hpp" #include "Runtime/World/CPatterned.hpp" -namespace urde::MP1 { +namespace urde { +class CCollisionActorManager; +class CSkinnedModel; +class CWeaponDescription; +namespace MP1 { class CMagdolite : public CPatterned { public: class CMagdoliteData { @@ -26,15 +31,95 @@ private: float x574_; float x578_; float x57c_; - u32 x580_; + std::unique_ptr x580_; CBoneTracking x584_boneTracker; + CDamageVulnerability x5bc_; + CDamageVulnerability x624_; + /* x68c_ */ + TLockedToken x690_headlessModel; + TToken x698_originalModel; + rstl::reserved_vector x69c_; + CFlameInfo x6a8_; + TUniqueId x6c8_flameThrowerId = kInvalidUniqueId; + TLockedToken x6cc_; + CDamageInfo x6d4_; + CDamageInfo x6f0_; + float x70c_; + zeus::CVector3f x710_; + zeus::CVector3f x71c_; + zeus::CVector3f x728_; + float x734_ = 0.f; + float x738_ = 0.f; + float x73c_ = 0.f; + float x740_ = 0.f; + float x744_; + float x748_; + float x74c_; + u32 x750_ = 0; +#if 0 + bool x754_24_ : 1 = false; + bool x754_25_ : 1 = false; + bool x754_26_ : 1 = false; + bool x754_27_ : 1 = false; + bool x754_28_ : 1 = false; + bool x754_29_ : 1 = true; + bool x754_30_ : 1 = false; +#else + bool x754_24_ = false; + bool x754_25_ = false; + bool x754_26_ = false; + bool x754_27_ = false; + bool x754_28_ = false; + bool x754_29_ = true; + bool x754_30_ = false; +#endif + float x758_ = 0.f; + + void ApplyContactDamage(TUniqueId uid, CStateManager& mgr); + void SetupCollisionActors(CStateManager& mgr); + void CreateFlameThrower(CStateManager& mgr); + void LaunchFlameThrower(CStateManager& mgr, bool fire); + void UpdateOrientation(CStateManager& mgr); + TUniqueId FindSuitableTarget(CStateManager& mgr, EScriptObjectState state, EScriptObjectMessage msg); public: DEFINE_PATTERNED(Magdolite) CMagdolite(TUniqueId, std::string_view, const CEntityInfo&, const zeus::CTransform&, CModelData&&, const CPatternedInfo&, const CActorParameters&, float, float, const CDamageInfo&, const CDamageInfo&, const CDamageVulnerability&, const CDamageVulnerability&, CAssetId, CAssetId, float, float, float, float, - const CMagdoliteData&, float, float, float); + const CFlameInfo& flameInfo, float, float, float); void AcceptScriptMsg(EScriptObjectMessage, TUniqueId, CStateManager&) override; + void Think(float dt, CStateManager& mgr) override; + void Touch(CActor&, CStateManager&) override {}; + const CDamageVulnerability* GetDamageVulnerability() const override { + return x400_25_alive ? CAi::GetDamageVulnerability() : &CDamageVulnerability::ImmuneVulnerabilty(); + } + + const CDamageVulnerability* GetDamageVulnerability(const zeus::CVector3f&, const zeus::CVector3f&, + const CDamageInfo&) const override { + return GetDamageVulnerability(); + } + + void DoUserAnimEvent(CStateManager& mgr, const CInt32POINode& node, EUserEventType type, float dt) override; + void Death(CStateManager& mgr, const zeus::CVector3f& direction, EScriptObjectState state) override; + void FluidFXThink(EFluidState state, CScriptWater& water, CStateManager& mgr) override; + void SelectTarget(CStateManager& mgr, EStateMsg msg, float arg) override; + void Generate(CStateManager& mgr, EStateMsg msg, float arg) override; + void Deactivate(CStateManager& mgr, EStateMsg msg, float arg) override; + void Attack(CStateManager& mgr, EStateMsg msg, float arg) override; + void Active(CStateManager& mgr, EStateMsg msg, float arg) override; + void InActive(CStateManager& mgr, EStateMsg msg, float arg) override; + void GetUp(CStateManager& mgr, EStateMsg msg, float arg) override; + void Taunt(CStateManager& mgr, EStateMsg msg, float arg) override; + void Lurk(CStateManager& mgr, EStateMsg msg, float arg) override; + void ProjectileAttack(CStateManager& mgr, EStateMsg msg, float arg) override; + void Flinch(CStateManager& mgr, EStateMsg msg, float arg) override; + void Retreat(CStateManager& mgr, EStateMsg msg, float arg) override; + bool InAttackPosition(CStateManager& mgr, float arg) override; + bool Leash(CStateManager& mgr, float arg) override; + bool HasAttackPattern(CStateManager& mgr, float arg) override; + bool LineOfSight(CStateManager& mgr, float arg) override; + bool ShouldRetreat(CStateManager& mgr, float arg) override; }; -} // namespace urde::MP1 +} // namespace MP1 +} // namespace urde diff --git a/Runtime/MP1/World/CRidley.cpp b/Runtime/MP1/World/CRidley.cpp index 80dbe047e..e38b83375 100644 --- a/Runtime/MP1/World/CRidley.cpp +++ b/Runtime/MP1/World/CRidley.cpp @@ -157,21 +157,25 @@ constexpr std::array skWingEffects{ "WingSparks8"sv, }; -constexpr std::array skTail{{{"Tail_1", "Tail_3", 0.66f}, - {"Tail_3", "Tail_5", 0.66f}, - {"Tail_5", "Tail_7", 0.66f}, - {"Tail_7", "Tail_9", 0.66f}}}; +constexpr std::array skTail{{ + {"Tail_1", "Tail_3", 0.66f}, + {"Tail_3", "Tail_5", 0.66f}, + {"Tail_5", "Tail_7", 0.66f}, + {"Tail_7", "Tail_9", 0.66f}, +}}; -constexpr std::array skSphereJoints{{{"Skeleton_Root", 0.6f}, - {"Spine_2", 0.6f}, - {"breastPlate_LCTR", 0.6f}, - {"Head_1", 0.6f}, - {"L_wrist", 0.5f}, - {"R_wrist", 0.5f}, - {"L_ankle", 0.6f}, - {"R_ankle", 0.6f}, - {"L_pinky_1", 0.4f}, - {"R_pinky_1", 0.4f}}}; +constexpr std::array skSphereJoints{{ + {"Skeleton_Root", 0.6f}, + {"Spine_2", 0.6f}, + {"breastPlate_LCTR", 0.6f}, + {"Head_1", 0.6f}, + {"L_wrist", 0.5f}, + {"R_wrist", 0.5f}, + {"L_ankle", 0.6f}, + {"R_ankle", 0.6f}, + {"L_pinky_1", 0.4f}, + {"R_pinky_1", 0.4f}, +}}; struct SSomeRidleyStruct3 { float x0_; diff --git a/Runtime/MP1/World/CSeedling.cpp b/Runtime/MP1/World/CSeedling.cpp index a8a98e5d9..608131dee 100644 --- a/Runtime/MP1/World/CSeedling.cpp +++ b/Runtime/MP1/World/CSeedling.cpp @@ -198,7 +198,7 @@ void CSeedling::Generate(CStateManager& mgr, EStateMsg msg, float) { if (msg == EStateMsg::Activate) x32c_animState = EAnimState::Ready; else if (msg == EStateMsg::Update) - TryCommand(mgr, pas::EAnimationState::Generate, &CPatterned::TryGenerate, 0); + TryCommand(mgr, pas::EAnimationState::Generate, &CPatterned::TryGenerateNoXf, 0); } bool CSeedling::ShouldAttack(CStateManager& mgr, float) { diff --git a/Runtime/Weapon/CFlameInfo.cpp b/Runtime/Weapon/CFlameInfo.cpp index 293c78233..b9a30ff0a 100644 --- a/Runtime/Weapon/CFlameInfo.cpp +++ b/Runtime/Weapon/CFlameInfo.cpp @@ -3,6 +3,14 @@ namespace urde { CFlameInfo::CFlameInfo(s32 w1, s32 w2, CAssetId flameFxId, s32 w3, float f1, float f2, float f3) -: x0_(w1), x4_attributes(w2), x8_flameFxId(flameFxId), xc_length(w3), x10_(f1), x18_(f2), x1c_(f3) {} +: x0_propertyCount(w1), x4_attributes(w2), x8_flameFxId(flameFxId), xc_length(w3), x10_(f1), x18_(f2), x1c_(f3) {} +CFlameInfo::CFlameInfo(CInputStream& in) +: x0_propertyCount(in.readUint32Big()) +, x4_attributes(in.readUint32Big()) +, x8_flameFxId(in) +, xc_length(in.readUint32Big()) +, x10_(in.readFloatBig()) +, x18_(in.readFloatBig()) +, x1c_(in.readFloatBig()) {} } // namespace urde diff --git a/Runtime/Weapon/CFlameInfo.hpp b/Runtime/Weapon/CFlameInfo.hpp index 03f754264..0461c56f8 100644 --- a/Runtime/Weapon/CFlameInfo.hpp +++ b/Runtime/Weapon/CFlameInfo.hpp @@ -1,11 +1,12 @@ #pragma once #include "Runtime/Weapon/CGameProjectile.hpp" +#include "Runtime/IOStreams.hpp" namespace urde { class CFlameInfo { friend class CFlameThrower; - s32 x0_; + s32 x0_propertyCount; s32 x4_attributes; CAssetId x8_flameFxId; s32 xc_length; @@ -15,6 +16,7 @@ class CFlameInfo { public: CFlameInfo(s32, s32, CAssetId, s32, float, float, float); + CFlameInfo(CInputStream& in); s32 GetAttributes() const { return x4_attributes; } s32 GetLength() const { return xc_length; } diff --git a/Runtime/World/CPatterned.cpp b/Runtime/World/CPatterned.cpp index f6a4af231..3f6c016f0 100644 --- a/Runtime/World/CPatterned.cpp +++ b/Runtime/World/CPatterned.cpp @@ -651,8 +651,10 @@ bool CPatterned::Leash(CStateManager&, float arg) { } bool CPatterned::InDetectionRange(CStateManager& mgr, float arg) { - zeus::CVector3f delta = mgr.GetPlayer().GetTranslation() - GetTranslation(); - if (delta.magSquared() < x3bc_detectionRange * x3bc_detectionRange) { + zeus::CVector3f delta = GetTranslation() - mgr.GetPlayer().GetTranslation(); + const float maxRange = x3bc_detectionRange * x3bc_detectionRange; + const float dist = delta.magSquared(); + if (dist < maxRange) { if (x3c0_detectionHeightRange > 0.f) return delta.z() * delta.z() < x3c0_detectionHeightRange * x3c0_detectionHeightRange; return true; @@ -884,7 +886,11 @@ void CPatterned::TryMeleeAttack(CStateManager& mgr, int arg) { } void CPatterned::TryGenerate(CStateManager& mgr, int arg) { - x450_bodyController->GetCommandMgr().DeliverCmd(CBCGenerateCmd(pas::EGenerateType(arg), x2e0_destPos, true)); + x450_bodyController->GetCommandMgr().DeliverCmd(CBCGenerateCmd(pas::EGenerateType(arg), x2e0_destPos, false)); +} + +void CPatterned::TryGenerateNoXf(CStateManager& mgr, int arg) { + x450_bodyController->GetCommandMgr().DeliverCmd(CBCGenerateCmd(pas::EGenerateType::Zero, x2e0_destPos, true)); } void CPatterned::TryJump(CStateManager& mgr, int arg) { @@ -939,6 +945,10 @@ void CPatterned::TryGenerateDeactivate(urde::CStateManager& mgr, int arg) { x450_bodyController->GetCommandMgr().DeliverCmd(CBCGenerateCmd(pas::EGenerateType(arg), zeus::skZero3f)); } +void CPatterned::TryStep(CStateManager& mgr, int arg) { + x450_bodyController->GetCommandMgr().DeliverCmd(CBCStepCmd(pas::EStepDirection(arg), pas::EStepType::Normal)); +} + void CPatterned::BuildBodyController(EBodyType bodyType) { if (x450_bodyController) return; diff --git a/Runtime/World/CPatterned.hpp b/Runtime/World/CPatterned.hpp index 4113af4a9..16a9362c4 100644 --- a/Runtime/World/CPatterned.hpp +++ b/Runtime/World/CPatterned.hpp @@ -322,6 +322,7 @@ public: void TryProjectileAttack(CStateManager& mgr, int arg); void TryMeleeAttack(CStateManager& mgr, int arg); void TryGenerate(CStateManager& mgr, int arg); + void TryGenerateNoXf(CStateManager& mgr, int arg); void TryJump(CStateManager& mgr, int arg); void TryTurn(CStateManager& mgr, int arg); void TryGetUp(CStateManager& mgr, int arg); @@ -334,6 +335,7 @@ public: void TryWallHang(CStateManager& mgr, int arg); void TryKnockBack(CStateManager& mgr, int arg); void TryGenerateDeactivate(CStateManager& mgr, int arg); + void TryStep(CStateManager& mgr, int arg); virtual bool KnockbackWhenFrozen() const { return true; } virtual void MassiveDeath(CStateManager& mgr); diff --git a/Runtime/World/CStateMachine.cpp b/Runtime/World/CStateMachine.cpp index 1208afb7b..aea7c6dd5 100644 --- a/Runtime/World/CStateMachine.cpp +++ b/Runtime/World/CStateMachine.cpp @@ -65,6 +65,8 @@ void CStateMachineState::Update(CStateManager& mgr, CAi& ai, float delta) { if (x4_state) { x8_time += delta; x4_state->CallFunc(mgr, ai, EStateMsg::Update, delta); + fmt::print(fmt("Update: {} {} {} - {} {}\n"), ai.GetUniqueId(), ai.GetEditorId(), ai.GetName(), + x4_state->xc_name, int(x4_state - x0_machine->GetStateVector().data())); for (int i = 0; i < x4_state->GetNumTriggers(); ++i) { CAiTrigger* trig = x4_state->GetTrig(i); CAiState* state = nullptr; diff --git a/Runtime/World/ScriptLoader.cpp b/Runtime/World/ScriptLoader.cpp index e7985007b..6d401a657 100644 --- a/Runtime/World/ScriptLoader.cpp +++ b/Runtime/World/ScriptLoader.cpp @@ -3019,7 +3019,7 @@ CEntity* ScriptLoader::LoadMagdolite(CStateManager& mgr, CInputStream& in, int p float f4 = in.readFloatBig(); float f5 = in.readFloatBig(); float f6 = in.readFloatBig(); - MP1::CMagdolite::CMagdoliteData magData(in); + CFlameInfo flameInfo(in); float f7 = in.readFloatBig(); float f8 = in.readFloatBig(); float f9 = in.readFloatBig(); @@ -3030,7 +3030,7 @@ CEntity* ScriptLoader::LoadMagdolite(CStateManager& mgr, CInputStream& in, int p return new MP1::CMagdolite(mgr.AllocateUniqueId(), actorHead.x0_name, info, actorHead.x10_transform, std::move(modelData), pInfo, actorParameters, f1, f2, damageInfo1, damageInfo2, - damageVulnerability1, damageVulnerability2, modelId, skinId, f3, f4, f5, f6, magData, f7, + damageVulnerability1, damageVulnerability2, modelId, skinId, f3, f4, f5, f6, flameInfo, f7, f8, f9); }