C:/DevKitPro/!MDuel/source/spriteObject.cpp

Go to the documentation of this file.
00001 /*
00002  * Marshmallow Duel DS v2
00003  * Copyright © 2007 Sam Pospischil http://pospi.spadgos.com
00004  * 
00005  * This program is free software; you can redistribute it and/or modify
00006  * it under the terms of the GNU General Public License as published by
00007  * the Free Software Foundation; either version 2 of the License, or
00008  * (at your option) any later version.
00009  * 
00010  * This program is distributed in the hope that it will be useful,
00011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
00013  * GNU General Public License for more details.
00014  * 
00015  * You should have received a copy of the GNU General Public License
00016  * along with this program; if not, write to the Free Software
00017  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
00018  */
00019 
00020 #include "spriteObject.h"
00021 
00027 spriteObject::spriteObject(spriteManager *newsm)
00028         :  bNeedsGFXCleanup(false), attachment(NULL), isAttachment(false), staticSprite(false), sm(newsm), 
00029         parentSprite(NULL), childSprite(NULL), spriteID(-1), palleteID(-1), gfxID(-1),
00030                 #ifdef __WITHSOUND
00031                 soundChannel(-1),
00032                 #endif
00033                 x(0), y(0), vx(0), vy(0), oldx(0), oldy(0), flippedh(false), flippedv(false),
00034           oldFliph(false), oldFlipv(false), layer(0), frame(0), oldFrame(0), arbitraryAnim(0),
00035           arbitrarySpeed(0), arbitraryFrameCounter(0), arbitraryTimeCounter(0), centerx(0),
00036           centery(0), collision(COL_NONE), checkCollision(false), ignoreUntilUntouched(NULL), rotSetID(-1), 
00037           rotAngle(0), bRotating(false), UPDATEDlayer(false), UPDATEDflip(false), framesToLive(-1)
00038 {
00039         sm->gameSprites.push_back(this);
00040         #ifdef __MDDEBUG
00041         className = "spriteObject";
00042         char buffer[255];
00043         sprintf(buffer, "%d: constructed at gameSprites %d", sm->screen, sm->gameSprites.size()-1);
00044         macros::debugMessage(className, buffer);
00045         #endif
00046 }
00047 
00051 spriteObject::~spriteObject()
00052 {
00053         #ifdef __WITHSOUND
00054         stopSound();
00055         #endif
00056         deleteSprite();
00057 }
00058 
00064 spriteObject::spriteObject(const spriteObject &s)
00065 {
00066         sm = s.sm;
00067         parentSprite = s.parentSprite;
00068         childSprite = s.childSprite;            //perhaps childSprite and attachment should be freshly created?
00069         attachment = s.attachment;
00070         ignoreUntilUntouched = s.ignoreUntilUntouched;
00071         
00072         bNeedsGFXCleanup = s.bNeedsGFXCleanup;
00073         isAttachment = s.isAttachment;
00074         staticSprite = s.staticSprite;
00075         spriteID = s.spriteID;
00076         palleteID = s.palleteID;
00077         gfxID = s.gfxID;
00078         #ifdef __WITHSOUND
00079         soundChannel = s.soundChannel;
00080         #endif
00081         x = s.x; y = s.y; vx = s.vx; vy = s.vy; oldx = s.oldx; oldy = s.oldy;
00082         flippedh = s.flippedh; flippedv = s.flippedv; oldFliph = s.oldFliph; oldFlipv = s.oldFlipv;
00083         layer = s.layer; frame = s.frame; oldFrame = s.oldFrame;
00084         arbitraryAnim = s.arbitraryAnim; arbitrarySpeed = s.arbitrarySpeed;
00085         arbitraryFrameCounter = s.arbitraryFrameCounter; arbitraryTimeCounter = s.arbitraryTimeCounter;
00086         centerx = s.centerx; centery = s.centery; collision = s.collision;
00087         checkCollision = s.checkCollision;
00088         rotSetID = s.rotSetID; rotAngle = s.rotAngle; bRotating = s.bRotating;
00089         UPDATEDlayer = s.UPDATEDlayer; UPDATEDflip = s.UPDATEDflip;
00090         framesToLive = s.framesToLive;
00091 }
00092 
00109 s8 spriteObject::giveSprite(const unsigned char *spriteData, u8 sizex, u8 sizey, u8 cx, u8 cy, u8 minIndex,  s16 nx, s16 ny, u8 colorMode)
00110 {
00111         if (palleteID == -1) {
00112                 #ifdef __MDDEBUG
00113                 macros::debugMessage(ERRTEXT, "giveSprite() but no pal!", ERRORPOS);
00114                 #endif
00115                 return -1;
00116         }
00117         
00118         //sprites are always created at OFFX,OFFY and then moved afterwards to prevent flickering.
00119         
00120         #ifdef __MDDEBUG
00121         char buffer[255];
00122         sprintf(buffer, "%d: attempting to get raw sprite", sm->screen);
00123         macros::debugMessage(className, buffer);
00124         #endif
00125         //lets try this a new way...
00126         gfxID = sm->loadGFX(spriteData, sizex, sizey, colorMode);
00127         spriteID = sm->loadSpriteFromGFX(gfxID, sizex, sizey, palleteID, minIndex, colorMode, OFFX, OFFY);
00128         bNeedsGFXCleanup = true;
00129 
00130         #ifdef __MDDEBUG
00131         memset(buffer, 0, 255);
00132         sprintf(buffer, "%d: raw sprite given (gfx #%d, sprite #%d)", sm->screen, gfxID, spriteID);
00133         macros::debugMessage(className, buffer);
00134         #endif
00135         
00136         //spriteID = sm->loadSprite(spriteData, sizex, sizey, minIndex, palleteID, OFFX, OFFY, colorMode);
00137         
00138         centerx = cx;
00139         centery = cy;
00140         setPos(nx, ny);
00141         return spriteID;
00142 }
00143 
00151 s8 spriteObject::givePallete(const unsigned short *palleteData)
00152 {
00153         #ifdef __MDDEBUG
00154         char buffer[255];
00155         sprintf(buffer, "%d: attempting to get raw pallete", sm->screen);
00156         macros::debugMessage(className, buffer);
00157         #endif
00158         palleteID = sm->loadPallete((void*)palleteData);
00159         #ifdef __MDDEBUG
00160         memset(buffer, 0, 255);
00161         sprintf(buffer, "%d: raw pallete given (#%d)", sm->screen, palleteID);
00162         macros::debugMessage(className, buffer);
00163         #endif
00164         return palleteID;
00165 }
00166 
00177 bool spriteObject::updatePallete(s8 newPalID, u8 sizex, u8 sizey, u8 colorMode)
00178 {
00179         #ifdef __MDDEBUG
00180         char buffer[255];
00181         sprintf(buffer, "%d: attempting pallete update (#%d -> #%d)", sm->screen, palleteID, newPalID);
00182         macros::debugMessage(className, buffer);
00183         #endif
00184         if (gfxID != -1 && spriteID != -1 && newPalID != -1)
00185         {
00186                 palleteID = newPalID;
00187                 sm->updateSpritePallete(spriteID, gfxID, newPalID, sizex, sizey, colorMode, x>>8, y>>8);
00188                 UPDATEDflip = true;             //giving a new pallete resets the GFX
00189                 return true;
00190         }
00191         return false;
00192 }
00193 
00210 s8 spriteObject::giveGFX(u16 GFXid, u8 sizex, u8 sizey, u8 cx, u8 cy, u8 minIndex, s16 nx, s16 ny, u8 colorMode)
00211 {
00212         #ifdef __MDDEBUG
00213         char buffer[255];
00214         sprintf(buffer, "%d: attempting to get preloaded GFX", sm->screen);
00215         macros::debugMessage(className, buffer);
00216         #endif
00217         gfxID = GFXid;
00218         spriteID = sm->loadSpriteFromGFX(GFXid, sizex, sizey, palleteID, minIndex, colorMode, OFFX, OFFY);
00219         #ifdef __MDDEBUG
00220         memset(buffer, 0, 255);
00221         sprintf(buffer, "%d: preloaded GFX given (gfx #%d, sprite #%d)", sm->screen, gfxID, spriteID);
00222         macros::debugMessage(className, buffer);
00223         #endif
00224         centerx = cx;
00225         centery = cy;
00226         setPos(nx, ny);
00227         return spriteID;
00228 }
00229 
00241 void spriteObject::deleteSprite()
00242 {       
00243         #ifdef __MDDEBUG
00244         char buffer[255];
00245         sprintf(buffer, "%d: deleting (gfx #%d, sprite #%d, pallete #%d)", sm->screen, gfxID, spriteID, palleteID);
00246         macros::debugMessage(className, buffer);
00247         #endif
00248         
00249         PA_StopSpriteAnim(sm->screen, getSpriteID());
00250         PA_SetSpriteXY(sm->screen, getSpriteID(), OFFX, OFFY);
00251         
00252         //this stuff isnt necessary if the entire sprite system is resetting
00253         if (!sm->isResetting())
00254         {
00255                 if (bNeedsGFXCleanup)
00256                         PA_DeleteGfx(sm->screen, getGFXid());
00257                 if (hasGraphics())
00258                         sm->removeSprite(getSpriteID());
00259                 
00260                 //if (parentSprite != NULL)
00261                 //      parentSprite->removeChild(this);
00262                 
00263                 #ifdef __MDDEBUG
00264                 memset(buffer, 0, 255);
00265                 sprintf(buffer, "%d: clearChildren", sm->screen);
00266                 macros::debugMessage(className, buffer);
00267                 #endif
00268 
00269                 if (childSprite != NULL)
00270                         childSprite->destroy();
00271                 childSprite = NULL;
00272                 
00273                 //TODO: this method of deletion requires that children be created after parents
00274                 /*if (!childSprites.empty())
00275                 {
00276                         vector<spriteObject*>::iterator it = childSprites.begin();
00277                         while ( it != childSprites.end() )
00278                         {
00279                                 if (*it != NULL)
00280                                 {
00281                                         (*it)->destroy();
00282                                 }
00283                                 #ifdef __MDDEBUG
00284                                 else
00285                                         macros::debugMessage(ERRTEXT, "NULL childSprite found", ERRORPOS);
00286                                 #endif
00287                                 ++it;
00288                         }
00289                         childSprites.clear();
00290                 }*/
00291         }
00292         
00293         #ifdef __MDDEBUG
00294         memset(buffer, 0, 255);
00295         sprintf(buffer, "%d: deleteSprite[end]", sm->screen);
00296         macros::debugMessage(className, buffer);
00297         #endif
00298 }
00299 
00305 bool spriteObject::hasGraphics() const
00306 {
00307         return (sm->isSprite(spriteID));
00308 }
00309 
00316 void spriteObject::updateSprite()
00317 {
00318         if (hasGraphics())
00319         {
00320                 if (isAttachment && parentSprite != NULL)
00321                         setFrame(parentSprite->getFrame());
00322                 else if (arbitraryAnim != 0)
00323                 {
00324                         if (arbitraryTimeCounter == 0)
00325                         {
00326                                 frame = arbFrames[arbitraryFrameCounter];
00327                                 ++arbitraryFrameCounter;
00328                         }
00329                         ++arbitraryTimeCounter;
00330                         arbitraryTimeCounter %= arbitrarySpeed;
00331                         if (arbitraryFrameCounter == arbFrames.size())
00332                         {
00333                                 if (arbitraryAnim != 2)
00334                                         arbitraryAnim = 0;      //only played once, so stop animation and break out
00335                                 else
00336                                         arbitraryFrameCounter = 0;
00337                         }
00338                 }
00339                 
00340                 if (oldFrame != frame)
00341                 {
00342                         PA_SetSpriteAnim(sm->screen, spriteID, frame);
00343                         oldFrame = frame;
00344                 }
00345         }
00346         
00347         if (staticSprite)
00348                 return;
00349         x += vx;        //take speed into account
00350         y += vy;
00351         
00352         if (hasGraphics())      //if this is an invisible sprite then we only really care about its position vars
00353         {
00354                 if (parentSprite != NULL)
00355                 {
00356                         //TODO! this actually depends on the order sprites are created, which is BAD!
00357                         //however, given that I never plan on making hierachies of more than 2, probably doesn't matter.
00358                         setFlipped(parentSprite->flippedh, parentSprite->flippedv);
00359                         s16 extrax = 0, extray = 0;
00360                         for (spriteObject *i = this; i != NULL; i = i->parentSprite)
00361                         {
00362                                 if (i->parentSprite == NULL)
00363                                 {
00364                                         extrax += i->getx();
00365                                         extray += i->gety();
00366                                 } else {
00367                                         extrax += (i->parentSprite->flippedh ? i->getx()*-1 : i->getx());
00368                                         extray += (i->parentSprite->flippedv ? i->gety()*-1 : i->gety());
00369                                 }
00370                         }
00371                         PA_SetSpriteXY(sm->screen, spriteID, extrax-centerx, extray-centery);
00372                 }
00373                 else if (x != oldx || y != oldy)
00374                 {
00375                         PA_SetSpriteXY(sm->screen, spriteID, (x>>8), (y>>8));   //use center as registration point
00376                         oldx = x;
00377                         oldy = y;
00378                 }
00379                 
00380                 if (UPDATEDflip)
00381                 {
00382                         PA_SetSpriteHflip(sm->screen, spriteID, flippedh);
00383                         PA_SetSpriteVflip(sm->screen, spriteID, flippedv);
00384                         UPDATEDflip = false;
00385                 }
00386                 
00387                 if (UPDATEDlayer)
00388                 {
00389                         PA_SetSpritePrio(sm->screen, spriteID, layer);
00390                         UPDATEDlayer = false;
00391                 }
00392                 
00393                 if (bRotating && rotSetID != -1)
00394                         PA_SetRotsetNoZoom(sm->screen, rotSetID, rotAngle);
00395         }
00396         if (framesToLive > -1)
00397                 framesToLive--;
00398 }
00399 
00410 void spriteObject::checkCollisions(u8 indexToCheckFrom)
00411 {
00412         if (!checkCollision)
00413                 return;
00414         for (vector<spriteObject*>::iterator it = sm->gameSprites.begin()+indexToCheckFrom; it != sm->gameSprites.end(); ++it)
00415         {
00416                 bool collides = isColliding(*it);
00417                 
00418                 if (collides)
00419                         collidingWith(*it);
00420                 else if (!collides && ignoreUntilUntouched == *it)
00421                 {
00422                         ignoreUntilUntouched = NULL;
00423                 }
00424         }
00425 }
00426 
00435 void spriteObject::setTransparency(u8 transLevel)
00436 {
00437         transLevel %= 15;       //make sure it's in the proper range
00438         PA_SetSpriteMode(sm->screen, spriteID, 1);              //turn alphablending on for this one
00439         sm->setAlpha(transLevel);
00440 }
00441 
00446 void spriteObject::turnAround()
00447 {
00448         //shift slightly so that we move away from whatever made us turn around and stop it flipping again
00449         vx > 0 ? --x : ++x;
00450         
00451         vx *= -1;
00452         setFlipped(!flippedh, flippedv);
00453 }
00454 
00461 bool spriteObject::isFacing(const spriteObject *other) const
00462 {
00463         return ((getx() > other->getx() && flippedh) || 
00464                         (getx() < other->getx() && !flippedh));
00465 }
00475 bool spriteObject::inHorizPlaneOf(const spriteObject *other) const
00476 {
00477         if (isOver(other)) return false;
00478     if (isUnder(other)) return false;
00479     return true;
00480 }
00490 bool spriteObject::inVertPlaneOf(const spriteObject *other) const
00491 {
00492         if (getRight() < other->getLeft()) return false;
00493     if (getLeft() > other->getRight()) return false;
00494     return true;
00495 }
00502 bool spriteObject::isUnder(const spriteObject *other) const
00503 {
00504     return (getTop() > other->getBottom());
00505 }
00512 bool spriteObject::isOver(const spriteObject *other) const
00513 {
00514     return (getBottom() < other->getTop());
00515 }
00525 bool spriteObject::isStandingOn(const spriteObject *other) const
00526 {
00527         if (other == this)
00528                 return false;
00529         //can only be based if falling downwards or stationary
00530         if (other->isBaseable() && inVertPlaneOf(other) && getBottom() == other->getTop() && vy >= 0)
00531                 return isColliding(other, true);
00532         return false;
00533 }
00534 
00542 bool spriteObject::isBaseable() const
00543 {
00544         return (collision & COL_BASEABLE);
00545 }
00546 
00559 bool spriteObject::isColliding(const spriteObject *other, bool checkReverse) const
00560 {       
00561         if (other == this || other == ignoreUntilUntouched)
00562                 return false;
00563 
00564         if (collision == COL_NONE)                                                                                              //check for no collision
00565                 return false;
00566                 
00567         if (collision <= COL_DOWNBASEABLE && other->getBottom() != getTop())    //check for down-only collision
00568                 return false;
00569 
00570         if (collision == COL_CENTERPOINT &&                                                                             //check for centerpoint collision
00571                  (getx() < other->getRight() && getx() > other->getLeft() && 
00572                         gety() < other->getBottom() && gety() > other->getTop()))
00573                 return (!checkReverse || other->isColliding(this, false));
00574         
00575         if (!inHorizPlaneOf(other)) return false;                                                               //check for solid collision
00576         if (!inVertPlaneOf(other)) return false;
00577 
00578         if (collision != COL_PIXELPERFECT)
00579                 return (!checkReverse || other->isColliding(this, false));
00580         
00587         s32 over_bottom, over_top, over_left, over_right, over_width, over_height;
00588         s32 startSearch1, startSearch2, startSearchOffset1, startSearchOffset2;
00589     
00590     //still not ruled out, so compute area of overlap
00591     if (getBottom() > other->getBottom())
00592         over_bottom = other->getBottom();
00593     else
00594         over_bottom = getBottom();
00595     if (getTop() < other->getTop())
00596         over_top = other->getTop();
00597     else
00598         over_top = getTop();
00599     if (getRight() > other->getRight())
00600         over_right = other->getRight();
00601     else
00602         over_right = getRight();
00603     if (getLeft() < other->getLeft())
00604         over_left = other->getLeft();
00605     else
00606         over_left = getLeft();
00607 
00608     over_width = over_right - over_left;
00609     over_height = over_bottom - over_top;
00610     
00611     // Now compute starting offsets into both objects' bitmaps:
00612     startSearchOffset1 = frame * centerx*2 * centery*2;
00613     startSearchOffset2 = other->frame * other->centerx*2 * other->centery*2;
00614         
00615         //retrieve GFX data from registers
00616         //TODO: will possibly run outside of bounds when searching. How to check max length?
00617         u16* myGFX = PA_SpriteAnimP[sm->screen][getGFXid()];
00618         u16* otherGFX = PA_SpriteAnimP[other->sm->screen][other->getGFXid()];
00619     
00620     //the insaneo logic here is to not forget that the sprite may be flipped (the data isnt!)
00621     //and also that the bounds are smaller than the actual sprite (so borders must be added)
00622     //TODO: check that this actually works properly when sprites are flipped.
00623     startSearch1 = startSearchOffset1 + (over_top - (y>>8) * centerx*2) + (over_left - (x>>8));
00624     startSearch2 = startSearchOffset2 + (over_top - (other->y>>8) * other->centerx*2) + (over_left - (other->x>>8));    
00625     //still not ruled out, so scan through intersecting rectangle to find pixels that are the same.
00626     for (int i=0; i < over_height; ++i)
00627     {
00628         for (int j=0; j < over_width; ++j)
00629         {              
00630             if (myGFX[startSearch1] > 0 && otherGFX[startSearch2] > 0)
00631                 return (!checkReverse || other->isColliding(this, false));
00632             ++startSearch1;
00633             ++startSearch2;
00634         }
00635         startSearch1 += (centerx*2 - over_width);
00636         startSearch2 += (other->centerx*2 - over_width);
00637     }
00638     //searched through all pixels and no match. Bit of a waste of time, really.
00639     return false;
00640 }
00641 
00648 void spriteObject::setFlipped(bool fh, bool fv)
00649 {
00650         if (bRotating && rotSetID != -1)        //cant be flipped if rotating at the same time
00651                 return;
00652         
00653         flippedh = fh;
00654         flippedv = fv;
00655         if (oldFliph != flippedh)
00656         {
00657                 swapHBounds();
00658                 oldFliph = flippedh;
00659         }
00660         if (oldFlipv != flippedv)
00661         {
00662                 swapVBounds();
00663                 oldFlipv = flippedv;
00664         }
00665         UPDATEDflip = true;
00666 }
00667 
00668 #ifdef __WITHSOUND
00669 
00678 void spriteObject::playSound(const u8 *sound, const u32 *size, bool repeat, u8 vol)
00679 {
00680         stopSound();
00681         setSoundChannel(sm->playSound(sound, size, repeat, vol));
00682         #ifdef __MDDEBUG
00683         char buffer[255];
00684         sprintf(buffer, "%d: sound playing (channel %d, raw)", sm->screen, soundChannel);
00685         macros::debugMessage(className, buffer);
00686         #endif
00687 }
00688 
00697 void spriteObject::playSound(spriteManager::soundData *sound, bool repeat, u8 vol)
00698 {
00699         stopSound();
00700         setSoundChannel(sm->playSound(sound->data, sound->size, repeat, vol));
00701         #ifdef __MDDEBUG
00702         char buffer[255];
00703         sprintf(buffer, "%d: sound playing (channel %d, soundData)", sm->screen, soundChannel);
00704         macros::debugMessage(className, buffer);
00705         #endif
00706 }
00707 #endif
00708 
00719 void spriteObject::setParent(spriteObject *other)
00720 {
00721         parentSprite = other;
00722 }
00723 
00736 void spriteObject::addChild(spriteObject *other)
00737 {
00738         if (other != NULL)
00739         {
00740                 childSprite = other;
00741                 //childSprites.push_back(other);
00742                 other->setParent(this);
00743         }
00744         #ifdef __MDDEBUG
00745         else
00746                 macros::debugMessage(ERRTEXT, "NULL childSprite added", ERRORPOS);
00747         #endif
00748 }
00749 
00756 bool spriteObject::removeChild(/*spriteObject *other*/)
00757 {
00758         /*u8 childID = 0;
00759         bool bFound = false;
00760         for (unsigned int i=0; i<childSprites.size(); ++i)
00761         {
00762                 if (childSprites[i] == other)
00763                 {
00764                         childSprites[i]->parentSprite = NULL;
00765                         childID = i;
00766                         bFound = true;
00767                         break;
00768                 }
00769         }
00770         if (bFound)
00771                 childSprites.erase(childSprites.begin()+childID, childSprites.begin()+childID+1);
00772         #ifdef __MDDEBUG
00773         else macros::debugMessage(ERRTEXT, "Removed invalid child sprite", ERRORPOS);
00774         #endif
00775         return bFound;*/
00776         childSprite = NULL;
00777         return true;
00778 }

Generated on Tue Mar 13 23:27:53 2007 for MDuel DS by  doxygen 1.5.1-p1