Compare commits

..

32 Commits

Author SHA1 Message Date
David Markowitz
c72ce4003b fix dimantling items from not the model inventory 2024-05-30 03:51:13 -07:00
Remco Hofman
cce5755366 Fix Dockerfile vanity COPY (#1604)
Corrected an unintended mistake in the COPY commands for adding the
vanity files to the Docker container, causing only the last file
contents to be added to the file '/app/vanity/*'
2024-05-27 17:46:09 -07:00
TAHuntling
e966d3a644 Chore: split VE script up (#1598)
* Testing Scripts

Testing splitting AgSpaceStuff into AgSpaceStuff and AgShipShake

* fixed inclusions

* Removed DoShake

* cleaning up

* consistent if statements

* Update dScripts/ai/AG/AgShipShake.h

Co-authored-by: David Markowitz <39972741+EmosewaMC@users.noreply.github.com>

---------

Co-authored-by: David Markowitz <39972741+EmosewaMC@users.noreply.github.com>
2024-05-27 01:24:48 -05:00
Gie "Max" Vanommeslaeghe
9328021339 Merge pull request #1600 from DarkflameUniverse/add-missing-scripts
fix: add back missing scripts from scripts refactor
2024-05-26 12:02:12 +02:00
Gie "Max" Vanommeslaeghe
d1134fdd62 Merge pull request #1601 from DarkflameUniverse/fix-using-skill-in-race
fix: players using non-car skills in a race
2024-05-26 12:01:54 +02:00
David Markowitz
efa658bc31 fix players using non-car skills in a race 2024-05-25 19:59:15 -07:00
David Markowitz
e59525d2ae Update CppScripts.cpp 2024-05-25 19:32:18 -07:00
David Markowitz
0348db72a5 fiux mission (#1596) 2024-05-25 12:24:02 -05:00
TAHuntling
debc2a96e2 Update CppScripts exclusion list (#1597) 2024-05-25 01:43:32 -07:00
David Markowitz
8ae1a8ff7c fix stale reference (#1594) 2024-05-24 09:15:30 -05:00
David Markowitz
f0960d48b2 Add more modular saving of config data for items (#1591)
* stubbing for saving item extra data

* add declaration to header

* modularize loading for all possible extra data

* move logic to Item

* remove extra map
2024-05-22 17:06:52 -07:00
David Markowitz
dc430d9758 Add reputation as a repeatable mission reward (#1590)
This reverts commit 7d1a28b492b263aba2008a5984dc0f5e7348a068.

Add stubbing for abbreviations

Reward reputation always if possible
2024-05-22 16:35:45 -07:00
TAHuntling
dea10c6d56 Client commands implementation (#1592)
* Adding Client Commands

Adding list of client commands provided to me by EmosewaMC

* Finished adding client commands
2024-05-22 08:32:24 -05:00
TAHuntling
ed00551982 feat: Help Command Pagination (#1581)
* Update SlashCommandHandler.cpp

* Update SlashCommandHandler.cpp

* Update SlashCommandHandler.cpp

* Update SlashCommandHandler.cpp

* Update SlashCommandHandler.cpp

* Update SlashCommandHandler.cpp

* Update SlashCommandHandler.cpp

* Update SlashCommandHandler.cpp

* Update SlashCommandHandler.cpp

* Update SlashCommandHandler.cpp

* Update SlashCommandHandler.cpp

* Fixed Comments

Now able to do /command help to see info for said command. Additionally this works for aliases. Fixed serialization missing from merge.

* Update SlashCommandHandler.cpp

* Update SlashCommandHandler.cpp

* Update SlashCommandHandler.cpp

* Update SlashCommandHandler.cpp

* Update SlashCommandHandler.cpp

* Update SlashCommandHandler.cpp

* Update SlashCommandHandler.cpp

* Update SlashCommandHandler.cpp
2024-05-21 20:02:07 -05:00
TAHuntling
d6cac65a8d fix: Falling Off Edge in Pet Puzzle (#1584)
* FloatFix

* game activity setting

* Update dNavMesh.cpp

---------

Co-authored-by: David Markowitz <EmosewaMC@gmail.com>
2024-05-21 20:01:44 -05:00
David Markowitz
d8f079cb1b fix mpc resetting on each world load (#1588) 2024-05-20 02:43:57 -05:00
TAHuntling
c8e0bb0db0 feat: Command Sorting (#1580)
* Update SlashCommandHandler.cpp

* Update SlashCommandHandler.cpp

* Update SlashCommandHandler.cpp

* Update SlashCommandHandler.cpp

* Update dGame/dUtilities/SlashCommandHandler.cpp

Co-authored-by: David Markowitz <39972741+EmosewaMC@users.noreply.github.com>

* Update SlashCommandHandler.cpp

* Update SlashCommandHandler.cpp

* Update SlashCommandHandler.cpp

* Update dGame/dUtilities/SlashCommandHandler.cpp

Co-authored-by: David Markowitz <39972741+EmosewaMC@users.noreply.github.com>

* Update SlashCommandHandler.cpp

* Update SlashCommandHandler.cpp

* Update dGame/dUtilities/SlashCommandHandler.cpp

Co-authored-by: David Markowitz <39972741+EmosewaMC@users.noreply.github.com>

* Update dGame/dUtilities/SlashCommandHandler.cpp

Co-authored-by: David Markowitz <39972741+EmosewaMC@users.noreply.github.com>

* Update dGame/dUtilities/SlashCommandHandler.cpp

Co-authored-by: David Markowitz <39972741+EmosewaMC@users.noreply.github.com>

* Update dGame/dUtilities/SlashCommandHandler.cpp

Co-authored-by: David Markowitz <39972741+EmosewaMC@users.noreply.github.com>

* Update dGame/dUtilities/SlashCommandHandler.cpp

Co-authored-by: David Markowitz <39972741+EmosewaMC@users.noreply.github.com>

* Update dGame/dUtilities/SlashCommandHandler.cpp

Co-authored-by: David Markowitz <39972741+EmosewaMC@users.noreply.github.com>

* Update SlashCommandHandler.cpp

* Update dGame/dUtilities/SlashCommandHandler.cpp

Co-authored-by: David Markowitz <39972741+EmosewaMC@users.noreply.github.com>

* Update SlashCommandHandler.cpp

* Update SlashCommandHandler.cpp

---------

Co-authored-by: David Markowitz <39972741+EmosewaMC@users.noreply.github.com>
2024-05-16 22:02:30 -07:00
David Markowitz
d9d262d3f1 prevent building in a folder which contains spaces (#1583) 2024-05-16 08:05:57 -05:00
TAHuntling
d0a5678290 chore: CppScripts refactor (#1579)
* Updating CppScripts

Rewrote file to use a lambda map rather than the massive if else chain. Kept the original comments alongside each of the different scripts they were by before.

* add script tests

* Update names

* More Changes to Scripts

* Update CppScripts.cpp

* Removing Unneeded Files

* Update CppScripts.cpp

* Delete tests/dGameTests/dScriptsTests/CMakeLists.txt

* Delete tests/dGameTests/dScriptsTests/dScriptsTests.cpp

* Delete tests/dGameTests/dScriptsTests/CppScriptsOld.cpp

* Delete tests/dGameTests/dScriptsTests/CppScriptsOld.h

* Update CMakeLists.txt

* finishing up

---------

Co-authored-by: David Markowitz <EmosewaMC@gmail.com>
2024-05-16 04:50:18 -05:00
David Markowitz
35321b22d9 script fixes (#1577)
fixes an issue where the sirens would not be destroyed correctly
fixes undefined behavior in buff station

ok for real this time

actual fix for mermaids

and for general death_behavior 0 skill stuff
2024-05-16 04:30:32 -05:00
David Markowitz
8837b110ab add include guards (#1569) 2024-05-16 04:30:00 -05:00
David Markowitz
09a8c99f3e fix: mail crash from underflow and document variables (#1582)
* fix mail crash and document variables

* const
2024-05-16 04:29:48 -05:00
Terrev
e3b108e00e fv race place atm (#1570) 2024-05-13 06:18:27 -05:00
David Markowitz
9f382aca42 fix: use after free in mission progression after removing item from inventory (#1567)
that method is cursed.

no longer has ub when deleting an item from the inventory
2024-05-12 07:30:03 -05:00
David Markowitz
4d1395e522 Update CheatDetection.cpp (#1559) 2024-05-10 16:20:42 -05:00
9e36510c6b chore: Bump verion to 2.3.0 (#1564)
fix versions.txt, update cmake version
2024-05-10 15:21:10 -05:00
David Markowitz
2ca61c3e57 feat: Dragonmaw (#1562)
* rigid as heck

* abstract physics creation to separate function

* loading

Update FvRacePillarDServer.cpp

consolidate abcd pillar logic

modularization

Update SimplePhysicsComponent.cpp

Update EntityManager.cpp

Update MovingPlatformComponent.cpp

still need another pass

* geiser works

* columns working finally

* consolidate logic

* constiness

* Update PhantomPhysicsComponent.cpp

* Update PhysicsComponent.cpp

* revert testing code

* add versions info

---------

Co-authored-by: Aaron Kimbre <aronwk.aaron@gmail.com>
2024-05-10 09:22:26 -05:00
07cb19cc30 chore: remove json (#1561)
we can't use it currently due to threadsafety issues, so just going to remove it until we actually need it and will re-add it as a vendored file later due to cmake issues pulling in things
2024-05-02 06:35:55 -05:00
David Markowitz
794b254fe7 remove md5 new (#1560)
tested that sqlite hash is still calculated
2024-05-02 06:35:44 -05:00
Gie "Max" Vanommeslaeghe
ab7f6f0b57 Merge pull request #1558 from DarkflameUniverse/swap-update-order
fix: out of order physics updates
2024-05-01 22:33:45 +02:00
David Markowitz
58cc569c75 fix out of order physics updates
fixes an issue where physics entities were not given a chance to be marked as sleeping, causing a initial sleeping calls to be missed and causing objects that collided with one another to not register new collisions since they were sleeping at the time the new collision fired off.

Tested that Brick Fury now corectly aggros the _first_ spawn of enemies near by to him.
Tested that the turrets in crux prime now correctly shoot the _first_ wave of enemies that spawn.
2024-04-30 23:09:35 -07:00
Daniel Seiler
35c463656d Use volume for mariadb persistence (#1555)
I initially used the bind mount because it's arguably easier to back up and move around than a volume, but turns out with https://github.com/DarkflameUniverse/NexusDashboard/issues/92 it's nothing we can recommend for Docker Desktop on WSL, which unfortunately is the primary setup newcomers will try this with. So changing the default to be a volume should address that (presumably by hosting the volume within the WSL Docker VM, as opposed to the host NTFS filesystem)
2024-04-29 22:51:13 +02:00
68 changed files with 1809 additions and 1458 deletions

View File

@@ -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)

View File

@@ -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.

View File

@@ -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

View File

@@ -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)

View File

@@ -1,5 +1,4 @@
set(DCHATSERVER_SOURCES
"ChatWebApi.cpp"
"ChatIgnoreList.cpp"
"ChatPacketHandler.cpp"
"PlayerContainer.cpp"

View File

@@ -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;
}

View File

@@ -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();
}

View File

@@ -1,6 +0,0 @@
#include <cstdint>
namespace ChatWebApi {
void Listen(const uint32_t port);
void Stop();
};

View File

@@ -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 =

View File

@@ -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;

View File

@@ -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__

View File

@@ -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);
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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());

View File

@@ -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) {

View File

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

View File

@@ -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);
}
}

View File

@@ -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;

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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);
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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));
}
}

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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);
}

View File

@@ -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();
}

View File

@@ -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

View File

@@ -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

View File

@@ -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) {}
};

View File

@@ -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

View File

@@ -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;

View File

@@ -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:

View File

@@ -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);
}

View File

@@ -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?

View File

@@ -1,3 +1,6 @@
set(DSCRIPTS_SOURCES_02_SERVER_MAP_FV_RACING
"RaceFireballs.cpp"
"RaceMaelstromGeiser.cpp"
"RaceShipLapColumnsServer.cpp"
"FvRacingColumns.cpp"
PARENT_SCOPE)

View 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);
}

View File

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

View 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));
}
}

View 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;
};

View File

@@ -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");
}
}

View File

@@ -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;
};

View File

@@ -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

View 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;
}

View 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);
};

View File

@@ -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;
}

View File

@@ -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);
};

View File

@@ -1,6 +1,7 @@
set(DSCRIPTS_SOURCES_AI_AG
"AgShipPlayerDeathTrigger.cpp"
"AgSpaceStuff.cpp"
"AgShipShake.cpp"
"AgShipPlayerShockServer.cpp"
"AgImagSmashable.cpp"
"ActSharkPlayerDeathTrigger.cpp"

View File

@@ -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)

View 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);
}

View 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;
};

View 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);
}
}

View 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;
};

View 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);
}
}

View 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;
};

View 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);
}
}

View 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

View File

@@ -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();

View File

@@ -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:

View File

@@ -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!

View File

@@ -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

View File

@@ -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-->

View File

@@ -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