From 306549db49e602e0c6fd02a282f779e254f371c1 Mon Sep 17 00:00:00 2001 From: Treeki Date: Thu, 28 Feb 2013 05:55:10 +0100 Subject: camera panning onto unlocked paths, among other fancy stuff and fixes --- src/koopatlas/camera.cpp | 116 +++++++++++++++++++++++- src/koopatlas/camera.h | 11 ++- src/koopatlas/hud.cpp | 19 ++-- src/koopatlas/hud.h | 3 + src/koopatlas/pathmanager.cpp | 202 ++++++++++++++++++++++++++++++++++-------- src/koopatlas/pathmanager.h | 17 +++- 6 files changed, 324 insertions(+), 44 deletions(-) diff --git a/src/koopatlas/camera.cpp b/src/koopatlas/camera.cpp index d98bff0..f80ebcc 100644 --- a/src/koopatlas/camera.cpp +++ b/src/koopatlas/camera.cpp @@ -31,7 +31,9 @@ dWorldCamera_c::dWorldCamera_c() { currentX = 416; currentY = -224; - zoomLevel = 2.8f; + zoomLevel = STD_ZOOM; + + followPlayer = true; } @@ -70,7 +72,31 @@ int dWorldCamera_c::onExecute() { currentX = 2040.0f; currentY = -1460.0f; zoomLevel = 3.4f; - } else { + + } else if (panning) { + // Calculate where we are +#define SMOOTHSTEP(x) ((x) * (x) * (3 - 2 * (x))) + float stepRatio = panCurrentStep / panTotalSteps; + stepRatio = 1.0f - SMOOTHSTEP(stepRatio); + //OSReport("PAN: Step %f / %f ---- Ratio: %f", panCurrentStep, panTotalSteps, stepRatio); + //OSReport("From %f, %f to %f, %f --- Zoom: %f to %f\n", panFromX, panFromY, panToX, panToY, panFromZoom, panToZoom); + + currentX = (panFromX * stepRatio) + (panToX * (1.0f - stepRatio)); + currentY = (panFromY * stepRatio) + (panToY * (1.0f - stepRatio)); + zoomLevel = (panFromZoom * stepRatio) + (panToZoom * (1.0f - stepRatio)); + //OSReport("Calculated: %f, %f with zoom %f\n", currentX, currentY, zoomLevel); + + panCurrentStep += 1.0f; + + if (panCurrentStep > panTotalSteps) { + // YAY, we reached the end + panning = false; + currentX = panToX; + currentY = panToY; + zoomLevel = panToZoom; + } + + } else if (followPlayer) { daWMPlayer_c *player = daWMPlayer_c::instance; currentX = player->pos.x; currentY = player->pos.y; @@ -83,6 +109,92 @@ int dWorldCamera_c::onExecute() { return true; } +void dWorldCamera_c::panToBounds(float left, float top, float right, float bottom) { + // Pad it a bit + left -= 64.0f; + right += 64.0f; + top -= 48.0f; + bottom += 48.0f; + + //OSReport("Panning camera to bounds %f,%f to %f,%f\n", left, top, right, bottom); + + // Figure out the centre x/y we want + float width = right - left; + float height = bottom - top; + + float desiredCentreX = left + (width * 0.5f); + float desiredCentreY = -(top + (height * 0.5f)); + + //OSReport("Size: %f x %f ; Desired Centre: %f, %f\n", width, height, desiredCentreX, desiredCentreY); + + // Our default zoom is 2.8 + float minScreenWidth = GlobalScreenWidth * 1.2f; + float minScreenHeight = GlobalScreenHeight * 1.2f; + float maxScreenWidth = GlobalScreenWidth * 4.0f; + float maxScreenHeight = GlobalScreenHeight * 4.0f; + + //OSReport("Screen Sizes: Minimum possible %f x %f ; Maximum possible %f x %f\n", minScreenWidth, minScreenHeight, maxScreenWidth, maxScreenHeight); + + // First off, gotta cap it to the ratio + float screenRatio = GlobalScreenWidth / GlobalScreenHeight; + float boundsRatio = width / height; + float correctedWidth = width, correctedHeight = height; + float desiredZoomLevel; + //OSReport("Actual screen size is %f x %f --- Screen Ratio: %f, Bounds Ratio: %f\n", GlobalScreenWidth, GlobalScreenHeight, screenRatio, boundsRatio); + + float widthScale = width / GlobalScreenWidth; + float heightScale = height / GlobalScreenHeight; + + if (heightScale > widthScale) { + // Thing is constrained on the top/bottom + desiredZoomLevel = heightScale; + } else { + // Thing is constrained on the left/right + desiredZoomLevel = widthScale; + } + + //OSReport("Desired zoom level is %f\n", desiredZoomLevel); + + // Cap the zoom + if (desiredZoomLevel < 2.0f) + desiredZoomLevel = 2.0f; + if (desiredZoomLevel > 4.5f) + desiredZoomLevel = 4.5f; + //OSReport("After capping: %f\n", desiredZoomLevel); + + // And we're almost there YAY + panToPosition(desiredCentreX, desiredCentreY, desiredZoomLevel); +} + + +void dWorldCamera_c::panToPosition(float x, float y, float zoom) { + panFromX = currentX; + panFromY = currentY; + panFromZoom = zoomLevel; + + panToX = x; + panToY = y; + panToZoom = zoom; + + float xDiff = abs(panToX - panFromX); + float yDiff = abs(panToY - panFromY); + + float panLength = sqrtf((xDiff*xDiff) + (yDiff*yDiff)); + float panSteps = panLength / 2.3f; + float scaleSteps = abs(panToZoom - panFromZoom) / 0.1f; + float stepCount = max(panSteps, scaleSteps); + + //OSReport("Pan length: %f over %f steps\n", panLength, panSteps); + //OSReport("Scale steps: %f\n", scaleSteps); + //OSReport("Step Count: %f\n", stepCount); + + panCurrentStep = 0.0f; + panTotalSteps = stepCount; + + panning = true; + followPlayer = false; +} + int dWorldCamera_c::onDraw() { GXRenderModeObj *rmode = nw4r::g3d::G3DState::GetRenderModeObj(); diff --git a/src/koopatlas/camera.h b/src/koopatlas/camera.h index 39e34d0..836c819 100644 --- a/src/koopatlas/camera.h +++ b/src/koopatlas/camera.h @@ -3,6 +3,8 @@ #include "koopatlas/core.h" +#define STD_ZOOM 2.8f + class dWorldCamera_c : public dBase_c { public: int onCreate(); @@ -20,7 +22,6 @@ class dWorldCamera_c : public dBase_c { Vec camPos, camTarget, camUp; - float currentX, currentY, zoomLevel; void calculateScreenGeometry(); @@ -28,6 +29,14 @@ class dWorldCamera_c : public dBase_c { float zoomDivisor, screenLeft, screenTop, screenWidth, screenHeight; + bool followPlayer; + void panToBounds(float left, float top, float right, float bottom); + void panToPosition(float x, float y, float zoom=STD_ZOOM); + bool panning; + float panFromX, panFromY, panToX, panToY; + float panFromZoom, panToZoom; + float panCurrentStep; + float panTotalSteps; void doStuff(float); void generateCameraMatrices(); diff --git a/src/koopatlas/hud.cpp b/src/koopatlas/hud.cpp index ce54d8f..22928a5 100644 --- a/src/koopatlas/hud.cpp +++ b/src/koopatlas/hud.cpp @@ -110,11 +110,6 @@ int dWMHud_c::onCreate() { willShowFooter = false; loadFooterInfo(); - SaveBlock *save = GetSaveFile()->GetBlock(-1); - willShowFooter = (save->newerWorldName[0] != 0) && (save->hudHintH != 2000); - - if (!dScKoopatlas_c::instance->pathManager.isMoving) - enteredNode(); setupLives(); } @@ -123,6 +118,20 @@ int dWMHud_c::onCreate() { } +void dWMHud_c::loadInitially() { + if (doneFirstShow) + return; + + doneFirstShow = true; + + SaveBlock *save = GetSaveFile()->GetBlock(-1); + willShowFooter = (save->newerWorldName[0] != 0) && (save->hudHintH != 2000); + + if (!dScKoopatlas_c::instance->pathManager.isMoving) + enteredNode(); +} + + int dWMHud_c::onDelete() { dWMHud_c::instance = 0; diff --git a/src/koopatlas/hud.h b/src/koopatlas/hud.h index 982b38a..2fd194e 100644 --- a/src/koopatlas/hud.h +++ b/src/koopatlas/hud.h @@ -19,6 +19,9 @@ class dWMHud_c : public dBase_c { static dWMHud_c *build(); static dWMHud_c *instance; + bool doneFirstShow; + void loadInitially(); + void enteredNode(dKPNode_s *node = 0); void leftNode(); diff --git a/src/koopatlas/pathmanager.cpp b/src/koopatlas/pathmanager.cpp index 89b9f59..48590f9 100644 --- a/src/koopatlas/pathmanager.cpp +++ b/src/koopatlas/pathmanager.cpp @@ -50,12 +50,14 @@ void dWMPathManager_c::setup() { levelStartWait = -1; unlockPaths(); - waitForAfterDeathAnim = -1; - mustPlayAfterDeathAnim = false; + waitAfterInitialPlayerAnim = -1; if (LastPowerupStoreType == LOSE_LEVEL) { mustPlayAfterDeathAnim = true; daWMPlayer_c::instance->visible = false; LastPowerupStoreType = BEAT_LEVEL; + } else if (LastPowerupStoreType == BEAT_LEVEL && LastLevelPlayed[0] != 0xFF) { + mustPlayAfterWinAnim = true; + daWMPlayer_c::instance->visible = false; } SpammyReport("done\n"); @@ -78,7 +80,7 @@ void dWMPathManager_c::setup() { found = true; currentNode = node; - //OSReport("Found CHANGE node: %d %p\n", changeID, node); + SpammyReport("Found CHANGE node: %d %p\n", changeID, node); // figure out where we should move to dKPPath_s *exitTo = 0; @@ -237,6 +239,8 @@ void dWMPathManager_c::setup() { if (wm->isAfterKamekCutscene) copyWorldDefToSave(wm->mapData.findWorldDef(1)); + + finalisePathUnlocks(); } static u8 *PathAvailabilityData = 0; @@ -505,10 +509,13 @@ void dWMPathManager_c::unlockPaths() { node->setLayerAlpha((node->isUnlocked() & !node->isNew) ? 255 : 0); } +} +void dWMPathManager_c::finalisePathUnlocks() { // if anything was new, set it as such if (newlyAvailablePaths || newlyAvailableNodes) { countdownToFadeIn = 30; + findCameraBoundsForUnlockedPaths(); } unlockingAlpha = -1; } @@ -592,7 +599,9 @@ bool dWMPathManager_c::evaluateUnlockCondition(u8 *&in, SaveBlock *save, int sta bool dWMPathManager_c::doingThings() { if (isEnteringLevel || (waitAfterUnlock > 0) || (completionAnimDelay > 0) || - (waitAtStart > 0) || (waitForAfterDeathAnim > 0) || + (waitAtStart > 0) || (waitAfterInitialPlayerAnim > 0) || + panningCameraToPaths || panningCameraFromPaths || + (waitBeforePanBack > 0) || (countdownToFadeIn > 0) || (unlockingAlpha != -1)) return true; @@ -613,11 +622,52 @@ void dWMPathManager_c::execute() { return; } + if (waitAtStart > 0) { + waitAtStart--; + if (waitAtStart == 0) { + if (mustPlayAfterDeathAnim) { + daWMPlayer_c::instance->visible = true; + daWMPlayer_c::instance->startAnimation(ending_wait, 1.0f, 0.0f, 0.0f); + waitAfterInitialPlayerAnim = 60; + + nw4r::snd::SoundHandle something; + PlaySoundWithFunctionB4(SoundRelatedClass, &something, SE_VOC_MA_CS_COURSE_MISS, 1); + } else if (mustPlayAfterWinAnim) { + daWMPlayer_c::instance->visible = true; + daWMPlayer_c::instance->startAnimation(dm_surp_wait, 1.0f, 0.0f, 0.0f); + waitAfterInitialPlayerAnim = 38; + + nw4r::snd::SoundHandle something; + PlaySoundWithFunctionB4(SoundRelatedClass, &something, SE_VOC_MA_CS_JUMP, 1); + } + } + return; + } + + if (waitAfterInitialPlayerAnim > 0) { + waitAfterInitialPlayerAnim--; + if (waitAfterInitialPlayerAnim == 0) + daWMPlayer_c::instance->startAnimation(wait_select, 1.0f, 0.0f, 0.0f); + if (mustPlayAfterWinAnim && (waitAfterInitialPlayerAnim == 9)) { + nw4r::snd::SoundHandle something; + PlaySoundWithFunctionB4(SoundRelatedClass, &something, SE_PLY_LAND_ROCK, 1); + } + return; + } + + // handle path fading if (countdownToFadeIn > 0) { countdownToFadeIn--; if (countdownToFadeIn <= 0) { - unlockingAlpha = 0; + if (camBoundsValid) { + dWorldCamera_c::instance->currentX = currentNode->x; + dWorldCamera_c::instance->currentY = -currentNode->y; + dWorldCamera_c::instance->panToBounds(camMinX, camMinY, camMaxX, camMaxY); + + panningCameraToPaths = true; + } else + unlockingAlpha = 0; nw4r::snd::SoundHandle something; PlaySoundWithFunctionB4(SoundRelatedClass, &something, SE_OBJ_GEN_LOAD, 1); @@ -626,6 +676,13 @@ void dWMPathManager_c::execute() { } } + if (panningCameraToPaths) { + if (dWorldCamera_c::instance->panning) + return; + panningCameraToPaths = false; + unlockingAlpha = 0; + } + if (unlockingAlpha != -1) { unlockingAlpha += 3; @@ -648,6 +705,7 @@ void dWMPathManager_c::execute() { unlockingAlpha = -1; nw4r::snd::SoundHandle something; PlaySoundWithFunctionB4(SoundRelatedClass, &something, SE_OBJ_GEN_NEW_COURSE, 1); + waitAfterUnlock = 15; for (int i = 0; i < pathLayer->nodeCount; i++) { @@ -667,16 +725,36 @@ void dWMPathManager_c::execute() { if (waitAfterUnlock > 0) { waitAfterUnlock--; + if (waitAfterUnlock == 0) + waitBeforePanBack = 20; return; } + if (waitBeforePanBack > 0) { + waitBeforePanBack--; + if (waitBeforePanBack == 0 && camBoundsValid) { + dWorldCamera_c::instance->panToPosition( + currentNode->x, -currentNode->y, + STD_ZOOM); + panningCameraFromPaths = true; + } + } + + if (panningCameraFromPaths) { + if (dWorldCamera_c::instance->panning) + return; + panningCameraFromPaths = false; + dWorldCamera_c::instance->followPlayer = true; + } + + if (dmGladDuration > 0) { + dmGladDuration--; + if (dmGladDuration == 0) + daWMPlayer_c::instance->startAnimation(wait_select, 1.0f, 0.0f, 0.0f); + } + if (completionAnimDelay > 0) { completionAnimDelay--; - if (dmGladDuration > 0) { - dmGladDuration--; - if (dmGladDuration == 0) - daWMPlayer_c::instance->startAnimation(wait_select, 1.0f, 0.0f, 0.0f); - } if (completionAnimDelay == 0) completionMessagePending = true; return; @@ -710,35 +788,17 @@ void dWMPathManager_c::execute() { return; } - if (waitAtStart > 0) { - waitAtStart--; - if (waitAtStart == 0) { - if (mustPlayAfterDeathAnim) { - daWMPlayer_c::instance->visible = true; - daWMPlayer_c::instance->startAnimation(ending_wait, 1.0f, 0.0f, 0.0f); - waitForAfterDeathAnim = 60; - - nw4r::snd::SoundHandle something; - PlaySoundWithFunctionB4(SoundRelatedClass, &something, SE_VOC_MA_CS_COURSE_MISS, 1); - } - - dScKoopatlas_c::instance->startMusic(); - } - return; - } - - if (waitForAfterDeathAnim > 0) { - waitForAfterDeathAnim--; - if (waitForAfterDeathAnim == 0) - daWMPlayer_c::instance->startAnimation(wait_select, 1.0f, 0.0f, 0.0f); - return; - } - if (shouldRequestSave) { dScKoopatlas_c::instance->showSaveWindow(); shouldRequestSave = false; } + if (!initialLoading) { + dScKoopatlas_c::instance->startMusic(); + dWMHud_c::instance->loadInitially(); + initialLoading = true; + } + int nowPressed = Remocon_GetPressed(GetActiveRemocon()); // Left, right, up, down @@ -1191,7 +1251,7 @@ void dWMPathManager_c::moveThroughPath(int pressedDir) { } else if (reallyStop) { // Stop here - player->startAnimation(0, 1.2, 10.0, 0.0); + player->startAnimation(wait_select, 1.2, 10.0, 0.0); player->hasEffect = false; player->hasSound = false; @@ -1314,3 +1374,75 @@ void dWMPathManager_c::unlockAllPaths(char type) { } + + +void dWMPathManager_c::findCameraBoundsForUnlockedPaths() { + dKPMapData_c *data = &dScKoopatlas_c::instance->mapData; + + camMinX = 10000; + camMaxX = 0; + camMinY = 10000; + camMaxY = 0; + + nodeStackLength = 0; + for (int i = 0; i < data->pathLayer->nodeCount; i++) + data->pathLayer->nodes[i]->reserved1 = false; + + visitNodeForCamCheck(currentNode); + OSReport("Worked out camera bounds: %d,%d to %d,%d with validity %d\n", camMinX, camMinY, camMaxX, camMaxY, camBoundsValid); +} + +void dWMPathManager_c::visitNodeForCamCheck(dKPNode_s *node) { + // Yay. + nodeStackLength++; + node->reserved1 = true; + + for (int i = 0; i < 4; i++) { + dKPPath_s *path = node->exits[i]; + if (!path) + continue; + + OSReport("Checking path %p, whose status is %d\n", path, path->isAvailable); + if (path->isAvailable == dKPPath_s::NEWLY_AVAILABLE) { + addNodeToCameraBounds(path->start); + addNodeToCameraBounds(path->end); + } + + // Should we follow the other node? + dKPNode_s *otherNode = path->getOtherNodeTo(node); + + if (otherNode->reserved1) + continue; + if (otherNode->type == otherNode->LEVEL) { + OSReport("Not travelling to %p because it's level %d-%d\n", otherNode, otherNode->levelNumber[0], otherNode->levelNumber[1]); + continue; + } + if (otherNode->type == otherNode->CHANGE) { + OSReport("Not travelling to %p because it's a change\n", otherNode); + continue; + } + if (otherNode->type == otherNode->WORLD_CHANGE) { + OSReport("Not travelling to %p because it's a world change\n", otherNode); + continue; + } + + visitNodeForCamCheck(otherNode); + } + + nodeStackLength--; +} + +void dWMPathManager_c::addNodeToCameraBounds(dKPNode_s *node) { + camBoundsValid = true; + OSReport("Adding node to camera bounds: %p at %d,%d\n", node, node->x, node->y); + + if (node->x < camMinX) + camMinX = node->x; + if (node->x > camMaxX) + camMaxX = node->x; + if (node->y < camMinY) + camMinY = node->y; + if (node->y > camMaxY) + camMaxY = node->y; +} + diff --git a/src/koopatlas/pathmanager.h b/src/koopatlas/pathmanager.h index 522db7f..27fcfd6 100644 --- a/src/koopatlas/pathmanager.h +++ b/src/koopatlas/pathmanager.h @@ -74,12 +74,16 @@ class dWMPathManager_c { int newlyAvailablePaths; int newlyAvailableNodes; + bool panningCameraToPaths; + bool panningCameraFromPaths; int unlockingAlpha; // -1 if not used int countdownToFadeIn; int waitAfterUnlock; + int waitBeforePanBack; private: void unlockPaths(); + void finalisePathUnlocks(); bool evaluateUnlockCondition(u8 *&in, SaveBlock *save, int stack); int cachedTotalStarCoinCount; int cachedUnspentStarCoinCount; @@ -97,11 +101,22 @@ class dWMPathManager_c { int levelStartWait; int waitAtStart; bool mustPlayAfterDeathAnim; - int waitForAfterDeathAnim; + bool mustPlayAfterWinAnim; + int waitAfterInitialPlayerAnim; + bool initialLoading; bool doingThings(); dLevelInfo_c::entry_s *enteredLevel; + + + private: + int camMinX, camMinY, camMaxX, camMaxY; + int nodeStackLength; + bool camBoundsValid; + void visitNodeForCamCheck(dKPNode_s *node); + void findCameraBoundsForUnlockedPaths(); + void addNodeToCameraBounds(dKPNode_s *node); }; #endif -- cgit v1.2.3