1
0
mirror of https://github.com/alliedmodders/hl2sdk.git synced 2024-12-23 01:59:43 +08:00
hl2sdk/vgui2/vgui_controls/AnimationController.cpp
2013-06-26 15:22:04 -07:00

1651 lines
50 KiB
C++

//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================//
#pragma warning( disable : 4244 ) // conversion from 'double' to 'float', possible loss of data
#include <vgui/IScheme.h>
#include <vgui/ISurface.h>
#include <vgui/ISystem.h>
#include <vgui/IVGui.h>
#include <KeyValues.h>
#include <vgui_controls/AnimationController.h>
#include "filesystem.h"
#include "filesystem_helpers.h"
#include <stdio.h>
#include <math.h>
#include "mempool.h"
#include "utldict.h"
#include "mathlib/mathlib.h"
#include "characterset.h"
// memdbgon must be the last include file in a .cpp file!!!
#include <tier0/dbg.h>
// for SRC
#include <vstdlib/random.h>
#include <tier0/memdbgon.h>
using namespace vgui;
static CUtlSymbolTable g_ScriptSymbols(0, 128, true);
// singleton accessor for animation controller for use by the vgui controls
namespace vgui
{
AnimationController *GetAnimationController()
{
static AnimationController *s_pAnimationController = new AnimationController(NULL);
return s_pAnimationController;
}
}
//-----------------------------------------------------------------------------
// Purpose: Constructor
//-----------------------------------------------------------------------------
AnimationController::AnimationController(Panel *parent) : BaseClass(parent, NULL)
{
m_hSizePanel = 0;
m_nScreenBounds[ 0 ] = m_nScreenBounds[ 1 ] = -1;
m_nScreenBounds[ 2 ] = m_nScreenBounds[ 3 ] = -1;
m_bAutoReloadScript = false;
// always invisible
SetVisible(false);
SetProportional(true);
// get the names of common types
m_sPosition = g_ScriptSymbols.AddString("position");
m_sSize = g_ScriptSymbols.AddString("size");
m_sFgColor = g_ScriptSymbols.AddString("fgcolor");
m_sBgColor = g_ScriptSymbols.AddString("bgcolor");
m_sXPos = g_ScriptSymbols.AddString("xpos");
m_sYPos = g_ScriptSymbols.AddString("ypos");
m_sWide = g_ScriptSymbols.AddString("wide");
m_sTall = g_ScriptSymbols.AddString("tall");
m_flCurrentTime = 0.0f;
}
//-----------------------------------------------------------------------------
// Purpose: Destructor
//-----------------------------------------------------------------------------
AnimationController::~AnimationController()
{
}
//-----------------------------------------------------------------------------
// Purpose: Sets which script file to use
//-----------------------------------------------------------------------------
bool AnimationController::SetScriptFile( VPANEL sizingPanel, const char *fileName, bool wipeAll /*=false*/ )
{
m_hSizePanel = sizingPanel;
if ( wipeAll )
{
// clear the current script
m_Sequences.RemoveAll();
m_ScriptFileNames.RemoveAll();
CancelAllAnimations();
}
// Store off this filename for reloading later on (if we don't have it already)
UtlSymId_t sFilename = g_ScriptSymbols.AddString( fileName );
if ( m_ScriptFileNames.Find( sFilename ) == m_ScriptFileNames.InvalidIndex() )
{
m_ScriptFileNames.AddToTail( sFilename );
}
UpdateScreenSize();
// load the new script file
return LoadScriptFile( fileName );
}
//-----------------------------------------------------------------------------
// Purpose: reloads the currently set script file
//-----------------------------------------------------------------------------
void AnimationController::ReloadScriptFile()
{
// Clear all current sequences
m_Sequences.RemoveAll();
UpdateScreenSize();
// Reload each file we've loaded
for ( int i = 0; i < m_ScriptFileNames.Count(); i++ )
{
const char *lpszFilename = g_ScriptSymbols.String( m_ScriptFileNames[i] );
if ( strlen( lpszFilename ) > 0)
{
if ( LoadScriptFile( lpszFilename ) == false )
{
Assert( 0 );
}
}
}
}
//-----------------------------------------------------------------------------
// Purpose: loads a script file from disk
//-----------------------------------------------------------------------------
bool AnimationController::LoadScriptFile(const char *fileName)
{
FileHandle_t f = g_pFullFileSystem->Open(fileName, "rt");
if (!f)
{
Warning("Couldn't find script file %s\n", fileName);
return false;
}
// read the whole thing into memory
int size = g_pFullFileSystem->Size(f);
// read into temporary memory block
int nBufSize = size+1;
if ( IsXbox() )
{
nBufSize = AlignValue( nBufSize, 512 );
}
char *pMem = (char *)malloc(nBufSize);
int bytesRead = g_pFullFileSystem->ReadEx(pMem, nBufSize, size, f);
Assert(bytesRead <= size);
pMem[bytesRead] = 0;
g_pFullFileSystem->Close(f);
// parse
bool success = ParseScriptFile(pMem, bytesRead);
free(pMem);
return success;
}
AnimationController::RelativeAlignmentLookup AnimationController::g_AlignmentLookup[] =
{
{ AnimationController::a_northwest , "northwest" },
{ AnimationController::a_north , "north" },
{ AnimationController::a_northeast , "northeast" },
{ AnimationController::a_west , "west" },
{ AnimationController::a_center , "center" },
{ AnimationController::a_east , "east" },
{ AnimationController::a_southwest , "southwest" },
{ AnimationController::a_south , "south" },
{ AnimationController::a_southeast , "southeast" },
{ AnimationController::a_northwest , "nw" },
{ AnimationController::a_north , "n" },
{ AnimationController::a_northeast , "ne" },
{ AnimationController::a_west , "w" },
{ AnimationController::a_center , "c" },
{ AnimationController::a_east , "e" },
{ AnimationController::a_southwest , "sw" },
{ AnimationController::a_south , "s" },
{ AnimationController::a_southeast , "se" },
};
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
AnimationController::RelativeAlignment AnimationController::LookupAlignment( char const *token )
{
int c = ARRAYSIZE( g_AlignmentLookup );
for ( int i = 0; i < c; i++ )
{
if ( !Q_stricmp( token, g_AlignmentLookup[ i ].name ) )
{
return g_AlignmentLookup[ i ].align;
}
}
return AnimationController::a_northwest;
}
//-----------------------------------------------------------------------------
// Purpose: Parse position including right edge and center adjustment out of a
// token. This is relative to the screen
//-----------------------------------------------------------------------------
void AnimationController::SetupPosition( AnimCmdAnimate_t& cmd, float *output, char const *psz, int screendimension )
{
bool r = false, c = false;
int pos;
if ( psz[0] == '(' )
{
psz++;
if ( Q_strstr( psz, ")" ) )
{
char sz[ 256 ];
Q_strncpy( sz, psz, sizeof( sz ) );
char *colon = Q_strstr( sz, ":" );
if ( colon )
{
*colon = 0;
RelativeAlignment ra = LookupAlignment( sz );
colon++;
char *panelName = colon;
char *panelEnd = Q_strstr( panelName, ")" );
if ( panelEnd )
{
*panelEnd = 0;
if ( Q_strlen( panelName ) > 0 )
{
//
cmd.align.relativePosition = true;
cmd.align.alignPanel = g_ScriptSymbols.AddString(panelName);
cmd.align.alignment = ra;
}
}
}
psz = Q_strstr( psz, ")" ) + 1;
}
}
else if (psz[0] == 'r' || psz[0] == 'R')
{
r = true;
psz++;
}
else if (psz[0] == 'c' || psz[0] == 'C')
{
c = true;
psz++;
}
// get the number
pos = atoi(psz);
// scale the values
if (IsProportional())
{
pos = vgui::scheme()->GetProportionalScaledValueEx( GetScheme(), pos );
}
// adjust the positions
if (r)
{
pos = screendimension - pos;
}
if (c)
{
pos = (screendimension / 2) + pos;
}
// set the value
*output = static_cast<float>( pos );
}
//-----------------------------------------------------------------------------
// Purpose: parses a script into sequences
//-----------------------------------------------------------------------------
bool AnimationController::ParseScriptFile(char *pMem, int length)
{
// get the scheme (for looking up color names)
IScheme *scheme = vgui::scheme()->GetIScheme(GetScheme());
// get our screen size (for left/right/center alignment)
int screenWide = m_nScreenBounds[ 2 ];
int screenTall = m_nScreenBounds[ 3 ];
// start by getting the first token
char token[512];
pMem = ParseFile(pMem, token, NULL);
while (token[0])
{
bool bAccepted = true;
// should be 'event'
if (stricmp(token, "event"))
{
Warning("Couldn't parse script file: expected 'event', found '%s'\n", token);
return false;
}
// get the event name
pMem = ParseFile(pMem, token, NULL);
if (strlen(token) < 1)
{
Warning("Couldn't parse script file: expected <event name>, found nothing\n");
return false;
}
int seqIndex;
UtlSymId_t nameIndex = g_ScriptSymbols.AddString(token);
// Create a new sequence
seqIndex = m_Sequences.AddToTail();
AnimSequence_t &seq = m_Sequences[seqIndex];
seq.name = nameIndex;
seq.duration = 0.0f;
// get the open brace or a conditional
pMem = ParseFile(pMem, token, NULL);
if ( Q_stristr( token, "[$" ) )
{
bAccepted = EvaluateConditional( token );
// now get the open brace
pMem = ParseFile(pMem, token, NULL);
}
if (stricmp(token, "{"))
{
Warning("Couldn't parse script sequence '%s': expected '{', found '%s'\n", g_ScriptSymbols.String(seq.name), token);
return false;
}
// walk the commands
while (token && token[0])
{
// get the command type
pMem = ParseFile(pMem, token, NULL);
// skip out when we hit the end of the sequence
if (token[0] == '}')
break;
// create a new command
int cmdIndex = seq.cmdList.AddToTail();
AnimCommand_t &animCmd = seq.cmdList[cmdIndex];
memset(&animCmd, 0, sizeof(animCmd));
if (!stricmp(token, "animate"))
{
animCmd.commandType = CMD_ANIMATE;
// parse out the animation commands
AnimCmdAnimate_t &cmdAnimate = animCmd.cmdData.animate;
// panel to manipulate
pMem = ParseFile(pMem, token, NULL);
cmdAnimate.panel = g_ScriptSymbols.AddString(token);
// variable to change
pMem = ParseFile(pMem, token, NULL);
cmdAnimate.variable = g_ScriptSymbols.AddString(token);
// target value
pMem = ParseFile(pMem, token, NULL);
if (cmdAnimate.variable == m_sPosition)
{
// Get first token
SetupPosition( cmdAnimate, &cmdAnimate.target.a, token, screenWide );
// Get second token from "token"
char token2[32];
char *psz = ParseFile(token, token2, NULL);
psz = ParseFile(psz, token2, NULL);
psz = token2;
// Position Y goes into ".b"
SetupPosition( cmdAnimate, &cmdAnimate.target.b, psz, screenTall );
}
else if ( cmdAnimate.variable == m_sXPos )
{
// XPos and YPos both use target ".a"
SetupPosition( cmdAnimate, &cmdAnimate.target.a, token, screenWide );
}
else if ( cmdAnimate.variable == m_sYPos )
{
// XPos and YPos both use target ".a"
SetupPosition( cmdAnimate, &cmdAnimate.target.a, token, screenTall );
}
else
{
// parse the floating point values right out
if (0 == sscanf(token, "%f %f %f %f", &cmdAnimate.target.a, &cmdAnimate.target.b, &cmdAnimate.target.c, &cmdAnimate.target.d))
{
//=============================================================================
// HPE_BEGIN:
// [pfreese] Improved handling colors not defined in scheme
//=============================================================================
// could be referencing a value in the scheme file, lookup
Color default_invisible_black(0, 0, 0, 0);
Color col = scheme->GetColor(token, default_invisible_black);
// we don't have a way of seeing if the color is not declared in the scheme, so we use this
// silly method of trying again with a different default to see if we get the fallback again
if (col == default_invisible_black)
{
Color error_pink(255, 0, 255, 255); // make it extremely obvious if a scheme lookup fails
col = scheme->GetColor(token, error_pink);
// commented out for Soldier/Demo release...(getting spammed in console)
// we'll try to figure this out after the update is out
// if (col == error_pink)
// {
// Warning("Missing color in scheme: %s\n", token);
// }
}
//=============================================================================
// HPE_END
//=============================================================================
cmdAnimate.target.a = col[0];
cmdAnimate.target.b = col[1];
cmdAnimate.target.c = col[2];
cmdAnimate.target.d = col[3];
}
}
// fix up scale
if (cmdAnimate.variable == m_sSize)
{
if (IsProportional())
{
cmdAnimate.target.a = static_cast<float>( vgui::scheme()->GetProportionalScaledValueEx(GetScheme(), cmdAnimate.target.a) );
cmdAnimate.target.b = static_cast<float>( vgui::scheme()->GetProportionalScaledValueEx(GetScheme(), cmdAnimate.target.b) );
}
}
else if (cmdAnimate.variable == m_sWide ||
cmdAnimate.variable == m_sTall )
{
if (IsProportional())
{
// Wide and tall both use.a
cmdAnimate.target.a = static_cast<float>( vgui::scheme()->GetProportionalScaledValueEx(GetScheme(), cmdAnimate.target.a) );
}
}
// interpolation function
pMem = ParseFile(pMem, token, NULL);
if (!stricmp(token, "Accel"))
{
cmdAnimate.interpolationFunction = INTERPOLATOR_ACCEL;
}
else if (!stricmp(token, "Deaccel"))
{
cmdAnimate.interpolationFunction = INTERPOLATOR_DEACCEL;
}
else if ( !stricmp(token, "Spline"))
{
cmdAnimate.interpolationFunction = INTERPOLATOR_SIMPLESPLINE;
}
else if (!stricmp(token,"Pulse"))
{
cmdAnimate.interpolationFunction = INTERPOLATOR_PULSE;
// frequencey
pMem = ParseFile(pMem, token, NULL);
cmdAnimate.interpolationParameter = (float)atof(token);
}
else if ( !stricmp( token, "Flicker"))
{
cmdAnimate.interpolationFunction = INTERPOLATOR_FLICKER;
// noiseamount
pMem = ParseFile(pMem, token, NULL);
cmdAnimate.interpolationParameter = (float)atof(token);
}
else if (!stricmp(token, "Bounce"))
{
cmdAnimate.interpolationFunction = INTERPOLATOR_BOUNCE;
}
else
{
cmdAnimate.interpolationFunction = INTERPOLATOR_LINEAR;
}
// start time
pMem = ParseFile(pMem, token, NULL);
cmdAnimate.startTime = (float)atof(token);
// duration
pMem = ParseFile(pMem, token, NULL);
cmdAnimate.duration = (float)atof(token);
// check max duration
if (cmdAnimate.startTime + cmdAnimate.duration > seq.duration)
{
seq.duration = cmdAnimate.startTime + cmdAnimate.duration;
}
}
else if (!stricmp(token, "runevent"))
{
animCmd.commandType = CMD_RUNEVENT;
pMem = ParseFile(pMem, token, NULL);
animCmd.cmdData.runEvent.event = g_ScriptSymbols.AddString(token);
pMem = ParseFile(pMem, token, NULL);
animCmd.cmdData.runEvent.timeDelay = (float)atof(token);
}
else if (!stricmp(token, "stopevent"))
{
animCmd.commandType = CMD_STOPEVENT;
pMem = ParseFile(pMem, token, NULL);
animCmd.cmdData.runEvent.event = g_ScriptSymbols.AddString(token);
pMem = ParseFile(pMem, token, NULL);
animCmd.cmdData.runEvent.timeDelay = (float)atof(token);
}
else if (!stricmp(token, "StopPanelAnimations"))
{
animCmd.commandType = CMD_STOPPANELANIMATIONS;
pMem = ParseFile(pMem, token, NULL);
animCmd.cmdData.runEvent.event = g_ScriptSymbols.AddString(token);
pMem = ParseFile(pMem, token, NULL);
animCmd.cmdData.runEvent.timeDelay = (float)atof(token);
}
else if (!stricmp(token, "stopanimation"))
{
animCmd.commandType = CMD_STOPANIMATION;
pMem = ParseFile(pMem, token, NULL);
animCmd.cmdData.runEvent.event = g_ScriptSymbols.AddString(token);
pMem = ParseFile(pMem, token, NULL);
animCmd.cmdData.runEvent.variable = g_ScriptSymbols.AddString(token);
pMem = ParseFile(pMem, token, NULL);
animCmd.cmdData.runEvent.timeDelay = (float)atof(token);
}
else if ( !stricmp( token, "SetFont" ))
{
animCmd.commandType = CMD_SETFONT;
// Panel name
pMem = ParseFile(pMem, token, NULL);
animCmd.cmdData.runEvent.event = g_ScriptSymbols.AddString(token);
// Font parameter
pMem = ParseFile(pMem, token, NULL);
animCmd.cmdData.runEvent.variable = g_ScriptSymbols.AddString(token);
// Font name from scheme
pMem = ParseFile(pMem, token, NULL);
animCmd.cmdData.runEvent.variable2 = g_ScriptSymbols.AddString(token);
// Set time
pMem = ParseFile(pMem, token, NULL);
animCmd.cmdData.runEvent.timeDelay = (float)atof(token);
}
else if ( !stricmp( token, "SetTexture" ))
{
animCmd.commandType = CMD_SETTEXTURE;
// Panel name
pMem = ParseFile(pMem, token, NULL);
animCmd.cmdData.runEvent.event = g_ScriptSymbols.AddString(token);
// Texture Id
pMem = ParseFile(pMem, token, NULL);
animCmd.cmdData.runEvent.variable = g_ScriptSymbols.AddString(token);
// material name
pMem = ParseFile(pMem, token, NULL);
animCmd.cmdData.runEvent.variable2 = g_ScriptSymbols.AddString(token);
// Set time
pMem = ParseFile(pMem, token, NULL);
animCmd.cmdData.runEvent.timeDelay = (float)atof(token);
}
else if ( !stricmp( token, "SetString" ))
{
animCmd.commandType = CMD_SETSTRING;
// Panel name
pMem = ParseFile(pMem, token, NULL);
animCmd.cmdData.runEvent.event = g_ScriptSymbols.AddString(token);
// String variable name
pMem = ParseFile(pMem, token, NULL);
animCmd.cmdData.runEvent.variable = g_ScriptSymbols.AddString(token);
// String value to set
pMem = ParseFile(pMem, token, NULL);
animCmd.cmdData.runEvent.variable2 = g_ScriptSymbols.AddString(token);
// Set time
pMem = ParseFile(pMem, token, NULL);
animCmd.cmdData.runEvent.timeDelay = (float)atof(token);
}
else
{
Warning("Couldn't parse script sequence '%s': expected <anim command>, found '%s'\n", g_ScriptSymbols.String(seq.name), token);
return false;
}
// Look ahead one token for a conditional
char *peek = ParseFile(pMem, token, NULL);
if ( Q_stristr( token, "[$" ) )
{
if ( !EvaluateConditional( token ) )
{
seq.cmdList.Remove( cmdIndex );
}
pMem = peek;
}
}
if ( bAccepted )
{
// Attempt to find a collision in the sequences, replacing the old one if found
int seqIterator;
for ( seqIterator = 0; seqIterator < m_Sequences.Count()-1; seqIterator++ )
{
if ( m_Sequences[seqIterator].name == nameIndex )
{
// Get rid of it, we're overriding it
m_Sequences.Remove( seqIndex );
break;
}
}
}
else
{
// Dump the entire sequence
m_Sequences.Remove( seqIndex );
}
// get the next token, if any
pMem = ParseFile(pMem, token, NULL);
}
return true;
}
//-----------------------------------------------------------------------------
// Purpose: checks all posted animation events, firing if time
//-----------------------------------------------------------------------------
void AnimationController::UpdatePostedMessages(bool bRunToCompletion)
{
CUtlVector<RanEvent_t> eventsRanThisFrame;
// check all posted messages
for (int i = 0; i < m_PostedMessages.Count(); i++)
{
PostedMessage_t &msgRef = m_PostedMessages[i];
if (m_flCurrentTime < msgRef.startTime && !bRunToCompletion)
continue;
// take a copy of th message
PostedMessage_t msg = msgRef;
// remove the event
// do this before handling the message because the message queue may be messed with
m_PostedMessages.Remove(i);
// reset the count, start the whole queue again
i = -1;
// handle the event
switch (msg.commandType)
{
case CMD_RUNEVENT:
{
RanEvent_t curEvent;
curEvent.event = msg.event;
curEvent.pParent = msg.parent.Get();
// run the event, but only if we haven't already run it this frame, for this parent
if (!eventsRanThisFrame.HasElement(curEvent))
{
eventsRanThisFrame.AddToTail(curEvent);
RunCmd_RunEvent(msg);
}
}
break;
case CMD_STOPEVENT:
RunCmd_StopEvent(msg);
break;
case CMD_STOPPANELANIMATIONS:
RunCmd_StopPanelAnimations(msg);
break;
case CMD_STOPANIMATION:
RunCmd_StopAnimation(msg);
break;
case CMD_SETFONT:
RunCmd_SetFont(msg);
break;
case CMD_SETTEXTURE:
RunCmd_SetTexture(msg);
break;
case CMD_SETSTRING:
RunCmd_SetString( msg );
break;
}
}
}
//-----------------------------------------------------------------------------
// Purpose: runs the current animations
//-----------------------------------------------------------------------------
void AnimationController::UpdateActiveAnimations(bool bRunToCompletion)
{
// iterate all the currently active animations
for (int i = 0; i < m_ActiveAnimations.Count(); i++)
{
ActiveAnimation_t &anim = m_ActiveAnimations[i];
// see if the anim is ready to start
if (m_flCurrentTime < anim.startTime && !bRunToCompletion)
continue;
if (!anim.panel.Get())
{
// panel is gone, remove the animation
m_ActiveAnimations.Remove(i);
--i;
continue;
}
if (!anim.started && !bRunToCompletion)
{
// start the animation from the current value
anim.startValue = GetValue(anim, anim.panel, anim.variable);
anim.started = true;
// Msg( "Starting animation of %s => %.2f (seq: %s) (%s)\n", g_ScriptSymbols.String(anim.variable), anim.endValue.a, g_ScriptSymbols.String(anim.seqName), anim.panel->GetName());
}
// get the interpolated value
Value_t val;
if (m_flCurrentTime >= anim.endTime || bRunToCompletion)
{
// animation is done, use the last value
val = anim.endValue;
}
else
{
// get the interpolated value
val = GetInterpolatedValue(anim.interpolator, anim.interpolatorParam, m_flCurrentTime, anim.startTime, anim.endTime, anim.startValue, anim.endValue);
}
// apply the new value to the panel
SetValue(anim, anim.panel, anim.variable, val);
// Msg( "Animate value: %s => %.2f for panel '%s'\n", g_ScriptSymbols.String(anim.variable), val.a, anim.panel->GetName());
// see if we can remove the animation
if (m_flCurrentTime >= anim.endTime || bRunToCompletion)
{
m_ActiveAnimations.Remove(i);
--i;
}
}
}
bool AnimationController::UpdateScreenSize()
{
// get our screen size (for left/right/center alignment)
int screenWide, screenTall;
int sx = 0, sy = 0;
if ( m_hSizePanel != 0 )
{
ipanel()->GetSize( m_hSizePanel, screenWide, screenTall );
ipanel()->GetPos( m_hSizePanel, sx, sy );
}
else
{
surface()->GetScreenSize(screenWide, screenTall);
}
bool changed = m_nScreenBounds[ 0 ] != sx ||
m_nScreenBounds[ 1 ] != sy ||
m_nScreenBounds[ 2 ] != screenWide ||
m_nScreenBounds[ 3 ] != screenTall;
m_nScreenBounds[ 0 ] = sx;
m_nScreenBounds[ 1 ] = sy;
m_nScreenBounds[ 2 ] = screenWide;
m_nScreenBounds[ 3 ] = screenTall;
return changed;
}
//-----------------------------------------------------------------------------
// Purpose: runs a frame of animation
//-----------------------------------------------------------------------------
void AnimationController::UpdateAnimations( float currentTime )
{
m_flCurrentTime = currentTime;
if ( UpdateScreenSize() && m_ScriptFileNames.Count() )
{
RunAllAnimationsToCompletion();
ReloadScriptFile();
}
UpdatePostedMessages(false);
UpdateActiveAnimations(false);
}
//-----------------------------------------------------------------------------
// Purpose: plays all animations to completion instantly
//-----------------------------------------------------------------------------
void AnimationController::RunAllAnimationsToCompletion()
{
// Msg( "AnimationController::RunAllAnimationsToCompletion()\n" );
UpdatePostedMessages(true);
UpdateActiveAnimations(true);
}
//-----------------------------------------------------------------------------
// Purpose: Stops all current animations
//-----------------------------------------------------------------------------
void AnimationController::CancelAllAnimations()
{
// Msg( "AnimationController::CancelAllAnimations()\n" );
m_ActiveAnimations.RemoveAll();
m_PostedMessages.RemoveAll();
}
//-----------------------------------------------------------------------------
// Purpose: produces an interpolated value
//-----------------------------------------------------------------------------
AnimationController::Value_t AnimationController::GetInterpolatedValue(int interpolator, float interpolatorParam, float currentTime, float startTime, float endTime, Value_t &startValue, Value_t &endValue)
{
// calculate how far we are into the animation
float pos = (currentTime - startTime) / (endTime - startTime);
// adjust the percentage through by the interpolation function
switch (interpolator)
{
case INTERPOLATOR_ACCEL:
pos *= pos;
break;
case INTERPOLATOR_DEACCEL:
pos = sqrtf(pos);
break;
case INTERPOLATOR_SIMPLESPLINE:
pos = SimpleSpline( pos );
break;
case INTERPOLATOR_PULSE:
// Make sure we end at 1.0, so use cosine
pos = 0.5f + 0.5f * ( cos( pos * 2.0f * M_PI * interpolatorParam ) );
break;
case INTERPOLATOR_FLICKER:
if ( RandomFloat( 0.0f, 1.0f ) < interpolatorParam )
{
pos = 1.0f;
}
else
{
pos = 0.0f;
}
break;
case INTERPOLATOR_BOUNCE:
{
// fall from startValue to endValue, bouncing a few times and settling out at endValue
const float hit1 = 0.33f;
const float hit2 = 0.67f;
const float hit3 = 1.0f;
if ( pos < hit1 )
{
pos = 1.0f - sin( M_PI * pos / hit1 );
}
else if ( pos < hit2 )
{
pos = 0.5f + 0.5f * ( 1.0f - sin( M_PI * ( pos - hit1 ) / ( hit2 - hit1 ) ) );
}
else
{
pos = 0.8f + 0.2f * ( 1.0f - sin( M_PI * ( pos - hit2 ) / ( hit3 - hit2 ) ) );
}
break;
}
case INTERPOLATOR_LINEAR:
default:
break;
}
// calculate the value
Value_t val;
val.a = ((endValue.a - startValue.a) * pos) + startValue.a;
val.b = ((endValue.b - startValue.b) * pos) + startValue.b;
val.c = ((endValue.c - startValue.c) * pos) + startValue.c;
val.d = ((endValue.d - startValue.d) * pos) + startValue.d;
return val;
}
//-----------------------------------------------------------------------------
// Purpose: sets that the script file should be reloaded each time a script is ran
// used for development
//-----------------------------------------------------------------------------
void AnimationController::SetAutoReloadScript(bool state)
{
m_bAutoReloadScript = state;
}
//-----------------------------------------------------------------------------
// Purpose: starts an animation sequence script
//-----------------------------------------------------------------------------
bool AnimationController::StartAnimationSequence(const char *sequenceName)
{
// We support calling an animation on elements that are not the calling
// panel's children. Use the base parent to start the search.
return StartAnimationSequence( GetParent(), sequenceName );
}
//-----------------------------------------------------------------------------
// Purpose: starts an animation sequence script
//-----------------------------------------------------------------------------
bool AnimationController::StartAnimationSequence(Panel *pWithinParent, const char *sequenceName)
{
Assert( pWithinParent );
if (m_bAutoReloadScript)
{
// Reload the script files
ReloadScriptFile();
}
// lookup the symbol for the name
UtlSymId_t seqName = g_ScriptSymbols.Find(sequenceName);
if (seqName == UTL_INVAL_SYMBOL)
return false;
// Msg("Starting animation sequence %s\n", sequenceName);
// remove the existing command from the queue
RemoveQueuedAnimationCommands(seqName, pWithinParent);
// look through for the sequence
int i;
for (i = 0; i < m_Sequences.Count(); i++)
{
if (m_Sequences[i].name == seqName)
break;
}
if (i >= m_Sequences.Count())
return false;
// execute the sequence
for (int cmdIndex = 0; cmdIndex < m_Sequences[i].cmdList.Count(); cmdIndex++)
{
ExecAnimationCommand(seqName, m_Sequences[i].cmdList[cmdIndex], pWithinParent);
}
return true;
}
//-----------------------------------------------------------------------------
// Purpose: Runs a custom command from code, not from a script file
//-----------------------------------------------------------------------------
void AnimationController::RunAnimationCommand(vgui::Panel *panel, const char *variable, float targetValue, float startDelaySeconds, float duration, Interpolators_e interpolator, float animParameter /* = 0 */ )
{
// clear any previous animations of this variable
UtlSymId_t var = g_ScriptSymbols.AddString(variable);
RemoveQueuedAnimationByType(panel, var, UTL_INVAL_SYMBOL);
// build a new animation
AnimCmdAnimate_t animateCmd;
memset(&animateCmd, 0, sizeof(animateCmd));
animateCmd.panel = 0;
animateCmd.variable = var;
animateCmd.target.a = targetValue;
animateCmd.interpolationFunction = interpolator;
animateCmd.interpolationParameter = animParameter;
animateCmd.startTime = startDelaySeconds;
animateCmd.duration = duration;
// start immediately
StartCmd_Animate(panel, 0, animateCmd);
}
//-----------------------------------------------------------------------------
// Purpose: Runs a custom command from code, not from a script file
//-----------------------------------------------------------------------------
void AnimationController::RunAnimationCommand(vgui::Panel *panel, const char *variable, Color targetValue, float startDelaySeconds, float duration, Interpolators_e interpolator, float animParameter /* = 0 */ )
{
// clear any previous animations of this variable
UtlSymId_t var = g_ScriptSymbols.AddString(variable);
RemoveQueuedAnimationByType(panel, var, UTL_INVAL_SYMBOL);
// build a new animation
AnimCmdAnimate_t animateCmd;
memset(&animateCmd, 0, sizeof(animateCmd));
animateCmd.panel = 0;
animateCmd.variable = var;
animateCmd.target.a = targetValue[0];
animateCmd.target.b = targetValue[1];
animateCmd.target.c = targetValue[2];
animateCmd.target.d = targetValue[3];
animateCmd.interpolationFunction = interpolator;
animateCmd.interpolationParameter = animParameter;
animateCmd.startTime = startDelaySeconds;
animateCmd.duration = duration;
// start immediately
StartCmd_Animate(panel, 0, animateCmd);
}
//-----------------------------------------------------------------------------
// Purpose: gets the length of an animation sequence, in seconds
//-----------------------------------------------------------------------------
float AnimationController::GetAnimationSequenceLength(const char *sequenceName)
{
// lookup the symbol for the name
UtlSymId_t seqName = g_ScriptSymbols.Find(sequenceName);
if (seqName == UTL_INVAL_SYMBOL)
return 0.0f;
// look through for the sequence
int i;
for (i = 0; i < m_Sequences.Count(); i++)
{
if (m_Sequences[i].name == seqName)
break;
}
if (i >= m_Sequences.Count())
return 0.0f;
// sequence found
return m_Sequences[i].duration;
}
//-----------------------------------------------------------------------------
// Purpose: removes an existing set of commands from the queue
//-----------------------------------------------------------------------------
void AnimationController::RemoveQueuedAnimationCommands(UtlSymId_t seqName, Panel *pWithinParent)
{
// Msg("Removing queued anims for sequence %s\n", g_ScriptSymbols.String(seqName));
// remove messages posted by this sequence
// if pWithinParent is specified, remove only messages under that parent
{for (int i = 0; i < m_PostedMessages.Count(); i++)
{
if ( ( m_PostedMessages[i].seqName == seqName ) &&
( !pWithinParent || ( m_PostedMessages[i].parent == pWithinParent ) ) )
{
m_PostedMessages.Remove(i);
--i;
}
}}
// remove all animations
// if pWithinParent is specified, remove only animations under that parent
for (int i = 0; i < m_ActiveAnimations.Count(); i++)
{
if ( m_ActiveAnimations[i].seqName != seqName )
continue;
// panel this anim is on, m_ActiveAnimations[i].panel
if ( pWithinParent )
{
Panel *animPanel = m_ActiveAnimations[i].panel;
if ( !animPanel )
continue;
Panel *foundPanel = pWithinParent->FindChildByName(animPanel->GetName(),true);
if ( foundPanel != animPanel )
continue;
}
m_ActiveAnimations.Remove(i);
--i;
}
}
//-----------------------------------------------------------------------------
// Purpose: removes the specified queued animation
//-----------------------------------------------------------------------------
void AnimationController::RemoveQueuedAnimationByType(vgui::Panel *panel, UtlSymId_t variable, UtlSymId_t sequenceToIgnore)
{
for (int i = 0; i < m_ActiveAnimations.Count(); i++)
{
if (m_ActiveAnimations[i].panel == panel && m_ActiveAnimations[i].variable == variable && m_ActiveAnimations[i].seqName != sequenceToIgnore)
{
// Msg("Removing queued anim %s::%s::%s\n", g_ScriptSymbols.String(m_ActiveAnimations[i].seqName), panel->GetName(), g_ScriptSymbols.String(variable));
m_ActiveAnimations.Remove(i);
break;
}
}
}
//-----------------------------------------------------------------------------
// Purpose: runs a single line of the script
//-----------------------------------------------------------------------------
void AnimationController::ExecAnimationCommand(UtlSymId_t seqName, AnimCommand_t &animCommand, Panel *pWithinParent)
{
if (animCommand.commandType == CMD_ANIMATE)
{
StartCmd_Animate(seqName, animCommand.cmdData.animate, pWithinParent);
}
else
{
// post the command to happen at the specified time
PostedMessage_t &msg = m_PostedMessages[m_PostedMessages.AddToTail()];
msg.seqName = seqName;
msg.commandType = animCommand.commandType;
msg.event = animCommand.cmdData.runEvent.event;
msg.variable = animCommand.cmdData.runEvent.variable;
msg.variable2 = animCommand.cmdData.runEvent.variable2;
msg.startTime = m_flCurrentTime + animCommand.cmdData.runEvent.timeDelay;
msg.parent = pWithinParent;
}
}
//-----------------------------------------------------------------------------
// Purpose: starts a variable animation
//-----------------------------------------------------------------------------
void AnimationController::StartCmd_Animate(UtlSymId_t seqName, AnimCmdAnimate_t &cmd, Panel *pWithinParent)
{
Assert( pWithinParent );
if ( !pWithinParent )
return;
// make sure the child exists
Panel *panel = pWithinParent->FindChildByName(g_ScriptSymbols.String(cmd.panel),true);
if ( !panel )
{
// Check the parent
Panel *parent = GetParent();
if ( !Q_stricmp( parent->GetName(), g_ScriptSymbols.String(cmd.panel) ) )
{
panel = parent;
}
}
if (!panel)
return;
StartCmd_Animate(panel, seqName, cmd);
}
//-----------------------------------------------------------------------------
// Purpose: Starts an animation command for the specified panel
//-----------------------------------------------------------------------------
void AnimationController::StartCmd_Animate(Panel *panel, UtlSymId_t seqName, AnimCmdAnimate_t &cmd)
{
// build a command to add to the animation queue
ActiveAnimation_t &anim = m_ActiveAnimations[m_ActiveAnimations.AddToTail()];
anim.panel = panel;
anim.seqName = seqName;
anim.variable = cmd.variable;
anim.interpolator = cmd.interpolationFunction;
anim.interpolatorParam = cmd.interpolationParameter;
// timings
anim.startTime = m_flCurrentTime + cmd.startTime;
anim.endTime = anim.startTime + cmd.duration;
// values
anim.started = false;
anim.endValue = cmd.target;
anim.align = cmd.align;
}
//-----------------------------------------------------------------------------
// Purpose: a posted message to run another event
//-----------------------------------------------------------------------------
void AnimationController::RunCmd_RunEvent(PostedMessage_t &msg)
{
StartAnimationSequence(msg.parent.Get(), g_ScriptSymbols.String(msg.event));
}
//-----------------------------------------------------------------------------
// Purpose: a posted message to stop another event
//-----------------------------------------------------------------------------
void AnimationController::RunCmd_StopEvent(PostedMessage_t &msg)
{
RemoveQueuedAnimationCommands(msg.event, msg.parent);
}
//-----------------------------------------------------------------------------
// Purpose: a posted message to stop all animations relevant to a specified panel
//-----------------------------------------------------------------------------
void AnimationController::RunCmd_StopPanelAnimations(PostedMessage_t &msg)
{
Panel *panel = FindSiblingByName(g_ScriptSymbols.String(msg.event));
Assert(panel != NULL);
if (!panel)
return;
// loop through all the active animations cancelling any that
// are operating on said panel, except for the event specified
for (int i = 0; i < m_ActiveAnimations.Count(); i++)
{
if (m_ActiveAnimations[i].panel == panel && m_ActiveAnimations[i].seqName != msg.seqName)
{
m_ActiveAnimations.Remove(i);
--i;
}
}
}
//-----------------------------------------------------------------------------
// Purpose: a posted message to stop animations of a specific type
//-----------------------------------------------------------------------------
void AnimationController::RunCmd_StopAnimation(PostedMessage_t &msg)
{
Panel *panel = FindSiblingByName(g_ScriptSymbols.String(msg.event));
Assert(panel != NULL);
if (!panel)
return;
RemoveQueuedAnimationByType(panel, msg.variable, msg.seqName);
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void AnimationController::RunCmd_SetFont( PostedMessage_t &msg )
{
Panel *parent = msg.parent.Get();
if ( !parent )
{
parent = GetParent();
}
Panel *panel = parent->FindChildByName(g_ScriptSymbols.String(msg.event), true);
Assert(panel != NULL);
if (!panel)
return;
KeyValues *inputData = new KeyValues(g_ScriptSymbols.String(msg.variable));
inputData->SetString(g_ScriptSymbols.String(msg.variable), g_ScriptSymbols.String(msg.variable2));
if (!panel->SetInfo(inputData))
{
// Assert(!("Unhandlable var in AnimationController::SetValue())"));
}
inputData->deleteThis();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void AnimationController::RunCmd_SetTexture( PostedMessage_t &msg )
{
Panel *panel = FindSiblingByName(g_ScriptSymbols.String(msg.event));
Assert(panel != NULL);
if (!panel)
return;
KeyValues *inputData = new KeyValues(g_ScriptSymbols.String(msg.variable));
inputData->SetString(g_ScriptSymbols.String(msg.variable), g_ScriptSymbols.String(msg.variable2));
if (!panel->SetInfo(inputData))
{
// Assert(!("Unhandlable var in AnimationController::SetValue())"));
}
inputData->deleteThis();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void AnimationController::RunCmd_SetString( PostedMessage_t &msg )
{
Panel *panel = FindSiblingByName(g_ScriptSymbols.String(msg.event));
Assert(panel != NULL);
if (!panel)
return;
KeyValues *inputData = new KeyValues(g_ScriptSymbols.String(msg.variable));
inputData->SetString(g_ScriptSymbols.String(msg.variable), g_ScriptSymbols.String(msg.variable2));
if (!panel->SetInfo(inputData))
{
// Assert(!("Unhandlable var in AnimationController::SetValue())"));
}
inputData->deleteThis();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
int AnimationController::GetRelativeOffset( AnimAlign_t& align, bool xcoord )
{
if ( !align.relativePosition )
return 0;
Panel *panel = GetParent()->FindChildByName(g_ScriptSymbols.String(align.alignPanel), true);
if ( !panel )
return 0;
int x, y, w, h;
panel->GetBounds( x, y, w, h );
int offset =0;
switch ( align.alignment )
{
default:
case a_northwest:
offset = xcoord ? x : y;
break;
case a_north:
offset = xcoord ? ( x + w ) / 2 : y;
break;
case a_northeast:
offset = xcoord ? ( x + w ) : y;
break;
case a_west:
offset = xcoord ? x : ( y + h ) / 2;
break;
case a_center:
offset = xcoord ? ( x + w ) / 2 : ( y + h ) / 2;
break;
case a_east:
offset = xcoord ? ( x + w ) : ( y + h ) / 2;
break;
case a_southwest:
offset = xcoord ? x : ( y + h );
break;
case a_south:
offset = xcoord ? ( x + w ) / 2 : ( y + h );
break;
case a_southeast:
offset = xcoord ? ( x + w ) : ( y + h );
break;
}
return offset;
}
//-----------------------------------------------------------------------------
// Purpose: Gets the specified value from a panel
//-----------------------------------------------------------------------------
AnimationController::Value_t AnimationController::GetValue(ActiveAnimation_t& anim, Panel *panel, UtlSymId_t var)
{
Value_t val = { 0, 0, 0, 0 };
if (var == m_sPosition)
{
int x, y;
panel->GetPos(x, y);
val.a = (float)(x - GetRelativeOffset( anim.align, true ) );
val.b = (float)(y - GetRelativeOffset( anim.align, false ) );
}
else if (var == m_sSize)
{
int w, t;
panel->GetSize(w, t);
val.a = (float)w;
val.b = (float)t;
}
else if (var == m_sFgColor)
{
Color col = panel->GetFgColor();
val.a = col[0];
val.b = col[1];
val.c = col[2];
val.d = col[3];
}
else if (var == m_sBgColor)
{
Color col = panel->GetBgColor();
val.a = col[0];
val.b = col[1];
val.c = col[2];
val.d = col[3];
}
else if ( var == m_sXPos )
{
int x, y;
panel->GetPos(x, y);
val.a = (float)( x - GetRelativeOffset( anim.align, true ) );
}
else if ( var == m_sYPos )
{
int x, y;
panel->GetPos(x, y);
val.a = (float)( y - GetRelativeOffset( anim.align, false ) );
}
else if ( var == m_sWide )
{
int w, h;
panel->GetSize(w, h);
val.a = (float)w;
}
else if ( var == m_sTall )
{
int w, h;
panel->GetSize(w, h);
val.a = (float)h;
}
else
{
KeyValues *outputData = new KeyValues(g_ScriptSymbols.String(var));
if (panel->RequestInfo(outputData))
{
// find the var and lookup it's type
KeyValues *kv = outputData->FindKey(g_ScriptSymbols.String(var));
if (kv && kv->GetDataType() == KeyValues::TYPE_FLOAT)
{
val.a = kv->GetFloat();
val.b = 0.0f;
val.c = 0.0f;
val.d = 0.0f;
}
else if (kv && kv->GetDataType() == KeyValues::TYPE_COLOR)
{
Color col = kv->GetColor();
val.a = col[0];
val.b = col[1];
val.c = col[2];
val.d = col[3];
}
}
else
{
// Assert(!("Unhandlable var in AnimationController::GetValue())"));
}
outputData->deleteThis();
}
return val;
}
//-----------------------------------------------------------------------------
// Purpose: Sets a value in a panel
//-----------------------------------------------------------------------------
void AnimationController::SetValue(ActiveAnimation_t& anim, Panel *panel, UtlSymId_t var, Value_t &value)
{
if (var == m_sPosition)
{
int x = (int)value.a + GetRelativeOffset( anim.align, true );
int y = (int)value.b + GetRelativeOffset( anim.align, false );
panel->SetPos(x, y);
}
else if (var == m_sSize)
{
panel->SetSize((int)value.a, (int)value.b);
}
else if (var == m_sFgColor)
{
Color col = panel->GetFgColor();
col[0] = (unsigned char)value.a;
col[1] = (unsigned char)value.b;
col[2] = (unsigned char)value.c;
col[3] = (unsigned char)value.d;
panel->SetFgColor(col);
}
else if (var == m_sBgColor)
{
Color col = panel->GetBgColor();
col[0] = (unsigned char)value.a;
col[1] = (unsigned char)value.b;
col[2] = (unsigned char)value.c;
col[3] = (unsigned char)value.d;
panel->SetBgColor(col);
}
else if (var == m_sXPos)
{
int newx = (int)value.a + GetRelativeOffset( anim.align, true );
int x, y;
panel->GetPos( x, y );
x = newx;
panel->SetPos(x, y);
}
else if (var == m_sYPos)
{
int newy = (int)value.a + GetRelativeOffset( anim.align, false );
int x, y;
panel->GetPos( x, y );
y = newy;
panel->SetPos(x, y);
}
else if (var == m_sWide)
{
int neww = (int)value.a;
int w, h;
panel->GetSize( w, h );
w = neww;
panel->SetSize(w, h);
}
else if (var == m_sTall)
{
int newh = (int)value.a;
int w, h;
panel->GetSize( w, h );
h = newh;
panel->SetSize(w, h);
}
else
{
KeyValues *inputData = new KeyValues(g_ScriptSymbols.String(var));
// set the custom value
if (value.b == 0.0f && value.c == 0.0f && value.d == 0.0f)
{
// only the first value is non-zero, so probably just a float value
inputData->SetFloat(g_ScriptSymbols.String(var), value.a);
}
else
{
// multivalue, set the color
Color col((unsigned char)value.a, (unsigned char)value.b, (unsigned char)value.c, (unsigned char)value.d);
inputData->SetColor(g_ScriptSymbols.String(var), col);
}
if (!panel->SetInfo(inputData))
{
// Assert(!("Unhandlable var in AnimationController::SetValue())"));
}
inputData->deleteThis();
}
}
// Hooks between panels and animation controller system
class CPanelAnimationDictionary
{
public:
CPanelAnimationDictionary() : m_PanelAnimationMapPool( 32 )
{
}
~CPanelAnimationDictionary()
{
m_PanelAnimationMapPool.Clear();
}
PanelAnimationMap *FindOrAddPanelAnimationMap( char const *className );
PanelAnimationMap *FindPanelAnimationMap( char const *className );
void PanelAnimationDumpVars( char const *className );
private:
struct PanelAnimationMapDictionaryEntry
{
PanelAnimationMap *map;
};
char const *StripNamespace( char const *className );
void PanelAnimationDumpMap( PanelAnimationMap *map, bool recursive );
CClassMemoryPool< PanelAnimationMap > m_PanelAnimationMapPool;
CUtlDict< PanelAnimationMapDictionaryEntry, int > m_AnimationMaps;
};
char const *CPanelAnimationDictionary::StripNamespace( char const *className )
{
if ( !Q_strnicmp( className, "vgui::", 6 ) )
{
return className + 6;
}
return className;
}
//-----------------------------------------------------------------------------
// Purpose: Find but don't add mapping
//-----------------------------------------------------------------------------
PanelAnimationMap *CPanelAnimationDictionary::FindPanelAnimationMap( char const *className )
{
int lookup = m_AnimationMaps.Find( StripNamespace( className ) );
if ( lookup != m_AnimationMaps.InvalidIndex() )
{
return m_AnimationMaps[ lookup ].map;
}
return NULL;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
PanelAnimationMap *CPanelAnimationDictionary::FindOrAddPanelAnimationMap( char const *className )
{
PanelAnimationMap *map = FindPanelAnimationMap( className );
if ( map )
return map;
Panel::InitPropertyConverters();
PanelAnimationMapDictionaryEntry entry;
entry.map = (PanelAnimationMap *)m_PanelAnimationMapPool.Alloc();
m_AnimationMaps.Insert( StripNamespace( className ), entry );
return entry.map;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CPanelAnimationDictionary::PanelAnimationDumpMap( PanelAnimationMap *map, bool recursive )
{
if ( map->pfnClassName )
{
Msg( "%s\n", (*map->pfnClassName)() );
}
int c = map->entries.Count();
for ( int i = 0; i < c; i++ )
{
PanelAnimationMapEntry *e = &map->entries[ i ];
Msg( " %s %s\n", e->type(), e->name() );
}
if ( recursive && map->baseMap )
{
PanelAnimationDumpMap( map->baseMap, recursive );
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CPanelAnimationDictionary::PanelAnimationDumpVars( char const *className )
{
if ( className == NULL )
{
for ( int i = 0; i < (int)m_AnimationMaps.Count(); i++ )
{
PanelAnimationDumpMap( m_AnimationMaps[ i ].map, false );
}
}
else
{
PanelAnimationMap *map = FindPanelAnimationMap( className );
if ( map )
{
PanelAnimationDumpMap( map, true );
}
else
{
Msg( "No such Panel Animation class %s\n", className );
}
}
}
//-----------------------------------------------------------------------------
// Purpose: singleton accessor
//-----------------------------------------------------------------------------
CPanelAnimationDictionary& GetPanelAnimationDictionary()
{
static CPanelAnimationDictionary dictionary;
return dictionary;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
PanelAnimationMap *FindOrAddPanelAnimationMap( char const *className )
{
return GetPanelAnimationDictionary().FindOrAddPanelAnimationMap( className );
}
//-----------------------------------------------------------------------------
// Purpose: Find but don't add mapping
//-----------------------------------------------------------------------------
PanelAnimationMap *FindPanelAnimationMap( char const *className )
{
return GetPanelAnimationDictionary().FindPanelAnimationMap( className );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void PanelAnimationDumpVars( char const *className )
{
GetPanelAnimationDictionary().PanelAnimationDumpVars( className );
}