MoofWars/TCommandTimer.cp

/*
    File:       TCommandTimer.cp
 
    Contains:   The command section accomplishes two things for the game -- first, providing
                a standard clock to which all actions can be synchonized, and second, grabbing
                the state of the keyboard to pass along to those game parts that need it.
 
                This code implements a time manager task that periodically grabs the state of
                the keyboard.  It stores these in a buffer where the main application task can
                retrieve it.
 
                We create a buffer for two seconds worth of commands, although in practice we'll
                almost never need more than about a 1/10 of a second.
 
                Eventually, this code will also probably need to deal with networking tasks.
 
    Written by:     
 
    Copyright:  Copyright © 1996-1999 by Apple Computer, Inc., All Rights Reserved.
 
                You may incorporate this Apple sample source code into your program(s) without
                restriction. This Apple sample source code has been provided "AS IS" and the
                responsibility for its operation is yours. You are not permitted to redistribute
                this Apple sample source code as "Apple sample source code" after having made
                changes. If you're going to re-distribute the source, we require that you make
                it clear in the source that the code was descended from Apple sample source
                code, but that you've made changes.
 
    Change History (most recent first):
                7/2/1999    Karl Groethe    Updated for Metrowerks Codewarror Pro 2.1
                
                8/7/98      Timothy Carroll Moved code over from VBL to time manager task.
                
                1/26/97     Timothy Carroll Now explicitly includes MoofWars.h
                
                8/15/96     Timothy Carroll Initial Release      
                
 
*/
#include <Errors.h>
#include <Timer.h>
 
#include "AppConditionals.h"
#include "Error Macros.h"
#include "TCommandTimer.h"
 
#if !TARGET_RT_MAC_CFM
#error This code is not useable on classic 68K architectures.
#endif
 
struct CommandTimerData
{
    TMTask      fTimer;
    Boolean     fRunning;
    SInt16      fFramesPerSecond;
    SInt16      fBufferSize;
    TInputState *fCommandBuffer;
    // these hold our current locations in the buffer
    SInt16      fInCommandIndex;
    SInt16      fOutCommandIndex;
    TimerUPP    fCommandTimerUPP;
    SInt32      timerDelay;
} *CommandTimerPtr;
 
// The actual timer task that gets called to retrieve inputs.
void CommandTimer (TMTaskPtr recPtr);
 
 
TCommandTimer::TCommandTimer (void)
{
    timerData = NULL;
}
 
TCommandTimer::~TCommandTimer (void)
{
    if (timerData == NULL)
        return;
    
    if (timerData->fRunning)
        this->AcceptCommands(false);
        
    if (timerData->fCommandTimerUPP != NULL)
        DisposeRoutineDescriptor (timerData->fCommandTimerUPP);
        
    if (timerData->fCommandBuffer != NULL)
        DisposePtr ((Ptr) timerData->fCommandBuffer);
    
    DisposePtr ((Ptr) timerData);
    timerData = NULL;
}
 
OSStatus TCommandTimer::Initialize (SInt16 framesDesired)
{
    OSStatus theErr = noErr;
    
#if qDebugging
    if (timerData != NULL)
        SIGNAL_ERROR("\pError: TCommandTimer may only be initialized once");
#endif
 
    // Allocate all the data structures
    
    timerData = (CommandTimerData *) NewPtrClear (sizeof (CommandTimerData));
    theErr = MemError();
    FAIL_OSERR (theErr, "\pError: Failed to allocate CommandTimerData")
    
    timerData->fFramesPerSecond = framesDesired;
    timerData->fBufferSize = 2 * timerData->fFramesPerSecond;
    
    if (framesDesired == 0) // Special case -- we're not actually using a timer!
        goto cleanup;
    
    timerData->fCommandBuffer = (TInputState *) NewPtrClear (sizeof (TInputState) * timerData->fBufferSize);
    theErr = MemError();
    FAIL_OSERR (theErr, "\pError: Failed to allocate commands buffer")
        
    timerData->fCommandTimerUPP = NewTimerProc (CommandTimer);
    if (timerData->fCommandTimerUPP == NULL)
        SIGNAL_ERROR ("\pError: Unable to allocate the timer UPP")
    
    // Set up the time manager data.  We don't actually install the task until we activate the timer.
        
    timerData->fTimer.tmAddr = timerData->fCommandTimerUPP;
    //timerData->timerDelay = - (1000000/timerData->fFramesPerSecond); // amount of time per frame in microseconds
    timerData->timerDelay = 1000/timerData->fFramesPerSecond;
    goto cleanup;
 
error:
    if (theErr == noErr)
        theErr = paramErr;
    // everything else is cleaned up by the regular destructor.
    
cleanup:
 
    return theErr;
}
 
 
Boolean TCommandTimer::IsAcceptingCommands (void)
{
#if qDebugging
    if (timerData == NULL)
        SIGNAL_ERROR("\pError: TCommandTimer must be initialized!");
#endif
 
    if (timerData->fFramesPerSecond == 0)
        return true;
        
    return timerData->fRunning;
    
    error:
    
    return false;
    
}
 
void TCommandTimer::AcceptCommands (Boolean commands)
{
#if qDebugging
    if (timerData == NULL)
        SIGNAL_ERROR("\pError: TCommandTimer must be initialized!");
#endif
 
    if (timerData->fFramesPerSecond == 0)
    {
        return;
    }
 
    if (timerData->fRunning == commands)
        return;
    
    
    if (commands)
    {
        timerData->fRunning = true;
        // tn1063 documents that we need to clear these fields to make sure nothing wacky happens.
        timerData->fTimer.qLink = NULL;
        timerData->fTimer.tmReserved = 0;
        InsXTime ((QElemPtr) &timerData->fTimer);
        PrimeTime ((QElemPtr) &timerData->fTimer, timerData->timerDelay);
    }
    else
    {
        timerData->fRunning = false;
        RmvTime((QElemPtr) &timerData->fTimer);
    }
 
    error:
        return;
}
        
void TCommandTimer::FlushCommands(void)
{
#if qDebugging
    if (timerData == NULL)
        SIGNAL_ERROR("\pError: TCommandTimer must be initialized!");
        
    if (timerData->fRunning)
        SIGNAL_ERROR ("\pError: TCommandTimer must not be accepting commands");
#endif
 
    if (timerData->fFramesPerSecond == 0)
    {
        return;
    }
 
    timerData-> fInCommandIndex = timerData->fOutCommandIndex = 0;
error:
    return;
}
        
Boolean TCommandTimer::IsCommandWaiting (void)
{
#if qDebugging
    if (timerData == NULL)
        SIGNAL_ERROR("\pError: TCommandTimer must be initialized!");
#endif
 
    if (timerData->fFramesPerSecond == 0)
    {
        return true;
    }
 
    if (timerData->fInCommandIndex != timerData->fOutCommandIndex)
        return true;
error:
    return false;
}
 
Boolean TCommandTimer::RetrieveCommand (TInputState *command)
{
    Boolean peek;
    
    if (timerData->fFramesPerSecond == 0)
    {
        GetKeys (command->keys);
        return true;
    }
    
    peek = this->PeekCommand (command);
    
    if (peek)
        timerData->fOutCommandIndex = (timerData->fOutCommandIndex +1) % (timerData->fBufferSize);
    
    return peek;
    
}
 
Boolean TCommandTimer::PeekCommand (TInputState *command)
{
    SInt16 index;
    
#if qDebugging
    if (timerData == NULL)
        SIGNAL_ERROR("\pError: TCommandTimer must be initialized!");
#endif
 
    if (timerData->fFramesPerSecond == 0)
    {
        GetKeys (command->keys);
        return true;
    }
 
    if (timerData->fInCommandIndex == timerData->fOutCommandIndex)
        return false;
    
    index = (timerData->fOutCommandIndex +1) % (timerData->fBufferSize);
    *command = (timerData->fCommandBuffer)[index];
    
    return true;
    
error:
    return false;
}
    
 
void CommandTimer (TMTaskPtr recPtr)
{
    CommandTimerData *timerData = (CommandTimerData *) recPtr;
    SInt16 index;
    
    if (!timerData->fRunning)
        return;
    
    index = (timerData->fInCommandIndex+1) % timerData->fBufferSize;
    
    if (index != timerData->fOutCommandIndex)
    {
        timerData->fInCommandIndex = index;
        GetKeys (((timerData->fCommandBuffer)[index]).keys);
    }
 
    // Reset the timer
    
    PrimeTime((QElemPtr) &timerData->fTimer, timerData->timerDelay);
 
}