mirror of
https://github.com/kevinbentley/Descent3.git
synced 2025-01-22 19:55:23 +00:00
505 lines
13 KiB
C++
505 lines
13 KiB
C++
#include "d3music.h"
|
|
#include "music.h"
|
|
#include "Macros.h"
|
|
#include <limits>
|
|
|
|
#ifdef min
|
|
# undef min
|
|
#endif
|
|
|
|
#ifdef max
|
|
# undef max
|
|
#endif
|
|
|
|
// generic constants
|
|
#define MUSIC_IDLE_TIME 240.0f // how many seconds before background music silences.
|
|
|
|
// flags for musicAI
|
|
#define MUSICAIF_PLAYERDEAD 0x1
|
|
#define MUSICAIF_PLAYERINVUL 0x2
|
|
#define MUSICAIF_STARTLEVEL 0x4 // game started a new level
|
|
#define MUSICAIF_HOSTILES 0x8 // there are hostiles around
|
|
#define MUSICAIF_NEWREGION 0x40
|
|
|
|
// names of themes. (match to music.h THEME_TYPES)
|
|
const char *Music_type_names[] =
|
|
{
|
|
"No song",
|
|
"Intro song",
|
|
"Idle song",
|
|
"Combat song",
|
|
"Death song",
|
|
"Idle-Combat song",
|
|
"Combat-Idle song"
|
|
};
|
|
|
|
// global data
|
|
#ifdef _DEBUG
|
|
bool Music_debug_verbose = false; // debug flag for music system
|
|
#endif
|
|
|
|
// in library data.
|
|
static OutrageMusicSeq Music_seq;
|
|
static bool Music_on = false;
|
|
static bool Allow_music = false;
|
|
static float Music_volume = 1.0f;
|
|
|
|
// music ai structure.
|
|
static struct
|
|
{
|
|
float peace_timer; // how long has player been out of combat.
|
|
int flags; // current state of music AI.
|
|
int cur_song; // current song
|
|
int n_hostiles; // number of hostiles ai thinks there are.
|
|
tMusicVal trigger;
|
|
short pending_region; // pending region change.
|
|
bool was_toggled_on; // music was turned on by user.
|
|
bool immediate_switch; // force switch immediately for regions
|
|
bool restart_sequencer; // restarts sequencer next frame.
|
|
}MusicAI;
|
|
|
|
|
|
// handles operations on current events.
|
|
void D3MusicSongSelector();
|
|
|
|
// creates music ai struction from music info passed in.
|
|
void D3MusicAI(tMusicSeqInfo *music_info);
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
void InitD3Music(bool allow_music)
|
|
{
|
|
Music_on = false;
|
|
Allow_music = allow_music;
|
|
}
|
|
|
|
|
|
void CloseD3Music()
|
|
{
|
|
Music_on = false;
|
|
}
|
|
|
|
|
|
// is music system on?
|
|
bool IsD3MusicOn()
|
|
{
|
|
return Music_on;
|
|
}
|
|
|
|
|
|
// starts up the music sequencer
|
|
void D3MusicStart(const char *theme_file)
|
|
{
|
|
if (theme_file && Music_seq.Init(theme_file)) {
|
|
Music_on = true;
|
|
Music_seq.SetVolume(Music_volume);
|
|
}
|
|
else {
|
|
Music_on = false;
|
|
}
|
|
|
|
if (!Allow_music) {
|
|
Music_on = false;
|
|
}
|
|
|
|
if (Music_on)
|
|
{
|
|
if (Music_volume > 0.0f)
|
|
{
|
|
Music_seq.Start();
|
|
}
|
|
else
|
|
{
|
|
Music_on = false; // volume was 0.0f, so don't mark music as on since we don't start
|
|
}
|
|
}
|
|
|
|
// reset AI state.
|
|
MusicAI.cur_song = OMS_THEME_TYPE_NONE;
|
|
//@@ MusicAI.mood_default = MUSIC_MOOD_DEFAULT;
|
|
//@@ MusicAI.mood = MUSIC_MOOD_DEFAULT;
|
|
MusicAI.flags = 0;
|
|
MusicAI.n_hostiles = 0;
|
|
MusicAI.was_toggled_on = false;
|
|
MusicAI.pending_region = -1;
|
|
MusicAI.restart_sequencer = false;
|
|
MusicAI.immediate_switch = false;
|
|
D3MusicSetRegion(0);
|
|
|
|
// reset AI timers
|
|
MusicAI.peace_timer = 0.0f;
|
|
}
|
|
|
|
|
|
// stops the music sequencer
|
|
void D3MusicStop()
|
|
{
|
|
Music_seq.Stop();
|
|
Music_seq.Shutdown();
|
|
Music_on = false;
|
|
}
|
|
|
|
|
|
// execute music sequencer.
|
|
void D3MusicDoFrame(tMusicSeqInfo *music_info)
|
|
{
|
|
// update music ai with new music info
|
|
if (!Music_on)
|
|
return;
|
|
|
|
D3MusicAI(music_info);
|
|
|
|
// handle current events.
|
|
D3MusicSongSelector();
|
|
|
|
// run frame.
|
|
Music_seq.Frame(music_info->frametime);
|
|
|
|
// end
|
|
music_info->cur_loop_name = Music_seq.GetCurrentLoopName(&music_info->cur_loop_count);
|
|
music_info->cur_song = MusicAI.cur_song;
|
|
}
|
|
|
|
|
|
// toggle music system.
|
|
void D3MusicToggle()
|
|
{
|
|
Music_on = (!Music_on && Allow_music && Music_volume > 0.0f) ? true : false;
|
|
if (Music_on)
|
|
{
|
|
Music_seq.Start();
|
|
MusicAI.was_toggled_on = true;
|
|
}
|
|
else
|
|
{
|
|
Music_seq.Stop();
|
|
MusicAI.was_toggled_on = false;
|
|
}
|
|
}
|
|
|
|
|
|
// toggle music system.
|
|
void D3MusicToggle(bool state)
|
|
{
|
|
if (state && !Music_on && Allow_music && Music_volume > 0.0f)
|
|
{
|
|
Music_seq.Start();
|
|
MusicAI.was_toggled_on = true;
|
|
Music_on = true;
|
|
}
|
|
else if (!state && Music_on) {
|
|
Music_seq.Stop();
|
|
MusicAI.was_toggled_on = false;
|
|
Music_on = false;
|
|
}
|
|
}
|
|
|
|
|
|
// pauses and or resumes music
|
|
void D3MusicPause()
|
|
{
|
|
Music_seq.Pause();
|
|
}
|
|
|
|
|
|
void D3MusicResume()
|
|
{
|
|
Music_seq.Resume();
|
|
}
|
|
|
|
|
|
// volume stuff
|
|
float D3MusicGetVolume()
|
|
{
|
|
return Music_volume;
|
|
}
|
|
|
|
|
|
void D3MusicSetVolume(float vol)
|
|
{
|
|
const float kEpsilon = std::numeric_limits<float>::min();
|
|
if( vol <= kEpsilon )
|
|
{
|
|
Music_seq.Stop();
|
|
MusicAI.was_toggled_on = false;
|
|
Music_on = false;
|
|
}
|
|
else if( Music_volume <= kEpsilon )
|
|
{
|
|
Music_on = true;
|
|
MusicAI.was_toggled_on = true;
|
|
if (MusicAI.pending_region == -1)
|
|
{
|
|
MusicAI.pending_region = D3MusicGetRegion();
|
|
}
|
|
Music_seq.Start();
|
|
}
|
|
Music_seq.SetVolume(vol);
|
|
Music_volume = vol;
|
|
}
|
|
|
|
|
|
// New Interface
|
|
// Sequencing within level.
|
|
// play intro track.
|
|
// play main background music.
|
|
// occasionally switch to other ambient music options.
|
|
// when hostility towards player is upped, play 'hostile' music.
|
|
// when player enters combat (player takes damage from hostile), switch to combat music.
|
|
// fade out background music after 2-3 minutes if no change in state.
|
|
//
|
|
|
|
// creates music ai struction from music info passed in.
|
|
void D3MusicAI(tMusicSeqInfo *music_info)
|
|
{
|
|
// determine flags state.
|
|
if (music_info->player_dead) {
|
|
MusicAI.flags |= MUSICAIF_PLAYERDEAD;
|
|
}
|
|
else {
|
|
MusicAI.flags &= (~MUSICAIF_PLAYERDEAD);
|
|
}
|
|
|
|
if (music_info->started_level) {
|
|
MusicAI.flags |= MUSICAIF_STARTLEVEL;
|
|
MusicAI.peace_timer = 0.0f;
|
|
}
|
|
else {
|
|
MusicAI.flags &= (~MUSICAIF_STARTLEVEL);
|
|
}
|
|
|
|
if (music_info->n_hostiles && !(MusicAI.flags & MUSICAIF_HOSTILES)) {
|
|
MusicAI.flags |= MUSICAIF_HOSTILES;
|
|
MusicAI.peace_timer = 0.0f;
|
|
}
|
|
else if (!music_info->n_hostiles && (MusicAI.flags & MUSICAIF_HOSTILES)) {
|
|
// make sure hostiles flags is cleared once. when no hostiles, but hostiles flag is on.
|
|
MusicAI.flags &= (~MUSICAIF_HOSTILES);
|
|
MusicAI.peace_timer = 0.0f;
|
|
//@@ MusicAI.mood_timer = 0.0f;
|
|
}
|
|
|
|
//@@// set mood timers
|
|
//@@ if (!CHECK_FLAG(MusicAI.flags, MUSICAIF_NEGMOODTIMER) && MusicAI.mood < 0) {
|
|
//@@ MusicAI.flags |= MUSICAIF_NEGMOODTIMER;
|
|
//@@ MusicAI.negmood_timer = 0.0f;
|
|
//@@ }
|
|
//@@ if (CHECK_FLAG(MusicAI.flags, MUSICAIF_NEGMOODTIMER) && MusicAI.mood >=0) {
|
|
//@@ MusicAI.flags &= (~MUSICAIF_NEGMOODTIMER);
|
|
//@@ MusicAI.negmood_timer = 0.0f;
|
|
//@@ }
|
|
//@@ if (!CHECK_FLAG(MusicAI.flags, MUSICAIF_POSMOODTIMER) && MusicAI.mood > 0) {
|
|
//@@ MusicAI.flags |= MUSICAIF_POSMOODTIMER;
|
|
//@@ MusicAI.posmood_timer = 0.0f;
|
|
//@@ }
|
|
//@@ if (CHECK_FLAG(MusicAI.flags, MUSICAIF_POSMOODTIMER) && MusicAI.mood <=0) {
|
|
//@@ MusicAI.flags &= (~MUSICAIF_POSMOODTIMER);
|
|
//@@ MusicAI.posmood_timer = 0.0f;
|
|
//@@ }
|
|
|
|
//@@// determine mood of music
|
|
//@@ float shield_scalar = (float)music_info->player_shield_level/MUSIC_PLR_SHIELD_LVLS;
|
|
//@@ if (shield_scalar > 1.0f) shield_scalar = 1.0f;
|
|
|
|
//@@// being damaged saddens mood
|
|
//@@ if (music_info->player_damaged) {
|
|
//@@ MusicAI.mood -= ((1.0f-shield_scalar)*MUSIC_MOOD_DAMAGE_MOD);
|
|
//@@ }
|
|
|
|
//@@// killing someone HOSTILE raises one's mood.
|
|
//@@ MusicAI.mood += (music_info->n_hostiles_player_killed*MUSIC_MOOD_KILLS_MOD);
|
|
|
|
//@@// a reduction in hostiles will also modify mood positively, and more hostiles has the opposite effect.
|
|
//@@ if ((music_info->n_hostiles-MusicAI.n_hostiles)) {
|
|
//@@ MusicAI.mood -= ((1.5f-shield_scalar)*((music_info->n_hostiles-MusicAI.n_hostiles)*MUSIC_MOOD_HOSTILES_MOD));
|
|
//@@ }
|
|
|
|
//@@// if no hostiles and mood != mood_default, then modify mood until it equals 0 again. do it every second.
|
|
//@@ if (MusicAI.mood != MusicAI.mood_default) {
|
|
//@@ short mod = (MusicAI.flags & MUSICAIF_HOSTILES) ? 1 : 2;
|
|
//@@ float time_mod = (MusicAI.flags & MUSICAIF_HOSTILES) ? 0.2f : 0.2f;
|
|
//@@ if (MusicAI.mood_timer >= time_mod) {
|
|
//@@ if (MusicAI.mood > MusicAI.mood_default) {
|
|
//@@ MusicAI.mood -= mod;
|
|
//@@ if (MusicAI.mood < MusicAI.mood_default) MusicAI.mood = MusicAI.mood_default;
|
|
//@@ }
|
|
//@@ if (MusicAI.mood < MusicAI.mood_default) {
|
|
//@@ MusicAI.mood += mod;
|
|
//@@ if (MusicAI.mood > MusicAI.mood_default) MusicAI.mood = MusicAI.mood_default;
|
|
//@@ }
|
|
//@@ MusicAI.mood_timer = 0.0f;
|
|
//@@ }
|
|
//@@ MusicAI.mood_timer += music_info->frametime;
|
|
//@@ }
|
|
//@@
|
|
//@@ if (CHECK_FLAG(MusicAI.flags, MUSICAIF_NEGMOODTIMER)) {
|
|
//@@ MusicAI.negmood_timer += music_info->frametime;
|
|
//@@ }
|
|
//@@ if (CHECK_FLAG(MusicAI.flags, MUSICAIF_POSMOODTIMER)) {
|
|
//@@ MusicAI.posmood_timer += music_info->frametime;
|
|
//@@ }
|
|
//@@
|
|
// other stuff
|
|
MusicAI.n_hostiles = music_info->n_hostiles;
|
|
|
|
if (!MusicAI.n_hostiles) { // how long have we been without hostiles
|
|
MusicAI.peace_timer += music_info->frametime;
|
|
}
|
|
|
|
//@@ if (MusicAI.flags & MUSICAIF_PLAYERDEAD) { // if dead, mood is always set to default.
|
|
//@@ MusicAI.mood = MusicAI.mood_default;
|
|
//@@ }
|
|
|
|
// output
|
|
//@@ Music_seq.SetRegister(MUSICREG_MOOD_VALUE, MusicAI.mood);
|
|
//@@ Music_seq.SetRegister(MUSICREG_POSMOOD_TIMER, MusicAI.posmood_timer);
|
|
//@@ Music_seq.SetRegister(MUSICREG_NEGMOOD_TIMER, MusicAI.negmood_timer);
|
|
Music_seq.SetRegister(MUSICREG_PEACE_TIMER, MusicAI.peace_timer);
|
|
Music_seq.SetRegister(MUSICREG_TRIGGER_VALUE, MusicAI.trigger);
|
|
|
|
music_info->peace_timer = MusicAI.peace_timer;
|
|
//@@ music_info->mood = MusicAI.mood;
|
|
//@@ music_info->nmood_timer = MusicAI.negmood_timer;
|
|
//@@ music_info->pmood_timer = MusicAI.posmood_timer;
|
|
|
|
if (MusicAI.trigger > 0) {
|
|
MusicAI.trigger = MUSICTRIGGER_NONE;
|
|
}
|
|
}
|
|
|
|
|
|
// handles operations on current events.
|
|
void D3MusicSongSelector()
|
|
{
|
|
oms_q_evt evt;
|
|
|
|
if ((MusicAI.cur_song != OMS_THEME_TYPE_DEATH) && (MusicAI.flags & MUSICAIF_PLAYERDEAD)) {
|
|
MusicAI.cur_song = OMS_THEME_TYPE_DEATH;
|
|
Music_seq.StartSong(MusicAI.cur_song, false);
|
|
}
|
|
|
|
if (MusicAI.was_toggled_on) {
|
|
if (MusicAI.pending_region > -1) {
|
|
Music_seq.SetCurrentRegion(MusicAI.pending_region);
|
|
MusicAI.pending_region = -1;
|
|
}
|
|
MusicAI.cur_song = OMS_THEME_TYPE_IDLE;
|
|
MusicAI.peace_timer = 0.0f;
|
|
Music_seq.StartSong(MusicAI.cur_song, false);
|
|
MusicAI.was_toggled_on = false;
|
|
}
|
|
|
|
// intro music will always play at start of level.
|
|
if (MusicAI.flags & MUSICAIF_STARTLEVEL) {
|
|
MusicAI.cur_song = OMS_THEME_TYPE_IDLE;
|
|
Music_seq.SetCurrentRegion(0);
|
|
Music_seq.StartSong(MusicAI.cur_song, false);
|
|
}
|
|
if (MusicAI.pending_region > -1 && MusicAI.immediate_switch) {
|
|
MusicAI.cur_song = OMS_THEME_TYPE_IDLE;
|
|
Music_seq.SetCurrentRegion(MusicAI.pending_region);
|
|
Music_seq.StartSong(MusicAI.cur_song, false);
|
|
MusicAI.pending_region = -1;
|
|
MusicAI.immediate_switch = false;
|
|
}
|
|
|
|
// process events and songs.
|
|
do
|
|
{
|
|
if (!Music_seq.m_output_q.recv(&evt)) {
|
|
evt.cmd = OMS_EVT_NONE;
|
|
}
|
|
|
|
switch (MusicAI.cur_song)
|
|
{
|
|
case OMS_THEME_TYPE_NONE:
|
|
if (MusicAI.pending_region > -1) {
|
|
mprintf((0, "D3MUSIC: new region request processed.\n"));
|
|
MusicAI.cur_song = OMS_THEME_TYPE_IDLE;
|
|
Music_seq.SetCurrentRegion(MusicAI.pending_region);
|
|
Music_seq.StartSong(MusicAI.cur_song, true);
|
|
MusicAI.pending_region = -1;
|
|
}
|
|
break;
|
|
|
|
case OMS_THEME_TYPE_IDLE: // IDLE MUSIC CURRENTLY PLAYING
|
|
if (evt.cmd == OMS_EVT_SONGENDED) {
|
|
MusicAI.cur_song = OMS_THEME_TYPE_NONE;
|
|
// if (Music_seq.GetCurrentRegion() == 0) {
|
|
// mprintf((0, "Ending Region 0.\n"));
|
|
// MusicAI.cur_song = OMS_THEME_TYPE_IDLE;
|
|
// Music_seq.SetCurrentRegion(1);
|
|
// Music_seq.StartSong(MusicAI.cur_song, true);
|
|
// }
|
|
// else {
|
|
// mprintf((0, "D3MUSIC: Song ended normally?\n"));
|
|
// }
|
|
}
|
|
else if (evt.cmd == OMS_EVT_LOOPENDING) {
|
|
// shall we switch regions?
|
|
if (MusicAI.pending_region > -1) {
|
|
mprintf((0, "D3MUSIC: new region request processed.\n"));
|
|
MusicAI.cur_song = OMS_THEME_TYPE_IDLE;
|
|
Music_seq.SetCurrentRegion(MusicAI.pending_region);
|
|
Music_seq.StartSong(MusicAI.cur_song, true);
|
|
MusicAI.pending_region = -1;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case OMS_THEME_TYPE_DEATH:
|
|
if (!(MusicAI.flags & MUSICAIF_PLAYERDEAD)) {
|
|
MusicAI.cur_song = OMS_THEME_TYPE_IDLE;
|
|
MusicAI.peace_timer = 0.0f;
|
|
Music_seq.StartSong(MusicAI.cur_song, false);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
Int3();
|
|
}
|
|
}
|
|
while (evt.cmd != OMS_EVT_NONE);
|
|
}
|
|
|
|
|
|
// set music region
|
|
void D3MusicSetRegion(short region,bool immediate)
|
|
{
|
|
if (Music_seq.GetCurrentRegion() != region) {
|
|
MusicAI.immediate_switch = immediate;
|
|
MusicAI.pending_region = region;
|
|
//@@ if (MusicAI.immediate_switch) {
|
|
//@@ // at next frame, start again!
|
|
//@@ Music_seq.Pause();
|
|
//@@ MusicAI.restart_sequencer = true;
|
|
//@@ }
|
|
}
|
|
}
|
|
|
|
|
|
// retreives current region
|
|
short D3MusicGetRegion()
|
|
{
|
|
return Music_seq.GetPlayingRegion();
|
|
}
|
|
|
|
|
|
// retreives current PLAYING region.
|
|
short D3MusicGetPendingRegion()
|
|
{
|
|
return MusicAI.pending_region;
|
|
}
|
|
|
|
|
|
// starts special in-game cinematic music
|
|
void D3MusicStartCinematic()
|
|
{
|
|
}
|
|
|
|
// stops special in-game cinematic music
|
|
void D3MusicStopCinematic()
|
|
{
|
|
}
|