this commit may be kinda broke but I'm gonna push it to check something

This commit is contained in:
jadebenn 2024-04-20 17:28:13 -05:00
parent f41321eb7b
commit 071c022058
3 changed files with 103 additions and 103 deletions

View File

@ -85,7 +85,7 @@ PetComponent::PetComponent(Entity* parentEntity, uint32_t componentId)
m_MovementAI = nullptr; m_MovementAI = nullptr;
m_ReadyToInteract = false; m_ReadyToInteract = false;
m_State = PetAiState::spawn; m_State = PetAiState::SPAWN;
SetIsHandlingInteraction(false); SetIsHandlingInteraction(false);
std::string checkPreconditions = GeneralUtils::UTF16ToWTF8(parentEntity->GetVar<std::u16string>(u"CheckPrecondition")); std::string checkPreconditions = GeneralUtils::UTF16ToWTF8(parentEntity->GetVar<std::u16string>(u"CheckPrecondition"));
@ -139,8 +139,8 @@ void PetComponent::Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpd
void PetComponent::OnUse(Entity* originator) { void PetComponent::OnUse(Entity* originator) {
LOG_DEBUG("PET USE!"); LOG_DEBUG("PET USE!");
if (!IsReadyToInteract()) return;
if (IsReadyToInteract()) {
switch (m_Interaction.ability) { switch (m_Interaction.ability) {
case ePetAbilityType::DigAtPosition: // Treasure dig case ePetAbilityType::DigAtPosition: // Treasure dig
StartInteractTreasureDig(); StartInteractTreasureDig();
@ -150,12 +150,65 @@ void PetComponent::OnUse(Entity* originator) {
StartInteractBouncer(); StartInteractBouncer();
break; break;
default: default: // Pet taming minigame
StartTamingMinigame(originator);
break; break;
} }
}
void PetComponent::Update(float deltaTime) {
// Update timers
m_TimerBounce -= deltaTime;
if (m_Timer > 0) {
m_Timer -= deltaTime;
return;
} }
// The minigame logic beneath this comment should be rewritten... eventually // Remove "left behind" pets and handle failing pet taming minigame
if (m_Owner != LWOOBJID_EMPTY) {
const Entity* const owner = GetOwner();
if (!owner) {
m_Parent->Kill();
return;
}
} else {
ClientFailTamingMinigame(); // TODO: This is not despawning the built model correctly
}
if (m_Flags.Has<PetFlag::SPAWNING>()) OnSpawn();
// Handle pet AI states
switch (m_State) {
case PetAiState::IDLE:
Wander();
break;
case PetAiState::FOLLOW:
OnFollow(deltaTime);
break;
case PetAiState::GO_TO_OBJ:
if (m_MovementAI->AtFinalWaypoint()) {
LOG_DEBUG("Reached object!");
m_MovementAI->Stop();
SetPetAiState(PetAiState::INTERACT);
} else {
m_Timer += 0.5f;
}
break;
case PetAiState::INTERACT:
OnInteract();
break;
default:
LOG_DEBUG("Unknown state: %d!", m_Flags);
break;
}
}
void PetComponent::StartTamingMinigame(Entity* originator) {
if (m_Owner != LWOOBJID_EMPTY) return; if (m_Owner != LWOOBJID_EMPTY) return;
if (m_Tamer != LWOOBJID_EMPTY) { if (m_Tamer != LWOOBJID_EMPTY) {
@ -167,7 +220,6 @@ void PetComponent::OnUse(Entity* originator) {
} }
auto* const inventoryComponent = originator->GetComponent<InventoryComponent>(); auto* const inventoryComponent = originator->GetComponent<InventoryComponent>();
if (!inventoryComponent) return; if (!inventoryComponent) return;
if (m_Preconditions.has_value() && !m_Preconditions->Check(originator, true)) return; if (m_Preconditions.has_value() && !m_Preconditions->Check(originator, true)) return;
@ -186,7 +238,7 @@ void PetComponent::OnUse(Entity* originator) {
std::string buildFile; std::string buildFile;
// It may make sense to move this minigame-specific logic into another file // It may make sense to move this minigame-specific logic into another file
if (cached == buildCache.end()) { if (cached == buildCache.cend()) {
auto query = CDClientDatabase::CreatePreppedStmt( auto query = CDClientDatabase::CreatePreppedStmt(
"SELECT ValidPiecesLXF, PuzzleModelLot, Timelimit, NumValidPieces, imagCostPerBuild FROM TamingBuildPuzzles WHERE NPCLot = ?;"); "SELECT ValidPiecesLXF, PuzzleModelLot, Timelimit, NumValidPieces, imagCostPerBuild FROM TamingBuildPuzzles WHERE NPCLot = ?;");
query.bind(1, static_cast<int>(m_Parent->GetLOT())); query.bind(1, static_cast<int>(m_Parent->GetLOT()));
@ -226,11 +278,9 @@ void PetComponent::OnUse(Entity* originator) {
} }
const auto* const destroyableComponent = originator->GetComponent<DestroyableComponent>(); const auto* const destroyableComponent = originator->GetComponent<DestroyableComponent>();
if (!destroyableComponent) return; if (!destroyableComponent) return;
const auto imagination = destroyableComponent->GetImagination(); const auto imagination = destroyableComponent->GetImagination();
if (imagination < imaginationCost) return; if (imagination < imaginationCost) return;
const auto& bricks = BrickDatabase::GetBricks(buildFile); const auto& bricks = BrickDatabase::GetBricks(buildFile);
@ -243,13 +293,11 @@ void PetComponent::OnUse(Entity* originator) {
} }
const auto petPosition = m_Parent->GetPosition(); const auto petPosition = m_Parent->GetPosition();
const auto originatorPosition = originator->GetPosition(); const auto originatorPosition = originator->GetPosition();
m_Parent->SetRotation(NiQuaternion::LookAt(petPosition, originatorPosition)); m_Parent->SetRotation(NiQuaternion::LookAt(petPosition, originatorPosition));
float interactionDistance = m_Parent->GetVar<float>(u"interaction_distance"); float interactionDistance = m_Parent->GetVar<float>(u"interaction_distance");
if (interactionDistance <= 0) { if (interactionDistance <= 0) {
interactionDistance = 15; interactionDistance = 15;
} }
@ -317,58 +365,6 @@ void PetComponent::OnUse(Entity* originator) {
m_Parent->GetScript()->OnNotifyPetTamingMinigame(m_Parent, originator, ePetTamingNotifyType::BEGIN); m_Parent->GetScript()->OnNotifyPetTamingMinigame(m_Parent, originator, ePetTamingNotifyType::BEGIN);
} }
void PetComponent::Update(float deltaTime) {
// Update timers
m_TimerBounce -= deltaTime;
if (m_Timer > 0) {
m_Timer -= deltaTime;
return;
}
// Remove "left behind" pets and handle failing pet taming minigame
if (m_Owner != LWOOBJID_EMPTY) {
const Entity* const owner = GetOwner();
if (!owner) {
m_Parent->Kill();
return;
}
} else {
ClientFailTamingMinigame(); // TODO: This is not despawning the built model correctly
}
if (m_Flags.Has<PetFlag::SPAWNING>()) OnSpawn();
// Handle pet AI states
switch (m_State) {
case PetAiState::idle:
Wander();
break;
case PetAiState::follow:
OnFollow(deltaTime);
break;
case PetAiState::goToObj:
if (m_MovementAI->AtFinalWaypoint()) {
LOG_DEBUG("Reached object!");
m_MovementAI->Stop();
SetPetAiState(PetAiState::interact);
} else {
m_Timer += 0.5f;
}
break;
case PetAiState::interact:
OnInteract();
break;
default:
LOG_DEBUG("Unknown state: %d!", m_Flags);
break;
}
}
void PetComponent::TryBuild(uint32_t numBricks, bool clientFailed) { void PetComponent::TryBuild(uint32_t numBricks, bool clientFailed) {
if (m_Tamer == LWOOBJID_EMPTY) return; if (m_Tamer == LWOOBJID_EMPTY) return;
@ -628,7 +624,7 @@ void PetComponent::ClientExitTamingMinigame(bool voluntaryExit) {
void PetComponent::StartTimer() { void PetComponent::StartTimer() {
const auto& cached = buildCache.find(m_Parent->GetLOT()); const auto& cached = buildCache.find(m_Parent->GetLOT());
if (cached == buildCache.end()) { if (cached == buildCache.cend()) {
return; return;
} }
@ -724,10 +720,10 @@ void PetComponent::OnSpawn() {
m_Parent->SetOwnerOverride(m_Owner); m_Parent->SetOwnerOverride(m_Owner);
m_MovementAI->SetMaxSpeed(m_PetInfo.sprintSpeed); m_MovementAI->SetMaxSpeed(m_PetInfo.sprintSpeed);
m_MovementAI->SetHaltDistance(m_FollowRadius); m_MovementAI->SetHaltDistance(m_FollowRadius);
SetPetAiState(PetAiState::follow); SetPetAiState(PetAiState::FOLLOW);
} else { } else {
m_Flags.Set<PetFlag::TAMEABLE>(); m_Flags.Set<PetFlag::TAMEABLE>();
SetPetAiState(PetAiState::idle); SetPetAiState(PetAiState::IDLE);
} }
m_Flags.Set<PetFlag::IDLE>(); m_Flags.Set<PetFlag::IDLE>();
@ -748,7 +744,7 @@ void PetComponent::OnFollow(const float deltaTime) {
const LWOOBJID switchID = closestSwitch->GetParentEntity()->GetObjectID(); const LWOOBJID switchID = closestSwitch->GetParentEntity()->GetObjectID();
const float distance = Vector3::DistanceSquared(ownerPos, switchPos); const float distance = Vector3::DistanceSquared(ownerPos, switchPos);
if (distance < 16 * 16) { if (distance < 16 * 16) {
StartInteract(switchPos, PetInteractType::bouncer, switchID); StartInteract(switchPos, PetInteractType::BOUNCER, switchID);
return; return;
} }
} }
@ -764,7 +760,7 @@ void PetComponent::OnFollow(const float deltaTime) {
const NiPoint3 treasurePos = closestTreasure->GetPosition(); const NiPoint3 treasurePos = closestTreasure->GetPosition();
const float distance = Vector3::DistanceSquared(ownerPos, treasurePos); const float distance = Vector3::DistanceSquared(ownerPos, treasurePos);
if (distance < 16 * 16) { if (distance < 16 * 16) {
StartInteract(treasurePos, PetInteractType::treasure, m_Owner); StartInteract(treasurePos, PetInteractType::TREASURE, m_Owner);
return; return;
} }
} }
@ -810,12 +806,12 @@ void PetComponent::OnInteract() {
} }
switch (m_Interaction.type) { switch (m_Interaction.type) {
case PetInteractType::bouncer: case PetInteractType::BOUNCER:
if (IsReadyToInteract()) HandleInteractBouncer(); if (IsReadyToInteract()) HandleInteractBouncer();
else SetupInteractBouncer(); else SetupInteractBouncer();
break; break;
case PetInteractType::treasure: case PetInteractType::TREASURE:
if (IsReadyToInteract()) HandleInteractTreasureDig(); if (IsReadyToInteract()) HandleInteractTreasureDig();
else SetupInteractTreasureDig(); else SetupInteractTreasureDig();
break; break;
@ -832,7 +828,7 @@ void PetComponent::StartInteract(const NiPoint3 position, const PetInteractType
m_Interaction.obj = interactID; // TODO: Check if this should be serialized for goToObj m_Interaction.obj = interactID; // TODO: Check if this should be serialized for goToObj
m_Interaction.type = interactionType; m_Interaction.type = interactionType;
m_Interaction.ability = ePetAbilityType::GoToObject; m_Interaction.ability = ePetAbilityType::GoToObject;
SetPetAiState(PetAiState::goToObj); SetPetAiState(PetAiState::GO_TO_OBJ);
m_MovementAI->SetMaxSpeed(m_PetInfo.runSpeed); m_MovementAI->SetMaxSpeed(m_PetInfo.runSpeed);
m_MovementAI->SetHaltDistance(0.0f); m_MovementAI->SetHaltDistance(0.0f);
m_MovementAI->SetDestination(position); m_MovementAI->SetDestination(position);
@ -847,11 +843,10 @@ void PetComponent::StopInteract(bool bDontSerialize) {
constexpr auto petAbility = ePetAbilityType::Invalid; constexpr auto petAbility = ePetAbilityType::Invalid;
m_Interaction.obj = LWOOBJID_EMPTY; m_Interaction.obj = LWOOBJID_EMPTY;
m_Interaction.type = PetInteractType::none; m_Interaction.type = PetInteractType::NONE;
m_Interaction.ability = petAbility; m_Interaction.ability = petAbility;
SetPetAiState(PetAiState::follow); SetPetAiState(PetAiState::FOLLOW);
m_Flags.Reset<PetFlag::IDLE>(); m_Flags.Reset<PetFlag::IDLE>();
SetIsReadyToInteract(false);
SetIsHandlingInteraction(false); // Needed? SetIsHandlingInteraction(false); // Needed?
m_MovementAI->SetMaxSpeed(m_PetInfo.sprintSpeed); m_MovementAI->SetMaxSpeed(m_PetInfo.sprintSpeed);
m_MovementAI->SetHaltDistance(m_FollowRadius); m_MovementAI->SetHaltDistance(m_FollowRadius);
@ -1170,7 +1165,7 @@ void PetComponent::Command(const NiPoint3& position, const LWOOBJID source, cons
GameMessages::SendPlayEmote(m_Parent->GetObjectID(), typeId, owner->GetObjectID(), UNASSIGNED_SYSTEM_ADDRESS); GameMessages::SendPlayEmote(m_Parent->GetObjectID(), typeId, owner->GetObjectID(), UNASSIGNED_SYSTEM_ADDRESS);
} else if (commandType == 3) { } else if (commandType == 3) {
StopInteract(); // TODO: Verify this is necessary StopInteract(); // TODO: Verify this is necessary
SetPetAiState(PetAiState::follow); SetPetAiState(PetAiState::FOLLOW);
} else if (commandType == 6) { } else if (commandType == 6) {
// TODO: Go to player // TODO: Go to player
} }

View File

@ -18,21 +18,21 @@
* The current state of the pet AI * The current state of the pet AI
*/ */
enum class PetAiState : uint8_t { enum class PetAiState : uint8_t {
idle = 0, // Doing nothing IDLE = 0, // Doing nothing
spawn, // Spawning into the world SPAWN, // Spawning into the world
follow, // Begin following FOLLOW, // Begin following
goToObj, // Go to object GO_TO_OBJ, // Go to object
interact, // Interact with an object INTERACT, // Interact with an object
despawn // Despawning from world DESPAWN // Despawning from world
}; };
/* /*
* The type of object the pet is interacting with * The type of object the pet is interacting with
*/ */
enum class PetInteractType : uint8_t { enum class PetInteractType : uint8_t {
none, // Not interacting NONE, // Not interacting
treasure, // Treasure dig TREASURE, // Treasure dig
bouncer // Bouncer switch BOUNCER // Bouncer switch
}; };
/** /**
@ -113,6 +113,11 @@ public:
*/ */
void OnUse(Entity* originator) override; void OnUse(Entity* originator) override;
/**
* Start the pet taming minigame
*/
void StartTamingMinigame(Entity* originator);
/** /**
* Attempts to complete the pet minigame by passing a list of bricks to build the minigame model. * Attempts to complete the pet minigame by passing a list of bricks to build the minigame model.
* @param bricks the bricks to try to complete the minigame with * @param bricks the bricks to try to complete the minigame with
@ -396,7 +401,7 @@ private:
/** /**
* The type of object that the pet is currently interacting with (e.g. a treasure chest or switch) * The type of object that the pet is currently interacting with (e.g. a treasure chest or switch)
*/ */
PetInteractType type = PetInteractType::none; PetInteractType type = PetInteractType::NONE;
/** /**
* The interaction ability * The interaction ability

View File

@ -41,25 +41,25 @@ TEST_F(PetTest, PlacementNewAddComponentTest) {
// Test getting initial status // Test getting initial status
ASSERT_EQ(petComponent->GetParent()->GetObjectID(), 15); ASSERT_EQ(petComponent->GetParent()->GetObjectID(), 15);
ASSERT_EQ(petComponent->GetPetAiState(), PetAiState::spawn); ASSERT_EQ(petComponent->GetPetAiState(), PetAiState::SPAWN);
} }
TEST_F(PetTest, PetAiState) { TEST_F(PetTest, PetAiState) {
const auto initialState = petComponent->GetPetAiState(); const auto initialState = petComponent->GetPetAiState();
ASSERT_EQ(initialState, PetAiState::spawn); ASSERT_EQ(initialState, PetAiState::SPAWN);
petComponent->SetPetAiState(PetAiState::follow); petComponent->SetPetAiState(PetAiState::FOLLOW);
ASSERT_EQ(PetAiState::follow, petComponent->GetPetAiState()); ASSERT_EQ(PetAiState::FOLLOW, petComponent->GetPetAiState());
petComponent->SetPetAiState(PetAiState::idle); petComponent->SetPetAiState(PetAiState::IDLE);
ASSERT_EQ(PetAiState::idle, petComponent->GetPetAiState()); ASSERT_EQ(PetAiState::IDLE, petComponent->GetPetAiState());
petComponent->SetPetAiState(PetAiState::interact); petComponent->SetPetAiState(PetAiState::INTERACT);
ASSERT_EQ(PetAiState::interact, petComponent->GetPetAiState()); ASSERT_EQ(PetAiState::INTERACT, petComponent->GetPetAiState());
petComponent->SetPetAiState(PetAiState::despawn); petComponent->SetPetAiState(PetAiState::DESPAWN);
ASSERT_EQ(PetAiState::despawn, petComponent->GetPetAiState()); ASSERT_EQ(PetAiState::DESPAWN, petComponent->GetPetAiState());
} }
// Test the pet use logic // Test the pet use logic