mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2024-11-26 15:37:20 +00:00
dCinema improvements
* Visiblity and effect records * Recorder will catch effects from behaviors * Documentation for setting up a scene to play automatically. * Documentation for server-side preconditions.
This commit is contained in:
parent
e4320d3e63
commit
cdc9dda3c4
@ -34,7 +34,13 @@ void dConfig::ReloadConfig() {
|
||||
}
|
||||
|
||||
const std::string& dConfig::GetValue(std::string key) {
|
||||
return this->m_ConfigValues[key];
|
||||
static std::string emptyString{};
|
||||
|
||||
const auto& it = this->m_ConfigValues.find(key);
|
||||
|
||||
if (it == this->m_ConfigValues.end()) return emptyString;
|
||||
|
||||
return it->second;
|
||||
}
|
||||
|
||||
void dConfig::ProcessLine(const std::string& line) {
|
||||
|
@ -4,6 +4,8 @@
|
||||
#include "Game.h"
|
||||
#include "Logger.h"
|
||||
|
||||
#include "Recorder.h"
|
||||
|
||||
void AttackDelayBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream, const BehaviorBranchContext branch) {
|
||||
uint32_t handle{};
|
||||
|
||||
@ -11,6 +13,8 @@ void AttackDelayBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bi
|
||||
LOG("Unable to read handle from bitStream, aborting Handle! %i", bitStream->GetNumberOfUnreadBits());
|
||||
return;
|
||||
};
|
||||
|
||||
Cinema::Recording::Recorder::RegisterEffectForActor(context->originator, this->m_effectId);
|
||||
|
||||
for (auto i = 0u; i < this->m_numIntervals; ++i) {
|
||||
context->RegisterSyncBehavior(handle, this, branch, this->m_delay * i, m_ignoreInterrupts);
|
||||
|
@ -3,13 +3,17 @@
|
||||
#include "BehaviorContext.h"
|
||||
#include "BehaviorBranchContext.h"
|
||||
|
||||
#include "Recorder.h"
|
||||
|
||||
void PlayEffectBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) {
|
||||
const auto& target = branch.target == LWOOBJID_EMPTY ? context->originator : branch.target;
|
||||
|
||||
Cinema::Recording::Recorder::RegisterEffectForActor(target, this->m_effectId);
|
||||
|
||||
// On managed behaviors this is handled by the client
|
||||
if (!context->unmanaged)
|
||||
return;
|
||||
|
||||
const auto& target = branch.target == LWOOBJID_EMPTY ? context->originator : branch.target;
|
||||
|
||||
PlayFx(u"", target);
|
||||
}
|
||||
|
||||
|
@ -36,9 +36,17 @@ void Cinema::Play::CheckForAudience() {
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (scene->IsPlayerInBounds(player)) {
|
||||
SignalBarrier("audience");
|
||||
|
||||
m_PlayerHasBeenInsideBounds = true;
|
||||
}
|
||||
|
||||
if (!scene->IsPlayerInBounds(player)) {
|
||||
Conclude();
|
||||
if (!scene->IsPlayerInShowingDistance(player)) {
|
||||
if (m_PlayerHasBeenInsideBounds) {
|
||||
Conclude();
|
||||
}
|
||||
|
||||
CleanUp();
|
||||
|
||||
@ -70,7 +78,6 @@ void Cinema::Play::SetupBarrier(const std::string& barrier, std::function<void()
|
||||
|
||||
void Cinema::Play::SignalBarrier(const std::string& barrier) {
|
||||
if (m_Barriers.find(barrier) == m_Barriers.end()) {
|
||||
LOG("Barrier %s does not exist", barrier.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -77,6 +77,8 @@ private:
|
||||
|
||||
bool m_CheckForAudience = false;
|
||||
|
||||
bool m_PlayerHasBeenInsideBounds = false;
|
||||
|
||||
std::unordered_map<std::string, std::vector<std::function<void()>>> m_Barriers;
|
||||
|
||||
};
|
||||
|
@ -22,6 +22,8 @@ A play is created is a couple of steps:
|
||||
2. Create prefabs of props in the scene.
|
||||
3. Put the NPCs and props in a scene file.
|
||||
4. Edit the performed acts to add synchronization, conditions and additional actions.
|
||||
5. Setting up the scene to be performed automatically.
|
||||
6. Hiding zone objects while performing.
|
||||
|
||||
### 1. Acts out the scene
|
||||
See <a href="./media/acting.mp4">media/acting.mp4</a> for an example of how to act out a scene.
|
||||
@ -260,4 +262,91 @@ This record sends a signal to all acts waiting for it.
|
||||
This record concludes the play.
|
||||
```xml
|
||||
<ConcludeRecord t="0" />
|
||||
```
|
||||
```
|
||||
|
||||
#### 4.15. VisiblityRecord
|
||||
This record makes the actor visible or invisible.
|
||||
```xml
|
||||
<VisiblityRecord visible="false" t="0" />
|
||||
```
|
||||
|
||||
#### 4.16. PlayEffectRecord
|
||||
This record plays an effect.
|
||||
```xml
|
||||
<PlayEffectRecord effect="5307" t="0" />
|
||||
```
|
||||
|
||||
### 5. Setting up the scene to be performed automatically
|
||||
Scenes can be appended with metadata to describe when they should be performed and what consequences they have. This is done by editing the scene file.
|
||||
|
||||
#### 5.1. Scene metadata
|
||||
There attributes can be added to the `Scene` tag:
|
||||
|
||||
| Attribute | Description |
|
||||
| --- | --- |
|
||||
| `x y z` | The center of where the following two attributes <br> are measured. |
|
||||
| `showingDistance` | The distance at which the scene will <br> be loaded for a player.<br><br> If the player exits this area the scene is unloaded. <br> If the scene has been registered as having been<br>viewed by the player, is is concluded. |
|
||||
| `performingDistance` | The scene is registred as having been <br>viewed by the player. This doesn't mean <br>it can't be viewed again.<br><br>A signal named `"audiance"` will be <br> sent when the player enters this area. <br>This can be used to trigger the main <br>part of the scene. |
|
||||
| `acceptMission` | The mission with the given id will be <br> accepted when the scene is concluded. |
|
||||
| `completeMission` | The mission with the given id will be <br> completed when the scene is concluded. |
|
||||
|
||||
Here is an example of a scene with metadata:
|
||||
```xml
|
||||
<Scene x="-368.272" y="1161.89" z="-5.25745" performingDistance="50" showingDistance="200">
|
||||
...
|
||||
</Scene>
|
||||
```
|
||||
|
||||
#### 5.2. Automatic scene setup
|
||||
In either the worldconfig.ini or sharedconfig.ini file, add the following:
|
||||
```
|
||||
# Path to where scenes are located.
|
||||
scenes_directory=vanity/scenes/
|
||||
```
|
||||
|
||||
Now move the scene into a subdirectory of the scenes directory. The name of the subdirectory should be **the zone id** of the zone the scene is located in.
|
||||
|
||||
For example:
|
||||
```
|
||||
build/
|
||||
├── vanity/
|
||||
│ ├── scenes/
|
||||
│ │ ├── 1900/
|
||||
│ │ │ ├── my-scene.xml
|
||||
```
|
||||
|
||||
Now the scene will be setup automatically and loaded when the player enters the `showingDistance` of the scene.
|
||||
|
||||
#### 5.3. Adding conditions
|
||||
Conditions can be added to the scene to make it only performable when the player fulfills specified preconditions. This is done by editing the scene file.
|
||||
|
||||
Here is an example of a scene with conditions:
|
||||
```xml
|
||||
<Scene x="-368.272" y="1161.89" z="-5.25745" performingDistance="50" showingDistance="200">
|
||||
<Precondition expression="42,99"/> <!-- The player must fulfill preconditions 42 and 99. -->
|
||||
<Precondition expression="666" not="1"/> <!-- The player cannot fulfill precondition 666. -->
|
||||
...
|
||||
</Scene>
|
||||
```
|
||||
|
||||
### 6. Hiding zone objects while performing
|
||||
When a scene should be performed, you might want to hide some objects in the zone. This is done by adding server preconditions. This is a seperate file.
|
||||
|
||||
In either the worldconfig.ini or sharedconfig.ini file, add the following:
|
||||
```
|
||||
# Path to where server preconditions are located.
|
||||
server_preconditions_directory=vanity/server-preconditions.xml
|
||||
```
|
||||
|
||||
Now create the server preconditions file in the directory specified.
|
||||
|
||||
Here is an example of a server preconditions file:
|
||||
```xml
|
||||
<Preconditions>
|
||||
<Entity lot="12261">
|
||||
<Precondition not="1">1006</Precondition>
|
||||
</Entity>
|
||||
</Preconditions>
|
||||
```
|
||||
|
||||
This will hide the objects with lot 12261 for players who fulfill precondition 1006.
|
||||
|
@ -13,6 +13,8 @@ using namespace Cinema::Recording;
|
||||
|
||||
std::unordered_map<LWOOBJID, Recorder*> m_Recorders = {};
|
||||
|
||||
std::unordered_map<int32_t, std::string> m_EffectAnimations = {};
|
||||
|
||||
Recorder::Recorder() {
|
||||
this->m_StartTime = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch());
|
||||
this->m_IsRecording = false;
|
||||
@ -169,7 +171,7 @@ void Recorder::ActingDispatch(Entity* actor, size_t index, Play* variables) {
|
||||
ActingDispatch(actor, index + 1, variables);
|
||||
});
|
||||
|
||||
if (barrierRecord->timeout <= 0.0f) {
|
||||
if (barrierRecord->timeout <= 0.0001f) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -211,6 +213,17 @@ void Recorder::ActingDispatch(Entity* actor, size_t index, Play* variables) {
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the record is a visibility record
|
||||
auto* visibilityRecord = dynamic_cast<VisibilityRecord*>(record);
|
||||
|
||||
if (visibilityRecord) {
|
||||
if (visibilityRecord->visible) {
|
||||
ServerPreconditions::AddExcludeFor(actor->GetObjectID(), variables->player);
|
||||
} else {
|
||||
ServerPreconditions::RemoveExcludeFor(actor->GetObjectID(), variables->player);
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the record is a player proximity record
|
||||
auto* playerProximityRecord = dynamic_cast<PlayerProximityRecord*>(record);
|
||||
|
||||
@ -359,6 +372,45 @@ Recorder* Recorder::GetRecorder(LWOOBJID actorID) {
|
||||
return iter->second;
|
||||
}
|
||||
|
||||
void Cinema::Recording::Recorder::RegisterEffectForActor(LWOOBJID actorID, const int32_t& effectId) {
|
||||
auto iter = m_Recorders.find(actorID);
|
||||
if (iter == m_Recorders.end()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto& recorder = iter->second;
|
||||
|
||||
const auto& effectIter = m_EffectAnimations.find(effectId);
|
||||
|
||||
if (effectIter == m_EffectAnimations.end()) {
|
||||
auto statement = CDClientDatabase::CreatePreppedStmt("SELECT animationName FROM BehaviorEffect WHERE effectID = ? LIMIT 1;");
|
||||
|
||||
statement.bind(1, effectId);
|
||||
|
||||
auto result = statement.execQuery();
|
||||
|
||||
if (result.eof()) {
|
||||
result.finalize();
|
||||
|
||||
m_EffectAnimations.emplace(effectId, "");
|
||||
}
|
||||
else {
|
||||
const auto animationName = result.getStringField(0);
|
||||
|
||||
m_EffectAnimations.emplace(effectId, animationName);
|
||||
|
||||
result.finalize();
|
||||
|
||||
recorder->AddRecord(new AnimationRecord(animationName));
|
||||
}
|
||||
}
|
||||
else {
|
||||
recorder->AddRecord(new AnimationRecord(effectIter->second));
|
||||
}
|
||||
|
||||
recorder->AddRecord(new PlayEffectRecord(std::to_string(effectId)));
|
||||
}
|
||||
|
||||
MovementRecord::MovementRecord(const NiPoint3& position, const NiQuaternion& rotation, const NiPoint3& velocity, const NiPoint3& angularVelocity, bool onGround, bool dirtyVelocity, bool dirtyAngularVelocity) {
|
||||
this->position = position;
|
||||
this->rotation = rotation;
|
||||
@ -691,6 +743,14 @@ Recorder* Recorder::LoadFromFile(const std::string& filename) {
|
||||
PlayerProximityRecord* record = new PlayerProximityRecord();
|
||||
record->Deserialize(element);
|
||||
recorder->m_Records.push_back(record);
|
||||
} else if (name == "VisibilityRecord") {
|
||||
VisibilityRecord* record = new VisibilityRecord();
|
||||
record->Deserialize(element);
|
||||
recorder->m_Records.push_back(record);
|
||||
} else if (name == "PlayEffectRecord") {
|
||||
PlayEffectRecord* record = new PlayEffectRecord();
|
||||
record->Deserialize(element);
|
||||
recorder->m_Records.push_back(record);
|
||||
}
|
||||
|
||||
if (element->Attribute("name")) {
|
||||
@ -943,4 +1003,64 @@ void Cinema::Recording::ConcludeRecord::Serialize(tinyxml2::XMLDocument& documen
|
||||
|
||||
void Cinema::Recording::ConcludeRecord::Deserialize(tinyxml2::XMLElement* element) {
|
||||
m_Delay = element->DoubleAttribute("t");
|
||||
}
|
||||
}
|
||||
|
||||
Cinema::Recording::VisibilityRecord::VisibilityRecord(bool visible) {
|
||||
this->visible = visible;
|
||||
}
|
||||
|
||||
void Cinema::Recording::VisibilityRecord::Act(Entity* actor) {
|
||||
}
|
||||
|
||||
void Cinema::Recording::VisibilityRecord::Serialize(tinyxml2::XMLDocument& document, tinyxml2::XMLElement* parent) {
|
||||
auto* element = document.NewElement("VisibilityRecord");
|
||||
|
||||
element->SetAttribute("visible", visible);
|
||||
|
||||
element->SetAttribute("t", m_Delay);
|
||||
|
||||
parent->InsertEndChild(element);
|
||||
}
|
||||
|
||||
void Cinema::Recording::VisibilityRecord::Deserialize(tinyxml2::XMLElement* element) {
|
||||
visible = element->BoolAttribute("visible");
|
||||
|
||||
m_Delay = element->DoubleAttribute("t");
|
||||
}
|
||||
|
||||
Cinema::Recording::PlayEffectRecord::PlayEffectRecord(const std::string& effect) {
|
||||
this->effect = effect;
|
||||
}
|
||||
|
||||
void Cinema::Recording::PlayEffectRecord::Act(Entity* actor) {
|
||||
int32_t effectID = 0;
|
||||
|
||||
if (!GeneralUtils::TryParse(effect, effectID)) {
|
||||
return;
|
||||
}
|
||||
|
||||
GameMessages::SendPlayFXEffect(
|
||||
actor->GetObjectID(),
|
||||
effectID,
|
||||
u"cast",
|
||||
std::to_string(ObjectIDManager::GenerateRandomObjectID())
|
||||
);
|
||||
|
||||
Game::entityManager->SerializeEntity(actor);
|
||||
}
|
||||
|
||||
void Cinema::Recording::PlayEffectRecord::Serialize(tinyxml2::XMLDocument& document, tinyxml2::XMLElement* parent) {
|
||||
auto* element = document.NewElement("PlayEffectRecord");
|
||||
|
||||
element->SetAttribute("effect", effect.c_str());
|
||||
|
||||
element->SetAttribute("t", m_Delay);
|
||||
|
||||
parent->InsertEndChild(element);
|
||||
}
|
||||
|
||||
void Cinema::Recording::PlayEffectRecord::Deserialize(tinyxml2::XMLElement* element) {
|
||||
effect = element->Attribute("effect");
|
||||
|
||||
m_Delay = element->DoubleAttribute("t");
|
||||
}
|
||||
|
@ -89,7 +89,7 @@ public:
|
||||
class EquipRecord : public Record
|
||||
{
|
||||
public:
|
||||
LOT item;
|
||||
LOT item = LOT_NULL;
|
||||
|
||||
EquipRecord() = default;
|
||||
|
||||
@ -105,7 +105,7 @@ public:
|
||||
class UnequipRecord : public Record
|
||||
{
|
||||
public:
|
||||
LOT item;
|
||||
LOT item = LOT_NULL;
|
||||
|
||||
UnequipRecord() = default;
|
||||
|
||||
@ -204,7 +204,7 @@ class BarrierRecord : public Record
|
||||
public:
|
||||
std::string signal;
|
||||
|
||||
float timeout;
|
||||
float timeout = 0.0f;
|
||||
|
||||
std::string timeoutLabel;
|
||||
|
||||
@ -250,9 +250,9 @@ public:
|
||||
class PlayerProximityRecord : public Record
|
||||
{
|
||||
public:
|
||||
float distance;
|
||||
float distance = 0.0f;
|
||||
|
||||
float timeout;
|
||||
float timeout = 0.0f;
|
||||
|
||||
std::string timeoutLabel;
|
||||
|
||||
@ -267,6 +267,37 @@ public:
|
||||
void Deserialize(tinyxml2::XMLElement* element) override;
|
||||
};
|
||||
|
||||
class VisibilityRecord : public Record
|
||||
{
|
||||
public:
|
||||
bool visible = false;
|
||||
|
||||
VisibilityRecord() = default;
|
||||
|
||||
VisibilityRecord(bool visible);
|
||||
|
||||
void Act(Entity* actor) override;
|
||||
|
||||
void Serialize(tinyxml2::XMLDocument& document, tinyxml2::XMLElement* parent) override;
|
||||
|
||||
void Deserialize(tinyxml2::XMLElement* element) override;
|
||||
};
|
||||
|
||||
class PlayEffectRecord : public Record
|
||||
{
|
||||
public:
|
||||
std::string effect;
|
||||
|
||||
PlayEffectRecord() = default;
|
||||
|
||||
PlayEffectRecord(const std::string& effect);
|
||||
|
||||
void Act(Entity* actor) override;
|
||||
|
||||
void Serialize(tinyxml2::XMLDocument& document, tinyxml2::XMLElement* parent) override;
|
||||
|
||||
void Deserialize(tinyxml2::XMLElement* element) override;
|
||||
};
|
||||
|
||||
class Recorder
|
||||
{
|
||||
@ -299,6 +330,8 @@ public:
|
||||
|
||||
static Recorder* GetRecorder(LWOOBJID actorID);
|
||||
|
||||
static void RegisterEffectForActor(LWOOBJID actorID, const int32_t& effectId);
|
||||
|
||||
private:
|
||||
void ActingDispatch(Entity* actor, size_t index, Play* variables);
|
||||
|
||||
|
@ -1,11 +1,14 @@
|
||||
#include "Scene.h"
|
||||
|
||||
#include <filesystem>
|
||||
|
||||
#include <tinyxml2.h>
|
||||
|
||||
#include "ServerPreconditions.hpp"
|
||||
#include "EntityManager.h"
|
||||
#include "EntityInfo.h"
|
||||
#include "MissionComponent.h"
|
||||
#include "dConfig.h"
|
||||
|
||||
using namespace Cinema;
|
||||
|
||||
@ -27,7 +30,7 @@ void Cinema::Scene::Rehearse() {
|
||||
CheckForShowings();
|
||||
}
|
||||
|
||||
void Cinema::Scene::Conclude(Entity* player) const {
|
||||
void Cinema::Scene::Conclude(Entity* player) {
|
||||
if (player == nullptr) {
|
||||
return;
|
||||
}
|
||||
@ -49,6 +52,10 @@ void Cinema::Scene::Conclude(Entity* player) const {
|
||||
if (m_AcceptMission != 0) {
|
||||
missionComponent->AcceptMission(m_AcceptMission);
|
||||
}
|
||||
|
||||
// Remove the player from the audience
|
||||
m_Audience.erase(player->GetObjectID());
|
||||
m_HasBeenOutside.erase(player->GetObjectID());
|
||||
}
|
||||
|
||||
bool Cinema::Scene::IsPlayerInBounds(Entity* player) const {
|
||||
@ -64,14 +71,60 @@ bool Cinema::Scene::IsPlayerInBounds(Entity* player) const {
|
||||
|
||||
auto distance = NiPoint3::Distance(position, m_Center);
|
||||
|
||||
LOG("Player distance from scene: %f, with bounds %f", distance, m_Bounds);
|
||||
return distance <= m_Bounds;
|
||||
}
|
||||
|
||||
// The player may be within 20% of the bounds
|
||||
return distance <= (m_Bounds * 1.2f);
|
||||
bool Cinema::Scene::IsPlayerInShowingDistance(Entity* player) const {
|
||||
if (player == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_ShowingDistance == 0.0f) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const auto& position = player->GetPosition();
|
||||
|
||||
auto distance = NiPoint3::Distance(position, m_Center);
|
||||
|
||||
return distance <= m_ShowingDistance;
|
||||
}
|
||||
|
||||
void Cinema::Scene::AutoLoadScenesForZone(LWOMAPID zone) {
|
||||
const auto& scenesRoot = Game::config->GetValue("scenes_directory");
|
||||
|
||||
if (scenesRoot.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto path = std::filesystem::path(scenesRoot) / std::to_string(zone);
|
||||
|
||||
if (!std::filesystem::exists(path)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Recursively iterate through the directory
|
||||
for (const auto& entry : std::filesystem::recursive_directory_iterator(path)) {
|
||||
if (!entry.is_regular_file()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check that extension is .xml
|
||||
if (entry.path().extension() != ".xml") {
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto& file = entry.path().string();
|
||||
|
||||
auto& scene = LoadFromFile(file);
|
||||
|
||||
scene.Rehearse();
|
||||
}
|
||||
}
|
||||
|
||||
void Cinema::Scene::CheckForShowings() {
|
||||
auto audience = m_Audience;
|
||||
auto hasBeenOutside = m_HasBeenOutside;
|
||||
|
||||
for (const auto& member : audience) {
|
||||
if (Game::entityManager->GetEntity(member) == nullptr) {
|
||||
@ -79,6 +132,15 @@ void Cinema::Scene::CheckForShowings() {
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& member : hasBeenOutside) {
|
||||
if (Game::entityManager->GetEntity(member) == nullptr) {
|
||||
m_HasBeenOutside.erase(member);
|
||||
}
|
||||
}
|
||||
|
||||
m_Audience = audience;
|
||||
m_HasBeenOutside = hasBeenOutside;
|
||||
|
||||
// I don't care
|
||||
Game::entityManager->GetZoneControlEntity()->AddCallbackTimer(1.0f, [this]() {
|
||||
for (auto* player : Player::GetAllPlayers()) {
|
||||
@ -87,9 +149,9 @@ void Cinema::Scene::CheckForShowings() {
|
||||
}
|
||||
|
||||
CheckTicket(player);
|
||||
|
||||
|
||||
}
|
||||
|
||||
CheckForShowings();
|
||||
});
|
||||
}
|
||||
|
||||
@ -104,6 +166,16 @@ void Cinema::Scene::CheckTicket(Entity* player) {
|
||||
}
|
||||
}
|
||||
|
||||
if (!IsPlayerInShowingDistance(player)) {
|
||||
m_HasBeenOutside.emplace(player->GetObjectID());
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_HasBeenOutside.find(player->GetObjectID()) == m_HasBeenOutside.end()) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_Audience.emplace(player->GetObjectID());
|
||||
|
||||
Act(player);
|
||||
@ -225,8 +297,14 @@ Scene& Cinema::Scene::LoadFromFile(std::string file) {
|
||||
scene.m_Center = NiPoint3(root->FloatAttribute("x"), root->FloatAttribute("y"), root->FloatAttribute("z"));
|
||||
}
|
||||
|
||||
if (root->Attribute("bounds")) {
|
||||
scene.m_Bounds = root->FloatAttribute("bounds");
|
||||
if (root->Attribute("performingDistance")) {
|
||||
scene.m_Bounds = root->FloatAttribute("performingDistance");
|
||||
}
|
||||
|
||||
if (root->Attribute("showingDistance")) {
|
||||
scene.m_ShowingDistance = root->FloatAttribute("showingDistance");
|
||||
} else {
|
||||
scene.m_ShowingDistance = scene.m_Bounds * 2.0f;
|
||||
}
|
||||
|
||||
// Load accept and complete mission
|
||||
|
@ -50,7 +50,7 @@ public:
|
||||
*
|
||||
* @param player The player to conclude the scene for (not nullptr).
|
||||
*/
|
||||
void Conclude(Entity* player) const;
|
||||
void Conclude(Entity* player);
|
||||
|
||||
/**
|
||||
* @brief Checks if a given player is within the bounds of the scene.
|
||||
@ -59,6 +59,13 @@ public:
|
||||
*/
|
||||
bool IsPlayerInBounds(Entity* player) const;
|
||||
|
||||
/**
|
||||
* @brief Checks if a given player is within the showing distance of the scene.
|
||||
*
|
||||
* @param player The player to check.
|
||||
*/
|
||||
bool IsPlayerInShowingDistance(Entity* player) const;
|
||||
|
||||
/**
|
||||
* @brief Act the scene.
|
||||
*
|
||||
@ -75,6 +82,13 @@ public:
|
||||
*/
|
||||
static Scene& LoadFromFile(std::string file);
|
||||
|
||||
/**
|
||||
* @brief Automatically loads the scenes for a given zone.
|
||||
*
|
||||
* @param zone The zone to load the scenes for.
|
||||
*/
|
||||
static void AutoLoadScenesForZone(LWOMAPID zone);
|
||||
|
||||
private:
|
||||
void CheckForShowings();
|
||||
|
||||
@ -86,6 +100,7 @@ private:
|
||||
|
||||
NiPoint3 m_Center;
|
||||
float m_Bounds = 0.0f;
|
||||
float m_ShowingDistance = 0.0f;
|
||||
|
||||
std::vector<std::pair<PreconditionExpression, bool>> m_Preconditions;
|
||||
|
||||
@ -93,6 +108,7 @@ private:
|
||||
int32_t m_CompleteMission = 0;
|
||||
|
||||
std::unordered_set<LWOOBJID> m_Audience;
|
||||
std::unordered_set<LWOOBJID> m_HasBeenOutside;
|
||||
|
||||
static std::unordered_map<std::string, Scene> m_Scenes;
|
||||
};
|
||||
|
@ -15,11 +15,21 @@
|
||||
|
||||
std::unordered_map<int32_t, float> RenderComponent::m_DurationCache{};
|
||||
|
||||
std::unordered_map<int32_t, std::vector<int32_t>> RenderComponent::m_AnimationGroupCache{};
|
||||
|
||||
RenderComponent::RenderComponent(Entity* parent, int32_t componentId): Component(parent) {
|
||||
m_Effects = std::vector<Effect*>();
|
||||
m_LastAnimationName = "";
|
||||
if (componentId == -1) return;
|
||||
|
||||
const auto& it = m_AnimationGroupCache.find(componentId);
|
||||
|
||||
if (it != m_AnimationGroupCache.end()) {
|
||||
m_animationGroupIds = it->second;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
auto query = CDClientDatabase::CreatePreppedStmt("SELECT * FROM RenderComponent WHERE id = ?;");
|
||||
query.bind(1, componentId);
|
||||
auto result = query.execQuery();
|
||||
@ -41,6 +51,8 @@ RenderComponent::RenderComponent(Entity* parent, int32_t componentId): Component
|
||||
}
|
||||
}
|
||||
result.finalize();
|
||||
|
||||
m_AnimationGroupCache[componentId] = m_animationGroupIds;
|
||||
}
|
||||
|
||||
RenderComponent::~RenderComponent() {
|
||||
|
@ -147,6 +147,11 @@ private:
|
||||
* Cache of queries that look for the length of each effect, indexed by effect ID
|
||||
*/
|
||||
static std::unordered_map<int32_t, float> m_DurationCache;
|
||||
|
||||
/**
|
||||
* Cache for animation groups, indexed by the component ID
|
||||
*/
|
||||
static std::unordered_map<int32_t, std::vector<int32_t>> m_AnimationGroupCache;
|
||||
};
|
||||
|
||||
#endif // RENDERCOMPONENT_H
|
||||
|
@ -1051,6 +1051,14 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit
|
||||
return;
|
||||
}
|
||||
|
||||
if (chatCommand == "scene-setup" && entity->GetGMLevel() >= eGameMasterLevel::DEVELOPER && args.size() >= 1) {
|
||||
auto& scene = Cinema::Scene::LoadFromFile(args[0]);
|
||||
|
||||
scene.Rehearse();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ((chatCommand == "teleport" || chatCommand == "tele") && entity->GetGMLevel() >= eGameMasterLevel::JUNIOR_MODERATOR) {
|
||||
NiPoint3 pos{};
|
||||
if (args.size() == 3) {
|
||||
|
@ -76,6 +76,7 @@
|
||||
#include "CheatDetection.h"
|
||||
|
||||
#include "ServerPreconditions.hpp"
|
||||
#include "Scene.h"
|
||||
|
||||
namespace Game {
|
||||
Logger* logger = nullptr;
|
||||
@ -257,8 +258,6 @@ int main(int argc, char** argv) {
|
||||
|
||||
PerformanceManager::SelectProfile(zoneID);
|
||||
|
||||
ServerPreconditions::LoadPreconditions("vanity/preconditions.xml");
|
||||
|
||||
Game::entityManager = new EntityManager();
|
||||
Game::zoneManager = new dZoneManager();
|
||||
//Load our level:
|
||||
@ -312,6 +311,16 @@ int main(int argc, char** argv) {
|
||||
LOG("FDB Checksum calculated as: %s", databaseChecksum.c_str());
|
||||
}
|
||||
|
||||
// Load server-side preconditions if they exist
|
||||
const auto& preconditionsPath = Game::config->GetValue("server_preconditions_path");
|
||||
|
||||
if (!preconditionsPath.empty()) {
|
||||
ServerPreconditions::LoadPreconditions(preconditionsPath);
|
||||
}
|
||||
|
||||
// Load scenes for the zone
|
||||
Cinema::Scene::AutoLoadScenesForZone(zoneID);
|
||||
|
||||
uint32_t currentFrameDelta = highFrameDelta;
|
||||
// These values are adjust them selves to the current framerate should it update.
|
||||
uint32_t logFlushTime = 15 * currentFramerate; // 15 seconds in frames
|
||||
|
Loading…
Reference in New Issue
Block a user