Compare commits

...

4 Commits

Author SHA1 Message Date
David Markowitz
5c296b8ef0 feat: add messaging system for manager and add example usage to Loot
tested that loot still drops at the entities position
2025-06-29 00:09:40 -07:00
David Markowitz
55d181ea4b fix: lxfml normalization (#1835) 2025-06-27 22:31:48 -07:00
David Markowitz
ecbb465020 fix: get entity from manager vs from character (#1833)
fixes a possible crash due to null entity
2025-06-26 07:04:05 -04:00
David Markowitz
ec9927acbb fix: multiplied speeds with Speed Behaviors (#1832) 2025-06-26 07:03:25 -04:00
33 changed files with 160 additions and 61 deletions

View File

@@ -3,6 +3,7 @@
// C++
#include <charconv>
#include <cstdint>
#include <cmath>
#include <ctime>
#include <functional>
#include <optional>
@@ -145,7 +146,7 @@ namespace GeneralUtils {
template <typename... Bases>
struct overload : Bases... {
using is_transparent = void;
using Bases::operator() ... ;
using Bases::operator() ...;
};
struct char_pointer_hash {
@@ -202,7 +203,7 @@ namespace GeneralUtils {
}
template<typename T>
requires(!Numeric<T>)
requires(!Numeric<T>)
[[nodiscard]] std::optional<T> TryParse(std::string_view str);
#if !(__GNUC__ >= 11 || _MSC_VER >= 1924)
@@ -221,7 +222,7 @@ namespace GeneralUtils {
*/
template <std::floating_point T>
[[nodiscard]] std::optional<T> TryParse(std::string_view str) noexcept
try {
try {
while (!str.empty() && std::isspace(str.front())) str.remove_prefix(1);
size_t parseNum;
@@ -323,4 +324,28 @@ namespace GeneralUtils {
return GenerateRandomNumber<T>(std::numeric_limits<T>::min(), std::numeric_limits<T>::max());
}
// https://www.quora.com/How-do-you-round-to-specific-increments-like-0-5-in-C
// Rounds to the nearest floating point value specified.
template <typename T, std::enable_if_t<std::is_floating_point_v<T>, int> = 0>
T RountToNearestEven(const T value, const T modulus) {
const auto modulo = std::fmod(value, modulus);
const auto abs_modulo_2 = std::abs(modulo * 2);
const auto abs_modulus = std::abs(modulus);
bool round_away_from_zero = false;
if (abs_modulo_2 > abs_modulus) {
round_away_from_zero = true;
} else if (abs_modulo_2 == abs_modulus) {
const auto trunc_quot = std::floor(std::abs(value / modulus));
const auto odd = std::fmod(trunc_quot, T{ 2 }) != 0;
round_away_from_zero = odd;
}
if (round_away_from_zero) {
return value + (std::copysign(modulus, value) - modulo);
} else {
return value - modulo;
}
}
}

View File

@@ -6,7 +6,7 @@
#include <ranges>
Lxfml::Result Lxfml::NormalizePosition(const std::string_view data) {
Lxfml::Result Lxfml::NormalizePosition(const std::string_view data, const NiPoint3& curPosition) {
Result toReturn;
tinyxml2::XMLDocument doc;
const auto err = doc.Parse(data.data());
@@ -44,29 +44,42 @@ Lxfml::Result Lxfml::NormalizePosition(const std::string_view data) {
NiPoint3 lowest{ 10'000.0f, 10'000.0f, 10'000.0f };
NiPoint3 highest{ -10'000.0f, -10'000.0f, -10'000.0f };
// Calculate the lowest and highest points on the entire model
for (const auto& transformation : transformations | std::views::values) {
auto split = GeneralUtils::SplitString(transformation, ',');
if (split.size() < 12) {
LOG("Not enough in the split?");
continue;
NiPoint3 delta = NiPoint3Constant::ZERO;
if (curPosition == NiPoint3Constant::ZERO) {
// Calculate the lowest and highest points on the entire model
for (const auto& transformation : transformations | std::views::values) {
auto split = GeneralUtils::SplitString(transformation, ',');
if (split.size() < 12) {
LOG("Not enough in the split?");
continue;
}
auto x = GeneralUtils::TryParse<float>(split[9]).value();
auto y = GeneralUtils::TryParse<float>(split[10]).value();
auto z = GeneralUtils::TryParse<float>(split[11]).value();
if (x < lowest.x) lowest.x = x;
if (y < lowest.y) lowest.y = y;
if (z < lowest.z) lowest.z = z;
if (highest.x < x) highest.x = x;
if (highest.y < y) highest.y = y;
if (highest.z < z) highest.z = z;
}
auto x = GeneralUtils::TryParse<float>(split[9]).value();
auto y = GeneralUtils::TryParse<float>(split[10]).value();
auto z = GeneralUtils::TryParse<float>(split[11]).value();
if (x < lowest.x) lowest.x = x;
if (y < lowest.y) lowest.y = y;
if (z < lowest.z) lowest.z = z;
if (highest.x < x) highest.x = x;
if (highest.y < y) highest.y = y;
if (highest.z < z) highest.z = z;
delta = (highest - lowest) / 2.0f;
} else {
lowest = curPosition;
highest = curPosition;
delta = NiPoint3Constant::ZERO;
}
auto delta = (highest - lowest) / 2.0f;
auto newRootPos = lowest + delta;
// Need to snap this chosen position to the nearest valid spot
// on the LEGO grid
newRootPos.x = GeneralUtils::RountToNearestEven(newRootPos.x, 0.8f);
newRootPos.z = GeneralUtils::RountToNearestEven(newRootPos.z, 0.8f);
// Clamp the Y to the lowest point on the model
newRootPos.y = lowest.y;
@@ -78,9 +91,9 @@ Lxfml::Result Lxfml::NormalizePosition(const std::string_view data) {
continue;
}
auto x = GeneralUtils::TryParse<float>(split[9]).value() - newRootPos.x;
auto y = GeneralUtils::TryParse<float>(split[10]).value() - newRootPos.y;
auto z = GeneralUtils::TryParse<float>(split[11]).value() - newRootPos.z;
auto x = GeneralUtils::TryParse<float>(split[9]).value() - newRootPos.x + curPosition.x;
auto y = GeneralUtils::TryParse<float>(split[10]).value() - newRootPos.y + curPosition.y;
auto z = GeneralUtils::TryParse<float>(split[11]).value() - newRootPos.z + curPosition.z;
std::stringstream stream;
for (int i = 0; i < 9; i++) {
stream << split[i];

View File

@@ -17,7 +17,7 @@ namespace Lxfml {
// Normalizes a LXFML model to be positioned relative to its local 0, 0, 0 rather than a game worlds 0, 0, 0.
// Returns a struct of its new center and the updated LXFML containing these edits.
[[nodiscard]] Result NormalizePosition(const std::string_view data);
[[nodiscard]] Result NormalizePosition(const std::string_view data, const NiPoint3& curPosition = NiPoint3Constant::ZERO);
// these are only for the migrations due to a bug in one of the implementations.
[[nodiscard]] Result NormalizePositionOnlyFirstPart(const std::string_view data);

View File

@@ -48,6 +48,7 @@ void MigrationRunner::RunMigrations() {
bool runSd0Migrations = false;
bool runNormalizeMigrations = false;
bool runNormalizeAfterFirstPartMigrations = false;
bool runBrickBuildsNotOnGrid = false;
for (const auto& entry : GeneralUtils::GetSqlFileNamesFromFolder((BinaryPathFinder::GetBinaryDir() / "./migrations/dlu/" / migrationFolder).string())) {
auto migration = LoadMigration("dlu/" + migrationFolder + "/", entry);
@@ -64,6 +65,8 @@ void MigrationRunner::RunMigrations() {
runNormalizeMigrations = true;
} else if (migration.name.ends_with("_normalize_model_positions_after_first_part.sql")) {
runNormalizeAfterFirstPartMigrations = true;
} else if (migration.name.ends_with("_brickbuilds_not_on_grid.sql")) {
runBrickBuildsNotOnGrid = true;
} else {
finalSQL.append(migration.data.c_str());
}
@@ -71,7 +74,7 @@ void MigrationRunner::RunMigrations() {
Database::Get()->InsertMigration(migration.name);
}
if (finalSQL.empty() && !runSd0Migrations && !runNormalizeMigrations && !runNormalizeAfterFirstPartMigrations) {
if (finalSQL.empty() && !runSd0Migrations && !runNormalizeMigrations && !runNormalizeAfterFirstPartMigrations && !runBrickBuildsNotOnGrid) {
LOG("Server database is up to date.");
return;
}
@@ -103,6 +106,10 @@ void MigrationRunner::RunMigrations() {
if (runNormalizeAfterFirstPartMigrations) {
ModelNormalizeMigration::RunAfterFirstPart();
}
if (runBrickBuildsNotOnGrid) {
ModelNormalizeMigration::RunBrickBuildGrid();
}
}
void MigrationRunner::RunSQLiteMigrations() {

View File

@@ -48,3 +48,24 @@ void ModelNormalizeMigration::RunAfterFirstPart() {
}
Database::Get()->SetAutoCommit(oldCommit);
}
void ModelNormalizeMigration::RunBrickBuildGrid() {
const auto oldCommit = Database::Get()->GetAutoCommit();
Database::Get()->SetAutoCommit(false);
for (auto& [lxfmlData, id, modelID] : Database::Get()->GetAllUgcModels()) {
const auto model = Database::Get()->GetModel(modelID);
// only BBB models (lot 14) need to have their position fixed from the above blunder
if (model.lot != 14) continue;
Sd0 sd0(lxfmlData);
const auto asStr = sd0.GetAsStringUncompressed();
const auto [newLxfml, newCenter] = Lxfml::NormalizePosition(asStr, model.position);
sd0.FromData(reinterpret_cast<const uint8_t*>(newLxfml.data()), newLxfml.size());
LOG("Updated model %llu to have a center of %f %f %f", modelID, newCenter.x, newCenter.y, newCenter.z);
auto asStream = sd0.GetAsStream();
Database::Get()->UpdateModel(model.id, newCenter, model.rotation, model.behaviors);
Database::Get()->UpdateUgcModelData(id, asStream);
}
Database::Get()->SetAutoCommit(oldCommit);
}

View File

@@ -7,6 +7,7 @@
namespace ModelNormalizeMigration {
void Run();
void RunAfterFirstPart();
void RunBrickBuildGrid();
};
#endif //!MODELNORMALIZEMIGRATION_H

View File

@@ -167,6 +167,7 @@ Entity::~Entity() {
if (m_Character) {
m_Character->SaveXMLToDatabase();
m_Character->SetEntity(nullptr);
}
CancelAllTimers();

View File

@@ -605,3 +605,14 @@ void EntityManager::FireEventServerSide(Entity* origin, std::string args) {
bool EntityManager::IsExcludedFromGhosting(LOT lot) {
return std::find(m_GhostingExcludedLOTs.begin(), m_GhostingExcludedLOTs.end(), lot) != m_GhostingExcludedLOTs.end();
}
bool EntityManager::SendMessage(GameMessages::GameMsg& msg) const {
bool handled = false;
const auto entityItr = m_Entities.find(msg.target);
if (entityItr != m_Entities.end()) {
auto* const entity = entityItr->second;
if (entity) handled = entity->HandleMsg(msg);
}
return handled;
}

View File

@@ -14,6 +14,10 @@ class Player;
class User;
enum class eReplicaComponentType : uint32_t;
namespace GameMessages {
struct GameMsg;
}
struct SystemAddress;
class EntityManager {
@@ -72,6 +76,9 @@ public:
const bool GetHardcoreDropinventoryOnDeath() { return m_HardcoreDropinventoryOnDeath; };
const uint32_t GetHardcoreUscoreEnemiesMultiplier() { return m_HardcoreUscoreEnemiesMultiplier; };
// Messaging
bool SendMessage(GameMessages::GameMsg& msg) const;
private:
void SerializeEntities();
void KillEntities();

View File

@@ -526,7 +526,7 @@ void UserManager::LoginCharacter(const SystemAddress& sysAddr, uint32_t playerID
ZoneInstanceManager::Instance()->RequestZoneTransfer(Game::server, zoneID, character->GetZoneClone(), false, [=](bool mythranShift, uint32_t zoneID, uint32_t zoneInstance, uint32_t zoneClone, std::string serverIP, uint16_t serverPort) {
LOG("Transferring %s to Zone %i (Instance %i | Clone %i | Mythran Shift: %s) with IP %s and Port %i", character->GetName().c_str(), zoneID, zoneInstance, zoneClone, mythranShift == true ? "true" : "false", serverIP.c_str(), serverPort);
if (character) {
auto* entity = character->GetEntity();
auto* entity = Game::entityManager->GetEntity(character->GetObjectID());
if (entity) {
auto* characterComponent = entity->GetComponent<CharacterComponent>();
if (characterComponent) {

View File

@@ -575,7 +575,7 @@ void ActivityInstance::RewardParticipant(Entity* participant) {
maxCoins = currencyTable[0].maxvalue;
}
Loot::DropLoot(participant, m_Parent, activityRewards[0].LootMatrixIndex, minCoins, maxCoins);
Loot::DropLoot(participant, m_Parent->GetObjectID(), activityRewards[0].LootMatrixIndex, minCoins, maxCoins);
}
}

View File

@@ -755,18 +755,18 @@ void DestroyableComponent::Smash(const LWOOBJID source, const eKillType killType
auto* member = Game::entityManager->GetEntity(specificOwner);
if (member) Loot::DropLoot(member, m_Parent, lootMatrixId, GetMinCoins(), GetMaxCoins());
if (member) Loot::DropLoot(member, m_Parent->GetObjectID(), lootMatrixId, GetMinCoins(), GetMaxCoins());
} else {
for (const auto memberId : team->members) { // Free for all
auto* member = Game::entityManager->GetEntity(memberId);
if (member == nullptr) continue;
Loot::DropLoot(member, m_Parent, lootMatrixId, GetMinCoins(), GetMaxCoins());
Loot::DropLoot(member, m_Parent->GetObjectID(), lootMatrixId, GetMinCoins(), GetMaxCoins());
}
}
} else { // drop loot for non team user
Loot::DropLoot(owner, m_Parent, GetLootMatrixID(), GetMinCoins(), GetMaxCoins());
Loot::DropLoot(owner, m_Parent->GetObjectID(), GetLootMatrixID(), GetMinCoins(), GetMaxCoins());
}
}
} else {
@@ -784,7 +784,7 @@ void DestroyableComponent::Smash(const LWOOBJID source, const eKillType killType
coinsTotal -= coinsToLose;
Loot::DropLoot(m_Parent, m_Parent, -1, coinsToLose, coinsToLose);
Loot::DropLoot(m_Parent, m_Parent->GetObjectID(), -1, coinsToLose, coinsToLose);
character->SetCoins(coinsTotal, eLootSourceType::PICKUP);
}
}

View File

@@ -265,7 +265,7 @@ bool ModelComponent::TrySetVelocity(const NiPoint3& velocity) const {
// If we're currently moving on an axis, prevent the move so only 1 behavior can have control over an axis
if (velocity != NiPoint3Constant::ZERO) {
const auto [x, y, z] = velocity;
const auto [x, y, z] = velocity * m_Speed;
if (x != 0.0f) {
if (currentVelocity.x != 0.0f) return false;
currentVelocity.x = x;
@@ -280,7 +280,6 @@ bool ModelComponent::TrySetVelocity(const NiPoint3& velocity) const {
currentVelocity = velocity;
}
currentVelocity *= m_Speed;
m_Parent->SetVelocity(currentVelocity);
return true;
}

View File

@@ -459,7 +459,7 @@ void QuickBuildComponent::CompleteQuickBuild(Entity* const user) {
auto* missionComponent = builder->GetComponent<MissionComponent>();
if (missionComponent) missionComponent->Progress(eMissionTaskType::ACTIVITY, m_ActivityId);
}
Loot::DropActivityLoot(builder, m_Parent, m_ActivityId, 1);
Loot::DropActivityLoot(builder, m_Parent->GetObjectID(), m_ActivityId, 1);
}
// Notify scripts

View File

@@ -393,7 +393,7 @@ void RacingControlComponent::HandleMessageBoxResponse(Entity* player, int32_t bu
}
const auto score = playersRating * 10 + data->finished;
Loot::GiveActivityLoot(player, m_Parent, m_ActivityID, score);
Loot::GiveActivityLoot(player, m_Parent->GetObjectID(), m_ActivityID, score);
// Giving rewards
GameMessages::SendNotifyRacingClient(

View File

@@ -6312,6 +6312,10 @@ void GameMessages::SendUpdateInventoryUi(LWOOBJID objectId, const SystemAddress&
}
namespace GameMessages {
bool GameMsg::Send() {
return Game::entityManager->SendMessage(*this);
}
void GameMsg::Send(const SystemAddress& sysAddr) const {
CBITSTREAM;
CMSGHEADER;

View File

@@ -54,6 +54,12 @@ namespace GameMessages {
GameMsg(MessageType::Game gmId, eGameMasterLevel lvl) : msgId{ gmId }, requiredGmLevel{ lvl } {}
GameMsg(MessageType::Game gmId) : GameMsg(gmId, eGameMasterLevel::CIVILIAN) {}
virtual ~GameMsg() = default;
// Sends a message to the entity manager to route to the target
bool Send();
// Sends the message to the specified client or
// all clients if UNASSIGNED_SYSTEM_ADDRESS is specified
void Send(const SystemAddress& sysAddr) const;
virtual void Serialize(RakNet::BitStream& bitStream) const {}
virtual bool Deserialize(RakNet::BitStream& bitStream) { return true; }

View File

@@ -214,7 +214,7 @@ void Loot::GiveLoot(Entity* player, std::unordered_map<LOT, int32_t>& result, eL
}
}
void Loot::GiveActivityLoot(Entity* player, Entity* source, uint32_t activityID, int32_t rating) {
void Loot::GiveActivityLoot(Entity* player, const LWOOBJID source, uint32_t activityID, int32_t rating) {
CDActivityRewardsTable* activityRewardsTable = CDClientManager::GetTable<CDActivityRewardsTable>();
std::vector<CDActivityRewards> activityRewards = activityRewardsTable->Query([activityID](CDActivityRewards entry) { return (entry.objectTemplate == activityID); });
@@ -248,7 +248,7 @@ void Loot::GiveActivityLoot(Entity* player, Entity* source, uint32_t activityID,
character->SetCoins(character->GetCoins() + coins, eLootSourceType::ACTIVITY);
}
void Loot::DropLoot(Entity* player, Entity* killedObject, uint32_t matrixIndex, uint32_t minCoins, uint32_t maxCoins) {
void Loot::DropLoot(Entity* player, const LWOOBJID source, uint32_t matrixIndex, uint32_t minCoins, uint32_t maxCoins) {
player = player->GetOwner(); // if the owner is overwritten, we collect that here
auto* inventoryComponent = player->GetComponent<InventoryComponent>();
@@ -258,10 +258,10 @@ void Loot::DropLoot(Entity* player, Entity* killedObject, uint32_t matrixIndex,
std::unordered_map<LOT, int32_t> result = RollLootMatrix(player, matrixIndex);
DropLoot(player, killedObject, result, minCoins, maxCoins);
DropLoot(player, source, result, minCoins, maxCoins);
}
void Loot::DropLoot(Entity* player, Entity* killedObject, std::unordered_map<LOT, int32_t>& result, uint32_t minCoins, uint32_t maxCoins) {
void Loot::DropLoot(Entity* player, const LWOOBJID source, std::unordered_map<LOT, int32_t>& result, uint32_t minCoins, uint32_t maxCoins) {
player = player->GetOwner(); // if the owner is overwritten, we collect that here
auto* inventoryComponent = player->GetComponent<InventoryComponent>();
@@ -269,9 +269,11 @@ void Loot::DropLoot(Entity* player, Entity* killedObject, std::unordered_map<LOT
if (!inventoryComponent)
return;
const auto spawnPosition = killedObject->GetPosition();
GameMessages::GetPosition posMsg;
posMsg.target = source;
posMsg.Send();
const auto source = killedObject->GetObjectID();
const auto spawnPosition = posMsg.pos;
for (const auto& pair : result) {
for (int i = 0; i < pair.second; ++i) {
@@ -284,7 +286,7 @@ void Loot::DropLoot(Entity* player, Entity* killedObject, std::unordered_map<LOT
GameMessages::SendDropClientLoot(player, source, LOT_NULL, coins, spawnPosition);
}
void Loot::DropActivityLoot(Entity* player, Entity* source, uint32_t activityID, int32_t rating) {
void Loot::DropActivityLoot(Entity* player, const LWOOBJID source, uint32_t activityID, int32_t rating) {
CDActivityRewardsTable* activityRewardsTable = CDClientManager::GetTable<CDActivityRewardsTable>();
std::vector<CDActivityRewards> activityRewards = activityRewardsTable->Query([activityID](CDActivityRewards entry) { return (entry.objectTemplate == activityID); });

View File

@@ -18,8 +18,8 @@ namespace Loot {
void CacheMatrix(const uint32_t matrixIndex);
void GiveLoot(Entity* player, uint32_t matrixIndex, eLootSourceType lootSourceType = eLootSourceType::NONE);
void GiveLoot(Entity* player, std::unordered_map<LOT, int32_t>& result, eLootSourceType lootSourceType = eLootSourceType::NONE);
void GiveActivityLoot(Entity* player, Entity* source, uint32_t activityID, int32_t rating = 0);
void DropLoot(Entity* player, Entity* killedObject, uint32_t matrixIndex, uint32_t minCoins, uint32_t maxCoins);
void DropLoot(Entity* player, Entity* killedObject, std::unordered_map<LOT, int32_t>& result, uint32_t minCoins, uint32_t maxCoins);
void DropActivityLoot(Entity* player, Entity* source, uint32_t activityID, int32_t rating = 0);
void GiveActivityLoot(Entity* player, const LWOOBJID source, uint32_t activityID, int32_t rating = 0);
void DropLoot(Entity* player, const LWOOBJID source, uint32_t matrixIndex, uint32_t minCoins, uint32_t maxCoins);
void DropLoot(Entity* player, const LWOOBJID source, std::unordered_map<LOT, int32_t>& result, uint32_t minCoins, uint32_t maxCoins);
void DropActivityLoot(Entity* player, const LWOOBJID source, uint32_t activityID, int32_t rating = 0);
};

View File

@@ -33,10 +33,10 @@ void TreasureChestDragonServer::OnUse(Entity* self, Entity* user) {
if (memberObject == nullptr) continue;
Loot::DropActivityLoot(memberObject, self, scriptedActivityComponent->GetActivityID(), rating);
Loot::DropActivityLoot(memberObject, self->GetObjectID(), scriptedActivityComponent->GetActivityID(), rating);
}
} else {
Loot::DropActivityLoot(user, self, scriptedActivityComponent->GetActivityID(), rating);
Loot::DropActivityLoot(user, self->GetObjectID(), scriptedActivityComponent->GetActivityID(), rating);
}
self->Smash(self->GetObjectID());

View File

@@ -45,7 +45,7 @@ BootyDigServer::OnFireEventServerSide(Entity* self, Entity* sender, std::string
if (renderComponent != nullptr)
renderComponent->PlayEffect(7730, u"cast", "bootyshine");
Loot::DropLoot(player, self, 231, 75, 75);
Loot::DropLoot(player, self->GetObjectID(), 231, 75, 75);
}
}
} else if (args == "ChestDead") {

View File

@@ -22,7 +22,7 @@ void BaseInteractDropLootServer::BaseUse(Entity* self, Entity* user) {
self->SetNetworkVar(u"bInUse", true);
Loot::DropLoot(user, self, lootMatrix, 0, 0);
Loot::DropLoot(user, self->GetObjectID(), lootMatrix, 0, 0);
self->AddCallbackTimer(cooldownTime, [this, self]() {
self->SetNetworkVar(u"bInUse", false);

View File

@@ -13,7 +13,7 @@ void GrowingFlower::OnSkillEventFired(Entity* self, Entity* target, const std::s
const auto mission1 = self->GetVar<int32_t>(u"missionID");
const auto mission2 = self->GetVar<int32_t>(u"missionID2");
Loot::DropActivityLoot(target, self, self->GetLOT(), 0);
Loot::DropActivityLoot(target, self->GetObjectID(), self->GetLOT(), 0);
auto* missionComponent = target->GetComponent<MissionComponent>();
if (missionComponent != nullptr) {

View File

@@ -23,7 +23,7 @@ void WishingWellServer::OnUse(Entity* self, Entity* user) {
Loot::DropActivityLoot(
user,
self,
self->GetObjectID(),
static_cast<uint32_t>(scriptedActivity->GetActivityID()),
GeneralUtils::GenerateRandomNumber<int32_t>(1, 1000)
);

View File

@@ -6,7 +6,7 @@
#include "eTerminateType.h"
void VeMissionConsole::OnUse(Entity* self, Entity* user) {
Loot::DropActivityLoot(user, self, 12551);
Loot::DropActivityLoot(user, self->GetObjectID(), 12551);
auto* inventoryComponent = user->GetComponent<InventoryComponent>();
if (inventoryComponent != nullptr) {

View File

@@ -14,6 +14,6 @@ void NjDragonEmblemChestServer::OnUse(Entity* self, Entity* user) {
auto* destroyable = self->GetComponent<DestroyableComponent>();
if (destroyable != nullptr) {
Loot::DropLoot(user, self, destroyable->GetLootMatrixID(), 0, 0);
Loot::DropLoot(user, self->GetObjectID(), destroyable->GetLootMatrixID(), 0, 0);
}
}

View File

@@ -27,7 +27,7 @@ void MinigameTreasureChestServer::OnUse(Entity* self, Entity* user) {
if (self->GetLOT() == frakjawChestId) activityRating = team->members.size();
Loot::DropActivityLoot(teamMember, self, sac->GetActivityID(), activityRating);
Loot::DropActivityLoot(teamMember, self->GetObjectID(), sac->GetActivityID(), activityRating);
}
}
} else {
@@ -35,7 +35,7 @@ void MinigameTreasureChestServer::OnUse(Entity* self, Entity* user) {
if (self->GetLOT() == frakjawChestId) activityRating = 1;
Loot::DropActivityLoot(user, self, sac->GetActivityID(), activityRating);
Loot::DropActivityLoot(user, self->GetObjectID(), sac->GetActivityID(), activityRating);
}
sac->PlayerRemove(user->GetObjectID());

View File

@@ -72,7 +72,7 @@ void ActivityManager::StopActivity(Entity* self, const LWOOBJID playerID, const
SetActivityValue(self, playerID, 1, value1);
SetActivityValue(self, playerID, 2, value2);
Loot::GiveActivityLoot(player, self, gameID, CalculateActivityRating(self, playerID));
Loot::GiveActivityLoot(player, self->GetObjectID(), gameID, CalculateActivityRating(self, playerID));
if (sac != nullptr) {
sac->PlayerRemove(player->GetObjectID());

View File

@@ -29,7 +29,7 @@ void ScriptedPowerupSpawner::OnTimerDone(Entity* self, std::string message) {
renderComponent->PlayEffect(0, u"cast", "N_cast");
}
Loot::DropLoot(owner, self, drops, 0, 0);
Loot::DropLoot(owner, self->GetObjectID(), drops, 0, 0);
}
// Increment the current cycle

View File

@@ -11,7 +11,7 @@ void AgPicnicBlanket::OnUse(Entity* self, Entity* user) {
self->SetVar<bool>(u"active", true);
auto lootTable = std::unordered_map<LOT, int32_t>{ {935, 3} };
Loot::DropLoot(user, self, lootTable, 0, 0);
Loot::DropLoot(user, self->GetObjectID(), lootTable, 0, 0);
self->AddCallbackTimer(5.0f, [self]() {
self->SetVar<bool>(u"active", false);

View File

@@ -572,7 +572,7 @@ void SGCannon::StopGame(Entity* self, bool cancel) {
missionComponent->Progress(eMissionTaskType::ACTIVITY, m_CannonLot, 0, "", self->GetVar<int32_t>(TotalScoreVariable));
}
Loot::GiveActivityLoot(player, self, GetGameID(self), self->GetVar<int32_t>(TotalScoreVariable));
Loot::GiveActivityLoot(player, self->GetObjectID(), GetGameID(self), self->GetVar<int32_t>(TotalScoreVariable));
SaveScore(self, player->GetObjectID(),
static_cast<float>(self->GetVar<int32_t>(TotalScoreVariable)), static_cast<float>(self->GetVar<uint32_t>(MaxStreakVariable)), percentage);

View File

@@ -0,0 +1 @@
/* See ModelNormalizeMigration.cpp for details */

View File

@@ -0,0 +1 @@
/* See ModelNormalizeMigration.cpp for details */