Skip to content

Commit

Permalink
Rewrite music zone system
Browse files Browse the repository at this point in the history
  • Loading branch information
frabert committed Sep 20, 2018
1 parent e0f423a commit f0691c0
Show file tree
Hide file tree
Showing 6 changed files with 157 additions and 143 deletions.
96 changes: 96 additions & 0 deletions src/engine/MusicZoneManager.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
#include "MusicZoneManager.h"

#include <components/Vob.h>
#include <components/VobClasses.h>
#include <engine/BaseEngine.h>
#include <logic/PlayerController.h>
#include <logic/ScriptEngine.h>

using namespace Engine;

static const std::array<const std::string, 6> instanceSuffixes =
{
"_DAY_STD",
"_DAY_THR",
"_DAY_FGT",
"_NGT_STD",
"_NGT_THR",
"_NGT_FGT",
};

MusicZoneManager::MusicZoneManager(World::WorldInstance& world)
: m_world(world)
{
}

void MusicZoneManager::addZone(const ZenLoad::zCVobData& zone)
{
assert(zone.vobType == ZenLoad::zCVobData::VT_oCZoneMusic);
m_zones.emplace_back(zone);
}

static bool isInside(const Math::float3& vec, const ZMath::float3& min, const ZMath::float3& max)
{
return (vec.x >= min.x && vec.x < max.x &&
vec.y >= min.y && vec.y < max.y &&
vec.z >= min.z && vec.z < max.z);
}

void MusicZoneManager::playTheme(const std::string& prefix, EMusicType type, EMusicTime time)
{
auto& audioWorld = m_world.getEngine()->getAudioWorld();
// First try the requested theme
if (!audioWorld.playMusicTheme(prefix + instanceSuffixes[time + type]))
{
// If it wasn't found, try the daytime variation
if (!audioWorld.playMusicTheme(prefix + instanceSuffixes[MT_Day + type]))
{
// Then try the standard daytime variation
audioWorld.playMusicTheme(prefix + instanceSuffixes[MT_Day + MT_Std]);
}
}
}

void MusicZoneManager::onUpdate()
{
// Get player's position
const auto& playerEntity = m_world.getScriptEngine().getPlayerEntity();
auto playerVob = VobTypes::asNpcVob(m_world, playerEntity);

if (!playerVob.isValid() || playerVob.playerController->getUsedMob().isValid())
return;

const auto& playerPos = playerVob.position->m_WorldMatrix.Translation();

const ZenLoad::zCVobData* currentZone = nullptr;
for (const auto& zone : m_zones)
{
// BBoxes are expressed in centimeters, but REGoth uses meters
if (isInside(playerPos, zone.bbox[0] * .01f, zone.bbox[1] * .01f))
{
if (!currentZone
|| currentZone->oCZoneMusic.priority < zone.oCZoneMusic.priority)
{
currentZone = &zone;
}
}
}

auto time = m_world.getEngine()->getGameClock().isDaytime() ? MT_Day : MT_Ngt;

auto& audioWorld = m_world.getEngine()->getAudioWorld();

if (!currentZone)
{
// If the player's not inside any zone, play the default theme
playTheme(m_defaultZonePrefix, MT_Std, time);
}
else
{
auto zoneName = currentZone->vobName;
auto instancePrefix = Utils::uppered(zoneName.substr(zoneName.find('_') + 1));

// Try playing the zone's theme
playTheme(instancePrefix, MT_Std, time);
}
}
47 changes: 47 additions & 0 deletions src/engine/MusicZoneManager.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#pragma once

#include <vector>

#include <engine/World.h>

namespace Engine
{
/**
* @brief Handles transitions between music zones
*
*/
class MusicZoneManager
{
public:
MusicZoneManager(World::WorldInstance& world);

/**
* @brief Adds a zone to the zone list
*/
void addZone(const ZenLoad::zCVobData& vob);

/**
* @brief Sets the world's default zone
*/
void setDefaultZone(const std::string& zone) { m_defaultZonePrefix = zone; }
void onUpdate();

private:
World::WorldInstance& m_world;
std::vector<ZenLoad::zCVobData> m_zones;
std::string m_defaultZonePrefix;

enum EMusicTime {
MT_Day = 0,
MT_Ngt = 3,
};

enum EMusicType {
MT_Std = 0,
MT_Thr = 1,
MT_Fgt = 2,
};

void playTheme(const std::string& prefix, EMusicType type, EMusicTime time);
};
}
12 changes: 6 additions & 6 deletions src/engine/World.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include <debugdraw/debugdraw.h>
#include <engine/BaseEngine.h>
#include <engine/GameEngine.h>
#include <engine/MusicZoneManager.h>
#include <engine/World.h>
#include <entry/input.h>
#include <handle/HandleDef.h>
Expand Down Expand Up @@ -80,8 +81,9 @@ WorldInstance::WorldInstance(Engine::BaseEngine& engine)
: m_pEngine(&engine)
, m_Allocators(std::make_unique<WorldAllocators>(engine))
, m_ClassContents(std::make_unique<ClassContents>(*this))
, m_MusicZoneManager(std::make_unique<Engine::MusicZoneManager>(*this))
{
Logic::MusicController::resetDefaults();
Logic::MusicController::disableDebugDraw();
}

WorldInstance::~WorldInstance()
Expand Down Expand Up @@ -376,6 +378,7 @@ bool WorldInstance::init(const std::string& zen,

VobTypes::MusicVobInformation mus = VobTypes::asMusicVob(*this, e);
mus.musicController->initFromVobDescriptor(v);
m_MusicZoneManager->addZone(v);

/* Sets an increased factor to allow detection of very large
music zones. For example, Khorinis's zone would be disabled
Expand All @@ -391,7 +394,7 @@ bool WorldInstance::init(const std::string& zen,
else if (v.objectClass == "oCZoneMusicDefault:oCZoneMusic:zCVob")
{
std::string zoneName = v.vobName.substr(v.vobName.find('_') + 1);
Logic::MusicController::setDefaultZone(zoneName);
m_MusicZoneManager->setDefaultZone(zoneName);

LogInfo() << "Found default music zone: " << v.vobName;
}
Expand Down Expand Up @@ -746,10 +749,7 @@ void WorldInstance::onFrameUpdate(double deltaTime, float updateRangeSquared, co
ddDrawAxis(fpPosition.x, fpPosition.y, fpPosition.z, 0.5f);
}*/

if (!Logic::MusicController::isMusicPlaying())
{
Logic::MusicController::playDefaultMusic(*this);
}
m_MusicZoneManager->onUpdate();
}

void WorldInstance::removeEntity(Handle::EntityHandle h)
Expand Down
2 changes: 2 additions & 0 deletions src/engine/World.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ namespace ZenLoad
namespace Engine
{
class BaseEngine;
class MusicZoneManager;
}

namespace Physics
Expand Down Expand Up @@ -411,5 +412,6 @@ namespace World
*/
std::unique_ptr<WorldAllocators> m_Allocators;
std::unique_ptr<ClassContents> m_ClassContents;
std::unique_ptr<Engine::MusicZoneManager> m_MusicZoneManager;
};
}
93 changes: 1 addition & 92 deletions src/logic/MusicController.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,26 +11,11 @@

using namespace Logic;

const std::array<const std::string, 6> MusicController::m_instanceSuffixes =
{
"_DAY_STD",
"_DAY_THR",
"_DAY_FGT",
"_NGT_STD",
"_NGT_THR",
"_NGT_FGT",
};

bool MusicController::m_debugDraw = false;
bool MusicController::m_playingMusic = false;

std::string MusicController::m_defaultZone = "DEF";

MusicController::MusicController(World::WorldInstance& world, Handle::EntityHandle entity)
: Controller(world, entity)
, m_isPlaying(false)
{
}
{ }

void MusicController::importObject(const json& j)
{
Expand All @@ -50,31 +35,6 @@ void MusicController::initFromVobDescriptor(const ZenLoad::zCVobData& vob)
// so we need to scale it by a factor of 100
m_bbox[0] = vob.bbox[0] * 0.01;
m_bbox[1] = vob.bbox[1] * 0.01;

m_zoneName = vob.vobName;
m_instancePrefix = Utils::uppered(vob.vobName.substr(vob.vobName.find('_') + 1));

LogInfo() << "Music: controller created for " << m_zoneName;
}

void MusicController::playZone(World::WorldInstance& world, const std::string& prefix, EMusicTime time, EMusicType type)
{
auto& audioWorld = world.getEngine()->getAudioWorld();
if (!audioWorld.playMusicTheme(prefix + m_instanceSuffixes[time + type]))
{
if (!audioWorld.playMusicTheme(prefix + m_instanceSuffixes[MT_Day + type]))
{
m_playingMusic = audioWorld.playMusicTheme(prefix + m_instanceSuffixes[MT_Day + MT_Std]) || m_playingMusic;
}
else
{
m_playingMusic = true;
}
}
else
{
m_playingMusic = true;
}
}

void MusicController::onUpdate(float deltaTime)
Expand All @@ -87,55 +47,4 @@ void MusicController::onUpdate(float deltaTime)
ddDraw(box);
ddPop();
}

EMusicTime time = m_World.getEngine()->getGameClock().isDaytime() ? MT_Day : MT_Ngt;
const auto& audioWorld = m_World.getEngine()->getAudioWorld();
const auto& currentTheme = audioWorld.currentMusicTheme();

// We need to play a new segment in either one of two scenarios:
// if the character has just entered the zone or
// if the character was already in the zone but the time of day
// changed.
// FIXME: It'll also be needed to check if the character is
// threatened or fighting to play the correct music.
if (((!m_isPlaying || currentTheme.find(m_instancePrefix) != 0) && isInBoundingBox()) || (m_isPlaying && m_currentTime != time))
{
m_isPlaying = true;

playZone(m_World, m_instancePrefix, time, MT_Std);
m_currentTime = time;

LogInfo() << "Music: entering " << m_zoneName;
}
else if (m_isPlaying && !isInBoundingBox())
{
m_isPlaying = false;

playZone(m_World, m_defaultZone, time, MT_Std);
m_currentTime = time;

LogInfo() << "Music: exiting " << m_zoneName;
}
}

bool MusicController::isInBoundingBox()
{
// Get player's position
VobTypes::NpcVobInformation player = VobTypes::asNpcVob(m_World, m_World.getScriptEngine().getPlayerEntity());
Math::float3 pos{};

if (player.isValid() && !player.playerController->getUsedMob().isValid())
{
pos = player.position->m_WorldMatrix.Translation();
}

return (pos.x >= m_bbox[0].x && pos.x < m_bbox[1].x &&
pos.y >= m_bbox[0].y && pos.y < m_bbox[1].y &&
pos.z >= m_bbox[0].z && pos.z < m_bbox[1].z);
}

void MusicController::playDefaultMusic(World::WorldInstance& world)
{
EMusicTime time = world.getEngine()->getGameClock().isDaytime() ? MT_Day : MT_Ngt;
playZone(world, m_defaultZone, time, MT_Std);
}
50 changes: 5 additions & 45 deletions src/logic/MusicController.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@
#include <daedalus/DaedalusGameState.h>

namespace Logic {
/**
* @brief If enabled, draws a box around a music zone
*
*/
class MusicController : public Controller {
public:
/**
Expand Down Expand Up @@ -38,57 +42,13 @@ namespace Logic {
static void toggleDebugDraw() { m_debugDraw = !m_debugDraw; }
static void disableDebugDraw() { m_debugDraw = false; }

static void setDefaultZone(const std::string& name) { m_defaultZone = name; }
static const std::string& getDefaultZone() { return m_defaultZone; }
static void playDefaultMusic(World::WorldInstance& world);
static bool isMusicPlaying() { return m_playingMusic; }

static void resetDefaults()
{
m_playingMusic = false;
m_defaultZone = "DEF";
}

protected:
/**
* @return True, if the current camera is in the bounding box of the zone
*/
bool isInBoundingBox();

void exportPart(json& j) override;

private:

std::array<ZMath::float3, 2> m_bbox;

bool m_isPlaying;

std::string m_zoneName, m_instancePrefix;

enum EMusicTime {
MT_Day = 0,
MT_Ngt = 3,
};

enum EMusicType {
MT_Std = 0,
MT_Thr = 1,
MT_Fgt = 2,
};

/**
* Tries to play the segment associated with the specified prefix according to
* the current time of day and character condition.
* Falls back to day and standard variants if the requested ones are not found
*/
static void playZone(World::WorldInstance& world, const std::string& prefix, EMusicTime time, EMusicType type);

EMusicTime m_currentTime;

static const std::array<const std::string, 6> m_instanceSuffixes;

static bool m_debugDraw, m_playingMusic;

static std::string m_defaultZone;
static bool m_debugDraw;
};
}

0 comments on commit f0691c0

Please sign in to comment.