//============================================================================ //---------------------------------------------------------------------------- // GameOver.c //---------------------------------------------------------------------------- //============================================================================ #include "PLToolUtils.h" #include "PLPasStr.h" #include "PLEventQueue.h" #include "PLKeyEncoding.h" #include "PLTimeTaggedVOSEvent.h" #include "Externs.h" #include "Environ.h" #include "FontManager.h" #include "FontFamily.h" #include "InputManager.h" #include "MainWindow.h" #include "Objects.h" #include "RectUtils.h" #include "Utilities.h" #define kNumCountDownFrames 16 #define kPageFrames 14 #define kPagesPictID 1990 #define kPagesMaskID 1989 #define kLettersPictID 1988 #define kMilkywayPictID 1021 typedef struct { Rect dest, was; short frame, counter; Boolean stuck; } pageType, *pagePtr; void DoGameOverStarAnimation (void); void SetUpFinalScreen (void); void InitDiedGameOver (void); void HandlePages (void); void DrawPages (void); pageType pages[8]; Rect pageSrcRect, pageSrc[kPageFrames], lettersSrc[8], angelSrcRect; DrawSurface *pageSrcMap, *gameOverSrcMap, *angelSrcMap; DrawSurface *pageMaskMap, *angelMaskMap; short countDown, stopPages, pagesStuck; Boolean gameOver; extern Rect justRoomsRect; extern short splashOriginH, splashOriginV, numWork2Main; extern short numBack2Work; extern Boolean playing, shadowVisible, demoGoing; //============================================================== Functions //-------------------------------------------------------------- DoGameOver // Handles a game over. This is a game over where the player hasÉ // completed the house. void DoGameOver (void) { DrawSurface *surface = mainWindow->GetDrawSurface(); playing = false; SetUpFinalScreen(); SetPort((GrafPtr)mainWindow); ColorRect(surface, mainWindowRect, 244); DoGameOverStarAnimation(); if (!TestHighScore()) RedrawSplashScreen(); } //-------------------------------------------------------------- SetUpFinalScreen // This sets up the game over screen (again, this function is for whenÉ // the player completes the house). void SetUpFinalScreen (void) { Rect tempRect; Str255 tempStr, subStr; short count, offset, i, textDown; char wasState; DrawSurface *surface = workSrcMap; ColorRect(surface, workSrcRect, 244); QSetRect(&tempRect, 0, 0, 640, 460); CenterRectInRect(&tempRect, &workSrcRect); LoadScaledGraphic(surface, kMilkywayPictID, &tempRect); textDown = tempRect.top; if (textDown < 0) textDown = 0; PasStringCopy((*thisHouse)->trailer, tempStr); count = 0; do { GetLineOfText(tempStr, count, subStr); offset = ((thisMac.screen.right - thisMac.screen.left) - surface->MeasureString(subStr)) / 2; surface->SetApplicationFont(12, PortabilityLayer::FontFamilyFlag_Bold); surface->SetForeColor(PortabilityLayer::RGBAColor::Create(0, 0, 0, 255)); const Point textShadowPos = Point::Create(offset + 1, textDown + 33 + (count * 20)); surface->DrawString(textShadowPos, subStr, true); surface->SetForeColor(PortabilityLayer::RGBAColor::Create(255, 255, 255, 255)); const Point textPos = Point::Create(offset, textDown + 32 + (count * 20)); surface->DrawString(textPos, subStr, true); count++; } while (subStr[0] > 0); CopyRectWorkToBack(&workSrcRect); for (i = 0; i < 5; i++) // initialize the falling stars { pages[i].dest = starSrc[0]; QOffsetRect(&pages[i].dest, workSrcRect.right + RandomInt(workSrcRect.right / 5) + (workSrcRect.right/ 4) * i, RandomInt(workSrcRect.bottom) - workSrcRect.bottom / 2); pages[i].was = pages[i].dest; pages[i].frame = RandomInt(6); } } //-------------------------------------------------------------- DoGameOverStarAnimation // This handles the falling stars and the flying angel when a playerÉ // completes a house. void DoGameOverStarAnimation (void) { #define kStarFalls 8 TimeTaggedVOSEvent theEvent; Rect angelDest; long nextLoop; short which, i, count, pass; Boolean noInteruption; angelDest = angelSrcRect; QOffsetRect(&angelDest, -96, 0); noInteruption = true; nextLoop = TickCount() + 2; count = 0; pass = 0; FlushEvents(everyEvent, 0); while (noInteruption) { if ((angelDest.left % 32) == 0) // add a star { PlayPrioritySound(kMysticSound, kMysticPriority); which = angelDest.left / 32; which = which % 5; ZeroRectCorner(&pages[which].dest); QOffsetRect(&pages[which].dest, angelDest.left, angelDest.bottom); if (count < (which + 1)) count = which + 1; } for (i = 0; i < count; i++) { pages[i].frame++; if (pages[i].frame >= 6) pages[i].frame = 0; CopyMask((BitMap *)*GetGWorldPixMap(bonusSrcMap), (BitMap *)*GetGWorldPixMap(bonusMaskMap), (BitMap *)*GetGWorldPixMap(workSrcMap), &starSrc[pages[i].frame], &starSrc[pages[i].frame], &pages[i].dest); pages[i].was = pages[i].dest; pages[i].was.top -= kStarFalls; AddRectToWorkRectsWhole(&pages[i].was); AddRectToBackRects(&pages[i].dest); if (pages[i].dest.top < workSrcRect.bottom) QOffsetRect(&pages[i].dest, 0, kStarFalls); } if (angelDest.left <= (workSrcRect.right + 2)) { CopyMask((BitMap *)*GetGWorldPixMap(angelSrcMap), (BitMap *)*GetGWorldPixMap(angelMaskMap), (BitMap *)*GetGWorldPixMap(workSrcMap), &angelSrcRect, &angelSrcRect, &angelDest); angelDest.left -= 2; AddRectToWorkRectsWhole(&angelDest); angelDest.left += 2; AddRectToBackRects(&angelDest); QOffsetRect(&angelDest, 2, 0); pass = 0; } CopyRectsQD(); numWork2Main = 0; numBack2Work = 0; do { const KeyDownStates *theKeys = PortabilityLayer::InputManager::GetInstance()->GetKeys(); if ((theKeys->IsSet(PL_KEY_EITHER_SPECIAL(kControl))) || (theKeys->IsSet(PL_KEY_EITHER_SPECIAL(kAlt))) || (theKeys->IsSet(PL_KEY_EITHER_SPECIAL(kShift)))) noInteruption = false; if (PortabilityLayer::EventQueue::GetInstance()->Dequeue(&theEvent)) { if (theEvent.IsLMouseDownEvent() || theEvent.IsKeyDownEvent()) noInteruption = false; } Delay(1, nullptr); } while (TickCount() < nextLoop); nextLoop = TickCount() + 2; if (pass < 80) pass++; else { WaitForInputEvent(5); noInteruption = false; } } } //-------------------------------------------------------------- FlagGameOver // Called to indicate (flag) that a game is over. Actual game overÉ // sequence comes up after a short delay. void FlagGameOver (void) { gameOver = true; countDown = kNumCountDownFrames; SetMusicalMode(kPlayWholeScoreMode); } //-------------------------------------------------------------- InitDiedGameOver // This is called when a game is over due to the fact that the playerÉ // lost their last glider (died), not due to getting through the entireÉ // house. This function initializes the strucures/variables. void InitDiedGameOver (void) { #define kPageSpacing 40 #define kPageRightOffset 128 #define kPageBackUp 128 short i; PLError_t theErr; QSetRect(&pageSrcRect, 0, 0, 25, 32 * 8); theErr = CreateOffScreenGWorld(&gameOverSrcMap, &pageSrcRect, kPreferredPixelFormat); LoadGraphic(gameOverSrcMap, kLettersPictID); QSetRect(&pageSrcRect, 0, 0, 32, 32 * kPageFrames); theErr = CreateOffScreenGWorld(&pageSrcMap, &pageSrcRect, kPreferredPixelFormat); LoadGraphic(pageSrcMap, kPagesPictID); theErr = CreateOffScreenGWorld(&pageMaskMap, &pageSrcRect, GpPixelFormats::kBW1); LoadGraphic(pageMaskMap, kPagesMaskID); for (i = 0; i < kPageFrames; i++) // initialize src page rects { QSetRect(&pageSrc[i], 0, 0, 32, 32); QOffsetRect(&pageSrc[i], 0, 32 * i); } for (i = 0; i < 8; i++) // initialize dest page rects { QSetRect(&pages[i].dest, 0, 0, 32, 32); CenterRectInRect(&pages[i].dest, &thisMac.screen); QOffsetRect(&pages[i].dest, -thisMac.screen.left, -thisMac.screen.top); if (i < 4) QOffsetRect(&pages[i].dest, -kPageSpacing * (4 - i), 0); else QOffsetRect(&pages[i].dest, kPageSpacing * (i - 3), 0); QOffsetRect(&pages[i].dest, (thisMac.screen.right - thisMac.screen.left) / -2, (thisMac.screen.right - thisMac.screen.left) / -2); if (pages[i].dest.left % 2 == 1) QOffsetRect(&pages[i].dest, 1, 0); pages[i].was = pages[i].dest; pages[i].frame = 0; pages[i].counter = RandomInt(32); pages[i].stuck = false; } for (i = 0; i < 8; i++) { QSetRect(&lettersSrc[i], 0, 0, 25, 32); QOffsetRect(&lettersSrc[i], 0, 32 * i); } pagesStuck = 0; stopPages = ((thisMac.screen.bottom - thisMac.screen.top) / 2) - 16; } //-------------------------------------------------------------- HandlePages // This handles the pieces of paper that blow across the screen. void HandlePages (void) { short i; for (i = 0; i < 8; i++) { if ((pages[i].dest.bottom + RandomInt(8)) > stopPages) { pages[i].frame = 0; if (!pages[i].stuck) { pages[i].dest.right = pages[i].dest.left + 25; pages[i].stuck = true; pagesStuck++; } } else { if (pages[i].frame == 0) { pages[i].counter--; if (pages[i].counter <= 0) pages[i].frame = 1; } else if (pages[i].frame == 7) { pages[i].counter--; if (pages[i].counter <= 0) { pages[i].frame = 8; if (RandomInt(2) == 0) PlayPrioritySound(kPaper3Sound, kPapersPriority); else PlayPrioritySound(kPaper4Sound, kPapersPriority); } else QOffsetRect(&pages[i].dest, 10, 10); } else { pages[i].frame++; switch (pages[i].frame) { case 5: QOffsetRect(&pages[i].dest, 6, 6); break; case 6: QOffsetRect(&pages[i].dest, 8, 8); break; case 7: QOffsetRect(&pages[i].dest, 8, 8); pages[i].counter = RandomInt(4) + 4; break; case 8: case 9: QOffsetRect(&pages[i].dest, 8, 8); break; case 10: QOffsetRect(&pages[i].dest, 6, 6); break; case kPageFrames: QOffsetRect(&pages[i].dest, 8, 0); pages[i].frame = 0; pages[i].counter = RandomInt(8) + 8; if (RandomInt(2) == 0) PlayPrioritySound(kPaper1Sound, kPapersPriority); else PlayPrioritySound(kPaper2Sound, kPapersPriority); break; } } } } } //-------------------------------------------------------------- DrawPages // This function does the drawing for the pieces of paper that blowÉ // across the screen. void DrawPages (void) { short i; for (i = 0; i < 8; i++) { if (pages[i].stuck) { CopyBitsConstrained((BitMap *)*GetGWorldPixMap(gameOverSrcMap), (BitMap *)*GetGWorldPixMap(workSrcMap), &lettersSrc[i], &pages[i].dest, srcCopy, &justRoomsRect); } else { CopyMask((BitMap *)*GetGWorldPixMap(pageSrcMap), (BitMap *)*GetGWorldPixMap(pageMaskMap), (BitMap *)*GetGWorldPixMap(workSrcMap), &pageSrc[pages[i].frame], &pageSrc[pages[i].frame], &pages[i].dest); } QUnionSimilarRect(&pages[i].dest, &pages[i].was, &pages[i].was); AddRectToWorkRects(&pages[i].was); AddRectToBackRects(&pages[i].dest); CopyRectsQD(); numWork2Main = 0; numBack2Work = 0; pages[i].was = pages[i].dest; } } //-------------------------------------------------------------- DoDiedGameOver // This is called when a game is over due to the fact that the playerÉ // lost their last glider (died), not due to getting through the entireÉ // house. void DoDiedGameOver (void) { TimeTaggedVOSEvent theEvent; long nextLoop; Boolean userAborted; userAborted = false; InitDiedGameOver(); CopyRectMainToWork(&workSrcRect); CopyRectMainToBack(&workSrcRect); FlushEvents(everyEvent, 0); nextLoop = TickCount() + 2; while (pagesStuck < 8) { HandlePages(); DrawPages(); do { const KeyDownStates *theKeys = PortabilityLayer::InputManager::GetInstance()->GetKeys(); if ((theKeys->IsSet(PL_KEY_EITHER_SPECIAL(kAlt))) || (theKeys->IsSet(PL_KEY_EITHER_SPECIAL(kControl))) || (theKeys->IsSet(PL_KEY_EITHER_SPECIAL(kShift)))) { pagesStuck = 8; userAborted = true; } if (PortabilityLayer::EventQueue::GetInstance()->Dequeue(&theEvent)) { if (theEvent.IsLMouseDownEvent() || theEvent.IsKeyDownEvent()) { pagesStuck = 8; userAborted = true; } } Delay(1, nullptr); } while (TickCount() < nextLoop); nextLoop = TickCount() + 2; } DisposeGWorld(pageSrcMap); pageSrcMap = nil; DisposeGWorld(pageMaskMap); pageMaskMap = nil; DisposeGWorld(gameOverSrcMap); gameOverSrcMap = nil; playing = false; if (demoGoing) { if (!userAborted) WaitForInputEvent(1); } else { if (!userAborted) WaitForInputEvent(10); TestHighScore(); } RedrawSplashScreen(); }