diff --git a/DataSpec/DNACommon/TXTR.cpp b/DataSpec/DNACommon/TXTR.cpp index 6a996fe73..459951094 100644 --- a/DataSpec/DNACommon/TXTR.cpp +++ b/DataSpec/DNACommon/TXTR.cpp @@ -238,8 +238,8 @@ static void DecodeIA4(png_structp png, png_infop info, const uint8_t* texels, in for (int y = height - 1; y >= 0; --y) { for (int x = 0; x < width; ++x) { uint8_t texel = Lookup8BPP(texels, width, x, y); - buf[x * 2] = Convert4To8(texel >> 4 & 0xf); - buf[x * 2 + 1] = Convert4To8(texel & 0xf); + buf[x * 2 ] = Convert4To8(texel & 0xf); + buf[x * 2 + 1] = Convert4To8(texel >> 4 & 0xf); } png_write_row(png, buf.get()); } @@ -252,8 +252,8 @@ static void EncodeIA4(const uint8_t* rgbaIn, uint8_t* texels, int width, int hei { for (int x=0 ; x CPlayerState {"thermalvisor"sv, EItemType::ThermalVisor}, {"chargebeam"sv, EItemType::ChargeBeam}, {"supermissile"sv, EItemType::SuperMissile}, + {"grapple"sv, EItemType::GrappleBeam}, {"grapplebeam"sv, EItemType::GrappleBeam}, {"xrayvisor"sv, EItemType::XRayVisor}, {"icespreader"sv, EItemType::IceSpreader}, + {"spacejump"sv, EItemType::SpaceJumpBoots}, {"spacejumpboots"sv, EItemType::SpaceJumpBoots}, {"morphball"sv, EItemType::MorphBall}, {"combatvisor"sv, EItemType::CombatVisor}, diff --git a/Runtime/MP1/World/CJellyZap.cpp b/Runtime/MP1/World/CJellyZap.cpp new file mode 100644 index 000000000..601855f0e --- /dev/null +++ b/Runtime/MP1/World/CJellyZap.cpp @@ -0,0 +1,186 @@ +#include "CJellyZap.hpp" +#include "CStateManager.hpp" +#include "World/CPlayer.hpp" +#include "TCastTo.hpp" + +namespace urde::MP1 { +const CMaterialFilter CJellyZap::kPlayerFilter = CMaterialFilter::MakeInclude({EMaterialTypes::Player}); + +CJellyZap::CJellyZap(TUniqueId uid, std::string_view name, const CEntityInfo& info, const zeus::CTransform& xf, + CModelData&& mData, const CDamageInfo& dInfo, bool b1, float f1, float f2, float f3, float f4, + float f5, float f6, float f7, float f8, float f9, float f10, float f11, float f12, + const CPatternedInfo& pInfo, const CActorParameters& actParms) +: CPatterned(ECharacter::JellyZap, uid, name, EFlavorType::Zero, info, xf, std::move(mData), pInfo, + EMovementType::Flyer, EColliderType::One, EBodyType::BiPedal, actParms, EKnockBackVariant::Medium) +, x56c_attackDamage(dInfo) +, x588_attackRadius(f1) +, x58c_(f2) +, x590_(f4) +, x594_(f3) +, x598_(f8) +, x59c_(f9) +, x5a0_(f10) +, x5a4_(f11) +, x5a8_attackDelay(f5) +, x5ac_(f6) +, x5b0_(f7) +, x5b4_(f12) +, x5b8_24_(false) +, x5b8_25_(false) +, x5b8_26_(b1) { + UpdateThermalFrozenState(true); + x50c_baseDamageMag = 0.f; +} + +void CJellyZap::Accept(IVisitor& visitor) { visitor.Visit(this); } + +void CJellyZap::AcceptScriptMsg(EScriptObjectMessage msg, TUniqueId uid, CStateManager& mgr) { + CPatterned::AcceptScriptMsg(msg, uid, mgr); + if (msg == EScriptObjectMessage::Registered) { + x450_bodyController->Activate(mgr); + } else if (msg == EScriptObjectMessage::Activate) { + AddAttractor(mgr); + } else if (msg == EScriptObjectMessage::Deleted || msg == EScriptObjectMessage::Deactivate) { + RemoveAllAttractors(mgr); + } +} + +void CJellyZap::Think(float dt, CStateManager& mgr) { + CPatterned::Think(dt, mgr); + if (!GetActive()) + return; + if (x5b8_24_) + x450_bodyController->FaceDirection(GetTranslation() - mgr.GetPlayer().GetTranslation(), dt); + + float fv = (x5b8_25_ && x450_bodyController->GetPercentageFrozen() == 0.f ? x50c_baseDamageMag + (dt / 0.3f) + : x50c_baseDamageMag - (dt / 0.75f)); + x50c_baseDamageMag = zeus::clamp(0.f, fv, 1.f); +} + +void CJellyZap::DoUserAnimEvent(CStateManager& mgr, const CInt32POINode& node, EUserEventType type, float dt) { + if (type == EUserEventType::DamageOn) { + mgr.ApplyDamageToWorld(GetUniqueId(), *this, GetTranslation(), x56c_attackDamage, kPlayerFilter); + return; + } + CPatterned::DoUserAnimEvent(mgr, node, type, dt); +} + +void CJellyZap::Attack(CStateManager& mgr, EStateMsg msg, float arg) { + if (msg == EStateMsg::Activate) { + x32c_animState = EAnimState::Ready; + AddRepulsor(mgr); + x5b8_25_ = true; + float dist = (mgr.GetPlayer().GetTranslation() - GetTranslation()).magSquared(); + if (dist < x56c_attackDamage.GetRadius()) { + float staticTimer = 3.f * (1.f - (dist / x56c_attackDamage.GetRadius())) + 2.f; + if (staticTimer > mgr.GetPlayer().GetStaticTimer()) { + mgr.GetPlayer().SetHudDisable(staticTimer, 0.5f, 2.5f); + mgr.GetPlayer().SetOrbitRequestForTarget(mgr.GetPlayer().GetOrbitTargetId(), + CPlayer::EPlayerOrbitRequest::ActivateOrbitSource, mgr); + } + + mgr.GetPlayerState()->GetStaticInterference().AddSource(GetUniqueId(), 0.5f, 0.5f); + } + x330_stateMachineState.SetDelay(x5ac_); + } else if (msg == EStateMsg::Update) { + TryCommand(mgr, pas::EAnimationState::MeleeAttack, &CPatterned::TryMeleeAttack, 1); + } else if (msg == EStateMsg::Deactivate) { + RemoveAllAttractors(mgr); + x32c_animState = EAnimState::NotReady; + x5b8_25_ = false; + } +} + +void CJellyZap::Suck(CStateManager& mgr, EStateMsg msg, float arg) { + if (msg == EStateMsg::Activate) { + x32c_animState = EAnimState::Ready; + RemoveAllAttractors(mgr); + } else if (msg == EStateMsg::Update) { + + } else if (msg == EStateMsg::Deactivate) { + + } +} + +void CJellyZap::Active(CStateManager& mgr, EStateMsg msg, float arg) { + if (msg == EStateMsg::Activate) { + x5b8_24_ = true; + x450_bodyController->SetLocomotionType(pas::ELocomotionType::Lurk); + x568_ = 0; + x330_stateMachineState.SetDelay(x3d0_playerLeashTime); + } else if (msg == EStateMsg::Update) { + zeus::CVector3f targetVector = + GetTranslation() - (zeus::CVector3f(0.f, 0.f, 1.f) + mgr.GetPlayer().GetTranslation()); + x450_bodyController->GetCommandMgr().SetTargetVector(targetVector); + if (x5b8_26_) { + zeus::CVector3f moveToImpulse = + GetMoveToORImpulseWR(GetTransform().transposeRotate(arg * (zeus::CVector3f(0.f, 1.f, 0.f) * x598_)), arg); + ApplyImpulseOR(moveToImpulse, zeus::CAxisAngle::sIdentity); + } + } else if (msg == EStateMsg::Deactivate) { + x5b8_24_ = false; + } +} + +void CJellyZap::InActive(CStateManager& mgr, EStateMsg msg, float) { + if (msg == EStateMsg::Activate) { + x400_24_hitByPlayerProjectile = false; + x450_bodyController->SetLocomotionType(pas::ELocomotionType::Relaxed); + AddAttractor(mgr); + x568_ = 0; + } +} + +void CJellyZap::Flinch(CStateManager& mgr, EStateMsg msg, float arg) { + if (msg == EStateMsg::Activate) { + x400_24_hitByPlayerProjectile = false; + x32c_animState = EAnimState::NotReady; + x330_stateMachineState.SetDelay(x5b0_); + } else if (msg == EStateMsg::Update) { + TryCommand(mgr, pas::EAnimationState::KnockBack, &CPatterned::TryKnockBack, 0); + } else if (msg == EStateMsg::Deactivate) { + x32c_animState = EAnimState::NotReady; + } +} + +bool CJellyZap::InAttackPosition(CStateManager& mgr, float) { + if (mgr.GetPlayer().GetFluidCounter() == 0) + return false; + + return (mgr.GetPlayer().GetTranslation() - GetTranslation()).magnitude() < x588_attackRadius * x588_attackRadius; +} + +bool CJellyZap::InDetectionRange(CStateManager& mgr, float arg) { + return (mgr.GetPlayer().GetFluidCounter() != 0 ? CPatterned::InDetectionRange(mgr, arg) : false); +} + +void CJellyZap::AddSelfToFishCloud(CStateManager, float, bool b) {} + +void CJellyZap::AddRepulsor(CStateManager&) {} + +void CJellyZap::AddAttractor(CStateManager&) {} + +void CJellyZap::RemoveSelfFromFishCloud(CStateManager&) {} + +void CJellyZap::RemoveAllAttractors(CStateManager& mgr) { RemoveSelfFromFishCloud(mgr); } + +bool CJellyZap::ClosestToPlayer(CStateManager& mgr) { + zeus::CVector3f playerPos = mgr.GetPlayer().GetTranslation(); + float ourDistance = (playerPos - GetTranslation()).magnitude(); + float closestDistance = ourDistance; + for (CEntity* ent : mgr.GetPhysicsActorObjectList()) { + if (CJellyZap* zap = CPatterned::CastTo(ent)) { + if (zap->GetAreaIdAlways() != GetAreaIdAlways()) + continue; + + float tmpDist = (playerPos - zap->GetTranslation()).magnitude(); + if (tmpDist < closestDistance) + closestDistance = tmpDist; + + if (zap->x5b8_25_) + return false; + } + } + return zeus::close_enough(closestDistance, ourDistance); +} +} // namespace urde::MP1 \ No newline at end of file diff --git a/Runtime/MP1/World/CJellyZap.hpp b/Runtime/MP1/World/CJellyZap.hpp new file mode 100644 index 000000000..3178d06e1 --- /dev/null +++ b/Runtime/MP1/World/CJellyZap.hpp @@ -0,0 +1,54 @@ +#pragma once + +#include "World/CPatterned.hpp" + +namespace urde::MP1 { +class CJellyZap : public CPatterned { + static const CMaterialFilter kPlayerFilter; + u32 x568_ = 0; + CDamageInfo x56c_attackDamage; + float x588_attackRadius; + float x58c_; + float x590_; + float x594_; + float x598_; + float x59c_; + float x5a0_; + float x5a4_; + float x5a8_attackDelay; + float x5ac_; + float x5b0_; + float x5b4_; + bool x5b8_24_ : 1; + bool x5b8_25_ : 1; + bool x5b8_26_ : 1; + + void AddSelfToFishCloud(CStateManager, float, bool); + void AddRepulsor(CStateManager&); + void AddAttractor(CStateManager&); + void RemoveSelfFromFishCloud(CStateManager&); + void RemoveAllAttractors(CStateManager&); + bool ClosestToPlayer(CStateManager&); + +public: + DEFINE_PATTERNED(JellyZap) + + CJellyZap(TUniqueId, std::string_view, const CEntityInfo&, const zeus::CTransform&, CModelData&&, const CDamageInfo&, + bool, float, float, float, float, float, float, float, float, float, float, float, float, + const CPatternedInfo&, const CActorParameters&); + + void Accept(IVisitor&); + void AcceptScriptMsg(EScriptObjectMessage, TUniqueId, CStateManager&); + void Think(float, CStateManager&); + void DoUserAnimEvent(CStateManager&, const CInt32POINode&, EUserEventType, float dt); + void Attack(CStateManager&, EStateMsg, float); + void Suck(CStateManager&, EStateMsg, float); + void Active(CStateManager&, EStateMsg, float); + void InActive(CStateManager&, EStateMsg, float); + void Flinch(CStateManager&, EStateMsg, float); + bool ShouldAttack(CStateManager&, float) { return x330_stateMachineState.GetTime() > x5a8_attackDelay; } + bool ShouldSpecialAttack(CStateManager& mgr, float) { return ClosestToPlayer(mgr); } + bool InAttackPosition(CStateManager&, float); + bool InDetectionRange(CStateManager&, float); +}; +} // namespace urde::MP1 \ No newline at end of file diff --git a/Runtime/MP1/World/CMakeLists.txt b/Runtime/MP1/World/CMakeLists.txt index 5c2cfe4f4..d4f15b72c 100644 --- a/Runtime/MP1/World/CMakeLists.txt +++ b/Runtime/MP1/World/CMakeLists.txt @@ -14,6 +14,7 @@ set(MP1_WORLD_SOURCES CAtomicAlpha.hpp CAtomicAlpha.cpp CAtomicBeta.hpp CAtomicBeta.cpp CFlickerBat.hpp CFlickerBat.cpp + CJellyZap.hpp CJellyZap.cpp CFlyingPirate.hpp CFlyingPirate.cpp CMetroidPrimeRelay.hpp CMetroidPrimeRelay.cpp CMetroidPrimeExo.hpp CMetroidPrimeExo.cpp diff --git a/Runtime/World/CPatterned.cpp b/Runtime/World/CPatterned.cpp index ce6570601..5afa68325 100644 --- a/Runtime/World/CPatterned.cpp +++ b/Runtime/World/CPatterned.cpp @@ -928,6 +928,10 @@ void CPatterned::TryWallHang(CStateManager& mgr, int arg) { x450_bodyController->GetCommandMgr().DeliverCmd(CBCWallHangCmd(x2dc_destObj)); } +void CPatterned::TryKnockBack(CStateManager& mgr, int arg) { + x450_bodyController->GetCommandMgr().DeliverCmd(CBCKnockBackCmd(GetTranslation(), pas::ESeverity(arg))); +} + void CPatterned::BuildBodyController(EBodyType bodyType) { if (x450_bodyController) return; diff --git a/Runtime/World/CPatterned.hpp b/Runtime/World/CPatterned.hpp index 4482cc04a..5004ecfbe 100644 --- a/Runtime/World/CPatterned.hpp +++ b/Runtime/World/CPatterned.hpp @@ -333,6 +333,7 @@ public: void TryBreakDodge(CStateManager& mgr, int arg); void TryCover(CStateManager& mgr, int arg); void TryWallHang(CStateManager& mgr, int arg); + void TryKnockBack(CStateManager& mgr, int arg); virtual bool KnockbackWhenFrozen() const { return true; } virtual void MassiveDeath(CStateManager& mgr); diff --git a/Runtime/World/CScriptWaypoint.cpp b/Runtime/World/CScriptWaypoint.cpp index 4dfe69361..3efe7f7c4 100644 --- a/Runtime/World/CScriptWaypoint.cpp +++ b/Runtime/World/CScriptWaypoint.cpp @@ -27,8 +27,7 @@ void CScriptWaypoint::Accept(IVisitor& visitor) { visitor.Visit(this); } void CScriptWaypoint::AcceptScriptMsg(EScriptObjectMessage msg, TUniqueId sender, CStateManager& mgr) { CActor::AcceptScriptMsg(msg, sender, mgr); - if (GetActive()) - if (msg == EScriptObjectMessage::Arrived) + if (GetActive() && msg == EScriptObjectMessage::Arrived) SendScriptMsgs(EScriptObjectState::Arrived, mgr, EScriptObjectMessage::None); } diff --git a/Runtime/World/ScriptLoader.cpp b/Runtime/World/ScriptLoader.cpp index 76b9a797d..2df31b8b0 100644 --- a/Runtime/World/ScriptLoader.cpp +++ b/Runtime/World/ScriptLoader.cpp @@ -29,6 +29,7 @@ #include "CScriptCameraShaker.hpp" #include "CScriptCameraWaypoint.hpp" #include "CScriptColorModulate.hpp" +#include "MP1/World/CJellyZap.hpp" #include "CScriptControllerAction.hpp" #include "CScriptCounter.hpp" #include "MP1/World/CElitePirate.hpp" @@ -2368,8 +2369,39 @@ CEntity* ScriptLoader::LoadVisorGoo(CStateManager& mgr, CInputStream& in, int pr } CEntity* ScriptLoader::LoadJellyZap(CStateManager& mgr, CInputStream& in, int propCount, const CEntityInfo& info) { - UNIMPLEMENTED("JellyZap"); - return nullptr; + if (!EnsurePropertyCount(propCount, 20, "JellyZap")) + return nullptr; + + SScaledActorHead aHead = LoadScaledActorHead(in, mgr); + auto pair = CPatternedInfo::HasCorrectParameterCount(in); + if (!pair.first) + return nullptr; + + CPatternedInfo pInfo(in, pair.second); + CActorParameters actParms = LoadActorParameters(in); + CDamageInfo dInfo(in); + float f1 = in.readFloatBig(); + float f2 = in.readFloatBig(); + float f3 = in.readFloatBig(); + float f4 = in.readFloatBig(); + float f5 = in.readFloatBig(); + float f6 = in.readFloatBig(); + float f7 = in.readFloatBig(); + float f8 = in.readFloatBig(); + float f9 = in.readFloatBig(); + float f10 = in.readFloatBig(); + float f11 = in.readFloatBig(); + float f12 = in.readFloatBig(); + bool b1 = in.readBool(); + + const CAnimationParameters& animParms = pInfo.GetAnimationParameters(); + if (g_ResFactory->GetResourceTypeById(animParms.GetACSFile()) != SBIG('ANCS')) + return nullptr; + + CModelData mData(CAnimRes(animParms.GetACSFile(), animParms.GetCharacter(), aHead.x40_scale, + animParms.GetInitialAnimation(), true)); + return new MP1::CJellyZap(mgr.AllocateUniqueId(), aHead.x0_name, info, aHead.x10_transform, std::move(mData), dInfo, + b1, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, pInfo, actParms); } CEntity* ScriptLoader::LoadControllerAction(CStateManager& mgr, CInputStream& in, int propCount, @@ -3116,8 +3148,38 @@ CEntity* ScriptLoader::LoadAtomicBeta(CStateManager& mgr, CInputStream& in, int } CEntity* ScriptLoader::LoadIceZoomer(CStateManager& mgr, CInputStream& in, int propCount, const CEntityInfo& info) { - UNIMPLEMENTED("IceZoomer"); - return nullptr; + if (!EnsurePropertyCount(propCount, 16, "IceZoomer")) + return nullptr; + SScaledActorHead actHead = LoadScaledActorHead(in, mgr); + auto pair = CPatternedInfo::HasCorrectParameterCount(in); + if (!pair.first) + return nullptr; + + CPatternedInfo pInfo(in, pair.second); + CActorParameters actParms = LoadActorParameters(in); + const CAnimationParameters& animParms = pInfo.GetAnimationParameters(); + if (!animParms.GetACSFile().IsValid()) + return nullptr; + + float advanceWpRadius = in.readFloatBig(); + float f2 = in.readFloatBig(); + float alignAngleVel = in.readFloatBig(); + float f4 = in.readFloatBig(); + float playerObstructionMinDist = in.readFloatBig(); + float moveFowardWeight = in.readFloatBig(); + CAssetId modelRes(in.readUint32Big()); + CAssetId skinRes(in.readUint32Big()); + CDamageVulnerability dVuln(in); + float iceZoomerJointHP = in.readFloatBig(); + + CModelData mData( + CAnimRes(animParms.GetACSFile(), animParms.GetCharacter(), actHead.x40_scale, animParms.GetInitialAnimation(), + true)); + return new MP1::CParasite(mgr.AllocateUniqueId(), actHead.x0_name, CPatterned::EFlavorType::Zero, info, + actHead.x10_transform, std::move(mData), pInfo, EBodyType::WallWalker, 0.f, advanceWpRadius, + f2, alignAngleVel, f4, 0.2f, 0.4f, 0.f, 0.f, 0.f, 0.f, 0.f, 1.f, moveFowardWeight, 0.f, 0.f, + playerObstructionMinDist, 0.f, false, CWallWalker::EWalkerType::IceZoomer, dVuln, + CDamageInfo(), 0xFFFF, 0xFFFF, 0xFFFF, modelRes, skinRes, iceZoomerJointHP, actParms); } CEntity* ScriptLoader::LoadPuffer(CStateManager& mgr, CInputStream& in, int propCount, const CEntityInfo& info) {