GameSource/ExplosionSprites.c

/*
    Explosion Sprites
    this is the code for handling the animated explosions in ZAM.
    
*/
#include "ZAM.h"
#include "SpriteFrameRates.h"
#include "GameSounds.h"
#include "ZAMProtos.h"
 
#define kFirstExpFrameNum   0
#define kLastExpFrameNum    5
 
spritePtr GetExplosionSprite(gamePtr game);
 
/* these are the globals used by this file.  They are private to this file */
/* other parts of the game deal with explosions by calling the routines in this file */
 
static spriteLayerPtr   gExplosionLayer;
static spritePtr        gExplosionSprite[kMaxExp];
 
void StartExplosionHere(gamePtr game, Fixed h, Fixed v)
/*
    This is the main explosion generation routine.  When you want one,
    just call this and pass the fixed point coordinates, and an explosion will
    start.  The game parameter is not really used any more, but the explosion list
    used to be kept in it. 
*/
{
    spritePtr   thisExp;
 
    thisExp = GetExplosionSprite(game);
 
    if(thisExp != nil) {
        thisExp->visible = false;
        SetSpriteCurrentFrame(thisExp,0);
        SetSpriteLoc(thisExp, h, v);
        thisExp->visible = true;
        (void)PlaySndAsynchChannel( kExplode, kExplosionChan, kStdPriority);
        StartSpriteAction(thisExp);
    }   
}
 
 
spritePtr GetExplosionSprite(gamePtr game)
/* 
        this function returns a spritePtr that is to be used for a new
        explosion.  At one time I had an elaborate queueing scheme the prevented searching a list,
        but then I simplified it in order to reduce complexity and track down bugs in other parts of
        the code.
        
 */
{
    short       i;
    
    for(i = 0; i < kMaxExp; i++) {
        if(!gExplosionSprite[i]->inUse) {
            gExplosionSprite[i]->inUse = true;
            return gExplosionSprite[i];
        }
    }
    
    return nil;
}
 
Boolean ExplosionLastFrameProc(spritePtr expSprite, frameCellPtr frame)
/*
    On the last frame of animation we want to hide the explosion
    so it erases properly, so explosion sprites are created with a frame callback
    on the last frame of animation.
*/
{
 
    expSprite->visible = false;                         // hide it
    expSprite->inUse = false;                           // free it up
    StopSpriteAction(expSprite);                        // stop its clock
    expSprite->spriteFlags |= kNeedsToBeErased;         // flag it for updating
    expSprite->ownerLayer->layerFlags |= kLayerDirty;   // be sure the layer is flagged too
    
    return false;                                       // don't want to be re-inserted in timer
}
 
 
Boolean FirstFrameProc(spritePtr explosionSprite, frameCellPtr frame)
/*
    Make sure the sprite is visible on the first frame of animation.
    I forget why I do this, I think it was because of problems when one explosion was
    cancelled to start a new one.
*/
{
    explosionSprite->visible = true;                    // just make sure we are visible
    return true;
}
 
 
void StartExplosionMoving(gamePtr game, Fixed h, Fixed v, fixPt *vel)
/*
    This routine was used when I wanted the explosions to drift around
    but I don't use it anymore.  The explosions will move though if you
    replace StartExplosionHere with this.  Pretty handy.
*/
 
{
    spritePtr   thisExp;
 
    thisExp = GetExplosionSprite(game);
 
    if(thisExp == nil)  { /* no more free explosions */
        ErrMsg("\pNo More Explosions are available");
    } else {
        thisExp->visible = false;
        /* prevent any drawing */
        StopSpriteAction(thisExp);
        SetSpriteCurrentFrame(thisExp,0);
        SetSpriteLoc(thisExp, h, v);
        SetSpriteVelocity(thisExp, vel->h >> 1, vel->v >> 1);
        thisExp->moveTimeInterval = 100;
 
        thisExp->visible = true;
        StartSpriteAction(thisExp);
    }
}
 
void LoadExplosionSprites(gamePtr game)
/*
    This function loads all the graphics for the explosion sprites
    and also creates all of them.  The game is limited to a fixed 
    number of explosions happening at any one time.
*/
{
    OSErr           err;
    short           ref;
    frameSetPtr     expFrameSet;
    short           i;
    short           y;
    
    ref = OpenResFile("\pExplosionFrames.rsrc");
            
    err = CreateColorIconFrameSet(&expFrameSet, kBaseID, kMaxExpFrames);
    if(err != noErr) {
        ErrMsgCode("\pCreateColorIconFrameSet failed.",err);
    }
    if(err == noErr)
        SetFrameSetCTSeed(expFrameSet,game->gameCTSeed);
 
 
    err = CreateSpriteLayer(&gExplosionLayer,game->tween,game->backdrop,game->gameWind);
    if(err != noErr) {
        ErrMsgCode("\pCreateSpriteLayer failed.",err);
    }
        
    if(err == noErr) {
        err = CreateEmptySprite(gExplosionLayer,
                            &gExplosionSprite[0],   /* returned sprite here */
                            kDefaultFrameAdvance,
                            0,                                  /* time between movement */
                            kExplosionFrameTime,                /* time between frame change */
                            (long)game);                        /* refcon, ptr to game */
 
        if(err != noErr) {
            ErrMsgCode("\pCreateEmptySprite failed.",err);
        } else {
            /* install the missile frame set */
            gExplosionSprite[0]->frameList = expFrameSet;
        }
    }
    
    
    for(i = 1; i < kMaxExp; i++) {
        if(err == noErr) {
            err = CreateEmptySprite(gExplosionLayer,
                                &gExplosionSprite[i],   /* returned sprite here */
                                kDefaultFrameAdvance,
                                0,                          /* time between movement */
                                kExplosionFrameTime,        /* time between frame change */
                                (long)game);
    
            if(err != noErr) {
                ErrMsgCode("\pCreateEmptySprite failed.",err);
            }
        }
        
        if(err == noErr) {
            /* dup the frame set */
            err = CopyFrameSet(expFrameSet, &gExplosionSprite[i]->frameList);
            if(err != noErr) {
                ErrMsgCode("\pCopyFrameSet failed!",err);
            }
        }
    }
    
    for(i = 0; i < kMaxExp; i++) {
        SetSpriteFrameCallback(gExplosionSprite[i],kFirstExpFrameNum,FirstFrameProc);
        SetSpriteFrameCallback(gExplosionSprite[i],kLastExpFrameNum,ExplosionLastFrameProc);
        SetSpriteFrameRef(gExplosionSprite[i],kLastExpFrameNum, i);
        gExplosionSprite[i]->inUse = false;
    }
 
    CloseResFile(ref);
 
}