|
|
//===== Copyright � Valve Corporation, All rights reserved. ======//
//
// Purpose: Defines scripting system.
//
//===========================================================================//
#include "gameuiscriptInterface.h"
#include "gameuisystemmgr.h"
#include "gameuidefinition.h"
#include "gamelayer.h"
#include "gamegraphic.h"
#include "fmtstr.h"
#include "gameuiscript.h"
#include "gameuisystem.h"
#include "gametext.h"
#include "dynamicrect.h"
#include "tier2/tier2.h"
#include "matchmaking/imatchframework.h"
BEGIN_SCRIPTDESC_ROOT_NAMED( CGameUIScriptInterface, "CGameUIScriptInterface", SCRIPT_SINGLETON "" ) // HSCRIPT table-kv functions
DEFINE_SCRIPTFUNC( LoadMenu, "LoadMenu( name, {params} ) : Load a menu." ) DEFINE_SCRIPTFUNC( CreateGraphic, "CreateGraphic( classname, {params} ) : Create a graphic." ) DEFINE_SCRIPTFUNC( CallScript, "CallScript( scripthandle, function, {params} ) : Execute other script function (scripthandle=0 will run local script)." ) DEFINE_SCRIPTFUNC( CallGraphic, "CallGraphic( graphichandle, commandname, {params} ) : Execute a graphic function." ) DEFINE_SCRIPTFUNC( Nugget, "Nugget( action, {params} ) : Interface with nuggets." ) END_SCRIPTDESC()
static ConVar ui_script_spew_level( "ui_script_spew_level", 0, FCVAR_DEVELOPMENTONLY );
//-----------------------------------------------------------------------------
// Constructor
//-----------------------------------------------------------------------------
CGameUIScriptInterface::CGameUIScriptInterface( IScriptVM *pScriptVM, CGameUIDefinition *pDef ) : m_Nuggets( UtlStringLessFunc ), m_GraphicScriptInstances( UtlStringLessFunc ), m_pScriptVM( pScriptVM ), m_pMenu( pDef ) { m_Scope = m_pScriptVM->RegisterInstance( this, "c" ); }
CGameUIScriptInterface::~CGameUIScriptInterface() { Shutdown(); }
void CGameUIScriptInterface::Shutdown() { // Unload all nuggets
for ( unsigned short k = m_Nuggets.FirstInorder(); k != m_Nuggets.InvalidIndex(); k = m_Nuggets.NextInorder( k ) ) { IGameUIScreenController *pNugget = m_Nuggets.Element( k ); pNugget->OnScreenDisconnected( m_pMenu->GetGameUISystem() ); } m_Nuggets.Purge(); }
//-----------------------------------------------------------------------------
// Show the ID menu next frame, and hide this menu next frame.
//-----------------------------------------------------------------------------
HSCRIPT CGameUIScriptInterface::LoadMenu( const char *szMenuName, HSCRIPT hParams ) { if ( !szMenuName || !*szMenuName ) return NULL;
// Build the key values for the command and broadcast (deleted inside broadcast system)
KeyValues *kvCommand = ScriptTableToKeyValues( m_pScriptVM, szMenuName, hParams ); KeyValues::AutoDelete autodelete_kvCommand( kvCommand );
if ( ui_script_spew_level.GetInt() > 0 ) { DevMsg( "CGameUIScriptInterface::LoadMenu\n" ); KeyValuesDumpAsDevMsg( kvCommand ); }
IGameUISystem *pUI = g_pGameUISystemMgrImpl->LoadGameUIScreen( kvCommand ); if ( !pUI ) return NULL; KeyValues *kvResult = new KeyValues( "" ); KeyValues::AutoDelete autodelete_kvResult( kvResult ); kvResult->SetInt( "scripthandle", pUI->GetScriptHandle() );
return ScriptTableFromKeyValues( m_pScriptVM, kvResult ); }
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
HSCRIPT CGameUIScriptInterface::CreateGraphic( const char *szGraphicClassName, HSCRIPT hParams ) { if ( !szGraphicClassName || !*szGraphicClassName ) return NULL;
if ( !m_pMenu || !m_pMenu->GetGameUISystem() ) { DevWarning( "Scripts not connected to game UI system and cannot create graphics!\n" ); return NULL; }
// Build the key values for the command
KeyValues *kvCommand = ScriptTableToKeyValues( m_pScriptVM, szGraphicClassName, hParams ); KeyValues::AutoDelete autodelete_kvCommand( kvCommand );
if ( ui_script_spew_level.GetInt() > 0 ) { DevMsg( "CGameUIScriptInterface::CreateGraphic\n" ); KeyValuesDumpAsDevMsg( kvCommand ); }
const char* szGraphicName = kvCommand->GetString( "name", NULL ); // Check if this instance is already created
if ( szGraphicName == NULL ) { DevWarning( "A must have a name!\n", szGraphicName ); return NULL; }
// Check if an instance of this graphic already exists
CGameGraphic *pGraphic = m_pMenu->GraphicExists( szGraphicName ); if ( pGraphic ) { DevWarning( "A graphic with this name %s is already loaded!\n", szGraphicName ); return NULL; }
IGameUIGraphicClassFactory *pFactory = g_pGameUISystemMgrImpl->GetGraphicClassFactory( szGraphicClassName ); if ( !pFactory ) { DevWarning( "No graphic class factory for %s!\n", szGraphicClassName ); return NULL; }
CGameGraphic *pNewGraphic = pFactory->CreateNewGraphicClass( kvCommand, m_pMenu ); if ( !pNewGraphic ) { DevWarning( "No graphic in factory %s!\n", szGraphicClassName ); return NULL; }
KeyValues *kvResult = new KeyValues( "" ); KeyValues::AutoDelete autodelete_kvResult( kvResult ); kvResult->SetInt( "scripthandle", pNewGraphic->GetScriptHandle() ); return ScriptTableFromKeyValues( m_pScriptVM, kvResult ); }
HSCRIPT CGameUIScriptInterface::CallScript( int32 iScriptHandle, const char *szCommandName, HSCRIPT hParams ) { if ( !szCommandName || !*szCommandName ) return NULL;
// Try to resolve other script handle specified
CGameUISystem *pOtherScript = iScriptHandle ? CGameUISystem::FromScriptHandle( iScriptHandle ) : ( CGameUISystem * ) m_pMenu->GetGameUISystem(); if ( !pOtherScript ) { Warning( "CGameUIScriptInterface::CallScript with invalid script handle %d!\n", iScriptHandle ); return NULL; }
// Build the key values for the command and call other script
KeyValues *kvCommand = ScriptTableToKeyValues( m_pScriptVM, szCommandName, hParams ); KeyValues::AutoDelete autodelete_kvCommand( kvCommand );
if ( ui_script_spew_level.GetInt() > 0 ) { DevMsg( "CGameUIScriptInterface::CallScript( %d : %s )\n", iScriptHandle, pOtherScript->GetName() ); KeyValuesDumpAsDevMsg( kvCommand ); }
// Pass the command to another script
KeyValues *kvResult = NULL; pOtherScript->ExecuteScript( kvCommand, &kvResult ); if ( ui_script_spew_level.GetInt() > 0 ) { KeyValuesDumpAsDevMsg( kvResult ); }
KeyValues::AutoDelete autodelete_kvResult( kvResult ); return ScriptTableFromKeyValues( m_pScriptVM, kvResult ); }
HSCRIPT CGameUIScriptInterface::CallGraphic( int32 iGraphicHandle, const char *szCommandName, HSCRIPT hParams ) { if ( !szCommandName || !*szCommandName || !iGraphicHandle ) return NULL;
CGameGraphic *pGraphic = CGameGraphic::FromScriptHandle( iGraphicHandle ); if ( !pGraphic ) { Warning( "CGameUIScriptInterface::CallGraphic with invalid graphic handle %d!\n", iGraphicHandle ); return NULL; }
// Build the key values for the command and call other script
KeyValues *kvCommand = ScriptTableToKeyValues( m_pScriptVM, szCommandName, hParams ); KeyValues::AutoDelete autodelete_kvCommand( kvCommand );
if ( ui_script_spew_level.GetInt() > 0 ) { DevMsg( "CGameUIScriptInterface::CallGraphic( %d : %s )\n", iGraphicHandle, pGraphic->GetName() ); KeyValuesDumpAsDevMsg( kvCommand ); }
// Pass the command to graphic
KeyValues *kvResult = pGraphic->HandleScriptCommand( kvCommand); if ( ui_script_spew_level.GetInt() > 0 ) { KeyValuesDumpAsDevMsg( kvResult ); }
KeyValues::AutoDelete autodelete_kvResult( kvResult ); return ScriptTableFromKeyValues( m_pScriptVM, kvResult ); }
HSCRIPT CGameUIScriptInterface::Nugget( const char *szCommandName, HSCRIPT hParams ) { if ( !szCommandName || !*szCommandName ) return NULL;
if ( !m_pMenu || !m_pMenu->GetGameUISystem() ) { DevWarning( "Scripts not connected to game UI system and cannot use nuggets!\n" ); return NULL; }
// Build the key values for the command
KeyValues *kvCommand = ScriptTableToKeyValues( m_pScriptVM, szCommandName, hParams ); KeyValues::AutoDelete autodelete_kvCommand( kvCommand );
if ( ui_script_spew_level.GetInt() > 0 ) { DevMsg( "CGameUIScriptInterface::Nugget\n" ); KeyValuesDumpAsDevMsg( kvCommand ); }
// Parse the command
if ( char const *szUseName = StringAfterPrefix( kvCommand->GetName(), "load:" ) ) { char const *szNuggetName = szUseName; szUseName = kvCommand->GetString( "usename", szNuggetName ); // Check if the nugget is already loaded
if ( m_Nuggets.Find( szUseName ) != m_Nuggets.InvalidIndex() ) { DevWarning( "Nugget factory %s is already loaded!\n", szUseName ); return NULL; }
IGameUIScreenControllerFactory *pFactory = g_pGameUISystemMgrImpl->GetScreenControllerFactory( szNuggetName ); if ( !pFactory ) { DevWarning( "No nugget factory for %s!\n", szNuggetName ); return NULL; } kvCommand->SetName( szNuggetName ); IGameUIScreenController *pNugget = pFactory->GetController( kvCommand ); if ( !pNugget ) { DevWarning( "No nugget in factory %s!\n", szNuggetName ); return NULL; }
// Connect the nugget with our screen
m_Nuggets.Insert( szUseName, pNugget ); pNugget->OnScreenConnected( m_pMenu->GetGameUISystem() );
KeyValues *kvResult = new KeyValues( "" ); KeyValues::AutoDelete autodelete_kvResult( kvResult ); kvResult->SetInt( "scripthandle", m_pMenu->GetGameUISystem()->GetScriptHandle() ); kvResult->SetString( "usename", szUseName ); kvResult->SetInt( "ptr", reinterpret_cast< int >( pNugget ) );
if ( ui_script_spew_level.GetInt() > 0 ) { DevMsg( "Loaded nugget %s\n", szNuggetName ); KeyValuesDumpAsDevMsg( kvResult ); } return ScriptTableFromKeyValues( m_pScriptVM, kvResult ); }
if ( char const *szUseName = StringAfterPrefix( kvCommand->GetName(), "ref:" ) ) { int iRefScriptHandle = kvCommand->GetInt( "scripthandle" ); char const *szRefUseName = kvCommand->GetString( "usename", szUseName );
// Check if the nugget is already loaded
if ( m_Nuggets.Find( szUseName ) != m_Nuggets.InvalidIndex() ) { DevWarning( "Nugget factory %s is already loaded!\n", szUseName ); return NULL; }
CGameUISystem *pMenu = iRefScriptHandle ? CGameUISystem::FromScriptHandle( iRefScriptHandle ) : ( CGameUISystem * ) m_pMenu->GetGameUISystem(); if ( !pMenu ) { DevWarning( "Nugget reference request %s with invalid script handle %d!\n", szUseName, iRefScriptHandle ); return NULL; }
CGameUIScriptInterface *pRefInterface = NULL; if ( CGameUIScript *pScript = pMenu->Definition().GetScript() ) pRefInterface = pScript->GetScriptInterface();
if ( !pRefInterface ) { DevWarning( "Nugget reference request %s with script handle %d(%s) which has no scripts!\n", szUseName, iRefScriptHandle, pMenu->GetName() ); return NULL; }
unsigned short usIdx = pRefInterface->m_Nuggets.Find( szRefUseName ); if ( usIdx == pRefInterface->m_Nuggets.InvalidIndex() ) { DevWarning( "Nugget reference request %s with script handle %d(%s) which has no nugget %s!\n", szUseName, iRefScriptHandle, pMenu->GetName(), szRefUseName ); return NULL; }
IGameUIScreenController *pNugget = pRefInterface->m_Nuggets.Element( usIdx ); if ( reinterpret_cast< int >( pNugget ) != kvCommand->GetInt( "ptr" ) ) { DevWarning( "Nugget reference request %s with script handle %d(%s) for nugget %s yielding %08X instead of expected %08X!\n", szUseName, iRefScriptHandle, pMenu->GetName(), szRefUseName, reinterpret_cast< int >( pNugget ), kvCommand->GetInt( "ptr" ) ); }
// Connect the nugget with our screen
m_Nuggets.Insert( szUseName, pNugget ); pNugget->OnScreenConnected( m_pMenu->GetGameUISystem() );
KeyValues *kvResult = new KeyValues( "" ); KeyValues::AutoDelete autodelete_kvResult( kvResult ); kvResult->SetInt( "scripthandle", m_pMenu->GetGameUISystem()->GetScriptHandle() ); kvResult->SetString( "usename", szUseName ); kvResult->SetInt( "ptr", reinterpret_cast< int >( pNugget ) );
if ( ui_script_spew_level.GetInt() > 0 ) { DevMsg( "Referenced nugget %s from %d(%s):%s\n", szUseName, iRefScriptHandle, pMenu->GetName(), szRefUseName ); KeyValuesDumpAsDevMsg( kvResult ); } return ScriptTableFromKeyValues( m_pScriptVM, kvResult ); }
if ( char const *szUseName = StringAfterPrefix( kvCommand->GetName(), "free:" ) ) { // Check if the nugget is already loaded
unsigned short usIdx = m_Nuggets.Find( szUseName ); if ( usIdx == m_Nuggets.InvalidIndex() ) { DevWarning( "Nugget factory %s is not loaded!\n", szUseName ); return NULL; }
// Unload the nugget
IGameUIScreenController *pNugget = m_Nuggets.Element( usIdx ); m_Nuggets.RemoveAt( usIdx );
pNugget->OnScreenDisconnected( m_pMenu->GetGameUISystem() ); if ( ui_script_spew_level.GetInt() > 0 ) { DevMsg( "Unloaded nugget %s\n", szUseName ); } return NULL; }
if ( char const *szUse = StringAfterPrefix( kvCommand->GetName(), "use:" ) ) { char const *szUseName = szUse; // Split off nugget name by :
if ( char const *pch = strchr( szUseName, ':' ) ) { char *buf = ( char * ) stackalloc( pch - szUseName + 1 ); Q_strncpy( buf, szUseName, pch - szUseName + 1 ); szUseName = buf; kvCommand->SetName( pch + 1 ); } else { kvCommand->SetName( "" ); }
// Check if the nugget is already loaded
unsigned short usIdx = m_Nuggets.Find( szUseName ); if ( usIdx == m_Nuggets.InvalidIndex() ) { DevWarning( "Nugget factory %s is not loaded!\n", szUseName ); return NULL; }
// Nugget operation
IGameUIScreenController *pNugget = m_Nuggets.Element( usIdx ); KeyValues *kvResult = pNugget->OnScreenEvent( m_pMenu->GetGameUISystem(), kvCommand ); KeyValues::AutoDelete autodelete_kvResult( kvResult ); if ( ui_script_spew_level.GetInt() > 0 ) { KeyValuesDumpAsDevMsg( kvResult ); }
// Push results for the script
return ScriptTableFromKeyValues( m_pScriptVM, kvResult ); }
return NULL; }
bool CGameUIScriptInterface::ScriptVmKeyValueToVariant( IScriptVM *pVM, KeyValues *val, ScriptVariant_t &varValue, char chScratchBuffer[KV_VARIANT_SCRATCH_BUF_SIZE] ) { switch ( val->GetDataType() ) { case KeyValues::TYPE_STRING: varValue = val->GetString(); return true; case KeyValues::TYPE_INT: varValue = val->GetInt(); return true; case KeyValues::TYPE_FLOAT: varValue = val->GetFloat(); return true; case KeyValues::TYPE_UINT64: Q_snprintf( chScratchBuffer, KV_VARIANT_SCRATCH_BUF_SIZE, "%llu", val->GetUint64() ); varValue = chScratchBuffer; return true; case KeyValues::TYPE_NONE: varValue = ScriptTableFromKeyValues( pVM, val ); return true; default: Warning( "ScriptVmKeyValueToVariant failed to package parameter %s (type %d)\n", val->GetName(), val->GetDataType() ); return false; } }
bool CGameUIScriptInterface::ScriptVmStringFromVariant( ScriptVariant_t &varValue, char chScratchBuffer[KV_VARIANT_SCRATCH_BUF_SIZE] ) { switch ( varValue.m_type ) { case FIELD_INTEGER: Q_snprintf( chScratchBuffer, KV_VARIANT_SCRATCH_BUF_SIZE, "%d", varValue.m_int ); return true; case FIELD_FLOAT: Q_snprintf( chScratchBuffer, KV_VARIANT_SCRATCH_BUF_SIZE, "%f", varValue.m_float ); return true; case FIELD_BOOLEAN: Q_snprintf( chScratchBuffer, KV_VARIANT_SCRATCH_BUF_SIZE, "%d", varValue.m_bool ); return true; case FIELD_CHARACTER: Q_snprintf( chScratchBuffer, KV_VARIANT_SCRATCH_BUF_SIZE, "%c", varValue.m_char ); return true; case FIELD_CSTRING: Q_snprintf( chScratchBuffer, KV_VARIANT_SCRATCH_BUF_SIZE, "%s", varValue.m_pszString ? varValue.m_pszString : "" ); return true; default: Warning( "ScriptVmStringFromVariant failed to unpack parameter variant type %d\n", varValue.m_type ); return false; } }
KeyValues * CGameUIScriptInterface::ScriptVmKeyValueFromVariant( IScriptVM *pVM, ScriptVariant_t &varValue ) { KeyValues *val = NULL;
switch ( varValue.m_type ) { case FIELD_INTEGER: val = new KeyValues( "" ); val->SetInt( NULL, varValue.m_int ); return val; case FIELD_FLOAT: val = new KeyValues( "" ); val->SetFloat( NULL, varValue.m_float ); return val; case FIELD_BOOLEAN: val = new KeyValues( "" ); val->SetInt( NULL, varValue.m_bool ? 1 : 0 ); return val; case FIELD_CHARACTER: val = new KeyValues( "" ); val->SetString( NULL, CFmtStr( "%c", varValue.m_char ) ); return val; case FIELD_CSTRING: val = new KeyValues( "" ); val->SetString( NULL, varValue.m_pszString ? varValue.m_pszString : "" ); return val; case FIELD_HSCRIPT: return ScriptTableToKeyValues( pVM, "", varValue.m_hScript ); default: Warning( "ScriptVmKeyValueFromVariant failed to unpack parameter variant type %d\n", varValue.m_type ); return NULL; } }
KeyValues * CGameUIScriptInterface::ScriptTableToKeyValues( IScriptVM *pVM, char const *szName, HSCRIPT hTable ) { if ( !szName ) szName = ""; KeyValues *kv = new KeyValues( szName );
if ( hTable && pVM ) { int numKeys = pVM->GetNumTableEntries( hTable ); for ( int k = 0; k < numKeys; ++ k ) { ScriptVariant_t varKey, varValue; pVM->GetKeyValue( hTable, k, &varKey, &varValue );
char chScratchBuffer[ KV_VARIANT_SCRATCH_BUF_SIZE ]; if ( !ScriptVmStringFromVariant( varKey, chScratchBuffer ) ) { Assert( 0 ); continue; }
KeyValues *sub = ScriptVmKeyValueFromVariant( pVM, varValue ); if ( !sub ) { Assert( 0 ); // sub->deleteThis();
// continue;
// still proceed - it will be a key with no data
sub = new KeyValues( "" ); } sub->SetName( chScratchBuffer ); kv->AddSubKey( sub ); } }
return kv; }
HSCRIPT CGameUIScriptInterface::ScriptTableFromKeyValues( IScriptVM *pVM, KeyValues *kv ) { if ( !kv || !pVM ) return NULL;
ScriptVariant_t varTable; pVM->CreateTable( varTable );
for ( KeyValues *val = kv->GetFirstSubKey(); val; val = val->GetNextKey() ) { ScriptVariant_t varValue; char chScratchBuffer[ KV_VARIANT_SCRATCH_BUF_SIZE ]; if ( !ScriptVmKeyValueToVariant( pVM, val, varValue, chScratchBuffer ) ) continue;
#ifdef GAMEUI_SCRIPT_LOWERCASE_ALL_TABLE_KEYS
char chNameBuffer[ KV_VARIANT_SCRATCH_BUF_SIZE ]; Q_strncpy( chNameBuffer, val->GetName(), KV_VARIANT_SCRATCH_BUF_SIZE ); Q_strlower( chNameBuffer ); #else
char const *chNameBuffer = val->GetName(); #endif
pVM->SetValue( varTable.m_hScript, chNameBuffer, varValue ); }
return varTable.m_hScript; }
|