2021-12-05 17:54:36 +00:00
# include "PetComponent.h"
# include "GameMessages.h"
# include "BrickDatabase.h"
# include "CDClientDatabase.h"
# include "ChatPackets.h"
# include "EntityManager.h"
# include "Character.h"
# include "CharacterComponent.h"
# include "InventoryComponent.h"
# include "Item.h"
# include "MissionComponent.h"
# include "SwitchComponent.h"
# include "DestroyableComponent.h"
# include "dpWorld.h"
# include "PetDigServer.h"
# include "../dWorldServer/ObjectIDManager.h"
2022-09-02 18:49:19 +00:00
# include "eUnequippableActiveType.h"
2023-05-02 22:39:21 +00:00
# include "eTerminateType.h"
# include "ePetTamingNotifyType.h"
2023-12-14 07:33:49 +00:00
# include "ePetAbilityType.h"
2023-05-02 22:39:21 +00:00
# include "eUseItemResponse.h"
# include "ePlayerFlag.h"
2023-12-14 07:33:49 +00:00
# include "eHelpType.h"
2021-12-05 17:54:36 +00:00
# include "Game.h"
2022-06-18 07:03:27 +00:00
# include "dConfig.h"
2021-12-05 17:54:36 +00:00
# include "dChatFilter.h"
2023-12-14 07:33:49 +00:00
# include "dZoneManager.h"
2021-12-05 17:54:36 +00:00
# include "Database.h"
2023-01-07 05:17:05 +00:00
# include "EntityInfo.h"
2021-12-05 17:54:36 +00:00
# include "eMissionTaskType.h"
2023-03-20 13:10:52 +00:00
# include "RenderComponent.h"
2023-04-25 18:17:40 +00:00
# include "eObjectBits.h"
2023-03-24 23:16:45 +00:00
# include "eGameMasterLevel.h"
2023-11-13 10:41:27 +00:00
# include "eMissionState.h"
2021-12-05 17:54:36 +00:00
std : : unordered_map < LOT , PetComponent : : PetPuzzleData > PetComponent : : buildCache { } ;
std : : unordered_map < LWOOBJID , LWOOBJID > PetComponent : : currentActivities { } ;
std : : unordered_map < LWOOBJID , LWOOBJID > PetComponent : : activePets { } ;
2023-12-14 07:33:49 +00:00
float PetComponent : : m_FollowRadius { } ;
2021-12-05 17:54:36 +00:00
/**
* Maps all the pet lots to a flag indicating that the player has caught it . All basic pets have been guessed by ObjID
* while the faction ones could be checked using their respective missions .
*/
2023-05-02 22:39:21 +00:00
std : : map < LOT , int32_t > PetComponent : : petFlags = {
2021-12-05 17:54:36 +00:00
{ 3050 , 801 } , // Elephant
{ 3054 , 803 } , // Cat
{ 3195 , 806 } , // Triceratops
{ 3254 , 807 } , // Terrier
{ 3261 , 811 } , // Skunk
{ 3672 , 813 } , // Bunny
{ 3994 , 814 } , // Crocodile
{ 5635 , 815 } , // Doberman
{ 5636 , 816 } , // Buffalo
{ 5637 , 818 } , // Robot Dog
{ 5639 , 819 } , // Red Dragon
{ 5640 , 820 } , // Tortoise
{ 5641 , 821 } , // Green Dragon
{ 5643 , 822 } , // Panda, see mission 786
{ 5642 , 823 } , // Mantis
{ 6720 , 824 } , // Warthog
{ 3520 , 825 } , // Lion, see mission 1318
{ 7638 , 826 } , // Goat
{ 7694 , 827 } , // Crab
{ 12294 , 829 } , // Reindeer
{ 12431 , 830 } , // Stegosaurus, see mission 1386
{ 12432 , 831 } , // Saber cat, see mission 1389
{ 12433 , 832 } , // Gryphon, see mission 1392
{ 12434 , 833 } , // Alien, see mission 1188
// 834: unknown?, see mission 506, 688
{ 16210 , 836 } , // Ninjago Earth Dragon, see mission 1836
{ 13067 , 838 } , // Skeleton dragon
} ;
2023-01-03 17:22:04 +00:00
PetComponent : : PetComponent ( Entity * parent , uint32_t componentId ) : Component ( parent ) {
2021-12-05 17:54:36 +00:00
m_ComponentId = componentId ;
2022-07-28 13:39:57 +00:00
2021-12-05 17:54:36 +00:00
m_Interaction = LWOOBJID_EMPTY ;
2023-12-12 03:10:29 +00:00
m_InteractType = PetInteractType : : none ;
2021-12-05 17:54:36 +00:00
m_Owner = LWOOBJID_EMPTY ;
m_ModerationStatus = 0 ;
m_Tamer = LWOOBJID_EMPTY ;
m_ModelId = LWOOBJID_EMPTY ;
m_Timer = 0 ;
m_TimerAway = 0 ;
2023-12-13 06:14:53 +00:00
m_TimerBounce = 0 ;
2021-12-05 17:54:36 +00:00
m_DatabaseId = LWOOBJID_EMPTY ;
2023-11-19 22:46:27 +00:00
m_Status = PetStatus : : TAMEABLE ; // Tameable
2023-12-14 07:33:49 +00:00
m_Ability = ePetAbilityType : : Invalid ;
2023-12-11 01:55:36 +00:00
m_StartPosition = m_Parent - > GetPosition ( ) ; //NiPoint3::ZERO;
2021-12-05 17:54:36 +00:00
m_MovementAI = nullptr ;
m_Preconditions = nullptr ;
2022-05-04 03:38:49 +00:00
2023-12-13 06:14:53 +00:00
m_ReadyToInteract = false ;
2023-12-11 01:55:36 +00:00
SetPetAiState ( PetAiState : : spawn ) ;
2023-12-14 07:33:49 +00:00
m_FollowRadius = Game : : zoneManager - > GetPetFollowRadius ( ) ;
SetIsHandlingInteraction ( false ) ;
2023-11-19 00:43:47 +00:00
2022-05-04 03:38:49 +00:00
std : : string checkPreconditions = GeneralUtils : : UTF16ToWTF8 ( parent - > GetVar < std : : u16string > ( u " CheckPrecondition " ) ) ;
if ( ! checkPreconditions . empty ( ) ) {
SetPreconditions ( checkPreconditions ) ;
}
2023-12-11 01:55:36 +00:00
// Get pet information from the CDClient
auto query = CDClientDatabase : : CreatePreppedStmt (
" SELECT walkSpeed, runSpeed, sprintSpeed, imaginationDrainRate FROM PetComponent WHERE id = ?; " ) ;
2022-06-18 06:53:09 +00:00
query . bind ( 1 , static_cast < int > ( componentId ) ) ;
auto result = query . execQuery ( ) ;
2023-12-11 01:55:36 +00:00
if ( ! result . eof ( ) ) {
2023-12-13 06:17:53 +00:00
m_WalkSpeed = result . getFloatField ( 0 , 2.5f ) ;
2023-12-12 03:10:29 +00:00
m_RunSpeed = result . getFloatField ( 1 , 5.0f ) ;
m_SprintSpeed = result . getFloatField ( 2 , 10.0f ) ;
imaginationDrainRate = result . getFloatField ( 3 , 60.0f ) ;
2022-06-18 06:53:09 +00:00
}
2023-12-11 01:55:36 +00:00
2022-06-18 06:53:09 +00:00
result . finalize ( ) ;
2021-12-05 17:54:36 +00:00
}
2023-08-10 21:33:15 +00:00
void PetComponent : : Serialize ( RakNet : : BitStream * outBitStream , bool bIsInitialUpdate ) {
2021-12-05 17:54:36 +00:00
const bool tamed = m_Owner ! = LWOOBJID_EMPTY ;
2022-07-25 02:26:51 +00:00
outBitStream - > Write1 ( ) ; // Always serialize as dirty for now
2021-12-05 17:54:36 +00:00
outBitStream - > Write < uint32_t > ( static_cast < unsigned int > ( m_Status ) ) ;
2023-12-14 07:33:49 +00:00
outBitStream - > Write < uint32_t > ( static_cast < uint32_t > ( tamed ? m_Ability : ePetAbilityType : : Invalid ) ) ; // Something with the overhead icon?
2021-12-05 17:54:36 +00:00
const bool interacting = m_Interaction ! = LWOOBJID_EMPTY ;
2022-07-25 02:26:51 +00:00
2021-12-05 17:54:36 +00:00
outBitStream - > Write ( interacting ) ;
if ( interacting ) {
outBitStream - > Write ( m_Interaction ) ;
}
outBitStream - > Write ( tamed ) ;
if ( tamed ) {
outBitStream - > Write ( m_Owner ) ;
2022-07-28 13:39:57 +00:00
}
2021-12-05 17:54:36 +00:00
2023-01-03 17:22:04 +00:00
if ( bIsInitialUpdate ) {
outBitStream - > Write ( tamed ) ;
if ( tamed ) {
outBitStream - > Write ( m_ModerationStatus ) ;
2021-12-05 17:54:36 +00:00
2023-01-03 17:22:04 +00:00
const auto nameData = GeneralUtils : : UTF8ToUTF16 ( m_Name ) ;
const auto ownerNameData = GeneralUtils : : UTF8ToUTF16 ( m_OwnerName ) ;
2021-12-05 17:54:36 +00:00
2023-01-03 17:22:04 +00:00
outBitStream - > Write ( static_cast < uint8_t > ( nameData . size ( ) ) ) ;
for ( const auto c : nameData ) {
outBitStream - > Write ( c ) ;
}
2021-12-05 17:54:36 +00:00
2023-01-03 17:22:04 +00:00
outBitStream - > Write ( static_cast < uint8_t > ( ownerNameData . size ( ) ) ) ;
for ( const auto c : ownerNameData ) {
outBitStream - > Write ( c ) ;
}
2022-07-28 13:39:57 +00:00
}
2021-12-05 17:54:36 +00:00
}
2022-07-28 13:39:57 +00:00
}
2022-07-25 02:26:51 +00:00
2023-12-11 01:55:36 +00:00
void PetComponent : : SetPetAiState ( PetAiState newState ) {
if ( newState = = GetPetAiState ( ) ) return ;
this - > m_State = newState ;
}
2021-12-05 17:54:36 +00:00
void PetComponent : : OnUse ( Entity * originator ) {
2023-11-19 00:43:47 +00:00
LOG ( " PET USE! " ) ;
2023-12-14 07:33:49 +00:00
if ( IsReadyToInteract ( ) ) {
switch ( GetAbility ( ) ) {
case ePetAbilityType : : DigAtPosition : // Treasure dig TODO: FIX ICON
StartInteractTreasureDig ( ) ;
break ;
case ePetAbilityType : : JumpOnObject : // Bouncer
//StartInteractBouncer();
break ;
default :
break ;
}
}
// TODO: Rewrite everything below this comment
2023-12-11 01:55:36 +00:00
if ( m_Owner ! = LWOOBJID_EMPTY ) return ;
2021-12-05 17:54:36 +00:00
if ( m_Tamer ! = LWOOBJID_EMPTY ) {
2023-07-15 20:56:33 +00:00
auto * tamer = Game : : entityManager - > GetEntity ( m_Tamer ) ;
2021-12-05 17:54:36 +00:00
2023-12-12 03:10:29 +00:00
if ( tamer ! = nullptr ) return ;
2021-12-05 17:54:36 +00:00
2022-01-13 03:48:27 +00:00
m_Tamer = LWOOBJID_EMPTY ;
2022-07-28 13:39:57 +00:00
}
2022-01-13 03:48:27 +00:00
auto * inventoryComponent = originator - > GetComponent < InventoryComponent > ( ) ;
2021-12-05 17:54:36 +00:00
2023-12-12 03:10:29 +00:00
if ( inventoryComponent = = nullptr ) return ;
2021-12-05 17:54:36 +00:00
2023-12-12 03:10:29 +00:00
if ( m_Preconditions ! = nullptr & & ! m_Preconditions - > Check ( originator , true ) ) return ;
2021-12-05 17:54:36 +00:00
auto * movementAIComponent = m_Parent - > GetComponent < MovementAIComponent > ( ) ;
if ( movementAIComponent ! = nullptr ) {
movementAIComponent - > Stop ( ) ;
}
inventoryComponent - > DespawnPet ( ) ;
const auto & cached = buildCache . find ( m_Parent - > GetLOT ( ) ) ;
int32_t imaginationCost = 0 ;
std : : string buildFile ;
if ( cached = = buildCache . end ( ) ) {
2022-01-13 03:48:27 +00:00
auto query = CDClientDatabase : : CreatePreppedStmt (
2021-12-05 17:54:36 +00:00
" SELECT ValidPiecesLXF, PuzzleModelLot, Timelimit, NumValidPieces, imagCostPerBuild FROM TamingBuildPuzzles WHERE NPCLot = ?; " ) ;
2022-01-13 03:48:27 +00:00
query . bind ( 1 , ( int ) m_Parent - > GetLOT ( ) ) ;
2021-12-05 17:54:36 +00:00
auto result = query . execQuery ( ) ;
if ( result . eof ( ) ) {
ChatPackets : : SendSystemMessage ( originator - > GetSystemAddress ( ) , u " Failed to find the puzzle minigame for this pet. " ) ;
return ;
2022-07-28 13:39:57 +00:00
}
2021-12-05 17:54:36 +00:00
if ( result . fieldIsNull ( 0 ) ) {
result . finalize ( ) ;
return ;
}
2022-11-06 00:09:26 +00:00
buildFile = std : : string ( result . getStringField ( 0 ) ) ;
2021-12-05 17:54:36 +00:00
PetPuzzleData data ;
data . buildFile = buildFile ;
data . puzzleModelLot = result . getIntField ( 1 ) ;
data . timeLimit = result . getFloatField ( 2 ) ;
data . numValidPieces = result . getIntField ( 3 ) ;
data . imaginationCost = result . getIntField ( 4 ) ;
if ( data . timeLimit < = 0 ) data . timeLimit = 60 ;
imaginationCost = data . imaginationCost ;
buildCache [ m_Parent - > GetLOT ( ) ] = data ;
result . finalize ( ) ;
2022-07-28 13:39:57 +00:00
} else {
2021-12-05 17:54:36 +00:00
buildFile = cached - > second . buildFile ;
imaginationCost = cached - > second . imaginationCost ;
2022-07-28 13:39:57 +00:00
}
2021-12-05 17:54:36 +00:00
auto * destroyableComponent = originator - > GetComponent < DestroyableComponent > ( ) ;
2023-12-12 03:10:29 +00:00
if ( destroyableComponent = = nullptr ) return ;
2022-07-28 13:39:57 +00:00
2021-12-05 17:54:36 +00:00
auto imagination = destroyableComponent - > GetImagination ( ) ;
2022-07-28 13:39:57 +00:00
2023-12-12 03:10:29 +00:00
if ( imagination < imaginationCost ) return ;
2022-07-28 13:39:57 +00:00
2023-07-17 22:55:25 +00:00
const auto & bricks = BrickDatabase : : GetBricks ( buildFile ) ;
2021-12-05 17:54:36 +00:00
if ( bricks . empty ( ) ) {
ChatPackets : : SendSystemMessage ( originator - > GetSystemAddress ( ) , u " Failed to load the puzzle minigame for this pet. " ) ;
2023-10-21 23:31:55 +00:00
LOG ( " Couldn't find %s for minigame! " , buildFile . c_str ( ) ) ;
2021-12-05 17:54:36 +00:00
return ;
2022-07-28 13:39:57 +00:00
}
2022-07-25 02:26:51 +00:00
2022-04-07 05:21:54 +00:00
auto petPosition = m_Parent - > GetPosition ( ) ;
2021-12-05 17:54:36 +00:00
auto originatorPosition = originator - > GetPosition ( ) ;
m_Parent - > SetRotation ( NiQuaternion : : LookAt ( petPosition , originatorPosition ) ) ;
float interactionDistance = m_Parent - > GetVar < float > ( u " interaction_distance " ) ;
if ( interactionDistance < = 0 ) {
interactionDistance = 15 ;
2022-07-28 13:39:57 +00:00
}
2022-07-25 02:26:51 +00:00
2021-12-05 17:54:36 +00:00
auto position = originatorPosition ;
NiPoint3 forward = NiQuaternion : : LookAt ( m_Parent - > GetPosition ( ) , originator - > GetPosition ( ) ) . GetForwardVector ( ) ;
forward . y = 0 ;
if ( dpWorld : : Instance ( ) . IsLoaded ( ) ) {
NiPoint3 attempt = petPosition + forward * interactionDistance ;
2022-07-25 02:26:51 +00:00
2022-08-02 05:30:19 +00:00
float y = dpWorld : : Instance ( ) . GetNavMesh ( ) - > GetHeightAtPoint ( attempt ) ;
2021-12-05 17:54:36 +00:00
while ( std : : abs ( y - petPosition . y ) > 4 & & interactionDistance > 10 ) {
const NiPoint3 forward = m_Parent - > GetRotation ( ) . GetForwardVector ( ) ;
2022-07-25 02:26:51 +00:00
2021-12-05 17:54:36 +00:00
attempt = originatorPosition + forward * interactionDistance ;
2022-08-02 05:30:19 +00:00
y = dpWorld : : Instance ( ) . GetNavMesh ( ) - > GetHeightAtPoint ( attempt ) ;
2021-12-05 17:54:36 +00:00
interactionDistance - = 0.5f ;
2022-07-28 13:39:57 +00:00
}
2021-12-05 17:54:36 +00:00
position = attempt ;
2022-07-28 13:39:57 +00:00
} else {
2021-12-05 17:54:36 +00:00
position = petPosition + forward * interactionDistance ;
2022-07-28 13:39:57 +00:00
}
2021-12-05 17:54:36 +00:00
auto rotation = NiQuaternion : : LookAt ( position , petPosition ) ;
2022-07-28 13:39:57 +00:00
2021-12-05 17:54:36 +00:00
GameMessages : : SendNotifyPetTamingMinigame (
originator - > GetObjectID ( ) ,
m_Parent - > GetObjectID ( ) ,
LWOOBJID_EMPTY ,
2022-07-28 13:39:57 +00:00
true ,
2023-05-02 22:39:21 +00:00
ePetTamingNotifyType : : BEGIN ,
2021-12-05 17:54:36 +00:00
petPosition ,
position ,
rotation ,
UNASSIGNED_SYSTEM_ADDRESS
2022-07-28 13:39:57 +00:00
) ;
2021-12-05 17:54:36 +00:00
GameMessages : : SendNotifyPetTamingMinigame (
m_Parent - > GetObjectID ( ) ,
LWOOBJID_EMPTY ,
originator - > GetObjectID ( ) ,
2022-07-28 13:39:57 +00:00
true ,
2023-05-02 22:39:21 +00:00
ePetTamingNotifyType : : BEGIN ,
2021-12-05 17:54:36 +00:00
petPosition ,
position ,
rotation ,
UNASSIGNED_SYSTEM_ADDRESS
2022-07-28 13:39:57 +00:00
) ;
2021-12-05 17:54:36 +00:00
GameMessages : : SendNotifyPetTamingPuzzleSelected ( originator - > GetObjectID ( ) , bricks , originator - > GetSystemAddress ( ) ) ;
2022-07-28 13:39:57 +00:00
2021-12-05 17:54:36 +00:00
m_Tamer = originator - > GetObjectID ( ) ;
SetStatus ( 5 ) ;
2022-07-28 13:39:57 +00:00
2021-12-05 17:54:36 +00:00
currentActivities . insert_or_assign ( m_Tamer , m_Parent - > GetObjectID ( ) ) ;
2022-07-28 13:39:57 +00:00
2021-12-05 17:54:36 +00:00
// Notify the start of a pet taming minigame
for ( CppScripts : : Script * script : CppScripts : : GetEntityScripts ( m_Parent ) ) {
2023-05-02 22:39:21 +00:00
script - > OnNotifyPetTamingMinigame ( m_Parent , originator , ePetTamingNotifyType : : BEGIN ) ;
2021-12-05 17:54:36 +00:00
}
}
2022-07-25 02:26:51 +00:00
void PetComponent : : Update ( float deltaTime ) {
2023-12-11 01:55:36 +00:00
// If pet does not have an owner, use the UpdateUnowned() loop
2023-12-12 03:10:29 +00:00
/*if (m_Owner == LWOOBJID_EMPTY) {
2023-11-22 03:19:30 +00:00
UpdateUnowned ( deltaTime ) ;
2022-07-28 13:39:57 +00:00
return ;
2023-12-12 03:10:29 +00:00
} */
2023-12-13 06:14:53 +00:00
// Update timers
m_TimerBounce - = deltaTime ;
2022-07-28 13:39:57 +00:00
2023-12-11 01:55:36 +00:00
if ( m_Timer > 0 ) {
m_Timer - = deltaTime ;
return ;
}
2022-07-28 13:39:57 +00:00
2023-12-13 06:14:53 +00:00
// Remove "left behind" pets
if ( m_Owner ! = LWOOBJID_EMPTY ) {
Entity * owner = GetOwner ( ) ;
if ( ! owner ) {
m_Parent - > Kill ( ) ;
return ;
}
}
// Handle pet AI states
2023-12-11 01:55:36 +00:00
switch ( m_State ) {
2023-12-12 03:10:29 +00:00
case PetAiState : : spawn :
LOG_DEBUG ( " Pet spawn beginning! " ) ;
OnSpawn ( ) ;
break ;
case PetAiState : : idle :
Wander ( ) ;
break ;
case PetAiState : : follow :
OnFollow ( ) ;
break ;
case PetAiState : : goToObj :
if ( m_MovementAI - > AtFinalWaypoint ( ) ) {
LOG_DEBUG ( " Reached object! " ) ;
m_MovementAI - > Stop ( ) ;
SetPetAiState ( PetAiState : : interact ) ;
2023-12-11 01:55:36 +00:00
}
2023-12-12 03:10:29 +00:00
else {
m_Timer + = 0.5f ;
2023-12-11 01:55:36 +00:00
}
2023-12-12 03:10:29 +00:00
break ;
2023-12-11 01:55:36 +00:00
2023-12-12 03:10:29 +00:00
case PetAiState : : interact :
OnInteract ( ) ;
break ;
2023-12-11 01:55:36 +00:00
2023-12-12 03:10:29 +00:00
default :
break ;
2023-12-11 01:55:36 +00:00
}
/*
2021-12-05 17:54:36 +00:00
auto destination = owner - > GetPosition ( ) ;
2023-08-04 02:38:04 +00:00
NiPoint3 position = m_MovementAI - > GetParent ( ) - > GetPosition ( ) ;
2022-07-28 13:39:57 +00:00
2021-12-05 17:54:36 +00:00
float distanceToOwner = Vector3 : : DistanceSquared ( position , destination ) ;
if ( distanceToOwner > 50 * 50 | | m_TimerAway > 5 ) {
m_MovementAI - > Warp ( destination ) ;
m_Timer = 1 ;
m_TimerAway = 0 ;
return ;
}
if ( distanceToOwner > 15 * 15 | | std : : abs ( destination . y - position . y ) > = 3 ) {
m_TimerAway + = deltaTime ;
2022-07-28 13:39:57 +00:00
} else {
2021-12-05 17:54:36 +00:00
m_TimerAway = 0 ;
2022-07-28 13:39:57 +00:00
}
2022-07-25 02:26:51 +00:00
2021-12-05 17:54:36 +00:00
if ( m_Timer > 0 ) {
m_Timer - = deltaTime ;
return ;
2022-07-28 13:39:57 +00:00
}
2021-12-05 17:54:36 +00:00
SwitchComponent * closestSwitch = SwitchComponent : : GetClosestSwitch ( position ) ;
float haltDistance = 5 ;
2023-12-11 01:55:36 +00:00
if ( closestSwitch ! = nullptr & & ! closestSwitch - > GetActive ( ) ) {
NiPoint3 switchPosition = closestSwitch - > GetParentEntity ( ) - > GetPosition ( ) ;
float distance = Vector3 : : DistanceSquared ( position , switchPosition ) ;
if ( distance < 3 * 3 ) {
m_Interaction = closestSwitch - > GetParentEntity ( ) - > GetObjectID ( ) ;
closestSwitch - > EntityEnter ( m_Parent ) ;
} else if ( distance < 20 * 20 ) {
haltDistance = 1 ;
destination = switchPosition ;
2022-07-28 13:39:57 +00:00
}
}
2023-12-13 06:57:01 +00:00
*/
2021-12-05 17:54:36 +00:00
}
2023-12-13 06:14:53 +00:00
void PetComponent : : UpdateUnowned ( float deltaTime ) { //TODO: CURRENTLY UNUSED
2023-11-22 03:19:30 +00:00
if ( m_Tamer ! = LWOOBJID_EMPTY ) {
if ( m_Timer > 0 ) {
m_Timer - = deltaTime ;
if ( m_Timer < = 0 ) {
m_Timer = 0 ;
ClientFailTamingMinigame ( ) ;
}
}
}
else {
if ( m_Timer > 0 ) {
m_Timer - = deltaTime ;
if ( m_Timer < = 0 ) Wander ( ) ;
} else {
m_Timer = 5 ;
}
}
}
2022-04-07 05:21:54 +00:00
void PetComponent : : TryBuild ( uint32_t numBricks , bool clientFailed ) {
2021-12-05 17:54:36 +00:00
if ( m_Tamer = = LWOOBJID_EMPTY ) return ;
2023-07-15 20:56:33 +00:00
auto * tamer = Game : : entityManager - > GetEntity ( m_Tamer ) ;
2021-12-05 17:54:36 +00:00
2022-04-07 05:21:54 +00:00
if ( tamer = = nullptr ) {
2021-12-05 17:54:36 +00:00
m_Tamer = LWOOBJID_EMPTY ;
return ;
}
const auto & cached = buildCache . find ( m_Parent - > GetLOT ( ) ) ;
2022-04-07 05:21:54 +00:00
if ( cached = = buildCache . end ( ) ) return ;
2021-12-05 17:54:36 +00:00
auto * destroyableComponent = tamer - > GetComponent < DestroyableComponent > ( ) ;
2022-04-07 05:21:54 +00:00
if ( destroyableComponent = = nullptr ) return ;
2021-12-05 17:54:36 +00:00
auto imagination = destroyableComponent - > GetImagination ( ) ;
imagination - = cached - > second . imaginationCost ;
destroyableComponent - > SetImagination ( imagination ) ;
2023-07-15 20:56:33 +00:00
Game : : entityManager - > SerializeEntity ( tamer ) ;
2021-12-05 17:54:36 +00:00
2022-04-07 05:21:54 +00:00
if ( clientFailed ) {
if ( imagination < cached - > second . imaginationCost ) {
2021-12-05 17:54:36 +00:00
ClientFailTamingMinigame ( ) ;
}
2022-04-07 05:21:54 +00:00
} else {
2021-12-05 17:54:36 +00:00
m_Timer = 0 ;
}
2022-04-07 05:21:54 +00:00
if ( numBricks = = 0 ) return ;
GameMessages : : SendPetTamingTryBuildResult ( m_Tamer , ! clientFailed , numBricks , tamer - > GetSystemAddress ( ) ) ;
2021-12-05 17:54:36 +00:00
}
2022-07-25 02:26:51 +00:00
void PetComponent : : NotifyTamingBuildSuccess ( NiPoint3 position ) {
2021-12-05 17:54:36 +00:00
if ( m_Tamer = = LWOOBJID_EMPTY ) return ;
2023-07-15 20:56:33 +00:00
auto * tamer = Game : : entityManager - > GetEntity ( m_Tamer ) ;
2021-12-05 17:54:36 +00:00
if ( tamer = = nullptr ) {
m_Tamer = LWOOBJID_EMPTY ;
return ;
}
const auto & cached = buildCache . find ( m_Parent - > GetLOT ( ) ) ;
if ( cached = = buildCache . end ( ) ) {
return ;
}
GameMessages : : SendPlayFXEffect ( tamer , - 1 , u " petceleb " , " " , LWOOBJID_EMPTY , 1 , 1 , true ) ;
2023-03-20 13:10:52 +00:00
RenderComponent : : PlayAnimation ( tamer , u " rebuild-celebrate " ) ;
2021-12-05 17:54:36 +00:00
EntityInfo info { } ;
info . lot = cached - > second . puzzleModelLot ;
info . pos = position ;
info . rot = NiQuaternion : : IDENTITY ;
info . spawnerID = tamer - > GetObjectID ( ) ;
2023-07-15 20:56:33 +00:00
auto * modelEntity = Game : : entityManager - > CreateEntity ( info ) ;
2022-07-25 02:26:51 +00:00
2021-12-05 17:54:36 +00:00
m_ModelId = modelEntity - > GetObjectID ( ) ;
2023-07-15 20:56:33 +00:00
Game : : entityManager - > ConstructEntity ( modelEntity ) ;
2021-12-05 17:54:36 +00:00
GameMessages : : SendNotifyTamingModelLoadedOnServer ( m_Tamer , tamer - > GetSystemAddress ( ) ) ;
GameMessages : : SendPetResponse ( m_Tamer , m_Parent - > GetObjectID ( ) , 0 , 10 , 0 , tamer - > GetSystemAddress ( ) ) ;
auto * inventoryComponent = tamer - > GetComponent < InventoryComponent > ( ) ;
2023-12-14 07:33:49 +00:00
if ( ! inventoryComponent ) return ;
2021-12-05 17:54:36 +00:00
LWOOBJID petSubKey = ObjectIDManager : : Instance ( ) - > GenerateRandomObjectID ( ) ;
2023-04-25 18:17:40 +00:00
GeneralUtils : : SetBit ( petSubKey , eObjectBits : : CHARACTER ) ;
GeneralUtils : : SetBit ( petSubKey , eObjectBits : : PERSISTENT ) ;
2021-12-05 17:54:36 +00:00
m_DatabaseId = petSubKey ;
2022-07-25 02:26:51 +00:00
2021-12-05 17:54:36 +00:00
std : : string petName = tamer - > GetCharacter ( ) - > GetName ( ) ;
petName + = " 's Pet " ;
2022-08-02 13:56:20 +00:00
GameMessages : : SendAddPetToPlayer ( m_Tamer , 0 , GeneralUtils : : UTF8ToUTF16 ( petName ) , petSubKey , m_Parent - > GetLOT ( ) , tamer - > GetSystemAddress ( ) ) ;
2022-07-25 02:26:51 +00:00
2021-12-05 17:54:36 +00:00
GameMessages : : SendRegisterPetID ( m_Tamer , m_Parent - > GetObjectID ( ) , tamer - > GetSystemAddress ( ) ) ;
GameMessages : : SendRegisterPetDBID ( m_Tamer , petSubKey , tamer - > GetSystemAddress ( ) ) ;
2022-07-25 02:26:51 +00:00
2023-05-02 22:39:21 +00:00
inventoryComponent - > AddItem ( m_Parent - > GetLOT ( ) , 1 , eLootSourceType : : ACTIVITY , eInventoryType : : MODELS , { } , LWOOBJID_EMPTY , true , false , petSubKey ) ;
2023-12-14 07:33:49 +00:00
2021-12-05 17:54:36 +00:00
auto * item = inventoryComponent - > FindItemBySubKey ( petSubKey , MODELS ) ;
2023-12-14 07:33:49 +00:00
if ( ! item ) return ;
2021-12-05 17:54:36 +00:00
DatabasePet databasePet { } ;
2022-07-25 02:26:51 +00:00
2021-12-05 17:54:36 +00:00
databasePet . lot = m_Parent - > GetLOT ( ) ;
databasePet . moderationState = 1 ;
databasePet . name = petName ;
inventoryComponent - > SetDatabasePet ( petSubKey , databasePet ) ;
2022-06-18 07:14:24 +00:00
Activate ( item , false , true ) ;
2021-12-05 17:54:36 +00:00
m_Timer = 0 ;
GameMessages : : SendNotifyPetTamingMinigame (
m_Tamer ,
LWOOBJID_EMPTY ,
LWOOBJID_EMPTY ,
false ,
2023-05-02 22:39:21 +00:00
ePetTamingNotifyType : : NAMINGPET ,
2021-12-05 17:54:36 +00:00
NiPoint3 : : ZERO ,
NiPoint3 : : ZERO ,
NiQuaternion : : IDENTITY ,
UNASSIGNED_SYSTEM_ADDRESS
) ;
// Triggers the catch a pet missions
if ( petFlags . find ( m_Parent - > GetLOT ( ) ) ! = petFlags . end ( ) ) {
tamer - > GetCharacter ( ) - > SetPlayerFlag ( petFlags . at ( m_Parent - > GetLOT ( ) ) , true ) ;
}
auto * missionComponent = tamer - > GetComponent < MissionComponent > ( ) ;
if ( missionComponent ! = nullptr ) {
missionComponent - > Progress ( eMissionTaskType : : PET_TAMING , m_Parent - > GetLOT ( ) ) ;
}
SetStatus ( 1 ) ;
auto * characterComponent = tamer - > GetComponent < CharacterComponent > ( ) ;
if ( characterComponent ! = nullptr ) {
characterComponent - > UpdatePlayerStatistic ( PetsTamed ) ;
}
}
2022-07-25 02:26:51 +00:00
void PetComponent : : RequestSetPetName ( std : : u16string name ) {
2021-12-05 17:54:36 +00:00
if ( m_Tamer = = LWOOBJID_EMPTY ) {
if ( m_Owner ! = LWOOBJID_EMPTY ) {
auto * owner = GetOwner ( ) ;
m_ModerationStatus = 1 ; // Pending
m_Name = " " ;
//Save our pet's new name to the db:
SetPetNameForModeration ( GeneralUtils : : UTF16ToWTF8 ( name ) ) ;
2022-08-02 13:56:20 +00:00
GameMessages : : SendSetPetName ( m_Owner , GeneralUtils : : UTF8ToUTF16 ( m_Name ) , m_DatabaseId , owner - > GetSystemAddress ( ) ) ;
2021-12-05 17:54:36 +00:00
GameMessages : : SendSetPetNameModerated ( m_Owner , m_DatabaseId , m_ModerationStatus , owner - > GetSystemAddress ( ) ) ;
}
return ;
}
2023-07-15 20:56:33 +00:00
auto * tamer = Game : : entityManager - > GetEntity ( m_Tamer ) ;
2021-12-05 17:54:36 +00:00
if ( tamer = = nullptr ) {
m_Tamer = LWOOBJID_EMPTY ;
return ;
}
2023-10-21 23:31:55 +00:00
LOG ( " Got set pet name (%s) " , GeneralUtils : : UTF16ToWTF8 ( name ) . c_str ( ) ) ;
2021-12-05 17:54:36 +00:00
auto * inventoryComponent = tamer - > GetComponent < InventoryComponent > ( ) ;
2023-12-14 07:33:49 +00:00
if ( ! inventoryComponent ) return ;
2021-12-05 17:54:36 +00:00
m_ModerationStatus = 1 ; // Pending
m_Name = " " ;
//Save our pet's new name to the db:
SetPetNameForModeration ( GeneralUtils : : UTF16ToWTF8 ( name ) ) ;
2023-07-15 20:56:33 +00:00
Game : : entityManager - > SerializeEntity ( m_Parent ) ;
2021-12-05 17:54:36 +00:00
2022-08-02 13:56:20 +00:00
std : : u16string u16name = GeneralUtils : : UTF8ToUTF16 ( m_Name ) ;
std : : u16string u16ownerName = GeneralUtils : : UTF8ToUTF16 ( m_OwnerName ) ;
GameMessages : : SendSetPetName ( m_Tamer , u16name , m_DatabaseId , tamer - > GetSystemAddress ( ) ) ;
GameMessages : : SendSetPetName ( m_Tamer , u16name , LWOOBJID_EMPTY , tamer - > GetSystemAddress ( ) ) ;
GameMessages : : SendPetNameChanged ( m_Parent - > GetObjectID ( ) , m_ModerationStatus , u16name , u16ownerName , UNASSIGNED_SYSTEM_ADDRESS ) ;
2021-12-05 17:54:36 +00:00
GameMessages : : SendSetPetNameModerated ( m_Tamer , m_DatabaseId , m_ModerationStatus , tamer - > GetSystemAddress ( ) ) ;
GameMessages : : SendNotifyPetTamingMinigame (
m_Tamer ,
m_Parent - > GetObjectID ( ) ,
m_Tamer ,
false ,
2023-05-02 22:39:21 +00:00
ePetTamingNotifyType : : SUCCESS ,
2021-12-05 17:54:36 +00:00
NiPoint3 : : ZERO ,
NiPoint3 : : ZERO ,
NiQuaternion : : IDENTITY ,
UNASSIGNED_SYSTEM_ADDRESS
) ;
GameMessages : : SendTerminateInteraction ( m_Tamer , eTerminateType : : FROM_INTERACTION , m_Parent - > GetObjectID ( ) ) ;
2023-07-15 20:56:33 +00:00
auto * modelEntity = Game : : entityManager - > GetEntity ( m_ModelId ) ;
2021-12-05 17:54:36 +00:00
if ( modelEntity ! = nullptr ) {
modelEntity - > Smash ( m_Tamer ) ;
}
currentActivities . erase ( m_Tamer ) ;
m_Tamer = LWOOBJID_EMPTY ;
// Notify the end of a pet taming minigame
for ( CppScripts : : Script * script : CppScripts : : GetEntityScripts ( m_Parent ) ) {
2023-05-02 22:39:21 +00:00
script - > OnNotifyPetTamingMinigame ( m_Parent , tamer , ePetTamingNotifyType : : SUCCESS ) ;
2021-12-05 17:54:36 +00:00
}
}
2022-07-25 02:26:51 +00:00
void PetComponent : : ClientExitTamingMinigame ( bool voluntaryExit ) {
2021-12-05 17:54:36 +00:00
if ( m_Tamer = = LWOOBJID_EMPTY ) return ;
2023-07-15 20:56:33 +00:00
auto * tamer = Game : : entityManager - > GetEntity ( m_Tamer ) ;
2021-12-05 17:54:36 +00:00
if ( tamer = = nullptr ) {
m_Tamer = LWOOBJID_EMPTY ;
return ;
}
GameMessages : : SendNotifyPetTamingMinigame (
m_Tamer ,
m_Parent - > GetObjectID ( ) ,
m_Tamer ,
false ,
2023-05-02 22:39:21 +00:00
ePetTamingNotifyType : : QUIT ,
2021-12-05 17:54:36 +00:00
NiPoint3 : : ZERO ,
NiPoint3 : : ZERO ,
NiQuaternion : : IDENTITY ,
UNASSIGNED_SYSTEM_ADDRESS
) ;
GameMessages : : SendNotifyTamingModelLoadedOnServer ( m_Tamer , tamer - > GetSystemAddress ( ) ) ;
GameMessages : : SendTerminateInteraction ( m_Tamer , eTerminateType : : FROM_INTERACTION , m_Parent - > GetObjectID ( ) ) ;
currentActivities . erase ( m_Tamer ) ;
2023-11-19 22:46:27 +00:00
SetStatus ( PetStatus : : TAMEABLE ) ;
2021-12-05 17:54:36 +00:00
m_Tamer = LWOOBJID_EMPTY ;
m_Timer = 0 ;
2022-07-25 02:26:51 +00:00
2023-07-15 20:56:33 +00:00
Game : : entityManager - > SerializeEntity ( m_Parent ) ;
2021-12-05 17:54:36 +00:00
// Notify the end of a pet taming minigame
for ( CppScripts : : Script * script : CppScripts : : GetEntityScripts ( m_Parent ) ) {
2023-05-02 22:39:21 +00:00
script - > OnNotifyPetTamingMinigame ( m_Parent , tamer , ePetTamingNotifyType : : QUIT ) ;
2021-12-05 17:54:36 +00:00
}
}
2022-07-25 02:26:51 +00:00
void PetComponent : : StartTimer ( ) {
2021-12-05 17:54:36 +00:00
const auto & cached = buildCache . find ( m_Parent - > GetLOT ( ) ) ;
if ( cached = = buildCache . end ( ) ) {
return ;
}
m_Timer = cached - > second . timeLimit ;
}
2022-07-25 02:26:51 +00:00
void PetComponent : : ClientFailTamingMinigame ( ) {
2021-12-05 17:54:36 +00:00
if ( m_Tamer = = LWOOBJID_EMPTY ) return ;
2023-07-15 20:56:33 +00:00
auto * tamer = Game : : entityManager - > GetEntity ( m_Tamer ) ;
2021-12-05 17:54:36 +00:00
if ( tamer = = nullptr ) {
m_Tamer = LWOOBJID_EMPTY ;
return ;
}
GameMessages : : SendNotifyPetTamingMinigame (
m_Tamer ,
m_Parent - > GetObjectID ( ) ,
m_Tamer ,
false ,
2023-05-02 22:39:21 +00:00
ePetTamingNotifyType : : FAILED ,
2021-12-05 17:54:36 +00:00
NiPoint3 : : ZERO ,
NiPoint3 : : ZERO ,
NiQuaternion : : IDENTITY ,
UNASSIGNED_SYSTEM_ADDRESS
) ;
GameMessages : : SendNotifyTamingModelLoadedOnServer ( m_Tamer , tamer - > GetSystemAddress ( ) ) ;
GameMessages : : SendTerminateInteraction ( m_Tamer , eTerminateType : : FROM_INTERACTION , m_Parent - > GetObjectID ( ) ) ;
currentActivities . erase ( m_Tamer ) ;
2023-11-19 22:46:27 +00:00
SetStatus ( PetStatus : : TAMEABLE ) ;
2021-12-05 17:54:36 +00:00
m_Tamer = LWOOBJID_EMPTY ;
m_Timer = 0 ;
2022-07-25 02:26:51 +00:00
2023-07-15 20:56:33 +00:00
Game : : entityManager - > SerializeEntity ( m_Parent ) ;
2021-12-05 17:54:36 +00:00
// Notify the end of a pet taming minigame
for ( CppScripts : : Script * script : CppScripts : : GetEntityScripts ( m_Parent ) ) {
2023-05-02 22:39:21 +00:00
script - > OnNotifyPetTamingMinigame ( m_Parent , tamer , ePetTamingNotifyType : : FAILED ) ;
2021-12-05 17:54:36 +00:00
}
}
2022-07-25 02:26:51 +00:00
void PetComponent : : Wander ( ) {
2023-12-14 07:33:49 +00:00
if ( ! m_MovementAI - > AtFinalWaypoint ( ) ) return ;
2021-12-05 17:54:36 +00:00
m_MovementAI - > SetHaltDistance ( 0 ) ;
2022-07-25 02:26:51 +00:00
2021-12-05 17:54:36 +00:00
const auto & info = m_MovementAI - > GetInfo ( ) ;
const auto div = static_cast < int > ( info . wanderDelayMax ) ;
m_Timer = ( div = = 0 ? 0 : GeneralUtils : : GenerateRandomNumber < int > ( 0 , div ) ) + info . wanderDelayMin ; //set a random timer to stay put.
2022-07-25 02:26:51 +00:00
2021-12-05 17:54:36 +00:00
const float radius = info . wanderRadius * sqrt ( static_cast < double > ( GeneralUtils : : GenerateRandomNumber < float > ( 0 , 1 ) ) ) ; //our wander radius + a bit of random range
const float theta = ( ( static_cast < double > ( GeneralUtils : : GenerateRandomNumber < float > ( 0 , 1 ) ) * 2 * PI ) ) ;
const NiPoint3 delta =
{
radius * cos ( theta ) ,
0 ,
radius * sin ( theta )
} ;
auto destination = m_StartPosition + delta ;
if ( dpWorld : : Instance ( ) . IsLoaded ( ) ) {
2022-08-02 05:30:19 +00:00
destination . y = dpWorld : : Instance ( ) . GetNavMesh ( ) - > GetHeightAtPoint ( destination ) ;
2021-12-05 17:54:36 +00:00
}
2023-08-04 02:38:04 +00:00
if ( Vector3 : : DistanceSquared ( destination , m_MovementAI - > GetParent ( ) - > GetPosition ( ) ) < 2 * 2 ) {
2021-12-05 17:54:36 +00:00
m_MovementAI - > Stop ( ) ;
return ;
}
2023-12-13 06:14:53 +00:00
m_MovementAI - > SetMaxSpeed ( m_SprintSpeed ) ; //info.wanderSpeed);
2021-12-05 17:54:36 +00:00
m_MovementAI - > SetDestination ( destination ) ;
2023-12-13 06:14:53 +00:00
m_Timer + = ( m_MovementAI - > GetParent ( ) - > GetPosition ( ) . x - destination . x ) / m_SprintSpeed ; //info.wanderSpeed;
2021-12-05 17:54:36 +00:00
}
2023-12-12 03:10:29 +00:00
void PetComponent : : OnSpawn ( ) {
m_MovementAI = m_Parent - > GetComponent < MovementAIComponent > ( ) ;
2023-12-13 06:14:53 +00:00
//if (!m_MovementAI) return;
if ( m_StartPosition = = NiPoint3 : : ZERO ) {
m_StartPosition = m_Parent - > GetPosition ( ) ;
}
2023-12-12 03:10:29 +00:00
LOG_DEBUG ( " Pet spawn complete, setting AI state. " ) ;
2023-12-13 06:14:53 +00:00
if ( m_Owner ! = LWOOBJID_EMPTY ) {
m_Parent - > SetOwnerOverride ( m_Owner ) ;
m_MovementAI - > SetMaxSpeed ( m_SprintSpeed ) ;
m_MovementAI - > SetHaltDistance ( m_FollowRadius ) ;
SetStatus ( PetStatus : : NONE ) ;
SetPetAiState ( PetAiState : : follow ) ;
}
else {
SetPetAiState ( PetAiState : : idle ) ;
}
2023-12-12 03:10:29 +00:00
}
void PetComponent : : OnFollow ( ) {
Entity * owner = GetOwner ( ) ;
2023-12-13 06:14:53 +00:00
if ( ! owner ) return ;
const NiPoint3 ownerPos = owner - > GetPosition ( ) ;
2023-12-12 03:10:29 +00:00
2023-12-13 06:14:53 +00:00
// Find interactions
2023-12-12 03:10:29 +00:00
SwitchComponent * closestSwitch = SwitchComponent : : GetClosestSwitch ( ownerPos ) ;
if ( closestSwitch ! = nullptr & & ! closestSwitch - > GetActive ( ) ) {
2023-12-13 06:14:53 +00:00
const NiPoint3 switchPos = closestSwitch - > GetParentEntity ( ) - > GetPosition ( ) ;
const LWOOBJID switchID = closestSwitch - > GetParentEntity ( ) - > GetObjectID ( ) ;
2023-12-12 03:10:29 +00:00
const float distance = Vector3 : : DistanceSquared ( ownerPos , switchPos ) ;
2023-12-13 06:14:53 +00:00
if ( distance < 16 * 16 ) {
StartInteract ( switchPos , PetInteractType : : bouncer , switchID ) ;
2023-12-12 03:10:29 +00:00
return ;
}
}
// Determine if the "Lost Tags" mission has been completed and digging has been unlocked
auto * missionComponent = owner - > GetComponent < MissionComponent > ( ) ;
if ( ! missionComponent ) return ;
const bool digUnlocked = missionComponent - > GetMissionState ( 842 ) = = eMissionState : : COMPLETE ;
Entity * closestTreasure = PetDigServer : : GetClosestTresure ( ownerPos ) ;
2023-12-13 06:56:41 +00:00
const bool nonDragonForBone = closestTreasure - > GetLOT ( ) = = 12192 & & m_Parent - > GetLOT ( ) ! = 13067 ;
2023-12-13 07:01:34 +00:00
if ( ! nonDragonForBone & & closestTreasure ! = nullptr & & digUnlocked ) {
2023-12-13 06:14:53 +00:00
const NiPoint3 treasurePos = closestTreasure - > GetPosition ( ) ;
const LWOOBJID treasureID = closestTreasure - > GetObjectID ( ) ;
2023-12-12 03:10:29 +00:00
const float distance = Vector3 : : DistanceSquared ( ownerPos , treasurePos ) ;
2023-12-13 06:14:53 +00:00
if ( distance < 16 * 16 ) {
StartInteract ( treasurePos , PetInteractType : : treasure , treasureID ) ;
2023-12-12 03:10:29 +00:00
return ;
}
}
2023-12-13 06:14:53 +00:00
// Handle actual following logic
const NiPoint3 currentPos = m_MovementAI - > GetParent ( ) - > GetPosition ( ) ;
// If the player's position is within range, stop moving
if ( Vector3 : : DistanceSquared ( currentPos , ownerPos ) < = m_FollowRadius * m_FollowRadius ) {
m_MovementAI - > Stop ( ) ;
}
else { // Chase the player's new position
m_MovementAI - > SetDestination ( ownerPos ) ;
LOG_DEBUG ( " New pet destination: %f %f %f " , ownerPos . x , ownerPos . y , ownerPos . z ) ;
}
2023-12-12 03:10:29 +00:00
m_Timer + = 0.5f ;
}
void PetComponent : : OnInteract ( ) {
2023-12-13 06:14:53 +00:00
Entity * owner = GetOwner ( ) ;
if ( ! owner ) return ;
2023-12-12 03:10:29 +00:00
2023-12-13 06:14:53 +00:00
const NiPoint3 ownerPos = owner - > GetPosition ( ) ;
const NiPoint3 currentPos = m_MovementAI - > GetParent ( ) - > GetPosition ( ) ;
2023-12-12 03:10:29 +00:00
const float distanceFromOwner = Vector3 : : DistanceSquared ( ownerPos , currentPos ) ;
if ( distanceFromOwner > 25 * 25 ) {
LOG_DEBUG ( " Disengaging from object interaction due to player distance. " ) ;
StopInteract ( ) ;
return ;
}
switch ( GetInteractType ( ) ) {
case PetInteractType : : bouncer :
2023-12-13 07:06:58 +00:00
if ( IsReadyToInteract ( ) ) LOG_DEBUG ( " Add the HandleInteractBouncer()! " ) ;
2023-12-13 06:14:53 +00:00
else SetupInteractBouncer ( ) ;
2023-12-12 03:10:29 +00:00
break ;
case PetInteractType : : treasure :
2023-12-13 07:06:58 +00:00
if ( IsReadyToInteract ( ) ) HandleInteractTreasureDig ( ) ;
2023-12-13 06:14:53 +00:00
else SetupInteractTreasureDig ( ) ;
2023-12-12 03:10:29 +00:00
break ;
default :
LOG_DEBUG ( " INTERACT = NONE! RETURNING! " ) ;
StopInteract ( ) ;
m_Timer + = 0.5f ;
break ;
}
}
2023-12-13 06:14:53 +00:00
void PetComponent : : StartInteract ( const NiPoint3 position , const PetInteractType interactType , const LWOOBJID interactID ) {
SetInteraction ( interactID ) ; // TODO: Check if this should be serialized for goToObj
2023-12-12 03:10:29 +00:00
SetInteractType ( interactType ) ;
2023-12-14 07:33:49 +00:00
SetAbility ( ePetAbilityType : : GoToObject ) ;
2023-12-12 03:10:29 +00:00
SetPetAiState ( PetAiState : : goToObj ) ;
m_MovementAI - > SetMaxSpeed ( m_RunSpeed ) ;
m_MovementAI - > SetHaltDistance ( 0.0f ) ;
m_MovementAI - > SetDestination ( position ) ;
LOG_DEBUG ( " Starting interaction! " ) ;
Game : : entityManager - > SerializeEntity ( m_Parent ) ;
}
void PetComponent : : StopInteract ( ) {
2023-12-14 07:33:49 +00:00
Entity * owner = GetOwner ( ) ;
if ( ! owner ) return ;
const auto petAbility = ePetAbilityType : : Invalid ;
2023-12-13 06:14:53 +00:00
SetInteraction ( LWOOBJID_EMPTY ) ;
2023-12-12 03:10:29 +00:00
SetInteractType ( PetInteractType : : none ) ;
2023-12-14 07:33:49 +00:00
SetAbility ( petAbility ) ;
2023-12-12 03:10:29 +00:00
SetPetAiState ( PetAiState : : follow ) ;
2023-12-13 06:14:53 +00:00
SetStatus ( PetStatus : : NONE ) ;
SetIsReadyToInteract ( false ) ;
2023-12-14 07:33:49 +00:00
SetIsHandlingInteraction ( false ) ; // Needed?
2023-12-12 03:10:29 +00:00
m_MovementAI - > SetMaxSpeed ( m_SprintSpeed ) ;
m_MovementAI - > SetHaltDistance ( m_FollowRadius ) ;
LOG_DEBUG ( " Stopping interaction! " ) ;
2023-12-14 07:33:49 +00:00
2023-12-12 03:10:29 +00:00
Game : : entityManager - > SerializeEntity ( m_Parent ) ;
2023-12-14 07:33:49 +00:00
GameMessages : : SendShowPetActionButton ( m_Owner , petAbility , false , owner - > GetSystemAddress ( ) ) ; // Needed?
2023-12-12 03:10:29 +00:00
}
2023-12-13 06:14:53 +00:00
void PetComponent : : SetupInteractBouncer ( ) {
// THIS IS ALL BAD, BAD, BAD! FIX IT, ME! >:(
2023-12-14 07:33:49 +00:00
/*SetAbility(ePetAbilityType::JumpOnObject);
2023-12-12 03:10:29 +00:00
NiPoint3 destination = m_MovementAI - > GetDestination ( ) ;
SwitchComponent * closestSwitch = SwitchComponent : : GetClosestSwitch ( destination ) ;
m_Interaction = closestSwitch - > GetParentEntity ( ) - > GetObjectID ( ) ;
Game : : entityManager - > SerializeEntity ( m_Parent ) ;
2023-12-13 06:14:53 +00:00
closestSwitch - > EntityEnter ( m_Parent ) ; */
2023-12-12 03:10:29 +00:00
}
2023-12-13 06:14:53 +00:00
void PetComponent : : SetupInteractTreasureDig ( ) {
2023-12-14 07:33:49 +00:00
auto * owner = GetOwner ( ) ;
if ( ! owner ) return ;
2023-12-13 06:14:53 +00:00
2023-12-14 07:33:49 +00:00
LOG_DEBUG ( " Setting up dig interaction! " ) ;
2023-12-13 06:14:53 +00:00
SetIsReadyToInteract ( true ) ;
2023-12-14 07:33:49 +00:00
auto petAbility = ePetAbilityType : : DigAtPosition ;
2023-12-13 06:14:53 +00:00
2023-12-14 07:33:49 +00:00
SetAbility ( petAbility ) ;
2023-12-13 06:14:53 +00:00
SetStatus ( PetStatus : : IS_NOT_WAITING ) ; // TODO: Double-check this is the right flag being set
Game : : entityManager - > SerializeEntity ( m_Parent ) ; // TODO: Double-check pet packet captures
2023-12-14 07:33:49 +00:00
const auto sysAddr = owner - > GetSystemAddress ( ) ;
GameMessages : : SendHelp ( m_Owner , eHelpType : : PR_DIG_TUTORIAL_01 , sysAddr ) ;
GameMessages : : SendShowPetActionButton ( m_Owner , petAbility , true , sysAddr ) ;
2023-12-13 06:14:53 +00:00
m_Timer + = 0.5f ;
}
void PetComponent : : StartInteractTreasureDig ( ) {
2023-12-14 07:33:49 +00:00
Entity * user = GetOwner ( ) ;
if ( IsHandlingInteraction ( ) | | ! user ) return ;
auto * destroyableComponent = user - > GetComponent < DestroyableComponent > ( ) ;
if ( ! destroyableComponent ) return ;
auto imagination = destroyableComponent - > GetImagination ( ) ;
int32_t imaginationCost = 1 ; // TODO: Get rid of this magic number - make static variable from lookup
if ( imagination < imaginationCost ) {
//GameMessages::SendHelp(user->GetObjectID(), eHelpType::PR_NEED_IMAGINATION, user->GetSystemAddress()); // Check if right message!
return ;
}
GameMessages : : SendShowPetActionButton ( m_Owner , ePetAbilityType : : Invalid , false , user - > GetSystemAddress ( ) ) ;
imagination - = imaginationCost ;
destroyableComponent - > SetImagination ( imagination ) ;
Game : : entityManager - > SerializeEntity ( user ) ;
SetIsHandlingInteraction ( true ) ;
auto newStatus = GeneralUtils : : ClearBit ( GetStatus ( ) , 6 ) ;
SetStatus ( newStatus ) ; // TODO: FIND THE CORRECT STATUS TO USE HERE
2023-12-13 06:14:53 +00:00
Game : : entityManager - > SerializeEntity ( m_Parent ) ;
Command ( NiPoint3 : : ZERO , LWOOBJID_EMPTY , 1 , PetEmote : : DigTreasure , true ) ; // Plays 'dig' animation
2023-12-14 07:33:49 +00:00
m_Timer = 2.0f ;
2023-12-13 06:14:53 +00:00
}
void PetComponent : : HandleInteractTreasureDig ( ) {
2023-12-14 07:33:49 +00:00
if ( IsHandlingInteraction ( ) ) {
2023-12-13 06:14:53 +00:00
auto * owner = GetOwner ( ) ;
auto * treasure = Game : : entityManager - > GetEntity ( GetInteraction ( ) ) ;
2023-12-14 07:33:49 +00:00
if ( ! treasure | | ! owner ) return ;
2023-12-13 06:14:53 +00:00
treasure - > Smash ( m_Parent - > GetObjectID ( ) ) ;
LOG_DEBUG ( " Pet dig completed! " ) ;
2023-12-14 07:33:49 +00:00
GameMessages : : SendHelp ( m_Owner , eHelpType : : PR_DIG_TUTORIAL_03 , owner - > GetSystemAddress ( ) ) ;
2023-12-13 06:14:53 +00:00
StopInteract ( ) ; //TODO: This may not be totally consistent with live behavior, where the pet seems to stay near the dig and not immediately follow
2023-12-14 07:33:49 +00:00
//m_Timer = 1.5f;
2023-12-13 06:14:53 +00:00
return ;
}
if ( m_TimerBounce < = 0.0f ) {
Command ( NiPoint3 : : ZERO , LWOOBJID_EMPTY , 1 , PetEmote : : Bounce , true ) ; // Plays 'bounce' animation
m_TimerBounce = 1.0f ;
}
m_Timer + = 0.5f ;
2023-12-12 03:10:29 +00:00
}
2023-12-14 07:33:49 +00:00
void PetComponent : : Activate ( Item * item , bool registerPet , bool fromTaming ) { // TODO: Offset spawn position so it's not on top of player char
2022-06-18 07:14:24 +00:00
AddDrainImaginationTimer ( item , fromTaming ) ;
2022-06-18 06:53:09 +00:00
2021-12-05 17:54:36 +00:00
m_ItemId = item - > GetId ( ) ;
m_DatabaseId = item - > GetSubKey ( ) ;
2022-07-25 02:26:51 +00:00
2021-12-05 17:54:36 +00:00
auto * inventoryComponent = item - > GetInventory ( ) - > GetComponent ( ) ;
if ( inventoryComponent = = nullptr ) return ;
inventoryComponent - > DespawnPet ( ) ;
m_Owner = inventoryComponent - > GetParent ( ) - > GetObjectID ( ) ;
auto * owner = GetOwner ( ) ;
if ( owner = = nullptr ) return ;
2023-12-13 06:14:53 +00:00
SetStatus ( PetStatus : : PLAY_SPAWN_ANIM ) ;
2021-12-05 17:54:36 +00:00
auto databaseData = inventoryComponent - > GetDatabasePet ( m_DatabaseId ) ;
m_ModerationStatus = databaseData . moderationState ;
bool updatedModerationStatus = false ;
//Load mod status from db:
if ( m_ModerationStatus ! = 2 ) {
LoadPetNameFromModeration ( ) ;
databaseData . name = m_Name ;
databaseData . moderationState = m_ModerationStatus ;
inventoryComponent - > SetDatabasePet ( m_DatabaseId , databaseData ) ;
updatedModerationStatus = true ;
} else {
m_Name = databaseData . name ;
}
m_OwnerName = owner - > GetCharacter ( ) - > GetName ( ) ;
if ( updatedModerationStatus ) {
2022-08-02 13:56:20 +00:00
GameMessages : : SendSetPetName ( m_Owner , GeneralUtils : : UTF8ToUTF16 ( m_Name ) , m_DatabaseId , owner - > GetSystemAddress ( ) ) ;
2021-12-05 17:54:36 +00:00
GameMessages : : SendSetPetNameModerated ( m_Owner , m_DatabaseId , m_ModerationStatus , owner - > GetSystemAddress ( ) ) ;
}
2022-09-02 18:49:19 +00:00
GameMessages : : SendMarkInventoryItemAsActive ( m_Owner , true , eUnequippableActiveType : : PET , m_ItemId , GetOwner ( ) - > GetSystemAddress ( ) ) ;
2021-12-05 17:54:36 +00:00
activePets [ m_Owner ] = m_Parent - > GetObjectID ( ) ;
2023-07-15 20:56:33 +00:00
Game : : entityManager - > SerializeEntity ( m_Parent ) ;
2021-12-05 17:54:36 +00:00
2023-05-02 22:39:21 +00:00
owner - > GetCharacter ( ) - > SetPlayerFlag ( ePlayerFlag : : FIRST_MANUAL_PET_HIBERNATE , true ) ;
2021-12-05 17:54:36 +00:00
if ( registerPet ) {
2022-08-02 13:56:20 +00:00
GameMessages : : SendAddPetToPlayer ( m_Owner , 0 , GeneralUtils : : UTF8ToUTF16 ( m_Name ) , m_DatabaseId , m_Parent - > GetLOT ( ) , owner - > GetSystemAddress ( ) ) ;
2021-12-05 17:54:36 +00:00
GameMessages : : SendRegisterPetID ( m_Owner , m_Parent - > GetObjectID ( ) , owner - > GetSystemAddress ( ) ) ;
GameMessages : : SendRegisterPetDBID ( m_Owner , m_DatabaseId , owner - > GetSystemAddress ( ) ) ;
}
}
2022-06-18 07:14:24 +00:00
void PetComponent : : AddDrainImaginationTimer ( Item * item , bool fromTaming ) {
2022-06-18 20:25:34 +00:00
if ( Game : : config - > GetValue ( " pets_take_imagination " ) ! = " 1 " ) return ;
2022-06-18 07:14:24 +00:00
2022-06-18 06:53:09 +00:00
auto playerInventory = item - > GetInventory ( ) ;
if ( ! playerInventory ) return ;
auto playerInventoryComponent = playerInventory - > GetComponent ( ) ;
if ( ! playerInventoryComponent ) return ;
auto playerEntity = playerInventoryComponent - > GetParent ( ) ;
if ( ! playerEntity ) return ;
auto playerDestroyableComponent = playerEntity - > GetComponent < DestroyableComponent > ( ) ;
if ( ! playerDestroyableComponent ) return ;
2022-06-18 07:14:24 +00:00
// Drain by 1 when you summon pet or when this method is called, but not when we have just tamed this pet.
if ( ! fromTaming ) playerDestroyableComponent - > Imagine ( - 1 ) ;
2022-06-18 06:53:09 +00:00
// Set this to a variable so when this is called back from the player the timer doesn't fire off.
m_Parent - > AddCallbackTimer ( imaginationDrainRate , [ playerDestroyableComponent , this , item ] ( ) {
if ( ! playerDestroyableComponent ) {
2023-10-21 23:31:55 +00:00
LOG ( " No petComponent and/or no playerDestroyableComponent " ) ;
2022-06-18 06:53:09 +00:00
return ;
}
2022-07-25 02:26:51 +00:00
2023-01-03 17:22:04 +00:00
// If we are out of imagination despawn the pet.
if ( playerDestroyableComponent - > GetImagination ( ) = = 0 ) {
this - > Deactivate ( ) ;
auto playerEntity = playerDestroyableComponent - > GetParent ( ) ;
if ( ! playerEntity ) return ;
2022-06-18 06:53:09 +00:00
2023-05-02 22:39:21 +00:00
GameMessages : : SendUseItemRequirementsResponse ( playerEntity - > GetObjectID ( ) , playerEntity - > GetSystemAddress ( ) , eUseItemResponse : : NoImaginationForPet ) ;
2023-01-03 17:22:04 +00:00
}
2022-06-18 06:53:09 +00:00
2023-01-03 17:22:04 +00:00
this - > AddDrainImaginationTimer ( item ) ;
2022-06-18 06:53:09 +00:00
} ) ;
}
2022-07-25 02:26:51 +00:00
void PetComponent : : Deactivate ( ) {
2021-12-05 17:54:36 +00:00
GameMessages : : SendPlayFXEffect ( m_Parent - > GetObjectID ( ) , - 1 , u " despawn " , " " , LWOOBJID_EMPTY , 1 , 1 , true ) ;
2022-09-02 18:49:19 +00:00
GameMessages : : SendMarkInventoryItemAsActive ( m_Owner , false , eUnequippableActiveType : : PET , m_ItemId , GetOwner ( ) - > GetSystemAddress ( ) ) ;
2021-12-05 17:54:36 +00:00
activePets . erase ( m_Owner ) ;
m_Parent - > Kill ( ) ;
auto * owner = GetOwner ( ) ;
if ( owner = = nullptr ) return ;
2022-07-25 02:26:51 +00:00
2021-12-05 17:54:36 +00:00
GameMessages : : SendAddPetToPlayer ( m_Owner , 0 , u " " , LWOOBJID_EMPTY , LOT_NULL , owner - > GetSystemAddress ( ) ) ;
GameMessages : : SendRegisterPetID ( m_Owner , LWOOBJID_EMPTY , owner - > GetSystemAddress ( ) ) ;
GameMessages : : SendRegisterPetDBID ( m_Owner , LWOOBJID_EMPTY , owner - > GetSystemAddress ( ) ) ;
2023-12-14 07:33:49 +00:00
GameMessages : : SendShowPetActionButton ( m_Owner , ePetAbilityType : : Invalid , false , owner - > GetSystemAddress ( ) ) ;
2021-12-05 17:54:36 +00:00
}
2022-07-25 02:26:51 +00:00
void PetComponent : : Release ( ) {
2021-12-05 17:54:36 +00:00
auto * inventoryComponent = GetOwner ( ) - > GetComponent < InventoryComponent > ( ) ;
if ( inventoryComponent = = nullptr ) {
return ;
}
2022-07-25 02:26:51 +00:00
2021-12-05 17:54:36 +00:00
Deactivate ( ) ;
inventoryComponent - > RemoveDatabasePet ( m_DatabaseId ) ;
auto * item = inventoryComponent - > FindItemBySubKey ( m_DatabaseId ) ;
item - > SetCount ( 0 , false , false ) ;
}
2022-07-25 02:26:51 +00:00
void PetComponent : : Command ( NiPoint3 position , LWOOBJID source , int32_t commandType , int32_t typeId , bool overrideObey ) {
2021-12-05 17:54:36 +00:00
auto * owner = GetOwner ( ) ;
2023-11-19 22:46:27 +00:00
if ( ! owner ) return ;
2022-07-28 13:39:57 +00:00
2021-12-05 17:54:36 +00:00
if ( commandType = = 1 ) {
// Emotes
GameMessages : : SendPlayEmote ( m_Parent - > GetObjectID ( ) , typeId , owner - > GetObjectID ( ) , UNASSIGNED_SYSTEM_ADDRESS ) ;
} else if ( commandType = = 3 ) {
2023-12-13 06:56:41 +00:00
StopInteract ( ) ; // TODO: Verify this is necessary
2023-12-11 01:55:36 +00:00
SetPetAiState ( PetAiState : : follow ) ;
2021-12-05 17:54:36 +00:00
} else if ( commandType = = 6 ) {
// TODO: Go to player
}
2022-07-28 13:39:57 +00:00
2023-03-24 23:16:45 +00:00
if ( owner - > GetGMLevel ( ) > = eGameMasterLevel : : DEVELOPER ) {
2021-12-05 17:54:36 +00:00
ChatPackets : : SendSystemMessage ( owner - > GetSystemAddress ( ) , u " Commmand Type: " + ( GeneralUtils : : to_u16string ( commandType ) ) + u " - Type Id: " + ( GeneralUtils : : to_u16string ( typeId ) ) ) ;
}
2023-11-19 23:31:31 +00:00
// Add movement functionality
if ( position ! = NiPoint3 : : ZERO ) {
m_MovementAI - > SetDestination ( position ) ;
}
2021-12-05 17:54:36 +00:00
}
LWOOBJID PetComponent : : GetOwnerId ( ) const {
return m_Owner ;
}
Entity * PetComponent : : GetOwner ( ) const {
2023-07-15 20:56:33 +00:00
return Game : : entityManager - > GetEntity ( m_Owner ) ;
2021-12-05 17:54:36 +00:00
}
LWOOBJID PetComponent : : GetDatabaseId ( ) const {
return m_DatabaseId ;
}
LWOOBJID PetComponent : : GetInteraction ( ) const {
return m_Interaction ;
}
LWOOBJID PetComponent : : GetItemId ( ) const {
return m_ItemId ;
}
uint32_t PetComponent : : GetStatus ( ) const {
return m_Status ;
}
2023-12-14 07:33:49 +00:00
ePetAbilityType PetComponent : : GetAbility ( ) const {
2021-12-05 17:54:36 +00:00
return m_Ability ;
}
void PetComponent : : SetInteraction ( LWOOBJID value ) {
m_Interaction = value ;
}
void PetComponent : : SetStatus ( uint32_t value ) {
m_Status = value ;
2023-12-14 07:33:49 +00:00
LOG_DEBUG ( " Pet status set to: %x " , m_Status ) ;
2021-12-05 17:54:36 +00:00
}
2023-12-14 07:33:49 +00:00
void PetComponent : : SetAbility ( ePetAbilityType value ) {
2021-12-05 17:54:36 +00:00
m_Ability = value ;
}
2022-07-25 02:26:51 +00:00
PetComponent * PetComponent : : GetTamingPet ( LWOOBJID tamer ) {
2021-12-05 17:54:36 +00:00
const auto & pair = currentActivities . find ( tamer ) ;
if ( pair = = currentActivities . end ( ) ) {
return nullptr ;
}
2023-07-15 20:56:33 +00:00
auto * entity = Game : : entityManager - > GetEntity ( pair - > second ) ;
2021-12-05 17:54:36 +00:00
if ( entity = = nullptr ) {
currentActivities . erase ( tamer ) ;
return nullptr ;
}
return entity - > GetComponent < PetComponent > ( ) ;
}
2022-07-25 02:26:51 +00:00
PetComponent * PetComponent : : GetActivePet ( LWOOBJID owner ) {
2021-12-05 17:54:36 +00:00
const auto & pair = activePets . find ( owner ) ;
if ( pair = = activePets . end ( ) ) {
return nullptr ;
}
2023-07-15 20:56:33 +00:00
auto * entity = Game : : entityManager - > GetEntity ( pair - > second ) ;
2021-12-05 17:54:36 +00:00
if ( entity = = nullptr ) {
activePets . erase ( owner ) ;
2022-07-25 02:26:51 +00:00
2021-12-05 17:54:36 +00:00
return nullptr ;
}
return entity - > GetComponent < PetComponent > ( ) ;
}
Entity * PetComponent : : GetParentEntity ( ) const {
return m_Parent ;
}
PetComponent : : ~ PetComponent ( ) {
}
void PetComponent : : SetPetNameForModeration ( const std : : string & petName ) {
int approved = 1 ; //default, in mod
//Make sure that the name isn't already auto-approved:
2023-03-24 23:16:45 +00:00
if ( Game : : chatFilter - > IsSentenceOkay ( petName , eGameMasterLevel : : CIVILIAN ) . empty ( ) ) {
2021-12-05 17:54:36 +00:00
approved = 2 ; //approved
}
//Save to db:
2023-11-18 00:47:18 +00:00
Database : : Get ( ) - > SetPetNameModerationStatus ( m_DatabaseId , IPetNames : : Info { petName , approved } ) ;
2021-12-05 17:54:36 +00:00
}
void PetComponent : : LoadPetNameFromModeration ( ) {
2023-11-18 00:47:18 +00:00
auto petNameInfo = Database : : Get ( ) - > GetPetNameInfo ( m_DatabaseId ) ;
if ( petNameInfo ) {
m_ModerationStatus = petNameInfo - > approvalStatus ;
2021-12-05 17:54:36 +00:00
if ( m_ModerationStatus = = 2 ) {
2023-11-18 00:47:18 +00:00
m_Name = petNameInfo - > petName ;
2021-12-05 17:54:36 +00:00
}
}
}
void PetComponent : : SetPreconditions ( std : : string & preconditions ) {
m_Preconditions = new PreconditionExpression ( preconditions ) ;
}