|
|
//========= Copyright � 1996-2005, 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"
#include "exprevaluator.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; 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 ];
CExpressionEvaluator ExpressionHandler;
// 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, "[$" ) ) { ExpressionHandler.Evaluate( bAccepted, 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, "[$" ) ) { bool bCondition = false; ExpressionHandler.Evaluate( bCondition, token ); if ( !bCondition ) { 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; 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 !defined( _CERT )
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 ); } } #endif // _CERT
}
//-----------------------------------------------------------------------------
// 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 ); }
|