Merge branch 'main' into main

This commit is contained in:
Aaron Kimbrell 2022-05-24 19:00:52 -05:00 committed by GitHub
commit e3422ac0c5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
169 changed files with 3164 additions and 1954 deletions

View File

@ -18,6 +18,14 @@ body:
- label: > - label: >
I have pulled the latest version of the main branch of DarkflameServer and have confirmed that the issue exists there. I have pulled the latest version of the main branch of DarkflameServer and have confirmed that the issue exists there.
required: true required: true
- type: input
id: server-version
attributes:
label: DarkflameServer Version
description: >
DarkflameServer version or commit SHA (can be obtained with `git rev-parse --short HEAD`)
validations:
required: true
- type: textarea - type: textarea
id: problem id: problem
attributes: attributes:
@ -29,7 +37,7 @@ body:
- type: textarea - type: textarea
id: reproduction id: reproduction
attributes: attributes:
label: Reproduction steps label: Reproduction Steps
description: > description: >
Please provide a concise list of steps needed to reproduce this issue. Please provide a concise list of steps needed to reproduce this issue.
validations: validations:

View File

@ -12,6 +12,14 @@ body:
- label: > - label: >
I have read the [installation guide](https://github.com/DarkflameUniverse/DarkflameServer/blob/main/README.md). I have read the [installation guide](https://github.com/DarkflameUniverse/DarkflameServer/blob/main/README.md).
required: true required: true
- type: input
id: server-version
attributes:
label: DarkflameServer Version
description: >
DarkflameServer version or commit SHA (can be obtained with `git rev-parse --short HEAD`)
validations:
required: true
- type: dropdown - type: dropdown
id: platform id: platform
attributes: attributes:

View File

@ -15,6 +15,22 @@ body:
- label: > - label: >
I have pulled the latest version of the main branch of DarkflameServer and have confirmed that the issue exists there. I have pulled the latest version of the main branch of DarkflameServer and have confirmed that the issue exists there.
required: true required: true
- type: input
id: server-version
attributes:
label: DarkflameServer Version
description: >
DarkflameServer version or commit SHA (can be obtained with `git rev-parse --short HEAD`)
validations:
required: true
- type: textarea
id: environment
attributes:
label: Environment
description: >
Please include the environment you're running DarkflameServer on (for example: Windows, macOS, Ubuntu, WSL, etc), available memory, number of CPU cores.
validations:
required: true
- type: textarea - type: textarea
id: example id: example
attributes: attributes:

View File

@ -53,7 +53,7 @@ FetchContent_Declare(
FetchContent_Declare( FetchContent_Declare(
zlib zlib
URL http://github.com/madler/zlib/archive/refs/tags/v1.2.11.zip URL https://github.com/madler/zlib/archive/refs/tags/v1.2.11.zip
URL_HASH MD5=9d6a627693163bbbf3f26403a3a0b0b1 URL_HASH MD5=9d6a627693163bbbf3f26403a3a0b0b1
) )

View File

@ -1,6 +1,6 @@
PROJECT_VERSION_MAJOR=1 PROJECT_VERSION_MAJOR=1
PROJECT_VERSION_MINOR=0 PROJECT_VERSION_MINOR=0
PROJECT_VERSION_PATCH=0 PROJECT_VERSION_PATCH=2
# LICENSE # LICENSE
LICENSE=AGPL-3.0 LICENSE=AGPL-3.0
# The network version. # The network version.

View File

@ -245,7 +245,7 @@ The client script for the survival minigame has a bug in it which can cause the
* Change `PlayerReady(self)` to `onPlayerReady(self)` * Change `PlayerReady(self)` to `onPlayerReady(self)`
* Save the file, overriding readonly mode if required * Save the file, overriding readonly mode if required
If you still experience the bug, try deleting/renaming `res/pack/scripts.pak`. If you still experience the bug, try deleting/renaming `res/pack/scripts.pk`.
### Brick-By-Brick building ### Brick-By-Brick building

View File

@ -57,7 +57,7 @@ int main(int argc, char** argv) {
Database::Connect(mysql_host, mysql_database, mysql_username, mysql_password); Database::Connect(mysql_host, mysql_database, mysql_username, mysql_password);
} catch (sql::SQLException& ex) { } catch (sql::SQLException& ex) {
Game::logger->Log("AuthServer", "Got an error while connecting to the database: %s\n", ex.what()); Game::logger->Log("AuthServer", "Got an error while connecting to the database: %s\n", ex.what());
Database::Destroy(); Database::Destroy("AuthServer");
delete Game::server; delete Game::server;
delete Game::logger; delete Game::logger;
return 0; return 0;
@ -143,11 +143,12 @@ int main(int argc, char** argv) {
} }
//Delete our objects here: //Delete our objects here:
Database::Destroy(); Database::Destroy("AuthServer");
delete Game::server; delete Game::server;
delete Game::logger; delete Game::logger;
return 0; exit(EXIT_SUCCESS);
return EXIT_SUCCESS;
} }
dLogger * SetupLogger() { dLogger * SetupLogger() {

View File

@ -61,7 +61,7 @@ int main(int argc, char** argv) {
} }
catch (sql::SQLException& ex) { catch (sql::SQLException& ex) {
Game::logger->Log("ChatServer", "Got an error while connecting to the database: %s\n", ex.what()); Game::logger->Log("ChatServer", "Got an error while connecting to the database: %s\n", ex.what());
Database::Destroy(); Database::Destroy("ChatServer");
delete Game::server; delete Game::server;
delete Game::logger; delete Game::logger;
return 0; return 0;
@ -150,11 +150,12 @@ int main(int argc, char** argv) {
} }
//Delete our objects here: //Delete our objects here:
Database::Destroy(); Database::Destroy("ChatServer");
delete Game::server; delete Game::server;
delete Game::logger; delete Game::logger;
return 0; exit(EXIT_SUCCESS);
return EXIT_SUCCESS;
} }
dLogger * SetupLogger() { dLogger * SetupLogger() {

View File

@ -469,6 +469,9 @@ enum eRebuildState : uint32_t {
REBUILD_INCOMPLETE REBUILD_INCOMPLETE
}; };
/**
* The loot source's type.
*/
enum eLootSourceType : int32_t { enum eLootSourceType : int32_t {
LOOT_SOURCE_NONE = 0, LOOT_SOURCE_NONE = 0,
LOOT_SOURCE_CHEST, LOOT_SOURCE_CHEST,
@ -490,7 +493,7 @@ enum eLootSourceType : int32_t {
LOOT_SOURCE_CLAIMCODE, LOOT_SOURCE_CLAIMCODE,
LOOT_SOURCE_CONSUMPTION, LOOT_SOURCE_CONSUMPTION,
LOOT_SOURCE_CRAFTING, LOOT_SOURCE_CRAFTING,
LOOT_SOURCE_LEVELREWARD, LOOT_SOURCE_LEVEL_REWARD,
LOOT_SOURCE_RELOCATE LOOT_SOURCE_RELOCATE
}; };

View File

@ -26,9 +26,10 @@ void Database::Connect(const string& host, const string& database, const string&
con->setClientOption("MYSQL_OPT_RECONNECT", &myTrue); con->setClientOption("MYSQL_OPT_RECONNECT", &myTrue);
} //Connect } //Connect
void Database::Destroy() { void Database::Destroy(std::string source) {
if (!con) return; if (!con) return;
Game::logger->Log("Database", "Destroying MySQL connection!\n"); if (source != "") Game::logger->Log("Database", "Destroying MySQL connection from %s!\n", source.c_str());
else Game::logger->Log("Database", "Destroying MySQL connection!\n");
con->close(); con->close();
delete con; delete con;
} //Destroy } //Destroy

View File

@ -22,7 +22,7 @@ private:
public: public:
static void Connect(const std::string& host, const std::string& database, const std::string& username, const std::string& password); static void Connect(const std::string& host, const std::string& database, const std::string& username, const std::string& password);
static void Destroy(); static void Destroy(std::string source="");
static sql::Statement* CreateStmt(); static sql::Statement* CreateStmt();
static sql::PreparedStatement* CreatePreppedStmt(const std::string& query); static sql::PreparedStatement* CreatePreppedStmt(const std::string& query);
}; };

View File

@ -3,6 +3,7 @@
// Custom Classes // Custom Classes
#include "CDTable.h" #include "CDTable.h"
#include <map> #include <map>
#include <cstdint>
/*! /*!
\file CDMissionsTable.hpp \file CDMissionsTable.hpp
@ -17,9 +18,9 @@ struct CDMissions {
int UISortOrder; //!< The UI Sort Order for the mission int UISortOrder; //!< The UI Sort Order for the mission
int offer_objectID; //!< The LOT of the mission giver int offer_objectID; //!< The LOT of the mission giver
int target_objectID; //!< The LOT of the mission's target int target_objectID; //!< The LOT of the mission's target
__int64 reward_currency; //!< The amount of currency to reward the player int64_t reward_currency; //!< The amount of currency to reward the player
int LegoScore; //!< The amount of LEGO Score to reward the player int LegoScore; //!< The amount of LEGO Score to reward the player
__int64 reward_reputation; //!< The reputation to award the player int64_t reward_reputation; //!< The reputation to award the player
bool isChoiceReward; //!< Whether or not the user has the option to choose their loot bool isChoiceReward; //!< Whether or not the user has the option to choose their loot
int reward_item1; //!< The first rewarded item int reward_item1; //!< The first rewarded item
int reward_item1_count; //!< The count of the first item to be rewarded int reward_item1_count; //!< The count of the first item to be rewarded
@ -40,7 +41,7 @@ struct CDMissions {
int reward_maxwidget; //!< ??? int reward_maxwidget; //!< ???
int reward_maxwallet; //!< ??? int reward_maxwallet; //!< ???
bool repeatable; //!< Whether or not this mission can be repeated (for instance, is it a daily mission) bool repeatable; //!< Whether or not this mission can be repeated (for instance, is it a daily mission)
__int64 reward_currency_repeatable; //!< The repeatable reward int64_t reward_currency_repeatable; //!< The repeatable reward
int reward_item1_repeatable; //!< The first rewarded item int reward_item1_repeatable; //!< The first rewarded item
int reward_item1_repeat_count; //!< The count of the first item to be rewarded int reward_item1_repeat_count; //!< The count of the first item to be rewarded
int reward_item2_repeatable; //!< The second rewarded item int reward_item2_repeatable; //!< The second rewarded item
@ -55,7 +56,7 @@ struct CDMissions {
std::string prereqMissionID; //!< A '|' seperated list of prerequisite missions std::string prereqMissionID; //!< A '|' seperated list of prerequisite missions
bool localize; //!< Whether or not to localize the mission bool localize; //!< Whether or not to localize the mission
bool inMOTD; //!< In Match of the Day(?) bool inMOTD; //!< In Match of the Day(?)
__int64 cooldownTime; //!< The mission cooldown time int64_t cooldownTime; //!< The mission cooldown time
bool isRandom; //!< ??? bool isRandom; //!< ???
std::string randomPool; //!< ??? std::string randomPool; //!< ???
int UIPrereqID; //!< ??? int UIPrereqID; //!< ???

View File

@ -534,7 +534,7 @@ void Character::OnZoneLoad()
*/ */
if (HasPermission(PermissionMap::Old)) { if (HasPermission(PermissionMap::Old)) {
if (GetCoins() > 1000000) { if (GetCoins() > 1000000) {
SetCoins(1000000, LOOT_SOURCE_NONE); SetCoins(1000000, eLootSourceType::LOOT_SOURCE_NONE);
} }
} }

View File

@ -22,6 +22,7 @@
#include "Component.h" #include "Component.h"
#include "ControllablePhysicsComponent.h" #include "ControllablePhysicsComponent.h"
#include "RenderComponent.h" #include "RenderComponent.h"
#include "RocketLaunchLupComponent.h"
#include "CharacterComponent.h" #include "CharacterComponent.h"
#include "DestroyableComponent.h" #include "DestroyableComponent.h"
#include "BuffComponent.h" #include "BuffComponent.h"
@ -231,7 +232,8 @@ void Entity::Initialize()
m_Components.insert(std::make_pair(COMPONENT_TYPE_RACING_STATS, nullptr)); m_Components.insert(std::make_pair(COMPONENT_TYPE_RACING_STATS, nullptr));
} }
if (compRegistryTable->GetByIDAndType(m_TemplateID, COMPONENT_TYPE_ITEM) > 0) { PetComponent* petComponent;
if (compRegistryTable->GetByIDAndType(m_TemplateID, COMPONENT_TYPE_ITEM) > 0 && !TryGetComponent(COMPONENT_TYPE_PET, petComponent)) {
m_Components.insert(std::make_pair(COMPONENT_TYPE_ITEM, nullptr)); m_Components.insert(std::make_pair(COMPONENT_TYPE_ITEM, nullptr));
} }
@ -346,7 +348,7 @@ void Entity::Initialize()
} }
/** /**
* Multiple components require te destructible component. * Multiple components require the destructible component.
*/ */
int buffComponentID = compRegistryTable->GetByIDAndType(m_TemplateID, COMPONENT_TYPE_BUFF); int buffComponentID = compRegistryTable->GetByIDAndType(m_TemplateID, COMPONENT_TYPE_BUFF);
@ -455,9 +457,10 @@ void Entity::Initialize()
else comp = new InventoryComponent(this); else comp = new InventoryComponent(this);
m_Components.insert(std::make_pair(COMPONENT_TYPE_INVENTORY, comp)); m_Components.insert(std::make_pair(COMPONENT_TYPE_INVENTORY, comp));
} }
// if this component exists, then we initialize it. it's value is always 0
if (compRegistryTable->GetByIDAndType(m_TemplateID, COMPONENT_TYPE_ROCKET_LAUNCH_LUP) > 0) { if (compRegistryTable->GetByIDAndType(m_TemplateID, COMPONENT_TYPE_ROCKET_LAUNCH_LUP, -1) != -1) {
m_Components.insert(std::make_pair(COMPONENT_TYPE_ROCKET_LAUNCH_LUP, nullptr)); auto comp = new RocketLaunchLupComponent(this);
m_Components.insert(std::make_pair(COMPONENT_TYPE_ROCKET_LAUNCH_LUP, comp));
} }
/** /**
@ -494,13 +497,6 @@ void Entity::Initialize()
std::string customScriptServer; std::string customScriptServer;
bool hasCustomServerScript = false; bool hasCustomServerScript = false;
// Custom script for the LUP teleporter
if (m_TemplateID == 14333)
{
hasCustomServerScript = true;
customScriptServer = "scripts\\02_server\\DLU\\L_SB_LUP_TELEPORT.lua";
}
const auto customScriptServerName = GetVarAsString(u"custom_script_server"); const auto customScriptServerName = GetVarAsString(u"custom_script_server");
const auto customScriptClientName = GetVarAsString(u"custom_script_client"); const auto customScriptClientName = GetVarAsString(u"custom_script_client");
@ -1636,7 +1632,7 @@ void Entity::PickupItem(const LWOOBJID& objectID) {
} }
} }
else { else {
inv->AddItem(p.second.lot, p.second.count, INVALID, {}, LWOOBJID_EMPTY, true, false, LWOOBJID_EMPTY, INVALID, 1); inv->AddItem(p.second.lot, p.second.count, eLootSourceType::LOOT_SOURCE_PICKUP, eInventoryType::INVALID, {}, LWOOBJID_EMPTY, true, false, LWOOBJID_EMPTY, eInventoryType::INVALID, 1);
} }
} }
} }
@ -2172,3 +2168,15 @@ void Entity::AddToGroup(const std::string& group) {
m_Groups.push_back(group); m_Groups.push_back(group);
} }
} }
void Entity::RetroactiveVaultSize() {
auto inventoryComponent = GetComponent<InventoryComponent>();
if (!inventoryComponent) return;
auto itemsVault = inventoryComponent->GetInventory(eInventoryType::VAULT_ITEMS);
auto modelVault = inventoryComponent->GetInventory(eInventoryType::VAULT_MODELS);
if (itemsVault->GetSize() == modelVault->GetSize()) return;
modelVault->SetSize(itemsVault->GetSize());
}

View File

@ -220,7 +220,11 @@ public:
/* /*
* Utility * Utility
*/ */
/**
* Retroactively corrects the model vault size due to incorrect initialization in a previous patch.
*
*/
void RetroactiveVaultSize();
bool GetBoolean(const std::u16string& name) const; bool GetBoolean(const std::u16string& name) const;
int32_t GetI32(const std::u16string& name) const; int32_t GetI32(const std::u16string& name) const;
int64_t GetI64(const std::u16string& name) const; int64_t GetI64(const std::u16string& name) const;

View File

@ -27,6 +27,7 @@ std::vector<LWOMAPID> EntityManager::m_GhostingExcludedZones = {
// Racing zones // Racing zones
1203, 1203,
1261,
1303, 1303,
1403, 1403,
@ -45,7 +46,7 @@ std::vector<LOT> EntityManager::m_GhostingExcludedLOTs = {
9524, 9524,
12408, 12408,
// AG - Fotrace // AG - Footrace
4967 4967
}; };

View File

@ -60,7 +60,7 @@ void Trade::SetCoins(LWOOBJID participant, uint64_t coins)
{ {
m_CoinsA = coins; m_CoinsA = coins;
} }
else if (participant = m_ParticipantB) else if (participant == m_ParticipantB)
{ {
m_CoinsB = coins; m_CoinsB = coins;
} }
@ -72,7 +72,7 @@ void Trade::SetItems(LWOOBJID participant, std::vector<TradeItem> items)
{ {
m_ItemsA = items; m_ItemsA = items;
} }
else if (participant = m_ParticipantB) else if (participant == m_ParticipantB)
{ {
m_ItemsB = items; m_ItemsB = items;
} }
@ -151,8 +151,8 @@ void Trade::Complete()
if (inventoryA == nullptr || inventoryB == nullptr || characterA == nullptr || characterB == nullptr || missionsA == nullptr || missionsB == nullptr) return; if (inventoryA == nullptr || inventoryB == nullptr || characterA == nullptr || characterB == nullptr || missionsA == nullptr || missionsB == nullptr) return;
characterA->SetCoins(characterA->GetCoins() - m_CoinsA + m_CoinsB, LOOT_SOURCE_TRADE); characterA->SetCoins(characterA->GetCoins() - m_CoinsA + m_CoinsB, eLootSourceType::LOOT_SOURCE_TRADE);
characterB->SetCoins(characterB->GetCoins() - m_CoinsB + m_CoinsA, LOOT_SOURCE_TRADE); characterB->SetCoins(characterB->GetCoins() - m_CoinsB + m_CoinsA, eLootSourceType::LOOT_SOURCE_TRADE);
for (const auto& tradeItem : m_ItemsA) for (const auto& tradeItem : m_ItemsA)
{ {
@ -170,12 +170,12 @@ void Trade::Complete()
for (const auto& tradeItem : m_ItemsA) for (const auto& tradeItem : m_ItemsA)
{ {
inventoryB->AddItem(tradeItem.itemLot, tradeItem.itemCount); inventoryB->AddItem(tradeItem.itemLot, tradeItem.itemCount, eLootSourceType::LOOT_SOURCE_TRADE);
} }
for (const auto& tradeItem : m_ItemsB) for (const auto& tradeItem : m_ItemsB)
{ {
inventoryA->AddItem(tradeItem.itemLot, tradeItem.itemCount); inventoryA->AddItem(tradeItem.itemLot, tradeItem.itemCount, eLootSourceType::LOOT_SOURCE_TRADE);
} }
TradingManager::Instance()->CancelTrade(m_TradeId); TradingManager::Instance()->CancelTrade(m_TradeId);

View File

@ -6,6 +6,7 @@
#include "Database.h" #include "Database.h"
#include "Game.h" #include "Game.h"
#include "dLogger.h"
#include "User.h" #include "User.h"
#include <WorldPackets.h> #include <WorldPackets.h>
#include "Character.h" #include "Character.h"
@ -68,16 +69,6 @@ void UserManager::Initialize() {
StripCR(line); StripCR(line);
m_PreapprovedNames.push_back(line); m_PreapprovedNames.push_back(line);
} }
//Load custom ones from MySQL too:
/*sql::PreparedStatement* stmt = Database::CreatePreppedStmt("SELECT name FROM approvedNames;");
sql::ResultSet* res = stmt->executeQuery();
while (res->next()) {
m_PreapprovedNames.push_back(res->getString(1));
}
delete res;
delete stmt;*/
} }
UserManager::~UserManager() { UserManager::~UserManager() {
@ -244,7 +235,6 @@ void UserManager::CreateCharacter(const SystemAddress& sysAddr, Packet* packet)
uint32_t middleNameIndex = PacketUtils::ReadPacketU32(78, packet); uint32_t middleNameIndex = PacketUtils::ReadPacketU32(78, packet);
uint32_t lastNameIndex = PacketUtils::ReadPacketU32(82, packet); uint32_t lastNameIndex = PacketUtils::ReadPacketU32(82, packet);
std::string predefinedName = GetPredefinedName(firstNameIndex, middleNameIndex, lastNameIndex); std::string predefinedName = GetPredefinedName(firstNameIndex, middleNameIndex, lastNameIndex);
Game::logger->Log("UserManager", "Got predefined name: %s\n", predefinedName.c_str());
uint32_t shirtColor = PacketUtils::ReadPacketU32(95, packet); uint32_t shirtColor = PacketUtils::ReadPacketU32(95, packet);
uint32_t shirtStyle = PacketUtils::ReadPacketU32(99, packet); uint32_t shirtStyle = PacketUtils::ReadPacketU32(99, packet);
@ -261,16 +251,23 @@ void UserManager::CreateCharacter(const SystemAddress& sysAddr, Packet* packet)
LOT pantsLOT = FindCharPantsID(pantsColor); LOT pantsLOT = FindCharPantsID(pantsColor);
if (name != "" && !UserManager::IsNameAvailable(name)) { if (name != "" && !UserManager::IsNameAvailable(name)) {
Game::logger->Log("UserManager", "AccountID: %i chose unavailable name: %s\n", u->GetAccountID(), name.c_str());
WorldPackets::SendCharacterCreationResponse(sysAddr, CREATION_RESPONSE_CUSTOM_NAME_IN_USE); WorldPackets::SendCharacterCreationResponse(sysAddr, CREATION_RESPONSE_CUSTOM_NAME_IN_USE);
return; return;
} }
if (!IsNameAvailable(predefinedName)) { if (!IsNameAvailable(predefinedName)) {
Game::logger->Log("UserManager", "AccountID: %i chose unavailable predefined name: %s\n", u->GetAccountID(), predefinedName.c_str());
WorldPackets::SendCharacterCreationResponse(sysAddr, CREATION_RESPONSE_PREDEFINED_NAME_IN_USE); WorldPackets::SendCharacterCreationResponse(sysAddr, CREATION_RESPONSE_PREDEFINED_NAME_IN_USE);
return; return;
} }
Game::logger->Log("UserManager", "AccountID: %i is creating a character with name: %s\n", u->GetAccountID(), name.c_str()); if (name == "") {
Game::logger->Log("UserManager", "AccountID: %i is creating a character with predefined name: %s\n", u->GetAccountID(), predefinedName.c_str());
}
else {
Game::logger->Log("UserManager", "AccountID: %i is creating a character with name: %s (temporary: %s)\n", u->GetAccountID(), name.c_str(), predefinedName.c_str());
}
//Now that the name is ok, we can get an objectID from Master: //Now that the name is ok, we can get an objectID from Master:
ObjectIDManager::Instance()->RequestPersistentID([=](uint32_t objectID) { ObjectIDManager::Instance()->RequestPersistentID([=](uint32_t objectID) {
@ -294,7 +291,7 @@ void UserManager::CreateCharacter(const SystemAddress& sysAddr, Packet* packet)
xml << "ls=\"0\" lzx=\"-626.5847\" lzy=\"613.3515\" lzz=\"-28.6374\" lzrx=\"0.0\" lzry=\"0.7015\" lzrz=\"0.0\" lzrw=\"0.7126\" "; xml << "ls=\"0\" lzx=\"-626.5847\" lzy=\"613.3515\" lzz=\"-28.6374\" lzrx=\"0.0\" lzry=\"0.7015\" lzrz=\"0.0\" lzrw=\"0.7126\" ";
xml << "stt=\"0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;\"></char>"; xml << "stt=\"0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;\"></char>";
xml << "<dest hm=\"4\" hc=\"4\" im=\"0\" ic=\"0\" am=\"0\" ac=\"0\" d=\"0\"/>"; xml << "<dest hm=\"4\" hc=\"4\" im=\"0\" ic=\"0\" am=\"0\" ac=\"0\" d=\"0\"/>";
xml << "<inv><bag><b t=\"0\" m=\"20\"/><b t=\"1\" m=\"240\"/><b t=\"2\" m=\"240\"/><b t=\"3\" m=\"240\"/></bag><items><in t=\"0\">"; xml << "<inv><bag><b t=\"0\" m=\"20\"/><b t=\"1\" m=\"40\"/><b t=\"2\" m=\"240\"/><b t=\"3\" m=\"240\"/><b t=\"14\" m=\"40\"/></bag><items><in t=\"0\">";
std::string xmlSave1 = xml.str(); std::string xmlSave1 = xml.str();
ObjectIDManager::Instance()->RequestPersistentID([=](uint32_t idforshirt) { ObjectIDManager::Instance()->RequestPersistentID([=](uint32_t idforshirt) {
@ -566,122 +563,38 @@ void UserManager::LoginCharacter(const SystemAddress& sysAddr, uint32_t playerID
} }
} }
uint32_t GetShirtColorId(uint32_t color) {
// get the index of the color in shirtColorVector
auto colorId = std::find(shirtColorVector.begin(), shirtColorVector.end(), color);
return color = std::distance(shirtColorVector.begin(), colorId);
}
uint32_t FindCharShirtID(uint32_t shirtColor, uint32_t shirtStyle) { uint32_t FindCharShirtID(uint32_t shirtColor, uint32_t shirtStyle) {
try {
shirtStyle--; // to start at 0 instead of 1 std::string shirtQuery = "select obj.id from Objects as obj JOIN (select * from ComponentsRegistry as cr JOIN ItemComponent as ic on ic.id = cr.component_id where cr.component_type == 11) as icc on icc.id = obj.id where lower(obj._internalNotes) == \"character create shirt\" AND icc.color1 == ";
uint32_t stylesCount = 34; shirtQuery += std::to_string(shirtColor);
uint32_t colorId = GetShirtColorId(shirtColor); shirtQuery += " AND icc.decal == ";
shirtQuery = shirtQuery + std::to_string(shirtStyle);
uint32_t startID = 4049; // item ID of the shirt with color 0 (red) and style 0 (plain) auto tableData = CDClientDatabase::ExecuteQuery(shirtQuery);
auto shirtLOT = tableData.getIntField(0, -1);
// For some reason, if the shirt style is 34 - 39, tableData.finalize();
// The ID is different than the original... Was this because return shirtLOT;
// these shirts were added later? }
if (shirtStyle >= 34) { catch (const std::exception&){
startID = 5730; // item ID of the shirt with color 0 (red) and style 34 (butterflies) Game::logger->Log("Character Create", "Failed to execute query! Using backup...");
shirtStyle -= stylesCount; //change style from range 35-40 to range 0-5 // in case of no shirt found in CDServer, return problematic red vest.
stylesCount = 6; return 4069;
} }
// Get the final ID of the shirt
uint32_t shirtID = startID + (colorId * stylesCount) + shirtStyle;
return shirtID;
} }
uint32_t FindCharPantsID(uint32_t pantsColor) { uint32_t FindCharPantsID(uint32_t pantsColor) {
uint32_t pantsID = 2508; try {
std::string pantsQuery = "select obj.id from Objects as obj JOIN (select * from ComponentsRegistry as cr JOIN ItemComponent as ic on ic.id = cr.component_id where cr.component_type == 11) as icc on icc.id = obj.id where lower(obj._internalNotes) == \"cc pants\" AND icc.color1 == ";
switch (pantsColor) { pantsQuery += std::to_string(pantsColor);
case 0: { auto tableData = CDClientDatabase::ExecuteQuery(pantsQuery);
pantsID = PANTS_BRIGHT_RED; auto pantsLOT = tableData.getIntField(0, -1);
break; tableData.finalize();
return pantsLOT;
} }
catch (const std::exception&){
case 1: { Game::logger->Log("Character Create", "Failed to execute query! Using backup...");
pantsID = PANTS_BRIGHT_BLUE; // in case of no pants color found in CDServer, return red pants.
break; return 2508;
} }
case 3: {
pantsID = PANTS_DARK_GREEN;
break;
}
case 5: {
pantsID = PANTS_BRIGHT_ORANGE;
break;
}
case 6: {
pantsID = PANTS_BLACK;
break;
}
case 7: {
pantsID = PANTS_DARK_STONE_GRAY;
break;
}
case 8: {
pantsID = PANTS_MEDIUM_STONE_GRAY;
break;
}
case 9: {
pantsID = PANTS_REDDISH_BROWN;
break;
}
case 10: {
pantsID = PANTS_WHITE;
break;
}
case 11: {
pantsID = PANTS_MEDIUM_BLUE;
break;
}
case 13: {
pantsID = PANTS_DARK_RED;
break;
}
case 14: {
pantsID = PANTS_EARTH_BLUE;
break;
}
case 15: {
pantsID = PANTS_EARTH_GREEN;
break;
}
case 16: {
pantsID = PANTS_BRICK_YELLOW;
break;
}
case 84: {
pantsID = PANTS_SAND_BLUE;
break;
}
case 96: {
pantsID = PANTS_SAND_GREEN;
break;
}
}
return pantsID;
} }
void UserManager::SaveAllActiveCharacters() { void UserManager::SaveAllActiveCharacters() {

View File

@ -44,7 +44,6 @@ public:
private: private:
static UserManager* m_Address; //Singleton static UserManager* m_Address; //Singleton
//std::vector<User*> m_Users;
std::map<SystemAddress, User*> m_Users; std::map<SystemAddress, User*> m_Users;
std::vector<User*> m_UsersToDelete; std::vector<User*> m_UsersToDelete;
@ -54,43 +53,4 @@ private:
std::vector<std::string> m_PreapprovedNames; std::vector<std::string> m_PreapprovedNames;
}; };
enum CharCreatePantsColor : uint32_t {
PANTS_BRIGHT_RED = 2508,
PANTS_BRIGHT_ORANGE = 2509,
PANTS_BRICK_YELLOW = 2511,
PANTS_MEDIUM_BLUE = 2513,
PANTS_SAND_GREEN = 2514,
PANTS_DARK_GREEN = 2515,
PANTS_EARTH_GREEN = 2516,
PANTS_EARTH_BLUE = 2517,
PANTS_BRIGHT_BLUE = 2519,
PANTS_SAND_BLUE = 2520,
PANTS_DARK_STONE_GRAY = 2521,
PANTS_MEDIUM_STONE_GRAY = 2522,
PANTS_WHITE = 2523,
PANTS_BLACK = 2524,
PANTS_REDDISH_BROWN = 2526,
PANTS_DARK_RED = 2527
};
const std::vector<uint32_t> shirtColorVector {
0, // BRIGHT_RED
1, // BRIGHT_BLUE
2, // BRIGHT_YELLOW
3, // DARK_GREEN
5, // BRIGHT_ORANGE
6, // BLACK
7, // DARK_STONE_GRAY
8, // MEDIUM_STONE_GRAY
9, // REDDISH_BROWN
10, // WHITE
11, // MEDIUM_BLUE
13, // DARK_RED
14, // EARTH_BLUE
15, // EARTH_GREEN
16, // BRICK_YELLOW
84, // SAND_BLUE
96 // SAND_GREEN
};
#endif // USERMANAGER_H #endif // USERMANAGER_H

View File

@ -19,6 +19,12 @@ void AndBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bitStre
} }
} }
void AndBehavior::UnCast(BehaviorContext* context, const BehaviorBranchContext branch) {
for (auto behavior : this->m_behaviors) {
behavior->UnCast(context, branch);
}
}
void AndBehavior::Load() void AndBehavior::Load()
{ {
const auto parameters = GetParameterNames(); const auto parameters = GetParameterNames();

View File

@ -20,5 +20,7 @@ public:
void Calculate(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) override; void Calculate(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) override;
void UnCast(BehaviorContext* context, BehaviorBranchContext branch) override;
void Load() override; void Load() override;
}; };

View File

@ -14,7 +14,7 @@ void BasicAttackBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bi
auto* destroyableComponent = entity->GetComponent<DestroyableComponent>(); auto* destroyableComponent = entity->GetComponent<DestroyableComponent>();
if (destroyableComponent != nullptr) { if (destroyableComponent != nullptr) {
PlayFx(u"onhit", entity->GetObjectID()); PlayFx(u"onhit", entity->GetObjectID());
destroyableComponent->Damage(this->m_maxDamage, context->originator); destroyableComponent->Damage(this->m_maxDamage, context->originator, context->skillID);
} }
this->m_onSuccess->Handle(context, bitStream, branch); this->m_onSuccess->Handle(context, bitStream, branch);
@ -56,7 +56,7 @@ void BasicAttackBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bi
auto* destroyableComponent = entity->GetComponent<DestroyableComponent>(); auto* destroyableComponent = entity->GetComponent<DestroyableComponent>();
if (destroyableComponent != nullptr) { if (destroyableComponent != nullptr) {
PlayFx(u"onhit", entity->GetObjectID()); PlayFx(u"onhit", entity->GetObjectID());
destroyableComponent->Damage(damageDealt, context->originator); destroyableComponent->Damage(damageDealt, context->originator, context->skillID);
} }
} }
} }
@ -113,7 +113,7 @@ void BasicAttackBehavior::Calculate(BehaviorContext* context, RakNet::BitStream*
auto* destroyableComponent = entity->GetComponent<DestroyableComponent>(); auto* destroyableComponent = entity->GetComponent<DestroyableComponent>();
if (damage != 0 && destroyableComponent != nullptr) { if (damage != 0 && destroyableComponent != nullptr) {
PlayFx(u"onhit", entity->GetObjectID(), 1); PlayFx(u"onhit", entity->GetObjectID(), 1);
destroyableComponent->Damage(damage, context->originator, false); destroyableComponent->Damage(damage, context->originator, context->skillID, false);
context->ScheduleUpdate(branch.target); context->ScheduleUpdate(branch.target);
} }
} }

View File

@ -18,6 +18,7 @@
#include "AreaOfEffectBehavior.h" #include "AreaOfEffectBehavior.h"
#include "DurationBehavior.h" #include "DurationBehavior.h"
#include "TacArcBehavior.h" #include "TacArcBehavior.h"
#include "LootBuffBehavior.h"
#include "AttackDelayBehavior.h" #include "AttackDelayBehavior.h"
#include "BasicAttackBehavior.h" #include "BasicAttackBehavior.h"
#include "ChainBehavior.h" #include "ChainBehavior.h"
@ -172,7 +173,9 @@ Behavior* Behavior::CreateBehavior(const uint32_t behaviorId)
behavior = new SpeedBehavior(behaviorId); behavior = new SpeedBehavior(behaviorId);
break; break;
case BehaviorTemplates::BEHAVIOR_DARK_INSPIRATION: break; case BehaviorTemplates::BEHAVIOR_DARK_INSPIRATION: break;
case BehaviorTemplates::BEHAVIOR_LOOT_BUFF: break; case BehaviorTemplates::BEHAVIOR_LOOT_BUFF:
behavior = new LootBuffBehavior(behaviorId);
break;
case BehaviorTemplates::BEHAVIOR_VENTURE_VISION: break; case BehaviorTemplates::BEHAVIOR_VENTURE_VISION: break;
case BehaviorTemplates::BEHAVIOR_SPAWN_OBJECT: case BehaviorTemplates::BEHAVIOR_SPAWN_OBJECT:
behavior = new SpawnBehavior(behaviorId); behavior = new SpawnBehavior(behaviorId);

View File

@ -64,8 +64,8 @@ void BehaviorContext::RegisterSyncBehavior(const uint32_t syncId, Behavior* beha
void BehaviorContext::RegisterTimerBehavior(Behavior* behavior, const BehaviorBranchContext& branchContext, const LWOOBJID second) void BehaviorContext::RegisterTimerBehavior(Behavior* behavior, const BehaviorBranchContext& branchContext, const LWOOBJID second)
{ {
BehaviorTimerEntry entry BehaviorTimerEntry entry;
;
entry.time = branchContext.duration; entry.time = branchContext.duration;
entry.behavior = behavior; entry.behavior = behavior;
entry.branchContext = branchContext; entry.branchContext = branchContext;

View File

@ -58,6 +58,8 @@ struct BehaviorContext
float skillTime = 0; float skillTime = 0;
uint32_t skillID = 0;
uint32_t skillUId = 0; uint32_t skillUId = 0;
bool failed = false; bool failed = false;

View File

@ -0,0 +1,38 @@
#include "LootBuffBehavior.h"
void LootBuffBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) {
auto target = EntityManager::Instance()->GetEntity(context->caster);
if (!target) return;
auto controllablePhysicsComponent = target->GetComponent<ControllablePhysicsComponent>();
if (!controllablePhysicsComponent) return;
controllablePhysicsComponent->AddPickupRadiusScale(m_Scale);
EntityManager::Instance()->SerializeEntity(target);
if (branch.duration > 0) context->RegisterTimerBehavior(this, branch);
}
void LootBuffBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) {
Handle(context, bitStream, branch);
}
void LootBuffBehavior::UnCast(BehaviorContext* context, BehaviorBranchContext branch) {
auto target = EntityManager::Instance()->GetEntity(context->caster);
if (!target) return;
auto controllablePhysicsComponent = target->GetComponent<ControllablePhysicsComponent>();
if (!controllablePhysicsComponent) return;
controllablePhysicsComponent->RemovePickupRadiusScale(m_Scale);
EntityManager::Instance()->SerializeEntity(target);
}
void LootBuffBehavior::Timer(BehaviorContext* context, BehaviorBranchContext branch, LWOOBJID second) {
UnCast(context, branch);
}
void LootBuffBehavior::Load() {
this->m_Scale = GetFloat("scale");
}

View File

@ -0,0 +1,32 @@
#pragma once
#include "Behavior.h"
#include "BehaviorBranchContext.h"
#include "BehaviorContext.h"
#include "ControllablePhysicsComponent.h"
/**
* @brief This is the behavior class to be used for all Loot Buff behavior nodes in the Behavior tree.
*
*/
class LootBuffBehavior final : public Behavior
{
public:
float m_Scale;
/*
* Inherited
*/
explicit LootBuffBehavior(const uint32_t behaviorId) : Behavior(behaviorId) {}
void Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) override;
void Calculate(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) override;
void UnCast(BehaviorContext* context, BehaviorBranchContext branch) override;
void Timer(BehaviorContext* context, BehaviorBranchContext branch, LWOOBJID second) override;
void Load() override;
};

View File

@ -7,62 +7,26 @@
#include "SkillComponent.h" #include "SkillComponent.h"
#include "DestroyableComponent.h" #include "DestroyableComponent.h"
/**
* The OverTime behavior is very inconsistent in how it appears in the skill tree vs. how it should behave.
*
* Items like "Doc in a Box" use an overtime behavior which you would expect have health & armor regen, but is only fallowed by a stun.
*
* Due to this inconsistency, we have to implement a special case for some items.
*/
void OverTimeBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) void OverTimeBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch)
{ {
const auto originator = context->originator; const auto originator = context->originator;
auto* entity = EntityManager::Instance()->GetEntity(originator); auto* entity = EntityManager::Instance()->GetEntity(originator);
if (entity == nullptr) if (entity == nullptr) return;
{
return;
}
for (size_t i = 0; i < m_NumIntervals; i++) for (size_t i = 0; i < m_NumIntervals; i++)
{ {
entity->AddCallbackTimer((i + 1) * m_Delay, [originator, branch, this]() { entity->AddCallbackTimer((i + 1) * m_Delay, [originator, branch, this]() {
auto* entity = EntityManager::Instance()->GetEntity(originator); auto* entity = EntityManager::Instance()->GetEntity(originator);
if (entity == nullptr) if (entity == nullptr) return;
{
return;
}
auto* skillComponent = entity->GetComponent<SkillComponent>(); auto* skillComponent = entity->GetComponent<SkillComponent>();
if (skillComponent == nullptr) if (skillComponent == nullptr) return;
{
return;
}
skillComponent->CalculateBehavior(0, m_Action->m_behaviorId, branch.target, true, true); skillComponent->CalculateBehavior(m_Action, m_ActionBehaviorId, branch.target, true, true);
auto* destroyableComponent = entity->GetComponent<DestroyableComponent>();
if (destroyableComponent == nullptr)
{
return;
}
/**
* Special cases for inconsistent behavior.
*/
switch (m_behaviorId)
{
case 26253: // "Doc in a Box", heal up to 6 health and regen up to 18 armor.
destroyableComponent->Heal(1);
destroyableComponent->Repair(3);
break;
}
}); });
} }
} }
@ -74,7 +38,12 @@ void OverTimeBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bi
void OverTimeBehavior::Load() void OverTimeBehavior::Load()
{ {
m_Action = GetAction("action"); m_Action = GetInt("action");
// Since m_Action is a skillID and not a behavior, get is correlated behaviorID.
CDSkillBehaviorTable* skillTable = CDClientManager::Instance()->GetTable<CDSkillBehaviorTable>("SkillBehavior");
m_ActionBehaviorId = skillTable->GetSkillByID(m_Action).behaviorID;
m_Delay = GetFloat("delay"); m_Delay = GetFloat("delay");
m_NumIntervals = GetInt("num_intervals"); m_NumIntervals = GetInt("num_intervals");
} }

View File

@ -4,7 +4,8 @@
class OverTimeBehavior final : public Behavior class OverTimeBehavior final : public Behavior
{ {
public: public:
Behavior* m_Action; uint32_t m_Action;
uint32_t m_ActionBehaviorId;
float m_Delay; float m_Delay;
int32_t m_NumIntervals; int32_t m_NumIntervals;

View File

@ -191,7 +191,7 @@ void BaseCombatAIComponent::Update(const float deltaTime) {
return; return;
} }
if (m_Stunned || m_SkillTime > 0) { if (m_Stunned) {
m_MovementAI->Stop(); m_MovementAI->Stop();
return; return;
@ -357,7 +357,7 @@ void BaseCombatAIComponent::CalculateCombat(const float deltaTime) {
return; return;
} }
m_Downtime = 0.5f; // TODO: find out if this is necessary m_Downtime = 0.5f;
auto* target = GetTargetEntity(); auto* target = GetTargetEntity();

View File

@ -51,7 +51,7 @@ public:
void LoadFromXML(tinyxml2::XMLDocument* doc); void LoadFromXML(tinyxml2::XMLDocument* doc);
void UpdateXml(tinyxml2::XMLDocument* doc); void UpdateXml(tinyxml2::XMLDocument* doc) override;
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags); void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags);

View File

@ -12,6 +12,8 @@
#include "EntityManager.h" #include "EntityManager.h"
#include "PossessorComponent.h" #include "PossessorComponent.h"
#include "VehiclePhysicsComponent.h" #include "VehiclePhysicsComponent.h"
#include "GameMessages.h"
#include "Item.h"
CharacterComponent::CharacterComponent(Entity* parent, Character* character) : Component(parent) { CharacterComponent::CharacterComponent(Entity* parent, Character* character) : Component(parent) {
m_Character = character; m_Character = character;
@ -31,6 +33,7 @@ CharacterComponent::CharacterComponent(Entity* parent, Character* character) : C
m_EditorEnabled = false; m_EditorEnabled = false;
m_EditorLevel = m_GMLevel; m_EditorLevel = m_GMLevel;
m_Reputation = 0;
m_CurrentActivity = 0; m_CurrentActivity = 0;
m_CountryCode = 0; m_CountryCode = 0;
@ -191,6 +194,7 @@ void CharacterComponent::HandleLevelUp()
auto* rewardsTable = CDClientManager::Instance()->GetTable<CDRewardsTable>("Rewards"); auto* rewardsTable = CDClientManager::Instance()->GetTable<CDRewardsTable>("Rewards");
const auto& rewards = rewardsTable->GetByLevelID(m_Level); const auto& rewards = rewardsTable->GetByLevelID(m_Level);
bool rewardingItem = rewards.size() > 0;
auto* parent = m_Character->GetEntity(); auto* parent = m_Character->GetEntity();
@ -206,37 +210,34 @@ void CharacterComponent::HandleLevelUp()
{ {
return; return;
} }
// Tell the client we beginning to send level rewards.
if(rewardingItem) GameMessages::NotifyLevelRewards(parent->GetObjectID(), parent->GetSystemAddress(), m_Level, rewardingItem);
for (auto* reward : rewards) for (auto* reward : rewards)
{ {
switch (reward->rewardType) switch (reward->rewardType)
{ {
case 0: case 0:
inventoryComponent->AddItem(reward->value, reward->count); inventoryComponent->AddItem(reward->value, reward->count, eLootSourceType::LOOT_SOURCE_LEVEL_REWARD);
break; break;
case 4: case 4:
{ {
auto* items = inventoryComponent->GetInventory(ITEMS); auto* items = inventoryComponent->GetInventory(eInventoryType::ITEMS);
items->SetSize(items->GetSize() + reward->value); items->SetSize(items->GetSize() + reward->value);
} }
break; break;
case 9: case 9:
controllablePhysicsComponent->SetSpeedMultiplier(static_cast<float>(reward->value) / 500.0f); controllablePhysicsComponent->SetSpeedMultiplier(static_cast<float>(reward->value) / 500.0f);
break; break;
case 11: case 11:
break;
case 12: case 12:
break; break;
default: default:
break; break;
} }
} }
// Tell the client we have finished sending level rewards.
if(rewardingItem) GameMessages::NotifyLevelRewards(parent->GetObjectID(), parent->GetSystemAddress(), m_Level, !rewardingItem);
} }
void CharacterComponent::SetGMLevel(int gmlevel) { void CharacterComponent::SetGMLevel(int gmlevel) {
@ -257,6 +258,9 @@ void CharacterComponent::LoadFromXML() {
Game::logger->Log("CharacterComponent", "Failed to find char tag while loading XML!\n"); Game::logger->Log("CharacterComponent", "Failed to find char tag while loading XML!\n");
return; return;
} }
if (character->QueryAttribute("rpt", &m_Reputation) == tinyxml2::XML_NO_ATTRIBUTE) {
SetReputation(0);
}
character->QueryInt64Attribute("ls", &m_Uscore); character->QueryInt64Attribute("ls", &m_Uscore);
@ -379,6 +383,8 @@ void CharacterComponent::UpdateXml(tinyxml2::XMLDocument* doc) {
} }
character->SetAttribute("ls", m_Uscore); character->SetAttribute("ls", m_Uscore);
// Custom attribute to keep track of reputation.
character->SetAttribute("rpt", GetReputation());
character->SetAttribute("stt", StatisticsToString().c_str()); character->SetAttribute("stt", StatisticsToString().c_str());
// Set the zone statistics of the form <zs><s/> ... <s/></zs> // Set the zone statistics of the form <zs><s/> ... <s/></zs>
@ -443,6 +449,56 @@ void CharacterComponent::SetLastRocketConfig(std::u16string config) {
m_LastRocketConfig = config; m_LastRocketConfig = config;
} }
Item* CharacterComponent::GetRocket(Entity* player) {
Item* rocket = nullptr;
auto* inventoryComponent = player->GetComponent<InventoryComponent>();
if (!inventoryComponent) return rocket;
// Select the rocket
if (!rocket){
rocket = inventoryComponent->FindItemById(GetLastRocketItemID());
}
if (!rocket) {
rocket = inventoryComponent->FindItemByLot(6416);
}
if (!rocket) {
Game::logger->Log("CharacterComponent", "Unable to find rocket to equip!\n");
return rocket;
}
return rocket;
}
Item* CharacterComponent::RocketEquip(Entity* player) {
Item* rocket = GetRocket(player);
if (!rocket) return rocket;
// build and define the rocket config
for (LDFBaseData* data : rocket->GetConfig()) {
if (data->GetKey() == u"assemblyPartLOTs") {
std::string newRocketStr = data->GetValueAsString() + ";";
GeneralUtils::ReplaceInString(newRocketStr, "+", ";");
SetLastRocketConfig(GeneralUtils::ASCIIToUTF16(newRocketStr));
}
}
// Store the last used rocket item's ID
SetLastRocketItemID(rocket->GetId());
// carry the rocket
rocket->Equip(true);
return rocket;
}
void CharacterComponent::RocketUnEquip(Entity* player) {
Item* rocket = GetRocket(player);
if (!rocket) return;
// We don't want to carry it anymore
rocket->UnEquip();
}
void CharacterComponent::TrackMissionCompletion(bool isAchievement) { void CharacterComponent::TrackMissionCompletion(bool isAchievement) {
UpdatePlayerStatistic(MissionsCompleted); UpdatePlayerStatistic(MissionsCompleted);

View File

@ -5,6 +5,7 @@
#include "RakNetTypes.h" #include "RakNetTypes.h"
#include "Character.h" #include "Character.h"
#include "Component.h" #include "Component.h"
#include "Item.h"
#include <string> #include <string>
#include "CDMissionsTable.h" #include "CDMissionsTable.h"
#include "tinyxml2.h" #include "tinyxml2.h"
@ -74,6 +75,26 @@ public:
*/ */
void SetLastRocketConfig(std::u16string config); void SetLastRocketConfig(std::u16string config);
/**
* Find a player's rocket
* @param player the entity that triggered the event
* @return rocket
*/
Item* GetRocket(Entity* player);
/**
* Equip a player's rocket
* @param player the entity that triggered the event
* @return rocket
*/
Item* RocketEquip(Entity* player);
/**
* Find a player's rocket and unequip it
* @param player the entity that triggered the event
*/
void RocketUnEquip(Entity* player);
/** /**
* Gets the current level of the entity * Gets the current level of the entity
* @return the current level of the entity * @return the current level of the entity
@ -146,6 +167,18 @@ public:
*/ */
bool GetPvpEnabled() const; bool GetPvpEnabled() const;
/**
* Returns the characters lifetime reputation
* @return The lifetime reputation of this character.
*/
int64_t GetReputation() { return m_Reputation; };
/**
* Sets the lifetime reputation of the character to newValue
* @param newValue the value to set reputation to
*/
void SetReputation(int64_t newValue) { m_Reputation = newValue; };
/** /**
* Sets the current value of PvP combat being enabled * Sets the current value of PvP combat being enabled
* @param value whether to enable PvP combat * @param value whether to enable PvP combat
@ -291,6 +324,11 @@ private:
*/ */
int64_t m_Uscore; int64_t m_Uscore;
/**
* The lifetime reputation earned by the entity
*/
int64_t m_Reputation;
/** /**
* Whether the character is landing by rocket * Whether the character is landing by rocket
*/ */

View File

@ -29,6 +29,8 @@ ControllablePhysicsComponent::ControllablePhysicsComponent(Entity* entity) : Com
m_GravityScale = 1; m_GravityScale = 1;
m_DirtyCheats = false; m_DirtyCheats = false;
m_IgnoreMultipliers = false; m_IgnoreMultipliers = false;
m_PickupRadius = 0.0f;
m_DirtyPickupRadiusScale = true;
if (entity->GetLOT() != 1) // Other physics entities we care about will be added by BaseCombatAI if (entity->GetLOT() != 1) // Other physics entities we care about will be added by BaseCombatAI
return; return;
@ -85,7 +87,13 @@ void ControllablePhysicsComponent::Serialize(RakNet::BitStream* outBitStream, bo
m_DirtyCheats = false; m_DirtyCheats = false;
} }
outBitStream->Write0(); outBitStream->Write(m_DirtyPickupRadiusScale);
if (m_DirtyPickupRadiusScale) {
outBitStream->Write(m_PickupRadius);
outBitStream->Write0(); //No clue what this is so im leaving it false.
m_DirtyPickupRadiusScale = false;
}
outBitStream->Write0(); outBitStream->Write0();
outBitStream->Write(m_DirtyPosition || bIsInitialUpdate); outBitStream->Write(m_DirtyPosition || bIsInitialUpdate);
@ -231,3 +239,31 @@ void ControllablePhysicsComponent::SetDirtyVelocity(bool val) {
void ControllablePhysicsComponent::SetDirtyAngularVelocity(bool val) { void ControllablePhysicsComponent::SetDirtyAngularVelocity(bool val) {
m_DirtyAngularVelocity = val; m_DirtyAngularVelocity = val;
} }
void ControllablePhysicsComponent::AddPickupRadiusScale(float value) {
m_ActivePickupRadiusScales.push_back(value);
if (value > m_PickupRadius) {
m_PickupRadius = value;
m_DirtyPickupRadiusScale = true;
}
}
void ControllablePhysicsComponent::RemovePickupRadiusScale(float value) {
// Attempt to remove pickup radius from active radii
const auto pos = std::find(m_ActivePickupRadiusScales.begin(), m_ActivePickupRadiusScales.end(), value);
if (pos != m_ActivePickupRadiusScales.end()) {
m_ActivePickupRadiusScales.erase(pos);
} else {
Game::logger->Log("ControllablePhysicsComponent", "Warning: Could not find pickup radius %f in list of active radii. List has %i active radii.\n", value, m_ActivePickupRadiusScales.size());
return;
}
// Recalculate pickup radius since we removed one by now
m_PickupRadius = 0.0f;
m_DirtyPickupRadiusScale = true;
for (uint32_t i = 0; i < m_ActivePickupRadiusScales.size(); i++) {
auto candidateRadius = m_ActivePickupRadiusScales[i];
if (m_PickupRadius < candidateRadius) m_PickupRadius = candidateRadius;
}
EntityManager::Instance()->SerializeEntity(m_Parent);
}

View File

@ -23,7 +23,7 @@ public:
ControllablePhysicsComponent(Entity* entity); ControllablePhysicsComponent(Entity* entity);
~ControllablePhysicsComponent() override; ~ControllablePhysicsComponent() override;
void Update(float deltaTime); void Update(float deltaTime) override;
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags); void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags);
void LoadFromXML(tinyxml2::XMLDocument* doc); void LoadFromXML(tinyxml2::XMLDocument* doc);
void ResetFlags(); void ResetFlags();
@ -227,6 +227,24 @@ public:
dpEntity* GetdpEntity() const { return m_dpEntity; } dpEntity* GetdpEntity() const { return m_dpEntity; }
/**
* I store this in a vector because if I have 2 separate pickup radii being applied to the player, I dont know which one is correctly active.
* This method adds the pickup radius to the vector of active radii and if its larger than the current one, is applied as the new pickup radius.
*/
void AddPickupRadiusScale(float value) ;
/**
* Removes the provided pickup radius scale from our list of buffs
* The recalculates what our pickup radius is.
*/
void RemovePickupRadiusScale(float value) ;
/**
* The pickup radii of this component.
* @return All active radii scales for this component.
*/
std::vector<float> GetActivePickupRadiusScales() { return m_ActivePickupRadiusScales; };
private: private:
/** /**
* The entity that owns this component * The entity that owns this component
@ -322,6 +340,21 @@ private:
* Whether this entity is static, making it unable to move * Whether this entity is static, making it unable to move
*/ */
bool m_Static; bool m_Static;
/**
* Whether the pickup scale is dirty.
*/
bool m_DirtyPickupRadiusScale;
/**
* The list of pickup radius scales for this entity
*/
std::vector<float> m_ActivePickupRadiusScales;
/**
* The active pickup radius for this entity
*/
float m_PickupRadius;
}; };
#endif // CONTROLLABLEPHYSICSCOMPONENT_H #endif // CONTROLLABLEPHYSICSCOMPONENT_H

View File

@ -26,6 +26,7 @@
#include "MissionComponent.h" #include "MissionComponent.h"
#include "CharacterComponent.h" #include "CharacterComponent.h"
#include "dZoneManager.h"
DestroyableComponent::DestroyableComponent(Entity* parent) : Component(parent) { DestroyableComponent::DestroyableComponent(Entity* parent) : Component(parent) {
m_iArmor = 0; m_iArmor = 0;
@ -214,6 +215,8 @@ void DestroyableComponent::SetHealth(int32_t value) {
void DestroyableComponent::SetMaxHealth(float value, bool playAnim) { void DestroyableComponent::SetMaxHealth(float value, bool playAnim) {
m_DirtyHealth = true; m_DirtyHealth = true;
// Used for playAnim if opted in for.
int32_t difference = static_cast<int32_t>(std::abs(m_fMaxHealth - value));
m_fMaxHealth = value; m_fMaxHealth = value;
if (m_iHealth > m_fMaxHealth) { if (m_iHealth > m_fMaxHealth) {
@ -224,27 +227,28 @@ void DestroyableComponent::SetMaxHealth(float value, bool playAnim) {
// Now update the player bar // Now update the player bar
if (!m_Parent->GetParentUser()) return; if (!m_Parent->GetParentUser()) return;
AMFStringValue* amount = new AMFStringValue(); AMFStringValue* amount = new AMFStringValue();
amount->SetStringValue(std::to_string(value)); amount->SetStringValue(std::to_string(difference));
AMFStringValue* type = new AMFStringValue(); AMFStringValue* type = new AMFStringValue();
type->SetStringValue("health"); type->SetStringValue("health");
AMFArrayValue args; AMFArrayValue args;
args.InsertValue("amount", amount); args.InsertValue("amount", amount);
args.InsertValue("type", type); args.InsertValue("type", type);
GameMessages::SendUIMessageServerToSingleClient(m_Parent, m_Parent->GetParentUser()->GetSystemAddress(), "MaxPlayerBarUpdate", &args); GameMessages::SendUIMessageServerToSingleClient(m_Parent, m_Parent->GetParentUser()->GetSystemAddress(), "MaxPlayerBarUpdate", &args);
delete amount; delete amount;
delete type; delete type;
} }
else {
EntityManager::Instance()->SerializeEntity(m_Parent); EntityManager::Instance()->SerializeEntity(m_Parent);
} }
}
void DestroyableComponent::SetArmor(int32_t value) { void DestroyableComponent::SetArmor(int32_t value) {
m_DirtyHealth = true; m_DirtyHealth = true;
// If Destroyable Component already has zero armor do not trigger the passive ability again.
bool hadArmor = m_iArmor > 0;
auto* characterComponent = m_Parent->GetComponent<CharacterComponent>(); auto* characterComponent = m_Parent->GetComponent<CharacterComponent>();
if (characterComponent != nullptr) { if (characterComponent != nullptr) {
characterComponent->TrackArmorDelta(value - m_iArmor); characterComponent->TrackArmorDelta(value - m_iArmor);
@ -253,7 +257,7 @@ void DestroyableComponent::SetArmor(int32_t value) {
m_iArmor = value; m_iArmor = value;
auto* inventroyComponent = m_Parent->GetComponent<InventoryComponent>(); auto* inventroyComponent = m_Parent->GetComponent<InventoryComponent>();
if (m_iArmor == 0 && inventroyComponent != nullptr) { if (m_iArmor == 0 && inventroyComponent != nullptr && hadArmor) {
inventroyComponent->TriggerPassiveAbility(PassiveAbilityTrigger::SentinelArmor); inventroyComponent->TriggerPassiveAbility(PassiveAbilityTrigger::SentinelArmor);
} }
} }
@ -283,10 +287,9 @@ void DestroyableComponent::SetMaxArmor(float value, bool playAnim) {
delete amount; delete amount;
delete type; delete type;
} }
else {
EntityManager::Instance()->SerializeEntity(m_Parent); EntityManager::Instance()->SerializeEntity(m_Parent);
} }
}
void DestroyableComponent::SetImagination(int32_t value) { void DestroyableComponent::SetImagination(int32_t value) {
m_DirtyHealth = true; m_DirtyHealth = true;
@ -306,6 +309,8 @@ void DestroyableComponent::SetImagination(int32_t value) {
void DestroyableComponent::SetMaxImagination(float value, bool playAnim) { void DestroyableComponent::SetMaxImagination(float value, bool playAnim) {
m_DirtyHealth = true; m_DirtyHealth = true;
// Used for playAnim if opted in for.
int32_t difference = static_cast<int32_t>(std::abs(m_fMaxImagination - value));
m_fMaxImagination = value; m_fMaxImagination = value;
if (m_iImagination > m_fMaxImagination) { if (m_iImagination > m_fMaxImagination) {
@ -316,23 +321,20 @@ void DestroyableComponent::SetMaxImagination(float value, bool playAnim) {
// Now update the player bar // Now update the player bar
if (!m_Parent->GetParentUser()) return; if (!m_Parent->GetParentUser()) return;
AMFStringValue* amount = new AMFStringValue(); AMFStringValue* amount = new AMFStringValue();
amount->SetStringValue(std::to_string(value)); amount->SetStringValue(std::to_string(difference));
AMFStringValue* type = new AMFStringValue(); AMFStringValue* type = new AMFStringValue();
type->SetStringValue("imagination"); type->SetStringValue("imagination");
AMFArrayValue args; AMFArrayValue args;
args.InsertValue("amount", amount); args.InsertValue("amount", amount);
args.InsertValue("type", type); args.InsertValue("type", type);
GameMessages::SendUIMessageServerToSingleClient(m_Parent, m_Parent->GetParentUser()->GetSystemAddress(), "MaxPlayerBarUpdate", &args); GameMessages::SendUIMessageServerToSingleClient(m_Parent, m_Parent->GetParentUser()->GetSystemAddress(), "MaxPlayerBarUpdate", &args);
delete amount; delete amount;
delete type; delete type;
} }
else {
EntityManager::Instance()->SerializeEntity(m_Parent); EntityManager::Instance()->SerializeEntity(m_Parent);
} }
}
void DestroyableComponent::SetDamageToAbsorb(int32_t value) void DestroyableComponent::SetDamageToAbsorb(int32_t value)
{ {
@ -591,7 +593,7 @@ void DestroyableComponent::Repair(const uint32_t armor)
} }
void DestroyableComponent::Damage(uint32_t damage, const LWOOBJID source, bool echo) void DestroyableComponent::Damage(uint32_t damage, const LWOOBJID source, uint32_t skillID, bool echo)
{ {
if (GetHealth() <= 0) if (GetHealth() <= 0)
{ {
@ -675,11 +677,10 @@ void DestroyableComponent::Damage(uint32_t damage, const LWOOBJID source, bool e
return; return;
} }
Smash(source, eKillType::VIOLENT, u"", skillID);
Smash(source);
} }
void DestroyableComponent::Smash(const LWOOBJID source, const eKillType killType, const std::u16string& deathType) void DestroyableComponent::Smash(const LWOOBJID source, const eKillType killType, const std::u16string& deathType, uint32_t skillID)
{ {
if (m_iHealth > 0) if (m_iHealth > 0)
{ {
@ -725,31 +726,20 @@ void DestroyableComponent::Smash(const LWOOBJID source, const eKillType killType
if (memberMissions == nullptr) continue; if (memberMissions == nullptr) continue;
memberMissions->Progress(MissionTaskType::MISSION_TASK_TYPE_SMASH, m_Parent->GetLOT()); memberMissions->Progress(MissionTaskType::MISSION_TASK_TYPE_SMASH, m_Parent->GetLOT());
memberMissions->Progress(MissionTaskType::MISSION_TASK_TYPE_SKILL, m_Parent->GetLOT(), skillID);
} }
} }
else else
{ {
missions->Progress(MissionTaskType::MISSION_TASK_TYPE_SMASH, m_Parent->GetLOT()); missions->Progress(MissionTaskType::MISSION_TASK_TYPE_SMASH, m_Parent->GetLOT());
missions->Progress(MissionTaskType::MISSION_TASK_TYPE_SKILL, m_Parent->GetLOT(), skillID);
} }
} }
} }
const auto isPlayer = m_Parent->IsPlayer(); const auto isPlayer = m_Parent->IsPlayer();
GameMessages::SendDie( GameMessages::SendDie(m_Parent, source, source, true, killType, deathType, 0, 0, 0, isPlayer, false, 1);
m_Parent,
source,
source,
true,
killType,
deathType,
0,
0,
0,
isPlayer,
false,
1
);
//NANI?! //NANI?!
if (!isPlayer) if (!isPlayer)
@ -792,9 +782,11 @@ void DestroyableComponent::Smash(const LWOOBJID source, const eKillType killType
} }
} }
else else
{
//Check if this zone allows coin drops
if (dZoneManager::Instance()->GetPlayerLoseCoinOnDeath())
{ {
auto* character = m_Parent->GetCharacter(); auto* character = m_Parent->GetCharacter();
uint64_t coinsTotal = character->GetCoins(); uint64_t coinsTotal = character->GetCoins();
if (coinsTotal > 0) if (coinsTotal > 0)
@ -815,9 +807,9 @@ void DestroyableComponent::Smash(const LWOOBJID source, const eKillType killType
coinsTotal -= coinsToLoose; coinsTotal -= coinsToLoose;
LootGenerator::Instance().DropLoot(m_Parent, m_Parent, -1, coinsToLoose, coinsToLoose); LootGenerator::Instance().DropLoot(m_Parent, m_Parent, -1, coinsToLoose, coinsToLoose);
character->SetCoins(coinsTotal, eLootSourceType::LOOT_SOURCE_PICKUP);
}
} }
character->SetCoins(coinsTotal, LOOT_SOURCE_PICKUP);
Entity* zoneControl = EntityManager::Instance()->GetZoneControlEntity(); Entity* zoneControl = EntityManager::Instance()->GetZoneControlEntity();
for (CppScripts::Script* script : CppScripts::GetEntityScripts(zoneControl)) { for (CppScripts::Script* script : CppScripts::GetEntityScripts(zoneControl)) {

View File

@ -377,17 +377,19 @@ public:
* Attempt to damage this entity, handles everything from health and armor to absorption, immunity and callbacks. * Attempt to damage this entity, handles everything from health and armor to absorption, immunity and callbacks.
* @param damage the damage to attempt to apply * @param damage the damage to attempt to apply
* @param source the attacker that caused this damage * @param source the attacker that caused this damage
* @param skillID the skill that damaged this entity
* @param echo whether or not to serialize the damage * @param echo whether or not to serialize the damage
*/ */
void Damage(uint32_t damage, LWOOBJID source, bool echo = true); void Damage(uint32_t damage, LWOOBJID source, uint32_t skillID = 0, bool echo = true);
/** /**
* Smashes this entity, notifying all clients * Smashes this entity, notifying all clients
* @param source the source that smashed this entity * @param source the source that smashed this entity
* @param skillID the skill that killed this entity
* @param killType the way this entity was killed, determines if a client animation is played * @param killType the way this entity was killed, determines if a client animation is played
* @param deathType the animation to play when killed * @param deathType the animation to play when killed
*/ */
void Smash(LWOOBJID source, eKillType killType = eKillType::VIOLENT, const std::u16string& deathType = u""); void Smash(LWOOBJID source, eKillType killType = eKillType::VIOLENT, const std::u16string& deathType = u"", uint32_t skillID = 0);
/** /**
* Pushes a layer of immunity to this entity, making it immune for longer * Pushes a layer of immunity to this entity, making it immune for longer

View File

@ -1,4 +1,4 @@
#include "InventoryComponent.h" #include "InventoryComponent.h"
#include <sstream> #include <sstream>
@ -23,6 +23,7 @@
#include "CharacterComponent.h" #include "CharacterComponent.h"
#include "dZoneManager.h" #include "dZoneManager.h"
#include "PropertyManagementComponent.h" #include "PropertyManagementComponent.h"
#include "DestroyableComponent.h"
InventoryComponent::InventoryComponent(Entity* parent, tinyxml2::XMLDocument* document) : Component(parent) InventoryComponent::InventoryComponent(Entity* parent, tinyxml2::XMLDocument* document) : Component(parent)
{ {
@ -83,10 +84,13 @@ Inventory* InventoryComponent::GetInventory(const eInventoryType type)
case eInventoryType::ITEMS: case eInventoryType::ITEMS:
size = 20u; size = 20u;
break; break;
case eInventoryType::VAULT_MODELS:
case eInventoryType::VAULT_ITEMS: case eInventoryType::VAULT_ITEMS:
size = 40u; size = 40u;
break; break;
case eInventoryType::VENDOR_BUYBACK:
size = 27u;
break;
default: default:
break; break;
} }
@ -137,6 +141,7 @@ const EquipmentMap& InventoryComponent::GetEquippedItems() const
void InventoryComponent::AddItem( void InventoryComponent::AddItem(
const LOT lot, const LOT lot,
const uint32_t count, const uint32_t count,
eLootSourceType lootSourceType,
eInventoryType inventoryType, eInventoryType inventoryType,
const std::vector<LDFBaseData*>& config, const std::vector<LDFBaseData*>& config,
const LWOOBJID parent, const LWOOBJID parent,
@ -176,7 +181,7 @@ void InventoryComponent::AddItem(
if (!config.empty() || bound) if (!config.empty() || bound)
{ {
const auto slot = inventory->FindEmptySlot(); const auto slot = preferredSlot != -1 && inventory->IsSlotEmpty(preferredSlot) ? preferredSlot : inventory->FindEmptySlot();
if (slot == -1) if (slot == -1)
{ {
@ -185,7 +190,7 @@ void InventoryComponent::AddItem(
return; return;
} }
auto* item = new Item(lot, inventory, slot, count, config, parent, showFlyingLoot, isModMoveAndEquip, subKey, bound); auto* item = new Item(lot, inventory, slot, count, config, parent, showFlyingLoot, isModMoveAndEquip, subKey, bound, lootSourceType);
if (missions != nullptr && !IsTransferInventory(inventoryType)) if (missions != nullptr && !IsTransferInventory(inventoryType))
{ {
@ -203,7 +208,7 @@ void InventoryComponent::AddItem(
auto stack = static_cast<uint32_t>(info.stackSize); auto stack = static_cast<uint32_t>(info.stackSize);
if (inventoryType == BRICKS) if (inventoryType == eInventoryType::BRICKS)
{ {
stack = 999; stack = 999;
} }
@ -220,7 +225,7 @@ void InventoryComponent::AddItem(
left -= delta; left -= delta;
existing->SetCount(existing->GetCount() + delta, false, true, showFlyingLoot); existing->SetCount(existing->GetCount() + delta, false, true, showFlyingLoot, lootSourceType);
if (isModMoveAndEquip) if (isModMoveAndEquip)
{ {
@ -280,8 +285,7 @@ void InventoryComponent::AddItem(
continue; continue;
} }
auto* item = new Item(lot, inventory, slot, size, {}, parent, showFlyingLoot, isModMoveAndEquip, subKey, false, lootSourceType);
auto* item = new Item(lot, inventory, slot, size, {}, parent, showFlyingLoot, isModMoveAndEquip, subKey);
isModMoveAndEquip = false; isModMoveAndEquip = false;
} }
@ -365,7 +369,7 @@ void InventoryComponent::MoveItemToInventory(Item* item, const eInventoryType in
left -= delta; left -= delta;
AddItem(lot, delta, inventory, {}, LWOOBJID_EMPTY, showFlyingLot, isModMoveAndEquip, LWOOBJID_EMPTY, origin->GetType(), 0, false, preferredSlot); AddItem(lot, delta, eLootSourceType::LOOT_SOURCE_NONE, inventory, {}, LWOOBJID_EMPTY, showFlyingLot, isModMoveAndEquip, LWOOBJID_EMPTY, origin->GetType(), 0, false, preferredSlot);
item->SetCount(item->GetCount() - delta, false, false); item->SetCount(item->GetCount() - delta, false, false);
@ -383,7 +387,7 @@ void InventoryComponent::MoveItemToInventory(Item* item, const eInventoryType in
const auto delta = std::min<uint32_t>(item->GetCount(), count); const auto delta = std::min<uint32_t>(item->GetCount(), count);
AddItem(lot, delta, inventory, config, LWOOBJID_EMPTY, showFlyingLot, isModMoveAndEquip, LWOOBJID_EMPTY, origin->GetType(), 0, item->GetBound(), preferredSlot); AddItem(lot, delta, eLootSourceType::LOOT_SOURCE_NONE, inventory, config, LWOOBJID_EMPTY, showFlyingLot, isModMoveAndEquip, LWOOBJID_EMPTY, origin->GetType(), 0, item->GetBound(), preferredSlot);
item->SetCount(item->GetCount() - delta, false, false); item->SetCount(item->GetCount() - delta, false, false);
} }
@ -794,7 +798,26 @@ void InventoryComponent::Serialize(RakNet::BitStream* outBitStream, const bool b
outBitStream->Write0(); outBitStream->Write0();
outBitStream->Write0(); //TODO: This is supposed to be true and write the assemblyPartLOTs when they're present. bool flag = !item.config.empty();
outBitStream->Write(flag);
if (flag) {
RakNet::BitStream ldfStream;
ldfStream.Write<int32_t>(item.config.size()); // Key count
for (LDFBaseData* data : item.config) {
if (data->GetKey() == u"assemblyPartLOTs") {
std::string newRocketStr = data->GetValueAsString() + ";";
GeneralUtils::ReplaceInString(newRocketStr, "+", ";");
LDFData<std::u16string>* ldf_data = new LDFData<std::u16string>(u"assemblyPartLOTs", GeneralUtils::ASCIIToUTF16(newRocketStr));
ldf_data->WriteToPacket(&ldfStream);
delete ldf_data;
} else {
data->WriteToPacket(&ldfStream);
}
}
outBitStream->Write(ldfStream.GetNumberOfBytesUsed() + 1);
outBitStream->Write<uint8_t>(0); // Don't compress
outBitStream->Write(ldfStream);
}
outBitStream->Write1(); outBitStream->Write1();
} }
@ -909,27 +932,45 @@ void InventoryComponent::EquipItem(Item* item, const bool skipChecks)
const auto type = static_cast<eItemType>(item->GetInfo().itemType); const auto type = static_cast<eItemType>(item->GetInfo().itemType);
if (item->GetLot() == 8092 && m_Parent->GetGMLevel() >= GAME_MASTER_LEVEL_DEVELOPER) if (item->GetLot() == 8092 && m_Parent->GetGMLevel() >= GAME_MASTER_LEVEL_OPERATOR && hasCarEquipped == false)
{ {
auto startPosition = m_Parent->GetPosition();
auto startRotation = NiQuaternion::LookAt(startPosition, startPosition + NiPoint3::UNIT_X);
auto angles = startRotation.GetEulerAngles();
angles.y -= PI;
startRotation = NiQuaternion::FromEulerAngles(angles);
GameMessages::SendTeleport(m_Parent->GetObjectID(), startPosition, startRotation, m_Parent->GetSystemAddress(), true, true);
EntityInfo info {}; EntityInfo info {};
info.lot = 8092; info.lot = 8092;
info.pos = m_Parent->GetPosition(); info.pos = startPosition;
info.rot = m_Parent->GetRotation(); info.rot = startRotation;
info.spawnerID = m_Parent->GetObjectID(); info.spawnerID = m_Parent->GetObjectID();
auto* carEntity = EntityManager::Instance()->CreateEntity(info, nullptr, dZoneManager::Instance()->GetZoneControlObject()); auto* carEntity = EntityManager::Instance()->CreateEntity(info, nullptr, m_Parent);
dZoneManager::Instance()->GetZoneControlObject()->AddChild(carEntity); m_Parent->AddChild(carEntity);
auto *destroyableComponent = carEntity->GetComponent<DestroyableComponent>();
// Setup the vehicle stats.
if (destroyableComponent != nullptr) {
destroyableComponent->SetIsSmashable(false);
destroyableComponent->SetIsImmune(true);
}
// #108
auto* possessableComponent = carEntity->GetComponent<PossessableComponent>(); auto* possessableComponent = carEntity->GetComponent<PossessableComponent>();
if (possessableComponent != nullptr) if (possessableComponent != nullptr)
{ {
previousPossessableID = possessableComponent->GetPossessor();
possessableComponent->SetPossessor(m_Parent->GetObjectID()); possessableComponent->SetPossessor(m_Parent->GetObjectID());
} }
auto* moduleAssemblyComponent = carEntity->GetComponent<ModuleAssemblyComponent>(); auto* moduleAssemblyComponent = carEntity->GetComponent<ModuleAssemblyComponent>();
if (moduleAssemblyComponent) if (moduleAssemblyComponent != nullptr)
{ {
moduleAssemblyComponent->SetSubKey(item->GetSubKey()); moduleAssemblyComponent->SetSubKey(item->GetSubKey());
moduleAssemblyComponent->SetUseOptionalParts(false); moduleAssemblyComponent->SetUseOptionalParts(false);
@ -942,11 +983,12 @@ void InventoryComponent::EquipItem(Item* item, const bool skipChecks)
} }
} }
} }
// #107
auto* possessorComponent = m_Parent->GetComponent<PossessorComponent>(); auto* possessorComponent = m_Parent->GetComponent<PossessorComponent>();
if (possessorComponent != nullptr) if (possessorComponent != nullptr)
{ {
previousPossessorID = possessorComponent->GetPossessable();
possessorComponent->SetPossessable(carEntity->GetObjectID()); possessorComponent->SetPossessable(carEntity->GetObjectID());
} }
@ -960,13 +1002,26 @@ void InventoryComponent::EquipItem(Item* item, const bool skipChecks)
EntityManager::Instance()->ConstructEntity(carEntity); EntityManager::Instance()->ConstructEntity(carEntity);
EntityManager::Instance()->SerializeEntity(m_Parent); EntityManager::Instance()->SerializeEntity(m_Parent);
//EntityManager::Instance()->SerializeEntity(dZoneManager::Instance()->GetZoneControlObject()); GameMessages::SendSetJetPackMode(m_Parent, false);
GameMessages::SendNotifyVehicleOfRacingObject(carEntity->GetObjectID(), dZoneManager::Instance()->GetZoneControlObject()->GetObjectID(), UNASSIGNED_SYSTEM_ADDRESS); GameMessages::SendNotifyVehicleOfRacingObject(carEntity->GetObjectID(), m_Parent->GetObjectID(), UNASSIGNED_SYSTEM_ADDRESS);
GameMessages::SendRacingPlayerLoaded(m_Parent->GetObjectID(), m_Parent->GetObjectID(), carEntity->GetObjectID(), UNASSIGNED_SYSTEM_ADDRESS); GameMessages::SendRacingPlayerLoaded(LWOOBJID_EMPTY, m_Parent->GetObjectID(), carEntity->GetObjectID(), UNASSIGNED_SYSTEM_ADDRESS);
GameMessages::SendVehicleUnlockInput(carEntity->GetObjectID(), false, UNASSIGNED_SYSTEM_ADDRESS); GameMessages::SendVehicleUnlockInput(carEntity->GetObjectID(), false, UNASSIGNED_SYSTEM_ADDRESS);
//GameMessages::SendVehicleSetWheelLockState(carEntity->GetObjectID(), false, false, UNASSIGNED_SYSTEM_ADDRESS); GameMessages::SendTeleport(m_Parent->GetObjectID(), startPosition, startRotation, m_Parent->GetSystemAddress(), true, true);
GameMessages::SendTeleport(carEntity->GetObjectID(), startPosition, startRotation, m_Parent->GetSystemAddress(), true, true);
EntityManager::Instance()->SerializeEntity(m_Parent);
hasCarEquipped = true;
equippedCarEntity = carEntity;
return;
} else if (item->GetLot() == 8092 && m_Parent->GetGMLevel() >= GAME_MASTER_LEVEL_OPERATOR && hasCarEquipped == true)
{
GameMessages::SendNotifyRacingClient(LWOOBJID_EMPTY, 3, 0, LWOOBJID_EMPTY, u"", m_Parent->GetObjectID(), UNASSIGNED_SYSTEM_ADDRESS);
auto player = dynamic_cast<Player*>(m_Parent);
player->SendToZone(player->GetCharacter()->GetZoneID());
equippedCarEntity->Kill();
hasCarEquipped = false;
equippedCarEntity = nullptr;
return; return;
} }
@ -1008,9 +1063,9 @@ void InventoryComponent::EquipItem(Item* item, const bool skipChecks)
GenerateProxies(item); GenerateProxies(item);
UpdateSlot(item->GetInfo().equipLocation, { item->GetId(), item->GetLot(), item->GetCount(), item->GetSlot() }); UpdateSlot(item->GetInfo().equipLocation, { item->GetId(), item->GetLot(), item->GetCount(), item->GetSlot(), item->GetConfig() });
ApplyBuff(item->GetLot()); ApplyBuff(item);
AddItemSkills(item->GetLot()); AddItemSkills(item->GetLot());
@ -1038,7 +1093,7 @@ void InventoryComponent::UnEquipItem(Item* item)
set->OnUnEquip(lot); set->OnUnEquip(lot);
} }
RemoveBuff(item->GetLot()); RemoveBuff(item);
RemoveItemSkills(item->GetLot()); RemoveItemSkills(item->GetLot());
@ -1056,9 +1111,9 @@ void InventoryComponent::UnEquipItem(Item* item)
} }
} }
void InventoryComponent::ApplyBuff(const LOT lot) const void InventoryComponent::ApplyBuff(Item* item) const
{ {
const auto buffs = FindBuffs(lot, true); const auto buffs = FindBuffs(item, true);
for (const auto buff : buffs) for (const auto buff : buffs)
{ {
@ -1066,9 +1121,9 @@ void InventoryComponent::ApplyBuff(const LOT lot) const
} }
} }
void InventoryComponent::RemoveBuff(const LOT lot) const void InventoryComponent::RemoveBuff(Item* item) const
{ {
const auto buffs = FindBuffs(lot, false); const auto buffs = FindBuffs(item, false);
for (const auto buff : buffs) for (const auto buff : buffs)
{ {
@ -1195,13 +1250,6 @@ void InventoryComponent::AddItemSkills(const LOT lot)
const auto index = m_Skills.find(slot); const auto index = m_Skills.find(slot);
if (index != m_Skills.end())
{
const auto old = index->second;
GameMessages::SendRemoveSkill(m_Parent, old);
}
const auto skill = FindSkill(lot); const auto skill = FindSkill(lot);
if (skill == 0) if (skill == 0)
@ -1209,6 +1257,13 @@ void InventoryComponent::AddItemSkills(const LOT lot)
return; return;
} }
if (index != m_Skills.end())
{
const auto old = index->second;
GameMessages::SendRemoveSkill(m_Parent, old);
}
GameMessages::SendAddSkill(m_Parent, skill, static_cast<int>(slot)); GameMessages::SendAddSkill(m_Parent, skill, static_cast<int>(slot));
m_Skills.insert_or_assign(slot, skill); m_Skills.insert_or_assign(slot, skill);
@ -1384,18 +1439,18 @@ uint32_t InventoryComponent::FindSkill(const LOT lot)
return 0; return 0;
} }
std::vector<uint32_t> InventoryComponent::FindBuffs(const LOT lot, bool castOnEquip) const std::vector<uint32_t> InventoryComponent::FindBuffs(Item* item, bool castOnEquip) const
{ {
std::vector<uint32_t> buffs;
if (item == nullptr) return buffs;
auto* table = CDClientManager::Instance()->GetTable<CDObjectSkillsTable>("ObjectSkills"); auto* table = CDClientManager::Instance()->GetTable<CDObjectSkillsTable>("ObjectSkills");
auto* behaviors = CDClientManager::Instance()->GetTable<CDSkillBehaviorTable>("SkillBehavior"); auto* behaviors = CDClientManager::Instance()->GetTable<CDSkillBehaviorTable>("SkillBehavior");
const auto results = table->Query([=](const CDObjectSkills& entry) const auto results = table->Query([=](const CDObjectSkills& entry)
{ {
return entry.objectTemplate == static_cast<unsigned int>(lot); return entry.objectTemplate == static_cast<unsigned int>(item->GetLot());
}); });
std::vector<uint32_t> buffs;
auto* missions = static_cast<MissionComponent*>(m_Parent->GetComponent(COMPONENT_TYPE_MISSION)); auto* missions = static_cast<MissionComponent*>(m_Parent->GetComponent(COMPONENT_TYPE_MISSION));
for (const auto& result : results) for (const auto& result : results)
@ -1416,7 +1471,8 @@ std::vector<uint32_t> InventoryComponent::FindBuffs(const LOT lot, bool castOnEq
missions->Progress(MissionTaskType::MISSION_TASK_TYPE_SKILL, result.skillID); missions->Progress(MissionTaskType::MISSION_TASK_TYPE_SKILL, result.skillID);
} }
buffs.push_back(static_cast<uint32_t>(entry.behaviorID)); // If item is not a proxy, add its buff to the added buffs.
if (item->GetParent() == LWOOBJID_EMPTY) buffs.push_back(static_cast<uint32_t>(entry.behaviorID));
} }
} }

View File

@ -15,6 +15,7 @@
#include "Component.h" #include "Component.h"
#include "ItemSetPassiveAbility.h" #include "ItemSetPassiveAbility.h"
#include "ItemSetPassiveAbilityID.h" #include "ItemSetPassiveAbilityID.h"
#include "PossessorComponent.h"
class Entity; class Entity;
class ItemSet; class ItemSet;
@ -86,10 +87,12 @@ public:
* @param sourceType the source of the item, used to determine if the item is dropped or mailed if the inventory is full * @param sourceType the source of the item, used to determine if the item is dropped or mailed if the inventory is full
* @param bound whether this item is bound * @param bound whether this item is bound
* @param preferredSlot the preferred slot to store this item * @param preferredSlot the preferred slot to store this item
* @param lootSourceType The source of the loot. Defaults to none.
*/ */
void AddItem( void AddItem(
LOT lot, LOT lot,
uint32_t count, uint32_t count,
eLootSourceType lootSourceType = eLootSourceType::LOOT_SOURCE_NONE,
eInventoryType inventoryType = INVALID, eInventoryType inventoryType = INVALID,
const std::vector<LDFBaseData*>& config = {}, const std::vector<LDFBaseData*>& config = {},
LWOOBJID parent = LWOOBJID_EMPTY, LWOOBJID parent = LWOOBJID_EMPTY,
@ -192,15 +195,15 @@ public:
/** /**
* Adds a buff related to equipping a lot to the entity * Adds a buff related to equipping a lot to the entity
* @param lot the lot to find buffs for * @param item the item to find buffs for
*/ */
void ApplyBuff(LOT lot) const; void ApplyBuff(Item* item) const;
/** /**
* Removes buffs related to equipping a lot from the entity * Removes buffs related to equipping a lot from the entity
* @param lot the lot to find buffs for * @param item the item to find buffs for
*/ */
void RemoveBuff(LOT lot) const; void RemoveBuff(Item* item) const;
/** /**
* Saves the equipped items into a temp state * Saves the equipped items into a temp state
@ -239,11 +242,11 @@ public:
/** /**
* Finds all the buffs related to a lot * Finds all the buffs related to a lot
* @param lot the lot to get the buffs for * @param item the item to get the buffs for
* @param castOnEquip if true, the skill missions for these buffs will be progressed * @param castOnEquip if true, the skill missions for these buffs will be progressed
* @return the buffs related to the specified lot * @return the buffs related to the specified lot
*/ */
std::vector<uint32_t> FindBuffs(LOT lot, bool castOnEquip) const; std::vector<uint32_t> FindBuffs(Item* item, bool castOnEquip) const;
/** /**
* Initializes the equipped items with a list of items * Initializes the equipped items with a list of items
@ -384,6 +387,13 @@ private:
*/ */
LOT m_Consumable; LOT m_Consumable;
/**
* Currently has a car equipped
*/
bool hasCarEquipped = false;
Entity* equippedCarEntity = nullptr;
LWOOBJID previousPossessableID = LWOOBJID_EMPTY;
LWOOBJID previousPossessorID = LWOOBJID_EMPTY;
/** /**
* Creates all the proxy items (subitems) for a parent item * Creates all the proxy items (subitems) for a parent item
* @param parent the parent item to generate all the subitems for * @param parent the parent item to generate all the subitems for
@ -399,8 +409,8 @@ private:
std::vector<Item*> FindProxies(LWOOBJID parent); std::vector<Item*> FindProxies(LWOOBJID parent);
/** /**
* Returns if the provided ID is a valid proxy item (e.g. we have children for it) * Returns true if the provided LWOOBJID is the parent of this Item.
* @param parent the parent item to check for * @param parent the parent item to check for proxies
* @return if the provided ID is a valid proxy item * @return if the provided ID is a valid proxy item
*/ */
bool IsValidProxy(LWOOBJID parent); bool IsValidProxy(LWOOBJID parent);

View File

@ -29,8 +29,8 @@ public:
explicit MissionComponent(Entity* parent); explicit MissionComponent(Entity* parent);
~MissionComponent() override; ~MissionComponent() override;
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags); void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags);
void LoadFromXml(tinyxml2::XMLDocument* doc); void LoadFromXml(tinyxml2::XMLDocument* doc) override;
void UpdateXml(tinyxml2::XMLDocument* doc); void UpdateXml(tinyxml2::XMLDocument* doc) override;
/** /**
* Returns all the missions for this entity, mapped by mission ID * Returns all the missions for this entity, mapped by mission ID

View File

@ -17,7 +17,7 @@ public:
~ModuleAssemblyComponent() override; ~ModuleAssemblyComponent() override;
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags); void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags);
void Update(float deltaTime); void Update(float deltaTime) override;
/** /**
* Sets the subkey of this entity * Sets the subkey of this entity

View File

@ -61,7 +61,7 @@ public:
MovementAIComponent(Entity* parentEntity, MovementAIInfo info); MovementAIComponent(Entity* parentEntity, MovementAIInfo info);
~MovementAIComponent() override; ~MovementAIComponent() override;
void Update(float deltaTime); void Update(float deltaTime) override;
/** /**
* Returns the basic settings that this entity uses to move around * Returns the basic settings that this entity uses to move around

View File

@ -75,13 +75,19 @@ PetComponent::PetComponent(Entity* parent, uint32_t componentId) : Component(par
m_MovementAI = nullptr; m_MovementAI = nullptr;
m_TresureTime = 0; m_TresureTime = 0;
m_Preconditions = nullptr; m_Preconditions = nullptr;
std::string checkPreconditions = GeneralUtils::UTF16ToWTF8(parent->GetVar<std::u16string>(u"CheckPrecondition"));
if (!checkPreconditions.empty()) {
SetPreconditions(checkPreconditions);
}
} }
void PetComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) void PetComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags)
{ {
const bool tamed = m_Owner != LWOOBJID_EMPTY; const bool tamed = m_Owner != LWOOBJID_EMPTY;
outBitStream->Write1(); // Dirty? outBitStream->Write1(); // Always serialize as dirty for now
outBitStream->Write<uint32_t>(static_cast<unsigned int>(m_Status)); outBitStream->Write<uint32_t>(static_cast<unsigned int>(m_Status));
outBitStream->Write<uint32_t>(static_cast<uint32_t>(tamed ? m_Ability : PetAbilityType::Invalid)); // Something with the overhead icon? outBitStream->Write<uint32_t>(static_cast<uint32_t>(tamed ? m_Ability : PetAbilityType::Invalid)); // Something with the overhead icon?
@ -262,30 +268,13 @@ void PetComponent::OnUse(Entity* originator)
auto position = originatorPosition; auto position = originatorPosition;
NiPoint3 forward = NiQuaternion::LookAt(m_Parent->GetPosition(), originator->GetPosition()).GetForwardVector(); //m_Parent->GetRotation().GetForwardVector(); NiPoint3 forward = NiQuaternion::LookAt(m_Parent->GetPosition(), originator->GetPosition()).GetForwardVector();
forward.y = 0; forward.y = 0;
if (dpWorld::Instance().IsLoaded()) if (dpWorld::Instance().IsLoaded())
{ {
/*
if (interactionDistance > 2)
{
interactionDistance -= 1;
}
*/
NiPoint3 attempt = petPosition + forward * interactionDistance; NiPoint3 attempt = petPosition + forward * interactionDistance;
/*
float deg = std::atan2(petPosition.z - originatorPosition.z, petPosition.x - originatorPosition.x); //* 180 / M_PI;
auto position = NiPoint3(
petPosition.x + interactionDistance * std::cos(-deg),
petPosition.y,
petPosition.z + interactionDistance * std::sin(-deg)
);
*/
float y = dpWorld::Instance().GetHeightAtPoint(attempt); float y = dpWorld::Instance().GetHeightAtPoint(attempt);
while (std::abs(y - petPosition.y) > 4 && interactionDistance > 10) while (std::abs(y - petPosition.y) > 4 && interactionDistance > 10)
@ -309,8 +298,6 @@ void PetComponent::OnUse(Entity* originator)
auto rotation = NiQuaternion::LookAt(position, petPosition); auto rotation = NiQuaternion::LookAt(position, petPosition);
//GameMessages::SendTeleport(originator->GetObjectID(), position, rotation, originator->GetSystemAddress(), true);
GameMessages::SendNotifyPetTamingMinigame( GameMessages::SendNotifyPetTamingMinigame(
originator->GetObjectID(), originator->GetObjectID(),
m_Parent->GetObjectID(), m_Parent->GetObjectID(),
@ -531,14 +518,12 @@ void PetComponent::Update(float deltaTime)
m_Timer = 1; m_Timer = 1;
} }
void PetComponent::TryBuild(std::vector<Brick>& bricks, bool clientFailed) void PetComponent::TryBuild(uint32_t numBricks, bool clientFailed) {
{
if (m_Tamer == LWOOBJID_EMPTY) return; if (m_Tamer == LWOOBJID_EMPTY) return;
auto* tamer = EntityManager::Instance()->GetEntity(m_Tamer); auto* tamer = EntityManager::Instance()->GetEntity(m_Tamer);
if (tamer == nullptr) if (tamer == nullptr) {
{
m_Tamer = LWOOBJID_EMPTY; m_Tamer = LWOOBJID_EMPTY;
return; return;
@ -546,19 +531,11 @@ void PetComponent::TryBuild(std::vector<Brick>& bricks, bool clientFailed)
const auto& cached = buildCache.find(m_Parent->GetLOT()); const auto& cached = buildCache.find(m_Parent->GetLOT());
if (cached == buildCache.end()) if (cached == buildCache.end()) return;
{
GameMessages::SendPetTamingTryBuildResult(m_Tamer, false, 0, tamer->GetSystemAddress());
return;
}
auto* destroyableComponent = tamer->GetComponent<DestroyableComponent>(); auto* destroyableComponent = tamer->GetComponent<DestroyableComponent>();
if (destroyableComponent == nullptr) if (destroyableComponent == nullptr) return;
{
return;
}
auto imagination = destroyableComponent->GetImagination(); auto imagination = destroyableComponent->GetImagination();
@ -568,59 +545,17 @@ void PetComponent::TryBuild(std::vector<Brick>& bricks, bool clientFailed)
EntityManager::Instance()->SerializeEntity(tamer); EntityManager::Instance()->SerializeEntity(tamer);
const auto& trueBricks = BrickDatabase::Instance()->GetBricks(cached->second.buildFile); if (clientFailed) {
if (imagination < cached->second.imaginationCost) {
if (trueBricks.empty() || bricks.empty())
{
GameMessages::SendPetTamingTryBuildResult(m_Tamer, false, 0, tamer->GetSystemAddress());
return;
}
auto* brickIDTable = CDClientManager::Instance()->GetTable<CDBrickIDTableTable>("BrickIDTable");
int32_t correct = 0;
for (const auto& brick : bricks)
{
const auto brickEntries = brickIDTable->Query([brick](const CDBrickIDTable& entry)
{
return entry.NDObjectID == brick.designerID;
});
if (brickEntries.empty())
{
continue;
}
const auto designerID = brickEntries[0].LEGOBrickID;
for (const auto& trueBrick : trueBricks)
{
if (designerID == trueBrick.designerID && brick.materialID == trueBrick.materialID)
{
correct++;
break;
}
}
}
const auto success = correct >= cached->second.numValidPieces;
GameMessages::SendPetTamingTryBuildResult(m_Tamer, success, correct, tamer->GetSystemAddress());
if (!success)
{
if (imagination < cached->second.imaginationCost)
{
ClientFailTamingMinigame(); ClientFailTamingMinigame();
} }
} } else {
else
{
m_Timer = 0; m_Timer = 0;
} }
if (numBricks == 0) return;
GameMessages::SendPetTamingTryBuildResult(m_Tamer, !clientFailed, numBricks, tamer->GetSystemAddress());
} }
void PetComponent::NotifyTamingBuildSuccess(NiPoint3 position) void PetComponent::NotifyTamingBuildSuccess(NiPoint3 position)
@ -685,7 +620,7 @@ void PetComponent::NotifyTamingBuildSuccess(NiPoint3 position)
GameMessages::SendRegisterPetDBID(m_Tamer, petSubKey, tamer->GetSystemAddress()); GameMessages::SendRegisterPetDBID(m_Tamer, petSubKey, tamer->GetSystemAddress());
inventoryComponent->AddItem(m_Parent->GetLOT(), 1, MODELS, {}, LWOOBJID_EMPTY, true, false, petSubKey); inventoryComponent->AddItem(m_Parent->GetLOT(), 1, eLootSourceType::LOOT_SOURCE_ACTIVITY, eInventoryType::MODELS, {}, LWOOBJID_EMPTY, true, false, petSubKey);
auto* item = inventoryComponent->FindItemBySubKey(petSubKey, MODELS); auto* item = inventoryComponent->FindItemBySubKey(petSubKey, MODELS);
if (item == nullptr) if (item == nullptr)
@ -726,7 +661,6 @@ void PetComponent::NotifyTamingBuildSuccess(NiPoint3 position)
if (missionComponent != nullptr) if (missionComponent != nullptr)
{ {
//missionComponent->ForceProgress(506, 768, 1, false);
missionComponent->Progress(MissionTaskType::MISSION_TASK_TYPE_PET_TAMING, m_Parent->GetLOT()); missionComponent->Progress(MissionTaskType::MISSION_TASK_TYPE_PET_TAMING, m_Parent->GetLOT());
} }

View File

@ -39,7 +39,7 @@ public:
* @param bricks the bricks to try to complete the minigame with * @param bricks the bricks to try to complete the minigame with
* @param clientFailed unused * @param clientFailed unused
*/ */
void TryBuild(std::vector<Brick>& bricks, bool clientFailed); void TryBuild(uint32_t numBricks, bool clientFailed);
/** /**
* Handles a notification from the client regarding the completion of the pet minigame, adding the pet to their * Handles a notification from the client regarding the completion of the pet minigame, adding the pet to their

View File

@ -1,12 +1,16 @@
#include <CDPropertyEntranceComponentTable.h>
#include "PropertyEntranceComponent.h" #include "PropertyEntranceComponent.h"
#include <CDPropertyEntranceComponentTable.h>
#include "Character.h"
#include "Database.h"
#include "GameMessages.h"
#include "PropertyManagementComponent.h"
#include "PropertySelectQueryProperty.h" #include "PropertySelectQueryProperty.h"
#include "RocketLaunchpadControlComponent.h" #include "RocketLaunchpadControlComponent.h"
#include "Character.h" #include "CharacterComponent.h"
#include "GameMessages.h" #include "UserManager.h"
#include "dLogger.h" #include "dLogger.h"
#include "Database.h"
#include "PropertyManagementComponent.h"
PropertyEntranceComponent::PropertyEntranceComponent(uint32_t componentID, Entity* parent) : Component(parent) PropertyEntranceComponent::PropertyEntranceComponent(uint32_t componentID, Entity* parent) : Component(parent)
{ {
@ -19,8 +23,13 @@ PropertyEntranceComponent::PropertyEntranceComponent(uint32_t componentID, Entit
this->m_PropertyName = entry.propertyName; this->m_PropertyName = entry.propertyName;
} }
void PropertyEntranceComponent::OnUse(Entity* entity) void PropertyEntranceComponent::OnUse(Entity* entity) {
{ auto* characterComponent = entity->GetComponent<CharacterComponent>();
if (!characterComponent) return;
auto* rocket = entity->GetComponent<CharacterComponent>()->RocketEquip(entity);
if (!rocket) return;
GameMessages::SendPropertyEntranceBegin(m_Parent->GetObjectID(), entity->GetSystemAddress()); GameMessages::SendPropertyEntranceBegin(m_Parent->GetObjectID(), entity->GetSystemAddress());
AMFArrayValue args; AMFArrayValue args;
@ -49,6 +58,7 @@ void PropertyEntranceComponent::OnEnterProperty(Entity* entity, uint32_t index,
} }
else if (index >= 0) else if (index >= 0)
{ {
// Increment index once here because the first index of other player properties is 2 in the propertyQueries cache.
index++; index++;
const auto& pair = propertyQueries.find(entity->GetObjectID()); const auto& pair = propertyQueries.find(entity->GetObjectID());
@ -71,167 +81,269 @@ void PropertyEntranceComponent::OnEnterProperty(Entity* entity, uint32_t index,
launcher->SetSelectedCloneId(entity->GetObjectID(), cloneId); launcher->SetSelectedCloneId(entity->GetObjectID(), cloneId);
launcher->Launch(entity, LWOOBJID_EMPTY, LWOMAPID_INVALID, cloneId); launcher->Launch(entity, launcher->GetTargetZone(), cloneId);
} }
void PropertyEntranceComponent::OnPropertyEntranceSync(Entity* entity, PropertySelectQueryProperty PropertyEntranceComponent::SetPropertyValues(PropertySelectQueryProperty property, LWOCLONEID cloneId, std::string ownerName, std::string propertyName, std::string propertyDescription, float reputation, bool isBFF, bool isFriend, bool isModeratorApproved, bool isAlt, bool isOwned, uint32_t privacyOption, uint32_t timeLastUpdated, float performanceCost) {
bool includeNullAddress, property.CloneId = cloneId;
bool includeNullDescription, property.OwnerName = ownerName;
bool playerOwn, property.Name = propertyName;
bool updateUi, property.Description = propertyDescription;
int32_t numResults, property.Reputation = reputation;
int32_t reputation, property.IsBestFriend = isBFF;
int32_t sortMethod, property.IsFriend = isFriend;
int32_t startIndex, property.IsModeratorApproved = isModeratorApproved;
std::string filterText, property.IsAlt = isAlt;
const SystemAddress& sysAddr) property.IsOwned = isOwned;
{ property.AccessType = privacyOption;
Game::logger->Log("PropertyEntranceComponent", "On Sync %d %d %d %d %i %i %i %i %s\n", property.DateLastPublished = timeLastUpdated;
includeNullAddress, property.PerformanceCost = performanceCost;
includeNullDescription,
playerOwn,
updateUi,
numResults,
reputation,
sortMethod,
startIndex,
filterText.c_str()
);
auto* launchpadComponent = m_Parent->GetComponent<RocketLaunchpadControlComponent>(); return property;
if (launchpadComponent == nullptr) }
return;
std::string PropertyEntranceComponent::BuildQuery(Entity* entity, int32_t sortMethod, std::string customQuery, bool wantLimits) {
std::string base;
if (customQuery == "") {
base = baseQueryForProperties;
} else {
base = customQuery;
}
std::string orderBy = "";
if (sortMethod == SORT_TYPE_FEATURED || sortMethod == SORT_TYPE_FRIENDS) {
std::string friendsList = " AND p.owner_id IN (";
auto friendsListQuery = Database::CreatePreppedStmt("SELECT * FROM (SELECT CASE WHEN player_id = ? THEN friend_id WHEN friend_id = ? THEN player_id END AS requested_player FROM friends ) AS fr WHERE requested_player IS NOT NULL ORDER BY requested_player DESC;");
friendsListQuery->setInt64(1, entity->GetObjectID());
friendsListQuery->setInt64(2, entity->GetObjectID());
auto friendsListQueryResult = friendsListQuery->executeQuery();
while (friendsListQueryResult->next()) {
auto playerIDToConvert = friendsListQueryResult->getInt64(1);
playerIDToConvert = GeneralUtils::ClearBit(playerIDToConvert, OBJECT_BIT_CHARACTER);
playerIDToConvert = GeneralUtils::ClearBit(playerIDToConvert, OBJECT_BIT_PERSISTENT);
friendsList = friendsList + std::to_string(playerIDToConvert) + ",";
}
// Replace trailing comma with the closing parenthesis.
if (friendsList.at(friendsList.size() - 1) == ',') friendsList.erase(friendsList.size() - 1, 1);
friendsList += ") ";
// If we have no friends then use a -1 for the query.
if (friendsList.find("()") != std::string::npos) friendsList = " AND p.owner_id IN (-1) ";
orderBy += friendsList + "ORDER BY ci.name ASC ";
delete friendsListQueryResult;
friendsListQueryResult = nullptr;
delete friendsListQuery;
friendsListQuery = nullptr;
}
else if (sortMethod == SORT_TYPE_RECENT) {
orderBy = "ORDER BY p.last_updated DESC ";
}
else if (sortMethod == SORT_TYPE_REPUTATION) {
orderBy = "ORDER BY p.reputation DESC, p.last_updated DESC ";
}
else {
orderBy = "ORDER BY p.last_updated DESC ";
}
return base + orderBy + (wantLimits ? "LIMIT ? OFFSET ?;" : ";");
}
void PropertyEntranceComponent::OnPropertyEntranceSync(Entity* entity, bool includeNullAddress, bool includeNullDescription, bool playerOwn, bool updateUi, int32_t numResults, int32_t lReputationTime, int32_t sortMethod, int32_t startIndex, std::string filterText, const SystemAddress& sysAddr){
std::vector<PropertySelectQueryProperty> entries {}; std::vector<PropertySelectQueryProperty> entries {};
PropertySelectQueryProperty playerEntry {}; PropertySelectQueryProperty playerEntry {};
auto* character = entity->GetCharacter(); auto character = entity->GetCharacter();
playerEntry.OwnerName = character->GetName(); if (!character) return;
playerEntry.Description = "No description.";
playerEntry.Name = "Your property!"; // Player property goes in index 1 of the vector. This is how the client expects it.
playerEntry.IsModeratorApproved = true; auto playerPropertyLookup = Database::CreatePreppedStmt("SELECT * FROM properties WHERE owner_id = ? AND zone_id = ?");
playerEntry.AccessType = 2;
playerEntry.CloneId = character->GetPropertyCloneID(); playerPropertyLookup->setInt(1, character->GetID());
playerPropertyLookup->setInt(2, this->m_MapID);
auto playerPropertyLookupResults = playerPropertyLookup->executeQuery();
// If the player has a property this query will have a single result.
if (playerPropertyLookupResults->next()) {
const auto cloneId = playerPropertyLookupResults->getUInt64(4);
const auto propertyName = playerPropertyLookupResults->getString(5).asStdString();
const auto propertyDescription = playerPropertyLookupResults->getString(6).asStdString();
const auto privacyOption = playerPropertyLookupResults->getInt(9);
const auto modApproved = playerPropertyLookupResults->getBoolean(10);
const auto dateLastUpdated = playerPropertyLookupResults->getInt64(11);
const auto reputation = playerPropertyLookupResults->getUInt(14);
const auto performanceCost = (float)playerPropertyLookupResults->getDouble(16);
playerEntry = SetPropertyValues(playerEntry, cloneId, character->GetName(), propertyName, propertyDescription, reputation, true, true, modApproved, true, true, privacyOption, dateLastUpdated, performanceCost);
} else {
playerEntry = SetPropertyValues(playerEntry, character->GetPropertyCloneID(), character->GetName(), "", "", 0, true, true);
}
delete playerPropertyLookupResults;
playerPropertyLookupResults = nullptr;
delete playerPropertyLookup;
playerPropertyLookup = nullptr;
entries.push_back(playerEntry); entries.push_back(playerEntry);
sql::ResultSet* propertyEntry; const auto query = BuildQuery(entity, sortMethod);
sql::PreparedStatement* propertyLookup;
const auto moderating = entity->GetGMLevel() >= GAME_MASTER_LEVEL_LEAD_MODERATOR; auto propertyLookup = Database::CreatePreppedStmt(query);
if (!moderating) const auto searchString = "%" + filterText + "%";
{ propertyLookup->setUInt(1, this->m_MapID);
propertyLookup = Database::CreatePreppedStmt(
"SELECT * FROM properties WHERE (name LIKE ? OR description LIKE ? OR "
"((SELECT name FROM charinfo WHERE prop_clone_id = clone_id) LIKE ?)) AND "
"(privacy_option = 2 AND mod_approved = true) OR (privacy_option >= 1 "
"AND (owner_id IN (SELECT friend_id FROM friends WHERE player_id = ?) OR owner_id IN (SELECT player_id FROM "
"friends WHERE friend_id = ?))) AND zone_id = ? LIMIT ? OFFSET ?;"
);
const std::string searchString = "%" + filterText + "%";
Game::logger->Log("PropertyEntranceComponent", "%s\n", searchString.c_str());
propertyLookup->setString(1, searchString.c_str());
propertyLookup->setString(2, searchString.c_str()); propertyLookup->setString(2, searchString.c_str());
propertyLookup->setString(3, searchString.c_str()); propertyLookup->setString(3, searchString.c_str());
propertyLookup->setInt64(4, entity->GetObjectID()); propertyLookup->setString(4, searchString.c_str());
propertyLookup->setInt64(5, entity->GetObjectID()); propertyLookup->setInt(5, sortMethod == SORT_TYPE_FEATURED || sortMethod == SORT_TYPE_FRIENDS ? (uint32_t)PropertyPrivacyOption::Friends : (uint32_t)PropertyPrivacyOption::Public);
propertyLookup->setUInt(6, launchpadComponent->GetTargetZone()); propertyLookup->setInt(6, numResults);
propertyLookup->setInt(7, numResults); propertyLookup->setInt(7, startIndex);
propertyLookup->setInt(8, startIndex);
propertyEntry = propertyLookup->executeQuery(); auto propertyEntry = propertyLookup->executeQuery();
}
else
{
propertyLookup = Database::CreatePreppedStmt(
"SELECT * FROM properties WHERE privacy_option = 2 AND mod_approved = false AND zone_id = ?;"
);
propertyLookup->setUInt(1, launchpadComponent->GetTargetZone()); while (propertyEntry->next()) {
propertyEntry = propertyLookup->executeQuery();
}
while (propertyEntry->next())
{
const auto propertyId = propertyEntry->getUInt64(1); const auto propertyId = propertyEntry->getUInt64(1);
const auto owner = propertyEntry->getUInt64(2); const auto owner = propertyEntry->getInt(2);
const auto cloneId = propertyEntry->getUInt64(4); const auto cloneId = propertyEntry->getUInt64(4);
const auto name = propertyEntry->getString(5).asStdString(); const auto propertyNameFromDb = propertyEntry->getString(5).asStdString();
const auto description = propertyEntry->getString(6).asStdString(); const auto propertyDescriptionFromDb = propertyEntry->getString(6).asStdString();
const auto privacyOption = propertyEntry->getInt(9); const auto privacyOption = propertyEntry->getInt(9);
const auto reputation = propertyEntry->getInt(15); const auto modApproved = propertyEntry->getBoolean(10);
const auto dateLastUpdated = propertyEntry->getInt(11);
const float reputation = propertyEntry->getInt(14);
const auto performanceCost = (float)propertyEntry->getDouble(16);
PropertySelectQueryProperty entry{}; PropertySelectQueryProperty entry{};
auto* nameLookup = Database::CreatePreppedStmt("SELECT name FROM charinfo WHERE prop_clone_id = ?;"); std::string ownerName = "";
bool isOwned = true;
auto nameLookup = Database::CreatePreppedStmt("SELECT name FROM charinfo WHERE prop_clone_id = ?;");
nameLookup->setUInt64(1, cloneId); nameLookup->setUInt64(1, cloneId);
auto* nameResult = nameLookup->executeQuery(); auto nameResult = nameLookup->executeQuery();
if (!nameResult->next()) if (!nameResult->next()) {
{
delete nameLookup; delete nameLookup;
nameLookup = nullptr;
Game::logger->Log("PropertyEntranceComponent", "Failed to find property owner name for %llu!\n", cloneId); Game::logger->Log("PropertyEntranceComponent", "Failed to find property owner name for %llu!\n", cloneId);
continue; continue;
} } else {
else isOwned = cloneId == character->GetPropertyCloneID();
{ ownerName = nameResult->getString(1).asStdString();
entry.IsOwner = owner == entity->GetObjectID();
entry.OwnerName = nameResult->getString(1).asStdString();
} }
if (!moderating) delete nameResult;
{ nameResult = nullptr;
entry.Name = name;
entry.Description = description;
}
else
{
entry.Name = "[Awaiting approval] " + name;
entry.Description = "[Awaiting approval] " + description;
}
entry.IsFriend = privacyOption == static_cast<int32_t>(PropertyPrivacyOption::Friends);
entry.Reputation = reputation;
entry.CloneId = cloneId;
entry.IsModeratorApproved = true;
entry.AccessType = 3;
entries.push_back(entry);
delete nameLookup; delete nameLookup;
nameLookup = nullptr;
std::string propertyName = propertyNameFromDb;
std::string propertyDescription = propertyDescriptionFromDb;
bool isBestFriend = false;
bool isFriend = false;
// Convert owner char id to LWOOBJID
LWOOBJID ownerObjId = owner;
ownerObjId = GeneralUtils::SetBit(ownerObjId, OBJECT_BIT_CHARACTER);
ownerObjId = GeneralUtils::SetBit(ownerObjId, OBJECT_BIT_PERSISTENT);
// Query to get friend and best friend fields
auto friendCheck = Database::CreatePreppedStmt("SELECT best_friend FROM friends WHERE (player_id = ? AND friend_id = ?) OR (player_id = ? AND friend_id = ?)");
friendCheck->setInt64(1, entity->GetObjectID());
friendCheck->setInt64(2, ownerObjId);
friendCheck->setInt64(3, ownerObjId);
friendCheck->setInt64(4, entity->GetObjectID());
auto friendResult = friendCheck->executeQuery();
// If we got a result than the two players are friends.
if (friendResult->next()) {
isFriend = true;
if (friendResult->getInt(1) == 2) {
isBestFriend = true;
} }
}
delete friendCheck;
friendCheck = nullptr;
delete friendResult;
friendResult = nullptr;
bool isModeratorApproved = propertyEntry->getBoolean(10);
if (!isModeratorApproved && entity->GetGMLevel() >= GAME_MASTER_LEVEL_LEAD_MODERATOR) {
propertyName = "[AWAITING APPROVAL]";
propertyDescription = "[AWAITING APPROVAL]";
isModeratorApproved = true;
}
bool isAlt = false;
// Query to determine whether this property is an alt character of the entity.
auto isAltQuery = Database::CreatePreppedStmt("SELECT id FROM charinfo where account_id in (SELECT account_id from charinfo WHERE id = ?) AND id = ?;");
isAltQuery->setInt(1, character->GetID());
isAltQuery->setInt(2, owner);
auto isAltQueryResults = isAltQuery->executeQuery();
if (isAltQueryResults->next()) {
isAlt = true;
}
delete isAltQueryResults;
isAltQueryResults = nullptr;
delete isAltQuery;
isAltQuery = nullptr;
entry = SetPropertyValues(entry, cloneId, ownerName, propertyName, propertyDescription, reputation, isBestFriend, isFriend, isModeratorApproved, isAlt, isOwned, privacyOption, dateLastUpdated, performanceCost);
entries.push_back(entry);
}
delete propertyEntry;
propertyEntry = nullptr;
delete propertyLookup; delete propertyLookup;
propertyLookup = nullptr;
/*
const auto entriesSize = entries.size();
if (startIndex != 0 && entriesSize > startIndex)
{
for (size_t i = 0; i < startIndex; i++)
{
entries.erase(entries.begin());
}
}
*/
propertyQueries[entity->GetObjectID()] = entries; propertyQueries[entity->GetObjectID()] = entries;
GameMessages::SendPropertySelectQuery( // Query here is to figure out whether or not to display the button to go to the next page or not.
m_Parent->GetObjectID(), int32_t numberOfProperties = 0;
startIndex,
entries.size() >= numResults, auto buttonQuery = BuildQuery(entity, sortMethod, "SELECT COUNT(*) FROM properties as p JOIN charinfo as ci ON ci.prop_clone_id = p.clone_id where p.zone_id = ? AND (p.description LIKE ? OR p.name LIKE ? OR ci.name LIKE ?) AND p.privacy_option >= ? ", false);
character->GetPropertyCloneID(), auto propertiesLeft = Database::CreatePreppedStmt(buttonQuery);
false,
true, propertiesLeft->setUInt(1, this->m_MapID);
entries, propertiesLeft->setString(2, searchString.c_str());
sysAddr propertiesLeft->setString(3, searchString.c_str());
); propertiesLeft->setString(4, searchString.c_str());
propertiesLeft->setInt(5, sortMethod == SORT_TYPE_FEATURED || sortMethod == SORT_TYPE_FRIENDS ? 1 : 2);
auto result = propertiesLeft->executeQuery();
result->next();
numberOfProperties = result->getInt(1);
delete result;
result = nullptr;
delete propertiesLeft;
propertiesLeft = nullptr;
GameMessages::SendPropertySelectQuery(m_Parent->GetObjectID(), startIndex, numberOfProperties - (startIndex + numResults) > 0, character->GetPropertyCloneID(), false, true, entries, sysAddr);
} }

View File

@ -1,16 +1,16 @@
#pragma once #pragma once
#include <map>
#include "Component.h"
#include "Entity.h" #include "Entity.h"
#include "EntityManager.h" #include "EntityManager.h"
#include "GameMessages.h" #include "GameMessages.h"
#include "Component.h"
#include <map>
/** /**
* Represents the launch pad that's used to select and browse properties * Represents the launch pad that's used to select and browse properties
*/ */
class PropertyEntranceComponent : public Component class PropertyEntranceComponent : public Component {
{
public: public:
static const uint32_t ComponentType = COMPONENT_TYPE_PROPERTY_ENTRANCE; static const uint32_t ComponentType = COMPONENT_TYPE_PROPERTY_ENTRANCE;
explicit PropertyEntranceComponent(uint32_t componentID, Entity* parent); explicit PropertyEntranceComponent(uint32_t componentID, Entity* parent);
@ -24,7 +24,7 @@ public:
/** /**
* Handles the event triggered when the entity selects a property to visit and makes the entity to there * Handles the event triggered when the entity selects a property to visit and makes the entity to there
* @param entity the entity that triggered the event * @param entity the entity that triggered the event
* @param index the clone ID of the property to visit * @param index the index of the property property
* @param returnToZone whether or not the entity wishes to go back to the launch zone * @param returnToZone whether or not the entity wishes to go back to the launch zone
* @param sysAddr the address to send gamemessage responses to * @param sysAddr the address to send gamemessage responses to
*/ */
@ -38,23 +38,13 @@ public:
* @param playerOwn only query properties owned by the entity * @param playerOwn only query properties owned by the entity
* @param updateUi unused * @param updateUi unused
* @param numResults unused * @param numResults unused
* @param reputation unused * @param lReputationTime unused
* @param sortMethod unused * @param sortMethod unused
* @param startIndex the minimum index to start the query off * @param startIndex the minimum index to start the query off
* @param filterText property names to search for * @param filterText property names to search for
* @param sysAddr the address to send gamemessage responses to * @param sysAddr the address to send gamemessage responses to
*/ */
void OnPropertyEntranceSync(Entity* entity, void OnPropertyEntranceSync(Entity* entity, bool includeNullAddress, bool includeNullDescription, bool playerOwn, bool updateUi, int32_t numResults, int32_t lReputationTime, int32_t sortMethod, int32_t startIndex, std::string filterText, const SystemAddress& sysAddr);
bool includeNullAddress,
bool includeNullDescription,
bool playerOwn,
bool updateUi,
int32_t numResults,
int32_t reputation,
int32_t sortMethod,
int32_t startIndex,
std::string filterText,
const SystemAddress &sysAddr);
/** /**
* Returns the name of this property * Returns the name of this property
@ -68,8 +58,11 @@ public:
*/ */
[[nodiscard]] LWOMAPID GetMapID() const { return m_MapID; }; [[nodiscard]] LWOMAPID GetMapID() const { return m_MapID; };
private: PropertySelectQueryProperty SetPropertyValues(PropertySelectQueryProperty property, LWOCLONEID cloneId = LWOCLONEID_INVALID, std::string ownerName = "", std::string propertyName = "", std::string propertyDescription = "", float reputation = 0, bool isBFF = false, bool isFriend = false, bool isModeratorApproved = false, bool isAlt = false, bool isOwned = false, uint32_t privacyOption = 0, uint32_t timeLastUpdated = 0, float performanceCost = 0.0f);
std::string BuildQuery(Entity* entity, int32_t sortMethod, std::string customQuery = "", bool wantLimits = true);
private:
/** /**
* Cache of property information that was queried for property launched, indexed by property ID * Cache of property information that was queried for property launched, indexed by property ID
*/ */
@ -84,4 +77,13 @@ private:
* The base map ID for this property (Avant Grove, etc). * The base map ID for this property (Avant Grove, etc).
*/ */
LWOMAPID m_MapID; LWOMAPID m_MapID;
enum ePropertySortType : int32_t {
SORT_TYPE_FRIENDS = 0,
SORT_TYPE_REPUTATION = 1,
SORT_TYPE_RECENT = 3,
SORT_TYPE_FEATURED = 5
};
std::string baseQueryForProperties = "SELECT p.* FROM properties as p JOIN charinfo as ci ON ci.prop_clone_id = p.clone_id where p.zone_id = ? AND (p.description LIKE ? OR p.name LIKE ? OR ci.name LIKE ?) AND p.privacy_option >= ? ";
}; };

View File

@ -68,10 +68,15 @@ PropertyManagementComponent::PropertyManagementComponent(Entity* parent) : Compo
this->owner = propertyEntry->getUInt64(2); this->owner = propertyEntry->getUInt64(2);
this->owner = GeneralUtils::SetBit(this->owner, OBJECT_BIT_CHARACTER); this->owner = GeneralUtils::SetBit(this->owner, OBJECT_BIT_CHARACTER);
this->owner = GeneralUtils::SetBit(this->owner, OBJECT_BIT_PERSISTENT); this->owner = GeneralUtils::SetBit(this->owner, OBJECT_BIT_PERSISTENT);
this->clone_Id = propertyEntry->getInt(2);
this->propertyName = propertyEntry->getString(5).c_str(); this->propertyName = propertyEntry->getString(5).c_str();
this->propertyDescription = propertyEntry->getString(6).c_str(); this->propertyDescription = propertyEntry->getString(6).c_str();
this->privacyOption = static_cast<PropertyPrivacyOption>(propertyEntry->getUInt(9)); this->privacyOption = static_cast<PropertyPrivacyOption>(propertyEntry->getUInt(9));
this->claimedTime = propertyEntry->getUInt64(13); this->moderatorRequested = propertyEntry->getInt(10) == 0 && rejectionReason == "" && privacyOption == PropertyPrivacyOption::Public;
this->LastUpdatedTime = propertyEntry->getUInt64(11);
this->claimedTime = propertyEntry->getUInt64(12);
this->rejectionReason = propertyEntry->getString(13).asStdString();
this->reputation = propertyEntry->getUInt(14);
Load(); Load();
} }
@ -152,12 +157,18 @@ void PropertyManagementComponent::SetPrivacyOption(PropertyPrivacyOption value)
value = PropertyPrivacyOption::Private; value = PropertyPrivacyOption::Private;
} }
if (value == PropertyPrivacyOption::Public && privacyOption != PropertyPrivacyOption::Public) {
rejectionReason = "";
moderatorRequested = true;
}
privacyOption = value; privacyOption = value;
auto* propertyUpdate = Database::CreatePreppedStmt("UPDATE properties SET privacy_option = ? WHERE id = ?;"); auto* propertyUpdate = Database::CreatePreppedStmt("UPDATE properties SET privacy_option = ?, rejection_reason = ?, mod_approved = ? WHERE id = ?;");
propertyUpdate->setInt(1, static_cast<int32_t>(value)); propertyUpdate->setInt(1, static_cast<int32_t>(value));
propertyUpdate->setInt64(2, propertyId); propertyUpdate->setString(2, "");
propertyUpdate->setInt(3, 0);
propertyUpdate->setInt64(4, propertyId);
propertyUpdate->executeUpdate(); propertyUpdate->executeUpdate();
} }
@ -181,38 +192,46 @@ void PropertyManagementComponent::UpdatePropertyDetails(std::string name, std::s
OnQueryPropertyData(GetOwner(), UNASSIGNED_SYSTEM_ADDRESS); OnQueryPropertyData(GetOwner(), UNASSIGNED_SYSTEM_ADDRESS);
} }
void PropertyManagementComponent::Claim(const LWOOBJID playerId) bool PropertyManagementComponent::Claim(const LWOOBJID playerId)
{ {
if (owner != LWOOBJID_EMPTY) if (owner != LWOOBJID_EMPTY)
{ {
return; return false;
} }
SetOwnerId(playerId);
auto* zone = dZoneManager::Instance()->GetZone();
const auto& worldId = zone->GetZoneID();
const auto zoneId = worldId.GetMapID();
const auto cloneId = worldId.GetCloneID();
auto* entity = EntityManager::Instance()->GetEntity(playerId); auto* entity = EntityManager::Instance()->GetEntity(playerId);
auto* user = entity->GetParentUser(); auto* user = entity->GetParentUser();
auto character = entity->GetCharacter();
if (!character) return false;
auto* zone = dZoneManager::Instance()->GetZone();
const auto& worldId = zone->GetZoneID();
const auto propertyZoneId = worldId.GetMapID();
const auto propertyCloneId = worldId.GetCloneID();
const auto playerCloneId = character->GetPropertyCloneID();
// If we are not on our clone do not allow us to claim the property
if (propertyCloneId != playerCloneId) return false;
SetOwnerId(playerId);
propertyId = ObjectIDManager::GenerateRandomObjectID(); propertyId = ObjectIDManager::GenerateRandomObjectID();
auto* insertion = Database::CreatePreppedStmt( auto* insertion = Database::CreatePreppedStmt(
"INSERT INTO properties" "INSERT INTO properties"
"(id, owner_id, template_id, clone_id, name, description, rent_amount, rent_due, privacy_option, last_updated, time_claimed, rejection_reason, reputation, zone_id)" "(id, owner_id, template_id, clone_id, name, description, rent_amount, rent_due, privacy_option, last_updated, time_claimed, rejection_reason, reputation, zone_id, performance_cost)"
"VALUES (?, ?, ?, ?, ?, '', 0, 0, 0, UNIX_TIMESTAMP(), UNIX_TIMESTAMP(), '', 0, ?)" "VALUES (?, ?, ?, ?, ?, '', 0, 0, 0, UNIX_TIMESTAMP(), UNIX_TIMESTAMP(), '', 0, ?, 0.0)"
); );
insertion->setUInt64(1, propertyId); insertion->setUInt64(1, propertyId);
insertion->setUInt64(2, (uint32_t) playerId); insertion->setUInt64(2, (uint32_t) playerId);
insertion->setUInt(3, templateId); insertion->setUInt(3, templateId);
insertion->setUInt64(4, cloneId); insertion->setUInt64(4, playerCloneId);
insertion->setString(5, zone->GetZoneName().c_str()); insertion->setString(5, zone->GetZoneName().c_str());
insertion->setInt(6, zoneId); insertion->setInt(6, propertyZoneId);
// Try and execute the query, print an error if it fails. // Try and execute the query, print an error if it fails.
try try
@ -224,12 +243,14 @@ void PropertyManagementComponent::Claim(const LWOOBJID playerId)
Game::logger->Log("PropertyManagementComponent", "Failed to execute query: (%s)!\n", exception.what()); Game::logger->Log("PropertyManagementComponent", "Failed to execute query: (%s)!\n", exception.what());
throw exception; throw exception;
return false;
} }
auto* zoneControlObject = dZoneManager::Instance()->GetZoneControlObject(); auto* zoneControlObject = dZoneManager::Instance()->GetZoneControlObject();
for (CppScripts::Script* script : CppScripts::GetEntityScripts(zoneControlObject)) { for (CppScripts::Script* script : CppScripts::GetEntityScripts(zoneControlObject)) {
script->OnZonePropertyRented(zoneControlObject, entity); script->OnZonePropertyRented(zoneControlObject, entity);
} }
return true;
} }
void PropertyManagementComponent::OnStartBuilding() void PropertyManagementComponent::OnStartBuilding()
@ -275,6 +296,8 @@ void PropertyManagementComponent::OnFinishBuilding()
SetPrivacyOption(originalPrivacyOption); SetPrivacyOption(originalPrivacyOption);
UpdateApprovedStatus(false); UpdateApprovedStatus(false);
Save();
} }
void PropertyManagementComponent::UpdateModelPosition(const LWOOBJID id, const NiPoint3 position, NiQuaternion rotation) void PropertyManagementComponent::UpdateModelPosition(const LWOOBJID id, const NiPoint3 position, NiQuaternion rotation)
@ -388,6 +411,9 @@ void PropertyManagementComponent::UpdateModelPosition(const LWOOBJID id, const N
EntityManager::Instance()->GetZoneControlEntity()->OnZonePropertyModelPlaced(entity); EntityManager::Instance()->GetZoneControlEntity()->OnZonePropertyModelPlaced(entity);
}); });
// Progress place model missions
auto missionComponent = entity->GetComponent<MissionComponent>();
if (missionComponent != nullptr) missionComponent->Progress(MissionTaskType::MISSION_TASK_TYPE_PLACE_MODEL, 0);
} }
void PropertyManagementComponent::DeleteModel(const LWOOBJID id, const int deleteReason) void PropertyManagementComponent::DeleteModel(const LWOOBJID id, const int deleteReason)
@ -465,7 +491,7 @@ void PropertyManagementComponent::DeleteModel(const LWOOBJID id, const int delet
settings.push_back(propertyObjectID); settings.push_back(propertyObjectID);
settings.push_back(modelType); settings.push_back(modelType);
inventoryComponent->AddItem(6662, 1, HIDDEN, settings, LWOOBJID_EMPTY, false, false, spawnerId, INVALID, 13, false, -1); inventoryComponent->AddItem(6662, 1, eLootSourceType::LOOT_SOURCE_DELETION, eInventoryType::HIDDEN, settings, LWOOBJID_EMPTY, false, false, spawnerId);
auto* item = inventoryComponent->FindItemBySubKey(spawnerId); auto* item = inventoryComponent->FindItemBySubKey(spawnerId);
if (item == nullptr) { if (item == nullptr) {
@ -500,7 +526,7 @@ void PropertyManagementComponent::DeleteModel(const LWOOBJID id, const int delet
return; return;
} }
inventoryComponent->AddItem(model->GetLOT(), 1, INVALID, {}, LWOOBJID_EMPTY, false); inventoryComponent->AddItem(model->GetLOT(), 1, eLootSourceType::LOOT_SOURCE_DELETION, INVALID, {}, LWOOBJID_EMPTY, false);
auto* item = inventoryComponent->FindItemByLot(model->GetLOT()); auto* item = inventoryComponent->FindItemByLot(model->GetLOT());
@ -682,9 +708,12 @@ void PropertyManagementComponent::Save()
auto* remove = Database::CreatePreppedStmt("DELETE FROM properties_contents WHERE id = ?;"); auto* remove = Database::CreatePreppedStmt("DELETE FROM properties_contents WHERE id = ?;");
lookup->setUInt64(1, propertyId); lookup->setUInt64(1, propertyId);
sql::ResultSet* lookupResult = nullptr;
auto* lookupResult = lookup->executeQuery(); try {
lookupResult = lookup->executeQuery();
} catch (sql::SQLException& ex) {
Game::logger->Log("PropertyManagementComponent", "lookup error %s\n", ex.what());
}
std::vector<LWOOBJID> present; std::vector<LWOOBJID> present;
while (lookupResult->next()) while (lookupResult->next())
@ -727,8 +756,11 @@ void PropertyManagementComponent::Save()
insertion->setDouble(9, rotation.y); insertion->setDouble(9, rotation.y);
insertion->setDouble(10, rotation.z); insertion->setDouble(10, rotation.z);
insertion->setDouble(11, rotation.w); insertion->setDouble(11, rotation.w);
try {
insertion->execute(); insertion->execute();
} catch (sql::SQLException& ex) {
Game::logger->Log("PropertyManagementComponent", "Error inserting into properties_contents. Error %s\n", ex.what());
}
} }
else else
{ {
@ -741,8 +773,11 @@ void PropertyManagementComponent::Save()
update->setDouble(7, rotation.w); update->setDouble(7, rotation.w);
update->setInt64(8, id); update->setInt64(8, id);
try {
update->executeUpdate(); update->executeUpdate();
} catch (sql::SQLException& ex) {
Game::logger->Log("PropertyManagementComponent", "Error updating properties_contents. Error: %s\n", ex.what());
}
} }
} }
@ -754,8 +789,11 @@ void PropertyManagementComponent::Save()
} }
remove->setInt64(1, id); remove->setInt64(1, id);
try {
remove->execute(); remove->execute();
} catch (sql::SQLException& ex) {
Game::logger->Log("PropertyManagementComponent", "Error removing from properties_contents. Error %s\n", ex.what());
}
} }
auto* removeUGC = Database::CreatePreppedStmt("DELETE FROM ugc WHERE id NOT IN (SELECT ugc_id FROM properties_contents);"); auto* removeUGC = Database::CreatePreppedStmt("DELETE FROM ugc WHERE id NOT IN (SELECT ugc_id FROM properties_contents);");
@ -779,7 +817,7 @@ PropertyManagementComponent* PropertyManagementComponent::Instance()
return instance; return instance;
} }
void PropertyManagementComponent::OnQueryPropertyData(Entity* originator, const SystemAddress& sysAddr, LWOOBJID author) const void PropertyManagementComponent::OnQueryPropertyData(Entity* originator, const SystemAddress& sysAddr, LWOOBJID author)
{ {
if (author == LWOOBJID_EMPTY) { if (author == LWOOBJID_EMPTY) {
author = m_Parent->GetObjectID(); author = m_Parent->GetObjectID();
@ -818,18 +856,44 @@ void PropertyManagementComponent::OnQueryPropertyData(Entity* originator, const
description = propertyDescription; description = propertyDescription;
claimed = claimedTime; claimed = claimedTime;
privacy = static_cast<char>(this->privacyOption); privacy = static_cast<char>(this->privacyOption);
} if (moderatorRequested) {
auto checkStatus = Database::CreatePreppedStmt("SELECT rejection_reason, mod_approved FROM properties WHERE id = ?;");
checkStatus->setInt64(1, propertyId);
auto result = checkStatus->executeQuery();
result->next();
const auto reason = result->getString(1).asStdString();;
const auto modApproved = result->getInt(2);
if (reason != "") {
moderatorRequested = false;
rejectionReason = reason;
} else if (reason == "" && modApproved == 1) {
moderatorRequested = false;
rejectionReason = "";
} else {
moderatorRequested = true;
rejectionReason = "";
}
}
}
message.moderatorRequested = moderatorRequested;
message.reputation = reputation;
message.LastUpdatedTime = LastUpdatedTime;
message.OwnerId = ownerId; message.OwnerId = ownerId;
message.OwnerName = ownerName; message.OwnerName = ownerName;
message.Name = name; message.Name = name;
message.Description = description; message.Description = description;
message.ClaimedTime = claimed; message.ClaimedTime = claimed;
message.PrivacyOption = privacy; message.PrivacyOption = privacy;
message.cloneId = clone_Id;
message.rejectionReason = rejectionReason;
message.Paths = GetPaths(); message.Paths = GetPaths();
SendDownloadPropertyData(author, message, UNASSIGNED_SYSTEM_ADDRESS); SendDownloadPropertyData(author, message, UNASSIGNED_SYSTEM_ADDRESS);
// send rejection here?
} }
void PropertyManagementComponent::OnUse(Entity* originator) void PropertyManagementComponent::OnUse(Entity* originator)

View File

@ -1,5 +1,6 @@
#pragma once #pragma once
#include <chrono>
#include "Entity.h" #include "Entity.h"
#include "Component.h" #include "Component.h"
@ -40,7 +41,7 @@ public:
* @param sysAddr the address to send game message responses to * @param sysAddr the address to send game message responses to
* @param author optional explicit ID for the property, if not set defaults to the originator * @param author optional explicit ID for the property, if not set defaults to the originator
*/ */
void OnQueryPropertyData(Entity* originator, const SystemAddress& sysAddr, LWOOBJID author = LWOOBJID_EMPTY) const; void OnQueryPropertyData(Entity* originator, const SystemAddress& sysAddr, LWOOBJID author = LWOOBJID_EMPTY);
/** /**
* Handles an OnUse event, telling the client who owns this property, etc. * Handles an OnUse event, telling the client who owns this property, etc.
@ -100,8 +101,10 @@ public:
/** /**
* Makes this property owned by the passed player ID, storing it in the database * Makes this property owned by the passed player ID, storing it in the database
* @param playerId the ID of the entity that claimed the property * @param playerId the ID of the entity that claimed the property
*
* @return If the claim is successful return true.
*/ */
void Claim(LWOOBJID playerId); bool Claim(LWOOBJID playerId);
/** /**
* Event triggered when the owner of the property starts building, will kick other entities out * Event triggered when the owner of the property starts building, will kick other entities out
@ -158,6 +161,8 @@ public:
*/ */
const std::map<LWOOBJID, LWOOBJID>& GetModels() const; const std::map<LWOOBJID, LWOOBJID>& GetModels() const;
LWOCLONEID GetCloneId() { return clone_Id; };
private: private:
/** /**
* This * This
@ -182,7 +187,7 @@ private:
/** /**
* The time since this property was claimed * The time since this property was claimed
*/ */
uint64_t claimedTime = 0; uint64_t claimedTime = std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch()).count();
/** /**
* The models that are placed on this property * The models that are placed on this property
@ -194,11 +199,36 @@ private:
*/ */
std::string propertyName = ""; std::string propertyName = "";
/**
* The clone ID of this property
*/
LWOCLONEID clone_Id = 0;
/**
* Whether a moderator was requested
*/
bool moderatorRequested = false;
/**
* The rejection reason for the property
*/
std::string rejectionReason = "";
/** /**
* The description of this property * The description of this property
*/ */
std::string propertyDescription = ""; std::string propertyDescription = "";
/**
* The reputation of this property
*/
uint32_t reputation = 0;
/**
* The last time this property was updated
*/
uint32_t LastUpdatedTime = std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch()).count();
/** /**
* Determines which players may visit this property * Determines which players may visit this property
*/ */

View File

@ -41,14 +41,17 @@ void PropertyVendorComponent::OnBuyFromVendor(Entity* originator, const bool con
{ {
if (PropertyManagementComponent::Instance() == nullptr) return; if (PropertyManagementComponent::Instance() == nullptr) return;
if (PropertyManagementComponent::Instance()->Claim(originator->GetObjectID()) == false) {
Game::logger->Log("PropertyVendorComponent", "FAILED TO CLAIM PROPERTY. PLAYER ID IS %llu\n", originator->GetObjectID());
return;
}
GameMessages::SendPropertyRentalResponse(m_Parent->GetObjectID(), 0, 0, 0, 0, originator->GetSystemAddress()); GameMessages::SendPropertyRentalResponse(m_Parent->GetObjectID(), 0, 0, 0, 0, originator->GetSystemAddress());
auto* controller = dZoneManager::Instance()->GetZoneControlObject(); auto* controller = dZoneManager::Instance()->GetZoneControlObject();
controller->OnFireEventServerSide(m_Parent, "propertyRented"); controller->OnFireEventServerSide(m_Parent, "propertyRented");
PropertyManagementComponent::Instance()->Claim(originator->GetObjectID());
PropertyManagementComponent::Instance()->SetOwner(originator); PropertyManagementComponent::Instance()->SetOwner(originator);
PropertyManagementComponent::Instance()->OnQueryPropertyData(originator, originator->GetSystemAddress()); PropertyManagementComponent::Instance()->OnQueryPropertyData(originator, originator->GetSystemAddress());

View File

@ -15,6 +15,7 @@
#include "Player.h" #include "Player.h"
#include "PossessableComponent.h" #include "PossessableComponent.h"
#include "PossessorComponent.h" #include "PossessorComponent.h"
#include "RacingTaskParam.h"
#include "Spawner.h" #include "Spawner.h"
#include "VehiclePhysicsComponent.h" #include "VehiclePhysicsComponent.h"
#include "dServer.h" #include "dServer.h"
@ -52,6 +53,11 @@ RacingControlComponent::RacingControlComponent(Entity *parent)
m_MainWorld = 1200; m_MainWorld = 1200;
break; break;
case 1261:
m_ActivityID = 60;
m_MainWorld = 1260;
break;
case 1303: case 1303:
m_ActivityID = 39; m_ActivityID = 39;
m_MainWorld = 1300; m_MainWorld = 1300;
@ -401,18 +407,21 @@ void RacingControlComponent::HandleMessageBoxResponse(Entity *player,
auto *missionComponent = player->GetComponent<MissionComponent>(); auto *missionComponent = player->GetComponent<MissionComponent>();
if (missionComponent != nullptr) { if (missionComponent == nullptr) return;
missionComponent->Progress(
MissionTaskType::MISSION_TASK_TYPE_RACING, 0, 13); // Enter race missionComponent->Progress(MissionTaskType::MISSION_TASK_TYPE_RACING, 0, (LWOOBJID)RacingTaskParam::RACING_TASK_PARAM_COMPETED_IN_RACE); // Progress task for competing in a race
missionComponent->Progress( missionComponent->Progress(MissionTaskType::MISSION_TASK_TYPE_RACING, data->smashedTimes, (LWOOBJID)RacingTaskParam::RACING_TASK_PARAM_SAFE_DRIVER); // Finish a race without being smashed.
MissionTaskType::MISSION_TASK_TYPE_RACING, data->finished,
1); // Finish with rating, one track // If solo racing is enabled OR if there are 3 players in the race, progress placement tasks.
missionComponent->Progress( if(m_SoloRacing || m_LoadedPlayers > 2) {
MissionTaskType::MISSION_TASK_TYPE_RACING, data->finished, missionComponent->Progress(MissionTaskType::MISSION_TASK_TYPE_RACING, data->finished, (LWOOBJID)RacingTaskParam::RACING_TASK_PARAM_FINISH_WITH_PLACEMENT); // Finish in 1st place on a race
15); // Finish with rating, multiple tracks if(data->finished == 1) {
missionComponent->Progress( missionComponent->Progress(MissionTaskType::MISSION_TASK_TYPE_RACING, dZoneManager::Instance()->GetZone()->GetWorldID(), (LWOOBJID)RacingTaskParam::RACING_TASK_PARAM_FIRST_PLACE_MULTIPLE_TRACKS); // Finish in 1st place on multiple tracks.
MissionTaskType::MISSION_TASK_TYPE_RACING, data->smashedTimes, missionComponent->Progress(MissionTaskType::MISSION_TASK_TYPE_RACING, dZoneManager::Instance()->GetZone()->GetWorldID(), (LWOOBJID)RacingTaskParam::RACING_TASK_PARAM_WIN_RACE_IN_WORLD); // Finished first place in specific world.
10); // Safe driver type missions }
if (data->finished == m_LoadedPlayers) {
missionComponent->Progress(MissionTaskType::MISSION_TASK_TYPE_RACING, dZoneManager::Instance()->GetZone()->GetWorldID(), (LWOOBJID)RacingTaskParam::RACING_TASK_PARAM_LAST_PLACE_FINISH); // Finished first place in specific world.
}
} }
} else if (id == "ACT_RACE_EXIT_THE_RACE?" || id == "Exit") { } else if (id == "ACT_RACE_EXIT_THE_RACE?" || id == "Exit") {
auto *vehicle = EntityManager::Instance()->GetEntity(data->vehicleID); auto *vehicle = EntityManager::Instance()->GetEntity(data->vehicleID);
@ -809,9 +818,7 @@ void RacingControlComponent::Update(float deltaTime) {
// Reached the start point, lapped // Reached the start point, lapped
if (respawnIndex == 0) { if (respawnIndex == 0) {
time_t lapTime = time_t lapTime = std::time(nullptr) - (player.lap == 1 ? m_StartTime : player.lapTime);
std::time(nullptr) -
(player.lap == 1 ? m_StartTime : player.lapTime);
// Cheating check // Cheating check
if (lapTime < 40) { if (lapTime < 40) {
@ -833,10 +840,9 @@ void RacingControlComponent::Update(float deltaTime) {
playerEntity->GetComponent<MissionComponent>(); playerEntity->GetComponent<MissionComponent>();
if (missionComponent != nullptr) { if (missionComponent != nullptr) {
// Lap time
missionComponent->Progress( // Progress lap time tasks
MissionTaskType::MISSION_TASK_TYPE_RACING, missionComponent->Progress(MissionTaskType::MISSION_TASK_TYPE_RACING, (lapTime)*1000, (LWOOBJID)RacingTaskParam::RACING_TASK_PARAM_LAP_TIME);
(lapTime)*1000, 2);
if (player.lap == 3) { if (player.lap == 3) {
m_Finished++; m_Finished++;
@ -852,15 +858,11 @@ void RacingControlComponent::Update(float deltaTime) {
raceTime, raceTime * 1000); raceTime, raceTime * 1000);
// Entire race time // Entire race time
missionComponent->Progress( missionComponent->Progress(MissionTaskType::MISSION_TASK_TYPE_RACING, (raceTime)*1000, (LWOOBJID)RacingTaskParam::RACING_TASK_PARAM_TOTAL_TRACK_TIME);
MissionTaskType::MISSION_TASK_TYPE_RACING,
(raceTime)*1000, 3);
auto *characterComponent = auto *characterComponent = playerEntity->GetComponent<CharacterComponent>();
playerEntity->GetComponent<CharacterComponent>();
if (characterComponent != nullptr) { if (characterComponent != nullptr) {
characterComponent->TrackRaceCompleted(m_Finished == characterComponent->TrackRaceCompleted(m_Finished == 1);
1);
} }
// TODO: Figure out how to update the GUI leaderboard. // TODO: Figure out how to update the GUI leaderboard.

View File

@ -146,7 +146,7 @@ public:
void HandleMessageBoxResponse(Entity* player, const std::string& id); void HandleMessageBoxResponse(Entity* player, const std::string& id);
/** /**
* Get the reacing data from a player's LWOOBJID. * Get the racing data from a player's LWOOBJID.
*/ */
RacingPlayerInfo* GetPlayerData(LWOOBJID playerID); RacingPlayerInfo* GetPlayerData(LWOOBJID playerID);

View File

@ -45,7 +45,14 @@ void RebuildComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitia
outBitStream->Write(false); outBitStream->Write(false);
} }
// If build state is completed and we've already serialized once in the completed state,
// don't serializing this component anymore as this will cause the build to jump again.
// If state changes, serialization will begin again.
if (!m_StateDirty && m_State == REBUILD_COMPLETED) {
outBitStream->Write0();
outBitStream->Write0();
return;
}
// BEGIN Scripted Activity // BEGIN Scripted Activity
outBitStream->Write1(); outBitStream->Write1();
@ -79,6 +86,7 @@ void RebuildComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitia
outBitStream->Write(m_ActivatorPosition); outBitStream->Write(m_ActivatorPosition);
outBitStream->Write(m_RepositionPlayer); outBitStream->Write(m_RepositionPlayer);
} }
m_StateDirty = false;
} }
void RebuildComponent::Update(float deltaTime) { void RebuildComponent::Update(float deltaTime) {
@ -139,7 +147,6 @@ void RebuildComponent::Update(float deltaTime) {
} }
if (m_Timer >= m_ResetTime) { if (m_Timer >= m_ResetTime) {
m_Builder = LWOOBJID_EMPTY;
GameMessages::SendDieNoImplCode(m_Parent, LWOOBJID_EMPTY, LWOOBJID_EMPTY, eKillType::VIOLENT, u"", 0.0f, 0.0f, 0.0f, false, true); GameMessages::SendDieNoImplCode(m_Parent, LWOOBJID_EMPTY, LWOOBJID_EMPTY, eKillType::VIOLENT, u"", 0.0f, 0.0f, 0.0f, false, true);
@ -380,11 +387,11 @@ void RebuildComponent::StartRebuild(Entity* user) {
EntityManager::Instance()->SerializeEntity(user); EntityManager::Instance()->SerializeEntity(user);
GameMessages::SendRebuildNotifyState(m_Parent, m_State, eRebuildState::REBUILD_COMPLETED, user->GetObjectID()); GameMessages::SendRebuildNotifyState(m_Parent, m_State, eRebuildState::REBUILD_BUILDING, user->GetObjectID());
GameMessages::SendEnableRebuild(m_Parent, true, false, false, eFailReason::REASON_NOT_GIVEN, 0.0f, user->GetObjectID()); GameMessages::SendEnableRebuild(m_Parent, true, false, false, eFailReason::REASON_NOT_GIVEN, 0.0f, user->GetObjectID());
m_State = eRebuildState::REBUILD_BUILDING; m_State = eRebuildState::REBUILD_BUILDING;
m_StateDirty = true;
EntityManager::Instance()->SerializeEntity(m_Parent); EntityManager::Instance()->SerializeEntity(m_Parent);
auto* movingPlatform = m_Parent->GetComponent<MovingPlatformComponent>(); auto* movingPlatform = m_Parent->GetComponent<MovingPlatformComponent>();
@ -421,17 +428,18 @@ void RebuildComponent::CompleteRebuild(Entity* user) {
EntityManager::Instance()->SerializeEntity(user); EntityManager::Instance()->SerializeEntity(user);
GameMessages::SendRebuildNotifyState(m_Parent, m_State, eRebuildState::REBUILD_COMPLETED, user->GetObjectID()); GameMessages::SendRebuildNotifyState(m_Parent, m_State, eRebuildState::REBUILD_COMPLETED, user->GetObjectID());
GameMessages::SendEnableRebuild(m_Parent, false, true, false, eFailReason::REASON_NOT_GIVEN, m_ResetTime, user->GetObjectID()); GameMessages::SendPlayFXEffect(m_Parent, 507, u"create", "BrickFadeUpVisCompleteEffect", LWOOBJID_EMPTY, 0.4f, 1.0f, true);
GameMessages::SendEnableRebuild(m_Parent, false, false, true, eFailReason::REASON_NOT_GIVEN, m_ResetTime, user->GetObjectID());
GameMessages::SendTerminateInteraction(user->GetObjectID(), eTerminateType::FROM_INTERACTION, m_Parent->GetObjectID());
m_State = eRebuildState::REBUILD_COMPLETED; m_State = eRebuildState::REBUILD_COMPLETED;
m_StateDirty = true;
m_Timer = 0.0f; m_Timer = 0.0f;
m_DrainedImagination = 0; m_DrainedImagination = 0;
EntityManager::Instance()->SerializeEntity(m_Parent); EntityManager::Instance()->SerializeEntity(m_Parent);
GameMessages::SendPlayFXEffect(m_Parent, 507, u"create", "BrickFadeUpVisCompleteEffect", LWOOBJID_EMPTY, 0.4f, 1.0f, true);
GameMessages::SendTerminateInteraction(user->GetObjectID(), eTerminateType::FROM_INTERACTION, m_Parent->GetObjectID());
// Removes extra item requirements, isn't live accurate. // Removes extra item requirements, isn't live accurate.
// In live, all items were removed at the start of the quickbuild, then returned if it was cancelled. // In live, all items were removed at the start of the quickbuild, then returned if it was cancelled.
// TODO: fix? // TODO: fix?
@ -455,8 +463,6 @@ void RebuildComponent::CompleteRebuild(Entity* user) {
LootGenerator::Instance().DropActivityLoot(builder, m_Parent, m_ActivityId, 1); LootGenerator::Instance().DropActivityLoot(builder, m_Parent, m_ActivityId, 1);
} }
m_Builder = LWOOBJID_EMPTY;
// Notify scripts // Notify scripts
for (auto* script : CppScripts::GetEntityScripts(m_Parent)) { for (auto* script : CppScripts::GetEntityScripts(m_Parent)) {
script->OnRebuildComplete(m_Parent, user); script->OnRebuildComplete(m_Parent, user);
@ -484,6 +490,7 @@ void RebuildComponent::CompleteRebuild(Entity* user) {
character->SetPlayerFlag(flagNumber, true); character->SetPlayerFlag(flagNumber, true);
} }
} }
GameMessages::SendPlayAnimation(user, u"rebuild-celebrate", 1.09f);
} }
void RebuildComponent::ResetRebuild(bool failed) { void RebuildComponent::ResetRebuild(bool failed) {
@ -500,13 +507,12 @@ void RebuildComponent::ResetRebuild(bool failed) {
GameMessages::SendRebuildNotifyState(m_Parent, m_State, eRebuildState::REBUILD_RESETTING, LWOOBJID_EMPTY); GameMessages::SendRebuildNotifyState(m_Parent, m_State, eRebuildState::REBUILD_RESETTING, LWOOBJID_EMPTY);
m_State = eRebuildState::REBUILD_RESETTING; m_State = eRebuildState::REBUILD_RESETTING;
m_StateDirty = true;
m_Timer = 0.0f; m_Timer = 0.0f;
m_TimerIncomplete = 0.0f; m_TimerIncomplete = 0.0f;
m_ShowResetEffect = false; m_ShowResetEffect = false;
m_DrainedImagination = 0; m_DrainedImagination = 0;
m_Builder = LWOOBJID_EMPTY;
EntityManager::Instance()->SerializeEntity(m_Parent); EntityManager::Instance()->SerializeEntity(m_Parent);
// Notify scripts and possible subscribers // Notify scripts and possible subscribers
@ -540,6 +546,7 @@ void RebuildComponent::CancelRebuild(Entity* entity, eFailReason failReason, boo
// Now update the component itself // Now update the component itself
m_State = eRebuildState::REBUILD_INCOMPLETE; m_State = eRebuildState::REBUILD_INCOMPLETE;
m_StateDirty = true;
// Notify scripts and possible subscribers // Notify scripts and possible subscribers
for (auto* script : CppScripts::GetEntityScripts(m_Parent)) for (auto* script : CppScripts::GetEntityScripts(m_Parent))

View File

@ -216,6 +216,10 @@ public:
*/ */
void CancelRebuild(Entity* builder, eFailReason failReason, bool skipChecks = false); void CancelRebuild(Entity* builder, eFailReason failReason, bool skipChecks = false);
private: private:
/**
* Whether or not the quickbuild state has been changed since we last serialized it.
*/
bool m_StateDirty = true;
/** /**
* The state the rebuild is currently in * The state the rebuild is currently in
@ -235,7 +239,7 @@ private:
/** /**
* The position that the rebuild activator is spawned at * The position that the rebuild activator is spawned at
*/ */
NiPoint3 m_ActivatorPosition {}; NiPoint3 m_ActivatorPosition = NiPoint3::ZERO;
/** /**
* The entity that represents the rebuild activator * The entity that represents the rebuild activator

View File

@ -0,0 +1,32 @@
#include "RocketLaunchLupComponent.h"
#include "RocketLaunchpadControlComponent.h"
#include "InventoryComponent.h"
#include "CharacterComponent.h"
RocketLaunchLupComponent::RocketLaunchLupComponent(Entity* parent) : Component(parent) {
m_Parent = parent;
std::string zoneString = GeneralUtils::UTF16ToWTF8(m_Parent->GetVar<std::u16string>(u"MultiZoneIDs"));
std::stringstream ss(zoneString);
for (int i; ss >> i;) {
m_LUPWorlds.push_back(i);
if (ss.peek() == ';')
ss.ignore();
}
}
RocketLaunchLupComponent::~RocketLaunchLupComponent() {}
void RocketLaunchLupComponent::OnUse(Entity* originator) {
auto* rocket = originator->GetComponent<CharacterComponent>()->RocketEquip(originator);
if (!rocket) return;
// the LUP world menu is just the property menu, the client knows how to handle it
GameMessages::SendPropertyEntranceBegin(m_Parent->GetObjectID(), m_Parent->GetSystemAddress());
}
void RocketLaunchLupComponent::OnSelectWorld(Entity* originator, uint32_t index) {
auto* rocketLaunchpadControlComponent = m_Parent->GetComponent<RocketLaunchpadControlComponent>();
if (!rocketLaunchpadControlComponent) return;
rocketLaunchpadControlComponent->Launch(originator, m_LUPWorlds[index], 0);
}

View File

@ -0,0 +1,39 @@
#pragma once
#include "Entity.h"
#include "GameMessages.h"
#include "Component.h"
/**
* Component that handles the LUP/WBL rocket launchpad that can be interacted with to travel to WBL worlds.
*
*/
class RocketLaunchLupComponent : public Component {
public:
static const uint32_t ComponentType = eReplicaComponentType::COMPONENT_TYPE_ROCKET_LAUNCH_LUP;
/**
* Constructor for this component, builds the m_LUPWorlds vector
* @param parent parent that contains this component
*/
RocketLaunchLupComponent(Entity* parent);
~RocketLaunchLupComponent() override;
/**
* Handles an OnUse event from some entity, preparing it for launch to some other world
* @param originator the entity that triggered the event
*/
void OnUse(Entity* originator) override;
/**
* Handles an OnUse event from some entity, preparing it for launch to some other world
* @param originator the entity that triggered the event
* @param index index of the world that was selected
*/
void OnSelectWorld(Entity* originator, uint32_t index);
private:
/**
* vector of the LUP World Zone IDs, built from CDServer's LUPZoneIDs table
*/
std::vector<LWOMAPID> m_LUPWorlds {};
};

View File

@ -13,6 +13,7 @@
#include "ChatPackets.h" #include "ChatPackets.h"
#include "MissionComponent.h" #include "MissionComponent.h"
#include "PropertyEntranceComponent.h" #include "PropertyEntranceComponent.h"
#include "RocketLaunchLupComponent.h"
#include "dServer.h" #include "dServer.h"
#include "dMessageIdentifiers.h" #include "dMessageIdentifiers.h"
#include "PacketUtils.h" #include "PacketUtils.h"
@ -40,19 +41,7 @@ RocketLaunchpadControlComponent::~RocketLaunchpadControlComponent() {
delete m_AltPrecondition; delete m_AltPrecondition;
} }
void RocketLaunchpadControlComponent::RocketEquip(Entity* entity, LWOOBJID rocketID) { void RocketLaunchpadControlComponent::Launch(Entity* originator, LWOMAPID mapId, LWOCLONEID cloneId) {
if (m_PlayersInRadius.find(entity->GetObjectID()) != m_PlayersInRadius.end()) {
Launch(entity, rocketID);
//Go ahead and save the player
//This causes a double-save, but it should prevent players from not being saved
//before the next world server starts loading their data.
if (entity->GetCharacter())
entity->GetCharacter()->SaveXMLToDatabase();
}
}
void RocketLaunchpadControlComponent::Launch(Entity* originator, LWOOBJID optionalRocketID, LWOMAPID mapId, LWOCLONEID cloneId) {
auto zone = mapId == LWOMAPID_INVALID ? m_TargetZone : mapId; auto zone = mapId == LWOMAPID_INVALID ? m_TargetZone : mapId;
if (zone == 0) if (zone == 0)
@ -60,53 +49,22 @@ void RocketLaunchpadControlComponent::Launch(Entity* originator, LWOOBJID option
return; return;
} }
TellMasterToPrepZone(zone);
// This also gets triggered by a proximity monitor + item equip, I will set that up when havok is ready // This also gets triggered by a proximity monitor + item equip, I will set that up when havok is ready
auto* inventoryComponent = originator->GetComponent<InventoryComponent>();
auto* characterComponent = originator->GetComponent<CharacterComponent>(); auto* characterComponent = originator->GetComponent<CharacterComponent>();
auto* character = originator->GetCharacter(); auto* character = originator->GetCharacter();
if (inventoryComponent == nullptr || characterComponent == nullptr || character == nullptr) { if (!characterComponent || !character) return;
auto* rocket = characterComponent->GetRocket(originator);
if (!rocket) {
Game::logger->Log("RocketLaunchpadControlComponent", "Unable to find rocket!\n");
return; return;
} }
// Select the rocket // we have the ability to launch, so now we prep the zone
TellMasterToPrepZone(zone);
Item* rocket = nullptr;
if (optionalRocketID != LWOOBJID_EMPTY)
{
rocket = inventoryComponent->FindItemById(optionalRocketID);
}
if (rocket == nullptr)
{
rocket = inventoryComponent->FindItemById(characterComponent->GetLastRocketItemID());
}
if (rocket == nullptr)
{
rocket = inventoryComponent->FindItemByLot(6416);
}
if (rocket == nullptr)
{
Game::logger->Log("RocketLaunchpadControlComponent", "Unable to find rocket (%llu)!\n", optionalRocketID);
return;
}
if (rocket->GetConfig().empty()) // Sanity check
{
rocket->SetCount(0, false, false);
return;
}
// Achievement unlocked: "All zones unlocked" // Achievement unlocked: "All zones unlocked"
if (!m_AltLandingScene.empty() && m_AltPrecondition->Check(originator)) { if (!m_AltLandingScene.empty() && m_AltPrecondition->Check(originator)) {
character->SetTargetScene(m_AltLandingScene); character->SetTargetScene(m_AltLandingScene);
} }
@ -114,27 +72,6 @@ void RocketLaunchpadControlComponent::Launch(Entity* originator, LWOOBJID option
character->SetTargetScene(m_TargetScene); character->SetTargetScene(m_TargetScene);
} }
if (characterComponent) {
for (LDFBaseData* data : rocket->GetConfig()) {
if (data->GetKey() == u"assemblyPartLOTs") {
std::string newRocketStr;
for (char character : data->GetValueAsString()) {
if (character == '+') {
newRocketStr.push_back(';');
}
else {
newRocketStr.push_back(character);
}
}
newRocketStr.push_back(';');
characterComponent->SetLastRocketConfig(GeneralUtils::ASCIIToUTF16(newRocketStr));
}
}
}
// Store the last used rocket item's ID
characterComponent->SetLastRocketItemID(rocket->GetId());
characterComponent->UpdatePlayerStatistic(RocketsUsed); characterComponent->UpdatePlayerStatistic(RocketsUsed);
character->SaveXMLToDatabase(); character->SaveXMLToDatabase();
@ -143,23 +80,31 @@ void RocketLaunchpadControlComponent::Launch(Entity* originator, LWOOBJID option
GameMessages::SendFireEventClientSide(m_Parent->GetObjectID(), originator->GetSystemAddress(), u"RocketEquipped", rocket->GetId(), cloneId, -1, originator->GetObjectID()); GameMessages::SendFireEventClientSide(m_Parent->GetObjectID(), originator->GetSystemAddress(), u"RocketEquipped", rocket->GetId(), cloneId, -1, originator->GetObjectID());
rocket->Equip(true);
GameMessages::SendChangeObjectWorldState(rocket->GetId(), WORLDSTATE_ATTACHED, UNASSIGNED_SYSTEM_ADDRESS); GameMessages::SendChangeObjectWorldState(rocket->GetId(), WORLDSTATE_ATTACHED, UNASSIGNED_SYSTEM_ADDRESS);
EntityManager::Instance()->SerializeEntity(originator); EntityManager::Instance()->SerializeEntity(originator);
} }
void RocketLaunchpadControlComponent::OnUse(Entity* originator) { void RocketLaunchpadControlComponent::OnUse(Entity* originator) {
// If we are have the property or the LUP component, we don't want to immediately launch
// instead we let their OnUse handlers do their things
// which components of an Object have their OnUse called when using them
// so we don't need to call it here
auto* propertyEntrance = m_Parent->GetComponent<PropertyEntranceComponent>(); auto* propertyEntrance = m_Parent->GetComponent<PropertyEntranceComponent>();
if (propertyEntrance) {
if (propertyEntrance != nullptr)
{
propertyEntrance->OnUse(originator);
return; return;
} }
auto* rocketLaunchLUP = m_Parent->GetComponent<RocketLaunchLupComponent>();
if (rocketLaunchLUP) {
return;
}
// No rocket no launch
auto* rocket = originator->GetComponent<CharacterComponent>()->RocketEquip(originator);
if (!rocket) {
return;
}
Launch(originator); Launch(originator);
} }

View File

@ -22,21 +22,13 @@ public:
RocketLaunchpadControlComponent(Entity* parent, int rocketId); RocketLaunchpadControlComponent(Entity* parent, int rocketId);
~RocketLaunchpadControlComponent() override; ~RocketLaunchpadControlComponent() override;
/**
* Launches the passed entity using the passed rocket and saves their data
* @param entity the entity to launch
* @param rocketID the ID of the rocket to use
*/
void RocketEquip(Entity* entity, LWOOBJID rocketID);
/** /**
* Launches some entity to another world * Launches some entity to another world
* @param originator the entity to launch * @param originator the entity to launch
* @param optionalRocketID the ID of the rocket to launch
* @param mapId the world to go to * @param mapId the world to go to
* @param cloneId the clone ID (for properties) * @param cloneId the clone ID (for properties)
*/ */
void Launch(Entity* originator, LWOOBJID optionalRocketID = LWOOBJID_EMPTY, LWOMAPID mapId = LWOMAPID_INVALID, LWOCLONEID cloneId = LWOCLONEID_INVALID); void Launch(Entity* originator, LWOMAPID mapId = LWOMAPID_INVALID, LWOCLONEID cloneId = LWOCLONEID_INVALID);
/** /**
* Handles an OnUse event from some entity, preparing it for launch to some other world * Handles an OnUse event from some entity, preparing it for launch to some other world

View File

@ -28,7 +28,7 @@ ScriptedActivityComponent::ScriptedActivityComponent(Entity* parent, int activit
const auto mapID = m_ActivityInfo.instanceMapID; const auto mapID = m_ActivityInfo.instanceMapID;
if ((mapID == 1203 || mapID == 1303 || mapID == 1403) && Game::config->GetValue("solo_racing") == "1") { if ((mapID == 1203 || mapID == 1261 || mapID == 1303 || mapID == 1403) && Game::config->GetValue("solo_racing") == "1") {
m_ActivityInfo.minTeamSize = 1; m_ActivityInfo.minTeamSize = 1;
m_ActivityInfo.minTeams = 1; m_ActivityInfo.minTeams = 1;
} }

View File

@ -17,6 +17,17 @@ SimplePhysicsComponent::SimplePhysicsComponent(uint32_t componentID, Entity* par
m_Position = m_Parent->GetDefaultPosition(); m_Position = m_Parent->GetDefaultPosition();
m_Rotation = m_Parent->GetDefaultRotation(); m_Rotation = m_Parent->GetDefaultRotation();
m_IsDirty = true; m_IsDirty = true;
const auto& climbable_type = m_Parent->GetVar<std::u16string>(u"climbable");
if (climbable_type == u"wall") {
SetClimbableType(eClimbableType::CLIMBABLE_TYPE_WALL);
} else if (climbable_type == u"ladder") {
SetClimbableType(eClimbableType::CLIMBABLE_TYPE_LADDER);
} else if (climbable_type == u"wallstick") {
SetClimbableType(eClimbableType::CLIMBABLE_TYPE_WALL_STICK);
} else {
SetClimbableType(eClimbableType::CLIMBABLE_TYPE_NOT);
}
} }
SimplePhysicsComponent::~SimplePhysicsComponent() { SimplePhysicsComponent::~SimplePhysicsComponent() {
@ -24,8 +35,8 @@ SimplePhysicsComponent::~SimplePhysicsComponent() {
void SimplePhysicsComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) { void SimplePhysicsComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) {
if (bIsInitialUpdate) { if (bIsInitialUpdate) {
outBitStream->Write0(); // climbable outBitStream->Write(m_ClimbableType != eClimbableType::CLIMBABLE_TYPE_NOT);
outBitStream->Write<int32_t>(0); // climbableType outBitStream->Write(m_ClimbableType);
} }
outBitStream->Write(m_DirtyVelocity || bIsInitialUpdate); outBitStream->Write(m_DirtyVelocity || bIsInitialUpdate);

View File

@ -14,6 +14,14 @@
class Entity; class Entity;
enum class eClimbableType : int32_t {
CLIMBABLE_TYPE_NOT = 0,
CLIMBABLE_TYPE_LADDER,
CLIMBABLE_TYPE_WALL,
CLIMBABLE_TYPE_WALL_STICK
};
/** /**
* Component that serializes locations of entities to the client * Component that serializes locations of entities to the client
*/ */
@ -86,6 +94,18 @@ public:
*/ */
void SetPhysicsMotionState(uint32_t value); void SetPhysicsMotionState(uint32_t value);
/**
* Returns the ClimbableType of this entity
* @return the ClimbableType of this entity
*/
const eClimbableType& GetClimabbleType() { return m_ClimbableType; }
/**
* Sets the ClimbableType of this entity
* @param value the ClimbableType to set
*/
void SetClimbableType(const eClimbableType& value) { m_ClimbableType = value; }
private: private:
/** /**
@ -122,6 +142,11 @@ private:
* The current physics motion state * The current physics motion state
*/ */
uint32_t m_PhysicsMotionState = 0; uint32_t m_PhysicsMotionState = 0;
/**
* Whether or not the entity is climbable
*/
eClimbableType m_ClimbableType = eClimbableType::CLIMBABLE_TYPE_NOT;
}; };
#endif // SIMPLEPHYSICSCOMPONENT_H #endif // SIMPLEPHYSICSCOMPONENT_H

View File

@ -25,12 +25,14 @@ ProjectileSyncEntry::ProjectileSyncEntry()
{ {
} }
bool SkillComponent::CastPlayerSkill(const uint32_t behaviorId, const uint32_t skillUid, RakNet::BitStream* bitStream, const LWOOBJID target) bool SkillComponent::CastPlayerSkill(const uint32_t behaviorId, const uint32_t skillUid, RakNet::BitStream* bitStream, const LWOOBJID target, uint32_t skillID)
{ {
auto* context = new BehaviorContext(this->m_Parent->GetObjectID()); auto* context = new BehaviorContext(this->m_Parent->GetObjectID());
context->caster = m_Parent->GetObjectID(); context->caster = m_Parent->GetObjectID();
context->skillID = skillID;
this->m_managedBehaviors.insert_or_assign(skillUid, context); this->m_managedBehaviors.insert_or_assign(skillUid, context);
auto* behavior = Behavior::CreateBehavior(behaviorId); auto* behavior = Behavior::CreateBehavior(behaviorId);

View File

@ -92,7 +92,7 @@ public:
* @param bitStream the bitSteam given by the client to determine the behavior path * @param bitStream the bitSteam given by the client to determine the behavior path
* @param target the explicit target of the skill * @param target the explicit target of the skill
*/ */
bool CastPlayerSkill(uint32_t behaviorId, uint32_t skillUid, RakNet::BitStream* bitStream, LWOOBJID target); bool CastPlayerSkill(uint32_t behaviorId, uint32_t skillUid, RakNet::BitStream* bitStream, LWOOBJID target, uint32_t skillID = 0);
/** /**
* Continues a player skill. Should only be called when the server receives a sync message from the client. * Continues a player skill. Should only be called when the server receives a sync message from the client.

View File

@ -1,88 +1,21 @@
#include "VendorComponent.h" #include "VendorComponent.h"
#include "Game.h"
#include "dServer.h"
#include <BitStream.h> #include <BitStream.h>
#include "Game.h"
#include "dServer.h"
VendorComponent::VendorComponent(Entity* parent) : Component(parent) { VendorComponent::VendorComponent(Entity* parent) : Component(parent) {
auto* compRegistryTable = CDClientManager::Instance()->GetTable<CDComponentsRegistryTable>("ComponentsRegistry"); SetupConstants();
auto* vendorComponentTable = CDClientManager::Instance()->GetTable<CDVendorComponentTable>("VendorComponent"); RefreshInventory(true);
auto* lootMatrixTable = CDClientManager::Instance()->GetTable<CDLootMatrixTable>("LootMatrix");
auto* lootTableTable = CDClientManager::Instance()->GetTable<CDLootTableTable>("LootTable");
int componentID = compRegistryTable->GetByIDAndType(m_Parent->GetLOT(), COMPONENT_TYPE_VENDOR);
std::vector<CDVendorComponent> vendorComps = vendorComponentTable->Query([=](CDVendorComponent entry) { return (entry.id == componentID); });
if (vendorComps.empty()) {
return;
}
m_BuyScalar = vendorComps[0].buyScalar;
m_SellScalar = vendorComps[0].sellScalar;
int lootMatrixID = vendorComps[0].LootMatrixIndex;
std::vector<CDLootMatrix> lootMatrices = lootMatrixTable->Query([=](CDLootMatrix entry) { return (entry.LootMatrixIndex == lootMatrixID); });
if (lootMatrices.empty()) {
return;
}
for (const auto& lootMatrix : lootMatrices) {
int lootTableID = lootMatrix.LootTableIndex;
std::vector<CDLootTable> vendorItems = lootTableTable->Query([=](CDLootTable entry) { return (entry.LootTableIndex == lootTableID); });
if (lootMatrix.maxToDrop == 0 || lootMatrix.minToDrop == 0) {
for (CDLootTable item : vendorItems) {
m_Inventory.insert({item.itemid, item.sortPriority});
}
} else {
auto randomCount = GeneralUtils::GenerateRandomNumber<int32_t>(lootMatrix.minToDrop, lootMatrix.maxToDrop);
for (size_t i = 0; i < randomCount; i++) {
if (vendorItems.empty()) {
break;
}
auto randomItemIndex = GeneralUtils::GenerateRandomNumber<int32_t>(0, vendorItems.size() - 1);
const auto& randomItem = vendorItems[randomItemIndex];
vendorItems.erase(vendorItems.begin() + randomItemIndex);
m_Inventory.insert({randomItem.itemid, randomItem.sortPriority});
}
}
}
//Because I want a vendor to sell these cameras
if (parent->GetLOT() == 13569) {
auto randomCamera = GeneralUtils::GenerateRandomNumber<int32_t>(0, 2);
switch (randomCamera) {
case 0:
m_Inventory.insert({16253, 0}); //Grungagroid
break;
case 1:
m_Inventory.insert({16254, 0}); //Hipstabrick
break;
case 2:
m_Inventory.insert({16204, 0}); //Megabrixel snapshot
break;
default:
break;
}
}
//Custom code for Max vanity NPC
if (parent->GetLOT() == 9749 && Game::server->GetZoneID() == 1201) {
m_Inventory.clear();
m_Inventory.insert({11909, 0}); //Top hat w frog
m_Inventory.insert({7785, 0}); //Flash bulb
m_Inventory.insert({12764, 0}); //Big fountain soda
m_Inventory.insert({12241, 0}); //Hot cocoa (from fb)
}
} }
VendorComponent::~VendorComponent() = default; VendorComponent::~VendorComponent() = default;
void VendorComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) { void VendorComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) {
outBitStream->Write1(); outBitStream->Write1();
outBitStream->Write1(); // this bit is REQUIRED for vendor + mission multiinteract outBitStream->Write1(); // Has standard items (Required for vendors with missions.)
outBitStream->Write(HasCraftingStation()); outBitStream->Write(HasCraftingStation()); // Has multi use items
} }
void VendorComponent::OnUse(Entity* originator) { void VendorComponent::OnUse(Entity* originator) {
@ -114,3 +47,85 @@ bool VendorComponent::HasCraftingStation() {
// As far as we know, only Umami has a crafting station // As far as we know, only Umami has a crafting station
return m_Parent->GetLOT() == 13800; return m_Parent->GetLOT() == 13800;
} }
void VendorComponent::RefreshInventory(bool isCreation) {
//Custom code for Max vanity NPC
if (m_Parent->GetLOT() == 9749 && Game::server->GetZoneID() == 1201) {
if (!isCreation) return;
m_Inventory.insert({11909, 0}); //Top hat w frog
m_Inventory.insert({7785, 0}); //Flash bulb
m_Inventory.insert({12764, 0}); //Big fountain soda
m_Inventory.insert({12241, 0}); //Hot cocoa (from fb)
return;
}
m_Inventory.clear();
auto* lootMatrixTable = CDClientManager::Instance()->GetTable<CDLootMatrixTable>("LootMatrix");
std::vector<CDLootMatrix> lootMatrices = lootMatrixTable->Query([=](CDLootMatrix entry) { return (entry.LootMatrixIndex == m_LootMatrixID); });
if (lootMatrices.empty()) return;
// Done with lootMatrix table
auto* lootTableTable = CDClientManager::Instance()->GetTable<CDLootTableTable>("LootTable");
for (const auto& lootMatrix : lootMatrices) {
int lootTableID = lootMatrix.LootTableIndex;
std::vector<CDLootTable> vendorItems = lootTableTable->Query([=](CDLootTable entry) { return (entry.LootTableIndex == lootTableID); });
if (lootMatrix.maxToDrop == 0 || lootMatrix.minToDrop == 0) {
for (CDLootTable item : vendorItems) {
m_Inventory.insert({item.itemid, item.sortPriority});
}
} else {
auto randomCount = GeneralUtils::GenerateRandomNumber<int32_t>(lootMatrix.minToDrop, lootMatrix.maxToDrop);
for (size_t i = 0; i < randomCount; i++) {
if (vendorItems.empty()) break;
auto randomItemIndex = GeneralUtils::GenerateRandomNumber<int32_t>(0, vendorItems.size() - 1);
const auto& randomItem = vendorItems[randomItemIndex];
vendorItems.erase(vendorItems.begin() + randomItemIndex);
m_Inventory.insert({randomItem.itemid, randomItem.sortPriority});
}
}
}
//Because I want a vendor to sell these cameras
if (m_Parent->GetLOT() == 13569) {
auto randomCamera = GeneralUtils::GenerateRandomNumber<int32_t>(0, 2);
switch (randomCamera) {
case 0:
m_Inventory.insert({16253, 0}); //Grungagroid
break;
case 1:
m_Inventory.insert({16254, 0}); //Hipstabrick
break;
case 2:
m_Inventory.insert({16204, 0}); //Megabrixel snapshot
break;
default:
break;
}
}
// Callback timer to refresh this inventory.
m_Parent->AddCallbackTimer(m_RefreshTimeSeconds, [this]() {
RefreshInventory();
});
GameMessages::SendVendorStatusUpdate(m_Parent, UNASSIGNED_SYSTEM_ADDRESS);
}
void VendorComponent::SetupConstants() {
auto* compRegistryTable = CDClientManager::Instance()->GetTable<CDComponentsRegistryTable>("ComponentsRegistry");
int componentID = compRegistryTable->GetByIDAndType(m_Parent->GetLOT(), COMPONENT_TYPE_VENDOR);
auto* vendorComponentTable = CDClientManager::Instance()->GetTable<CDVendorComponentTable>("VendorComponent");
std::vector<CDVendorComponent> vendorComps = vendorComponentTable->Query([=](CDVendorComponent entry) { return (entry.id == componentID); });
if (vendorComps.empty()) return;
m_BuyScalar = vendorComps[0].buyScalar;
m_SellScalar = vendorComps[0].sellScalar;
m_RefreshTimeSeconds = vendorComps[0].refreshTimeSeconds;
m_LootMatrixID = vendorComps[0].LootMatrixIndex;
}

View File

@ -1,11 +1,12 @@
#pragma once
#ifndef VENDORCOMPONENT_H #ifndef VENDORCOMPONENT_H
#define VENDORCOMPONENT_H #define VENDORCOMPONENT_H
#include "RakNetTypes.h"
#include "Entity.h"
#include "GameMessages.h"
#include "CDClientManager.h" #include "CDClientManager.h"
#include "Component.h" #include "Component.h"
#include "Entity.h"
#include "GameMessages.h"
#include "RakNetTypes.h"
/** /**
* A component for vendor NPCs. A vendor sells items to the player. * A component for vendor NPCs. A vendor sells items to the player.
@ -19,7 +20,7 @@ public:
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags); void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags);
void OnUse(Entity* originator); void OnUse(Entity* originator) override;
/** /**
* Gets the buy scaler * Gets the buy scaler
@ -56,17 +57,36 @@ public:
*/ */
std::map<LOT, int>& GetInventory(); std::map<LOT, int>& GetInventory();
/**
* Refresh the inventory of this vendor.
*/
void RefreshInventory(bool isCreation = false);
/**
* Called on startup of vendor to setup the variables for the component.
*/
void SetupConstants();
private: private:
/** /**
* The buy scaler. * The buy scalar.
*/ */
float m_BuyScalar; float m_BuyScalar;
/** /**
* The sell scaler. * The sell scalar.
*/ */
float m_SellScalar; float m_SellScalar;
/**
* The refresh time of this vendors' inventory.
*/
float m_RefreshTimeSeconds;
/**
* Loot matrix id of this vendor.
*/
uint32_t m_LootMatrixID;
/** /**
* The list of items the vendor sells. * The list of items the vendor sells.
*/ */

View File

@ -285,7 +285,7 @@ void GameMessageHandler::HandleMessage(RakNet::BitStream* inStream, const System
auto* skillComponent = entity->GetComponent<SkillComponent>(); auto* skillComponent = entity->GetComponent<SkillComponent>();
success = skillComponent->CastPlayerSkill(behaviorId, startSkill.uiSkillHandle, bs, startSkill.optionalTargetID); success = skillComponent->CastPlayerSkill(behaviorId, startSkill.uiSkillHandle, bs, startSkill.optionalTargetID, startSkill.skillID);
if (success && entity->GetCharacter()) { if (success && entity->GetCharacter()) {
DestroyableComponent* destComp = entity->GetComponent<DestroyableComponent>(); DestroyableComponent* destComp = entity->GetComponent<DestroyableComponent>();
@ -595,7 +595,9 @@ void GameMessageHandler::HandleMessage(RakNet::BitStream* inStream, const System
case GAME_MSG_VEHICLE_NOTIFY_HIT_IMAGINATION_SERVER: case GAME_MSG_VEHICLE_NOTIFY_HIT_IMAGINATION_SERVER:
GameMessages::HandleVehicleNotifyHitImaginationServer(inStream, entity, sysAddr); GameMessages::HandleVehicleNotifyHitImaginationServer(inStream, entity, sysAddr);
break; break;
case GAME_MSG_UPDATE_PROPERTY_PERFORMANCE_COST:
GameMessages::HandleUpdatePropertyPerformanceCost(inStream, entity, sysAddr);
break;
// SG // SG
case GAME_MSG_UPDATE_SHOOTING_GALLERY_ROTATION: case GAME_MSG_UPDATE_SHOOTING_GALLERY_ROTATION:
GameMessages::HandleUpdateShootingGalleryRotation(inStream, entity, sysAddr); GameMessages::HandleUpdateShootingGalleryRotation(inStream, entity, sysAddr);

View File

@ -26,6 +26,7 @@
#include "TeamManager.h" #include "TeamManager.h"
#include "ChatPackets.h" #include "ChatPackets.h"
#include "GameConfig.h" #include "GameConfig.h"
#include "RocketLaunchLupComponent.h"
#include <sstream> #include <sstream>
#include <future> #include <future>
@ -411,7 +412,7 @@ void GameMessages::SendGMLevelBroadcast(const LWOOBJID& objectID, uint8_t level)
SEND_PACKET_BROADCAST SEND_PACKET_BROADCAST
} }
void GameMessages::SendAddItemToInventoryClientSync(Entity* entity, const SystemAddress& sysAddr, Item* item, const LWOOBJID& objectID, bool showFlyingLoot, int itemCount, LWOOBJID subKey) { void GameMessages::SendAddItemToInventoryClientSync(Entity* entity, const SystemAddress& sysAddr, Item* item, const LWOOBJID& objectID, bool showFlyingLoot, int itemCount, LWOOBJID subKey, eLootSourceType lootSourceType) {
CBITSTREAM CBITSTREAM
CMSGHEADER CMSGHEADER
@ -421,9 +422,8 @@ void GameMessages::SendAddItemToInventoryClientSync(Entity* entity, const System
bitStream.Write(item->GetInfo().isBOE); bitStream.Write(item->GetInfo().isBOE);
bitStream.Write(item->GetInfo().isBOP); bitStream.Write(item->GetInfo().isBOP);
bitStream.Write0(); // Loot source bitStream.Write(lootSourceType != eLootSourceType::LOOT_SOURCE_NONE); // Loot source
//if (invType != LOOTTYPE_NONE) bitStream.Write(invType); if (lootSourceType != eLootSourceType::LOOT_SOURCE_NONE) bitStream.Write(lootSourceType);
LWONameValue extraInfo; LWONameValue extraInfo;
auto config = item->GetConfig(); auto config = item->GetConfig();
@ -451,8 +451,8 @@ void GameMessages::SendAddItemToInventoryClientSync(Entity* entity, const System
auto* inventory = item->GetInventory(); auto* inventory = item->GetInventory();
const auto inventoryType = inventory->GetType(); const auto inventoryType = inventory->GetType();
bitStream.Write(inventoryType != INVENTORY_DEFAULT); bitStream.Write(inventoryType != eInventoryType::ITEMS);
if (inventoryType != INVENTORY_DEFAULT) bitStream.Write(inventoryType); if (inventoryType != eInventoryType::ITEMS) bitStream.Write(inventoryType);
bitStream.Write(itemCount != 1); bitStream.Write(itemCount != 1);
if (itemCount != 1) bitStream.Write(itemCount); if (itemCount != 1) bitStream.Write(itemCount);
@ -558,7 +558,7 @@ void GameMessages::SendNotifyMissionTask(Entity* entity, const SystemAddress& sy
SEND_PACKET SEND_PACKET
} }
void GameMessages::SendModifyLEGOScore(Entity* entity, const SystemAddress& sysAddr, int64_t score, int sourceType) { void GameMessages::SendModifyLEGOScore(Entity* entity, const SystemAddress& sysAddr, int64_t score, eLootSourceType sourceType) {
CBITSTREAM CBITSTREAM
CMSGHEADER CMSGHEADER
@ -566,9 +566,8 @@ void GameMessages::SendModifyLEGOScore(Entity* entity, const SystemAddress& sysA
bitStream.Write((uint16_t)GAME_MSG_MODIFY_LEGO_SCORE); bitStream.Write((uint16_t)GAME_MSG_MODIFY_LEGO_SCORE);
bitStream.Write(score); bitStream.Write(score);
//Stuff stolen from the old codebase, no idea why this works. The proper implementation didn't for some reason. bitStream.Write(sourceType != eLootSourceType::LOOT_SOURCE_NONE);
bitStream.Write((int32_t)129); if (sourceType != eLootSourceType::LOOT_SOURCE_NONE) bitStream.Write(sourceType);
bitStream.Write((unsigned char)0);
SEND_PACKET SEND_PACKET
} }
@ -1254,8 +1253,7 @@ void GameMessages::SendVendorOpenWindow(Entity* entity, const SystemAddress& sys
SEND_PACKET SEND_PACKET
} }
// ah yes, impl code in a send function, beautiful! void GameMessages::SendVendorStatusUpdate(Entity* entity, const SystemAddress& sysAddr, bool bUpdateOnly) {
void GameMessages::SendVendorStatusUpdate(Entity* entity, const SystemAddress& sysAddr) {
CBITSTREAM CBITSTREAM
CMSGHEADER CMSGHEADER
@ -1267,7 +1265,7 @@ void GameMessages::SendVendorStatusUpdate(Entity* entity, const SystemAddress& s
bitStream.Write(entity->GetObjectID()); bitStream.Write(entity->GetObjectID());
bitStream.Write(GAME_MSG::GAME_MSG_VENDOR_STATUS_UPDATE); bitStream.Write(GAME_MSG::GAME_MSG_VENDOR_STATUS_UPDATE);
bitStream.Write(false); bitStream.Write(bUpdateOnly);
bitStream.Write(static_cast<uint32_t>(vendorItems.size())); bitStream.Write(static_cast<uint32_t>(vendorItems.size()));
for (std::pair<LOT, int> item : vendorItems) { for (std::pair<LOT, int> item : vendorItems) {
@ -1275,6 +1273,7 @@ void GameMessages::SendVendorStatusUpdate(Entity* entity, const SystemAddress& s
bitStream.Write(static_cast<int>(item.second)); bitStream.Write(static_cast<int>(item.second));
} }
if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST
SEND_PACKET SEND_PACKET
} }
@ -1517,6 +1516,18 @@ void GameMessages::SendRequestActivityEnter(LWOOBJID objectId, const SystemAddre
SEND_PACKET SEND_PACKET
} }
void GameMessages::NotifyLevelRewards(LWOOBJID objectID, const SystemAddress& sysAddr, int level, bool sending_rewards) {
CBITSTREAM
CMSGHEADER
bitStream.Write(objectID);
bitStream.Write((uint16_t)GAME_MSG::GAME_MSG_NOTIFY_LEVEL_REWARDS);
bitStream.Write(level);
bitStream.Write(sending_rewards);
SEND_PACKET
}
void GameMessages::SendSetShootingGalleryParams(LWOOBJID objectId, const SystemAddress& sysAddr, void GameMessages::SendSetShootingGalleryParams(LWOOBJID objectId, const SystemAddress& sysAddr,
float cameraFOV, float cameraFOV,
@ -2716,10 +2727,15 @@ void GameMessages::HandleEnterProperty(RakNet::BitStream* inStream, Entity* enti
auto* player = Player::GetPlayer(sysAddr); auto* player = Player::GetPlayer(sysAddr);
auto* entranceComponent = entity->GetComponent<PropertyEntranceComponent>(); auto* entranceComponent = entity->GetComponent<PropertyEntranceComponent>();
if (entranceComponent != nullptr) {
if (entranceComponent == nullptr) return;
entranceComponent->OnEnterProperty(player, index, returnToZone, sysAddr); entranceComponent->OnEnterProperty(player, index, returnToZone, sysAddr);
return;
}
auto rocketLaunchLupComponent = entity->GetComponent<RocketLaunchLupComponent>();
if (rocketLaunchLupComponent != nullptr) {
rocketLaunchLupComponent->OnSelectWorld(player, index);
}
} }
void GameMessages::HandleSetConsumableItem(RakNet::BitStream* inStream, Entity* entity, const SystemAddress& sysAddr) void GameMessages::HandleSetConsumableItem(RakNet::BitStream* inStream, Entity* entity, const SystemAddress& sysAddr)
@ -3227,12 +3243,12 @@ void GameMessages::HandleClientTradeRequest(RakNet::BitStream* inStream, Entity*
void GameMessages::HandleClientTradeCancel(RakNet::BitStream* inStream, Entity* entity, const SystemAddress& sysAddr) void GameMessages::HandleClientTradeCancel(RakNet::BitStream* inStream, Entity* entity, const SystemAddress& sysAddr)
{ {
Game::logger->Log("GameMessages", "Trade canceled from (%llu)\n", entity->GetObjectID());
auto* trade = TradingManager::Instance()->GetPlayerTrade(entity->GetObjectID()); auto* trade = TradingManager::Instance()->GetPlayerTrade(entity->GetObjectID());
if (trade == nullptr) return; if (trade == nullptr) return;
Game::logger->Log("GameMessages", "Trade canceled from (%llu)\n", entity->GetObjectID());
TradingManager::Instance()->CancelTrade(trade->GetTradeId()); TradingManager::Instance()->CancelTrade(trade->GetTradeId());
} }
@ -3665,7 +3681,7 @@ void GameMessages::HandlePetTamingTryBuild(RakNet::BitStream* inStream, Entity*
return; return;
} }
petComponent->TryBuild(bricks, clientFailed); petComponent->TryBuild(bricks.size(), clientFailed);
} }
void GameMessages::HandleNotifyTamingBuildSuccess(RakNet::BitStream* inStream, Entity* entity, const SystemAddress& sysAddr) void GameMessages::HandleNotifyTamingBuildSuccess(RakNet::BitStream* inStream, Entity* entity, const SystemAddress& sysAddr)
@ -4126,6 +4142,41 @@ void GameMessages::HandleRacingPlayerInfoResetFinished(RakNet::BitStream* inStre
} }
} }
void GameMessages::SendUpdateReputation(const LWOOBJID objectId, const int64_t reputation, const SystemAddress& sysAddr) {
CBITSTREAM;
CMSGHEADER;
bitStream.Write(objectId);
bitStream.Write(GAME_MSG::GAME_MSG_UPDATE_REPUTATION);
bitStream.Write(reputation);
SEND_PACKET;
}
void GameMessages::HandleUpdatePropertyPerformanceCost(RakNet::BitStream* inStream, Entity* entity, const SystemAddress& sysAddr) {
float performanceCost = 0.0f;
if (inStream->ReadBit()) inStream->Read(performanceCost);
if (performanceCost == 0.0f) return;
auto zone = dZoneManager::Instance()->GetZone();
const auto& worldId = zone->GetZoneID();
const auto cloneId = worldId.GetCloneID();
const auto zoneId = worldId.GetMapID();
auto updatePerformanceCostQuery = Database::CreatePreppedStmt("UPDATE properties SET performance_cost = ? WHERE clone_id = ? AND zone_id = ?");
updatePerformanceCostQuery->setDouble(1, performanceCost);
updatePerformanceCostQuery->setInt(2, cloneId);
updatePerformanceCostQuery->setInt(3, zoneId);
updatePerformanceCostQuery->executeUpdate();
delete updatePerformanceCostQuery;
updatePerformanceCostQuery = nullptr;
}
void GameMessages::HandleVehicleNotifyHitImaginationServer(RakNet::BitStream* inStream, Entity* entity, const SystemAddress& sysAddr) void GameMessages::HandleVehicleNotifyHitImaginationServer(RakNet::BitStream* inStream, Entity* entity, const SystemAddress& sysAddr)
{ {
@ -4654,7 +4705,7 @@ void GameMessages::HandleBuyFromVendor(RakNet::BitStream* inStream, Entity* enti
inv->RemoveItem(tokenId, altCurrencyCost); inv->RemoveItem(tokenId, altCurrencyCost);
inv->AddItem(item, count); inv->AddItem(item, count, eLootSourceType::LOOT_SOURCE_VENDOR);
} }
else else
{ {
@ -4679,8 +4730,8 @@ void GameMessages::HandleBuyFromVendor(RakNet::BitStream* inStream, Entity* enti
inv->RemoveItem(itemComp.currencyLOT, altCurrencyCost); inv->RemoveItem(itemComp.currencyLOT, altCurrencyCost);
} }
character->SetCoins(character->GetCoins() - (coinCost), LOOT_SOURCE_VENDOR); character->SetCoins(character->GetCoins() - (coinCost), eLootSourceType::LOOT_SOURCE_VENDOR);
inv->AddItem(item, count); inv->AddItem(item, count, eLootSourceType::LOOT_SOURCE_VENDOR);
} }
GameMessages::SendVendorTransactionResult(entity, sysAddr); GameMessages::SendVendorTransactionResult(entity, sysAddr);
@ -4723,12 +4774,12 @@ void GameMessages::HandleSellToVendor(RakNet::BitStream* inStream, Entity* entit
if (Inventory::IsValidItem(itemComp.currencyLOT)) if (Inventory::IsValidItem(itemComp.currencyLOT))
{ {
const auto altCurrency = (itemComp.altCurrencyCost * sellScalar) * count; const auto altCurrency = (itemComp.altCurrencyCost * sellScalar) * count;
inv->AddItem(itemComp.currencyLOT, std::floor(altCurrency)); // Return alt currencies like faction tokens. inv->AddItem(itemComp.currencyLOT, std::floor(altCurrency), eLootSourceType::LOOT_SOURCE_VENDOR); // Return alt currencies like faction tokens.
} }
//inv->RemoveItem(count, -1, iObjID); //inv->RemoveItem(count, -1, iObjID);
inv->MoveItemToInventory(item, VENDOR_BUYBACK, count, true, false, true); inv->MoveItemToInventory(item, eInventoryType::VENDOR_BUYBACK, count, true, false, true);
character->SetCoins(std::floor(character->GetCoins() + ((itemComp.baseValue * sellScalar)*count)), LOOT_SOURCE_VENDOR); character->SetCoins(std::floor(character->GetCoins() + ((itemComp.baseValue * sellScalar)*count)), eLootSourceType::LOOT_SOURCE_VENDOR);
//EntityManager::Instance()->SerializeEntity(player); // so inventory updates //EntityManager::Instance()->SerializeEntity(player); // so inventory updates
GameMessages::SendVendorTransactionResult(entity, sysAddr); GameMessages::SendVendorTransactionResult(entity, sysAddr);
} }
@ -4790,7 +4841,7 @@ void GameMessages::HandleBuybackFromVendor(RakNet::BitStream* inStream, Entity*
//inv->RemoveItem(count, -1, iObjID); //inv->RemoveItem(count, -1, iObjID);
inv->MoveItemToInventory(item, Inventory::FindInventoryTypeForLot(item->GetLot()), count, true, false); inv->MoveItemToInventory(item, Inventory::FindInventoryTypeForLot(item->GetLot()), count, true, false);
character->SetCoins(character->GetCoins() - cost, LOOT_SOURCE_VENDOR); character->SetCoins(character->GetCoins() - cost, eLootSourceType::LOOT_SOURCE_VENDOR);
//EntityManager::Instance()->SerializeEntity(player); // so inventory updates //EntityManager::Instance()->SerializeEntity(player); // so inventory updates
GameMessages::SendVendorTransactionResult(entity, sysAddr); GameMessages::SendVendorTransactionResult(entity, sysAddr);
} }
@ -4981,17 +5032,6 @@ void GameMessages::HandleRequestUse(RakNet::BitStream* inStream, Entity* entity,
missionComponent->Progress(MissionTaskType::MISSION_TASK_TYPE_MISSION_INTERACTION, interactedObject->GetLOT(), interactedObject->GetObjectID()); missionComponent->Progress(MissionTaskType::MISSION_TASK_TYPE_MISSION_INTERACTION, interactedObject->GetLOT(), interactedObject->GetObjectID());
missionComponent->Progress(MissionTaskType::MISSION_TASK_TYPE_NON_MISSION_INTERACTION, interactedObject->GetLOT(), interactedObject->GetObjectID()); missionComponent->Progress(MissionTaskType::MISSION_TASK_TYPE_NON_MISSION_INTERACTION, interactedObject->GetLOT(), interactedObject->GetObjectID());
//Do mail stuff:
if (interactedObject->GetLOT() == 3964) {
AMFStringValue* value = new AMFStringValue();
value->SetStringValue("Mail");
AMFArrayValue args;
args.InsertValue("state", value);
GameMessages::SendUIMessageServerToSingleClient(entity, sysAddr, "pushGameState", &args);
delete value;
}
} }
void GameMessages::HandlePlayEmote(RakNet::BitStream* inStream, Entity* entity) { void GameMessages::HandlePlayEmote(RakNet::BitStream* inStream, Entity* entity) {
@ -5068,7 +5108,7 @@ void GameMessages::HandleModularBuildConvertModel(RakNet::BitStream* inStream, E
item->Disassemble(TEMP_MODELS); item->Disassemble(TEMP_MODELS);
item->SetCount(item->GetCount() - 1, false, false); item->SetCount(item->GetCount() - 1, false, false, true, eLootSourceType::LOOT_SOURCE_QUICKBUILD);
} }
void GameMessages::HandleSetFlag(RakNet::BitStream* inStream, Entity* entity) { void GameMessages::HandleSetFlag(RakNet::BitStream* inStream, Entity* entity) {
@ -5234,7 +5274,7 @@ void GameMessages::HandlePickupCurrency(RakNet::BitStream* inStream, Entity* ent
auto* ch = entity->GetCharacter(); auto* ch = entity->GetCharacter();
if (entity->CanPickupCoins(currency)) { if (entity->CanPickupCoins(currency)) {
ch->SetCoins(ch->GetCoins() + currency, LOOT_SOURCE_PICKUP); ch->SetCoins(ch->GetCoins() + currency, eLootSourceType::LOOT_SOURCE_PICKUP);
} }
} }
@ -5289,21 +5329,11 @@ void GameMessages::HandleEquipItem(RakNet::BitStream* inStream, Entity* entity)
Item* item = inv->FindItemById(objectID); Item* item = inv->FindItemById(objectID);
if (!item) return; if (!item) return;
/*if (item->GetLot() == 6416) { // if it's a rocket
std::vector<Entity*> rocketPads = EntityManager::Instance()->GetEntitiesByComponent(COMPONENT_TYPE_ROCKET_LAUNCH);
for (Entity* rocketPad : rocketPads) {
RocketLaunchpadControlComponent* rocketComp = static_cast<RocketLaunchpadControlComponent*>(rocketPad->GetComponent(COMPONENT_TYPE_ROCKET_LAUNCH));
if (rocketComp) {
rocketComp->RocketEquip(entity, objectID);
}
}
}
else*/ {
item->Equip(); item->Equip();
EntityManager::Instance()->SerializeEntity(entity); EntityManager::Instance()->SerializeEntity(entity);
} }
}
void GameMessages::HandleUnequipItem(RakNet::BitStream* inStream, Entity* entity) { void GameMessages::HandleUnequipItem(RakNet::BitStream* inStream, Entity* entity) {
bool immediate; bool immediate;
@ -5398,6 +5428,8 @@ void GameMessages::HandleRemoveItemFromInventory(RakNet::BitStream* inStream, En
return; return;
} }
iStackCount = std::min<uint32_t>(item->GetCount(), iStackCount);
if (bConfirmed) { if (bConfirmed) {
for (auto i = 0; i < iStackCount; ++i) { for (auto i = 0; i < iStackCount; ++i) {
if (eInvType == eInventoryType::MODELS) if (eInvType == eInventoryType::MODELS)
@ -5447,8 +5479,8 @@ void GameMessages::HandleMoveItemInInventory(RakNet::BitStream* inStream, Entity
} }
void GameMessages::HandleMoveItemBetweenInventoryTypes(RakNet::BitStream* inStream, Entity* entity, const SystemAddress& sysAddr) { void GameMessages::HandleMoveItemBetweenInventoryTypes(RakNet::BitStream* inStream, Entity* entity, const SystemAddress& sysAddr) {
int inventoryTypeA; eInventoryType inventoryTypeA;
int inventoryTypeB; eInventoryType inventoryTypeB;
LWOOBJID objectID; LWOOBJID objectID;
bool showFlyingLoot = true; bool showFlyingLoot = true;
bool stackCountIsDefault = false; bool stackCountIsDefault = false;
@ -5465,7 +5497,7 @@ void GameMessages::HandleMoveItemBetweenInventoryTypes(RakNet::BitStream* inStre
inStream->Read(templateIDIsDefault); inStream->Read(templateIDIsDefault);
if (templateIDIsDefault) inStream->Read(templateID); if (templateIDIsDefault) inStream->Read(templateID);
InventoryComponent* inv = static_cast<InventoryComponent*>(entity->GetComponent(COMPONENT_TYPE_INVENTORY)); auto inv = entity->GetComponent<InventoryComponent>();
if (!inv) return; if (!inv) return;
auto* item = inv->FindItemById(objectID); auto* item = inv->FindItemById(objectID);
@ -5486,7 +5518,7 @@ void GameMessages::HandleMoveItemBetweenInventoryTypes(RakNet::BitStream* inStre
} }
} }
inv->MoveItemToInventory(item, static_cast<eInventoryType>(inventoryTypeB), stackCount, showFlyingLoot, false); inv->MoveItemToInventory(item, inventoryTypeB, stackCount, showFlyingLoot);
EntityManager::Instance()->SerializeEntity(entity); EntityManager::Instance()->SerializeEntity(entity);
} }
@ -5553,11 +5585,11 @@ void GameMessages::HandleModularBuildFinish(RakNet::BitStream* inStream, Entity*
if (count == 3) if (count == 3)
{ {
inv->AddItem(6416, 1, MODELS, config); inv->AddItem(6416, 1, eLootSourceType::LOOT_SOURCE_QUICKBUILD, eInventoryType::MODELS, config);
} }
else if (count == 7) else if (count == 7)
{ {
inv->AddItem(8092, 1, MODELS, config); inv->AddItem(8092, 1, eLootSourceType::LOOT_SOURCE_QUICKBUILD, eInventoryType::MODELS, config);
} }
auto* missionComponent = character->GetComponent<MissionComponent>(); auto* missionComponent = character->GetComponent<MissionComponent>();
@ -5587,7 +5619,7 @@ void GameMessages::HandleModularBuildFinish(RakNet::BitStream* inStream, Entity*
for (auto* item : items) for (auto* item : items)
{ {
inv->MoveItemToInventory(item, MODELS, item->GetCount(), false); inv->MoveItemToInventory(item, eInventoryType::MODELS, item->GetCount(), false);
} }
} }
@ -5702,7 +5734,7 @@ void GameMessages::HandleDoneArrangingWithItem(RakNet::BitStream* inStream, Enti
for (auto* item : items) for (auto* item : items)
{ {
inv->MoveItemToInventory(item, MODELS, item->GetCount(), false, false); inv->MoveItemToInventory(item, eInventoryType::MODELS, item->GetCount(), false, false);
} }
} }
@ -5728,7 +5760,7 @@ void GameMessages::HandleModularBuildMoveAndEquip(RakNet::BitStream* inStream, E
return; return;
} }
inv->MoveItemToInventory(item, MODELS, 1, false, true); inv->MoveItemToInventory(item, eInventoryType::MODELS, 1, false, true);
} }
void GameMessages::HandlePickupItem(RakNet::BitStream* inStream, Entity* entity) { void GameMessages::HandlePickupItem(RakNet::BitStream* inStream, Entity* entity) {
@ -5890,6 +5922,7 @@ void GameMessages::HandleReportBug(RakNet::BitStream* inStream, Entity* entity)
std::string nOtherPlayerID; std::string nOtherPlayerID;
std::string selection; std::string selection;
uint32_t messageLength; uint32_t messageLength;
int32_t reporterID = 0;
//Reading: //Reading:
inStream->Read(messageLength); inStream->Read(messageLength);
@ -5900,6 +5933,9 @@ void GameMessages::HandleReportBug(RakNet::BitStream* inStream, Entity* entity)
body.push_back(character); body.push_back(character);
} }
auto character = entity->GetCharacter();
if (character) reporterID = character->GetID();
uint32_t clientVersionLength; uint32_t clientVersionLength;
inStream->Read(clientVersionLength); inStream->Read(clientVersionLength);
for (unsigned int k = 0; k < clientVersionLength; k++) { for (unsigned int k = 0; k < clientVersionLength; k++) {
@ -5915,6 +5951,9 @@ void GameMessages::HandleReportBug(RakNet::BitStream* inStream, Entity* entity)
inStream->Read(character); inStream->Read(character);
nOtherPlayerID.push_back(character); nOtherPlayerID.push_back(character);
} }
// Convert other player id from LWOOBJID to the database id.
uint32_t otherPlayer = LWOOBJID_EMPTY;
if (nOtherPlayerID != "") otherPlayer = std::atoi(nOtherPlayerID.c_str());
uint32_t selectionLength; uint32_t selectionLength;
inStream->Read(selectionLength); inStream->Read(selectionLength);
@ -5925,16 +5964,17 @@ void GameMessages::HandleReportBug(RakNet::BitStream* inStream, Entity* entity)
} }
try { try {
sql::PreparedStatement* insertBug = Database::CreatePreppedStmt("INSERT INTO `bug_reports`(body, client_version, other_player_id, selection) VALUES (?, ?, ?, ?)"); sql::PreparedStatement* insertBug = Database::CreatePreppedStmt("INSERT INTO `bug_reports`(body, client_version, other_player_id, selection, reporter_id) VALUES (?, ?, ?, ?, ?)");
insertBug->setString(1, GeneralUtils::UTF16ToWTF8(body)); insertBug->setString(1, GeneralUtils::UTF16ToWTF8(body));
insertBug->setString(2, clientVersion); insertBug->setString(2, clientVersion);
insertBug->setString(3, nOtherPlayerID); insertBug->setString(3, std::to_string(otherPlayer));
insertBug->setString(4, selection); insertBug->setString(4, selection);
insertBug->setInt(5, reporterID);
insertBug->execute(); insertBug->execute();
delete insertBug; delete insertBug;
} }
catch (sql::SQLException& e) { catch (sql::SQLException& e) {
Game::logger->Log("HandleReportBug", "Couldn't save bug report!\n"); Game::logger->Log("HandleReportBug", "Couldn't save bug report! (%s)\n", e.what());
} }
} }

View File

@ -61,15 +61,16 @@ namespace GameMessages {
void SendGMLevelBroadcast(const LWOOBJID& objectID, uint8_t level); void SendGMLevelBroadcast(const LWOOBJID& objectID, uint8_t level);
void SendChatModeUpdate(const LWOOBJID& objectID, uint8_t level); void SendChatModeUpdate(const LWOOBJID& objectID, uint8_t level);
void SendAddItemToInventoryClientSync(Entity* entity, const SystemAddress& sysAddr, Item* item, const LWOOBJID& objectID, bool showFlyingLoot, int itemCount, LWOOBJID subKey = LWOOBJID_EMPTY); void SendAddItemToInventoryClientSync(Entity* entity, const SystemAddress& sysAddr, Item* item, const LWOOBJID& objectID, bool showFlyingLoot, int itemCount, LWOOBJID subKey = LWOOBJID_EMPTY, eLootSourceType lootSourceType = eLootSourceType::LOOT_SOURCE_NONE);
void SendNotifyClientFlagChange(const LWOOBJID& objectID, int iFlagID, bool bFlag, const SystemAddress& sysAddr); void SendNotifyClientFlagChange(const LWOOBJID& objectID, int iFlagID, bool bFlag, const SystemAddress& sysAddr);
void SendChangeObjectWorldState(const LWOOBJID& objectID, int state, const SystemAddress& sysAddr); void SendChangeObjectWorldState(const LWOOBJID& objectID, int state, const SystemAddress& sysAddr);
void SendOfferMission(const LWOOBJID& entity, const SystemAddress& sysAddr, int32_t missionID, const LWOOBJID& offererID); void SendOfferMission(const LWOOBJID& entity, const SystemAddress& sysAddr, int32_t missionID, const LWOOBJID& offererID);
void SendNotifyMission(Entity * entity, const SystemAddress& sysAddr, int missionID, int missionState, bool sendingRewards); void SendNotifyMission(Entity * entity, const SystemAddress& sysAddr, int missionID, int missionState, bool sendingRewards);
void SendNotifyMissionTask(Entity * entity, const SystemAddress& sysAddr, int missionID, int taskMask, std::vector<float> updates); void SendNotifyMissionTask(Entity * entity, const SystemAddress& sysAddr, int missionID, int taskMask, std::vector<float> updates);
void NotifyLevelRewards(LWOOBJID objectID, const SystemAddress& sysAddr, int level, bool sending_rewards);
void SendModifyLEGOScore(Entity* entity, const SystemAddress& sysAddr, int64_t score, int sourceType); void SendModifyLEGOScore(Entity* entity, const SystemAddress& sysAddr, int64_t score, eLootSourceType sourceType);
void SendUIMessageServerToSingleClient(Entity* entity, const SystemAddress& sysAddr, const std::string& message, NDGFxValue args); void SendUIMessageServerToSingleClient(Entity* entity, const SystemAddress& sysAddr, const std::string& message, NDGFxValue args);
void SendUIMessageServerToAllClients(const std::string& message, NDGFxValue args); void SendUIMessageServerToAllClients(const std::string& message, NDGFxValue args);
@ -108,7 +109,7 @@ namespace GameMessages {
void SendModularBuildEnd(Entity* entity); void SendModularBuildEnd(Entity* entity);
void SendVendorOpenWindow(Entity* entity, const SystemAddress& sysAddr); void SendVendorOpenWindow(Entity* entity, const SystemAddress& sysAddr);
void SendVendorStatusUpdate(Entity* entity, const SystemAddress& sysAddr); void SendVendorStatusUpdate(Entity* entity, const SystemAddress& sysAddr, bool bUpdateOnly = false);
void SendVendorTransactionResult(Entity* entity, const SystemAddress& sysAddr); void SendVendorTransactionResult(Entity* entity, const SystemAddress& sysAddr);
void SendRemoveItemFromInventory(Entity* entity, const SystemAddress& sysAddr, LWOOBJID iObjID, LOT templateID, int inventoryType, uint32_t stackCount, uint32_t stackRemaining); void SendRemoveItemFromInventory(Entity* entity, const SystemAddress& sysAddr, LWOOBJID iObjID, LOT templateID, int inventoryType, uint32_t stackCount, uint32_t stackRemaining);
@ -385,6 +386,8 @@ namespace GameMessages {
bool bUseLeaderboards bool bUseLeaderboards
); );
void HandleUpdatePropertyPerformanceCost(RakNet::BitStream* inStream, Entity* entity, const SystemAddress& sysAddr);
void SendNotifyClientShootingGalleryScore(LWOOBJID objectId, const SystemAddress& sysAddr, void SendNotifyClientShootingGalleryScore(LWOOBJID objectId, const SystemAddress& sysAddr,
float addTime, float addTime,
int32_t score, int32_t score,
@ -394,6 +397,8 @@ namespace GameMessages {
void HandleUpdateShootingGalleryRotation(RakNet::BitStream* inStream, Entity* entity, const SystemAddress& sysAddr); void HandleUpdateShootingGalleryRotation(RakNet::BitStream* inStream, Entity* entity, const SystemAddress& sysAddr);
void SendUpdateReputation(const LWOOBJID objectId, const int64_t reputation, const SystemAddress& sysAddr);
// Leaderboards // Leaderboards
void SendActivitySummaryLeaderboardData(const LWOOBJID& objectID, const Leaderboard* leaderboard, void SendActivitySummaryLeaderboardData(const LWOOBJID& objectID, const Leaderboard* leaderboard,
const SystemAddress& sysAddr = UNASSIGNED_SYSTEM_ADDRESS); const SystemAddress& sysAddr = UNASSIGNED_SYSTEM_ADDRESS);

View File

@ -13,7 +13,7 @@ void GameMessages::PropertyDataMessage::Serialize(RakNet::BitStream& stream) con
stream.Write<int32_t>(TemplateID); // - template id stream.Write<int32_t>(TemplateID); // - template id
stream.Write<uint16_t>(ZoneId); // - map id stream.Write<uint16_t>(ZoneId); // - map id
stream.Write<uint16_t>(VendorMapId); // - vendor map id stream.Write<uint16_t>(VendorMapId); // - vendor map id
stream.Write<uint32_t>(1); stream.Write<uint32_t>(cloneId); // clone id
const auto& name = GeneralUtils::ASCIIToUTF16(Name); const auto& name = GeneralUtils::ASCIIToUTF16(Name);
stream.Write(uint32_t(name.size())); stream.Write(uint32_t(name.size()));
@ -40,11 +40,12 @@ void GameMessages::PropertyDataMessage::Serialize(RakNet::BitStream& stream) con
stream.Write<uint32_t>(0); // - minimum price stream.Write<uint32_t>(0); // - minimum price
stream.Write<uint32_t>(1); // - rent duration stream.Write<uint32_t>(1); // - rent duration
stream.Write(ClaimedTime); // - timestamp stream.Write<uint64_t>(LastUpdatedTime); // - timestamp
stream.Write<uint32_t>(1); stream.Write<uint32_t>(1);
stream.Write<uint64_t>(0); stream.Write<uint32_t>(reputation); // Reputation
stream.Write<uint32_t>(0);
const auto& spawn = GeneralUtils::ASCIIToUTF16(SpawnName); const auto& spawn = GeneralUtils::ASCIIToUTF16(SpawnName);
stream.Write(uint32_t(spawn.size())); stream.Write(uint32_t(spawn.size()));
@ -63,9 +64,18 @@ void GameMessages::PropertyDataMessage::Serialize(RakNet::BitStream& stream) con
stream.Write<uint64_t>(0); stream.Write<uint64_t>(0);
stream.Write<uint32_t>(1); if (rejectionReason != "") stream.Write<uint32_t>(REJECTION_STATUS_REJECTED);
else if (moderatorRequested == true && rejectionReason == "") stream.Write<uint32_t>(REJECTION_STATUS_APPROVED);
else stream.Write<uint32_t>(REJECTION_STATUS_PENDING);
stream.Write<uint32_t>(0); // String length // Does this go here???
// const auto& rejectionReasonConverted = GeneralUtils::ASCIIToUTF16(rejectionReason);
// stream.Write(uint32_t(rejectionReasonConverted.size()));
// for (uint32_t i = 0; i < rejectionReasonConverted.size(); ++i) {
// stream.Write(uint16_t(rejectionReasonConverted[i]));
// }
stream.Write<uint32_t>(0);
stream.Write<uint64_t>(0); stream.Write<uint64_t>(0);

View File

@ -28,12 +28,23 @@ namespace GameMessages
std::string Name = ""; std::string Name = "";
std::string Description = ""; std::string Description = "";
std::string rejectionReason = "";
bool moderatorRequested = 0;
LWOCLONEID cloneId = 0;
uint32_t reputation = 0;
uint64_t ClaimedTime = 0; uint64_t ClaimedTime = 0;
uint64_t LastUpdatedTime = 0;
NiPoint3 ZonePosition = { 548.0f, 406.0f, 178.0f }; NiPoint3 ZonePosition = { 548.0f, 406.0f, 178.0f };
char PrivacyOption = 0; char PrivacyOption = 0;
float MaxBuildHeight = 128.0f; float MaxBuildHeight = 128.0f;
std::vector<NiPoint3> Paths = {}; std::vector<NiPoint3> Paths = {};
private:
enum RejectionStatus : uint32_t {
REJECTION_STATUS_APPROVED = 0,
REJECTION_STATUS_PENDING = 1,
REJECTION_STATUS_REJECTED = 2
};
}; };
} }

View File

@ -27,9 +27,10 @@ void PropertySelectQueryProperty::Serialize(RakNet::BitStream& stream) const
stream.Write(IsFriend); stream.Write(IsFriend);
stream.Write(IsModeratorApproved); stream.Write(IsModeratorApproved);
stream.Write(IsAlt); stream.Write(IsAlt);
stream.Write(IsOwner); stream.Write(IsOwned);
stream.Write(AccessType); stream.Write(AccessType);
stream.Write(DatePublished); stream.Write(DateLastPublished);
stream.Write(PerformanceIndex);
stream.Write(PerformanceCost); stream.Write(PerformanceCost);
} }

View File

@ -9,17 +9,18 @@ public:
void Deserialize(RakNet::BitStream& stream) const; void Deserialize(RakNet::BitStream& stream) const;
LWOCLONEID CloneId = LWOCLONEID_INVALID; LWOCLONEID CloneId = LWOCLONEID_INVALID; // The cloneID of the property
std::string OwnerName = ""; std::string OwnerName = ""; // The property owners name
std::string Name = ""; std::string Name = ""; // The property name
std::string Description = ""; std::string Description = ""; // The property description
uint32_t Reputation = 0; float Reputation = 0; // The reputation of the property
bool IsBestFriend = false; bool IsBestFriend = false; // Whether or not the property belongs to a best friend
bool IsFriend = false; bool IsFriend = false; // Whether or not the property belongs to a friend
bool IsModeratorApproved = false; bool IsModeratorApproved = false; // Whether or not a moderator has approved this property
bool IsAlt = false; bool IsAlt = false; // Whether or not the property is owned by an alt of the account owner
bool IsOwner = false; bool IsOwned = false; // Whether or not the property is owned
uint32_t AccessType = 0; uint32_t AccessType = 0; // The privacy option of the property
uint32_t DatePublished = 0; uint32_t DateLastPublished = 0; // The last day the property was published
uint64_t PerformanceCost = 0; float PerformanceCost = 0; // The performance cost of the property
uint32_t PerformanceIndex = 0; // The performance index of the property? Always 0?
}; };

View File

@ -1,6 +1,7 @@
#pragma once #pragma once
#include "dCommonVars.h" #include "dCommonVars.h"
#include "LDFFormat.h"
/** /**
* An item that's equipped, generally as a smaller return type than the regular Item class * An item that's equipped, generally as a smaller return type than the regular Item class
@ -26,4 +27,9 @@ struct EquippedItem
* The slot this item is stored in * The slot this item is stored in
*/ */
uint32_t slot = 0; uint32_t slot = 0;
/**
* The configuration of the item with any extra data
*/
std::vector<LDFBaseData*> config = {};
}; };

View File

@ -90,17 +90,21 @@ int32_t Inventory::FindEmptySlot()
{ {
if (free <= 6) // Up from 1 if (free <= 6) // Up from 1
{ {
if (type != ITEMS && type != VAULT_ITEMS) if (type != ITEMS && type != VAULT_ITEMS && type != eInventoryType::VAULT_MODELS)
{ {
uint32_t newSize = size; uint32_t newSize = size;
if (type == MODELS || type == VAULT_MODELS) if (type == MODELS)
{ {
newSize = 240; newSize = 240;
} }
else if (type == eInventoryType::VENDOR_BUYBACK)
{
newSize += 9u;
}
else else
{ {
newSize += 20; newSize += 10u;
} }
if (newSize > GetSize()) if (newSize > GetSize())

View File

@ -14,7 +14,7 @@
class Inventory; class Inventory;
Item::Item(const LWOOBJID id, const LOT lot, Inventory* inventory, const uint32_t slot, const uint32_t count, const bool bound, const std::vector<LDFBaseData*>& config, const LWOOBJID parent, LWOOBJID subKey) Item::Item(const LWOOBJID id, const LOT lot, Inventory* inventory, const uint32_t slot, const uint32_t count, const bool bound, const std::vector<LDFBaseData*>& config, const LWOOBJID parent, LWOOBJID subKey, eLootSourceType lootSourceType)
{ {
if (!Inventory::IsValidItem(lot)) if (!Inventory::IsValidItem(lot))
{ {
@ -46,7 +46,8 @@ Item::Item(
bool showFlyingLoot, bool showFlyingLoot,
bool isModMoveAndEquip, bool isModMoveAndEquip,
LWOOBJID subKey, LWOOBJID subKey,
bool bound) bool bound,
eLootSourceType lootSourceType)
{ {
if (!Inventory::IsValidItem(lot)) if (!Inventory::IsValidItem(lot))
{ {
@ -80,8 +81,7 @@ Item::Item(
inventory->AddManagedItem(this); inventory->AddManagedItem(this);
auto* entity = inventory->GetComponent()->GetParent(); auto* entity = inventory->GetComponent()->GetParent();
GameMessages::SendAddItemToInventoryClientSync(entity, entity->GetSystemAddress(), this, id, showFlyingLoot, static_cast<int>(this->count), subKey, lootSourceType);
GameMessages::SendAddItemToInventoryClientSync(entity, entity->GetSystemAddress(), this, id, showFlyingLoot, static_cast<int>(this->count), subKey);
if (isModMoveAndEquip) if (isModMoveAndEquip)
{ {
@ -148,7 +148,7 @@ PreconditionExpression* Item::GetPreconditionExpression() const
return preconditions; return preconditions;
} }
void Item::SetCount(const uint32_t value, const bool silent, const bool disassemble, const bool showFlyingLoot) void Item::SetCount(const uint32_t value, const bool silent, const bool disassemble, const bool showFlyingLoot, eLootSourceType lootSourceType)
{ {
if (value == count) if (value == count)
{ {
@ -176,7 +176,7 @@ void Item::SetCount(const uint32_t value, const bool silent, const bool disassem
if (value > count) if (value > count)
{ {
GameMessages::SendAddItemToInventoryClientSync(entity, entity->GetSystemAddress(), this, id, showFlyingLoot, delta); GameMessages::SendAddItemToInventoryClientSync(entity, entity->GetSystemAddress(), this, id, showFlyingLoot, delta, LWOOBJID_EMPTY, lootSourceType);
} }
else else
{ {
@ -340,7 +340,7 @@ bool Item::UseNonEquip()
return false; return false;
} }
LootGenerator::Instance().GiveLoot(inventory->GetComponent()->GetParent(), result); LootGenerator::Instance().GiveLoot(inventory->GetComponent()->GetParent(), result, eLootSourceType::LOOT_SOURCE_CONSUMPTION);
} }
inventory->GetComponent()->RemoveItem(lot, 1); inventory->GetComponent()->RemoveItem(lot, 1);
@ -374,7 +374,7 @@ void Item::Disassemble(const eInventoryType inventoryType)
for (const auto mod : modArray) for (const auto mod : modArray)
{ {
inventory->GetComponent()->AddItem(mod, 1, inventoryType); inventory->GetComponent()->AddItem(mod, 1, eLootSourceType::LOOT_SOURCE_DELETION, inventoryType);
} }
} }
} }
@ -472,7 +472,7 @@ void Item::DisassembleModel()
continue; continue;
} }
GetInventory()->GetComponent()->AddItem(brickID[0].NDObjectID, 1); GetInventory()->GetComponent()->AddItem(brickID[0].NDObjectID, 1, eLootSourceType::LOOT_SOURCE_DELETION);
} }
} }

View File

@ -36,7 +36,8 @@ public:
bool bound, bool bound,
const std::vector<LDFBaseData*>& config, const std::vector<LDFBaseData*>& config,
LWOOBJID parent, LWOOBJID parent,
LWOOBJID subKey LWOOBJID subKey,
eLootSourceType lootSourceType = eLootSourceType::LOOT_SOURCE_NONE
); );
/** /**
@ -62,7 +63,8 @@ public:
bool showFlyingLoot = true, bool showFlyingLoot = true,
bool isModMoveAndEquip = false, bool isModMoveAndEquip = false,
LWOOBJID subKey = LWOOBJID_EMPTY, LWOOBJID subKey = LWOOBJID_EMPTY,
bool bound = false bool bound = false,
eLootSourceType lootSourceType = eLootSourceType::LOOT_SOURCE_NONE
); );
~Item(); ~Item();
@ -86,7 +88,7 @@ public:
* @param disassemble if items were removed, this returns all the sub parts of the item individually if it had assembly part lots * @param disassemble if items were removed, this returns all the sub parts of the item individually if it had assembly part lots
* @param showFlyingLoot shows flying loot to the client, if not silent * @param showFlyingLoot shows flying loot to the client, if not silent
*/ */
void SetCount(uint32_t value, bool silent = false, bool disassemble = true, bool showFlyingLoot = true); void SetCount(uint32_t value, bool silent = false, bool disassemble = true, bool showFlyingLoot = true, eLootSourceType lootSourceType = eLootSourceType::LOOT_SOURCE_NONE);
/** /**
* Returns the number of items this item represents (e.g. for stacks) * Returns the number of items this item represents (e.g. for stacks)

View File

@ -11,9 +11,12 @@
#include "GameMessages.h" #include "GameMessages.h"
#include "Mail.h" #include "Mail.h"
#include "MissionComponent.h" #include "MissionComponent.h"
#include "RacingTaskParam.h"
#include "dLocale.h" #include "dLocale.h"
#include "dLogger.h" #include "dLogger.h"
#include "dServer.h" #include "dServer.h"
#include "dZoneManager.h"
#include "Database.h"
Mission::Mission(MissionComponent* missionComponent, const uint32_t missionId) { Mission::Mission(MissionComponent* missionComponent, const uint32_t missionId) {
m_MissionComponent = missionComponent; m_MissionComponent = missionComponent;
@ -313,6 +316,10 @@ void Mission::Complete(const bool yieldRewards) {
missionComponent->Progress(MissionTaskType::MISSION_TASK_TYPE_MISSION_COMPLETE, info->id); missionComponent->Progress(MissionTaskType::MISSION_TASK_TYPE_MISSION_COMPLETE, info->id);
missionComponent->Progress(MissionTaskType::MISSION_TASK_TYPE_RACING, info->id, (LWOOBJID)RacingTaskParam::RACING_TASK_PARAM_COMPLETE_ANY_RACING_TASK);
missionComponent->Progress(MissionTaskType::MISSION_TASK_TYPE_RACING, info->id, (LWOOBJID)RacingTaskParam::RACING_TASK_PARAM_COMPLETE_TRACK_TASKS);
auto* missionEmailTable = CDClientManager::Instance()->GetTable<CDMissionEmailTable>("MissionEmail"); auto* missionEmailTable = CDClientManager::Instance()->GetTable<CDMissionEmailTable>("MissionEmail");
const auto missionId = GetMissionId(); const auto missionId = GetMissionId();
@ -350,7 +357,7 @@ void Mission::CheckCompletion() {
return; return;
} }
SetMissionState(MissionState::MISSION_STATE_READY_TO_COMPLETE); MakeReadyToComplete();
} }
void Mission::Catchup() { void Mission::Catchup() {
@ -421,11 +428,15 @@ void Mission::YieldRewards() {
} }
} }
int32_t coinsToSend = 0;
if (info->LegoScore > 0) { if (info->LegoScore > 0) {
eLootSourceType lootSource = info->isMission ? eLootSourceType::LOOT_SOURCE_MISSION : eLootSourceType::LOOT_SOURCE_ACHIEVEMENT;
if(characterComponent->GetLevel() >= dZoneManager::Instance()->GetMaxLevel()) {
// Since the character is at the level cap we reward them with coins instead of UScore.
coinsToSend += info->LegoScore * dZoneManager::Instance()->GetLevelCapCurrencyConversion();
} else {
characterComponent->SetUScore(characterComponent->GetUScore() + info->LegoScore); characterComponent->SetUScore(characterComponent->GetUScore() + info->LegoScore);
GameMessages::SendModifyLEGOScore(entity, entity->GetSystemAddress(), info->LegoScore, lootSource);
if (info->isMission) {
GameMessages::SendModifyLEGOScore(entity, entity->GetSystemAddress(), info->LegoScore, 2);
} }
} }
@ -452,11 +463,12 @@ void Mission::YieldRewards() {
count = 0; count = 0;
} }
inventoryComponent->AddItem(pair.first, count); inventoryComponent->AddItem(pair.first, count, IsMission() ? eLootSourceType::LOOT_SOURCE_MISSION : eLootSourceType::LOOT_SOURCE_ACHIEVEMENT);
} }
if (info->reward_currency_repeatable > 0) { if (info->reward_currency_repeatable > 0 || coinsToSend > 0) {
character->SetCoins(character->GetCoins() + info->reward_currency_repeatable, LOOT_SOURCE_MISSION); eLootSourceType lootSource = info->isMission ? eLootSourceType::LOOT_SOURCE_MISSION : eLootSourceType::LOOT_SOURCE_ACHIEVEMENT;
character->SetCoins(character->GetCoins() + info->reward_currency_repeatable + coinsToSend, lootSource);
} }
return; return;
@ -484,12 +496,12 @@ void Mission::YieldRewards() {
count = 0; count = 0;
} }
inventoryComponent->AddItem(pair.first, count); inventoryComponent->AddItem(pair.first, count, IsMission() ? eLootSourceType::LOOT_SOURCE_MISSION : eLootSourceType::LOOT_SOURCE_ACHIEVEMENT);
} }
if (info->reward_currency > 0) { if (info->reward_currency > 0 || coinsToSend > 0) {
eLootSourceType lootSource = info->isMission ? LOOT_SOURCE_MISSION : LOOT_SOURCE_ACHIEVEMENT; eLootSourceType lootSource = info->isMission ? eLootSourceType::LOOT_SOURCE_MISSION : eLootSourceType::LOOT_SOURCE_ACHIEVEMENT;
character->SetCoins(character->GetCoins() + info->reward_currency, lootSource); character->SetCoins(character->GetCoins() + info->reward_currency + coinsToSend, lootSource);
} }
if (info->reward_maxinventory > 0) { if (info->reward_maxinventory > 0) {
@ -499,21 +511,28 @@ void Mission::YieldRewards() {
} }
if (info->reward_bankinventory > 0) { if (info->reward_bankinventory > 0) {
auto* inventory = inventoryComponent->GetInventory(VAULT_ITEMS); auto* inventory = inventoryComponent->GetInventory(eInventoryType::VAULT_ITEMS);
auto modelInventory = inventoryComponent->GetInventory(eInventoryType::VAULT_MODELS);
inventory->SetSize(inventory->GetSize() + info->reward_bankinventory); inventory->SetSize(inventory->GetSize() + info->reward_bankinventory);
modelInventory->SetSize(modelInventory->GetSize() + info->reward_bankinventory);
} }
if (info->reward_reputation > 0) { if (info->reward_reputation > 0) {
// TODO: In case of reputation, write code missionComponent->Progress(MissionTaskType::MISSION_TASK_TYPE_EARN_REPUTATION, 0, 0L, "", info->reward_reputation);
auto character = entity->GetComponent<CharacterComponent>();
if (character) {
character->SetReputation(character->GetReputation() + info->reward_reputation);
GameMessages::SendUpdateReputation(entity->GetObjectID(), character->GetReputation(), entity->GetSystemAddress());
}
} }
if (info->reward_maxhealth > 0) { if (info->reward_maxhealth > 0) {
destroyableComponent->SetMaxHealth(destroyableComponent->GetMaxHealth() + static_cast<float>(info->reward_maxhealth)); destroyableComponent->SetMaxHealth(destroyableComponent->GetMaxHealth() + static_cast<float>(info->reward_maxhealth), true);
} }
if (info->reward_maximagination > 0) { if (info->reward_maximagination > 0) {
destroyableComponent->SetMaxImagination(destroyableComponent->GetMaxImagination() + static_cast<float>(info->reward_maximagination)); destroyableComponent->SetMaxImagination(destroyableComponent->GetMaxImagination() + static_cast<float>(info->reward_maximagination), true);
} }
EntityManager::Instance()->SerializeEntity(entity); EntityManager::Instance()->SerializeEntity(entity);

View File

@ -78,14 +78,7 @@ void MissionTask::SetProgress(const uint32_t value, const bool echo)
std::vector<float> updates; std::vector<float> updates;
updates.push_back(static_cast<float>(progress)); updates.push_back(static_cast<float>(progress));
GameMessages::SendNotifyMissionTask(entity, entity->GetSystemAddress(), static_cast<int>(info->id), static_cast<int>(1 << (mask + 1)), updates);
GameMessages::SendNotifyMissionTask(
entity,
entity->GetSystemAddress(),
static_cast<int>(info->id),
static_cast<int>(1 << (mask + 1)),
updates
);
} }
@ -190,15 +183,19 @@ bool MissionTask::InParameters(const uint32_t value) const
bool MissionTask::IsComplete() const bool MissionTask::IsComplete() const
{ {
// Minigames are the only ones where the target value is a score they need to get but the actual target is the act ID // Mission 668 has task uid 984 which is a bit mask. Its completion value is 3.
return GetType() == MissionTaskType::MISSION_TASK_TYPE_MINIGAME ? progress == info->target : progress >= info->targetValue; if (info->uid == 984) {
return progress >= 3;
}
else {
return progress >= info->targetValue;
}
} }
void MissionTask::Complete() void MissionTask::Complete()
{ {
// Minigames are the only ones where the target value is a score they need to get but the actual target is the act ID SetProgress(info->targetValue);
SetProgress(GetType() == MissionTaskType::MISSION_TASK_TYPE_MINIGAME ? info->target : info->targetValue);
} }
@ -329,18 +326,17 @@ void MissionTask::Progress(int32_t value, LWOOBJID associate, const std::string&
case MissionTaskType::MISSION_TASK_TYPE_SKILL: case MissionTaskType::MISSION_TASK_TYPE_SKILL:
{ {
if (!InParameters(value)) break; // This is a complicated check because for some missions we need to check for the associate being in the parameters instead of the value being in the parameters.
if (associate == LWOOBJID_EMPTY && GetAllTargets().size() == 1 && GetAllTargets()[0] == -1) {
AddProgress(count); if (InParameters(value)) AddProgress(count);
} else {
if (InParameters(associate) && InAllTargets(value)) AddProgress(count);
}
break; break;
} }
case MissionTaskType::MISSION_TASK_TYPE_MINIGAME: case MissionTaskType::MISSION_TASK_TYPE_MINIGAME:
{ {
if (targets != info->targetGroup || info->targetValue > value)
break;
auto* minigameManager = EntityManager::Instance()->GetEntity(associate); auto* minigameManager = EntityManager::Instance()->GetEntity(associate);
if (minigameManager == nullptr) if (minigameManager == nullptr)
break; break;
@ -355,11 +351,16 @@ void MissionTask::Progress(int32_t value, LWOOBJID associate, const std::string&
if (info->target != gameID) { if (info->target != gameID) {
break; break;
} }
// This special case is for shooting gallery missions that want their
Game::logger->Log("Minigame Task", "Progressing minigame with %s %d > %d (%d)\n", // progress value set to 1 instead of being set to the target value.
targets.c_str(), value, info->targetValue, gameID); if(info->targetGroup == targets && value >= info->targetValue && GetMission()->IsMission() && info->target == 1864 && info->targetGroup == "performact_score") {
SetProgress(info->target); SetProgress(1);
break;
}
if(info->targetGroup == targets && value >= info->targetValue) {
SetProgress(info->targetValue);
break;
}
break; break;
} }
@ -423,30 +424,46 @@ void MissionTask::Progress(int32_t value, LWOOBJID associate, const std::string&
case MissionTaskType::MISSION_TASK_TYPE_RACING: case MissionTaskType::MISSION_TASK_TYPE_RACING:
{ {
// The meaning of associate can be found in RacingTaskParam.h
if (parameters.empty()) break; if (parameters.empty()) break;
if (!InAllTargets(dZoneManager::Instance()->GetZone()->GetWorldID())) break; if (!InAllTargets(dZoneManager::Instance()->GetZone()->GetWorldID()) && !(parameters[0] == 4 || parameters[0] == 5) && !InAllTargets(value)) break;
if (parameters[0] != associate) break; if (parameters[0] != associate) break;
if (associate == 1 || associate == 15) if (associate == 1 || associate == 2 || associate == 3)
{ {
if (value > info->targetValue) break; if (value > info->targetValue) break;
AddProgress(1);
}
else if (associate == 2 || associate == 3)
{
if (info->targetValue < value) break;
AddProgress(info->targetValue); AddProgress(info->targetValue);
} }
// task 15 is a bit mask!
else if (associate == 15) {
if (!InAllTargets(value)) break;
auto tempProgress = GetProgress();
// If we won at Nimbus Station, set bit 0
if (value == 1203) SetProgress(tempProgress |= 1 << 0);
// If we won at Gnarled Forest, set bit 1
else if (value == 1303) SetProgress(tempProgress |= 1 << 1);
// If both bits are set, then the client sees the mission as complete.
}
else if (associate == 10) else if (associate == 10)
{ {
if (info->targetValue > value) // If the player did not crash during the race, progress this task by count.
{ if (value != 0) break;
AddProgress(info->targetValue);
AddProgress(count);
} }
else if (associate == 4 || associate == 5 || associate == 14)
{
if (!InAllTargets(value)) break;
AddProgress(count);
}
else if (associate == 17)
{
if (!InAllTargets(value)) break;
AddProgress(count);
} }
else else
{ {
@ -464,6 +481,7 @@ void MissionTask::Progress(int32_t value, LWOOBJID associate, const std::string&
case MissionTaskType::MISSION_TASK_TYPE_SMASH: case MissionTaskType::MISSION_TASK_TYPE_SMASH:
case MissionTaskType::MISSION_TASK_TYPE_ITEM_COLLECTION: case MissionTaskType::MISSION_TASK_TYPE_ITEM_COLLECTION:
case MissionTaskType::MISSION_TASK_TYPE_PLAYER_FLAG: case MissionTaskType::MISSION_TASK_TYPE_PLAYER_FLAG:
case MissionTaskType::MISSION_TASK_TYPE_EARN_REPUTATION:
{ {
if (!InAllTargets(value)) break; if (!InAllTargets(value)) break;
@ -471,7 +489,11 @@ void MissionTask::Progress(int32_t value, LWOOBJID associate, const std::string&
break; break;
} }
case MissionTaskType::MISSION_TASK_TYPE_PLACE_MODEL:
{
AddProgress(count);
break;
}
default: default:
Game::logger->Log("MissionTask", "Invalid mission task type (%i)!\n", static_cast<int>(type)); Game::logger->Log("MissionTask", "Invalid mission task type (%i)!\n", static_cast<int>(type));
return; return;

View File

@ -19,7 +19,7 @@ public:
* Attempts to progress this task using the provided parameters. Note that the behavior of this method is different * Attempts to progress this task using the provided parameters. Note that the behavior of this method is different
* for each mission task type. * for each mission task type.
* @param value the value to progress by * @param value the value to progress by
* @param associate optional object ID of an entity that was related to the porgression * @param associate optional object ID of an entity that was related to the progression
* @param targets optional multiple targets that need to be met to progress * @param targets optional multiple targets that need to be met to progress
* @param count a number that indicates the times to progress * @param count a number that indicates the times to progress
*/ */

View File

@ -16,9 +16,11 @@ enum class MissionTaskType : int {
MISSION_TASK_TYPE_MINIGAME = 14, //!< A task for doing something in a minigame MISSION_TASK_TYPE_MINIGAME = 14, //!< A task for doing something in a minigame
MISSION_TASK_TYPE_NON_MISSION_INTERACTION = 15, //!< A task for interacting with a non-mission MISSION_TASK_TYPE_NON_MISSION_INTERACTION = 15, //!< A task for interacting with a non-mission
MISSION_TASK_TYPE_MISSION_COMPLETE = 16, //!< A task for completing a mission MISSION_TASK_TYPE_MISSION_COMPLETE = 16, //!< A task for completing a mission
MISSION_TASK_TYPE_EARN_REPUTATION = 17, //!< A task for earning reputation
MISSION_TASK_TYPE_POWERUP = 21, //!< A task for collecting a powerup MISSION_TASK_TYPE_POWERUP = 21, //!< A task for collecting a powerup
MISSION_TASK_TYPE_PET_TAMING = 22, //!< A task for taming a pet MISSION_TASK_TYPE_PET_TAMING = 22, //!< A task for taming a pet
MISSION_TASK_TYPE_RACING = 23, //!< A task for racing MISSION_TASK_TYPE_RACING = 23, //!< A task for racing
MISSION_TASK_TYPE_PLAYER_FLAG = 24, //!< A task for setting a player flag MISSION_TASK_TYPE_PLAYER_FLAG = 24, //!< A task for setting a player flag
MISSION_TASK_TYPE_PLACE_MODEL = 25, //!< A task for picking up a model
MISSION_TASK_TYPE_VISIT_PROPERTY = 30 //!< A task for visiting a property MISSION_TASK_TYPE_VISIT_PROPERTY = 30 //!< A task for visiting a property
}; };

View File

@ -0,0 +1,20 @@
#pragma once
#include <cstdint>
enum class RacingTaskParam : int32_t {
RACING_TASK_PARAM_FINISH_WITH_PLACEMENT = 1, //<! A task param for finishing with a specific placement.
RACING_TASK_PARAM_LAP_TIME = 2, //<! A task param for finishing with a specific lap time.
RACING_TASK_PARAM_TOTAL_TRACK_TIME = 3, //<! A task param for finishing with a specific track time.
RACING_TASK_PARAM_COMPLETE_ANY_RACING_TASK = 4, //<! A task param for completing a racing task.
RACING_TASK_PARAM_COMPLETE_TRACK_TASKS = 5, //<! A task param for completing a task for a specific track.
RACING_TASK_PARAM_MODULAR_BUILDING = 6, //<! A task param for modular building with racing builds.
RACING_TASK_PARAM_SAFE_DRIVER = 10, //<! A task param for completing a race without smashing.
RACING_TASK_PARAM_SMASHABLES = 11, //<! A task param for smashing entities during a race.
RACING_TASK_PARAM_COLLECT_IMAGINATION = 12, //<! A task param for collecting imagination during a race.
RACING_TASK_PARAM_COMPETED_IN_RACE = 13, //<! A task param for competing in a race.
RACING_TASK_PARAM_WIN_RACE_IN_WORLD = 14, //<! A task param for winning a race in a specific world.
RACING_TASK_PARAM_FIRST_PLACE_MULTIPLE_TRACKS = 15, //<! A task param for finishing in first place on multiple tracks.
RACING_TASK_PARAM_LAST_PLACE_FINISH = 16, //<! A task param for finishing in last place.
RACING_TASK_PARAM_SMASH_SPECIFIC_SMASHABLE = 17 //<! A task param for smashing dragon eggs during a race.
};

View File

@ -268,15 +268,15 @@ std::unordered_map<LOT, int32_t> LootGenerator::RollLootMatrix(uint32_t matrixIn
return drops; return drops;
} }
void LootGenerator::GiveLoot(Entity* player, uint32_t matrixIndex) { void LootGenerator::GiveLoot(Entity* player, uint32_t matrixIndex, eLootSourceType lootSourceType) {
player = player->GetOwner(); // If the owner is overwritten, we collect that here player = player->GetOwner(); // If the owner is overwritten, we collect that here
std::unordered_map<LOT, int32_t> result = RollLootMatrix(player, matrixIndex); std::unordered_map<LOT, int32_t> result = RollLootMatrix(player, matrixIndex);
GiveLoot(player, result); GiveLoot(player, result, lootSourceType);
} }
void LootGenerator::GiveLoot(Entity* player, std::unordered_map<LOT, int32_t>& result) { void LootGenerator::GiveLoot(Entity* player, std::unordered_map<LOT, int32_t>& result, eLootSourceType lootSourceType) {
player = player->GetOwner(); // if the owner is overwritten, we collect that here player = player->GetOwner(); // if the owner is overwritten, we collect that here
auto* inventoryComponent = player->GetComponent<InventoryComponent>(); auto* inventoryComponent = player->GetComponent<InventoryComponent>();
@ -285,7 +285,7 @@ void LootGenerator::GiveLoot(Entity* player, std::unordered_map<LOT, int32_t>& r
return; return;
for (const auto& pair : result) { for (const auto& pair : result) {
inventoryComponent->AddItem(pair.first, pair.second); inventoryComponent->AddItem(pair.first, pair.second, lootSourceType);
} }
} }
@ -314,13 +314,13 @@ void LootGenerator::GiveActivityLoot(Entity* player, Entity* source, uint32_t ac
maxCoins = currencyTable[0].maxvalue; maxCoins = currencyTable[0].maxvalue;
} }
GiveLoot(player, selectedReward->LootMatrixIndex); GiveLoot(player, selectedReward->LootMatrixIndex, eLootSourceType::LOOT_SOURCE_ACTIVITY);
uint32_t coins = (int)(minCoins + GeneralUtils::GenerateRandomNumber<float>(0, 1) * (maxCoins - minCoins)); uint32_t coins = (int)(minCoins + GeneralUtils::GenerateRandomNumber<float>(0, 1) * (maxCoins - minCoins));
auto* character = player->GetCharacter(); auto* character = player->GetCharacter();
character->SetCoins(character->GetCoins() + coins, LOOT_SOURCE_ACTIVITY); character->SetCoins(character->GetCoins() + coins, eLootSourceType::LOOT_SOURCE_ACTIVITY);
} }
void LootGenerator::DropLoot(Entity* player, Entity* killedObject, uint32_t matrixIndex, uint32_t minCoins, uint32_t maxCoins) { void LootGenerator::DropLoot(Entity* player, Entity* killedObject, uint32_t matrixIndex, uint32_t minCoins, uint32_t maxCoins) {

View File

@ -47,8 +47,8 @@ class LootGenerator : public Singleton<LootGenerator> {
std::unordered_map<LOT, int32_t> RollLootMatrix(Entity* player, uint32_t matrixIndex); std::unordered_map<LOT, int32_t> RollLootMatrix(Entity* player, uint32_t matrixIndex);
std::unordered_map<LOT, int32_t> RollLootMatrix(uint32_t matrixIndex); std::unordered_map<LOT, int32_t> RollLootMatrix(uint32_t matrixIndex);
void GiveLoot(Entity* player, uint32_t matrixIndex); void GiveLoot(Entity* player, uint32_t matrixIndex, eLootSourceType lootSourceType = eLootSourceType::LOOT_SOURCE_NONE);
void GiveLoot(Entity* player, std::unordered_map<LOT, int32_t>& result); void GiveLoot(Entity* player, std::unordered_map<LOT, int32_t>& result, eLootSourceType lootSourceType = eLootSourceType::LOOT_SOURCE_NONE);
void GiveActivityLoot(Entity* player, Entity* source, uint32_t activityID, int32_t rating = 0); 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, 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 DropLoot(Entity* player, Entity* killedObject, std::unordered_map<LOT, int32_t>& result, uint32_t minCoins, uint32_t maxCoins);

View File

@ -262,7 +262,7 @@ void Mail::HandleSendMail(RakNet::BitStream* packet, const SystemAddress& sysAdd
} }
Mail::SendSendResponse(sysAddr, Mail::MailSendResponse::Success); Mail::SendSendResponse(sysAddr, Mail::MailSendResponse::Success);
entity->GetCharacter()->SetCoins(entity->GetCharacter()->GetCoins() - mailCost, LOOT_SOURCE_MAIL); entity->GetCharacter()->SetCoins(entity->GetCharacter()->GetCoins() - mailCost, eLootSourceType::LOOT_SOURCE_MAIL);
Game::logger->Log("Mail", "Seeing if we need to remove item with ID/count/LOT: %i %i %i\n", itemID, attachmentCount, itemLOT); Game::logger->Log("Mail", "Seeing if we need to remove item with ID/count/LOT: %i %i %i\n", itemID, attachmentCount, itemLOT);
@ -363,7 +363,7 @@ void Mail::HandleAttachmentCollect(RakNet::BitStream* packet, const SystemAddres
auto inv = static_cast<InventoryComponent*>(player->GetComponent(COMPONENT_TYPE_INVENTORY)); auto inv = static_cast<InventoryComponent*>(player->GetComponent(COMPONENT_TYPE_INVENTORY));
if (!inv) return; if (!inv) return;
inv->AddItem(attachmentLOT, attachmentCount); inv->AddItem(attachmentLOT, attachmentCount, eLootSourceType::LOOT_SOURCE_MAIL);
Mail::SendAttachmentRemoveConfirm(sysAddr, mailID); Mail::SendAttachmentRemoveConfirm(sysAddr, mailID);

View File

@ -131,6 +131,7 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit
bool success = user->GetMaxGMLevel() >= level; bool success = user->GetMaxGMLevel() >= level;
if (success) { if (success) {
if (entity->GetGMLevel() > GAME_MASTER_LEVEL_CIVILIAN && level == GAME_MASTER_LEVEL_CIVILIAN) if (entity->GetGMLevel() > GAME_MASTER_LEVEL_CIVILIAN && level == GAME_MASTER_LEVEL_CIVILIAN)
{ {
GameMessages::SendToggleGMInvis(entity->GetObjectID(), false, UNASSIGNED_SYSTEM_ADDRESS); GameMessages::SendToggleGMInvis(entity->GetObjectID(), false, UNASSIGNED_SYSTEM_ADDRESS);
@ -304,27 +305,15 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit
if (chatCommand == "leave-zone") { if (chatCommand == "leave-zone") {
const auto currentZone = dZoneManager::Instance()->GetZone()->GetZoneID().GetMapID(); const auto currentZone = dZoneManager::Instance()->GetZone()->GetZoneID().GetMapID();
auto newZone = 1100; auto newZone = 0;
if (currentZone % 100 == 0) {
switch (currentZone)
{
case 1101:
newZone = 1100;
break;
case 1204:
newZone = 1200;
break;
default:
newZone = 1100;
break;
}
if (currentZone == newZone)
{
ChatPackets::SendSystemMessage(sysAddr, u"You are not in an instanced zone."); ChatPackets::SendSystemMessage(sysAddr, u"You are not in an instanced zone.");
return; return;
} else {
newZone = (currentZone / 100) * 100;
} }
// If new zone would be inaccessible, then default to Avant Gardens.
if (!CheckIfAccessibleZone(newZone)) newZone = 1100;
ChatPackets::SendSystemMessage(sysAddr, u"Leaving zone..."); ChatPackets::SendSystemMessage(sysAddr, u"Leaving zone...");
@ -375,11 +364,6 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit
} }
if (user->GetMaxGMLevel() == 0 || entity->GetGMLevel() >= 0) { if (user->GetMaxGMLevel() == 0 || entity->GetGMLevel() >= 0) {
if ((chatCommand == "playanimation" || chatCommand == "playanim") && args.size() == 1) {
std::u16string anim = GeneralUtils::ASCIIToUTF16(args[0], args[0].size());
GameMessages::SendPlayAnimation(entity, anim);
}
if (chatCommand == "die") { if (chatCommand == "die") {
entity->Smash(entity->GetObjectID()); entity->Smash(entity->GetObjectID());
} }
@ -459,6 +443,11 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit
GameMessages::SendToggleGMInvis(entity->GetObjectID(), false, UNASSIGNED_SYSTEM_ADDRESS); // need to retoggle because it gets reenabled on creation of new character GameMessages::SendToggleGMInvis(entity->GetObjectID(), false, UNASSIGNED_SYSTEM_ADDRESS); // need to retoggle because it gets reenabled on creation of new character
} }
if ((chatCommand == "playanimation" || chatCommand == "playanim") && args.size() == 1 && entity->GetGMLevel() >= GAME_MASTER_LEVEL_DEVELOPER) {
std::u16string anim = GeneralUtils::ASCIIToUTF16(args[0], args[0].size());
GameMessages::SendPlayAnimation(entity, anim);
}
if (chatCommand == "list-spawns" && entity->GetGMLevel() >= GAME_MASTER_LEVEL_DEVELOPER) { if (chatCommand == "list-spawns" && entity->GetGMLevel() >= GAME_MASTER_LEVEL_DEVELOPER) {
for (const auto& pair : EntityManager::Instance()->GetSpawnPointEntities()) { for (const auto& pair : EntityManager::Instance()->GetSpawnPointEntities()) {
ChatPackets::SendSystemMessage(sysAddr, GeneralUtils::ASCIIToUTF16(pair.first)); ChatPackets::SendSystemMessage(sysAddr, GeneralUtils::ASCIIToUTF16(pair.first));
@ -810,7 +799,7 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit
InventoryComponent * inventory = static_cast<InventoryComponent*>(entity->GetComponent(COMPONENT_TYPE_INVENTORY)); InventoryComponent * inventory = static_cast<InventoryComponent*>(entity->GetComponent(COMPONENT_TYPE_INVENTORY));
inventory->AddItem(itemLOT, 1); inventory->AddItem(itemLOT, 1, eLootSourceType::LOOT_SOURCE_MODERATION);
} else if(args.size() == 2) { } else if(args.size() == 2) {
uint32_t itemLOT; uint32_t itemLOT;
@ -830,7 +819,7 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit
InventoryComponent* inventory = static_cast<InventoryComponent*>(entity->GetComponent(COMPONENT_TYPE_INVENTORY)); InventoryComponent* inventory = static_cast<InventoryComponent*>(entity->GetComponent(COMPONENT_TYPE_INVENTORY));
inventory->AddItem(itemLOT, count); inventory->AddItem(itemLOT, count, eLootSourceType::LOOT_SOURCE_MODERATION);
} }
else { else {
ChatPackets::SendSystemMessage(sysAddr, u"Correct usage: /gmadditem <lot>"); ChatPackets::SendSystemMessage(sysAddr, u"Correct usage: /gmadditem <lot>");
@ -943,6 +932,7 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit
Game::logger->Log("SlashCommandHandler", "Teleporting objectID: %llu to %f, %f, %f\n", entity->GetObjectID(), pos.x, pos.y, pos.z); Game::logger->Log("SlashCommandHandler", "Teleporting objectID: %llu to %f, %f, %f\n", entity->GetObjectID(), pos.x, pos.y, pos.z);
GameMessages::SendTeleport(entity->GetObjectID(), pos, NiQuaternion(), sysAddr); GameMessages::SendTeleport(entity->GetObjectID(), pos, NiQuaternion(), sysAddr);
} else if (args.size() == 2) { } else if (args.size() == 2) {
float x, z; float x, z;
if (!GeneralUtils::TryParse(args[0], x)) if (!GeneralUtils::TryParse(args[0], x))
@ -961,7 +951,6 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit
pos.SetY(0.0f); pos.SetY(0.0f);
pos.SetZ(z); pos.SetZ(z);
Game::logger->Log("SlashCommandHandler", "Teleporting objectID: %llu to X: %f, Z: %f\n", entity->GetObjectID(), pos.x, pos.z); Game::logger->Log("SlashCommandHandler", "Teleporting objectID: %llu to X: %f, Z: %f\n", entity->GetObjectID(), pos.x, pos.z);
GameMessages::SendTeleport(entity->GetObjectID(), pos, NiQuaternion(), sysAddr); GameMessages::SendTeleport(entity->GetObjectID(), pos, NiQuaternion(), sysAddr);
} else { } else {
@ -1216,7 +1205,6 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit
dest->SetImagination(999); dest->SetImagination(999);
dest->SetMaxImagination(999.0f); dest->SetMaxImagination(999.0f);
} }
EntityManager::Instance()->SerializeEntity(entity); EntityManager::Instance()->SerializeEntity(entity);
} }
@ -1242,11 +1230,11 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit
dest->SetImagination(9); dest->SetImagination(9);
dest->SetMaxImagination(9.0f); dest->SetMaxImagination(9.0f);
} }
EntityManager::Instance()->SerializeEntity(entity); EntityManager::Instance()->SerializeEntity(entity);
} }
if (chatCommand == "refillstats" && entity->GetGMLevel() >= GAME_MASTER_LEVEL_DEVELOPER) { if (chatCommand == "refillstats" && entity->GetGMLevel() >= GAME_MASTER_LEVEL_DEVELOPER) {
auto dest = static_cast<DestroyableComponent*>(entity->GetComponent(COMPONENT_TYPE_DESTROYABLE)); auto dest = static_cast<DestroyableComponent*>(entity->GetComponent(COMPONENT_TYPE_DESTROYABLE));
if (dest) { if (dest) {
dest->SetHealth((int)dest->GetMaxHealth()); dest->SetHealth((int)dest->GetMaxHealth());
@ -1314,8 +1302,8 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit
CharacterComponent* character = entity->GetComponent<CharacterComponent>(); CharacterComponent* character = entity->GetComponent<CharacterComponent>();
if (character) character->SetUScore(character->GetUScore() + uscore); if (character) character->SetUScore(character->GetUScore() + uscore);
// LOOT_SOURCE_MODERATION should work but it doesn't. Relog to see uscore changes
GameMessages::SendModifyLEGOScore(entity, entity->GetSystemAddress(), uscore, LOOTTYPE_NONE); GameMessages::SendModifyLEGOScore(entity, entity->GetSystemAddress(), uscore, eLootSourceType::LOOT_SOURCE_MODERATION);
} }
if (chatCommand == "pos" && entity->GetGMLevel() >= GAME_MASTER_LEVEL_DEVELOPER) { if (chatCommand == "pos" && entity->GetGMLevel() >= GAME_MASTER_LEVEL_DEVELOPER) {
@ -1359,7 +1347,7 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit
} }
auto* ch = entity->GetCharacter(); auto* ch = entity->GetCharacter();
ch->SetCoins(ch->GetCoins() + money, LOOT_SOURCE_MODERATION); ch->SetCoins(ch->GetCoins() + money, eLootSourceType::LOOT_SOURCE_MODERATION);
} }
if ((chatCommand == "setcurrency") && args.size() == 1 && entity->GetGMLevel() >= GAME_MASTER_LEVEL_DEVELOPER) { if ((chatCommand == "setcurrency") && args.size() == 1 && entity->GetGMLevel() >= GAME_MASTER_LEVEL_DEVELOPER) {
@ -1372,7 +1360,7 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit
} }
auto* ch = entity->GetCharacter(); auto* ch = entity->GetCharacter();
ch->SetCoins(money, LOOT_SOURCE_MODERATION); ch->SetCoins(money, eLootSourceType::LOOT_SOURCE_MODERATION);
} }
// Allow for this on even while not a GM, as it sometimes toggles incorrrectly. // Allow for this on even while not a GM, as it sometimes toggles incorrrectly.
@ -1509,7 +1497,6 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit
WorldPackets::SendTransferToWorld(sysAddr, serverIP, serverPort, mythranShift); WorldPackets::SendTransferToWorld(sysAddr, serverIP, serverPort, mythranShift);
}); });
return; return;
}); });
} else { } else {
@ -1648,7 +1635,7 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit
std::vector<LDFBaseData*> data {}; std::vector<LDFBaseData*> data {};
data.push_back(new LDFData<int32_t>(u"reforgedLOT", reforgedItem)); data.push_back(new LDFData<int32_t>(u"reforgedLOT", reforgedItem));
inventoryComponent->AddItem(baseItem, 1, INVALID, data); inventoryComponent->AddItem(baseItem, 1, eLootSourceType::LOOT_SOURCE_MODERATION, eInventoryType::INVALID, data);
} }
if (chatCommand == "crash" && entity->GetGMLevel() >= GAME_MASTER_LEVEL_OPERATOR) if (chatCommand == "crash" && entity->GetGMLevel() >= GAME_MASTER_LEVEL_OPERATOR)
@ -1953,54 +1940,17 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit
} }
bool SlashCommandHandler::CheckIfAccessibleZone(const unsigned int zoneID) { bool SlashCommandHandler::CheckIfAccessibleZone(const unsigned int zoneID) {
switch (zoneID) { //We're gonna go ahead and presume we've got the db loaded already:
case 98: CDZoneTableTable * zoneTable = CDClientManager::Instance()->GetTable<CDZoneTableTable>("ZoneTable");
case 1000: const CDZoneTable* zone = zoneTable->Query(zoneID);
case 1001: if (zone != nullptr) {
std::string zonePath = "./res/maps/" + zone->zoneName;
case 1100: std::transform(zonePath.begin(), zonePath.end(), zonePath.begin(), ::tolower);
case 1101: std::ifstream f(zonePath.c_str());
case 1150: return f.good();
case 1151: } else {
case 1152:
case 1200:
case 1201:
case 1250:
case 1251:
case 1260:
case 1300:
case 1350:
case 1351:
case 1400:
case 1401:
case 1450:
case 1451:
case 1600:
case 1601:
case 1602:
case 1603:
case 1604:
case 1700:
case 1800:
case 1900:
case 2000:
case 58004:
case 58005:
case 58006:
return true;
default:
return false; return false;
} }
return false;
} }
void SlashCommandHandler::SendAnnouncement(const std::string& title, const std::string& message) { void SlashCommandHandler::SendAnnouncement(const std::string& title, const std::string& message) {

View File

@ -47,6 +47,7 @@ namespace Game {
bool shutdownSequenceStarted = false; bool shutdownSequenceStarted = false;
void ShutdownSequence(); void ShutdownSequence();
int FinalizeShutdown();
dLogger* SetupLogger(); dLogger* SetupLogger();
void StartAuthServer(); void StartAuthServer();
void StartChatServer(); void StartChatServer();
@ -63,10 +64,11 @@ int main(int argc, char** argv) {
//Triggers the shutdown sequence at application exit //Triggers the shutdown sequence at application exit
std::atexit(ShutdownSequence); std::atexit(ShutdownSequence);
signal(SIGINT, [](int) { ShutdownSequence(); }); signal(SIGINT, [](int) { ShutdownSequence(); });
signal(SIGTERM, [](int) { ShutdownSequence(); });
//Create all the objects we need to run our service: //Create all the objects we need to run our service:
Game::logger = SetupLogger(); Game::logger = SetupLogger();
if (!Game::logger) return 0; if (!Game::logger) return EXIT_FAILURE;
Game::logger->Log("MasterServer", "Starting Master server...\n"); Game::logger->Log("MasterServer", "Starting Master server...\n");
Game::logger->Log("MasterServer", "Version: %i.%i\n", PROJECT_VERSION_MAJOR, PROJECT_VERSION_MINOR); Game::logger->Log("MasterServer", "Version: %i.%i\n", PROJECT_VERSION_MAJOR, PROJECT_VERSION_MINOR);
@ -83,7 +85,7 @@ int main(int argc, char** argv) {
std::ifstream cdclient_fd(cdclient_path); std::ifstream cdclient_fd(cdclient_path);
if (!cdclient_fd.good()) { if (!cdclient_fd.good()) {
Game::logger->Log("WorldServer", "%s could not be opened\n", cdclient_path.c_str()); Game::logger->Log("WorldServer", "%s could not be opened\n", cdclient_path.c_str());
return -1; return EXIT_FAILURE;
} }
cdclient_fd.close(); cdclient_fd.close();
@ -94,7 +96,7 @@ int main(int argc, char** argv) {
Game::logger->Log("WorldServer", "Unable to connect to CDServer SQLite Database\n"); Game::logger->Log("WorldServer", "Unable to connect to CDServer SQLite Database\n");
Game::logger->Log("WorldServer", "Error: %s\n", e.errorMessage()); Game::logger->Log("WorldServer", "Error: %s\n", e.errorMessage());
Game::logger->Log("WorldServer", "Error Code: %i\n", e.errorCode()); Game::logger->Log("WorldServer", "Error Code: %i\n", e.errorCode());
return -1; return EXIT_FAILURE;
} }
//Get CDClient initial information //Get CDClient initial information
@ -105,7 +107,7 @@ int main(int argc, char** argv) {
Game::logger->Log("WorldServer", "May be caused by corrupted file: %s\n", cdclient_path.c_str()); Game::logger->Log("WorldServer", "May be caused by corrupted file: %s\n", cdclient_path.c_str());
Game::logger->Log("WorldServer", "Error: %s\n", e.errorMessage()); Game::logger->Log("WorldServer", "Error: %s\n", e.errorMessage());
Game::logger->Log("WorldServer", "Error Code: %i\n", e.errorCode()); Game::logger->Log("WorldServer", "Error Code: %i\n", e.errorCode());
return -1; return EXIT_FAILURE;
} }
//Connect to the MySQL Database //Connect to the MySQL Database
@ -118,7 +120,7 @@ int main(int argc, char** argv) {
Database::Connect(mysql_host, mysql_database, mysql_username, mysql_password); Database::Connect(mysql_host, mysql_database, mysql_username, mysql_password);
} catch (sql::SQLException& ex) { } catch (sql::SQLException& ex) {
Game::logger->Log("MasterServer", "Got an error while connecting to the database: %s\n", ex.what()); Game::logger->Log("MasterServer", "Got an error while connecting to the database: %s\n", ex.what());
return 0; return EXIT_FAILURE;
} }
//If the first command line argument is -a or --account then make the user //If the first command line argument is -a or --account then make the user
@ -166,7 +168,11 @@ int main(int argc, char** argv) {
delete statement; delete statement;
std::cout << "Account created successfully!\n"; std::cout << "Account created successfully!\n";
return 0;
Database::Destroy("MasterServer");
delete Game::logger;
return EXIT_SUCCESS;
} }
int maxClients = 999; int maxClients = 999;
@ -266,7 +272,8 @@ int main(int argc, char** argv) {
//10m shutdown for universe kill command //10m shutdown for universe kill command
if (shouldShutdown) { if (shouldShutdown) {
if (framesSinceKillUniverseCommand >= 40000) { if (framesSinceKillUniverseCommand >= 40000) {
std::exit(0); //Break main loop and exit
break;
} }
else else
framesSinceKillUniverseCommand++; framesSinceKillUniverseCommand++;
@ -312,14 +319,9 @@ int main(int argc, char** argv) {
t += std::chrono::milliseconds(highFrameRate); t += std::chrono::milliseconds(highFrameRate);
std::this_thread::sleep_until(t); std::this_thread::sleep_until(t);
} }
FinalizeShutdown();
//Delete our objects here: exit(EXIT_SUCCESS);
Database::Destroy(); return EXIT_SUCCESS;
delete Game::im;
delete Game::server;
delete Game::logger;
return 0;
} }
dLogger* SetupLogger() { dLogger* SetupLogger() {
@ -739,12 +741,20 @@ void ShutdownSequence() {
auto ticks = 0; auto ticks = 0;
if (!Game::im) { if (!Game::im) {
exit(0); exit(EXIT_SUCCESS);
} }
Game::logger->Log("MasterServer", "Attempting to shutdown instances, max 60 seconds...\n"); Game::logger->Log("MasterServer", "Attempting to shutdown instances, max 60 seconds...\n");
while (true) { while (true) {
auto packet = Game::server->Receive();
if (packet) {
HandlePacket(packet);
Game::server->DeallocatePacket(packet);
packet = nullptr;
}
auto done = true; auto done = true;
for (auto* instance : Game::im->GetInstances()) { for (auto* instance : Game::im->GetInstances()) {
@ -758,6 +768,7 @@ void ShutdownSequence() {
} }
if (done) { if (done) {
Game::logger->Log("MasterServer", "Finished shutting down MasterServer!\n");
break; break;
} }
@ -767,9 +778,21 @@ void ShutdownSequence() {
ticks++; ticks++;
if (ticks == 600 * 6) { if (ticks == 600 * 6) {
Game::logger->Log("MasterServer", "Finished shutting down by timeout!\n");
break; break;
} }
} }
exit(0); FinalizeShutdown();
}
int FinalizeShutdown() {
//Delete our objects here:
Database::Destroy("MasterServer");
delete Game::im;
delete Game::server;
delete Game::logger;
exit(EXIT_SUCCESS);
return EXIT_SUCCESS;
} }

View File

@ -12,6 +12,7 @@
#include "LDFFormat.h" #include "LDFFormat.h"
#include "dServer.h" #include "dServer.h"
#include "dZoneManager.h" #include "dZoneManager.h"
#include "CharacterComponent.h"
#include "ZCompression.h" #include "ZCompression.h"
void WorldPackets::SendLoadStaticZone(const SystemAddress& sysAddr, float x, float y, float z, uint32_t checksum) { void WorldPackets::SendLoadStaticZone(const SystemAddress& sysAddr, float x, float y, float z, uint32_t checksum) {
@ -126,19 +127,26 @@ void WorldPackets::SendServerState ( const SystemAddress& sysAddr ) {
SEND_PACKET SEND_PACKET
} }
void WorldPackets::SendCreateCharacter(const SystemAddress& sysAddr, const LWOOBJID& objectID, const std::string& xmlData, const std::u16string& username, int32_t gm) { void WorldPackets::SendCreateCharacter(const SystemAddress& sysAddr, Entity* entity, const std::string& xmlData, const std::u16string& username, int32_t gm) {
RakNet::BitStream bitStream; RakNet::BitStream bitStream;
PacketUtils::WriteHeader(bitStream, CLIENT, MSG_CLIENT_CREATE_CHARACTER); PacketUtils::WriteHeader(bitStream, CLIENT, MSG_CLIENT_CREATE_CHARACTER);
RakNet::BitStream data; RakNet::BitStream data;
data.Write<uint32_t>(6); //LDF key count data.Write<uint32_t>(7); //LDF key count
LDFData<LWOOBJID>* objid = new LDFData<LWOOBJID>(u"objid", objectID); auto character = entity->GetComponent<CharacterComponent>();
if (!character) {
Game::logger->Log("WorldPackets", "Entity is not a character?? what??");
return;
}
LDFData<LWOOBJID>* objid = new LDFData<LWOOBJID>(u"objid", entity->GetObjectID());
LDFData<LOT>* lot = new LDFData<LOT>(u"template", 1); LDFData<LOT>* lot = new LDFData<LOT>(u"template", 1);
LDFData<std::string> * xmlConfigData = new LDFData<std::string>(u"xmlData", xmlData); LDFData<std::string> * xmlConfigData = new LDFData<std::string>(u"xmlData", xmlData);
LDFData<std::u16string>* name = new LDFData<std::u16string>(u"name", username); LDFData<std::u16string>* name = new LDFData<std::u16string>(u"name", username);
LDFData<int32_t>* gmlevel = new LDFData<int32_t>(u"gmlevel", gm); LDFData<int32_t>* gmlevel = new LDFData<int32_t>(u"gmlevel", gm);
LDFData<int32_t>* chatmode = new LDFData<int32_t>(u"chatmode", gm); LDFData<int32_t>* chatmode = new LDFData<int32_t>(u"chatmode", gm);
LDFData<int64_t>* reputation = new LDFData<int64_t>(u"reputation", character->GetReputation());
objid->WriteToPacket(&data); objid->WriteToPacket(&data);
lot->WriteToPacket(&data); lot->WriteToPacket(&data);
@ -146,6 +154,7 @@ void WorldPackets::SendCreateCharacter(const SystemAddress& sysAddr, const LWOOB
gmlevel->WriteToPacket(&data); gmlevel->WriteToPacket(&data);
chatmode->WriteToPacket(&data); chatmode->WriteToPacket(&data);
xmlConfigData->WriteToPacket(&data); xmlConfigData->WriteToPacket(&data);
reputation->WriteToPacket(&data);
delete objid; delete objid;
delete lot; delete lot;
@ -153,6 +162,7 @@ void WorldPackets::SendCreateCharacter(const SystemAddress& sysAddr, const LWOOB
delete gmlevel; delete gmlevel;
delete chatmode; delete chatmode;
delete name; delete name;
delete reputation;
#ifdef _WIN32 #ifdef _WIN32
bitStream.Write<uint32_t>(data.GetNumberOfBytesUsed() + 1); bitStream.Write<uint32_t>(data.GetNumberOfBytesUsed() + 1);
@ -175,7 +185,7 @@ void WorldPackets::SendCreateCharacter(const SystemAddress& sysAddr, const LWOOB
PacketUtils::SavePacket("chardata.bin", (const char *)bitStream.GetData(), static_cast<uint32_t>(bitStream.GetNumberOfBytesUsed())); PacketUtils::SavePacket("chardata.bin", (const char *)bitStream.GetData(), static_cast<uint32_t>(bitStream.GetNumberOfBytesUsed()));
SEND_PACKET SEND_PACKET
Game::logger->Log("WorldPackets", "Sent CreateCharacter for ID: %llu\n", objectID); Game::logger->Log("WorldPackets", "Sent CreateCharacter for ID: %llu\n", entity->GetObjectID());
} }
void WorldPackets::SendChatModerationResponse(const SystemAddress& sysAddr, bool requestAccepted, uint32_t requestID, const std::string& receiver, std::unordered_map<char, char> unacceptedItems) { void WorldPackets::SendChatModerationResponse(const SystemAddress& sysAddr, bool requestAccepted, uint32_t requestID, const std::string& receiver, std::unordered_map<char, char> unacceptedItems) {

View File

@ -4,6 +4,7 @@
#include "dCommonVars.h" #include "dCommonVars.h"
#include <string> #include <string>
#include <unordered_map> #include <unordered_map>
#include "Entity.h"
class User; class User;
struct SystemAddress; struct SystemAddress;
@ -16,7 +17,7 @@ namespace WorldPackets {
void SendCharacterDeleteResponse(const SystemAddress& sysAddr, bool response); void SendCharacterDeleteResponse(const SystemAddress& sysAddr, bool response);
void SendTransferToWorld(const SystemAddress& sysAddr, const std::string& serverIP, uint32_t serverPort, bool mythranShift); void SendTransferToWorld(const SystemAddress& sysAddr, const std::string& serverIP, uint32_t serverPort, bool mythranShift);
void SendServerState(const SystemAddress& sysAddr); void SendServerState(const SystemAddress& sysAddr);
void SendCreateCharacter(const SystemAddress& sysAddr, const LWOOBJID& objectID, const std::string& xmlData, const std::u16string& username, int32_t gm); void SendCreateCharacter(const SystemAddress& sysAddr, Entity* entity, const std::string& xmlData, const std::u16string& username, int32_t gm);
void SendChatModerationResponse(const SystemAddress& sysAddr, bool requestAccepted, uint32_t requestID, const std::string& receiver, std::unordered_map<char, char> unacceptedItems); void SendChatModerationResponse(const SystemAddress& sysAddr, bool requestAccepted, uint32_t requestID, const std::string& receiver, std::unordered_map<char, char> unacceptedItems);
void SendGMLevelChange(const SystemAddress& sysAddr, bool success, uint8_t highestLevel, uint8_t prevLevel, uint8_t newLevel); void SendGMLevelChange(const SystemAddress& sysAddr, bool success, uint8_t highestLevel, uint8_t prevLevel, uint8_t newLevel);
} }

View File

@ -324,6 +324,7 @@ enum GAME_MSG : unsigned short {
GAME_MSG_ACTIVITY_STOP = 408, GAME_MSG_ACTIVITY_STOP = 408,
GAME_MSG_SHOOTING_GALLERY_CLIENT_AIM_UPDATE = 409, GAME_MSG_SHOOTING_GALLERY_CLIENT_AIM_UPDATE = 409,
GAME_MSG_SHOOTING_GALLERY_FIRE = 411, GAME_MSG_SHOOTING_GALLERY_FIRE = 411,
GAME_MSG_REQUEST_VENDOR_STATUS_UPDATE = 416,
GAME_MSG_VENDOR_STATUS_UPDATE = 417, GAME_MSG_VENDOR_STATUS_UPDATE = 417,
GAME_MSG_NOTIFY_CLIENT_SHOOTING_GALLERY_SCORE = 425, GAME_MSG_NOTIFY_CLIENT_SHOOTING_GALLERY_SCORE = 425,
GAME_MSG_CONSUME_CLIENT_ITEM = 427, GAME_MSG_CONSUME_CLIENT_ITEM = 427,
@ -382,6 +383,7 @@ enum GAME_MSG : unsigned short {
GAME_MSG_PROPERTY_EDITOR_END = 725, GAME_MSG_PROPERTY_EDITOR_END = 725,
GAME_MSG_START_PATHING = 735, GAME_MSG_START_PATHING = 735,
GAME_MSG_NOTIFY_CLIENT_ZONE_OBJECT = 737, GAME_MSG_NOTIFY_CLIENT_ZONE_OBJECT = 737,
GAME_MSG_UPDATE_REPUTATION = 746,
GAME_MSG_PROPERTY_RENTAL_RESPONSE = 750, GAME_MSG_PROPERTY_RENTAL_RESPONSE = 750,
GAME_MSG_REQUEST_PLATFORM_RESYNC = 760, GAME_MSG_REQUEST_PLATFORM_RESYNC = 760,
GAME_MSG_PLATFORM_RESYNC = 761, GAME_MSG_PLATFORM_RESYNC = 761,
@ -527,11 +529,13 @@ enum GAME_MSG : unsigned short {
GAME_MSG_VEHICLE_NOTIFY_HIT_IMAGINATION_SERVER = 1606, GAME_MSG_VEHICLE_NOTIFY_HIT_IMAGINATION_SERVER = 1606,
GAME_MSG_ADD_RUN_SPEED_MODIFIER = 1505, GAME_MSG_ADD_RUN_SPEED_MODIFIER = 1505,
GAME_MSG_REMOVE_RUN_SPEED_MODIFIER = 1506, GAME_MSG_REMOVE_RUN_SPEED_MODIFIER = 1506,
GAME_MSG_UPDATE_PROPERTY_PERFORMANCE_COST = 1547,
GAME_MSG_PROPERTY_ENTRANCE_BEGIN = 1553, GAME_MSG_PROPERTY_ENTRANCE_BEGIN = 1553,
GAME_MSG_REQUEST_MOVE_ITEM_BETWEEN_INVENTORY_TYPES = 1666, GAME_MSG_REQUEST_MOVE_ITEM_BETWEEN_INVENTORY_TYPES = 1666,
GAME_MSG_RESPONSE_MOVE_ITEM_BETWEEN_INVENTORY_TYPES = 1667, GAME_MSG_RESPONSE_MOVE_ITEM_BETWEEN_INVENTORY_TYPES = 1667,
GAME_MSG_PLAYER_SET_CAMERA_CYCLING_MODE = 1676, GAME_MSG_PLAYER_SET_CAMERA_CYCLING_MODE = 1676,
GAME_MSG_NOTIFY_SERVER_LEVEL_PROCESSING_COMPLETE = 1734, GAME_MSG_NOTIFY_SERVER_LEVEL_PROCESSING_COMPLETE = 1734,
GAME_MSG_NOTIFY_LEVEL_REWARDS = 1735,
GAME_MSG_MARK_INVENTORY_ITEM_AS_ACTIVE = 1767, GAME_MSG_MARK_INVENTORY_ITEM_AS_ACTIVE = 1767,
END END
}; };

53
dScripts/ActMine.cpp Normal file
View File

@ -0,0 +1,53 @@
#include "ActMine.h"
#include "SkillComponent.h"
#include "DestroyableComponent.h"
#include "RebuildComponent.h"
void ActMine::OnStartup(Entity* self) {
self->SetVar(u"RebuildComplete", false);
self->SetProximityRadius(MINE_RADIUS, "mineRadius");
}
void ActMine::OnRebuildNotifyState(Entity* self, eRebuildState state)
{
if (state == eRebuildState::REBUILD_COMPLETED) {
auto* rebuild = self->GetComponent<RebuildComponent>();
if (rebuild) {
auto* builder = rebuild->GetBuilder();
self->SetVar(u"Builder", builder->GetObjectID());
}
self->SetVar(u"RebuildComplete", true);
self->SetVar(u"NumWarnings", 0);
self->AddToGroup("reset");
}
}
void ActMine::OnProximityUpdate(Entity* self, Entity* entering, std::string name, std::string status) {
auto* detroyable = self->GetComponent<DestroyableComponent>();
if (!detroyable) return;
if (status == "ENTER" && self->GetVar<bool>(u"RebuildComplete") == true && detroyable->IsEnemy(entering)) {
GameMessages::SendPlayFXEffect(self->GetObjectID(), 242, u"orange", "sirenlight_B");
self->AddTimer("Tick", TICK_TIME);
}
}
void ActMine::OnTimerDone(Entity* self, std::string timerName) {
if (timerName == "Tick") {
if (self->GetVar<int>(u"NumWarnings") >= MAX_WARNINGS){
auto* skill = self->GetComponent<SkillComponent>();
if (!skill) return;
skill->CalculateBehavior(SKILL_ID, BEHAVIOR_ID, LWOOBJID_EMPTY);
self->AddTimer("BlowedUp", BLOWED_UP_TIME);
} else {
GameMessages::SendPlayFXEffect(self->GetObjectID(), 242, u"orange", "sirenlight_B");
self->AddTimer("Tick", TICK_TIME);
self->SetVar(u"NumWarnings", self->GetVar<int>(u"NumWarnings") + 1);
}
}
if (timerName == "BlowedUp") {
self->Kill(self);
}
}

18
dScripts/ActMine.h Normal file
View File

@ -0,0 +1,18 @@
#pragma once
#include "CppScripts.h"
class ActMine : public CppScripts::Script {
public:
void OnStartup(Entity* self);
void OnRebuildNotifyState(Entity* self, eRebuildState state) override;
void OnProximityUpdate(Entity* self, Entity* entering, std::string name, std::string status);
void OnTimerDone(Entity* self, std::string timerName);
private:
int MAX_WARNINGS = 3;
float MINE_RADIUS = 10.0;
float TICK_TIME = 0.25;
float BLOWED_UP_TIME = 0.1;
uint32_t SKILL_ID = 317;
uint32_t BEHAVIOR_ID = 3719;
};

View File

@ -7,10 +7,16 @@
class AgFans : public CppScripts::Script class AgFans : public CppScripts::Script
{ {
public: public:
void OnStartup(Entity* self); void OnStartup(Entity* self) override;
void OnDie(Entity* self, Entity* killer); void OnDie(Entity* self, Entity* killer) override;
void OnFireEventServerSide(Entity *self, Entity *sender, std::string args, int32_t param1, int32_t param2, void OnFireEventServerSide(
int32_t param3); Entity *self,
Entity *sender,
std::string args,
int32_t param1,
int32_t param2,
int32_t param3
) override;
private: private:
void ToggleFX(Entity* self, bool hit); void ToggleFX(Entity* self, bool hit);
}; };

View File

@ -1,15 +1,66 @@
#include "AgSurvivalBuffStation.h" #include "AgSurvivalBuffStation.h"
#include "DestroyableComponent.h"
#include "EntityManager.h"
#include "GameMessages.h"
#include "SkillComponent.h" #include "SkillComponent.h"
#include "dLogger.h" #include "dLogger.h"
#include "TeamManager.h"
void AgSurvivalBuffStation::OnRebuildComplete(Entity* self, Entity* target) { void AgSurvivalBuffStation::OnRebuildComplete(Entity* self, Entity* target) {
auto destroyableComponent = self->GetComponent<DestroyableComponent>();
// We set the faction to 1 so that the buff station sees players as friendly targets to buff
if (destroyableComponent != nullptr) destroyableComponent->SetFaction(1);
auto skillComponent = self->GetComponent<SkillComponent>(); auto skillComponent = self->GetComponent<SkillComponent>();
if (skillComponent == nullptr) return; if (skillComponent != nullptr) skillComponent->CalculateBehavior(skillIdForBuffStation, behaviorIdForBuffStation, self->GetObjectID());
skillComponent->CalculateBehavior(201, 1784, self->GetObjectID()); self->AddCallbackTimer(smashTimer, [self]() {
self->AddCallbackTimer(10.0f, [self]() {
self->Smash(); self->Smash();
}); });
self->AddTimer("DropArmor", dropArmorTimer);
self->AddTimer("DropLife", dropLifeTimer);
self->AddTimer("Dropimagination", dropImaginationTimer);
// Since all survival players should be on the same team, we get the team.
auto team = TeamManager::Instance()->GetTeam(target->GetObjectID());
std::vector<LWOOBJID> builderTeam;
// Not on a team
if (team == nullptr) {
builderTeam.push_back(target->GetObjectID());
self->SetVar<std::vector<LWOOBJID>>(u"BuilderTeam", builderTeam);
return;
}
for (auto memberID : team->members) {
builderTeam.push_back(memberID);
}
self->SetVar<std::vector<LWOOBJID>>(u"BuilderTeam", builderTeam);
}
void AgSurvivalBuffStation::OnTimerDone(Entity* self, std::string timerName) {
uint32_t powerupToDrop = lifePowerup;
if (timerName == "DropArmor") {
powerupToDrop = armorPowerup;
self->AddTimer("DropArmor", dropArmorTimer);
}
if (timerName == "DropLife") {
powerupToDrop = lifePowerup;
self->AddTimer("DropLife", dropLifeTimer);
}
if (timerName == "Dropimagination") {
powerupToDrop = imaginationPowerup;
self->AddTimer("Dropimagination", dropImaginationTimer);
}
auto team = self->GetVar<std::vector<LWOOBJID>>(u"BuilderTeam");
for (auto memberID : team) {
auto member = EntityManager::Instance()->GetEntity(memberID);
if (member != nullptr && !member->GetIsDead()) {
GameMessages::SendDropClientLoot(member, self->GetObjectID(), powerupToDrop, 0, self->GetPosition());
} else {
// If player left the team or left early erase them from the team variable.
team.erase(std::find(team.begin(), team.end(), memberID));
self->SetVar<std::vector<LWOOBJID>>(u"BuilderTeam", team);
}
}
} }

View File

@ -11,6 +11,7 @@ public:
* @param target The target of the self that called this script. * @param target The target of the self that called this script.
*/ */
void OnRebuildComplete(Entity* self, Entity* target) override; void OnRebuildComplete(Entity* self, Entity* target) override;
void OnTimerDone(Entity* self, std::string timerName) override;
private: private:
/** /**
* Skill ID for the buff station. * Skill ID for the buff station.
@ -20,4 +21,32 @@ private:
* Behavior ID for the buff station. * Behavior ID for the buff station.
*/ */
uint32_t behaviorIdForBuffStation = 1784; uint32_t behaviorIdForBuffStation = 1784;
/**
* Timer for dropping armor.
*/
float dropArmorTimer = 6.0f;
/**
* Timer for dropping life.
*/
float dropLifeTimer = 3.0f;
/**
* Timer for dropping imagination.
*/
float dropImaginationTimer = 4.0f;
/**
* Timer for smashing.
*/
float smashTimer = 25.0f;
/**
* LOT for armor powerup.
*/
LOT armorPowerup = 6431;
/**
* LOT for life powerup.
*/
LOT lifePowerup = 177;
/**
* LOT for imagination powerup.
*/
LOT imaginationPowerup = 935;
}; };

View File

@ -0,0 +1,164 @@
#include "AmDarklingDragon.h"
#include "BaseCombatAIComponent.h"
#include "DestroyableComponent.h"
#include "EntityManager.h"
#include "GameMessages.h"
#include "SkillComponent.h"
#include "BaseCombatAIComponent.h"
void AmDarklingDragon::OnStartup(Entity* self) {
self->SetVar<int32_t>(u"weakspot", 0);
auto* baseCombatAIComponent = self->GetComponent<BaseCombatAIComponent>();
if (baseCombatAIComponent != nullptr) {
baseCombatAIComponent->SetStunImmune(true);
}
}
void AmDarklingDragon::OnDie(Entity* self, Entity* killer) {
if (self->GetVar<bool>(u"bDied")) {
return;
}
self->SetVar<bool>(u"bDied", true);
auto golemId = self->GetVar<LWOOBJID>(u"Golem");
auto* golem = EntityManager::Instance()->GetEntity(golemId);
if (golem != nullptr) {
golem->Smash(self->GetObjectID());
}
}
void AmDarklingDragon::OnHitOrHealResult(Entity* self, Entity* attacker, int32_t damage) {
GameMessages::SendPlayFXEffect(self, -1, u"gothit", "", LWOOBJID_EMPTY, 1, 1, true);
if (true) {
auto weakpoint = self->GetVar<int32_t>(u"weakspot");
if (weakpoint == 1)
{
self->Smash(attacker->GetObjectID());
}
}
auto* destroyableComponent = self->GetComponent<DestroyableComponent>();
if (destroyableComponent != nullptr) {
Game::logger->Log("AmDarklingDragon", "Armor is %i\n", destroyableComponent->GetArmor());
if (destroyableComponent->GetArmor() > 0) return;
auto weakpoint = self->GetVar<int32_t>(u"weakpoint");
if (weakpoint == 0) {
Game::logger->Log("AmDarklingDragon", "Activating weakpoint\n");
self->AddTimer("ReviveTimer", 12);
auto* baseCombatAIComponent = self->GetComponent<BaseCombatAIComponent>();
auto* skillComponent = self->GetComponent<SkillComponent>();
if (baseCombatAIComponent != nullptr) {
baseCombatAIComponent->SetDisabled(true);
baseCombatAIComponent->SetStunned(true);
}
if (skillComponent != nullptr) {
skillComponent->Interrupt();
}
self->SetVar<int32_t>(u"weakpoint", 2);
GameMessages::SendPlayAnimation(self, u"stunstart", 1.7f);
self->AddTimer("timeToStunLoop", 1);
auto position = self->GetPosition();
auto forward = self->GetRotation().GetForwardVector();
auto backwards = forward * -1;
forward.x *= 10;
forward.z *= 10;
auto rotation = self->GetRotation();
auto objectPosition = NiPoint3();
objectPosition.y = position.y;
objectPosition.x = position.x - (backwards.x * 8);
objectPosition.z = position.z - (backwards.z * 8);
auto golem = self->GetVar<int32_t>(u"DragonSmashingGolem");
EntityInfo info {};
info.lot = golem != 0 ? golem : 8340;
info.pos = objectPosition;
info.rot = rotation;
info.spawnerID = self->GetObjectID();
info.settings = {
new LDFData<std::string>(u"rebuild_activators",
std::to_string(objectPosition.x + forward.x) + "\x1f" +
std::to_string(objectPosition.y) + "\x1f" +
std::to_string(objectPosition.z + forward.z)
),
new LDFData<int32_t>(u"respawn", 100000),
new LDFData<float>(u"rebuild_reset_time", 15),
new LDFData<bool>(u"no_timed_spawn", true),
new LDFData<LWOOBJID>(u"Dragon", self->GetObjectID())
};
auto* golemObject = EntityManager::Instance()->CreateEntity(info);
EntityManager::Instance()->ConstructEntity(golemObject);
}
}
}
void AmDarklingDragon::OnTimerDone(Entity* self, std::string timerName) {
if (timerName == "ReviveHeldTimer") {
self->AddTimer("backToAttack", 2.5);
}
else if (timerName == "ExposeWeakSpotTimer") {
self->SetVar<int32_t>(u"weakspot", 1);
}
else if (timerName == "timeToStunLoop") {
GameMessages::SendPlayAnimation(self, u"stunloop", 1.8f);
}
else if (timerName == "ReviveTimer") {
GameMessages::SendPlayAnimation(self, u"stunend", 2.0f);
self->AddTimer("backToAttack", 1);
}
else if (timerName == "backToAttack") {
auto* baseCombatAIComponent = self->GetComponent<BaseCombatAIComponent>();
auto* skillComponent = self->GetComponent<SkillComponent>();
if (baseCombatAIComponent != nullptr)
{
baseCombatAIComponent->SetDisabled(false);
baseCombatAIComponent->SetStunned(false);
}
if (skillComponent != nullptr)
{
skillComponent->Interrupt();
}
self->SetVar<int32_t>(u"weakspot", -1);
GameMessages::SendNotifyObject(self->GetObjectID(), self->GetObjectID(), u"DragonRevive", UNASSIGNED_SYSTEM_ADDRESS);
}
}
void AmDarklingDragon::OnFireEventServerSide(Entity *self, Entity *sender, std::string args, int32_t param1, int32_t param2, int32_t param3) {
if (args != "rebuildDone") return;
self->AddTimer("ExposeWeakSpotTimer", 3.8f);
self->CancelTimer("ReviveTimer");
self->AddTimer("ReviveHeldTimer", 10.5f);
self->SetVar<LWOOBJID>(u"Golem", sender->GetObjectID());
GameMessages::SendPlayAnimation(self, u"quickbuildhold", 1.9f);
}

View File

@ -0,0 +1,48 @@
#pragma once
#include "CppScripts.h"
class AmDarklingDragon : public CppScripts::Script
{
public:
/**
* @brief When called, this function will make self immune to stuns and initialize a weakspot boolean to false.
*
* @param self The Entity that called this function.
*/
void OnStartup(Entity* self) override;
/**
* @brief When called, this function will destroy the golem if it was alive, otherwise returns immediately.
*
* @param self The Entity that called this function.
* @param killer The Entity that killed self.
*/
void OnDie(Entity* self, Entity* killer) override;
/**
* @brief When self is hit or healed, this function will check if self is at zero armor. If self is at zero armor, a golem Entity Quick Build
* is spawned that, when built, will reveal a weakpoint on the dragon that if hit will smash the dragon instantly. If at more than zero armor,
* this function returns early.
*
* @param self The Entity that was hit.
* @param attacker The Entity that attacked self.
* @param damage The amount of damage attacker did to self.
*/
void OnHitOrHealResult(Entity* self, Entity* attacker, int32_t damage) override;
/**
* @brief Called when self has a timer that ended.
*
* @param self The Entity who owns a timer that finished.
* @param timerName The name of a timer attacked to self that has ended.
*/
void OnTimerDone(Entity* self, std::string timerName) override;
/**
* @brief When the Client has finished rebuilding the Golem for the dragon, this function exposes the weak spot for a set amount of time.
*
* @param self The Entity that called this script.
* @param sender The Entity that sent a fired event.
* @param args The argument that tells us what event has been fired off.
* @param param1 Unused in this script.
* @param param2 Unused in this script.
* @param param3 Unused in this script.
*/
void OnFireEventServerSide(Entity *self, Entity *sender, std::string args, int32_t param1, int32_t param2, int32_t param3) override;
};

View File

@ -7,7 +7,7 @@ public:
void OnStartup(Entity* self) override; void OnStartup(Entity* self) override;
void OnUse(Entity* self, Entity* user) override; void OnUse(Entity* self, Entity* user) override;
void OnTimerDone(Entity* self, std::string timerName) override; void OnTimerDone(Entity* self, std::string timerName) override;
void OnNotifyObject(Entity *self, Entity *sender, const std::string& name, int32_t param1 = 0, int32_t param2 = 0); void OnNotifyObject(Entity *self, Entity *sender, const std::string& name, int32_t param1 = 0, int32_t param2 = 0) override;
void MoveBridgeDown(Entity* self, Entity* bridge, bool down); void MoveBridgeDown(Entity* self, Entity* bridge, bool down);
void NotifyDie(Entity* self, Entity* other); void NotifyDie(Entity* self, Entity* other);

View File

@ -32,7 +32,7 @@ void AmDropshipComputer::OnUse(Entity* self, Entity* user)
return; return;
} }
inventoryComponent->AddItem(12323, 1); inventoryComponent->AddItem(12323, 1, eLootSourceType::LOOT_SOURCE_NONE);
} }
void AmDropshipComputer::OnDie(Entity* self, Entity* killer) void AmDropshipComputer::OnDie(Entity* self, Entity* killer)

View File

@ -1,12 +0,0 @@
#include "AmNamedDarklingDragon.h"
#include "BaseCombatAIComponent.h"
void AmNamedDarklingDragon::OnStartup(Entity* self)
{
auto* baseCombatAIComponent = self->GetComponent<BaseCombatAIComponent>();
if (baseCombatAIComponent != nullptr)
{
baseCombatAIComponent->SetStunImmune(true);
}
}

View File

@ -1,8 +0,0 @@
#pragma once
#include "CppScripts.h"
class AmNamedDarklingDragon : public CppScripts::Script
{
public:
void OnStartup(Entity* self) override;
};

View File

@ -0,0 +1,15 @@
#include "AmTeapotServer.h"
#include "InventoryComponent.h"
#include "GameMessages.h"
void AmTeapotServer::OnUse(Entity* self, Entity* user) {
auto* inventoryComponent = user->GetComponent<InventoryComponent>();
if (!inventoryComponent) return;
if (inventoryComponent->GetLotCount(BLUE_FLOWER_LEAVES) >= 10){
inventoryComponent->RemoveItem(BLUE_FLOWER_LEAVES, 10);
inventoryComponent->AddItem(WU_S_IMAGINATION_TEA, 1);
}
GameMessages::SendTerminateInteraction(user->GetObjectID(), FROM_INTERACTION, self->GetObjectID());
}

Some files were not shown because too many files have changed in this diff Show More