dhewm3-sdk/d3cclib/CCBst.cpp
2021-07-05 23:53:24 +02:00

1528 lines
43 KiB
C++

/*
===========================================================================
DOOM3[CC] Source Code
Copyright (C) 2004-2005 the DOOM3[CC] team
Copyright (C) 2004-2005 Reid Kimball, Jason Sadler, Kevin Maulding and Matt Sefton
Copyright (C) 2020 Reid Kimball
Copyright (C) 2020-2021 Daniel Gibson
This file is part of the DOOM3[CC] GPL Source Code ("DOOM3[CC]
Source Code").
DOOM3[CC] Source Code is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
DOOM3[CC] Source Code is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with DOOM3[CC] Source Code. If not, see <http://www.gnu.org/licenses/>.
===========================================================================
*/
//#define GAME_DLL
//#include "../src/idlib/precompiled.h"
//#pragma hdrstop
// DG: dhewm3 (and its SDK) don't have precompiled.h, so I added all needed includes here
#include "../sys/platform.h"
#include "../idlib/Lib.h"
#include "../idlib/Str.h"
#include "../framework/CVarSystem.h"
#include "../framework/FileSystem.h"
#ifdef _D3XP
#include "../d3xp/Game_local.h"
#include "../d3xp/Player.h"
#else
#include "../game/Game_local.h"
#include "../game/Player.h"
#endif
idCVar cc_dialogue( "cc_dialogue", "1", CVAR_BOOL|CVAR_SYSTEM|CVAR_NOCHEAT|CVAR_ARCHIVE, "Captions all dialogue" );
idCVar cc_environment( "cc_environment", "1", CVAR_BOOL|CVAR_SYSTEM|CVAR_NOCHEAT|CVAR_ARCHIVE, "Captions all sound effects" );
//idCVar cc_languageSelector("cc_language", "0", CVAR_INTEGER|CVAR_SYSTEM, "Chooses language" );
idCVar cc_languageSelector( "cc_language", "0", CVAR_INTEGER|CVAR_SYSTEM|CVAR_NOCHEAT|CVAR_ARCHIVE, "Chooses the language!" );
/*cc_Language* languages = new cc_Language[3];
languages[0].index = 0;
languages[0].language = "English";*/
//cc_LanguageStrings = {new idStr("English"), new idStr("Espanol"), new idStr("Deutsch")};
#include "CCBst.h"
// DG: this helper function is only used in this file anyway, so no need to add it to idStr
static int FindCharReverse(const idStr& str, const char c, int start)
{
int x;
int len = strlen(str) - 1; // TODO: why not str.Length() ?
if(len >= start)
{
for( x = start; x >= 0; x--)
{
if( str[x] == c)
return x;
}
return -1;
}
else
return -1;
}
ccBst::ccBst()
{
Tree = NULL;
ccColorInfo.Reset();
CInit = false;
output[0].Data.Reset();
output[1].Data.Reset();
output[2].Data.Reset();
output[0].Data.Clear();
output[1].Data.Clear();
output[2].Data.Clear();
//Jason added 12/18/04
// DG: can't use the cvar here, it might not be initialized yet
// (it's a global and so is gameLocal which contains ccBst ccDisplayInfo
// and if gameLocal is initialized first, like it's right now on my system,
// it calls this constructor even though the CVars above haven't been initialized yet)
// so this is now done in InitOnce(), called from GameLocal::Init()
//language = cc_languageSelector.GetInteger();
//gameLocal.Printf("CC INSTANTIATED!");
// DG: the following were uninitialized; now my IDE is happy ;)
language = 0;
languages = NULL;
numLanguages = 0;
languagesLoaded = 0;
}
ccBst::~ccBst()
{
delete Tree;
}
void ccBst::InitOnce()
{
language = cc_languageSelector.GetInteger();
initLanguages();
}
// FIXME: DG: apparently initLanguages() is never called? what's this good for?
void ccBst::initLanguages()
{
char* buf = NULL; // For initing languages, scope is only inside the constructor. Added Jason 04/02/05
idStr currentLanguage = "";
bool comment = false; // DG: make sure to init this..
numLanguages = 0;
languagesLoaded = false;
idFile *pFile = idLib::fileSystem->OpenFileRead("caption/languages.dcc");
if (pFile)
{
int length = pFile->Length();
buf = new char[length+1]; // DG: +1 for terminating '\0' set in the next line
buf[length] = '\0'; // allocate enough memory to read the whole file
pFile->Read( buf, length ); // read the file
bool read = false;
for(int x = 0; x < length; x++)
{
if(buf[x] == '/' && buf[x + 1] == '/')
{
x++;
if(comment)
{
comment = false;
}
else
comment = true;
}
else if(comment)
{
if(buf[x] == '\n')
{
comment = false;
}
}
else if(read && buf[x] != '"')
{
currentLanguage += buf[x];
}
else if(buf[x] == '"')
{
if (!read)
{
read = true;
}
else
{
read = false;
numLanguages++;
idStr *temp = new idStr[numLanguages];
for (int i = 0; i < numLanguages - 1; i++)
{
temp[i] = languages[i];
}
temp[numLanguages - 1] = currentLanguage;
languages = temp;
//gameLocal.Printf(languages[numLanguages - 1] + "\n");
currentLanguage = "";
}
}
}
languagesLoaded = true;
delete[] buf;
}
else
{
gameLocal.Printf("\nError: languages.dcc missing!\n");
}
/*for(int x = 0; x < numLanguages; x++)
{
gameLocal.Printf(idStr(x) + " " + languages[x] + "\n");
}*/
}
//added November 20, 2004
//resets the Root of the BST
void ccBst::Reset()
{
Tree = NULL; // TODO: DG: remove elements?
output[0].Data.Reset();
output[1].Data.Reset();
output[2].Data.Reset();
output[0].Data.Clear();
output[1].Data.Clear();
output[2].Data.Clear();
}
void ccBst::Init(idStr mapName, bool MFS)
{
//Jason added this 12/18/04 for language support
if (!MFS) //This works since only the outer file (which is the actual map) is called without MFS
currentMap = mapName;
if (!checkLanguage())
{
idStr languagePath = getLanguageName(language);
bool flag = false;
int period;
int slash;
int length;
char *buf;
if(!MFS)//added in for multifolder support
{
period = mapName.Find('.', 0, -1);
//Find( const char c, int start = 0, int end = -1 )
slash = FindCharReverse(mapName, '/', period);
gameLocal.Printf("\n" + idStr(slash) + "\n");
mapName = "caption/" + languagePath + "/" + mapName.Mid(slash + 1, period - slash) + "dcc"; //added "+languagePath" 12/18/04
gameLocal.Printf("\nccBst::Init() inside MFS conditional...path for DCCs: " + mapName);
//THIS MEANS THE DCC FILES MUST BE IN THE caption/english folder (or spanish or...)
}
else //Added by Jason for languages - 12/18/04
{
/*period = mapName.Find('.', 0, -1);
slash = FindCharReverse(mapName, '/', period);
mapName = "caption/" + languagePath + "/" + mapName.Mid(slash +1, period - slash) + "dcc"; //added "+languagePath" 12/18/04*/
gameLocal.Printf("\nccBst::Init() MFS is true, path for DCCs: " + mapName);
}
idFile *f = idLib::fileSystem->OpenFileRead(mapName);
if ( f )
{
length = f->Length();
buf = (char *) Mem_Alloc( length + 1 );
buf[length] = '\0';//allocate enough memory to read the whole file
f->Read( buf, length );//read the file
bool quote = false;
bool inside = false;
// DG: made sure the next 3 variables are initialized (compiler was complaining)
bool first = false; //used to keep track of first and second digits in timecode
int code = 0; //used to keep track between minutes, seconds and milliseconds
int tm = 0; //used to add up the time
int lenscroll = 0;
int y = 0;
idStr temp = "";
bool comment = false;
if(mapName.FindText(mapName, "colors", false) >= 0)
{
//ADDED 1-30-05
//a specail reader for the color file could not get it to load while in another function
ccColor tempcolor;
tempcolor.Reset();
for(int x = 0; x < length;x++)
{
if(buf[x] == '/' && buf[x + 1] == '/')
{
x++;
if(comment)
{
comment = false;
}
else
comment = true;
}
else if(comment)
{
if(buf[x] == '\n')
{
comment = false;
}
}
else if(buf[x] <= ' ' || buf[x] == '\n')
{
if(inside == false && temp != "")
{
if(temp == "None" || temp == "none" || temp == "NONE")
{
ccColorInfo.Default = true;
return;
}
tempcolor.WordToString(temp);
//gameLocal.Printf("Color: " + temp + "\n");
temp = "";
}
else if(inside == true && temp!= "")
{
tempcolor.Name[tempcolor.size - 1] = temp;
tempcolor.Resize(tempcolor.size+1);
//gameLocal.Printf("Name: " + temp + "\n");
temp = "";
}
}
else if(buf[x] == '{')
{
inside = true;
}
else if(buf[x] == '}')
{
inside = false;
//tempcolor.Check();
ccColorInfo.Colors[ccColorInfo.size - 1] = tempcolor;
ccColorInfo.Resize(ccColorInfo.size + 1);
//gameLocal.Printf("Added \n\n");
tempcolor.Reset();
}
else
{
temp+= buf[x];
}
}
CInit = true;
}
else
{//regular dcc file loader
ccParamaters ntemp;
ntemp.empty = false;
ntemp.Reset();
ntemp.Clear();
for(int x = 0; x < length;x++)
{
if(buf[x] == '/' && buf[x + 1] == '/')
{
x++;
if(comment)
{
comment = false;
}
else
comment = true;
}
else if(comment)
{
if(buf[x] == '\n')
{
comment = false;
}
}
else if(buf[x] == '\n')
{
if(ntemp.timecode && quote )
{ //if it is in the time code, resize the list for one more and go to the next one
if(lenscroll >= cc_SCROLLHALF && lenscroll < cc_SCROLLFULL)
{
ntemp.scroll[y] = 1;
//added to make sure the display() knows it scrolls somewhere
ntemp.needsscroll = true;
}
else if(lenscroll >= cc_SCROLLFULL && lenscroll < cc_SCROLLONEANDHALF)
{
ntemp.scroll[y] = 2;
ntemp.needsscroll = true;
}
else if(lenscroll >= cc_SCROLLONEANDHALF && lenscroll < cc_SCROLLTWO)
{
ntemp.scroll[y] = 3;
ntemp.needsscroll = true;
}
else if(lenscroll >= cc_SCROLLTWO)
{
ntemp.scroll[y] = 4;
ntemp.needsscroll = true;
}
else
ntemp.scroll[y] = 0;
ntemp.caption[y] = temp;
lenscroll = 0;
temp = "";
y++;
ntemp.Resize(y + 1);
//gameLocal.Printf("TCC: " + ntemp.caption[y-1] + "\n");
}
}
else if(buf[x] <= ' ' && !quote)
{
if(ntemp.soundName == "" && !inside)
{
if(temp != "")
{
ntemp.soundName = temp;
//gameLocal.Printf("S: " + ntemp.soundName + "\n");
temp = "";
ntemp.timecode = false;
}
}
if(ntemp.soundName != "" && !inside && temp == "timecode")
{
ntemp.timecode = true;
//gameLocal.Printf("Timecode == true \n");
temp = "";
}
}
else
{
if(buf[x] == '{')
{
inside = true;
}
else if(buf[x] == '}')
{
inside = false;
//gameLocal.Printf(ntemp.priority + " \n");
if(ntemp.priority != "load")//reset the required for the time code structure and add
{
quote = false;
bool status = true;
ntemp.empty = false;
while(status && ntemp.timecode)
{
status = ntemp.Check();
}
//make sure it is not true
Add(ntemp);
y = 0;
/*gameLocal.Printf(ntemp.soundName + "\n");
gameLocal.Printf(ntemp.priority + "\n");
gameLocal.Printf("TC : " + idStr(ntemp.timecode) + "\n");
for(int x = 0; x < ntemp.size; x++)
{
gameLocal.Printf(idStr(ntemp.time[x]) + "\n");
gameLocal.Printf(ntemp.caption[x] + "\n");
}
gameLocal.Printf("\n");*/
ntemp.Reset();
ntemp.Clear();
}
else
{
ntemp.Reset();
ntemp.Clear();
}
}
else if(buf[x] == '"')
{
if(!quote)
{
quote = true;
}
else
{
quote = false;
if(lenscroll > cc_SCROLLHALF && lenscroll < cc_SCROLLFULL)
{
ntemp.scroll[0] = 1;
//added to make sure the display() knows it scrolls somewhere
ntemp.needsscroll = true;
}
else if(lenscroll >= cc_SCROLLFULL && lenscroll < cc_SCROLLONEANDHALF)
{
ntemp.scroll[0] = 2;
ntemp.needsscroll = true;
}
else if(lenscroll >= cc_SCROLLONEANDHALF && lenscroll < cc_SCROLLTWO)
{
ntemp.scroll[0] = 3;
ntemp.needsscroll = true;
}
else if(lenscroll >= cc_SCROLLTWO)
{
ntemp.scroll[0] = 4;
ntemp.needsscroll = true;
}
else
{
ntemp.scroll[0] = 0;
ntemp.needsscroll = false;
}
lenscroll = 0;
ntemp.caption[0] = temp;
//gameLocal.Printf("C: " + ntemp.caption + "\n\n");
temp = "";
}
}
else if(!quote)
{
temp += buf[x];
if((buf[x+1] == '\n' || buf[x+1] <= ' ') && inside)
{
ntemp.priority = temp;
if(temp == "load")
{
temp = "";
Init("caption/" + getLanguageName(language) + "/" +ntemp.soundName + ".dcc", true);
//gameLocal.Printf("Loading File: /" + ntemp.soundName + ".dcc \n");
}
if(ntemp.timecode)
{
quote = true;
x++;
x++;//needed to make sure there is not an empty caption
}
//gameLocal.Printf("P: " + ntemp.priority + "\n");
temp = "";
}
}
else if(quote && buf[x] != '\n')
{
if(ntemp.timecode)
{
if(buf[x] == '[')
{
first = true;
code = 0;
}
else if(code < 3)
{
if(first)
{
tm = toInt(buf[x]) * 10;
first = false;
}
else
{
tm += toInt(buf[x]);
first = true;
if(code == 0)
{
ntemp.time[y] = tm * 60000;
x++;
}
else if(code == 1)
{
ntemp.time[y] += tm * 1000;
x++;
}
else if(code == 2)
{
ntemp.time[y] += tm;
//gameLocal.Printf("TCT: " + idStr(ntemp.time[y]) + "\n");
x++;
x++;
}
code++;
}
}
if(code == 3)
{
//am using this method to track the amount of characters in the string
//as other methods have failed to work one way or another
if(buf[x] == '*')
{
x++;
lenscroll++;
temp+= buf[x];
}
else
{
lenscroll++;
temp += buf[x];
}
}
}
else
{
if(buf[x] == '*')
{
x++;
lenscroll++;
temp+= buf[x];
}
else
{
lenscroll++;
temp += buf[x];
}
}
}
}
}
}
}
else
{
gameLocal.Printf("\nError: Could not Open file " + mapName + "\n");
}
}
}
bool ccBst::IsEmpty()
{
if(Tree == NULL)
return true;
else
return false;
}
void ccBst::Display(idStr soundName, int length, idVec3 ent_vec, float max, idStr entName)
{
//Check for language change
checkLanguage();
idPlayer *player = gameLocal.GetLocalPlayer();
if(player)
{
int period;
period = soundName.Find('.', 0, -1);
idStr soundName_Path = soundName.Mid(0, period);
int slash = FindCharReverse(soundName, '/', period);
soundName = soundName.Mid(slash+1, period - (slash + 1));
//gameLocal.Printf(soundName_Path + " " + soundName + "\n");
if((output[0].Data.soundName == soundName || output[0].Data.soundName == soundName_Path) && !output[0].Data.empty)
{//added another 5 seconds, should stop some hecticness of the gui
//a true timecode should never enter this conditional
//ADDED MINIMUM TIME OF 4 SECONDS TO THIS CONDITIONAL - 11/29/04 by JASON
if (gameLocal.GetTime() - output[0].time > 4000)
{
output[0].Data.ent_vec = ent_vec;
output[0].time = gameLocal.GetTime();
//TO PUT "CONTINUES" IN CAPS AND INSIDE SQUARE BRACKETS USE FOLLOWING CODE:
/*
if(output[0].Data.caption[0].Find("continues", false, 0, -1) < 0)
{
idStr tempCaption = output[0].Data.caption[0];
if (tempCaption.Last(')') > 0)
{
output[0].Data.caption[0].Insert(" CONTINUES", strlen(tempCaption) - 1);
}
else
output[0].Data.caption[0] += " continues";
player->hud->SetStateString("Hdialog", output[0].Data.caption[0]);
}*/
//INSTEAD OF THE FOLLOWING IF:
if(output[0].Data.caption[0].Find("continues", true, 0, -1) < 0)
{
output[0].Data.caption[0] += " continues";
player->hud->SetStateString("Hdialog", output[0].Data.caption[0]);
}
}
}
else if((output[1].Data.soundName == soundName || output[1].Data.soundName == soundName_Path) && !output[1].Data.empty )
{
//ADDED MINIMUM TIME OF 4 SECONDS TO THIS CONDITIONAL - 11/29/04 by JASON
if (gameLocal.GetTime() - output[1].time > 4000)\
{
output[1].time = gameLocal.GetTime();
output[1].Data.ent_vec = ent_vec;
//TO PUT "CONTINUES" IN CAPS AND INSIDE SQUARE BRACKETS USE FOLLOWING CODE:
/*
if(output[1].Data.caption[0].Find("continues", false, 0, -1) < 0)
{
idStr tempCaption = output[1].Data.caption[0];
if (tempCaption.Last(')') > 0)
{
output[1].Data.caption[0].Insert(" CONTINUES", strlen(tempCaption) - 1);
}
else
output[1].Data.caption[0] += " continues";
player->hud->SetStateString("Hdialog", output[0].Data.caption[0]);
}*/
//INSTEAD OF THE FOLLOWING IF:
if(output[1].Data.caption[0].Find("continues", true, 0, -1) < 0)
{
output[1].Data.caption[0] += " continues";
player->hud->SetStateString("Mdialog", output[1].Data.caption[0]);
}
}
}
else if((output[2].Data.soundName == soundName || output[2].Data.soundName == soundName_Path) && !output[2].Data.empty)
{
//ADDED MINIMUM TIME OF 4 SECONDS TO THIS CONDITIONAL - 11/29/04 by JASON
if (gameLocal.GetTime() - output[2].time > 4000)
{
output[2].time = gameLocal.GetTime();
output[2].Data.ent_vec = ent_vec;
//TO PUT "CONTINUES" IN CAPS AND INSIDE SQUARE BRACKETS USE FOLLOWING CODE:
/*
if(output[2].Data.caption[0].Find("continues", false, 0, -1) < 0)
{
idStr tempCaption = output[2].Data.caption[0];
if (tempCaption.Last(')') > 0)
{
output[2].Data.caption[0].Insert(" CONTINUES", strlen(tempCaption) - 1);
}
else
output[2].Data.caption[0] += " continues";
player->hud->SetStateString("Hdialog", output[0].Data.caption[0]);
}*/
//INSTEAD OF THE FOLLOWING IF:
if(output[2].Data.caption[0].Find("continues", true, 0, -1) < 0)
{
output[2].Data.caption[0] += " continues";
player->hud->SetStateString("Ldialog", output[2].Data.caption[0]);
}
}
}
else
{
ccParamaters display;
display.Reset();
display = Find(soundName);
display.scrolled = false;
display.current = 0;
//not in use yet
display.ent_vec = ent_vec;
display.max = max;
// gameLocal.Printf("SN: " + soundName + " " + "\n");
if(display.caption[0] == "ERROR")
{
display.Reset();
display.Clear();
display = Find(soundName_Path);
//gameLocal.Printf(display.soundName + "\n");
}
if(display.caption[0] == "ERROR")
{
// player = player;
gameLocal.Printf("Error: Couldn't find sound, " + soundName + "\n");
}
else if ((display.priority[0] == '0' && cc_dialogue.GetBool()) || (display.priority[0] != '0' && cc_environment.GetBool()))
{
if(inDistance(ent_vec, player->GetWorldCoordinates(player->GetPhysics()->GetOrigin()), max, false) || gameLocal.inCinematic || display.priority[0] == '0')
{
idVec3 temp = player->GetWorldCoordinates(player->GetPhysics()->GetOrigin());
// if(display.priority[0] != 'A' && ent_vec != temp)
// {
//gameLocal.Printf("Entity: " + entName + "\n");
// HandleRadar(ent_vec - temp, player->viewAngles.yaw);
// }
display.ent_vec = ent_vec;
display.max = max;
//gameLocal.Printf("Caption: " + display.soundName + "\n");
gameLocal.Printf("Caption: " + display.caption[0] + "\n");
//gameLocal.Printf("Entity: " + entName + "\n");
bool output2WasEmpty = output[2].Data.empty; //ADDED 11/23/04 BY JASON
//has a priority higher than the high box
if((display.priority.Cmp(display.priority, output[0].Data.priority) <= 0 && !output[0].Data.needsscroll)
|| (DistanceBetween(display.ent_vec, player->GetWorldCoordinates(player->GetPhysics()->GetOrigin()), output[0].Data.ent_vec, display.priority, output[0].Data.priority, output[0].Data.empty)
&& (output[0].Data.priority[0] != '0' || (output[0].Data.timecode && output[0].Data.scroll[output[0].Data.current] == 0))))
{
player->hud->HandleNamedEvent("resetHigh");
int pos = output[2].Data.caption[0].Find("continues", true, 0, -1);
if(pos > 0)//make sure the continues is removed
{
output[2].Data.caption[0] = output[2].Data.caption[0].Mid(0, pos - 1);
}
output[2] = output[1];
output[1] = output[0];
if(output[2].Data.empty)
{
if (!output2WasEmpty) //ADDED 11/23/04 BY JASON
{
player->hud->SetStateString("Ldialog", "");
player->hud->HandleNamedEvent("removedlow");
player->hud->HandleNamedEvent("LowPHide");
output[2].Data.radar = false;
}
}
else
{ //This means that even if there was a caption there before, it will still play the animation. This is a design decision. - Jason
//ADDED 1-30-05
//Set the low color to the old color
player->hud->HandleNamedEvent(output[2].Data.currentcolor + "Low");
player->hud->SetStateString("Ldialog", output[2].Data.caption[output[2].Data.current]);
player->hud->HandleNamedEvent("captionlow");
HandleRadar(output[2].Data.ent_vec - temp, player->viewAngles.yaw, "Low", output[2].Data.currentcolor);
player->hud->HandleNamedEvent("LowPVis");
output[2].Data.radar = true;
}
if(output[1].Data.empty)
{
if(!output[2].Data.empty) //ADDED 11/23/04 BY JASON
{
player->hud->SetStateString("Mdialog", "");
player->hud->HandleNamedEvent("removedmid");
player->hud->HandleNamedEvent("MidPHide");
output[1].Data.radar = false;
}
}
else //This means that even if there was a caption there before, it will still play the animation. This is a design decision. - Jason
{
//ADDED 1-30-05
//Set the mid color to the old color
player->hud->HandleNamedEvent(output[1].Data.currentcolor + "Mid");
player->hud->SetStateString("Mdialog", output[1].Data.caption[output[1].Data.current]);
player->hud->HandleNamedEvent("captionmid");
HandleRadar(output[1].Data.ent_vec - temp, player->viewAngles.yaw, "Mid", output[1].Data.currentcolor);
player->hud->HandleNamedEvent("MidPVis");
output[1].Data.radar = true;
}
//ADDED 1-30-05
//search and find the color, call the on Name event to handle the color change
display.currentcolor = ccColorInfo.Search(entName);
player->hud->HandleNamedEvent(display.currentcolor + "High");
//
player->hud->SetStateString("Hdialog", display.caption[0]);
HandleRadar(display.ent_vec - temp, player->viewAngles.yaw, "High", display.currentcolor);
player->hud->HandleNamedEvent("HighPVis");
output[0].Data = display;
output[0].Data.radar = true;
output[0].time = gameLocal.GetTime();
//gameLocal.Printf(output[0].Data.soundName + " " + idStr(output[0].time) + "\n");
if(length >= 5000)
{
output[0].length = length;
}
else
output[0].length = 5000;
player->hud->HandleNamedEvent("captionhigh");
}
//has a priority higher than the mid box
else if((display.priority.Cmp(display.priority, output[1].Data.priority) <= 0 && !output[1].Data.needsscroll)
|| (DistanceBetween(display.ent_vec, player->GetWorldCoordinates(player->GetPhysics()->GetOrigin()), output[1].Data.ent_vec, display.priority, output[1].Data.priority, output[1].Data.empty)
&& (output[1].Data.priority[0] != '0'|| (output[1].Data.timecode && output[1].Data.scroll[output[1].Data.current] == 0))))
{
int pos = output[2].Data.caption[0].Find("continues", true, 0, -1);
if(pos > 0)//make sure the continues is removed
{
output[2].Data.caption[0] = output[2].Data.caption[0].Mid(0, pos - 1);
}
player->hud->HandleNamedEvent("resetMid");
output[2] = output[1];
player->hud->SetStateString("Ldialog", output[2].Data.caption[output[2].Data.current]);
if(output[2].Data.empty)
{
if (!output2WasEmpty) //ADDED 11/23/04 BY JASON
{
player->hud->SetStateString("Ldialog", "");
player->hud->HandleNamedEvent("removedlow");
player->hud->HandleNamedEvent("LowPHide");
output[2].Data.radar = false;
}
}
else
{ //This means that even if there was a caption there before, it will still play the animation. This is a design decision. - Jason
player->hud->HandleNamedEvent(output[2].Data.currentcolor + "Low");
player->hud->SetStateString("Ldialog", output[2].Data.caption[output[2].Data.current]);
player->hud->HandleNamedEvent("captionlow");
HandleRadar(output[2].Data.ent_vec - temp, player->viewAngles.yaw, "Low", output[2].Data.currentcolor);
player->hud->HandleNamedEvent("LowPVis");
output[2].Data.radar = true;
}
display.currentcolor = ccColorInfo.Search(entName);
player->hud->HandleNamedEvent(display.currentcolor + "Mid");
player->hud->SetStateString("Mdialog", display.caption[0]);
output[1].Data = display;
output[1].Data.radar = true;
output[1].time = gameLocal.GetTime();
HandleRadar(output[1].Data.ent_vec - temp, player->viewAngles.yaw, "Mid", output[1].Data.currentcolor);
player->hud->HandleNamedEvent("MidPVis");
if(length >= 5000)
{
output[1].length = length;
}
else
output[1].length = 5000;
player->hud->HandleNamedEvent("captionmid");
}
//it has a priority higher than the low box
else if((display.priority.Cmp(display.priority, output[2].Data.priority) <= 0 && !output[2].Data.needsscroll)
|| (DistanceBetween(display.ent_vec, player->GetWorldCoordinates(player->GetPhysics()->GetOrigin()), output[2].Data.ent_vec, display.priority, output[2].Data.priority, output[2].Data.empty)
&& (output[2].Data.priority[0] != '0' || (output[2].Data.timecode && output[2].Data.scroll[output[2].Data.current] == 0))))
{
int pos = output[2].Data.caption[0].Find("continues", true, 0, -1);
if(pos > 0)//make sure the continues is removed
{
output[2].Data.caption[0] = output[2].Data.caption[0].Mid(0, pos - 1);
}
player->hud->HandleNamedEvent("resetLow");
display.currentcolor = ccColorInfo.Search(entName);
player->hud->HandleNamedEvent(display.currentcolor + "Low");
player->hud->SetStateString("Ldialog", display.caption[0]);
output[2].Data = display;
output[2].Data.radar = true;
output[2].time = gameLocal.GetTime();
HandleRadar(output[2].Data.ent_vec - temp, player->viewAngles.yaw, "Low", output[2].Data.currentcolor);
player->hud->HandleNamedEvent("LowPVis");
if(length >= 5000)
{
output[2].length = length;
}
else
output[2].length = 5000;
player->hud->HandleNamedEvent("captionlow");
}
}
}
}
}
}
void ccBst::Update()
{
//Check for language change
checkLanguage();
idPlayer *player = gameLocal.GetLocalPlayer();
idVec3 temppos = player->GetWorldCoordinates(player->GetPhysics()->GetOrigin());
//check for the end of the first windowdef box (high priority) and shift accordingly
if( (!output[0].Data.empty && output[0].length <= gameLocal.GetTime() - output[0].time)
|| (!inDistance(output[0].Data.ent_vec, player->GetWorldCoordinates(player->GetPhysics()->GetOrigin()), output[0].Data.max, output[0].Data.empty)
&& !gameLocal.inCinematic && (output[0].Data.priority[0] != '0' || output[0].Data.timecode) ) )
{
int pos = output[0].Data.caption[0].Find("continues", true, 0, -1);
if(pos > 0)//make sure the continues is removed
{
output[0].Data.caption[0] = output[0].Data.caption[0].Mid(0, pos - 1);
}
output[0] = output[1];
output[1] = output[2];
if(output[0].Data.scrolled)
{
output[0].Data.scrolled = false;
}
//ADDED 1-30-05
//Make sure the old color follows the caption
player->hud->HandleNamedEvent(output[0].Data.currentcolor + "High");
player->hud->SetStateString("Hdialog", output[0].Data.caption[output[0].Data.current]);
player->hud->HandleNamedEvent("resetHigh");
player->hud->HandleNamedEvent("HighPVis");
if(output[0].Data.empty)
{
player->hud->HandleNamedEvent("removedhigh");
player->hud->HandleNamedEvent("HighPHide");
}
if(output[1].Data.scrolled)
{
output[1].Data.scrolled = false;
}
player->hud->HandleNamedEvent("resetMid");
//ADDED 1-30-05
//Make sure the old color follows the caption
player->hud->HandleNamedEvent(output[1].Data.currentcolor + "Mid");
player->hud->SetStateString("Mdialog", output[1].Data.caption[output[1].Data.current]);
player->hud->HandleNamedEvent("MidPVis");
if(output[1].Data.empty && !output[0].Data.empty)
{//added the second check to make sure the previous wasn't empty
player->hud->HandleNamedEvent("removedmid");
player->hud->HandleNamedEvent("MidPHide");
}
if(!output[1].Data.empty)
{//made sure previous wasn't empty
player->hud->HandleNamedEvent("resetlow");
player->hud->SetStateString("Ldialog", "");
player->hud->HandleNamedEvent("removedlow");
player->hud->HandleNamedEvent("LowPHide");
}
output[2].Data.Reset();
output[2].Data.Clear();
}
//check the second, and shift accordingly
if( (!output[1].Data.empty && output[1].length <= gameLocal.GetTime() - output[1].time)
|| (!inDistance(output[1].Data.ent_vec, player->GetWorldCoordinates(player->GetPhysics()->GetOrigin()), output[1].Data.max , output[1].Data.empty)
&& !gameLocal.inCinematic && (output[1].Data.priority[0] != '0' || output[1].Data.timecode) ) )
{
int pos = output[1].Data.caption[0].Find("continues", true, 0, -1);
if(pos > 0)
{
output[1].Data.caption[0] = output[1].Data.caption[0].Mid(0, pos - 1);
}
output[1] = output[2];
if(output[1].Data.scrolled)
{
output[1].Data.scrolled = false;
}
player->hud->HandleNamedEvent("resetMid");
player->hud->HandleNamedEvent("resetlow");
if(output[1].Data.empty)
{
player->hud->SetStateString("Mdialog", "");
player->hud->HandleNamedEvent("removedmid");
player->hud->HandleNamedEvent("MidPHide");
}
else //SWITCHED THIS FROM if(output[1].Data.caption != "") TO else FOR EFFICIENCY - Jason on 11/23/04
{
//ADDED 1-30-05
//Make sure the old color follows the caption
player->hud->HandleNamedEvent(output[1].Data.currentcolor + "Mid");
player->hud->SetStateString("Mdialog", output[1].Data.caption[output[1].Data.current]);
player->hud->HandleNamedEvent("MidPVis");
player->hud->SetStateString("Ldialog", "");
player->hud->HandleNamedEvent("removedlow");
player->hud->HandleNamedEvent("LowPHide");
}
output[2].Data.Reset();
output[2].Data.Clear();
}
//check third, shift accordingly
if( (!output[2].Data.empty && output[2].length <= gameLocal.GetTime() - output[2].time)
|| (!inDistance(output[2].Data.ent_vec, player->GetWorldCoordinates(player->GetPhysics()->GetOrigin()), output[2].Data.max , output[2].Data.empty)
&& !gameLocal.inCinematic && (output[2].Data.priority[0] != '0' || output[2].Data.timecode)) )
{//should not need a check since the previous was not empty
int pos = output[2].Data.caption[0].Find("continues", true, 0, -1);
if(pos > 0)
{
output[2].Data.caption[0] = output[2].Data.caption[0].Mid(0, pos - 1);
}
player->hud->SetStateString("Ldialog", "");
player->hud->HandleNamedEvent("resetLow");
player->hud->HandleNamedEvent("removedlow");
player->hud->HandleNamedEvent("LowPHide");
output[2].Data.Reset();
output[2].Data.Clear();
}
//check for timecode switch, high
if(output[0].Data.timecode)
{//due to some problems displaying the last caption, I had to run a check to make sure it isn't the last element
if(output[0].Data.current != output[0].Data.size -1)
{
if(gameLocal.GetTime() >= output[0].time + output[0].Data.time[output[0].Data.current + 1])
{
output[0].Data.current++;
player->hud->HandleNamedEvent("resetHigh");
output[0].Data.scrolled = false;
gameLocal.Printf("Caption: " + output[0].Data.caption[output[0].Data.current] + "\n");
player->hud->SetStateString("Hdialog", output[0].Data.caption[output[0].Data.current]);
}
}
}
//check for timecode switch, mid
if(output[1].Data.timecode)
{
if(output[1].Data.current != output[1].Data.size -1)
{
if(gameLocal.GetTime() >= output[1].time + output[1].Data.time[output[1].Data.current + 1])
{
output[1].Data.current++;
player->hud->HandleNamedEvent("resetMid");
output[1].Data.scrolled = false;
gameLocal.Printf("Caption: " + output[1].Data.caption[output[1].Data.current] + "\n");
player->hud->SetStateString("Mdialog", output[1].Data.caption[output[1].Data.current]);
}
}
}
if(output[2].Data.timecode)
{ //check for scrolling in current
//check for timecode switch, low
if(output[2].Data.current != output[2].Data.size -1)
{
if(gameLocal.GetTime() >= output[2].time + output[2].Data.time[output[2].Data.current + 1])
{
output[2].Data.current++;
player->hud->HandleNamedEvent("resetLow");
output[2].Data.scrolled = false;
gameLocal.Printf("Caption: " + output[2].Data.caption[output[2].Data.current] + "\n");
player->hud->SetStateString("Ldialog", output[2].Data.caption[output[2].Data.current]);
}
}
}
//check for scrolling High
if(output[0].Data.scroll[output[0].Data.current] > 0 && !output[0].Data.scrolled)
{
//if(output[0].time + output[0].Data.time[output[0].Data.current] + cc_DELAY >= gameLocal.GetTime())
//{
if(output[0].length <= output[0].Data.scroll[output[0].Data.current] * 3500 + 3000)
{
output[0].length = output[0].Data.scroll[output[0].Data.current] * 3500 + 3000;
}
Scroll(output[0].Data.scroll[output[0].Data.current], "High_");
output[0].Data.scrolled = true;
//}
}
//check for scrolling Mid
if(output[1].Data.scroll[output[1].Data.current] > 0 && !output[1].Data.scrolled)
{
// if(output[1].time + output[1].Data.time[output[1].Data.current] + cc_DELAY >= gameLocal.GetTime())
// {
if(output[1].length <= output[1].Data.scroll[output[1].Data.current] * 3500 + 3000)
{
output[1].length = output[1].Data.scroll[output[1].Data.current] * 3500 + 3000;
}
Scroll(output[1].Data.scroll[output[1].Data.current], "Mid_");
output[1].Data.scrolled = true;
//}
}
//check for scrolling Low
if(output[2].Data.scroll[output[2].Data.current] > 0 && !output[2].Data.scrolled)
{
//if(output[2].time + output[2].Data.time[output[2].Data.current] + cc_DELAY >= gameLocal.GetTime())
//{ //if it is not longer than the scroll time w/ 1.5 seconds to spare, set the length that long
if(output[2].length <= output[2].Data.scroll[output[2].Data.current] * 3500 + 3000)
{
output[2].length = output[2].Data.scroll[output[2].Data.current] * 3500 + 3000;
}
Scroll(output[2].Data.scroll[output[2].Data.current], "Low_");
output[2].Data.scrolled = true;
//}
}
//These will update the radar accordingly to the sounds first known position, and the players latest.
if(output[0].Data.radar)
{
HandleRadar(output[0].Data.ent_vec - temppos, player->viewAngles.yaw, "High", output[0].Data.currentcolor);
}
if(output[1].Data.radar)
{
HandleRadar(output[1].Data.ent_vec - temppos, player->viewAngles.yaw, "Mid", output[1].Data.currentcolor);
}
if(output[2].Data.radar)
{
HandleRadar(output[2].Data.ent_vec - temppos, player->viewAngles.yaw, "Low", output[2].Data.currentcolor);
}
}
ccParamaters ccBst::Find(idStr requisition)
{
ccParamaters temp;
temp.Reset();
temp.Clear();
temp.caption[0] = "ERROR";
if(!IsEmpty())
{
Node *p = Tree;
while(p != NULL)
{
//gameLocal.Printf(p->data.soundName + "\n");
int comp = requisition.Cmp(requisition, p->data.soundName);
if(comp == 0 || requisition == p->data.soundName)
{
temp = p->data;
//gameLocal.Printf(temp.soundName + "\n");
p = NULL;
}
else if(comp > 0)
{
p = p->right;
}
else if(comp < 0)
{
p = p->left;
}
}
}
return temp;
}
void ccBst::Add(ccParamaters newData)
{
if(Tree == NULL)
{
Node *temp = new Node;
temp->data = newData;
temp->left = NULL;
temp->right = NULL;
Tree = temp;
}
else
{
AddHelper(Tree, newData);
}
}
void ccBst::AddHelper(Node *head, ccParamaters newData)
{
if(newData.soundName.Cmp(newData.soundName, head->data.soundName) < 0 && head->left == NULL)
{
Node *temp = new Node;
temp->data = newData;
temp->left = NULL;
temp->right = NULL;
head->left = temp;
//used to make sure the caption file is formatted correctly
//gameLocal.Printf("Left " + newData.soundName +" " + head->data.soundName + "\n");
}
else if(newData.soundName.Cmp(newData.soundName, head->data.soundName) > 0 && head->right == NULL)
{
Node *temp = new Node;
temp->data = newData;
temp->left = NULL;
temp->right = NULL;
head->right = temp;
//used to make sure the caption file is formatted correctly
//gameLocal.Printf("Right " + newData.soundName +" " + head->data.soundName + "\n");
}
else if(newData.soundName.Cmp(newData.soundName, head->data.soundName) > 0 )
{
AddHelper(head->right, newData);
}
else if(newData.soundName.Cmp(newData.soundName, head->data.soundName) < 0)
{
AddHelper(head->left, newData);
}
}
int ccBst::toInt(char t)
{
switch (t)
{
case '0' :
return 0;
case '1' :
return 1;
case '2' :
return 2;
case '3' :
return 3;
case '4' :
return 4;
case '5' :
return 5;
case '6' :
return 6;
case '7' :
return 7;
case '8' :
return 8;
case '9' :
return 9;
case 'A' :
return 10;
}
return 0;
}
//added 11/29/04
void ccBst::Scroll(int scr, idStr prefix)
{
idPlayer *player = gameLocal.GetLocalPlayer();
switch (scr)
{
case 1:
//gameLocal.Printf("Scrolling 1 \n");
player->hud->HandleNamedEvent(prefix + "scroll_half");
break;
case 2:
//gameLocal.Printf("Scrolling 2 \n");
player->hud->HandleNamedEvent(prefix + "scroll");
break;
case 3:
//gameLocal.Printf("Scrolling 3 \n");
player->hud->HandleNamedEvent(prefix + "scroll_one_and_half");
break;
case 4:
//gameLocal.Printf("Scrolling 4 \n");
player->hud->HandleNamedEvent(prefix + "scroll_full");
break;
}
}
bool ccBst::DistanceBetween(idVec3 ent_vec, idVec3 player_vec, idVec3 old_vec, idStr NP, idStr OP, bool empty)
{
//idVec3 player_vec = physicsObj.current.origin;
//return 10;
int NPI = toInt(NP[0]);
int OPI = toInt(OP[0]);
int overide = cc_OVERIDE_DISTANCE * (NPI - OPI);
float temp = sqrt((player_vec.x - ent_vec.x)*(player_vec.x - ent_vec.x) + (player_vec.y - ent_vec.y)*(player_vec.y - ent_vec.y) +(player_vec.z - ent_vec.z)*(player_vec.z - ent_vec.z))*DOOM_TO_METERS;
float tmp = sqrt((player_vec.x - old_vec.x)*(player_vec.x - old_vec.x) + (player_vec.y - old_vec.y)*(player_vec.y - old_vec.y) +(player_vec.z - old_vec.z)*(player_vec.z - old_vec.z))*DOOM_TO_METERS;
//gameLocal.Printf("TMP = " + idStr(tmp) + "\n");
if(gameLocal.inCinematic)
{
return false;
}
else if (empty)
{
return true;
}
if(overide < 0)
{
if(temp < (overide*-1))
{
return true;
}
else
{
return false;
}
}
if(overide > 0)
{
if(tmp > overide)
{
return true;
}
else
{
return false;
}
}
else
{
if(tmp > temp)
{
return false;
}
else
{
return true;
}
}
}
bool ccBst::inDistance(idVec3 ent_vec, idVec3 player_vec, float maxDis, bool empty)
{
/*
delta = end.ToVec2() - start.ToVec2();
dist = delta.LengthFast();*/
if(maxDis <=0)
return true;
if(empty)
{
return true;
}
float temp = sqrt((player_vec.x - ent_vec.x)*(player_vec.x - ent_vec.x) + (player_vec.y - ent_vec.y)*(player_vec.y - ent_vec.y) +(player_vec.z - ent_vec.z)*(player_vec.z - ent_vec.z))*DOOM_TO_METERS;
//idVec2 tempy= ent_vec.ToVec2() - player_vec.ToVec2();
//float tmp = tempy.Length() * DOOM_TO_METERS; //used to verify the correctness of my equation
//gameLocal.Printf("Max: " + idStr(maxDis) + "Dis: " + idStr(temp) + "\n");
//gameLocal.Printf("tempy: " + idStr(tmp) + "\n");
if(temp < maxDis)
return true;
else
return false;
}
idStr ccBst::getLanguageName(int index)
{
if (languagesLoaded)
{
//languages[index].ToLower();
return languages[index];
}
else
return "english"; //Default to english if languages.dcc missing.
}
bool ccBst::checkLanguage()
{
if (language != cc_languageSelector.GetInteger())
{
//int oldLanguage = language; //In case we need to roll back
Reset();
language = cc_languageSelector.GetInteger();
gameLocal.Printf("ccBst::checkLanguage() - the language has changed!\n");
Init(currentMap, false);
return true;
}
return false;
}
//ADDED Feb 27 2005
bool ccBst::HandleRadar(idVec3 difVec, float vAngle, idStr Prior, idStr Color)
{
float angle = AngleBetween(difVec, vAngle);
int dis = sqrt(difVec.x*difVec.x + difVec.y*difVec.y)*DOOM_TO_METERS;
if(dis > 30)
dis = 30;
int y = sin(angle*(idMath::PI / 180))*dis;
float tempf = dis*dis - y*y;
int x = sqrt(tempf);
x -= 2;
y += 2;
if(x > 30)
x = 30;
if(x < -30)
x = -30;
if(y > 30)
x = 30;
if(y < -30)
y = -30;
x = 1.5*x;
y = 1.5*y;
if((angle >= 0 && angle <= 90) || angle == 360)
{
x = abs(x);
y = abs(y);
}
if((angle > 90 && angle <= 180) || angle == -180)
{
x = abs(x) * -1;
y = abs(y);
}
if((angle < 0 && angle >= -90) || (angle >= 270 && angle < 360))
{
x = abs(x) *1;
y = abs(y) *-1;
}
if((angle < -90 && angle > -180) || (angle > 180 && angle <270))
{
x = abs(x) *-1;
y = abs(y) *-1;
}
//gameLocal.Printf("X: " + idStr(x) + " Y: " + idStr(y) + "Anagle: " + angle + "\n\n");
y = 45-y;
x = 45+x;
x -= 3;
y += 3;
idPlayer *player = gameLocal.GetLocalPlayer();
player->hud->SetStateFloat(Prior+"X", x);
player->hud->SetStateFloat(Prior+"Y", y);
//method not working, will needs to be reworked the way it is done for captions
player->hud->HandleNamedEvent(Color + Prior + "R");
return true;
}
float ccBst::AngleBetween(idVec3 difVec, float vAngle)
{
int angle = 0;
float temp = abs(difVec.y/difVec.x);
angle = int(atan(temp)*(180/idMath::PI));
if(difVec.x == 0 && difVec.y == 0)
angle = 0;
else if(difVec.x == 0)
{
if(difVec.y < 0)
angle = -270;
else if(difVec.y > 0)
angle = 90;
}
else if(difVec.y == 0)
{
if(difVec.x > 0)
angle = 0;
else if(difVec.x <0 )
angle = 180;
}
int tempy = angle;
angle = angle - vAngle +90;
if (angle >= 360)
{
angle = angle - 360;
}
return angle;
}