mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2025-12-19 20:20:08 -06:00
Compare commits
32 Commits
chat-http-
...
fix-disman
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c72ce4003b | ||
|
|
cce5755366 | ||
|
|
e966d3a644 | ||
|
|
9328021339 | ||
|
|
d1134fdd62 | ||
|
|
efa658bc31 | ||
|
|
e59525d2ae | ||
|
|
0348db72a5 | ||
|
|
debc2a96e2 | ||
|
|
8ae1a8ff7c | ||
|
|
f0960d48b2 | ||
|
|
dc430d9758 | ||
|
|
dea10c6d56 | ||
|
|
ed00551982 | ||
|
|
d6cac65a8d | ||
|
|
d8f079cb1b | ||
|
|
c8e0bb0db0 | ||
|
|
d9d262d3f1 | ||
|
|
d0a5678290 | ||
|
|
35321b22d9 | ||
|
|
8837b110ab | ||
|
|
09a8c99f3e | ||
|
|
e3b108e00e | ||
|
|
9f382aca42 | ||
|
|
4d1395e522 | ||
| 9e36510c6b | |||
|
|
2ca61c3e57 | ||
| 07cb19cc30 | |||
|
|
794b254fe7 | ||
|
|
ab7f6f0b57 | ||
|
|
58cc569c75 | ||
|
|
35c463656d |
@@ -1,5 +1,11 @@
|
||||
cmake_minimum_required(VERSION 3.25)
|
||||
project(Darkflame)
|
||||
|
||||
# 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)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
@@ -96,7 +102,6 @@ set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
|
||||
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
|
||||
|
||||
find_package(MariaDB)
|
||||
find_package(JSON)
|
||||
|
||||
# Create a /resServer directory
|
||||
make_directory(${CMAKE_BINARY_DIR}/resServer)
|
||||
@@ -285,7 +290,7 @@ add_subdirectory(dPhysics)
|
||||
add_subdirectory(dServer)
|
||||
|
||||
# Create a list of common libraries shared between all binaries
|
||||
set(COMMON_LIBRARIES "dCommon" "dDatabase" "dNet" "raknet" "MariaDB::ConnCpp" "magic_enum" "nlohmann_json::nlohmann_json")
|
||||
set(COMMON_LIBRARIES "dCommon" "dDatabase" "dNet" "raknet" "MariaDB::ConnCpp" "magic_enum")
|
||||
|
||||
# Add platform specific common libraries
|
||||
if(UNIX)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
PROJECT_VERSION_MAJOR=1
|
||||
PROJECT_VERSION_MINOR=1
|
||||
PROJECT_VERSION_PATCH=1
|
||||
PROJECT_VERSION_MAJOR=2
|
||||
PROJECT_VERSION_MINOR=3
|
||||
PROJECT_VERSION_PATCH=0
|
||||
|
||||
# Debugging
|
||||
# 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
|
||||
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/migrations /app/migrations
|
||||
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
|
||||
# of a mount clobbering the copy from above
|
||||
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
|
||||
# and therefore sudo doesn't exist
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
include(FetchContent)
|
||||
|
||||
message(STATUS "Fetching json...")
|
||||
|
||||
FetchContent_Declare(
|
||||
json
|
||||
GIT_REPOSITORY https://github.com/nlohmann/json
|
||||
GIT_TAG v3.11.3
|
||||
)
|
||||
|
||||
FetchContent_GetProperties(json)
|
||||
if(NOT json_POPULATED)
|
||||
FetchContent_Populate(json)
|
||||
add_subdirectory(${json_SOURCE_DIR} ${json_BINARY_DIR} EXCLUDE_FROM_ALL)
|
||||
endif()
|
||||
|
||||
FetchContent_MakeAvailable(json)
|
||||
|
||||
message(STATUS "json fetched and is now ready.")
|
||||
set(JSON_FOUND TRUE)
|
||||
@@ -1,5 +1,4 @@
|
||||
set(DCHATSERVER_SOURCES
|
||||
"ChatWebApi.cpp"
|
||||
"ChatIgnoreList.cpp"
|
||||
"ChatPacketHandler.cpp"
|
||||
"PlayerContainer.cpp"
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
#include "eWorldMessageType.h"
|
||||
#include "ChatIgnoreList.h"
|
||||
#include "StringifiedEnum.h"
|
||||
#include "ChatWebApi.h"
|
||||
|
||||
#include "Game.h"
|
||||
#include "Server.h"
|
||||
@@ -37,7 +36,7 @@ namespace Game {
|
||||
AssetManager* assetManager = nullptr;
|
||||
Game::signal_t lastSignal = 0;
|
||||
std::mt19937 randomEngine;
|
||||
PlayerContainer playerContainer;
|
||||
PlayerContainer playerContainer;
|
||||
}
|
||||
|
||||
void HandlePacket(Packet* packet);
|
||||
@@ -123,9 +122,6 @@ int main(int argc, char** argv) {
|
||||
uint32_t framesSinceMasterDisconnect = 0;
|
||||
uint32_t framesSinceLastSQLPing = 0;
|
||||
|
||||
// start the web api thread
|
||||
std::thread webAPIThread(ChatWebApi::Listen, ourPort);
|
||||
|
||||
Game::logger->Flush(); // once immediately before main loop
|
||||
while (!Game::ShouldShutdown()) {
|
||||
//Check if we're still connected to master:
|
||||
@@ -178,10 +174,6 @@ int main(int argc, char** argv) {
|
||||
delete Game::server;
|
||||
delete Game::logger;
|
||||
delete Game::config;
|
||||
|
||||
// rejoin the web api thread
|
||||
ChatWebApi::Stop();
|
||||
webAPIThread.join();
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -1,93 +0,0 @@
|
||||
#include "ChatWebApi.h"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "dCommonVars.h"
|
||||
#include "eConnectionType.h"
|
||||
#include "eChatMessageType.h"
|
||||
#include "httplib.h"
|
||||
#include "dServer.h"
|
||||
#include "PlayerContainer.h"
|
||||
#include "dConfig.h"
|
||||
#include "httplib.h"
|
||||
#include "nlohmann/json.hpp"
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
namespace {
|
||||
httplib::Server m_APIServer;
|
||||
}
|
||||
|
||||
void ChatWebApi::Listen(const uint32_t port) {
|
||||
if (Game::config->GetValue("enable_chat_web_api") != "1") {
|
||||
LOG("Chat Web API is disabled");
|
||||
return;
|
||||
}
|
||||
LOG("Chat Web API is enabled, starting web server...");
|
||||
|
||||
m_APIServer.Post("/announce", [](const httplib::Request& req, httplib::Response& res) {
|
||||
const json data = json::parse(req.body);
|
||||
if (!data.contains("title")) {
|
||||
res.set_content("{\"error\":\"Missing paramater: title\"}", "application/json");
|
||||
res.status = 400;
|
||||
return;
|
||||
}
|
||||
std::string title = data["title"];
|
||||
if (!data.contains("message")) {
|
||||
res.set_content("{\"error\":\"Missing paramater: message\"}", "application/json");
|
||||
res.status = 400;
|
||||
return;
|
||||
}
|
||||
std::string message = data["message"];
|
||||
// build and send the packet to all world servers
|
||||
CBITSTREAM;
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::GM_ANNOUNCE);
|
||||
bitStream.Write<uint32_t>(title.size());
|
||||
bitStream.Write(title);
|
||||
bitStream.Write<uint32_t>(message.size());
|
||||
bitStream.Write(message);
|
||||
Game::server->Send(bitStream, UNASSIGNED_SYSTEM_ADDRESS, true);
|
||||
});
|
||||
|
||||
m_APIServer.Get("/players", [](const httplib::Request& req, httplib::Response& res) {
|
||||
auto data = json::array();
|
||||
for (auto& [playerID, playerData ]: Game::playerContainer.GetAllPlayers()){
|
||||
if (!playerData) continue;
|
||||
data.push_back(playerData.to_json());
|
||||
}
|
||||
res.set_content(data.dump(), "application/json");
|
||||
if (data.empty()) res.status = 204;
|
||||
});
|
||||
|
||||
m_APIServer.Get("/teams", [](const httplib::Request& req, httplib::Response& res) {
|
||||
auto data = json::array();
|
||||
for (auto& teamData: Game::playerContainer.GetAllTeams()){
|
||||
if (!teamData) continue;
|
||||
json toInsert;
|
||||
toInsert["id"] = teamData->teamID;
|
||||
toInsert["loot_flag"] = teamData->lootFlag;
|
||||
toInsert["local"] = teamData->local;
|
||||
|
||||
auto& leader = Game::playerContainer.GetPlayerData(teamData->leaderID);
|
||||
toInsert["leader"] = leader.to_json();
|
||||
|
||||
json members;
|
||||
for (auto& member : teamData->memberIDs){
|
||||
auto playerData = Game::playerContainer.GetPlayerData(member);
|
||||
if (!playerData) continue;
|
||||
members.push_back(playerData.to_json());
|
||||
}
|
||||
toInsert["members"] = members;
|
||||
data.push_back(toInsert);
|
||||
}
|
||||
res.set_content(data.dump(), "application/json");
|
||||
if (data.empty()) res.status = 204;
|
||||
});
|
||||
|
||||
m_APIServer.listen(Game::config->GetValue("chat_web_api_listen_address").c_str(), port);
|
||||
};
|
||||
|
||||
void ChatWebApi::Stop(){
|
||||
LOG("Stopping Chat Web API server...");
|
||||
m_APIServer.stop();
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
#include <cstdint>
|
||||
|
||||
namespace ChatWebApi {
|
||||
void Listen(const uint32_t port);
|
||||
void Stop();
|
||||
};
|
||||
@@ -1,9 +1,7 @@
|
||||
#include "PlayerContainer.h"
|
||||
|
||||
#include "dNetCommon.h"
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
|
||||
#include "dNetCommon.h"
|
||||
#include "Game.h"
|
||||
#include "Logger.h"
|
||||
#include "ChatPacketHandler.h"
|
||||
@@ -13,24 +11,7 @@
|
||||
#include "eConnectionType.h"
|
||||
#include "ChatPackets.h"
|
||||
#include "dConfig.h"
|
||||
#include "eChatMessageType.h"
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
const json PlayerData::to_json() const {
|
||||
json data;
|
||||
data["id"] = this->playerID;
|
||||
data["name"] = this->playerName;
|
||||
data["gm_level"] = this->gmLevel;
|
||||
data["muted"] = this->GetIsMuted();
|
||||
|
||||
json zoneID;
|
||||
zoneID["map_id"] = std::to_string(this->zoneID.GetMapID());
|
||||
zoneID["instance_id"] = std::to_string(this->zoneID.GetInstanceID());
|
||||
zoneID["clone_id"] = std::to_string(this->zoneID.GetCloneID());
|
||||
data["zone_id"] = zoneID;
|
||||
return data;
|
||||
}
|
||||
#include "eChatMessageType.h"
|
||||
|
||||
void PlayerContainer::Initialize() {
|
||||
m_MaxNumberOfBestFriends =
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
#include "Game.h"
|
||||
#include "dServer.h"
|
||||
#include <unordered_map>
|
||||
#include "nlohmann/json.hpp"
|
||||
|
||||
enum class eGameMasterLevel : uint8_t;
|
||||
|
||||
@@ -37,8 +36,6 @@ struct PlayerData {
|
||||
return muteExpire == 1 || muteExpire > time(NULL);
|
||||
}
|
||||
|
||||
const nlohmann::json to_json() const;
|
||||
|
||||
SystemAddress sysAddr{};
|
||||
LWOZONEID zoneID{};
|
||||
LWOOBJID playerID = LWOOBJID_EMPTY;
|
||||
@@ -91,7 +88,6 @@ public:
|
||||
LWOOBJID GetId(const std::u16string& playerName);
|
||||
uint32_t GetMaxNumberOfBestFriends() { return m_MaxNumberOfBestFriends; }
|
||||
uint32_t GetMaxNumberOfFriends() { return m_MaxNumberOfFriends; }
|
||||
const std::vector<TeamData*>& GetAllTeams() { return mTeams;};
|
||||
|
||||
private:
|
||||
LWOOBJID m_TeamIDCounter = 0;
|
||||
|
||||
@@ -31,22 +31,22 @@ public:
|
||||
|
||||
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
|
||||
* @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
|
||||
* @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.
|
||||
@@ -62,7 +62,7 @@ private:
|
||||
T value;
|
||||
|
||||
//! 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));
|
||||
for (uint32_t i = 0; i < this->key.length(); ++i) {
|
||||
packet.Write<uint16_t>(this->key[i]);
|
||||
@@ -70,7 +70,7 @@ private:
|
||||
}
|
||||
|
||||
//! 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(this->value);
|
||||
}
|
||||
@@ -90,7 +90,7 @@ public:
|
||||
/*!
|
||||
\return The value
|
||||
*/
|
||||
const T& GetValue(void) { return this->value; }
|
||||
const T& GetValue(void) const { return this->value; }
|
||||
|
||||
//! Sets the value
|
||||
/*!
|
||||
@@ -102,13 +102,13 @@ public:
|
||||
/*!
|
||||
\return The value string
|
||||
*/
|
||||
std::string GetValueString(void) { return ""; }
|
||||
std::string GetValueString(void) const { return ""; }
|
||||
|
||||
//! Writes the data to a packet
|
||||
/*!
|
||||
\param packet The packet
|
||||
*/
|
||||
void WriteToPacket(RakNet::BitStream& packet) override {
|
||||
void WriteToPacket(RakNet::BitStream& packet) const override {
|
||||
this->WriteKey(packet);
|
||||
this->WriteValue(packet);
|
||||
}
|
||||
@@ -117,13 +117,13 @@ public:
|
||||
/*!
|
||||
\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
|
||||
/*!
|
||||
\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
|
||||
/*!
|
||||
@@ -131,7 +131,7 @@ public:
|
||||
\param includeTypeId Whether or not to include the type id in 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) {
|
||||
return GeneralUtils::UTF16ToWTF8(this->key) + "=-1:<server variable>";
|
||||
}
|
||||
@@ -154,11 +154,11 @@ public:
|
||||
return stream.str();
|
||||
}
|
||||
|
||||
std::string GetValueAsString() override {
|
||||
std::string GetValueAsString() const override {
|
||||
return this->GetValueString();
|
||||
}
|
||||
|
||||
LDFBaseData* Copy() override {
|
||||
LDFBaseData* Copy() const override {
|
||||
return new LDFData<T>(key, value);
|
||||
}
|
||||
|
||||
@@ -166,19 +166,19 @@ public:
|
||||
};
|
||||
|
||||
// LDF Types
|
||||
template<> inline eLDFType LDFData<std::u16string>::GetValueType(void) { return LDF_TYPE_UTF_16; };
|
||||
template<> inline eLDFType LDFData<int32_t>::GetValueType(void) { return LDF_TYPE_S32; };
|
||||
template<> inline eLDFType LDFData<float>::GetValueType(void) { return LDF_TYPE_FLOAT; };
|
||||
template<> inline eLDFType LDFData<double>::GetValueType(void) { return LDF_TYPE_DOUBLE; };
|
||||
template<> inline eLDFType LDFData<uint32_t>::GetValueType(void) { return LDF_TYPE_U32; };
|
||||
template<> inline eLDFType LDFData<bool>::GetValueType(void) { return LDF_TYPE_BOOLEAN; };
|
||||
template<> inline eLDFType LDFData<uint64_t>::GetValueType(void) { return LDF_TYPE_U64; };
|
||||
template<> inline eLDFType LDFData<LWOOBJID>::GetValueType(void) { return LDF_TYPE_OBJID; };
|
||||
template<> inline eLDFType LDFData<std::string>::GetValueType(void) { return LDF_TYPE_UTF_8; };
|
||||
template<> inline eLDFType LDFData<std::u16string>::GetValueType(void) const { return LDF_TYPE_UTF_16; };
|
||||
template<> inline eLDFType LDFData<int32_t>::GetValueType(void) const { return LDF_TYPE_S32; };
|
||||
template<> inline eLDFType LDFData<float>::GetValueType(void) const { return LDF_TYPE_FLOAT; };
|
||||
template<> inline eLDFType LDFData<double>::GetValueType(void) const { return LDF_TYPE_DOUBLE; };
|
||||
template<> inline eLDFType LDFData<uint32_t>::GetValueType(void) const { return LDF_TYPE_U32; };
|
||||
template<> inline eLDFType LDFData<bool>::GetValueType(void) const { return LDF_TYPE_BOOLEAN; };
|
||||
template<> inline eLDFType LDFData<uint64_t>::GetValueType(void) const { return LDF_TYPE_U64; };
|
||||
template<> inline eLDFType LDFData<LWOOBJID>::GetValueType(void) const { return LDF_TYPE_OBJID; };
|
||||
template<> inline eLDFType LDFData<std::string>::GetValueType(void) const { return LDF_TYPE_UTF_8; };
|
||||
|
||||
// The specialized version for std::u16string (UTF-16)
|
||||
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<uint32_t>(this->value.length());
|
||||
@@ -189,7 +189,7 @@ inline void LDFData<std::u16string>::WriteValue(RakNet::BitStream& packet) {
|
||||
|
||||
// The specialized version for bool
|
||||
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->value);
|
||||
@@ -197,7 +197,7 @@ inline void LDFData<bool>::WriteValue(RakNet::BitStream& packet) {
|
||||
|
||||
// The specialized version for std::string (UTF-8)
|
||||
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<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());
|
||||
}
|
||||
|
||||
template<> inline std::string LDFData<int32_t>::GetValueString() { return std::to_string(this->value); }
|
||||
template<> inline std::string LDFData<float>::GetValueString() { return std::to_string(this->value); }
|
||||
template<> inline std::string LDFData<double>::GetValueString() { 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<bool>::GetValueString() { 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<LWOOBJID>::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() const { 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() const { 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() const { 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__
|
||||
|
||||
@@ -1534,7 +1534,7 @@ void Entity::Kill(Entity* murderer, const eKillType killType) {
|
||||
bool waitForDeathAnimation = false;
|
||||
|
||||
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!
|
||||
@@ -1840,6 +1840,12 @@ const NiPoint3& Entity::GetPosition() const {
|
||||
return vehicel->GetPosition();
|
||||
}
|
||||
|
||||
auto* rigidBodyPhantomPhysicsComponent = GetComponent<RigidbodyPhantomPhysicsComponent>();
|
||||
|
||||
if (rigidBodyPhantomPhysicsComponent != nullptr) {
|
||||
return rigidBodyPhantomPhysicsComponent->GetPosition();
|
||||
}
|
||||
|
||||
return NiPoint3Constant::ZERO;
|
||||
}
|
||||
|
||||
@@ -1868,6 +1874,12 @@ const NiQuaternion& Entity::GetRotation() const {
|
||||
return vehicel->GetRotation();
|
||||
}
|
||||
|
||||
auto* rigidBodyPhantomPhysicsComponent = GetComponent<RigidbodyPhantomPhysicsComponent>();
|
||||
|
||||
if (rigidBodyPhantomPhysicsComponent != nullptr) {
|
||||
return rigidBodyPhantomPhysicsComponent->GetRotation();
|
||||
}
|
||||
|
||||
return NiQuaternionConstant::IDENTITY;
|
||||
}
|
||||
|
||||
@@ -1896,6 +1908,12 @@ void Entity::SetPosition(const NiPoint3& position) {
|
||||
vehicel->SetPosition(position);
|
||||
}
|
||||
|
||||
auto* rigidBodyPhantomPhysicsComponent = GetComponent<RigidbodyPhantomPhysicsComponent>();
|
||||
|
||||
if (rigidBodyPhantomPhysicsComponent != nullptr) {
|
||||
rigidBodyPhantomPhysicsComponent->SetPosition(position);
|
||||
}
|
||||
|
||||
Game::entityManager->SerializeEntity(this);
|
||||
}
|
||||
|
||||
@@ -1924,6 +1942,12 @@ void Entity::SetRotation(const NiQuaternion& rotation) {
|
||||
vehicel->SetRotation(rotation);
|
||||
}
|
||||
|
||||
auto* rigidBodyPhantomPhysicsComponent = GetComponent<RigidbodyPhantomPhysicsComponent>();
|
||||
|
||||
if (rigidBodyPhantomPhysicsComponent != nullptr) {
|
||||
rigidBodyPhantomPhysicsComponent->SetRotation(rotation);
|
||||
}
|
||||
|
||||
Game::entityManager->SerializeEntity(this);
|
||||
}
|
||||
|
||||
|
||||
@@ -224,6 +224,16 @@ bool BehaviorContext::CalculateUpdate(const float deltaTime) {
|
||||
for (auto i = 0u; i < this->syncEntries.size(); ++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) {
|
||||
entry.time -= deltaTime;
|
||||
|
||||
@@ -333,7 +343,7 @@ void BehaviorContext::FilterTargets(std::vector<Entity*>& targets, std::forward_
|
||||
}
|
||||
|
||||
// handle targeting the caster
|
||||
if (candidate == caster){
|
||||
if (candidate == caster) {
|
||||
// if we aren't targeting self, erase, otherise increment and continue
|
||||
if (!targetSelf) index = targets.erase(index);
|
||||
else index++;
|
||||
@@ -356,24 +366,24 @@ void BehaviorContext::FilterTargets(std::vector<Entity*>& targets, std::forward_
|
||||
}
|
||||
|
||||
// if they are dead, then earse and continue
|
||||
if (candidateDestroyableComponent->GetIsDead()){
|
||||
if (candidateDestroyableComponent->GetIsDead()) {
|
||||
index = targets.erase(index);
|
||||
continue;
|
||||
}
|
||||
|
||||
// if their faction is explicitly included, increment and continue
|
||||
auto candidateFactions = candidateDestroyableComponent->GetFactionIDs();
|
||||
if (CheckFactionList(includeFactionList, candidateFactions)){
|
||||
if (CheckFactionList(includeFactionList, candidateFactions)) {
|
||||
index++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// check if they are a team member
|
||||
if (targetTeam){
|
||||
if (targetTeam) {
|
||||
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(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++;
|
||||
continue;
|
||||
}
|
||||
@@ -419,8 +429,8 @@ bool BehaviorContext::CheckTargetingRequirements(const Entity* target) const {
|
||||
// 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 {
|
||||
if (factionList.empty() || objectsFactions.empty()) return false;
|
||||
for (auto faction : factionList){
|
||||
if(std::find(objectsFactions.begin(), objectsFactions.end(), faction) != objectsFactions.end()) return true;
|
||||
for (auto faction : factionList) {
|
||||
if (std::find(objectsFactions.begin(), objectsFactions.end(), faction) != objectsFactions.end()) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -558,19 +558,9 @@ void InventoryComponent::LoadXml(const tinyxml2::XMLDocument& document) {
|
||||
itemElement->QueryAttribute("parent", &parent);
|
||||
// End custom xml
|
||||
|
||||
std::vector<LDFBaseData*> config;
|
||||
auto* item = new Item(id, lot, inventory, slot, count, bound, {}, parent, subKey);
|
||||
|
||||
auto* extraInfo = itemElement->FirstChildElement("x");
|
||||
|
||||
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);
|
||||
item->LoadConfigXml(*itemElement);
|
||||
|
||||
if (equipped) {
|
||||
const auto info = Inventory::FindItemComponent(lot);
|
||||
@@ -676,17 +666,7 @@ void InventoryComponent::UpdateXml(tinyxml2::XMLDocument& document) {
|
||||
itemElement->SetAttribute("parent", item->GetParent());
|
||||
// End custom xml
|
||||
|
||||
for (auto* data : item->GetConfig()) {
|
||||
if (data->GetKey() != u"assemblyPartLOTs") {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto* extraInfo = document.NewElement("x");
|
||||
|
||||
extraInfo->SetAttribute("ma", data->GetString(false).c_str());
|
||||
|
||||
itemElement->LinkEndChild(extraInfo);
|
||||
}
|
||||
item->SaveConfigXml(*itemElement);
|
||||
|
||||
bagElement->LinkEndChild(itemElement);
|
||||
}
|
||||
@@ -1600,18 +1580,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;
|
||||
if (slot == 1 ) behaviorSlot = BehaviorSlot::Primary;
|
||||
else if (slot == 2 ) behaviorSlot = BehaviorSlot::Offhand;
|
||||
else if (slot == 3 ) behaviorSlot = BehaviorSlot::Neck;
|
||||
else if (slot == 4 ) behaviorSlot = BehaviorSlot::Head;
|
||||
else if (slot == 5 ) behaviorSlot = BehaviorSlot::Consumable;
|
||||
if (slot == 1) behaviorSlot = BehaviorSlot::Primary;
|
||||
else if (slot == 2) behaviorSlot = BehaviorSlot::Offhand;
|
||||
else if (slot == 3) behaviorSlot = BehaviorSlot::Neck;
|
||||
else if (slot == 4) behaviorSlot = BehaviorSlot::Head;
|
||||
else if (slot == 5) behaviorSlot = BehaviorSlot::Consumable;
|
||||
else return false;
|
||||
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;
|
||||
const auto index = m_Skills.find(slot);
|
||||
if (index != m_Skills.end()) {
|
||||
@@ -1623,4 +1603,3 @@ bool InventoryComponent::SetSkill(BehaviorSlot slot, uint32_t skillId){
|
||||
m_Skills.insert_or_assign(slot, skillId);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -32,6 +32,8 @@
|
||||
#include "eGameMasterLevel.h"
|
||||
#include "eMissionState.h"
|
||||
#include "dNavMesh.h"
|
||||
#include "eGameActivity.h"
|
||||
#include "eStateChangeType.h"
|
||||
|
||||
std::unordered_map<LWOOBJID, LWOOBJID> PetComponent::currentActivities{};
|
||||
std::unordered_map<LWOOBJID, LWOOBJID> PetComponent::activePets{};
|
||||
@@ -210,24 +212,23 @@ void PetComponent::OnUse(Entity* originator) {
|
||||
if (dpWorld::IsLoaded()) {
|
||||
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();
|
||||
|
||||
attempt = originatorPosition + forward * interactionDistance;
|
||||
|
||||
y = dpWorld::GetNavMesh()->GetHeightAtPoint(attempt);
|
||||
nearestPoint = dpWorld::GetNavMesh()->NearestPoint(attempt);
|
||||
|
||||
interactionDistance -= 0.5f;
|
||||
}
|
||||
|
||||
position = attempt;
|
||||
position = nearestPoint;
|
||||
} else {
|
||||
position = petPosition + forward * interactionDistance;
|
||||
}
|
||||
|
||||
|
||||
auto rotation = NiQuaternion::LookAt(position, petPosition);
|
||||
|
||||
GameMessages::SendNotifyPetTamingMinigame(
|
||||
@@ -246,11 +247,11 @@ void PetComponent::OnUse(Entity* originator) {
|
||||
m_Parent->GetObjectID(),
|
||||
LWOOBJID_EMPTY,
|
||||
originator->GetObjectID(),
|
||||
true,
|
||||
false,
|
||||
ePetTamingNotifyType::BEGIN,
|
||||
petPosition,
|
||||
position,
|
||||
rotation,
|
||||
NiPoint3Constant::ZERO,
|
||||
NiPoint3Constant::ZERO,
|
||||
NiQuaternion(0.0f, 0.0f, 0.0f, 0.0f),
|
||||
UNASSIGNED_SYSTEM_ADDRESS
|
||||
);
|
||||
|
||||
@@ -258,11 +259,18 @@ void PetComponent::OnUse(Entity* originator) {
|
||||
|
||||
m_Tamer = originator->GetObjectID();
|
||||
SetStatus(5);
|
||||
Game::entityManager->SerializeEntity(m_Parent);
|
||||
|
||||
currentActivities.insert_or_assign(m_Tamer, m_Parent->GetObjectID());
|
||||
|
||||
// Notify the start of a pet taming minigame
|
||||
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) {
|
||||
@@ -627,6 +635,11 @@ void PetComponent::RequestSetPetName(std::u16string name) {
|
||||
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());
|
||||
|
||||
auto* modelEntity = Game::entityManager->GetEntity(m_ModelId);
|
||||
@@ -666,6 +679,11 @@ void PetComponent::ClientExitTamingMinigame(bool voluntaryExit) {
|
||||
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::SendTerminateInteraction(m_Tamer, eTerminateType::FROM_INTERACTION, m_Parent->GetObjectID());
|
||||
@@ -712,6 +730,11 @@ void PetComponent::ClientFailTamingMinigame() {
|
||||
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::SendTerminateInteraction(m_Tamer, eTerminateType::FROM_INTERACTION, m_Parent->GetObjectID());
|
||||
|
||||
@@ -47,7 +47,7 @@ PhantomPhysicsComponent::PhantomPhysicsComponent(Entity* parent) : PhysicsCompon
|
||||
m_Direction = NiPoint3(); // * m_DirectionalMultiplier
|
||||
|
||||
if (m_Parent->GetVar<bool>(u"create_physics")) {
|
||||
CreatePhysics();
|
||||
m_dpEntity = CreatePhysicsLnv(m_Scale, ComponentType);
|
||||
}
|
||||
|
||||
if (m_Parent->GetVar<bool>(u"respawnVol")) {
|
||||
@@ -89,105 +89,9 @@ PhantomPhysicsComponent::PhantomPhysicsComponent(Entity* parent) : PhysicsCompon
|
||||
m_RespawnRot = m_Rotation;
|
||||
}
|
||||
|
||||
/*
|
||||
for (LDFBaseData* data : settings) {
|
||||
if (data) {
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
@@ -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) {
|
||||
PhysicsComponent::Serialize(outBitStream, bIsInitialUpdate);
|
||||
|
||||
@@ -308,8 +149,9 @@ void ApplyCollisionEffect(const LWOOBJID& target, const ePhysicsEffectType effec
|
||||
controllablePhysicsComponent->SetGravityScale(effectScale);
|
||||
GameMessages::SendSetGravityScale(target, effectScale, targetEntity->GetSystemAddress());
|
||||
}
|
||||
break;
|
||||
}
|
||||
// The other types are not handled by the server
|
||||
|
||||
case ePhysicsEffectType::ATTRACT:
|
||||
case ePhysicsEffectType::FRICTION:
|
||||
case ePhysicsEffectType::PUSH:
|
||||
@@ -317,6 +159,7 @@ void ApplyCollisionEffect(const LWOOBJID& target, const ePhysicsEffectType effec
|
||||
default:
|
||||
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) {
|
||||
@@ -356,24 +199,12 @@ void PhantomPhysicsComponent::SetDirection(const NiPoint3& pos) {
|
||||
m_IsDirectional = true;
|
||||
}
|
||||
|
||||
void PhantomPhysicsComponent::SpawnVertices() {
|
||||
if (!m_dpEntity) return;
|
||||
|
||||
LOG("%llu", m_Parent->GetObjectID());
|
||||
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);
|
||||
void PhantomPhysicsComponent::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);
|
||||
}
|
||||
|
||||
void PhantomPhysicsComponent::SetDirectionalMultiplier(float mul) {
|
||||
|
||||
@@ -18,6 +18,7 @@ class LDFBaseData;
|
||||
class Entity;
|
||||
class dpEntity;
|
||||
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
|
||||
@@ -34,11 +35,6 @@ public:
|
||||
void Update(float deltaTime) 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
|
||||
* @param pos the direction to set
|
||||
@@ -109,7 +105,7 @@ public:
|
||||
/**
|
||||
* Spawns an object at each of the vertices for debugging purposes
|
||||
*/
|
||||
void SpawnVertices();
|
||||
void SpawnVertices() const;
|
||||
|
||||
/**
|
||||
* Legacy stuff no clue what this does
|
||||
@@ -166,11 +162,6 @@ private:
|
||||
*/
|
||||
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
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,19 @@
|
||||
#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) {
|
||||
m_Position = NiPoint3Constant::ZERO;
|
||||
m_Rotation = NiQuaternionConstant::IDENTITY;
|
||||
@@ -19,3 +33,190 @@ void PhysicsComponent::Serialize(RakNet::BitStream& outBitStream, bool bIsInitia
|
||||
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;
|
||||
};
|
||||
|
||||
enum class eReplicaComponentType : uint32_t;
|
||||
|
||||
class dpEntity;
|
||||
|
||||
class PhysicsComponent : public Component {
|
||||
public:
|
||||
PhysicsComponent(Entity* parent);
|
||||
@@ -22,6 +26,12 @@ public:
|
||||
const NiQuaternion& GetRotation() const { return m_Rotation; }
|
||||
virtual void SetRotation(const NiQuaternion& rot) { if (m_Rotation == rot) return; m_Rotation = rot; m_DirtyPosition = true; }
|
||||
protected:
|
||||
dpEntity* CreatePhysicsEntity(eReplicaComponentType type);
|
||||
|
||||
dpEntity* CreatePhysicsLnv(const float scale, const eReplicaComponentType type) const;
|
||||
|
||||
void SpawnVertices(dpEntity* entity) const;
|
||||
|
||||
NiPoint3 m_Position;
|
||||
|
||||
NiQuaternion m_Rotation;
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include "LeaderboardManager.h"
|
||||
#include "dZoneManager.h"
|
||||
#include "CDActivitiesTable.h"
|
||||
#include "eStateChangeType.h"
|
||||
#include <ctime>
|
||||
|
||||
#ifndef M_PI
|
||||
@@ -77,6 +78,9 @@ void RacingControlComponent::OnPlayerLoaded(Entity* player) {
|
||||
|
||||
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",
|
||||
m_LoadedPlayers);
|
||||
m_LobbyPlayers.push_back(player->GetObjectID());
|
||||
@@ -817,8 +821,10 @@ void RacingControlComponent::Update(float deltaTime) {
|
||||
|
||||
// Some offset up to make they don't fall through the terrain on a
|
||||
// respawn, seems to fix itself to the track anyhow
|
||||
player.respawnPosition = position + NiPoint3Constant::UNIT_Y * 5;
|
||||
player.respawnRotation = vehicle->GetRotation();
|
||||
if (waypoint.racing.isResetNode) {
|
||||
player.respawnPosition = position + NiPoint3Constant::UNIT_Y * 5;
|
||||
player.respawnRotation = vehicle->GetRotation();
|
||||
}
|
||||
player.respawnIndex = respawnIndex;
|
||||
|
||||
// Reached the start point, lapped
|
||||
|
||||
@@ -1,16 +1,57 @@
|
||||
/*
|
||||
* Darkflame Universe
|
||||
* Copyright 2023
|
||||
*/
|
||||
// Darkflame Universe
|
||||
// Copyright 2024
|
||||
|
||||
#include "RigidbodyPhantomPhysicsComponent.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) {
|
||||
m_Position = m_Parent->GetDefaultPosition();
|
||||
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) {
|
||||
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
|
||||
* Copyright 2023
|
||||
*/
|
||||
// Darkflame Universe
|
||||
// Copyright 2024
|
||||
|
||||
#ifndef __RIGIDBODYPHANTOMPHYSICS_H__
|
||||
#define __RIGIDBODYPHANTOMPHYSICS_H__
|
||||
#ifndef RIGIDBODYPHANTOMPHYSICS_H
|
||||
#define RIGIDBODYPHANTOMPHYSICS_H
|
||||
|
||||
#include "BitStream.h"
|
||||
#include "dCommonVars.h"
|
||||
@@ -13,6 +11,8 @@
|
||||
#include "PhysicsComponent.h"
|
||||
#include "eReplicaComponentType.h"
|
||||
|
||||
class dpEntity;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
@@ -23,7 +23,15 @@ public:
|
||||
|
||||
RigidbodyPhantomPhysicsComponent(Entity* parent);
|
||||
|
||||
void Update(const float deltaTime) 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
|
||||
|
||||
@@ -76,8 +76,8 @@ void VendorComponent::RefreshInventory(bool isCreation) {
|
||||
if (vendorItems.empty()) break;
|
||||
auto randomItemIndex = GeneralUtils::GenerateRandomNumber<int32_t>(0, vendorItems.size() - 1);
|
||||
const auto& randomItem = vendorItems.at(randomItemIndex);
|
||||
vendorItems.erase(vendorItems.begin() + randomItemIndex);
|
||||
if (SetupItem(randomItem.itemid)) m_Inventory.push_back(SoldItem(randomItem.itemid, randomItem.sortPriority));
|
||||
vendorItems.erase(vendorItems.begin() + randomItemIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,6 +102,7 @@
|
||||
|
||||
#include "CDComponentsRegistryTable.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) {
|
||||
CBITSTREAM;
|
||||
@@ -369,8 +370,8 @@ void GameMessages::SendPlatformResync(Entity* entity, const SystemAddress& sysAd
|
||||
|
||||
const auto lot = entity->GetLOT();
|
||||
|
||||
if (lot == 12341 || lot == 5027 || lot == 5028 || lot == 14335 || lot == 14447 || lot == 14449) {
|
||||
iDesiredWaypointIndex = 0;
|
||||
if (lot == 12341 || lot == 5027 || lot == 5028 || lot == 14335 || lot == 14447 || lot == 14449 || lot == 11306 || lot == 11308) {
|
||||
iDesiredWaypointIndex = (lot == 11306 || lot == 11308) ? 1 : 0;
|
||||
iIndex = 0;
|
||||
nextIndex = 0;
|
||||
bStopAtDesiredWaypoint = true;
|
||||
@@ -412,7 +413,8 @@ void GameMessages::SendPlatformResync(Entity* entity, const SystemAddress& sysAd
|
||||
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) {
|
||||
@@ -5351,17 +5353,18 @@ void GameMessages::HandleRemoveItemFromInventory(RakNet::BitStream& inStream, En
|
||||
iStackCount = std::min<uint32_t>(item->GetCount(), iStackCount);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
auto lot = item->GetLot();
|
||||
item->SetCount(item->GetCount() - iStackCount, true);
|
||||
Game::entityManager->SerializeEntity(entity);
|
||||
|
||||
auto* missionComponent = entity->GetComponent<MissionComponent>();
|
||||
|
||||
if (missionComponent != nullptr) {
|
||||
missionComponent->Progress(eMissionTaskType::GATHER, item->GetLot(), LWOOBJID_EMPTY, "", -iStackCount);
|
||||
missionComponent->Progress(eMissionTaskType::GATHER, lot, LWOOBJID_EMPTY, "", -iStackCount);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,6 +27,23 @@
|
||||
#include "CDComponentsRegistryTable.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) {
|
||||
if (!Inventory::IsValidItem(lot)) {
|
||||
return;
|
||||
@@ -122,6 +139,10 @@ uint32_t Item::GetSlot() const {
|
||||
return slot;
|
||||
}
|
||||
|
||||
std::vector<LDFBaseData*> Item::GetConfig() const {
|
||||
return config;
|
||||
}
|
||||
|
||||
std::vector<LDFBaseData*>& Item::GetConfig() {
|
||||
return config;
|
||||
}
|
||||
@@ -251,7 +272,7 @@ bool Item::Consume() {
|
||||
|
||||
auto skills = skillsTable->Query([this](const CDObjectSkills entry) {
|
||||
return entry.objectTemplate == static_cast<uint32_t>(lot);
|
||||
});
|
||||
});
|
||||
|
||||
auto success = false;
|
||||
|
||||
@@ -515,3 +536,35 @@ Item::~Item() {
|
||||
|
||||
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 "eLootSourceType.h"
|
||||
|
||||
namespace tinyxml2 {
|
||||
class XMLElement;
|
||||
};
|
||||
|
||||
/**
|
||||
* An item that can be stored in an inventory and optionally consumed or equipped
|
||||
* TODO: ideally this should be a component
|
||||
@@ -116,6 +120,12 @@ public:
|
||||
*/
|
||||
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
|
||||
* @return the database info for this item
|
||||
@@ -214,6 +224,10 @@ public:
|
||||
*/
|
||||
void RemoveFromInventory();
|
||||
|
||||
void SaveConfigXml(tinyxml2::XMLElement& i) const;
|
||||
|
||||
void LoadConfigXml(const tinyxml2::XMLElement& i);
|
||||
|
||||
private:
|
||||
/**
|
||||
* The object ID of this item
|
||||
|
||||
@@ -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) {
|
||||
std::vector<std::pair<LOT, uint32_t>> items;
|
||||
|
||||
@@ -532,15 +542,6 @@ void Mission::YieldRewards() {
|
||||
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) {
|
||||
destroyableComponent->SetMaxHealth(destroyableComponent->GetMaxHealth() + static_cast<float>(info.reward_maxhealth), true);
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
// 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;
|
||||
}
|
||||
|
||||
@@ -94,35 +94,6 @@ void Mail::SendMail(const LWOOBJID sender, const std::string& senderName, LWOOBJ
|
||||
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) {
|
||||
int mailStuffID = 0;
|
||||
packet.Read(mailStuffID);
|
||||
@@ -176,11 +147,20 @@ void Mail::HandleSendMail(RakNet::BitStream& packet, const SystemAddress& sysAdd
|
||||
return;
|
||||
}
|
||||
|
||||
std::string subject = ReadWStringAsString(packet, 50);
|
||||
std::string body = ReadWStringAsString(packet, 400);
|
||||
std::string recipient = ReadWStringAsString(packet, 32);
|
||||
LUWString subjectRead(50);
|
||||
packet.Read(subjectRead);
|
||||
|
||||
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:
|
||||
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;
|
||||
LWOOBJID attachmentID;
|
||||
@@ -267,40 +247,44 @@ void Mail::HandleDataRequest(RakNet::BitStream& packet, const SystemAddress& sys
|
||||
RakNet::BitStream bitStream;
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::MAIL);
|
||||
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);
|
||||
|
||||
for (const auto& mail : playerMail) {
|
||||
bitStream.Write(mail.id); //MailID
|
||||
|
||||
WriteStringAsWString(bitStream, mail.subject.c_str(), 50); //subject
|
||||
WriteStringAsWString(bitStream, mail.body.c_str(), 400); //body
|
||||
WriteStringAsWString(bitStream, mail.senderUsername.c_str(), 32); //sender
|
||||
const LUWString subject(mail.subject, 50);
|
||||
bitStream.Write(subject); //subject
|
||||
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));
|
||||
bitStream.Write(uint64_t(0)); // attachedCurrency
|
||||
|
||||
bitStream.Write(mail.itemID); //Attachment ID
|
||||
LOT lot = mail.itemLOT;
|
||||
if (lot <= 0) bitStream.Write(LOT(-1));
|
||||
else bitStream.Write(lot);
|
||||
bitStream.Write(uint32_t(0));
|
||||
bitStream.Write(uint32_t(0)); // packing
|
||||
|
||||
bitStream.Write(mail.itemSubkey); //Attachment subKey
|
||||
bitStream.Write<uint16_t>(mail.itemCount); //Attachment count
|
||||
bitStream.Write(mail.itemSubkey); // Attachment subKey
|
||||
|
||||
bitStream.Write(uint32_t(0));
|
||||
bitStream.Write(uint16_t(0));
|
||||
bitStream.Write<uint16_t>(mail.itemCount); // Attachment count
|
||||
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);
|
||||
bitStream.Write<uint64_t>(mail.timeSent); // expiration date
|
||||
bitStream.Write<uint64_t>(mail.timeSent);// send date
|
||||
bitStream.Write<uint8_t>(mail.wasRead); //was read
|
||||
|
||||
bitStream.Write(uint8_t(0));
|
||||
bitStream.Write(uint16_t(0));
|
||||
bitStream.Write(uint32_t(0));
|
||||
bitStream.Write(uint8_t(0)); // isLocalized
|
||||
bitStream.Write(uint16_t(0)); // packing
|
||||
bitStream.Write(uint32_t(0)); // packing
|
||||
}
|
||||
|
||||
Game::server->Send(bitStream, sysAddr, false);
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "SlashCommandHandler.h"
|
||||
|
||||
#include <iomanip>
|
||||
#include <ranges>
|
||||
|
||||
#include "DEVGMCommands.h"
|
||||
#include "GMGreaterThanZeroCommands.h"
|
||||
@@ -19,7 +20,7 @@
|
||||
#include "dServer.h"
|
||||
|
||||
namespace {
|
||||
std::vector<Command> CommandInfos;
|
||||
std::map<std::string, Command> CommandInfos;
|
||||
std::map<std::string, Command> RegisteredCommands;
|
||||
}
|
||||
|
||||
@@ -38,9 +39,8 @@ void SlashCommandHandler::RegisterCommand(Command command) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
CommandInfos.push_back(command);
|
||||
};
|
||||
CommandInfos[command.aliases[0]] = command;
|
||||
}
|
||||
|
||||
void SlashCommandHandler::HandleChatCommand(const std::u16string& chat, Entity* entity, const SystemAddress& sysAddr) {
|
||||
auto input = GeneralUtils::UTF16ToWTF8(chat);
|
||||
@@ -61,11 +61,9 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& chat, Entity*
|
||||
if (commandHandle.requiredLevel > eGameMasterLevel::CIVILIAN) Database::Get()->InsertSlashCommandUsage(entity->GetObjectID(), input);
|
||||
commandHandle.handle(entity, sysAddr, args);
|
||||
} 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 + "\"";
|
||||
}
|
||||
} else if (entity->GetGMLevel() == eGameMasterLevel::CIVILIAN) {
|
||||
// We don't need to tell civilians commands don't exist
|
||||
error = "Command " + command + " does not exist!";
|
||||
}
|
||||
|
||||
@@ -74,41 +72,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) {
|
||||
std::ostringstream feedback;
|
||||
if (args.empty()) {
|
||||
feedback << "----- Commands -----";
|
||||
for (const auto& command : CommandInfos) {
|
||||
// TODO: Limit displaying commands based on GM level they require
|
||||
if (command.requiredLevel > entity->GetGMLevel()) continue;
|
||||
LOG("Help command: %s", command.aliases[0].c_str());
|
||||
feedback << "\n/" << command.aliases[0] << ": " << command.help;
|
||||
constexpr size_t pageSize = 10;
|
||||
|
||||
std::string trimmedArgs = args;
|
||||
trimmedArgs.erase(trimmedArgs.begin(), std::find_if_not(trimmedArgs.begin(), trimmedArgs.end(), [](unsigned char ch) {
|
||||
return std::isspace(ch);
|
||||
}));
|
||||
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;
|
||||
foundCommand = true;
|
||||
feedback << "----- " << command.aliases.at(0) << " -----\n";
|
||||
// info can be a localizable string
|
||||
feedback << command.info;
|
||||
if (command.aliases.size() == 1) break;
|
||||
size_t totalPages = (accessibleCommands.size() + pageSize - 1) / pageSize;
|
||||
|
||||
feedback << "\nAliases: ";
|
||||
for (size_t i = 0; i < command.aliases.size(); i++) {
|
||||
if (page < 1 || page > totalPages) {
|
||||
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 << ", ";
|
||||
feedback << command.aliases[i];
|
||||
}
|
||||
}
|
||||
|
||||
// Let GameMasters know if the command doesn't exist
|
||||
if (!foundCommand && entity->GetGMLevel() > eGameMasterLevel::CIVILIAN) feedback << "Command " << std::quoted(args) << " does not exist!";
|
||||
} else {
|
||||
feedback << "Command not found.";
|
||||
}
|
||||
|
||||
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) {
|
||||
@@ -900,7 +933,7 @@ void SlashCommandHandler::Startup() {
|
||||
|
||||
Command HelpCommand{
|
||||
.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"},
|
||||
.handle = GMZeroCommands::Help,
|
||||
.requiredLevel = eGameMasterLevel::CIVILIAN
|
||||
@@ -1015,4 +1048,383 @@ void SlashCommandHandler::Startup() {
|
||||
};
|
||||
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 "SkillComponent.h"
|
||||
#include "TriggerComponent.h"
|
||||
#include "RigidbodyPhantomPhysicsComponent.h"
|
||||
|
||||
// Enums
|
||||
#include "eGameMasterLevel.h"
|
||||
@@ -1129,8 +1130,13 @@ namespace DEVGMCommands {
|
||||
void SpawnPhysicsVerts(Entity* entity, const SystemAddress& sysAddr, const std::string args) {
|
||||
//Go tell physics to spawn all the vertices:
|
||||
auto entities = Game::entityManager->GetEntitiesByComponent(eReplicaComponentType::PHANTOM_PHYSICS);
|
||||
for (auto en : entities) {
|
||||
auto phys = static_cast<PhantomPhysicsComponent*>(en->GetComponent(eReplicaComponentType::PHANTOM_PHYSICS));
|
||||
for (const auto* en : entities) {
|
||||
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)
|
||||
phys->SpawnVertices();
|
||||
}
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
#ifndef DEVGMCOMMANDS_H
|
||||
#define DEVGMCOMMANDS_H
|
||||
|
||||
namespace DEVGMCommands {
|
||||
void SetGMLevel(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 DeleteInven(Entity* entity, const SystemAddress& sysAddr, const std::string args);
|
||||
}
|
||||
|
||||
#endif //!DEVGMCOMMANDS_H
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
#ifndef GMGREATERTHANZEROCOMMANDS_H
|
||||
#define GMGREATERTHANZEROCOMMANDS_H
|
||||
|
||||
namespace GMGreaterThanZeroCommands {
|
||||
void Kick(Entity* entity, const SystemAddress& sysAddr, const std::string args);
|
||||
void MailItem(Entity* entity, const SystemAddress& sysAddr, const std::string args);
|
||||
@@ -13,3 +16,5 @@ namespace GMGreaterThanZeroCommands {
|
||||
void ShowAll(Entity* entity, const SystemAddress& sysAddr, const std::string args);
|
||||
void FindPlayer(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())));
|
||||
}
|
||||
|
||||
//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 {
|
||||
void Help(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 Resurrect(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
|
||||
|
||||
@@ -112,6 +112,31 @@ void dNavMesh::LoadNavmesh() {
|
||||
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 {
|
||||
if (m_NavMesh == nullptr) {
|
||||
return location.y;
|
||||
|
||||
@@ -21,7 +21,7 @@ public:
|
||||
|
||||
/**
|
||||
* Get the height at a point
|
||||
*
|
||||
*
|
||||
* @param location The location to check for height at. This is the center of the search area.
|
||||
* @param halfExtentsHeight The half extents height of the search area. This is the distance from the center to the top and bottom of the search area.
|
||||
* The larger the value of halfExtentsHeight is, the larger the performance cost of the query.
|
||||
@@ -29,7 +29,7 @@ public:
|
||||
*/
|
||||
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);
|
||||
|
||||
NiPoint3 NearestPoint(const NiPoint3& location, const float halfExtent = 32.0f) const;
|
||||
bool IsNavmeshLoaded() { return m_NavMesh != nullptr; }
|
||||
|
||||
private:
|
||||
|
||||
@@ -82,7 +82,7 @@ void AuthPackets::SendHandshake(dServer* server, const SystemAddress& sysAddr, c
|
||||
if (serverType == ServerType::Auth) bitStream.Write(ServiceId::Auth);
|
||||
else if (serverType == ServerType::World) bitStream.Write(ServiceId::World);
|
||||
else bitStream.Write(ServiceId::General);
|
||||
bitStream.Write<uint64_t>(215523405360);
|
||||
bitStream.Write<uint64_t>(215523470896);
|
||||
|
||||
server->Send(bitStream, sysAddr, false);
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include "SkillComponent.h"
|
||||
#include "eReplicaComponentType.h"
|
||||
#include "RenderComponent.h"
|
||||
#include "PlayerManager.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
@@ -53,11 +54,13 @@ void BossSpiderQueenEnemyServer::OnStartup(Entity* self) {
|
||||
|
||||
void BossSpiderQueenEnemyServer::OnDie(Entity* self, Entity* killer) {
|
||||
if (Game::zoneManager->GetZoneID().GetMapID() == instanceZoneID && killer) {
|
||||
auto* missionComponent = killer->GetComponent<MissionComponent>();
|
||||
if (missionComponent == nullptr)
|
||||
return;
|
||||
for (const auto& player : PlayerManager::GetAllPlayers()) {
|
||||
auto* missionComponent = player->GetComponent<MissionComponent>();
|
||||
if (missionComponent == nullptr)
|
||||
return;
|
||||
|
||||
missionComponent->CompleteMission(instanceMissionID);
|
||||
missionComponent->CompleteMission(instanceMissionID);
|
||||
}
|
||||
}
|
||||
|
||||
// There is suppose to be a 0.1 second delay here but that may be admitted?
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
set(DSCRIPTS_SOURCES_02_SERVER_MAP_FV_RACING
|
||||
"RaceFireballs.cpp"
|
||||
"RaceMaelstromGeiser.cpp"
|
||||
"RaceShipLapColumnsServer.cpp"
|
||||
"FvRacingColumns.cpp"
|
||||
PARENT_SCOPE)
|
||||
|
||||
15
dScripts/02_server/Map/FV/Racing/FvRacingColumns.cpp
Normal file
15
dScripts/02_server/Map/FV/Racing/FvRacingColumns.cpp
Normal file
@@ -0,0 +1,15 @@
|
||||
#include "FvRacingColumns.h"
|
||||
#include "MovingPlatformComponent.h"
|
||||
|
||||
void FvRacingColumns::OnStartup(Entity* self) {
|
||||
auto* movingPlatformComponent = self->GetComponent<MovingPlatformComponent>();
|
||||
if (!movingPlatformComponent) return;
|
||||
|
||||
movingPlatformComponent->StopPathing();
|
||||
movingPlatformComponent->SetSerialized(true);
|
||||
int32_t pathStart = 0;
|
||||
if (self->HasVar(u"attached_path_start")) {
|
||||
pathStart = self->GetVar<uint32_t>(u"attached_path_start");
|
||||
}
|
||||
movingPlatformComponent->WarpToWaypoint(pathStart);
|
||||
}
|
||||
6
dScripts/02_server/Map/FV/Racing/FvRacingColumns.h
Normal file
6
dScripts/02_server/Map/FV/Racing/FvRacingColumns.h
Normal file
@@ -0,0 +1,6 @@
|
||||
#include "CppScripts.h"
|
||||
|
||||
class FvRacingColumns : public CppScripts::Script {
|
||||
public:
|
||||
void OnStartup(Entity* self) override;
|
||||
};
|
||||
15
dScripts/02_server/Map/FV/Racing/RaceFireballs.cpp
Normal file
15
dScripts/02_server/Map/FV/Racing/RaceFireballs.cpp
Normal file
@@ -0,0 +1,15 @@
|
||||
#include "RaceFireballs.h"
|
||||
#include "SkillComponent.h"
|
||||
|
||||
void RaceFireballs::OnStartup(Entity* self) {
|
||||
self->AddTimer("fire", GeneralUtils::GenerateRandomNumber<float>(3.0f, 10.0f));
|
||||
}
|
||||
|
||||
void RaceFireballs::OnTimerDone(Entity* self, std::string timerName) {
|
||||
if (timerName == "fire") {
|
||||
auto* skillComponent = self->GetComponent<SkillComponent>();
|
||||
if (skillComponent) skillComponent->CastSkill(894);
|
||||
self->AddTimer("fire", GeneralUtils::GenerateRandomNumber<float>(3.0f, 10.0f));
|
||||
|
||||
}
|
||||
}
|
||||
9
dScripts/02_server/Map/FV/Racing/RaceFireballs.h
Normal file
9
dScripts/02_server/Map/FV/Racing/RaceFireballs.h
Normal file
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
#include "CppScripts.h"
|
||||
|
||||
class RaceFireballs : public CppScripts::Script
|
||||
{
|
||||
public:
|
||||
void OnStartup(Entity* self) override;
|
||||
void OnTimerDone(Entity* self, std::string timerName) override;
|
||||
};
|
||||
@@ -0,0 +1,47 @@
|
||||
#include "RaceShipLapColumnsServer.h"
|
||||
|
||||
#include "RacingControlComponent.h"
|
||||
#include "MovingPlatformComponent.h"
|
||||
|
||||
void RaceShipLapColumnsServer::OnStartup(Entity* self) {
|
||||
self->SetVar(u"Lap2Complete", false);
|
||||
self->SetVar(u"Lap3Complete", false);
|
||||
}
|
||||
|
||||
void SetMovingToWaypoint(const int32_t waypointIndex, const std::string group) {
|
||||
const auto entities = Game::entityManager->GetEntitiesInGroup(group);
|
||||
if (entities.empty()) return;
|
||||
|
||||
auto* entity = entities[0];
|
||||
entity->SetIsGhostingCandidate(false);
|
||||
|
||||
auto* movingPlatfromComponent = entity->GetComponent<MovingPlatformComponent>();
|
||||
if (!movingPlatfromComponent) return;
|
||||
|
||||
movingPlatfromComponent->SetSerialized(true);
|
||||
movingPlatfromComponent->GotoWaypoint(waypointIndex);
|
||||
Game::entityManager->SerializeEntity(entity);
|
||||
}
|
||||
|
||||
void RaceShipLapColumnsServer::OnCollisionPhantom(Entity* self, Entity* target) {
|
||||
if (!target) return;
|
||||
|
||||
const auto racingControllers = Game::entityManager->GetEntitiesByComponent(eReplicaComponentType::RACING_CONTROL);
|
||||
if (racingControllers.empty()) return;
|
||||
|
||||
auto* racingControlComponent = racingControllers[0]->GetComponent<RacingControlComponent>();
|
||||
if (!racingControlComponent) return;
|
||||
|
||||
const auto* player = racingControlComponent->GetPlayerData(target->GetObjectID());
|
||||
if (!player) return;
|
||||
|
||||
if (player->lap == 1 && !self->GetVar<bool>(u"Lap2Complete")) {
|
||||
self->SetVar(u"Lap2Complete", true);
|
||||
SetMovingToWaypoint(1, "Lap2Column");
|
||||
SetMovingToWaypoint(0, "Lap2Ramp");
|
||||
} else if (player->lap == 2 && !self->GetVar<bool>(u"Lap3Complete")) {
|
||||
self->SetVar(u"Lap3Complete", true);
|
||||
SetMovingToWaypoint(1, "Lap3Column");
|
||||
SetMovingToWaypoint(0, "Lap3Ramp");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
#include "CppScripts.h"
|
||||
|
||||
class RaceShipLapColumnsServer : public CppScripts::Script {
|
||||
public:
|
||||
void OnStartup(Entity* self) override;
|
||||
void OnCollisionPhantom(Entity* self, Entity* target) override;
|
||||
};
|
||||
@@ -56,10 +56,6 @@ void AgSurvivalBuffStation::OnTimerDone(Entity* self, std::string timerName) {
|
||||
auto member = Game::entityManager->GetEntity(memberID);
|
||||
if (member != nullptr && !member->GetIsDead()) {
|
||||
GameMessages::SendDropClientLoot(member, self->GetObjectID(), powerupToDrop, 0, self->GetPosition());
|
||||
} else {
|
||||
// If player left the team or left early erase them from the team variable.
|
||||
team.erase(std::find(team.begin(), team.end(), memberID));
|
||||
self->SetVar<std::vector<LWOOBJID>>(u"BuilderTeam", team);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
81
dScripts/ai/AG/AgShipShake.cpp
Normal file
81
dScripts/ai/AG/AgShipShake.cpp
Normal file
@@ -0,0 +1,81 @@
|
||||
#include "AgShipShake.h"
|
||||
#include "EntityInfo.h"
|
||||
#include "GeneralUtils.h"
|
||||
#include "GameMessages.h"
|
||||
#include "EntityManager.h"
|
||||
#include "RenderComponent.h"
|
||||
#include "Entity.h"
|
||||
|
||||
void AgShipShake::OnStartup(Entity* self) {
|
||||
EntityInfo info{};
|
||||
|
||||
info.pos = { -418, 585, -30 };
|
||||
info.lot = 33;
|
||||
info.spawnerID = self->GetObjectID();
|
||||
|
||||
auto* ref = Game::entityManager->CreateEntity(info);
|
||||
|
||||
Game::entityManager->ConstructEntity(ref);
|
||||
|
||||
self->SetVar(u"ShakeObject", ref->GetObjectID());
|
||||
|
||||
self->AddTimer("ShipShakeIdle", 2.0f);
|
||||
self->SetVar(u"RandomTime", 10);
|
||||
}
|
||||
|
||||
void AgShipShake::OnTimerDone(Entity* self, std::string timerName) {
|
||||
auto* shipFxObject = GetEntityInGroup(ShipFX);
|
||||
auto* shipFxObject2 = GetEntityInGroup(ShipFX2);
|
||||
auto* debrisObject = GetEntityInGroup(DebrisFX);
|
||||
if (timerName == "ShipShakeIdle") {
|
||||
auto* ref = Game::entityManager->GetEntity(self->GetVar<LWOOBJID>(u"ShakeObject"));
|
||||
|
||||
const auto randomTime = self->GetVar<int>(u"RandomTime");
|
||||
auto time = GeneralUtils::GenerateRandomNumber<int>(0, randomTime + 1);
|
||||
|
||||
if (time < randomTime / 2) {
|
||||
time += randomTime / 2;
|
||||
}
|
||||
|
||||
self->AddTimer("ShipShakeIdle", static_cast<float>(time));
|
||||
|
||||
if (ref)
|
||||
GameMessages::SendPlayEmbeddedEffectOnAllClientsNearObject(ref, FXName, ref->GetObjectID(), 500.0f);
|
||||
|
||||
|
||||
if (debrisObject)
|
||||
GameMessages::SendPlayFXEffect(debrisObject, -1, u"DebrisFall", "Debris", LWOOBJID_EMPTY, 1.0f, 1.0f, true);
|
||||
|
||||
const auto randomFx = GeneralUtils::GenerateRandomNumber<int>(0, 3);
|
||||
|
||||
if (shipFxObject) {
|
||||
std::string effectType = "shipboom" + std::to_string(randomFx);
|
||||
GameMessages::SendPlayFXEffect(shipFxObject, 559, GeneralUtils::ASCIIToUTF16(effectType), "FX", LWOOBJID_EMPTY, 1.0f, 1.0f, true);
|
||||
}
|
||||
|
||||
self->AddTimer("ShipShakeExplode", 5.0f);
|
||||
|
||||
if (shipFxObject2)
|
||||
RenderComponent::PlayAnimation(shipFxObject2, u"explosion");
|
||||
} else if (timerName == "ShipShakeExplode") {
|
||||
if (shipFxObject)
|
||||
RenderComponent::PlayAnimation(shipFxObject, u"idle");
|
||||
if (shipFxObject2)
|
||||
RenderComponent::PlayAnimation(shipFxObject2, u"idle");
|
||||
}
|
||||
}
|
||||
|
||||
Entity* AgShipShake::GetEntityInGroup(const std::string& group) {
|
||||
auto entities = Game::entityManager->GetEntitiesInGroup(group);
|
||||
Entity* en = nullptr;
|
||||
|
||||
for (auto entity : entities) {
|
||||
if (entity) {
|
||||
en = entity;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return en;
|
||||
}
|
||||
|
||||
16
dScripts/ai/AG/AgShipShake.h
Normal file
16
dScripts/ai/AG/AgShipShake.h
Normal file
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
#include "CppScripts.h"
|
||||
|
||||
class AgShipShake : public CppScripts::Script {
|
||||
public:
|
||||
void OnStartup(Entity* self) override;
|
||||
void OnTimerDone(Entity* self, std::string timerName) override;
|
||||
|
||||
std::string DebrisFX = "DebrisFX";
|
||||
std::string ShipFX = "ShipFX";
|
||||
std::string ShipFX2 = "ShipFX2";
|
||||
std::u16string FXName = u"camshake-bridge";
|
||||
|
||||
private:
|
||||
Entity* GetEntityInGroup(const std::string& group);
|
||||
};
|
||||
@@ -8,21 +8,6 @@
|
||||
|
||||
void AgSpaceStuff::OnStartup(Entity* self) {
|
||||
self->AddTimer("FloaterScale", 5.0f);
|
||||
|
||||
EntityInfo info{};
|
||||
|
||||
info.pos = { -418, 585, -30 };
|
||||
info.lot = 33;
|
||||
info.spawnerID = self->GetObjectID();
|
||||
|
||||
auto* ref = Game::entityManager->CreateEntity(info);
|
||||
|
||||
Game::entityManager->ConstructEntity(ref);
|
||||
|
||||
self->SetVar(u"ShakeObject", ref->GetObjectID());
|
||||
|
||||
self->AddTimer("ShipShakeIdle", 2.0f);
|
||||
self->SetVar(u"RandomTime", 10);
|
||||
}
|
||||
|
||||
void AgSpaceStuff::OnTimerDone(Entity* self, std::string timerName) {
|
||||
@@ -37,70 +22,5 @@ void AgSpaceStuff::OnTimerDone(Entity* self, std::string timerName) {
|
||||
|
||||
RenderComponent::PlayAnimation(self, u"path_0" + (GeneralUtils::to_u16string(pathType)));
|
||||
self->AddTimer("FloaterScale", randTime);
|
||||
} else if (timerName == "ShipShakeExplode") {
|
||||
DoShake(self, true);
|
||||
} else if (timerName == "ShipShakeIdle") {
|
||||
DoShake(self, false);
|
||||
}
|
||||
}
|
||||
|
||||
void AgSpaceStuff::DoShake(Entity* self, bool explodeIdle) {
|
||||
|
||||
if (!explodeIdle) {
|
||||
auto* ref = Game::entityManager->GetEntity(self->GetVar<LWOOBJID>(u"ShakeObject"));
|
||||
|
||||
const auto randomTime = self->GetVar<int>(u"RandomTime");
|
||||
auto time = GeneralUtils::GenerateRandomNumber<int>(0, randomTime + 1);
|
||||
|
||||
if (time < randomTime / 2) {
|
||||
time += randomTime / 2;
|
||||
}
|
||||
|
||||
self->AddTimer("ShipShakeIdle", static_cast<float>(time));
|
||||
|
||||
if (ref)
|
||||
GameMessages::SendPlayEmbeddedEffectOnAllClientsNearObject(ref, FXName, ref->GetObjectID(), 500.0f);
|
||||
|
||||
auto* debrisObject = GetEntityInGroup(DebrisFX);
|
||||
|
||||
if (debrisObject)
|
||||
GameMessages::SendPlayFXEffect(debrisObject, -1, u"DebrisFall", "Debris", LWOOBJID_EMPTY, 1.0f, 1.0f, true);
|
||||
|
||||
const auto randomFx = GeneralUtils::GenerateRandomNumber<int>(0, 3);
|
||||
|
||||
auto* shipFxObject = GetEntityInGroup(ShipFX);
|
||||
if (shipFxObject) {
|
||||
std::string effectType = "shipboom" + std::to_string(randomFx);
|
||||
GameMessages::SendPlayFXEffect(shipFxObject, 559, GeneralUtils::ASCIIToUTF16(effectType), "FX", LWOOBJID_EMPTY, 1.0f, 1.0f, true);
|
||||
}
|
||||
|
||||
self->AddTimer("ShipShakeExplode", 5.0f);
|
||||
|
||||
auto* shipFxObject2 = GetEntityInGroup(ShipFX2);
|
||||
if (shipFxObject2)
|
||||
RenderComponent::PlayAnimation(shipFxObject2, u"explosion");
|
||||
} else {
|
||||
auto* shipFxObject = GetEntityInGroup(ShipFX);
|
||||
auto* shipFxObject2 = GetEntityInGroup(ShipFX2);
|
||||
|
||||
if (shipFxObject)
|
||||
RenderComponent::PlayAnimation(shipFxObject, u"idle");
|
||||
|
||||
if (shipFxObject2)
|
||||
RenderComponent::PlayAnimation(shipFxObject2, u"idle");
|
||||
}
|
||||
}
|
||||
|
||||
Entity* AgSpaceStuff::GetEntityInGroup(const std::string& group) {
|
||||
auto entities = Game::entityManager->GetEntitiesInGroup(group);
|
||||
Entity* en = nullptr;
|
||||
|
||||
for (auto entity : entities) {
|
||||
if (entity) {
|
||||
en = entity;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return en;
|
||||
}
|
||||
|
||||
@@ -5,14 +5,5 @@ class AgSpaceStuff : public CppScripts::Script {
|
||||
public:
|
||||
void OnStartup(Entity* self);
|
||||
void OnTimerDone(Entity* self, std::string timerName);
|
||||
void DoShake(Entity* self, bool explodeIdle);
|
||||
|
||||
std::string DebrisFX = "DebrisFX";
|
||||
std::string ShipFX = "ShipFX";
|
||||
std::string ShipFX2 = "ShipFX2";
|
||||
std::u16string FXName = u"camshake-bridge";
|
||||
|
||||
private:
|
||||
Entity* GetEntityInGroup(const std::string& group);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
set(DSCRIPTS_SOURCES_AI_AG
|
||||
"AgShipPlayerDeathTrigger.cpp"
|
||||
"AgSpaceStuff.cpp"
|
||||
"AgShipShake.cpp"
|
||||
"AgShipPlayerShockServer.cpp"
|
||||
"AgImagSmashable.cpp"
|
||||
"ActSharkPlayerDeathTrigger.cpp"
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
set(DSCRIPTS_SOURCES_AI_RACING_OBJECTS
|
||||
"RaceImagineCrateServer.cpp"
|
||||
"RaceImaginePowerup.cpp"
|
||||
"FvRaceDragon.cpp"
|
||||
"FvRacePillarServer.cpp"
|
||||
"FvRacePillarABCServer.cpp"
|
||||
"FvRacePillarDServer.cpp"
|
||||
"FvRaceSmashEggImagineServer.cpp"
|
||||
"RaceSmashServer.cpp"
|
||||
PARENT_SCOPE)
|
||||
|
||||
30
dScripts/ai/RACING/OBJECTS/FvRaceDragon.cpp
Normal file
30
dScripts/ai/RACING/OBJECTS/FvRaceDragon.cpp
Normal file
@@ -0,0 +1,30 @@
|
||||
#include "FvRaceDragon.h"
|
||||
#include "RenderComponent.h"
|
||||
#include "RacingControlComponent.h"
|
||||
|
||||
void FvRaceDragon::OnCollisionPhantom(Entity* self, Entity* target) {
|
||||
if (!target) return;
|
||||
|
||||
const auto racingControllers = Game::entityManager->GetEntitiesByComponent(eReplicaComponentType::RACING_CONTROL);
|
||||
if (racingControllers.empty()) return;
|
||||
|
||||
auto* racingControlComponent = racingControllers[0]->GetComponent<RacingControlComponent>();
|
||||
if (!racingControlComponent) return;
|
||||
|
||||
const auto* player = racingControlComponent->GetPlayerData(target->GetObjectID());
|
||||
if (!player) return;
|
||||
|
||||
if (player->lap != m_Lap) return;
|
||||
|
||||
const auto dragons = Game::entityManager->GetEntitiesInGroup("dragon");
|
||||
for (const auto& dragon : dragons) {
|
||||
if (!dragon || dragon->GetLOT() != this->m_Dragon) continue;
|
||||
|
||||
auto* renderComponent = dragon->GetComponent<RenderComponent>();
|
||||
if (!renderComponent) continue;
|
||||
|
||||
renderComponent->PlayAnimation(dragon, m_LapAnimName);
|
||||
|
||||
}
|
||||
Game::entityManager->DestroyEntity(self);
|
||||
}
|
||||
15
dScripts/ai/RACING/OBJECTS/FvRaceDragon.h
Normal file
15
dScripts/ai/RACING/OBJECTS/FvRaceDragon.h
Normal file
@@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
#include "CppScripts.h"
|
||||
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
class FvRaceDragon : public CppScripts::Script {
|
||||
public:
|
||||
FvRaceDragon(const std::string_view lapAnimName, const int32_t lap) : m_LapAnimName(lapAnimName), m_Lap(lap) {}
|
||||
private:
|
||||
void OnCollisionPhantom(Entity* self, Entity* target) override;
|
||||
const std::string m_LapAnimName;
|
||||
const int32_t m_Lap;
|
||||
const LOT m_Dragon = 11898;
|
||||
};
|
||||
34
dScripts/ai/RACING/OBJECTS/FvRacePillarABCServer.cpp
Normal file
34
dScripts/ai/RACING/OBJECTS/FvRacePillarABCServer.cpp
Normal file
@@ -0,0 +1,34 @@
|
||||
#include "FvRacePillarABCServer.h"
|
||||
#include "RenderComponent.h"
|
||||
#include "RacingControlComponent.h"
|
||||
|
||||
void FvRacePillarABCServer::OnCollisionPhantom(Entity* self, Entity* target) {
|
||||
if (!target) return;
|
||||
|
||||
const auto racingControllers = Game::entityManager->GetEntitiesByComponent(eReplicaComponentType::RACING_CONTROL);
|
||||
if (racingControllers.empty()) return;
|
||||
|
||||
auto* racingControlComponent = racingControllers[0]->GetComponent<RacingControlComponent>();
|
||||
if (!racingControlComponent) return;
|
||||
|
||||
const auto* player = racingControlComponent->GetPlayerData(target->GetObjectID());
|
||||
if (!player || player->lap != 1) return;
|
||||
|
||||
PlayAnimation("crumble", "pillars", m_PillarA);
|
||||
PlayAnimation("roar", "dragon", m_Dragon);
|
||||
|
||||
self->AddTimer("PillarBFall", 2.5f);
|
||||
self->AddTimer("PillarCFall", 3.7f);
|
||||
self->AddTimer("DeleteObject", 3.8f);
|
||||
}
|
||||
|
||||
void FvRacePillarABCServer::OnTimerDone(Entity* self, std::string timerName) {
|
||||
if (timerName == "PillarBFall") {
|
||||
PlayAnimation("crumble", "pillars", m_PillarB);
|
||||
} else if (timerName == "PillarCFall") {
|
||||
PlayAnimation("crumble", "pillars", m_PillarC);
|
||||
} else if (timerName == "DeleteObject") {
|
||||
Game::entityManager->DestroyEntity(self);
|
||||
}
|
||||
}
|
||||
|
||||
13
dScripts/ai/RACING/OBJECTS/FvRacePillarABCServer.h
Normal file
13
dScripts/ai/RACING/OBJECTS/FvRacePillarABCServer.h
Normal file
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
#include "CppScripts.h"
|
||||
#include "FvRacePillarServer.h"
|
||||
|
||||
class FvRacePillarABCServer : public FvRacePillarServer {
|
||||
void OnCollisionPhantom(Entity* self, Entity* target) override;
|
||||
void OnTimerDone(Entity* self, std::string timerName) override;
|
||||
private:
|
||||
const LOT m_PillarA = 11946;
|
||||
const LOT m_PillarB = 11947;
|
||||
const LOT m_PillarC = 11948;
|
||||
const LOT m_Dragon = 11898;
|
||||
};
|
||||
21
dScripts/ai/RACING/OBJECTS/FvRacePillarDServer.cpp
Normal file
21
dScripts/ai/RACING/OBJECTS/FvRacePillarDServer.cpp
Normal file
@@ -0,0 +1,21 @@
|
||||
#include "FvRacePillarDServer.h"
|
||||
#include "RenderComponent.h"
|
||||
#include "RacingControlComponent.h"
|
||||
|
||||
void FvRacePillarDServer::OnCollisionPhantom(Entity* self, Entity* target) {
|
||||
if (!target) return;
|
||||
|
||||
const auto racingControllers = Game::entityManager->GetEntitiesByComponent(eReplicaComponentType::RACING_CONTROL);
|
||||
if (racingControllers.empty()) return;
|
||||
|
||||
auto* racingControlComponent = racingControllers[0]->GetComponent<RacingControlComponent>();
|
||||
if (!racingControlComponent) return;
|
||||
|
||||
const auto* player = racingControlComponent->GetPlayerData(target->GetObjectID());
|
||||
if (!player) return;
|
||||
|
||||
if (player->lap == 2) {
|
||||
PlayAnimation("crumble", "pillars", m_PillarD);
|
||||
PlayAnimation("roar", "dragon", m_Dragon);
|
||||
}
|
||||
}
|
||||
10
dScripts/ai/RACING/OBJECTS/FvRacePillarDServer.h
Normal file
10
dScripts/ai/RACING/OBJECTS/FvRacePillarDServer.h
Normal file
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
#include "CppScripts.h"
|
||||
#include "FvRacePillarServer.h"
|
||||
|
||||
class FvRacePillarDServer : public FvRacePillarServer {
|
||||
void OnCollisionPhantom(Entity* self, Entity* target) override;
|
||||
private:
|
||||
const LOT m_PillarD = 11949;
|
||||
const LOT m_Dragon = 11898;
|
||||
};
|
||||
15
dScripts/ai/RACING/OBJECTS/FvRacePillarServer.cpp
Normal file
15
dScripts/ai/RACING/OBJECTS/FvRacePillarServer.cpp
Normal file
@@ -0,0 +1,15 @@
|
||||
#include "FvRacePillarServer.h"
|
||||
|
||||
#include "Game.h"
|
||||
#include "EntityManager.h"
|
||||
#include "RenderComponent.h"
|
||||
|
||||
void FvRacePillarServer::PlayAnimation(const std::string animName, const std::string group, const LOT lot) {
|
||||
const auto entities = Game::entityManager->GetEntitiesInGroup(group);
|
||||
for (const auto& entity : entities) {
|
||||
if (!entity || entity->GetLOT() != lot) continue;
|
||||
auto* renderComponent = entity->GetComponent<RenderComponent>();
|
||||
if (!renderComponent) continue;
|
||||
renderComponent->PlayAnimation(entity, animName);
|
||||
}
|
||||
}
|
||||
12
dScripts/ai/RACING/OBJECTS/FvRacePillarServer.h
Normal file
12
dScripts/ai/RACING/OBJECTS/FvRacePillarServer.h
Normal file
@@ -0,0 +1,12 @@
|
||||
#ifndef FVRACEPILLARSERVER__H
|
||||
#define FVRACEPILLARSERVER__H
|
||||
|
||||
#include "CppScripts.h"
|
||||
|
||||
class FvRacePillarServer : public virtual CppScripts::Script {
|
||||
protected:
|
||||
// Plays an animation on all entities in a group with a specific LOT
|
||||
void PlayAnimation(const std::string animName, const std::string group, const LOT lot);
|
||||
};
|
||||
|
||||
#endif // FVRACEPILLARSERVER__H
|
||||
@@ -282,24 +282,22 @@ int main(int argc, char** argv) {
|
||||
}
|
||||
|
||||
const int32_t bufferSize = 1024;
|
||||
MD5* md5 = new MD5();
|
||||
MD5 md5;
|
||||
|
||||
char fileStreamBuffer[1024] = {};
|
||||
|
||||
while (!fileStream.eof()) {
|
||||
memset(fileStreamBuffer, 0, bufferSize);
|
||||
fileStream.read(fileStreamBuffer, bufferSize);
|
||||
md5->update(fileStreamBuffer, fileStream.gcount());
|
||||
md5.update(fileStreamBuffer, fileStream.gcount());
|
||||
}
|
||||
|
||||
fileStream.close();
|
||||
|
||||
const char* nullTerminateBuffer = "\0";
|
||||
md5->update(nullTerminateBuffer, 1); // null terminate the data
|
||||
md5->finalize();
|
||||
databaseChecksum = md5->hexdigest();
|
||||
|
||||
delete md5;
|
||||
md5.update(nullTerminateBuffer, 1); // null terminate the data
|
||||
md5.finalize();
|
||||
databaseChecksum = md5.hexdigest();
|
||||
|
||||
LOG("FDB Checksum calculated as: %s", databaseChecksum.c_str());
|
||||
}
|
||||
@@ -388,14 +386,14 @@ int main(int argc, char** argv) {
|
||||
//In world we'd update our other systems here.
|
||||
|
||||
if (zoneID != 0 && deltaTime > 0.0f) {
|
||||
Metrics::StartMeasurement(MetricVariable::Physics);
|
||||
dpWorld::StepWorld(deltaTime);
|
||||
Metrics::EndMeasurement(MetricVariable::Physics);
|
||||
|
||||
Metrics::StartMeasurement(MetricVariable::UpdateEntities);
|
||||
Game::entityManager->UpdateEntities(deltaTime);
|
||||
Metrics::EndMeasurement(MetricVariable::UpdateEntities);
|
||||
|
||||
Metrics::StartMeasurement(MetricVariable::Physics);
|
||||
dpWorld::StepWorld(deltaTime);
|
||||
Metrics::EndMeasurement(MetricVariable::Physics);
|
||||
|
||||
Metrics::StartMeasurement(MetricVariable::Ghosting);
|
||||
if (std::chrono::duration<float>(currentTime - ghostingLastTime).count() >= 1.0f) {
|
||||
Game::entityManager->UpdateGhosting();
|
||||
|
||||
@@ -7,7 +7,7 @@ services:
|
||||
- darkflame
|
||||
image: mariadb:latest
|
||||
volumes:
|
||||
- ${DB_DATA_DIR:-./db/data}:/var/lib/mysql
|
||||
- ${DB_DATA_DIR:-db-data}:/var/lib/mysql
|
||||
environment:
|
||||
- MARIADB_RANDOM_ROOT_PASSWORD=1
|
||||
- MARIADB_USER=${MARIADB_USER:-darkflame}
|
||||
@@ -79,3 +79,6 @@ services:
|
||||
|
||||
networks:
|
||||
darkflame:
|
||||
|
||||
volumes:
|
||||
db-data:
|
||||
|
||||
@@ -1,151 +0,0 @@
|
||||
openapi: 3.0.3
|
||||
info:
|
||||
title: DLU Chat Server API
|
||||
description: |-
|
||||
This documents the available api endpoints for the DLU Chat Server Web API
|
||||
|
||||
contact:
|
||||
name: DarkflameUniverse Github
|
||||
url: https://github.com/DarkflameUniverse/DarkflameServer/issues
|
||||
license:
|
||||
name: GNU AGPL v3.0
|
||||
url: https://github.com/DarkflameUniverse/DarkflameServer/blob/main/LICENSE
|
||||
version: 1.0.0
|
||||
|
||||
externalDocs:
|
||||
description: Find out more about Swagger
|
||||
url: http://swagger.io
|
||||
|
||||
servers:
|
||||
- url: http://localhost:2005/
|
||||
description: localhost
|
||||
|
||||
tags:
|
||||
- name: management
|
||||
description: Server Management Utilities
|
||||
- name: user
|
||||
description: User Data Utilities
|
||||
|
||||
paths:
|
||||
/announce:
|
||||
post:
|
||||
tags:
|
||||
- management
|
||||
summary: Send an announcement to the game server
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/Announce"
|
||||
required: true
|
||||
responses:
|
||||
"200":
|
||||
description: Successful operation
|
||||
"400":
|
||||
description: Missing Parameter
|
||||
|
||||
/players:
|
||||
get:
|
||||
tags:
|
||||
- user
|
||||
summary: Get all online Players
|
||||
responses:
|
||||
"200":
|
||||
description: Successful operation
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/components/schemas/Player"
|
||||
"204":
|
||||
description: No Data
|
||||
|
||||
/teams:
|
||||
get:
|
||||
tags:
|
||||
- user
|
||||
summary: Get all active Teams
|
||||
responses:
|
||||
"200":
|
||||
description: Successful operation
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/components/schemas/Team"
|
||||
"204":
|
||||
description: No Data
|
||||
|
||||
components:
|
||||
schemas:
|
||||
Player:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
format: int64
|
||||
example: 1152921508901824000
|
||||
gm_level:
|
||||
type: integer
|
||||
format: uint8
|
||||
example: 0
|
||||
name:
|
||||
type: string
|
||||
example: thisisatestname
|
||||
muted:
|
||||
type: boolean
|
||||
example: false
|
||||
zone_id:
|
||||
$ref: "#/components/schemas/ZoneID"
|
||||
|
||||
ZoneID:
|
||||
type: object
|
||||
properties:
|
||||
map_id:
|
||||
type: integer
|
||||
format: uint16
|
||||
example: 1200
|
||||
instance_id:
|
||||
type: integer
|
||||
format: uint16
|
||||
example: 2
|
||||
clone_id:
|
||||
type: integer
|
||||
format: uint32
|
||||
example: 0
|
||||
|
||||
Team:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
format: int64
|
||||
example: 1152921508901824000
|
||||
loot_flag:
|
||||
type: integer
|
||||
format: uint8
|
||||
example: 1
|
||||
local:
|
||||
type: boolean
|
||||
example: false
|
||||
leader:
|
||||
$ref: "#/components/schemas/Player"
|
||||
members:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/components/schemas/Player"
|
||||
|
||||
Announce:
|
||||
required:
|
||||
- title
|
||||
- message
|
||||
type: object
|
||||
properties:
|
||||
title:
|
||||
type: string
|
||||
example: A Mythran has taken Action against you!
|
||||
message:
|
||||
type: string
|
||||
example: Check your mailbox for details!
|
||||
@@ -6,11 +6,3 @@ max_number_of_best_friends=5
|
||||
# Change the value below to what you would like this to be (50 is live accurate)
|
||||
# going over 50 will be allowed in some secnarios, but proper handling will require client modding
|
||||
max_number_of_friends=50
|
||||
|
||||
# Enable or disable the chat web API, disabled by default
|
||||
# It will run on the same port the chat server is running on, defined in shardconfig.ini
|
||||
enable_chat_web_api=0
|
||||
|
||||
# If that chat web api is enabled, it will only listen for connections on this ip address
|
||||
# 127.0.0.1 is localhost
|
||||
chat_web_api_listen_address=127.0.0.1
|
||||
|
||||
@@ -27,6 +27,8 @@
|
||||
<location zone="1300" x="51.848" y="329.0" z="561.114" rw="-0.277656" rx="0.00" ry="0.960681" rz="0.00" />
|
||||
<!--GF Pirate Camp-->
|
||||
<location zone="1300" x="363.259" y="259.367" z="-210.834" rw="0.961918" rx="0.00" ry="-0.273340" rz="0.00" />
|
||||
<!--FV Race Place-->
|
||||
<location zone="1400" x="746.784" y="237.957" z="495.015" rw="-0.036648" rx="0.00" ry="0.999328" rz="0.00" />
|
||||
<!--FV Great Tree-->
|
||||
<location zone="1400" x="-194.288" y="381.275" z="-93.292" rw="0.935135" rx="0.00" ry="0.354292" rz="0.00" />
|
||||
<!--FV Paradox Refinery-->
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
2.3 - Dragonmaw functional, new slash command system, vanity system overhaul
|
||||
2.2 - Code cleanup and QoL fixes
|
||||
2.1 - Bug and crash fixes
|
||||
2.0 - Whole lot of fixed bugs and implemented features
|
||||
1.0 - Final cleanup and bug fixing for public release
|
||||
0.9 - Includes BBB without the need for a UGC server, cannon cove minigame, and bug fixes.
|
||||
0.8 - Added Ninjago! and it's various features + frakjaw minigame. AG survival now works.
|
||||
@@ -7,4 +11,4 @@
|
||||
0.4 - Added Havok to replace Bullet, Instancing, Quickbuilds, rockets, and a ton more fixes and additions.
|
||||
0.3 - FrostBurgh, Snowdrift and Snowman's Land testing version. Includes bodged systems.
|
||||
0.2 - Transfer to VS2019 & Bullet
|
||||
0.1 - Initial transfer from NixLU, up until BehaviorManager inclusion
|
||||
0.1 - Initial transfer from NixLU, up until BehaviorManager inclusion
|
||||
|
||||
Reference in New Issue
Block a user