mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2024-11-22 05:27:19 +00:00
Merge branch 'main' into MSVCCompilerFlags
This commit is contained in:
commit
d1bfe9f15d
@ -3,6 +3,12 @@ project(Darkflame
|
|||||||
HOMEPAGE_URL "https://github.com/DarkflameUniverse/DarkflameServer"
|
HOMEPAGE_URL "https://github.com/DarkflameUniverse/DarkflameServer"
|
||||||
LANGUAGES C CXX
|
LANGUAGES C CXX
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# check if the path to the source directory contains a space
|
||||||
|
if("${CMAKE_SOURCE_DIR}" MATCHES " ")
|
||||||
|
message(FATAL_ERROR "The server cannot build in the path (" ${CMAKE_SOURCE_DIR} ") because it contains a space. Please move the server to a path without spaces.")
|
||||||
|
endif()
|
||||||
|
|
||||||
include(CTest)
|
include(CTest)
|
||||||
|
|
||||||
set(CMAKE_C_STANDARD 99)
|
set(CMAKE_C_STANDARD 99)
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
PROJECT_VERSION_MAJOR=1
|
PROJECT_VERSION_MAJOR=2
|
||||||
PROJECT_VERSION_MINOR=1
|
PROJECT_VERSION_MINOR=3
|
||||||
PROJECT_VERSION_PATCH=1
|
PROJECT_VERSION_PATCH=0
|
||||||
|
|
||||||
# Debugging
|
# Debugging
|
||||||
# Set DYNAMIC to 1 to enable the -rdynamic flag for the linker, yielding some symbols in crashlogs.
|
# Set DYNAMIC to 1 to enable the -rdynamic flag for the linker, yielding some symbols in crashlogs.
|
||||||
|
@ -31,7 +31,7 @@ COPY --from=build /app/build/*Server /app/
|
|||||||
|
|
||||||
# Necessary suplimentary files
|
# Necessary suplimentary files
|
||||||
COPY --from=build /app/build/*.ini /app/configs/
|
COPY --from=build /app/build/*.ini /app/configs/
|
||||||
COPY --from=build /app/build/vanity/*.* /app/vanity/*
|
COPY --from=build /app/build/vanity/*.* /app/vanity/
|
||||||
COPY --from=build /app/build/navmeshes /app/navmeshes
|
COPY --from=build /app/build/navmeshes /app/navmeshes
|
||||||
COPY --from=build /app/build/migrations /app/migrations
|
COPY --from=build /app/build/migrations /app/migrations
|
||||||
COPY --from=build /app/build/*.dcf /app/
|
COPY --from=build /app/build/*.dcf /app/
|
||||||
@ -39,7 +39,7 @@ COPY --from=build /app/build/*.dcf /app/
|
|||||||
# backup of config and vanity files to copy to the host incase
|
# backup of config and vanity files to copy to the host incase
|
||||||
# of a mount clobbering the copy from above
|
# of a mount clobbering the copy from above
|
||||||
COPY --from=build /app/build/*.ini /app/default-configs/
|
COPY --from=build /app/build/*.ini /app/default-configs/
|
||||||
COPY --from=build /app/build/vanity/*.* /app/default-vanity/*
|
COPY --from=build /app/build/vanity/*.* /app/default-vanity/
|
||||||
|
|
||||||
# needed as the container runs with the root user
|
# needed as the container runs with the root user
|
||||||
# and therefore sudo doesn't exist
|
# and therefore sudo doesn't exist
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
#include "eGameMessageType.h"
|
#include "eGameMessageType.h"
|
||||||
#include "StringifiedEnum.h"
|
#include "StringifiedEnum.h"
|
||||||
#include "eGameMasterLevel.h"
|
#include "eGameMasterLevel.h"
|
||||||
|
#include "ChatPackets.h"
|
||||||
|
|
||||||
void ChatPacketHandler::HandleFriendlistRequest(Packet* packet) {
|
void ChatPacketHandler::HandleFriendlistRequest(Packet* packet) {
|
||||||
//Get from the packet which player we want to do something with:
|
//Get from the packet which player we want to do something with:
|
||||||
@ -354,6 +355,67 @@ void ChatPacketHandler::HandleGMLevelUpdate(Packet* packet) {
|
|||||||
inStream.Read(player.gmLevel);
|
inStream.Read(player.gmLevel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void ChatPacketHandler::HandleWho(Packet* packet) {
|
||||||
|
CINSTREAM_SKIP_HEADER;
|
||||||
|
FindPlayerRequest request;
|
||||||
|
request.Deserialize(inStream);
|
||||||
|
|
||||||
|
const auto& sender = Game::playerContainer.GetPlayerData(request.requestor);
|
||||||
|
if (!sender) return;
|
||||||
|
|
||||||
|
const auto& player = Game::playerContainer.GetPlayerData(request.playerName.GetAsString());
|
||||||
|
bool online = player;
|
||||||
|
|
||||||
|
CBITSTREAM;
|
||||||
|
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::WORLD_ROUTE_PACKET);
|
||||||
|
bitStream.Write(request.requestor);
|
||||||
|
|
||||||
|
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::WHO_RESPONSE);
|
||||||
|
bitStream.Write<uint8_t>(online);
|
||||||
|
bitStream.Write(player.zoneID.GetMapID());
|
||||||
|
bitStream.Write(player.zoneID.GetInstanceID());
|
||||||
|
bitStream.Write(player.zoneID.GetCloneID());
|
||||||
|
bitStream.Write(request.playerName);
|
||||||
|
|
||||||
|
SystemAddress sysAddr = sender.sysAddr;
|
||||||
|
SEND_PACKET;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatPacketHandler::HandleShowAll(Packet* packet) {
|
||||||
|
CINSTREAM_SKIP_HEADER;
|
||||||
|
ShowAllRequest request;
|
||||||
|
request.Deserialize(inStream);
|
||||||
|
|
||||||
|
const auto& sender = Game::playerContainer.GetPlayerData(request.requestor);
|
||||||
|
if (!sender) return;
|
||||||
|
|
||||||
|
CBITSTREAM;
|
||||||
|
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::WORLD_ROUTE_PACKET);
|
||||||
|
bitStream.Write(request.requestor);
|
||||||
|
|
||||||
|
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::SHOW_ALL_RESPONSE);
|
||||||
|
bitStream.Write<uint8_t>(!request.displayZoneData && !request.displayIndividualPlayers);
|
||||||
|
bitStream.Write(Game::playerContainer.GetPlayerCount());
|
||||||
|
bitStream.Write(Game::playerContainer.GetSimCount());
|
||||||
|
bitStream.Write<uint8_t>(request.displayIndividualPlayers);
|
||||||
|
bitStream.Write<uint8_t>(request.displayZoneData);
|
||||||
|
if (request.displayZoneData || request.displayIndividualPlayers){
|
||||||
|
for (auto& [playerID, playerData ]: Game::playerContainer.GetAllPlayers()){
|
||||||
|
if (!playerData) continue;
|
||||||
|
bitStream.Write<uint8_t>(0); // structure packing
|
||||||
|
if (request.displayIndividualPlayers) bitStream.Write(LUWString(playerData.playerName));
|
||||||
|
if (request.displayZoneData) {
|
||||||
|
bitStream.Write(playerData.zoneID.GetMapID());
|
||||||
|
bitStream.Write(playerData.zoneID.GetInstanceID());
|
||||||
|
bitStream.Write(playerData.zoneID.GetCloneID());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SystemAddress sysAddr = sender.sysAddr;
|
||||||
|
SEND_PACKET;
|
||||||
|
}
|
||||||
|
|
||||||
// the structure the client uses to send this packet is shared in many chat messages
|
// the structure the client uses to send this packet is shared in many chat messages
|
||||||
// that are sent to the server. Because of this, there are large gaps of unused data in chat messages
|
// that are sent to the server. Because of this, there are large gaps of unused data in chat messages
|
||||||
void ChatPacketHandler::HandleChatMessage(Packet* packet) {
|
void ChatPacketHandler::HandleChatMessage(Packet* packet) {
|
||||||
|
@ -50,6 +50,8 @@ namespace ChatPacketHandler {
|
|||||||
void HandleFriendResponse(Packet* packet);
|
void HandleFriendResponse(Packet* packet);
|
||||||
void HandleRemoveFriend(Packet* packet);
|
void HandleRemoveFriend(Packet* packet);
|
||||||
void HandleGMLevelUpdate(Packet* packet);
|
void HandleGMLevelUpdate(Packet* packet);
|
||||||
|
void HandleWho(Packet* packet);
|
||||||
|
void HandleShowAll(Packet* packet);
|
||||||
|
|
||||||
void HandleChatMessage(Packet* packet);
|
void HandleChatMessage(Packet* packet);
|
||||||
void HandlePrivateChatMessage(Packet* packet);
|
void HandlePrivateChatMessage(Packet* packet);
|
||||||
|
@ -179,6 +179,7 @@ int main(int argc, char** argv) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void HandlePacket(Packet* packet) {
|
void HandlePacket(Packet* packet) {
|
||||||
|
if (packet->length < 1) return;
|
||||||
if (packet->data[0] == ID_DISCONNECTION_NOTIFICATION || packet->data[0] == ID_CONNECTION_LOST) {
|
if (packet->data[0] == ID_DISCONNECTION_NOTIFICATION || packet->data[0] == ID_CONNECTION_LOST) {
|
||||||
LOG("A server has disconnected, erasing their connected players from the list.");
|
LOG("A server has disconnected, erasing their connected players from the list.");
|
||||||
} else if (packet->data[0] == ID_NEW_INCOMING_CONNECTION) {
|
} else if (packet->data[0] == ID_NEW_INCOMING_CONNECTION) {
|
||||||
@ -289,7 +290,11 @@ void HandlePacket(Packet* packet) {
|
|||||||
Game::playerContainer.RemovePlayer(packet);
|
Game::playerContainer.RemovePlayer(packet);
|
||||||
break;
|
break;
|
||||||
case eChatMessageType::WHO:
|
case eChatMessageType::WHO:
|
||||||
|
ChatPacketHandler::HandleWho(packet);
|
||||||
|
break;
|
||||||
case eChatMessageType::SHOW_ALL:
|
case eChatMessageType::SHOW_ALL:
|
||||||
|
ChatPacketHandler::HandleShowAll(packet);
|
||||||
|
break;
|
||||||
case eChatMessageType::USER_CHANNEL_CHAT_MESSAGE:
|
case eChatMessageType::USER_CHANNEL_CHAT_MESSAGE:
|
||||||
case eChatMessageType::WORLD_DISCONNECT_REQUEST:
|
case eChatMessageType::WORLD_DISCONNECT_REQUEST:
|
||||||
case eChatMessageType::WORLD_PROXIMITY_RESPONSE:
|
case eChatMessageType::WORLD_PROXIMITY_RESPONSE:
|
||||||
|
@ -36,19 +36,23 @@ void PlayerContainer::InsertPlayer(Packet* packet) {
|
|||||||
data.playerID = playerId;
|
data.playerID = playerId;
|
||||||
|
|
||||||
uint32_t len;
|
uint32_t len;
|
||||||
inStream.Read<uint32_t>(len);
|
if (!inStream.Read<uint32_t>(len)) return;
|
||||||
|
|
||||||
for (int i = 0; i < len; i++) {
|
if (len > 33) {
|
||||||
char character; inStream.Read<char>(character);
|
LOG("Received a really long player name, probably a fake packet %i.", len);
|
||||||
data.playerName += character;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
inStream.Read(data.zoneID);
|
data.playerName.resize(len);
|
||||||
inStream.Read(data.muteExpire);
|
inStream.ReadAlignedBytes(reinterpret_cast<unsigned char*>(data.playerName.data()), len);
|
||||||
inStream.Read(data.gmLevel);
|
|
||||||
|
if (!inStream.Read(data.zoneID)) return;
|
||||||
|
if (!inStream.Read(data.muteExpire)) return;
|
||||||
|
if (!inStream.Read(data.gmLevel)) return;
|
||||||
data.sysAddr = packet->systemAddress;
|
data.sysAddr = packet->systemAddress;
|
||||||
|
|
||||||
m_Names[data.playerID] = GeneralUtils::UTF8ToUTF16(data.playerName);
|
m_Names[data.playerID] = GeneralUtils::UTF8ToUTF16(data.playerName);
|
||||||
|
m_PlayerCount++;
|
||||||
|
|
||||||
LOG("Added user: %s (%llu), zone: %i", data.playerName.c_str(), data.playerID, data.zoneID.GetMapID());
|
LOG("Added user: %s (%llu), zone: %i", data.playerName.c_str(), data.playerID, data.zoneID.GetMapID());
|
||||||
|
|
||||||
@ -87,6 +91,7 @@ void PlayerContainer::RemovePlayer(Packet* packet) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_PlayerCount--;
|
||||||
LOG("Removed user: %llu", playerID);
|
LOG("Removed user: %llu", playerID);
|
||||||
m_Players.erase(playerID);
|
m_Players.erase(playerID);
|
||||||
|
|
||||||
@ -120,6 +125,11 @@ void PlayerContainer::CreateTeamServer(Packet* packet) {
|
|||||||
size_t membersSize = 0;
|
size_t membersSize = 0;
|
||||||
inStream.Read(membersSize);
|
inStream.Read(membersSize);
|
||||||
|
|
||||||
|
if (membersSize >= 4) {
|
||||||
|
LOG("Tried to create a team with more than 4 players");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<LWOOBJID> members;
|
std::vector<LWOOBJID> members;
|
||||||
|
|
||||||
members.reserve(membersSize);
|
members.reserve(membersSize);
|
||||||
|
@ -71,6 +71,9 @@ public:
|
|||||||
const PlayerData& GetPlayerData(const std::string& playerName);
|
const PlayerData& GetPlayerData(const std::string& playerName);
|
||||||
PlayerData& GetPlayerDataMutable(const LWOOBJID& playerID);
|
PlayerData& GetPlayerDataMutable(const LWOOBJID& playerID);
|
||||||
PlayerData& GetPlayerDataMutable(const std::string& playerName);
|
PlayerData& GetPlayerDataMutable(const std::string& playerName);
|
||||||
|
uint32_t GetPlayerCount() { return m_PlayerCount; };
|
||||||
|
uint32_t GetSimCount() { return m_SimCount; };
|
||||||
|
const std::map<LWOOBJID, PlayerData>& GetAllPlayers() { return m_Players; };
|
||||||
|
|
||||||
TeamData* CreateLocalTeam(std::vector<LWOOBJID> members);
|
TeamData* CreateLocalTeam(std::vector<LWOOBJID> members);
|
||||||
TeamData* CreateTeam(LWOOBJID leader, bool local = false);
|
TeamData* CreateTeam(LWOOBJID leader, bool local = false);
|
||||||
@ -93,5 +96,7 @@ private:
|
|||||||
std::unordered_map<LWOOBJID, std::u16string> m_Names;
|
std::unordered_map<LWOOBJID, std::u16string> m_Names;
|
||||||
uint32_t m_MaxNumberOfBestFriends = 5;
|
uint32_t m_MaxNumberOfBestFriends = 5;
|
||||||
uint32_t m_MaxNumberOfFriends = 50;
|
uint32_t m_MaxNumberOfFriends = 50;
|
||||||
|
uint32_t m_PlayerCount = 0;
|
||||||
|
uint32_t m_SimCount = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -201,7 +201,7 @@ void OnTerminate() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void MakeBacktrace() {
|
void MakeBacktrace() {
|
||||||
struct sigaction sigact;
|
struct sigaction sigact{};
|
||||||
|
|
||||||
sigact.sa_sigaction = CritErrHdlr;
|
sigact.sa_sigaction = CritErrHdlr;
|
||||||
sigact.sa_flags = SA_RESTART | SA_SIGINFO;
|
sigact.sa_flags = SA_RESTART | SA_SIGINFO;
|
||||||
|
@ -8,23 +8,23 @@
|
|||||||
#include <map>
|
#include <map>
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
inline size_t MinSize(size_t size, const std::basic_string_view<T>& string) {
|
static inline size_t MinSize(const size_t size, const std::basic_string_view<T> string) {
|
||||||
if (size == size_t(-1) || size > string.size()) {
|
if (size == SIZE_MAX || size > string.size()) {
|
||||||
return string.size();
|
return string.size();
|
||||||
} else {
|
} else {
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool IsLeadSurrogate(char16_t c) {
|
inline bool IsLeadSurrogate(const char16_t c) {
|
||||||
return (0xD800 <= c) && (c <= 0xDBFF);
|
return (0xD800 <= c) && (c <= 0xDBFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool IsTrailSurrogate(char16_t c) {
|
inline bool IsTrailSurrogate(const char16_t c) {
|
||||||
return (0xDC00 <= c) && (c <= 0xDFFF);
|
return (0xDC00 <= c) && (c <= 0xDFFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void PushUTF8CodePoint(std::string& ret, char32_t cp) {
|
inline void PushUTF8CodePoint(std::string& ret, const char32_t cp) {
|
||||||
if (cp <= 0x007F) {
|
if (cp <= 0x007F) {
|
||||||
ret.push_back(static_cast<uint8_t>(cp));
|
ret.push_back(static_cast<uint8_t>(cp));
|
||||||
} else if (cp <= 0x07FF) {
|
} else if (cp <= 0x07FF) {
|
||||||
@ -46,16 +46,16 @@ inline void PushUTF8CodePoint(std::string& ret, char32_t cp) {
|
|||||||
|
|
||||||
constexpr const char16_t REPLACEMENT_CHARACTER = 0xFFFD;
|
constexpr const char16_t REPLACEMENT_CHARACTER = 0xFFFD;
|
||||||
|
|
||||||
bool _IsSuffixChar(uint8_t c) {
|
bool static _IsSuffixChar(const uint8_t c) {
|
||||||
return (c & 0xC0) == 0x80;
|
return (c & 0xC0) == 0x80;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GeneralUtils::_NextUTF8Char(std::string_view& slice, uint32_t& out) {
|
bool GeneralUtils::details::_NextUTF8Char(std::string_view& slice, uint32_t& out) {
|
||||||
size_t rem = slice.length();
|
const size_t rem = slice.length();
|
||||||
if (slice.empty()) return false;
|
if (slice.empty()) return false;
|
||||||
const uint8_t* bytes = reinterpret_cast<const uint8_t*>(&slice.front());
|
const uint8_t* bytes = reinterpret_cast<const uint8_t*>(&slice.front());
|
||||||
if (rem > 0) {
|
if (rem > 0) {
|
||||||
uint8_t first = bytes[0];
|
const uint8_t first = bytes[0];
|
||||||
if (first < 0x80) { // 1 byte character
|
if (first < 0x80) { // 1 byte character
|
||||||
out = static_cast<uint32_t>(first & 0x7F);
|
out = static_cast<uint32_t>(first & 0x7F);
|
||||||
slice.remove_prefix(1);
|
slice.remove_prefix(1);
|
||||||
@ -64,7 +64,7 @@ bool GeneralUtils::_NextUTF8Char(std::string_view& slice, uint32_t& out) {
|
|||||||
// middle byte, not valid at start, fall through
|
// middle byte, not valid at start, fall through
|
||||||
} else if (first < 0xE0) { // two byte character
|
} else if (first < 0xE0) { // two byte character
|
||||||
if (rem > 1) {
|
if (rem > 1) {
|
||||||
uint8_t second = bytes[1];
|
const uint8_t second = bytes[1];
|
||||||
if (_IsSuffixChar(second)) {
|
if (_IsSuffixChar(second)) {
|
||||||
out = (static_cast<uint32_t>(first & 0x1F) << 6)
|
out = (static_cast<uint32_t>(first & 0x1F) << 6)
|
||||||
+ static_cast<uint32_t>(second & 0x3F);
|
+ static_cast<uint32_t>(second & 0x3F);
|
||||||
@ -74,8 +74,8 @@ bool GeneralUtils::_NextUTF8Char(std::string_view& slice, uint32_t& out) {
|
|||||||
}
|
}
|
||||||
} else if (first < 0xF0) { // three byte character
|
} else if (first < 0xF0) { // three byte character
|
||||||
if (rem > 2) {
|
if (rem > 2) {
|
||||||
uint8_t second = bytes[1];
|
const uint8_t second = bytes[1];
|
||||||
uint8_t third = bytes[2];
|
const uint8_t third = bytes[2];
|
||||||
if (_IsSuffixChar(second) && _IsSuffixChar(third)) {
|
if (_IsSuffixChar(second) && _IsSuffixChar(third)) {
|
||||||
out = (static_cast<uint32_t>(first & 0x0F) << 12)
|
out = (static_cast<uint32_t>(first & 0x0F) << 12)
|
||||||
+ (static_cast<uint32_t>(second & 0x3F) << 6)
|
+ (static_cast<uint32_t>(second & 0x3F) << 6)
|
||||||
@ -86,9 +86,9 @@ bool GeneralUtils::_NextUTF8Char(std::string_view& slice, uint32_t& out) {
|
|||||||
}
|
}
|
||||||
} else if (first < 0xF8) { // four byte character
|
} else if (first < 0xF8) { // four byte character
|
||||||
if (rem > 3) {
|
if (rem > 3) {
|
||||||
uint8_t second = bytes[1];
|
const uint8_t second = bytes[1];
|
||||||
uint8_t third = bytes[2];
|
const uint8_t third = bytes[2];
|
||||||
uint8_t fourth = bytes[3];
|
const uint8_t fourth = bytes[3];
|
||||||
if (_IsSuffixChar(second) && _IsSuffixChar(third) && _IsSuffixChar(fourth)) {
|
if (_IsSuffixChar(second) && _IsSuffixChar(third) && _IsSuffixChar(fourth)) {
|
||||||
out = (static_cast<uint32_t>(first & 0x07) << 18)
|
out = (static_cast<uint32_t>(first & 0x07) << 18)
|
||||||
+ (static_cast<uint32_t>(second & 0x3F) << 12)
|
+ (static_cast<uint32_t>(second & 0x3F) << 12)
|
||||||
@ -107,7 +107,7 @@ bool GeneralUtils::_NextUTF8Char(std::string_view& slice, uint32_t& out) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// See <https://www.ietf.org/rfc/rfc2781.html#section-2.1>
|
/// See <https://www.ietf.org/rfc/rfc2781.html#section-2.1>
|
||||||
bool PushUTF16CodePoint(std::u16string& output, uint32_t U, size_t size) {
|
bool PushUTF16CodePoint(std::u16string& output, const uint32_t U, const size_t size) {
|
||||||
if (output.length() >= size) return false;
|
if (output.length() >= size) return false;
|
||||||
if (U < 0x10000) {
|
if (U < 0x10000) {
|
||||||
// If U < 0x10000, encode U as a 16-bit unsigned integer and terminate.
|
// If U < 0x10000, encode U as a 16-bit unsigned integer and terminate.
|
||||||
@ -120,7 +120,7 @@ bool PushUTF16CodePoint(std::u16string& output, uint32_t U, size_t size) {
|
|||||||
// Let U' = U - 0x10000. Because U is less than or equal to 0x10FFFF,
|
// Let U' = U - 0x10000. Because U is less than or equal to 0x10FFFF,
|
||||||
// U' must be less than or equal to 0xFFFFF. That is, U' can be
|
// U' must be less than or equal to 0xFFFFF. That is, U' can be
|
||||||
// represented in 20 bits.
|
// represented in 20 bits.
|
||||||
uint32_t Ut = U - 0x10000;
|
const uint32_t Ut = U - 0x10000;
|
||||||
|
|
||||||
// Initialize two 16-bit unsigned integers, W1 and W2, to 0xD800 and
|
// Initialize two 16-bit unsigned integers, W1 and W2, to 0xD800 and
|
||||||
// 0xDC00, respectively. These integers each have 10 bits free to
|
// 0xDC00, respectively. These integers each have 10 bits free to
|
||||||
@ -141,25 +141,25 @@ bool PushUTF16CodePoint(std::u16string& output, uint32_t U, size_t size) {
|
|||||||
} else return false;
|
} else return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::u16string GeneralUtils::UTF8ToUTF16(const std::string_view& string, size_t size) {
|
std::u16string GeneralUtils::UTF8ToUTF16(const std::string_view string, const size_t size) {
|
||||||
size_t newSize = MinSize(size, string);
|
const size_t newSize = MinSize(size, string);
|
||||||
std::u16string output;
|
std::u16string output;
|
||||||
output.reserve(newSize);
|
output.reserve(newSize);
|
||||||
std::string_view iterator = string;
|
std::string_view iterator = string;
|
||||||
|
|
||||||
uint32_t c;
|
uint32_t c;
|
||||||
while (_NextUTF8Char(iterator, c) && PushUTF16CodePoint(output, c, size)) {}
|
while (details::_NextUTF8Char(iterator, c) && PushUTF16CodePoint(output, c, size)) {}
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
//! Converts an std::string (ASCII) to UCS-2 / UTF-16
|
//! Converts an std::string (ASCII) to UCS-2 / UTF-16
|
||||||
std::u16string GeneralUtils::ASCIIToUTF16(const std::string_view& string, size_t size) {
|
std::u16string GeneralUtils::ASCIIToUTF16(const std::string_view string, const size_t size) {
|
||||||
size_t newSize = MinSize(size, string);
|
const size_t newSize = MinSize(size, string);
|
||||||
std::u16string ret;
|
std::u16string ret;
|
||||||
ret.reserve(newSize);
|
ret.reserve(newSize);
|
||||||
|
|
||||||
for (size_t i = 0; i < newSize; i++) {
|
for (size_t i = 0; i < newSize; ++i) {
|
||||||
char c = string[i];
|
const char c = string[i];
|
||||||
// Note: both 7-bit ascii characters and REPLACEMENT_CHARACTER fit in one char16_t
|
// Note: both 7-bit ascii characters and REPLACEMENT_CHARACTER fit in one char16_t
|
||||||
ret.push_back((c > 0 && c <= 127) ? static_cast<char16_t>(c) : REPLACEMENT_CHARACTER);
|
ret.push_back((c > 0 && c <= 127) ? static_cast<char16_t>(c) : REPLACEMENT_CHARACTER);
|
||||||
}
|
}
|
||||||
@ -169,18 +169,18 @@ std::u16string GeneralUtils::ASCIIToUTF16(const std::string_view& string, size_t
|
|||||||
|
|
||||||
//! Converts a (potentially-ill-formed) UTF-16 string to UTF-8
|
//! Converts a (potentially-ill-formed) UTF-16 string to UTF-8
|
||||||
//! See: <http://simonsapin.github.io/wtf-8/#decoding-ill-formed-utf-16>
|
//! See: <http://simonsapin.github.io/wtf-8/#decoding-ill-formed-utf-16>
|
||||||
std::string GeneralUtils::UTF16ToWTF8(const std::u16string_view& string, size_t size) {
|
std::string GeneralUtils::UTF16ToWTF8(const std::u16string_view string, const size_t size) {
|
||||||
size_t newSize = MinSize(size, string);
|
const size_t newSize = MinSize(size, string);
|
||||||
std::string ret;
|
std::string ret;
|
||||||
ret.reserve(newSize);
|
ret.reserve(newSize);
|
||||||
|
|
||||||
for (size_t i = 0; i < newSize; i++) {
|
for (size_t i = 0; i < newSize; ++i) {
|
||||||
char16_t u = string[i];
|
const char16_t u = string[i];
|
||||||
if (IsLeadSurrogate(u) && (i + 1) < newSize) {
|
if (IsLeadSurrogate(u) && (i + 1) < newSize) {
|
||||||
char16_t next = string[i + 1];
|
const char16_t next = string[i + 1];
|
||||||
if (IsTrailSurrogate(next)) {
|
if (IsTrailSurrogate(next)) {
|
||||||
i += 1;
|
i += 1;
|
||||||
char32_t cp = 0x10000
|
const char32_t cp = 0x10000
|
||||||
+ ((static_cast<char32_t>(u) - 0xD800) << 10)
|
+ ((static_cast<char32_t>(u) - 0xD800) << 10)
|
||||||
+ (static_cast<char32_t>(next) - 0xDC00);
|
+ (static_cast<char32_t>(next) - 0xDC00);
|
||||||
PushUTF8CodePoint(ret, cp);
|
PushUTF8CodePoint(ret, cp);
|
||||||
@ -195,40 +195,40 @@ std::string GeneralUtils::UTF16ToWTF8(const std::u16string_view& string, size_t
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GeneralUtils::CaseInsensitiveStringCompare(const std::string& a, const std::string& b) {
|
bool GeneralUtils::CaseInsensitiveStringCompare(const std::string_view a, const std::string_view b) {
|
||||||
return std::equal(a.begin(), a.end(), b.begin(), b.end(), [](char a, char b) { return tolower(a) == tolower(b); });
|
return std::equal(a.begin(), a.end(), b.begin(), b.end(), [](char a, char b) { return tolower(a) == tolower(b); });
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Bits
|
// MARK: Bits
|
||||||
|
|
||||||
//! Sets a specific bit in a signed 64-bit integer
|
//! Sets a specific bit in a signed 64-bit integer
|
||||||
int64_t GeneralUtils::SetBit(int64_t value, uint32_t index) {
|
int64_t GeneralUtils::SetBit(int64_t value, const uint32_t index) {
|
||||||
return value |= 1ULL << index;
|
return value |= 1ULL << index;
|
||||||
}
|
}
|
||||||
|
|
||||||
//! Clears a specific bit in a signed 64-bit integer
|
//! Clears a specific bit in a signed 64-bit integer
|
||||||
int64_t GeneralUtils::ClearBit(int64_t value, uint32_t index) {
|
int64_t GeneralUtils::ClearBit(int64_t value, const uint32_t index) {
|
||||||
return value &= ~(1ULL << index);
|
return value &= ~(1ULL << index);
|
||||||
}
|
}
|
||||||
|
|
||||||
//! Checks a specific bit in a signed 64-bit integer
|
//! Checks a specific bit in a signed 64-bit integer
|
||||||
bool GeneralUtils::CheckBit(int64_t value, uint32_t index) {
|
bool GeneralUtils::CheckBit(int64_t value, const uint32_t index) {
|
||||||
return value & (1ULL << index);
|
return value & (1ULL << index);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GeneralUtils::ReplaceInString(std::string& str, const std::string& from, const std::string& to) {
|
bool GeneralUtils::ReplaceInString(std::string& str, const std::string_view from, const std::string_view to) {
|
||||||
size_t start_pos = str.find(from);
|
const size_t start_pos = str.find(from);
|
||||||
if (start_pos == std::string::npos)
|
if (start_pos == std::string::npos)
|
||||||
return false;
|
return false;
|
||||||
str.replace(start_pos, from.length(), to);
|
str.replace(start_pos, from.length(), to);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::wstring> GeneralUtils::SplitString(std::wstring& str, wchar_t delimiter) {
|
std::vector<std::wstring> GeneralUtils::SplitString(const std::wstring_view str, const wchar_t delimiter) {
|
||||||
std::vector<std::wstring> vector = std::vector<std::wstring>();
|
std::vector<std::wstring> vector = std::vector<std::wstring>();
|
||||||
std::wstring current;
|
std::wstring current;
|
||||||
|
|
||||||
for (const auto& c : str) {
|
for (const wchar_t c : str) {
|
||||||
if (c == delimiter) {
|
if (c == delimiter) {
|
||||||
vector.push_back(current);
|
vector.push_back(current);
|
||||||
current = L"";
|
current = L"";
|
||||||
@ -237,15 +237,15 @@ std::vector<std::wstring> GeneralUtils::SplitString(std::wstring& str, wchar_t d
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
vector.push_back(current);
|
vector.push_back(std::move(current));
|
||||||
return vector;
|
return vector;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::u16string> GeneralUtils::SplitString(const std::u16string& str, char16_t delimiter) {
|
std::vector<std::u16string> GeneralUtils::SplitString(const std::u16string_view str, const char16_t delimiter) {
|
||||||
std::vector<std::u16string> vector = std::vector<std::u16string>();
|
std::vector<std::u16string> vector = std::vector<std::u16string>();
|
||||||
std::u16string current;
|
std::u16string current;
|
||||||
|
|
||||||
for (const auto& c : str) {
|
for (const char16_t c : str) {
|
||||||
if (c == delimiter) {
|
if (c == delimiter) {
|
||||||
vector.push_back(current);
|
vector.push_back(current);
|
||||||
current = u"";
|
current = u"";
|
||||||
@ -254,17 +254,15 @@ std::vector<std::u16string> GeneralUtils::SplitString(const std::u16string& str,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
vector.push_back(current);
|
vector.push_back(std::move(current));
|
||||||
return vector;
|
return vector;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::string> GeneralUtils::SplitString(const std::string& str, char delimiter) {
|
std::vector<std::string> GeneralUtils::SplitString(const std::string_view str, const char delimiter) {
|
||||||
std::vector<std::string> vector = std::vector<std::string>();
|
std::vector<std::string> vector = std::vector<std::string>();
|
||||||
std::string current = "";
|
std::string current = "";
|
||||||
|
|
||||||
for (size_t i = 0; i < str.length(); i++) {
|
for (const char c : str) {
|
||||||
char c = str[i];
|
|
||||||
|
|
||||||
if (c == delimiter) {
|
if (c == delimiter) {
|
||||||
vector.push_back(current);
|
vector.push_back(current);
|
||||||
current = "";
|
current = "";
|
||||||
@ -273,8 +271,7 @@ std::vector<std::string> GeneralUtils::SplitString(const std::string& str, char
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
vector.push_back(current);
|
vector.push_back(std::move(current));
|
||||||
|
|
||||||
return vector;
|
return vector;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -283,7 +280,7 @@ std::u16string GeneralUtils::ReadWString(RakNet::BitStream& inStream) {
|
|||||||
inStream.Read<uint32_t>(length);
|
inStream.Read<uint32_t>(length);
|
||||||
|
|
||||||
std::u16string string;
|
std::u16string string;
|
||||||
for (auto i = 0; i < length; i++) {
|
for (uint32_t i = 0; i < length; ++i) {
|
||||||
uint16_t c;
|
uint16_t c;
|
||||||
inStream.Read(c);
|
inStream.Read(c);
|
||||||
string.push_back(c);
|
string.push_back(c);
|
||||||
@ -292,29 +289,29 @@ std::u16string GeneralUtils::ReadWString(RakNet::BitStream& inStream) {
|
|||||||
return string;
|
return string;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::string> GeneralUtils::GetSqlFileNamesFromFolder(const std::string& folder) {
|
std::vector<std::string> GeneralUtils::GetSqlFileNamesFromFolder(const std::string_view folder) {
|
||||||
// Because we dont know how large the initial number before the first _ is we need to make it a map like so.
|
// Because we dont know how large the initial number before the first _ is we need to make it a map like so.
|
||||||
std::map<uint32_t, std::string> filenames{};
|
std::map<uint32_t, std::string> filenames{};
|
||||||
for (auto& t : std::filesystem::directory_iterator(folder)) {
|
for (const auto& t : std::filesystem::directory_iterator(folder)) {
|
||||||
auto filename = t.path().filename().string();
|
auto filename = t.path().filename().string();
|
||||||
auto index = std::stoi(GeneralUtils::SplitString(filename, '_').at(0));
|
const auto index = std::stoi(GeneralUtils::SplitString(filename, '_').at(0));
|
||||||
filenames.insert(std::make_pair(index, filename));
|
filenames.emplace(index, std::move(filename));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now sort the map by the oldest migration.
|
// Now sort the map by the oldest migration.
|
||||||
std::vector<std::string> sortedFiles{};
|
std::vector<std::string> sortedFiles{};
|
||||||
auto fileIterator = filenames.begin();
|
auto fileIterator = filenames.cbegin();
|
||||||
std::map<uint32_t, std::string>::iterator oldest = filenames.begin();
|
auto oldest = filenames.cbegin();
|
||||||
while (!filenames.empty()) {
|
while (!filenames.empty()) {
|
||||||
if (fileIterator == filenames.end()) {
|
if (fileIterator == filenames.cend()) {
|
||||||
sortedFiles.push_back(oldest->second);
|
sortedFiles.push_back(oldest->second);
|
||||||
filenames.erase(oldest);
|
filenames.erase(oldest);
|
||||||
fileIterator = filenames.begin();
|
fileIterator = filenames.cbegin();
|
||||||
oldest = filenames.begin();
|
oldest = filenames.cbegin();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (oldest->first > fileIterator->first) oldest = fileIterator;
|
if (oldest->first > fileIterator->first) oldest = fileIterator;
|
||||||
fileIterator++;
|
++fileIterator;
|
||||||
}
|
}
|
||||||
|
|
||||||
return sortedFiles;
|
return sortedFiles;
|
||||||
|
@ -3,17 +3,18 @@
|
|||||||
// C++
|
// C++
|
||||||
#include <charconv>
|
#include <charconv>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <random>
|
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
|
#include <functional>
|
||||||
|
#include <optional>
|
||||||
|
#include <random>
|
||||||
|
#include <span>
|
||||||
|
#include <stdexcept>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
#include <optional>
|
|
||||||
#include <functional>
|
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#include <stdexcept>
|
|
||||||
#include "BitStream.h"
|
#include "BitStream.h"
|
||||||
#include "NiPoint3.h"
|
#include "NiPoint3.h"
|
||||||
|
|
||||||
#include "dPlatforms.h"
|
#include "dPlatforms.h"
|
||||||
#include "Game.h"
|
#include "Game.h"
|
||||||
#include "Logger.h"
|
#include "Logger.h"
|
||||||
@ -32,29 +33,31 @@ namespace GeneralUtils {
|
|||||||
//! Converts a plain ASCII string to a UTF-16 string
|
//! Converts a plain ASCII string to a UTF-16 string
|
||||||
/*!
|
/*!
|
||||||
\param string The string to convert
|
\param string The string to convert
|
||||||
\param size A size to trim the string to. Default is -1 (No trimming)
|
\param size A size to trim the string to. Default is SIZE_MAX (No trimming)
|
||||||
\return An UTF-16 representation of the string
|
\return An UTF-16 representation of the string
|
||||||
*/
|
*/
|
||||||
std::u16string ASCIIToUTF16(const std::string_view& string, size_t size = -1);
|
std::u16string ASCIIToUTF16(const std::string_view string, const size_t size = SIZE_MAX);
|
||||||
|
|
||||||
//! Converts a UTF-8 String to a UTF-16 string
|
//! Converts a UTF-8 String to a UTF-16 string
|
||||||
/*!
|
/*!
|
||||||
\param string The string to convert
|
\param string The string to convert
|
||||||
\param size A size to trim the string to. Default is -1 (No trimming)
|
\param size A size to trim the string to. Default is SIZE_MAX (No trimming)
|
||||||
\return An UTF-16 representation of the string
|
\return An UTF-16 representation of the string
|
||||||
*/
|
*/
|
||||||
std::u16string UTF8ToUTF16(const std::string_view& string, size_t size = -1);
|
std::u16string UTF8ToUTF16(const std::string_view string, const size_t size = SIZE_MAX);
|
||||||
|
|
||||||
//! Internal, do not use
|
namespace details {
|
||||||
bool _NextUTF8Char(std::string_view& slice, uint32_t& out);
|
//! Internal, do not use
|
||||||
|
bool _NextUTF8Char(std::string_view& slice, uint32_t& out);
|
||||||
|
}
|
||||||
|
|
||||||
//! Converts a UTF-16 string to a UTF-8 string
|
//! Converts a UTF-16 string to a UTF-8 string
|
||||||
/*!
|
/*!
|
||||||
\param string The string to convert
|
\param string The string to convert
|
||||||
\param size A size to trim the string to. Default is -1 (No trimming)
|
\param size A size to trim the string to. Default is SIZE_MAX (No trimming)
|
||||||
\return An UTF-8 representation of the string
|
\return An UTF-8 representation of the string
|
||||||
*/
|
*/
|
||||||
std::string UTF16ToWTF8(const std::u16string_view& string, size_t size = -1);
|
std::string UTF16ToWTF8(const std::u16string_view string, const size_t size = SIZE_MAX);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Compares two basic strings but does so ignoring case sensitivity
|
* Compares two basic strings but does so ignoring case sensitivity
|
||||||
@ -62,7 +65,7 @@ namespace GeneralUtils {
|
|||||||
* \param b the second string to compare against the first string
|
* \param b the second string to compare against the first string
|
||||||
* @return if the two strings are equal
|
* @return if the two strings are equal
|
||||||
*/
|
*/
|
||||||
bool CaseInsensitiveStringCompare(const std::string& a, const std::string& b);
|
bool CaseInsensitiveStringCompare(const std::string_view a, const std::string_view b);
|
||||||
|
|
||||||
// MARK: Bits
|
// MARK: Bits
|
||||||
|
|
||||||
@ -70,9 +73,9 @@ namespace GeneralUtils {
|
|||||||
|
|
||||||
//! Sets a bit on a numerical value
|
//! Sets a bit on a numerical value
|
||||||
template <typename T>
|
template <typename T>
|
||||||
inline void SetBit(T& value, eObjectBits bits) {
|
inline void SetBit(T& value, const eObjectBits bits) {
|
||||||
static_assert(std::is_arithmetic<T>::value, "Not an arithmetic type");
|
static_assert(std::is_arithmetic<T>::value, "Not an arithmetic type");
|
||||||
auto index = static_cast<size_t>(bits);
|
const auto index = static_cast<size_t>(bits);
|
||||||
if (index > (sizeof(T) * 8) - 1) {
|
if (index > (sizeof(T) * 8) - 1) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -82,9 +85,9 @@ namespace GeneralUtils {
|
|||||||
|
|
||||||
//! Clears a bit on a numerical value
|
//! Clears a bit on a numerical value
|
||||||
template <typename T>
|
template <typename T>
|
||||||
inline void ClearBit(T& value, eObjectBits bits) {
|
inline void ClearBit(T& value, const eObjectBits bits) {
|
||||||
static_assert(std::is_arithmetic<T>::value, "Not an arithmetic type");
|
static_assert(std::is_arithmetic<T>::value, "Not an arithmetic type");
|
||||||
auto index = static_cast<size_t>(bits);
|
const auto index = static_cast<size_t>(bits);
|
||||||
if (index > (sizeof(T) * 8 - 1)) {
|
if (index > (sizeof(T) * 8 - 1)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -97,14 +100,14 @@ namespace GeneralUtils {
|
|||||||
\param value The value to set the bit for
|
\param value The value to set the bit for
|
||||||
\param index The index of the bit
|
\param index The index of the bit
|
||||||
*/
|
*/
|
||||||
int64_t SetBit(int64_t value, uint32_t index);
|
int64_t SetBit(int64_t value, const uint32_t index);
|
||||||
|
|
||||||
//! Clears a specific bit in a signed 64-bit integer
|
//! Clears a specific bit in a signed 64-bit integer
|
||||||
/*!
|
/*!
|
||||||
\param value The value to clear the bit from
|
\param value The value to clear the bit from
|
||||||
\param index The index of the bit
|
\param index The index of the bit
|
||||||
*/
|
*/
|
||||||
int64_t ClearBit(int64_t value, uint32_t index);
|
int64_t ClearBit(int64_t value, const uint32_t index);
|
||||||
|
|
||||||
//! Checks a specific bit in a signed 64-bit integer
|
//! Checks a specific bit in a signed 64-bit integer
|
||||||
/*!
|
/*!
|
||||||
@ -112,19 +115,19 @@ namespace GeneralUtils {
|
|||||||
\param index The index of the bit
|
\param index The index of the bit
|
||||||
\return Whether or not the bit is set
|
\return Whether or not the bit is set
|
||||||
*/
|
*/
|
||||||
bool CheckBit(int64_t value, uint32_t index);
|
bool CheckBit(int64_t value, const uint32_t index);
|
||||||
|
|
||||||
bool ReplaceInString(std::string& str, const std::string& from, const std::string& to);
|
bool ReplaceInString(std::string& str, const std::string_view from, const std::string_view to);
|
||||||
|
|
||||||
std::u16string ReadWString(RakNet::BitStream& inStream);
|
std::u16string ReadWString(RakNet::BitStream& inStream);
|
||||||
|
|
||||||
std::vector<std::wstring> SplitString(std::wstring& str, wchar_t delimiter);
|
std::vector<std::wstring> SplitString(const std::wstring_view str, const wchar_t delimiter);
|
||||||
|
|
||||||
std::vector<std::u16string> SplitString(const std::u16string& str, char16_t delimiter);
|
std::vector<std::u16string> SplitString(const std::u16string_view str, const char16_t delimiter);
|
||||||
|
|
||||||
std::vector<std::string> SplitString(const std::string& str, char delimiter);
|
std::vector<std::string> SplitString(const std::string_view str, const char delimiter);
|
||||||
|
|
||||||
std::vector<std::string> GetSqlFileNamesFromFolder(const std::string& folder);
|
std::vector<std::string> GetSqlFileNamesFromFolder(const std::string_view folder);
|
||||||
|
|
||||||
// Concept constraining to enum types
|
// Concept constraining to enum types
|
||||||
template <typename T>
|
template <typename T>
|
||||||
@ -144,7 +147,7 @@ namespace GeneralUtils {
|
|||||||
|
|
||||||
// If a boolean, present an alias to an intermediate integral type for parsing
|
// If a boolean, present an alias to an intermediate integral type for parsing
|
||||||
template <Numeric T> requires std::same_as<T, bool>
|
template <Numeric T> requires std::same_as<T, bool>
|
||||||
struct numeric_parse<T> { using type = uint32_t; };
|
struct numeric_parse<T> { using type = uint8_t; };
|
||||||
|
|
||||||
// Shorthand type alias
|
// Shorthand type alias
|
||||||
template <Numeric T>
|
template <Numeric T>
|
||||||
@ -205,7 +208,7 @@ namespace GeneralUtils {
|
|||||||
* @returns An std::optional containing the desired NiPoint3 if it can be constructed from the string parameters
|
* @returns An std::optional containing the desired NiPoint3 if it can be constructed from the string parameters
|
||||||
*/
|
*/
|
||||||
template <typename T>
|
template <typename T>
|
||||||
[[nodiscard]] std::optional<NiPoint3> TryParse(const std::string& strX, const std::string& strY, const std::string& strZ) {
|
[[nodiscard]] std::optional<NiPoint3> TryParse(const std::string_view strX, const std::string_view strY, const std::string_view strZ) {
|
||||||
const auto x = TryParse<float>(strX);
|
const auto x = TryParse<float>(strX);
|
||||||
if (!x) return std::nullopt;
|
if (!x) return std::nullopt;
|
||||||
|
|
||||||
@ -217,17 +220,17 @@ namespace GeneralUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The TryParse overload for handling NiPoint3 by passingn a reference to a vector of three strings
|
* The TryParse overload for handling NiPoint3 by passing a span of three strings
|
||||||
* @param str The string vector representing the X, Y, and Xcoordinates
|
* @param str The string vector representing the X, Y, and Z coordinates
|
||||||
* @returns An std::optional containing the desired NiPoint3 if it can be constructed from the string parameters
|
* @returns An std::optional containing the desired NiPoint3 if it can be constructed from the string parameters
|
||||||
*/
|
*/
|
||||||
template <typename T>
|
template <typename T>
|
||||||
[[nodiscard]] std::optional<NiPoint3> TryParse(const std::vector<std::string>& str) {
|
[[nodiscard]] std::optional<NiPoint3> TryParse(const std::span<const std::string> str) {
|
||||||
return (str.size() == 3) ? TryParse<NiPoint3>(str[0], str[1], str[2]) : std::nullopt;
|
return (str.size() == 3) ? TryParse<NiPoint3>(str[0], str[1], str[2]) : std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
std::u16string to_u16string(T value) {
|
std::u16string to_u16string(const T value) {
|
||||||
return GeneralUtils::ASCIIToUTF16(std::to_string(value));
|
return GeneralUtils::ASCIIToUTF16(std::to_string(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -246,7 +249,7 @@ namespace GeneralUtils {
|
|||||||
\param max The maximum to generate to
|
\param max The maximum to generate to
|
||||||
*/
|
*/
|
||||||
template <typename T>
|
template <typename T>
|
||||||
inline T GenerateRandomNumber(std::size_t min, std::size_t max) {
|
inline T GenerateRandomNumber(const std::size_t min, const std::size_t max) {
|
||||||
// Make sure it is a numeric type
|
// Make sure it is a numeric type
|
||||||
static_assert(std::is_arithmetic<T>::value, "Not an arithmetic type");
|
static_assert(std::is_arithmetic<T>::value, "Not an arithmetic type");
|
||||||
|
|
||||||
@ -273,10 +276,10 @@ namespace GeneralUtils {
|
|||||||
|
|
||||||
// on Windows we need to undef these or else they conflict with our numeric limits calls
|
// on Windows we need to undef these or else they conflict with our numeric limits calls
|
||||||
// DEVELOPERS DEVELOPERS DEVELOPERS DEVELOPERS DEVELOPERS DEVELOPERS DEVELOPERS DEVELOPERS
|
// DEVELOPERS DEVELOPERS DEVELOPERS DEVELOPERS DEVELOPERS DEVELOPERS DEVELOPERS DEVELOPERS
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#undef min
|
#undef min
|
||||||
#undef max
|
#undef max
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
inline T GenerateRandomNumber() {
|
inline T GenerateRandomNumber() {
|
||||||
|
@ -31,22 +31,22 @@ public:
|
|||||||
|
|
||||||
virtual ~LDFBaseData() {}
|
virtual ~LDFBaseData() {}
|
||||||
|
|
||||||
virtual void WriteToPacket(RakNet::BitStream& packet) = 0;
|
virtual void WriteToPacket(RakNet::BitStream& packet) const = 0;
|
||||||
|
|
||||||
virtual const std::u16string& GetKey() = 0;
|
virtual const std::u16string& GetKey() const = 0;
|
||||||
|
|
||||||
virtual eLDFType GetValueType() = 0;
|
virtual eLDFType GetValueType() const = 0;
|
||||||
|
|
||||||
/** Gets a string from the key/value pair
|
/** Gets a string from the key/value pair
|
||||||
* @param includeKey Whether or not to include the key in the data
|
* @param includeKey Whether or not to include the key in the data
|
||||||
* @param includeTypeId Whether or not to include the type id in the data
|
* @param includeTypeId Whether or not to include the type id in the data
|
||||||
* @return The string representation of the data
|
* @return The string representation of the data
|
||||||
*/
|
*/
|
||||||
virtual std::string GetString(bool includeKey = true, bool includeTypeId = true) = 0;
|
virtual std::string GetString(bool includeKey = true, bool includeTypeId = true) const = 0;
|
||||||
|
|
||||||
virtual std::string GetValueAsString() = 0;
|
virtual std::string GetValueAsString() const = 0;
|
||||||
|
|
||||||
virtual LDFBaseData* Copy() = 0;
|
virtual LDFBaseData* Copy() const = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Given an input string, return the data as a LDF key.
|
* Given an input string, return the data as a LDF key.
|
||||||
@ -62,7 +62,7 @@ private:
|
|||||||
T value;
|
T value;
|
||||||
|
|
||||||
//! Writes the key to the packet
|
//! Writes the key to the packet
|
||||||
void WriteKey(RakNet::BitStream& packet) {
|
void WriteKey(RakNet::BitStream& packet) const {
|
||||||
packet.Write<uint8_t>(this->key.length() * sizeof(uint16_t));
|
packet.Write<uint8_t>(this->key.length() * sizeof(uint16_t));
|
||||||
for (uint32_t i = 0; i < this->key.length(); ++i) {
|
for (uint32_t i = 0; i < this->key.length(); ++i) {
|
||||||
packet.Write<uint16_t>(this->key[i]);
|
packet.Write<uint16_t>(this->key[i]);
|
||||||
@ -70,7 +70,7 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
//! Writes the value to the packet
|
//! Writes the value to the packet
|
||||||
void WriteValue(RakNet::BitStream& packet) {
|
void WriteValue(RakNet::BitStream& packet) const {
|
||||||
packet.Write<uint8_t>(this->GetValueType());
|
packet.Write<uint8_t>(this->GetValueType());
|
||||||
packet.Write(this->value);
|
packet.Write(this->value);
|
||||||
}
|
}
|
||||||
@ -90,7 +90,7 @@ public:
|
|||||||
/*!
|
/*!
|
||||||
\return The value
|
\return The value
|
||||||
*/
|
*/
|
||||||
const T& GetValue(void) { return this->value; }
|
const T& GetValue(void) const { return this->value; }
|
||||||
|
|
||||||
//! Sets the value
|
//! Sets the value
|
||||||
/*!
|
/*!
|
||||||
@ -102,13 +102,13 @@ public:
|
|||||||
/*!
|
/*!
|
||||||
\return The value string
|
\return The value string
|
||||||
*/
|
*/
|
||||||
std::string GetValueString(void) { return ""; }
|
std::string GetValueString(void) const { return ""; }
|
||||||
|
|
||||||
//! Writes the data to a packet
|
//! Writes the data to a packet
|
||||||
/*!
|
/*!
|
||||||
\param packet The packet
|
\param packet The packet
|
||||||
*/
|
*/
|
||||||
void WriteToPacket(RakNet::BitStream& packet) override {
|
void WriteToPacket(RakNet::BitStream& packet) const override {
|
||||||
this->WriteKey(packet);
|
this->WriteKey(packet);
|
||||||
this->WriteValue(packet);
|
this->WriteValue(packet);
|
||||||
}
|
}
|
||||||
@ -117,13 +117,13 @@ public:
|
|||||||
/*!
|
/*!
|
||||||
\return The key
|
\return The key
|
||||||
*/
|
*/
|
||||||
const std::u16string& GetKey(void) override { return this->key; }
|
const std::u16string& GetKey(void) const override { return this->key; }
|
||||||
|
|
||||||
//! Gets the LDF Type
|
//! Gets the LDF Type
|
||||||
/*!
|
/*!
|
||||||
\return The LDF value type
|
\return The LDF value type
|
||||||
*/
|
*/
|
||||||
eLDFType GetValueType(void) override { return LDF_TYPE_UNKNOWN; }
|
eLDFType GetValueType(void) const override { return LDF_TYPE_UNKNOWN; }
|
||||||
|
|
||||||
//! Gets the string data
|
//! Gets the string data
|
||||||
/*!
|
/*!
|
||||||
@ -131,7 +131,7 @@ public:
|
|||||||
\param includeTypeId Whether or not to include the type id in the data
|
\param includeTypeId Whether or not to include the type id in the data
|
||||||
\return The string representation of the data
|
\return The string representation of the data
|
||||||
*/
|
*/
|
||||||
std::string GetString(const bool includeKey = true, const bool includeTypeId = true) override {
|
std::string GetString(const bool includeKey = true, const bool includeTypeId = true) const override {
|
||||||
if (GetValueType() == -1) {
|
if (GetValueType() == -1) {
|
||||||
return GeneralUtils::UTF16ToWTF8(this->key) + "=-1:<server variable>";
|
return GeneralUtils::UTF16ToWTF8(this->key) + "=-1:<server variable>";
|
||||||
}
|
}
|
||||||
@ -154,11 +154,11 @@ public:
|
|||||||
return stream.str();
|
return stream.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string GetValueAsString() override {
|
std::string GetValueAsString() const override {
|
||||||
return this->GetValueString();
|
return this->GetValueString();
|
||||||
}
|
}
|
||||||
|
|
||||||
LDFBaseData* Copy() override {
|
LDFBaseData* Copy() const override {
|
||||||
return new LDFData<T>(key, value);
|
return new LDFData<T>(key, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -166,19 +166,19 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
// LDF Types
|
// LDF Types
|
||||||
template<> inline eLDFType LDFData<std::u16string>::GetValueType(void) { return LDF_TYPE_UTF_16; };
|
template<> inline eLDFType LDFData<std::u16string>::GetValueType(void) const { return LDF_TYPE_UTF_16; };
|
||||||
template<> inline eLDFType LDFData<int32_t>::GetValueType(void) { return LDF_TYPE_S32; };
|
template<> inline eLDFType LDFData<int32_t>::GetValueType(void) const { return LDF_TYPE_S32; };
|
||||||
template<> inline eLDFType LDFData<float>::GetValueType(void) { return LDF_TYPE_FLOAT; };
|
template<> inline eLDFType LDFData<float>::GetValueType(void) const { return LDF_TYPE_FLOAT; };
|
||||||
template<> inline eLDFType LDFData<double>::GetValueType(void) { return LDF_TYPE_DOUBLE; };
|
template<> inline eLDFType LDFData<double>::GetValueType(void) const { return LDF_TYPE_DOUBLE; };
|
||||||
template<> inline eLDFType LDFData<uint32_t>::GetValueType(void) { return LDF_TYPE_U32; };
|
template<> inline eLDFType LDFData<uint32_t>::GetValueType(void) const { return LDF_TYPE_U32; };
|
||||||
template<> inline eLDFType LDFData<bool>::GetValueType(void) { return LDF_TYPE_BOOLEAN; };
|
template<> inline eLDFType LDFData<bool>::GetValueType(void) const { return LDF_TYPE_BOOLEAN; };
|
||||||
template<> inline eLDFType LDFData<uint64_t>::GetValueType(void) { return LDF_TYPE_U64; };
|
template<> inline eLDFType LDFData<uint64_t>::GetValueType(void) const { return LDF_TYPE_U64; };
|
||||||
template<> inline eLDFType LDFData<LWOOBJID>::GetValueType(void) { return LDF_TYPE_OBJID; };
|
template<> inline eLDFType LDFData<LWOOBJID>::GetValueType(void) const { return LDF_TYPE_OBJID; };
|
||||||
template<> inline eLDFType LDFData<std::string>::GetValueType(void) { return LDF_TYPE_UTF_8; };
|
template<> inline eLDFType LDFData<std::string>::GetValueType(void) const { return LDF_TYPE_UTF_8; };
|
||||||
|
|
||||||
// The specialized version for std::u16string (UTF-16)
|
// The specialized version for std::u16string (UTF-16)
|
||||||
template<>
|
template<>
|
||||||
inline void LDFData<std::u16string>::WriteValue(RakNet::BitStream& packet) {
|
inline void LDFData<std::u16string>::WriteValue(RakNet::BitStream& packet) const {
|
||||||
packet.Write<uint8_t>(this->GetValueType());
|
packet.Write<uint8_t>(this->GetValueType());
|
||||||
|
|
||||||
packet.Write<uint32_t>(this->value.length());
|
packet.Write<uint32_t>(this->value.length());
|
||||||
@ -189,7 +189,7 @@ inline void LDFData<std::u16string>::WriteValue(RakNet::BitStream& packet) {
|
|||||||
|
|
||||||
// The specialized version for bool
|
// The specialized version for bool
|
||||||
template<>
|
template<>
|
||||||
inline void LDFData<bool>::WriteValue(RakNet::BitStream& packet) {
|
inline void LDFData<bool>::WriteValue(RakNet::BitStream& packet) const {
|
||||||
packet.Write<uint8_t>(this->GetValueType());
|
packet.Write<uint8_t>(this->GetValueType());
|
||||||
|
|
||||||
packet.Write<uint8_t>(this->value);
|
packet.Write<uint8_t>(this->value);
|
||||||
@ -197,7 +197,7 @@ inline void LDFData<bool>::WriteValue(RakNet::BitStream& packet) {
|
|||||||
|
|
||||||
// The specialized version for std::string (UTF-8)
|
// The specialized version for std::string (UTF-8)
|
||||||
template<>
|
template<>
|
||||||
inline void LDFData<std::string>::WriteValue(RakNet::BitStream& packet) {
|
inline void LDFData<std::string>::WriteValue(RakNet::BitStream& packet) const {
|
||||||
packet.Write<uint8_t>(this->GetValueType());
|
packet.Write<uint8_t>(this->GetValueType());
|
||||||
|
|
||||||
packet.Write<uint32_t>(this->value.length());
|
packet.Write<uint32_t>(this->value.length());
|
||||||
@ -206,18 +206,18 @@ inline void LDFData<std::string>::WriteValue(RakNet::BitStream& packet) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template<> inline std::string LDFData<std::u16string>::GetValueString() {
|
template<> inline std::string LDFData<std::u16string>::GetValueString() const {
|
||||||
return GeneralUtils::UTF16ToWTF8(this->value, this->value.size());
|
return GeneralUtils::UTF16ToWTF8(this->value, this->value.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
template<> inline std::string LDFData<int32_t>::GetValueString() { return std::to_string(this->value); }
|
template<> inline std::string LDFData<int32_t>::GetValueString() const { return std::to_string(this->value); }
|
||||||
template<> inline std::string LDFData<float>::GetValueString() { return std::to_string(this->value); }
|
template<> inline std::string LDFData<float>::GetValueString() const { return std::to_string(this->value); }
|
||||||
template<> inline std::string LDFData<double>::GetValueString() { return std::to_string(this->value); }
|
template<> inline std::string LDFData<double>::GetValueString() const { return std::to_string(this->value); }
|
||||||
template<> inline std::string LDFData<uint32_t>::GetValueString() { return std::to_string(this->value); }
|
template<> inline std::string LDFData<uint32_t>::GetValueString() const { return std::to_string(this->value); }
|
||||||
template<> inline std::string LDFData<bool>::GetValueString() { return std::to_string(this->value); }
|
template<> inline std::string LDFData<bool>::GetValueString() const { return std::to_string(this->value); }
|
||||||
template<> inline std::string LDFData<uint64_t>::GetValueString() { return std::to_string(this->value); }
|
template<> inline std::string LDFData<uint64_t>::GetValueString() const { return std::to_string(this->value); }
|
||||||
template<> inline std::string LDFData<LWOOBJID>::GetValueString() { return std::to_string(this->value); }
|
template<> inline std::string LDFData<LWOOBJID>::GetValueString() const { return std::to_string(this->value); }
|
||||||
|
|
||||||
template<> inline std::string LDFData<std::string>::GetValueString() { return this->value; }
|
template<> inline std::string LDFData<std::string>::GetValueString() const { return this->value; }
|
||||||
|
|
||||||
#endif //!__LDFFORMAT__H__
|
#endif //!__LDFFORMAT__H__
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
#include "dConfig.h"
|
#include "dConfig.h"
|
||||||
|
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
#include "BinaryPathFinder.h"
|
#include "BinaryPathFinder.h"
|
||||||
#include "GeneralUtils.h"
|
#include "GeneralUtils.h"
|
||||||
|
@ -4,6 +4,9 @@
|
|||||||
#define __EINVENTORYTYPE__H__
|
#define __EINVENTORYTYPE__H__
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
||||||
|
#include "magic_enum.hpp"
|
||||||
|
|
||||||
static const uint8_t NUMBER_OF_INVENTORIES = 17;
|
static const uint8_t NUMBER_OF_INVENTORIES = 17;
|
||||||
/**
|
/**
|
||||||
* Represents the different types of inventories an entity may have
|
* Represents the different types of inventories an entity may have
|
||||||
@ -56,4 +59,10 @@ public:
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct magic_enum::customize::enum_range<eInventoryType> {
|
||||||
|
static constexpr int min = 0;
|
||||||
|
static constexpr int max = 16;
|
||||||
|
};
|
||||||
|
|
||||||
#endif //!__EINVENTORYTYPE__H__
|
#endif //!__EINVENTORYTYPE__H__
|
||||||
|
21
dCommon/dEnums/eReponseMoveItemBetweenInventoryTypeCode.h
Normal file
21
dCommon/dEnums/eReponseMoveItemBetweenInventoryTypeCode.h
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
#ifndef __EREPONSEMOVEITEMBETWEENINVENTORYTYPECODE__H__
|
||||||
|
#define __EREPONSEMOVEITEMBETWEENINVENTORYTYPECODE__H__
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
enum class eReponseMoveItemBetweenInventoryTypeCode : int32_t {
|
||||||
|
SUCCESS,
|
||||||
|
FAIL_GENERIC,
|
||||||
|
FAIL_INV_FULL,
|
||||||
|
FAIL_ITEM_NOT_FOUND,
|
||||||
|
FAIL_CANT_MOVE_TO_THAT_INV_TYPE,
|
||||||
|
FAIL_NOT_NEAR_BANK,
|
||||||
|
FAIL_CANT_SWAP_ITEMS,
|
||||||
|
FAIL_SOURCE_TYPE,
|
||||||
|
FAIL_WRONG_DEST_TYPE,
|
||||||
|
FAIL_SWAP_DEST_TYPE,
|
||||||
|
FAIL_CANT_MOVE_THINKING_HAT,
|
||||||
|
FAIL_DISMOUNT_BEFORE_MOVING
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif //!__EREPONSEMOVEITEMBETWEENINVENTORYTYPECODE__H__
|
@ -25,6 +25,7 @@
|
|||||||
#include "CDScriptComponentTable.h"
|
#include "CDScriptComponentTable.h"
|
||||||
#include "CDSkillBehaviorTable.h"
|
#include "CDSkillBehaviorTable.h"
|
||||||
#include "CDZoneTableTable.h"
|
#include "CDZoneTableTable.h"
|
||||||
|
#include "CDTamingBuildPuzzleTable.h"
|
||||||
#include "CDVendorComponentTable.h"
|
#include "CDVendorComponentTable.h"
|
||||||
#include "CDActivitiesTable.h"
|
#include "CDActivitiesTable.h"
|
||||||
#include "CDPackageComponentTable.h"
|
#include "CDPackageComponentTable.h"
|
||||||
@ -41,8 +42,6 @@
|
|||||||
#include "CDRewardCodesTable.h"
|
#include "CDRewardCodesTable.h"
|
||||||
#include "CDPetComponentTable.h"
|
#include "CDPetComponentTable.h"
|
||||||
|
|
||||||
#include <exception>
|
|
||||||
|
|
||||||
#ifndef CDCLIENT_CACHE_ALL
|
#ifndef CDCLIENT_CACHE_ALL
|
||||||
// Uncomment this to cache the full cdclient database into memory. This will make the server load faster, but will use more memory.
|
// Uncomment this to cache the full cdclient database into memory. This will make the server load faster, but will use more memory.
|
||||||
// A vanilla CDClient takes about 46MB of memory + the regular world data.
|
// A vanilla CDClient takes about 46MB of memory + the regular world data.
|
||||||
@ -55,13 +54,6 @@
|
|||||||
#define CDCLIENT_DONT_CACHE_TABLE(x)
|
#define CDCLIENT_DONT_CACHE_TABLE(x)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
class CDClientConnectionException : public std::exception {
|
|
||||||
public:
|
|
||||||
virtual const char* what() const throw() {
|
|
||||||
return "CDClientDatabase is not connected!";
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Using a macro to reduce repetitive code and issues from copy and paste.
|
// Using a macro to reduce repetitive code and issues from copy and paste.
|
||||||
// As a note, ## in a macro is used to concatenate two tokens together.
|
// As a note, ## in a macro is used to concatenate two tokens together.
|
||||||
|
|
||||||
@ -108,11 +100,14 @@ DEFINE_TABLE_STORAGE(CDRewardCodesTable);
|
|||||||
DEFINE_TABLE_STORAGE(CDRewardsTable);
|
DEFINE_TABLE_STORAGE(CDRewardsTable);
|
||||||
DEFINE_TABLE_STORAGE(CDScriptComponentTable);
|
DEFINE_TABLE_STORAGE(CDScriptComponentTable);
|
||||||
DEFINE_TABLE_STORAGE(CDSkillBehaviorTable);
|
DEFINE_TABLE_STORAGE(CDSkillBehaviorTable);
|
||||||
|
DEFINE_TABLE_STORAGE(CDTamingBuildPuzzleTable);
|
||||||
DEFINE_TABLE_STORAGE(CDVendorComponentTable);
|
DEFINE_TABLE_STORAGE(CDVendorComponentTable);
|
||||||
DEFINE_TABLE_STORAGE(CDZoneTableTable);
|
DEFINE_TABLE_STORAGE(CDZoneTableTable);
|
||||||
|
|
||||||
void CDClientManager::LoadValuesFromDatabase() {
|
void CDClientManager::LoadValuesFromDatabase() {
|
||||||
if (!CDClientDatabase::isConnected) throw CDClientConnectionException();
|
if (!CDClientDatabase::isConnected) {
|
||||||
|
throw std::runtime_error{ "CDClientDatabase is not connected!" };
|
||||||
|
}
|
||||||
|
|
||||||
CDActivityRewardsTable::Instance().LoadValuesFromDatabase();
|
CDActivityRewardsTable::Instance().LoadValuesFromDatabase();
|
||||||
CDActivitiesTable::Instance().LoadValuesFromDatabase();
|
CDActivitiesTable::Instance().LoadValuesFromDatabase();
|
||||||
@ -152,6 +147,7 @@ void CDClientManager::LoadValuesFromDatabase() {
|
|||||||
CDRewardsTable::Instance().LoadValuesFromDatabase();
|
CDRewardsTable::Instance().LoadValuesFromDatabase();
|
||||||
CDScriptComponentTable::Instance().LoadValuesFromDatabase();
|
CDScriptComponentTable::Instance().LoadValuesFromDatabase();
|
||||||
CDSkillBehaviorTable::Instance().LoadValuesFromDatabase();
|
CDSkillBehaviorTable::Instance().LoadValuesFromDatabase();
|
||||||
|
CDTamingBuildPuzzleTable::Instance().LoadValuesFromDatabase();
|
||||||
CDVendorComponentTable::Instance().LoadValuesFromDatabase();
|
CDVendorComponentTable::Instance().LoadValuesFromDatabase();
|
||||||
CDZoneTableTable::Instance().LoadValuesFromDatabase();
|
CDZoneTableTable::Instance().LoadValuesFromDatabase();
|
||||||
}
|
}
|
||||||
|
@ -50,7 +50,7 @@ void CDPetComponentTable::LoadValuesFromDatabase() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void CDPetComponentTable::LoadValuesFromDefaults() {
|
void CDPetComponentTable::LoadValuesFromDefaults() {
|
||||||
GetEntriesMutable().insert(std::make_pair(defaultEntry.id, defaultEntry));
|
GetEntriesMutable().emplace(defaultEntry.id, defaultEntry);
|
||||||
}
|
}
|
||||||
|
|
||||||
CDPetComponent& CDPetComponentTable::GetByID(const uint32_t componentID) {
|
CDPetComponent& CDPetComponentTable::GetByID(const uint32_t componentID) {
|
||||||
|
@ -0,0 +1,35 @@
|
|||||||
|
#include "CDTamingBuildPuzzleTable.h"
|
||||||
|
|
||||||
|
void CDTamingBuildPuzzleTable::LoadValuesFromDatabase() {
|
||||||
|
// First, get the size of the table
|
||||||
|
uint32_t size = 0;
|
||||||
|
auto tableSize = CDClientDatabase::ExecuteQuery("SELECT COUNT(*) FROM TamingBuildPuzzles");
|
||||||
|
while (!tableSize.eof()) {
|
||||||
|
size = tableSize.getIntField(0, 0);
|
||||||
|
tableSize.nextRow();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reserve the size
|
||||||
|
auto& entries = GetEntriesMutable();
|
||||||
|
entries.reserve(size);
|
||||||
|
|
||||||
|
// Now get the data
|
||||||
|
auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM TamingBuildPuzzles");
|
||||||
|
while (!tableData.eof()) {
|
||||||
|
const auto lot = static_cast<LOT>(tableData.getIntField("NPCLot", LOT_NULL));
|
||||||
|
entries.emplace(lot, CDTamingBuildPuzzle{
|
||||||
|
.puzzleModelLot = lot,
|
||||||
|
.validPieces{ tableData.getStringField("ValidPiecesLXF") },
|
||||||
|
.timeLimit = static_cast<float>(tableData.getFloatField("Timelimit", 30.0f)),
|
||||||
|
.numValidPieces = tableData.getIntField("NumValidPieces", 6),
|
||||||
|
.imaginationCost = tableData.getIntField("imagCostPerBuild", 10)
|
||||||
|
});
|
||||||
|
tableData.nextRow();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const CDTamingBuildPuzzle* CDTamingBuildPuzzleTable::GetByLOT(const LOT lot) const {
|
||||||
|
const auto& entries = GetEntries();
|
||||||
|
const auto itr = entries.find(lot);
|
||||||
|
return itr != entries.cend() ? &itr->second : nullptr;
|
||||||
|
}
|
@ -0,0 +1,60 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "CDTable.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Information for the minigame to be completed
|
||||||
|
*/
|
||||||
|
struct CDTamingBuildPuzzle {
|
||||||
|
UNUSED_COLUMN(uint32_t id = 0;)
|
||||||
|
|
||||||
|
// The LOT of the object that is to be created
|
||||||
|
LOT puzzleModelLot = LOT_NULL;
|
||||||
|
|
||||||
|
// The LOT of the NPC
|
||||||
|
UNUSED_COLUMN(LOT npcLot = LOT_NULL;)
|
||||||
|
|
||||||
|
// The .lxfml file that contains the bricks required to build the model
|
||||||
|
std::string validPieces{};
|
||||||
|
|
||||||
|
// The .lxfml file that contains the bricks NOT required to build the model
|
||||||
|
UNUSED_COLUMN(std::string invalidPieces{};)
|
||||||
|
|
||||||
|
// Difficulty value
|
||||||
|
UNUSED_COLUMN(int32_t difficulty = 1;)
|
||||||
|
|
||||||
|
// The time limit to complete the build
|
||||||
|
float timeLimit = 30.0f;
|
||||||
|
|
||||||
|
// The number of pieces required to complete the minigame
|
||||||
|
int32_t numValidPieces = 6;
|
||||||
|
|
||||||
|
// Number of valid pieces
|
||||||
|
UNUSED_COLUMN(int32_t totalNumPieces = 16;)
|
||||||
|
|
||||||
|
// Model name
|
||||||
|
UNUSED_COLUMN(std::string modelName{};)
|
||||||
|
|
||||||
|
// The .lxfml file that contains the full model
|
||||||
|
UNUSED_COLUMN(std::string fullModel{};)
|
||||||
|
|
||||||
|
// The duration of the pet taming minigame
|
||||||
|
UNUSED_COLUMN(float duration = 45.0f;)
|
||||||
|
|
||||||
|
// The imagination cost for the tamer to start the minigame
|
||||||
|
int32_t imaginationCost = 10;
|
||||||
|
};
|
||||||
|
|
||||||
|
class CDTamingBuildPuzzleTable : public CDTable<CDTamingBuildPuzzleTable, std::unordered_map<LOT, CDTamingBuildPuzzle>> {
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Load values from the CD client database
|
||||||
|
*/
|
||||||
|
void LoadValuesFromDatabase();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the pet ability table corresponding to the pet LOT
|
||||||
|
* @returns A pointer to the corresponding table, or nullptr if one cannot be found
|
||||||
|
*/
|
||||||
|
[[nodiscard]]
|
||||||
|
const CDTamingBuildPuzzle* GetByLOT(const LOT lot) const;
|
||||||
|
};
|
@ -36,5 +36,6 @@ set(DDATABASE_CDCLIENTDATABASE_CDCLIENTTABLES_SOURCES "CDActivitiesTable.cpp"
|
|||||||
"CDRewardsTable.cpp"
|
"CDRewardsTable.cpp"
|
||||||
"CDScriptComponentTable.cpp"
|
"CDScriptComponentTable.cpp"
|
||||||
"CDSkillBehaviorTable.cpp"
|
"CDSkillBehaviorTable.cpp"
|
||||||
|
"CDTamingBuildPuzzleTable.cpp"
|
||||||
"CDVendorComponentTable.cpp"
|
"CDVendorComponentTable.cpp"
|
||||||
"CDZoneTableTable.cpp" PARENT_SCOPE)
|
"CDZoneTableTable.cpp" PARENT_SCOPE)
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
#include "IActivityLog.h"
|
#include "IActivityLog.h"
|
||||||
#include "IIgnoreList.h"
|
#include "IIgnoreList.h"
|
||||||
#include "IAccountsRewardCodes.h"
|
#include "IAccountsRewardCodes.h"
|
||||||
|
#include "IBehaviors.h"
|
||||||
|
|
||||||
namespace sql {
|
namespace sql {
|
||||||
class Statement;
|
class Statement;
|
||||||
@ -40,7 +41,8 @@ class GameDatabase :
|
|||||||
public IMail, public ICommandLog, public IPlayerCheatDetections, public IBugReports,
|
public IMail, public ICommandLog, public IPlayerCheatDetections, public IBugReports,
|
||||||
public IPropertyContents, public IProperty, public IPetNames, public ICharXml,
|
public IPropertyContents, public IProperty, public IPetNames, public ICharXml,
|
||||||
public IMigrationHistory, public IUgc, public IFriends, public ICharInfo,
|
public IMigrationHistory, public IUgc, public IFriends, public ICharInfo,
|
||||||
public IAccounts, public IActivityLog, public IAccountsRewardCodes, public IIgnoreList {
|
public IAccounts, public IActivityLog, public IAccountsRewardCodes, public IIgnoreList,
|
||||||
|
public IBehaviors {
|
||||||
public:
|
public:
|
||||||
virtual ~GameDatabase() = default;
|
virtual ~GameDatabase() = default;
|
||||||
// TODO: These should be made private.
|
// TODO: These should be made private.
|
||||||
|
@ -33,6 +33,9 @@ public:
|
|||||||
|
|
||||||
// Add a new account to the database.
|
// Add a new account to the database.
|
||||||
virtual void InsertNewAccount(const std::string_view username, const std::string_view bcryptpassword) = 0;
|
virtual void InsertNewAccount(const std::string_view username, const std::string_view bcryptpassword) = 0;
|
||||||
|
|
||||||
|
// Update the GameMaster level of an account.
|
||||||
|
virtual void UpdateAccountGmLevel(const uint32_t accountId, const eGameMasterLevel gmLevel) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif //!__IACCOUNTS__H__
|
#endif //!__IACCOUNTS__H__
|
||||||
|
22
dDatabase/GameDatabase/ITables/IBehaviors.h
Normal file
22
dDatabase/GameDatabase/ITables/IBehaviors.h
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
#ifndef IBEHAVIORS_H
|
||||||
|
#define IBEHAVIORS_H
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
#include "dCommonVars.h"
|
||||||
|
|
||||||
|
class IBehaviors {
|
||||||
|
public:
|
||||||
|
struct Info {
|
||||||
|
int32_t behaviorId{};
|
||||||
|
uint32_t characterId{};
|
||||||
|
std::string behaviorInfo;
|
||||||
|
};
|
||||||
|
|
||||||
|
// This Add also takes care of updating if it exists.
|
||||||
|
virtual void AddBehavior(const Info& info) = 0;
|
||||||
|
virtual std::string GetBehavior(const int32_t behaviorId) = 0;
|
||||||
|
virtual void RemoveBehavior(const int32_t behaviorId) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif //!IBEHAVIORS_H
|
@ -1,6 +1,7 @@
|
|||||||
#ifndef __IPROPERTIESCONTENTS__H__
|
#ifndef __IPROPERTIESCONTENTS__H__
|
||||||
#define __IPROPERTIESCONTENTS__H__
|
#define __IPROPERTIESCONTENTS__H__
|
||||||
|
|
||||||
|
#include <array>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
|
|
||||||
@ -16,6 +17,7 @@ public:
|
|||||||
LWOOBJID id{};
|
LWOOBJID id{};
|
||||||
LOT lot{};
|
LOT lot{};
|
||||||
uint32_t ugcId{};
|
uint32_t ugcId{};
|
||||||
|
std::array<int32_t, 5> behaviors{};
|
||||||
};
|
};
|
||||||
|
|
||||||
// Inserts a new UGC model into the database.
|
// Inserts a new UGC model into the database.
|
||||||
@ -32,7 +34,7 @@ public:
|
|||||||
virtual void InsertNewPropertyModel(const LWOOBJID& propertyId, const IPropertyContents::Model& model, const std::string_view name) = 0;
|
virtual void InsertNewPropertyModel(const LWOOBJID& propertyId, const IPropertyContents::Model& model, const std::string_view name) = 0;
|
||||||
|
|
||||||
// Update the model position and rotation for the given property id.
|
// Update the model position and rotation for the given property id.
|
||||||
virtual void UpdateModelPositionRotation(const LWOOBJID& propertyId, const NiPoint3& position, const NiQuaternion& rotation) = 0;
|
virtual void UpdateModel(const LWOOBJID& propertyId, const NiPoint3& position, const NiQuaternion& rotation, const std::array<std::pair<int32_t, std::string>, 5>& behaviors) = 0;
|
||||||
|
|
||||||
// Remove the model for the given property id.
|
// Remove the model for the given property id.
|
||||||
virtual void RemoveModel(const LWOOBJID& modelId) = 0;
|
virtual void RemoveModel(const LWOOBJID& modelId) = 0;
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
#include "Game.h"
|
#include "Game.h"
|
||||||
#include "dConfig.h"
|
#include "dConfig.h"
|
||||||
#include "Logger.h"
|
#include "Logger.h"
|
||||||
|
#include "dPlatforms.h"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
std::string databaseName;
|
std::string databaseName;
|
||||||
@ -39,14 +40,13 @@ void MySQLDatabase::Connect() {
|
|||||||
properties["autoReconnect"] = "true";
|
properties["autoReconnect"] = "true";
|
||||||
|
|
||||||
databaseName = Game::config->GetValue("mysql_database").c_str();
|
databaseName = Game::config->GetValue("mysql_database").c_str();
|
||||||
|
|
||||||
// `connect(const Properties& props)` segfaults in windows debug, but
|
// `connect(const Properties& props)` segfaults in windows debug, but
|
||||||
// `connect(const SQLString& host, const SQLString& user, const SQLString& pwd)` doesn't handle pipes/unix sockets correctly
|
// `connect(const SQLString& host, const SQLString& user, const SQLString& pwd)` doesn't handle pipes/unix sockets correctly
|
||||||
if (properties.find("localSocket") != properties.end() || properties.find("pipe") != properties.end()) {
|
#if defined(DARKFLAME_PLATFORM_WIN32) && defined(_DEBUG)
|
||||||
con = driver->connect(properties);
|
|
||||||
} else {
|
|
||||||
con = driver->connect(properties["hostName"].c_str(), properties["user"].c_str(), properties["password"].c_str());
|
con = driver->connect(properties["hostName"].c_str(), properties["user"].c_str(), properties["password"].c_str());
|
||||||
}
|
#else
|
||||||
|
con = driver->connect(properties);
|
||||||
|
#endif
|
||||||
con->setSchema(databaseName.c_str());
|
con->setSchema(databaseName.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -74,7 +74,7 @@ public:
|
|||||||
std::vector<IPropertyContents::Model> GetPropertyModels(const LWOOBJID& propertyId) override;
|
std::vector<IPropertyContents::Model> GetPropertyModels(const LWOOBJID& propertyId) override;
|
||||||
void RemoveUnreferencedUgcModels() override;
|
void RemoveUnreferencedUgcModels() override;
|
||||||
void InsertNewPropertyModel(const LWOOBJID& propertyId, const IPropertyContents::Model& model, const std::string_view name) override;
|
void InsertNewPropertyModel(const LWOOBJID& propertyId, const IPropertyContents::Model& model, const std::string_view name) override;
|
||||||
void UpdateModelPositionRotation(const LWOOBJID& propertyId, const NiPoint3& position, const NiQuaternion& rotation) override;
|
void UpdateModel(const LWOOBJID& propertyId, const NiPoint3& position, const NiQuaternion& rotation, const std::array<std::pair<int32_t, std::string>, 5>& behaviors) override;
|
||||||
void RemoveModel(const LWOOBJID& modelId) override;
|
void RemoveModel(const LWOOBJID& modelId) override;
|
||||||
void UpdatePerformanceCost(const LWOZONEID& zoneId, const float performanceCost) override;
|
void UpdatePerformanceCost(const LWOZONEID& zoneId, const float performanceCost) override;
|
||||||
void InsertNewBugReport(const IBugReports::Info& info) override;
|
void InsertNewBugReport(const IBugReports::Info& info) override;
|
||||||
@ -108,6 +108,10 @@ public:
|
|||||||
std::vector<IIgnoreList::Info> GetIgnoreList(const uint32_t playerId) override;
|
std::vector<IIgnoreList::Info> GetIgnoreList(const uint32_t playerId) override;
|
||||||
void InsertRewardCode(const uint32_t account_id, const uint32_t reward_code) override;
|
void InsertRewardCode(const uint32_t account_id, const uint32_t reward_code) override;
|
||||||
std::vector<uint32_t> GetRewardCodesByAccountID(const uint32_t account_id) override;
|
std::vector<uint32_t> GetRewardCodesByAccountID(const uint32_t account_id) override;
|
||||||
|
void AddBehavior(const IBehaviors::Info& info) override;
|
||||||
|
std::string GetBehavior(const int32_t behaviorId) override;
|
||||||
|
void RemoveBehavior(const int32_t characterId) override;
|
||||||
|
void UpdateAccountGmLevel(const uint32_t accountId, const eGameMasterLevel gmLevel) override;
|
||||||
private:
|
private:
|
||||||
|
|
||||||
// Generic query functions that can be used for any query.
|
// Generic query functions that can be used for any query.
|
||||||
|
@ -35,3 +35,7 @@ void MySQLDatabase::UpdateAccountPassword(const uint32_t accountId, const std::s
|
|||||||
void MySQLDatabase::InsertNewAccount(const std::string_view username, const std::string_view bcryptpassword) {
|
void MySQLDatabase::InsertNewAccount(const std::string_view username, const std::string_view bcryptpassword) {
|
||||||
ExecuteInsert("INSERT INTO accounts (name, password, gm_level) VALUES (?, ?, ?);", username, bcryptpassword, static_cast<int32_t>(eGameMasterLevel::OPERATOR));
|
ExecuteInsert("INSERT INTO accounts (name, password, gm_level) VALUES (?, ?, ?);", username, bcryptpassword, static_cast<int32_t>(eGameMasterLevel::OPERATOR));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MySQLDatabase::UpdateAccountGmLevel(const uint32_t accountId, const eGameMasterLevel gmLevel) {
|
||||||
|
ExecuteUpdate("UPDATE accounts SET gm_level = ? WHERE id = ?;", static_cast<int32_t>(gmLevel), accountId);
|
||||||
|
}
|
||||||
|
19
dDatabase/GameDatabase/MySQL/Tables/Behaviors.cpp
Normal file
19
dDatabase/GameDatabase/MySQL/Tables/Behaviors.cpp
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
#include "IBehaviors.h"
|
||||||
|
|
||||||
|
#include "MySQLDatabase.h"
|
||||||
|
|
||||||
|
void MySQLDatabase::AddBehavior(const IBehaviors::Info& info) {
|
||||||
|
ExecuteInsert(
|
||||||
|
"INSERT INTO behaviors (behavior_info, character_id, behavior_id) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE behavior_info = ?",
|
||||||
|
info.behaviorInfo, info.characterId, info.behaviorId, info.behaviorInfo
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MySQLDatabase::RemoveBehavior(const int32_t behaviorId) {
|
||||||
|
ExecuteDelete("DELETE FROM behaviors WHERE behavior_id = ?", behaviorId);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string MySQLDatabase::GetBehavior(const int32_t behaviorId) {
|
||||||
|
auto result = ExecuteSelect("SELECT behavior_info FROM behaviors WHERE behavior_id = ?", behaviorId);
|
||||||
|
return result->next() ? result->getString("behavior_info").c_str() : "";
|
||||||
|
}
|
@ -2,6 +2,7 @@ set(DDATABASES_DATABASES_MYSQL_TABLES_SOURCES
|
|||||||
"Accounts.cpp"
|
"Accounts.cpp"
|
||||||
"AccountsRewardCodes.cpp"
|
"AccountsRewardCodes.cpp"
|
||||||
"ActivityLog.cpp"
|
"ActivityLog.cpp"
|
||||||
|
"Behaviors.cpp"
|
||||||
"BugReports.cpp"
|
"BugReports.cpp"
|
||||||
"CharInfo.cpp"
|
"CharInfo.cpp"
|
||||||
"CharXml.cpp"
|
"CharXml.cpp"
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
#include "MySQLDatabase.h"
|
#include "MySQLDatabase.h"
|
||||||
|
|
||||||
std::vector<IPropertyContents::Model> MySQLDatabase::GetPropertyModels(const LWOOBJID& propertyId) {
|
std::vector<IPropertyContents::Model> MySQLDatabase::GetPropertyModels(const LWOOBJID& propertyId) {
|
||||||
auto result = ExecuteSelect("SELECT id, lot, x, y, z, rx, ry, rz, rw, ugc_id FROM properties_contents WHERE property_id = ?;", propertyId);
|
auto result = ExecuteSelect(
|
||||||
|
"SELECT id, lot, x, y, z, rx, ry, rz, rw, ugc_id, "
|
||||||
|
"behavior_1, behavior_2, behavior_3, behavior_4, behavior_5 "
|
||||||
|
"FROM properties_contents WHERE property_id = ?;", propertyId);
|
||||||
|
|
||||||
std::vector<IPropertyContents::Model> toReturn;
|
std::vector<IPropertyContents::Model> toReturn;
|
||||||
toReturn.reserve(result->rowsCount());
|
toReturn.reserve(result->rowsCount());
|
||||||
@ -17,6 +20,12 @@ std::vector<IPropertyContents::Model> MySQLDatabase::GetPropertyModels(const LWO
|
|||||||
model.rotation.y = result->getFloat("ry");
|
model.rotation.y = result->getFloat("ry");
|
||||||
model.rotation.z = result->getFloat("rz");
|
model.rotation.z = result->getFloat("rz");
|
||||||
model.ugcId = result->getUInt64("ugc_id");
|
model.ugcId = result->getUInt64("ugc_id");
|
||||||
|
model.behaviors[0] = result->getInt("behavior_1");
|
||||||
|
model.behaviors[1] = result->getInt("behavior_2");
|
||||||
|
model.behaviors[2] = result->getInt("behavior_3");
|
||||||
|
model.behaviors[3] = result->getInt("behavior_4");
|
||||||
|
model.behaviors[4] = result->getInt("behavior_5");
|
||||||
|
|
||||||
toReturn.push_back(std::move(model));
|
toReturn.push_back(std::move(model));
|
||||||
}
|
}
|
||||||
return toReturn;
|
return toReturn;
|
||||||
@ -32,21 +41,23 @@ void MySQLDatabase::InsertNewPropertyModel(const LWOOBJID& propertyId, const IPr
|
|||||||
model.id, propertyId, model.ugcId == 0 ? std::nullopt : std::optional(model.ugcId), static_cast<uint32_t>(model.lot),
|
model.id, propertyId, model.ugcId == 0 ? std::nullopt : std::optional(model.ugcId), static_cast<uint32_t>(model.lot),
|
||||||
model.position.x, model.position.y, model.position.z, model.rotation.x, model.rotation.y, model.rotation.z, model.rotation.w,
|
model.position.x, model.position.y, model.position.z, model.rotation.x, model.rotation.y, model.rotation.z, model.rotation.w,
|
||||||
name, "", // Model description. TODO implement this.
|
name, "", // Model description. TODO implement this.
|
||||||
0, // behavior 1. TODO implement this.
|
model.behaviors[0], // behavior 1
|
||||||
0, // behavior 2. TODO implement this.
|
model.behaviors[1], // behavior 2
|
||||||
0, // behavior 3. TODO implement this.
|
model.behaviors[2], // behavior 3
|
||||||
0, // behavior 4. TODO implement this.
|
model.behaviors[3], // behavior 4
|
||||||
0 // behavior 5. TODO implement this.
|
model.behaviors[4] // behavior 5
|
||||||
);
|
);
|
||||||
} catch (sql::SQLException& e) {
|
} catch (sql::SQLException& e) {
|
||||||
LOG("Error inserting new property model: %s", e.what());
|
LOG("Error inserting new property model: %s", e.what());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MySQLDatabase::UpdateModelPositionRotation(const LWOOBJID& propertyId, const NiPoint3& position, const NiQuaternion& rotation) {
|
void MySQLDatabase::UpdateModel(const LWOOBJID& propertyId, const NiPoint3& position, const NiQuaternion& rotation, const std::array<std::pair<int32_t, std::string>, 5>& behaviors) {
|
||||||
ExecuteUpdate(
|
ExecuteUpdate(
|
||||||
"UPDATE properties_contents SET x = ?, y = ?, z = ?, rx = ?, ry = ?, rz = ?, rw = ? WHERE id = ?;",
|
"UPDATE properties_contents SET x = ?, y = ?, z = ?, rx = ?, ry = ?, rz = ?, rw = ?, "
|
||||||
position.x, position.y, position.z, rotation.x, rotation.y, rotation.z, rotation.w, propertyId);
|
"behavior_1 = ?, behavior_2 = ?, behavior_3 = ?, behavior_4 = ?, behavior_5 = ? WHERE id = ?;",
|
||||||
|
position.x, position.y, position.z, rotation.x, rotation.y, rotation.z, rotation.w,
|
||||||
|
behaviors[0].first, behaviors[1].first, behaviors[2].first, behaviors[3].first, behaviors[4].first, propertyId);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MySQLDatabase::RemoveModel(const LWOOBJID& modelId) {
|
void MySQLDatabase::RemoveModel(const LWOOBJID& modelId) {
|
||||||
|
@ -27,6 +27,8 @@ Character::Character(uint32_t id, User* parentUser) {
|
|||||||
m_ID = id;
|
m_ID = id;
|
||||||
m_ParentUser = parentUser;
|
m_ParentUser = parentUser;
|
||||||
m_OurEntity = nullptr;
|
m_OurEntity = nullptr;
|
||||||
|
m_GMLevel = eGameMasterLevel::CIVILIAN;
|
||||||
|
m_PermissionMap = static_cast<ePermissionMap>(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
Character::~Character() {
|
Character::~Character() {
|
||||||
|
@ -464,22 +464,22 @@ private:
|
|||||||
/**
|
/**
|
||||||
* The ID of this character. First 32 bits of the ObjectID.
|
* The ID of this character. First 32 bits of the ObjectID.
|
||||||
*/
|
*/
|
||||||
uint32_t m_ID;
|
uint32_t m_ID{};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The 64-bit unique ID used in the game.
|
* The 64-bit unique ID used in the game.
|
||||||
*/
|
*/
|
||||||
LWOOBJID m_ObjectID;
|
LWOOBJID m_ObjectID{ LWOOBJID_EMPTY };
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The user that owns this character.
|
* The user that owns this character.
|
||||||
*/
|
*/
|
||||||
User* m_ParentUser;
|
User* m_ParentUser{};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If the character is in game, this is the entity that it represents, else nullptr.
|
* If the character is in game, this is the entity that it represents, else nullptr.
|
||||||
*/
|
*/
|
||||||
Entity* m_OurEntity;
|
Entity* m_OurEntity{};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 0-9, the Game Master level of this character.
|
* 0-9, the Game Master level of this character.
|
||||||
@ -506,17 +506,17 @@ private:
|
|||||||
/**
|
/**
|
||||||
* Whether the custom name of this character is rejected
|
* Whether the custom name of this character is rejected
|
||||||
*/
|
*/
|
||||||
bool m_NameRejected;
|
bool m_NameRejected{};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The current amount of coins of this character
|
* The current amount of coins of this character
|
||||||
*/
|
*/
|
||||||
int64_t m_Coins;
|
int64_t m_Coins{};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether the character is building
|
* Whether the character is building
|
||||||
*/
|
*/
|
||||||
bool m_BuildMode;
|
bool m_BuildMode{};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The items equipped by the character on world load
|
* The items equipped by the character on world load
|
||||||
@ -583,7 +583,7 @@ private:
|
|||||||
/**
|
/**
|
||||||
* The ID of the properties of this character
|
* The ID of the properties of this character
|
||||||
*/
|
*/
|
||||||
uint32_t m_PropertyCloneID;
|
uint32_t m_PropertyCloneID{};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The XML data for this character, stored as string
|
* The XML data for this character, stored as string
|
||||||
@ -613,7 +613,7 @@ private:
|
|||||||
/**
|
/**
|
||||||
* The last time this character logged in
|
* The last time this character logged in
|
||||||
*/
|
*/
|
||||||
uint64_t m_LastLogin;
|
uint64_t m_LastLogin{};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The gameplay flags this character has (not just true values)
|
* The gameplay flags this character has (not just true values)
|
||||||
|
@ -225,7 +225,7 @@ void Entity::Initialize() {
|
|||||||
|
|
||||||
AddComponent<SimplePhysicsComponent>(simplePhysicsComponentID);
|
AddComponent<SimplePhysicsComponent>(simplePhysicsComponentID);
|
||||||
|
|
||||||
AddComponent<ModelComponent>();
|
AddComponent<ModelComponent>()->LoadBehaviors();
|
||||||
|
|
||||||
AddComponent<RenderComponent>();
|
AddComponent<RenderComponent>();
|
||||||
|
|
||||||
@ -649,7 +649,7 @@ void Entity::Initialize() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::MODEL, -1) != -1 && !GetComponent<PetComponent>()) {
|
if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::MODEL, -1) != -1 && !GetComponent<PetComponent>()) {
|
||||||
AddComponent<ModelComponent>();
|
AddComponent<ModelComponent>()->LoadBehaviors();
|
||||||
if (!HasComponent(eReplicaComponentType::DESTROYABLE)) {
|
if (!HasComponent(eReplicaComponentType::DESTROYABLE)) {
|
||||||
auto* destroyableComponent = AddComponent<DestroyableComponent>();
|
auto* destroyableComponent = AddComponent<DestroyableComponent>();
|
||||||
destroyableComponent->SetHealth(1);
|
destroyableComponent->SetHealth(1);
|
||||||
@ -1534,7 +1534,7 @@ void Entity::Kill(Entity* murderer, const eKillType killType) {
|
|||||||
bool waitForDeathAnimation = false;
|
bool waitForDeathAnimation = false;
|
||||||
|
|
||||||
if (destroyableComponent) {
|
if (destroyableComponent) {
|
||||||
waitForDeathAnimation = destroyableComponent->GetDeathBehavior() == 0 && killType != eKillType::SILENT;
|
waitForDeathAnimation = !destroyableComponent->GetIsSmashable() && destroyableComponent->GetDeathBehavior() == 0 && killType != eKillType::SILENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Live waited a hard coded 12 seconds for death animations of type 0 before networking destruction!
|
// Live waited a hard coded 12 seconds for death animations of type 0 before networking destruction!
|
||||||
@ -1840,6 +1840,12 @@ const NiPoint3& Entity::GetPosition() const {
|
|||||||
return vehicel->GetPosition();
|
return vehicel->GetPosition();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto* rigidBodyPhantomPhysicsComponent = GetComponent<RigidbodyPhantomPhysicsComponent>();
|
||||||
|
|
||||||
|
if (rigidBodyPhantomPhysicsComponent != nullptr) {
|
||||||
|
return rigidBodyPhantomPhysicsComponent->GetPosition();
|
||||||
|
}
|
||||||
|
|
||||||
return NiPoint3Constant::ZERO;
|
return NiPoint3Constant::ZERO;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1868,6 +1874,12 @@ const NiQuaternion& Entity::GetRotation() const {
|
|||||||
return vehicel->GetRotation();
|
return vehicel->GetRotation();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto* rigidBodyPhantomPhysicsComponent = GetComponent<RigidbodyPhantomPhysicsComponent>();
|
||||||
|
|
||||||
|
if (rigidBodyPhantomPhysicsComponent != nullptr) {
|
||||||
|
return rigidBodyPhantomPhysicsComponent->GetRotation();
|
||||||
|
}
|
||||||
|
|
||||||
return NiQuaternionConstant::IDENTITY;
|
return NiQuaternionConstant::IDENTITY;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1896,6 +1908,12 @@ void Entity::SetPosition(const NiPoint3& position) {
|
|||||||
vehicel->SetPosition(position);
|
vehicel->SetPosition(position);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto* rigidBodyPhantomPhysicsComponent = GetComponent<RigidbodyPhantomPhysicsComponent>();
|
||||||
|
|
||||||
|
if (rigidBodyPhantomPhysicsComponent != nullptr) {
|
||||||
|
rigidBodyPhantomPhysicsComponent->SetPosition(position);
|
||||||
|
}
|
||||||
|
|
||||||
Game::entityManager->SerializeEntity(this);
|
Game::entityManager->SerializeEntity(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1924,6 +1942,12 @@ void Entity::SetRotation(const NiQuaternion& rotation) {
|
|||||||
vehicel->SetRotation(rotation);
|
vehicel->SetRotation(rotation);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto* rigidBodyPhantomPhysicsComponent = GetComponent<RigidbodyPhantomPhysicsComponent>();
|
||||||
|
|
||||||
|
if (rigidBodyPhantomPhysicsComponent != nullptr) {
|
||||||
|
rigidBodyPhantomPhysicsComponent->SetRotation(rotation);
|
||||||
|
}
|
||||||
|
|
||||||
Game::entityManager->SerializeEntity(this);
|
Game::entityManager->SerializeEntity(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,6 +12,7 @@ User::User(const SystemAddress& sysAddr, const std::string& username, const std:
|
|||||||
m_AccountID = 0;
|
m_AccountID = 0;
|
||||||
m_Username = "";
|
m_Username = "";
|
||||||
m_SessionKey = "";
|
m_SessionKey = "";
|
||||||
|
m_MuteExpire = 0;
|
||||||
|
|
||||||
m_MaxGMLevel = eGameMasterLevel::CIVILIAN; //The max GM level this account can assign to it's characters
|
m_MaxGMLevel = eGameMasterLevel::CIVILIAN; //The max GM level this account can assign to it's characters
|
||||||
m_LastCharID = 0;
|
m_LastCharID = 0;
|
||||||
|
@ -536,13 +536,13 @@ void UserManager::LoginCharacter(const SystemAddress& sysAddr, uint32_t playerID
|
|||||||
uint32_t FindCharShirtID(uint32_t shirtColor, uint32_t shirtStyle) {
|
uint32_t FindCharShirtID(uint32_t shirtColor, uint32_t shirtStyle) {
|
||||||
try {
|
try {
|
||||||
auto stmt = CDClientDatabase::CreatePreppedStmt(
|
auto stmt = CDClientDatabase::CreatePreppedStmt(
|
||||||
"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) == ? AND icc.color1 == ? AND icc.decal == ?"
|
"select obj.id as objectId 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) == ? AND icc.color1 == ? AND icc.decal == ?"
|
||||||
);
|
);
|
||||||
stmt.bind(1, "character create shirt");
|
stmt.bind(1, "character create shirt");
|
||||||
stmt.bind(2, static_cast<int>(shirtColor));
|
stmt.bind(2, static_cast<int>(shirtColor));
|
||||||
stmt.bind(3, static_cast<int>(shirtStyle));
|
stmt.bind(3, static_cast<int>(shirtStyle));
|
||||||
auto tableData = stmt.execQuery();
|
auto tableData = stmt.execQuery();
|
||||||
auto shirtLOT = tableData.getIntField(0, 4069);
|
auto shirtLOT = tableData.getIntField("objectId", 4069);
|
||||||
tableData.finalize();
|
tableData.finalize();
|
||||||
return shirtLOT;
|
return shirtLOT;
|
||||||
} catch (const std::exception& ex) {
|
} catch (const std::exception& ex) {
|
||||||
@ -555,12 +555,12 @@ uint32_t FindCharShirtID(uint32_t shirtColor, uint32_t shirtStyle) {
|
|||||||
uint32_t FindCharPantsID(uint32_t pantsColor) {
|
uint32_t FindCharPantsID(uint32_t pantsColor) {
|
||||||
try {
|
try {
|
||||||
auto stmt = CDClientDatabase::CreatePreppedStmt(
|
auto stmt = CDClientDatabase::CreatePreppedStmt(
|
||||||
"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) == ? AND icc.color1 == ?"
|
"select obj.id as objectId 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) == ? AND icc.color1 == ?"
|
||||||
);
|
);
|
||||||
stmt.bind(1, "cc pants");
|
stmt.bind(1, "cc pants");
|
||||||
stmt.bind(2, static_cast<int>(pantsColor));
|
stmt.bind(2, static_cast<int>(pantsColor));
|
||||||
auto tableData = stmt.execQuery();
|
auto tableData = stmt.execQuery();
|
||||||
auto pantsLOT = tableData.getIntField(0, 2508);
|
auto pantsLOT = tableData.getIntField("objectId", 2508);
|
||||||
tableData.finalize();
|
tableData.finalize();
|
||||||
return pantsLOT;
|
return pantsLOT;
|
||||||
} catch (const std::exception& ex) {
|
} catch (const std::exception& ex) {
|
||||||
|
@ -377,10 +377,10 @@ void Behavior::PlayFx(std::u16string type, const LWOOBJID target, const LWOOBJID
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto name = std::string(result.getStringField(0));
|
const auto name = std::string(result.getStringField("effectName"));
|
||||||
|
|
||||||
if (type.empty()) {
|
if (type.empty()) {
|
||||||
const auto typeResult = result.getStringField(1);
|
const auto typeResult = result.getStringField("effectType");
|
||||||
|
|
||||||
type = GeneralUtils::ASCIIToUTF16(typeResult);
|
type = GeneralUtils::ASCIIToUTF16(typeResult);
|
||||||
|
|
||||||
|
@ -105,7 +105,7 @@ void BehaviorContext::ExecuteUpdates() {
|
|||||||
this->scheduledUpdates.clear();
|
this->scheduledUpdates.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void BehaviorContext::SyncBehavior(const uint32_t syncId, RakNet::BitStream& bitStream) {
|
bool BehaviorContext::SyncBehavior(const uint32_t syncId, RakNet::BitStream& bitStream) {
|
||||||
BehaviorSyncEntry entry;
|
BehaviorSyncEntry entry;
|
||||||
auto found = false;
|
auto found = false;
|
||||||
|
|
||||||
@ -128,7 +128,7 @@ void BehaviorContext::SyncBehavior(const uint32_t syncId, RakNet::BitStream& bit
|
|||||||
if (!found) {
|
if (!found) {
|
||||||
LOG("Failed to find behavior sync entry with sync id (%i)!", syncId);
|
LOG("Failed to find behavior sync entry with sync id (%i)!", syncId);
|
||||||
|
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto* behavior = entry.behavior;
|
auto* behavior = entry.behavior;
|
||||||
@ -137,10 +137,11 @@ void BehaviorContext::SyncBehavior(const uint32_t syncId, RakNet::BitStream& bit
|
|||||||
if (behavior == nullptr) {
|
if (behavior == nullptr) {
|
||||||
LOG("Invalid behavior for sync id (%i)!", syncId);
|
LOG("Invalid behavior for sync id (%i)!", syncId);
|
||||||
|
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
behavior->Sync(this, bitStream, branch);
|
behavior->Sync(this, bitStream, branch);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -198,6 +199,26 @@ void BehaviorContext::UpdatePlayerSyncs(float deltaTime) {
|
|||||||
i++;
|
i++;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this->skillUId != 0 && !clientInitalized) {
|
||||||
|
EchoSyncSkill echo;
|
||||||
|
echo.bDone = true;
|
||||||
|
echo.uiSkillHandle = this->skillUId;
|
||||||
|
echo.uiBehaviorHandle = entry.handle;
|
||||||
|
|
||||||
|
RakNet::BitStream bitStream{};
|
||||||
|
entry.behavior->SyncCalculation(this, bitStream, entry.branchContext);
|
||||||
|
|
||||||
|
echo.sBitStream.assign(reinterpret_cast<char*>(bitStream.GetData()), bitStream.GetNumberOfBytesUsed());
|
||||||
|
|
||||||
|
RakNet::BitStream message;
|
||||||
|
BitStreamUtils::WriteHeader(message, eConnectionType::CLIENT, eClientMessageType::GAME_MSG);
|
||||||
|
message.Write(this->originator);
|
||||||
|
echo.Serialize(message);
|
||||||
|
|
||||||
|
Game::server->Send(message, UNASSIGNED_SYSTEM_ADDRESS, true);
|
||||||
|
}
|
||||||
|
|
||||||
this->syncEntries.erase(this->syncEntries.begin() + i);
|
this->syncEntries.erase(this->syncEntries.begin() + i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -224,6 +245,16 @@ bool BehaviorContext::CalculateUpdate(const float deltaTime) {
|
|||||||
for (auto i = 0u; i < this->syncEntries.size(); ++i) {
|
for (auto i = 0u; i < this->syncEntries.size(); ++i) {
|
||||||
auto entry = this->syncEntries.at(i);
|
auto entry = this->syncEntries.at(i);
|
||||||
|
|
||||||
|
if (entry.behavior->m_templateId == BehaviorTemplate::ATTACK_DELAY) {
|
||||||
|
auto* self = Game::entityManager->GetEntity(originator);
|
||||||
|
if (self) {
|
||||||
|
auto* destroyableComponent = self->GetComponent<DestroyableComponent>();
|
||||||
|
if (destroyableComponent && destroyableComponent->GetHealth() <= 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (entry.time > 0) {
|
if (entry.time > 0) {
|
||||||
entry.time -= deltaTime;
|
entry.time -= deltaTime;
|
||||||
|
|
||||||
@ -333,7 +364,7 @@ void BehaviorContext::FilterTargets(std::vector<Entity*>& targets, std::forward_
|
|||||||
}
|
}
|
||||||
|
|
||||||
// handle targeting the caster
|
// handle targeting the caster
|
||||||
if (candidate == caster){
|
if (candidate == caster) {
|
||||||
// if we aren't targeting self, erase, otherise increment and continue
|
// if we aren't targeting self, erase, otherise increment and continue
|
||||||
if (!targetSelf) index = targets.erase(index);
|
if (!targetSelf) index = targets.erase(index);
|
||||||
else index++;
|
else index++;
|
||||||
@ -356,24 +387,24 @@ void BehaviorContext::FilterTargets(std::vector<Entity*>& targets, std::forward_
|
|||||||
}
|
}
|
||||||
|
|
||||||
// if they are dead, then earse and continue
|
// if they are dead, then earse and continue
|
||||||
if (candidateDestroyableComponent->GetIsDead()){
|
if (candidateDestroyableComponent->GetIsDead()) {
|
||||||
index = targets.erase(index);
|
index = targets.erase(index);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if their faction is explicitly included, increment and continue
|
// if their faction is explicitly included, increment and continue
|
||||||
auto candidateFactions = candidateDestroyableComponent->GetFactionIDs();
|
auto candidateFactions = candidateDestroyableComponent->GetFactionIDs();
|
||||||
if (CheckFactionList(includeFactionList, candidateFactions)){
|
if (CheckFactionList(includeFactionList, candidateFactions)) {
|
||||||
index++;
|
index++;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if they are a team member
|
// check if they are a team member
|
||||||
if (targetTeam){
|
if (targetTeam) {
|
||||||
auto* team = TeamManager::Instance()->GetTeam(this->caster);
|
auto* team = TeamManager::Instance()->GetTeam(this->caster);
|
||||||
if (team){
|
if (team) {
|
||||||
// if we find a team member keep it and continue to skip enemy checks
|
// if we find a team member keep it and continue to skip enemy checks
|
||||||
if(std::find(team->members.begin(), team->members.end(), candidate->GetObjectID()) != team->members.end()){
|
if (std::find(team->members.begin(), team->members.end(), candidate->GetObjectID()) != team->members.end()) {
|
||||||
index++;
|
index++;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -419,8 +450,8 @@ bool BehaviorContext::CheckTargetingRequirements(const Entity* target) const {
|
|||||||
// returns true if any of the object factions are in the faction list
|
// returns true if any of the object factions are in the faction list
|
||||||
bool BehaviorContext::CheckFactionList(std::forward_list<int32_t>& factionList, std::vector<int32_t>& objectsFactions) const {
|
bool BehaviorContext::CheckFactionList(std::forward_list<int32_t>& factionList, std::vector<int32_t>& objectsFactions) const {
|
||||||
if (factionList.empty() || objectsFactions.empty()) return false;
|
if (factionList.empty() || objectsFactions.empty()) return false;
|
||||||
for (auto faction : factionList){
|
for (auto faction : factionList) {
|
||||||
if(std::find(objectsFactions.begin(), objectsFactions.end(), faction) != objectsFactions.end()) return true;
|
if (std::find(objectsFactions.begin(), objectsFactions.end(), faction) != objectsFactions.end()) return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -93,7 +93,7 @@ struct BehaviorContext
|
|||||||
|
|
||||||
void ExecuteUpdates();
|
void ExecuteUpdates();
|
||||||
|
|
||||||
void SyncBehavior(uint32_t syncId, RakNet::BitStream& bitStream);
|
bool SyncBehavior(uint32_t syncId, RakNet::BitStream& bitStream);
|
||||||
|
|
||||||
void Update(float deltaTime);
|
void Update(float deltaTime);
|
||||||
|
|
||||||
|
@ -27,6 +27,8 @@ void DamageAbsorptionBehavior::Handle(BehaviorContext* context, RakNet::BitStrea
|
|||||||
destroyable->SetIsShielded(true);
|
destroyable->SetIsShielded(true);
|
||||||
|
|
||||||
context->RegisterTimerBehavior(this, branch, target->GetObjectID());
|
context->RegisterTimerBehavior(this, branch, target->GetObjectID());
|
||||||
|
|
||||||
|
Game::entityManager->SerializeEntity(target);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DamageAbsorptionBehavior::Calculate(BehaviorContext* context, RakNet::BitStream& bitStream, BehaviorBranchContext branch) {
|
void DamageAbsorptionBehavior::Calculate(BehaviorContext* context, RakNet::BitStream& bitStream, BehaviorBranchContext branch) {
|
||||||
@ -52,7 +54,13 @@ void DamageAbsorptionBehavior::Timer(BehaviorContext* context, BehaviorBranchCon
|
|||||||
|
|
||||||
const auto toRemove = std::min(present, this->m_absorbAmount);
|
const auto toRemove = std::min(present, this->m_absorbAmount);
|
||||||
|
|
||||||
destroyable->SetDamageToAbsorb(present - toRemove);
|
const auto remaining = present - toRemove;
|
||||||
|
|
||||||
|
destroyable->SetDamageToAbsorb(remaining);
|
||||||
|
|
||||||
|
destroyable->SetIsShielded(remaining > 0);
|
||||||
|
|
||||||
|
Game::entityManager->SerializeEntity(target);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DamageAbsorptionBehavior::Load() {
|
void DamageAbsorptionBehavior::Load() {
|
||||||
|
@ -47,11 +47,11 @@ void SwitchMultipleBehavior::Load() {
|
|||||||
auto result = query.execQuery();
|
auto result = query.execQuery();
|
||||||
|
|
||||||
while (!result.eof()) {
|
while (!result.eof()) {
|
||||||
const auto behavior_id = static_cast<uint32_t>(result.getFloatField(1));
|
const auto behavior_id = static_cast<uint32_t>(result.getFloatField("behavior"));
|
||||||
|
|
||||||
auto* behavior = CreateBehavior(behavior_id);
|
auto* behavior = CreateBehavior(behavior_id);
|
||||||
|
|
||||||
auto value = result.getFloatField(2);
|
auto value = result.getFloatField("value");
|
||||||
|
|
||||||
this->m_behaviors.emplace_back(value, behavior);
|
this->m_behaviors.emplace_back(value, behavior);
|
||||||
|
|
||||||
|
@ -29,7 +29,8 @@
|
|||||||
|
|
||||||
BaseCombatAIComponent::BaseCombatAIComponent(Entity* parent, const uint32_t id): Component(parent) {
|
BaseCombatAIComponent::BaseCombatAIComponent(Entity* parent, const uint32_t id): Component(parent) {
|
||||||
m_Target = LWOOBJID_EMPTY;
|
m_Target = LWOOBJID_EMPTY;
|
||||||
SetAiState(AiState::spawn);
|
m_DirtyStateOrTarget = true;
|
||||||
|
m_State = AiState::spawn;
|
||||||
m_Timer = 1.0f;
|
m_Timer = 1.0f;
|
||||||
m_StartPosition = parent->GetPosition();
|
m_StartPosition = parent->GetPosition();
|
||||||
m_MovementAI = nullptr;
|
m_MovementAI = nullptr;
|
||||||
@ -45,20 +46,20 @@ BaseCombatAIComponent::BaseCombatAIComponent(Entity* parent, const uint32_t id):
|
|||||||
auto componentResult = componentQuery.execQuery();
|
auto componentResult = componentQuery.execQuery();
|
||||||
|
|
||||||
if (!componentResult.eof()) {
|
if (!componentResult.eof()) {
|
||||||
if (!componentResult.fieldIsNull(0))
|
if (!componentResult.fieldIsNull("aggroRadius"))
|
||||||
m_AggroRadius = componentResult.getFloatField(0);
|
m_AggroRadius = componentResult.getFloatField("aggroRadius");
|
||||||
|
|
||||||
if (!componentResult.fieldIsNull(1))
|
if (!componentResult.fieldIsNull("tetherSpeed"))
|
||||||
m_TetherSpeed = componentResult.getFloatField(1);
|
m_TetherSpeed = componentResult.getFloatField("tetherSpeed");
|
||||||
|
|
||||||
if (!componentResult.fieldIsNull(2))
|
if (!componentResult.fieldIsNull("pursuitSpeed"))
|
||||||
m_PursuitSpeed = componentResult.getFloatField(2);
|
m_PursuitSpeed = componentResult.getFloatField("pursuitSpeed");
|
||||||
|
|
||||||
if (!componentResult.fieldIsNull(3))
|
if (!componentResult.fieldIsNull("softTetherRadius"))
|
||||||
m_SoftTetherRadius = componentResult.getFloatField(3);
|
m_SoftTetherRadius = componentResult.getFloatField("softTetherRadius");
|
||||||
|
|
||||||
if (!componentResult.fieldIsNull(4))
|
if (!componentResult.fieldIsNull("hardTetherRadius"))
|
||||||
m_HardTetherRadius = componentResult.getFloatField(4);
|
m_HardTetherRadius = componentResult.getFloatField("hardTetherRadius");
|
||||||
}
|
}
|
||||||
|
|
||||||
componentResult.finalize();
|
componentResult.finalize();
|
||||||
@ -82,11 +83,11 @@ BaseCombatAIComponent::BaseCombatAIComponent(Entity* parent, const uint32_t id):
|
|||||||
auto result = skillQuery.execQuery();
|
auto result = skillQuery.execQuery();
|
||||||
|
|
||||||
while (!result.eof()) {
|
while (!result.eof()) {
|
||||||
const auto skillId = static_cast<uint32_t>(result.getIntField(0));
|
const auto skillId = static_cast<uint32_t>(result.getIntField("skillID"));
|
||||||
|
|
||||||
const auto abilityCooldown = static_cast<float>(result.getFloatField(1));
|
const auto abilityCooldown = static_cast<float>(result.getFloatField("cooldown"));
|
||||||
|
|
||||||
const auto behaviorId = static_cast<uint32_t>(result.getIntField(2));
|
const auto behaviorId = static_cast<uint32_t>(result.getIntField("behaviorID"));
|
||||||
|
|
||||||
auto* behavior = Behavior::CreateBehavior(behaviorId);
|
auto* behavior = Behavior::CreateBehavior(behaviorId);
|
||||||
|
|
||||||
|
@ -450,7 +450,7 @@ const std::vector<BuffParameter>& BuffComponent::GetBuffParameters(int32_t buffI
|
|||||||
param.value = result.getFloatField("NumberValue");
|
param.value = result.getFloatField("NumberValue");
|
||||||
param.effectId = result.getIntField("EffectID");
|
param.effectId = result.getIntField("EffectID");
|
||||||
|
|
||||||
if (!result.fieldIsNull(3)) {
|
if (!result.fieldIsNull("StringValue")) {
|
||||||
std::istringstream stream(result.getStringField("StringValue"));
|
std::istringstream stream(result.getStringField("StringValue"));
|
||||||
std::string token;
|
std::string token;
|
||||||
|
|
||||||
|
@ -389,9 +389,9 @@ void DestroyableComponent::AddFaction(const int32_t factionID, const bool ignore
|
|||||||
|
|
||||||
if (result.eof()) return;
|
if (result.eof()) return;
|
||||||
|
|
||||||
if (result.fieldIsNull(0)) return;
|
if (result.fieldIsNull("enemyList")) return;
|
||||||
|
|
||||||
const auto* list_string = result.getStringField(0);
|
const auto* list_string = result.getStringField("enemyList");
|
||||||
|
|
||||||
std::stringstream ss(list_string);
|
std::stringstream ss(list_string);
|
||||||
std::string token;
|
std::string token;
|
||||||
|
@ -37,6 +37,9 @@
|
|||||||
#include "CDScriptComponentTable.h"
|
#include "CDScriptComponentTable.h"
|
||||||
#include "CDObjectSkillsTable.h"
|
#include "CDObjectSkillsTable.h"
|
||||||
#include "CDSkillBehaviorTable.h"
|
#include "CDSkillBehaviorTable.h"
|
||||||
|
#include "StringifiedEnum.h"
|
||||||
|
|
||||||
|
#include <ranges>
|
||||||
|
|
||||||
InventoryComponent::InventoryComponent(Entity* parent) : Component(parent) {
|
InventoryComponent::InventoryComponent(Entity* parent) : Component(parent) {
|
||||||
this->m_Dirty = true;
|
this->m_Dirty = true;
|
||||||
@ -492,6 +495,11 @@ void InventoryComponent::LoadXml(const tinyxml2::XMLDocument& document) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto* const groups = inventoryElement->FirstChildElement("grps");
|
||||||
|
if (groups) {
|
||||||
|
LoadGroupXml(*groups);
|
||||||
|
}
|
||||||
|
|
||||||
m_Consumable = inventoryElement->IntAttribute("csl", LOT_NULL);
|
m_Consumable = inventoryElement->IntAttribute("csl", LOT_NULL);
|
||||||
|
|
||||||
auto* bag = bags->FirstChildElement();
|
auto* bag = bags->FirstChildElement();
|
||||||
@ -558,19 +566,9 @@ void InventoryComponent::LoadXml(const tinyxml2::XMLDocument& document) {
|
|||||||
itemElement->QueryAttribute("parent", &parent);
|
itemElement->QueryAttribute("parent", &parent);
|
||||||
// End custom xml
|
// End custom xml
|
||||||
|
|
||||||
std::vector<LDFBaseData*> config;
|
auto* item = new Item(id, lot, inventory, slot, count, bound, {}, parent, subKey);
|
||||||
|
|
||||||
auto* extraInfo = itemElement->FirstChildElement("x");
|
item->LoadConfigXml(*itemElement);
|
||||||
|
|
||||||
if (extraInfo) {
|
|
||||||
std::string modInfo = extraInfo->Attribute("ma");
|
|
||||||
|
|
||||||
LDFBaseData* moduleAssembly = new LDFData<std::u16string>(u"assemblyPartLOTs", GeneralUtils::ASCIIToUTF16(modInfo.substr(2, modInfo.size() - 1)));
|
|
||||||
|
|
||||||
config.push_back(moduleAssembly);
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto* item = new Item(id, lot, inventory, slot, count, bound, config, parent, subKey);
|
|
||||||
|
|
||||||
if (equipped) {
|
if (equipped) {
|
||||||
const auto info = Inventory::FindItemComponent(lot);
|
const auto info = Inventory::FindItemComponent(lot);
|
||||||
@ -640,6 +638,15 @@ void InventoryComponent::UpdateXml(tinyxml2::XMLDocument& document) {
|
|||||||
bags->LinkEndChild(bag);
|
bags->LinkEndChild(bag);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto* groups = inventoryElement->FirstChildElement("grps");
|
||||||
|
if (groups) {
|
||||||
|
groups->DeleteChildren();
|
||||||
|
} else {
|
||||||
|
groups = inventoryElement->InsertNewChildElement("grps");
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateGroupXml(*groups);
|
||||||
|
|
||||||
auto* items = inventoryElement->FirstChildElement("items");
|
auto* items = inventoryElement->FirstChildElement("items");
|
||||||
|
|
||||||
if (items == nullptr) {
|
if (items == nullptr) {
|
||||||
@ -676,17 +683,7 @@ void InventoryComponent::UpdateXml(tinyxml2::XMLDocument& document) {
|
|||||||
itemElement->SetAttribute("parent", item->GetParent());
|
itemElement->SetAttribute("parent", item->GetParent());
|
||||||
// End custom xml
|
// End custom xml
|
||||||
|
|
||||||
for (auto* data : item->GetConfig()) {
|
item->SaveConfigXml(*itemElement);
|
||||||
if (data->GetKey() != u"assemblyPartLOTs") {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto* extraInfo = document.NewElement("x");
|
|
||||||
|
|
||||||
extraInfo->SetAttribute("ma", data->GetString(false).c_str());
|
|
||||||
|
|
||||||
itemElement->LinkEndChild(extraInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
bagElement->LinkEndChild(itemElement);
|
bagElement->LinkEndChild(itemElement);
|
||||||
}
|
}
|
||||||
@ -895,8 +892,6 @@ void InventoryComponent::UnEquipItem(Item* item) {
|
|||||||
|
|
||||||
RemoveSlot(item->GetInfo().equipLocation);
|
RemoveSlot(item->GetInfo().equipLocation);
|
||||||
|
|
||||||
PurgeProxies(item);
|
|
||||||
|
|
||||||
UnequipScripts(item);
|
UnequipScripts(item);
|
||||||
|
|
||||||
Game::entityManager->SerializeEntity(m_Parent);
|
Game::entityManager->SerializeEntity(m_Parent);
|
||||||
@ -906,6 +901,8 @@ void InventoryComponent::UnEquipItem(Item* item) {
|
|||||||
PropertyManagementComponent::Instance()->GetParent()->OnZonePropertyModelRemovedWhileEquipped(m_Parent);
|
PropertyManagementComponent::Instance()->GetParent()->OnZonePropertyModelRemovedWhileEquipped(m_Parent);
|
||||||
Game::zoneManager->GetZoneControlObject()->OnZonePropertyModelRemovedWhileEquipped(m_Parent);
|
Game::zoneManager->GetZoneControlObject()->OnZonePropertyModelRemovedWhileEquipped(m_Parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PurgeProxies(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1094,7 +1091,7 @@ void InventoryComponent::CheckItemSet(const LOT lot) {
|
|||||||
auto result = query.execQuery();
|
auto result = query.execQuery();
|
||||||
|
|
||||||
while (!result.eof()) {
|
while (!result.eof()) {
|
||||||
const auto id = result.getIntField(0);
|
const auto id = result.getIntField("setID");
|
||||||
|
|
||||||
bool found = false;
|
bool found = false;
|
||||||
|
|
||||||
@ -1525,10 +1522,10 @@ void InventoryComponent::PurgeProxies(Item* item) {
|
|||||||
const auto root = item->GetParent();
|
const auto root = item->GetParent();
|
||||||
|
|
||||||
if (root != LWOOBJID_EMPTY) {
|
if (root != LWOOBJID_EMPTY) {
|
||||||
item = FindItemById(root);
|
Item* itemRoot = FindItemById(root);
|
||||||
|
|
||||||
if (item != nullptr) {
|
if (itemRoot != nullptr) {
|
||||||
UnEquipItem(item);
|
UnEquipItem(itemRoot);
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
@ -1600,18 +1597,18 @@ void InventoryComponent::UpdatePetXml(tinyxml2::XMLDocument& document) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool InventoryComponent::SetSkill(int32_t slot, uint32_t skillId){
|
bool InventoryComponent::SetSkill(int32_t slot, uint32_t skillId) {
|
||||||
BehaviorSlot behaviorSlot = BehaviorSlot::Invalid;
|
BehaviorSlot behaviorSlot = BehaviorSlot::Invalid;
|
||||||
if (slot == 1 ) behaviorSlot = BehaviorSlot::Primary;
|
if (slot == 1) behaviorSlot = BehaviorSlot::Primary;
|
||||||
else if (slot == 2 ) behaviorSlot = BehaviorSlot::Offhand;
|
else if (slot == 2) behaviorSlot = BehaviorSlot::Offhand;
|
||||||
else if (slot == 3 ) behaviorSlot = BehaviorSlot::Neck;
|
else if (slot == 3) behaviorSlot = BehaviorSlot::Neck;
|
||||||
else if (slot == 4 ) behaviorSlot = BehaviorSlot::Head;
|
else if (slot == 4) behaviorSlot = BehaviorSlot::Head;
|
||||||
else if (slot == 5 ) behaviorSlot = BehaviorSlot::Consumable;
|
else if (slot == 5) behaviorSlot = BehaviorSlot::Consumable;
|
||||||
else return false;
|
else return false;
|
||||||
return SetSkill(behaviorSlot, skillId);
|
return SetSkill(behaviorSlot, skillId);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool InventoryComponent::SetSkill(BehaviorSlot slot, uint32_t skillId){
|
bool InventoryComponent::SetSkill(BehaviorSlot slot, uint32_t skillId) {
|
||||||
if (skillId == 0) return false;
|
if (skillId == 0) return false;
|
||||||
const auto index = m_Skills.find(slot);
|
const auto index = m_Skills.find(slot);
|
||||||
if (index != m_Skills.end()) {
|
if (index != m_Skills.end()) {
|
||||||
@ -1624,3 +1621,109 @@ bool InventoryComponent::SetSkill(BehaviorSlot slot, uint32_t skillId){
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void InventoryComponent::UpdateGroup(const GroupUpdate& groupUpdate) {
|
||||||
|
if (groupUpdate.groupId.empty()) return;
|
||||||
|
|
||||||
|
if (groupUpdate.inventory != eInventoryType::BRICKS && groupUpdate.inventory != eInventoryType::MODELS) {
|
||||||
|
LOG("Invalid inventory type for grouping %s", StringifiedEnum::ToString(groupUpdate.inventory).data());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto& groups = m_Groups[groupUpdate.inventory];
|
||||||
|
auto groupItr = std::ranges::find_if(groups, [&groupUpdate](const Group& group) {
|
||||||
|
return group.groupId == groupUpdate.groupId;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (groupUpdate.command != GroupUpdateCommand::ADD && groupItr == groups.end()) {
|
||||||
|
LOG("Group %s not found in inventory %s. Cannot process command.", groupUpdate.groupId.c_str(), StringifiedEnum::ToString(groupUpdate.inventory).data());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (groupUpdate.command == GroupUpdateCommand::ADD && groups.size() >= MaximumGroupCount) {
|
||||||
|
LOG("Cannot add group to inventory %s. Maximum group count reached.", StringifiedEnum::ToString(groupUpdate.inventory).data());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (groupUpdate.command) {
|
||||||
|
case GroupUpdateCommand::ADD: {
|
||||||
|
auto& group = groups.emplace_back();
|
||||||
|
group.groupId = groupUpdate.groupId;
|
||||||
|
group.groupName = groupUpdate.groupName;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case GroupUpdateCommand::ADD_LOT: {
|
||||||
|
groupItr->lots.insert(groupUpdate.lot);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case GroupUpdateCommand::REMOVE: {
|
||||||
|
groups.erase(groupItr);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case GroupUpdateCommand::REMOVE_LOT: {
|
||||||
|
groupItr->lots.erase(groupUpdate.lot);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case GroupUpdateCommand::MODIFY: {
|
||||||
|
groupItr->groupName = groupUpdate.groupName;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
LOG("Invalid group update command %i", groupUpdate.command);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void InventoryComponent::UpdateGroupXml(tinyxml2::XMLElement& groups) const {
|
||||||
|
for (const auto& [inventory, groupsData] : m_Groups) {
|
||||||
|
for (const auto& group : groupsData) {
|
||||||
|
auto* const groupElement = groups.InsertNewChildElement("grp");
|
||||||
|
|
||||||
|
groupElement->SetAttribute("id", group.groupId.c_str());
|
||||||
|
groupElement->SetAttribute("n", group.groupName.c_str());
|
||||||
|
groupElement->SetAttribute("t", static_cast<uint32_t>(inventory));
|
||||||
|
groupElement->SetAttribute("u", 0);
|
||||||
|
std::ostringstream lots;
|
||||||
|
bool first = true;
|
||||||
|
for (const auto lot : group.lots) {
|
||||||
|
if (!first) lots << ' ';
|
||||||
|
first = false;
|
||||||
|
|
||||||
|
lots << lot;
|
||||||
|
}
|
||||||
|
groupElement->SetAttribute("l", lots.str().c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void InventoryComponent::LoadGroupXml(const tinyxml2::XMLElement& groups) {
|
||||||
|
auto* groupElement = groups.FirstChildElement("grp");
|
||||||
|
|
||||||
|
while (groupElement) {
|
||||||
|
const char* groupId = nullptr;
|
||||||
|
const char* groupName = nullptr;
|
||||||
|
const char* lots = nullptr;
|
||||||
|
uint32_t inventory = eInventoryType::INVALID;
|
||||||
|
|
||||||
|
groupElement->QueryStringAttribute("id", &groupId);
|
||||||
|
groupElement->QueryStringAttribute("n", &groupName);
|
||||||
|
groupElement->QueryStringAttribute("l", &lots);
|
||||||
|
groupElement->QueryAttribute("t", &inventory);
|
||||||
|
|
||||||
|
if (!groupId || !groupName || !lots) {
|
||||||
|
LOG("Failed to load group from xml id %i name %i lots %i",
|
||||||
|
groupId == nullptr, groupName == nullptr, lots == nullptr);
|
||||||
|
} else {
|
||||||
|
auto& group = m_Groups[static_cast<eInventoryType>(inventory)].emplace_back();
|
||||||
|
group.groupId = groupId;
|
||||||
|
group.groupName = groupName;
|
||||||
|
|
||||||
|
for (const auto& lotStr : GeneralUtils::SplitString(lots, ' ')) {
|
||||||
|
auto lot = GeneralUtils::TryParse<LOT>(lotStr);
|
||||||
|
if (lot) group.lots.insert(*lot);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
groupElement = groupElement->NextSiblingElement("grp");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -37,6 +37,35 @@ enum class eItemType : int32_t;
|
|||||||
*/
|
*/
|
||||||
class InventoryComponent final : public Component {
|
class InventoryComponent final : public Component {
|
||||||
public:
|
public:
|
||||||
|
struct Group {
|
||||||
|
// Generated ID for the group. The ID is sent by the client and has the format user_group + Math.random() * UINT_MAX.
|
||||||
|
std::string groupId;
|
||||||
|
// Custom name assigned by the user.
|
||||||
|
std::string groupName;
|
||||||
|
// All the lots the user has in the group.
|
||||||
|
std::set<LOT> lots;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class GroupUpdateCommand {
|
||||||
|
ADD,
|
||||||
|
ADD_LOT,
|
||||||
|
MODIFY,
|
||||||
|
REMOVE,
|
||||||
|
REMOVE_LOT,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Based on the command, certain fields will be used or not used.
|
||||||
|
// for example, ADD_LOT wont use groupName, MODIFY wont use lots, etc.
|
||||||
|
struct GroupUpdate {
|
||||||
|
std::string groupId;
|
||||||
|
std::string groupName;
|
||||||
|
LOT lot;
|
||||||
|
eInventoryType inventory;
|
||||||
|
GroupUpdateCommand command;
|
||||||
|
};
|
||||||
|
|
||||||
|
static constexpr uint32_t MaximumGroupCount = 50;
|
||||||
|
|
||||||
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::INVENTORY;
|
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::INVENTORY;
|
||||||
InventoryComponent(Entity* parent);
|
InventoryComponent(Entity* parent);
|
||||||
|
|
||||||
@ -367,14 +396,23 @@ public:
|
|||||||
*/
|
*/
|
||||||
void UnequipScripts(Item* unequippedItem);
|
void UnequipScripts(Item* unequippedItem);
|
||||||
|
|
||||||
std::map<BehaviorSlot, uint32_t> GetSkills(){ return m_Skills; };
|
std::map<BehaviorSlot, uint32_t> GetSkills() { return m_Skills; };
|
||||||
|
|
||||||
bool SetSkill(int slot, uint32_t skillId);
|
bool SetSkill(int slot, uint32_t skillId);
|
||||||
bool SetSkill(BehaviorSlot slot, uint32_t skillId);
|
bool SetSkill(BehaviorSlot slot, uint32_t skillId);
|
||||||
|
|
||||||
|
void UpdateGroup(const GroupUpdate& groupUpdate);
|
||||||
|
void RemoveGroup(const std::string& groupId);
|
||||||
|
|
||||||
~InventoryComponent() override;
|
~InventoryComponent() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
/**
|
||||||
|
* The key is the inventory the group belongs to, the value maps' key is the id for the group.
|
||||||
|
* This is only used for bricks and model inventories.
|
||||||
|
*/
|
||||||
|
std::map<eInventoryType, std::vector<Group>> m_Groups{ { eInventoryType::BRICKS, {} }, { eInventoryType::MODELS, {} } };
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* All the inventory this entity possesses
|
* All the inventory this entity possesses
|
||||||
*/
|
*/
|
||||||
@ -477,6 +515,9 @@ private:
|
|||||||
* @param document the xml doc to load from
|
* @param document the xml doc to load from
|
||||||
*/
|
*/
|
||||||
void UpdatePetXml(tinyxml2::XMLDocument& document);
|
void UpdatePetXml(tinyxml2::XMLDocument& document);
|
||||||
|
|
||||||
|
void LoadGroupXml(const tinyxml2::XMLElement& groups);
|
||||||
|
void UpdateGroupXml(tinyxml2::XMLElement& groups) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -466,8 +466,8 @@ bool MissionComponent::RequiresItem(const LOT lot) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!result.fieldIsNull(0)) {
|
if (!result.fieldIsNull("type")) {
|
||||||
const auto type = std::string(result.getStringField(0));
|
const auto type = std::string(result.getStringField("type"));
|
||||||
|
|
||||||
result.finalize();
|
result.finalize();
|
||||||
|
|
||||||
|
@ -6,6 +6,9 @@
|
|||||||
|
|
||||||
#include "BehaviorStates.h"
|
#include "BehaviorStates.h"
|
||||||
#include "ControlBehaviorMsgs.h"
|
#include "ControlBehaviorMsgs.h"
|
||||||
|
#include "tinyxml2.h"
|
||||||
|
|
||||||
|
#include "Database.h"
|
||||||
|
|
||||||
ModelComponent::ModelComponent(Entity* parent) : Component(parent) {
|
ModelComponent::ModelComponent(Entity* parent) : Component(parent) {
|
||||||
m_OriginalPosition = m_Parent->GetDefaultPosition();
|
m_OriginalPosition = m_Parent->GetDefaultPosition();
|
||||||
@ -14,6 +17,33 @@ ModelComponent::ModelComponent(Entity* parent) : Component(parent) {
|
|||||||
m_userModelID = m_Parent->GetVarAs<LWOOBJID>(u"userModelID");
|
m_userModelID = m_Parent->GetVarAs<LWOOBJID>(u"userModelID");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ModelComponent::LoadBehaviors() {
|
||||||
|
auto behaviors = GeneralUtils::SplitString(m_Parent->GetVar<std::string>(u"userModelBehaviors"), ',');
|
||||||
|
for (const auto& behavior : behaviors) {
|
||||||
|
if (behavior.empty()) continue;
|
||||||
|
|
||||||
|
const auto behaviorId = GeneralUtils::TryParse<int32_t>(behavior);
|
||||||
|
if (!behaviorId.has_value() || behaviorId.value() == 0) continue;
|
||||||
|
|
||||||
|
LOG_DEBUG("Loading behavior %d", behaviorId.value());
|
||||||
|
auto& inserted = m_Behaviors.emplace_back();
|
||||||
|
inserted.SetBehaviorId(*behaviorId);
|
||||||
|
|
||||||
|
const auto behaviorStr = Database::Get()->GetBehavior(behaviorId.value());
|
||||||
|
|
||||||
|
tinyxml2::XMLDocument behaviorXml;
|
||||||
|
auto res = behaviorXml.Parse(behaviorStr.c_str(), behaviorStr.size());
|
||||||
|
LOG_DEBUG("Behavior %i %d: %s", res, behaviorId.value(), behaviorStr.c_str());
|
||||||
|
|
||||||
|
const auto* const behaviorRoot = behaviorXml.FirstChildElement("Behavior");
|
||||||
|
if (!behaviorRoot) {
|
||||||
|
LOG("Failed to load behavior %d due to missing behavior root", behaviorId.value());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
inserted.Deserialize(*behaviorRoot);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void ModelComponent::Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) {
|
void ModelComponent::Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) {
|
||||||
// ItemComponent Serialization. Pets do not get this serialization.
|
// ItemComponent Serialization. Pets do not get this serialization.
|
||||||
if (!m_Parent->HasComponent(eReplicaComponentType::PET)) {
|
if (!m_Parent->HasComponent(eReplicaComponentType::PET)) {
|
||||||
@ -72,3 +102,23 @@ void ModelComponent::MoveToInventory(MoveToInventoryMessage& msg) {
|
|||||||
m_Behaviors.erase(m_Behaviors.begin() + msg.GetBehaviorIndex());
|
m_Behaviors.erase(m_Behaviors.begin() + msg.GetBehaviorIndex());
|
||||||
// TODO move to the inventory
|
// TODO move to the inventory
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::array<std::pair<int32_t, std::string>, 5> ModelComponent::GetBehaviorsForSave() const {
|
||||||
|
std::array<std::pair<int32_t, std::string>, 5> toReturn{};
|
||||||
|
for (auto i = 0; i < m_Behaviors.size(); i++) {
|
||||||
|
const auto& behavior = m_Behaviors.at(i);
|
||||||
|
if (behavior.GetBehaviorId() == -1) continue;
|
||||||
|
auto& [id, behaviorData] = toReturn[i];
|
||||||
|
id = behavior.GetBehaviorId();
|
||||||
|
|
||||||
|
tinyxml2::XMLDocument doc;
|
||||||
|
auto* root = doc.NewElement("Behavior");
|
||||||
|
behavior.Serialize(*root);
|
||||||
|
doc.InsertFirstChild(root);
|
||||||
|
|
||||||
|
tinyxml2::XMLPrinter printer(0, true, 0);
|
||||||
|
doc.Print(&printer);
|
||||||
|
behaviorData = printer.CStr();
|
||||||
|
}
|
||||||
|
return toReturn;
|
||||||
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <array>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
|
||||||
#include "dCommonVars.h"
|
#include "dCommonVars.h"
|
||||||
@ -28,6 +29,8 @@ public:
|
|||||||
|
|
||||||
ModelComponent(Entity* parent);
|
ModelComponent(Entity* parent);
|
||||||
|
|
||||||
|
void LoadBehaviors();
|
||||||
|
|
||||||
void Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) override;
|
void Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) override;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -109,6 +112,8 @@ public:
|
|||||||
|
|
||||||
void VerifyBehaviors();
|
void VerifyBehaviors();
|
||||||
|
|
||||||
|
std::array<std::pair<int32_t, std::string>, 5> GetBehaviorsForSave() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/**
|
/**
|
||||||
* The behaviors of the model
|
* The behaviors of the model
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
#include "GameMessages.h"
|
#include "GameMessages.h"
|
||||||
#include "BrickDatabase.h"
|
#include "BrickDatabase.h"
|
||||||
#include "CDClientDatabase.h"
|
#include "CDClientDatabase.h"
|
||||||
|
#include "CDTamingBuildPuzzleTable.h"
|
||||||
#include "ChatPackets.h"
|
#include "ChatPackets.h"
|
||||||
#include "EntityManager.h"
|
#include "EntityManager.h"
|
||||||
#include "Character.h"
|
#include "Character.h"
|
||||||
@ -31,8 +32,9 @@
|
|||||||
#include "eGameMasterLevel.h"
|
#include "eGameMasterLevel.h"
|
||||||
#include "eMissionState.h"
|
#include "eMissionState.h"
|
||||||
#include "dNavMesh.h"
|
#include "dNavMesh.h"
|
||||||
|
#include "eGameActivity.h"
|
||||||
|
#include "eStateChangeType.h"
|
||||||
|
|
||||||
std::unordered_map<LOT, PetComponent::PetPuzzleData> PetComponent::buildCache{};
|
|
||||||
std::unordered_map<LWOOBJID, LWOOBJID> PetComponent::currentActivities{};
|
std::unordered_map<LWOOBJID, LWOOBJID> PetComponent::currentActivities{};
|
||||||
std::unordered_map<LWOOBJID, LWOOBJID> PetComponent::activePets{};
|
std::unordered_map<LWOOBJID, LWOOBJID> PetComponent::activePets{};
|
||||||
|
|
||||||
@ -40,7 +42,7 @@ std::unordered_map<LWOOBJID, LWOOBJID> PetComponent::activePets{};
|
|||||||
* Maps all the pet lots to a flag indicating that the player has caught it. All basic pets have been guessed by ObjID
|
* Maps all the pet lots to a flag indicating that the player has caught it. All basic pets have been guessed by ObjID
|
||||||
* while the faction ones could be checked using their respective missions.
|
* while the faction ones could be checked using their respective missions.
|
||||||
*/
|
*/
|
||||||
std::map<LOT, int32_t> PetComponent::petFlags = {
|
const std::map<LOT, int32_t> PetComponent::petFlags{
|
||||||
{ 3050, 801 }, // Elephant
|
{ 3050, 801 }, // Elephant
|
||||||
{ 3054, 803 }, // Cat
|
{ 3054, 803 }, // Cat
|
||||||
{ 3195, 806 }, // Triceratops
|
{ 3195, 806 }, // Triceratops
|
||||||
@ -87,7 +89,6 @@ PetComponent::PetComponent(Entity* parentEntity, uint32_t componentId) : Compone
|
|||||||
m_StartPosition = NiPoint3Constant::ZERO;
|
m_StartPosition = NiPoint3Constant::ZERO;
|
||||||
m_MovementAI = nullptr;
|
m_MovementAI = nullptr;
|
||||||
m_TresureTime = 0;
|
m_TresureTime = 0;
|
||||||
m_Preconditions = nullptr;
|
|
||||||
|
|
||||||
std::string checkPreconditions = GeneralUtils::UTF16ToWTF8(parentEntity->GetVar<std::u16string>(u"CheckPrecondition"));
|
std::string checkPreconditions = GeneralUtils::UTF16ToWTF8(parentEntity->GetVar<std::u16string>(u"CheckPrecondition"));
|
||||||
|
|
||||||
@ -152,96 +153,53 @@ void PetComponent::OnUse(Entity* originator) {
|
|||||||
m_Tamer = LWOOBJID_EMPTY;
|
m_Tamer = LWOOBJID_EMPTY;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto* inventoryComponent = originator->GetComponent<InventoryComponent>();
|
auto* const inventoryComponent = originator->GetComponent<InventoryComponent>();
|
||||||
|
|
||||||
if (inventoryComponent == nullptr) {
|
if (inventoryComponent == nullptr) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_Preconditions != nullptr && !m_Preconditions->Check(originator, true)) {
|
if (m_Preconditions.has_value() && !m_Preconditions->Check(originator, true)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto* movementAIComponent = m_Parent->GetComponent<MovementAIComponent>();
|
auto* const movementAIComponent = m_Parent->GetComponent<MovementAIComponent>();
|
||||||
|
|
||||||
if (movementAIComponent != nullptr) {
|
if (movementAIComponent != nullptr) {
|
||||||
movementAIComponent->Stop();
|
movementAIComponent->Stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
inventoryComponent->DespawnPet();
|
inventoryComponent->DespawnPet();
|
||||||
|
|
||||||
const auto& cached = buildCache.find(m_Parent->GetLOT());
|
const auto* const entry = CDClientManager::GetTable<CDTamingBuildPuzzleTable>()->GetByLOT(m_Parent->GetLOT());
|
||||||
int32_t imaginationCost = 0;
|
if (!entry) {
|
||||||
|
ChatPackets::SendSystemMessage(originator->GetSystemAddress(), u"Failed to find the puzzle minigame for this pet.");
|
||||||
std::string buildFile;
|
return;
|
||||||
|
|
||||||
if (cached == buildCache.end()) {
|
|
||||||
auto query = CDClientDatabase::CreatePreppedStmt(
|
|
||||||
"SELECT ValidPiecesLXF, PuzzleModelLot, Timelimit, NumValidPieces, imagCostPerBuild FROM TamingBuildPuzzles WHERE NPCLot = ?;");
|
|
||||||
query.bind(1, static_cast<int>(m_Parent->GetLOT()));
|
|
||||||
|
|
||||||
auto result = query.execQuery();
|
|
||||||
|
|
||||||
if (result.eof()) {
|
|
||||||
ChatPackets::SendSystemMessage(originator->GetSystemAddress(), u"Failed to find the puzzle minigame for this pet.");
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (result.fieldIsNull(0)) {
|
|
||||||
result.finalize();
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
buildFile = std::string(result.getStringField(0));
|
|
||||||
|
|
||||||
PetPuzzleData data;
|
|
||||||
data.buildFile = buildFile;
|
|
||||||
data.puzzleModelLot = result.getIntField(1);
|
|
||||||
data.timeLimit = result.getFloatField(2);
|
|
||||||
data.numValidPieces = result.getIntField(3);
|
|
||||||
data.imaginationCost = result.getIntField(4);
|
|
||||||
if (data.timeLimit <= 0) data.timeLimit = 60;
|
|
||||||
imaginationCost = data.imaginationCost;
|
|
||||||
|
|
||||||
buildCache[m_Parent->GetLOT()] = data;
|
|
||||||
|
|
||||||
result.finalize();
|
|
||||||
} else {
|
|
||||||
buildFile = cached->second.buildFile;
|
|
||||||
imaginationCost = cached->second.imaginationCost;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto* destroyableComponent = originator->GetComponent<DestroyableComponent>();
|
const auto* const destroyableComponent = originator->GetComponent<DestroyableComponent>();
|
||||||
|
|
||||||
if (destroyableComponent == nullptr) {
|
if (destroyableComponent == nullptr) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto imagination = destroyableComponent->GetImagination();
|
const auto imagination = destroyableComponent->GetImagination();
|
||||||
|
if (imagination < entry->imaginationCost) {
|
||||||
if (imagination < imaginationCost) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto& bricks = BrickDatabase::GetBricks(buildFile);
|
const auto& bricks = BrickDatabase::GetBricks(entry->validPieces);
|
||||||
|
|
||||||
if (bricks.empty()) {
|
if (bricks.empty()) {
|
||||||
ChatPackets::SendSystemMessage(originator->GetSystemAddress(), u"Failed to load the puzzle minigame for this pet.");
|
ChatPackets::SendSystemMessage(originator->GetSystemAddress(), u"Failed to load the puzzle minigame for this pet.");
|
||||||
LOG("Couldn't find %s for minigame!", buildFile.c_str());
|
LOG("Couldn't find %s for minigame!", entry->validPieces.c_str());
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto petPosition = m_Parent->GetPosition();
|
const auto petPosition = m_Parent->GetPosition();
|
||||||
|
|
||||||
auto originatorPosition = originator->GetPosition();
|
const auto originatorPosition = originator->GetPosition();
|
||||||
|
|
||||||
m_Parent->SetRotation(NiQuaternion::LookAt(petPosition, originatorPosition));
|
m_Parent->SetRotation(NiQuaternion::LookAt(petPosition, originatorPosition));
|
||||||
|
|
||||||
float interactionDistance = m_Parent->GetVar<float>(u"interaction_distance");
|
float interactionDistance = m_Parent->GetVar<float>(u"interaction_distance");
|
||||||
|
|
||||||
if (interactionDistance <= 0) {
|
if (interactionDistance <= 0) {
|
||||||
interactionDistance = 15;
|
interactionDistance = 15;
|
||||||
}
|
}
|
||||||
@ -254,24 +212,23 @@ void PetComponent::OnUse(Entity* originator) {
|
|||||||
if (dpWorld::IsLoaded()) {
|
if (dpWorld::IsLoaded()) {
|
||||||
NiPoint3 attempt = petPosition + forward * interactionDistance;
|
NiPoint3 attempt = petPosition + forward * interactionDistance;
|
||||||
|
|
||||||
float y = dpWorld::GetNavMesh()->GetHeightAtPoint(attempt);
|
NiPoint3 nearestPoint = dpWorld::GetNavMesh()->NearestPoint(attempt);
|
||||||
|
|
||||||
while (std::abs(y - petPosition.y) > 4 && interactionDistance > 10) {
|
while (std::abs(nearestPoint.y - petPosition.y) > 4 && interactionDistance > 10) {
|
||||||
const NiPoint3 forward = m_Parent->GetRotation().GetForwardVector();
|
const NiPoint3 forward = m_Parent->GetRotation().GetForwardVector();
|
||||||
|
|
||||||
attempt = originatorPosition + forward * interactionDistance;
|
attempt = originatorPosition + forward * interactionDistance;
|
||||||
|
|
||||||
y = dpWorld::GetNavMesh()->GetHeightAtPoint(attempt);
|
nearestPoint = dpWorld::GetNavMesh()->NearestPoint(attempt);
|
||||||
|
|
||||||
interactionDistance -= 0.5f;
|
interactionDistance -= 0.5f;
|
||||||
}
|
}
|
||||||
|
|
||||||
position = attempt;
|
position = nearestPoint;
|
||||||
} else {
|
} else {
|
||||||
position = petPosition + forward * interactionDistance;
|
position = petPosition + forward * interactionDistance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
auto rotation = NiQuaternion::LookAt(position, petPosition);
|
auto rotation = NiQuaternion::LookAt(position, petPosition);
|
||||||
|
|
||||||
GameMessages::SendNotifyPetTamingMinigame(
|
GameMessages::SendNotifyPetTamingMinigame(
|
||||||
@ -290,11 +247,11 @@ void PetComponent::OnUse(Entity* originator) {
|
|||||||
m_Parent->GetObjectID(),
|
m_Parent->GetObjectID(),
|
||||||
LWOOBJID_EMPTY,
|
LWOOBJID_EMPTY,
|
||||||
originator->GetObjectID(),
|
originator->GetObjectID(),
|
||||||
true,
|
false,
|
||||||
ePetTamingNotifyType::BEGIN,
|
ePetTamingNotifyType::BEGIN,
|
||||||
petPosition,
|
NiPoint3Constant::ZERO,
|
||||||
position,
|
NiPoint3Constant::ZERO,
|
||||||
rotation,
|
NiQuaternion(0.0f, 0.0f, 0.0f, 0.0f),
|
||||||
UNASSIGNED_SYSTEM_ADDRESS
|
UNASSIGNED_SYSTEM_ADDRESS
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -302,11 +259,18 @@ void PetComponent::OnUse(Entity* originator) {
|
|||||||
|
|
||||||
m_Tamer = originator->GetObjectID();
|
m_Tamer = originator->GetObjectID();
|
||||||
SetStatus(5);
|
SetStatus(5);
|
||||||
|
Game::entityManager->SerializeEntity(m_Parent);
|
||||||
|
|
||||||
currentActivities.insert_or_assign(m_Tamer, m_Parent->GetObjectID());
|
currentActivities.insert_or_assign(m_Tamer, m_Parent->GetObjectID());
|
||||||
|
|
||||||
// Notify the start of a pet taming minigame
|
// Notify the start of a pet taming minigame
|
||||||
m_Parent->GetScript()->OnNotifyPetTamingMinigame(m_Parent, originator, ePetTamingNotifyType::BEGIN);
|
m_Parent->GetScript()->OnNotifyPetTamingMinigame(m_Parent, originator, ePetTamingNotifyType::BEGIN);
|
||||||
|
|
||||||
|
auto* characterComponent = originator->GetComponent<CharacterComponent>();
|
||||||
|
if (characterComponent != nullptr) {
|
||||||
|
characterComponent->SetCurrentActivity(eGameActivity::PET_TAMING);
|
||||||
|
Game::entityManager->SerializeEntity(originator);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PetComponent::Update(float deltaTime) {
|
void PetComponent::Update(float deltaTime) {
|
||||||
@ -477,9 +441,8 @@ void PetComponent::TryBuild(uint32_t numBricks, bool clientFailed) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto& cached = buildCache.find(m_Parent->GetLOT());
|
const auto* const entry = CDClientManager::GetTable<CDTamingBuildPuzzleTable>()->GetByLOT(m_Parent->GetLOT());
|
||||||
|
if (!entry) return;
|
||||||
if (cached == buildCache.end()) return;
|
|
||||||
|
|
||||||
auto* destroyableComponent = tamer->GetComponent<DestroyableComponent>();
|
auto* destroyableComponent = tamer->GetComponent<DestroyableComponent>();
|
||||||
|
|
||||||
@ -487,14 +450,14 @@ void PetComponent::TryBuild(uint32_t numBricks, bool clientFailed) {
|
|||||||
|
|
||||||
auto imagination = destroyableComponent->GetImagination();
|
auto imagination = destroyableComponent->GetImagination();
|
||||||
|
|
||||||
imagination -= cached->second.imaginationCost;
|
imagination -= entry->imaginationCost;
|
||||||
|
|
||||||
destroyableComponent->SetImagination(imagination);
|
destroyableComponent->SetImagination(imagination);
|
||||||
|
|
||||||
Game::entityManager->SerializeEntity(tamer);
|
Game::entityManager->SerializeEntity(tamer);
|
||||||
|
|
||||||
if (clientFailed) {
|
if (clientFailed) {
|
||||||
if (imagination < cached->second.imaginationCost) {
|
if (imagination < entry->imaginationCost) {
|
||||||
ClientFailTamingMinigame();
|
ClientFailTamingMinigame();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -517,17 +480,14 @@ void PetComponent::NotifyTamingBuildSuccess(NiPoint3 position) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto& cached = buildCache.find(m_Parent->GetLOT());
|
const auto* const entry = CDClientManager::GetTable<CDTamingBuildPuzzleTable>()->GetByLOT(m_Parent->GetLOT());
|
||||||
|
if (!entry) return;
|
||||||
if (cached == buildCache.end()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
GameMessages::SendPlayFXEffect(tamer, -1, u"petceleb", "", LWOOBJID_EMPTY, 1, 1, true);
|
GameMessages::SendPlayFXEffect(tamer, -1, u"petceleb", "", LWOOBJID_EMPTY, 1, 1, true);
|
||||||
RenderComponent::PlayAnimation(tamer, u"rebuild-celebrate");
|
RenderComponent::PlayAnimation(tamer, u"rebuild-celebrate");
|
||||||
|
|
||||||
EntityInfo info{};
|
EntityInfo info{};
|
||||||
info.lot = cached->second.puzzleModelLot;
|
info.lot = entry->puzzleModelLot;
|
||||||
info.pos = position;
|
info.pos = position;
|
||||||
info.rot = NiQuaternionConstant::IDENTITY;
|
info.rot = NiQuaternionConstant::IDENTITY;
|
||||||
info.spawnerID = tamer->GetObjectID();
|
info.spawnerID = tamer->GetObjectID();
|
||||||
@ -675,6 +635,11 @@ void PetComponent::RequestSetPetName(std::u16string name) {
|
|||||||
UNASSIGNED_SYSTEM_ADDRESS
|
UNASSIGNED_SYSTEM_ADDRESS
|
||||||
);
|
);
|
||||||
|
|
||||||
|
auto* characterComponent = tamer->GetComponent<CharacterComponent>();
|
||||||
|
if (characterComponent != nullptr) {
|
||||||
|
characterComponent->SetCurrentActivity(eGameActivity::NONE);
|
||||||
|
Game::entityManager->SerializeEntity(tamer);
|
||||||
|
}
|
||||||
GameMessages::SendTerminateInteraction(m_Tamer, eTerminateType::FROM_INTERACTION, m_Parent->GetObjectID());
|
GameMessages::SendTerminateInteraction(m_Tamer, eTerminateType::FROM_INTERACTION, m_Parent->GetObjectID());
|
||||||
|
|
||||||
auto* modelEntity = Game::entityManager->GetEntity(m_ModelId);
|
auto* modelEntity = Game::entityManager->GetEntity(m_ModelId);
|
||||||
@ -714,6 +679,11 @@ void PetComponent::ClientExitTamingMinigame(bool voluntaryExit) {
|
|||||||
UNASSIGNED_SYSTEM_ADDRESS
|
UNASSIGNED_SYSTEM_ADDRESS
|
||||||
);
|
);
|
||||||
|
|
||||||
|
auto* characterComponent = tamer->GetComponent<CharacterComponent>();
|
||||||
|
if (characterComponent != nullptr) {
|
||||||
|
characterComponent->SetCurrentActivity(eGameActivity::NONE);
|
||||||
|
Game::entityManager->SerializeEntity(tamer);
|
||||||
|
}
|
||||||
GameMessages::SendNotifyTamingModelLoadedOnServer(m_Tamer, tamer->GetSystemAddress());
|
GameMessages::SendNotifyTamingModelLoadedOnServer(m_Tamer, tamer->GetSystemAddress());
|
||||||
|
|
||||||
GameMessages::SendTerminateInteraction(m_Tamer, eTerminateType::FROM_INTERACTION, m_Parent->GetObjectID());
|
GameMessages::SendTerminateInteraction(m_Tamer, eTerminateType::FROM_INTERACTION, m_Parent->GetObjectID());
|
||||||
@ -731,13 +701,10 @@ void PetComponent::ClientExitTamingMinigame(bool voluntaryExit) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void PetComponent::StartTimer() {
|
void PetComponent::StartTimer() {
|
||||||
const auto& cached = buildCache.find(m_Parent->GetLOT());
|
const auto* const entry = CDClientManager::GetTable<CDTamingBuildPuzzleTable>()->GetByLOT(m_Parent->GetLOT());
|
||||||
|
if (!entry) return;
|
||||||
|
|
||||||
if (cached == buildCache.end()) {
|
m_Timer = entry->timeLimit;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_Timer = cached->second.timeLimit;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void PetComponent::ClientFailTamingMinigame() {
|
void PetComponent::ClientFailTamingMinigame() {
|
||||||
@ -763,6 +730,11 @@ void PetComponent::ClientFailTamingMinigame() {
|
|||||||
UNASSIGNED_SYSTEM_ADDRESS
|
UNASSIGNED_SYSTEM_ADDRESS
|
||||||
);
|
);
|
||||||
|
|
||||||
|
auto* characterComponent = tamer->GetComponent<CharacterComponent>();
|
||||||
|
if (characterComponent != nullptr) {
|
||||||
|
characterComponent->SetCurrentActivity(eGameActivity::NONE);
|
||||||
|
Game::entityManager->SerializeEntity(tamer);
|
||||||
|
}
|
||||||
GameMessages::SendNotifyTamingModelLoadedOnServer(m_Tamer, tamer->GetSystemAddress());
|
GameMessages::SendNotifyTamingModelLoadedOnServer(m_Tamer, tamer->GetSystemAddress());
|
||||||
|
|
||||||
GameMessages::SendTerminateInteraction(m_Tamer, eTerminateType::FROM_INTERACTION, m_Parent->GetObjectID());
|
GameMessages::SendTerminateInteraction(m_Tamer, eTerminateType::FROM_INTERACTION, m_Parent->GetObjectID());
|
||||||
@ -823,8 +795,6 @@ void PetComponent::Wander() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void PetComponent::Activate(Item* item, bool registerPet, bool fromTaming) {
|
void PetComponent::Activate(Item* item, bool registerPet, bool fromTaming) {
|
||||||
AddDrainImaginationTimer(item, fromTaming);
|
|
||||||
|
|
||||||
m_ItemId = item->GetId();
|
m_ItemId = item->GetId();
|
||||||
m_DatabaseId = item->GetSubKey();
|
m_DatabaseId = item->GetSubKey();
|
||||||
|
|
||||||
@ -835,6 +805,7 @@ void PetComponent::Activate(Item* item, bool registerPet, bool fromTaming) {
|
|||||||
inventoryComponent->DespawnPet();
|
inventoryComponent->DespawnPet();
|
||||||
|
|
||||||
m_Owner = inventoryComponent->GetParent()->GetObjectID();
|
m_Owner = inventoryComponent->GetParent()->GetObjectID();
|
||||||
|
AddDrainImaginationTimer(fromTaming);
|
||||||
|
|
||||||
auto* owner = GetOwner();
|
auto* owner = GetOwner();
|
||||||
|
|
||||||
@ -887,17 +858,14 @@ void PetComponent::Activate(Item* item, bool registerPet, bool fromTaming) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PetComponent::AddDrainImaginationTimer(Item* item, bool fromTaming) {
|
void PetComponent::AddDrainImaginationTimer(bool fromTaming) {
|
||||||
if (Game::config->GetValue("pets_take_imagination") != "1") return;
|
if (Game::config->GetValue("pets_take_imagination") != "1") return;
|
||||||
|
|
||||||
auto playerInventory = item->GetInventory();
|
auto* playerEntity = Game::entityManager->GetEntity(m_Owner);
|
||||||
if (!playerInventory) return;
|
if (!playerEntity) {
|
||||||
|
LOG("owner was null or didnt exist!");
|
||||||
auto playerInventoryComponent = playerInventory->GetComponent();
|
return;
|
||||||
if (!playerInventoryComponent) return;
|
}
|
||||||
|
|
||||||
auto playerEntity = playerInventoryComponent->GetParent();
|
|
||||||
if (!playerEntity) return;
|
|
||||||
|
|
||||||
auto playerDestroyableComponent = playerEntity->GetComponent<DestroyableComponent>();
|
auto playerDestroyableComponent = playerEntity->GetComponent<DestroyableComponent>();
|
||||||
if (!playerDestroyableComponent) return;
|
if (!playerDestroyableComponent) return;
|
||||||
@ -906,12 +874,16 @@ void PetComponent::AddDrainImaginationTimer(Item* item, bool fromTaming) {
|
|||||||
if (!fromTaming) playerDestroyableComponent->Imagine(-1);
|
if (!fromTaming) playerDestroyableComponent->Imagine(-1);
|
||||||
|
|
||||||
// Set this to a variable so when this is called back from the player the timer doesn't fire off.
|
// Set this to a variable so when this is called back from the player the timer doesn't fire off.
|
||||||
m_Parent->AddCallbackTimer(m_PetInfo.imaginationDrainRate, [playerDestroyableComponent, this, item]() {
|
m_Parent->AddCallbackTimer(m_PetInfo.imaginationDrainRate, [this]() {
|
||||||
if (!playerDestroyableComponent) {
|
const auto* owner = Game::entityManager->GetEntity(m_Owner);
|
||||||
LOG("No petComponent and/or no playerDestroyableComponent");
|
if (!owner) {
|
||||||
|
LOG("owner was null or didnt exist!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const auto* playerDestroyableComponent = owner->GetComponent<DestroyableComponent>();
|
||||||
|
if (!playerDestroyableComponent) return;
|
||||||
|
|
||||||
// If we are out of imagination despawn the pet.
|
// If we are out of imagination despawn the pet.
|
||||||
if (playerDestroyableComponent->GetImagination() == 0) {
|
if (playerDestroyableComponent->GetImagination() == 0) {
|
||||||
this->Deactivate();
|
this->Deactivate();
|
||||||
@ -921,15 +893,13 @@ void PetComponent::AddDrainImaginationTimer(Item* item, bool fromTaming) {
|
|||||||
GameMessages::SendUseItemRequirementsResponse(playerEntity->GetObjectID(), playerEntity->GetSystemAddress(), eUseItemResponse::NoImaginationForPet);
|
GameMessages::SendUseItemRequirementsResponse(playerEntity->GetObjectID(), playerEntity->GetSystemAddress(), eUseItemResponse::NoImaginationForPet);
|
||||||
}
|
}
|
||||||
|
|
||||||
this->AddDrainImaginationTimer(item);
|
this->AddDrainImaginationTimer();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void PetComponent::Deactivate() {
|
void PetComponent::Deactivate() {
|
||||||
GameMessages::SendPlayFXEffect(m_Parent->GetObjectID(), -1, u"despawn", "", LWOOBJID_EMPTY, 1, 1, true);
|
GameMessages::SendPlayFXEffect(m_Parent->GetObjectID(), -1, u"despawn", "", LWOOBJID_EMPTY, 1, 1, true);
|
||||||
|
|
||||||
GameMessages::SendMarkInventoryItemAsActive(m_Owner, false, eUnequippableActiveType::PET, m_ItemId, GetOwner()->GetSystemAddress());
|
|
||||||
|
|
||||||
activePets.erase(m_Owner);
|
activePets.erase(m_Owner);
|
||||||
|
|
||||||
m_Parent->Kill();
|
m_Parent->Kill();
|
||||||
@ -938,6 +908,8 @@ void PetComponent::Deactivate() {
|
|||||||
|
|
||||||
if (owner == nullptr) return;
|
if (owner == nullptr) return;
|
||||||
|
|
||||||
|
GameMessages::SendMarkInventoryItemAsActive(m_Owner, false, eUnequippableActiveType::PET, m_ItemId, owner->GetSystemAddress());
|
||||||
|
|
||||||
GameMessages::SendAddPetToPlayer(m_Owner, 0, u"", LWOOBJID_EMPTY, LOT_NULL, owner->GetSystemAddress());
|
GameMessages::SendAddPetToPlayer(m_Owner, 0, u"", LWOOBJID_EMPTY, LOT_NULL, owner->GetSystemAddress());
|
||||||
|
|
||||||
GameMessages::SendRegisterPetID(m_Owner, LWOOBJID_EMPTY, owner->GetSystemAddress());
|
GameMessages::SendRegisterPetID(m_Owner, LWOOBJID_EMPTY, owner->GetSystemAddress());
|
||||||
@ -1062,6 +1034,7 @@ Entity* PetComponent::GetParentEntity() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
PetComponent::~PetComponent() {
|
PetComponent::~PetComponent() {
|
||||||
|
m_Owner = LWOOBJID_EMPTY;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PetComponent::SetPetNameForModeration(const std::string& petName) {
|
void PetComponent::SetPetNameForModeration(const std::string& petName) {
|
||||||
@ -1086,6 +1059,6 @@ void PetComponent::LoadPetNameFromModeration() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PetComponent::SetPreconditions(std::string& preconditions) {
|
void PetComponent::SetPreconditions(const std::string& preconditions) {
|
||||||
m_Preconditions = new PreconditionExpression(preconditions);
|
m_Preconditions = std::make_optional<PreconditionExpression>(preconditions);
|
||||||
}
|
}
|
||||||
|
@ -165,7 +165,7 @@ public:
|
|||||||
* Sets preconditions for the pet that need to be met before it can be tamed
|
* Sets preconditions for the pet that need to be met before it can be tamed
|
||||||
* @param conditions the preconditions to set
|
* @param conditions the preconditions to set
|
||||||
*/
|
*/
|
||||||
void SetPreconditions(std::string& conditions);
|
void SetPreconditions(const std::string& conditions);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the entity that this component belongs to
|
* Returns the entity that this component belongs to
|
||||||
@ -205,7 +205,7 @@ public:
|
|||||||
*
|
*
|
||||||
* @param item The item that represents this pet in the inventory.
|
* @param item The item that represents this pet in the inventory.
|
||||||
*/
|
*/
|
||||||
void AddDrainImaginationTimer(Item* item, bool fromTaming = false);
|
void AddDrainImaginationTimer(bool fromTaming = false);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
@ -250,15 +250,10 @@ private:
|
|||||||
*/
|
*/
|
||||||
static std::unordered_map<LWOOBJID, LWOOBJID> currentActivities;
|
static std::unordered_map<LWOOBJID, LWOOBJID> currentActivities;
|
||||||
|
|
||||||
/**
|
|
||||||
* Cache of all the minigames and their information from the database
|
|
||||||
*/
|
|
||||||
static std::unordered_map<LOT, PetComponent::PetPuzzleData> buildCache;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Flags that indicate that a player has tamed a pet, indexed by the LOT of the pet
|
* Flags that indicate that a player has tamed a pet, indexed by the LOT of the pet
|
||||||
*/
|
*/
|
||||||
static std::map<LOT, int32_t> petFlags;
|
static const std::map<LOT, int32_t> petFlags;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The ID of the component in the pet component table
|
* The ID of the component in the pet component table
|
||||||
@ -349,7 +344,7 @@ private:
|
|||||||
/**
|
/**
|
||||||
* Preconditions that need to be met before an entity can tame this pet
|
* Preconditions that need to be met before an entity can tame this pet
|
||||||
*/
|
*/
|
||||||
PreconditionExpression* m_Preconditions;
|
std::optional<PreconditionExpression> m_Preconditions{};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pet information loaded from the CDClientDatabase
|
* Pet information loaded from the CDClientDatabase
|
||||||
|
@ -47,7 +47,7 @@ PhantomPhysicsComponent::PhantomPhysicsComponent(Entity* parent) : PhysicsCompon
|
|||||||
m_Direction = NiPoint3(); // * m_DirectionalMultiplier
|
m_Direction = NiPoint3(); // * m_DirectionalMultiplier
|
||||||
|
|
||||||
if (m_Parent->GetVar<bool>(u"create_physics")) {
|
if (m_Parent->GetVar<bool>(u"create_physics")) {
|
||||||
CreatePhysics();
|
m_dpEntity = CreatePhysicsLnv(m_Scale, ComponentType);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_Parent->GetVar<bool>(u"respawnVol")) {
|
if (m_Parent->GetVar<bool>(u"respawnVol")) {
|
||||||
@ -89,105 +89,9 @@ PhantomPhysicsComponent::PhantomPhysicsComponent(Entity* parent) : PhysicsCompon
|
|||||||
m_RespawnRot = m_Rotation;
|
m_RespawnRot = m_Rotation;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
if (!m_dpEntity) {
|
||||||
for (LDFBaseData* data : settings) {
|
m_dpEntity = CreatePhysicsEntity(ComponentType);
|
||||||
if (data) {
|
if (!m_dpEntity) return;
|
||||||
if (data->GetKey() == u"create_physics") {
|
|
||||||
if (bool(std::stoi(data->GetValueAsString()))) {
|
|
||||||
CreatePhysics(settings);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data->GetKey() == u"respawnVol") {
|
|
||||||
if (bool(std::stoi(data->GetValueAsString()))) {
|
|
||||||
m_IsRespawnVolume = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_IsRespawnVolume) {
|
|
||||||
if (data->GetKey() == u"rspPos") {
|
|
||||||
//Joy, we get to split strings!
|
|
||||||
std::stringstream test(data->GetValueAsString());
|
|
||||||
std::string segment;
|
|
||||||
std::vector<std::string> seglist;
|
|
||||||
|
|
||||||
while (std::getline(test, segment, '\x1f')) {
|
|
||||||
seglist.push_back(segment);
|
|
||||||
}
|
|
||||||
|
|
||||||
m_RespawnPos = NiPoint3(std::stof(seglist[0]), std::stof(seglist[1]), std::stof(seglist[2]));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data->GetKey() == u"rspRot") {
|
|
||||||
//Joy, we get to split strings!
|
|
||||||
std::stringstream test(data->GetValueAsString());
|
|
||||||
std::string segment;
|
|
||||||
std::vector<std::string> seglist;
|
|
||||||
|
|
||||||
while (std::getline(test, segment, '\x1f')) {
|
|
||||||
seglist.push_back(segment);
|
|
||||||
}
|
|
||||||
|
|
||||||
m_RespawnRot = NiQuaternion(std::stof(seglist[0]), std::stof(seglist[1]), std::stof(seglist[2]), std::stof(seglist[3]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_Parent->GetLOT() == 4945) // HF - RespawnPoints
|
|
||||||
{
|
|
||||||
m_IsRespawnVolume = true;
|
|
||||||
m_RespawnPos = m_Position;
|
|
||||||
m_RespawnRot = m_Rotation;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
if (!m_HasCreatedPhysics) {
|
|
||||||
CDComponentsRegistryTable* compRegistryTable = CDClientManager::GetTable<CDComponentsRegistryTable>();
|
|
||||||
auto componentID = compRegistryTable->GetByIDAndType(m_Parent->GetLOT(), eReplicaComponentType::PHANTOM_PHYSICS);
|
|
||||||
|
|
||||||
CDPhysicsComponentTable* physComp = CDClientManager::GetTable<CDPhysicsComponentTable>();
|
|
||||||
|
|
||||||
if (physComp == nullptr) return;
|
|
||||||
|
|
||||||
auto* info = physComp->GetByID(componentID);
|
|
||||||
if (info == nullptr || info->physicsAsset == "" || info->physicsAsset == "NO_PHYSICS") return;
|
|
||||||
|
|
||||||
//temp test
|
|
||||||
if (info->physicsAsset == "miscellaneous\\misc_phys_10x1x5.hkx") {
|
|
||||||
m_dpEntity = new dpEntity(m_Parent->GetObjectID(), 10.0f, 5.0f, 1.0f);
|
|
||||||
} else if (info->physicsAsset == "miscellaneous\\misc_phys_640x640.hkx") {
|
|
||||||
// TODO Fix physics simulation to do simulation at high velocities due to bullet through paper problem...
|
|
||||||
m_dpEntity = new dpEntity(m_Parent->GetObjectID(), 1638.4f, 13.521004f * 2.0f, 1638.4f);
|
|
||||||
|
|
||||||
// Move this down by 13.521004 units so it is still effectively at the same height as before
|
|
||||||
m_Position = m_Position - NiPoint3Constant::UNIT_Y * 13.521004f;
|
|
||||||
} else if (info->physicsAsset == "env\\trigger_wall_tall.hkx") {
|
|
||||||
m_dpEntity = new dpEntity(m_Parent->GetObjectID(), 10.0f, 25.0f, 1.0f);
|
|
||||||
} else if (info->physicsAsset == "env\\env_gen_placeholderphysics.hkx") {
|
|
||||||
m_dpEntity = new dpEntity(m_Parent->GetObjectID(), 20.0f, 20.0f, 20.0f);
|
|
||||||
} else if (info->physicsAsset == "env\\POI_trigger_wall.hkx") {
|
|
||||||
m_dpEntity = new dpEntity(m_Parent->GetObjectID(), 1.0f, 12.5f, 20.0f); // Not sure what the real size is
|
|
||||||
} else if (info->physicsAsset == "env\\NG_NinjaGo\\env_ng_gen_gate_chamber_puzzle_ceiling_tile_falling_phantom.hkx") {
|
|
||||||
m_dpEntity = new dpEntity(m_Parent->GetObjectID(), 18.0f, 5.0f, 15.0f);
|
|
||||||
m_Position += m_Rotation.GetForwardVector() * 7.5f;
|
|
||||||
} else if (info->physicsAsset == "env\\NG_NinjaGo\\ng_flamejet_brick_phantom.HKX") {
|
|
||||||
m_dpEntity = new dpEntity(m_Parent->GetObjectID(), 1.0f, 1.0f, 12.0f);
|
|
||||||
m_Position += m_Rotation.GetForwardVector() * 6.0f;
|
|
||||||
} else if (info->physicsAsset == "env\\Ring_Trigger.hkx") {
|
|
||||||
m_dpEntity = new dpEntity(m_Parent->GetObjectID(), 6.0f, 6.0f, 6.0f);
|
|
||||||
} else if (info->physicsAsset == "env\\vfx_propertyImaginationBall.hkx") {
|
|
||||||
m_dpEntity = new dpEntity(m_Parent->GetObjectID(), 4.5f);
|
|
||||||
} else if (info->physicsAsset == "env\\env_won_fv_gas-blocking-volume.hkx") {
|
|
||||||
m_dpEntity = new dpEntity(m_Parent->GetObjectID(), 390.496826f, 111.467964f, 600.821534f, true);
|
|
||||||
m_Position.y -= (111.467964f * m_Scale) / 2;
|
|
||||||
} else {
|
|
||||||
// LOG_DEBUG("This one is supposed to have %s", info->physicsAsset.c_str());
|
|
||||||
|
|
||||||
//add fallback cube:
|
|
||||||
m_dpEntity = new dpEntity(m_Parent->GetObjectID(), 2.0f, 2.0f, 2.0f);
|
|
||||||
}
|
|
||||||
|
|
||||||
m_dpEntity->SetScale(m_Scale);
|
m_dpEntity->SetScale(m_Scale);
|
||||||
m_dpEntity->SetRotation(m_Rotation);
|
m_dpEntity->SetRotation(m_Rotation);
|
||||||
m_dpEntity->SetPosition(m_Position);
|
m_dpEntity->SetPosition(m_Position);
|
||||||
@ -201,69 +105,6 @@ PhantomPhysicsComponent::~PhantomPhysicsComponent() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PhantomPhysicsComponent::CreatePhysics() {
|
|
||||||
unsigned char alpha;
|
|
||||||
unsigned char red;
|
|
||||||
unsigned char green;
|
|
||||||
unsigned char blue;
|
|
||||||
int type = -1;
|
|
||||||
float x = 0.0f;
|
|
||||||
float y = 0.0f;
|
|
||||||
float z = 0.0f;
|
|
||||||
float width = 0.0f; //aka "radius"
|
|
||||||
float height = 0.0f;
|
|
||||||
|
|
||||||
if (m_Parent->HasVar(u"primitiveModelType")) {
|
|
||||||
type = m_Parent->GetVar<int32_t>(u"primitiveModelType");
|
|
||||||
x = m_Parent->GetVar<float>(u"primitiveModelValueX");
|
|
||||||
y = m_Parent->GetVar<float>(u"primitiveModelValueY");
|
|
||||||
z = m_Parent->GetVar<float>(u"primitiveModelValueZ");
|
|
||||||
} else {
|
|
||||||
CDComponentsRegistryTable* compRegistryTable = CDClientManager::GetTable<CDComponentsRegistryTable>();
|
|
||||||
auto componentID = compRegistryTable->GetByIDAndType(m_Parent->GetLOT(), eReplicaComponentType::PHANTOM_PHYSICS);
|
|
||||||
|
|
||||||
CDPhysicsComponentTable* physComp = CDClientManager::GetTable<CDPhysicsComponentTable>();
|
|
||||||
|
|
||||||
if (physComp == nullptr) return;
|
|
||||||
|
|
||||||
auto info = physComp->GetByID(componentID);
|
|
||||||
|
|
||||||
if (info == nullptr) return;
|
|
||||||
|
|
||||||
type = info->pcShapeType;
|
|
||||||
width = info->playerRadius;
|
|
||||||
height = info->playerHeight;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (type) {
|
|
||||||
case 1: { //Make a new box shape
|
|
||||||
NiPoint3 boxSize(x, y, z);
|
|
||||||
if (x == 0.0f) {
|
|
||||||
//LU has some weird values, so I think it's best to scale them down a bit
|
|
||||||
if (height < 0.5f) height = 2.0f;
|
|
||||||
if (width < 0.5f) width = 2.0f;
|
|
||||||
|
|
||||||
//Scale them:
|
|
||||||
width = width * m_Scale;
|
|
||||||
height = height * m_Scale;
|
|
||||||
|
|
||||||
boxSize = NiPoint3(width, height, width);
|
|
||||||
}
|
|
||||||
|
|
||||||
m_dpEntity = new dpEntity(m_Parent->GetObjectID(), boxSize);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!m_dpEntity) return;
|
|
||||||
|
|
||||||
m_dpEntity->SetPosition({ m_Position.x, m_Position.y - (height / 2), m_Position.z });
|
|
||||||
|
|
||||||
dpWorld::AddEntity(m_dpEntity);
|
|
||||||
|
|
||||||
m_HasCreatedPhysics = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void PhantomPhysicsComponent::Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) {
|
void PhantomPhysicsComponent::Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) {
|
||||||
PhysicsComponent::Serialize(outBitStream, bIsInitialUpdate);
|
PhysicsComponent::Serialize(outBitStream, bIsInitialUpdate);
|
||||||
|
|
||||||
@ -308,8 +149,9 @@ void ApplyCollisionEffect(const LWOOBJID& target, const ePhysicsEffectType effec
|
|||||||
controllablePhysicsComponent->SetGravityScale(effectScale);
|
controllablePhysicsComponent->SetGravityScale(effectScale);
|
||||||
GameMessages::SendSetGravityScale(target, effectScale, targetEntity->GetSystemAddress());
|
GameMessages::SendSetGravityScale(target, effectScale, targetEntity->GetSystemAddress());
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
// The other types are not handled by the server
|
|
||||||
case ePhysicsEffectType::ATTRACT:
|
case ePhysicsEffectType::ATTRACT:
|
||||||
case ePhysicsEffectType::FRICTION:
|
case ePhysicsEffectType::FRICTION:
|
||||||
case ePhysicsEffectType::PUSH:
|
case ePhysicsEffectType::PUSH:
|
||||||
@ -317,6 +159,7 @@ void ApplyCollisionEffect(const LWOOBJID& target, const ePhysicsEffectType effec
|
|||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
// The other types are not handled by the server and are here to handle all cases of the enum.
|
||||||
}
|
}
|
||||||
|
|
||||||
void PhantomPhysicsComponent::Update(float deltaTime) {
|
void PhantomPhysicsComponent::Update(float deltaTime) {
|
||||||
@ -356,24 +199,12 @@ void PhantomPhysicsComponent::SetDirection(const NiPoint3& pos) {
|
|||||||
m_IsDirectional = true;
|
m_IsDirectional = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PhantomPhysicsComponent::SpawnVertices() {
|
void PhantomPhysicsComponent::SpawnVertices() const {
|
||||||
if (!m_dpEntity) return;
|
if (!m_dpEntity) {
|
||||||
|
LOG("No dpEntity to spawn vertices for %llu:%i", m_Parent->GetObjectID(), m_Parent->GetLOT());
|
||||||
LOG("%llu", m_Parent->GetObjectID());
|
return;
|
||||||
auto box = static_cast<dpShapeBox*>(m_dpEntity->GetShape());
|
|
||||||
for (auto vert : box->GetVertices()) {
|
|
||||||
LOG("%f, %f, %f", vert.x, vert.y, vert.z);
|
|
||||||
|
|
||||||
EntityInfo info;
|
|
||||||
info.lot = 33;
|
|
||||||
info.pos = vert;
|
|
||||||
info.spawner = nullptr;
|
|
||||||
info.spawnerID = m_Parent->GetObjectID();
|
|
||||||
info.spawnerNodeID = 0;
|
|
||||||
|
|
||||||
Entity* newEntity = Game::entityManager->CreateEntity(info, nullptr);
|
|
||||||
Game::entityManager->ConstructEntity(newEntity);
|
|
||||||
}
|
}
|
||||||
|
PhysicsComponent::SpawnVertices(m_dpEntity);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PhantomPhysicsComponent::SetDirectionalMultiplier(float mul) {
|
void PhantomPhysicsComponent::SetDirectionalMultiplier(float mul) {
|
||||||
|
@ -18,6 +18,7 @@ class LDFBaseData;
|
|||||||
class Entity;
|
class Entity;
|
||||||
class dpEntity;
|
class dpEntity;
|
||||||
enum class ePhysicsEffectType : uint32_t ;
|
enum class ePhysicsEffectType : uint32_t ;
|
||||||
|
enum class eReplicaComponentType : uint32_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Allows the creation of phantom physics for an entity: a physics object that is generally invisible but can be
|
* Allows the creation of phantom physics for an entity: a physics object that is generally invisible but can be
|
||||||
@ -34,11 +35,6 @@ public:
|
|||||||
void Update(float deltaTime) override;
|
void Update(float deltaTime) override;
|
||||||
void Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) override;
|
void Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) override;
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates the physics shape for this entity based on LDF data
|
|
||||||
*/
|
|
||||||
void CreatePhysics();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the direction this physics object is pointed at
|
* Sets the direction this physics object is pointed at
|
||||||
* @param pos the direction to set
|
* @param pos the direction to set
|
||||||
@ -109,7 +105,7 @@ public:
|
|||||||
/**
|
/**
|
||||||
* Spawns an object at each of the vertices for debugging purposes
|
* Spawns an object at each of the vertices for debugging purposes
|
||||||
*/
|
*/
|
||||||
void SpawnVertices();
|
void SpawnVertices() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Legacy stuff no clue what this does
|
* Legacy stuff no clue what this does
|
||||||
@ -166,11 +162,6 @@ private:
|
|||||||
*/
|
*/
|
||||||
dpEntity* m_dpEntity;
|
dpEntity* m_dpEntity;
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether or not the physics object has been created yet
|
|
||||||
*/
|
|
||||||
bool m_HasCreatedPhysics = false;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether or not this physics object represents an object that updates the respawn pos of an entity that crosses it
|
* Whether or not this physics object represents an object that updates the respawn pos of an entity that crosses it
|
||||||
*/
|
*/
|
||||||
|
@ -1,5 +1,19 @@
|
|||||||
#include "PhysicsComponent.h"
|
#include "PhysicsComponent.h"
|
||||||
|
|
||||||
|
#include "eReplicaComponentType.h"
|
||||||
|
#include "NiPoint3.h"
|
||||||
|
#include "NiQuaternion.h"
|
||||||
|
|
||||||
|
#include "CDComponentsRegistryTable.h"
|
||||||
|
#include "CDPhysicsComponentTable.h"
|
||||||
|
|
||||||
|
#include "dpEntity.h"
|
||||||
|
#include "dpWorld.h"
|
||||||
|
#include "dpShapeBox.h"
|
||||||
|
#include "dpShapeSphere.h"
|
||||||
|
|
||||||
|
#include "EntityInfo.h"
|
||||||
|
|
||||||
PhysicsComponent::PhysicsComponent(Entity* parent) : Component(parent) {
|
PhysicsComponent::PhysicsComponent(Entity* parent) : Component(parent) {
|
||||||
m_Position = NiPoint3Constant::ZERO;
|
m_Position = NiPoint3Constant::ZERO;
|
||||||
m_Rotation = NiQuaternionConstant::IDENTITY;
|
m_Rotation = NiQuaternionConstant::IDENTITY;
|
||||||
@ -19,3 +33,190 @@ void PhysicsComponent::Serialize(RakNet::BitStream& outBitStream, bool bIsInitia
|
|||||||
if (!bIsInitialUpdate) m_DirtyPosition = false;
|
if (!bIsInitialUpdate) m_DirtyPosition = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dpEntity* PhysicsComponent::CreatePhysicsEntity(eReplicaComponentType type) {
|
||||||
|
CDComponentsRegistryTable* compRegistryTable = CDClientManager::GetTable<CDComponentsRegistryTable>();
|
||||||
|
auto componentID = compRegistryTable->GetByIDAndType(m_Parent->GetLOT(), type);
|
||||||
|
|
||||||
|
CDPhysicsComponentTable* physComp = CDClientManager::GetTable<CDPhysicsComponentTable>();
|
||||||
|
|
||||||
|
if (physComp == nullptr) return nullptr;
|
||||||
|
|
||||||
|
auto* info = physComp->GetByID(componentID);
|
||||||
|
if (info == nullptr || info->physicsAsset == "" || info->physicsAsset == "NO_PHYSICS") return nullptr;
|
||||||
|
|
||||||
|
dpEntity* toReturn;
|
||||||
|
if (info->physicsAsset == "miscellaneous\\misc_phys_10x1x5.hkx") {
|
||||||
|
toReturn = new dpEntity(m_Parent->GetObjectID(), 10.0f, 5.0f, 1.0f);
|
||||||
|
} else if (info->physicsAsset == "miscellaneous\\misc_phys_640x640.hkx") {
|
||||||
|
// TODO Fix physics simulation to do simulation at high velocities due to bullet through paper problem...
|
||||||
|
toReturn = new dpEntity(m_Parent->GetObjectID(), 1638.4f, 13.521004f * 2.0f, 1638.4f);
|
||||||
|
|
||||||
|
// Move this down by 13.521004 units so it is still effectively at the same height as before
|
||||||
|
m_Position = m_Position - NiPoint3Constant::UNIT_Y * 13.521004f;
|
||||||
|
} else if (info->physicsAsset == "env\\trigger_wall_tall.hkx") {
|
||||||
|
toReturn = new dpEntity(m_Parent->GetObjectID(), 10.0f, 25.0f, 1.0f);
|
||||||
|
} else if (info->physicsAsset == "env\\env_gen_placeholderphysics.hkx") {
|
||||||
|
toReturn = new dpEntity(m_Parent->GetObjectID(), 20.0f, 20.0f, 20.0f);
|
||||||
|
} else if (info->physicsAsset == "env\\POI_trigger_wall.hkx") {
|
||||||
|
toReturn = new dpEntity(m_Parent->GetObjectID(), 1.0f, 12.5f, 20.0f); // Not sure what the real size is
|
||||||
|
} else if (info->physicsAsset == "env\\NG_NinjaGo\\env_ng_gen_gate_chamber_puzzle_ceiling_tile_falling_phantom.hkx") {
|
||||||
|
toReturn = new dpEntity(m_Parent->GetObjectID(), 18.0f, 5.0f, 15.0f);
|
||||||
|
m_Position += m_Rotation.GetForwardVector() * 7.5f;
|
||||||
|
} else if (info->physicsAsset == "env\\NG_NinjaGo\\ng_flamejet_brick_phantom.HKX") {
|
||||||
|
toReturn = new dpEntity(m_Parent->GetObjectID(), 1.0f, 1.0f, 12.0f);
|
||||||
|
m_Position += m_Rotation.GetForwardVector() * 6.0f;
|
||||||
|
} else if (info->physicsAsset == "env\\Ring_Trigger.hkx") {
|
||||||
|
toReturn = new dpEntity(m_Parent->GetObjectID(), 6.0f, 6.0f, 6.0f);
|
||||||
|
} else if (info->physicsAsset == "env\\vfx_propertyImaginationBall.hkx") {
|
||||||
|
toReturn = new dpEntity(m_Parent->GetObjectID(), 4.5f);
|
||||||
|
} else if (info->physicsAsset == "env\\env_won_fv_gas-blocking-volume.hkx") {
|
||||||
|
toReturn = new dpEntity(m_Parent->GetObjectID(), 390.496826f, 111.467964f, 600.821534f, true);
|
||||||
|
m_Position.y -= (111.467964f * m_Parent->GetDefaultScale()) / 2;
|
||||||
|
} else {
|
||||||
|
// LOG_DEBUG("This one is supposed to have %s", info->physicsAsset.c_str());
|
||||||
|
|
||||||
|
//add fallback cube:
|
||||||
|
toReturn = new dpEntity(m_Parent->GetObjectID(), 2.0f, 2.0f, 2.0f);
|
||||||
|
}
|
||||||
|
return toReturn;
|
||||||
|
}
|
||||||
|
|
||||||
|
dpEntity* PhysicsComponent::CreatePhysicsLnv(const float scale, const eReplicaComponentType type) const {
|
||||||
|
int pcShapeType = -1;
|
||||||
|
float x = 0.0f;
|
||||||
|
float y = 0.0f;
|
||||||
|
float z = 0.0f;
|
||||||
|
float width = 0.0f; //aka "radius"
|
||||||
|
float height = 0.0f;
|
||||||
|
dpEntity* toReturn = nullptr;
|
||||||
|
|
||||||
|
if (m_Parent->HasVar(u"primitiveModelType")) {
|
||||||
|
pcShapeType = m_Parent->GetVar<int32_t>(u"primitiveModelType");
|
||||||
|
x = m_Parent->GetVar<float>(u"primitiveModelValueX");
|
||||||
|
y = m_Parent->GetVar<float>(u"primitiveModelValueY");
|
||||||
|
z = m_Parent->GetVar<float>(u"primitiveModelValueZ");
|
||||||
|
} else {
|
||||||
|
CDComponentsRegistryTable* compRegistryTable = CDClientManager::GetTable<CDComponentsRegistryTable>();
|
||||||
|
auto componentID = compRegistryTable->GetByIDAndType(m_Parent->GetLOT(), type);
|
||||||
|
|
||||||
|
CDPhysicsComponentTable* physComp = CDClientManager::GetTable<CDPhysicsComponentTable>();
|
||||||
|
|
||||||
|
if (physComp == nullptr) return nullptr;
|
||||||
|
|
||||||
|
auto info = physComp->GetByID(componentID);
|
||||||
|
|
||||||
|
if (info == nullptr) return nullptr;
|
||||||
|
|
||||||
|
pcShapeType = info->pcShapeType;
|
||||||
|
width = info->playerRadius;
|
||||||
|
height = info->playerHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (pcShapeType) {
|
||||||
|
case 0: { // HKX type
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 1: { //Make a new box shape
|
||||||
|
NiPoint3 boxSize(x, y, z);
|
||||||
|
if (x == 0.0f) {
|
||||||
|
//LU has some weird values, so I think it's best to scale them down a bit
|
||||||
|
if (height < 0.5f) height = 2.0f;
|
||||||
|
if (width < 0.5f) width = 2.0f;
|
||||||
|
|
||||||
|
//Scale them:
|
||||||
|
width = width * scale;
|
||||||
|
height = height * scale;
|
||||||
|
|
||||||
|
boxSize = NiPoint3(width, height, width);
|
||||||
|
}
|
||||||
|
|
||||||
|
toReturn = new dpEntity(m_Parent->GetObjectID(), boxSize);
|
||||||
|
|
||||||
|
toReturn->SetPosition({ m_Position.x, m_Position.y - (height / 2), m_Position.z });
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 2: { //Make a new cylinder shape
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 3: { //Make a new sphere shape
|
||||||
|
auto [x, y, z] = m_Position;
|
||||||
|
toReturn = new dpEntity(m_Parent->GetObjectID(), width);
|
||||||
|
toReturn->SetPosition({ x, y, z });
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 4: { //Make a new capsule shape
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (toReturn) dpWorld::AddEntity(toReturn);
|
||||||
|
|
||||||
|
return toReturn;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PhysicsComponent::SpawnVertices(dpEntity* entity) const {
|
||||||
|
if (!entity) return;
|
||||||
|
|
||||||
|
LOG("Spawning vertices for %llu", m_Parent->GetObjectID());
|
||||||
|
EntityInfo info;
|
||||||
|
info.lot = 33;
|
||||||
|
info.spawner = nullptr;
|
||||||
|
info.spawnerID = m_Parent->GetObjectID();
|
||||||
|
info.spawnerNodeID = 0;
|
||||||
|
|
||||||
|
// These don't use overloaded methods as dPhysics does not link with dGame at the moment.
|
||||||
|
auto box = dynamic_cast<dpShapeBox*>(entity->GetShape());
|
||||||
|
if (box) {
|
||||||
|
for (auto vert : box->GetVertices()) {
|
||||||
|
LOG("Vertex at %f, %f, %f", vert.x, vert.y, vert.z);
|
||||||
|
|
||||||
|
info.pos = vert;
|
||||||
|
Entity* newEntity = Game::entityManager->CreateEntity(info);
|
||||||
|
Game::entityManager->ConstructEntity(newEntity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
auto sphere = dynamic_cast<dpShapeSphere*>(entity->GetShape());
|
||||||
|
if (sphere) {
|
||||||
|
auto [x, y, z] = entity->GetPosition(); // Use shapes position instead of the parent's position in case it's different
|
||||||
|
float plusX = x + sphere->GetRadius();
|
||||||
|
float minusX = x - sphere->GetRadius();
|
||||||
|
float plusY = y + sphere->GetRadius();
|
||||||
|
float minusY = y - sphere->GetRadius();
|
||||||
|
float plusZ = z + sphere->GetRadius();
|
||||||
|
float minusZ = z - sphere->GetRadius();
|
||||||
|
|
||||||
|
auto radius = sphere->GetRadius();
|
||||||
|
LOG("Radius: %f", radius);
|
||||||
|
LOG("Plus Vertices %f %f %f", plusX, plusY, plusZ);
|
||||||
|
LOG("Minus Vertices %f %f %f", minusX, minusY, minusZ);
|
||||||
|
|
||||||
|
info.pos = NiPoint3{ x, plusY, z };
|
||||||
|
Entity* newEntity = Game::entityManager->CreateEntity(info);
|
||||||
|
Game::entityManager->ConstructEntity(newEntity);
|
||||||
|
|
||||||
|
info.pos = NiPoint3{ x, minusY, z };
|
||||||
|
newEntity = Game::entityManager->CreateEntity(info);
|
||||||
|
Game::entityManager->ConstructEntity(newEntity);
|
||||||
|
|
||||||
|
info.pos = NiPoint3{ plusX, y, z };
|
||||||
|
newEntity = Game::entityManager->CreateEntity(info);
|
||||||
|
Game::entityManager->ConstructEntity(newEntity);
|
||||||
|
|
||||||
|
info.pos = NiPoint3{ minusX, y, z };
|
||||||
|
newEntity = Game::entityManager->CreateEntity(info);
|
||||||
|
Game::entityManager->ConstructEntity(newEntity);
|
||||||
|
|
||||||
|
info.pos = NiPoint3{ x, y, plusZ };
|
||||||
|
newEntity = Game::entityManager->CreateEntity(info);
|
||||||
|
Game::entityManager->ConstructEntity(newEntity);
|
||||||
|
|
||||||
|
info.pos = NiPoint3{ x, y, minusZ };
|
||||||
|
newEntity = Game::entityManager->CreateEntity(info);
|
||||||
|
Game::entityManager->ConstructEntity(newEntity);
|
||||||
|
|
||||||
|
info.pos = NiPoint3{ x, y, z };
|
||||||
|
newEntity = Game::entityManager->CreateEntity(info);
|
||||||
|
Game::entityManager->ConstructEntity(newEntity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -9,6 +9,10 @@ namespace Raknet {
|
|||||||
class BitStream;
|
class BitStream;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class eReplicaComponentType : uint32_t;
|
||||||
|
|
||||||
|
class dpEntity;
|
||||||
|
|
||||||
class PhysicsComponent : public Component {
|
class PhysicsComponent : public Component {
|
||||||
public:
|
public:
|
||||||
PhysicsComponent(Entity* parent);
|
PhysicsComponent(Entity* parent);
|
||||||
@ -22,6 +26,12 @@ public:
|
|||||||
const NiQuaternion& GetRotation() const { return m_Rotation; }
|
const NiQuaternion& GetRotation() const { return m_Rotation; }
|
||||||
virtual void SetRotation(const NiQuaternion& rot) { if (m_Rotation == rot) return; m_Rotation = rot; m_DirtyPosition = true; }
|
virtual void SetRotation(const NiQuaternion& rot) { if (m_Rotation == rot) return; m_Rotation = rot; m_DirtyPosition = true; }
|
||||||
protected:
|
protected:
|
||||||
|
dpEntity* CreatePhysicsEntity(eReplicaComponentType type);
|
||||||
|
|
||||||
|
dpEntity* CreatePhysicsLnv(const float scale, const eReplicaComponentType type) const;
|
||||||
|
|
||||||
|
void SpawnVertices(dpEntity* entity) const;
|
||||||
|
|
||||||
NiPoint3 m_Position;
|
NiPoint3 m_Position;
|
||||||
|
|
||||||
NiQuaternion m_Rotation;
|
NiQuaternion m_Rotation;
|
||||||
|
@ -18,8 +18,8 @@ PossessableComponent::PossessableComponent(Entity* parent, uint32_t componentId)
|
|||||||
|
|
||||||
// Should a result not exist for this default to attached visible
|
// Should a result not exist for this default to attached visible
|
||||||
if (!result.eof()) {
|
if (!result.eof()) {
|
||||||
m_PossessionType = static_cast<ePossessionType>(result.getIntField(0, 1)); // Default to Attached Visible
|
m_PossessionType = static_cast<ePossessionType>(result.getIntField("possessionType", 1)); // Default to Attached Visible
|
||||||
m_DepossessOnHit = static_cast<bool>(result.getIntField(1, 0));
|
m_DepossessOnHit = static_cast<bool>(result.getIntField("depossessOnHit", 0));
|
||||||
} else {
|
} else {
|
||||||
m_PossessionType = ePossessionType::ATTACHED_VISIBLE;
|
m_PossessionType = ePossessionType::ATTACHED_VISIBLE;
|
||||||
m_DepossessOnHit = false;
|
m_DepossessOnHit = false;
|
||||||
|
@ -21,9 +21,11 @@
|
|||||||
#include "eObjectBits.h"
|
#include "eObjectBits.h"
|
||||||
#include "CharacterComponent.h"
|
#include "CharacterComponent.h"
|
||||||
#include "PlayerManager.h"
|
#include "PlayerManager.h"
|
||||||
|
#include "ModelComponent.h"
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include "CppScripts.h"
|
#include "CppScripts.h"
|
||||||
|
#include <ranges>
|
||||||
|
|
||||||
PropertyManagementComponent* PropertyManagementComponent::instance = nullptr;
|
PropertyManagementComponent* PropertyManagementComponent::instance = nullptr;
|
||||||
|
|
||||||
@ -49,11 +51,11 @@ PropertyManagementComponent::PropertyManagementComponent(Entity* parent) : Compo
|
|||||||
|
|
||||||
auto result = query.execQuery();
|
auto result = query.execQuery();
|
||||||
|
|
||||||
if (result.eof() || result.fieldIsNull(0)) {
|
if (result.eof() || result.fieldIsNull("id")) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
templateId = result.getIntField(0);
|
templateId = result.getIntField("id");
|
||||||
|
|
||||||
auto propertyInfo = Database::Get()->GetPropertyInfo(zoneId, cloneId);
|
auto propertyInfo = Database::Get()->GetPropertyInfo(zoneId, cloneId);
|
||||||
|
|
||||||
@ -105,7 +107,7 @@ std::vector<NiPoint3> PropertyManagementComponent::GetPaths() const {
|
|||||||
|
|
||||||
std::vector<float> points;
|
std::vector<float> points;
|
||||||
|
|
||||||
std::istringstream stream(result.getStringField(0));
|
std::istringstream stream(result.getStringField("path"));
|
||||||
std::string token;
|
std::string token;
|
||||||
|
|
||||||
while (std::getline(stream, token, ' ')) {
|
while (std::getline(stream, token, ' ')) {
|
||||||
@ -593,6 +595,20 @@ void PropertyManagementComponent::Load() {
|
|||||||
settings.push_back(new LDFData<int>(u"componentWhitelist", 1));
|
settings.push_back(new LDFData<int>(u"componentWhitelist", 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::ostringstream userModelBehavior;
|
||||||
|
bool firstAdded = false;
|
||||||
|
for (auto behavior : databaseModel.behaviors) {
|
||||||
|
if (behavior < 0) {
|
||||||
|
LOG("Invalid behavior ID: %d, removing behavior reference from model", behavior);
|
||||||
|
behavior = 0;
|
||||||
|
}
|
||||||
|
if (firstAdded) userModelBehavior << ",";
|
||||||
|
userModelBehavior << behavior;
|
||||||
|
firstAdded = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.push_back(new LDFData<std::string>(u"userModelBehaviors", userModelBehavior.str()));
|
||||||
|
|
||||||
node->config = settings;
|
node->config = settings;
|
||||||
|
|
||||||
const auto spawnerId = Game::zoneManager->MakeSpawner(info);
|
const auto spawnerId = Game::zoneManager->MakeSpawner(info);
|
||||||
@ -610,6 +626,12 @@ void PropertyManagementComponent::Save() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const auto* const owner = GetOwner();
|
||||||
|
if (!owner) return;
|
||||||
|
|
||||||
|
const auto* const character = owner->GetCharacter();
|
||||||
|
if (!character) return;
|
||||||
|
|
||||||
auto present = Database::Get()->GetPropertyModels(propertyId);
|
auto present = Database::Get()->GetPropertyModels(propertyId);
|
||||||
|
|
||||||
std::vector<LWOOBJID> modelIds;
|
std::vector<LWOOBJID> modelIds;
|
||||||
@ -624,6 +646,20 @@ void PropertyManagementComponent::Save() {
|
|||||||
if (entity == nullptr) {
|
if (entity == nullptr) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
auto* modelComponent = entity->GetComponent<ModelComponent>();
|
||||||
|
if (!modelComponent) continue;
|
||||||
|
const auto modelBehaviors = modelComponent->GetBehaviorsForSave();
|
||||||
|
|
||||||
|
// save the behaviors of the model
|
||||||
|
for (const auto& [behaviorId, behaviorStr] : modelBehaviors) {
|
||||||
|
if (behaviorStr.empty() || behaviorId == -1 || behaviorId == 0) continue;
|
||||||
|
IBehaviors::Info info {
|
||||||
|
.behaviorId = behaviorId,
|
||||||
|
.characterId = character->GetID(),
|
||||||
|
.behaviorInfo = behaviorStr
|
||||||
|
};
|
||||||
|
Database::Get()->AddBehavior(info);
|
||||||
|
}
|
||||||
|
|
||||||
const auto position = entity->GetPosition();
|
const auto position = entity->GetPosition();
|
||||||
const auto rotation = entity->GetRotation();
|
const auto rotation = entity->GetRotation();
|
||||||
@ -635,10 +671,13 @@ void PropertyManagementComponent::Save() {
|
|||||||
model.position = position;
|
model.position = position;
|
||||||
model.rotation = rotation;
|
model.rotation = rotation;
|
||||||
model.ugcId = 0;
|
model.ugcId = 0;
|
||||||
|
for (auto i = 0; i < model.behaviors.size(); i++) {
|
||||||
|
model.behaviors[i] = modelBehaviors[i].first;
|
||||||
|
}
|
||||||
|
|
||||||
Database::Get()->InsertNewPropertyModel(propertyId, model, "Objects_" + std::to_string(model.lot) + "_name");
|
Database::Get()->InsertNewPropertyModel(propertyId, model, "Objects_" + std::to_string(model.lot) + "_name");
|
||||||
} else {
|
} else {
|
||||||
Database::Get()->UpdateModelPositionRotation(id, position, rotation);
|
Database::Get()->UpdateModel(id, position, rotation, modelBehaviors);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,6 +25,7 @@
|
|||||||
#include "LeaderboardManager.h"
|
#include "LeaderboardManager.h"
|
||||||
#include "dZoneManager.h"
|
#include "dZoneManager.h"
|
||||||
#include "CDActivitiesTable.h"
|
#include "CDActivitiesTable.h"
|
||||||
|
#include "eStateChangeType.h"
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
|
|
||||||
#ifndef M_PI
|
#ifndef M_PI
|
||||||
@ -44,7 +45,6 @@ RacingControlComponent::RacingControlComponent(Entity* parent)
|
|||||||
m_LoadedPlayers = 0;
|
m_LoadedPlayers = 0;
|
||||||
m_LoadTimer = 0;
|
m_LoadTimer = 0;
|
||||||
m_Finished = 0;
|
m_Finished = 0;
|
||||||
m_StartTime = 0;
|
|
||||||
m_EmptyTimer = 0;
|
m_EmptyTimer = 0;
|
||||||
m_SoloRacing = Game::config->GetValue("solo_racing") == "1";
|
m_SoloRacing = Game::config->GetValue("solo_racing") == "1";
|
||||||
|
|
||||||
@ -77,6 +77,9 @@ void RacingControlComponent::OnPlayerLoaded(Entity* player) {
|
|||||||
|
|
||||||
m_LoadedPlayers++;
|
m_LoadedPlayers++;
|
||||||
|
|
||||||
|
// not live accurate to stun the player but prevents them from using skills during the race that are not meant to be used.
|
||||||
|
GameMessages::SendSetStunned(player->GetObjectID(), eStateChangeType::PUSH, player->GetSystemAddress(), LWOOBJID_EMPTY, true, true, true, true, true, true, true, true, true);
|
||||||
|
|
||||||
LOG("Loading player %i",
|
LOG("Loading player %i",
|
||||||
m_LoadedPlayers);
|
m_LoadedPlayers);
|
||||||
m_LobbyPlayers.push_back(player->GetObjectID());
|
m_LobbyPlayers.push_back(player->GetObjectID());
|
||||||
@ -394,25 +397,6 @@ void RacingControlComponent::HandleMessageBoxResponse(Entity* player, int32_t bu
|
|||||||
GameMessages::SendNotifyRacingClient(
|
GameMessages::SendNotifyRacingClient(
|
||||||
m_Parent->GetObjectID(), 2, 0, LWOOBJID_EMPTY, u"",
|
m_Parent->GetObjectID(), 2, 0, LWOOBJID_EMPTY, u"",
|
||||||
player->GetObjectID(), UNASSIGNED_SYSTEM_ADDRESS);
|
player->GetObjectID(), UNASSIGNED_SYSTEM_ADDRESS);
|
||||||
|
|
||||||
auto* missionComponent = player->GetComponent<MissionComponent>();
|
|
||||||
|
|
||||||
if (missionComponent == nullptr) return;
|
|
||||||
|
|
||||||
missionComponent->Progress(eMissionTaskType::RACING, 0, static_cast<LWOOBJID>(eRacingTaskParam::COMPETED_IN_RACE)); // Progress task for competing in a race
|
|
||||||
missionComponent->Progress(eMissionTaskType::RACING, data->smashedTimes, static_cast<LWOOBJID>(eRacingTaskParam::SAFE_DRIVER)); // Finish a race without being smashed.
|
|
||||||
|
|
||||||
// If solo racing is enabled OR if there are 3 players in the race, progress placement tasks.
|
|
||||||
if (m_SoloRacing || m_LoadedPlayers > 2) {
|
|
||||||
missionComponent->Progress(eMissionTaskType::RACING, data->finished, static_cast<LWOOBJID>(eRacingTaskParam::FINISH_WITH_PLACEMENT)); // Finish in 1st place on a race
|
|
||||||
if (data->finished == 1) {
|
|
||||||
missionComponent->Progress(eMissionTaskType::RACING, Game::zoneManager->GetZone()->GetWorldID(), static_cast<LWOOBJID>(eRacingTaskParam::FIRST_PLACE_MULTIPLE_TRACKS)); // Finish in 1st place on multiple tracks.
|
|
||||||
missionComponent->Progress(eMissionTaskType::RACING, Game::zoneManager->GetZone()->GetWorldID(), static_cast<LWOOBJID>(eRacingTaskParam::WIN_RACE_IN_WORLD)); // Finished first place in specific world.
|
|
||||||
}
|
|
||||||
if (data->finished == m_LoadedPlayers) {
|
|
||||||
missionComponent->Progress(eMissionTaskType::RACING, Game::zoneManager->GetZone()->GetWorldID(), static_cast<LWOOBJID>(eRacingTaskParam::LAST_PLACE_FINISH)); // Finished first place in specific world.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if ((id == "ACT_RACE_EXIT_THE_RACE?" || id == "Exit") && button == m_ActivityExitConfirm) {
|
} else if ((id == "ACT_RACE_EXIT_THE_RACE?" || id == "Exit") && button == m_ActivityExitConfirm) {
|
||||||
auto* vehicle = Game::entityManager->GetEntity(data->vehicleID);
|
auto* vehicle = Game::entityManager->GetEntity(data->vehicleID);
|
||||||
|
|
||||||
@ -442,9 +426,9 @@ void RacingControlComponent::Serialize(RakNet::BitStream& outBitStream, bool bIs
|
|||||||
outBitStream.Write(player.playerID);
|
outBitStream.Write(player.playerID);
|
||||||
|
|
||||||
outBitStream.Write(player.data[0]);
|
outBitStream.Write(player.data[0]);
|
||||||
if (player.finished != 0) outBitStream.Write<float>(player.raceTime);
|
if (player.finished != 0) outBitStream.Write<float>(player.raceTime.count() / 1000.0f);
|
||||||
else outBitStream.Write(player.data[1]);
|
else outBitStream.Write(player.data[1]);
|
||||||
if (player.finished != 0) outBitStream.Write<float>(player.bestLapTime);
|
if (player.finished != 0) outBitStream.Write<float>(player.bestLapTime.count() / 1000.0f);
|
||||||
else outBitStream.Write(player.data[2]);
|
else outBitStream.Write(player.data[2]);
|
||||||
if (player.finished == 1) outBitStream.Write<float>(1.0f);
|
if (player.finished == 1) outBitStream.Write<float>(1.0f);
|
||||||
else outBitStream.Write(player.data[3]);
|
else outBitStream.Write(player.data[3]);
|
||||||
@ -505,8 +489,8 @@ void RacingControlComponent::Serialize(RakNet::BitStream& outBitStream, bool bIs
|
|||||||
if (player.finished == 0) continue;
|
if (player.finished == 0) continue;
|
||||||
outBitStream.Write1(); // Has more data
|
outBitStream.Write1(); // Has more data
|
||||||
outBitStream.Write(player.playerID);
|
outBitStream.Write(player.playerID);
|
||||||
outBitStream.Write<float>(player.bestLapTime);
|
outBitStream.Write<float>(player.bestLapTime.count() / 1000.0f);
|
||||||
outBitStream.Write<float>(player.raceTime);
|
outBitStream.Write<float>(player.raceTime.count() / 1000.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
outBitStream.Write0(); // No more data
|
outBitStream.Write0(); // No more data
|
||||||
@ -736,7 +720,7 @@ void RacingControlComponent::Update(float deltaTime) {
|
|||||||
|
|
||||||
Game::entityManager->SerializeEntity(m_Parent);
|
Game::entityManager->SerializeEntity(m_Parent);
|
||||||
|
|
||||||
m_StartTime = std::time(nullptr);
|
m_StartTime = std::chrono::high_resolution_clock::now();
|
||||||
}
|
}
|
||||||
|
|
||||||
m_StartTimer += deltaTime;
|
m_StartTimer += deltaTime;
|
||||||
@ -817,52 +801,68 @@ void RacingControlComponent::Update(float deltaTime) {
|
|||||||
|
|
||||||
// Some offset up to make they don't fall through the terrain on a
|
// Some offset up to make they don't fall through the terrain on a
|
||||||
// respawn, seems to fix itself to the track anyhow
|
// respawn, seems to fix itself to the track anyhow
|
||||||
player.respawnPosition = position + NiPoint3Constant::UNIT_Y * 5;
|
if (waypoint.racing.isResetNode) {
|
||||||
player.respawnRotation = vehicle->GetRotation();
|
player.respawnPosition = position + NiPoint3Constant::UNIT_Y * 5;
|
||||||
|
player.respawnRotation = vehicle->GetRotation();
|
||||||
|
}
|
||||||
player.respawnIndex = respawnIndex;
|
player.respawnIndex = respawnIndex;
|
||||||
|
|
||||||
// Reached the start point, lapped
|
// Reached the start point, lapped
|
||||||
if (respawnIndex == 0) {
|
if (respawnIndex == 0) {
|
||||||
time_t lapTime = std::time(nullptr) - (player.lap == 0 ? m_StartTime : player.lapTime);
|
const auto now = std::chrono::high_resolution_clock::now();
|
||||||
|
const auto lapTime = std::chrono::duration_cast<std::chrono::milliseconds>(now - (player.lap == 0 ? m_StartTime : player.lapTime));
|
||||||
|
|
||||||
// Cheating check
|
// Cheating check
|
||||||
if (lapTime < 40) {
|
if (lapTime.count() < 40000) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
player.lap++;
|
player.lapTime = now;
|
||||||
|
|
||||||
player.lapTime = std::time(nullptr);
|
if (player.bestLapTime > lapTime || player.lap == 0) {
|
||||||
|
|
||||||
if (player.bestLapTime == 0 || player.bestLapTime > lapTime) {
|
|
||||||
player.bestLapTime = lapTime;
|
player.bestLapTime = lapTime;
|
||||||
|
|
||||||
LOG("Best lap time (%llu)", lapTime);
|
LOG("Best lap time (%llu)", lapTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
player.lap++;
|
||||||
|
|
||||||
auto* missionComponent =
|
auto* missionComponent =
|
||||||
playerEntity->GetComponent<MissionComponent>();
|
playerEntity->GetComponent<MissionComponent>();
|
||||||
|
|
||||||
if (missionComponent != nullptr) {
|
if (missionComponent != nullptr) {
|
||||||
|
|
||||||
// Progress lap time tasks
|
// Progress lap time tasks
|
||||||
missionComponent->Progress(eMissionTaskType::RACING, (lapTime) * 1000, static_cast<LWOOBJID>(eRacingTaskParam::LAP_TIME));
|
missionComponent->Progress(eMissionTaskType::RACING, lapTime.count(), static_cast<LWOOBJID>(eRacingTaskParam::LAP_TIME));
|
||||||
|
|
||||||
if (player.lap == 3) {
|
if (player.lap == 3) {
|
||||||
m_Finished++;
|
m_Finished++;
|
||||||
player.finished = m_Finished;
|
player.finished = m_Finished;
|
||||||
|
|
||||||
const auto raceTime =
|
const auto raceTime = std::chrono::duration_cast<std::chrono::milliseconds>(now - m_StartTime);
|
||||||
(std::time(nullptr) - m_StartTime);
|
|
||||||
|
|
||||||
player.raceTime = raceTime;
|
player.raceTime = raceTime;
|
||||||
|
|
||||||
LOG("Completed time %llu, %llu",
|
LOG("Completed time %llums %fs", raceTime.count(), raceTime.count() / 1000.0f);
|
||||||
raceTime, raceTime * 1000);
|
|
||||||
|
|
||||||
LeaderboardManager::SaveScore(playerEntity->GetObjectID(), m_ActivityID, static_cast<float>(player.raceTime), static_cast<float>(player.bestLapTime), static_cast<float>(player.finished == 1));
|
LeaderboardManager::SaveScore(playerEntity->GetObjectID(), m_ActivityID, static_cast<float>(player.raceTime.count()) / 1000, static_cast<float>(player.bestLapTime.count()) / 1000, static_cast<float>(player.finished == 1));
|
||||||
// Entire race time
|
// Entire race time
|
||||||
missionComponent->Progress(eMissionTaskType::RACING, (raceTime) * 1000, static_cast<LWOOBJID>(eRacingTaskParam::TOTAL_TRACK_TIME));
|
missionComponent->Progress(eMissionTaskType::RACING, player.raceTime.count(), static_cast<LWOOBJID>(eRacingTaskParam::TOTAL_TRACK_TIME));
|
||||||
|
|
||||||
|
missionComponent->Progress(eMissionTaskType::RACING, 0, static_cast<LWOOBJID>(eRacingTaskParam::COMPETED_IN_RACE)); // Progress task for competing in a race
|
||||||
|
missionComponent->Progress(eMissionTaskType::RACING, player.smashedTimes, static_cast<LWOOBJID>(eRacingTaskParam::SAFE_DRIVER)); // Finish a race without being smashed.
|
||||||
|
|
||||||
|
// If solo racing is enabled OR if there are 3 players in the race, progress placement tasks.
|
||||||
|
if (m_SoloRacing || m_RacingPlayers.size() > 2) {
|
||||||
|
missionComponent->Progress(eMissionTaskType::RACING, player.finished, static_cast<LWOOBJID>(eRacingTaskParam::FINISH_WITH_PLACEMENT)); // Finish in 1st place on a race
|
||||||
|
if (player.finished == 1) {
|
||||||
|
missionComponent->Progress(eMissionTaskType::RACING, Game::zoneManager->GetZone()->GetWorldID(), static_cast<LWOOBJID>(eRacingTaskParam::FIRST_PLACE_MULTIPLE_TRACKS)); // Finish in 1st place on multiple tracks.
|
||||||
|
missionComponent->Progress(eMissionTaskType::RACING, Game::zoneManager->GetZone()->GetWorldID(), static_cast<LWOOBJID>(eRacingTaskParam::WIN_RACE_IN_WORLD)); // Finished first place in specific world.
|
||||||
|
}
|
||||||
|
if (player.finished == m_RacingPlayers.size()) {
|
||||||
|
missionComponent->Progress(eMissionTaskType::RACING, Game::zoneManager->GetZone()->GetWorldID(), static_cast<LWOOBJID>(eRacingTaskParam::LAST_PLACE_FINISH)); // Finished first place in specific world.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
auto* characterComponent = playerEntity->GetComponent<CharacterComponent>();
|
auto* characterComponent = playerEntity->GetComponent<CharacterComponent>();
|
||||||
if (characterComponent != nullptr) {
|
if (characterComponent != nullptr) {
|
||||||
@ -871,8 +871,8 @@ void RacingControlComponent::Update(float deltaTime) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG("Lapped (%i) in (%llu)", player.lap,
|
LOG("Lapped (%i) in (%llums %fs)", player.lap,
|
||||||
lapTime);
|
lapTime.count(), lapTime.count() / 1000.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG("Reached point (%i)/(%i)", player.respawnIndex,
|
LOG("Reached point (%i)/(%i)", player.respawnIndex,
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
#include "Entity.h"
|
#include "Entity.h"
|
||||||
#include "Component.h"
|
#include "Component.h"
|
||||||
#include "eReplicaComponentType.h"
|
#include "eReplicaComponentType.h"
|
||||||
|
#include <chrono>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Information for each player in the race
|
* Information for each player in the race
|
||||||
@ -72,12 +73,12 @@ struct RacingPlayerInfo {
|
|||||||
/**
|
/**
|
||||||
* The fastest lap time of the player
|
* The fastest lap time of the player
|
||||||
*/
|
*/
|
||||||
time_t bestLapTime = 0;
|
std::chrono::milliseconds bestLapTime;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The current lap time of the player
|
* The current lap time of the player
|
||||||
*/
|
*/
|
||||||
time_t lapTime = 0;
|
std::chrono::high_resolution_clock::time_point lapTime;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The number of times this player smashed their car
|
* The number of times this player smashed their car
|
||||||
@ -97,7 +98,7 @@ struct RacingPlayerInfo {
|
|||||||
/**
|
/**
|
||||||
* Unused
|
* Unused
|
||||||
*/
|
*/
|
||||||
time_t raceTime = 0;
|
std::chrono::milliseconds raceTime;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -231,7 +232,7 @@ private:
|
|||||||
/**
|
/**
|
||||||
* The time the race was started
|
* The time the race was started
|
||||||
*/
|
*/
|
||||||
time_t m_StartTime;
|
std::chrono::high_resolution_clock::time_point m_StartTime;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Timer for tracking how long a player was alone in this race
|
* Timer for tracking how long a player was alone in this race
|
||||||
|
@ -117,7 +117,7 @@ void RenderComponent::PlayEffect(const int32_t effectId, const std::u16string& e
|
|||||||
|
|
||||||
auto result = query.execQuery();
|
auto result = query.execQuery();
|
||||||
|
|
||||||
if (result.eof() || result.fieldIsNull(0)) {
|
if (result.eof() || result.fieldIsNull("animation_length")) {
|
||||||
result.finalize();
|
result.finalize();
|
||||||
|
|
||||||
m_DurationCache[effectId] = 0;
|
m_DurationCache[effectId] = 0;
|
||||||
@ -127,7 +127,7 @@ void RenderComponent::PlayEffect(const int32_t effectId, const std::u16string& e
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
effect.time = static_cast<float>(result.getFloatField(0));
|
effect.time = static_cast<float>(result.getFloatField("animation_length"));
|
||||||
|
|
||||||
result.finalize();
|
result.finalize();
|
||||||
|
|
||||||
|
@ -1,16 +1,57 @@
|
|||||||
/*
|
// Darkflame Universe
|
||||||
* Darkflame Universe
|
// Copyright 2024
|
||||||
* Copyright 2023
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "RigidbodyPhantomPhysicsComponent.h"
|
#include "RigidbodyPhantomPhysicsComponent.h"
|
||||||
#include "Entity.h"
|
#include "Entity.h"
|
||||||
|
|
||||||
|
#include "dpEntity.h"
|
||||||
|
#include "CDComponentsRegistryTable.h"
|
||||||
|
#include "CDPhysicsComponentTable.h"
|
||||||
|
#include "dpWorld.h"
|
||||||
|
#include "dpShapeBox.h"
|
||||||
|
#include "dpShapeSphere.h"
|
||||||
|
#include"EntityInfo.h"
|
||||||
|
|
||||||
RigidbodyPhantomPhysicsComponent::RigidbodyPhantomPhysicsComponent(Entity* parent) : PhysicsComponent(parent) {
|
RigidbodyPhantomPhysicsComponent::RigidbodyPhantomPhysicsComponent(Entity* parent) : PhysicsComponent(parent) {
|
||||||
m_Position = m_Parent->GetDefaultPosition();
|
m_Position = m_Parent->GetDefaultPosition();
|
||||||
m_Rotation = m_Parent->GetDefaultRotation();
|
m_Rotation = m_Parent->GetDefaultRotation();
|
||||||
|
m_Scale = m_Parent->GetDefaultScale();
|
||||||
|
|
||||||
|
if (m_Parent->GetVar<bool>(u"create_physics")) {
|
||||||
|
m_dpEntity = CreatePhysicsLnv(m_Scale, ComponentType);
|
||||||
|
if (!m_dpEntity) {
|
||||||
|
m_dpEntity = CreatePhysicsEntity(ComponentType);
|
||||||
|
if (!m_dpEntity) return;
|
||||||
|
m_dpEntity->SetScale(m_Scale);
|
||||||
|
m_dpEntity->SetRotation(m_Rotation);
|
||||||
|
m_dpEntity->SetPosition(m_Position);
|
||||||
|
dpWorld::AddEntity(m_dpEntity);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RigidbodyPhantomPhysicsComponent::Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) {
|
void RigidbodyPhantomPhysicsComponent::Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) {
|
||||||
PhysicsComponent::Serialize(outBitStream, bIsInitialUpdate);
|
PhysicsComponent::Serialize(outBitStream, bIsInitialUpdate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RigidbodyPhantomPhysicsComponent::Update(const float deltaTime) {
|
||||||
|
if (!m_dpEntity) return;
|
||||||
|
|
||||||
|
//Process enter events
|
||||||
|
for (const auto id : m_dpEntity->GetNewObjects()) {
|
||||||
|
m_Parent->OnCollisionPhantom(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Process exit events
|
||||||
|
for (const auto id : m_dpEntity->GetRemovedObjects()) {
|
||||||
|
m_Parent->OnCollisionLeavePhantom(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RigidbodyPhantomPhysicsComponent::SpawnVertices() const {
|
||||||
|
if (!m_dpEntity) {
|
||||||
|
LOG("No dpEntity to spawn vertices for %llu:%i", m_Parent->GetObjectID(), m_Parent->GetLOT());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
PhysicsComponent::SpawnVertices(m_dpEntity);
|
||||||
|
}
|
||||||
|
@ -1,10 +1,8 @@
|
|||||||
/*
|
// Darkflame Universe
|
||||||
* Darkflame Universe
|
// Copyright 2024
|
||||||
* Copyright 2023
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef __RIGIDBODYPHANTOMPHYSICS_H__
|
#ifndef RIGIDBODYPHANTOMPHYSICS_H
|
||||||
#define __RIGIDBODYPHANTOMPHYSICS_H__
|
#define RIGIDBODYPHANTOMPHYSICS_H
|
||||||
|
|
||||||
#include "BitStream.h"
|
#include "BitStream.h"
|
||||||
#include "dCommonVars.h"
|
#include "dCommonVars.h"
|
||||||
@ -13,6 +11,8 @@
|
|||||||
#include "PhysicsComponent.h"
|
#include "PhysicsComponent.h"
|
||||||
#include "eReplicaComponentType.h"
|
#include "eReplicaComponentType.h"
|
||||||
|
|
||||||
|
class dpEntity;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component that handles rigid bodies that can be interacted with, mostly client-side rendered. An example is the
|
* Component that handles rigid bodies that can be interacted with, mostly client-side rendered. An example is the
|
||||||
* bananas that fall from trees in GF.
|
* bananas that fall from trees in GF.
|
||||||
@ -23,7 +23,15 @@ public:
|
|||||||
|
|
||||||
RigidbodyPhantomPhysicsComponent(Entity* parent);
|
RigidbodyPhantomPhysicsComponent(Entity* parent);
|
||||||
|
|
||||||
|
void Update(const float deltaTime) override;
|
||||||
|
|
||||||
void Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) override;
|
void Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) override;
|
||||||
|
|
||||||
|
void SpawnVertices() const;
|
||||||
|
private:
|
||||||
|
float m_Scale{};
|
||||||
|
|
||||||
|
dpEntity* m_dpEntity{};
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // __RIGIDBODYPHANTOMPHYSICS_H__
|
#endif // RIGIDBODYPHANTOMPHYSICS_H
|
||||||
|
@ -27,12 +27,12 @@ RocketLaunchpadControlComponent::RocketLaunchpadControlComponent(Entity* parent,
|
|||||||
|
|
||||||
auto result = query.execQuery();
|
auto result = query.execQuery();
|
||||||
|
|
||||||
if (!result.eof() && !result.fieldIsNull(0)) {
|
if (!result.eof() && !result.fieldIsNull("targetZone")) {
|
||||||
m_TargetZone = result.getIntField(0);
|
m_TargetZone = result.getIntField("targetZone");
|
||||||
m_DefaultZone = result.getIntField(1);
|
m_DefaultZone = result.getIntField("defaultZoneID");
|
||||||
m_TargetScene = result.getStringField(2);
|
m_TargetScene = result.getStringField("targetScene");
|
||||||
m_AltPrecondition = new PreconditionExpression(result.getStringField(3));
|
m_AltPrecondition = new PreconditionExpression(result.getStringField("altLandingPrecondition"));
|
||||||
m_AltLandingScene = result.getStringField(4);
|
m_AltLandingScene = result.getStringField("altLandingSpawnPointName");
|
||||||
}
|
}
|
||||||
|
|
||||||
result.finalize();
|
result.finalize();
|
||||||
|
@ -38,7 +38,7 @@ bool SkillComponent::CastPlayerSkill(const uint32_t behaviorId, const uint32_t s
|
|||||||
|
|
||||||
context->skillID = skillID;
|
context->skillID = skillID;
|
||||||
|
|
||||||
this->m_managedBehaviors.insert_or_assign(skillUid, context);
|
this->m_managedBehaviors.insert({ skillUid, context });
|
||||||
|
|
||||||
auto* behavior = Behavior::CreateBehavior(behaviorId);
|
auto* behavior = Behavior::CreateBehavior(behaviorId);
|
||||||
|
|
||||||
@ -52,17 +52,24 @@ bool SkillComponent::CastPlayerSkill(const uint32_t behaviorId, const uint32_t s
|
|||||||
}
|
}
|
||||||
|
|
||||||
void SkillComponent::SyncPlayerSkill(const uint32_t skillUid, const uint32_t syncId, RakNet::BitStream& bitStream) {
|
void SkillComponent::SyncPlayerSkill(const uint32_t skillUid, const uint32_t syncId, RakNet::BitStream& bitStream) {
|
||||||
const auto index = this->m_managedBehaviors.find(skillUid);
|
const auto index = this->m_managedBehaviors.equal_range(skillUid);
|
||||||
|
|
||||||
if (index == this->m_managedBehaviors.end()) {
|
if (index.first == this->m_managedBehaviors.end()) {
|
||||||
LOG("Failed to find skill with uid (%i)!", skillUid, syncId);
|
LOG("Failed to find skill with uid (%i)!", skillUid, syncId);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto* context = index->second;
|
bool foundSyncId = false;
|
||||||
|
for (auto it = index.first; it != index.second && !foundSyncId; ++it) {
|
||||||
|
const auto& context = it->second;
|
||||||
|
|
||||||
context->SyncBehavior(syncId, bitStream);
|
foundSyncId = context->SyncBehavior(syncId, bitStream);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!foundSyncId) {
|
||||||
|
LOG("Failed to find sync id (%i) for skill with uid (%i)!", syncId, skillUid);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -99,7 +106,7 @@ void SkillComponent::SyncPlayerProjectile(const LWOOBJID projectileId, RakNet::B
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto behavior_id = static_cast<uint32_t>(result.getIntField(0));
|
const auto behavior_id = static_cast<uint32_t>(result.getIntField("behaviorID"));
|
||||||
|
|
||||||
result.finalize();
|
result.finalize();
|
||||||
|
|
||||||
@ -138,7 +145,7 @@ void SkillComponent::Update(const float deltaTime) {
|
|||||||
for (const auto& pair : this->m_managedBehaviors) pair.second->UpdatePlayerSyncs(deltaTime);
|
for (const auto& pair : this->m_managedBehaviors) pair.second->UpdatePlayerSyncs(deltaTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::map<uint32_t, BehaviorContext*> keep{};
|
std::multimap<uint32_t, BehaviorContext*> keep{};
|
||||||
|
|
||||||
for (const auto& pair : this->m_managedBehaviors) {
|
for (const auto& pair : this->m_managedBehaviors) {
|
||||||
auto* context = pair.second;
|
auto* context = pair.second;
|
||||||
@ -176,7 +183,7 @@ void SkillComponent::Update(const float deltaTime) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
keep.insert_or_assign(pair.first, context);
|
keep.insert({ pair.first, context });
|
||||||
}
|
}
|
||||||
|
|
||||||
this->m_managedBehaviors = keep;
|
this->m_managedBehaviors = keep;
|
||||||
@ -285,7 +292,7 @@ SkillExecutionResult SkillComponent::CalculateBehavior(
|
|||||||
return { false, 0 };
|
return { false, 0 };
|
||||||
}
|
}
|
||||||
|
|
||||||
this->m_managedBehaviors.insert_or_assign(context->skillUId, context);
|
this->m_managedBehaviors.insert({ context->skillUId, context });
|
||||||
|
|
||||||
if (!clientInitalized) {
|
if (!clientInitalized) {
|
||||||
// Echo start skill
|
// Echo start skill
|
||||||
@ -425,7 +432,7 @@ void SkillComponent::SyncProjectileCalculation(const ProjectileSyncEntry& entry)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto behaviorId = static_cast<uint32_t>(result.getIntField(0));
|
const auto behaviorId = static_cast<uint32_t>(result.getIntField("behaviorID"));
|
||||||
|
|
||||||
result.finalize();
|
result.finalize();
|
||||||
|
|
||||||
|
@ -188,7 +188,7 @@ private:
|
|||||||
/**
|
/**
|
||||||
* All of the active skills mapped by their unique ID.
|
* All of the active skills mapped by their unique ID.
|
||||||
*/
|
*/
|
||||||
std::map<uint32_t, BehaviorContext*> m_managedBehaviors;
|
std::multimap<uint32_t, BehaviorContext*> m_managedBehaviors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* All active projectiles.
|
* All active projectiles.
|
||||||
|
@ -76,8 +76,8 @@ void VendorComponent::RefreshInventory(bool isCreation) {
|
|||||||
if (vendorItems.empty()) break;
|
if (vendorItems.empty()) break;
|
||||||
auto randomItemIndex = GeneralUtils::GenerateRandomNumber<int32_t>(0, vendorItems.size() - 1);
|
auto randomItemIndex = GeneralUtils::GenerateRandomNumber<int32_t>(0, vendorItems.size() - 1);
|
||||||
const auto& randomItem = vendorItems.at(randomItemIndex);
|
const auto& randomItem = vendorItems.at(randomItemIndex);
|
||||||
vendorItems.erase(vendorItems.begin() + randomItemIndex);
|
|
||||||
if (SetupItem(randomItem.itemid)) m_Inventory.push_back(SoldItem(randomItem.itemid, randomItem.sortPriority));
|
if (SetupItem(randomItem.itemid)) m_Inventory.push_back(SoldItem(randomItem.itemid, randomItem.sortPriority));
|
||||||
|
vendorItems.erase(vendorItems.begin() + randomItemIndex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -685,6 +685,13 @@ void GameMessageHandler::HandleMessage(RakNet::BitStream& inStream, const System
|
|||||||
case eGameMessageType::REQUEST_VENDOR_STATUS_UPDATE:
|
case eGameMessageType::REQUEST_VENDOR_STATUS_UPDATE:
|
||||||
GameMessages::SendVendorStatusUpdate(entity, sysAddr, true);
|
GameMessages::SendVendorStatusUpdate(entity, sysAddr, true);
|
||||||
break;
|
break;
|
||||||
|
case eGameMessageType::UPDATE_INVENTORY_GROUP:
|
||||||
|
GameMessages::HandleUpdateInventoryGroup(inStream, entity, sysAddr);
|
||||||
|
break;
|
||||||
|
case eGameMessageType::UPDATE_INVENTORY_GROUP_CONTENTS:
|
||||||
|
GameMessages::HandleUpdateInventoryGroupContents(inStream, entity, sysAddr);
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
LOG_DEBUG("Received Unknown GM with ID: %4i, %s", messageID, StringifiedEnum::ToString(messageID).data());
|
LOG_DEBUG("Received Unknown GM with ID: %4i, %s", messageID, StringifiedEnum::ToString(messageID).data());
|
||||||
break;
|
break;
|
||||||
|
@ -99,9 +99,11 @@
|
|||||||
#include "ActivityManager.h"
|
#include "ActivityManager.h"
|
||||||
#include "PlayerManager.h"
|
#include "PlayerManager.h"
|
||||||
#include "eVendorTransactionResult.h"
|
#include "eVendorTransactionResult.h"
|
||||||
|
#include "eReponseMoveItemBetweenInventoryTypeCode.h"
|
||||||
|
|
||||||
#include "CDComponentsRegistryTable.h"
|
#include "CDComponentsRegistryTable.h"
|
||||||
#include "CDObjectsTable.h"
|
#include "CDObjectsTable.h"
|
||||||
|
#include "eItemType.h"
|
||||||
|
|
||||||
void GameMessages::SendFireEventClientSide(const LWOOBJID& objectID, const SystemAddress& sysAddr, std::u16string args, const LWOOBJID& object, int64_t param1, int param2, const LWOOBJID& sender) {
|
void GameMessages::SendFireEventClientSide(const LWOOBJID& objectID, const SystemAddress& sysAddr, std::u16string args, const LWOOBJID& object, int64_t param1, int param2, const LWOOBJID& sender) {
|
||||||
CBITSTREAM;
|
CBITSTREAM;
|
||||||
@ -369,8 +371,8 @@ void GameMessages::SendPlatformResync(Entity* entity, const SystemAddress& sysAd
|
|||||||
|
|
||||||
const auto lot = entity->GetLOT();
|
const auto lot = entity->GetLOT();
|
||||||
|
|
||||||
if (lot == 12341 || lot == 5027 || lot == 5028 || lot == 14335 || lot == 14447 || lot == 14449) {
|
if (lot == 12341 || lot == 5027 || lot == 5028 || lot == 14335 || lot == 14447 || lot == 14449 || lot == 11306 || lot == 11308) {
|
||||||
iDesiredWaypointIndex = 0;
|
iDesiredWaypointIndex = (lot == 11306 || lot == 11308) ? 1 : 0;
|
||||||
iIndex = 0;
|
iIndex = 0;
|
||||||
nextIndex = 0;
|
nextIndex = 0;
|
||||||
bStopAtDesiredWaypoint = true;
|
bStopAtDesiredWaypoint = true;
|
||||||
@ -412,7 +414,8 @@ void GameMessages::SendPlatformResync(Entity* entity, const SystemAddress& sysAd
|
|||||||
bitStream.Write(qUnexpectedRotation.w);
|
bitStream.Write(qUnexpectedRotation.w);
|
||||||
}
|
}
|
||||||
|
|
||||||
SEND_PACKET_BROADCAST;
|
if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST;
|
||||||
|
SEND_PACKET;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GameMessages::SendRestoreToPostLoadStats(Entity* entity, const SystemAddress& sysAddr) {
|
void GameMessages::SendRestoreToPostLoadStats(Entity* entity, const SystemAddress& sysAddr) {
|
||||||
@ -4563,16 +4566,31 @@ void GameMessages::HandleRequestMoveItemBetweenInventoryTypes(RakNet::BitStream&
|
|||||||
if (inStream.ReadBit()) inStream.Read(itemLOT);
|
if (inStream.ReadBit()) inStream.Read(itemLOT);
|
||||||
|
|
||||||
if (invTypeDst == invTypeSrc) {
|
if (invTypeDst == invTypeSrc) {
|
||||||
|
SendResponseMoveItemBetweenInventoryTypes(entity->GetObjectID(), sysAddr, invTypeDst, invTypeSrc, eReponseMoveItemBetweenInventoryTypeCode::FAIL_GENERIC);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto* inventoryComponent = entity->GetComponent<InventoryComponent>();
|
auto* inventoryComponent = entity->GetComponent<InventoryComponent>();
|
||||||
|
|
||||||
if (inventoryComponent != nullptr) {
|
if (inventoryComponent) {
|
||||||
if (itemID != LWOOBJID_EMPTY) {
|
if (itemID != LWOOBJID_EMPTY) {
|
||||||
auto* item = inventoryComponent->FindItemById(itemID);
|
auto* item = inventoryComponent->FindItemById(itemID);
|
||||||
|
|
||||||
if (!item) return;
|
if (!item) {
|
||||||
|
SendResponseMoveItemBetweenInventoryTypes(entity->GetObjectID(), sysAddr, invTypeDst, invTypeSrc, eReponseMoveItemBetweenInventoryTypeCode::FAIL_ITEM_NOT_FOUND);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item->GetLot() == 6086) { // Thinking hat
|
||||||
|
SendResponseMoveItemBetweenInventoryTypes(entity->GetObjectID(), sysAddr, invTypeDst, invTypeSrc, eReponseMoveItemBetweenInventoryTypeCode::FAIL_CANT_MOVE_THINKING_HAT);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* destInv = inventoryComponent->GetInventory(invTypeDst);
|
||||||
|
if (destInv && destInv->GetEmptySlots() == 0) {
|
||||||
|
SendResponseMoveItemBetweenInventoryTypes(entity->GetObjectID(), sysAddr, invTypeDst, invTypeSrc, eReponseMoveItemBetweenInventoryTypeCode::FAIL_INV_FULL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Despawn the pet if we are moving that pet to the vault.
|
// Despawn the pet if we are moving that pet to the vault.
|
||||||
auto* petComponent = PetComponent::GetActivePet(entity->GetObjectID());
|
auto* petComponent = PetComponent::GetActivePet(entity->GetObjectID());
|
||||||
@ -4581,10 +4599,32 @@ void GameMessages::HandleRequestMoveItemBetweenInventoryTypes(RakNet::BitStream&
|
|||||||
}
|
}
|
||||||
|
|
||||||
inventoryComponent->MoveItemToInventory(item, invTypeDst, iStackCount, showFlyingLoot, false, false, destSlot);
|
inventoryComponent->MoveItemToInventory(item, invTypeDst, iStackCount, showFlyingLoot, false, false, destSlot);
|
||||||
|
SendResponseMoveItemBetweenInventoryTypes(entity->GetObjectID(), sysAddr, invTypeDst, invTypeSrc, eReponseMoveItemBetweenInventoryTypeCode::SUCCESS);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
SendResponseMoveItemBetweenInventoryTypes(entity->GetObjectID(), sysAddr, invTypeDst, invTypeSrc, eReponseMoveItemBetweenInventoryTypeCode::FAIL_GENERIC);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GameMessages::SendResponseMoveItemBetweenInventoryTypes(LWOOBJID objectId, const SystemAddress& sysAddr, eInventoryType inventoryTypeDestination, eInventoryType inventoryTypeSource, eReponseMoveItemBetweenInventoryTypeCode response) {
|
||||||
|
CBITSTREAM;
|
||||||
|
CMSGHEADER;
|
||||||
|
|
||||||
|
bitStream.Write(objectId);
|
||||||
|
bitStream.Write(eGameMessageType::RESPONSE_MOVE_ITEM_BETWEEN_INVENTORY_TYPES);
|
||||||
|
|
||||||
|
bitStream.Write(inventoryTypeDestination != eInventoryType::ITEMS);
|
||||||
|
if (inventoryTypeDestination != eInventoryType::ITEMS) bitStream.Write(inventoryTypeDestination);
|
||||||
|
|
||||||
|
bitStream.Write(inventoryTypeSource != eInventoryType::ITEMS);
|
||||||
|
if (inventoryTypeSource != eInventoryType::ITEMS) bitStream.Write(inventoryTypeSource);
|
||||||
|
|
||||||
|
bitStream.Write(response != eReponseMoveItemBetweenInventoryTypeCode::FAIL_GENERIC);
|
||||||
|
if (response != eReponseMoveItemBetweenInventoryTypeCode::FAIL_GENERIC) bitStream.Write(response);
|
||||||
|
|
||||||
|
SEND_PACKET;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void GameMessages::SendShowActivityCountdown(LWOOBJID objectId, bool bPlayAdditionalSound, bool bPlayCountdownSound, std::u16string sndName, int32_t stateToPlaySoundOn, const SystemAddress& sysAddr) {
|
void GameMessages::SendShowActivityCountdown(LWOOBJID objectId, bool bPlayAdditionalSound, bool bPlayCountdownSound, std::u16string sndName, int32_t stateToPlaySoundOn, const SystemAddress& sysAddr) {
|
||||||
CBITSTREAM;
|
CBITSTREAM;
|
||||||
@ -5351,17 +5391,18 @@ void GameMessages::HandleRemoveItemFromInventory(RakNet::BitStream& inStream, En
|
|||||||
iStackCount = std::min<uint32_t>(item->GetCount(), iStackCount);
|
iStackCount = std::min<uint32_t>(item->GetCount(), iStackCount);
|
||||||
|
|
||||||
if (bConfirmed) {
|
if (bConfirmed) {
|
||||||
if (eInvType == eInventoryType::MODELS) {
|
const auto itemType = static_cast<eItemType>(item->GetInfo().itemType);
|
||||||
|
if (itemType == eItemType::MODEL || itemType == eItemType::LOOT_MODEL) {
|
||||||
item->DisassembleModel(iStackCount);
|
item->DisassembleModel(iStackCount);
|
||||||
}
|
}
|
||||||
|
auto lot = item->GetLot();
|
||||||
item->SetCount(item->GetCount() - iStackCount, true);
|
item->SetCount(item->GetCount() - iStackCount, true);
|
||||||
Game::entityManager->SerializeEntity(entity);
|
Game::entityManager->SerializeEntity(entity);
|
||||||
|
|
||||||
auto* missionComponent = entity->GetComponent<MissionComponent>();
|
auto* missionComponent = entity->GetComponent<MissionComponent>();
|
||||||
|
|
||||||
if (missionComponent != nullptr) {
|
if (missionComponent != nullptr) {
|
||||||
missionComponent->Progress(eMissionTaskType::GATHER, item->GetLot(), LWOOBJID_EMPTY, "", -iStackCount);
|
missionComponent->Progress(eMissionTaskType::GATHER, lot, LWOOBJID_EMPTY, "", -iStackCount);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -6209,3 +6250,81 @@ void GameMessages::SendSlashCommandFeedbackText(Entity* entity, std::u16string t
|
|||||||
auto sysAddr = entity->GetSystemAddress();
|
auto sysAddr = entity->GetSystemAddress();
|
||||||
SEND_PACKET;
|
SEND_PACKET;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GameMessages::HandleUpdateInventoryGroup(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr) {
|
||||||
|
std::string action;
|
||||||
|
std::u16string groupName;
|
||||||
|
InventoryComponent::GroupUpdate groupUpdate;
|
||||||
|
bool locked{}; // All groups are locked by default
|
||||||
|
|
||||||
|
uint32_t size{};
|
||||||
|
if (!inStream.Read(size)) return;
|
||||||
|
action.resize(size);
|
||||||
|
if (!inStream.Read(action.data(), size)) return;
|
||||||
|
|
||||||
|
if (!inStream.Read(size)) return;
|
||||||
|
groupUpdate.groupId.resize(size);
|
||||||
|
if (!inStream.Read(groupUpdate.groupId.data(), size)) return;
|
||||||
|
|
||||||
|
if (!inStream.Read(size)) return;
|
||||||
|
groupName.resize(size);
|
||||||
|
if (!inStream.Read(reinterpret_cast<char*>(groupName.data()), size * 2)) return;
|
||||||
|
|
||||||
|
if (!inStream.Read(groupUpdate.inventory)) return;
|
||||||
|
if (!inStream.Read(locked)) return;
|
||||||
|
|
||||||
|
groupUpdate.groupName = GeneralUtils::UTF16ToWTF8(groupName);
|
||||||
|
|
||||||
|
if (action == "ADD") groupUpdate.command = InventoryComponent::GroupUpdateCommand::ADD;
|
||||||
|
else if (action == "MODIFY") groupUpdate.command = InventoryComponent::GroupUpdateCommand::MODIFY;
|
||||||
|
else if (action == "REMOVE") groupUpdate.command = InventoryComponent::GroupUpdateCommand::REMOVE;
|
||||||
|
else {
|
||||||
|
LOG("Invalid action %s", action.c_str());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* inventoryComponent = entity->GetComponent<InventoryComponent>();
|
||||||
|
if (inventoryComponent) inventoryComponent->UpdateGroup(groupUpdate);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GameMessages::HandleUpdateInventoryGroupContents(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr) {
|
||||||
|
std::string action;
|
||||||
|
InventoryComponent::GroupUpdate groupUpdate;
|
||||||
|
|
||||||
|
uint32_t size{};
|
||||||
|
if (!inStream.Read(size)) return;
|
||||||
|
action.resize(size);
|
||||||
|
if (!inStream.Read(action.data(), size)) return;
|
||||||
|
|
||||||
|
if (action == "ADD") groupUpdate.command = InventoryComponent::GroupUpdateCommand::ADD_LOT;
|
||||||
|
else if (action == "REMOVE") groupUpdate.command = InventoryComponent::GroupUpdateCommand::REMOVE_LOT;
|
||||||
|
else {
|
||||||
|
LOG("Invalid action %s", action.c_str());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!inStream.Read(size)) return;
|
||||||
|
groupUpdate.groupId.resize(size);
|
||||||
|
if (!inStream.Read(groupUpdate.groupId.data(), size)) return;
|
||||||
|
|
||||||
|
if (!inStream.Read(groupUpdate.inventory)) return;
|
||||||
|
if (!inStream.Read(groupUpdate.lot)) return;
|
||||||
|
|
||||||
|
auto* inventoryComponent = entity->GetComponent<InventoryComponent>();
|
||||||
|
if (inventoryComponent) inventoryComponent->UpdateGroup(groupUpdate);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GameMessages::SendForceCameraTargetCycle(Entity* entity, bool bForceCycling, eCameraTargetCyclingMode cyclingMode, LWOOBJID optionalTargetID) {
|
||||||
|
CBITSTREAM;
|
||||||
|
CMSGHEADER;
|
||||||
|
|
||||||
|
bitStream.Write(entity->GetObjectID());
|
||||||
|
bitStream.Write(eGameMessageType::FORCE_CAMERA_TARGET_CYCLE);
|
||||||
|
bitStream.Write(bForceCycling);
|
||||||
|
bitStream.Write(cyclingMode != eCameraTargetCyclingMode::ALLOW_CYCLE_TEAMMATES);
|
||||||
|
if (cyclingMode != eCameraTargetCyclingMode::ALLOW_CYCLE_TEAMMATES) bitStream.Write(cyclingMode);
|
||||||
|
bitStream.Write(optionalTargetID);
|
||||||
|
|
||||||
|
auto sysAddr = entity->GetSystemAddress();
|
||||||
|
SEND_PACKET;
|
||||||
|
}
|
||||||
|
@ -39,6 +39,12 @@ enum class eQuickBuildFailReason : uint32_t;
|
|||||||
enum class eQuickBuildState : uint32_t;
|
enum class eQuickBuildState : uint32_t;
|
||||||
enum class BehaviorSlot : int32_t;
|
enum class BehaviorSlot : int32_t;
|
||||||
enum class eVendorTransactionResult : uint32_t;
|
enum class eVendorTransactionResult : uint32_t;
|
||||||
|
enum class eReponseMoveItemBetweenInventoryTypeCode : int32_t;
|
||||||
|
|
||||||
|
enum class eCameraTargetCyclingMode : int32_t {
|
||||||
|
ALLOW_CYCLE_TEAMMATES,
|
||||||
|
DISALLOW_CYCLING
|
||||||
|
};
|
||||||
|
|
||||||
namespace GameMessages {
|
namespace GameMessages {
|
||||||
class PropertyDataMessage;
|
class PropertyDataMessage;
|
||||||
@ -589,6 +595,7 @@ namespace GameMessages {
|
|||||||
//NT:
|
//NT:
|
||||||
|
|
||||||
void HandleRequestMoveItemBetweenInventoryTypes(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr);
|
void HandleRequestMoveItemBetweenInventoryTypes(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr);
|
||||||
|
void SendResponseMoveItemBetweenInventoryTypes(LWOOBJID objectId, const SystemAddress& sysAddr, eInventoryType inventoryTypeDestination, eInventoryType inventoryTypeSource, eReponseMoveItemBetweenInventoryTypeCode response);
|
||||||
|
|
||||||
void SendShowActivityCountdown(LWOOBJID objectId, bool bPlayAdditionalSound, bool bPlayCountdownSound, std::u16string sndName, int32_t stateToPlaySoundOn, const SystemAddress& sysAddr);
|
void SendShowActivityCountdown(LWOOBJID objectId, bool bPlayAdditionalSound, bool bPlayCountdownSound, std::u16string sndName, int32_t stateToPlaySoundOn, const SystemAddress& sysAddr);
|
||||||
|
|
||||||
@ -666,6 +673,10 @@ namespace GameMessages {
|
|||||||
void HandleCancelDonationOnPlayer(RakNet::BitStream& inStream, Entity* entity);
|
void HandleCancelDonationOnPlayer(RakNet::BitStream& inStream, Entity* entity);
|
||||||
|
|
||||||
void SendSlashCommandFeedbackText(Entity* entity, std::u16string text);
|
void SendSlashCommandFeedbackText(Entity* entity, std::u16string text);
|
||||||
|
|
||||||
|
void HandleUpdateInventoryGroup(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr);
|
||||||
|
void HandleUpdateInventoryGroupContents(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr);
|
||||||
|
void SendForceCameraTargetCycle(Entity* entity, bool bForceCycling, eCameraTargetCyclingMode cyclingMode, LWOOBJID optionalTargetID);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // GAMEMESSAGES_H
|
#endif // GAMEMESSAGES_H
|
||||||
|
@ -27,6 +27,23 @@
|
|||||||
#include "CDComponentsRegistryTable.h"
|
#include "CDComponentsRegistryTable.h"
|
||||||
#include "CDPackageComponentTable.h"
|
#include "CDPackageComponentTable.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
const std::map<std::string, std::string> ExtraSettingAbbreviations = {
|
||||||
|
{ "assemblyPartLOTs", "ma" },
|
||||||
|
{ "blueprintID", "b" },
|
||||||
|
{ "userModelID", "ui" },
|
||||||
|
{ "userModelName", "un" },
|
||||||
|
{ "userModelDesc", "ud" },
|
||||||
|
{ "userModelHasBhvr", "ub" },
|
||||||
|
{ "userModelBehaviors", "ubh" },
|
||||||
|
{ "userModelBehaviorSourceID", "ubs" },
|
||||||
|
{ "userModelPhysicsType", "up" },
|
||||||
|
{ "userModelMod", "um" },
|
||||||
|
{ "userModelOpt", "uo" },
|
||||||
|
{ "reforgedLOT", "rl" },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
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) {
|
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)) {
|
||||||
return;
|
return;
|
||||||
@ -122,6 +139,10 @@ uint32_t Item::GetSlot() const {
|
|||||||
return slot;
|
return slot;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<LDFBaseData*> Item::GetConfig() const {
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<LDFBaseData*>& Item::GetConfig() {
|
std::vector<LDFBaseData*>& Item::GetConfig() {
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
@ -251,7 +272,7 @@ bool Item::Consume() {
|
|||||||
|
|
||||||
auto skills = skillsTable->Query([this](const CDObjectSkills entry) {
|
auto skills = skillsTable->Query([this](const CDObjectSkills entry) {
|
||||||
return entry.objectTemplate == static_cast<uint32_t>(lot);
|
return entry.objectTemplate == static_cast<uint32_t>(lot);
|
||||||
});
|
});
|
||||||
|
|
||||||
auto success = false;
|
auto success = false;
|
||||||
|
|
||||||
@ -405,18 +426,18 @@ void Item::DisassembleModel(uint32_t numToDismantle) {
|
|||||||
|
|
||||||
auto result = query.execQuery();
|
auto result = query.execQuery();
|
||||||
|
|
||||||
if (result.eof() || result.fieldIsNull(0)) {
|
if (result.eof() || result.fieldIsNull("render_asset")) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string renderAsset = std::string(result.getStringField(0));
|
std::string renderAsset = std::string(result.getStringField("render_asset"));
|
||||||
|
|
||||||
// normalize path slashes
|
// normalize path slashes
|
||||||
for (auto& c : renderAsset) {
|
for (auto& c : renderAsset) {
|
||||||
if (c == '\\') c = '/';
|
if (c == '\\') c = '/';
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string lxfmlFolderName = std::string(result.getStringField(1));
|
std::string lxfmlFolderName = std::string(result.getStringField("LXFMLFolder"));
|
||||||
if (!lxfmlFolderName.empty()) lxfmlFolderName.insert(0, "/");
|
if (!lxfmlFolderName.empty()) lxfmlFolderName.insert(0, "/");
|
||||||
|
|
||||||
std::vector<std::string> renderAssetSplit = GeneralUtils::SplitString(renderAsset, '/');
|
std::vector<std::string> renderAssetSplit = GeneralUtils::SplitString(renderAsset, '/');
|
||||||
@ -515,3 +536,35 @@ Item::~Item() {
|
|||||||
|
|
||||||
config.clear();
|
config.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Item::SaveConfigXml(tinyxml2::XMLElement& i) const {
|
||||||
|
tinyxml2::XMLElement* x = nullptr;
|
||||||
|
|
||||||
|
for (const auto* config : this->config) {
|
||||||
|
const auto& key = GeneralUtils::UTF16ToWTF8(config->GetKey());
|
||||||
|
const auto saveKey = ExtraSettingAbbreviations.find(key);
|
||||||
|
if (saveKey == ExtraSettingAbbreviations.end()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!x) {
|
||||||
|
x = i.InsertNewChildElement("x");
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto dataToSave = config->GetString(false);
|
||||||
|
x->SetAttribute(saveKey->second.c_str(), dataToSave.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Item::LoadConfigXml(const tinyxml2::XMLElement& i) {
|
||||||
|
const auto* x = i.FirstChildElement("x");
|
||||||
|
if (!x) return;
|
||||||
|
|
||||||
|
for (const auto& pair : ExtraSettingAbbreviations) {
|
||||||
|
const auto* data = x->Attribute(pair.second.c_str());
|
||||||
|
if (!data) continue;
|
||||||
|
|
||||||
|
const auto value = pair.first + "=" + data;
|
||||||
|
config.push_back(LDFBaseData::DataFromString(value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -9,6 +9,10 @@
|
|||||||
#include "eInventoryType.h"
|
#include "eInventoryType.h"
|
||||||
#include "eLootSourceType.h"
|
#include "eLootSourceType.h"
|
||||||
|
|
||||||
|
namespace tinyxml2 {
|
||||||
|
class XMLElement;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An item that can be stored in an inventory and optionally consumed or equipped
|
* An item that can be stored in an inventory and optionally consumed or equipped
|
||||||
* TODO: ideally this should be a component
|
* TODO: ideally this should be a component
|
||||||
@ -116,6 +120,12 @@ public:
|
|||||||
*/
|
*/
|
||||||
std::vector<LDFBaseData*>& GetConfig();
|
std::vector<LDFBaseData*>& GetConfig();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns current config info for this item, e.g. for rockets
|
||||||
|
* @return current config info for this item
|
||||||
|
*/
|
||||||
|
std::vector<LDFBaseData*> GetConfig() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the database info for this item
|
* Returns the database info for this item
|
||||||
* @return the database info for this item
|
* @return the database info for this item
|
||||||
@ -214,6 +224,10 @@ public:
|
|||||||
*/
|
*/
|
||||||
void RemoveFromInventory();
|
void RemoveFromInventory();
|
||||||
|
|
||||||
|
void SaveConfigXml(tinyxml2::XMLElement& i) const;
|
||||||
|
|
||||||
|
void LoadConfigXml(const tinyxml2::XMLElement& i);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/**
|
/**
|
||||||
* The object ID of this item
|
* The object ID of this item
|
||||||
|
@ -8,10 +8,13 @@
|
|||||||
#include "MissionComponent.h"
|
#include "MissionComponent.h"
|
||||||
#include "eMissionTaskType.h"
|
#include "eMissionTaskType.h"
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <array>
|
||||||
|
|
||||||
#include "CDSkillBehaviorTable.h"
|
#include "CDSkillBehaviorTable.h"
|
||||||
|
|
||||||
ItemSet::ItemSet(const uint32_t id, InventoryComponent* inventoryComponent) {
|
ItemSet::ItemSet(const uint32_t id, InventoryComponent* inventoryComponent) {
|
||||||
|
using namespace std::string_view_literals;
|
||||||
|
|
||||||
this->m_ID = id;
|
this->m_ID = id;
|
||||||
this->m_InventoryComponent = inventoryComponent;
|
this->m_InventoryComponent = inventoryComponent;
|
||||||
|
|
||||||
@ -27,14 +30,16 @@ ItemSet::ItemSet(const uint32_t id, InventoryComponent* inventoryComponent) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto i = 0; i < 5; ++i) {
|
constexpr std::array rowNames = { "skillSetWith2"sv, "skillSetWith3"sv, "skillSetWith4"sv, "skillSetWith5"sv, "skillSetWith6"sv };
|
||||||
if (result.fieldIsNull(i)) {
|
for (auto i = 0; i < rowNames.size(); ++i) {
|
||||||
|
const auto rowName = rowNames[i];
|
||||||
|
if (result.fieldIsNull(rowName.data())) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto skillQuery = CDClientDatabase::CreatePreppedStmt(
|
auto skillQuery = CDClientDatabase::CreatePreppedStmt(
|
||||||
"SELECT SkillID FROM ItemSetSkills WHERE SkillSetID = ?;");
|
"SELECT SkillID FROM ItemSetSkills WHERE SkillSetID = ?;");
|
||||||
skillQuery.bind(1, result.getIntField(i));
|
skillQuery.bind(1, result.getIntField(rowName.data()));
|
||||||
|
|
||||||
auto skillResult = skillQuery.execQuery();
|
auto skillResult = skillQuery.execQuery();
|
||||||
|
|
||||||
@ -43,13 +48,13 @@ ItemSet::ItemSet(const uint32_t id, InventoryComponent* inventoryComponent) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
while (!skillResult.eof()) {
|
while (!skillResult.eof()) {
|
||||||
if (skillResult.fieldIsNull(0)) {
|
if (skillResult.fieldIsNull("SkillID")) {
|
||||||
skillResult.nextRow();
|
skillResult.nextRow();
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto skillId = skillResult.getIntField(0);
|
const auto skillId = skillResult.getIntField("SkillID");
|
||||||
|
|
||||||
switch (i) {
|
switch (i) {
|
||||||
case 0:
|
case 0:
|
||||||
@ -75,7 +80,7 @@ ItemSet::ItemSet(const uint32_t id, InventoryComponent* inventoryComponent) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string ids = result.getStringField(5);
|
std::string ids = result.getStringField("itemIDs");
|
||||||
|
|
||||||
ids.erase(std::remove_if(ids.begin(), ids.end(), ::isspace), ids.end());
|
ids.erase(std::remove_if(ids.begin(), ids.end(), ::isspace), ids.end());
|
||||||
|
|
||||||
|
@ -454,6 +454,16 @@ void Mission::YieldRewards() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Even with no repeatable column, reputation is repeatable
|
||||||
|
if (info.reward_reputation > 0) {
|
||||||
|
missionComponent->Progress(eMissionTaskType::EARN_REPUTATION, 0, LWOOBJID_EMPTY, "", info.reward_reputation);
|
||||||
|
auto* const character = entity->GetComponent<CharacterComponent>();
|
||||||
|
if (character) {
|
||||||
|
character->SetReputation(character->GetReputation() + info.reward_reputation);
|
||||||
|
GameMessages::SendUpdateReputation(entity->GetObjectID(), character->GetReputation(), entity->GetSystemAddress());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (m_Completions > 0) {
|
if (m_Completions > 0) {
|
||||||
std::vector<std::pair<LOT, uint32_t>> items;
|
std::vector<std::pair<LOT, uint32_t>> items;
|
||||||
|
|
||||||
@ -532,15 +542,6 @@ void Mission::YieldRewards() {
|
|||||||
modelInventory->SetSize(modelInventory->GetSize() + info.reward_bankinventory);
|
modelInventory->SetSize(modelInventory->GetSize() + info.reward_bankinventory);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (info.reward_reputation > 0) {
|
|
||||||
missionComponent->Progress(eMissionTaskType::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), true);
|
destroyableComponent->SetMaxHealth(destroyableComponent->GetMaxHealth() + static_cast<float>(info.reward_maxhealth), true);
|
||||||
}
|
}
|
||||||
|
@ -186,7 +186,7 @@ void MissionTask::Progress(int32_t value, LWOOBJID associate, const std::string&
|
|||||||
|
|
||||||
if (count < 0) {
|
if (count < 0) {
|
||||||
if (mission->IsMission() && type == eMissionTaskType::GATHER && InAllTargets(value)) {
|
if (mission->IsMission() && type == eMissionTaskType::GATHER && InAllTargets(value)) {
|
||||||
if (parameters.size() > 0 && (parameters[0] & 1) != 0) {
|
if (parameters.size() > 0 && (parameters[0] & 4) != 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
#include "Action.h"
|
#include "Action.h"
|
||||||
#include "Amf3.h"
|
#include "Amf3.h"
|
||||||
|
|
||||||
|
#include "tinyxml2.h"
|
||||||
|
|
||||||
Action::Action(const AMFArrayValue& arguments) {
|
Action::Action(const AMFArrayValue& arguments) {
|
||||||
for (const auto& [paramName, paramValue] : arguments.GetAssociative()) {
|
for (const auto& [paramName, paramValue] : arguments.GetAssociative()) {
|
||||||
if (paramName == "Type") {
|
if (paramName == "Type") {
|
||||||
@ -32,3 +34,46 @@ void Action::SendBehaviorBlocksToClient(AMFArrayValue& args) const {
|
|||||||
actionArgs->Insert(m_ValueParameterName, m_ValueParameterDouble);
|
actionArgs->Insert(m_ValueParameterName, m_ValueParameterDouble);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Action::Serialize(tinyxml2::XMLElement& action) const {
|
||||||
|
action.SetAttribute("Type", m_Type.c_str());
|
||||||
|
|
||||||
|
if (m_ValueParameterName.empty()) return;
|
||||||
|
|
||||||
|
action.SetAttribute("ValueParameterName", m_ValueParameterName.c_str());
|
||||||
|
|
||||||
|
if (m_ValueParameterName == "Message") {
|
||||||
|
action.SetAttribute("Value", m_ValueParameterString.c_str());
|
||||||
|
} else {
|
||||||
|
action.SetAttribute("Value", m_ValueParameterDouble);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Action::Deserialize(const tinyxml2::XMLElement& action) {
|
||||||
|
const char* type = nullptr;
|
||||||
|
action.QueryAttribute("Type", &type);
|
||||||
|
if (!type) {
|
||||||
|
LOG("No type found for an action?");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_Type = type;
|
||||||
|
|
||||||
|
const char* valueParameterName = nullptr;
|
||||||
|
action.QueryAttribute("ValueParameterName", &valueParameterName);
|
||||||
|
if (valueParameterName) {
|
||||||
|
m_ValueParameterName = valueParameterName;
|
||||||
|
|
||||||
|
if (m_ValueParameterName == "Message") {
|
||||||
|
const char* value = nullptr;
|
||||||
|
action.QueryAttribute("Value", &value);
|
||||||
|
if (value) {
|
||||||
|
m_ValueParameterString = value;
|
||||||
|
} else {
|
||||||
|
LOG("No value found for an action message?");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
action.QueryDoubleAttribute("Value", &m_ValueParameterDouble);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -3,6 +3,10 @@
|
|||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
namespace tinyxml2 {
|
||||||
|
class XMLElement;
|
||||||
|
};
|
||||||
|
|
||||||
class AMFArrayValue;
|
class AMFArrayValue;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -20,6 +24,8 @@ public:
|
|||||||
|
|
||||||
void SendBehaviorBlocksToClient(AMFArrayValue& args) const;
|
void SendBehaviorBlocksToClient(AMFArrayValue& args) const;
|
||||||
|
|
||||||
|
void Serialize(tinyxml2::XMLElement& action) const;
|
||||||
|
void Deserialize(const tinyxml2::XMLElement& action);
|
||||||
private:
|
private:
|
||||||
double m_ValueParameterDouble{ 0.0 };
|
double m_ValueParameterDouble{ 0.0 };
|
||||||
std::string m_Type{ "" };
|
std::string m_Type{ "" };
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
#include "StripUiPosition.h"
|
#include "StripUiPosition.h"
|
||||||
|
|
||||||
#include "Amf3.h"
|
#include "Amf3.h"
|
||||||
|
#include "tinyxml2.h"
|
||||||
|
|
||||||
StripUiPosition::StripUiPosition(const AMFArrayValue& arguments, const std::string& uiKeyName) {
|
StripUiPosition::StripUiPosition(const AMFArrayValue& arguments, const std::string& uiKeyName) {
|
||||||
const auto* const uiArray = arguments.GetArray(uiKeyName);
|
const auto* const uiArray = arguments.GetArray(uiKeyName);
|
||||||
@ -21,3 +22,13 @@ void StripUiPosition::SendBehaviorBlocksToClient(AMFArrayValue& args) const {
|
|||||||
uiArgs->Insert("x", m_XPosition);
|
uiArgs->Insert("x", m_XPosition);
|
||||||
uiArgs->Insert("y", m_YPosition);
|
uiArgs->Insert("y", m_YPosition);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void StripUiPosition::Serialize(tinyxml2::XMLElement& position) const {
|
||||||
|
position.SetAttribute("x", m_XPosition);
|
||||||
|
position.SetAttribute("y", m_YPosition);
|
||||||
|
}
|
||||||
|
|
||||||
|
void StripUiPosition::Deserialize(const tinyxml2::XMLElement& position) {
|
||||||
|
position.QueryDoubleAttribute("x", &m_XPosition);
|
||||||
|
position.QueryDoubleAttribute("y", &m_YPosition);
|
||||||
|
}
|
||||||
|
@ -3,6 +3,10 @@
|
|||||||
|
|
||||||
class AMFArrayValue;
|
class AMFArrayValue;
|
||||||
|
|
||||||
|
namespace tinyxml2 {
|
||||||
|
class XMLElement;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief The position of the first Action in a Strip
|
* @brief The position of the first Action in a Strip
|
||||||
*
|
*
|
||||||
@ -15,6 +19,8 @@ public:
|
|||||||
[[nodiscard]] double GetX() const noexcept { return m_XPosition; }
|
[[nodiscard]] double GetX() const noexcept { return m_XPosition; }
|
||||||
[[nodiscard]] double GetY() const noexcept { return m_YPosition; }
|
[[nodiscard]] double GetY() const noexcept { return m_YPosition; }
|
||||||
|
|
||||||
|
void Serialize(tinyxml2::XMLElement& position) const;
|
||||||
|
void Deserialize(const tinyxml2::XMLElement& position);
|
||||||
private:
|
private:
|
||||||
double m_XPosition{ 0.0 };
|
double m_XPosition{ 0.0 };
|
||||||
double m_YPosition{ 0.0 };
|
double m_YPosition{ 0.0 };
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
#include "Amf3.h"
|
#include "Amf3.h"
|
||||||
#include "BehaviorStates.h"
|
#include "BehaviorStates.h"
|
||||||
#include "ControlBehaviorMsgs.h"
|
#include "ControlBehaviorMsgs.h"
|
||||||
|
#include "tinyxml2.h"
|
||||||
|
|
||||||
PropertyBehavior::PropertyBehavior() {
|
PropertyBehavior::PropertyBehavior() {
|
||||||
m_LastEditedState = BehaviorState::HOME_STATE;
|
m_LastEditedState = BehaviorState::HOME_STATE;
|
||||||
@ -124,3 +125,31 @@ void PropertyBehavior::SendBehaviorBlocksToClient(AMFArrayValue& args) const {
|
|||||||
|
|
||||||
// TODO Serialize the execution state of the behavior
|
// TODO Serialize the execution state of the behavior
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PropertyBehavior::Serialize(tinyxml2::XMLElement& behavior) const {
|
||||||
|
behavior.SetAttribute("id", m_BehaviorId);
|
||||||
|
behavior.SetAttribute("name", m_Name.c_str());
|
||||||
|
behavior.SetAttribute("isLocked", isLocked);
|
||||||
|
behavior.SetAttribute("isLoot", isLoot);
|
||||||
|
|
||||||
|
for (const auto& [stateId, state] : m_States) {
|
||||||
|
if (state.IsEmpty()) continue;
|
||||||
|
auto* const stateElement = behavior.InsertNewChildElement("State");
|
||||||
|
stateElement->SetAttribute("id", static_cast<uint32_t>(stateId));
|
||||||
|
state.Serialize(*stateElement);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void PropertyBehavior::Deserialize(const tinyxml2::XMLElement& behavior) {
|
||||||
|
m_Name = behavior.Attribute("name");
|
||||||
|
behavior.QueryBoolAttribute("isLocked", &isLocked);
|
||||||
|
behavior.QueryBoolAttribute("isLoot", &isLoot);
|
||||||
|
|
||||||
|
for (const auto* stateElement = behavior.FirstChildElement("State"); stateElement; stateElement = stateElement->NextSiblingElement("State")) {
|
||||||
|
int32_t stateId = -1;
|
||||||
|
stateElement->QueryIntAttribute("id", &stateId);
|
||||||
|
if (stateId < 0 || stateId > 5) continue;
|
||||||
|
m_States[static_cast<BehaviorState>(stateId)].Deserialize(*stateElement);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -3,6 +3,10 @@
|
|||||||
|
|
||||||
#include "State.h"
|
#include "State.h"
|
||||||
|
|
||||||
|
namespace tinyxml2 {
|
||||||
|
class XMLElement;
|
||||||
|
}
|
||||||
|
|
||||||
enum class BehaviorState : uint32_t;
|
enum class BehaviorState : uint32_t;
|
||||||
|
|
||||||
class AMFArrayValue;
|
class AMFArrayValue;
|
||||||
@ -25,6 +29,8 @@ public:
|
|||||||
[[nodiscard]] int32_t GetBehaviorId() const noexcept { return m_BehaviorId; }
|
[[nodiscard]] int32_t GetBehaviorId() const noexcept { return m_BehaviorId; }
|
||||||
void SetBehaviorId(int32_t id) noexcept { m_BehaviorId = id; }
|
void SetBehaviorId(int32_t id) noexcept { m_BehaviorId = id; }
|
||||||
|
|
||||||
|
void Serialize(tinyxml2::XMLElement& behavior) const;
|
||||||
|
void Deserialize(const tinyxml2::XMLElement& behavior);
|
||||||
private:
|
private:
|
||||||
|
|
||||||
// The states this behavior has.
|
// The states this behavior has.
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include "Amf3.h"
|
#include "Amf3.h"
|
||||||
#include "ControlBehaviorMsgs.h"
|
#include "ControlBehaviorMsgs.h"
|
||||||
|
#include "tinyxml2.h"
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
void State::HandleMsg(AddStripMessage& msg) {
|
void State::HandleMsg(AddStripMessage& msg) {
|
||||||
@ -134,4 +135,20 @@ void State::SendBehaviorBlocksToClient(AMFArrayValue& args) const {
|
|||||||
|
|
||||||
strip.SendBehaviorBlocksToClient(*stripArgs);
|
strip.SendBehaviorBlocksToClient(*stripArgs);
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
|
void State::Serialize(tinyxml2::XMLElement& state) const {
|
||||||
|
for (const auto& strip : m_Strips) {
|
||||||
|
if (strip.IsEmpty()) continue;
|
||||||
|
|
||||||
|
auto* const stripElement = state.InsertNewChildElement("Strip");
|
||||||
|
strip.Serialize(*stripElement);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void State::Deserialize(const tinyxml2::XMLElement& state) {
|
||||||
|
for (const auto* stripElement = state.FirstChildElement("Strip"); stripElement; stripElement = stripElement->NextSiblingElement("Strip")) {
|
||||||
|
auto& strip = m_Strips.emplace_back();
|
||||||
|
strip.Deserialize(*stripElement);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -3,6 +3,10 @@
|
|||||||
|
|
||||||
#include "Strip.h"
|
#include "Strip.h"
|
||||||
|
|
||||||
|
namespace tinyxml2 {
|
||||||
|
class XMLElement;
|
||||||
|
}
|
||||||
|
|
||||||
class AMFArrayValue;
|
class AMFArrayValue;
|
||||||
|
|
||||||
class State {
|
class State {
|
||||||
@ -13,6 +17,8 @@ public:
|
|||||||
void SendBehaviorBlocksToClient(AMFArrayValue& args) const;
|
void SendBehaviorBlocksToClient(AMFArrayValue& args) const;
|
||||||
bool IsEmpty() const;
|
bool IsEmpty() const;
|
||||||
|
|
||||||
|
void Serialize(tinyxml2::XMLElement& state) const;
|
||||||
|
void Deserialize(const tinyxml2::XMLElement& state);
|
||||||
private:
|
private:
|
||||||
std::vector<Strip> m_Strips;
|
std::vector<Strip> m_Strips;
|
||||||
};
|
};
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include "Amf3.h"
|
#include "Amf3.h"
|
||||||
#include "ControlBehaviorMsgs.h"
|
#include "ControlBehaviorMsgs.h"
|
||||||
|
#include "tinyxml2.h"
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
void Strip::HandleMsg(AddStripMessage& msg) {
|
void Strip::HandleMsg(AddStripMessage& msg) {
|
||||||
@ -83,4 +84,25 @@ void Strip::SendBehaviorBlocksToClient(AMFArrayValue& args) const {
|
|||||||
for (const auto& action : m_Actions) {
|
for (const auto& action : m_Actions) {
|
||||||
action.SendBehaviorBlocksToClient(*actions);
|
action.SendBehaviorBlocksToClient(*actions);
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
|
void Strip::Serialize(tinyxml2::XMLElement& strip) const {
|
||||||
|
auto* const positionElement = strip.InsertNewChildElement("Position");
|
||||||
|
m_Position.Serialize(*positionElement);
|
||||||
|
for (const auto& action : m_Actions) {
|
||||||
|
auto* const actionElement = strip.InsertNewChildElement("Action");
|
||||||
|
action.Serialize(*actionElement);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Strip::Deserialize(const tinyxml2::XMLElement& strip) {
|
||||||
|
const auto* positionElement = strip.FirstChildElement("Position");
|
||||||
|
if (positionElement) {
|
||||||
|
m_Position.Deserialize(*positionElement);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto* actionElement = strip.FirstChildElement("Action"); actionElement; actionElement = actionElement->NextSiblingElement("Action")) {
|
||||||
|
auto& action = m_Actions.emplace_back();
|
||||||
|
action.Deserialize(*actionElement);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -6,6 +6,10 @@
|
|||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
namespace tinyxml2 {
|
||||||
|
class XMLElement;
|
||||||
|
}
|
||||||
|
|
||||||
class AMFArrayValue;
|
class AMFArrayValue;
|
||||||
|
|
||||||
class Strip {
|
class Strip {
|
||||||
@ -16,6 +20,8 @@ public:
|
|||||||
void SendBehaviorBlocksToClient(AMFArrayValue& args) const;
|
void SendBehaviorBlocksToClient(AMFArrayValue& args) const;
|
||||||
bool IsEmpty() const noexcept { return m_Actions.empty(); }
|
bool IsEmpty() const noexcept { return m_Actions.empty(); }
|
||||||
|
|
||||||
|
void Serialize(tinyxml2::XMLElement& strip) const;
|
||||||
|
void Deserialize(const tinyxml2::XMLElement& strip);
|
||||||
private:
|
private:
|
||||||
std::vector<Action> m_Actions;
|
std::vector<Action> m_Actions;
|
||||||
StripUiPosition m_Position;
|
StripUiPosition m_Position;
|
||||||
|
@ -130,12 +130,6 @@ bool CheatDetection::VerifyLwoobjidIsSender(const LWOOBJID& id, const SystemAddr
|
|||||||
|
|
||||||
// This will be true if the player does not possess the entity they are trying to send a packet as.
|
// This will be true if the player does not possess the entity they are trying to send a packet as.
|
||||||
// or if the user does not own the character they are trying to send a packet as.
|
// or if the user does not own the character they are trying to send a packet as.
|
||||||
if (invalidPacket) {
|
|
||||||
va_list args;
|
|
||||||
va_start(args, messageIfNotSender);
|
|
||||||
LogAndSaveFailedAntiCheatCheck(id, sysAddr, checkType, messageIfNotSender, args);
|
|
||||||
va_end(args);
|
|
||||||
}
|
|
||||||
|
|
||||||
return !invalidPacket;
|
return !invalidPacket;
|
||||||
}
|
}
|
||||||
|
@ -94,35 +94,6 @@ void Mail::SendMail(const LWOOBJID sender, const std::string& senderName, LWOOBJ
|
|||||||
SendNotification(sysAddr, 1); //Show the "one new mail" message
|
SendNotification(sysAddr, 1); //Show the "one new mail" message
|
||||||
}
|
}
|
||||||
|
|
||||||
//Because we need it:
|
|
||||||
std::string ReadWStringAsString(RakNet::BitStream& bitStream, uint32_t size) {
|
|
||||||
std::string toReturn = "";
|
|
||||||
uint8_t buffer;
|
|
||||||
bool isFinishedReading = false;
|
|
||||||
|
|
||||||
for (uint32_t i = 0; i < size; ++i) {
|
|
||||||
bitStream.Read(buffer);
|
|
||||||
if (!isFinishedReading) toReturn.push_back(buffer);
|
|
||||||
if (buffer == '\0') isFinishedReading = true; //so we don't continue to read garbage as part of the string.
|
|
||||||
bitStream.Read(buffer); //Read the null term
|
|
||||||
}
|
|
||||||
|
|
||||||
return toReturn;
|
|
||||||
}
|
|
||||||
|
|
||||||
void WriteStringAsWString(RakNet::BitStream& bitStream, std::string str, uint32_t size) {
|
|
||||||
uint32_t sizeToFill = size - str.size();
|
|
||||||
|
|
||||||
for (uint32_t i = 0; i < str.size(); ++i) {
|
|
||||||
bitStream.Write(str[i]);
|
|
||||||
bitStream.Write(uint8_t(0));
|
|
||||||
}
|
|
||||||
|
|
||||||
for (uint32_t i = 0; i < sizeToFill; ++i) {
|
|
||||||
bitStream.Write(uint16_t(0));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Mail::HandleMailStuff(RakNet::BitStream& packet, const SystemAddress& sysAddr, Entity* entity) {
|
void Mail::HandleMailStuff(RakNet::BitStream& packet, const SystemAddress& sysAddr, Entity* entity) {
|
||||||
int mailStuffID = 0;
|
int mailStuffID = 0;
|
||||||
packet.Read(mailStuffID);
|
packet.Read(mailStuffID);
|
||||||
@ -176,11 +147,20 @@ void Mail::HandleSendMail(RakNet::BitStream& packet, const SystemAddress& sysAdd
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string subject = ReadWStringAsString(packet, 50);
|
LUWString subjectRead(50);
|
||||||
std::string body = ReadWStringAsString(packet, 400);
|
packet.Read(subjectRead);
|
||||||
std::string recipient = ReadWStringAsString(packet, 32);
|
|
||||||
|
LUWString bodyRead(400);
|
||||||
|
packet.Read(bodyRead);
|
||||||
|
|
||||||
|
LUWString recipientRead(32);
|
||||||
|
packet.Read(recipientRead);
|
||||||
|
|
||||||
|
const std::string subject = subjectRead.GetAsString();
|
||||||
|
const std::string body = bodyRead.GetAsString();
|
||||||
|
|
||||||
//Cleanse recipient:
|
//Cleanse recipient:
|
||||||
recipient = std::regex_replace(recipient, std::regex("[^0-9a-zA-Z]+"), "");
|
const std::string recipient = std::regex_replace(recipientRead.GetAsString(), std::regex("[^0-9a-zA-Z]+"), "");
|
||||||
|
|
||||||
uint64_t unknown64 = 0;
|
uint64_t unknown64 = 0;
|
||||||
LWOOBJID attachmentID;
|
LWOOBJID attachmentID;
|
||||||
@ -267,40 +247,44 @@ void Mail::HandleDataRequest(RakNet::BitStream& packet, const SystemAddress& sys
|
|||||||
RakNet::BitStream bitStream;
|
RakNet::BitStream bitStream;
|
||||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::MAIL);
|
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::MAIL);
|
||||||
bitStream.Write(int(MailMessageID::MailData));
|
bitStream.Write(int(MailMessageID::MailData));
|
||||||
bitStream.Write(int(0));
|
bitStream.Write(int(0)); // throttled
|
||||||
|
|
||||||
bitStream.Write<uint16_t>(playerMail.size());
|
bitStream.Write<uint16_t>(playerMail.size()); // size
|
||||||
bitStream.Write<uint16_t>(0);
|
bitStream.Write<uint16_t>(0);
|
||||||
|
|
||||||
for (const auto& mail : playerMail) {
|
for (const auto& mail : playerMail) {
|
||||||
bitStream.Write(mail.id); //MailID
|
bitStream.Write(mail.id); //MailID
|
||||||
|
|
||||||
WriteStringAsWString(bitStream, mail.subject.c_str(), 50); //subject
|
const LUWString subject(mail.subject, 50);
|
||||||
WriteStringAsWString(bitStream, mail.body.c_str(), 400); //body
|
bitStream.Write(subject); //subject
|
||||||
WriteStringAsWString(bitStream, mail.senderUsername.c_str(), 32); //sender
|
const LUWString body(mail.body, 400);
|
||||||
|
bitStream.Write(body); //body
|
||||||
|
const LUWString sender(mail.senderUsername, 32);
|
||||||
|
bitStream.Write(sender); //sender
|
||||||
|
bitStream.Write(uint32_t(0)); // packing
|
||||||
|
|
||||||
bitStream.Write(uint32_t(0));
|
bitStream.Write(uint64_t(0)); // attachedCurrency
|
||||||
bitStream.Write(uint64_t(0));
|
|
||||||
|
|
||||||
bitStream.Write(mail.itemID); //Attachment ID
|
bitStream.Write(mail.itemID); //Attachment ID
|
||||||
LOT lot = mail.itemLOT;
|
LOT lot = mail.itemLOT;
|
||||||
if (lot <= 0) bitStream.Write(LOT(-1));
|
if (lot <= 0) bitStream.Write(LOT(-1));
|
||||||
else bitStream.Write(lot);
|
else bitStream.Write(lot);
|
||||||
bitStream.Write(uint32_t(0));
|
bitStream.Write(uint32_t(0)); // packing
|
||||||
|
|
||||||
bitStream.Write(mail.itemSubkey); //Attachment subKey
|
bitStream.Write(mail.itemSubkey); // Attachment subKey
|
||||||
bitStream.Write<uint16_t>(mail.itemCount); //Attachment count
|
|
||||||
|
|
||||||
bitStream.Write(uint32_t(0));
|
bitStream.Write<uint16_t>(mail.itemCount); // Attachment count
|
||||||
bitStream.Write(uint16_t(0));
|
bitStream.Write(uint8_t(0)); // subject type (used for auction)
|
||||||
|
bitStream.Write(uint8_t(0)); // packing
|
||||||
|
bitStream.Write(uint32_t(0)); // packing
|
||||||
|
|
||||||
bitStream.Write<uint64_t>(mail.timeSent); //time sent (twice?)
|
bitStream.Write<uint64_t>(mail.timeSent); // expiration date
|
||||||
bitStream.Write<uint64_t>(mail.timeSent);
|
bitStream.Write<uint64_t>(mail.timeSent);// send date
|
||||||
bitStream.Write<uint8_t>(mail.wasRead); //was read
|
bitStream.Write<uint8_t>(mail.wasRead); //was read
|
||||||
|
|
||||||
bitStream.Write(uint8_t(0));
|
bitStream.Write(uint8_t(0)); // isLocalized
|
||||||
bitStream.Write(uint16_t(0));
|
bitStream.Write(uint16_t(0)); // packing
|
||||||
bitStream.Write(uint32_t(0));
|
bitStream.Write(uint32_t(0)); // packing
|
||||||
}
|
}
|
||||||
|
|
||||||
Game::server->Send(bitStream, sysAddr, false);
|
Game::server->Send(bitStream, sysAddr, false);
|
||||||
|
@ -26,17 +26,18 @@ Precondition::Precondition(const uint32_t condition) {
|
|||||||
if (result.eof()) {
|
if (result.eof()) {
|
||||||
this->type = PreconditionType::ItemEquipped;
|
this->type = PreconditionType::ItemEquipped;
|
||||||
this->count = 1;
|
this->count = 1;
|
||||||
this->values = { 0 };
|
this->values.clear();
|
||||||
|
this->values.push_back(0);
|
||||||
|
|
||||||
LOG("Failed to find precondition of id (%i)!", condition);
|
LOG("Failed to find precondition of id (%i)!", condition);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this->type = static_cast<PreconditionType>(result.fieldIsNull(0) ? 0 : result.getIntField(0));
|
this->type = static_cast<PreconditionType>(result.fieldIsNull("type") ? 0 : result.getIntField("type"));
|
||||||
|
|
||||||
if (!result.fieldIsNull(1)) {
|
if (!result.fieldIsNull("targetLOT")) {
|
||||||
std::istringstream stream(result.getStringField(1));
|
std::istringstream stream(result.getStringField("targetLOT"));
|
||||||
std::string token;
|
std::string token;
|
||||||
|
|
||||||
while (std::getline(stream, token, ',')) {
|
while (std::getline(stream, token, ',')) {
|
||||||
@ -45,7 +46,7 @@ Precondition::Precondition(const uint32_t condition) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this->count = result.fieldIsNull(2) ? 1 : result.getIntField(2);
|
this->count = result.fieldIsNull("targetCount") ? 1 : result.getIntField("targetCount");
|
||||||
|
|
||||||
result.finalize();
|
result.finalize();
|
||||||
}
|
}
|
||||||
@ -138,21 +139,20 @@ bool Precondition::CheckValue(Entity* player, const uint32_t value, bool evaluat
|
|||||||
case PreconditionType::DoesNotHaveItem:
|
case PreconditionType::DoesNotHaveItem:
|
||||||
return inventoryComponent->IsEquipped(value) < count;
|
return inventoryComponent->IsEquipped(value) < count;
|
||||||
case PreconditionType::HasAchievement:
|
case PreconditionType::HasAchievement:
|
||||||
mission = missionComponent->GetMission(value);
|
if (missionComponent == nullptr) return false;
|
||||||
|
return missionComponent->GetMissionState(value) >= eMissionState::COMPLETE;
|
||||||
return mission == nullptr || mission->GetMissionState() >= eMissionState::COMPLETE;
|
|
||||||
case PreconditionType::MissionAvailable:
|
case PreconditionType::MissionAvailable:
|
||||||
mission = missionComponent->GetMission(value);
|
if (missionComponent == nullptr) return false;
|
||||||
|
return missionComponent->GetMissionState(value) == eMissionState::AVAILABLE || missionComponent->GetMissionState(value) == eMissionState::COMPLETE_AVAILABLE;
|
||||||
return mission == nullptr || mission->GetMissionState() >= eMissionState::AVAILABLE;
|
|
||||||
case PreconditionType::OnMission:
|
case PreconditionType::OnMission:
|
||||||
mission = missionComponent->GetMission(value);
|
if (missionComponent == nullptr) return false;
|
||||||
|
return missionComponent->GetMissionState(value) == eMissionState::ACTIVE ||
|
||||||
return mission == nullptr || mission->GetMissionState() >= eMissionState::ACTIVE;
|
missionComponent->GetMissionState(value) == eMissionState::COMPLETE_ACTIVE ||
|
||||||
|
missionComponent->GetMissionState(value) == eMissionState::READY_TO_COMPLETE ||
|
||||||
|
missionComponent->GetMissionState(value) == eMissionState::COMPLETE_READY_TO_COMPLETE;
|
||||||
case PreconditionType::MissionComplete:
|
case PreconditionType::MissionComplete:
|
||||||
mission = missionComponent->GetMission(value);
|
if (missionComponent == nullptr) return false;
|
||||||
|
return missionComponent->GetMissionState(value) >= eMissionState::COMPLETE;
|
||||||
return mission == nullptr ? false : mission->GetMissionState() >= eMissionState::COMPLETE;
|
|
||||||
case PreconditionType::PetDeployed:
|
case PreconditionType::PetDeployed:
|
||||||
return false; // TODO
|
return false; // TODO
|
||||||
case PreconditionType::HasFlag:
|
case PreconditionType::HasFlag:
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
#include "SlashCommandHandler.h"
|
#include "SlashCommandHandler.h"
|
||||||
|
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
|
#include <ranges>
|
||||||
|
|
||||||
#include "DEVGMCommands.h"
|
#include "DEVGMCommands.h"
|
||||||
#include "GMGreaterThanZeroCommands.h"
|
#include "GMGreaterThanZeroCommands.h"
|
||||||
@ -19,7 +20,7 @@
|
|||||||
#include "dServer.h"
|
#include "dServer.h"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
std::vector<Command> CommandInfos;
|
std::map<std::string, Command> CommandInfos;
|
||||||
std::map<std::string, Command> RegisteredCommands;
|
std::map<std::string, Command> RegisteredCommands;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -30,7 +31,6 @@ void SlashCommandHandler::RegisterCommand(Command command) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (const auto& alias : command.aliases) {
|
for (const auto& alias : command.aliases) {
|
||||||
LOG_DEBUG("Registering command %s", alias.c_str());
|
|
||||||
auto [_, success] = RegisteredCommands.try_emplace(alias, command);
|
auto [_, success] = RegisteredCommands.try_emplace(alias, command);
|
||||||
// Don't allow duplicate commands
|
// Don't allow duplicate commands
|
||||||
if (!success) {
|
if (!success) {
|
||||||
@ -38,9 +38,8 @@ void SlashCommandHandler::RegisterCommand(Command command) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
CommandInfos[command.aliases[0]] = command;
|
||||||
CommandInfos.push_back(command);
|
}
|
||||||
};
|
|
||||||
|
|
||||||
void SlashCommandHandler::HandleChatCommand(const std::u16string& chat, Entity* entity, const SystemAddress& sysAddr) {
|
void SlashCommandHandler::HandleChatCommand(const std::u16string& chat, Entity* entity, const SystemAddress& sysAddr) {
|
||||||
auto input = GeneralUtils::UTF16ToWTF8(chat);
|
auto input = GeneralUtils::UTF16ToWTF8(chat);
|
||||||
@ -61,11 +60,9 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& chat, Entity*
|
|||||||
if (commandHandle.requiredLevel > eGameMasterLevel::CIVILIAN) Database::Get()->InsertSlashCommandUsage(entity->GetObjectID(), input);
|
if (commandHandle.requiredLevel > eGameMasterLevel::CIVILIAN) Database::Get()->InsertSlashCommandUsage(entity->GetObjectID(), input);
|
||||||
commandHandle.handle(entity, sysAddr, args);
|
commandHandle.handle(entity, sysAddr, args);
|
||||||
} else if (entity->GetGMLevel() != eGameMasterLevel::CIVILIAN) {
|
} else if (entity->GetGMLevel() != eGameMasterLevel::CIVILIAN) {
|
||||||
// We don't need to tell civilians they aren't high enough level
|
|
||||||
error = "You are not high enough GM level to use \"" + command + "\"";
|
error = "You are not high enough GM level to use \"" + command + "\"";
|
||||||
}
|
}
|
||||||
} else if (entity->GetGMLevel() == eGameMasterLevel::CIVILIAN) {
|
} else if (entity->GetGMLevel() == eGameMasterLevel::CIVILIAN) {
|
||||||
// We don't need to tell civilians commands don't exist
|
|
||||||
error = "Command " + command + " does not exist!";
|
error = "Command " + command + " does not exist!";
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -74,41 +71,76 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& chat, Entity*
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// This commands in here so we can access the CommandInfos to display info
|
|
||||||
void GMZeroCommands::Help(Entity* entity, const SystemAddress& sysAddr, const std::string args) {
|
void GMZeroCommands::Help(Entity* entity, const SystemAddress& sysAddr, const std::string args) {
|
||||||
std::ostringstream feedback;
|
std::ostringstream feedback;
|
||||||
if (args.empty()) {
|
constexpr size_t pageSize = 10;
|
||||||
feedback << "----- Commands -----";
|
|
||||||
for (const auto& command : CommandInfos) {
|
std::string trimmedArgs = args;
|
||||||
// TODO: Limit displaying commands based on GM level they require
|
trimmedArgs.erase(trimmedArgs.begin(), std::find_if_not(trimmedArgs.begin(), trimmedArgs.end(), [](unsigned char ch) {
|
||||||
if (command.requiredLevel > entity->GetGMLevel()) continue;
|
return std::isspace(ch);
|
||||||
LOG("Help command: %s", command.aliases[0].c_str());
|
}));
|
||||||
feedback << "\n/" << command.aliases[0] << ": " << command.help;
|
trimmedArgs.erase(std::find_if_not(trimmedArgs.rbegin(), trimmedArgs.rend(), [](unsigned char ch) {
|
||||||
|
return std::isspace(ch);
|
||||||
|
}).base(), trimmedArgs.end());
|
||||||
|
|
||||||
|
std::optional<uint32_t> parsedPage = GeneralUtils::TryParse<uint32_t>(trimmedArgs);
|
||||||
|
if (trimmedArgs.empty() || parsedPage.has_value()) {
|
||||||
|
size_t page = parsedPage.value_or(1);
|
||||||
|
|
||||||
|
std::map<std::string, Command> accessibleCommands;
|
||||||
|
for (const auto& [commandName, command] : CommandInfos) {
|
||||||
|
if (command.requiredLevel <= entity->GetGMLevel()) {
|
||||||
|
accessibleCommands.emplace(commandName, command);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
bool foundCommand = false;
|
|
||||||
for (const auto& command : CommandInfos) {
|
|
||||||
if (std::ranges::find(command.aliases, args) == command.aliases.end()) continue;
|
|
||||||
|
|
||||||
if (entity->GetGMLevel() < command.requiredLevel) break;
|
size_t totalPages = (accessibleCommands.size() + pageSize - 1) / pageSize;
|
||||||
foundCommand = true;
|
|
||||||
feedback << "----- " << command.aliases.at(0) << " -----\n";
|
|
||||||
// info can be a localizable string
|
|
||||||
feedback << command.info;
|
|
||||||
if (command.aliases.size() == 1) break;
|
|
||||||
|
|
||||||
feedback << "\nAliases: ";
|
if (page < 1 || page > totalPages) {
|
||||||
for (size_t i = 0; i < command.aliases.size(); i++) {
|
feedback << "Invalid page number. Total pages: " << totalPages;
|
||||||
|
GameMessages::SendSlashCommandFeedbackText(entity, GeneralUtils::ASCIIToUTF16(feedback.str()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto it = accessibleCommands.begin();
|
||||||
|
std::advance(it, (page - 1) * pageSize);
|
||||||
|
size_t endIdx = std::min(page * pageSize, accessibleCommands.size());
|
||||||
|
|
||||||
|
feedback << "----- Commands (Page " << page << " of " << totalPages << ") -----";
|
||||||
|
for (size_t i = (page - 1) * pageSize; i < endIdx; ++i, ++it) {
|
||||||
|
feedback << "\n/" << it->first << ": " << it->second.help;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto feedbackStr = feedback.str();
|
||||||
|
if (!feedbackStr.empty()) {
|
||||||
|
GameMessages::SendSlashCommandFeedbackText(entity, GeneralUtils::ASCIIToUTF16(feedbackStr));
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto it = std::ranges::find_if(CommandInfos, [&trimmedArgs](const auto& pair) {
|
||||||
|
return std::ranges::find(pair.second.aliases, trimmedArgs) != pair.second.aliases.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
if (it != CommandInfos.end() && entity->GetGMLevel() >= it->second.requiredLevel) {
|
||||||
|
const auto& command = it->second;
|
||||||
|
feedback << "----- " << it->first << " Info -----\n";
|
||||||
|
feedback << command.info << "\n";
|
||||||
|
if (command.aliases.size() > 1) {
|
||||||
|
feedback << "Aliases: ";
|
||||||
|
for (size_t i = 0; i < command.aliases.size(); ++i) {
|
||||||
if (i > 0) feedback << ", ";
|
if (i > 0) feedback << ", ";
|
||||||
feedback << command.aliases[i];
|
feedback << command.aliases[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
// Let GameMasters know if the command doesn't exist
|
feedback << "Command not found.";
|
||||||
if (!foundCommand && entity->GetGMLevel() > eGameMasterLevel::CIVILIAN) feedback << "Command " << std::quoted(args) << " does not exist!";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto feedbackStr = feedback.str();
|
const auto feedbackStr = feedback.str();
|
||||||
if (!feedbackStr.empty()) GameMessages::SendSlashCommandFeedbackText(entity, GeneralUtils::ASCIIToUTF16(feedbackStr));
|
if (!feedbackStr.empty()) {
|
||||||
|
GameMessages::SendSlashCommandFeedbackText(entity, GeneralUtils::ASCIIToUTF16(feedbackStr));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SlashCommandHandler::SendAnnouncement(const std::string& title, const std::string& message) {
|
void SlashCommandHandler::SendAnnouncement(const std::string& title, const std::string& message) {
|
||||||
@ -878,11 +910,38 @@ void SlashCommandHandler::Startup() {
|
|||||||
};
|
};
|
||||||
RegisterCommand(TitleCommand);
|
RegisterCommand(TitleCommand);
|
||||||
|
|
||||||
|
Command ShowAllCommand{
|
||||||
|
.help = "Show all online players across World Servers",
|
||||||
|
.info = "Usage: /showall (displayZoneData: Default 1) (displayIndividualPlayers: Default 1)",
|
||||||
|
.aliases = { "showall" },
|
||||||
|
.handle = GMGreaterThanZeroCommands::ShowAll,
|
||||||
|
.requiredLevel = eGameMasterLevel::JUNIOR_MODERATOR
|
||||||
|
};
|
||||||
|
RegisterCommand(ShowAllCommand);
|
||||||
|
|
||||||
|
Command FindPlayerCommand{
|
||||||
|
.help = "Find the World Server a player is in if they are online",
|
||||||
|
.info = "Find the World Server a player is in if they are online",
|
||||||
|
.aliases = { "findplayer" },
|
||||||
|
.handle = GMGreaterThanZeroCommands::FindPlayer,
|
||||||
|
.requiredLevel = eGameMasterLevel::JUNIOR_MODERATOR
|
||||||
|
};
|
||||||
|
RegisterCommand(FindPlayerCommand);
|
||||||
|
|
||||||
|
Command SpectateCommand{
|
||||||
|
.help = "Spectate a player",
|
||||||
|
.info = "Specify a player name to spectate. They must be in the same world as you. Leave blank to stop spectating",
|
||||||
|
.aliases = { "spectate", "follow" },
|
||||||
|
.handle = GMGreaterThanZeroCommands::Spectate,
|
||||||
|
.requiredLevel = eGameMasterLevel::JUNIOR_MODERATOR
|
||||||
|
};
|
||||||
|
RegisterCommand(SpectateCommand);
|
||||||
|
|
||||||
// Register GM Zero Commands
|
// Register GM Zero Commands
|
||||||
|
|
||||||
Command HelpCommand{
|
Command HelpCommand{
|
||||||
.help = "Display command info",
|
.help = "Display command info",
|
||||||
.info = "If a command is given, display detailed info on that command. Otherwise display a list of commands with short desctiptions.",
|
.info = "If a command is given, display detailed info on that command. Otherwise display a list of commands with short descriptions.",
|
||||||
.aliases = { "help", "h"},
|
.aliases = { "help", "h"},
|
||||||
.handle = GMZeroCommands::Help,
|
.handle = GMZeroCommands::Help,
|
||||||
.requiredLevel = eGameMasterLevel::CIVILIAN
|
.requiredLevel = eGameMasterLevel::CIVILIAN
|
||||||
@ -997,4 +1056,383 @@ void SlashCommandHandler::Startup() {
|
|||||||
};
|
};
|
||||||
RegisterCommand(InstanceInfoCommand);
|
RegisterCommand(InstanceInfoCommand);
|
||||||
|
|
||||||
|
//Commands that are handled by the client
|
||||||
|
|
||||||
|
Command faqCommand{
|
||||||
|
.help = "Show the LU FAQ Page",
|
||||||
|
.info = "Show the LU FAQ Page",
|
||||||
|
.aliases = {"faq","faqs"},
|
||||||
|
.handle = GMZeroCommands::ClientHandled,
|
||||||
|
.requiredLevel = eGameMasterLevel::CIVILIAN
|
||||||
|
};
|
||||||
|
RegisterCommand(faqCommand);
|
||||||
|
|
||||||
|
Command teamChatCommand{
|
||||||
|
.help = "Send a message to your teammates.",
|
||||||
|
.info = "Send a message to your teammates.",
|
||||||
|
.aliases = {"team","t"},
|
||||||
|
.handle = GMZeroCommands::ClientHandled,
|
||||||
|
.requiredLevel = eGameMasterLevel::CIVILIAN
|
||||||
|
};
|
||||||
|
RegisterCommand(teamChatCommand);
|
||||||
|
|
||||||
|
Command showStoreCommand{
|
||||||
|
.help = "Show the LEGO shop page.",
|
||||||
|
.info = "Show the LEGO shop page.",
|
||||||
|
.aliases = {"shop","store"},
|
||||||
|
.handle = GMZeroCommands::ClientHandled,
|
||||||
|
.requiredLevel = eGameMasterLevel::CIVILIAN
|
||||||
|
};
|
||||||
|
RegisterCommand(showStoreCommand);
|
||||||
|
|
||||||
|
Command minigamesCommand{
|
||||||
|
.help = "Show the LEGO minigames page!",
|
||||||
|
.info = "Show the LEGO minigames page!",
|
||||||
|
.aliases = {"minigames"},
|
||||||
|
.handle = GMZeroCommands::ClientHandled,
|
||||||
|
.requiredLevel = eGameMasterLevel::CIVILIAN
|
||||||
|
};
|
||||||
|
RegisterCommand(minigamesCommand);
|
||||||
|
|
||||||
|
Command forumsCommand{
|
||||||
|
.help = "Show the LU Forums!",
|
||||||
|
.info = "Show the LU Forums!",
|
||||||
|
.aliases = {"forums"},
|
||||||
|
.handle = GMZeroCommands::ClientHandled,
|
||||||
|
.requiredLevel = eGameMasterLevel::CIVILIAN
|
||||||
|
};
|
||||||
|
RegisterCommand(forumsCommand);
|
||||||
|
|
||||||
|
Command exitGameCommand{
|
||||||
|
.help = "Exit to desktop",
|
||||||
|
.info = "Exit to desktop",
|
||||||
|
.aliases = {"exit","quit"},
|
||||||
|
.handle = GMZeroCommands::ClientHandled,
|
||||||
|
.requiredLevel = eGameMasterLevel::CIVILIAN
|
||||||
|
};
|
||||||
|
RegisterCommand(exitGameCommand);
|
||||||
|
|
||||||
|
Command thumbsUpCommand{
|
||||||
|
.help = "Oh, yeah!",
|
||||||
|
.info = "Oh, yeah!",
|
||||||
|
.aliases = {"thumb","thumbs","thumbsup"},
|
||||||
|
.handle = GMZeroCommands::ClientHandled,
|
||||||
|
.requiredLevel = eGameMasterLevel::CIVILIAN
|
||||||
|
};
|
||||||
|
RegisterCommand(thumbsUpCommand);
|
||||||
|
|
||||||
|
Command victoryCommand{
|
||||||
|
.help = "Victory!",
|
||||||
|
.info = "Victory!",
|
||||||
|
.aliases = {"victory!"},
|
||||||
|
.handle = GMZeroCommands::ClientHandled,
|
||||||
|
.requiredLevel = eGameMasterLevel::CIVILIAN
|
||||||
|
};
|
||||||
|
RegisterCommand(victoryCommand);
|
||||||
|
|
||||||
|
Command backflipCommand{
|
||||||
|
.help = "Do a flip!",
|
||||||
|
.info = "Do a flip!",
|
||||||
|
.aliases = {"backflip"},
|
||||||
|
.handle = GMZeroCommands::ClientHandled,
|
||||||
|
.requiredLevel = eGameMasterLevel::CIVILIAN
|
||||||
|
};
|
||||||
|
RegisterCommand(backflipCommand);
|
||||||
|
|
||||||
|
Command clapCommand{
|
||||||
|
.help = "A round of applause!",
|
||||||
|
.info = "A round of applause!",
|
||||||
|
.aliases = {"clap"},
|
||||||
|
.handle = GMZeroCommands::ClientHandled,
|
||||||
|
.requiredLevel = eGameMasterLevel::CIVILIAN
|
||||||
|
};
|
||||||
|
RegisterCommand(clapCommand);
|
||||||
|
|
||||||
|
Command logoutCharacterCommand{
|
||||||
|
.help = "Returns you to the character select screen.",
|
||||||
|
.info = "Returns you to the character select screen.",
|
||||||
|
.aliases = {"camp","logoutcharacter"},
|
||||||
|
.handle = GMZeroCommands::ClientHandled,
|
||||||
|
.requiredLevel = eGameMasterLevel::CIVILIAN
|
||||||
|
};
|
||||||
|
RegisterCommand(logoutCharacterCommand);
|
||||||
|
|
||||||
|
Command sayCommand{
|
||||||
|
.help = "Say something outloud so that everyone can hear you",
|
||||||
|
.info = "Say something outloud so that everyone can hear you",
|
||||||
|
.aliases = {"s","say"},
|
||||||
|
.handle = GMZeroCommands::ClientHandled,
|
||||||
|
.requiredLevel = eGameMasterLevel::CIVILIAN
|
||||||
|
};
|
||||||
|
RegisterCommand(sayCommand);
|
||||||
|
|
||||||
|
Command whisperCommand{
|
||||||
|
.help = "Send a private message to another player.",
|
||||||
|
.info = "Send a private message to another player.",
|
||||||
|
.aliases = {"tell","w","whisper"},
|
||||||
|
.handle = GMZeroCommands::ClientHandled,
|
||||||
|
.requiredLevel = eGameMasterLevel::CIVILIAN
|
||||||
|
};
|
||||||
|
RegisterCommand(whisperCommand);
|
||||||
|
|
||||||
|
Command locationCommand{
|
||||||
|
.help = "Output your current location on the map to the chat box.",
|
||||||
|
.info = "Output your current location on the map to the chat box.",
|
||||||
|
.aliases = {"loc","locate","location"},
|
||||||
|
.handle = GMZeroCommands::ClientHandled,
|
||||||
|
.requiredLevel = eGameMasterLevel::CIVILIAN
|
||||||
|
};
|
||||||
|
RegisterCommand(locationCommand);
|
||||||
|
|
||||||
|
Command logoutCommand{
|
||||||
|
.help = "Returns you to the login screen.",
|
||||||
|
.info = "Returns you to the login screen.",
|
||||||
|
.aliases = {"logout","logoutaccount"},
|
||||||
|
.handle = GMZeroCommands::ClientHandled,
|
||||||
|
.requiredLevel = eGameMasterLevel::CIVILIAN
|
||||||
|
};
|
||||||
|
RegisterCommand(logoutCommand);
|
||||||
|
|
||||||
|
Command shrugCommand{
|
||||||
|
.help = "I dunno...",
|
||||||
|
.info = "I dunno...",
|
||||||
|
.aliases = {"shrug"},
|
||||||
|
.handle = GMZeroCommands::ClientHandled,
|
||||||
|
.requiredLevel = eGameMasterLevel::CIVILIAN
|
||||||
|
};
|
||||||
|
RegisterCommand(shrugCommand);
|
||||||
|
|
||||||
|
Command leaveTeamCommand{
|
||||||
|
.help = "Leave your current team.",
|
||||||
|
.info = "Leave your current team.",
|
||||||
|
.aliases = {"leave","leaveteam","teamleave","tleave"},
|
||||||
|
.handle = GMZeroCommands::ClientHandled,
|
||||||
|
.requiredLevel = eGameMasterLevel::CIVILIAN
|
||||||
|
};
|
||||||
|
RegisterCommand(leaveTeamCommand);
|
||||||
|
|
||||||
|
Command teamLootTypeCommand{
|
||||||
|
.help = "[rr|ffa] Set the loot for your current team (round-robin/free for all).",
|
||||||
|
.info = "[rr|ffa] Set the loot for your current team (round-robin/free for all).",
|
||||||
|
.aliases = {"setloot","teamsetloot","tloot","tsetloot"},
|
||||||
|
.handle = GMZeroCommands::ClientHandled,
|
||||||
|
.requiredLevel = eGameMasterLevel::CIVILIAN
|
||||||
|
};
|
||||||
|
RegisterCommand(teamLootTypeCommand);
|
||||||
|
|
||||||
|
Command removeFriendCommand{
|
||||||
|
.help = "[name] Removes a player from your friends list.",
|
||||||
|
.info = "[name] Removes a player from your friends list.",
|
||||||
|
.aliases = {"removefriend"},
|
||||||
|
.handle = GMZeroCommands::ClientHandled,
|
||||||
|
.requiredLevel = eGameMasterLevel::CIVILIAN
|
||||||
|
};
|
||||||
|
RegisterCommand(removeFriendCommand);
|
||||||
|
|
||||||
|
Command yesCommand{
|
||||||
|
.help = "Aye aye, captain!",
|
||||||
|
.info = "Aye aye, captain!",
|
||||||
|
.aliases = {"yes"},
|
||||||
|
.handle = GMZeroCommands::ClientHandled,
|
||||||
|
.requiredLevel = eGameMasterLevel::CIVILIAN
|
||||||
|
};
|
||||||
|
RegisterCommand(yesCommand);
|
||||||
|
|
||||||
|
Command teamInviteCommand{
|
||||||
|
.help = "[name] Invite a player to your team.",
|
||||||
|
.info = "[name] Invite a player to your team.",
|
||||||
|
.aliases = {"invite","inviteteam","teaminvite","tinvite"},
|
||||||
|
.handle = GMZeroCommands::ClientHandled,
|
||||||
|
.requiredLevel = eGameMasterLevel::CIVILIAN
|
||||||
|
};
|
||||||
|
RegisterCommand(teamInviteCommand);
|
||||||
|
|
||||||
|
Command danceCommand{
|
||||||
|
.help = "Dance 'til you can't dance no more.",
|
||||||
|
.info = "Dance 'til you can't dance no more.",
|
||||||
|
.aliases = {"dance"},
|
||||||
|
.handle = GMZeroCommands::ClientHandled,
|
||||||
|
.requiredLevel = eGameMasterLevel::CIVILIAN
|
||||||
|
};
|
||||||
|
RegisterCommand(danceCommand);
|
||||||
|
|
||||||
|
Command sighCommand{
|
||||||
|
.help = "Another day, another brick.",
|
||||||
|
.info = "Another day, another brick.",
|
||||||
|
.aliases = {"sigh"},
|
||||||
|
.handle = GMZeroCommands::ClientHandled,
|
||||||
|
.requiredLevel = eGameMasterLevel::CIVILIAN
|
||||||
|
};
|
||||||
|
RegisterCommand(sighCommand);
|
||||||
|
|
||||||
|
Command recommendedOptionsCommand{
|
||||||
|
.help = "Sets the recommended performance options in the cfg file",
|
||||||
|
.info = "Sets the recommended performance options in the cfg file",
|
||||||
|
.aliases = {"recommendedperfoptions"},
|
||||||
|
.handle = GMZeroCommands::ClientHandled,
|
||||||
|
.requiredLevel = eGameMasterLevel::CIVILIAN
|
||||||
|
};
|
||||||
|
RegisterCommand(recommendedOptionsCommand);
|
||||||
|
|
||||||
|
Command setTeamLeaderCommand{
|
||||||
|
.help = "[name] Set the leader for your current team.",
|
||||||
|
.info = "[name] Set the leader for your current team.",
|
||||||
|
.aliases = {"leader","setleader","teamsetleader","tleader","tsetleader"},
|
||||||
|
.handle = GMZeroCommands::ClientHandled,
|
||||||
|
.requiredLevel = eGameMasterLevel::CIVILIAN
|
||||||
|
};
|
||||||
|
RegisterCommand(setTeamLeaderCommand);
|
||||||
|
|
||||||
|
Command cringeCommand{
|
||||||
|
.help = "I don't even want to talk about it...",
|
||||||
|
.info = "I don't even want to talk about it...",
|
||||||
|
.aliases = {"cringe"},
|
||||||
|
.handle = GMZeroCommands::ClientHandled,
|
||||||
|
.requiredLevel = eGameMasterLevel::CIVILIAN
|
||||||
|
};
|
||||||
|
RegisterCommand(cringeCommand);
|
||||||
|
|
||||||
|
Command talkCommand{
|
||||||
|
.help = "Jibber Jabber",
|
||||||
|
.info = "Jibber Jabber",
|
||||||
|
.aliases = {"talk"},
|
||||||
|
.handle = GMZeroCommands::ClientHandled,
|
||||||
|
.requiredLevel = eGameMasterLevel::CIVILIAN
|
||||||
|
};
|
||||||
|
RegisterCommand(talkCommand);
|
||||||
|
|
||||||
|
Command cancelQueueCommand{
|
||||||
|
.help = "Cancel Your position in the queue if you are in one.",
|
||||||
|
.info = "Cancel Your position in the queue if you are in one.",
|
||||||
|
.aliases = {"cancelqueue"},
|
||||||
|
.handle = GMZeroCommands::ClientHandled,
|
||||||
|
.requiredLevel = eGameMasterLevel::CIVILIAN
|
||||||
|
};
|
||||||
|
RegisterCommand(cancelQueueCommand);
|
||||||
|
|
||||||
|
Command lowPerformanceCommand{
|
||||||
|
.help = "Sets the default low-spec performance options in the cfg file",
|
||||||
|
.info = "Sets the default low-spec performance options in the cfg file",
|
||||||
|
.aliases = {"perfoptionslow"},
|
||||||
|
.handle = GMZeroCommands::ClientHandled,
|
||||||
|
.requiredLevel = eGameMasterLevel::CIVILIAN
|
||||||
|
};
|
||||||
|
RegisterCommand(lowPerformanceCommand);
|
||||||
|
|
||||||
|
Command kickFromTeamCommand{
|
||||||
|
.help = "[name] Kick a player from your current team.",
|
||||||
|
.info = "[name] Kick a player from your current team.",
|
||||||
|
.aliases = {"kick","kickplayer","teamkickplayer","tkick","tkickplayer"},
|
||||||
|
.handle = GMZeroCommands::ClientHandled,
|
||||||
|
.requiredLevel = eGameMasterLevel::CIVILIAN
|
||||||
|
};
|
||||||
|
RegisterCommand(kickFromTeamCommand);
|
||||||
|
|
||||||
|
Command thanksCommand{
|
||||||
|
.help = "Express your gratitude for another.",
|
||||||
|
.info = "Express your gratitude for another.",
|
||||||
|
.aliases = {"thanks"},
|
||||||
|
.handle = GMZeroCommands::ClientHandled,
|
||||||
|
.requiredLevel = eGameMasterLevel::CIVILIAN
|
||||||
|
};
|
||||||
|
RegisterCommand(thanksCommand);
|
||||||
|
|
||||||
|
Command waveCommand{
|
||||||
|
.help = "Wave to other players.",
|
||||||
|
.info = "Wave to other players.",
|
||||||
|
.aliases = {"wave"},
|
||||||
|
.handle = GMZeroCommands::ClientHandled,
|
||||||
|
.requiredLevel = eGameMasterLevel::CIVILIAN
|
||||||
|
};
|
||||||
|
RegisterCommand(waveCommand);
|
||||||
|
|
||||||
|
Command whyCommand{
|
||||||
|
.help = "Why|!?!!",
|
||||||
|
.info = "Why|!?!!",
|
||||||
|
.aliases = {"why"},
|
||||||
|
.handle = GMZeroCommands::ClientHandled,
|
||||||
|
.requiredLevel = eGameMasterLevel::CIVILIAN
|
||||||
|
};
|
||||||
|
RegisterCommand(whyCommand);
|
||||||
|
|
||||||
|
Command midPerformanceCommand{
|
||||||
|
.help = "Sets the default medium-spec performance options in the cfg file",
|
||||||
|
.info = "Sets the default medium-spec performance options in the cfg file",
|
||||||
|
.aliases = {"perfoptionsmid"},
|
||||||
|
.handle = GMZeroCommands::ClientHandled,
|
||||||
|
.requiredLevel = eGameMasterLevel::CIVILIAN
|
||||||
|
};
|
||||||
|
RegisterCommand(midPerformanceCommand);
|
||||||
|
|
||||||
|
Command highPerformanceCommand{
|
||||||
|
.help = "Sets the default high-spec performance options in the cfg file",
|
||||||
|
.info = "Sets the default high-spec performance options in the cfg file",
|
||||||
|
.aliases = {"perfoptionshigh"},
|
||||||
|
.handle = GMZeroCommands::ClientHandled,
|
||||||
|
.requiredLevel = eGameMasterLevel::CIVILIAN
|
||||||
|
};
|
||||||
|
RegisterCommand(highPerformanceCommand);
|
||||||
|
|
||||||
|
Command gaspCommand{
|
||||||
|
.help = "Oh my goodness!",
|
||||||
|
.info = "Oh my goodness!",
|
||||||
|
.aliases = {"gasp"},
|
||||||
|
.handle = GMZeroCommands::ClientHandled,
|
||||||
|
.requiredLevel = eGameMasterLevel::CIVILIAN
|
||||||
|
};
|
||||||
|
RegisterCommand(gaspCommand);
|
||||||
|
|
||||||
|
Command ignoreCommand{
|
||||||
|
.help = "[name] Add a player to your ignore list.",
|
||||||
|
.info = "[name] Add a player to your ignore list.",
|
||||||
|
.aliases = {"addignore"},
|
||||||
|
.handle = GMZeroCommands::ClientHandled,
|
||||||
|
.requiredLevel = eGameMasterLevel::CIVILIAN
|
||||||
|
};
|
||||||
|
RegisterCommand(ignoreCommand);
|
||||||
|
|
||||||
|
Command addFriendCommand{
|
||||||
|
.help = "[name] Add a player to your friends list.",
|
||||||
|
.info = "[name] Add a player to your friends list.",
|
||||||
|
.aliases = {"addfriend"},
|
||||||
|
.handle = GMZeroCommands::ClientHandled,
|
||||||
|
.requiredLevel = eGameMasterLevel::CIVILIAN
|
||||||
|
};
|
||||||
|
RegisterCommand(addFriendCommand);
|
||||||
|
|
||||||
|
Command cryCommand{
|
||||||
|
.help = "Show everyone your 'Aw' face.",
|
||||||
|
.info = "Show everyone your 'Aw' face.",
|
||||||
|
.aliases = {"cry"},
|
||||||
|
.handle = GMZeroCommands::ClientHandled,
|
||||||
|
.requiredLevel = eGameMasterLevel::CIVILIAN
|
||||||
|
};
|
||||||
|
RegisterCommand(cryCommand);
|
||||||
|
|
||||||
|
Command giggleCommand{
|
||||||
|
.help = "A good little chuckle",
|
||||||
|
.info = "A good little chuckle",
|
||||||
|
.aliases = {"giggle"},
|
||||||
|
.handle = GMZeroCommands::ClientHandled,
|
||||||
|
.requiredLevel = eGameMasterLevel::CIVILIAN
|
||||||
|
};
|
||||||
|
RegisterCommand(giggleCommand);
|
||||||
|
|
||||||
|
Command saluteCommand{
|
||||||
|
.help = "For those about to build...",
|
||||||
|
.info = "For those about to build...",
|
||||||
|
.aliases = {"salute"},
|
||||||
|
.handle = GMZeroCommands::ClientHandled,
|
||||||
|
.requiredLevel = eGameMasterLevel::CIVILIAN
|
||||||
|
};
|
||||||
|
RegisterCommand(saluteCommand);
|
||||||
|
|
||||||
|
Command removeIgnoreCommand{
|
||||||
|
.help = "[name] Removes a player from your ignore list.",
|
||||||
|
.info = "[name] Removes a player from your ignore list.",
|
||||||
|
.aliases = {"removeIgnore"},
|
||||||
|
.handle = GMZeroCommands::ClientHandled,
|
||||||
|
.requiredLevel = eGameMasterLevel::CIVILIAN
|
||||||
|
};
|
||||||
|
RegisterCommand(removeIgnoreCommand);
|
||||||
}
|
}
|
||||||
|
@ -41,6 +41,7 @@
|
|||||||
#include "ScriptedActivityComponent.h"
|
#include "ScriptedActivityComponent.h"
|
||||||
#include "SkillComponent.h"
|
#include "SkillComponent.h"
|
||||||
#include "TriggerComponent.h"
|
#include "TriggerComponent.h"
|
||||||
|
#include "RigidbodyPhantomPhysicsComponent.h"
|
||||||
|
|
||||||
// Enums
|
// Enums
|
||||||
#include "eGameMasterLevel.h"
|
#include "eGameMasterLevel.h"
|
||||||
@ -704,7 +705,7 @@ namespace DEVGMCommands {
|
|||||||
auto tables = query.execQuery();
|
auto tables = query.execQuery();
|
||||||
|
|
||||||
while (!tables.eof()) {
|
while (!tables.eof()) {
|
||||||
std::string message = std::to_string(tables.getIntField(0)) + " - " + tables.getStringField(1);
|
std::string message = std::to_string(tables.getIntField("id")) + " - " + tables.getStringField("name");
|
||||||
ChatPackets::SendSystemMessage(sysAddr, GeneralUtils::UTF8ToUTF16(message, message.size()));
|
ChatPackets::SendSystemMessage(sysAddr, GeneralUtils::UTF8ToUTF16(message, message.size()));
|
||||||
tables.nextRow();
|
tables.nextRow();
|
||||||
}
|
}
|
||||||
@ -1129,8 +1130,13 @@ namespace DEVGMCommands {
|
|||||||
void SpawnPhysicsVerts(Entity* entity, const SystemAddress& sysAddr, const std::string args) {
|
void SpawnPhysicsVerts(Entity* entity, const SystemAddress& sysAddr, const std::string args) {
|
||||||
//Go tell physics to spawn all the vertices:
|
//Go tell physics to spawn all the vertices:
|
||||||
auto entities = Game::entityManager->GetEntitiesByComponent(eReplicaComponentType::PHANTOM_PHYSICS);
|
auto entities = Game::entityManager->GetEntitiesByComponent(eReplicaComponentType::PHANTOM_PHYSICS);
|
||||||
for (auto en : entities) {
|
for (const auto* en : entities) {
|
||||||
auto phys = static_cast<PhantomPhysicsComponent*>(en->GetComponent(eReplicaComponentType::PHANTOM_PHYSICS));
|
const auto* phys = static_cast<PhantomPhysicsComponent*>(en->GetComponent(eReplicaComponentType::PHANTOM_PHYSICS));
|
||||||
|
if (phys)
|
||||||
|
phys->SpawnVertices();
|
||||||
|
}
|
||||||
|
for (const auto* en : Game::entityManager->GetEntitiesByComponent(eReplicaComponentType::RIGID_BODY_PHANTOM_PHYSICS)) {
|
||||||
|
const auto* phys = en->GetComponent<RigidbodyPhantomPhysicsComponent>();
|
||||||
if (phys)
|
if (phys)
|
||||||
phys->SpawnVertices();
|
phys->SpawnVertices();
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
#ifndef DEVGMCOMMANDS_H
|
||||||
|
#define DEVGMCOMMANDS_H
|
||||||
|
|
||||||
namespace DEVGMCommands {
|
namespace DEVGMCommands {
|
||||||
void SetGMLevel(Entity* entity, const SystemAddress& sysAddr, const std::string args);
|
void SetGMLevel(Entity* entity, const SystemAddress& sysAddr, const std::string args);
|
||||||
void ToggleNameplate(Entity* entity, const SystemAddress& sysAddr, const std::string args);
|
void ToggleNameplate(Entity* entity, const SystemAddress& sysAddr, const std::string args);
|
||||||
@ -71,3 +74,5 @@ namespace DEVGMCommands {
|
|||||||
void CastSkill(Entity* entity, const SystemAddress& sysAddr, const std::string args);
|
void CastSkill(Entity* entity, const SystemAddress& sysAddr, const std::string args);
|
||||||
void DeleteInven(Entity* entity, const SystemAddress& sysAddr, const std::string args);
|
void DeleteInven(Entity* entity, const SystemAddress& sysAddr, const std::string args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif //!DEVGMCOMMANDS_H
|
||||||
|
@ -287,4 +287,54 @@ namespace GMGreaterThanZeroCommands {
|
|||||||
std::string name = entity->GetCharacter()->GetName() + " - " + args;
|
std::string name = entity->GetCharacter()->GetName() + " - " + args;
|
||||||
GameMessages::SendSetName(entity->GetObjectID(), GeneralUtils::UTF8ToUTF16(name), UNASSIGNED_SYSTEM_ADDRESS);
|
GameMessages::SendSetName(entity->GetObjectID(), GeneralUtils::UTF8ToUTF16(name), UNASSIGNED_SYSTEM_ADDRESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ShowAll(Entity* entity, const SystemAddress& sysAddr, const std::string args) {
|
||||||
|
bool displayZoneData = true;
|
||||||
|
bool displayIndividualPlayers = true;
|
||||||
|
const auto splitArgs = GeneralUtils::SplitString(args, ' ');
|
||||||
|
|
||||||
|
if (!splitArgs.empty() && !splitArgs.at(0).empty()) displayZoneData = splitArgs.at(0) == "1";
|
||||||
|
if (splitArgs.size() > 1) displayIndividualPlayers = splitArgs.at(1) == "1";
|
||||||
|
|
||||||
|
ShowAllRequest request {
|
||||||
|
.requestor = entity->GetObjectID(),
|
||||||
|
.displayZoneData = displayZoneData,
|
||||||
|
.displayIndividualPlayers = displayIndividualPlayers
|
||||||
|
};
|
||||||
|
|
||||||
|
CBITSTREAM;
|
||||||
|
request.Serialize(bitStream);
|
||||||
|
Game::chatServer->Send(&bitStream, SYSTEM_PRIORITY, RELIABLE, 0, Game::chatSysAddr, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FindPlayer(Entity* entity, const SystemAddress& sysAddr, const std::string args) {
|
||||||
|
if (args.empty()) {
|
||||||
|
GameMessages::SendSlashCommandFeedbackText(entity, u"No player Given");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
FindPlayerRequest request {
|
||||||
|
.requestor = entity->GetObjectID(),
|
||||||
|
.playerName = LUWString(args)
|
||||||
|
};
|
||||||
|
|
||||||
|
CBITSTREAM;
|
||||||
|
request.Serialize(bitStream);
|
||||||
|
Game::chatServer->Send(&bitStream, SYSTEM_PRIORITY, RELIABLE, 0, Game::chatSysAddr, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Spectate(Entity* entity, const SystemAddress& sysAddr, const std::string args) {
|
||||||
|
if (args.empty()) {
|
||||||
|
GameMessages::SendForceCameraTargetCycle(entity, false, eCameraTargetCyclingMode::DISALLOW_CYCLING, entity->GetObjectID());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto player = PlayerManager::GetPlayer(args);
|
||||||
|
if (!player) {
|
||||||
|
GameMessages::SendSlashCommandFeedbackText(entity, u"Player not found");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
GameMessages::SendSlashCommandFeedbackText(entity, u"Spectating Player");
|
||||||
|
GameMessages::SendForceCameraTargetCycle(entity, false, eCameraTargetCyclingMode::DISALLOW_CYCLING, player->GetObjectID());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
#ifndef GMGREATERTHANZEROCOMMANDS_H
|
||||||
|
#define GMGREATERTHANZEROCOMMANDS_H
|
||||||
|
|
||||||
namespace GMGreaterThanZeroCommands {
|
namespace GMGreaterThanZeroCommands {
|
||||||
void Kick(Entity* entity, const SystemAddress& sysAddr, const std::string args);
|
void Kick(Entity* entity, const SystemAddress& sysAddr, const std::string args);
|
||||||
void MailItem(Entity* entity, const SystemAddress& sysAddr, const std::string args);
|
void MailItem(Entity* entity, const SystemAddress& sysAddr, const std::string args);
|
||||||
@ -10,4 +13,9 @@ namespace GMGreaterThanZeroCommands {
|
|||||||
void GmInvis(Entity* entity, const SystemAddress& sysAddr, const std::string args);
|
void GmInvis(Entity* entity, const SystemAddress& sysAddr, const std::string args);
|
||||||
void SetName(Entity* entity, const SystemAddress& sysAddr, const std::string args);
|
void SetName(Entity* entity, const SystemAddress& sysAddr, const std::string args);
|
||||||
void Title(Entity* entity, const SystemAddress& sysAddr, const std::string args);
|
void Title(Entity* entity, const SystemAddress& sysAddr, const std::string args);
|
||||||
|
void ShowAll(Entity* entity, const SystemAddress& sysAddr, const std::string args);
|
||||||
|
void FindPlayer(Entity* entity, const SystemAddress& sysAddr, const std::string args);
|
||||||
|
void Spectate(Entity* entity, const SystemAddress& sysAddr, const std::string args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif //!GMGREATERTHANZEROCOMMANDS_H
|
||||||
|
@ -224,5 +224,9 @@ namespace GMZeroCommands {
|
|||||||
|
|
||||||
ChatPackets::SendSystemMessage(sysAddr, u"Map: " + (GeneralUtils::to_u16string(zoneId.GetMapID())) + u"\nClone: " + (GeneralUtils::to_u16string(zoneId.GetCloneID())) + u"\nInstance: " + (GeneralUtils::to_u16string(zoneId.GetInstanceID())));
|
ChatPackets::SendSystemMessage(sysAddr, u"Map: " + (GeneralUtils::to_u16string(zoneId.GetMapID())) + u"\nClone: " + (GeneralUtils::to_u16string(zoneId.GetCloneID())) + u"\nInstance: " + (GeneralUtils::to_u16string(zoneId.GetInstanceID())));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//For client side commands
|
||||||
|
void ClientHandled(Entity* entity, const SystemAddress& sysAddr, const std::string args) {}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
#ifndef GMZEROCOMMANDS_H
|
||||||
|
#define GMZEROCOMMANDS_H
|
||||||
|
|
||||||
namespace GMZeroCommands {
|
namespace GMZeroCommands {
|
||||||
void Help(Entity* entity, const SystemAddress& sysAddr, const std::string args);
|
void Help(Entity* entity, const SystemAddress& sysAddr, const std::string args);
|
||||||
void Credits(Entity* entity, const SystemAddress& sysAddr, const std::string args);
|
void Credits(Entity* entity, const SystemAddress& sysAddr, const std::string args);
|
||||||
@ -12,4 +15,7 @@ namespace GMZeroCommands {
|
|||||||
void LeaveZone(Entity* entity, const SystemAddress& sysAddr, const std::string args);
|
void LeaveZone(Entity* entity, const SystemAddress& sysAddr, const std::string args);
|
||||||
void Resurrect(Entity* entity, const SystemAddress& sysAddr, const std::string args);
|
void Resurrect(Entity* entity, const SystemAddress& sysAddr, const std::string args);
|
||||||
void InstanceInfo(Entity* entity, const SystemAddress& sysAddr, const std::string args);
|
void InstanceInfo(Entity* entity, const SystemAddress& sysAddr, const std::string args);
|
||||||
|
void ClientHandled(Entity* entity, const SystemAddress& sysAddr, const std::string args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif //!GMZEROCOMMANDS_H
|
||||||
|
@ -285,7 +285,6 @@ void ParseXml(const std::string& file) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (zoneID.value() != currentZoneID) {
|
if (zoneID.value() != currentZoneID) {
|
||||||
LOG_DEBUG("Skipping (%s) %i location because it is in %i and not the current zone (%i)", name, lot, zoneID.value(), currentZoneID);
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,6 +40,8 @@
|
|||||||
#include "BitStreamUtils.h"
|
#include "BitStreamUtils.h"
|
||||||
#include "Start.h"
|
#include "Start.h"
|
||||||
#include "Server.h"
|
#include "Server.h"
|
||||||
|
#include "CDZoneTableTable.h"
|
||||||
|
#include "eGameMasterLevel.h"
|
||||||
|
|
||||||
namespace Game {
|
namespace Game {
|
||||||
Logger* logger = nullptr;
|
Logger* logger = nullptr;
|
||||||
@ -186,15 +188,20 @@ int main(int argc, char** argv) {
|
|||||||
std::cout << "Enter a username: ";
|
std::cout << "Enter a username: ";
|
||||||
std::cin >> username;
|
std::cin >> username;
|
||||||
|
|
||||||
|
const auto checkIsAdmin = []() {
|
||||||
|
std::string admin;
|
||||||
|
std::cout << "What level of privilege should this account have? Please enter a number between 0 (Player) and 9 (Admin) inclusive. No entry will default to 0." << std::endl;
|
||||||
|
std::cin >> admin;
|
||||||
|
return admin;
|
||||||
|
};
|
||||||
|
|
||||||
auto accountId = Database::Get()->GetAccountInfo(username);
|
auto accountId = Database::Get()->GetAccountInfo(username);
|
||||||
if (accountId) {
|
if (accountId && accountId->id != 0) {
|
||||||
LOG("Account with name \"%s\" already exists", username.c_str());
|
LOG("Account with name \"%s\" already exists", username.c_str());
|
||||||
std::cout << "Do you want to change the password of that account? [y/n]?";
|
std::cout << "Do you want to change the password of that account? [y/n]?";
|
||||||
std::string prompt = "";
|
std::string prompt = "";
|
||||||
std::cin >> prompt;
|
std::cin >> prompt;
|
||||||
if (prompt == "y" || prompt == "yes") {
|
if (prompt == "y" || prompt == "yes") {
|
||||||
if (accountId->id == 0) return EXIT_FAILURE;
|
|
||||||
|
|
||||||
//Read the password from the console without echoing it.
|
//Read the password from the console without echoing it.
|
||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
//This function is obsolete, but it only meant to be used by the
|
//This function is obsolete, but it only meant to be used by the
|
||||||
@ -219,6 +226,20 @@ int main(int argc, char** argv) {
|
|||||||
} else {
|
} else {
|
||||||
LOG("Account \"%s\" was not updated.", username.c_str());
|
LOG("Account \"%s\" was not updated.", username.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::cout << "Update admin privileges? [y/n]? ";
|
||||||
|
std::string admin;
|
||||||
|
std::cin >> admin;
|
||||||
|
bool updateAdmin = admin == "y" || admin == "yes";
|
||||||
|
if (updateAdmin) {
|
||||||
|
auto gmLevel = GeneralUtils::TryParse<int32_t>(checkIsAdmin()).value_or(0);
|
||||||
|
if (gmLevel > 9 || gmLevel < 0) {
|
||||||
|
LOG("Invalid admin level. Defaulting to 0");
|
||||||
|
gmLevel = 0;
|
||||||
|
}
|
||||||
|
Database::Get()->UpdateAccountGmLevel(accountId->id, static_cast<eGameMasterLevel>(gmLevel));
|
||||||
|
}
|
||||||
|
|
||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -249,6 +270,17 @@ int main(int argc, char** argv) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
LOG("Account created successfully!");
|
LOG("Account created successfully!");
|
||||||
|
|
||||||
|
accountId = Database::Get()->GetAccountInfo(username);
|
||||||
|
if (accountId) {
|
||||||
|
auto gmLevel = GeneralUtils::TryParse<int32_t>(checkIsAdmin()).value_or(0);
|
||||||
|
if (gmLevel > 9 || gmLevel < 0) {
|
||||||
|
LOG("Invalid admin level. Defaulting to 0");
|
||||||
|
gmLevel = 0;
|
||||||
|
}
|
||||||
|
Database::Get()->UpdateAccountGmLevel(accountId->id, static_cast<eGameMasterLevel>(gmLevel));
|
||||||
|
}
|
||||||
|
|
||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -277,6 +309,17 @@ int main(int argc, char** argv) {
|
|||||||
PersistentIDManager::Initialize();
|
PersistentIDManager::Initialize();
|
||||||
Game::im = new InstanceManager(Game::logger, Game::server->GetIP());
|
Game::im = new InstanceManager(Game::logger, Game::server->GetIP());
|
||||||
|
|
||||||
|
//Get CDClient initial information
|
||||||
|
try {
|
||||||
|
CDClientManager::LoadValuesFromDatabase();
|
||||||
|
} catch (CppSQLite3Exception& e) {
|
||||||
|
LOG("Failed to initialize CDServer SQLite Database");
|
||||||
|
LOG("May be caused by corrupted file: %s", (Game::assetManager->GetResPath() / "CDServer.sqlite").string().c_str());
|
||||||
|
LOG("Error: %s", e.errorMessage());
|
||||||
|
LOG("Error Code: %i", e.errorCode());
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
//Depending on the config, start up servers:
|
//Depending on the config, start up servers:
|
||||||
if (Game::config->GetValue("prestart_servers") != "0") {
|
if (Game::config->GetValue("prestart_servers") != "0") {
|
||||||
StartChatServer();
|
StartChatServer();
|
||||||
@ -382,6 +425,7 @@ int main(int argc, char** argv) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void HandlePacket(Packet* packet) {
|
void HandlePacket(Packet* packet) {
|
||||||
|
if (packet->length < 1) return;
|
||||||
if (packet->data[0] == ID_DISCONNECTION_NOTIFICATION) {
|
if (packet->data[0] == ID_DISCONNECTION_NOTIFICATION) {
|
||||||
LOG("A server has disconnected");
|
LOG("A server has disconnected");
|
||||||
|
|
||||||
|
@ -112,6 +112,31 @@ void dNavMesh::LoadNavmesh() {
|
|||||||
m_NavMesh = mesh;
|
m_NavMesh = mesh;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NiPoint3 dNavMesh::NearestPoint(const NiPoint3& location, const float halfExtent) const {
|
||||||
|
NiPoint3 toReturn = location;
|
||||||
|
if (m_NavMesh != nullptr) {
|
||||||
|
float pos[3];
|
||||||
|
pos[0] = location.x;
|
||||||
|
pos[1] = location.y;
|
||||||
|
pos[2] = location.z;
|
||||||
|
|
||||||
|
dtPolyRef nearestRef = 0;
|
||||||
|
float polyPickExt[3] = { halfExtent, halfExtent, halfExtent };
|
||||||
|
float nearestPoint[3] = { 0.0f, 0.0f, 0.0f };
|
||||||
|
dtQueryFilter filter{};
|
||||||
|
|
||||||
|
auto hasPoly = m_NavQuery->findNearestPoly(pos, polyPickExt, &filter, &nearestRef, nearestPoint);
|
||||||
|
if (hasPoly != DT_SUCCESS) {
|
||||||
|
toReturn = location;
|
||||||
|
} else {
|
||||||
|
toReturn.x = nearestPoint[0];
|
||||||
|
toReturn.y = nearestPoint[1];
|
||||||
|
toReturn.z = nearestPoint[2];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return toReturn;
|
||||||
|
}
|
||||||
|
|
||||||
float dNavMesh::GetHeightAtPoint(const NiPoint3& location, const float halfExtentsHeight) const {
|
float dNavMesh::GetHeightAtPoint(const NiPoint3& location, const float halfExtentsHeight) const {
|
||||||
if (m_NavMesh == nullptr) {
|
if (m_NavMesh == nullptr) {
|
||||||
return location.y;
|
return location.y;
|
||||||
|
@ -29,7 +29,7 @@ public:
|
|||||||
*/
|
*/
|
||||||
float GetHeightAtPoint(const NiPoint3& location, const float halfExtentsHeight = 32.0f) const;
|
float GetHeightAtPoint(const NiPoint3& location, const float halfExtentsHeight = 32.0f) const;
|
||||||
std::vector<NiPoint3> GetPath(const NiPoint3& startPos, const NiPoint3& endPos, float speed = 10.0f);
|
std::vector<NiPoint3> GetPath(const NiPoint3& startPos, const NiPoint3& endPos, float speed = 10.0f);
|
||||||
|
NiPoint3 NearestPoint(const NiPoint3& location, const float halfExtent = 32.0f) const;
|
||||||
bool IsNavmeshLoaded() { return m_NavMesh != nullptr; }
|
bool IsNavmeshLoaded() { return m_NavMesh != nullptr; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -82,7 +82,7 @@ void AuthPackets::SendHandshake(dServer* server, const SystemAddress& sysAddr, c
|
|||||||
if (serverType == ServerType::Auth) bitStream.Write(ServiceId::Auth);
|
if (serverType == ServerType::Auth) bitStream.Write(ServiceId::Auth);
|
||||||
else if (serverType == ServerType::World) bitStream.Write(ServiceId::World);
|
else if (serverType == ServerType::World) bitStream.Write(ServiceId::World);
|
||||||
else bitStream.Write(ServiceId::General);
|
else bitStream.Write(ServiceId::General);
|
||||||
bitStream.Write<uint64_t>(215523405360);
|
bitStream.Write<uint64_t>(215523470896);
|
||||||
|
|
||||||
server->Send(bitStream, sysAddr, false);
|
server->Send(bitStream, sysAddr, false);
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
#include "MessageIdentifiers.h"
|
#include "MessageIdentifiers.h"
|
||||||
#include "BitStream.h"
|
#include "BitStream.h"
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
enum class eConnectionType : uint16_t;
|
enum class eConnectionType : uint16_t;
|
||||||
|
|
||||||
|
@ -12,6 +12,30 @@
|
|||||||
#include "eConnectionType.h"
|
#include "eConnectionType.h"
|
||||||
#include "eChatMessageType.h"
|
#include "eChatMessageType.h"
|
||||||
|
|
||||||
|
void ShowAllRequest::Serialize(RakNet::BitStream& bitStream) {
|
||||||
|
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::SHOW_ALL);
|
||||||
|
bitStream.Write(this->requestor);
|
||||||
|
bitStream.Write(this->displayZoneData);
|
||||||
|
bitStream.Write(this->displayIndividualPlayers);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShowAllRequest::Deserialize(RakNet::BitStream& inStream) {
|
||||||
|
inStream.Read(this->requestor);
|
||||||
|
inStream.Read(this->displayZoneData);
|
||||||
|
inStream.Read(this->displayIndividualPlayers);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FindPlayerRequest::Serialize(RakNet::BitStream& bitStream) {
|
||||||
|
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::WHO);
|
||||||
|
bitStream.Write(this->requestor);
|
||||||
|
bitStream.Write(this->playerName);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FindPlayerRequest::Deserialize(RakNet::BitStream& inStream) {
|
||||||
|
inStream.Read(this->requestor);
|
||||||
|
inStream.Read(this->playerName);
|
||||||
|
}
|
||||||
|
|
||||||
void ChatPackets::SendChatMessage(const SystemAddress& sysAddr, char chatChannel, const std::string& senderName, LWOOBJID playerObjectID, bool senderMythran, const std::u16string& message) {
|
void ChatPackets::SendChatMessage(const SystemAddress& sysAddr, char chatChannel, const std::string& senderName, LWOOBJID playerObjectID, bool senderMythran, const std::u16string& message) {
|
||||||
CBITSTREAM;
|
CBITSTREAM;
|
||||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::GENERAL_CHAT_MESSAGE);
|
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::GENERAL_CHAT_MESSAGE);
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user