// File : fwActor.c // Author : John Wojcik ( QuietBloke ) // Created : August 2000 // Desc : 2D game Framework Actor Manager. // This manager controls the actors. An actor // is simply a 2D sprite. Functions include rotation, collision detection, // and linking to animations for costumes and x,y positions. Actors can // be set either on or off stage and a lifespan allows an actor to // automatically become offstage after a set timespan. // // History : // Date Author Description // -------- --------------- ---------------------------------------- // 01/10/00 J.Wojcik Added functions to allow the scale of the actor to be set // Additional code added to resize bounding box accordingly. // Added functions to return pixel width/height of scaled actor. // 01/10/00 J.Wojcik When setting actor to be onstage se the MOVED flag so we can // re-calc its true position taking into account animations which // may have been changing while the actor was off stage. // 04/10/00 J.Wojcik When seting actor angle make sure its a valid value ( 0 - 359 ) // 05/10/00 J.Wojcik An actor now has a calculated bounding area based on a // percentage of the actual sprite size. New function allows the // bounding percentage to be given for an actor. // 05/10/00 J.Wojcik Added a new function to reset the actor List ( ie. Clear it out ). // 24/10/00 QuietBloke Added functions to set transparency for an actor #include #include // #include "fwCostum.h" // #include "fwActor.h" // #include "fwAnimat.h" // #include "fwAngle.h" #include "fw.h" typedef struct { GsSPRITE Sprite; short OnStageFlag; // Is the sprite On the stage int BaseCostumeID; // Base Costume int LifeSpan; // How many frames this actor is onstage. -1 = Infinate int CostumeIDAnimID; // Costume ID animator ( -1 if not assigned ) int CostumeIDCombined; // Actual Costume based on Base and Animated values int BaseStageXPos; // Base X Position int StageXPosAnimID; // X Position Animation ( -1 if not assigned ) int StageXPosCombined; // Actual X Position based on Base and Animated values int BaseStageYPos; // Base Y Position int StageYPosAnimID; // Y Position Animation ( -1 if not assigned ) int StageYPosCombined; // Actual Y Position based on Base and Animated Values int Angle; // Angle of the sprite short StageLayer; // Layer(s) that this actor exists on short CollisionLayers; // Layer(s) that this actor can collide with int SpriteWidth; // Width in pixels of scaled actor int SpriteHeight; // Height in pixels of scaled actor int ScaleX; // Scale of actor in x direction int ScaleY; // Scale of actor in y direction int StageScaleAnimID; // Scale Animation ( -1 if not assigned ) int BoundingType; // Type of collision detection ( currently Box or Circle ) int BoundingWidth; // Width of bounding box ( or x*x for circle ) int BoundingHeight; // Height of bounding box int BoundingScale; // % of bounding size based on actual sprite size int Actions; } Actor; Actor ActorList[MAX_ACTORS]; int NextActor = 0; void InitialiseSprite ( GsSPRITE* sprite ); void fwActorReset ( void ) { NextActor = 0; } int fwActorAdd ( int CostumeID ) { int StoreActorID; StoreActorID = NextActor; NextActor++; InitialiseSprite ( &ActorList[StoreActorID].Sprite ); // Default is to have no animations applied to the actor. ActorList[StoreActorID].BaseCostumeID = CostumeID; ActorList[StoreActorID].CostumeIDAnimID = -1; ActorList[StoreActorID].CostumeIDCombined = CostumeID; fwCostumeApplyToSprite ( &ActorList[StoreActorID].Sprite, CostumeID ); ActorList[StoreActorID].BaseStageXPos = 0; ActorList[StoreActorID].StageXPosAnimID = -1; ActorList[StoreActorID].BaseStageYPos = 0; ActorList[StoreActorID].StageYPosAnimID = -1; ActorList[StoreActorID].OnStageFlag = ON_STAGE; ActorList[StoreActorID].LifeSpan = -1; // The actor by default is normal scale. ActorList[StoreActorID].ScaleX = 4096; ActorList[StoreActorID].ScaleY = 4096; ActorList[StoreActorID].StageScaleAnimID = -1; ActorList[StoreActorID].SpriteWidth = ActorList[StoreActorID].Sprite.w; ActorList[StoreActorID].SpriteHeight = ActorList[StoreActorID].Sprite.h; // The defualt bounding setup is Box, 100% sprite size ActorList[StoreActorID].BoundingWidth = ActorList[StoreActorID].SpriteWidth; ActorList[StoreActorID].BoundingHeight = ActorList[StoreActorID].SpriteHeight; ActorList[StoreActorID].BoundingScale = 100; // The default is the actor lives on layer zero // which means nothing can collide with it and // it is also set to collide with nothing. ActorList[StoreActorID].StageLayer = STAGE_LAYER_0; ActorList[StoreActorID].CollisionLayers = STAGE_LAYER_0; if (NextActor >= MAX_ACTORS ) { printf ( "Max actors exceeded" ); } return ( StoreActorID ); } void fwActorPos ( int ActorID, int xpos, int ypos ) { ActorList[ActorID].BaseStageXPos = xpos; ActorList[ActorID].BaseStageYPos = ypos; // Mark the fact that this actor has moved ActorList[ActorID].Actions |= ACTOR_ACTION_MOVED; } int fwActorGetXPos ( int ActorID ) { return ( ActorList[ActorID].StageXPosCombined ); } int fwActorGetYPos ( int ActorID ) { return ( ActorList[ActorID].StageYPosCombined ); } void fwActorSetOnStage ( int ActorID, short OnStageFlag ) { ActorList[ActorID].OnStageFlag = OnStageFlag; // Mark the fact that this actor has moved // W MUST assume something has changed since the actor was last onstage. ActorList[ActorID].Actions |= ACTOR_ACTION_MOVED; // pretend to 'require the sprite' which will FORCE the newly displayed sprite // to re-calc its actual stage and physical positions fwActorGet ( ActorID ); } void fwActorSetBoundingScale ( int ActorID, int NewScale ) { ActorList[ActorID].BoundingScale = NewScale; // Let the framework think we have changed scale. This will force it to re-calc the bounding area ActorList[ActorID].Actions |= ACTOR_ACTION_RESIZED; } void fwActorSetTransparency ( int ActorID, int NewTransparency ) { unsigned long BitFlags; BitFlags = 0x00000000; if ( NewTransparency & ACTOR_TRANSPARENCY_ON ) { BitFlags |= (1<<30); } if ( NewTransparency & ACTOR_TRANSPARENCY_ADD ) { BitFlags |= (1<<28); } // if ( NewTransparency & ACTOR_TRANSPARENCY_ADD_AND_HALF ) // { // BitFlags |= 0x00000000; // } if ( NewTransparency & ACTOR_TRANSPARENCY_SUBTRACT ) { BitFlags |= (2<<28); } if ( NewTransparency & ACTOR_TRANSPARENCY_ADD_QUARTER ) { BitFlags |= (3<<28); } // Clear the bits out first ActorList[ActorID].Sprite.attribute = ActorList[ActorID].Sprite.attribute & 0x8fffffff; // Now add our new ones ActorList[ActorID].Sprite.attribute |= BitFlags; } int fwActorGetOnStage ( int ActorID ) { return ( ActorList[ActorID].OnStageFlag ); } void fwActorSetLifeSpan ( int ActorID, int LifeSpan ) { ActorList[ActorID].LifeSpan = LifeSpan; } int fwActorGetLifeSpan ( int ActorID ) { return ( ActorList[ActorID].LifeSpan ); } void fwActorReduceLifeSpan ( int ActorID ) { if ( ActorList[ActorID].LifeSpan != -1 ) { ActorList[ActorID].LifeSpan--; if ( ActorList[ActorID].LifeSpan == 0 ) { fwActorSetOnStage ( ActorID, OFF_STAGE ); } } } // Changing costume applies to the actors Base costume ( Animation value not included ). // Do NOT actually change costumes in the sprite yet. This will be done // if Needed when retireving the sprite. void fwActorChangeCostume ( int ActorID, int CostumeID ) { // fwCostumeApplyToSprite ( &ActorList[ActorID].Sprite, CostumeID ); ActorList[ActorID].BaseCostumeID = CostumeID; } void fwActorCollisionLayers ( int ActorID, int CollisionLayers ) { ActorList[ActorID].CollisionLayers = CollisionLayers; } void fwActorSetLayer ( int ActorID, int StageLayer ) { ActorList[ActorID].StageLayer = StageLayer; } GsSPRITE* fwActorGet ( int ActorID ) { int CostumeIDCombinedNew; int StageXPosCombinedNew; int StageYPosCombinedNew; int StageScaleCombinedNew; Actor* currentActor; currentActor = &ActorList[ActorID]; // If the calculated costume ID has changed then we must re-assign the costume CostumeIDCombinedNew = currentActor->BaseCostumeID + fwAnimateGetValue ( currentActor->CostumeIDAnimID ); if ( CostumeIDCombinedNew != currentActor->CostumeIDCombined ) { fwCostumeApplyToSprite ( ¤tActor->Sprite, CostumeIDCombinedNew ); currentActor->CostumeIDCombined = CostumeIDCombinedNew; } // If the calculated Stage XPos has changed then mark the sprite as having moved StageXPosCombinedNew = currentActor->BaseStageXPos + fwAnimateGetValue ( currentActor->StageXPosAnimID ); if ( StageXPosCombinedNew != currentActor->StageXPosCombined ) { currentActor->StageXPosCombined = StageXPosCombinedNew; currentActor->Actions |= ACTOR_ACTION_MOVED; } // If the calculated Stage YPos has changed then mark the sprite as having moved StageYPosCombinedNew = currentActor->BaseStageYPos + fwAnimateGetValue ( currentActor->StageYPosAnimID ); if ( StageYPosCombinedNew != currentActor->StageYPosCombined ) { currentActor->StageYPosCombined = StageYPosCombinedNew; currentActor->Actions |= ACTOR_ACTION_MOVED; } // If the calculated Scale X has changed then mark the sprite as having resized StageScaleCombinedNew = currentActor->ScaleX + fwAnimateGetValue ( currentActor->StageScaleAnimID ); if ( StageScaleCombinedNew != currentActor->ScaleX ) { currentActor->Actions |= ACTOR_ACTION_RESIZED; } // If the actor has moved then we must re-calculate its physical position if( currentActor->Actions & ACTOR_ACTION_MOVED ) { currentActor->Sprite.x = ( currentActor->StageXPosCombined - fwStageGetCameraLeftPos () ) >> fwStageGetPixelShift(); currentActor->Sprite.y = ( currentActor->StageYPosCombined - fwStageGetCameraTopPos () ) >> fwStageGetPixelShift(); } if( currentActor->Actions & ACTOR_ACTION_RESIZED ) { if ( currentActor->StageScaleAnimID != -1 ) { currentActor->ScaleX = fwAnimateGetValue ( currentActor->StageScaleAnimID ); currentActor->ScaleY = fwAnimateGetValue ( currentActor->StageScaleAnimID ); } currentActor->SpriteWidth = currentActor->Sprite.w * currentActor->ScaleX / 4096; currentActor->BoundingWidth = currentActor->SpriteWidth * currentActor->BoundingScale / 100; currentActor->SpriteHeight = currentActor->Sprite.h * currentActor->ScaleY / 4096; currentActor->BoundingHeight = currentActor->SpriteHeight * currentActor->BoundingScale / 100; currentActor->Sprite.scalex = currentActor->ScaleX; currentActor->Sprite.scaley = currentActor->ScaleY; } return ( ¤tActor->Sprite ); } void InitialiseSprite ( GsSPRITE* sprite ) { // Initialize a sprite to some sort of valid default values sprite->attribute = 0; sprite->x = 50; sprite->y = 50; sprite->w = 0; sprite->h = 0; sprite->tpage = 0; sprite->u = 0; sprite->v = 0; sprite->cx = 0; sprite->cy = 0; sprite->r = 128; sprite->g = 128; sprite->b = 128; sprite->mx = 0; sprite->my = 0; sprite->scalex = 4096; sprite->scaley = 4096; sprite->rotate = 0; } int fwActorFirstOnStage ( void ) { return ( fwActorNextOnStage ( -1 ) ); } int fwActorNextOnStage ( int CurrentActor ) { Actor* currentActor; currentActor = &ActorList[CurrentActor]; while ( ++CurrentActor < NextActor ) { currentActor++; if ( currentActor->OnStageFlag == ON_STAGE ) { return ( CurrentActor ); } } return ( -1 ); } int fwActorFirstCollision ( int MyActor ) { return ( fwActorNextCollision ( MyActor, -1 ) ); } // // collisions only occur with objects which are in the actors allowed collisin layers; // int fwActorNextCollision ( int MyActor, int ActorID ) { int xDistance; int yDistance; unsigned xAllowedDistance; unsigned yAllowedDistance; Actor* currentActor; Actor* MyActorPtr; // Only bother checking if our actor is onstage // otherwise it cant collide with anything MyActorPtr = &ActorList[MyActor]; currentActor = &ActorList[ActorID]; if ( MyActorPtr->OnStageFlag && MyActorPtr->CollisionLayers != STAGE_LAYER_0 ) { while ( ++ActorID < NextActor ) { currentActor++; if ( currentActor->OnStageFlag == ON_STAGE && ActorID != MyActor && ( currentActor->StageLayer & MyActorPtr->CollisionLayers ) != 0 ) { // We have found an actor so lets see if we // have a collision. // We shall just use bounding boxes // For the sake of speed we make an assumption // that the Origin of the sprite is its center. xDistance = MyActorPtr->Sprite.x - currentActor->Sprite.x; yDistance = MyActorPtr->Sprite.y - currentActor->Sprite.y; xAllowedDistance = ( MyActorPtr->BoundingWidth + currentActor->BoundingWidth ) >> 1; yAllowedDistance = ( MyActorPtr->BoundingHeight + currentActor->BoundingHeight ) >> 1; if ( xDistance < 0 ) xDistance *= -1; if ( yDistance < 0 ) yDistance *= -1; if ( xDistance <= xAllowedDistance && yDistance <= yAllowedDistance ) return ( ActorID ); } } } return ( -1 ); } void fwActorAnimateCostume ( int ActorID, int AnimationID ) { ActorList[ActorID].CostumeIDAnimID = AnimationID; } void fwActorAnimateCostumeOff ( int ActorID ) { ActorList[ActorID].CostumeIDAnimID = -1; } void fwActorAnimateXPos ( int ActorID, int AnimationID ) { ActorList[ActorID].StageXPosAnimID = AnimationID; } int fwActorGetAnimateXPos ( int ActorID ) { return ( ActorList[ActorID].StageXPosAnimID ); } void fwActorAnimateXPosOff ( int ActorID ) { ActorList[ActorID].StageXPosAnimID = -1; } void fwActorAnimateYPos ( int ActorID, int AnimationID ) { ActorList[ActorID].StageYPosAnimID = AnimationID; } void fwActorAnimateYPosOff ( int ActorID ) { ActorList[ActorID].StageYPosAnimID = -1; } void fwActorAnimateScale ( int ActorID, int AnimationID ) { ActorList[ActorID].StageScaleAnimID = AnimationID; } // Actors have specialized animation routines which act on the x and y animations // The first are internal functions used to determine the speed and angle of the actor // based on the animation values. void fwActorSetAngle ( int ActorID, int Angle ) { // Make sure angle is valid ( 0 - 359 ) while ( Angle < 0 ) Angle+=360; while ( Angle > 359 ) Angle-=360; ActorList[ActorID].Angle = Angle; ActorList[ActorID].Sprite.rotate = Angle << 12; } int fwActorGetAngle ( int ActorID ) { return ( ActorList[ActorID].Angle ); } void fwActorAddVelocity ( int ActorID, int Angle, int Speed ) { int currentXSpeed; int currentYSpeed; int addXSpeed; int addYSpeed; currentXSpeed = fwAnimateGetChangeValue (ActorList[ActorID].StageXPosAnimID ); currentYSpeed = fwAnimateGetChangeValue (ActorList[ActorID].StageYPosAnimID ); addXSpeed = ( fwAngleGetSin ( Angle ) * Speed ) >> 8; addYSpeed = ( fwAngleGetCos ( Angle ) * Speed ) >> 8; fwAnimateSetChangeValue ( ActorList[ActorID].StageXPosAnimID, currentXSpeed + addXSpeed ); fwAnimateSetChangeValue ( ActorList[ActorID].StageYPosAnimID, currentYSpeed + addYSpeed ); } void fwActorSetVelocity ( int ActorID, int Angle, int Speed ) { int XSpeed; int YSpeed; XSpeed = ( fwAngleGetSin ( Angle ) * Speed ) >> 8; YSpeed = ( fwAngleGetCos ( Angle ) * Speed ) >> 8; fwAnimateSetChangeValue ( ActorList[ActorID].StageXPosAnimID, XSpeed ); fwAnimateSetChangeValue ( ActorList[ActorID].StageYPosAnimID, YSpeed ); } void fwActorSetScale ( int ActorID, int NewScale ) { fwActorSetXScale ( ActorID, NewScale ); fwActorSetYScale ( ActorID, NewScale ); } void fwActorSetXScale ( int ActorID, int NewXScale ) { ActorList[ActorID].ScaleX = NewXScale; // Mark the fact that this actor has resized ActorList[ActorID].Actions |= ACTOR_ACTION_RESIZED; } void fwActorSetYScale ( int ActorID, int NewYScale ) { ActorList[ActorID].ScaleY = NewYScale; // Mark the fact that this actor has resized ActorList[ActorID].Actions |= ACTOR_ACTION_RESIZED; } int fwActorGetPixelHeight ( int ActorID ) { int ActorHeight; ActorHeight = ActorList[ActorID].Sprite.h * ActorList[ActorID].ScaleY / 4096; return ( ActorHeight ); } int fwActorGetPixelWidth ( int ActorID ) { int ActorWidth; ActorWidth = ActorList[ActorID].Sprite.w * ActorList[ActorID].ScaleX / 4096; return ( ActorWidth ); } void fwActorSetRGB ( int ActorID, int red, int green, int blue ) { ActorList[ActorID].Sprite.r = red; ActorList[ActorID].Sprite.g = green; ActorList[ActorID].Sprite.b = blue; }