Compare commits

..

1 Commits

Author SHA1 Message Date
David Markowitz
f2f02d0720 feat: hatchlings
tested that hatchlings now function
fixes #759

Update MovementAIComponent.cpp

Update MovementAIComponent.cpp

Update HatchlingPets.cpp
2026-06-20 04:15:52 -07:00
10 changed files with 160 additions and 8 deletions

View File

@@ -105,7 +105,15 @@ void PlayerContainer::RemovePlayer(const LWOOBJID playerID) {
auto* team = TeamContainer::GetTeam(playerID); auto* team = TeamContainer::GetTeam(playerID);
if (team != nullptr) { if (team != nullptr) {
TeamContainer::RemoveMember(team, playerID, false, false, true); const auto memberName = GeneralUtils::UTF8ToUTF16(player.playerName);
for (const auto memberId : team->memberIDs) {
const auto& otherMember = GetPlayerData(memberId);
if (!otherMember) continue;
TeamContainer::SendTeamSetOffWorldFlag(otherMember, playerID, { 0, 0, 0 });
}
} }
ChatWeb::SendWSPlayerUpdate(player, eActivityType::PlayerLoggedOut); ChatWeb::SendWSPlayerUpdate(player, eActivityType::PlayerLoggedOut);

View File

@@ -2237,6 +2237,7 @@ bool Entity::MsgRequestServerObjectInfo(GameMessages::RequestServerObjectInfo& r
objectInfo.PushDebug<AMFIntValue>("Template ID(LOT)") = GetLOT(); objectInfo.PushDebug<AMFIntValue>("Template ID(LOT)") = GetLOT();
objectInfo.PushDebug<AMFStringValue>("Object ID") = std::to_string(GetObjectID()); objectInfo.PushDebug<AMFStringValue>("Object ID") = std::to_string(GetObjectID());
objectInfo.PushDebug<AMFStringValue>("Spawner's Object ID") = std::to_string(GetSpawnerID()); objectInfo.PushDebug<AMFStringValue>("Spawner's Object ID") = std::to_string(GetSpawnerID());
objectInfo.PushDebug<AMFStringValue>("Owner override") = std::to_string(m_OwnerOverride);
auto& componentDetails = objectInfo.PushDebug("Component Information"); auto& componentDetails = objectInfo.PushDebug("Component Information");
for (const auto [id, component] : m_Components) { for (const auto [id, component] : m_Components) {

View File

@@ -189,6 +189,15 @@ void MovementAIComponent::Update(const float deltaTime) {
} }
} else { } else {
Stop(); Stop();
if (m_FollowedTarget != LWOOBJID_EMPTY) {
GameMessages::GetPosition getPos;
if (!getPos.Send(m_FollowedTarget)) {
LOG("Target %llu does not exist anymore to follow", m_FollowedTarget);
m_FollowedTarget = LWOOBJID_EMPTY;
} else {
SetDestination(getPos.pos);
}
}
return; return;
} }
} else { } else {
@@ -555,5 +564,25 @@ bool MovementAIComponent::OnGetObjectReportInfo(GameMessages::GetObjectReportInf
pathCopy.pop(); pathCopy.pop();
} }
movementInfo.PushDebug<AMFStringValue>("Followed Target") = std::to_string(m_FollowedTarget);
return true; return true;
} }
void MovementAIComponent::FollowTarget(const LWOOBJID target) {
if (target == LWOOBJID_EMPTY) {
m_FollowedTarget = target;
return;
}
GameMessages::GetPosition getPos;
if (!getPos.Send(target)) {
LOG("Tried to follow target %llu but they don't exist", target);
m_FollowedTarget = LWOOBJID_EMPTY;
return;
}
m_FollowedTarget = target;
SetMaxSpeed(1.0f);
m_CurrentSpeed = 1.0f;
SetDestination(getPos.pos);
}

View File

@@ -31,27 +31,27 @@ struct MovementAIInfo {
/** /**
* The radius that the entity can wander in * The radius that the entity can wander in
*/ */
float wanderRadius; float wanderRadius{};
/** /**
* The speed at which the entity wanders * The speed at which the entity wanders
*/ */
float wanderSpeed; float wanderSpeed{};
/** /**
* This is only used for the emotes * This is only used for the emotes
*/ */
float wanderChance; float wanderChance{};
/** /**
* The min amount of delay before wandering * The min amount of delay before wandering
*/ */
float wanderDelayMin; float wanderDelayMin{};
/** /**
* The max amount of delay before wandering * The max amount of delay before wandering
*/ */
float wanderDelayMax; float wanderDelayMax{};
}; };
/** /**
@@ -214,6 +214,8 @@ public:
bool OnGetObjectReportInfo(GameMessages::GetObjectReportInfo& reportInfo); bool OnGetObjectReportInfo(GameMessages::GetObjectReportInfo& reportInfo);
bool HasPath() const { return m_Path != nullptr; } bool HasPath() const { return m_Path != nullptr; }
void FollowTarget(const LWOOBJID target);
private: private:
/** /**
@@ -337,6 +339,8 @@ private:
// The number of waypoints that were on the path in the call to SetPath // The number of waypoints that were on the path in the call to SetPath
uint32_t m_CurrentPathWaypointCount{ 0 }; uint32_t m_CurrentPathWaypointCount{ 0 };
LWOOBJID m_FollowedTarget{ LWOOBJID_EMPTY };
}; };
#endif // MOVEMENTAICOMPONENT_H #endif // MOVEMENTAICOMPONENT_H

View File

@@ -37,6 +37,7 @@ target_include_directories(dScriptsServerBase PUBLIC "."
"Minigame" "Minigame"
"Minigame/General" "Minigame/General"
"Objects" "Objects"
"Objects/Hatchlings"
) )
target_precompile_headers(dScriptsServerBase REUSE_FROM dScriptsBase) target_precompile_headers(dScriptsServerBase REUSE_FROM dScriptsBase)

View File

@@ -1,4 +1,11 @@
add_subdirectory(Hatchlings)
set(DSCRIPTS_SOURCES_02_SERVER_OBJECTS set(DSCRIPTS_SOURCES_02_SERVER_OBJECTS
"AgSurvivalBuffStation.cpp" "AgSurvivalBuffStation.cpp"
"StinkyFishTarget.cpp" "StinkyFishTarget.cpp")
PARENT_SCOPE)
foreach(file ${DSCRIPTS_SOURCES_02_SERVER_OBJECTS_HATCHLINGS})
set(DSCRIPTS_SOURCES_02_SERVER_OBJECTS ${DSCRIPTS_SOURCES_02_SERVER_OBJECTS} "Hatchlings/${file}")
endforeach()
set(DSCRIPTS_SOURCES_02_SERVER_OBJECTS ${DSCRIPTS_SOURCES_02_SERVER_OBJECTS} PARENT_SCOPE)

View File

@@ -0,0 +1,3 @@
set(DSCRIPTS_SOURCES_02_SERVER_OBJECTS_HATCHLINGS
"HatchlingPets.cpp"
PARENT_SCOPE)

View File

@@ -0,0 +1,83 @@
#include "HatchlingPets.h"
#include "Entity.h"
#include "MovementAIComponent.h"
void HatchlingPets::OnStartup(Entity* self) {
self->SetVar(u"follow", false);
self->SetProximityRadius(5, "StopFollow");
self->SetProximityRadius(15, "Wander");
self->SetProximityRadius(50, "Teleport");
Wander(*self, *self->GetOwner());
self->AddComponent<MovementAIComponent>(-1, MovementAIInfo{ .wanderRadius = 2.5f });
}
void HatchlingPets::OnProximityUpdate(Entity* self, Entity* entering, std::string name, std::string status) {
auto* const parent = self->GetOwner();
if (!entering || !entering->IsPlayer() || parent->GetObjectID() != entering->GetObjectID()) return;
if (name == "StopFollow") {
if (status == "ENTER") {
if (self->GetVar<bool>(u"follow")) {
const auto randomWanderTime = GeneralUtils::GenerateRandomNumber<float>(4, 9);
self->AddTimer("StartWander", randomWanderTime);
// stop following the player
auto* const movementAI = self->GetComponent<MovementAIComponent>();
if (movementAI) {
movementAI->Stop();
movementAI->FollowTarget(LWOOBJID_EMPTY);
}
self->SetVar(u"follow", false);
}
}
} else if (name == "Wander") {
if (status == "LEAVE") {
self->CancelAllTimers();
// follow the player
auto* const movementAI = self->GetComponent<MovementAIComponent>();
if (movementAI) {
movementAI->Stop();
movementAI->FollowTarget(entering->GetObjectID());
}
self->SetVar(u"follow", true);
}
} else if (name == "Teleport") {
if (status == "LEAVE") {
// stop following the player
auto* const movementAI = self->GetComponent<MovementAIComponent>();
if (movementAI) {
movementAI->Stop();
movementAI->FollowTarget(LWOOBJID_EMPTY);
}
GameMessages::GetPosition getPos;
getPos.Send(entering->GetObjectID());
getPos.pos.z += 5.0f;
self->SetPosition(getPos.pos);
Game::entityManager->SerializeEntity(*self);
}
}
}
void HatchlingPets::OnTimerDone(Entity* self, std::string timerName) {
if (timerName == "StartWander") {
Wander(*self, *self->GetOwner());
}
}
void HatchlingPets::Wander(Entity& self, Entity& player) {
GameMessages::GetPosition getPos;
if (!getPos.Send(player.GetObjectID())) {
LOG("Failed to get position for %llu", player.GetObjectID());
return;
}
const auto xWander = GeneralUtils::GenerateRandomNumber<float>(0, 20) - 10.0f;
const auto zWander = GeneralUtils::GenerateRandomNumber<float>(0, 20) - 10.0f;
getPos.pos.x += xWander;
getPos.pos.z += zWander;
auto* const movementAI = self.GetComponent<MovementAIComponent>();
if (movementAI) movementAI->SetDestination(getPos.pos);
self.AddTimer("StartWander", GeneralUtils::GenerateRandomNumber<float>(4, 9));
}

View File

@@ -0,0 +1,14 @@
#ifndef HATCHLINGPETS_H
#define HATCHLINGPETS_H
#include "CppScripts.h"
#include "NiPoint3.h"
class HatchlingPets : public CppScripts::Script {
void OnStartup(Entity* self) override;
void OnProximityUpdate(Entity* self, Entity* entering, std::string name, std::string status) override;
void OnTimerDone(Entity* self, std::string timerName) override;
void Wander(Entity& self, Entity& player);
};
#endif //!HATCHLINGPETS_H

View File

@@ -166,6 +166,7 @@
#include "AgSalutingNpcs.h" #include "AgSalutingNpcs.h"
#include "BossSpiderQueenEnemyServer.h" #include "BossSpiderQueenEnemyServer.h"
#include "RockHydrantSmashable.h" #include "RockHydrantSmashable.h"
#include "HatchlingPets.h"
// Misc Scripts // Misc Scripts
#include "ExplodingAsset.h" #include "ExplodingAsset.h"
@@ -423,6 +424,7 @@ namespace {
{"scripts\\ai\\NS\\L_NS_CONCERT_INSTRUMENT_QB.lua", []() {return new NsConcertInstrument();}}, {"scripts\\ai\\NS\\L_NS_CONCERT_INSTRUMENT_QB.lua", []() {return new NsConcertInstrument();}},
{"scripts\\ai\\NS\\L_NS_JONNY_FLAG_MISSION_SERVER.lua", []() {return new NsJohnnyMissionServer();}}, {"scripts\\ai\\NS\\L_NS_JONNY_FLAG_MISSION_SERVER.lua", []() {return new NsJohnnyMissionServer();}},
{"scripts\\02_server\\Objects\\L_STINKY_FISH_TARGET.lua", []() {return new StinkyFishTarget();}}, {"scripts\\02_server\\Objects\\L_STINKY_FISH_TARGET.lua", []() {return new StinkyFishTarget();}},
{"scripts\\02_server\\Objects\\Hatchlings\\L_HATCHLING_PETS.lua", []() {return new HatchlingPets();}},
{"scripts\\zone\\PROPERTY\\NS\\L_ZONE_NS_PROPERTY.lua", []() {return new ZoneNsProperty();}}, {"scripts\\zone\\PROPERTY\\NS\\L_ZONE_NS_PROPERTY.lua", []() {return new ZoneNsProperty();}},
{"scripts\\02_server\\Map\\Property\\NS_Med\\L_ZONE_NS_MED_PROPERTY.lua", []() {return new ZoneNsMedProperty();}}, {"scripts\\02_server\\Map\\Property\\NS_Med\\L_ZONE_NS_MED_PROPERTY.lua", []() {return new ZoneNsMedProperty();}},
{"scripts\\02_server\\Map\\NS\\L_NS_TOKEN_CONSOLE_SERVER.lua", []() {return new NsTokenConsoleServer();}}, {"scripts\\02_server\\Map\\NS\\L_NS_TOKEN_CONSOLE_SERVER.lua", []() {return new NsTokenConsoleServer();}},