Compare commits

...

12 Commits

Author SHA1 Message Date
David Markowitz
03b1c2b183 fix: Split String returning an empty string when an empty string is passed in
Tested that slash commands function as before, except now if you pass no arguments to a command like /fly, it no longer reports an invalid command.
2025-06-29 02:02:25 -07:00
David Markowitz
48510b7315 feat: add messaging system for manager and add example usage to Loot (#1837)
tested that loot still drops at the entities position
2025-06-29 03:22:41 -04:00
David Markowitz
c697f8ad97 feat: Add Restart Behavior (#1836)
Resets the model to the default state at the end of the models frame.  Will see if in the future designers want this to be more strict on the resetting timing.
2025-06-29 03:22:20 -04: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
David Markowitz
1f580491c7 feat: add speed behavior (#1831) 2025-06-25 05:04:25 -04:00
David Markowitz
2618e9a864 fix: specifiy width of integer being written (#1830) 2025-06-25 00:58:11 -04:00
David Markowitz
0f0d0a6dee optimizations (#1829) 2025-06-24 22:13:48 -05:00
David Markowitz
f63a9a6bea fix: don't construct zone control twice on player loadin (#1828)
checked that the logs no longer have an error about zone control mis matched pointers

Update EntityManager.cpp
2025-06-24 22:03:13 -05:00
David Markowitz
f0f98a6108 fix: consuming items not decrementing mission progress (#1827)
tested that consuming water no longer leaves a mission unable to be completed
2025-06-24 22:01:59 -05:00
David Markowitz
4ed7bd6767 fix: some mail features (#1826) 2025-06-23 23:58:55 -04:00
45 changed files with 527 additions and 446 deletions

View File

@@ -237,57 +237,6 @@ bool GeneralUtils::ReplaceInString(std::string& str, const std::string_view from
return true;
}
std::vector<std::wstring> GeneralUtils::SplitString(const std::wstring_view str, const wchar_t delimiter) {
std::vector<std::wstring> vector = std::vector<std::wstring>();
std::wstring current;
for (const wchar_t c : str) {
if (c == delimiter) {
vector.push_back(current);
current = L"";
} else {
current += c;
}
}
vector.push_back(std::move(current));
return vector;
}
std::vector<std::u16string> GeneralUtils::SplitString(const std::u16string_view str, const char16_t delimiter) {
std::vector<std::u16string> vector = std::vector<std::u16string>();
std::u16string current;
for (const char16_t c : str) {
if (c == delimiter) {
vector.push_back(current);
current = u"";
} else {
current += c;
}
}
vector.push_back(std::move(current));
return vector;
}
std::vector<std::string> GeneralUtils::SplitString(const std::string_view str, const char delimiter) {
std::vector<std::string> vector = std::vector<std::string>();
std::string current = "";
for (const char c : str) {
if (c == delimiter) {
vector.push_back(current);
current = "";
} else {
current += c;
}
}
vector.push_back(std::move(current));
return vector;
}
std::u16string GeneralUtils::ReadWString(RakNet::BitStream& inStream) {
uint32_t length;
inStream.Read<uint32_t>(length);

View File

@@ -3,6 +3,7 @@
// C++
#include <charconv>
#include <cstdint>
#include <cmath>
#include <ctime>
#include <functional>
#include <optional>
@@ -129,11 +130,23 @@ namespace GeneralUtils {
std::u16string ReadWString(RakNet::BitStream& inStream);
std::vector<std::wstring> SplitString(const std::wstring_view str, const wchar_t delimiter);
template<typename StringType, typename CharType = typename StringType::value_type>
std::vector<std::basic_string<CharType>> SplitString(const StringType& str, const typename StringType::value_type delimiter) {
std::vector<std::basic_string<CharType>> toReturn{};
std::vector<std::u16string> SplitString(const std::u16string_view str, const char16_t delimiter);
toReturn.emplace_back();
auto itr = toReturn.rbegin();
for (const auto c : str) {
if (c == delimiter) {
toReturn.emplace_back();
itr = toReturn.rbegin();
} else {
(*itr).push_back(c);
}
}
std::vector<std::string> SplitString(const std::string_view str, const char delimiter);
return toReturn;
}
std::vector<std::string> GetSqlFileNamesFromFolder(const std::string_view folder);
@@ -145,7 +158,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 +215,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 +234,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 +336,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

File diff suppressed because it is too large Load Diff

View File

@@ -34,7 +34,6 @@ namespace tinyxml2 {
};
class Player;
class EntityInfo;
class User;
class Spawner;
class ScriptComponent;
@@ -45,6 +44,7 @@ class Item;
class Character;
class EntityCallbackTimer;
class PositionUpdate;
struct EntityInfo;
enum class eTriggerEventType;
enum class eGameMasterLevel : uint8_t;
enum class eReplicaComponentType : uint32_t;
@@ -60,7 +60,7 @@ namespace CppScripts {
*/
class Entity {
public:
explicit Entity(const LWOOBJID& objectID, EntityInfo info, User* parentUser = nullptr, Entity* parentEntity = nullptr);
Entity(const LWOOBJID& objectID, const EntityInfo& info, User* parentUser = nullptr, Entity* parentEntity = nullptr);
~Entity();
void Initialize();
@@ -113,7 +113,7 @@ public:
float GetDefaultScale() const;
const NiPoint3& GetPosition() const;
NiPoint3 GetPosition() const;
const NiQuaternion& GetRotation() const;
@@ -146,9 +146,9 @@ public:
void SetRotation(const NiQuaternion& rotation);
void SetRespawnPos(const NiPoint3& position);
void SetRespawnPos(const NiPoint3& position) const;
void SetRespawnRot(const NiQuaternion& rotation);
void SetRespawnRot(const NiQuaternion& rotation) const;
void SetVelocity(const NiPoint3& velocity);
@@ -169,7 +169,7 @@ public:
void AddComponent(eReplicaComponentType componentId, Component* component);
// This is expceted to never return nullptr, an assert checks this.
CppScripts::Script* const GetScript();
CppScripts::Script* const GetScript() const;
void Subscribe(LWOOBJID scriptObjId, CppScripts::Script* scriptToAdd, const std::string& notificationName);
void Unsubscribe(LWOOBJID scriptObjId, const std::string& notificationName);
@@ -182,8 +182,8 @@ public:
void RemoveParent();
// Adds a timer to start next frame with the given name and time.
void AddTimer(std::string name, float time);
void AddCallbackTimer(float time, std::function<void()> callback);
void AddTimer(const std::string& name, float time);
void AddCallbackTimer(float time, const std::function<void()> callback);
bool HasTimer(const std::string& name);
void CancelCallbackTimers();
void CancelAllTimers();
@@ -195,7 +195,7 @@ public:
std::unordered_map<eReplicaComponentType, Component*>& GetComponents() { return m_Components; } // TODO: Remove
void WriteBaseReplicaData(RakNet::BitStream& outBitStream, eReplicaPacketType packetType);
void WriteComponents(RakNet::BitStream& outBitStream, eReplicaPacketType packetType);
void WriteComponents(RakNet::BitStream& outBitStream, eReplicaPacketType packetType) const;
void UpdateXMLDoc(tinyxml2::XMLDocument& doc);
void Update(float deltaTime);
@@ -242,21 +242,21 @@ public:
void AddDieCallback(const std::function<void()>& callback);
void Resurrect();
void AddLootItem(const Loot::Info& info);
void PickupItem(const LWOOBJID& objectID);
void AddLootItem(const Loot::Info& info) const;
void PickupItem(const LWOOBJID& objectID) const;
bool CanPickupCoins(uint64_t count);
void RegisterCoinDrop(uint64_t count);
bool PickupCoins(uint64_t count) const;
void RegisterCoinDrop(uint64_t count) const;
void ScheduleKillAfterUpdate(Entity* murderer = nullptr);
void TriggerEvent(eTriggerEventType event, Entity* optionalTarget = nullptr);
void TriggerEvent(eTriggerEventType event, Entity* optionalTarget = nullptr) const;
void ScheduleDestructionAfterUpdate() { m_ShouldDestroyAfterUpdate = true; }
const NiPoint3& GetRespawnPosition() const;
const NiQuaternion& GetRespawnRotation() const;
void Sleep();
void Wake();
void Sleep() const;
void Wake() const;
bool IsSleeping() const;
/*
@@ -266,7 +266,7 @@ public:
* Retroactively corrects the model vault size due to incorrect initialization in a previous patch.
*
*/
void RetroactiveVaultSize();
void RetroactiveVaultSize() const;
bool GetBoolean(const std::u16string& name) const;
int32_t GetI32(const std::u16string& name) const;
int64_t GetI64(const std::u16string& name) const;
@@ -334,7 +334,8 @@ public:
*/
static Observable<Entity*, const PositionUpdate&> OnPlayerPositionUpdate;
protected:
private:
void WriteLDFData(const std::vector<LDFBaseData*>& ldf, RakNet::BitStream& outBitStream) const;
LWOOBJID m_ObjectID;
LOT m_TemplateID;
@@ -357,7 +358,6 @@ protected:
Entity* m_ParentEntity; //For spawners and the like
std::vector<Entity*> m_ChildEntities;
eGameMasterLevel m_GMLevel;
uint16_t m_CollectibleID;
std::vector<std::string> m_Groups;
uint16_t m_NetworkID;
std::vector<std::function<void()>> m_DieCallbacks;
@@ -383,6 +383,8 @@ protected:
bool m_IsParentChildDirty = true;
bool m_IsSleeping = false;
/*
* Collision
*/
@@ -391,7 +393,7 @@ protected:
// objectID of receiver and map of notification name to script
std::map<LWOOBJID, std::map<std::string, CppScripts::Script*>> m_Subscriptions;
std::multimap<MessageType::Game, std::function<bool(GameMessages::GameMsg&)>> m_MsgHandlers;
std::unordered_multimap<MessageType::Game, std::function<bool(GameMessages::GameMsg&)>> m_MsgHandlers;
};
/**

View File

@@ -394,7 +394,7 @@ void EntityManager::ConstructAllEntities(const SystemAddress& sysAddr) {
}
}
UpdateGhosting(PlayerManager::GetPlayer(sysAddr));
UpdateGhosting(PlayerManager::GetPlayer(sysAddr), true);
}
void EntityManager::DestructEntity(Entity* entity, const SystemAddress& sysAddr) {
@@ -417,7 +417,7 @@ void EntityManager::DestructEntity(Entity* entity, const SystemAddress& sysAddr)
void EntityManager::SerializeEntity(Entity* entity) {
if (!entity) return;
EntityManager::SerializeEntity(*entity);
}
@@ -463,7 +463,7 @@ void EntityManager::UpdateGhosting() {
m_PlayersToUpdateGhosting.clear();
}
void EntityManager::UpdateGhosting(Entity* player) {
void EntityManager::UpdateGhosting(Entity* player, const bool constructAll) {
if (!player) return;
auto* missionComponent = player->GetComponent<MissionComponent>();
@@ -511,9 +511,12 @@ void EntityManager::UpdateGhosting(Entity* player) {
ghostComponent->ObserveEntity(id);
ConstructEntity(entity, player->GetSystemAddress());
entity->SetObservers(entity->GetObservers() + 1);
// TODO: figure out if zone control should be ghosted at all
if (constructAll && entity->GetObjectID() == GetZoneControlEntity()->GetObjectID()) continue;
ConstructEntity(entity, player->GetSystemAddress());
}
}
}
@@ -602,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

@@ -9,11 +9,15 @@
#include "dCommonVars.h"
class Entity;
class EntityInfo;
struct EntityInfo;
class Player;
class User;
enum class eReplicaComponentType : uint32_t;
namespace GameMessages {
struct GameMsg;
}
struct SystemAddress;
class EntityManager {
@@ -54,7 +58,7 @@ public:
void SetGhostDistanceMin(float value);
void QueueGhostUpdate(LWOOBJID playerID);
void UpdateGhosting();
void UpdateGhosting(Entity* player);
void UpdateGhosting(Entity* player, const bool constructAll = false);
void CheckGhosting(Entity* entity);
Entity* GetGhostCandidate(LWOOBJID id) const;
bool GetGhostingEnabled() const;
@@ -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

@@ -1777,7 +1777,7 @@ void InventoryComponent::LoadGroupXml(const tinyxml2::XMLElement& groups) {
group.groupId = groupId;
group.groupName = groupName;
for (const auto& lotStr : GeneralUtils::SplitString(lots, ' ')) {
for (const auto& lotStr : GeneralUtils::SplitString(std::string_view(lots), ' ')) {
auto lot = GeneralUtils::TryParse<LOT>(lotStr);
if (lot) group.lots.insert(*lot);
}

View File

@@ -37,6 +37,7 @@ bool ModelComponent::OnResetModelToDefaults(GameMessages::GameMsg& msg) {
m_Parent->SetRotation(m_OriginalRotation);
m_Parent->SetVelocity(NiPoint3Constant::ZERO);
m_Speed = 3.0f;
m_NumListeningInteract = 0;
m_NumActiveUnSmash = 0;
m_Dirty = true;
@@ -62,6 +63,12 @@ void ModelComponent::Update(float deltaTime) {
for (auto& behavior : m_Behaviors) {
behavior.Update(deltaTime, *this);
}
if (!m_RestartAtEndOfFrame) return;
GameMessages::ResetModelToDefaults reset{};
OnResetModelToDefaults(reset);
m_RestartAtEndOfFrame = false;
}
void ModelComponent::LoadBehaviors() {
@@ -264,7 +271,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;

View File

@@ -66,7 +66,7 @@ public:
*
* @tparam Msg The message type to pass
* @param args the arguments of the message to be deserialized
*
*
* @return returns true if a new behaviorID is needed.
*/
template<typename Msg>
@@ -145,6 +145,12 @@ public:
void SetVelocity(const NiPoint3& velocity) const;
void OnChatMessageReceived(const std::string& sMessage);
// Sets the speed of the model
void SetSpeed(const float newSpeed) { m_Speed = newSpeed; }
// Whether or not to restart at the end of the frame
void RestartAtEndOfFrame() { m_RestartAtEndOfFrame = true; }
private:
// Loads a behavior from the database.
@@ -185,4 +191,10 @@ private:
* The ID of the user that made the model
*/
LWOOBJID m_userModelID;
// The speed at which this model moves
float m_Speed{ 3.0f };
// Whether or not to restart at the end of the frame.
bool m_RestartAtEndOfFrame{ false };
};

View File

@@ -29,6 +29,13 @@ PhysicsComponent::PhysicsComponent(Entity* parent, int32_t componentId) : Compon
}
if (m_Parent->HasVar(u"CollisionGroupID")) m_CollisionGroup = m_Parent->GetVar<int32_t>(u"CollisionGroupID");
RegisterMsg(MessageType::Game::GET_POSITION, this, &PhysicsComponent::OnGetPosition);
}
bool PhysicsComponent::OnGetPosition(GameMessages::GameMsg& msg) {
static_cast<GameMessages::GetPosition&>(msg).pos = GetPosition();
return true;
}
void PhysicsComponent::Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) {

View File

@@ -20,7 +20,7 @@ public:
void Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) override;
const NiPoint3& GetPosition() const { return m_Position; }
const NiPoint3& GetPosition() const noexcept { return m_Position; }
virtual void SetPosition(const NiPoint3& pos) { if (m_Position == pos) return; m_Position = pos; m_DirtyPosition = true; }
const NiQuaternion& GetRotation() const { return m_Rotation; }
@@ -35,6 +35,8 @@ protected:
void SpawnVertices(dpEntity* entity) const;
bool OnGetPosition(GameMessages::GameMsg& msg);
NiPoint3 m_Position;
NiQuaternion m_Rotation;

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

@@ -5207,7 +5207,7 @@ void GameMessages::HandlePickupCurrency(RakNet::BitStream& inStream, Entity* ent
if (currency == 0) return;
auto* ch = entity->GetCharacter();
if (ch && entity->CanPickupCoins(currency)) {
if (ch && entity->PickupCoins(currency)) {
ch->SetCoins(ch->GetCoins() + currency, eLootSourceType::PICKUP);
}
}
@@ -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; }
@@ -843,5 +849,10 @@ namespace GameMessages {
LWOOBJID targetID;
};
struct GetPosition : public GameMsg {
GetPosition() : GameMsg(MessageType::Game::GET_POSITION) {}
NiPoint3 pos{};
};
};
#endif // GAMEMESSAGES_H

View File

@@ -21,6 +21,8 @@
#include "eUseItemResponse.h"
#include "dZoneManager.h"
#include "ChatPackets.h"
#include "MissionComponent.h"
#include "eMissionTaskType.h"
#include "CDBrickIDTableTable.h"
#include "CDObjectSkillsTable.h"
@@ -268,9 +270,9 @@ bool Item::IsEquipped() const {
}
bool Item::Consume() {
auto* skillsTable = CDClientManager::GetTable<CDObjectSkillsTable>();
auto* const skillsTable = CDClientManager::GetTable<CDObjectSkillsTable>();
auto skills = skillsTable->Query([this](const CDObjectSkills entry) {
const auto skills = skillsTable->Query([this](const CDObjectSkills& entry) {
return entry.objectTemplate == static_cast<uint32_t>(lot);
});
@@ -288,7 +290,12 @@ bool Item::Consume() {
GameMessages::SendUseItemResult(inventory->GetComponent()->GetParent(), lot, success);
if (success) {
// Save this because if this is the last item in the inventory
// we may delete ourself (lol)
const auto myLot = this->lot;
inventory->GetComponent()->RemoveItem(lot, 1);
auto* missionComponent = inventory->GetComponent()->GetParent()->GetComponent<MissionComponent>();
if (missionComponent) missionComponent->Progress(eMissionTaskType::GATHER, myLot, LWOOBJID_EMPTY, "", -1);
}
return success;

View File

@@ -8,6 +8,7 @@
#include "ChatPackets.h"
#include "PropertyManagementComponent.h"
#include "PlayerManager.h"
#include "SimplePhysicsComponent.h"
#include "dChatFilter.h"
@@ -154,16 +155,18 @@ void Strip::ProcNormalAction(float deltaTime, ModelComponent& modelComponent) {
if (nextActionType == "MoveRight" || nextActionType == "MoveLeft") {
// X axis
bool isMoveLeft = nextActionType == "MoveLeft";
int negative = isMoveLeft ? -1 : 1;
// Default velocity is 3 units per second.
if (modelComponent.TrySetVelocity(NiPoint3{ isMoveLeft ? -3.0f : 3.0f, 0.0f, 0.0f })) {
if (modelComponent.TrySetVelocity(NiPoint3Constant::UNIT_X * negative)) {
m_PreviousFramePosition = entity.GetPosition();
m_InActionMove.x = isMoveLeft ? -number : number;
}
} else if (nextActionType == "FlyUp" || nextActionType == "FlyDown") {
// Y axis
bool isFlyDown = nextActionType == "FlyDown";
int negative = isFlyDown ? -1 : 1;
// Default velocity is 3 units per second.
if (modelComponent.TrySetVelocity(NiPoint3{ 0.0f, isFlyDown ? -3.0f : 3.0f, 0.0f })) {
if (modelComponent.TrySetVelocity(NiPoint3Constant::UNIT_Y * negative)) {
m_PreviousFramePosition = entity.GetPosition();
m_InActionMove.y = isFlyDown ? -number : number;
}
@@ -171,14 +174,21 @@ void Strip::ProcNormalAction(float deltaTime, ModelComponent& modelComponent) {
} else if (nextActionType == "MoveForward" || nextActionType == "MoveBackward") {
// Z axis
bool isMoveBackward = nextActionType == "MoveBackward";
int negative = isMoveBackward ? -1 : 1;
// Default velocity is 3 units per second.
if (modelComponent.TrySetVelocity(NiPoint3{ 0.0f, 0.0f, isMoveBackward ? -3.0f : 3.0f })) {
if (modelComponent.TrySetVelocity(NiPoint3Constant::UNIT_Z * negative)) {
m_PreviousFramePosition = entity.GetPosition();
m_InActionMove.z = isMoveBackward ? -number : number;
}
}
/* END Move */
/* BEGIN Navigation */
else if (nextActionType == "SetSpeed") {
modelComponent.SetSpeed(number);
}
/* END Navigation */
/* BEGIN Action */
else if (nextActionType == "Smash") {
if (!modelComponent.IsUnSmashing()) {
@@ -211,6 +221,8 @@ void Strip::ProcNormalAction(float deltaTime, ModelComponent& modelComponent) {
sound.target = modelComponent.GetParent()->GetObjectID();
sound.soundID = numberAsInt;
sound.Send(UNASSIGNED_SYSTEM_ADDRESS);
} else if (nextActionType == "Restart") {
modelComponent.RestartAtEndOfFrame();
}
/* END Action */
/* BEGIN Gameplay */

View File

@@ -65,6 +65,8 @@ private:
// The position of the parent model on the previous frame
NiPoint3 m_PreviousFramePosition{};
NiPoint3 m_SavedVelocity{};
};
#endif //!__STRIP__H__

View File

@@ -24,9 +24,8 @@ namespace {
}
void Loot::CacheMatrix(uint32_t matrixIndex) {
if (CachedMatrices.find(matrixIndex) != CachedMatrices.end()) {
return;
}
if (CachedMatrices.contains(matrixIndex)) return;
CachedMatrices.insert(matrixIndex);
CDComponentsRegistryTable* componentsRegistryTable = CDClientManager::GetTable<CDComponentsRegistryTable>();
CDItemComponentTable* itemComponentTable = CDClientManager::GetTable<CDItemComponentTable>();
@@ -215,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); });
@@ -249,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>();
@@ -259,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>();
@@ -270,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) {
@@ -285,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

@@ -132,6 +132,7 @@ namespace Mail {
} else {
response.status = eSendResponse::SenderAccountIsMuted;
}
LOG("Finished send with status %s", StringifiedEnum::ToString(response.status).data());
response.Send(sysAddr);
}
@@ -158,6 +159,7 @@ namespace Mail {
DataResponse response;
response.playerMail = playerMail;
response.Send(sysAddr);
LOG("DataRequest");
}
void DataResponse::Serialize(RakNet::BitStream& bitStream) const {
@@ -196,6 +198,7 @@ namespace Mail {
response.status = eAttachmentCollectResponse::Success;
}
}
LOG("AttachmentCollectResponse %s", StringifiedEnum::ToString(response.status).data());
response.Send(sysAddr);
}
@@ -218,14 +221,15 @@ namespace Mail {
response.mailID = mailID;
auto mailData = Database::Get()->GetMail(mailID);
if (mailData && !(mailData->itemLOT != 0 && mailData->itemCount > 0)) {
if (mailData && !(mailData->itemLOT > 0 && mailData->itemCount > 0)) {
Database::Get()->DeleteMail(mailID);
response.status = eDeleteResponse::Success;
} else if (mailData && mailData->itemLOT != 0 && mailData->itemCount > 0) {
} else if (mailData && mailData->itemLOT > 0 && mailData->itemCount > 0) {
response.status = eDeleteResponse::HasAttachments;
} else {
response.status = eDeleteResponse::NotFound;
}
LOG("DeleteRequest status %s", StringifiedEnum::ToString(response.status).data());
response.Send(sysAddr);
}
@@ -249,7 +253,9 @@ namespace Mail {
if (Database::Get()->GetMail(mailID)) {
response.status = eReadResponse::Success;
Database::Get()->MarkMailRead(mailID);
}
}
LOG("ReadRequest %s", StringifiedEnum::ToString(response.status).data());
response.Send(sysAddr);
}
@@ -267,6 +273,8 @@ namespace Mail {
response.status = eNotificationResponse::NewMail;
response.mailCount = unreadMailCount;
}
LOG("NotificationRequest %s", StringifiedEnum::ToString(response.status).data());
response.Send(sysAddr);
}
}
@@ -288,6 +296,7 @@ void Mail::HandleMail(RakNet::BitStream& inStream, const SystemAddress& sysAddr,
LOG_DEBUG("Error Reading Mail Request: %s", StringifiedEnum::ToString(data.messageID).data());
return;
}
LOG("Received mail message %s", StringifiedEnum::ToString(data.messageID).data());
request->Handle();
} else {
LOG_DEBUG("Unhandled Mail Request with ID: %i", data.messageID);

View File

@@ -119,12 +119,15 @@ namespace Mail {
struct SendRequest : public MailLUBitStream {
MailInfo mailInfo;
SendRequest() : MailLUBitStream(eMessageID::SendRequest) {}
bool Deserialize(RakNet::BitStream& bitStream) override;
void Handle() override;
};
struct SendResponse :public MailLUBitStream {
eSendResponse status = eSendResponse::UnknownError;
SendResponse() : MailLUBitStream(eMessageID::SendResponse) {}
void Serialize(RakNet::BitStream& bitStream) const override;
};
@@ -137,6 +140,7 @@ namespace Mail {
};
struct DataRequest : public MailLUBitStream {
DataRequest() : MailLUBitStream(eMessageID::DataRequest) {}
bool Deserialize(RakNet::BitStream& bitStream) override { return true; };
void Handle() override;
};

View File

@@ -58,6 +58,7 @@ bool MailInfo::Deserialize(RakNet::BitStream& bitStream) {
bitStream.IgnoreBytes(4); // padding
DluAssert(bitStream.GetNumberOfUnreadBits() == 0);
LOG_DEBUG("MailInfo: %llu, %s, %s, %s, %llu, %i, %llu, %i, %llu, %i", id, subject.GetAsString().c_str(), body.GetAsString().c_str(), recipientName.GetAsString().c_str(), itemID, itemLOT, itemSubkey, itemCount, timeSent, wasRead);
return true;
}

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 */