Compare commits

...

29 Commits

Author SHA1 Message Date
David Markowitz
8f6ee6327e add deep copy of config data 2024-05-24 21:17:56 -07:00
David Markowitz
d572ce1f1c add back deleter 2024-05-24 03:56:38 -07:00
David Markowitz
85cd609168 remove extra item code 2024-05-23 20:10:44 -07:00
David Markowitz
d4e6939dbd Merge branch 'main' into naming-of-models 2024-05-23 19:51:45 -07:00
David Markowitz
f8d5110290 Update PropertyManagementComponent.cpp
Revert "fix stale reference"

This reverts commit 03b17c0374787bca6dfec2da663da74ac76c26fb.

fix stale reference
2024-05-23 14:42:15 -07: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
a153d0a78c add settings if they dont exist
add helper for spawnerinfo config data
2024-05-21 04:27:17 -07:00
David Markowitz
bfeb10c972 update ldf logic 2024-05-21 04:06:55 -07:00
David Markowitz
82507e642a add erase var helper for entity 2024-05-21 04:06:16 -07:00
David Markowitz
abd978c348 names and descriptions work 2024-05-20 22:39:56 -07:00
David Markowitz
10f8d40b69 saving changes 2024-05-20 21:24:52 -07:00
David Markowitz
37c2c5db5d remove extra map 2024-05-20 20:51:03 -07:00
David Markowitz
693a2fef35 move logic to Item 2024-05-20 20:49:42 -07:00
David Markowitz
787237c930 modularize loading for all possible extra data 2024-05-20 14:33:10 -07:00
David Markowitz
0464f37fab add declaration to header 2024-05-20 13:39:40 -07:00
David Markowitz
78ae8bc6b6 stubbing for saving item extra data 2024-05-20 13:38:59 -07: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
32 changed files with 1242 additions and 900 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)

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

@@ -239,7 +239,7 @@ void Character::SaveXMLToDatabase() {
auto zoneInfo = Game::zoneManager->GetZone()->GetZoneID();
// lzid garbage, binary concat of zoneID, zoneInstance and zoneClone
if (zoneInfo.GetMapID() != 0 && zoneInfo.GetCloneID() == 0 && !Game::zoneManager->GetDisableSaveLocation()) {
// if (zoneInfo.GetMapID() != 0 && zoneInfo.GetCloneID() == 0 && !Game::zoneManager->GetDisableSaveLocation()) {
uint64_t lzidConcat = zoneInfo.GetCloneID();
lzidConcat = (lzidConcat << 16) | uint16_t(zoneInfo.GetInstanceID());
lzidConcat = (lzidConcat << 16) | uint16_t(zoneInfo.GetMapID());
@@ -251,7 +251,7 @@ void Character::SaveXMLToDatabase() {
// Set the target scene, custom attribute
character->SetAttribute("tscene", m_TargetScene.c_str());
}
// }
auto emotes = character->FirstChildElement("ue");
if (!emotes) emotes = m_Doc.NewElement("ue");

View File

@@ -660,7 +660,7 @@ void Entity::Initialize() {
}
PetComponent* petComponent;
if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::ITEM) > 0 && !TryGetComponent(eReplicaComponentType::PET, petComponent) && !HasComponent(eReplicaComponentType::MODEL)) {
if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::ITEM) > 0) {
AddComponent<ItemComponent>();
}
@@ -901,6 +901,14 @@ void Entity::WriteBaseReplicaData(RakNet::BitStream& outBitStream, eReplicaPacke
for (size_t i = 0; i < name.size(); ++i) {
outBitStream.Write<uint16_t>(name[i]);
}
} else if (HasVar(u"userModelName")) {
const auto& name = GetVar<std::string>(u"userModelName");
outBitStream.Write<uint8_t>(uint8_t(name.size()));
for (size_t i = 0; i < name.size(); ++i) {
outBitStream.Write<uint16_t>(name[i]);
}
} else {
const auto& name = GetVar<std::string>(u"npcName");
outBitStream.Write<uint8_t>(uint8_t(name.size()));
@@ -1534,7 +1542,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!
@@ -2158,3 +2166,12 @@ void Entity::SetRespawnRot(const NiQuaternion& rotation) {
auto* characterComponent = GetComponent<CharacterComponent>();
if (characterComponent) characterComponent->SetRespawnRot(rotation);
}
void Entity::EraseVar(const std::u16string& name) {
for (auto it = m_Settings.begin(); it != m_Settings.end(); ++it) {
if ((*it)->GetKey() == name) {
m_Settings.erase(it);
return;
}
}
}

View File

@@ -299,6 +299,7 @@ public:
// Scale will only be communicated to the client when the construction packet is sent
void SetScale(const float scale) { m_Scale = scale; };
void EraseVar(const std::u16string& name);
protected:
LWOOBJID m_ObjectID;

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

@@ -187,7 +187,7 @@ void ControllablePhysicsComponent::UpdateXml(tinyxml2::XMLDocument& doc) {
auto zoneInfo = Game::zoneManager->GetZone()->GetZoneID();
if (zoneInfo.GetMapID() != 0 && zoneInfo.GetCloneID() == 0 && !Game::zoneManager->GetDisableSaveLocation()) {
// if (zoneInfo.GetMapID() != 0 && zoneInfo.GetCloneID() == 0 && !Game::zoneManager->GetDisableSaveLocation()) {
character->SetAttribute("lzx", m_Position.x);
character->SetAttribute("lzy", m_Position.y);
character->SetAttribute("lzz", m_Position.z);
@@ -195,7 +195,7 @@ void ControllablePhysicsComponent::UpdateXml(tinyxml2::XMLDocument& doc) {
character->SetAttribute("lzry", m_Rotation.y);
character->SetAttribute("lzrz", m_Rotation.z);
character->SetAttribute("lzrw", m_Rotation.w);
}
// }
}
void ControllablePhysicsComponent::SetPosition(const NiPoint3& pos) {

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

@@ -1,5 +1,29 @@
#include "ItemComponent.h"
void ItemComponent::Serialize(RakNet::BitStream& outBitStream, bool isConstruction) {
outBitStream.Write0();
ItemComponent::ItemComponent(Entity* entity) : Component(entity) {
m_UgcId = m_Parent->GetVarAs<LWOOBJID>(u"userModelID");
m_Description = GeneralUtils::ASCIIToUTF16(m_Parent->GetVar<std::string>(u"userModelDesc"));
m_Dirty = false;
m_ModerationStatus = 2;
LOG("%s", m_Parent->GetVarAsString(u"userModelDesc").c_str());
}
void ItemComponent::Serialize(RakNet::BitStream& outBitStream, bool isConstruction) {
outBitStream.Write(m_Dirty || isConstruction);
if (m_Dirty || isConstruction) {
outBitStream.Write(m_UgcId);
outBitStream.Write(m_ModerationStatus);
outBitStream.Write(!m_Description.empty());
if (!m_Description.empty()) {
outBitStream.Write<uint32_t>(m_Description.size());
outBitStream.Write(m_Description);
}
if (!isConstruction) m_Dirty = false;
}
}
void ItemComponent::UpdateDescription(const std::u16string& description) {
if (m_Description == description) return;
m_Dirty = true;
m_Description = description;
}

View File

@@ -8,9 +8,16 @@ class ItemComponent final : public Component {
public:
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::ITEM;
ItemComponent(Entity* entity) : Component(entity) {}
ItemComponent(Entity* entity);
void Serialize(RakNet::BitStream& bitStream, bool isConstruction) override;
void UpdateDescription(const std::u16string& description);
private:
std::u16string m_Description;
bool m_Dirty = false;
LWOOBJID m_UgcId = LWOOBJID_EMPTY;
uint32_t m_ModerationStatus = 0;
};
#endif //!__ITEMCOMPONENT__H__

View File

@@ -10,19 +10,9 @@
ModelComponent::ModelComponent(Entity* parent) : Component(parent) {
m_OriginalPosition = m_Parent->GetDefaultPosition();
m_OriginalRotation = m_Parent->GetDefaultRotation();
m_userModelID = m_Parent->GetVarAs<LWOOBJID>(u"userModelID");
}
void ModelComponent::Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) {
// ItemComponent Serialization. Pets do not get this serialization.
if (!m_Parent->HasComponent(eReplicaComponentType::PET)) {
outBitStream.Write1();
outBitStream.Write<LWOOBJID>(m_userModelID != LWOOBJID_EMPTY ? m_userModelID : m_Parent->GetObjectID());
outBitStream.Write<int>(0);
outBitStream.Write0();
}
//actual model component:
outBitStream.Write1(); // Yes we are writing model info
outBitStream.Write0(); // Is pickable

View File

@@ -126,9 +126,4 @@ private:
* The rotation original of the model
*/
NiQuaternion m_OriginalRotation;
/**
* The ID of the user that made the model
*/
LWOOBJID m_userModelID;
};

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

@@ -21,7 +21,9 @@
#include "eObjectBits.h"
#include "CharacterComponent.h"
#include "PlayerManager.h"
#include "ItemComponent.h"
#include <ranges>
#include <vector>
#include "CppScripts.h"
@@ -152,21 +154,49 @@ void PropertyManagementComponent::SetPrivacyOption(PropertyPrivacyOption value)
Database::Get()->UpdatePropertyModerationInfo(info);
}
void PropertyManagementComponent::UpdatePropertyDetails(std::string name, std::string description) {
void PropertyManagementComponent::UpdatePropertyDetails(UpdatePropertyWithFilterCheck& update) {
if (owner == LWOOBJID_EMPTY) return;
propertyName = name;
if (update.isProperty) {
propertyName = update.name;
propertyDescription = description;
propertyDescription = update.description;
IProperty::Info info;
info.id = propertyId;
info.name = propertyName;
info.description = propertyDescription;
IProperty::Info info;
info.id = propertyId;
info.name = propertyName;
info.description = propertyDescription;
Database::Get()->UpdatePropertyDetails(info);
Database::Get()->UpdatePropertyDetails(info);
OnQueryPropertyData(GetOwner(), UNASSIGNED_SYSTEM_ADDRESS);
OnQueryPropertyData(GetOwner(), UNASSIGNED_SYSTEM_ADDRESS);
} else {
auto* entity = Game::entityManager->GetEntity(update.worldId);
if (!entity) return;
if (update.name.empty()) {
entity->EraseVar(u"userModelName");
} else {
entity->SetVar<std::string>(u"userModelName", update.name);
}
if (update.description.empty()) {
entity->EraseVar(u"userModelDesc");
} else {
entity->SetVar<std::string>(u"userModelDesc", update.description);
}
auto* owner = GetOwner();
if (!owner) return;
GameMessages::SendSetName(update.worldId, GeneralUtils::ASCIIToUTF16(update.name), owner->GetSystemAddress());
auto* itemComponent = entity->GetComponent<ItemComponent>();
if (itemComponent) {
itemComponent->UpdateDescription(GeneralUtils::ASCIIToUTF16(update.description));
}
Game::entityManager->SerializeEntity(entity);
}
}
bool PropertyManagementComponent::Claim(const LWOOBJID playerId) {
@@ -195,7 +225,7 @@ bool PropertyManagementComponent::Claim(const LWOOBJID playerId) {
auto prop_path = zone->GetPath(m_Parent->GetVarAsString(u"propertyName"));
if (prop_path){
if (prop_path) {
if (!prop_path->property.displayName.empty()) name = prop_path->property.displayName;
description = prop_path->property.displayDesc;
}
@@ -325,12 +355,15 @@ void PropertyManagementComponent::UpdateModelPosition(const LWOOBJID id, const N
return;
}
item->SetCount(item->GetCount() - 1);
auto* node = new SpawnerNode();
node->position = position;
node->rotation = rotation;
for (const auto config : item->GetConfig()) {
node->config.push_back(config->Copy());
}
item->SetCount(item->GetCount() - 1);
ObjectIDManager::RequestPersistentID([this, node, modelLOT, entity, position, rotation, originalRotation](uint32_t persistentId) {
SpawnerInfo info{};
@@ -352,11 +385,27 @@ void PropertyManagementComponent::UpdateModelPosition(const LWOOBJID id, const N
auto* spawner = Game::zoneManager->GetSpawner(spawnerId);
info.nodes[0]->config.push_back(new LDFData<LWOOBJID>(u"modelBehaviors", 0));
info.nodes[0]->config.push_back(new LDFData<LWOOBJID>(u"userModelID", info.spawnerID));
info.nodes[0]->config.push_back(new LDFData<int>(u"modelType", 2));
info.nodes[0]->config.push_back(new LDFData<bool>(u"propertyObjectID", true));
info.nodes[0]->config.push_back(new LDFData<int>(u"componentWhitelist", 1));
// If empty, insert the default config data since it doesn't exist yet
if (!node->HasVar(u"modelBehaviors")) {
node->config.push_back(new LDFData<LWOOBJID>(u"modelBehaviors", 0));
}
if (!node->HasVar(u"userModelID")) {
node->config.push_back(new LDFData<LWOOBJID>(u"userModelID", info.spawnerID));
}
if (!node->HasVar(u"modelType")) {
node->config.push_back(new LDFData<int>(u"modelType", 2));
}
if (!node->HasVar(u"propertyObjectID")) {
node->config.push_back(new LDFData<bool>(u"propertyObjectID", true));
}
if (!node->HasVar(u"componentWhitelist")) {
node->config.push_back(new LDFData<int>(u"componentWhitelist", 1));
}
auto* model = spawner->Spawn();
@@ -436,7 +485,7 @@ void PropertyManagementComponent::DeleteModel(const LWOOBJID id, const int delet
std::vector<LDFBaseData*> settings;
//fill our settings with BBB gurbage
LDFBaseData* ldfBlueprintID = new LDFData<LWOOBJID>(u"blueprintid", model->GetVar<LWOOBJID>(u"blueprintid"));
LDFBaseData* ldfBlueprintID = new LDFData<LWOOBJID>(u"blueprintID", model->GetVar<LWOOBJID>(u"blueprintID"));
LDFBaseData* userModelDesc = new LDFData<std::u16string>(u"userModelDesc", u"A cool model you made!");
LDFBaseData* userModelHasBhvr = new LDFData<bool>(u"userModelHasBhvr", false);
LDFBaseData* userModelID = new LDFData<LWOOBJID>(u"userModelID", model->GetVar<LWOOBJID>(u"userModelID"));
@@ -484,8 +533,9 @@ void PropertyManagementComponent::DeleteModel(const LWOOBJID id, const int delet
return;
}
inventoryComponent->AddItem(model->GetLOT(), 1, eLootSourceType::DELETION, INVALID, {}, LWOOBJID_EMPTY, false);
inventoryComponent->AddItem(model->GetLOT(), 1, eLootSourceType::DELETION, INVALID, model->GetSettings(), LWOOBJID_EMPTY, false);
// figure out how to get the actual item we picked up instead of just the first lot we find
auto* item = inventoryComponent->FindItemByLot(model->GetLOT());
if (item == nullptr) {
@@ -572,28 +622,21 @@ void PropertyManagementComponent::Load() {
info.spawnerID = databaseModel.id;
std::vector<LDFBaseData*> settings;
//BBB property models need to have extra stuff set for them:
if (databaseModel.lot == 14) {
LWOOBJID blueprintID = databaseModel.ugcId;
GeneralUtils::SetBit(blueprintID, eObjectBits::CHARACTER);
GeneralUtils::SetBit(blueprintID, eObjectBits::PERSISTENT);
settings.push_back(new LDFData<LWOOBJID>(u"blueprintid", blueprintID));
settings.push_back(new LDFData<int>(u"componentWhitelist", 1));
settings.push_back(new LDFData<int>(u"modelType", 2));
settings.push_back(new LDFData<bool>(u"propertyObjectID", true));
settings.push_back(new LDFData<LWOOBJID>(u"userModelID", databaseModel.id));
if (!node->HasVar(u"blueprintID")) node->config.push_back(new LDFData<LWOOBJID>(u"blueprintID", blueprintID));
} else {
settings.push_back(new LDFData<int>(u"modelType", 2));
settings.push_back(new LDFData<LWOOBJID>(u"userModelID", databaseModel.id));
settings.push_back(new LDFData<LWOOBJID>(u"modelBehaviors", 0));
settings.push_back(new LDFData<bool>(u"propertyObjectID", true));
settings.push_back(new LDFData<int>(u"componentWhitelist", 1));
if (!node->HasVar(u"modelBehaviors")) node->config.push_back(new LDFData<LWOOBJID>(u"modelBehaviors", 0));
}
node->config = settings;
if (!node->HasVar(u"modelType")) node->config.push_back(new LDFData<int>(u"modelType", 2));
if (!node->HasVar(u"componentWhitelist")) node->config.push_back(new LDFData<int>(u"componentWhitelist", 1));
if (!node->HasVar(u"propertyObjectID")) node->config.push_back(new LDFData<bool>(u"propertyObjectID", true));
if (!node->HasVar(u"userModelID")) node->config.push_back(new LDFData<LWOOBJID>(u"userModelID", databaseModel.id));
const auto spawnerId = Game::zoneManager->MakeSpawner(info);

View File

@@ -17,12 +17,12 @@ enum class PropertyPrivacyOption {
/**
* Your friends can visit your property
*/
Friends = 1,
Friends = 1,
/**
* Requires Mythran approval, everyone can visit your property
*/
Public = 2
/**
* Requires Mythran approval, everyone can visit your property
*/
Public = 2
};
/**
@@ -30,6 +30,13 @@ enum class PropertyPrivacyOption {
*/
class PropertyManagementComponent final : public Component {
public:
struct UpdatePropertyWithFilterCheck {
LWOOBJID objectId;
LWOOBJID worldId;
bool isProperty;
std::string name;
std::string description;
};
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::PROPERTY_MANAGEMENT;
PropertyManagementComponent(Entity* parent);
static PropertyManagementComponent* Instance();
@@ -95,7 +102,7 @@ public:
* @param name the name to set for the property
* @param description the description to set for the property
*/
void UpdatePropertyDetails(std::string name, std::string description);
void UpdatePropertyDetails(UpdatePropertyWithFilterCheck& update);
/**
* Makes this property owned by the passed player ID, storing it in the database

View File

@@ -412,7 +412,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) {
@@ -2206,19 +2207,17 @@ void GameMessages::HandleUnUseModel(RakNet::BitStream& inStream, Entity* entity,
}
void GameMessages::HandleUpdatePropertyOrModelForFilterCheck(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr) {
bool isProperty{};
LWOOBJID objectId{};
PropertyManagementComponent::UpdatePropertyWithFilterCheck filterCheck{};
LWOOBJID playerId{};
LWOOBJID worldId{};
uint32_t nameLength{};
std::u16string name{};
uint32_t descriptionLength{};
std::u16string description{};
inStream.Read(isProperty);
inStream.Read(objectId);
inStream.Read(filterCheck.isProperty);
inStream.Read(filterCheck.objectId);
inStream.Read(playerId);
inStream.Read(worldId);
inStream.Read(filterCheck.worldId);
inStream.Read(descriptionLength);
for (uint32_t i = 0; i < descriptionLength; ++i) {
@@ -2226,6 +2225,7 @@ void GameMessages::HandleUpdatePropertyOrModelForFilterCheck(RakNet::BitStream&
inStream.Read(character);
description.push_back(character);
}
filterCheck.description = GeneralUtils::UTF16ToWTF8(description);
inStream.Read(nameLength);
for (uint32_t i = 0; i < nameLength; ++i) {
@@ -2233,8 +2233,10 @@ void GameMessages::HandleUpdatePropertyOrModelForFilterCheck(RakNet::BitStream&
inStream.Read(character);
name.push_back(character);
}
filterCheck.name = GeneralUtils::UTF16ToWTF8(name);
LOG("names %s desc %s", filterCheck.name.c_str(), filterCheck.description.c_str());
PropertyManagementComponent::Instance()->UpdatePropertyDetails(GeneralUtils::UTF16ToWTF8(name), GeneralUtils::UTF16ToWTF8(description));
PropertyManagementComponent::Instance()->UpdatePropertyDetails(filterCheck);
}
void GameMessages::HandleQueryPropertyData(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr) {
@@ -2663,7 +2665,7 @@ void GameMessages::HandleBBBSaveRequest(RakNet::BitStream& inStream, Entity* ent
info.spawnerID = entity->GetObjectID();
info.spawnerNodeID = 0;
info.settings.push_back(new LDFData<LWOOBJID>(u"blueprintid", blueprintID));
info.settings.push_back(new LDFData<LWOOBJID>(u"blueprintID", blueprintID));
info.settings.push_back(new LDFData<int>(u"componentWhitelist", 1));
info.settings.push_back(new LDFData<int>(u"modelType", 2));
info.settings.push_back(new LDFData<bool>(u"propertyObjectID", true));
@@ -5354,14 +5356,14 @@ void GameMessages::HandleRemoveItemFromInventory(RakNet::BitStream& inStream, En
if (eInvType == eInventoryType::MODELS) {
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

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

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

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

@@ -7,6 +7,15 @@
#include "GeneralUtils.h"
#include "dZoneManager.h"
bool SpawnerNode::HasVar(const std::u16string_view view) {
for (const auto* data : config) {
if (data->GetKey() == view) {
return true;
}
}
return false;
}
Spawner::Spawner(const SpawnerInfo info) {
m_Info = info;
m_Active = m_Info.activeOnLoad && info.spawnActivator;

View File

@@ -10,6 +10,7 @@
#include <functional>
#include "LDFFormat.h"
#include "EntityInfo.h"
#include <string_view>
struct SpawnerNode {
NiPoint3 position = NiPoint3Constant::ZERO;
@@ -18,6 +19,8 @@ struct SpawnerNode {
uint32_t nodeMax = 1;
std::vector<LWOOBJID> entities;
std::vector<LDFBaseData*> config;
bool HasVar(const std::u16string_view view);
};
struct SpawnerInfo {

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