/*
===========================================================================
Doom 3 GPL Source Code
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code").
Doom 3 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.
Doom 3 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 Doom 3 Source Code. If not, see .
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
===========================================================================
*/
#include "sys/platform.h"
#include "idlib/LangDict.h"
#include "framework/Session.h"
#include "sound/sound.h"
#include "ui/Window.h"
#include "ui/Winvar.h"
#include "ui/UserInterfaceLocal.h"
#include "ui/GuiScript.h"
/*
=========================
Script_Set
=========================
*/
void Script_Set(idWindow *window, idList *src) {
idStr key, val;
idWinStr *dest = dynamic_cast((*src)[0].var);
if (dest) {
if (idStr::Icmp(*dest, "cmd") == 0) {
dest = dynamic_cast((*src)[1].var);
int parmCount = src->Num();
if (parmCount > 2) {
val = dest->c_str();
int i = 2;
while (i < parmCount) {
val += " \"";
val += (*src)[i].var->c_str();
val += "\"";
i++;
}
window->AddCommand(val);
} else {
window->AddCommand(*dest);
}
return;
}
}
(*src)[0].var->Set((*src)[1].var->c_str());
(*src)[0].var->SetEval(false);
}
/*
=========================
Script_SetFocus
=========================
*/
void Script_SetFocus(idWindow *window, idList *src) {
idWinStr *parm = dynamic_cast((*src)[0].var);
if (parm) {
drawWin_t *win = window->GetGui()->GetDesktop()->FindChildByName(*parm);
if (win && win->win) {
window->SetFocus(win->win);
}
}
}
/*
=========================
Script_ShowCursor
=========================
*/
void Script_ShowCursor(idWindow *window, idList *src) {
idWinStr *parm = dynamic_cast((*src)[0].var);
if ( parm ) {
if ( atoi( *parm ) ) {
window->GetGui()->GetDesktop()->ClearFlag( WIN_NOCURSOR );
} else {
window->GetGui()->GetDesktop()->SetFlag( WIN_NOCURSOR );
}
}
}
/*
=========================
Script_RunScript
run scripts must come after any set cmd set's in the script
=========================
*/
void Script_RunScript(idWindow *window, idList *src) {
idWinStr *parm = dynamic_cast((*src)[0].var);
if (parm) {
idStr str = window->cmd;
str += " ; runScript ";
str += parm->c_str();
window->cmd = str;
}
}
/*
=========================
Script_LocalSound
=========================
*/
void Script_LocalSound(idWindow *window, idList *src) {
idWinStr *parm = dynamic_cast((*src)[0].var);
if (parm) {
session->sw->PlayShaderDirectly(*parm);
}
}
/*
=========================
Script_EvalRegs
=========================
*/
void Script_EvalRegs(idWindow *window, idList *src) {
window->EvalRegs(-1, true);
}
/*
=========================
Script_EndGame
=========================
*/
void Script_EndGame( idWindow *window, idList *src ) {
cvarSystem->SetCVarBool( "g_nightmare", true );
cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "disconnect\n" );
}
/*
=========================
Script_ResetTime
=========================
*/
void Script_ResetTime(idWindow *window, idList *src) {
idWinStr *parm = dynamic_cast((*src)[0].var);
drawWin_t *win = NULL;
if (parm && src->Num() > 1) {
win = window->GetGui()->GetDesktop()->FindChildByName(*parm);
parm = dynamic_cast((*src)[1].var);
}
if (win && win->win) {
win->win->ResetTime(atoi(*parm));
win->win->EvalRegs(-1, true);
} else {
window->ResetTime(atoi(*parm));
window->EvalRegs(-1, true);
}
}
/*
=========================
Script_ResetCinematics
=========================
*/
void Script_ResetCinematics(idWindow *window, idList *src) {
window->ResetCinematics();
}
/*
=========================
Script_Transition
=========================
*/
void Script_Transition(idWindow *window, idList *src) {
// transitions always affect rect or vec4 vars
if (src->Num() >= 4) {
idWinRectangle *rect = NULL;
idWinVec4 *vec4 = dynamic_cast((*src)[0].var);
//
// added float variable
idWinFloat* val = NULL;
//
if (vec4 == NULL) {
rect = dynamic_cast((*src)[0].var);
//
// added float variable
if ( NULL == rect ) {
val = dynamic_cast((*src)[0].var);
}
//
}
idWinVec4 *from = dynamic_cast((*src)[1].var);
idWinVec4 *to = dynamic_cast((*src)[2].var);
idWinStr *timeStr = dynamic_cast((*src)[3].var);
//
// added float variable
if (!((vec4 || rect || val) && from && to && timeStr)) {
//
common->Warning("Bad transition in gui %s in window %s\n", window->GetGui()->GetSourceFile(), window->GetName());
return;
}
int time = atoi(*timeStr);
float ac = 0.0f;
float dc = 0.0f;
if (src->Num() > 4) {
idWinStr *acv = dynamic_cast((*src)[4].var);
idWinStr *dcv = dynamic_cast((*src)[5].var);
assert(acv && dcv);
ac = atof(*acv);
dc = atof(*dcv);
}
if (vec4) {
vec4->SetEval(false);
window->AddTransition(vec4, *from, *to, time, ac, dc);
//
// added float variable
} else if ( val ) {
val->SetEval ( false );
window->AddTransition(val, *from, *to, time, ac, dc);
//
} else {
rect->SetEval(false);
window->AddTransition(rect, *from, *to, time, ac, dc);
}
window->StartTransition();
}
}
typedef struct {
const char *name;
void (*handler) (idWindow *window, idList *src);
int mMinParms;
int mMaxParms;
} guiCommandDef_t;
guiCommandDef_t commandList[] = {
{ "set", Script_Set, 2, 999 },
{ "setFocus", Script_SetFocus, 1, 1 },
{ "endGame", Script_EndGame, 0, 0 },
{ "resetTime", Script_ResetTime, 0, 2 },
{ "showCursor", Script_ShowCursor, 1, 1 },
{ "resetCinematics", Script_ResetCinematics, 0, 2 },
{ "transition", Script_Transition, 4, 6 },
{ "localSound", Script_LocalSound, 1, 1 },
{ "runScript", Script_RunScript, 1, 1 },
{ "evalRegs", Script_EvalRegs, 0, 0 }
};
int scriptCommandCount = sizeof(commandList) / sizeof(guiCommandDef_t);
/*
=========================
idGuiScript::idGuiScript
=========================
*/
idGuiScript::idGuiScript() {
ifList = NULL;
elseList = NULL;
conditionReg = -1;
handler = NULL;
parms.SetGranularity( 2 );
}
/*
=========================
idGuiScript::~idGuiScript
=========================
*/
idGuiScript::~idGuiScript() {
delete ifList;
delete elseList;
int c = parms.Num();
for ( int i = 0; i < c; i++ ) {
if ( parms[i].own ) {
delete parms[i].var;
}
}
}
/*
=========================
idGuiScript::WriteToSaveGame
=========================
*/
void idGuiScript::WriteToSaveGame( idFile *savefile ) {
int i;
if ( ifList ) {
ifList->WriteToSaveGame( savefile );
}
if ( elseList ) {
elseList->WriteToSaveGame( savefile );
}
savefile->Write( &conditionReg, sizeof( conditionReg ) );
for ( i = 0; i < parms.Num(); i++ ) {
if ( parms[i].own ) {
parms[i].var->WriteToSaveGame( savefile );
}
}
}
/*
=========================
idGuiScript::ReadFromSaveGame
=========================
*/
void idGuiScript::ReadFromSaveGame( idFile *savefile ) {
int i;
if ( ifList ) {
ifList->ReadFromSaveGame( savefile );
}
if ( elseList ) {
elseList->ReadFromSaveGame( savefile );
}
savefile->Read( &conditionReg, sizeof( conditionReg ) );
for ( i = 0; i < parms.Num(); i++ ) {
if ( parms[i].own ) {
parms[i].var->ReadFromSaveGame( savefile );
}
}
}
/*
=========================
idGuiScript::Parse
=========================
*/
bool idGuiScript::Parse(idParser *src) {
int i;
// first token should be function call
// then a potentially variable set of parms
// ended with a ;
idToken token;
if ( !src->ReadToken(&token) ) {
src->Error( "Unexpected end of file" );
return false;
}
handler = NULL;
for ( i = 0; i < scriptCommandCount ; i++ ) {
if ( idStr::Icmp(token, commandList[i].name) == 0 ) {
handler = commandList[i].handler;
break;
}
}
if (handler == NULL) {
src->Error("Unknown script call %s", token.c_str());
}
// now read parms til ;
// all parms are read as idWinStr's but will be fixed up later
// to be proper types
while (1) {
if ( !src->ReadToken(&token) ) {
src->Error( "Unexpected end of file" );
return false;
}
if (idStr::Icmp(token, ";") == 0) {
break;
}
if (idStr::Icmp(token, "}") == 0) {
src->UnreadToken(&token);
break;
}
idWinStr *str = new idWinStr();
*str = token;
idGSWinVar wv;
wv.own = true;
wv.var = str;
parms.Append( wv );
}
//
// verify min/max params
if ( handler && (parms.Num() < commandList[i].mMinParms || parms.Num() > commandList[i].mMaxParms ) ) {
src->Error("incorrect number of parameters for script %s", commandList[i].name );
}
//
return true;
}
/*
=========================
idGuiScriptList::Execute
=========================
*/
void idGuiScriptList::Execute(idWindow *win) {
int c = list.Num();
for (int i = 0; i < c; i++) {
idGuiScript *gs = list[i];
assert(gs);
if (gs->conditionReg >= 0) {
if (win->HasOps()) {
float f = win->EvalRegs(gs->conditionReg);
if (f) {
if (gs->ifList) {
win->RunScriptList(gs->ifList);
}
} else if (gs->elseList) {
win->RunScriptList(gs->elseList);
}
}
}
gs->Execute(win);
}
}
/*
=========================
idGuiScriptList::FixupParms
=========================
*/
void idGuiScript::FixupParms(idWindow *win) {
if (handler == &Script_Set) {
bool precacheBackground = false;
bool precacheSounds = false;
idWinStr *str = dynamic_cast(parms[0].var);
assert(str);
idWinVar *dest = win->GetWinVarByName(*str, true);
if (dest) {
delete parms[0].var;
parms[0].var = dest;
parms[0].own = false;
if ( dynamic_cast(dest) != NULL ) {
precacheBackground = true;
}
} else if ( idStr::Icmp( str->c_str(), "cmd" ) == 0 ) {
precacheSounds = true;
}
int parmCount = parms.Num();
for (int i = 1; i < parmCount; i++) {
idWinStr *str = dynamic_cast(parms[i].var);
if (idStr::Icmpn(*str, "gui::", 5) == 0) {
// always use a string here, no point using a float if it is one
// FIXME: This creates duplicate variables, while not technically a problem since they
// are all bound to the same guiDict, it does consume extra memory and is generally a bad thing
idWinStr* defvar = new idWinStr();
defvar->Init ( *str, win );
win->AddDefinedVar ( defvar );
delete parms[i].var;
parms[i].var = defvar;
parms[i].own = false;
//dest = win->GetWinVarByName(*str, true);
//if (dest) {
// delete parms[i].var;
// parms[i].var = dest;
// parms[i].own = false;
//}
//
} else if ((*str[0]) == '$') {
//
// dont include the $ when asking for variable
dest = win->GetGui()->GetDesktop()->GetWinVarByName((const char*)(*str) + 1, true);
//
if (dest) {
delete parms[i].var;
parms[i].var = dest;
parms[i].own = false;
}
} else if ( idStr::Cmpn( str->c_str(), STRTABLE_ID, STRTABLE_ID_LENGTH ) == 0 ) {
str->Set( common->GetLanguageDict()->GetString( str->c_str() ) );
} else if ( precacheBackground ) {
const idMaterial *mat = declManager->FindMaterial( str->c_str() );
mat->SetSort( SS_GUI );
} else if ( precacheSounds ) {
// Search for "play <...>"
idToken token;
idParser parser( LEXFL_NOSTRINGCONCAT | LEXFL_ALLOWMULTICHARLITERALS | LEXFL_ALLOWBACKSLASHSTRINGCONCAT );
parser.LoadMemory(str->c_str(), str->Length(), "command");
while ( parser.ReadToken(&token) ) {
if ( token.Icmp("play") == 0 ) {
if ( parser.ReadToken(&token) && ( token != "" ) ) {
declManager->FindSound( token.c_str() );
}
}
}
}
}
} else if (handler == &Script_Transition) {
if (parms.Num() < 4) {
common->Warning("Window %s in gui %s has a bad transition definition", win->GetName(), win->GetGui()->GetSourceFile());
}
idWinStr *str = dynamic_cast(parms[0].var);
assert(str);
//
drawWin_t *destowner;
idWinVar *dest = win->GetWinVarByName(*str, true, &destowner );
//
if (dest) {
delete parms[0].var;
parms[0].var = dest;
parms[0].own = false;
} else {
common->Warning("Window %s in gui %s: a transition does not have a valid destination var %s", win->GetName(), win->GetGui()->GetSourceFile(),str->c_str());
}
//
// support variables as parameters
int c;
for ( c = 1; c < 3; c ++ ) {
str = dynamic_cast(parms[c].var);
idWinVec4 *v4 = new idWinVec4;
parms[c].var = v4;
parms[c].own = true;
drawWin_t* owner;
if ( (*str[0]) == '$' ) {
dest = win->GetWinVarByName ( (const char*)(*str) + 1, true, &owner );
} else {
dest = NULL;
}
if ( dest ) {
idWindow* ownerparent;
idWindow* destparent;
if ( owner ) {
ownerparent = owner->simp?owner->simp->GetParent():owner->win->GetParent();
destparent = destowner->simp?destowner->simp->GetParent():destowner->win->GetParent();
// If its the rectangle they are referencing then adjust it
if ( ownerparent && destparent &&
(dest == (owner->simp?owner->simp->GetWinVarByName ( "rect" ):owner->win->GetWinVarByName ( "rect" ) ) ) )
{
idRectangle rect;
rect = *(dynamic_cast(dest));
ownerparent->ClientToScreen ( &rect );
destparent->ScreenToClient ( &rect );
*v4 = rect.ToVec4 ( );
} else {
v4->Set ( dest->c_str ( ) );
}
} else {
v4->Set ( dest->c_str ( ) );
}
} else {
v4->Set(*str);
}
delete str;
}
//
} else {
int c = parms.Num();
for (int i = 0; i < c; i++) {
parms[i].var->Init(parms[i].var->c_str(), win);
}
}
}
/*
=========================
idGuiScriptList::FixupParms
=========================
*/
void idGuiScriptList::FixupParms(idWindow *win) {
int c = list.Num();
for (int i = 0; i < c; i++) {
idGuiScript *gs = list[i];
gs->FixupParms(win);
if (gs->ifList) {
gs->ifList->FixupParms(win);
}
if (gs->elseList) {
gs->elseList->FixupParms(win);
}
}
}
/*
=========================
idGuiScriptList::WriteToSaveGame
=========================
*/
void idGuiScriptList::WriteToSaveGame( idFile *savefile ) {
int i;
for ( i = 0; i < list.Num(); i++ ) {
list[i]->WriteToSaveGame( savefile );
}
}
/*
=========================
idGuiScriptList::ReadFromSaveGame
=========================
*/
void idGuiScriptList::ReadFromSaveGame( idFile *savefile ) {
int i;
for ( i = 0; i < list.Num(); i++ ) {
list[i]->ReadFromSaveGame( savefile );
}
}