mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2025-12-20 11:59:40 -06:00
Compare commits
33 Commits
naming-of-
...
property_b
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b984cd6a0b | ||
|
|
bcf1058759 | ||
|
|
fee0238e79 | ||
|
|
1454fcd003 | ||
|
|
af651f0d63 | ||
| ff38503597 | |||
| 3f22bf5cc0 | |||
| 9d5d2a68ee | |||
| 1a14c29c39 | |||
| 2ef45bd7ee | |||
| b56d077892 | |||
|
|
a54600b41e | ||
|
|
342da927f5 | ||
|
|
01086d05c8 | ||
|
|
cce5755366 | ||
|
|
e966d3a644 | ||
|
|
9328021339 | ||
|
|
d1134fdd62 | ||
|
|
efa658bc31 | ||
|
|
e59525d2ae | ||
|
|
0348db72a5 | ||
|
|
debc2a96e2 | ||
| 86f335d64b | |||
| 8ca05241f2 | |||
|
|
8ae1a8ff7c | ||
|
|
db2d4f02b5 | ||
|
|
00f36f3f28 | ||
|
|
a50b256689 | ||
|
|
b3548de7da | ||
|
|
387c37505c | ||
|
|
0c4108e730 | ||
|
|
fd1c6ab2ea | ||
|
|
f2bf9a2a28 |
@@ -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
|
||||
|
||||
@@ -179,6 +179,7 @@ int main(int argc, char** argv) {
|
||||
}
|
||||
|
||||
void HandlePacket(Packet* packet) {
|
||||
if (packet->length < 1) return;
|
||||
if (packet->data[0] == ID_DISCONNECTION_NOTIFICATION || packet->data[0] == ID_CONNECTION_LOST) {
|
||||
LOG("A server has disconnected, erasing their connected players from the list.");
|
||||
} else if (packet->data[0] == ID_NEW_INCOMING_CONNECTION) {
|
||||
|
||||
@@ -201,7 +201,7 @@ void OnTerminate() {
|
||||
}
|
||||
|
||||
void MakeBacktrace() {
|
||||
struct sigaction sigact;
|
||||
struct sigaction sigact{};
|
||||
|
||||
sigact.sa_sigaction = CritErrHdlr;
|
||||
sigact.sa_flags = SA_RESTART | SA_SIGINFO;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "dConfig.h"
|
||||
|
||||
#include <sstream>
|
||||
#include <algorithm>
|
||||
|
||||
#include "BinaryPathFinder.h"
|
||||
#include "GeneralUtils.h"
|
||||
|
||||
21
dCommon/dEnums/eReponseMoveItemBetweenInventoryTypeCode.h
Normal file
21
dCommon/dEnums/eReponseMoveItemBetweenInventoryTypeCode.h
Normal file
@@ -0,0 +1,21 @@
|
||||
#ifndef __EREPONSEMOVEITEMBETWEENINVENTORYTYPECODE__H__
|
||||
#define __EREPONSEMOVEITEMBETWEENINVENTORYTYPECODE__H__
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
enum class eReponseMoveItemBetweenInventoryTypeCode : int32_t {
|
||||
SUCCESS,
|
||||
FAIL_GENERIC,
|
||||
FAIL_INV_FULL,
|
||||
FAIL_ITEM_NOT_FOUND,
|
||||
FAIL_CANT_MOVE_TO_THAT_INV_TYPE,
|
||||
FAIL_NOT_NEAR_BANK,
|
||||
FAIL_CANT_SWAP_ITEMS,
|
||||
FAIL_SOURCE_TYPE,
|
||||
FAIL_WRONG_DEST_TYPE,
|
||||
FAIL_SWAP_DEST_TYPE,
|
||||
FAIL_CANT_MOVE_THINKING_HAT,
|
||||
FAIL_DISMOUNT_BEFORE_MOVING
|
||||
};
|
||||
|
||||
#endif //!__EREPONSEMOVEITEMBETWEENINVENTORYTYPECODE__H__
|
||||
@@ -23,6 +23,7 @@
|
||||
#include "IActivityLog.h"
|
||||
#include "IIgnoreList.h"
|
||||
#include "IAccountsRewardCodes.h"
|
||||
#include "IBehaviors.h"
|
||||
|
||||
namespace sql {
|
||||
class Statement;
|
||||
@@ -40,7 +41,8 @@ class GameDatabase :
|
||||
public IMail, public ICommandLog, public IPlayerCheatDetections, public IBugReports,
|
||||
public IPropertyContents, public IProperty, public IPetNames, public ICharXml,
|
||||
public IMigrationHistory, public IUgc, public IFriends, public ICharInfo,
|
||||
public IAccounts, public IActivityLog, public IAccountsRewardCodes, public IIgnoreList {
|
||||
public IAccounts, public IActivityLog, public IAccountsRewardCodes, public IIgnoreList,
|
||||
public IBehaviors {
|
||||
public:
|
||||
virtual ~GameDatabase() = default;
|
||||
// TODO: These should be made private.
|
||||
|
||||
22
dDatabase/GameDatabase/ITables/IBehaviors.h
Normal file
22
dDatabase/GameDatabase/ITables/IBehaviors.h
Normal file
@@ -0,0 +1,22 @@
|
||||
#ifndef IBEHAVIORS_H
|
||||
#define IBEHAVIORS_H
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "dCommonVars.h"
|
||||
|
||||
class IBehaviors {
|
||||
public:
|
||||
struct Info {
|
||||
int32_t behaviorId{};
|
||||
uint32_t characterId{};
|
||||
std::string behaviorInfo;
|
||||
};
|
||||
|
||||
// This Add also takes care of updating if it exists.
|
||||
virtual void AddBehavior(const Info& info) = 0;
|
||||
virtual std::string GetBehavior(const int32_t behaviorId) = 0;
|
||||
virtual void RemoveBehavior(const int32_t behaviorId) = 0;
|
||||
};
|
||||
|
||||
#endif //!IBEHAVIORS_H
|
||||
@@ -1,6 +1,7 @@
|
||||
#ifndef __IPROPERTIESCONTENTS__H__
|
||||
#define __IPROPERTIESCONTENTS__H__
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <string_view>
|
||||
|
||||
@@ -16,6 +17,7 @@ public:
|
||||
LWOOBJID id{};
|
||||
LOT lot{};
|
||||
uint32_t ugcId{};
|
||||
std::array<int32_t, 5> behaviors{};
|
||||
};
|
||||
|
||||
// Inserts a new UGC model into the database.
|
||||
@@ -32,7 +34,7 @@ public:
|
||||
virtual void InsertNewPropertyModel(const LWOOBJID& propertyId, const IPropertyContents::Model& model, const std::string_view name) = 0;
|
||||
|
||||
// Update the model position and rotation for the given property id.
|
||||
virtual void UpdateModelPositionRotation(const LWOOBJID& propertyId, const NiPoint3& position, const NiQuaternion& rotation) = 0;
|
||||
virtual void UpdateModel(const LWOOBJID& propertyId, const NiPoint3& position, const NiQuaternion& rotation, const std::array<std::pair<int32_t, std::string>, 5>& behaviors) = 0;
|
||||
|
||||
// Remove the model for the given property id.
|
||||
virtual void RemoveModel(const LWOOBJID& modelId) = 0;
|
||||
|
||||
@@ -74,7 +74,7 @@ public:
|
||||
std::vector<IPropertyContents::Model> GetPropertyModels(const LWOOBJID& propertyId) override;
|
||||
void RemoveUnreferencedUgcModels() override;
|
||||
void InsertNewPropertyModel(const LWOOBJID& propertyId, const IPropertyContents::Model& model, const std::string_view name) override;
|
||||
void UpdateModelPositionRotation(const LWOOBJID& propertyId, const NiPoint3& position, const NiQuaternion& rotation) override;
|
||||
void UpdateModel(const LWOOBJID& propertyId, const NiPoint3& position, const NiQuaternion& rotation, const std::array<std::pair<int32_t, std::string>, 5>& behaviors) override;
|
||||
void RemoveModel(const LWOOBJID& modelId) override;
|
||||
void UpdatePerformanceCost(const LWOZONEID& zoneId, const float performanceCost) override;
|
||||
void InsertNewBugReport(const IBugReports::Info& info) override;
|
||||
@@ -108,6 +108,9 @@ public:
|
||||
std::vector<IIgnoreList::Info> GetIgnoreList(const uint32_t playerId) override;
|
||||
void InsertRewardCode(const uint32_t account_id, const uint32_t reward_code) override;
|
||||
std::vector<uint32_t> GetRewardCodesByAccountID(const uint32_t account_id) override;
|
||||
void AddBehavior(const IBehaviors::Info& info) override;
|
||||
std::string GetBehavior(const int32_t behaviorId) override;
|
||||
void RemoveBehavior(const int32_t characterId) override;
|
||||
private:
|
||||
|
||||
// Generic query functions that can be used for any query.
|
||||
|
||||
19
dDatabase/GameDatabase/MySQL/Tables/Behaviors.cpp
Normal file
19
dDatabase/GameDatabase/MySQL/Tables/Behaviors.cpp
Normal file
@@ -0,0 +1,19 @@
|
||||
#include "IBehaviors.h"
|
||||
|
||||
#include "MySQLDatabase.h"
|
||||
|
||||
void MySQLDatabase::AddBehavior(const IBehaviors::Info& info) {
|
||||
ExecuteInsert(
|
||||
"INSERT INTO behaviors (behavior_info, character_id, behavior_id) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE behavior_info = ?",
|
||||
info.behaviorInfo, info.characterId, info.behaviorId, info.behaviorInfo
|
||||
);
|
||||
}
|
||||
|
||||
void MySQLDatabase::RemoveBehavior(const int32_t behaviorId) {
|
||||
ExecuteDelete("DELETE FROM behaviors WHERE behavior_id = ?", behaviorId);
|
||||
}
|
||||
|
||||
std::string MySQLDatabase::GetBehavior(const int32_t behaviorId) {
|
||||
auto result = ExecuteSelect("SELECT behavior_info FROM behaviors WHERE behavior_id = ?", behaviorId);
|
||||
return result->next() ? result->getString("behavior_info").c_str() : "";
|
||||
}
|
||||
@@ -2,6 +2,7 @@ set(DDATABASES_DATABASES_MYSQL_TABLES_SOURCES
|
||||
"Accounts.cpp"
|
||||
"AccountsRewardCodes.cpp"
|
||||
"ActivityLog.cpp"
|
||||
"Behaviors.cpp"
|
||||
"BugReports.cpp"
|
||||
"CharInfo.cpp"
|
||||
"CharXml.cpp"
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
#include "MySQLDatabase.h"
|
||||
|
||||
std::vector<IPropertyContents::Model> MySQLDatabase::GetPropertyModels(const LWOOBJID& propertyId) {
|
||||
auto result = ExecuteSelect("SELECT id, lot, x, y, z, rx, ry, rz, rw, ugc_id FROM properties_contents WHERE property_id = ?;", propertyId);
|
||||
auto result = ExecuteSelect(
|
||||
"SELECT id, lot, x, y, z, rx, ry, rz, rw, ugc_id, "
|
||||
"behavior_1, behavior_2, behavior_3, behavior_4, behavior_5 "
|
||||
"FROM properties_contents WHERE property_id = ?;", propertyId);
|
||||
|
||||
std::vector<IPropertyContents::Model> toReturn;
|
||||
toReturn.reserve(result->rowsCount());
|
||||
@@ -17,6 +20,12 @@ std::vector<IPropertyContents::Model> MySQLDatabase::GetPropertyModels(const LWO
|
||||
model.rotation.y = result->getFloat("ry");
|
||||
model.rotation.z = result->getFloat("rz");
|
||||
model.ugcId = result->getUInt64("ugc_id");
|
||||
model.behaviors[0] = result->getInt("behavior_1");
|
||||
model.behaviors[1] = result->getInt("behavior_2");
|
||||
model.behaviors[2] = result->getInt("behavior_3");
|
||||
model.behaviors[3] = result->getInt("behavior_4");
|
||||
model.behaviors[4] = result->getInt("behavior_5");
|
||||
|
||||
toReturn.push_back(std::move(model));
|
||||
}
|
||||
return toReturn;
|
||||
@@ -32,21 +41,23 @@ void MySQLDatabase::InsertNewPropertyModel(const LWOOBJID& propertyId, const IPr
|
||||
model.id, propertyId, model.ugcId == 0 ? std::nullopt : std::optional(model.ugcId), static_cast<uint32_t>(model.lot),
|
||||
model.position.x, model.position.y, model.position.z, model.rotation.x, model.rotation.y, model.rotation.z, model.rotation.w,
|
||||
name, "", // Model description. TODO implement this.
|
||||
0, // behavior 1. TODO implement this.
|
||||
0, // behavior 2. TODO implement this.
|
||||
0, // behavior 3. TODO implement this.
|
||||
0, // behavior 4. TODO implement this.
|
||||
0 // behavior 5. TODO implement this.
|
||||
model.behaviors[0], // behavior 1
|
||||
model.behaviors[1], // behavior 2
|
||||
model.behaviors[2], // behavior 3
|
||||
model.behaviors[3], // behavior 4
|
||||
model.behaviors[4] // behavior 5
|
||||
);
|
||||
} catch (sql::SQLException& e) {
|
||||
LOG("Error inserting new property model: %s", e.what());
|
||||
}
|
||||
}
|
||||
|
||||
void MySQLDatabase::UpdateModelPositionRotation(const LWOOBJID& propertyId, const NiPoint3& position, const NiQuaternion& rotation) {
|
||||
void MySQLDatabase::UpdateModel(const LWOOBJID& propertyId, const NiPoint3& position, const NiQuaternion& rotation, const std::array<std::pair<int32_t, std::string>, 5>& behaviors) {
|
||||
ExecuteUpdate(
|
||||
"UPDATE properties_contents SET x = ?, y = ?, z = ?, rx = ?, ry = ?, rz = ?, rw = ? WHERE id = ?;",
|
||||
position.x, position.y, position.z, rotation.x, rotation.y, rotation.z, rotation.w, propertyId);
|
||||
"UPDATE properties_contents SET x = ?, y = ?, z = ?, rx = ?, ry = ?, rz = ?, rw = ?, "
|
||||
"behavior_1 = ?, behavior_2 = ?, behavior_3 = ?, behavior_4 = ?, behavior_5 = ? WHERE id = ?;",
|
||||
position.x, position.y, position.z, rotation.x, rotation.y, rotation.z, rotation.w,
|
||||
behaviors[0].first, behaviors[1].first, behaviors[2].first, behaviors[3].first, behaviors[4].first, propertyId);
|
||||
}
|
||||
|
||||
void MySQLDatabase::RemoveModel(const LWOOBJID& modelId) {
|
||||
|
||||
@@ -27,6 +27,8 @@ Character::Character(uint32_t id, User* parentUser) {
|
||||
m_ID = id;
|
||||
m_ParentUser = parentUser;
|
||||
m_OurEntity = nullptr;
|
||||
m_GMLevel = eGameMasterLevel::CIVILIAN;
|
||||
m_PermissionMap = static_cast<ePermissionMap>(0);
|
||||
}
|
||||
|
||||
Character::~Character() {
|
||||
|
||||
@@ -464,22 +464,22 @@ private:
|
||||
/**
|
||||
* The ID of this character. First 32 bits of the ObjectID.
|
||||
*/
|
||||
uint32_t m_ID;
|
||||
uint32_t m_ID{};
|
||||
|
||||
/**
|
||||
* The 64-bit unique ID used in the game.
|
||||
*/
|
||||
LWOOBJID m_ObjectID;
|
||||
LWOOBJID m_ObjectID{ LWOOBJID_EMPTY };
|
||||
|
||||
/**
|
||||
* The user that owns this character.
|
||||
*/
|
||||
User* m_ParentUser;
|
||||
User* m_ParentUser{};
|
||||
|
||||
/**
|
||||
* If the character is in game, this is the entity that it represents, else nullptr.
|
||||
*/
|
||||
Entity* m_OurEntity;
|
||||
Entity* m_OurEntity{};
|
||||
|
||||
/**
|
||||
* 0-9, the Game Master level of this character.
|
||||
@@ -506,17 +506,17 @@ private:
|
||||
/**
|
||||
* Whether the custom name of this character is rejected
|
||||
*/
|
||||
bool m_NameRejected;
|
||||
bool m_NameRejected{};
|
||||
|
||||
/**
|
||||
* The current amount of coins of this character
|
||||
*/
|
||||
int64_t m_Coins;
|
||||
int64_t m_Coins{};
|
||||
|
||||
/**
|
||||
* Whether the character is building
|
||||
*/
|
||||
bool m_BuildMode;
|
||||
bool m_BuildMode{};
|
||||
|
||||
/**
|
||||
* The items equipped by the character on world load
|
||||
@@ -583,7 +583,7 @@ private:
|
||||
/**
|
||||
* The ID of the properties of this character
|
||||
*/
|
||||
uint32_t m_PropertyCloneID;
|
||||
uint32_t m_PropertyCloneID{};
|
||||
|
||||
/**
|
||||
* The XML data for this character, stored as string
|
||||
@@ -613,7 +613,7 @@ private:
|
||||
/**
|
||||
* The last time this character logged in
|
||||
*/
|
||||
uint64_t m_LastLogin;
|
||||
uint64_t m_LastLogin{};
|
||||
|
||||
/**
|
||||
* The gameplay flags this character has (not just true values)
|
||||
|
||||
@@ -225,7 +225,7 @@ void Entity::Initialize() {
|
||||
|
||||
AddComponent<SimplePhysicsComponent>(simplePhysicsComponentID);
|
||||
|
||||
AddComponent<ModelComponent>();
|
||||
AddComponent<ModelComponent>()->LoadBehaviors();
|
||||
|
||||
AddComponent<RenderComponent>();
|
||||
|
||||
@@ -649,7 +649,7 @@ void Entity::Initialize() {
|
||||
}
|
||||
|
||||
if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::MODEL, -1) != -1 && !GetComponent<PetComponent>()) {
|
||||
AddComponent<ModelComponent>();
|
||||
AddComponent<ModelComponent>()->LoadBehaviors();
|
||||
if (!HasComponent(eReplicaComponentType::DESTROYABLE)) {
|
||||
auto* destroyableComponent = AddComponent<DestroyableComponent>();
|
||||
destroyableComponent->SetHealth(1);
|
||||
|
||||
@@ -12,6 +12,7 @@ User::User(const SystemAddress& sysAddr, const std::string& username, const std:
|
||||
m_AccountID = 0;
|
||||
m_Username = "";
|
||||
m_SessionKey = "";
|
||||
m_MuteExpire = 0;
|
||||
|
||||
m_MaxGMLevel = eGameMasterLevel::CIVILIAN; //The max GM level this account can assign to it's characters
|
||||
m_LastCharID = 0;
|
||||
|
||||
@@ -105,7 +105,7 @@ void BehaviorContext::ExecuteUpdates() {
|
||||
this->scheduledUpdates.clear();
|
||||
}
|
||||
|
||||
void BehaviorContext::SyncBehavior(const uint32_t syncId, RakNet::BitStream& bitStream) {
|
||||
bool BehaviorContext::SyncBehavior(const uint32_t syncId, RakNet::BitStream& bitStream) {
|
||||
BehaviorSyncEntry entry;
|
||||
auto found = false;
|
||||
|
||||
@@ -128,7 +128,7 @@ void BehaviorContext::SyncBehavior(const uint32_t syncId, RakNet::BitStream& bit
|
||||
if (!found) {
|
||||
LOG("Failed to find behavior sync entry with sync id (%i)!", syncId);
|
||||
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
auto* behavior = entry.behavior;
|
||||
@@ -137,10 +137,11 @@ void BehaviorContext::SyncBehavior(const uint32_t syncId, RakNet::BitStream& bit
|
||||
if (behavior == nullptr) {
|
||||
LOG("Invalid behavior for sync id (%i)!", syncId);
|
||||
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
behavior->Sync(this, bitStream, branch);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -93,7 +93,7 @@ struct BehaviorContext
|
||||
|
||||
void ExecuteUpdates();
|
||||
|
||||
void SyncBehavior(uint32_t syncId, RakNet::BitStream& bitStream);
|
||||
bool SyncBehavior(uint32_t syncId, RakNet::BitStream& bitStream);
|
||||
|
||||
void Update(float deltaTime);
|
||||
|
||||
|
||||
@@ -29,7 +29,8 @@
|
||||
|
||||
BaseCombatAIComponent::BaseCombatAIComponent(Entity* parent, const uint32_t id): Component(parent) {
|
||||
m_Target = LWOOBJID_EMPTY;
|
||||
SetAiState(AiState::spawn);
|
||||
m_DirtyStateOrTarget = true;
|
||||
m_State = AiState::spawn;
|
||||
m_Timer = 1.0f;
|
||||
m_StartPosition = parent->GetPosition();
|
||||
m_MovementAI = nullptr;
|
||||
|
||||
@@ -875,8 +875,6 @@ void InventoryComponent::UnEquipItem(Item* item) {
|
||||
|
||||
RemoveSlot(item->GetInfo().equipLocation);
|
||||
|
||||
PurgeProxies(item);
|
||||
|
||||
UnequipScripts(item);
|
||||
|
||||
Game::entityManager->SerializeEntity(m_Parent);
|
||||
@@ -886,6 +884,8 @@ void InventoryComponent::UnEquipItem(Item* item) {
|
||||
PropertyManagementComponent::Instance()->GetParent()->OnZonePropertyModelRemovedWhileEquipped(m_Parent);
|
||||
Game::zoneManager->GetZoneControlObject()->OnZonePropertyModelRemovedWhileEquipped(m_Parent);
|
||||
}
|
||||
|
||||
PurgeProxies(item);
|
||||
}
|
||||
|
||||
|
||||
@@ -1505,10 +1505,10 @@ void InventoryComponent::PurgeProxies(Item* item) {
|
||||
const auto root = item->GetParent();
|
||||
|
||||
if (root != LWOOBJID_EMPTY) {
|
||||
item = FindItemById(root);
|
||||
Item* itemRoot = FindItemById(root);
|
||||
|
||||
if (item != nullptr) {
|
||||
UnEquipItem(item);
|
||||
if (itemRoot != nullptr) {
|
||||
UnEquipItem(itemRoot);
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
@@ -6,6 +6,9 @@
|
||||
|
||||
#include "BehaviorStates.h"
|
||||
#include "ControlBehaviorMsgs.h"
|
||||
#include "tinyxml2.h"
|
||||
|
||||
#include "Database.h"
|
||||
|
||||
ModelComponent::ModelComponent(Entity* parent) : Component(parent) {
|
||||
m_OriginalPosition = m_Parent->GetDefaultPosition();
|
||||
@@ -14,6 +17,33 @@ ModelComponent::ModelComponent(Entity* parent) : Component(parent) {
|
||||
m_userModelID = m_Parent->GetVarAs<LWOOBJID>(u"userModelID");
|
||||
}
|
||||
|
||||
void ModelComponent::LoadBehaviors() {
|
||||
auto behaviors = GeneralUtils::SplitString(m_Parent->GetVar<std::string>(u"userModelBehaviors"), ',');
|
||||
for (const auto& behavior : behaviors) {
|
||||
if (behavior.empty()) continue;
|
||||
|
||||
const auto behaviorId = GeneralUtils::TryParse<int32_t>(behavior);
|
||||
if (!behaviorId.has_value() || behaviorId.value() == 0) continue;
|
||||
|
||||
LOG_DEBUG("Loading behavior %d", behaviorId.value());
|
||||
auto& inserted = m_Behaviors.emplace_back();
|
||||
inserted.SetBehaviorId(*behaviorId);
|
||||
|
||||
const auto behaviorStr = Database::Get()->GetBehavior(behaviorId.value());
|
||||
|
||||
tinyxml2::XMLDocument behaviorXml;
|
||||
auto res = behaviorXml.Parse(behaviorStr.c_str(), behaviorStr.size());
|
||||
LOG_DEBUG("Behavior %i %d: %s", res, behaviorId.value(), behaviorStr.c_str());
|
||||
|
||||
const auto* const behaviorRoot = behaviorXml.FirstChildElement("Behavior");
|
||||
if (!behaviorRoot) {
|
||||
LOG("Failed to load behavior %d due to missing behavior root", behaviorId.value());
|
||||
continue;
|
||||
}
|
||||
inserted.Deserialize(*behaviorRoot);
|
||||
}
|
||||
}
|
||||
|
||||
void ModelComponent::Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) {
|
||||
// ItemComponent Serialization. Pets do not get this serialization.
|
||||
if (!m_Parent->HasComponent(eReplicaComponentType::PET)) {
|
||||
@@ -72,3 +102,23 @@ void ModelComponent::MoveToInventory(MoveToInventoryMessage& msg) {
|
||||
m_Behaviors.erase(m_Behaviors.begin() + msg.GetBehaviorIndex());
|
||||
// TODO move to the inventory
|
||||
}
|
||||
|
||||
std::array<std::pair<int32_t, std::string>, 5> ModelComponent::GetBehaviorsForSave() const {
|
||||
std::array<std::pair<int32_t, std::string>, 5> toReturn{};
|
||||
for (auto i = 0; i < m_Behaviors.size(); i++) {
|
||||
const auto& behavior = m_Behaviors.at(i);
|
||||
if (behavior.GetBehaviorId() == -1) continue;
|
||||
auto& [id, behaviorData] = toReturn[i];
|
||||
id = behavior.GetBehaviorId();
|
||||
|
||||
tinyxml2::XMLDocument doc;
|
||||
auto* root = doc.NewElement("Behavior");
|
||||
behavior.Serialize(*root);
|
||||
doc.InsertFirstChild(root);
|
||||
|
||||
tinyxml2::XMLPrinter printer(0, true, 0);
|
||||
doc.Print(&printer);
|
||||
behaviorData = printer.CStr();
|
||||
}
|
||||
return toReturn;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <map>
|
||||
|
||||
#include "dCommonVars.h"
|
||||
@@ -28,6 +29,8 @@ public:
|
||||
|
||||
ModelComponent(Entity* parent);
|
||||
|
||||
void LoadBehaviors();
|
||||
|
||||
void Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) override;
|
||||
|
||||
/**
|
||||
@@ -109,6 +112,8 @@ public:
|
||||
|
||||
void VerifyBehaviors();
|
||||
|
||||
std::array<std::pair<int32_t, std::string>, 5> GetBehaviorsForSave() const;
|
||||
|
||||
private:
|
||||
/**
|
||||
* The behaviors of the model
|
||||
|
||||
@@ -21,9 +21,11 @@
|
||||
#include "eObjectBits.h"
|
||||
#include "CharacterComponent.h"
|
||||
#include "PlayerManager.h"
|
||||
#include "ModelComponent.h"
|
||||
|
||||
#include <vector>
|
||||
#include "CppScripts.h"
|
||||
#include <ranges>
|
||||
|
||||
PropertyManagementComponent* PropertyManagementComponent::instance = nullptr;
|
||||
|
||||
@@ -593,6 +595,20 @@ void PropertyManagementComponent::Load() {
|
||||
settings.push_back(new LDFData<int>(u"componentWhitelist", 1));
|
||||
}
|
||||
|
||||
std::ostringstream userModelBehavior;
|
||||
bool firstAdded = false;
|
||||
for (auto behavior : databaseModel.behaviors) {
|
||||
if (behavior < 0) {
|
||||
LOG("Invalid behavior ID: %d, removing behavior reference from model", behavior);
|
||||
behavior = 0;
|
||||
}
|
||||
if (firstAdded) userModelBehavior << ",";
|
||||
userModelBehavior << behavior;
|
||||
firstAdded = true;
|
||||
}
|
||||
|
||||
settings.push_back(new LDFData<std::string>(u"userModelBehaviors", userModelBehavior.str()));
|
||||
|
||||
node->config = settings;
|
||||
|
||||
const auto spawnerId = Game::zoneManager->MakeSpawner(info);
|
||||
@@ -610,6 +626,12 @@ void PropertyManagementComponent::Save() {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto* const owner = GetOwner();
|
||||
if (!owner) return;
|
||||
|
||||
const auto* const character = owner->GetCharacter();
|
||||
if (!character) return;
|
||||
|
||||
auto present = Database::Get()->GetPropertyModels(propertyId);
|
||||
|
||||
std::vector<LWOOBJID> modelIds;
|
||||
@@ -624,6 +646,20 @@ void PropertyManagementComponent::Save() {
|
||||
if (entity == nullptr) {
|
||||
continue;
|
||||
}
|
||||
auto* modelComponent = entity->GetComponent<ModelComponent>();
|
||||
if (!modelComponent) continue;
|
||||
const auto modelBehaviors = modelComponent->GetBehaviorsForSave();
|
||||
|
||||
// save the behaviors of the model
|
||||
for (const auto& [behaviorId, behaviorStr] : modelBehaviors) {
|
||||
if (behaviorStr.empty() || behaviorId == -1 || behaviorId == 0) continue;
|
||||
IBehaviors::Info info {
|
||||
.behaviorId = behaviorId,
|
||||
.characterId = character->GetID(),
|
||||
.behaviorInfo = behaviorStr
|
||||
};
|
||||
Database::Get()->AddBehavior(info);
|
||||
}
|
||||
|
||||
const auto position = entity->GetPosition();
|
||||
const auto rotation = entity->GetRotation();
|
||||
@@ -635,10 +671,13 @@ void PropertyManagementComponent::Save() {
|
||||
model.position = position;
|
||||
model.rotation = rotation;
|
||||
model.ugcId = 0;
|
||||
for (auto i = 0; i < model.behaviors.size(); i++) {
|
||||
model.behaviors[i] = modelBehaviors[i].first;
|
||||
}
|
||||
|
||||
Database::Get()->InsertNewPropertyModel(propertyId, model, "Objects_" + std::to_string(model.lot) + "_name");
|
||||
} else {
|
||||
Database::Get()->UpdateModelPositionRotation(id, position, rotation);
|
||||
Database::Get()->UpdateModel(id, position, rotation, modelBehaviors);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -38,7 +38,7 @@ bool SkillComponent::CastPlayerSkill(const uint32_t behaviorId, const uint32_t s
|
||||
|
||||
context->skillID = skillID;
|
||||
|
||||
this->m_managedBehaviors.insert_or_assign(skillUid, context);
|
||||
this->m_managedBehaviors.insert({ skillUid, context });
|
||||
|
||||
auto* behavior = Behavior::CreateBehavior(behaviorId);
|
||||
|
||||
@@ -52,17 +52,24 @@ bool SkillComponent::CastPlayerSkill(const uint32_t behaviorId, const uint32_t s
|
||||
}
|
||||
|
||||
void SkillComponent::SyncPlayerSkill(const uint32_t skillUid, const uint32_t syncId, RakNet::BitStream& bitStream) {
|
||||
const auto index = this->m_managedBehaviors.find(skillUid);
|
||||
const auto index = this->m_managedBehaviors.equal_range(skillUid);
|
||||
|
||||
if (index == this->m_managedBehaviors.end()) {
|
||||
if (index.first == this->m_managedBehaviors.end()) {
|
||||
LOG("Failed to find skill with uid (%i)!", skillUid, syncId);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
auto* context = index->second;
|
||||
bool foundSyncId = false;
|
||||
for (auto it = index.first; it != index.second && !foundSyncId; ++it) {
|
||||
const auto& context = it->second;
|
||||
|
||||
context->SyncBehavior(syncId, bitStream);
|
||||
foundSyncId = context->SyncBehavior(syncId, bitStream);
|
||||
}
|
||||
|
||||
if (!foundSyncId) {
|
||||
LOG("Failed to find sync id (%i) for skill with uid (%i)!", syncId, skillUid);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -138,7 +145,7 @@ void SkillComponent::Update(const float deltaTime) {
|
||||
for (const auto& pair : this->m_managedBehaviors) pair.second->UpdatePlayerSyncs(deltaTime);
|
||||
}
|
||||
|
||||
std::map<uint32_t, BehaviorContext*> keep{};
|
||||
std::multimap<uint32_t, BehaviorContext*> keep{};
|
||||
|
||||
for (const auto& pair : this->m_managedBehaviors) {
|
||||
auto* context = pair.second;
|
||||
@@ -176,7 +183,7 @@ void SkillComponent::Update(const float deltaTime) {
|
||||
}
|
||||
}
|
||||
|
||||
keep.insert_or_assign(pair.first, context);
|
||||
keep.insert({ pair.first, context });
|
||||
}
|
||||
|
||||
this->m_managedBehaviors = keep;
|
||||
@@ -285,7 +292,7 @@ SkillExecutionResult SkillComponent::CalculateBehavior(
|
||||
return { false, 0 };
|
||||
}
|
||||
|
||||
this->m_managedBehaviors.insert_or_assign(context->skillUId, context);
|
||||
this->m_managedBehaviors.insert({ context->skillUId, context });
|
||||
|
||||
if (!clientInitalized) {
|
||||
// Echo start skill
|
||||
|
||||
@@ -188,7 +188,7 @@ private:
|
||||
/**
|
||||
* All of the active skills mapped by their unique ID.
|
||||
*/
|
||||
std::map<uint32_t, BehaviorContext*> m_managedBehaviors;
|
||||
std::multimap<uint32_t, BehaviorContext*> m_managedBehaviors;
|
||||
|
||||
/**
|
||||
* All active projectiles.
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,9 +99,11 @@
|
||||
#include "ActivityManager.h"
|
||||
#include "PlayerManager.h"
|
||||
#include "eVendorTransactionResult.h"
|
||||
#include "eReponseMoveItemBetweenInventoryTypeCode.h"
|
||||
|
||||
#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;
|
||||
@@ -4564,16 +4566,31 @@ void GameMessages::HandleRequestMoveItemBetweenInventoryTypes(RakNet::BitStream&
|
||||
if (inStream.ReadBit()) inStream.Read(itemLOT);
|
||||
|
||||
if (invTypeDst == invTypeSrc) {
|
||||
SendResponseMoveItemBetweenInventoryTypes(entity->GetObjectID(), sysAddr, invTypeDst, invTypeSrc, eReponseMoveItemBetweenInventoryTypeCode::FAIL_GENERIC);
|
||||
return;
|
||||
}
|
||||
|
||||
auto* inventoryComponent = entity->GetComponent<InventoryComponent>();
|
||||
|
||||
if (inventoryComponent != nullptr) {
|
||||
if (inventoryComponent) {
|
||||
if (itemID != LWOOBJID_EMPTY) {
|
||||
auto* item = inventoryComponent->FindItemById(itemID);
|
||||
|
||||
if (!item) return;
|
||||
if (!item) {
|
||||
SendResponseMoveItemBetweenInventoryTypes(entity->GetObjectID(), sysAddr, invTypeDst, invTypeSrc, eReponseMoveItemBetweenInventoryTypeCode::FAIL_ITEM_NOT_FOUND);
|
||||
return;
|
||||
}
|
||||
|
||||
if (item->GetLot() == 6086) { // Thinking hat
|
||||
SendResponseMoveItemBetweenInventoryTypes(entity->GetObjectID(), sysAddr, invTypeDst, invTypeSrc, eReponseMoveItemBetweenInventoryTypeCode::FAIL_CANT_MOVE_THINKING_HAT);
|
||||
return;
|
||||
}
|
||||
|
||||
auto* destInv = inventoryComponent->GetInventory(invTypeDst);
|
||||
if (destInv && destInv->GetEmptySlots() == 0) {
|
||||
SendResponseMoveItemBetweenInventoryTypes(entity->GetObjectID(), sysAddr, invTypeDst, invTypeSrc, eReponseMoveItemBetweenInventoryTypeCode::FAIL_INV_FULL);
|
||||
return;
|
||||
}
|
||||
|
||||
// Despawn the pet if we are moving that pet to the vault.
|
||||
auto* petComponent = PetComponent::GetActivePet(entity->GetObjectID());
|
||||
@@ -4582,10 +4599,32 @@ void GameMessages::HandleRequestMoveItemBetweenInventoryTypes(RakNet::BitStream&
|
||||
}
|
||||
|
||||
inventoryComponent->MoveItemToInventory(item, invTypeDst, iStackCount, showFlyingLoot, false, false, destSlot);
|
||||
SendResponseMoveItemBetweenInventoryTypes(entity->GetObjectID(), sysAddr, invTypeDst, invTypeSrc, eReponseMoveItemBetweenInventoryTypeCode::SUCCESS);
|
||||
}
|
||||
} else {
|
||||
SendResponseMoveItemBetweenInventoryTypes(entity->GetObjectID(), sysAddr, invTypeDst, invTypeSrc, eReponseMoveItemBetweenInventoryTypeCode::FAIL_GENERIC);
|
||||
}
|
||||
}
|
||||
|
||||
void GameMessages::SendResponseMoveItemBetweenInventoryTypes(LWOOBJID objectId, const SystemAddress& sysAddr, eInventoryType inventoryTypeDestination, eInventoryType inventoryTypeSource, eReponseMoveItemBetweenInventoryTypeCode response) {
|
||||
CBITSTREAM;
|
||||
CMSGHEADER;
|
||||
|
||||
bitStream.Write(objectId);
|
||||
bitStream.Write(eGameMessageType::RESPONSE_MOVE_ITEM_BETWEEN_INVENTORY_TYPES);
|
||||
|
||||
bitStream.Write(inventoryTypeDestination != eInventoryType::ITEMS);
|
||||
if (inventoryTypeDestination != eInventoryType::ITEMS) bitStream.Write(inventoryTypeDestination);
|
||||
|
||||
bitStream.Write(inventoryTypeSource != eInventoryType::ITEMS);
|
||||
if (inventoryTypeSource != eInventoryType::ITEMS) bitStream.Write(inventoryTypeSource);
|
||||
|
||||
bitStream.Write(response != eReponseMoveItemBetweenInventoryTypeCode::FAIL_GENERIC);
|
||||
if (response != eReponseMoveItemBetweenInventoryTypeCode::FAIL_GENERIC) bitStream.Write(response);
|
||||
|
||||
SEND_PACKET;
|
||||
}
|
||||
|
||||
|
||||
void GameMessages::SendShowActivityCountdown(LWOOBJID objectId, bool bPlayAdditionalSound, bool bPlayCountdownSound, std::u16string sndName, int32_t stateToPlaySoundOn, const SystemAddress& sysAddr) {
|
||||
CBITSTREAM;
|
||||
@@ -5352,7 +5391,8 @@ 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();
|
||||
@@ -6210,3 +6250,18 @@ void GameMessages::SendSlashCommandFeedbackText(Entity* entity, std::u16string t
|
||||
auto sysAddr = entity->GetSystemAddress();
|
||||
SEND_PACKET;
|
||||
}
|
||||
|
||||
void GameMessages::SendForceCameraTargetCycle(Entity* entity, bool bForceCycling, eCameraTargetCyclingMode cyclingMode, LWOOBJID optionalTargetID) {
|
||||
CBITSTREAM;
|
||||
CMSGHEADER;
|
||||
|
||||
bitStream.Write(entity->GetObjectID());
|
||||
bitStream.Write(eGameMessageType::FORCE_CAMERA_TARGET_CYCLE);
|
||||
bitStream.Write(bForceCycling);
|
||||
bitStream.Write(cyclingMode != eCameraTargetCyclingMode::ALLOW_CYCLE_TEAMMATES);
|
||||
if (cyclingMode != eCameraTargetCyclingMode::ALLOW_CYCLE_TEAMMATES) bitStream.Write(cyclingMode);
|
||||
bitStream.Write(optionalTargetID);
|
||||
|
||||
auto sysAddr = entity->GetSystemAddress();
|
||||
SEND_PACKET;
|
||||
}
|
||||
|
||||
@@ -39,6 +39,12 @@ enum class eQuickBuildFailReason : uint32_t;
|
||||
enum class eQuickBuildState : uint32_t;
|
||||
enum class BehaviorSlot : int32_t;
|
||||
enum class eVendorTransactionResult : uint32_t;
|
||||
enum class eReponseMoveItemBetweenInventoryTypeCode : int32_t;
|
||||
|
||||
enum class eCameraTargetCyclingMode : int32_t {
|
||||
ALLOW_CYCLE_TEAMMATES,
|
||||
DISALLOW_CYCLING
|
||||
};
|
||||
|
||||
namespace GameMessages {
|
||||
class PropertyDataMessage;
|
||||
@@ -589,6 +595,7 @@ namespace GameMessages {
|
||||
//NT:
|
||||
|
||||
void HandleRequestMoveItemBetweenInventoryTypes(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr);
|
||||
void SendResponseMoveItemBetweenInventoryTypes(LWOOBJID objectId, const SystemAddress& sysAddr, eInventoryType inventoryTypeDestination, eInventoryType inventoryTypeSource, eReponseMoveItemBetweenInventoryTypeCode response);
|
||||
|
||||
void SendShowActivityCountdown(LWOOBJID objectId, bool bPlayAdditionalSound, bool bPlayCountdownSound, std::u16string sndName, int32_t stateToPlaySoundOn, const SystemAddress& sysAddr);
|
||||
|
||||
@@ -666,6 +673,7 @@ namespace GameMessages {
|
||||
void HandleCancelDonationOnPlayer(RakNet::BitStream& inStream, Entity* entity);
|
||||
|
||||
void SendSlashCommandFeedbackText(Entity* entity, std::u16string text);
|
||||
void SendForceCameraTargetCycle(Entity* entity, bool bForceCycling, eCameraTargetCyclingMode cyclingMode, LWOOBJID optionalTargetID);
|
||||
};
|
||||
|
||||
#endif // GAMEMESSAGES_H
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#include "Action.h"
|
||||
#include "Amf3.h"
|
||||
|
||||
#include "tinyxml2.h"
|
||||
|
||||
Action::Action(const AMFArrayValue& arguments) {
|
||||
for (const auto& [paramName, paramValue] : arguments.GetAssociative()) {
|
||||
if (paramName == "Type") {
|
||||
@@ -32,3 +34,46 @@ void Action::SendBehaviorBlocksToClient(AMFArrayValue& args) const {
|
||||
actionArgs->Insert(m_ValueParameterName, m_ValueParameterDouble);
|
||||
}
|
||||
}
|
||||
|
||||
void Action::Serialize(tinyxml2::XMLElement& action) const {
|
||||
action.SetAttribute("Type", m_Type.c_str());
|
||||
|
||||
if (m_ValueParameterName.empty()) return;
|
||||
|
||||
action.SetAttribute("ValueParameterName", m_ValueParameterName.c_str());
|
||||
|
||||
if (m_ValueParameterName == "Message") {
|
||||
action.SetAttribute("Value", m_ValueParameterString.c_str());
|
||||
} else {
|
||||
action.SetAttribute("Value", m_ValueParameterDouble);
|
||||
}
|
||||
}
|
||||
|
||||
void Action::Deserialize(const tinyxml2::XMLElement& action) {
|
||||
const char* type = nullptr;
|
||||
action.QueryAttribute("Type", &type);
|
||||
if (!type) {
|
||||
LOG("No type found for an action?");
|
||||
return;
|
||||
}
|
||||
|
||||
m_Type = type;
|
||||
|
||||
const char* valueParameterName = nullptr;
|
||||
action.QueryAttribute("ValueParameterName", &valueParameterName);
|
||||
if (valueParameterName) {
|
||||
m_ValueParameterName = valueParameterName;
|
||||
|
||||
if (m_ValueParameterName == "Message") {
|
||||
const char* value = nullptr;
|
||||
action.QueryAttribute("Value", &value);
|
||||
if (value) {
|
||||
m_ValueParameterString = value;
|
||||
} else {
|
||||
LOG("No value found for an action message?");
|
||||
}
|
||||
} else {
|
||||
action.QueryDoubleAttribute("Value", &m_ValueParameterDouble);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace tinyxml2 {
|
||||
class XMLElement;
|
||||
};
|
||||
|
||||
class AMFArrayValue;
|
||||
|
||||
/**
|
||||
@@ -20,6 +24,8 @@ public:
|
||||
|
||||
void SendBehaviorBlocksToClient(AMFArrayValue& args) const;
|
||||
|
||||
void Serialize(tinyxml2::XMLElement& action) const;
|
||||
void Deserialize(const tinyxml2::XMLElement& action);
|
||||
private:
|
||||
double m_ValueParameterDouble{ 0.0 };
|
||||
std::string m_Type{ "" };
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "StripUiPosition.h"
|
||||
|
||||
#include "Amf3.h"
|
||||
#include "tinyxml2.h"
|
||||
|
||||
StripUiPosition::StripUiPosition(const AMFArrayValue& arguments, const std::string& uiKeyName) {
|
||||
const auto* const uiArray = arguments.GetArray(uiKeyName);
|
||||
@@ -21,3 +22,13 @@ void StripUiPosition::SendBehaviorBlocksToClient(AMFArrayValue& args) const {
|
||||
uiArgs->Insert("x", m_XPosition);
|
||||
uiArgs->Insert("y", m_YPosition);
|
||||
}
|
||||
|
||||
void StripUiPosition::Serialize(tinyxml2::XMLElement& position) const {
|
||||
position.SetAttribute("x", m_XPosition);
|
||||
position.SetAttribute("y", m_YPosition);
|
||||
}
|
||||
|
||||
void StripUiPosition::Deserialize(const tinyxml2::XMLElement& position) {
|
||||
position.QueryDoubleAttribute("x", &m_XPosition);
|
||||
position.QueryDoubleAttribute("y", &m_YPosition);
|
||||
}
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
|
||||
class AMFArrayValue;
|
||||
|
||||
namespace tinyxml2 {
|
||||
class XMLElement;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief The position of the first Action in a Strip
|
||||
*
|
||||
@@ -15,6 +19,8 @@ public:
|
||||
[[nodiscard]] double GetX() const noexcept { return m_XPosition; }
|
||||
[[nodiscard]] double GetY() const noexcept { return m_YPosition; }
|
||||
|
||||
void Serialize(tinyxml2::XMLElement& position) const;
|
||||
void Deserialize(const tinyxml2::XMLElement& position);
|
||||
private:
|
||||
double m_XPosition{ 0.0 };
|
||||
double m_YPosition{ 0.0 };
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "Amf3.h"
|
||||
#include "BehaviorStates.h"
|
||||
#include "ControlBehaviorMsgs.h"
|
||||
#include "tinyxml2.h"
|
||||
|
||||
PropertyBehavior::PropertyBehavior() {
|
||||
m_LastEditedState = BehaviorState::HOME_STATE;
|
||||
@@ -124,3 +125,31 @@ void PropertyBehavior::SendBehaviorBlocksToClient(AMFArrayValue& args) const {
|
||||
|
||||
// TODO Serialize the execution state of the behavior
|
||||
}
|
||||
|
||||
void PropertyBehavior::Serialize(tinyxml2::XMLElement& behavior) const {
|
||||
behavior.SetAttribute("id", m_BehaviorId);
|
||||
behavior.SetAttribute("name", m_Name.c_str());
|
||||
behavior.SetAttribute("isLocked", isLocked);
|
||||
behavior.SetAttribute("isLoot", isLoot);
|
||||
|
||||
for (const auto& [stateId, state] : m_States) {
|
||||
if (state.IsEmpty()) continue;
|
||||
auto* const stateElement = behavior.InsertNewChildElement("State");
|
||||
stateElement->SetAttribute("id", static_cast<uint32_t>(stateId));
|
||||
state.Serialize(*stateElement);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void PropertyBehavior::Deserialize(const tinyxml2::XMLElement& behavior) {
|
||||
m_Name = behavior.Attribute("name");
|
||||
behavior.QueryBoolAttribute("isLocked", &isLocked);
|
||||
behavior.QueryBoolAttribute("isLoot", &isLoot);
|
||||
|
||||
for (const auto* stateElement = behavior.FirstChildElement("State"); stateElement; stateElement = stateElement->NextSiblingElement("State")) {
|
||||
int32_t stateId = -1;
|
||||
stateElement->QueryIntAttribute("id", &stateId);
|
||||
if (stateId < 0 || stateId > 5) continue;
|
||||
m_States[static_cast<BehaviorState>(stateId)].Deserialize(*stateElement);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
|
||||
#include "State.h"
|
||||
|
||||
namespace tinyxml2 {
|
||||
class XMLElement;
|
||||
}
|
||||
|
||||
enum class BehaviorState : uint32_t;
|
||||
|
||||
class AMFArrayValue;
|
||||
@@ -25,6 +29,8 @@ public:
|
||||
[[nodiscard]] int32_t GetBehaviorId() const noexcept { return m_BehaviorId; }
|
||||
void SetBehaviorId(int32_t id) noexcept { m_BehaviorId = id; }
|
||||
|
||||
void Serialize(tinyxml2::XMLElement& behavior) const;
|
||||
void Deserialize(const tinyxml2::XMLElement& behavior);
|
||||
private:
|
||||
|
||||
// The states this behavior has.
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include "Amf3.h"
|
||||
#include "ControlBehaviorMsgs.h"
|
||||
#include "tinyxml2.h"
|
||||
|
||||
template <>
|
||||
void State::HandleMsg(AddStripMessage& msg) {
|
||||
@@ -134,4 +135,20 @@ void State::SendBehaviorBlocksToClient(AMFArrayValue& args) const {
|
||||
|
||||
strip.SendBehaviorBlocksToClient(*stripArgs);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
void State::Serialize(tinyxml2::XMLElement& state) const {
|
||||
for (const auto& strip : m_Strips) {
|
||||
if (strip.IsEmpty()) continue;
|
||||
|
||||
auto* const stripElement = state.InsertNewChildElement("Strip");
|
||||
strip.Serialize(*stripElement);
|
||||
}
|
||||
}
|
||||
|
||||
void State::Deserialize(const tinyxml2::XMLElement& state) {
|
||||
for (const auto* stripElement = state.FirstChildElement("Strip"); stripElement; stripElement = stripElement->NextSiblingElement("Strip")) {
|
||||
auto& strip = m_Strips.emplace_back();
|
||||
strip.Deserialize(*stripElement);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
|
||||
#include "Strip.h"
|
||||
|
||||
namespace tinyxml2 {
|
||||
class XMLElement;
|
||||
}
|
||||
|
||||
class AMFArrayValue;
|
||||
|
||||
class State {
|
||||
@@ -13,6 +17,8 @@ public:
|
||||
void SendBehaviorBlocksToClient(AMFArrayValue& args) const;
|
||||
bool IsEmpty() const;
|
||||
|
||||
void Serialize(tinyxml2::XMLElement& state) const;
|
||||
void Deserialize(const tinyxml2::XMLElement& state);
|
||||
private:
|
||||
std::vector<Strip> m_Strips;
|
||||
};
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include "Amf3.h"
|
||||
#include "ControlBehaviorMsgs.h"
|
||||
#include "tinyxml2.h"
|
||||
|
||||
template <>
|
||||
void Strip::HandleMsg(AddStripMessage& msg) {
|
||||
@@ -83,4 +84,25 @@ void Strip::SendBehaviorBlocksToClient(AMFArrayValue& args) const {
|
||||
for (const auto& action : m_Actions) {
|
||||
action.SendBehaviorBlocksToClient(*actions);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
void Strip::Serialize(tinyxml2::XMLElement& strip) const {
|
||||
auto* const positionElement = strip.InsertNewChildElement("Position");
|
||||
m_Position.Serialize(*positionElement);
|
||||
for (const auto& action : m_Actions) {
|
||||
auto* const actionElement = strip.InsertNewChildElement("Action");
|
||||
action.Serialize(*actionElement);
|
||||
}
|
||||
}
|
||||
|
||||
void Strip::Deserialize(const tinyxml2::XMLElement& strip) {
|
||||
const auto* positionElement = strip.FirstChildElement("Position");
|
||||
if (positionElement) {
|
||||
m_Position.Deserialize(*positionElement);
|
||||
}
|
||||
|
||||
for (const auto* actionElement = strip.FirstChildElement("Action"); actionElement; actionElement = actionElement->NextSiblingElement("Action")) {
|
||||
auto& action = m_Actions.emplace_back();
|
||||
action.Deserialize(*actionElement);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,10 @@
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace tinyxml2 {
|
||||
class XMLElement;
|
||||
}
|
||||
|
||||
class AMFArrayValue;
|
||||
|
||||
class Strip {
|
||||
@@ -16,6 +20,8 @@ public:
|
||||
void SendBehaviorBlocksToClient(AMFArrayValue& args) const;
|
||||
bool IsEmpty() const noexcept { return m_Actions.empty(); }
|
||||
|
||||
void Serialize(tinyxml2::XMLElement& strip) const;
|
||||
void Deserialize(const tinyxml2::XMLElement& strip);
|
||||
private:
|
||||
std::vector<Action> m_Actions;
|
||||
StripUiPosition m_Position;
|
||||
|
||||
@@ -31,7 +31,6 @@ void SlashCommandHandler::RegisterCommand(Command command) {
|
||||
}
|
||||
|
||||
for (const auto& alias : command.aliases) {
|
||||
LOG_DEBUG("Registering command %s", alias.c_str());
|
||||
auto [_, success] = RegisteredCommands.try_emplace(alias, command);
|
||||
// Don't allow duplicate commands
|
||||
if (!success) {
|
||||
@@ -929,6 +928,15 @@ void SlashCommandHandler::Startup() {
|
||||
};
|
||||
RegisterCommand(FindPlayerCommand);
|
||||
|
||||
Command SpectateCommand{
|
||||
.help = "Spectate a player",
|
||||
.info = "Specify a player name to spectate. They must be in the same world as you. Leave blank to stop spectating",
|
||||
.aliases = { "spectate", "follow" },
|
||||
.handle = GMGreaterThanZeroCommands::Spectate,
|
||||
.requiredLevel = eGameMasterLevel::JUNIOR_MODERATOR
|
||||
};
|
||||
RegisterCommand(SpectateCommand);
|
||||
|
||||
// Register GM Zero Commands
|
||||
|
||||
Command HelpCommand{
|
||||
|
||||
@@ -322,4 +322,19 @@ namespace GMGreaterThanZeroCommands {
|
||||
request.Serialize(bitStream);
|
||||
Game::chatServer->Send(&bitStream, SYSTEM_PRIORITY, RELIABLE, 0, Game::chatSysAddr, false);
|
||||
}
|
||||
|
||||
void Spectate(Entity* entity, const SystemAddress& sysAddr, const std::string args) {
|
||||
if (args.empty()) {
|
||||
GameMessages::SendForceCameraTargetCycle(entity, false, eCameraTargetCyclingMode::DISALLOW_CYCLING, entity->GetObjectID());
|
||||
return;
|
||||
}
|
||||
|
||||
auto player = PlayerManager::GetPlayer(args);
|
||||
if (!player) {
|
||||
GameMessages::SendSlashCommandFeedbackText(entity, u"Player not found");
|
||||
return;
|
||||
}
|
||||
GameMessages::SendSlashCommandFeedbackText(entity, u"Spectating Player");
|
||||
GameMessages::SendForceCameraTargetCycle(entity, false, eCameraTargetCyclingMode::DISALLOW_CYCLING, player->GetObjectID());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ namespace GMGreaterThanZeroCommands {
|
||||
void Title(Entity* entity, const SystemAddress& sysAddr, const std::string args);
|
||||
void ShowAll(Entity* entity, const SystemAddress& sysAddr, const std::string args);
|
||||
void FindPlayer(Entity* entity, const SystemAddress& sysAddr, const std::string args);
|
||||
void Spectate(Entity* entity, const SystemAddress& sysAddr, const std::string args);
|
||||
}
|
||||
|
||||
#endif //!GMGREATERTHANZEROCOMMANDS_H
|
||||
|
||||
@@ -285,7 +285,6 @@ void ParseXml(const std::string& file) {
|
||||
}
|
||||
|
||||
if (zoneID.value() != currentZoneID) {
|
||||
LOG_DEBUG("Skipping (%s) %i location because it is in %i and not the current zone (%i)", name, lot, zoneID.value(), currentZoneID);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
#include "BitStreamUtils.h"
|
||||
#include "Start.h"
|
||||
#include "Server.h"
|
||||
#include "CDZoneTableTable.h"
|
||||
|
||||
namespace Game {
|
||||
Logger* logger = nullptr;
|
||||
@@ -277,6 +278,17 @@ int main(int argc, char** argv) {
|
||||
PersistentIDManager::Initialize();
|
||||
Game::im = new InstanceManager(Game::logger, Game::server->GetIP());
|
||||
|
||||
//Get CDClient initial information
|
||||
try {
|
||||
CDClientManager::LoadValuesFromDatabase();
|
||||
} catch (CppSQLite3Exception& e) {
|
||||
LOG("Failed to initialize CDServer SQLite Database");
|
||||
LOG("May be caused by corrupted file: %s", (Game::assetManager->GetResPath() / "CDServer.sqlite").string().c_str());
|
||||
LOG("Error: %s", e.errorMessage());
|
||||
LOG("Error Code: %i", e.errorCode());
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
//Depending on the config, start up servers:
|
||||
if (Game::config->GetValue("prestart_servers") != "0") {
|
||||
StartChatServer();
|
||||
@@ -382,6 +394,7 @@ int main(int argc, char** argv) {
|
||||
}
|
||||
|
||||
void HandlePacket(Packet* packet) {
|
||||
if (packet->length < 1) return;
|
||||
if (packet->data[0] == ID_DISCONNECTION_NOTIFICATION) {
|
||||
LOG("A server has disconnected");
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "MessageIdentifiers.h"
|
||||
#include "BitStream.h"
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
|
||||
enum class eConnectionType : uint16_t;
|
||||
|
||||
|
||||
@@ -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?
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include "AgShipPlayerDeathTrigger.h"
|
||||
#include "AgShipPlayerShockServer.h"
|
||||
#include "AgSpaceStuff.h"
|
||||
#include "AgShipShake.h"
|
||||
#include "AgImagSmashable.h"
|
||||
#include "NpcNpSpacemanBob.h"
|
||||
#include "StoryBoxInteractServer.h"
|
||||
@@ -341,6 +342,7 @@ namespace {
|
||||
{ "scripts\\ai\\AG\\L_AG_SHIP_PLAYER_DEATH_TRIGGER.lua", []() { return new AgShipPlayerDeathTrigger(); } },
|
||||
{"scripts\\ai\\NP\\L_NPC_NP_SPACEMAN_BOB.lua", []() { return new NpcNpSpacemanBob(); } },
|
||||
{"scripts\\ai\\AG\\L_AG_SPACE_STUFF.lua", []() { return new AgSpaceStuff();} },
|
||||
{"scripts\\ai\\AG\\L_AG_SHIP_SHAKE.lua", []() { return new AgShipShake();}},
|
||||
{"scripts\\ai\\AG\\L_AG_SHIP_PLAYER_SHOCK_SERVER.lua", []() { return new AgShipPlayerShockServer();} },
|
||||
{"scripts\\ai\\AG\\L_AG_IMAG_SMASHABLE.lua", []() { return new AgImagSmashable();} },
|
||||
{"scripts\\02_server\\Map\\General\\L_STORY_BOX_INTERACT_SERVER.lua", []() { return new StoryBoxInteractServer();} },
|
||||
@@ -580,6 +582,7 @@ namespace {
|
||||
{"scripts\\02_server\\Map\\AM\\L_SKULLKIN_DRILL_STAND.lua", []() {return new AmSkullkinDrillStand();}},
|
||||
{"scripts\\02_server\\Map\\AM\\L_SKULLKIN_TOWER.lua", []() {return new AmSkullkinTower();}},
|
||||
{"scripts\\02_server\\Enemy\\AM\\L_AM_NAMED_DARKLING_DRAGON.lua", []() {return new AmDarklingDragon();}},
|
||||
{"scripts\\02_server\\Enemy\\AM\\L_AM_DARKLING_DRAGON.lua", []() {return new AmDarklingDragon();}},
|
||||
{"scripts\\02_server\\Enemy\\AM\\L_AM_DARKLING_APE.lua", []() {return new BaseEnemyApe();}},
|
||||
{"scripts\\02_server\\Map\\AM\\L_BLUE_X.lua", []() {return new AmBlueX();}},
|
||||
{"scripts\\02_server\\Map\\AM\\L_TEAPOT_SERVER.lua", []() {return new AmTeapotServer();}},
|
||||
@@ -654,6 +657,7 @@ namespace {
|
||||
|
||||
//Pickups
|
||||
{"scripts\\ai\\SPEC\\L_SPECIAL_1_BRONZE-COIN-SPAWNER.lua", []() {return new SpecialCoinSpawner(1);}},
|
||||
{"scripts\\ai\\SPEC\\L_SPECIAL_1_GOLD-COIN-SPAWNER.lua", []() {return new SpecialCoinSpawner(10000);}},
|
||||
{"scripts\\ai\\SPEC\\L_SPECIAL_1_SILVER-COIN-SPAWNER.lua", []() {return new SpecialCoinSpawner(100);}},
|
||||
{"scripts\\ai\\SPEC\\L_SPECIAL_10_BRONZE-COIN-SPAWNER.lua", []() {return new SpecialCoinSpawner(10);}},
|
||||
{"scripts\\ai\\SPEC\\L_SPECIAL_10_GOLD-COIN-SPAWNER.lua", []() {return new SpecialCoinSpawner(100000);}},
|
||||
@@ -700,7 +704,8 @@ CppScripts::Script* const CppScripts::GetScript(Entity* parent, const std::strin
|
||||
(scriptName == "scripts\\02_server\\Enemy\\General\\L_BASE_ENEMY_SPIDERLING.lua") ||
|
||||
(scriptName == "scripts\\ai\\FV\\L_ACT_NINJA_STUDENT.lua") ||
|
||||
(scriptName == "scripts\\ai\\WILD\\L_WILD_GF_FROG.lua") ||
|
||||
(scriptName == "scripts\\empty.lua")
|
||||
(scriptName == "scripts\\empty.lua") ||
|
||||
(scriptName == "scripts\\ai\\AG\\L_AG_SENTINEL_GUARD.lua")
|
||||
)) LOG_DEBUG("LOT %i attempted to load CppScript for '%s', but returned InvalidScript.", parent->GetLOT(), scriptName.c_str());
|
||||
}
|
||||
|
||||
|
||||
81
dScripts/ai/AG/AgShipShake.cpp
Normal file
81
dScripts/ai/AG/AgShipShake.cpp
Normal file
@@ -0,0 +1,81 @@
|
||||
#include "AgShipShake.h"
|
||||
#include "EntityInfo.h"
|
||||
#include "GeneralUtils.h"
|
||||
#include "GameMessages.h"
|
||||
#include "EntityManager.h"
|
||||
#include "RenderComponent.h"
|
||||
#include "Entity.h"
|
||||
|
||||
void AgShipShake::OnStartup(Entity* self) {
|
||||
EntityInfo info{};
|
||||
|
||||
info.pos = { -418, 585, -30 };
|
||||
info.lot = 33;
|
||||
info.spawnerID = self->GetObjectID();
|
||||
|
||||
auto* ref = Game::entityManager->CreateEntity(info);
|
||||
|
||||
Game::entityManager->ConstructEntity(ref);
|
||||
|
||||
self->SetVar(u"ShakeObject", ref->GetObjectID());
|
||||
|
||||
self->AddTimer("ShipShakeIdle", 2.0f);
|
||||
self->SetVar(u"RandomTime", 10);
|
||||
}
|
||||
|
||||
void AgShipShake::OnTimerDone(Entity* self, std::string timerName) {
|
||||
auto* shipFxObject = GetEntityInGroup(ShipFX);
|
||||
auto* shipFxObject2 = GetEntityInGroup(ShipFX2);
|
||||
auto* debrisObject = GetEntityInGroup(DebrisFX);
|
||||
if (timerName == "ShipShakeIdle") {
|
||||
auto* ref = Game::entityManager->GetEntity(self->GetVar<LWOOBJID>(u"ShakeObject"));
|
||||
|
||||
const auto randomTime = self->GetVar<int>(u"RandomTime");
|
||||
auto time = GeneralUtils::GenerateRandomNumber<int>(0, randomTime + 1);
|
||||
|
||||
if (time < randomTime / 2) {
|
||||
time += randomTime / 2;
|
||||
}
|
||||
|
||||
self->AddTimer("ShipShakeIdle", static_cast<float>(time));
|
||||
|
||||
if (ref)
|
||||
GameMessages::SendPlayEmbeddedEffectOnAllClientsNearObject(ref, FXName, ref->GetObjectID(), 500.0f);
|
||||
|
||||
|
||||
if (debrisObject)
|
||||
GameMessages::SendPlayFXEffect(debrisObject, -1, u"DebrisFall", "Debris", LWOOBJID_EMPTY, 1.0f, 1.0f, true);
|
||||
|
||||
const auto randomFx = GeneralUtils::GenerateRandomNumber<int>(0, 3);
|
||||
|
||||
if (shipFxObject) {
|
||||
std::string effectType = "shipboom" + std::to_string(randomFx);
|
||||
GameMessages::SendPlayFXEffect(shipFxObject, 559, GeneralUtils::ASCIIToUTF16(effectType), "FX", LWOOBJID_EMPTY, 1.0f, 1.0f, true);
|
||||
}
|
||||
|
||||
self->AddTimer("ShipShakeExplode", 5.0f);
|
||||
|
||||
if (shipFxObject2)
|
||||
RenderComponent::PlayAnimation(shipFxObject2, u"explosion");
|
||||
} else if (timerName == "ShipShakeExplode") {
|
||||
if (shipFxObject)
|
||||
RenderComponent::PlayAnimation(shipFxObject, u"idle");
|
||||
if (shipFxObject2)
|
||||
RenderComponent::PlayAnimation(shipFxObject2, u"idle");
|
||||
}
|
||||
}
|
||||
|
||||
Entity* AgShipShake::GetEntityInGroup(const std::string& group) {
|
||||
auto entities = Game::entityManager->GetEntitiesInGroup(group);
|
||||
Entity* en = nullptr;
|
||||
|
||||
for (auto entity : entities) {
|
||||
if (entity) {
|
||||
en = entity;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return en;
|
||||
}
|
||||
|
||||
16
dScripts/ai/AG/AgShipShake.h
Normal file
16
dScripts/ai/AG/AgShipShake.h
Normal file
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
#include "CppScripts.h"
|
||||
|
||||
class AgShipShake : public CppScripts::Script {
|
||||
public:
|
||||
void OnStartup(Entity* self) override;
|
||||
void OnTimerDone(Entity* self, std::string timerName) override;
|
||||
|
||||
std::string DebrisFX = "DebrisFX";
|
||||
std::string ShipFX = "ShipFX";
|
||||
std::string ShipFX2 = "ShipFX2";
|
||||
std::u16string FXName = u"camshake-bridge";
|
||||
|
||||
private:
|
||||
Entity* GetEntityInGroup(const std::string& group);
|
||||
};
|
||||
@@ -8,21 +8,6 @@
|
||||
|
||||
void AgSpaceStuff::OnStartup(Entity* self) {
|
||||
self->AddTimer("FloaterScale", 5.0f);
|
||||
|
||||
EntityInfo info{};
|
||||
|
||||
info.pos = { -418, 585, -30 };
|
||||
info.lot = 33;
|
||||
info.spawnerID = self->GetObjectID();
|
||||
|
||||
auto* ref = Game::entityManager->CreateEntity(info);
|
||||
|
||||
Game::entityManager->ConstructEntity(ref);
|
||||
|
||||
self->SetVar(u"ShakeObject", ref->GetObjectID());
|
||||
|
||||
self->AddTimer("ShipShakeIdle", 2.0f);
|
||||
self->SetVar(u"RandomTime", 10);
|
||||
}
|
||||
|
||||
void AgSpaceStuff::OnTimerDone(Entity* self, std::string timerName) {
|
||||
@@ -37,70 +22,5 @@ void AgSpaceStuff::OnTimerDone(Entity* self, std::string timerName) {
|
||||
|
||||
RenderComponent::PlayAnimation(self, u"path_0" + (GeneralUtils::to_u16string(pathType)));
|
||||
self->AddTimer("FloaterScale", randTime);
|
||||
} else if (timerName == "ShipShakeExplode") {
|
||||
DoShake(self, true);
|
||||
} else if (timerName == "ShipShakeIdle") {
|
||||
DoShake(self, false);
|
||||
}
|
||||
}
|
||||
|
||||
void AgSpaceStuff::DoShake(Entity* self, bool explodeIdle) {
|
||||
|
||||
if (!explodeIdle) {
|
||||
auto* ref = Game::entityManager->GetEntity(self->GetVar<LWOOBJID>(u"ShakeObject"));
|
||||
|
||||
const auto randomTime = self->GetVar<int>(u"RandomTime");
|
||||
auto time = GeneralUtils::GenerateRandomNumber<int>(0, randomTime + 1);
|
||||
|
||||
if (time < randomTime / 2) {
|
||||
time += randomTime / 2;
|
||||
}
|
||||
|
||||
self->AddTimer("ShipShakeIdle", static_cast<float>(time));
|
||||
|
||||
if (ref)
|
||||
GameMessages::SendPlayEmbeddedEffectOnAllClientsNearObject(ref, FXName, ref->GetObjectID(), 500.0f);
|
||||
|
||||
auto* debrisObject = GetEntityInGroup(DebrisFX);
|
||||
|
||||
if (debrisObject)
|
||||
GameMessages::SendPlayFXEffect(debrisObject, -1, u"DebrisFall", "Debris", LWOOBJID_EMPTY, 1.0f, 1.0f, true);
|
||||
|
||||
const auto randomFx = GeneralUtils::GenerateRandomNumber<int>(0, 3);
|
||||
|
||||
auto* shipFxObject = GetEntityInGroup(ShipFX);
|
||||
if (shipFxObject) {
|
||||
std::string effectType = "shipboom" + std::to_string(randomFx);
|
||||
GameMessages::SendPlayFXEffect(shipFxObject, 559, GeneralUtils::ASCIIToUTF16(effectType), "FX", LWOOBJID_EMPTY, 1.0f, 1.0f, true);
|
||||
}
|
||||
|
||||
self->AddTimer("ShipShakeExplode", 5.0f);
|
||||
|
||||
auto* shipFxObject2 = GetEntityInGroup(ShipFX2);
|
||||
if (shipFxObject2)
|
||||
RenderComponent::PlayAnimation(shipFxObject2, u"explosion");
|
||||
} else {
|
||||
auto* shipFxObject = GetEntityInGroup(ShipFX);
|
||||
auto* shipFxObject2 = GetEntityInGroup(ShipFX2);
|
||||
|
||||
if (shipFxObject)
|
||||
RenderComponent::PlayAnimation(shipFxObject, u"idle");
|
||||
|
||||
if (shipFxObject2)
|
||||
RenderComponent::PlayAnimation(shipFxObject2, u"idle");
|
||||
}
|
||||
}
|
||||
|
||||
Entity* AgSpaceStuff::GetEntityInGroup(const std::string& group) {
|
||||
auto entities = Game::entityManager->GetEntitiesInGroup(group);
|
||||
Entity* en = nullptr;
|
||||
|
||||
for (auto entity : entities) {
|
||||
if (entity) {
|
||||
en = entity;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return en;
|
||||
}
|
||||
|
||||
@@ -5,14 +5,5 @@ class AgSpaceStuff : public CppScripts::Script {
|
||||
public:
|
||||
void OnStartup(Entity* self);
|
||||
void OnTimerDone(Entity* self, std::string timerName);
|
||||
void DoShake(Entity* self, bool explodeIdle);
|
||||
|
||||
std::string DebrisFX = "DebrisFX";
|
||||
std::string ShipFX = "ShipFX";
|
||||
std::string ShipFX2 = "ShipFX2";
|
||||
std::u16string FXName = u"camshake-bridge";
|
||||
|
||||
private:
|
||||
Entity* GetEntityInGroup(const std::string& group);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
set(DSCRIPTS_SOURCES_AI_AG
|
||||
"AgShipPlayerDeathTrigger.cpp"
|
||||
"AgSpaceStuff.cpp"
|
||||
"AgShipShake.cpp"
|
||||
"AgShipPlayerShockServer.cpp"
|
||||
"AgImagSmashable.cpp"
|
||||
"ActSharkPlayerDeathTrigger.cpp"
|
||||
|
||||
@@ -529,6 +529,7 @@ int main(int argc, char** argv) {
|
||||
}
|
||||
|
||||
void HandlePacketChat(Packet* packet) {
|
||||
if (packet->length < 1) return;
|
||||
if (packet->data[0] == ID_DISCONNECTION_NOTIFICATION || packet->data[0] == ID_CONNECTION_LOST) {
|
||||
LOG("Lost our connection to chat, zone(%i), instance(%i)", Game::server->GetZoneID(), Game::server->GetInstanceID());
|
||||
|
||||
@@ -542,7 +543,7 @@ void HandlePacketChat(Packet* packet) {
|
||||
chatConnected = true;
|
||||
}
|
||||
|
||||
if (packet->data[0] == ID_USER_PACKET_ENUM) {
|
||||
if (packet->data[0] == ID_USER_PACKET_ENUM && packet->length >= 4) {
|
||||
if (static_cast<eConnectionType>(packet->data[1]) == eConnectionType::CHAT) {
|
||||
switch (static_cast<eChatMessageType>(packet->data[3])) {
|
||||
case eChatMessageType::WORLD_ROUTE_PACKET: {
|
||||
@@ -557,8 +558,9 @@ void HandlePacketChat(Packet* packet) {
|
||||
|
||||
//Write our stream outwards:
|
||||
CBITSTREAM;
|
||||
for (BitSize_t i = 0; i < inStream.GetNumberOfBytesUsed(); i++) {
|
||||
bitStream.Write(packet->data[i + 16]); //16 bytes == header + playerID to skip
|
||||
unsigned char data;
|
||||
while (inStream.Read(data)) {
|
||||
bitStream.Write(data);
|
||||
}
|
||||
|
||||
SEND_PACKET; //send routed packet to player
|
||||
@@ -659,7 +661,7 @@ void HandlePacketChat(Packet* packet) {
|
||||
}
|
||||
|
||||
void HandleMasterPacket(Packet* packet) {
|
||||
|
||||
if (packet->length < 2) return;
|
||||
if (static_cast<eConnectionType>(packet->data[1]) != eConnectionType::MASTER || packet->length < 4) return;
|
||||
switch (static_cast<eMasterMessageType>(packet->data[3])) {
|
||||
case eMasterMessageType::REQUEST_PERSISTENT_ID_RESPONSE: {
|
||||
@@ -785,6 +787,7 @@ void HandleMasterPacket(Packet* packet) {
|
||||
}
|
||||
|
||||
void HandlePacket(Packet* packet) {
|
||||
if (packet->length < 1) return;
|
||||
if (packet->data[0] == ID_DISCONNECTION_NOTIFICATION || packet->data[0] == ID_CONNECTION_LOST) {
|
||||
auto user = UserManager::Instance()->GetUser(packet->systemAddress);
|
||||
if (!user) return;
|
||||
@@ -1207,8 +1210,8 @@ void HandlePacket(Packet* packet) {
|
||||
|
||||
//Now write the rest of the data:
|
||||
auto data = inStream.GetData();
|
||||
for (uint32_t i = 0; i < size; ++i) {
|
||||
bitStream.Write(data[i + 23]);
|
||||
for (uint32_t i = 23; i - 23 < size && i < packet->length; ++i) {
|
||||
bitStream.Write(data[i]);
|
||||
}
|
||||
|
||||
Game::chatServer->Send(&bitStream, SYSTEM_PRIORITY, RELIABLE_ORDERED, 0, Game::chatSysAddr, false);
|
||||
|
||||
3
migrations/dlu/15_behavior_owner.sql
Normal file
3
migrations/dlu/15_behavior_owner.sql
Normal file
@@ -0,0 +1,3 @@
|
||||
ALTER TABLE behaviors ADD COLUMN character_id BIGINT NOT NULL DEFAULT 0;
|
||||
ALTER TABLE behaviors ADD COLUMN behavior_id BIGINT NOT NULL PRIMARY KEY;
|
||||
ALTER TABLE behaviors DROP COLUMN id;
|
||||
5
thirdparty/CMakeLists.txt
vendored
5
thirdparty/CMakeLists.txt
vendored
@@ -23,6 +23,11 @@ if(NOT WIN32)
|
||||
target_include_directories(bcrypt PRIVATE "libbcrypt/include/bcrypt")
|
||||
endif()
|
||||
|
||||
# Need to define this on Clang and GNU for 'strdup' support
|
||||
if(CMAKE_CXX_COMPILER_ID MATCHES "Clang|GNU")
|
||||
target_compile_definitions(bcrypt PRIVATE "_POSIX_C_SOURCE=200809L")
|
||||
endif()
|
||||
|
||||
target_include_directories(bcrypt INTERFACE "libbcrypt/include")
|
||||
target_include_directories(bcrypt PRIVATE "libbcrypt/src")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user