//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// // // Purpose: // //=============================================================================// #pragma warning( disable : 4244 ) // conversion from 'double' to 'float', possible loss of data #include #include #include #include #include #include #include "filesystem.h" #include "filesystem_helpers.h" #include #include #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 // for SRC #include #include 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( 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 , 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( vgui::scheme()->GetProportionalScaledValueEx(GetScheme(), cmdAnimate.target.a) ); cmdAnimate.target.b = static_cast( 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( 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 , 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 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 ); }